1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. Welcome to Smashboards, the world's largest Super Smash Brothers community! Over 250,000 Smash Bros. fans from around the world have come to discuss these great games in over 19 million posts!

    You are currently viewing our boards as a visitor. Click here to sign up right now and start on your path in the Smash community!

Completed Primitive Drawing Module

Discussion in 'Melee Codes' started by Punkline, Apr 15, 2018.

  1. Punkline

    Punkline
    Expand Collapse
    Dr. Frankenstack

    • Premium
    Joined:
    May 15, 2015
    Messages:
    242
    This MCM module recycles parts of the sword trail drawing function using a couple of projection mods. The resulting functions have been used to create a light-weight library that allows developers to draw various primitive types.

    To make use of the functions in this library, install the DOL mod with the latest version of Melee Code Manager.

    Code:
    -==-
    
    Primitive Drawing Module
    Use included functions to send GX FIFO stack vertex information for drawing primitives.
    See ( https://smashboards.com/threads/primitive-drawing-module.454232/ ) for more info.
    [Punkline]
    1.02 ----- 0x800c2684 --- 386000013880000438a0000538c00005 -> 57E3A7BE 57E4C77E 57E5E77E 57E6073E
    1.02 ----- 0x800c26b0 --- 386000013880000338a00000 ->57C3A7FE 57C4C77E 57C5AFFE
    1.02 ----- 0x800c26c0 --- 38600000 -> 57C39FFE
    1.02 ----- 0x800c272c --- 38600000 -> 57C317BE
    1.02 ----- 0x800c2da0 --- 38b80001 -> branch
    b <projection_return>
    00000000
    1.02 ----- 0x800c2674 --- 38600001 -> branch
    bl <prim_default_swordtrails>
    7C8802A6 7C6444AA
    bl <prim_setup>
    881C2101
    b 0x800c2738
    60000000
    1.02 ----- 0x800c2734 --- 881c2101 -> branch
    BBC10010
    b <projection_return>
    60000000
    1.02 ----- 0x800c2d3c --- 4827968d -> branch
    bl <prim_mtx_setup>
    38B80001
    b 0x800c2da4
    60000000
    <prim_setup> 1.02
    7C0802A6 90010004 9421C000 BFC10010 7C7E1B78 7C9F2378 38600001
    b 0x800c2678
    <prim_mtx_setup> 1.02
    7C0802A6 90010004 9421C000
    bl 0x8033C3C8
    b  0x800c2d40
    <projection_return> All
    38214000 80010004 7C0803A6 4E800020
    <GXBegin> 1.02
    b 0x8033D0DC
    <GXSetLineWidth> 1.02
    b 0x8033D240
    <GXSetPointSize> 1.02
    b 0x8033d298
    <prim_close> 1.02
    3860FFFF
    b 0x80361fc4
    <prim_main> All
    7C0802A6 90010004 9421FFF8
    bl <prim_defaults>
    7D0802A6 80A80000 7C842B78 80A80004 3CC00010 60C61300 38E01455 7CC845AA
    bl <prim_main_parametric>
    38210008 80010004 7C0803A6 4E800020
    <prim_main_parametric> All
    7C0802A6 90010004 9421FFC0 BFA10010 7C7E1B78 7C9F2378 7C832378 7CA42B78
    bl <prim_setup>
    bl <prim_mtx_setup>
    57E3063E 2C030005 41A00024 2C030007 4181001C 57E385BE 38800005 4182000C
    bl <GXSetLineWidth>
    48000008
    bl <GXSetPointSize>
    57E31E38 38630080 38800000 57C5043E
    bl     <GXBegin>
    3C60CC01 38638000 BBA10010 38210040 80010004 7C0803A6 4E800020
    <prim_quads> All
    38800000
    b <prim_main>
    <prim_triangles> All
    38800002
    b <prim_main>
    <prim_trianglestrip> All
    38800003
    b <prim_main>
    <prim_trianglefan> All
    38800004
    b <prim_main>
    <prim_lines> All
    38800005
    b <prim_main>
    <prim_linestrip> All
    38800006
    b <prim_main>
    <prim_points> All
    38800007
    b <prim_main>
    <prim_defaults>
    4E800021 00101300 00001455
    <prim_default_swordtrails>
    4E800021 00101300 00001455
    <prim_toggle_z>
    7C0802A6 90010004 9421FFF8
    bl <prim_defaults>
    7C6802A6 80830000 68841000 90830000 38210008 80010004 7C0803A6 4E800020
    
    Code:
    -==-
    !
    ASM - Primitive Drawing Module
    Use included functions to send GX FIFO stack vertex information for drawing primitives.
    See ( https://smashboards.com/threads/primitive-drawing-module.454232/ ) for more info.
    [Punkline]
    1.02 ----- 0x800c2684 --- 386000013880000438a0000538c00005 -> 57E3A7BE 57E4C77E 57E5E77E 57E6073E
    1.02 ----- 0x800c26b0 --- 386000013880000338a00000 ->57C3A7FE 57C4C77E 57C5AFFE
    1.02 ----- 0x800c26c0 --- 38600000 -> 57C39FFE
    1.02 ----- 0x800c272c --- 38600000 -> 57C317BE
    # these overwrites convert loaded immediates into rlwinms
    # -- this allows the primitives to be drawn somewhat parametrically
    
    
    1.02 ----- 0x800c2da0 --- 38b80001 -> branch
    # projection end <prim_mtx_setup>
    b <projection_return>
    .long 0
    
    
    1.02 ----- 0x800c2674 --- 38600001 -> branch
    # -- projection start for <prim_setup>
    # External prolog fabricates an interface for the
    # purpose of creating parameters in the setup process:
    #    r3       r4
    # C0000000 00000000 = r3 for GXSetCullMode
    # 03FF0000 00000000 =  (line width, or point size)
    # 00002000 00000000 = r3 for GXSetCompLoc
    # 00001000 00000000 = r3 for GXSetZMode
    # 00000800 00000000 = r5 for GXSetZMode
    # 00000700 00000000 = r4 for GXSetZMode
    # 000000FF 00000000 =  (primitive type)
    # 00000000 00003000 = r3 for GXSetBlendMode
    # 00000000 00000700 = r4 for GXSetBlendMode
    # 00000000 00000070 = r5 for GXSetBlendMode
    # 00000000 0000000F = r6 for GXSetBlendMode
    
    # 00101303 00001455 = defaults
    
    # each paraam is parsed by an rlwinm (see static overwrites)
    # (line width and primite type are not parsed here)
    
    bl <prim_default_swordtrails>
    mflr r4
    lswi r3, r4, 0x8
    # load sword trail parameters for natural trail drawing
    # modifying these params will change the way sword trails are drawn
    
    bl <prim_setup>
    lbz r0, 0x2101 (r28) # repair INJ2 hook instruction externally
    b 0x800c2738
    nop
    
    
    1.02 ----- 0x800c2734 --- 881c2101 -> branch
    # projection end <prim_setup>
    # lbz r0, 0x2101 (r28)
    lmw r30, 0x10(sp)
    b <projection_return>
    nop
    
    
    1.02 ----- 0x800c2d3c --- 4827968d -> branch
    # projection start <prim_mtx_setup>
    bl <prim_mtx_setup>
    addi r5, r24, 1
    b 0x800c2da4
    nop
    
    
    <prim_setup> 1.02
    # projection handle
    # r3 = params 1 = C WWW ZZ PP  (W is unused here)
    # r4 = params 2 = ---- BBBB
    # r30 and r31 are safe to use for projection duration
    mflr r0
    stw  r0, 0x4(sp)
    stwu sp, -0x4000(sp)
    stmw r30, 0x10(sp)
    
    mr r30, r3  # Static overwrite strings will create rlwinms that extract from r30 and r31.
    mr r31, r4  # These will replace some hardcoded arguments made for GX function calls
    
    li r3, 1    # original instruction; arg for GXSetColorUpdate
    b 0x800c2678
    
    
    <prim_mtx_setup> 1.02
    # projection handle
    mflr r0
    stw  r0, 0x4(sp)
    stwu sp, -0x4000(sp)
    
    bl 0x8033C3C8    # GXClearVtxDesc -- original instruction
    b  0x800c2d40
    
    
    <projection_return> All
    # reusable return function
    addi  sp, sp, 0x4000
    lwz  r0, 0x4(sp)
    mtlr r0
    blr
    
    
    <GXBegin> 1.02
    b 0x8033D0DC # portable function call
    
    
    <GXSetLineWidth> 1.02
    b 0x8033D240
    
    
    <GXSetPointSize> 1.02
    b 0x8033d298
    
    
    <prim_close> 1.02
    li r3, -1
    b 0x80361fc4
    
    
    <prim_main> All
    # r3 = vert count
    # r4 = primitive type
    mflr r0
    stw  r0, 0x4(sp)
    stwu sp, -0x8(sp)
    bl <prim_defaults>
    mflr r8
    lwz r5, 0(r8)
    or r4, r4, r5
    lwz r5, 4(r8)
    # load params
    
    lis r6,      0x0010
    ori r6, r6,  0x1300
    li   r7,     0x1455
    stswi r6, r8, 0x8
    # reset params to default for next draw
    
    bl <prim_main_parametric>
    
    _return:
    addi sp, sp, 8
    lwz r0, 0x4(sp)
    mtlr r0
    blr
    
    
    
    <prim_main_parametric> All
    # r3 = vert count
    #    r4        r5
    # C0000000  00000000 = Cull Mode
    #  +8 = cull backface
    #  +4 = cull frontface
    
    # 03FF0000  00000000 = Line Width/Point Size
    #   1/16 pixel increments; 0x2A8 maximum width
    
    # 00002000  00000000 = Compare Location
    #  +1 = z buffer compares before texturing
    
    # 00001000  00000000 = Z Buffer Compare
    #  +1 = z buffer enables compare
    
    # 00000800  00000000 = Z Buffer Update
    #  +1 = z buffer enables update
    
    # 00000700  00000000 = Z Buffer Comparison Logic
    #  +4 = greater than
    #  +2 = equal to
    #  +1 = less than
    
    # 000000FF  00000000 = Primitive Type
    #   0 = quads
    #   1 = --
    #   2 = triangles
    #   3 = trianglestrip
    #   4 = trianglefan
    #   5 = lines
    #   6 = linestrip
    #   7 = points
    
    # 00000000  00003000 = Blend Type
    #   0 = None  -- write input directly to EFB
    #   1 = Blend -- blend using blending equation
    #   2 = Logic -- blend using bitwise operation
    #   3 = Subtract -- input subtracts from existing pixel
    
    # 00000000  00000700 = Blend Source
    #   0 = zero -- 0.0
    #   1 = one  -- 1.0
    #   2 = source color
    #   3 = inverted source color
    #   4 = source alpha
    #   5 = inverted source alpha
    #   6 = destination alpha
    #   7 = inverted destination alpha
    
    # 00000000  00000070 = Blend Dest
    #   (see above)
    
    # 00000000  0000000F = Blend Logic
    #   0 -- CLEAR;   dst = 0
    #   1 -- AND;     dst = src & dst
    #   2 -- REVAND;  dst = src & ~dst
    #   3 -- COPY;    dst = src
    #   4 -- INVAND;  dst = ~src & dst
    #   5 -- NOOP;    dst = dst
    #   6 -- XOR;     dst = src ^ dst
    #   7 -- OR;      dst = src | dst
    #   8 -- NOR;     dst = ~(src | dst)
    #   9 -- EQUIV;   dst = ~(src ^ dst)
    #   A -- INV;     dst = ~dst
    #   B -- REVOR;   dst = src | ~dst
    #   C -- INVCOPY; dst = ~src
    #   D -- INVOR;   dst = ~src | dst
    #   E -- NAND;    dst = ~(src & dst)
    #   F -- SET;     dst = 1
    
    _macros:
    .macro countShift  m, c
      # countShift = recursive macro updates symbol "maskShift"
      # maskShift  = number of 0 bits padding first mask bit (from right to left)
    
      # <- termination condition allows us to escape loop
      .if \m&1               # if (mask & 0x00000001) is not == 0
        .set maskShift, \c     # maskshift = count   -- termination
    
        # <- recursive macro call allows us to iterate loop
        .else                  # else
        .set maskShift, \c+1   # maskshift = count++ -- incr counter
        countShift \m>>1, \c+1 # call self
    
      .endif # close if block
    .endm  # close macro block
    
    # macro "countShift" can be used to count the zero bits
    # resulting symbol "maskShift" can be used to imply shift amount in rlwinm and rlwimi syntax
    
    .macro mext  rD, rS, m
    # mext : masked extract
      countShift \m, 0    # m = 32-bit mask
      rlwinm \rD, \rS, (32-maskShift)%32, \m>>maskShift
    .endm
    
    mflr r0
    stw  r0, 0x4(sp)
    stwu sp, -0x40(sp)
    stmw r29, 0x10(sp)
    
    mr r30, r3 # save r3 and r4 for after projection calls
    mr r31, r4 #
    mr r3,  r4 # r3 = params 1
    mr r4,  r5 # r4 = params 2
    
    bl <prim_setup>     # set up params
    bl <prim_mtx_setup> # set up mtx
    
    mext   r3, r31, 0x000000FF  # r3 = primitive ID
    cmpwi  r3, 5                 # if it's larger than
    blt+ _end_width_handle
      cmpwi  r3, 7
      bgt- _end_width_handle
        mext   r3, r31, 0x03FF0000
        li     r4, 5
        beq- _point_handle
    
        _width_handle: # exclusively for lines and linestrips
        bl <GXSetLineWidth>
        b _end_width_handle
    
        _point_handle: # exclusively for points
        bl <GXSetPointSize>
    
    _end_width_handle:
    
    rlwinm  r3, r31, 3, 0x000000F8
    addi   r3, r3, 0x80  # r3 is now a primitive ID that can be fed to GXBegin
    li     r4, 0         # use vtx format 0
    mext   r5, r30, 0x0000FFFF # use N number of vertices
    bl     <GXBegin>
    
    lis  r3, 0xCC01
    addi r3, r3, -0x8000 # r3 = address of hardware FIFO stack
    # write vertex(X, Y, Z, C) to address in r3
    # each write must be to the exact same address
    
    lmw  r29, 0x10(sp)
    addi sp, sp, 0x40
    lwz  r0, 0x4(sp)
    mtlr r0
    blr
    
    
    # convenience functions:
    <prim_quads> All
    li r4, 0
    b <prim_main>
    
    
    <prim_triangles> All
    li r4, 2
    b <prim_main>
    
    
    <prim_trianglestrip> All
    li r4, 3
    b <prim_main>
    
    
    <prim_trianglefan> All
    li r4, 4
    b <prim_main>
    
    
    <prim_lines> All
    li r4, 5
    b <prim_main>
    
    
    <prim_linestrip> All
    li r4, 6
    b <prim_main>
    
    
    <prim_points> All
    li r4, 7
    b <prim_main>
    
    <prim_defaults>
    blrl
    .long 0x00101300
    .long 0x00001455
    
    <prim_default_swordtrails>
    blrl
    .long 0x00101300
    .long 0x00001455
    
    <prim_toggle_z>
    # this function simply toggle the z buffer comparison in the Z buffer options
    # it's meant to make it easy for people who just want to draw on top of things
    mflr r0
    stw  r0, 0x4(sp)
    stwu sp, -0x8(sp)
    bl <prim_defaults>
    mflr r3
    lwz r4, 0(r3)
    xori r4, r4, 0x1000 # toggle z comparison bit
    stw r4, 0(r3)
    addi sp, sp, 8
    lwz r0, 4(sp)
    mtlr r0
    blr
    
    

    ---

    Example codes:
    - ECB Anchor Visualization
    - Player JObj tracer
    - Colored Player Skeletons

    ---

    With this module installed, developers can make use of the following functions for the purpose of drawing to the EFB:

    bl <prim_quads>
    # draw a series of individual quadrilateral 4-point polygons

    bl <prim_triangles>
    # draw a series of individual triangle 3-point polygons

    bl <prim_trianglestrip>
    # draw a contiguous strip of connecting 3-point triangles that share an edge for each connection in the sequence.

    bl <prim_trianglefan>
    # draw a triangle strip where one side of the strip circles around a central vertex

    bl <prim_lines>
    # draw a series of individual lines using 2 points per line. Line width is the same for each line in the series.

    bl <prim_linestrip>
    # draw a contiguous strip of connecting 2-point lines

    bl <prim_points>
    # draw a series of individual points, using single vertices. Point size is the same for each point in the series.

    Each of the above functions only require the argument:
    r3 = number of vertices

    When the function returns, you may use the address returned in r3 to store vertices.
    note - when storing vertex information, all data goes to the exact same address; 0(r3)

    To describe a vertex, store the following values to 0(r3) in a sequence:
    1 - floating point single = vertex X
    2 - floating point single = vertex Y
    3 - floating point single = vertex Z
    4 - 32-bit UINT = RGBA color

    ---

    The following two functions can be called without arguments:

    bl <prim_close>
    # use this at the end of your drawing to prevent params from corrupting the next GFX drawing in the game

    bl <prim_toggle_z>
    # use this before starting a primitive to toggle the zbuffer comparison on or off. If off, primitives will be drawn on top of other geometry. On by default.

    bl <prim_main>
    # r3 = vert count
    # r4 = primitive type -- used to provide all of the above basic functions
    # this call creates parameters similar to the basic sword trail drawings by default
    # <prim_defaults> can be modified to change the params for the next drawn primitive
    # primitive types for r4 argument
    # 0 - quads
    # 1 - (quads2)
    # 2 - triangles
    # 3 - trianglestrip
    # 4 - trianglefan
    # 5 - lines
    # 6 - linestrip
    # 7 - points

    bl <prim_defaults>
    # this blrl table can be accessed with an mflr instruction upon return.
    # it may be modified in order to change the parameters of the nex <prim_main> call
    # parameters will be reverted after call to <prim_main>
    # see the following function for details about params structure

    bl <prim_main_parametric>
    # this version of the function lets you specify the parameters directly using immediates
    # r3 = vert count
    # r4 = params 1
    # r5 = params 2

    # the parameters are encoded like so:

    # r4 r5
    # C0000000 00000000 = Cull Mode
    # +8 = cull backface
    # +4 = cull frontface

    # 03FF0000 00000000 = Line Width/Point Size
    # 1/16 pixel increments; 0x2A8 maximum width
    # only used for points and lines; otherwise ignored

    # 00002000 00000000 = Compare Location
    # +1 = z buffer compares before texturing
    # 00001000 00000000 = Z Buffer Compare
    # +1 = z buffer enables compare

    # 00000800 00000000 = Z Buffer Update
    # +1 = z buffer enables update

    # 00000700 00000000 = Z Buffer Comparison Logic
    # +4 = greater than
    # +2 = equal to
    # +1 = less than

    # 000000FF 00000000 = Primitive Type
    # 0 = quads
    # 1 = --
    # 2 = triangles
    # 3 = trianglestrip
    # 4 = trianglefan
    # 5 = lines
    # 6 = linestrip
    # 7 = points

    # 00000000 00003000 = Blend Type
    # 0 = None -- write input directly to EFB
    # 1 = Blend -- blend using blending equation
    # 2 = Logic -- blend using bitwise operation
    # 3 = Subtract -- input subtracts from existing pixel

    # 00000000 00000700 = Blend Source
    # 0 = zero -- 0.0
    # 1 = one -- 1.0
    # 2 = source color
    # 3 = inverted source color
    # 4 = source alpha
    # 5 = inverted source alpha
    # 6 = destination alpha
    # 7 = inverted destination alpha

    # 00000000 00000070 = Blend Dest
    # (see above)

    # 00000000 0000000F = Blend Logic
    # 0 -- CLEAR; dst = 0
    # 1 -- AND; dst = src & dst
    # 2 -- REVAND; dst = src & ~dst
    # 3 -- COPY; dst = src
    # 4 -- INVAND; dst = ~src & dst
    # 5 -- NOOP; dst = dst
    # 6 -- XOR; dst = src ^ dst
    # 7 -- OR; dst = src | dst
    # 8 -- NOR; dst = ~(src | dst)
    # 9 -- EQUIV; dst = ~(src ^ dst)
    # A -- INV; dst = ~dst
    # B -- REVOR; dst = src | ~dst
    # C -- INVCOPY; dst = ~src
    # D -- INVOR; dst = ~src | dst
    # E -- NAND; dst = ~(src & dst)
    # F -- SET; dst = 1

    ---

    I will post some demonstrations soon, and link them in the example codes section.
     
    #1 Punkline, Apr 15, 2018
    Last edited: Apr 15, 2018
    DRGN, Yakult, tauKhan and 1 other person like this.
  2. Punkline

    Punkline
    Expand Collapse
    Dr. Frankenstack

    • Premium
    Joined:
    May 15, 2015
    Messages:
    242
    To demonstrate, I’ll be making a series of example codes. I have a few things in mind, so it’ll take me some time to come up with it all.

    To start, here are a few codes that use the drawing module to trace JObjs with line and dot primitives.

    ---

    ECB Anchor Visualization

    Code:
    -==-
    
    ECB Anchor Visualization
    primitive drawing test traces ECB anchor joints on players
    [Punkline]
    1.02 ----- 80081104 --- bb61008c -> branch
    2C1D0002 40A2004C
    bl <prim_toggle_z>
    38600007
    bl <prim_linestrip>
    809C002C 38000007 7C0903A6 3800FFFF 80A407F8 C0050050 D0030000 C0050060 D0030000 C0050070 D0030000 90030000 38840004 4200FFDC
    bl <prim_close>
    BB61008C 00000000
    
    Code:
    -==-
    !
    ECB Anchor Visualization
    primitive drawing test traces ECB anchor joints on players
    [Punkline]
    1.02 ----- 80081104 --- bb61008c -> branch
    # this injection is in the middle of a drawing loop used by players
    # it has the active camera set to the main camera, so drawing will appear on screen
    # injection placement ensures it draws after the player
    # r28 = player entity
    # r29 = z loop iteration (out of 2)
    cmpwi r29, 2
    bne+ _return
    # with this, our drawing will only happen after everything on the player is drawn
    
    bl <prim_toggle_z>
    # we want to make our line drawing appear on top of the player
    # toggling z buffer comparison makes it so that it ignores intersection with player
    
    li r3, 7  # r3 argument = number of vertices to draw
    bl <prim_linestrip>
    # returns r3 = fifo pipe address
    # writing to this will let us create vertices
    # we need to write to 0(r3) for every piece of information given to GX
    
    lwz r4, 0x2C(r28)
    li r0, 7
    mtctr r0
    # load player data, and set up a bdnz loop for 7 iterations
    # this will catch each JObj we want to trace
    
    li r0, -1
    # r0 = 0xFFFFFFFF -- or the RGBA color white
    
    _loop:
    lwz  r5, 0x7F8(r4)
    lfs  f0, 0x50(r5) # load X vert translation
    stfs f0, 0(r3)    # write to GX FIFO
    lfs  f0, 0x60(r5) # load Y
    stfs f0, 0(r3)    # write Y
    lfs  f0, 0x70(r5) # load Z
    stfs f0, 0(r3)    # write Z
    stw  r0, 0(r3)    # write RGBA color
    # X, Y, Z, and color create a vertex
    
    addi r4, r4, 4   # move to next JObj
    bdnz+ _loop      # iterate loop
    # loop will finish after 7 vertices have been drawn
    
    bl <prim_close>
    # finish the primitive with this
    
    _return:
    lmw    r27, 0x008C (sp)  # original instruction
    .long 0
    

    This code simply draws lines between the bones that anchor a player’s ECB quad. It’s useful for seeing how the ECB are influenced by the player skeleton. @DRGN

    Player data offset 0x7F8 is the start of a series of JObj pointers, which stand for “joint” objects. Joints carry information about where they are in 3D space using offsets 0x50, 0x60, and 0x70 to store an absolute translation coordinate as part of a transformation matrix.

    By accessing the 7 joint pointers in a player using a loop, it’s possible to use these coordinates to draw the location of the bone. In this code, a simple default line primitive is created using <prim_linestrip> and 7 vertices are drawn using the X Y and Z translation coordinates of the corresponding JObjs:

    [​IMG]

    By calling <prim_linestrip> with 7 in r3, we tell the GX that we’re ready to draw 7 vertices for a linestrip primitive. The default parameters are set up to be just like sword trails, with lines taking up 1 native pixel in rasterized width:

    [​IMG]

    As you can see, the lines illustrate where the ECB joints are located, starting at the bottom with TopN. The lines are visible through the player because we called <prim_toggle_z> before <prim_linestrip>

    [​IMG]

    ---

    Player JObj Tracer

    Code:
    -==-
    
    Player JObj Tracer
    This primitive drawing test includes a function that traces JObj skeletons.
    By default, it's used to illustrate player joints.
    [Punkline]
    1.02 ----- 800c2660 --- 8383002c -> branch
    8383002C 807C05E8 80630050
    bl <illustrate_JObj_skeleton>
    00000000
    <illustrate_JObj_skeleton>
    7C0802A6 90010004 9421FFE0 BFC10008 7C7F1B78 807F0010 2C030000 4180FFE5 807F0008 2C030000 4180FFD9
    bl <prim_toggle_z>
    38600002 38800005 3BC0FFFF 48000018
    bl <prim_toggle_z>
    38600001 38800007 3FC0FF00 63DE00FF
    bl <prim_main>
    C01F0050 D0030000 C01F0060 D0030000 C01F0070 D0030000 93C30000 2C1EFFFF 40820028 83FF000C C01F0050 D0030000 C01F0060 D0030000 C01F0070 D0030000 93C30000 4BFFFFA4
    bl <prim_close>
    BBC10008 38210020 80010004 7C0803A6 4E800020
    
    Code:
    -==-
    !
    ASM - Player JObj Tracer
    This primitive drawing test includes a function that traces JObj skeletons.
    By default, it's used to illustrate player joints.
    [Punkline]
    1.02 ----- 800c2660 --- 8383002c -> branch
    lwz    r28, 0x002C (r3) # original instruction
    
    lwz r3, 0x5E8(r28)
    lwz r3, 0x50(r3) # load 5th bone in skeleton (to skip TopN, TransN, XRotN, YRotN)
    bl <illustrate_JObj_skeleton>
    .long 0
    
    
    
    <illustrate_JObj_skeleton>
    # r3 = THIS JObj
    
    _recursion:
    # recursive function will call itself for each remaining JObj in the family
    mflr r0
    stw  r0, 0x4(sp)
    stwu sp, -0x20(sp)
    stmw  r30, 0x8(sp)
    mr r31, r3
    
    lwz r3, 0x10(r31)
    # r3 = child
    cmpwi r3, 0
    bltl _recursion
    # if  child exists, recursively call self
    
    lwz r3, 0x8(r31)
    # r3 = sibling
    cmpwi r3, 0
    bltl _recursion
    # if sibling exists, recursively call self
    
    _line:
    bl <prim_toggle_z>
    li r3, 2   # 2 verts per line
    li r4, 5   # lines
    li r30, -1 # white
    # lines on first pass
    # note these are not linestrips, because we can't predict the number of verts we need
    b _draw
    
    _point:
    bl <prim_toggle_z>
    li r3, 1         # 1 vert per point
    li r4, 7         # points
    lis r30, 0xFF00  # red
    ori r30, r30, 0xFF
    # the saved register makes it so we can check for second pass conclusion
    
    _draw:
    # r3 = vert count
    # r4 = primitive type
    #     type 5 = lines
    #     type 7 = points
    bl <prim_main>
    # r3 = GX FIFO hardware address
    
    
    lfs f0, 0x50(r31)
    stfs f0, 0(r3)
    lfs f0, 0x60(r31)
    stfs f0, 0(r3)
    lfs f0, 0x70(r31)
    stfs f0, 0(r3)
    stw r30, 0(r3)
    # first JOBj vertex has been drawn
    
    cmpwi r30, -1
    bne _close
    # if on second pass, don't draw second vert
    
    lwz r31, 0xC(r31)
    # load JObj parent
    
    lfs f0, 0x50(r31)
    stfs f0, 0(r3)
    lfs f0, 0x60(r31)
    stfs f0, 0(r3)
    lfs f0, 0x70(r31)
    stfs f0, 0(r3)
    stw r30, 0(r3)
    # second vertex has been draw, completing the line
    # line should appear in the EFB for the next frame
    
    b _point
    # this branch is only made on the first pass
    
    _close:
    bl <prim_close>
    
    _return:
    lmw  r30, 0x8(sp)
    addi sp, sp, 0x20
    lwz  r0, 0x4(sp)
    mtlr r0
    blr
    

    This code uses recursion to iterate through all bones in a player skeleton, and draws the child->parent relationships. Instead of just using lines, this code makes a second pass and creates tiny red dots where a player bone is located using point primitives.

    The recursion is made possible by this structure:

    [​IMG]

    There is a child <-> parent and sibling -> sibling link in each skeletal JObj. By creating a function that recursively checks for each child and sibling from a root joint, it’s possible to create something that acts on them all:

    [​IMG]

    I was very surprised by the nature of player JObjs after observing this code. It seems that several bones in different player skeletons are not included in some animations -- causing them to stop being transformed and get left behind without the rest of the skeleton. This is really apparent in bowser’s jump animations, or any teleport move.



    For some, untransformed bones are just for like, a falcon punch graphic, or cape model for captain falcon and mario respectively; but for others it seems to be ragdoll joint ends, and some other special joints. If they haven’t been included in ANY animations, the joints will appear at coordinates 0,0,0 on the stage:



    For the drawing, the function <prim_main> is used to create a primitive type that’s specified by r4, allowing both lines and dots to be created from the same function:

    [​IMG]

    ---

    Colored Player Skeletons

    Code:
    -==-
    
    Colored Player Skeletons
    This is a test of the primitive drawing module.
    It draws lines between player joints that are moving, and dots where joints have stopped moving.
    Many characters have joints that stop being animated after a move, or that never move from the beginning of a match.
    [Punkline]
    1.02 ----- 80373804 --- 39000088 -> 390000A0
    1.02 ----- 800737e8 --- 39000088 -> 390000A0
    1.02 ----- 80073718 --- 39000088 -> 390000A0
    1.02 ----- 800304c8 --- 48338141 -> branch
    806DC18C 83E30020 2C1F0000 418200A4 38800001 80FF0028 80C7009C 811F002C 806805E8 80A80894 90A7009C 7C062800 40A20014 80A80010 2C05000B 41820008 38800000 38841000 80630050 38A0FF00 88E8000C 2C070004 4080004C 38C004E1 1D070003 7CC64430 54C9C1CE 50C97BDE 50C935EE 1CA900FF 3D008045 61083130 1C070E90 7CC8002E 7C06F800 41A20018 55203032 7CA50278 3C003F3F 60003F00 7CA50278
    bl <trace_JObj_skeleton>
    83FF0010 4BFFFF5C
    bl 0x80368608
    00000000
    <trace_JObj_skeleton> All
    7C0802A6 90010004 9421FFD0 BF810008 7C7F1B78 7C9E2378 7CBC2B78 807F0010 2C030000 4180FFDD 7FC4F378 7F85E378 807F0008 2C030000 4180FFC9 395F0094 7CCA64AA 893F0093 807F0050 809F0060 80BF0070 73C00001 41820008 7C6A65AA 7D3CE378 57C5421E 38800005 7F033000 7F843800 4CC73202 7F854000 4CC73202 2F8900FF 7FA00026 41820030 419A0018 39690040 2C0B00FF 4081001C 396000FF 48000014 3969FFFE 2C0B0000 40800008 39600000 997F0093 38600002 41BE0018 40BA0014 38600001 57C5421E 3CA50018 38800007 7CA42378 60840300 38A01455
    bl <prim_main_parametric>
    C01F0050 D0030000 C01F0060 D0030000 C01F0070 D0030000 93830000 7FAFF120 419E0008 419A0024 83FF000C C01F0050 D0030000 C01F0060 D0030000 C01F0070 D0030000 93830000
    bl <prim_close>
    BB810008 38210030 80010004 7C0803A6 4E800020
    
    Code:
    -==-
    !
    ASM - Colored Player Skeletons
    This is a test of the primitive drawing module.
    It draws lines between player joints that are moving, and dots where joints have stopped moving.
    Many characters have joints that stop being animated after a move, or that never move from the beginning of a match.
    [Punkline]
    1.02 ----- 80373804 --- 39000088 -> 390000A0
    1.02 ----- 800737e8 --- 39000088 -> 390000A0
    1.02 ----- 80073718 --- 39000088 -> 390000A0
    # JObj padding, 0x18 bytes are zeroed after JObj structure allocation
    # this does not change the resulting allocation size, since it aligns to 32 bytes
    
    
    1.02 ----- 800304c8 --- 48338141 -> branch
    # bl ->0x80368608 --  HSD_CObjEndCurrent
    # This is the end of a general match camera display function.
    # It's the same function that hosts displaying ECB quads for players and items.
    # The current HSD CObj = main camera entity CObj
    # The original instruction dismisses the current CObj
    # -- we fit in our drawing function before that.
    lwz r3, -0x3e74(r13)
    lwz r31,0x20(r3)
    # r31 = THIS player
    
    _for_each_player:
    cmpwi r31, 0
    beq- _return
    # termination condition
    
    li r4, 1
    lwz r7, 0x28(r31)
    lwz r6, 0x9C(r7)
    lwz r8, 0x2C(r31)
    lwz r3, 0x5E8(r8)
    # r3 = player skeleton base index
    # r4 = argument for updating JObj delta in draw iteration
    # r6 = recorded action state frame (stored in root JObj padding)
    # r7 = root JObj
    # r8 = player data
    
    lwz r5, 0x894(r8)
    stw r5, 0x9C(r7)
    # update recorded action state frame with new data from player entity
    
    cmpw r6, r5
    bne+ _draw_setup
    # if action frame is different from record, then update XYZ pos for delta calc
    # delta calc is true or false, and simply determines whether to draw a line or a dot
    # dots will slowly disappear with a fading alpha
    
      lwz r5, 0x10(r8)
      cmpwi r5, 0xB
      beq- _draw_setup
      # if character is "sleeping" we'll force the update so the skeleton dots disappear
    
        li r4, 0
        # else, we leave the records alone for things like hitlag, or slowmo frames
    
    _draw_setup:
    addi r4, r4, 0x1000
    
    lwz r3, 0x50(r3)
    # r3 = load 5th bone in skeleton (to skip drawing TopN, TransN, XRotN, YRotN)
    
    li r5, -256
    # r27 = RGBA color; defaults to white
    # FF FF FF 00 (alpha gets ORed in)
    
    lbz r7, 0xC(r8)
    cmpwi r7, 4
    bge- _draw
    # if player slot is not in range of controller ports, just use white color
    # else, generate red, blue, yellow, or green according to player slot
    
    li r6, 0b010011100001
    
    # 3-bit BGR color array, indexed according to player colors
    
    mulli r8, r7, 3
    srw r6, r6, r8
    # r6 = unmasked color
    
    rlwinm r9, r6, 24, 7, 7    # 01 00 00 00  <-  01
    rlwimi r9, r6, 15, 15, 15  # 00 01 00 00  <-  02
    rlwimi r9, r6, 6, 23, 23   # 00 00 01 00  <-  04
    mulli  r5, r9, 0xFF       #  R  G  B 00  *=  FF
    # 8-bit channels, RGBA
    
    lis r8, 0x8045
    ori r8, r8, 0x3130
    mulli r0, r7, 0xE90
    lwzx r6, r8, r0
    cmpw r6, r31
    beq+ _draw
    # if player doesn't match player slot, then character is a sub-player like nana
    # we'll darken the color for this case
    
    _sub_player:
    slwi r0, r9, 6
    xor r5, r5, r0
    # mix 25% black to color
    
    lis r0, 0x3f3f
    ori r0, r0, 0x3f00
    xor r5, r5, r0
    # mix ~25% mid-gray to color
    
    _draw:
    # r3 = bone to start on
    # r4 = condition for updating delta records
    # r5 = color without a blank alpha channel
    bl <trace_JObj_skeleton>
    
    _iterate:
    lwz r31, 0x10(r31)
    b _for_each_player
    
    _return:
    bl 0x80368608
    .long 0
    
    
    
    
    <trace_JObj_skeleton> All
    # r3 = THIS JObj
    # r4 = params
    # 0x00000001 = bool for updating delta record
    # 0x0000FF00 = line width base
    # r5 = color with no alpha channel
    _recursion:
    mflr r0
    stw  r0, 0x4(sp)
    stwu sp, -0x30(sp)
    stmw r28, 0x8(sp)
    mr   r31, r3
    mr   r30, r4
    mr   r28, r5
    
    lwz r3, 0x10(r31)
    cmpwi r3, 0
    bltl _recursion
    # if  child exists, recursively call self
    
    mr  r4, r30
    mr  r5, r28
    lwz r3, 0x8(r31)
    cmpwi r3, 0
    bltl _recursion
    # if sibling exists, recursively call self
    
    # done with recursion
    # before each stack frame collapses, the involved joints are drawn
    
    addi r10, r31, 0x94
    lswi r6, r10, 0xC
    lbz r9, 0x93(r31)
    lwz r3, 0x50(r31)
    lwz r4, 0x60(r31)
    lwz r5, 0x70(r31)
    # r3...r5 = XYZ absolute mtx transposition
    # r6...r7 = XYZ records from previous check
    
    _update:
    andi. r0, r30, 1
    beq- _draw
    # if saved r4 argument is false, then don't update the records in JObj padding
    
      stswi r3, r10, 0xC
      # else, update XYZ positions for next delta check
      # memory in registers are used for this frame's check
    
    _draw:
    or r28, r9, r28
    # color gets an alpha value that reflects the time it has not been moving.
    # a byte is stored alongside each record of XYZ to be ORed into given color.
    
    rlwinm r5, r30, 8, 8, 15
    li r4, 5
    # line primitive params
    
    cmpw cr6, r3, r6   # X vs X
    cmpw cr7, r4, r7   # Y vs Y
    crand cr6, cr7, cr6
    cmpw cr7, r5, r8   # Z vs Z
    crand cr6, cr7, cr6
    cmpwi cr7, r9, 0xFF # has the alpha been dropping?
    mfcr r29
    
    # cr0 = comparison of r4 argument
    # cr6 = comparison of XYZ delta
    # cr7 = comparison of alpha to (almost) full
    # r29 holds saved comparisons
    beq cr0, _end_update
    
      beq cr6, _alpha_decr
    
        _alpha_incr:
        addi r11, r9, 0x40  # 4 frames to go from 0% -> 100%
        cmpwi r11, 0xFF
        ble- _alpha_store
    
          _cap_FF:
          li r11, 0xFF
          b _alpha_store
    
        _alpha_decr:
        addi r11, r9, -2    # 128 frames to go from 100% -> 0%
        cmpwi r11, 0
        bge- _alpha_store
    
          _cap_00:
          li r11, 0
    
        _alpha_store:
        stb r11, 0x93(r31)
    _end_update:
    
    li r3, 2
    beq+ cr7, _setup
    # if alpha is almost full, draw line regardless of delta check result
    # we want to only resort to drawing disappearing dots for bones that are holding still
    # this small frame buffer helps prevent an erroneous trigger of dots on hitlag
    
      bne+ cr6, _setup
      # else, if alpha is not full, we check the delta comparison
    
      # if XYZ delta == 0; then draw dot
      # if XYZ delta != 0; then draw line
    
        _dot_setup:
        li  r3, 1
        rlwinm r5, r30, 8, 8, 15
        addis r5, r5, 0x18
        li r4, 7
        # dot primitives params take place of line primitives
        # dot radius is half a native pixel larger than input width
    
    _setup:
    or r4, r5, r4
    ori r4, r4, 0x0300  # default primitive settings, but ignores the Z buffer
    # with this setting, lines will be drawn on top of anything they come after in the draw order
    
    li r5, 0x1455 # default primitive settings for params string 2
    bl <prim_main_parametric>
    
    # r3 now = GX FIFO hardware address
    # all information stored to this address goes into the drawing pipe
    # all information is stored to the same address 0(r3)
    
    _draw_child:
    lfs f0, 0x50(r31)
    stfs f0, 0(r3)
    lfs f0, 0x60(r31)
    stfs f0, 0(r3)
    lfs f0, 0x70(r31)
    stfs f0, 0(r3)
    stw r28, 0(r3)
    # vertex stored
    
    mtcr r29
    beq- cr7, _draw_parent
    beq- cr6, _end_draw
    # check saved delta; if 0, then draw only a point at the child vertex
    # else, draw a line between parent <- child
    
    _draw_parent:
    lwz r31, 0xC(r31)
    # load JObj parent
    
    lfs f0, 0x50(r31)
    stfs f0, 0(r3)
    lfs f0, 0x60(r31)
    stfs f0, 0(r3)
    lfs f0, 0x70(r31)
    stfs f0, 0(r3)
    stw r28, 0(r3)
    # second vertex stored
    
    _end_draw:
    bl <prim_close>
    
    _return:
    lmw r28, 0x8(sp)
    addi sp, sp, 0x30
    lwz  r0, 0x4(sp)
    mtlr r0
    blr
    

    This last code (for now) is just a fancy version of the previous demonstration. It uses some padding in JObj structures to store X Y and Z translation values of the last measurement, so it can create a delta check each time it draws a frame.



    This translation delta is checked against 0 to determine whether to draw a line OR a point, but not both. When a joint’s position in space is different from the last frame, it will be drawn as a line colored by the player slot. Sub-character players are drawn with a hue mixed with black and gray to make a desaturated color. For player slots that are higher than the controller port maximum are drawn with a base hue of white.

    When a joint stops translating for more than a frame, its drawing turns into a point, and its alpha slowly decreases over ~ 2 seconds worth of action frame data.

    Since it only records translation coords for the delta check, it isn’t perfect -- but this cuts away most of the unanimated joints from the display, causing a visualization that (for the most part) only illustrates actively animated joints.

    This code uses calls to <prim_main_parametric>, which may be used to specify all of the available drawing parameters as immediates. r4 and r5 contain 8 bytes (6, really) that describe the following parameters:

    Code:
    #    r4        r5
    # C0000000  00000000 = Cull Mode
    #  +8 = cull backface
    #  +4 = cull frontface
    
    # 03FF0000  00000000 = Line Width/Point Size
    #   1/16 pixel increments; 0x2A8 maximum width
    
    # 00002000  00000000 = Compare Location
    #  +1 = z buffer compares before texturing
    
    # 00001000  00000000 = Z Buffer Compare
    #  +1 = z buffer enables compare
    
    # 00000800  00000000 = Z Buffer Update
    #  +1 = z buffer enables update
    
    # 00000700  00000000 = Z Buffer Comparison Logic
    #  +4 = greater than
    #  +2 = equal to
    #  +1 = less than
    
    # 000000FF  00000000 = Primitive Type
    #   0 = quads
    #   1 = --
    #   2 = triangles
    #   3 = trianglestrip
    #   4 = trianglefan
    #   5 = lines
    #   6 = linestrip
    #   7 = points
    
    # 00000000  00003000 = Blend Type
    #   0 = None  -- write input directly to EFB
    #   1 = Blend -- blend using blending equation
    #   2 = Logic -- blend using bitwise operation
    #   3 = Subtract -- input subtracts from existing pixel
    
    # 00000000  00000700 = Blend Source
    #   0 = zero -- 0.0
    #   1 = one  -- 1.0
    #   2 = source color
    #   3 = inverted source color
    #   4 = source alpha
    #   5 = inverted source alpha
    #   6 = destination alpha
    #   7 = inverted destination alpha
    
    # 00000000  00000070 = Blend Dest
    #   (see above)
    
    # 00000000  0000000F = Blend Logic
    #   0 -- CLEAR;   dst = 0
    #   1 -- AND;     dst = src & dst
    #   2 -- REVAND;  dst = src & ~dst
    #   3 -- COPY;    dst = src
    #   4 -- INVAND;  dst = ~src & dst
    #   5 -- NOOP;    dst = dst
    #   6 -- XOR;     dst = src ^ dst
    #   7 -- OR;      dst = src | dst
    #   8 -- NOR;     dst = ~(src | dst)
    #   9 -- EQUIV;   dst = ~(src ^ dst)
    #   A -- INV;     dst = ~dst
    #   B -- REVOR;   dst = src | ~dst
    #   C -- INVCOPY; dst = ~src
    #   D -- INVOR;   dst = ~src | dst
    #   E -- NAND;    dst = ~(src & dst)
    #   F -- SET;     dst = 1
    
    ---

    I’ll have more examples soon. In the meantime, feel free to make your own.
    @UnclePunch @tauKhan @rmn
     
    #2 Punkline, Apr 15, 2018
    Last edited: Apr 16, 2018 at 8:51 PM
  3. tauKhan

    tauKhan
    Expand Collapse
    Smash Lord

    Joined:
    Feb 9, 2014
    Messages:
    1,326
    Oh wow, this is just the library I wanted :) . Amazing stuff, will definitively put this into use.

    Also almost did a code similar to ECB Anchor earlier. Sidenote: I'd remove the first ECB node (0x7F8) from the anchor draw. It just points to TopN and isn't used for determining ECB vertices. In fact it looks like it's probably not read anywhere. Having the TopN in the anchor also distracts from the fact that some characters have TopN as another node, actually used for calculation.
     
    Punkline likes this.
  4. DRGN

    DRGN
    Expand Collapse
    Technowizard

    • Moderator
    • Premium
    Joined:
    Aug 20, 2005
    Messages:
    1,984
    Location:
    Sacramento County (916), CA
    Very cool!

    This could be really useful when working on creating new animations.
     
    Punkline likes this.

Share This Page

Users Viewing Thread (Users: 0, Guests: 0)

We know you don't like ads
Why not buy Premium?