Drawing basic shapes with gecko codes

Eonbot

Smash Rookie
Joined
May 27, 2015
Messages
2
This tutorial is for asm coders who want to start to try to draw gfx to the screen during a code for whatever reason. It is fairly complicated and assumes a moderately high level in asm coding already, and only shows how to draw the most basic of shapes.


GX is drawn once per frame and so you have to be calling the code detailed below once per frame if you want it to correctly draw.

For more detailed stuff, https://pokeacer.xyz/wii/pdf/GX.pdf this document is awesome but hard to work with, it is the nintendo documentation for the rendering engine.

%callFunc() will be denoting calling the function specified within, i will follow it with a comment of the functions name. this will either be performed by you with a `bl offset` or by what i use

Code:
callfunc(0x80019FA4)

=

{

  lis r12, 0x8001

  ori r12, r12, 0x9FA4

  mtctr r12

  bctrl

}
i set this up as a macro in GCTRM that performs it every time i write %callfunc(<addr>)
Code:
.macro callfunc(<addr>)
{
.alias temp_Hi = <addr> / 0x10000
.alias temp_Lo = <addr> & 0xFFFF
  lis r12, temp_Hi
  ori r12, r12, temp_Lo
  mtctr r12
  bctrl
}
Beginning of a basic shape draw will require calling the following two functions
`
%callfunc(0x80019FA4) #getManager/[gfCameraManager]
%callfunc(0x80018DE4) #setGX/[gfCamera]
`
If you're familiar with 3d logic, this gets the world matrix and applies it to whatever you will draw. If you're not familiar with it, just know it orientates your shape to the in-game world.
There is probably a fairly simple matrix that you could set up to draw in-line with the UI, but i currently don't know it (its probably just the identity)
next you want to do the following
Code:
    %callfunc(0x8001A5C0) #gfDrawSetVtxPosColorPrimEnviroment/gfDraw
    li r3, 0
    %callFunc(0x801F136C) #GXSetCullMode(0)
this sets up the gfx to be a basic vertex + colour input, instead of the much fancier stuff you can do. It then sets the culling of the object to no culling, since otherwise the previous state of the graphics renderer will arbritarily determine it.
(dont ask why this isnt just part of the setup function, but it isnt)
If you are going to be drawing lines, then you will need the following command before drawing, otherwise just skip this block
Code:
    #convert line width as double into line width as integer
    lfs f0, -0x7B68(r2)
    fmuls f0, f0, f30 #assumes a "width" is set within f30, if you just want to set up a width yourself, just do `li r3, n` and experiment
    fctiwz f0, f0
    stfd f0, 0x8(r1)
    lwz r3, 0xC(r1)
    rlwinm r3, r3, 0, 24, 31
    li r4, 2
    %callfunc(0x801f12ac) #GXSetLineWidth(width, 2)
This does what it says on the tin and sets the line width of what you're about to draw
Final thing before actually placing the vertices you want to the screen, you have to tell the game what type of shapes you are drawing
Code:
    li r3, 0xB0 #line strip
    li r4, 1
    li r5, 5 #5 vertices
    %callfunc(0x801F1088) GXBegin(type, 1, numOfVertices)
    lis r3, 0xCC01 #gfx mem-loc
place into r3 the type of shape you are drawing.
0x80 means triangle strip,
0xB0 means line strip
will update further as i learn other types, all types without their respective IDs found on page 17 of the GX documentation. https://pokeacer.xyz/wii/pdf/GX.pdf#page=29
place into r4 1, that just defines that you are using the stuff setup by gfDrawSetVtxPosColorPrimEnviroment.
place into r5 the number of vertices you plan to draw.
the lis r3 after the function call is getting the graphics input address into r3 to send data too. its a magic number address that instead sends anything you write there to the gfx card.
at this point you draw vertices by doing the following
Code:
   stfs f1, -0x8000(r3) #x position
   stfs f2, -0x8000(r3) #y position
   stfs f3, -0x8000(r3) #z position
   stw r31, -0x8000(r3) #colour of vertex, honestly not sure what it does if you pass in different colours for each vertex rn
All that matters here is you pass the information you want into -0x8000(r3) in the order x,y,z,c, and you must send exactly the number you specified before.




an example code that uses all of the above information is here :
Code:
draw ledgegrabbox [Eon]
.macro callfunc(<addr>)
{
.alias temp_Hi = <addr> / 0x10000
.alias temp_Lo = <addr> & 0xFFFF
  lis r12, temp_Hi
  ori r12, r12, temp_Lo
  mtctr r12
  bctrl
}
HOOK @ $80120674
{ 
    %callfunc(0x80019FA4) #getManager/[gfCamera]
    %callfunc(0x80018DE4) #setGX/[gfCamera]
    %callfunc(0x8001A5C0) #gfDrawSetVtxPosColorPrimEnviroment/gfDraw
    li r3, 0
    %callFunc(0x801F136C) #GXSetCullMode(0)
    li r3, 0x10
    li r4, 2
    %callfunc(0x801f12ac) #GXSetLineWidth(0x10, 2)
  
    li r3, 0xB0 #line strip
    li r4, 1
    li r5, 5 #5 vertices
    %callfunc(0x801F1088) #GXBegin(LINE_STRIP, 1, 5)
    lis r3, 0xCC01 #gfx mem-loc
    lis r4, 0x00FF
    ori r4, r4, 0x00FF #0x00FF00FF = pure red
    li r0, 0 #setting z position to 0, so just storing here for now
    #known values of positions based on hook location
    lfs f0, 0x50(r1)  #Left X
    lfs f1, 0x54(r1) #top y
    lfs f2, 0x58(r1) #right x
    lfs f3, 0x5C(r1) #bottom y
 
#vertex 0 = left x, top y
    stfs f0, -0x8000(r3) #x
    stfs f1, -0x8000(r3) #y
    stw r0, -0x8000(r3)  #z
    stw r4, -0x8000(r3)  #colour
#vertex 1 = left x, bottom y
    stfs f0, -0x8000(r3) #x
    stfs f3, -0x8000(r3) #y
    stw r0, -0x8000(r3)  #z
    stw r4, -0x8000(r3)  #colour
#vertex 2 = right x, bottom y
    stfs f2, -0x8000(r3) #x
    stfs f3, -0x8000(r3) #y
    stw r0, -0x8000(r3)  #z
    stw r4, -0x8000(r3)  #colour
#vertex 3 = right x, top y
    stfs f2, -0x8000(r3) #x
    stfs f1, -0x8000(r3) #y
    stw r0, -0x8000(r3)  #z
    stw r4, -0x8000(r3)  #colour
#vertex 4 = left x, top y #repeated since draws end to end of positions specified
    stfs f0, -0x8000(r3) #x
    stfs f1, -0x8000(r3) #y
    stw r0, -0x8000(r3)  #z
    stw r4, -0x8000(r3)  #colour
    addi r3, r1, 0x60
}
This code is hooked into the ledgegrab logic and draws your current ledgegrabbox to the screen. The result of it is shown here:


Since its currently hooked into the actual game logic, it will only ever draw when unpaused, but its a good basic display for if you want some debug information to the screen.

This is the basis for my debugmode rework im currently in the process of writing. I'm planning to hopefully expand this writeup as i learn better how graphics work in this game/on the wii as a whole (its the same system on most things e.g. melee uses the exact same engine and i was able to copy paste (with a lot of work) the hurtbox bubble drawing logic over to pm using it.)

Hopefully this is all sensical, sortof wrote it all out without proof reading much..

See ya
 
Last edited:
Top Bottom