Sword Trail Experiments

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
379
#1
I suppose this thread is a continuation of the Costume Dependant Sword Trail Colors thread. I thought about replying in there, but this goes off topic a bit.

I have so many open projects right now that most are neglected. I was working on something a while back though that was an exploration of the ways that sword trails can be manipulated with code. I went over some of the details in this post, but I didn’t include a mod because I was (and still am) unsure of what I wanted to implement (and how.)

I figure this new thread can serve as a more general purpose thread for discussing the nature of sword trails, and perhaps other joint “articles” or use of GX FIFO for primitive drawing.

I’ll be sharing code here, but it’s for the purpose of explanation and discussion. Feel free to contribute with your own codes/words.

Edits - cleaned up some stuff

---

Here’s the topmost fossil from the record that was my experiment:

Code:
-==-

Custom Sword Trails v0.70
Define custom sword trails for any character.
Use the C4 subaction event to control trail toggles.
( Edit ) CSWT_getCustomTrailData for customization.
- follow the format detailed by the commented examples in order to add any number of custom sword trails to the game.
[Punkline]
Version -- DOL Offset ------ Hex to Replace ---------- ASM Code
1.02 ----- 0x800C3098 ---- 807F0004 -> Branch
# Mod Overview: CSWT_070

# 5 hooks @ 2 game functions, responsible for updating a sword trail.
# -- One for bone origin -> article spawn defines position/rotation/vertices
# -- The other for drawing the primitive accross the 3 last keyframes, stored in the player entity.

# The included data tables in this code uses a blrl to make its location arbitrary
# - This allows the data to be easily compiled into available MCM code-space; making it reusable/replacable
# - Without known static locations, the data can be called using the MCM special branch syntax: bl <get___>; mflr r3

# The data tables are used between several hooks in each function, and handled by functions written with MCM to create a reusable library.

# The override data table is used like a volatile, stack-independent buffer; populated with fake parameters before each sword trail update
# - Injections intercept the sword trail data from their functions' contexts, and use standalone functions to determine if they should be overridden
# -- If character/costume identiy matches a user definition, or another code using the provided library functions indicate there should be an override;
# -- then the real structures are overridden with pointers to fake data as they are passed through the update logic.

# The buffer consists of 3 data partitions:

# - 1st part is used to indicate whether or not data is available for overriding the next sword trail draw.
# These alone decide the logic of the override, so I often refer to them as "indicators" in my comments
# - 2 pointers in 0x0 and 0x4 tell the override function where to grab formatted sword trail information from before an update, in place of what it
# -- 0x0 - pointer is to fake/real sword trail parameters struct (like Marth's entity data--written to by trail color codes in the past)
# -- 0x4 - pointer is to XYZ position/rotation float coords that adjust each new swordtrail keyframe.
# -- 0x8 - pointer is to a specified bone instance, used to keep track of the ID specified in Bt.
# - 0 as a null vallue for any of these will cause the game to take default actions.
# - Bone adjustments are optional, but a trail parameters pointer is mandatory in order for an override to occur.

# - The next 2 partitions are what the code functions use in order to store data (to be pointed to by the override indicators.)
# -- 0x0C -- 0x28 - mimics a real sword trail struct, like those contained at the end of sword-user article data (and beam swords.)
# -- 0x2C -- 0x44 - mimics 1/3 of a the structure at internal player.0x20B0 (includes pos/rot global coords of 3 trail keyframes)
# - The indicators from part 1 are what determine the override, but these values are still volatile.

# - the indicators in part 1 are wiped to 0 as soon as they are used to override the sword trail update routines.
# -- the 2 update functions will update each player asyncronously from one another, so this wipe happens twice per frame per player.
# --- because of this, 2 hooks per function are responsible for initializing/populating/terminating the override indicators/data
# - a 3rd hook in the keyframe/bone update applies post-update adjustments to the coordinate information in the player entity.

# ---

# INJ: (BONE) OPEN REGISTER
# @ 800C3098 : lwz r3, 0x4(r31)
# r31 = internal player data pointer
# r5 = (future) sword trail struct pointer
# r29 is safe to use
# - This gets called just as the update recognizes the player is going to use a sword trail bone update
# -- Code uses this to 0 out an important register so that a later hook can recognize null pointers in place of a struct
# --- it also decides whether or not to populate the override pointers with user defined default trail parameters
lbz r3, 0x2101(r31)
andi. r3, r3, 1         # check bit 0x01 for beam sword trail condition
bne- return             # don't use custom trails if the beam sword is active

# else, attempt to feed override struct any custom data
bl <CSWT_getTrailOverride> # <- not actually a function; just returns pointer in LR
mflr r29                # r29 = override struct

lwz r3, 0(r29)          # sword trail parameters override indicator; gets wiped to 0 each trail update
andis. r3, r3, 0x8000   # check if there's already a pointer in the sword trail override indicator
bne- return             # if there is, then use that instead of generating one from user definitions (library support)

mr r3, r31
bl <CSWT_returnCustomTrail> # else, see if the user has defined a trail that matches current character/costume
stw r3, 0(r29)          # r3 returns pointer to fake sword trail parameters buffer
stw r4, 4(r29)          # r4 returns pointer to start of bone adjustment floats in buffer; cast from XtYtZt and XrYrZr
stw r5, 8(r29)          # r5 returns pointer to bone instance specified by bone ID in "Bt" byte parse

return:
li r5, 0                # 0 out register to make arbitrary data recognizable as "null"
# without this hook, the register will go unhandled and attempt to read arbitrary data
# this is because the game has hardcoded handles for sword users and beamswords; but not non-sword users without a beamsword

lwz r3, 0x4(r31)
.long 0x00000000

----------- 0x800c30d4 ---- C0050018 -> Branch

# INJ: (BONE) TRAIL UPDATE
# @ 800c30d4 : lfs f0, 0x0018 (r5)       (setting up call that handles bone ID)
# r31 = internal player data pointer
# r5 = sword trail struct pointer
# - This gets called just as the game thinks it knows what sword trail it's going to use for the update.
# -- The code uses this as an opportunity to override the sword trail parameters that get used.
bl <CSWT_getTrailOverride># <- not actually a function; just returns pointer in LR
mflr r9                 # pointer to sword trail override indicators/flags
lwz r10, 0(r9)          # r10 = trail parameters override pointer

andis. r0, r10, 0x8000  # check if sword trail override indicator is a pointer
beq+ checkRegister

mr r5, r10              # if override is valid, it becomes our new sword trail struct

checkRegister:
andis. r0, r5, 0x8000   # check if value is a pointer
bne+ default
abort:                  # if no pointer is in r5, then it isn't safe to run the rest of the function
b 0x800c316c            # so treat it as though the player weren't even flagged to render a trail (return from host function)

default:
lfs f0, 0x0018 (r5)
.long 0x00000000

----------- 0x800c3128 ---- 887F2101 -> Branch

# INJ: (BONE) ADJUST GLOBAL POSITION/ROTATION
# @ 800c3128 : lbz r3, 0x2101 (r31)
# r30 = internal player data offset 0x20B0 + (keyframeID*0x18)
# - This gets called just after the coordinates for the trail have all been stored
# -- The code uses this as an opportunity to adjust these coordinates each update

lbz r3, 0x2101 (r31)
andi. r0, r3, 1         # check for beamsword flag
bne- return             # if beamsword flag is on, then our previous hooks didn't set up the override struct

mr r3, r31              # else, we apply the bone adjustments pointed to in 0x4 of the override struct
mr r4, r30
bl <CSWT_applyBoneAdjustments>

return:
lbz r3, 0x2101 (r31)
.long 0x00000000


----------- 0x800c2674 ---- 38600001 -> Branch

# INJ: (RENDER) OPEN REGISTER
# @ 800c2674 : li r3, 1
# r28 = internal player data pointer
# r30 = (future) sword trail struct pointer
# - This gets called just as the update recognizes the player is flagged for a sword trail render
# -- The code uses this as an opportunity to update the override indicator value with any user-defined trails
# --- It also 0s out an important register so that a later hook can recognize null pointers in place of a struct
bl <CSWT_getTrailOverride> # <- not actually a function; just returns pointer in LR
mflr r30                #r30 now holds a pointer to the custom (temporary) sword trail override structure
lwz r3, 0(r30)
andis. r0, r3, 0x8000   # check if there's a pointer in the sword trail override indicator
bne- zeroOut            # if so, we leave the override alone (so that other codes may use it too)

lbz r3, 0x2101(r31)     # we also want to leave it alone if there's an active beam sword
andi. r3, r3, 1         # check bit 0x01 for beam sword trail condition
bne- zeroOut            # don't use custom trails if the beam sword is active

mr r3, r31              # else, we check to see if character/costume ID matches a user-defined custom trail
bl <CSWT_returnCustomTrail>
stw r3, 0(r30)          # the function returns either 0 or a pointer, and the result is stored in the override

zeroOut:
li r30, 0               # 0 out register to make arbitrary data recognizable as "null" later on in r30

return:
li r3, 1
.long 0x00000000

----------- 0x800c27c8 ---- 887C2101 -> Branch

# INJ: (RENDER) TRAIL UPDATE
# @ 800c27c8 : lbz r3, 0x2101 (r28) (int becomes right shifted >> 1)
# r28 = internal player data pointer
# r30 = sword trail struct pointer
# - This gets called just as the game thinks it knows what sword trail it's going to use for the update.
# -- The code uses this as an opportunity to override what sword trail parameters get used.
bl <CSWT_getTrailOverride> # <- not actually a function; just returns pointer in LR
mflr r3
lwz r4, 0(r3)           # load only the trail parameters override pointer; bones have already updated
li r0, 0
stw r0, 0(r3)           # wipe override indicators
stw r0, 4(r3)
stw r0, 8(r3)

andis. r0, r4, 0x8000   # check if override value is a pointer
beq+ checkRegister
mr r30, r4              # if it is, overwrite the sword trail struct pointer

checkRegister:
andis. r0, r30, 0x8000  # check if final register value is a pointer
bne+ default
abort:
b 0x800C2f7c            # if not, then abort the update as though the player weren't flagged for a sword trail

default:
lbz r3, 0x2101 (r28)
.long 0x00000000



<CSWT_getCustomTrailData> ALL
4E800021 # this "blrl" line allows the following data to be called like a function
# Sword trails are defined between this line and the ending "FFFFFFFF" line.
# You may create as many character/costume combinations as you wish.
# Use these with non-sword characters via subaction event C4 in Crazy Hand.
# MCM will ignore whitespace, so you can format these according to preference.
# Ch Cs XtYtZt XrYrZr ffBoBt AA Vrt1 Vrt2 R1G1B1 E1 R2G2B2 E2

# Mario Test (0 - NR)
00      # Ch     - Character ID (Mario == 00)
00      # Cs     - Costume ID (costume 0 == normal)
000000  # XtYtZt - Translate XYZ from bone
000000  # XrYrZr - Rotate XYZ from bone
00      # ff     - flags, see below for more info
21      # Bo     - Bo Bone ID Origin (used to specify origin rotation/scale)
21      # Bt     - Bt Bone ID Translation (used to specify origin position)
F0      # AA     - Alpha Gradient - ex: FF = 100%->100% -- F0 = 100%->0%
01C0    # Vrt1   - Vertex 1 (base)
09A1    # Vrt2   - Vertex 2 (tip)
FF7788  # R1G1B1 - Gradient Color 1 (Base, hexcolor)
0C      # E1     - End taper 1 (% of defined vertices)
FFAA22  # R2G2B2 - Gradient Color 2 (Tip, hexcolor)
40      # E2     - End taper 2 (% of defined vertices)

# experimental ff flag options:
# 0xF0 = translation flags (position offset)
#  -- 0x70 = use X, Y, Z translation origin from TopN instead of Bt bone
#  -- 0x80 = use Bo instead of TopN values for above flags
# 0x0F = rotation flags (rotation offset)
#  -- 0x70 = use X, Y, Z rotation origin from TopN instead of Bo bone
#  -- 0x80 = use "0.0" instead of TopN values for above flags


# Ch Cs XtYtZt XrYrZr
# ffBoBt AA Vrt1 Vrt2
# R1G1B1 E1 R2G2B2 E2

#Marth (0 - CS NR)   "Cherry Tipper"
12 00 000000 000000 # red streak from tip
004B4B F0 0800 0A00
C00000 30 FF4040 40

#Marth (1 - CS Red)  "Shield Breaker"
12 01 000000 000000 # shield breaker colors with a fiery edge
004B4B F0 01C0 0B00
FF40FF E8 0040FF 58

#Marth (2 - CS Green) "Pinwheel"
12 02 202000 000000 # extends trail past hilt to create double edge
804B0B FF F380 0A00 # uses 2nd bone ID 0B to translate position
DC5E0A 00 FFC600 40

#Roy (0 - CS NR)  "roy example"
1A 00 000000 000000
004D4D FF 029A 0B33
C62902 00 FBF481 40

#Ylink (0 - CS NR) "ylink example"
14 00 000000 000000
001B1B F0 0197 0897
73c478 26 daf4c7 33

#Link (0 - CS NR) "link example"
06 00 000000 000000
001919 F0 0197 0897
FF0000 26 FF5010 33

# (end of trail definitions)
FFFFFFFF # This termination line is important



<CSWT_returnCustomTrail> ALL
# CSWT_returnCustomTrail
# r3 = internal player data pointer
# returns 0 or a pointer to a newly constructed sword trail input if data matches player entity
# r3 = return parsed trail parameters
# r4 = return parsed bone adjustment floats for XYZpos/XYZrot
# r5 = return parsed flags and bones
# uses the space provided in the override struct
mflr r0
stw r0, 0x4(sp)
stwu sp, -0x80(sp)
stmw r6, 0x8(sp)        # *shrug* just to make it easier to implement from contexts that are using volatile registers
mr r31, r3              # and this, I guess

bl <CSWT_getCustomTrailData> # <- not actually a function; just returns pointer in LR
mflr r4                 # r4 = start of custom sword trail data table
bl <CSWT_findCustomTrail_byCharacterCostume>
cmplwi r3, 0
li r4, 0
beq- return
bl <CSWT_getTrailOverride> # <- just returns pointer in lr
mflr r30                # r30 = saved sword trail override struct (0x0 override indicators/flags, 0xC vol trail parameters, 0x2C vol bone adjustments)
addi r4, r30, 0xC       # r4 = start of volatile entity trail parameters
mr r5, r31              # r5 = internal player data pointer
bl <CSWT_parseDataBuffer> # interprets parse data and writes to buffer pointer passed in r4
#returned r3 = start of fake sword trail parameters, from volatile buffer
#returned r4 = start of fake sword trail bone adjustments, from volatile buffer
#returned r5 = parsed flags and bone IDs

return:                 # returns r3 and r4
lmw r6, 0x8(sp)
addi sp, sp, 0x80
lwz r0, 0x4(sp)
mtlr r0
blr



<CSWT_findCustomTrail_byCharacterCostume> ALL
# searchCustomTrailMatch
# r3 = internal player data pointer
# r4 = start of custom sword trail data parse array
# - returns 0 or pointer in r3 if player entity Character ID and Costume ID can be matched on table
# -- returned pointer is to a 0x18 block of custom sword trail data that can be parsed by "writeCustomTrailData"
# --- !! 0xFF for character ID terminates loop. Without a termination line, infinity will eat you.
subi r4, r4, 0x18       # set up r4 for lwzu in loop
matchCustomTrailDataLoop:
lwz r5, 0x4(r3)         # r5 = player character ID
lbzu r0, 0x18(r4)       # r0 = parsed character ID
cmplwi r0, 0xFF         # !!
beq terminateLoop       # !! 0xFF for character ID is termination code
cmplw r0, r5
bne+ matchCustomTrailDataLoop
lbz r5, 0x619(r3)       # r5 = player costume ID
lbz r0, 0x1(r4)         # r0 = parsed costume ID
cmplw r0, r5
bne+ matchCustomTrailDataLoop
mr r3, r4
b return
terminateLoop:
li r3, 0
return:
blr



<CSWT_parseDataBuffer> ALL
# CSWT_parseDataBuffer
# r3 = 0x18 bytes of parse data (-- -- XtYtZt XrYrZr AsAe BoBt Vrt1Vrt2 R1G1B1 E1 R2G2B2 E2)
# r4 = 0x38 bytes of space for storing ouput (buffer)
# r5 = internal player data pointer
# - parses 0x16 bytes of data from r3 and populates buffer in r4 with 0x38 bytes of formated data
# - r3 returns pointer to fake sword trail parameters in buffer
# - r4 returns pointer to array of 6 floats; bone adjustments to XYZ position, rotation
# - r5 returns parsed bones and flags
mflr r0
stw r0, 0x4(sp)
stwu sp, -0x30(sp)
stmw r22, 0x8(sp)

mr r29, r3              # r29 = location of parsable data
mr r30, r4              # r30 = location to dump output
mr r31, r5              # r31 = internal player data pointer

# data we're parsing looks like this:
# 0    2   4  5      8    A
# ---- XtYtZt XrYrZr ffBoBt AA
# Vrt1Vrt2 R1G1B1 E1 R2G2B2 E2
# C        10     13 14     17

lbz r3, 0x9(r29)        # load parsed 8-bit Bo boneID (origin for rotation and scale)
stw r3, 0x14(r30)       # store 32-bit boneID in sword trail struct data

lbz r5, 0xB(r29)        # SE are a pair of 4-bit nibbles describing Start and End Alpha %
rlwinm r4, r5, 0, 28, 31
rlwinm r3, r5, 28, 28, 31
rlwimi r3, r3, 4, 24, 27        # r3 = 0xSS
rlwimi r4, r4, 4, 24, 27        # r4 = 0xEE
stb r3, 0x8(r30)
stb r4, 0x9(r30)                #store formated Alpha bytes in sword trail parameters

addi r3, r30, 0xA       # r3 = location of RRGGBB--RRGGBB start in sword trail struct
addi r4, r29, 0x10      # r4 = location of RRGGBBE1RRGGBB start in parse data
li r5, 7                # copy 7 bytes
bl 0x800031F4           # SYS_memcpy (this does not require porting between Melee versions)
li r3, 0
stb r3, 0xD(r30)        # 0 out the included E1 byte, because it's not actually used by the game

# at this point, all unsigned bytes have been copied
# now we need to shift the signed bytes and convert them into floats using the melemeter coefficient

#  the writeArray utility used here has a method of calling that can summarize r3 and r4 within the UIMM range of r5
# r3 == r5 0x-FF- = number of SINTs in array
# r4 == r5 0xF--- = amount to signed left shift each SINT by
# r5 == r5 0x---3 = 0-based byte count of each SINT (max 3 = 32-bit SINT)

# this is simply designed to make sequential calls like this easier to make with fewer lines

#Vert1/2 - vertices
                        # r4 = 0 - no shift, use 0xNN.NN
                        # r3 = 02 - hwords Vrt1 Vrt2
li r5, 0x0021           # r5 = 1 - 2 bytes each SINT (0-based)
addi r6, r29, 0xC       # start hwords to parse
addi r7, r30, 0x18      # start of floats to dump
bl <util_slawSINTtoMeleeMeters_writeArray>

#E1 taper vertex
                        # r4 = 2 - shift byte left 2 times (creates -200% -- +200% range for taper)
                        # r3 = 01 -  byte E1
li r5, 0x2010           # r5 = 0 - (0-based byte count) (the rest of these are 1-byte as well)
addi r6, r29, 0x13      # start of bytes array in parse data
addi r7, r30, 0x0       # start of float array in sword trail struct
bl <util_slawSINTtoMeleeMeters_writeArray>

#E2 taper vertex        -- use previous r3-r5
addi r6, r29, 0x17      # start of bytes array in parse data
addi r7, r30, 0x4       # start of float array in sword trail struct
bl <util_slawSINTtoMeleeMeters_writeArray>

#XtYtZt - bone translations
                        # r4 = 5 - shift left 5 times
                        # r3 = 03 - 3 bytes Xt Yt Zt
li r5, 0x5030           # r5 = 0
addi r6, r29, 0x02      # start of bytes array in parse data
addi r7, r30, 0x20      # start of float array in bone adjustments
bl <util_slawSINTtoMeleeMeters_writeArray>

#XrYrZr - bone rotations
li r4, 1                # use previous r3 and r5, but change r4 left shift to 1
addi r6, r29, 0x05      # start of bytes array in parse data
addi r7, r30, 0x2C      # start of float array in bone adjustments
bl <util_slawSINTtoMeleeMeters_writeArray>

# we've created 10 floats from only 12 bytes of input data.
# now we just make sure to pass back all of the related info

mr r3, r30              # return r3 = pointer to fresh trail parameters
addi r4, r30, 0x20      # return r4 = pointer to fresh bone adjustments
lwz r5, 8(r29)
srwi r5, r5, 8          # return r5 = --ffBoBt

return:
lmw r22, 0x8(sp)
addi sp, sp, 0x30
lwz r0, 0x4(sp)
mtlr r0
blr



<CSWT_applyBoneAdjustments> ALL
# applyBoneAdjustments
# r3 = internal player data pointer
# r4 = location to dump bone adjustments
# -- flag logic is a bit involved, so I moved this to a function for portability
# this will attempt to apply a pending update to bone adjustments at the end of the keyframe update
# it voids all the override indicators and applies flag logic according to a previous parse from CSWT_parseDataBuffer
mflr r0
stw r0, 0x4(sp)
stwu sp, -0x80(sp)
stmw r3, 0x8(sp)

mr r31, r3              # r31 = internal player data pointer
mr r30, r4              # r30 = assumed to be current trail keyframe location

bl <CSWT_getTrailOverride> # <- not actually a function; just returns pointer in LR
mflr r5
lwz r4, 0x4(r5)         # r4 = bone adjustments pointer
lwz r12, 0x8(r5)        # (saved 0x--ffBoBt : 3 bytes from parse)
li r0, 0
stw r0, 0(r5)
stw r0, 4(r5)
stw r0, 8(r5)         # 0 out volatile indicators so the next updates have a null base
andis. r0, r4, 0x8000
beq- return             # if no bone adjustments indicator, then just return

# else, apply adjustments
lwz r9, 0x5E8(r31)              # r9 = Bone Lookup table, used to find bone instances in a player
rlwinm r10, r12, 4, 20, 27      # r10 = parsed Bt << 4 (bone ID for Bone Origin -- Translation)
rlwinm r11, r12, 28, 20, 27     # r11 = parsed Bo << 4 (bone ID for Bone Origin -- rot/scale)
rlwinm r12, r12, 16, 24, 31     # r12 = parsed flags   (options for translation and rotation)
lfs f2, 0x2C(r31)               # f2 = facing direction
li r5, 6                # for each of 6 floats

# the following loop adds offsets to origin XYZ translation/rotation floats
# the origin XYZ values that the offsets apply to are logically selected by the flags in r12
# 2 bone IDs and a set of pair of flag nibbles are included in the parse:
# Rotation/scale uses Bo (bone 1) and translation uses Bt (bone 2) by default
# the flags however may specify that:
# - rotations can instead use TopN or static 0.0 origin values
# - translations can instead use TopN, or Bo origin values

writeBoneAdjustments:           # loop start
subi r5, r5, 1                  # r5 -= 1 decrement loop count
cmpwi r5, 2                     # check if this iteration is for translation or rotation
ble checkPos                    # positions and rotations are handled differently

checkRot: # if r5 > 2, then this is a rotation adjustment
subi r6, r5, 3                  # XYZ ID ( 0 = X, 1 = Y, 2 = Z)
li r7, 0x44                     # global rotation starts at 0x44 in bone instance
rlwinm r0, r12, 29, 0, 2        # masked out XYZ rotation flags, shifted for RLWNM
rlwnm. r0, r0, r6, 0, 0         # flag pertaining to THIS X Y or Z rotation
mr r8, r11                      # r8 = Bo by default (bone origin ID)
beq+ finalizeOrigin             # if no flag option, then finalize with default Bo
li r8, 0                        # else, r8 = 0 (TopN offset)
andi. r0, r12, 0x0008           # check rotation "use 0.0" flag
beq+ finalizeOrigin
lfs f1, -0x778C(rtoc)           # if true, load static origin value of 0.0 instead of using a bone
b addOffset

checkPos: # if r5 <= 2, then this is a position adjustment ; also r5 already == XYZ ID
mr r6, r5                       # we want r6 to commonly keep an XYZ ID for a following step
li r7, 0x50                     # global position starts at 0x50 in bone instance
rlwinm r0, r12, 25, 0, 2        # masked out XYZ Translation flags, shifted for RLWNM
rlwnm. r0, r0, r5, 0, 0         # flag pertaining to THIS X Y or Z translation
mr r8, r10                      # r8 = Bt by default (bone translation ID)
beq+ finalizeOrigin             # if no flag option, then finalize with default Bt
li r8, 0                        # else, r8 = 0 (TopN offset)
andi. r0, r12, 0x0080           # check translation "use Bo" flag
beq+ finalizeOrigin
mr r8, r11                      # if true, don't use Bt origin at all

finalizeOrigin:
slwi r6, r6, 4          # r6 still holds XYZ ID, so multiply that by 0x10 to align to global MTX values
lwzx r8, r8, r9         # r8 now = final specificaiton of bone instance (BA)
add r7, r7, r6          # r7 now = finalized offset from specified bone instance (displacement)
lfsx f1, r7, r8         # f1 now = finalized origin float

addOffset:              # f1 = finalized origin float
slwi. r6, r5, 2         # r6 = word alignment, also checks for 0 (final loop increment, XPos)
lfsx f0, r4, r6         # f0 = bone adjustment float
bne+ notXPos
fmuls f0, f2, f0        # if this is Xpos, multiply offset by facing direction
notXPos:
fadds f1, f1, f0        # add that mofo
stfsx f1, r6, r30       # store finalized global coordinate in current keyframe slot
bne+ writeBoneAdjustments # continue loop until all floats have been handled

return:                 # all writes are done to the passed r4 pointer, so nothing needs to be returned
lmw r3, 0x8(sp)
addi sp, sp, 0x80
lwz r0, 0x4(sp)
mtlr r0
blr



<util_slawSINTtoMeleeMeters> ALL
# util_slawSINTtoMeleeMeters
# r3 = signed int; convert to float; * melee meter coef
# r4 = signed shift r3 left amount (0 = 0xNNNNNN.NN, 4 = 0xNNNNNNN.N, 8 = 0xNNNNNNNN, 12 = 0xNNNNNNN0, etc)
# -- r4 is returned without being modified
# f1 = returned float value
andis. r0, r3, 0x8000 # save signed bit
slw r3, r3, r4
or r3, r3, r0           # as far as I know, there isn't a slaw instruction...

lis r0, 0x4330
xoris r3, r3, 0x8000
stw r0, -0xC(sp)
stw r3, -0x8(sp)        # store double in redzone
lfd f0, -0xC(sp)        # immediately load from redzone (now it's garbage)
lfd f2, -0x7730(rtoc)   # 43300000 80000000 (for casting)
lfs f3, -0x7740(rtoc)   # Melee Meters coef (useful for more than melee meters!)
fsubs f0, f0, f2        # cast
fmuls f1, f3, f0        # conversion returned in f1
blr



<util_slawSINTtoMeleeMeters_writeArray> ALL
# util_slawSINTtoMeleeMeters_writeArray
# r3 = number of SINTs to convert
# r4 = signed shift left amount (0 = 0xNNNNNN.NN, 4 = 0xNNNNNNN.N, 8 = 0xNNNNNNNN, 12 = 0xNNNNNNN0, etc)
# r5 = 0-based 2-bit number of bytes in each SINT (0 = 8-bit, 1 = 16-bit, 2 = 24-bit, 3 = 32-bit)
# -- if value overflows from 2-bit range, then r3 and r4 are filled from UIMM in r5:
# --- r3 becomes zero-shifted 0x-----FF-
# --- r4 becomes zero-shifted 0x----F---
# r6 = location of SINT array start
# r7 = location of float array start
# uses the previous utility function in order to operate on an array of SINTs
# also automates the process of storing results
mflr r0
stw r0, 0x4(sp)
stwu sp, -0x20(sp)
stmw r26, 0x8(sp)
cmpwi r5, 3
ble+ startLoop          # if r5 contains a value within a 2-bit range, use passed r3 and r4
# else, interpret r3 and r4 from UIMM in r5 (a shorthand)
rlwinm r3, r5, 28, 24, 31       # 0x-----FF- mask
rlwinm r4, r5, 20, 28, 31       # 0x----F--- mask
rlwinm r5, r5, 0, 30, 31        # 0x-------3 mask
# from this point, arguments r3-r31 are unmodified and treated as read-only in this function
# this is to make it easy for the function to be reused from the same context in sequence
# CTR is also unused

startLoop:
mr r26, r3              # save r3
mr r30, r26             # r30 = decrementing 1-based loop count
arrayLoop:
subi r30, r30, 1        # decrement 1-based loop count
addi r29, r5, 1
mullw r29, r29, r30     # r29 = offset of next SINT to fetch in decrementing loop
lwzx r3, r6, r29        # load 32-bit word from designated location

li r27, 3
sub r27, r27, r5       # r27 = 3 - 0-based byte count
slwi r27, r27, 3        # 0 = 0, 1 = 8, 2 = 16, 3 = 24 (byte count to bit count)
sraw r3, r3, r27       # r3 = signed right shift 32-bit word to correct the length of SINT
bl <util_slawSINTtoMeleeMeters> # r4 = shift amount

# returns converted float in f1
slwi r29, r30, 2        # word alignment from 0-based CTR
stfsx f1, r29, r7       # store float in output array from r7
cmpwi r30, 0
bgt+ arrayLoop          # loop decrements at start

return:
mr r3, r26
lmw r26, 0x8(sp)
addi sp, sp, 0x20
lwz r0, 0x4(sp)
mtlr r0
blr




<CSWT_getTrailOverride> ALL
# getTrailOverride
blrl # allows the following data to be called like function

# 0x0 override indicators - pointers determine what data to use during override
.long 0x00000000 # 0x0 - "Override Data" - a pointer to a sword trail struct, for overriding update
.long 0x00000000 # 0x4 - "Bone Adjustments" - a pointer to a set of floats that adjust pos/rotation from origin bone

.long 0x00000000 # 0x8 - flags and bone IDs saved from parse--used with the Bone Adjustments values

# 0xC fake sword trail struct - can be used and pointed to by 0x0 "Override Data"
.long 0x00000000 # trail End taper % 1
.long 0x00000000 # trail End taper % 2
.long 0x00000000 # As, Ae, R1, G1
.long 0x00000000 # B1, --, R2, G2
.long 0x00000000 # B2, --, --, -- (blank bytes appear to be unused)
.long 0x00000000 # Bone ID (used to generate global scale/rot coords that define pole origin for vertices)
.long 0x00000000 # Vertex 1 (base) -- stored at internal player 0x20F8 (these seem to scale with bone size)
.long 0x00000000 # Vertex 2 (tip)  -- stored at internal player 0x20FC

# 0x2C Bone adjustment floats - can be used and pointed to by 0x4 "Bone Adjustments"
# (these offset *global* coordinates written to internal player data offset 0x20B0 + ((player.0x2101 >> 1) * 0x18)
# how they apply is heavily dependent on what's stored in 0x8 (ff flags, Bo bone ID, and Bt bone ID)
.long 0x00000000 # X translation (from Bt bone, TopN bone, or Bo bone)
.long 0x00000000 # Y translation
.long 0x00000000 # Z translation
.long 0x00000000 # X rotation    (from Bo bone, TopN bone, or 0)
.long 0x00000000 # Y rotation
.long 0x00000000 # Z rotation

This mostly-working concept is the result of lots of experimentation, but is more of a snapshot of a brainstorm rather than a final code. It could use some work.

It’s a bit involved, and--in anticipation of my habit of propagating new projects from unfinished ones--I left myself descriptive notes of the code and its functions:

“Custom sword trails” - experiment notes

The code creates 2 portable data tables that can be called like functions in order to access their data from an arbitrary location in RAM.

The first table is a list of user definitions for custom trails. They are like a compressed version of the actual data used by Marth/Roy, and Link/Ylink.

The 2 “portable data tables” I mentioned are actually MCM Standalone functions that do nothing but point to a body of included data. For clarity, I’m trying to develop a habit of using the prefix “get” to describe such functions:

(edit: These could feasibly be modified to instead work with file data instead of just the DOL)


<CSWT_getCustomTrailData>
4E800021 # blrl line allows the data to be called like a function (putting a pointer in the LR on return)
# nothing following this line is read as an instruction--only pointed to as data

# <-- any number of user definitions go here. See instructions below this spoiler for the syntax.

FFFFFFFF # this line terminates the table; letting the code know when it’s reached the last definition.

---

The second table is used like a permanently allocated buffer. The code uses it as a place to store mock sword trail parameters in between hooks. It’s possible to use the stack for this purpose, but assigning these to a callable buffer allows the code to be written in a way that doesn’t require us to overhaul the entire sword trail system.

(edit: I think this was written before actually writing the code. Now that it consists of so many injections, it might be more appropriate to use the stack to save code space~)

The buffer is partitioned into 3 parts. The first part contains pointers that indicate where a mock-structure is located for use in the override, while the other 2 parts mimic the format of real structures used for sword trails in the player data.

An included function in the mod can be called to populate these structures with data pulled from a parse of the user definitions; from which the injection codes may reference via the indicator pointers in the first part of this table when creating a sword trail update:

<CSWT_getTrailOverride>
4E800021 # blrl line allows the data to be called like a function
# 1 - @0x00: structure for the code to monitor and use in order to decide how to override the updates
# these values are wiped after each player update (once for bone, once for render; each player/frame)
0x00 - Sword Trail Parameters Override Pointer - 0 = take default action
0x04 - Sword Trail Keyframe Adjustments Pointer - 0 = take default action
0x08 - flags and bone IDs, used for transformations (00 ff Bo Bt)

# 2 - @0x0C: the mod uses this space to write volatile Sword Trail Parameters from parse data
# The game stores similar variables describing vertices and colors as sword user article data

(8 words of parameters described by Bo AA Vrt1 Vrt2 R1G1B1 E1 R2G2B2 E2)

# 3 - @0x2C: the mod uses this to write volatile Keyframe Adjustments from parse data
# The game stores global coords pulled from a player bone to write the sword trail article
# stored at internal player 0x20B0 + ((internal player byte 0x2101 >> 1) * 0x18)

(6 floats cast from bytes XtYtZt XrYrZr)

# these floats are used to offset the final keyframe coords

---

The two tables are called with MCM’s special branch syntax like so:

bl <CSWT_getTrailOverride>
mflr r3 # r3 holds pointer to override struct
bl <CSWT_getCustomTrailData>
mflr r4 #r4 holds pointer to custom trail definitions

<CSWT_findCustomTrail_byCharacterCostume>
- searches each entry in the custom trail definitions for a character/costume identity that matches a given player entity
- returns the position of any match in the data, which should include bytes usable for the following parse

<CSWT_parseDataBuffer>
- take a 6-word input and parse it for -- -- XtYtZt XrYrZr ffBoBt AA Vrt1 Vrt2 R1G1B1 E1 R2G2B2 E2
- place the resulting 14 words of data in the volatile override buffer struct included with the mod
- this information is “volatile” in that--once it’s done being used--it’s no longer reliably retrievable
- returns pointers to the beginning of each partition in the buffer data.

<util_slawSINTtoMeleeMeters>
- this utility takes advantage of the Melee Meter coefficient in order to provide a function that allows ints to represent a linear range of floats with an 8-bit mantissa/significand.
- r3 = a signed int to cast to melee meters, and r4 = the amount to signed shift r3 left before casting
- can be used to write approximate float data from compact bytes (or smaller, potentially)

<util_arraySlawSINTtoMeleeMeters>
- this utility expands on the previous one by allowing it to be called to handle an array of SINTs of similar size/parameters
- limited to 1, 2, 3, or 4-byte SINTs

<CSWT_returnCustomTrail>
makes use of the other functions/tables in order to:
- find an appropriate match (if any) in the custom trail definitions for a player
- parse the data if it exists, and store it in the volatile buffer
- return pointers (or 0) to each of the locations in the buffer. The injections may then store these pointers at the top of the override structure in order to tell the rest of the code that it will be used to render the next sword trail frame.

<CSWT_applyBoneAdjustments>
- takes parsed flags and bone IDs and uses them to work out logic for applying bone adjustments; assuming there is a valid indicator pointer in the override struct for bone adjustments.
- A specified pair of bone IDs (Bo and Bt) are used for an origin point, and a translation transformation respectively.
- Then, according to the flags, 3 rotation and translation offsets are then applied to the origin of Bo, Bt, or TopN.

---

Finally, 5 injections make use of these functions to provide the following order of operations each player/frame.


INJ # specifies custom code, where a lowercase letter specifies a step in the host function:
  • Bone data update - small function finds handles for various sword trail conditions and uses an assigned player bone to update global coordinates during an article spawn responsible for creating sword trails.
    • a - if internal player 0x2100 “render status” byte is 0xFF (-1) then return (from function) without updating. (this is a termination flag used by the game)

    • b - else, host function checks for beamsword conditions. If beamsword trail is active, handle bone keyframe coordinates differently than normal swords.
      • INJ 1 - If all beamsword handle conditions are false, code takes first opportunity to 0 out the now free (volatile) r5 register; which (later in the host function) will define a null-pointer in cases where no struct is found for the inappropriate use of the C4 event. If the override struct from the mod library has no indicator pointers already populating it, then attempt to populate it with user definitions from the custom sword trails data table. These will be checked in INJ 2 and 3.
    • c - Host function checks for Marth/Roy or Link/Ylink article data. In cases where player is not one of these characters, the vanilla game assumes there is a valid struct already in r5 and will attempt to read variables from it like a pointer. We exploit this.
      • INJ 2 - If the override struct has override indicators pointing to mock data (from INJ 1, or other codes) then use that in place of whatever is in r5. Else, if r5 holds a valid default struct (like vanilla sword trails) then use that for the update. Else, abort the host function, and return without updating the bone data to prevent consequences of improperly using the C4 event.
    • d - Host function updates the current keyframe vertices, and prepares a call that “spawns” articles using the specified bone from sword trail parameters.

    • e - On return, host function has already updated the current keyframe position--as well as a performed calculation with data from every bone instance connected to the specified bone up to TopN. Rotation data returned from the function is applied as a final update to the current keyframe bone coordinates starting at internal player 0x20B0.
      • INJ 3 - If the second override indicator pointer for “bone adjustments” is valid, then read the float data from it and add adjustments to the finalized keyframe coordinates. 0 out volatile override indicators to refresh their null status. (the volatile struct can now be used again with new data)
    • f - the host function increments the keyframe count, which overflows at 2. The keyframe count determines where these coordinate floats are stored in array starting at 0x20B0. It also increments the render status by 1 if it is not already >= 3.
  • Draw data update - a much larger function that takes the data created from these keyframe coordinates and uses it (with the color information from the sword trail parameters) to draw the primitives used for sword trails.
    • g - This host function is a part of a much larger loop, and will only run when the loop is on its 3rd iteration this frame. Its first and 2nd iteration will immediately return because of this g section.

    • h - This tiny check causes the host function to immediately return if the byte at internal player 0x2100 is at <= 1. The bone update increments this by 1 each frame that it is below 3, and C4 events usually initialize trails by setting it to 0. Later on during the render, it is used in several parts of the draw calculation; including a CTR count for a loop that draws part of the arc in the trail. I’ve been calling this variable “render status” because it seems to increase the strength of the trail opacity as the number increments.
      • INJ 4 - The sword trail struct in this host function is stored in a saved register (r30) so the first opportunity to 0 it out is very early on. Like in the previous host function, this INJ also sets up the volatile override struct with any matching user definitions using similar logic.
    • i - several calls that write to GX FIFO; see this post in CSM for more info

    • j - this portion of the code contains handles for using default trails and beamswords.
      • INJ 5 - Similar to INJ 2 and 3, this final portion of the code applies any override struct pointed to by the override indicators to r30, and 0s out the override struct indicators to restore their null status.
    • k - the rest of this function takes the struct in r30, loads variables from it that are used in various calculations, populates the stack with the results, and feeds it all into GX FIFO.

To summarize, the code works by creating an isolated buffer that acts as a “common” set of sword trail arguments that can be repopulated at any time with new variables taken from storage. These “common” arguments satisfy the parameters used in the sword trail update/draw functions, and so the injection codes conditionally replace normal parameters with newly populated “common” arguments in order to take control of sword trail mechanics.

---

The experiment gives control over the following in real time (as in, changes are reflected on the next frame without the need to respawn the player)

CH - Character ID -- specify what character gets this sword trail (can be non-sword users)
CS - Costume ID -- specify what costume gets this sword trail

XtYtZt - XYZ translation (position offset from origin bone) (final coords are global, relative to stage)
XrYrZr - XYZ rotation (rotation offset from origin bone, not working as intended--WIP)
Vrt1 - 2-byte Sword Trail Vertex 1 (trail base, length from origin of pole)
Vrt2 - 2-byte Sword Trail vertex 2 (trail tip, length from origin of pole)
ff - Flags (see below for more info)
Bo - BoneID used for creating origin rotation/scale for pole geometry
Bt - BoneID used for translating the position of the final trail keyframe; can be different from Bo
Ag - Alpha gradient -- two 4-bit nibbles, one for Starting Alpha, one for Ending Alpha
R1G1B1 - Gradient RGB hexcolor 1 (trail base) (copy/pastable RRGGBB format)
E1 - Ending taper strength 1 (trail base, % of vertex 1 length)
R2G2B2 - Gradient RGB hexcolor 2 (trail tip)
E2 - Ending taper strength 2 (trail tip, % of vertex 2 length)

Signed shifts and the melee meter conversion create interpolations that approximate the following linear ranges as resulting floats. I’m not sure, but I think this is more commonly called “quantization” or “float packing” and is a very useful technique for compressing arrays of floats into a more manageable size:
Xt, Yt, Zt - Signed 8-bit range = -1600% -- 1600% (covers +/- 16.0 global units)
Xr, Yr, Zr - Signed 8-bit range = -100% -- 100% (rotation summarized as %) (currently a WIP…)
Vrt1, Vrt2 - Signed 16-bit range = 0xNN.NN (covers +/- 128.0 units relative to bone scale)
E1, E2 - Signed 8-bit range = -200% -- 200% (% of length defined by vertices)

ff” Flags are used to create options for handling the XtYtZt XrYrZr bytes differently:
0xF0 = translation flags (position offset)
-- 0x70 = use X, Y, Z translation origin from TopN instead of Bt bone
-- 0x80 = use Bo instead of TopN values for above flag
0x0F = rotation flags (rotation offset)
-- 0x70 = use X, Y, Z rotation origin from TopN instead of Bo bone
-- 0x80 = use "0.0" instead of TopN values for above flag

You can modify these easily by using the ( Edit ) feature in MCM:



---

I was close to posting about this a while back when it was in development, and I have some screenshots that help explain what these variables do. Please note that these are from an earlier version, and the position of the variables are now different--but the meaning is the same:





Edit - and here's one I missed that covers using basic use of XtYtZt:

There are some major differences in version 0.70 of the experiment compared to these screenshots. I'll have to redo them if I ever work on this code again.

The alpha (opacity) bytes have been compressed into 4-bit nibbles, and a 2nd bone ID may now be used to create trail behavior that follows one bone but translates its origin to another bone.

To use sword trails created for non-sword characters, use the C4 event in Crazy Hand:

(this mario trail is included in the default examples)

---

@Achilles1515 @DRGN -- I designed this back in october with the intention of making a custom subaction event. In this sense, it would be doing something very similar to what you guys were discussing in the sword trail color thread--in that the sword trail data would come from inside of a character file rather than in the DOL.

I’m fascinated by the prospect of using file data to store arguments for flexible code. I really like DRGN’s idea of putting default trail arguments in the costume file to simplify costume designing. In a similar sense, a code that changes these colors on the fly would accomplish a similar simplification by existing as event data in the moveset file.

My code above is just an experiment, and includes a lot of stuff I'd like to gut. I had made the code use a Character ID and Costume ID to identify a set of sword trail arguments to be applied by default. If you look carefully, you’ll notice that this identification takes up 2 bytes at the beginning of the syntax I described.

These 2 bytes are only for implementation. They are parsed by the code, but only as a means of keying a character costume to a trail definition. The parse function that actually reads the argument data for creating the sword trail buffer skips these IDs.

In other words, in any other implementation, these 2 bytes are free. This allows them to be implemented according to Achilles’s specification for custom subaction events. I believe that we can apply these specifications to the C4 event--which happens to be the event used to toggle sword trails. In this way, we could write custom events that modify some or all of the above variables as a part of the event data in movesets. Since the code only overrides pointers it has stored away, the actual default trails are untouched in this way and can be reverted back.

---

Anyway, I’m just getting this out there. I got very caught up in the research phase of this project, which eventually turned into a hunt for other drawing functions--and then several other projects. I’ll eventually get back to this, but in the meantime I thought some of you might be interested in the details.

At some point I’m gonna try making the draw function either iterative or recursive so that it can be used to draw multiple sword trails per frame; and make modifications to the gradient function to allow for 4-point color interpolation. Give Mewtwo a kamehameha or something.​
 
Last edited:

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
379
#3
Unfortunately, this old experiment uses a bone ID to select a source joint for the trail.

This works with “swords” used by Links/Marth/Roy presumably because of the fact that their skeletons are designed with a hilt joint and a blade joint that aligns the blade model with a pole that can be generated by default from the joint’s inherent rotation, translation, and scale.

With Ganondorf’s sword however, you have the model joint separate from the player skeleton, and so it can’t be referenced from just a bone ID. The only referencable bone that can be used to create a pole in the correct hand extends from the wrong orientation, and becomes misaligned with sword model:



I attempted to add some rotation modifiers in the syntax back when I made this to remedy problems like this in a crude way, but doing so ruins the draw routine that smooths out the pole keyframe data, resulting in jagged trails.

Here’s a gif that previews the same frame of a sword trail with various amounts of artificial rotation, demonstrating the error:


Not only does the drawing break between keyframes, but the code assumes a euler angle format when the game in fact uses quaternion data. (Thanks for clearing that up, SinsOfApathy SinsOfApathy )

---


I think a new experiment that attempts to generate a dummy joint for the sword trail may allow for the rotation to be more freely manipulated through modification of the joint’s inherent rotation matrix. It’s possible that the dummy joint could then use an RObj to attach itself to the transformations of a target bone in ganondorf’s right hand in order to align it with the sword.

I’ll have to try some things, but I'll let you know if I have any success, Super4ng Super4ng . Several aspects of this code could be improved upon.
 
Joined
Mar 6, 2012
Messages
70
#5
We should also as well include the blending operations for the trail effects which start at 0x800c2684 to give it a brightening/darkening appearance.

GALE01-11.png
 
Last edited:

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
379
#6
B Brandondorf9999 You can actually do this by configuring the primitive drawing module with the following highlighted lines.
These will overwrite default sword trail drawing properties:


-==-


PRIM LITE
Primitive Drawing Module: LITE
rtoc - 0x2194 = 804DD84C = <prim.new>
rtoc - 0x2198 = 804DD848 = <prim.close>
See ( https://smashboards.com/threads/primitive-drawing-module.454232/ ) for more info.
[Punkline]



1.02 ----- 0x800c2684 ----
38600001 38800004 38a00005 38c00005
->
57E3A7BE 57E4C77E 57E5E77E 57E6073E
1.02 ----- 0x800c26b0 ----
38600001 38800003 38A00000
->
57C3A7FE 57C4C77E 57C5AFFE
1.02 ----- 0x800c26c0 --- 38600000 -> 57C39FFE
1.02 ----- 0x800c272c --- 38600000 -> 57C317BE
<prim.setup> 1.02
7C0802A6 90010004 9421C000 BFC10010 7C7E1B78 7C9F2378 38600001
b 0x800c2678
<prim.setup2> 1.02
7C0802A6 90010004 9421C000
bl 0x8033C3C8
b 0x800c2d40
<projection_return> 1.02
38214000 80010004 7C0803A6 4E800020
1.02 ----- 0x800c2da0 --- 38b80001 -> b <projection_return>
1.02 ----- 0x800c2674 --- 38600001 -> b <prim.setup_projection>
<prim.setup_projection> 1.02
3C60
0010 38801455 60631303
bl <prim.setup>
881C2101
b 0x800c2738
1.02 ----- 0x800c2734 --- 881c2101 -> b <prim.setup_projection_epilog>
<prim.setup_projection_epilog> 1.02
BBC10010
b <projection_return>
1.02 ----- 0x800c2d3c --- 4827968d -> b <prim.setup2_projection>
<prim.setup2_projection> 1.02
bl <prim.setup2>
38B80001
b 0x800c2da4
1.02 ----- 0x802E8B48 --- C822DE68 -> C82280A0
1.02 ----- 0x804DD848 --- 43300000 -> b <prim.close>
<prim.close> 1.02
3860FFFF
b 0x80361fc4
1.02 ----- 0x804DD84C --- 80000000 -> b <prim.new>
<prim.new> 1.02
7C0802A6 90010004 9421FFC0 BFA10010 7C7E1B78 7C9F2378 7C832378 7CA42B78
bl <prim.setup>
bl <prim.setup2>
57E3063E 2C030005 41A00024 2C030007 4181001C 57E385BE 38800005 4182000C
bl 0x8033D240
48000008
bl 0x8033d298
57E31E38 38630080 38800000 57C5043E
bl 0x8033D0DC
3C60CC01 38638000 BBA10010 38210040 80010004 7C0803A6 4E800020
#
$PRIM LITE Mastercode [Punkline]
C20C2684 00000003

57E3A7BE 57E4C77E
57E5E77E 57E6073E
60000000 00000000
040c2688 4800000C
040c26b0 57C3A7FE
040c26b4 57C4C77E
040c26b8 57C5AFFE
040c26c0 57C39FFE
040c272c 57C317BE
C20C268C 00000006
7C0802A6 90010004
9421C000 BFC10010
7C7E1B78 7C9F2378
3C00800C 38600001
60002678 7C0903A6
4E800420 00000000
C20C2690 00000006
7C0802A6 90010004
9421C000 3C008033
6000C3C8 7C0803A6
4E800021 3C00800C
60002D40 7C0903A6
4E800420 00000000
C20C2DA0 00000003
38214000 80010004
7C0803A6 4E800020
60000000 00000000
C20C2674 00000007
3C60
0010 38801455
60631303 3C00800C
6000268C 7C0803A6
4E800021 3C00800C
60002738 7C0903A6
881C2101 4E800420
60000000 00000000
C20C2734 00000003
BBC10010 38214000
80010004 7C0803A6
4E800020 00000000
C20C2D3C 00000005
3C00800C 60002690
7C0803A6 4E800021
38B80001 3C00800C
60002DA4 7C0903A6
4E800420 00000000
042E8B48 C82280A0
C24DD848 00000003
3C008036 60031FC4
7C6903A6 3860FFFF
4E800420 00000000
C24DD84C 00000019
7C0802A6 90010004
9421FFC0 BFA10010
7C7E1B78 7C9F2378
7C832378 7CA42B78
3C00800C 6000268C
7C0803A6 4E800021
3C00800C 60002690
7C0803A6 4E800021
57E3063E 2C030005
41A0003C 2C030007
41810034 57E385BE
38800005 41820018
3C008033 6000D240
7C0803A6 4E800021
48000014 3C008033
6000D298 7C0803A6
4E800021 57E31E38
38630080 38800000
57C5043E 3C008033
6000D0DC 7C0803A6
4E800021 3C60CC01
38638000 BBA10010
38210040 80010004
7C0803A6 4E800020
60000000 00000000

The highlighted bytes 0010 1455 1303 can be modified using any of the following options:

Sword trails and 1-pixel (native) lines use the following argument values:
r3 = n
r4 = 0x00101303
r5
= 0x00001455

The above values will create standard primitive geometry that should obey the z-buffer logic in a way similar to sword trails.
Each value can be tweaked using any of the following options:



-- r4 -- -- r5 --
C0000000 00000000 = Cull Mode

+8 = cull backface
+4 = cull frontface


03FF0000 00000000 = Line Width/Point Size
unit = 1/16 pixels; 0x2A8 maximum width


00002000 00000000 = Z Compare Timing
+2 = z buffer compares before texturing

00001000 00000000 = Z Buffer Compare
+1 = z buffer enables compare

00000800 00000000 = Z Buffer Update
+8 = 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
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 key)

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
---

Installing this will create a function that you can use to draw geometry to the screen using various primitive types, allowing you to draw things like sword trails from the perspective of any CObj (including ortho cameras). It works by projecting the sword trail gx pipeline calls, and mimicking the vanilla function context to preserve the original sword trail drawing functionality.

By changing the highlighted bytes, you change the inputs used for normal sword trails. The options are just arguments for the various pipeline calls such as the one you pointed out -- so it can be used to change the blend type, source, destination, and logic.
 
Last edited:

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
379
#8
Thanks, but could it be made based on the character and costume for the trail effects?
I think I’ll need to make a minor modification to the primitive drawing module to do that. If I store the option bytes I mentioned above as global parameters values that are baked into the code, then other codes could override them as needed. That way, a costume-dependent sword trail overhaul could include something in the syntax that safely defines manipulations to the parameters used for default trails without creating any conflicts.

I don’t think adding blending options like this will be an issue if I include the drawing module with it; so I’ll be sure to do this when I make the code.
 

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
379
#10
Any luck with the sword trails yet? I have made a few strides myself. Animation still needs some work mostly at the end, but still looks pretty cool.
https://streamable.com/puq3e
Wow, that looks great! I’d love to see a lot more of this kind of stuff.


I’ve only recently started working on the new code, but I’m deep in the design phase right now. I’m currently considering all of the following:
Independent Trail Definition and Keyframe Objects:
  • use <generator> to create variables that do not rely on player GObj data
    • trail definitions can then be temporarily modified through their instantiated attributes
    • different trails can be used from the same character without overwriting character data

Simultaneous Trails:
  • allow for multiple independent or grouped trails; for complex gradients, or use with multiple joints

Custom Trail Lengths:

  • allow the number of keyframes to be set to a number other than 3
  • memory pool can generate/recycle keyframe objects as needed

Post-Animation:
  • allow for trails to optionally conclude their animations by fading instead of abruptly disappearing

Beamsword Support:
  • allow for costume-specific beamsword colors

Independent Alpha Channels:
  • allow for tip/base colors to be given their own distinct amount of transparency
    • will also be weighted by the normal start/fade alpha

Crazy Hand Subaction Events:
  • surgery on the [C4] event can create new custom syntaxes for manipulating sword trails
    • select from a list of defined custom trails for each costume/character
    • temporarily modify sword trail shape/color attributes through movedata

Bone Assignment Callbacks:
  • complex JObj selections can be handled by an input callback function in place of a bone ID
    • allows for non-indexed bones to be selected via procedure

Puppet Joint:
  • use an external joint attached to a target joint via RObj
    • RObj flags can toggle trans/rot/scale updates from target bone
    • JObj can memorize a modification to the resulting trail orientation, for corrections
    • Transformations to the sword trail will not affect the target bone

GX Options:

  • allow for the definition of non-default GX options that may be indexed and referenced via ID


Regarding the ganondorf sword trail problem specifically, I’ve found that the crazy hand “model mod” event that can be used to toggle the sword just flips a flag in the DObj attached to bone 0x4E. In my earlier experiment, attaching a trail to this bone created a strangely oriented pole that is very small, rotated by about 90 degrees, and off to the side -- but still appears to follow the model.

My plan is to overhaul the sword trail update mechanics to use a “puppet joint” to handle transformations of the trail independently from the bone they are attached to. There are some handy RObj functions that might work well for this, so I’ll be experimenting with that next. My hope is that this will make it easier to implement trails in bones that were not designed to use them.

The other features mentioned in the spoiler are things that I need to deliberate before writing the whole code, so It’s likely going to end up being a large project. I’ll prioritize making a proof of concept for the rotation problem that works specifically for ganondorf’s sword though, so you can try it out with your animation as soon as I figure it out.
 

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
379
#12
Made some good progress with the rotation problem. I ended up using some math functions to transform the output of a target bone instead of relying on an entirely new “puppet joint” to do the heavy lifting, and it seems to work well:


To keep the rotation syntax compact, 1 full rotation will be quantized into 256 equal steps (1.40625 degrees per step).
This allows X, Y, and Z to be specified in 1-byte channels, and thus easily interpreted through the use of graphical quantization registers.

By using 0x00, 0x40, 0x80, or 0xC0 -- you can rotate to exact 90 degree angles, with 63 steps in-between each.
This should let me implement rotation modifiers into the syntax without requiring much space, like I had attempted in the original experiment.

By default each axis is 0x00. For Ganondorf’s sword, changing the Z rotation to 0x40 seems to correct the angle perfectly:


---


Super4ng Super4ng - Here’s an experiment that enables a trail JUST for Ganondorf, rotating it to correct its orientation. It doesn’t do any of the other customization stuff, since it’s just meant to prove that the concept is possible before I go on to make the bigger code -- but you can still customize the color and transparency in the <ganondorf_swordtrail_definition> options.


Enable Ganondorf Sword Trails
Proof of Concept

Code:
-==-

Enable Ganondorf Sword Trails
Enables sword trails for Ganondorf
Proof of Concept for trail rotation modifier
[Punkline]
<ganondorf_swordtrail_definition> ALL
3DCCCCCD 3F800000 # arc taper floats

D0 # starting alpha (transparency)
00 # faded alpha

E43CC3 # color 1

00

800040 # color 2

000000
0000004E # Bone ID
41740000 40680000 # pole vertex floats

00 # X rotation
00 # Y rotation
40 # Z rotation
00

1.02 ----- 803C6CA0 ---- 800c30d4 -> .long <<ganondorf_swordtrail_update_callback>>
<ganondorf_swordtrail_update_callback> All
lis r0, <<ganondorf_swordtrail_definition>>@h
ori r5, r0, <<ganondorf_swordtrail_definition>>@l
b 0x800c30d4
1.02 ----- 803c6c4c ---- 800c27c8 -> .long <<ganondorf_swordtrail_drawing_callback>>
<ganondorf_swordtrail_drawing_callback> All
lis r0, <<ganondorf_swordtrail_definition>>@h
ori r30, r0, <<ganondorf_swordtrail_definition>>@l
b 0x800c27c8
1.02 ----- 800c3128 ---- 887F2101 -> Branch
801F0004 2C000019 40A20078
lis r0, <<ganondorf_swordtrail_definition>>@h
ori r29, r0, <<ganondorf_swordtrail_definition>>@l
7C0802A6 9421FF80 90010084 C0028B5C 3C000804 38810010 7C17E3A6 E03D7020 10610032 E05DF022 10820032 F0640000 D0840008 38610020
bl 0x8037a120
387C0044 38810020 38A10050
bl 0x80342204
387E000C 80A10050 80C10060 80E10070 7CA365AA 80010084 38210080 7C0803A6 887F2101 00000000
#
Code:
-==-
!
ASM - Enable Ganondorf Sword Trails
Enables sword trails for Ganondorf
Proof of Concept for trail rotation modifier
[Punkline]
<ganondorf_swordtrail_definition> ALL
.float 0.1 # trail End taper % 1
.float 1.0 # trail End taper % 2
.long 0xD000E43C # As, Ae, R1, G1
.long 0xC3008000 # B1, --, R2, G2
.long 0x40000000 # B2, --, --, --
.long 0x0000004E # Bone ID
.float 15.25 # Vertex 1 (tip)  -- stored at player 0x20F8
.float 3.625 # Vertex 2 (base) -- stored at player 0x20FC

.long 0x00004000 # XYZ rotation mod

1.02 ----- 803C6CA0 ---- 800c30d4 -> .long <<ganondorf_swordtrail_update_callback>>
<ganondorf_swordtrail_update_callback> All
lis r0, <<ganondorf_swordtrail_definition>>@h
ori r5, r0, <<ganondorf_swordtrail_definition>>@l
b 0x800c30d4
# handles case of ganondorf for keyframe update
# "callback" is a bctr branch, rather than a blrl call


1.02 ----- 803c6c4c ---- 800c27c8 -> .long <<ganondorf_swordtrail_drawing_callback>>
<ganondorf_swordtrail_drawing_callback> All
lis r0, <<ganondorf_swordtrail_definition>>@h
ori r30, r0, <<ganondorf_swordtrail_definition>>@l
b 0x800c27c8
# handles case of ganondorf for drawing
# "callback" is a bctr branch, rather than a blrl call


1.02 ----- 800c3128 ---- 887F2101 -> Branch
# INJ: UPDATE -- post-update overwrites
# @ 800c3128 : lbz r3, 0x2101 (r31)
# r30 = player data offset 0x20B0 + (keyframeID*0x18)
# r29 = free to use
# r28 = target JObj, post update

.set rDef, 29   # custom sword trail definition
.set xRot, 0x20 # offset of rotation bytes
lwz r0, 0x4(r31)
cmpwi r0, 0x19
bne+ _return

  _if_ganondorf:
  lis r0, <<ganondorf_swordtrail_definition>>@h
  ori rDef, r0, <<ganondorf_swordtrail_definition>>@l
  # load rDef in saved register

  mflr r0
  stwu sp, -0x80(sp)
  stw  r0, 0x84(sp)
  # temporary stack frame, with full prolog just to be safe

  lfs f0, -0x74A4(rtoc)
  # f0 0 = 2pi
  # f0 1 = 2pi
  # rotation bytes will be used to create a coefficient of 2pi for XYZ

  lis r0, 0x804
  addi r4, sp, 0x10
  mtspr 919, r0
  # QR7 = load dequantizes 1/256 unit, signed bytes

  psq_l f1, 0x20(rDef), 0, 7
  # f1 0 = X coef
  # f1 1 = Y coef

  ps_mul f3, f1, f0
  # f3 0 = 2pi * (X coef)
  # f3 1 = 2pi * (Y coef)

  psq_l f2, 0x22(rDef), 1, 7
  # f2 0 = Z coef
  # f2 1 = 1.0

  ps_mul f4, f2, f0
  # f4 0 = 2pi * (Z coef)

  psq_st f3, 0x0(r4), 0, 0
  stfs f4, 0x8(r4)
  addi r3, sp, 0x20
  # r3 = output for HSD_MkRotationMtx
  # r4 = dequantized XYZ Vector argument for HSD_MkRotationMtx

  bl 0x8037a120
  # HSD_MkRotationMtx

  addi r3, r28, 0x44
  addi r4, sp, 0x20
  addi r5, sp, 0x50
  bl 0x80342204
  # HSD_MTXConcat

  addi r3, r30, 0xC
  lwz  r5, 0x50(sp)
  lwz  r6, 0x60(sp)
  lwz  r7, 0x70(sp)
  stswi r5, r3, 0xC
  # write rotation information to keyframe element in r30

  lwz r0, 0x84(sp)
  addi sp, sp, 0x80
  mtlr r0
  # epilog for temp stack frame

_return:
lbz r3, 0x2101(r31)
.long 0

NOTE: this code requires Melee Code Manager 4.2 or above -- updated 4/22/2019
- this version fixes a bug that was causing problems during installation in the previous version

---


To install this code, copy the DOL Mod text into a .txt file inside of you Mods Library folder. It can be a new file, or one of the existing .txt files. When you start up MCM, the text file containing the code will show up as something that can be selected for installation on a given DOL or ISO. You may save your selections to the ISO by clicking Save or Save As:


To customize the shape/colors, make sure the code is uninstalled and click the edit button. From the menu that appears, find the <ganondorf_swordtrail_definition> function under the “For All” tab. When you make your changes, you can save them, and re-install the code.


Finally, in Crazy Hand, you can add the event C4000000 to start a sword trail in any given action. It will end on new actions.


If you want to terminate a trail early, you may use a second event with the value C5FFFFFF.

Let me know if anyone has any questions.
 
Last edited:
Top