Completed HSD_Hide Module - non-destructive model hiding

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
408
This light-weight module lets you easily hide JObjs (Joint Objects) and PObjs (Polygon Objects) in a way that can be toggled on or off without destroying information. These edits can be done either from codes that access these objects, or from edits to the file structures that the objects are defined in. GObj GX Callback events have also been modified to be toggleable, allowing the drawing of any GObj to become optional.

Together these extra GObj, JObj, and PObj Hide flags afford varying degrees of specificity for toggling the visibility of model assets in Melee in a way that is universal, non-destructive, and easy to invoke without requiring any kind of external functions -- just 3 lines of code that directly modify the object structures. (Hiding these assets in the game is possible by conventional means -- but every method of doing so is destructive, and can’t be undone without first making some kind of copy of the destroyed information.)


Any other code may make use of this module after it has been installed, regardless of the installation method used:

HSD_Hide Module:
-==-

HSD_Hide module
Enables easy control over how model assets are hidden through the following
extensions:

0x1C of GObj: GX Callback (sign logic)
# +80000000 = bit 0: pretend sign is a bool -- "Draw_GObj"

0x14 of JObj: flags field
0x04 of JObjDesc: flag field
# +08000000 = bit 4: "Hide_JObj" (without interfering with JObj_Hide logic)

0x0C of PObj: flags field
0x0C of PObjDesc: flags field
# +08000000 = bit 4: "Hide_PObj" (forces geometry culling)
[Punkline]
NTSC 1.02 ----- 8036e8d8 --- a0e3000c -> Branch
A0E3000C 70E00800 41A20008 60E7C000 00000000
--------------- 8037449c --- 80630014 -> Branch
80630014 74600800 41A20008 60630010 00000000
--------------- 80390ff8 --- 28000000 -> Branch
2C800000 540F007F 4C422342 00000000
--------------- 80390f38 --- 28000000 -> Branch
2C800000 540F007F 4C422342 00000000
Rich (BB code):
-==-
!
ASM - HSD_Hide module
Enables easy control over how model assets are hidden through the following extensions:

0x1C of GObj: GX Callback (sign logic)
# +80000000 = bit 0: pretend sign is a bool -- "Draw_GObj"
  
0x14 of JObj: flags field
0x04 of JObjDesc: flag field
# +08000000 = bit 4: "Hide_JObj" (without interfering with JObj_Hide logic)
  
0x0C of PObj: flags field
0x0C of PObjDesc: flags field
# +08000000 = bit 4: "Hide_PObj" (forces geometry culling)
[Punkline]

NTSC 1.02 ----- 8036e8d8 --- a0e3000c -> Branch
# --- PObjs
lhz r7, 0xC(r3)
andi. r0, r7, 0x0800
beq+ _return
  ori r7, r7, 0xC000
_return:
.long 0
  
--------------- 8037449c --- 80630014 -> Branch
# --- JObjs
lwz r3, 0x14(r3)
andis. r0, r3, 0x0800
beq+ _return
  ori r3, r3, 0x10
_return:
.long 0
  
--------------- 80390ff8 --- 28000000 -> Branch
# --- GObjs
cmpwi cr1, r0, 0; cr1.lt=4; eq=2
rlwinm. r15, r0, 0, ~(1<<31)
crorc eq, eq, cr1.lt
.long 0
--------------- 80390f38 --- 28000000 -> Branch
cmpwi cr1, r0, 0; cr1.lt=4; eq=2
rlwinm. r15, r0, 0, ~(1<<31)
crorc eq, eq, cr1.lt
.long 0
Code:
$HSD_Hide module [Punkline]
C236E8D8 00000003
A0E3000C 70E00800
41A20008 60E7C000
60000000 00000000
C237449C 00000003
80630014 74600800
41A20008 60630010
60000000 00000000
C2390FF8 00000002
2C800000 540F007F
4C422342 00000000
C2390F38 00000002
2C800000 540F007F
4C422342 00000000

You may include the module with your own codes to automatically install the module if it is missing from an MCM Mods Library.
To access the installed flags, use the following mask interfaces with their corresponding objects:

Rich (BB code):
0x1C of GObj: GX Callback (sign logic)
# +80000000 = bit 0: pretend sign is a bool -- "Draw_GObj"
  
0x14 of JObj: flags field
0x04 of JObjDesc: flag field
# +08000000 = bit 4: "Hide_JObj" (without interfering with JObj_Hide logic)
  
0x0C of PObj: flags field
0x0C of PObjDesc: flags field
# +08000000 = bit 4: "Hide_PObj" (forces geometry culling)


Below is a brief tutorial demonstrating how to use each flag type, with standalone code examples that you can install through MCM.


---



The Draw_GObj sign bit:

GObjs are like general-purpose objects that can be fashioned into things like model skeletons for drawing and updating each frame. They are used to streamline the process of instantiating parts of the game that vary between things as different as menu items, player characters, or cameras inside of a scene. The GX Callback of a GObj is used to streamline the process of drawing most models in Melee through the use of HSD Object assets that can be attached to it.

GObjs are organized into meaningful groups that can be navigated from the p_link and gx_link arrays available at r13 offsets -0x3E74 and -0x3E7C, respectively. These arrays create 64 slots for pointers to be indexed at the start of scenes for the purpose of starting different lists of GObjs. The function of each slot varies depending on the scene.

These p_link and gx_link arrays are globally accessible, meaning that you can easily access them at any point in the game code.
For example, using: lwz r3, -0x3E74(r13); lwz rGObj, 0x14(r3) will let you access the list of stage assets used by any stage from any code that executes in the Melee VS scene.

Every GObj has a variable at offset 0x1C in its structure that points to a GX callback function. This basically tells the game what function to use in order to draw this GObj to the camera that’s in charge of its gx_link group. GObjs that are grouped together in the same gx_link are usually related to the same drawing process.

Not all GObjs have one of these GX Callback functions, so the pointer is blank (0) if there is none.

To Hide GObjs, this code simply lets you use a positive number (>0) to mimic the logic of using one of these blank pointer values -- allowing you to toggle the sign bit from the address in order to turn off the display of a GObj. Toggling the sign bit back on will restore the preserved pointer address, re-enabling the drawing function. This may be used like an inverted “Hide” flag. A “Show” flag; or a “Draw_GObj” flag.

It’s worth noting that this is very similar to the code in this thread, but has an additional safeguard to prevent attempting to execute re-enabled null pointers.




Pokemon Stadium - Remove Entire Background
An example of hiding GObjs

Rich (BB code):
-==- 
  
Pokemon Stadium - Remove Entire Background 
Example of the "Draw GObj" flag. Requires HSD_Hide module. 
[Punkline] 
NTSC 1.02 ----- 801d10b8 --- 8001000c -> Branch 
  
# --- Symbols --  x- indeX  r- Register  m- Mask 
r13.xPLink=-0x3E74; PLink.xStage=5<<2 
GObj.xNext=0x8; GObj.xGX=0x1C; 
mDraw_GObj=1<<31 
rGObj=3; 
  
# --- Find GObj: 
lwz rGObj, r13.xPLink(r13) 
# r13 can be used like this anywhere in the game code to reach the PLink table 
  
lwz rGObj, PLink.xStage(rGObj) 
# PLink 5 is used by Melee stages to group stage assets into a linked list 
  
lwz rGObj, GObj.xNext(rGObj) 
# The second GObj in the stage assets list has the pokemon stadium background assets 
  
# --- Hide GObj: 
lwz r0, GObj.xGX(rGObj) 
rlwinm r0, r0, 0, ~mDraw_GObj  # ~ prefix inverts the mask 
stw r0, GObj.xGX(rGObj) 
# mask out sign bit and update GX pointer to disable GX drawing 
  
_return: 
lwz r0, 0xC(sp) 
.long 0 
  
NTSC 1.02 ----- 8036e8d8 --- a0e3000c -> Branch 
A0E3000C 70E00800 41A20008 60E7C000 00000000 
--------------- 8037449c --- 80630014 -> Branch 
80630014 74600800 41A20008 60630010 00000000 
--------------- 80390ff8 --- 28000000 -> Branch 
2C800000 540F007F 4C422342 00000000 
--------------- 80390f38 --- 28000000 -> Branch 
2C800000 540F007F 4C422342 00000000 


With this module, you can hide any GObj like so:

Rich (BB code):
GObj.xGX=0x1C; mDraw_GObj=1<<31
  
lwz r0, GObj.xGX(rGObj)
rlwinm r0, r0, 0, ~mDraw_GObj  # ~ prefix inverts the mask
stw r0, GObj.xGX(rGObj)
# mask out sign bit and update GX pointer to disable GX drawing
---


The Hide_JObj Flag:

JObjs are like puppet joints that store information about the size, position, rotation, and other things that a collection of Polygon objects can be transformed by. JObjs link together to make trees that can be used to represent skeletons. Each JObj has a set of flags that can be used to control various statuses. Included in this set of default flags is a bit called JOBJ_HIDDEN -- however the game uses this to drive the logic of certain processes in many implementations, so interfering with it can sometimes create unwanted changes in behavior.

To prevent this destruction of information, an unused bit in the field (08000000) has been assigned in this code to create a sort of subtle doppelganger for the vanilla JObj_HIDDEN flag, extending the logic of the check normally made right before drawing from a list of DObjs. This extra check is made at no other place in the game, so the modification only applies to the moment DObjs are parsed for drawing. In other words, it cannot interfere with the normal hide logic, and will not be broken by vanilla mechanics in the game.

By loading the flags from offset 0x14 of any JObj, you can OR in this extra Hide_JObj flag to update it with a hide effect that will not be overwritten by other processes in the game. You may also AND it out of the mask to disable the effect:




Pokemon Stadium - Remove Background Screen
An example of hiding JObjs

Rich (BB code):
-==-
  
Pokemon Stadium - Remove Background Screen
Example of the "Hide JObj" flag.
[Punkline]
NTSC 1.02 ----- 801d10b8 --- 8001000c -> Branch
  
# --- Symbols:
r13.xPLink=-0x3E74; PLink.xStage=5<<2
GObj.xNext=0x8; GObj.xGX=0x1C; GObj.xObj=0x28;
JObj.xChild=0x10; JObj.xNext=0x8; JObj.xFlags=0x14
mDraw_GObj=1<<31; mHide_JObj=1<<27
rGObj=3; rJObj=4
  
# --- Find GObj:
lwz rGObj, r13.xPLink(r13)
lwz rGObj, PLink.xStage(rGObj)
lwz rGObj, GObj.xNext(rGObj)
# rGObj = object containing joints we want to hide, and joints we want to keep visible
  
# --- Find JObjs:
lwz rJObj, GObj.xObj(rGObj)
# load root JObj from GObj
  
.rept 2; lwz rJObj, JObj.xChild(rJObj); .endr
# load last child JObj from root
# rJObj = first of 10 visible Screen JObj siblings
  
# --- Hide JObjs:
li r0, 9; mtctr r0
_for_8_JObjs:
  lwz r0, JObj.xFlags(rJObj)
  oris r0, r0, mHide_JObj@h  # use @h to select only high 16-bits for oris instruction
  stw r0, JObj.xFlags(rJObj)
  lwz rJObj, JObj.xNext(rJObj)
bdnz+ _for_8_JObjs
# OR in flags and update 8 JObjs
# Leave the last 2 visible so there's something in the background
  
_return:
lwz r0, 0xC(sp)
.long 0

NTSC 1.02 ----- 8036e8d8 --- a0e3000c -> Branch 
A0E3000C 70E00800 41A20008 60E7C000 00000000 
--------------- 8037449c --- 80630014 -> Branch 
80630014 74600800 41A20008 60630010 00000000 
--------------- 80390ff8 --- 28000000 -> Branch 
2C800000 540F007F 4C422342 00000000 
--------------- 80390f38 --- 28000000 -> Branch 
2C800000 540F007F 4C422342 00000000 


With the HSD_Hide module, you can hide any JObj like so:

Rich (BB code):
JObj.xFlags=0x14; mHide_JObj=1<<27
  
lwz r0, JObj.xFlags(rJObj)
oris r0, r0, mHide_JObj@h  # use @h to select only high 16-bits for oris instruction
stw r0, JObj.xFlags(rJObj)
# OR in the Hide_JObj flag
---


The Hide_PObj Flag:

PObjs are Polygon objects. They hold a list of vertices, with other information describing the geometry of a model.

PObjs have a pair of flags that let you control whether or not to “cull” the drawing of geometry belonging to front or back faces -- preventing those faces from being drawn. If both flags are set to true, then the whole model is culled -- effectively hiding it -- however there is no way to recover the lost culling flags when doing this.

To prevent this destruction of information, this code uses the unused bit (08000000 -- same as the JObj) to create a Hide_PObj flag that invokes 2-sided culling behavior without using the culling flags.

You can load the PObj flags from offset 0xC of any PObj, and OR in this flag to hide it. You may also AND it out of the mask to disable the effect:




Pokemon Stadium - No Grain effect in Background Screen
An example of hiding PObjs

Rich (BB code):
Pokemon Stadium - No Grain effect in Background Screen
Example of the "Hide PObj" flag.
[Punkline]
NTSC 1.02 ----- 801d10b8 --- 8001000c -> Branch
  
# --- Symbols:
r13.xPLink=-0x3E74; PLink.xStage=5<<2
GObj.xNext=0x8; GObj.xGX=0x1C; GObj.xObj=0x28;
JObj.xChild=0x10; JObj.xNext=0x8; JObj.xFlags=0x14; JObj.xDObj=0x18;
DObj.xNext=0x4; DObj.xPObj=0xC; PObj.xFlags=0xC
mDraw_GObj=1<<31; mHide_JObj=1<<27; mHide_PObj=1<<27
rGObj=3; rJObj=4; rDObj=5; rPObj=6
  
# --- Find GObj:
lwz rGObj, r13.xPLink(r13)
lwz rGObj, PLink.xStage(rGObj)
lwz rGObj, GObj.xNext(rGObj)
# rGObj = object screen overlay model
  
# --- Find JObj:
lwz rJObj, GObj.xObj(rGObj);
.rept 2; lwz rJObj, JObj.xChild(rJObj); .endr
# rJObj = Screen frame/overlay
  
# --- Find PObj:
lwz rDObj, JObj.xDObj(rJObj)
# load root DObj in list of display objects for this joint
  
lwz rDObj, DObj.xNext(rDObj)
# load second DObj to select the overlay that causes grain, and the grid pattern
  
lwz rPObj, DObj.xPObj(rDObj)
# rPObj= polygon mesh used for this display object.
  
# --- Hide PObj:
lwz r0, PObj.xFlags(rPObj)
oris r0, r0, mHide_PObj@h
stw r0, PObj.xFlags(rPObj)
# update flags with the Hide_PObj flag
  
_return:
lwz r0, 0xC(sp)
.long 0

NTSC 1.02 ----- 8036e8d8 --- a0e3000c -> Branch 
A0E3000C 70E00800 41A20008 60E7C000 00000000 
--------------- 8037449c --- 80630014 -> Branch 
80630014 74600800 41A20008 60630010 00000000 
--------------- 80390ff8 --- 28000000 -> Branch 
2C800000 540F007F 4C422342 00000000 
--------------- 80390f38 --- 28000000 -> Branch 
2C800000 540F007F 4C422342 00000000 

Pokemon Stadium - Remove Sides of Background Screen
An example of hiding PObjs

Rich (BB code):
-==-
  
Pokemon Stadium - Remove Sides of Background Screen
Example of the "Hide PObj" flag.
[Punkline]
NTSC 1.02 ----- 801d10b8 --- 8001000c -> Branch
  
# --- Symbols:
r13.xPLink=-0x3E74; PLink.xStage=5<<2
GObj.xNext=0x8; GObj.xGX=0x1C; GObj.xObj=0x28;
JObj.xChild=0x10; JObj.xNext=0x8; JObj.xFlags=0x14; JObj.xDObj=0x18;
DObj.xNext=0x4; DObj.xPObj=0xC; PObj.xFlags=0xC
mDraw_GObj=1<<31; mHide_JObj=1<<27; mHide_PObj=1<<27
rGObj=3; rJObj=4; rDObj=5; rPObj=6
  
# --- Find GObj:
lwz rGObj, r13.xPLink(r13)
lwz rGObj, PLink.xStage(rGObj)
lwz rGObj, GObj.xNext(rGObj)
# rGObj = object screen overlay model
  
# --- Find JObj:
lwz rJObj, GObj.xObj(rGObj);
.rept 2; lwz rJObj, JObj.xChild(rJObj); .endr
# rJObj = Screen frame/overlay
  
# --- Find PObjs:
lwz rDObj, JObj.xDObj(rJObj)
i=-1
.rept 5; i=i+1
  .if i;  lwz rDObj, DObj.xNext(rDObj)
  .else;  lwz rDObj, JObj.xDObj(rJObj)
  .endif  # load root DObj first, then load siblings for each after
  .if i>2 # for last 2 DObjs in siblings list...
     
    # --- Hide PObjs:
    lwz rPObj, DObj.xPObj(rDObj)
    lwz r0, PObj.xFlags(rPObj)
    oris r0, r0, mHide_PObj@h
    stw r0, PObj.xFlags(rPObj)
    # Hide side-bar things in the Screen frame
     
  .endif
.endr
  
_return:
lwz r0, 0xC(sp)
.long 0

NTSC 1.02 ----- 8036e8d8 --- a0e3000c -> Branch 
A0E3000C 70E00800 41A20008 60E7C000 00000000 
--------------- 8037449c --- 80630014 -> Branch 
80630014 74600800 41A20008 60630010 00000000 
--------------- 80390ff8 --- 28000000 -> Branch 
2C800000 540F007F 4C422342 00000000 
--------------- 80390f38 --- 28000000 -> Branch 
2C800000 540F007F 4C422342 00000000 


With the HSD_Hide module, you can hide any PObj like so:

Rich (BB code):
PObj.xFlags=0xC; mHide_PObj=1<<27
  
lwz r0, PObj.xFlags(rPObj)
oris r0, r0, mHide_PObj@h
stw r0, PObj.xFlags(rPObj)
# Hide PObj
 
Last edited:

UnclePunch

Smash Ace
Joined
Nov 9, 2014
Messages
666
was wondering what that grain effect was!

awesome job making PObj's toggleable.
 

Brandondorf9999

Smash Cadet
Joined
Mar 6, 2012
Messages
71
There's also one address that also hides Jobjs which is at 0x80371d9c. I found that from the codes that hide the continue elements by a game over state boolean.
 

Punkline

Dr. Frankenstack
Premium
Joined
May 15, 2015
Messages
408
B Brandondorf9999 : That function can be used to set the JObj_HIDDEN flag in joints; but you should know that it’s actually a part of a general-purpose get/set interface for all JObj flags.


Here are some related functions you might be interested in:
Rich (BB code):
# for each, r3=JObj  r4=flags (32-bit mask)
HSD_JObjSetFlags      = 0x80371d00
HSD_JObjSetFlagsAll   = 0x80371d9c
HSD_JObjClearFlags    = 0x80371f00
HSD_JObjClearFlagsAll = 0x80371f9c
HSD_JObjGetFlags      = 0x80371ce8  # returns r3 = flags


Check out the enumerations here in SinsofApathy’s FRAY project for a list of all the flags you can use with these functions.
IIRC, you can combine multiple flags in a 32-bit mask for the -All methods, but I don't quite remember for sure.

Here’s something you can throw in your ASM to reference and combine flags with the OR | operator:
Rich (BB code):
JOBJ_SKELETON         = (1 << 0) 
JOBJ_SKELETON_ROOT    = (1 << 1) 
JOBJ_ENVELOPE_MODEL   = (1 << 2) 
JOBJ_CLASSICAL_SCALE  = (1 << 3) 
JOBJ_HIDDEN           = (1 << 4) 
JOBJ_PTCL             = (1 << 5) 
JOBJ_MTX_DIRTY        = (1 << 6) 
JOBJ_LIGHTING         = (1 << 7) 
JOBJ_TEXGEN           = (1 << 8) 
JOBJ_INSTANCE         = (1 << 12) 
JOBJ_PBILLBOARD       = (1 << 13) 
JOBJ_SPLINE           = (1 << 14) 
JOBJ_FLIP_IK          = (1 << 15) 
JOBJ_SPECULAR         = (1 << 16) 
JOBJ_USE_QUATERNION   = (1 << 17) 
JOBJ_NULL_OBJ         = (0 << 21) 
JOBJ_JOINT1           = (1 << 21) 
JOBJ_JOINT2           = (2 << 21) 
JOBJ_EFFECTOR         = (3 << 21) 
JOBJ_USER_DEF_MTX     = (1 << 23) 
JOBJ_MTX_INDEP_PARENT = (1 << 24) 
JOBJ_MTX_INDEP_SRT    = (1 << 25) 
JOBJ_ROOT_OPA         = (1 << 28) 
JOBJ_ROOT_XLU         = (1 << 29) 
JOBJ_ROOT_TEXEDGE     = (1 << 30) 
  
## example: 
mr r3, r31 
li r4, JOBJ_HIDDEN
bl 0x80371d9c 
# cause JObj to become hidden

So, when you see the game setting r4 to 16 (1 << 4) when calling one of these functions -- you know it's interfacing with the JOBJ_HIDDEN flag.

---



I actually almost ended up using the vanilla JObj_SPLINE flag instead of making a new flag for JObjs in this code -- because it too appears to cause a joint to become hidden (or at least refuse to display its DObj). I didn’t however know what the side effects of using that would be, so I opted to make a separate flag out of one of the few unused bits in the JObj flagfield.

The reason I didn’t use the vanilla JObj_HIDDEN flag for this code is because it gets set and cleared procedurally in GObjs that have GProcs that update a display each frame. In addition, I recall finding that some GProcs seem to actually check the flag in a JObj to see if it is enabled or disabled -- making it possible to accidentally start some kind of update procedure just by hiding/unhiding something.

For example, if you wanted to hide something that gets hidden and shown by a game function -- like a digit from the damage HUD, or the various pokemon stadium stage screens -- then your edit to JObj_HIDDEN in the digit joint would get overwritten later in the frame/scene by the update process. You can get around this in many cases by just overhauling the logic responsible for writing these updates, however your edits to the flags may be interpreted as enabling/disabling an object by other processes that exist in the game. By creating and using a flag not available to the game developers, you can ensure that no process in the game can interfere with the logic you set for a hidden joint -- and vice versa.


So basically -- it’s like an isolated priority hide flag that takes precedence over the vanilla hide flag, and won’t be messed up by game updates.
In my example in the top post, I use this priority to get around the blinking screen updates made to the pokemon stadium stage without having to modify the function that updates the stage transitions.


---

Somewhat related; the Joints of Truth code causes the vanilla flag to be ignored, and constantly shows all joints that are normally hidden by game processes. It's a good way to get a look at how many things in the game are procedurally hidden that may possibly be shown later by a procedure -- overwriting any edits you may have made to it.
 
Last edited:
Top Bottom