F3DEX3
|
Modded GBI for use with F3DEX3 custom microcode. More...
#include "ultra64/mbi.h"
#include "ultra64/sptask.h"
Go to the source code of this file.
Data Structures | |
struct | Vtx_t |
struct | Vtx_tn |
union | Vtx |
struct | Tri |
struct | Vp_t |
struct | Light_t |
struct | PointLight_t |
struct | Ambient_t |
struct | Gdma |
struct | Gdma2 |
struct | Gmovewd |
struct | Gmovemem |
struct | Gtri |
struct | Gsetimg |
struct | TexRect |
struct | Gwords |
union | Gfx |
Macros | |
#define | G_MTX_MODEL 0x00 |
Specifies whether the matrix operation will be performed on the model or the view*projection matrix. | |
#define | G_MTX_MODELVIEW G_MTX_MODEL |
Equivalent to G_MTX_MODEL, for backwards compatibility. The view matrix used to be put in the same stack as the model matrix, whereas now it should be multiplied with the projection matrix. In SM64, this is called "mat stack fix"; in OoT, the vanilla game already does this. | |
#define | G_MTX_VIEWPROJECTION 0x04 |
Specifies whether the matrix operation will be performed on the model or the view*projection matrix. | |
#define | G_MTX_PROJECTION G_MTX_VIEWPROJECTION |
Equivalent to G_MTX_VIEWPROJECTION,. | |
#define | G_MTX_MUL 0x00 |
Multiplies the incoming matrix into the top of the matrix stack. | |
#define | G_MTX_LOAD 0x02 |
Replaces the top of the matrix stack with the incoming matrix. | |
#define | G_MTX_NOPUSH 0x00 |
Do not push the top of the matrix stack to DRAM prior to matrix operations. | |
#define | G_MTX_PUSH 0x01 |
Push the top of the matrix stack to DRAM prior to matrix operations. This is not supported for G_MTX_VIEWPROJECTION, only G_MTX_MODEL. | |
#define | G_MWO_POINT_RGBA 0x10 |
changes the color of the vertex. The val parameter is interpreted as 4 bytes: red (high byte), green, blue, and alpha (low byte). | |
#define | G_MWO_POINT_ST 0x14 |
changes the S and T values (texture coordinates of the vertex). The high 16 bits of val specify the S coordinate, and the low 16 bits specify the T coordinate. Each coordinate is an S10.5 number. | |
#define | G_MWO_POINT_XYSCREEN 0x18 |
change the screen coordinates of the vertex. The high 16 bits of val specify the X coordinate and the low 16 bits specify the Y coordinate. Both coordinates are S13.2 numbers with 0,0 being the upper-left of the screen, positive X going right, and positive Y going down. | |
#define | G_MWO_POINT_ZSCREEN 0x1C |
changes the screen Z coordinate of the vertex. The entire 32-bit val is taken as the new screen Z value. It is a 16.16 number in the range 0x00000000 to 0x03ff0000. | |
#define | G_MAXZ Error_please_update_viewport_Z_and_Y_see_GBI |
#define | G_NEW_MAXZ 0x7FFF |
#define | LIGHT_TYPE_DIR 0 |
#define | LIGHT_TYPE_POINT(kc) kc |
#define | gSPMatrix(pkt, m, p) gDma2p((pkt),G_MTX, (m), sizeof(Mtx), (p) ^ G_MTX_PUSH ^ G_MTX_LOAD, 0) |
macro which inserts a matrix operation at the end display list. | |
#define | gsSPMatrix(m, p) gsDma2p( G_MTX, (m), sizeof(Mtx), (p) ^ G_MTX_PUSH ^ G_MTX_LOAD, 0) |
macro which inserts a matrix operation in a static display list. | |
#define | gSPPopMatrixN(pkt, mtx, num) gDma2p((pkt), G_POPMTX, (num) * 64, 64, (mtx) + G_MV_MMTX, 0) |
macro which pops multiple matrices from a matrix stack. | |
#define | gsSPPopMatrixN(mtx, num) gsDma2p( G_POPMTX, (num) * 64, 64, (mtx) + G_MV_MMTX, 0) |
macro which pops multiple matrices from a matrix stack. | |
#define | gSPPopMatrix(pkt, mtx) gSPPopMatrixN((pkt), (mtx), 1) |
macro which pops one matrix from a matrix stack in a static display list. | |
#define | gsSPPopMatrix(mtx) gsSPPopMatrixN( (mtx), 1) |
macro which pops one matrix from a matrix stack in a static display list. | |
#define | gSPVertex(pkt, v, n, v0) |
macro which loads an internal vertex buffer in the RSP with points that are used by gSP1Triangle macros to generate polygons at the end display list. | |
#define | gsSPVertex(v, n, v0) |
macro which loads an internal vertex buffer in the RSP with points that are used by gSP1Triangle macros to generate polygons in a static display list. | |
#define | gSPDisplayListHint(pkt, dl, count) _gSPDisplayListRaw(pkt, dl, _DLHINTVALUE(count)) |
#define | gsSPDisplayListHint( dl, count) _gsSPDisplayListRaw( dl, _DLHINTVALUE(count)) |
#define | gSPBranchListHint(pkt, dl, count) _gSPBranchListRaw( pkt, dl, _DLHINTVALUE(count)) |
#define | gsSPBranchListHint( dl, count) _gsSPBranchListRaw( dl, _DLHINTVALUE(count)) |
#define | gSPEndDisplayListHint(pkt, count) _gSPEndDisplayListRaw( pkt, _DLHINTVALUE(count)) |
#define | gsSPEndDisplayListHint( count) _gsSPEndDisplayListRaw( _DLHINTVALUE(count)) |
#define | gSPDisplayList(pkt, dl) _gSPDisplayListRaw(pkt, dl, 0) |
#define | gsSPDisplayList( dl) _gsSPDisplayListRaw( dl, 0) |
#define | gSPBranchList(pkt, dl) _gSPBranchListRaw( pkt, dl, 0) |
#define | gsSPBranchList( dl) _gsSPBranchListRaw( dl, 0) |
#define | gSPEndDisplayList(pkt) _gSPEndDisplayListRaw( pkt, 0) |
#define | gsSPEndDisplayList() _gsSPEndDisplayListRaw( 0) |
#define | gSPLoadUcodeEx(pkt, uc_start, uc_dstart, uc_dsize) |
#define | gsSPLoadUcodeEx(uc_start, uc_dstart, uc_dsize) |
#define | gSPDma_io(pkt, flag, dmem, dram, size) |
#define | gsSPDma_io(flag, dmem, dram, size) |
#define | gSPMemset(pkt, dram, value, size) |
#define | gsSPMemset(dram, value, size) |
#define | gSPFlush(pkt) g1Word(pkt, G_FLUSH, 0) |
#define | gsSPFlush() gs1Word( G_FLUSH, 0) |
#define | gSP1Triangle(pkt, v0, v1, v2, flag) g1Word(pkt, G_TRI1, __gsSP1Triangle_w1f(v0, v1, v2, flag)) |
#define | gsSP1Triangle(v0, v1, v2, flag) gs1Word(G_TRI1, __gsSP1Triangle_w1f(v0, v1, v2, flag)) |
#define | gSP1Quadrangle(pkt, v0, v1, v2, v3, flag) |
#define | gsSP1Quadrangle(v0, v1, v2, v3, flag) |
#define | gSP2Triangles(pkt, v00, v01, v02, flag0, v10, v11, v12, flag1) |
#define | gsSP2Triangles(v00, v01, v02, flag0, v10, v11, v12, flag1) |
#define | G_SNAKE_RIGHT 0 |
#define | G_SNAKE_LEFT 1 |
#define | G_SNAKE_LAST 0x40 |
#define | gSPTriSnake(pkt, i1, i2, i3, i4, i4d, i5, i5d, i6, i6d, i7, i7d) |
#define | gsSPTriSnake(i1, i2, i3, i4, i4d, i5, i5d, i6, i6d, i7, i7d) |
#define | gSPContinueSnake(pkt, i0, i0d, i1, i1d, i2, i2d, i3, i3d, i4, i4d, i5, i5d, i6, i6d, i7, i7d) |
#define | gsSPContinueSnake(i0, i0d, i1, i1d, i2, i2d, i3, i3d, i4, i4d, i5, i5d, i6, i6d, i7, i7d) |
#define | gSPTriStrip(pkt, v1, v2, v3, v4, v5, v6, v7) |
#define | gsSPTriStrip(v1, v2, v3, v4, v5, v6, v7) |
#define | gSPTriFan(pkt, v1, v2, v3, v4, v5, v6, v7) |
#define | gsSPTriFan(v1, v2, v3, v4, v5, v6, v7) |
#define | gSPClipRatio(pkt, r) gSPNoOp(pkt) |
Clipping Macros. | |
#define | gsSPClipRatio(r) gsSPNoOp() |
Clipping Macros. | |
#define | gSPForceMatrix(pkt, mptr) gSPNoOp(pkt) |
Load new MVP matrix directly. | |
#define | gsSPForceMatrix(mptr) gsSPNoOp() |
Load new MVP matrix directly. | |
#define | gSPAmbOcclusionAmb(pkt, amb) gMoveHalfwd(pkt, G_MW_FX, G_MWO_AO_AMBIENT, amb) |
#define | gsSPAmbOcclusionAmb(amb) gsMoveHalfwd( G_MW_FX, G_MWO_AO_AMBIENT, amb) |
#define | gSPAmbOcclusionDir(pkt, dir) gMoveHalfwd(pkt, G_MW_FX, G_MWO_AO_DIRECTIONAL, dir) |
#define | gsSPAmbOcclusionDir(dir) gsMoveHalfwd( G_MW_FX, G_MWO_AO_DIRECTIONAL, dir) |
#define | gSPAmbOcclusionPoint(pkt, point) gMoveHalfwd(pkt, G_MW_FX, G_MWO_AO_POINT, point) |
#define | gsSPAmbOcclusionPoint(point) gsMoveHalfwd( G_MW_FX, G_MWO_AO_POINT, point) |
#define | gSPFresnelScale(pkt, scale) gMoveHalfwd(pkt, G_MW_FX, G_MWO_FRESNEL_SCALE, scale) |
#define | gsSPFresnelScale(scale) gsMoveHalfwd(G_MW_FX, G_MWO_FRESNEL_SCALE, scale) |
#define | gSPFresnelOffset(pkt, offset) gMoveHalfwd(pkt, G_MW_FX, G_MWO_FRESNEL_OFFSET, offset) |
#define | gsSPFresnelOffset(offset) gsMoveHalfwd(G_MW_FX, G_MWO_FRESNEL_OFFSET, offset) |
#define | gSPFresnel(pkt, scale, offset) |
#define | gsSPFresnel(scale, offset) |
#define | gSPAttrOffsetST(pkt, s, t) |
#define | gsSPAttrOffsetST(s, t) |
#define | gSPAlphaCompareCull(pkt, mode, thresh) |
#define | gsSPAlphaCompareCull(mode, thresh) |
#define | gSPDontSkipTexLoadsAcross(pkt) gMoveWd(pkt, G_MW_FX, G_MWO_LAST_MAT_DL_ADDR, 0xFFFFFFFF) |
#define | gsSPDontSkipTexLoadsAcross() gsMoveWd(G_MW_FX, G_MWO_LAST_MAT_DL_ADDR, 0xFFFFFFFF) |
#define | gSPModifyVertex(pkt, vtx, where, val) |
You can use this macro to modify certain sections of a vertex after it has been sent to the RSP (by the gSPVertex macro). | |
#define | gsSPModifyVertex(vtx, where, val) |
You can use this macro to modify certain sections of a vertex after it has been sent to the RSP (by the gSPVertex macro). | |
#define | gSPCullDisplayList(pkt, vstart, vend) |
#define | gsSPCullDisplayList(vstart, vend) |
#define | gSPBranchLessZraw(pkt, dl, vtx, zval) |
#define | gsSPBranchLessZraw(dl, vtx, zval) |
#define | ENABLE_POINT_LIGHTS (0x8000 >> 4) |
#define | NUMLIGHTS_0 0 |
#define | gSPNumLights(pkt, n) gMoveWd(pkt, G_MW_NUMLIGHT, G_MWO_NUMLIGHT, NUML(n)) |
#define | gsSPNumLights(n) gsMoveWd( G_MW_NUMLIGHT, G_MWO_NUMLIGHT, NUML(n)) |
#define | LIGHT_1 1 |
#define | gSPLight(pkt, l, n) gDma2p((pkt), G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, _LIGHT_TO_OFFSET(n)) |
#define | gsSPLight(l, n) gsDma2p( G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, _LIGHT_TO_OFFSET(n)) |
#define | gSPAmbient(pkt, l, n) gDma2p((pkt), G_MOVEMEM, (l), sizeof(Ambient), G_MV_LIGHT, _LIGHT_TO_OFFSET(n)) |
#define | gsSPAmbient(l, n) gsDma2p( G_MOVEMEM, (l), sizeof(Ambient), G_MV_LIGHT, _LIGHT_TO_OFFSET(n)) |
#define | gSPLightColor(pkt, n, col) |
#define | gsSPLightColor(n, col) |
#define | gSPSetLights(pkt, n, name) |
#define | gsSPSetLights(n, name) |
#define | gSPCameraWorld(pkt, cam) gDma2p((pkt), G_MOVEMEM, (cam), sizeof(PlainVtx), G_MV_LIGHT, 0) |
#define | gsSPCameraWorld(cam) gsDma2p( G_MOVEMEM, (cam), sizeof(PlainVtx), G_MV_LIGHT, 0) |
#define | gSPLookAt(pkt, la) gDma2p((pkt), G_MOVEMEM, (la), sizeof(LookAt), G_MV_LIGHT, 8) |
#define | gsSPLookAt(la) gsDma2p( G_MOVEMEM, (la), sizeof(LookAt), G_MV_LIGHT, 8) |
#define | gSPLookAtX(pkt, l) gSPLookAt(pkt, l) |
#define | gsSPLookAtX(l) gsSPLookAt(l) |
#define | gSPLookAtY(pkt, l) gSPNoOp(pkt) |
#define | gsSPLookAtY(l) gsSPNoOp() |
#define | gSPOcclusionPlane(pkt, o) |
#define | gsSPOcclusionPlane(o) |
#define | gSPFogFactor(pkt, fm, fo) |
#define | gsSPFogFactor(fm, fo) |
#define | gSPTexture(pkt, s, t, level, tile, on) |
#define | gsSPTexture(s, t, level, tile, on) |
#define | gSPTextureL(pkt, s, t, level, bowtie, tile, on) gSPTexture(pkt, s, t, level, tile, on) |
#define | gsSPTextureL(s, t, level, bowtie, tile, on) gsSPTexture(s, t, level, tile, on) |
#define | gSPGeometryMode(pkt, c, s) |
#define | gsSPGeometryMode(c, s) |
#define | gDPPipelineMode(pkt, mode) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_PIPELINE, 1, mode) |
#define | gsDPPipelineMode(mode) gsSPSetOtherMode( G_SETOTHERMODE_H, G_MDSFT_PIPELINE, 1, mode) |
#define | gDPSetBlendMask(pkt, mask) gSPNoOp(pkt) |
#define | gsDPSetBlendMask(mask) gsSPNoOp() |
#define | gDPSetCombineMode(pkt, a, b) gDPSetCombineLERP(pkt, a, b) |
#define | gsDPSetCombineMode(a, b) gsDPSetCombineLERP( a, b) |
#define | gSPLightToRDP(pkt, light, alpha, word0) |
#define | gsSPLightToRDP(light, alpha, word0) |
#define | gDPSetOtherMode(pkt, mode0, mode1) |
#define | gsDPSetOtherMode(mode0, mode1) |
#define | gDPLoadMultiBlockS(pkt, timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) |
#define | gDPLoadMultiBlock(pkt, timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) |
#define | gsDPLoadMultiBlock(timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) |
#define | gsDPLoadMultiBlockS(timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) |
#define | gDPLoadMultiBlock_4b(pkt, timg, tmem, rtile, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) |
#define | gDPLoadMultiBlock_4bS(pkt, timg, tmem, rtile, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) |
#define | gsDPLoadMultiBlock_4b(timg, tmem, rtile, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) |
#define | gsDPLoadMultiBlock_4bS(timg, tmem, rtile, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) |
#define | gDPLoadMultiTile(pkt, timg, tmem, rtile, fmt, siz, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, shifts, shiftt) |
#define | gsDPLoadMultiTile(timg, tmem, rtile, fmt, siz, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, shifts, shiftt) |
#define | gDPLoadMultiTile_4b(pkt, timg, tmem, rtile, fmt, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, shifts, shiftt) |
#define | gsDPLoadMultiTile_4b(timg, tmem, rtile, fmt, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, shifts, shiftt) |
#define | gDPLoadTLUT_pal16(pkt, pal, dram) |
#define | gsDPLoadTLUT_pal16(pal, dram) |
#define | gDPLoadTLUT_pal256(pkt, dram) |
#define | gsDPLoadTLUT_pal256(dram) |
#define | gsDPTextureRectangle(xl, yl, xh, yh, tile, s, t, dsdx, dtdy) |
#define | gDPTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy) |
Typedefs | |
typedef long int | Mtx_t[4][4] |
Modded GBI for use with F3DEX3 custom microcode.
#define ENABLE_POINT_LIGHTS (0x8000 >> 4) |
OR this flag into n in SPNumLights or SPSetLights* to indicate that one or more of the lights are point lights. Example: gSPSetLights(POLY_OPA_DISP++, numLights | ENABLE_POINT_LIGHTS, *lights);
#define G_MAXZ Error_please_update_viewport_Z_and_Y_see_GBI |
There have been two breaking changes made to the F3DEX3 viewport relative to F3DEX2 and all previous F3D microcodes.
The reason for these changes is as follows. Apparently, SGI initially intended the actual range of Z values in RDP triangle commands to be 0x03FF (integer, plus 16 fractional bits). So this was set up in the GBI and games were initially made this way. The actual range is 0x7FFF (plus 16 frac), at least for RCP HW V2 (retail). So, instead of updating the value in the GBI, SGI just added a scale up by 0x20 at the very end of the triangle processing when writing out the Z coefficients to the RDP. Because of the way this writing was implemented, there did not appear to be any performance penalty to this scaling, so it just worked.
F3DEX3 moves this scale up to the viewport affine transformation step at the end of the vertex processing, which allows an extra 5 bits of Z precision to be retained during some intermediate calculations for the attributes in the triangle write. In EX2, precision is sometimes lost in these calculations, leading to Z fighting. In addition, F3DEX3 optimizes the Z writing, saving a few cycles per written tri.
As far as the Y scale being negated, F3DEX2 did this at the cost of 3 instructions, but doing this on the CPU when creating the viewport is effectively free.
Both of these changes were made together–and this macro was changed to an error instead of just being changed to the new value–in order to maximize the chances the developer actually changes their viewport. Some game codebases might have the value derived from G_MAXZ hardcoded, so they might miss this change and start getting weird Z buffer issues. But they won't miss their game being upside down.
#define G_MTX_LOAD 0x02 |
Replaces the top of the matrix stack with the incoming matrix.
#define G_MTX_MUL 0x00 |
Multiplies the incoming matrix into the top of the matrix stack.
#define G_MTX_NOPUSH 0x00 |
Do not push the top of the matrix stack to DRAM prior to matrix operations.
#define G_MTX_PROJECTION G_MTX_VIEWPROJECTION |
Equivalent to G_MTX_VIEWPROJECTION,.
#define G_MTX_PUSH 0x01 |
Push the top of the matrix stack to DRAM prior to matrix operations. This is not supported for G_MTX_VIEWPROJECTION, only G_MTX_MODEL.
#define G_MWO_POINT_XYSCREEN 0x18 |
change the screen coordinates of the vertex. The high 16 bits of val specify the X coordinate and the low 16 bits specify the Y coordinate. Both coordinates are S13.2 numbers with 0,0 being the upper-left of the screen, positive X going right, and positive Y going down.
#define G_MWO_POINT_ZSCREEN 0x1C |
changes the screen Z coordinate of the vertex. The entire 32-bit val is taken as the new screen Z value. It is a 16.16 number in the range 0x00000000 to 0x03ff0000.
#define G_NEW_MAXZ 0x7FFF |
New max Z value for viewport.
#define G_SNAKE_LAST 0x40 |
Logical-OR this into a triangle index to mark it as the last triangle of the snake. In other words, this gets OR'd into the last valid index, not the first invalid index.
#define G_SNAKE_LEFT 1 |
Make the triangle snake turn left before drawing this triangle. In other words, build the new triangle off the newest and oldest vertices of the last triangle.
#define G_SNAKE_RIGHT 0 |
Make the triangle snake turn right before drawing this triangle. In other words, build the new triangle off the newest and middle-age vertices of the last triangle.
#define gDPLoadMultiBlock | ( | pkt, | |
timg, | |||
tmem, | |||
rtile, | |||
fmt, | |||
siz, | |||
width, | |||
height, | |||
pal, | |||
cms, | |||
cmt, | |||
masks, | |||
maskt, | |||
shifts, | |||
shiftt ) |
allows tmem address and render tile to be specified
#define gDPLoadMultiBlock_4b | ( | pkt, | |
timg, | |||
tmem, | |||
rtile, | |||
fmt, | |||
width, | |||
height, | |||
pal, | |||
cms, | |||
cmt, | |||
masks, | |||
maskt, | |||
shifts, | |||
shiftt ) |
4-bit load block. Useful when loading multiple tiles
#define gDPLoadMultiBlock_4bS | ( | pkt, | |
timg, | |||
tmem, | |||
rtile, | |||
fmt, | |||
width, | |||
height, | |||
pal, | |||
cms, | |||
cmt, | |||
masks, | |||
maskt, | |||
shifts, | |||
shiftt ) |
4-bit load block. Allows tmem and render tile to be specified. Useful when loading multiple tiles. The S means odd lines are already word swapped.
#define gDPLoadMultiBlockS | ( | pkt, | |
timg, | |||
tmem, | |||
rtile, | |||
fmt, | |||
siz, | |||
width, | |||
height, | |||
pal, | |||
cms, | |||
cmt, | |||
masks, | |||
maskt, | |||
shifts, | |||
shiftt ) |
Allow tmem address and render tile to be specified. The S at the end means odd lines are already word Swapped
#define gDPLoadMultiTile | ( | pkt, | |
timg, | |||
tmem, | |||
rtile, | |||
fmt, | |||
siz, | |||
width, | |||
height, | |||
uls, | |||
ult, | |||
lrs, | |||
lrt, | |||
pal, | |||
cms, | |||
cmt, | |||
masks, | |||
maskt, | |||
shifts, | |||
shiftt ) |
Load texture tile. Allows tmem address and render tile to be specified. Useful for loading multiple tiles.
#define gDPLoadMultiTile_4b | ( | pkt, | |
timg, | |||
tmem, | |||
rtile, | |||
fmt, | |||
width, | |||
height, | |||
uls, | |||
ult, | |||
lrs, | |||
lrt, | |||
pal, | |||
cms, | |||
cmt, | |||
masks, | |||
maskt, | |||
shifts, | |||
shiftt ) |
Load texture tile. Allows tmem address and render tile to be specified. Useful for loading multiple tiles.
#define gDPLoadTLUT_pal16 | ( | pkt, | |
pal, | |||
dram ) |
Load a 16-entry palette (for 4-bit CI textures) Assumes a 16 entry tlut is being loaded, palette # is 0-15 With NO_SYNCS_IN_TEXTURE_LOADS: assumes that palette 0 is for multitexture texture 0 and palette 1 is for texture 1 (uses load tiles 5 and 4)
#define gDPLoadTLUT_pal256 | ( | pkt, | |
dram ) |
Load a 256-entry palette (for 8-bit CI textures) Assumes a 256 entry tlut is being loaded, palette # is not used
#define gDPPipelineMode | ( | pkt, | |
mode ) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_PIPELINE, 1, mode) |
RDP setothermode register commands - register shadowed in RSP
#define gDPSetBlendMask | ( | pkt, | |
mask ) gSPNoOp(pkt) |
'blendmask' is not supported anymore. The bits are reserved for future use. Fri May 26 13:45:55 PDT 1995
#define gDPSetCombineMode | ( | pkt, | |
a, | |||
b ) gDPSetCombineLERP(pkt, a, b) |
SetCombineMode macros are NOT redunant. It allow the C preprocessor to substitute single parameter which includes commas in the token and rescan for higher parameter count macro substitution.
eg. gsDPSetCombineMode(G_CC_MODULATE, G_CC_MODULATE) turns into gsDPSetCombineLERP(TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0)
#define gDPSetOtherMode | ( | pkt, | |
mode0, | |||
mode1 ) |
gDPSetOtherMode (This is for expert user.)
This command makes all othermode parameters set. Do not use this command in the same DL with another g*SPSetOtherMode DLs.
[Usage] gDPSetOtherMode(pkt, modeA, modeB)
'modeA' is described all parameters of GroupA GBI command. 'modeB' is also described all parameters of GroupB GBI command.
GroupA: gDPPipelineMode, gDPSetCycleType, gSPSetTexturePersp, gDPSetTextureDetail, gDPSetTextureLOD, gDPSetTextureLUT, gDPSetTextureFilter, gDPSetTextureConvert, gDPSetCombineKey, gDPSetColorDither, gDPSetAlphaDither
GroupB: gDPSetAlphaCompare, gDPSetDepthSource, gDPSetRenderMode
Use 'OR' operation to get modeA and modeB.
modeA = G_PM_* | G_CYC_* | G_TP_* | G_TD_* | G_TL_* | G_TT_* | G_TF_* G_TC_* | G_CK_* | G_CD_* | G_AD_*;
modeB = G_AC_* | G_ZS_* | G_RM_* | G_RM_*2;
#define gDPTextureRectangle | ( | pkt, | |
xl, | |||
yl, | |||
xh, | |||
yh, | |||
tile, | |||
s, | |||
t, | |||
dsdx, | |||
dtdy ) |
Notice that textured rectangles are 128-bit commands, therefore gsDPTextureRectangle() should not be used in display lists under normal circumstances (use gsSPTextureRectangle()). That is also why there is no gDPTextureRectangle() macros.
#define gsDPLoadMultiBlock | ( | timg, | |
tmem, | |||
rtile, | |||
fmt, | |||
siz, | |||
width, | |||
height, | |||
pal, | |||
cms, | |||
cmt, | |||
masks, | |||
maskt, | |||
shifts, | |||
shiftt ) |
Allow tmem address and render_tile to be specified, useful when loading mutilple tiles at a time.
#define gsDPLoadMultiBlock_4b | ( | timg, | |
tmem, | |||
rtile, | |||
fmt, | |||
width, | |||
height, | |||
pal, | |||
cms, | |||
cmt, | |||
masks, | |||
maskt, | |||
shifts, | |||
shiftt ) |
4-bit load block. Allows tmem address and render tile to be specified. Useful when loading multiple tiles.
#define gsDPLoadMultiBlock_4bS | ( | timg, | |
tmem, | |||
rtile, | |||
fmt, | |||
width, | |||
height, | |||
pal, | |||
cms, | |||
cmt, | |||
masks, | |||
maskt, | |||
shifts, | |||
shiftt ) |
4-bit load block. Allows tmem address and render tile to be specified. Useful when loading multiple tiles. S means odd lines are already swapped.
#define gsDPLoadMultiBlockS | ( | timg, | |
tmem, | |||
rtile, | |||
fmt, | |||
siz, | |||
width, | |||
height, | |||
pal, | |||
cms, | |||
cmt, | |||
masks, | |||
maskt, | |||
shifts, | |||
shiftt ) |
Allows tmem and render tile to be specified. Useful when loading several tiles at a time.
Here is the static form of the pre-swapped texture block loading See gDPLoadTextureBlockS() for reference. Basically, just don't calculate DxT, use 0
#define gsDPLoadMultiTile | ( | timg, | |
tmem, | |||
rtile, | |||
fmt, | |||
siz, | |||
width, | |||
height, | |||
uls, | |||
ult, | |||
lrs, | |||
lrt, | |||
pal, | |||
cms, | |||
cmt, | |||
masks, | |||
maskt, | |||
shifts, | |||
shiftt ) |
Load texture tile. Allows tmem address and render tile to be specified. Useful for loading multiple tiles.
#define gsDPLoadMultiTile_4b | ( | timg, | |
tmem, | |||
rtile, | |||
fmt, | |||
width, | |||
height, | |||
uls, | |||
ult, | |||
lrs, | |||
lrt, | |||
pal, | |||
cms, | |||
cmt, | |||
masks, | |||
maskt, | |||
shifts, | |||
shiftt ) |
Load texture tile. Allows tmem address and render tile to be specified. Useful for loading multiple tiles.
#define gsDPLoadTLUT_pal16 | ( | pal, | |
dram ) |
Load a 16-entry palette (for 4-bit CI textures) Assumes a 16 entry tlut is being loaded, palette # is 0-15 With NO_SYNCS_IN_TEXTURE_LOADS: assumes that palette 0 is for multitexture texture 0 and palette 1 is for texture 1 (uses load tiles 5 and 4)
#define gsDPLoadTLUT_pal256 | ( | dram | ) |
Load a 256-entry palette (for 8-bit CI textures) Assumes a 256 entry tlut is being loaded, palette # is not used
#define gsDPPipelineMode | ( | mode | ) | gsSPSetOtherMode( G_SETOTHERMODE_H, G_MDSFT_PIPELINE, 1, mode) |
RDP setothermode register commands - register shadowed in RSP
#define gsDPSetBlendMask | ( | mask | ) | gsSPNoOp() |
'blendmask' is not supported anymore. The bits are reserved for future use. Fri May 26 13:45:55 PDT 1995
#define gsDPSetCombineMode | ( | a, | |
b ) gsDPSetCombineLERP( a, b) |
SetCombineMode macros are NOT redunant. It allow the C preprocessor to substitute single parameter which includes commas in the token and rescan for higher parameter count macro substitution.
eg. gsDPSetCombineMode(G_CC_MODULATE, G_CC_MODULATE) turns into gsDPSetCombineLERP(TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0)
#define gsDPSetOtherMode | ( | mode0, | |
mode1 ) |
gDPSetOtherMode (This is for expert user.)
This command makes all othermode parameters set. Do not use this command in the same DL with another g*SPSetOtherMode DLs.
[Usage] gDPSetOtherMode(pkt, modeA, modeB)
'modeA' is described all parameters of GroupA GBI command. 'modeB' is also described all parameters of GroupB GBI command.
GroupA: gDPPipelineMode, gDPSetCycleType, gSPSetTexturePersp, gDPSetTextureDetail, gDPSetTextureLOD, gDPSetTextureLUT, gDPSetTextureFilter, gDPSetTextureConvert, gDPSetCombineKey, gDPSetColorDither, gDPSetAlphaDither
GroupB: gDPSetAlphaCompare, gDPSetDepthSource, gDPSetRenderMode
Use 'OR' operation to get modeA and modeB.
modeA = G_PM_* | G_CYC_* | G_TP_* | G_TD_* | G_TL_* | G_TT_* | G_TF_* G_TC_* | G_CK_* | G_CD_* | G_AD_*;
modeB = G_AC_* | G_ZS_* | G_RM_* | G_RM_*2;
#define gsDPTextureRectangle | ( | xl, | |
yl, | |||
xh, | |||
yh, | |||
tile, | |||
s, | |||
t, | |||
dsdx, | |||
dtdy ) |
Notice that textured rectangles are 128-bit commands, therefore gsDPTextureRectangle() should not be used in display lists under normal circumstances (use gsSPTextureRectangle()). That is also why there is no gDPTextureRectangle() macros.
#define gSP1Quadrangle | ( | pkt, | |
v0, | |||
v1, | |||
v2, | |||
v3, | |||
flag ) |
#define gSP1Triangle | ( | pkt, | |
v0, | |||
v1, | |||
v2, | |||
flag ) g1Word(pkt, G_TRI1, __gsSP1Triangle_w1f(v0, v1, v2, flag)) |
1 Triangle
#define gSP2Triangles | ( | pkt, | |
v00, | |||
v01, | |||
v02, | |||
flag0, | |||
v10, | |||
v11, | |||
v12, | |||
flag1 ) |
#define gSPAlphaCompareCull | ( | pkt, | |
mode, | |||
thresh ) |
Alpha compare culling. This was originally created as an optimization for cel shading, but it can also be used for other scenarios. In particular, it can be used with fog to cull tris which are entirely in the fog. This could also be accomplished with far clipping, but far clipping is removed in F3DEX3.
If mode == G_ALPHA_COMPARE_CULL_DISABLE, tris are drawn normally.
Otherwise:
For the light pass:
For the dark pass:
The idea is to cull tris early on the RSP which won't have any of their fragments drawn on the RDP, to save RDP time and memory bandwidth.
#define gSPAmbient | ( | pkt, | |
l, | |||
n ) gDma2p((pkt), G_MOVEMEM, (l), sizeof(Ambient), G_MV_LIGHT, _LIGHT_TO_OFFSET(n)) |
l should point to an Ambient struct. n should be an integer 1-10 to load lights 0-9.
#define gSPAmbOcclusionAmb | ( | pkt, | |
amb ) gMoveHalfwd(pkt, G_MW_FX, G_MWO_AO_AMBIENT, amb) |
Ambient occlusion Enabled with the G_AMBOCCLUSION bit in geometry mode. Each of these factors sets how much ambient occlusion affects lights of the given type (ambient, directional, point). They are u16s. You can set each independently or two adjacent values with one moveword. A two-command macro is also provided to set all three values.
When building the model, you must encode the amount of ambient occlusion at each vertex–effectively the shadow map for the model–in vertex alpha, where 00 means darkest and FF means lightest. Then, the factors set with the SPAmbOcclusion command determine how much the vertex alpha values affect the light intensity. For example, if the ambient factor is set to 0x8000, this means that in the darkest parts of the model, the ambient light intensity will be reduced by 50%, and in the lightest parts of the model, the ambient light intensity won't be reduced at all.
The default is: amb = 0xFFFF (ambient light fully affected by vertex alpha) dir = 0xA000 (directional lights 62% affected by vertex alpha) point = 0 (point lights not at all affected by vertex alpha)
Two reasons to use ambient occlusion rather than darkening the vertex colors:
Two reasons to use these factors to modify ambient occlusion rather than just manually scaling and offsetting all the vertex alpha values:
#define gSPAmbOcclusionDir | ( | pkt, | |
dir ) gMoveHalfwd(pkt, G_MW_FX, G_MWO_AO_DIRECTIONAL, dir) |
Ambient occlusion Enabled with the G_AMBOCCLUSION bit in geometry mode. Each of these factors sets how much ambient occlusion affects lights of the given type (ambient, directional, point). They are u16s. You can set each independently or two adjacent values with one moveword. A two-command macro is also provided to set all three values.
When building the model, you must encode the amount of ambient occlusion at each vertex–effectively the shadow map for the model–in vertex alpha, where 00 means darkest and FF means lightest. Then, the factors set with the SPAmbOcclusion command determine how much the vertex alpha values affect the light intensity. For example, if the ambient factor is set to 0x8000, this means that in the darkest parts of the model, the ambient light intensity will be reduced by 50%, and in the lightest parts of the model, the ambient light intensity won't be reduced at all.
The default is: amb = 0xFFFF (ambient light fully affected by vertex alpha) dir = 0xA000 (directional lights 62% affected by vertex alpha) point = 0 (point lights not at all affected by vertex alpha)
Two reasons to use ambient occlusion rather than darkening the vertex colors:
Two reasons to use these factors to modify ambient occlusion rather than just manually scaling and offsetting all the vertex alpha values:
#define gSPAmbOcclusionPoint | ( | pkt, | |
point ) gMoveHalfwd(pkt, G_MW_FX, G_MWO_AO_POINT, point) |
Ambient occlusion Enabled with the G_AMBOCCLUSION bit in geometry mode. Each of these factors sets how much ambient occlusion affects lights of the given type (ambient, directional, point). They are u16s. You can set each independently or two adjacent values with one moveword. A two-command macro is also provided to set all three values.
When building the model, you must encode the amount of ambient occlusion at each vertex–effectively the shadow map for the model–in vertex alpha, where 00 means darkest and FF means lightest. Then, the factors set with the SPAmbOcclusion command determine how much the vertex alpha values affect the light intensity. For example, if the ambient factor is set to 0x8000, this means that in the darkest parts of the model, the ambient light intensity will be reduced by 50%, and in the lightest parts of the model, the ambient light intensity won't be reduced at all.
The default is: amb = 0xFFFF (ambient light fully affected by vertex alpha) dir = 0xA000 (directional lights 62% affected by vertex alpha) point = 0 (point lights not at all affected by vertex alpha)
Two reasons to use ambient occlusion rather than darkening the vertex colors:
Two reasons to use these factors to modify ambient occlusion rather than just manually scaling and offsetting all the vertex alpha values:
#define gSPAttrOffsetST | ( | pkt, | |
s, | |||
t ) |
Attribute offsets These are added to ST values after vertices are loaded and transformed. The values are s16s. The addition is after the multiplication for ST scale in SPTexture. Whether it is enabled or disabled at a given time is determined by the G_ATTROFFSET_ST_ENABLE bit in the geometry mode. Normally you would use ST offsets for UV scrolling.
#define gSPBranchLessZraw | ( | pkt, | |
dl, | |||
vtx, | |||
zval ) |
gSPBranchLessZraw Branch DL if (vtx.z) less than or equal (raw zval).
dl = DL branch to vtx = Vertex zval = Raw value of screen depth
#define gSPBranchList | ( | pkt, | |
dl ) _gSPBranchListRaw( pkt, dl, 0) |
Normal control flow commands; same as gSPBranchListHint but with hint of 0
#define gSPBranchListHint | ( | pkt, | |
dl, | |||
count ) _gSPBranchListRaw( pkt, dl, _DLHINTVALUE(count)) |
Optimization for reduced memory traffic. In count, put the estimated number of DL commands in the target DL (the DL being called / jumped to, or the DL being returned to, starting from the next command to be executed) up to and including the next call / jump / return. Normally, for SPDisplayList, this is just the total number of commands in the target DL. The actual on-screen result will not change regardless of the value of count, but the performance will be best if count is correct, and potentially worse than not specifying count if it is wrong. Feature suggested by Kaze Emanuar
#define gSPCameraWorld | ( | pkt, | |
cam ) gDma2p((pkt), G_MOVEMEM, (cam), sizeof(PlainVtx), G_MV_LIGHT, 0) |
Camera world position for Fresnel and specular lighting. Set this whenever you set the VP matrix, viewport, etc. cam is the address of a PlainVtx struct.
#define gSPClipRatio | ( | pkt, | |
r ) gSPNoOp(pkt) |
Clipping Macros.
#define gSPContinueSnake | ( | pkt, | |
i0, | |||
i0d, | |||
i1, | |||
i1d, | |||
i2, | |||
i2d, | |||
i3, | |||
i3d, | |||
i4, | |||
i4d, | |||
i5, | |||
i5d, | |||
i6, | |||
i6d, | |||
i7, | |||
i7d ) |
Continue a triangle snake for up to 8 more triangles. This is actually not a display list command–there's no command byte. The data is just the next 8 bytes of the display list data, still being processed by the previous gSPTriSnake. Note that the microcode implementation does correctly handle the case when the snake continues past the end of the current data in the input buffer (which is a copy in DMEM of a chunk of the display list); the input buffer is reloaded like it would be for more commands. So the snake can be an unlimited length by continuing to append gSPContinueSnake commands.
#define gSPCullDisplayList | ( | pkt, | |
vstart, | |||
vend ) |
Cull the display list based on screen clip flags of range of loaded verts. Executes SPEndDisplayList if the convex hull formed by the specified range of already-loaded vertices is offscreen.
#define gSPDisplayList | ( | pkt, | |
dl ) _gSPDisplayListRaw(pkt, dl, 0) |
Normal control flow commands; same as gSPDisplayListHint but with hint of 0
#define gSPDisplayListHint | ( | pkt, | |
dl, | |||
count ) _gSPDisplayListRaw(pkt, dl, _DLHINTVALUE(count)) |
Optimization for reduced memory traffic. In count, put the estimated number of DL commands in the target DL (the DL being called / jumped to, or the DL being returned to, starting from the next command to be executed) up to and including the next call / jump / return. Normally, for SPDisplayList, this is just the total number of commands in the target DL. The actual on-screen result will not change regardless of the value of count, but the performance will be best if count is correct, and potentially worse than not specifying count if it is wrong. Feature suggested by Kaze Emanuar
#define gSPDma_io | ( | pkt, | |
flag, | |||
dmem, | |||
dram, | |||
size ) |
#define gSPDontSkipTexLoadsAcross | ( | pkt | ) | gMoveWd(pkt, G_MW_FX, G_MWO_LAST_MAT_DL_ADDR, 0xFFFFFFFF) |
F3DEX3 has a basic auto-batched rendering system. At a high level, if a material display list being run is the same as the last material, the texture loads are automatically skipped the second time as they should already be in TMEM.
This design generally works, but can break if you call a display list twice but in between change a segment mapping so that a referenced image inside is actually different the two times. In these cases, run the below command between the two calls (e.g. when you change the segment) and the microcode will not skip the second texture loads.
Internally, a material is defined to start with any set image command, and end on any of the following: call, branch, return, vertex, all tri commands, tex/fill rectangles, and successes on cull or branch w/z (which are usually preceded by vertex loads anyway). The physical address of the display list –not the address of the image–is stored when a material is started. If a material starts and its physical address is the same as the stored last start address, i.e. we're executing the same material display list as the last material, material cull mode is set. In this mode, load block, load tile, and load TLUT all are skipped. This mode is cleared when the material ends.
This design has the benefit that it works correctly even with complex materials, e.g. with two CI4 textures (four loads), whereas it would be difficult to implement tracking all these loads separately. Furthermore, a design based on tracking the image addresses could break if you loaded different tile sections of the same image in consecutive materials.
#define gSPEndDisplayList | ( | pkt | ) | _gSPEndDisplayListRaw( pkt, 0) |
Normal control flow commands; same as gSPEndDisplayListHint but with hint of 0
#define gSPEndDisplayListHint | ( | pkt, | |
count ) _gSPEndDisplayListRaw( pkt, _DLHINTVALUE(count)) |
Optimization for reduced memory traffic. In count, put the estimated number of DL commands in the target DL (the DL being called / jumped to, or the DL being returned to, starting from the next command to be executed) up to and including the next call / jump / return. Normally, for SPDisplayList, this is just the total number of commands in the target DL. The actual on-screen result will not change regardless of the value of count, but the performance will be best if count is correct, and potentially worse than not specifying count if it is wrong. Feature suggested by Kaze Emanuar
#define gSPFlush | ( | pkt | ) | g1Word(pkt, G_FLUSH, 0) |
Flush the internal DMEM buffer of RDP commands to the RDP FIFO in DRAM, causing the RDP to immediately begin executing any previous commands. Without SPFlush, the RDP may not begin executing any given command until up to 46 more RDP commands after that have been processed by the RSP (or the final end of the display list for the frame).
The primary use case is if your frame's display list begins with clearing the framebuffer and/or Z buffer, and then proceeds to things which take significant time on the RSP before emitting many RDP commands, such as matrix and lighting for drawing a character model. You should insert SPFlush after the first large buffer clear to cause the RDP to begin executing those long operations immediately while the RSP is continuing to work. If you are clearing both the framebuffer and Z buffer, you would usually only need one SPFlush after the first of these two DPFillRect commands.
#define gSPFogFactor | ( | pkt, | |
fm, | |||
fo ) |
FOG macros fm = z multiplier fo = z offset FOG FORMULA: alpha(fog) = (eyespace z) * fm + fo CLAMPED 0 to 255 note: (eyespace z) ranges -1 to 1
Alternate method of setting fog: min, max: range 0 to 1000: 0=nearplane, 1000=farplane min is where fog begins (usually less than max and often 0) max is where fog is thickest (usually 1000)
#define gSPForceMatrix | ( | pkt, | |
mptr ) gSPNoOp(pkt) |
Load new MVP matrix directly.
This is no longer supported as it was not used in production games.
#define gSPFresnel | ( | pkt, | |
scale, | |||
offset ) |
Fresnel - Feature suggested by thecozies Enabled with the G_FRESNEL bit in geometry mode. The dot product between a vertex normal and the vector from the vertex to the camera is computed. The offset and scale here convert this to a shade alpha value. This is useful for making surfaces fade between transparent when viewed straight-on and opaque when viewed at a large angle, or for applying a fake "outline" around the border of meshes.
If using Fresnel, you need to set the camera world position whenever you set the VP matrix, viewport, etc. See SPCameraWorld.
The RSP does: s16 dotProduct = dot(vertex normal, camera pos - vertex pos); dotProduct = abs(dotProduct); // 0 = points to side, 7FFF = points at or away s32 factor = ((scale * dotProduct) >> 15) + offset; s16 result = clamp(factor << 8, 0, 7FFF); color_or_alpha = result >> 7;
At dotMax, color_or_alpha = FF, result = 7F80, factor = 7F At dotMin, color_or_alpha = 00, result = 0, factor = 0 7F = ((scale * dotMax) >> 15) + offset 00 = ((scale * dotMin) >> 15) + offset Subtract: 7F = (scale * (dotMax - dotMin)) >> 15 3F8000 = scale * (dotMax - dotMin) scale = 3F8000 / (dotMax - dotMin) <– offset = -(((3F8000 / (dotMax - dotMin)) * dotMin) >> 15) offset = -((7F * dotMin) / (dotMax - dotMin)) <–
To convert in the opposite direction: ((7F - offset) << 15) / scale = dotMax ((00 - offset) << 15) / scale = dotMin
#define gSPFresnelOffset | ( | pkt, | |
offset ) gMoveHalfwd(pkt, G_MW_FX, G_MWO_FRESNEL_OFFSET, offset) |
Fresnel - Feature suggested by thecozies Enabled with the G_FRESNEL bit in geometry mode. The dot product between a vertex normal and the vector from the vertex to the camera is computed. The offset and scale here convert this to a shade alpha value. This is useful for making surfaces fade between transparent when viewed straight-on and opaque when viewed at a large angle, or for applying a fake "outline" around the border of meshes.
If using Fresnel, you need to set the camera world position whenever you set the VP matrix, viewport, etc. See SPCameraWorld.
The RSP does: s16 dotProduct = dot(vertex normal, camera pos - vertex pos); dotProduct = abs(dotProduct); // 0 = points to side, 7FFF = points at or away s32 factor = ((scale * dotProduct) >> 15) + offset; s16 result = clamp(factor << 8, 0, 7FFF); color_or_alpha = result >> 7;
At dotMax, color_or_alpha = FF, result = 7F80, factor = 7F At dotMin, color_or_alpha = 00, result = 0, factor = 0 7F = ((scale * dotMax) >> 15) + offset 00 = ((scale * dotMin) >> 15) + offset Subtract: 7F = (scale * (dotMax - dotMin)) >> 15 3F8000 = scale * (dotMax - dotMin) scale = 3F8000 / (dotMax - dotMin) <– offset = -(((3F8000 / (dotMax - dotMin)) * dotMin) >> 15) offset = -((7F * dotMin) / (dotMax - dotMin)) <–
To convert in the opposite direction: ((7F - offset) << 15) / scale = dotMax ((00 - offset) << 15) / scale = dotMin
#define gSPFresnelScale | ( | pkt, | |
scale ) gMoveHalfwd(pkt, G_MW_FX, G_MWO_FRESNEL_SCALE, scale) |
Fresnel - Feature suggested by thecozies Enabled with the G_FRESNEL bit in geometry mode. The dot product between a vertex normal and the vector from the vertex to the camera is computed. The offset and scale here convert this to a shade alpha value. This is useful for making surfaces fade between transparent when viewed straight-on and opaque when viewed at a large angle, or for applying a fake "outline" around the border of meshes.
If using Fresnel, you need to set the camera world position whenever you set the VP matrix, viewport, etc. See SPCameraWorld.
The RSP does: s16 dotProduct = dot(vertex normal, camera pos - vertex pos); dotProduct = abs(dotProduct); // 0 = points to side, 7FFF = points at or away s32 factor = ((scale * dotProduct) >> 15) + offset; s16 result = clamp(factor << 8, 0, 7FFF); color_or_alpha = result >> 7;
At dotMax, color_or_alpha = FF, result = 7F80, factor = 7F At dotMin, color_or_alpha = 00, result = 0, factor = 0 7F = ((scale * dotMax) >> 15) + offset 00 = ((scale * dotMin) >> 15) + offset Subtract: 7F = (scale * (dotMax - dotMin)) >> 15 3F8000 = scale * (dotMax - dotMin) scale = 3F8000 / (dotMax - dotMin) <– offset = -(((3F8000 / (dotMax - dotMin)) * dotMin) >> 15) offset = -((7F * dotMin) / (dotMax - dotMin)) <–
To convert in the opposite direction: ((7F - offset) << 15) / scale = dotMax ((00 - offset) << 15) / scale = dotMin
#define gSPGeometryMode | ( | pkt, | |
c, | |||
s ) |
One gSPGeometryMode(pkt,c,s) GBI is equal to these two GBIs.
gSPClearGeometryMode(pkt,c) gSPSetGeometryMode(pkt,s)
gSPLoadGeometryMode(pkt, word) sets GeometryMode directly.
#define gSPLight | ( | pkt, | |
l, | |||
n ) gDma2p((pkt), G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, _LIGHT_TO_OFFSET(n)) |
l should point to a Light struct. n should be an integer 1-9 to load lights 0-8. Can also load Ambient lights to lights 0-8 with this. However, if you have 9 directional / point lights, you must use SPAmbient to load light 9 (LIGHT_10) with an ambient light. (That is, the memory for light 9 (LIGHT_10) is only sizeof(Ambient), so if you load this with SPLight, it will overwrite other DMEM and corrupt unrelated things.) New code should not generally use SPLight, and instead use SPSetLights to set all lights in one memory transaction.
#define gSPLightColor | ( | pkt, | |
n, | |||
col ) |
gSPLightColor changes the color of a directional light without an additional DMA transfer. col is a 32 bit word where (col >> 24) & 0xFF is red, (col >> 16) & 0xFF is green, and (col >> 8) & 0xFF is blue. (col & 0xFF) is ignored and masked to zero. n should be an integer 1-10 to apply to light 0-9.
#define gSPLightToRDP | ( | pkt, | |
light, | |||
alpha, | |||
word0 ) |
Send the color of the specified light to one of the RDP's color registers. light is the index of a light in the RSP counting from the end, i.e. 0 is the ambient light, 1 is the last directional / point light, etc. The RGB color of the selected light is combined with the alpha specified in this command as word 1 of a RDP command, and word 0 is specified in this command. Specialized versions are provided below for prim color and fog color, because these are the two versions needed for cel shading, but any RDP color command could be specified this way.
#define gSPLoadUcodeEx | ( | pkt, | |
uc_start, | |||
uc_dstart, | |||
uc_dsize ) |
gSPLoadUcode RSP loads specified ucode.
uc_start = ucode text section start uc_dstart = ucode data section start
#define gSPLookAt | ( | pkt, | |
la ) gDma2p((pkt), G_MOVEMEM, (la), sizeof(LookAt), G_MV_LIGHT, 8) |
Reflection/Hiliting Macros. la is the address of a LookAt struct.
#define gSPLookAtX | ( | pkt, | |
l ) gSPLookAt(pkt, l) |
These versions are deprecated, please use g*SPLookAt. The two directions cannot be set independently anymore as they both fit within one memory word. (They could be set with moveword, but then the values would have to be within the command itself, not at a memory address.) This deprecated version has the X command set both (assuming l is the name / address of a LookAt struct) and has the Y command as a SP no-op.
#define gSPLookAtY | ( | pkt, | |
l ) gSPNoOp(pkt) |
These versions are deprecated, please use g*SPLookAt. The two directions cannot be set independently anymore as they both fit within one memory word. (They could be set with moveword, but then the values would have to be within the command itself, not at a memory address.) This deprecated version has the X command set both (assuming l is the name / address of a LookAt struct) and has the Y command as a SP no-op.
#define gSPMatrix | ( | pkt, | |
m, | |||
p ) gDma2p((pkt),G_MTX, (m), sizeof(Mtx), (p) ^ G_MTX_PUSH ^ G_MTX_LOAD, 0) |
macro which inserts a matrix operation at the end display list.
It inserts a matrix operation in the display list. The parameters allow you to select which matrix stack to use (projection or model view), whether to load or multiply, and whether or not to push the matrix stack. The following parameters are bitwise OR'ed together:
The legacy parameters G_MTX_MODELVIEW and G_MTX_PROJECTION are also supported, but in F3DEX3 you should always multiply the view matrix with the projection matrix as G_MTX_VIEWPROJECTION, and only put model matrices in the G_MTX_MODEL stack.
The format of the fixed-point matrices may seem a little awkward to the application programmer because it is optimized for the RSP geometry engine. This unusual format is hidden in the graphics utility libraries and not usually exposed to the application programmer, but in some cases (static matrix declarations or direct element manipulation) it is necessary to understand the format.
The integer and fractional components of the matrix elements are separated. The first 8 words (16 shorts) hold the 16-bit integer elements, the second 8 words (16 shorts) hold the 16-bit fractional elements. The fact that the Mtx type is declared as a long [4][4] array is slightly misleading. For example, to declare a static identity matrix, use code similar to this:
To force the translation elements of a matrix to be (10.5, 20.5, 30.5), use code similar to this:
Matrix multiplication in the RSP geometry engine is done using 32-bit integer arithmetic, in s15.16 format (16 integer, 16 fractional bits, in other words representing -32768.0 to 32767.999985 with a resolution of about 0.000015). A 32 x 32 bit multiply results in a 64-bit number. Only the middle 32 bits of this 64-bit result are kept for the new matrix, to preserve the s15.16 format.
A typical game object's transformation will have a scale around the range of 1/100, a rotation which is always values -1.0 to 1.0, and a translation around the range of 1000. When producing a final transformation matrix, you will typically compose (multiply) one scale, multiple rotations (for limbs), and finally one translation. Each matrix multiply on the RSP will lose precision, especially if the scale has been applied before the rotations.
Therefore, your game should usually maintain a matrix stack on the CPU in floating point, and once you have a final model matrix for each limb / object, convert it to fixed point and load it to the RSP. Occasional uses of G_MTX_MUL, such as multiplying view * projection or for HUD elements, are okay. Both SM64 and OoT already operate this way.
Your game generally should not use G_MTX_PUSH or SPPopMatrix*, even in a scene graph style engine like SM64.
If you have taken the advice above to just compute and upload final model matrices, there is no need to use push or pop–you'll always just do a single load before rendering any model. If you need to return to a previous transformation matrix, just upload that already-computed matrix again. Again, both SM64 and OoT already do this.
In F3DEX3, the code for G_MTX_PUSH and SPPopMatrix* is moved to overlay 3, meaning these operations will be slower on average than in F3DEX2.
m | is the pointer to the 4x4 fixed-point matrix (see note above about format) |
p | are the bit OR'd parameters to the matrix macro (G_MTX_MODEL, G_MTX_VIEWPROJECTION, G_MTX_MUL, G_MTX_LOAD, G_MTX_NOPUSH, G_MTX_PUSH) |
#define gSPMemset | ( | pkt, | |
dram, | |||
value, | |||
size ) |
Use RSP DMAs to set a region of memory to a repeated 16-bit value. This can clear the color framebuffer or Z-buffer faster than the RDP can in fill mode. SPMemset overwrites the DMEM vertex buffer, so vertices loaded before this command cannot be used after it (though this would not normally be done).
dram: Segmented or physical start address. Must be aligned to 16 bytes. value: 16-bit value to fill the memory with. e.g. 0 for color, 0xFFFC for Z. size: Size in bytes to fill, must be nonzero and a multiple of 16 bytes.
#define gSPModifyVertex | ( | pkt, | |
vtx, | |||
where, | |||
val ) |
You can use this macro to modify certain sections of a vertex after it has been sent to the RSP (by the gSPVertex macro).
This is an advanced macro. You need a good understanding of how vertices work in the RSP microcode before you use this macro (refer to gSPVertex).
You can use this macro to modify certain sections of a vertex after it has been sent to the RSP (by the gSPVertex macro). This is useful for vertices that are shared between two or more triangles that must have different properties when associated with one triangle versus the other triangle.
For example, you might have two adjacent triangles that both need smooth-shaded color, but one is smooth-shaded red-to-yellow and the other is smooth-shaded green-to-cyan. In this case, the vertex that is shared by both triangles is sent with red/yellow color by using the gSPVertex macro. The first triangle is drawn. Then, the gSPModifyVertex macro is used to change the color to green/cyan, and the second triangle is drawn.
The primary use of the gSPModifyVertex macro is to modify the texture coordinate of a vertex so that a vertex that is shared by two triangles with different textures and different texture coordinate spaces can contain the texture coordinate for the first texture and then be modified to contain the texture coordinate for the second texture.
It is faster to use the gSPModifyVertex macro than to send a new vertex macro with a different but similar vertex because no transformations or lighting are done to the vertex when you use the gSPModifyVertex macro.
The where argument specifies which part of the vertex is to be modified. It can hold one of the following values:
The S and T coordinates supplied in the gSPModifyVertex macro are never multiplied by the texture scale (from the gSPTexture macro), so you must pre-scale them before sending them. For example, if you want a texture scale of 1/2 (0x8000), make the S and T values sent with the gSPModifyVertex macro half the value of the equivalent values used with the gSPVertex macro.
To share a vertex between two triangles with different textures and texture coordinates, use this code:
vtx | specifies which of the RSP's vertices (0-55) to modify |
where | specifies which part of the vertex to modify (G_MWO_POINT_RGBA, G_MWO_POINT_ST, G_MWO_POINT_XYSCREEN or G_MWO_POINT_ZSCREEN) |
val | is the new value for the part of the vertex to be modified (a 32 bit integer number) |
#define gSPNumLights | ( | pkt, | |
n ) gMoveWd(pkt, G_MW_NUMLIGHT, G_MWO_NUMLIGHT, NUML(n)) |
Number of directional / point lights, in the range 0-9. There is also always one ambient light not counted in this number. See also ENABLE_POINT_LIGHTS.
#define gSPOcclusionPlane | ( | pkt, | |
o ) |
Set the occlusion plane. This is a quadrilateral in 3D space where all geometry behind it is culled. You should create occlusion plane candidates just behind walls and other large objects, and have your game engine pick the most optimal one every frame to send to the RSP.
Computing the coefficients for the occlusion plane is far too complicated to explain here. The reference implementation guOcclusionPlane
is provided separately.
o is the address of an OcclusionPlane struct
#define gSPPopMatrix | ( | pkt, | |
mtx ) gSPPopMatrixN((pkt), (mtx), 1) |
macro which pops one matrix from a matrix stack in a static display list.
This is just SPPopMatrixN with num=1:
It pops num
matrices from the stack.
mtx | is the flag field that identifies which matrix stack to pop:
|
num | is the number of matrices to pop |
#define gSPPopMatrixN | ( | pkt, | |
mtx, | |||
num ) gDma2p((pkt), G_POPMTX, (num) * 64, 64, (mtx) + G_MV_MMTX, 0) |
macro which pops multiple matrices from a matrix stack.
It pops num
matrices from the stack.
mtx | is the flag field that identifies which matrix stack to pop:
|
num | is the number of matrices to pop |
#define gSPSetLights | ( | pkt, | |
n, | |||
name ) |
Set all your scene's lights (directional/point + ambient) with one memory transaction. n is the number of directional / point lights, from 0 to 9. There is also always an ambient light. If there are point lights, set ENABLE_POINT_LIGHTS in n via logical or (i.e. set n to (numLights | ENABLE_POINT_LIGHTS)) name should be the NAME of a Lights struct (NOT A POINTER) filled in with all the lighting data. You can use the gdSPDef* macros to fill in the struct or just do it manually. Example: Lights2 myLights; // 2 dir/pos + 1 ambient gSPSetLights(POLY_OPA_DISP++, 2, myLights);
If you need to use a pointer, e.g. if the number of lights is variable at runtime: Light *lights = memory_allocate((numLights + 1) * sizeof(Light)); lights[0].p.pos = ...; lights[1].l.dir = ...; ... lights[numLights].l.col = ambient_color(); gSPSetLights(POLY_OPA_DISP++, ENABLE_POINT_LIGHTS | numLights, *lights); // <- NOTE DEREFERENCE
If you're wondering why this macro takes a name / dereference instead of a pointer, it's for backwards compatibility.
#define gSPTexture | ( | pkt, | |
s, | |||
t, | |||
level, | |||
tile, | |||
on ) |
#define gSPTextureL | ( | pkt, | |
s, | |||
t, | |||
level, | |||
bowtie, | |||
tile, | |||
on ) gSPTexture(pkt, s, t, level, tile, on) |
The bowtie value is a workaround for a bug in HW V1, and is not supported by F3DEX2, let alone F3DEX3.
#define gSPTriFan | ( | pkt, | |
v1, | |||
v2, | |||
v3, | |||
v4, | |||
v5, | |||
v6, | |||
v7 ) |
5 Triangles in fan arrangement. Draws the following tris: v3-v1-v2, v4-v1-v3, v5-v1-v4, v6-v1-v5, v7-v1-v6 Otherwise works the same as
#define gSPTriSnake | ( | pkt, | |
i1, | |||
i2, | |||
i3, | |||
i4, | |||
i4d, | |||
i5, | |||
i5d, | |||
i6, | |||
i6d, | |||
i7, | |||
i7d ) |
Triangle snake is F3DEX3's accelerated triangles command. It is a generalized form of a triangle strip or fan, which can represent any sequential chain of connected triangles by encoding which side of the current triangle the next triangle attaches to. This allows the chain of triangles to "snake" around and double back next to itself, unlike a triangle strip. For more information on the design, see Triangle Snake in the documentation.
The drawing algorithm is:
For example, after drawing the first triangle i3-i1-i2, if i4 is G_SNAKE_RIGHT, the snake turns right and draws i4-i3-i2: 3 –<– 4 /'\ '/ (winding order and / \ / first vertex for flat / \ / shading are marked) 1 -->– 2 Conversely, after the first triangle i3-i1-i2, if i4 is G_SNAKE_LEFT, the snake turns left and draws i4-i1-i3: 4 –<– 3 \' /'\ \ / \ \ / \ 1 -->– 2 If the snake turns in the same direction repeatedly, it will coil up, forming a triangle fan. If it slithers left and right alternately, this will form a triangle strip. Any combination of these is also possible. In particular, a useful shape is a triangle strip for a few tris, then a tri fan for a couple tris to "turn around", then another tri strip alongside the first, and so on. This shape can cover almost all tris of a typical surface with a single snake, except for tris which have two unconnected edges which can only be the first or last tris of the snake.
Logical-OR G_SNAKE_LAST into the last valid index of the snake. This index still needs a valid G_SNAKE_LEFT or G_SNAKE_RIGHT for its direction. However, for all indices after this, you can fill the index and direction parameters with 0s.
#define gSPTriStrip | ( | pkt, | |
v1, | |||
v2, | |||
v3, | |||
v4, | |||
v5, | |||
v6, | |||
v7 ) |
5 Triangles in strip arrangement. Draws the following tris: v3-v1-v2, v4-v3-v2, v5-v3-v4, v6-v5-v4, v7-v5-v6 To draw fewer than 5 tris, set indices to -1 from the right; for example to draw 4 tris, set v7 to -1, or to draw 3 tris set v6 to -1.
#define gSPVertex | ( | pkt, | |
v, | |||
n, | |||
v0 ) |
macro which loads an internal vertex buffer in the RSP with points that are used by gSP1Triangle macros to generate polygons at the end display list.
It loads an internal vertex buffer in the RSP with points that are used by gSP1Triangle macros to generate polygons. This vertex cache can hold up to 56 vertices, and the vertex loading can begin at any entry (index) within the cache. The vertex coordinates (x,y,z) are encoded in signed 2's complement, 16-bit integers. The texture coordinates (s,t) are encoded in S10.5 format. A vertex either has a color or a normal (for shading). These values are 8-bit values. The colors and alphas are treated as 8-bit unsigned values (0-255), but the normals are treated as 8-bit signed values (-128 to 127). Therefore, the appropriate member of the union to use (.v. or .n.) depends on whether you are using colors or normals.
Normal coordinates range from -1.0 to 1.0. A value of -1.0 is represented as -128, and a value of 1.0 is represented as 128, but because the maximum positive value of a signed byte is 127, a value of 1.0 can't really be represented. Therefore, 0.992 is the maximum representable positive value, which is good enough for this purpose.
The flag value is used for the packed normals feature to store normals with octahedral encoding.
The coordinates (x,y,z) are transformed using the current 4x4 projection and model view matrices, and (s,t) are transformed using the scale defined by gSPTexture.
To load vertex cache entry 2,3,4, use this code:
v | is the pointer to the vertex list (segment address) |
n | is the number of vertices |
v0 | is the load vertex by index vo(0~55) in vertex buffer |
#define gsSP1Quadrangle | ( | v0, | |
v1, | |||
v2, | |||
v3, | |||
flag ) |
1 Quadrangle
#define gsSP1Triangle | ( | v0, | |
v1, | |||
v2, | |||
flag ) gs1Word(G_TRI1, __gsSP1Triangle_w1f(v0, v1, v2, flag)) |
1 Triangle
#define gsSP2Triangles | ( | v00, | |
v01, | |||
v02, | |||
flag0, | |||
v10, | |||
v11, | |||
v12, | |||
flag1 ) |
2 Triangles
#define gsSPAlphaCompareCull | ( | mode, | |
thresh ) |
Alpha compare culling. This was originally created as an optimization for cel shading, but it can also be used for other scenarios. In particular, it can be used with fog to cull tris which are entirely in the fog. This could also be accomplished with far clipping, but far clipping is removed in F3DEX3.
If mode == G_ALPHA_COMPARE_CULL_DISABLE, tris are drawn normally.
Otherwise:
For the light pass:
For the dark pass:
The idea is to cull tris early on the RSP which won't have any of their fragments drawn on the RDP, to save RDP time and memory bandwidth.
#define gsSPAmbient | ( | l, | |
n ) gsDma2p( G_MOVEMEM, (l), sizeof(Ambient), G_MV_LIGHT, _LIGHT_TO_OFFSET(n)) |
l should point to an Ambient struct. n should be an integer 1-10 to load lights 0-9.
#define gsSPAmbOcclusionAmb | ( | amb | ) | gsMoveHalfwd( G_MW_FX, G_MWO_AO_AMBIENT, amb) |
Ambient occlusion Enabled with the G_AMBOCCLUSION bit in geometry mode. Each of these factors sets how much ambient occlusion affects lights of the given type (ambient, directional, point). They are u16s. You can set each independently or two adjacent values with one moveword. A two-command macro is also provided to set all three values.
When building the model, you must encode the amount of ambient occlusion at each vertex–effectively the shadow map for the model–in vertex alpha, where 00 means darkest and FF means lightest. Then, the factors set with the SPAmbOcclusion command determine how much the vertex alpha values affect the light intensity. For example, if the ambient factor is set to 0x8000, this means that in the darkest parts of the model, the ambient light intensity will be reduced by 50%, and in the lightest parts of the model, the ambient light intensity won't be reduced at all.
The default is: amb = 0xFFFF (ambient light fully affected by vertex alpha) dir = 0xA000 (directional lights 62% affected by vertex alpha) point = 0 (point lights not at all affected by vertex alpha)
Two reasons to use ambient occlusion rather than darkening the vertex colors:
Two reasons to use these factors to modify ambient occlusion rather than just manually scaling and offsetting all the vertex alpha values:
#define gsSPAmbOcclusionDir | ( | dir | ) | gsMoveHalfwd( G_MW_FX, G_MWO_AO_DIRECTIONAL, dir) |
Ambient occlusion Enabled with the G_AMBOCCLUSION bit in geometry mode. Each of these factors sets how much ambient occlusion affects lights of the given type (ambient, directional, point). They are u16s. You can set each independently or two adjacent values with one moveword. A two-command macro is also provided to set all three values.
When building the model, you must encode the amount of ambient occlusion at each vertex–effectively the shadow map for the model–in vertex alpha, where 00 means darkest and FF means lightest. Then, the factors set with the SPAmbOcclusion command determine how much the vertex alpha values affect the light intensity. For example, if the ambient factor is set to 0x8000, this means that in the darkest parts of the model, the ambient light intensity will be reduced by 50%, and in the lightest parts of the model, the ambient light intensity won't be reduced at all.
The default is: amb = 0xFFFF (ambient light fully affected by vertex alpha) dir = 0xA000 (directional lights 62% affected by vertex alpha) point = 0 (point lights not at all affected by vertex alpha)
Two reasons to use ambient occlusion rather than darkening the vertex colors:
Two reasons to use these factors to modify ambient occlusion rather than just manually scaling and offsetting all the vertex alpha values:
#define gsSPAmbOcclusionPoint | ( | point | ) | gsMoveHalfwd( G_MW_FX, G_MWO_AO_POINT, point) |
Ambient occlusion Enabled with the G_AMBOCCLUSION bit in geometry mode. Each of these factors sets how much ambient occlusion affects lights of the given type (ambient, directional, point). They are u16s. You can set each independently or two adjacent values with one moveword. A two-command macro is also provided to set all three values.
When building the model, you must encode the amount of ambient occlusion at each vertex–effectively the shadow map for the model–in vertex alpha, where 00 means darkest and FF means lightest. Then, the factors set with the SPAmbOcclusion command determine how much the vertex alpha values affect the light intensity. For example, if the ambient factor is set to 0x8000, this means that in the darkest parts of the model, the ambient light intensity will be reduced by 50%, and in the lightest parts of the model, the ambient light intensity won't be reduced at all.
The default is: amb = 0xFFFF (ambient light fully affected by vertex alpha) dir = 0xA000 (directional lights 62% affected by vertex alpha) point = 0 (point lights not at all affected by vertex alpha)
Two reasons to use ambient occlusion rather than darkening the vertex colors:
Two reasons to use these factors to modify ambient occlusion rather than just manually scaling and offsetting all the vertex alpha values:
#define gsSPAttrOffsetST | ( | s, | |
t ) |
Attribute offsets These are added to ST values after vertices are loaded and transformed. The values are s16s. The addition is after the multiplication for ST scale in SPTexture. Whether it is enabled or disabled at a given time is determined by the G_ATTROFFSET_ST_ENABLE bit in the geometry mode. Normally you would use ST offsets for UV scrolling.
#define gsSPBranchLessZraw | ( | dl, | |
vtx, | |||
zval ) |
gSPBranchLessZraw Branch DL if (vtx.z) less than or equal (raw zval).
dl = DL branch to vtx = Vertex zval = Raw value of screen depth
#define gsSPBranchList | ( | dl | ) | _gsSPBranchListRaw( dl, 0) |
Normal control flow commands; same as gsSPBranchListHint but with hint of 0
#define gsSPBranchListHint | ( | dl, | |
count ) _gsSPBranchListRaw( dl, _DLHINTVALUE(count)) |
Optimization for reduced memory traffic. In count, put the estimated number of DL commands in the target DL (the DL being called / jumped to, or the DL being returned to, starting from the next command to be executed) up to and including the next call / jump / return. Normally, for SPDisplayList, this is just the total number of commands in the target DL. The actual on-screen result will not change regardless of the value of count, but the performance will be best if count is correct, and potentially worse than not specifying count if it is wrong. Feature suggested by Kaze Emanuar
#define gsSPCameraWorld | ( | cam | ) | gsDma2p( G_MOVEMEM, (cam), sizeof(PlainVtx), G_MV_LIGHT, 0) |
Camera world position for Fresnel and specular lighting. Set this whenever you set the VP matrix, viewport, etc. cam is the address of a PlainVtx struct.
#define gsSPClipRatio | ( | r | ) | gsSPNoOp() |
Clipping Macros.
#define gsSPContinueSnake | ( | i0, | |
i0d, | |||
i1, | |||
i1d, | |||
i2, | |||
i2d, | |||
i3, | |||
i3d, | |||
i4, | |||
i4d, | |||
i5, | |||
i5d, | |||
i6, | |||
i6d, | |||
i7, | |||
i7d ) |
Continue a triangle snake for up to 8 more triangles. This is actually not a display list command–there's no command byte. The data is just the next 8 bytes of the display list data, still being processed by the previous gSPTriSnake. Note that the microcode implementation does correctly handle the case when the snake continues past the end of the current data in the input buffer (which is a copy in DMEM of a chunk of the display list); the input buffer is reloaded like it would be for more commands. So the snake can be an unlimited length by continuing to append gSPContinueSnake commands.
#define gsSPCullDisplayList | ( | vstart, | |
vend ) |
Cull the display list based on screen clip flags of range of loaded verts. Executes SPEndDisplayList if the convex hull formed by the specified range of already-loaded vertices is offscreen.
#define gsSPDisplayList | ( | dl | ) | _gsSPDisplayListRaw( dl, 0) |
Normal control flow commands; same as gsSPDisplayListHint but with hint of 0
#define gsSPDisplayListHint | ( | dl, | |
count ) _gsSPDisplayListRaw( dl, _DLHINTVALUE(count)) |
Optimization for reduced memory traffic. In count, put the estimated number of DL commands in the target DL (the DL being called / jumped to, or the DL being returned to, starting from the next command to be executed) up to and including the next call / jump / return. Normally, for SPDisplayList, this is just the total number of commands in the target DL. The actual on-screen result will not change regardless of the value of count, but the performance will be best if count is correct, and potentially worse than not specifying count if it is wrong. Feature suggested by Kaze Emanuar
#define gsSPDma_io | ( | flag, | |
dmem, | |||
dram, | |||
size ) |
gSPDma_io DMA to/from DMEM/IMEM for DEBUG.
#define gsSPDontSkipTexLoadsAcross | ( | ) | gsMoveWd(G_MW_FX, G_MWO_LAST_MAT_DL_ADDR, 0xFFFFFFFF) |
F3DEX3 has a basic auto-batched rendering system. At a high level, if a material display list being run is the same as the last material, the texture loads are automatically skipped the second time as they should already be in TMEM.
This design generally works, but can break if you call a display list twice but in between change a segment mapping so that a referenced image inside is actually different the two times. In these cases, run the below command between the two calls (e.g. when you change the segment) and the microcode will not skip the second texture loads.
Internally, a material is defined to start with any set image command, and end on any of the following: call, branch, return, vertex, all tri commands, tex/fill rectangles, and successes on cull or branch w/z (which are usually preceded by vertex loads anyway). The physical address of the display list –not the address of the image–is stored when a material is started. If a material starts and its physical address is the same as the stored last start address, i.e. we're executing the same material display list as the last material, material cull mode is set. In this mode, load block, load tile, and load TLUT all are skipped. This mode is cleared when the material ends.
This design has the benefit that it works correctly even with complex materials, e.g. with two CI4 textures (four loads), whereas it would be difficult to implement tracking all these loads separately. Furthermore, a design based on tracking the image addresses could break if you loaded different tile sections of the same image in consecutive materials.
#define gsSPEndDisplayList | ( | ) | _gsSPEndDisplayListRaw( 0) |
Normal control flow commands; same as gsSPEndDisplayListHint but with hint of 0
#define gsSPEndDisplayListHint | ( | count | ) | _gsSPEndDisplayListRaw( _DLHINTVALUE(count)) |
Optimization for reduced memory traffic. In count, put the estimated number of DL commands in the target DL (the DL being called / jumped to, or the DL being returned to, starting from the next command to be executed) up to and including the next call / jump / return. Normally, for SPDisplayList, this is just the total number of commands in the target DL. The actual on-screen result will not change regardless of the value of count, but the performance will be best if count is correct, and potentially worse than not specifying count if it is wrong. Feature suggested by Kaze Emanuar
#define gsSPFlush | ( | ) | gs1Word( G_FLUSH, 0) |
Flush the internal DMEM buffer of RDP commands to the RDP FIFO in DRAM, causing the RDP to immediately begin executing any previous commands. Without SPFlush, the RDP may not begin executing any given command until up to 46 more RDP commands after that have been processed by the RSP (or the final end of the display list for the frame).
The primary use case is if your frame's display list begins with clearing the framebuffer and/or Z buffer, and then proceeds to things which take significant time on the RSP before emitting many RDP commands, such as matrix and lighting for drawing a character model. You should insert SPFlush after the first large buffer clear to cause the RDP to begin executing those long operations immediately while the RSP is continuing to work. If you are clearing both the framebuffer and Z buffer, you would usually only need one SPFlush after the first of these two DPFillRect commands.
#define gsSPFogFactor | ( | fm, | |
fo ) |
FOG macros fm = z multiplier fo = z offset FOG FORMULA: alpha(fog) = (eyespace z) * fm + fo CLAMPED 0 to 255 note: (eyespace z) ranges -1 to 1
Alternate method of setting fog: min, max: range 0 to 1000: 0=nearplane, 1000=farplane min is where fog begins (usually less than max and often 0) max is where fog is thickest (usually 1000)
#define gsSPForceMatrix | ( | mptr | ) | gsSPNoOp() |
Load new MVP matrix directly.
This is no longer supported as it was not used in production games.
#define gsSPFresnel | ( | scale, | |
offset ) |
Fresnel - Feature suggested by thecozies Enabled with the G_FRESNEL bit in geometry mode. The dot product between a vertex normal and the vector from the vertex to the camera is computed. The offset and scale here convert this to a shade alpha value. This is useful for making surfaces fade between transparent when viewed straight-on and opaque when viewed at a large angle, or for applying a fake "outline" around the border of meshes.
If using Fresnel, you need to set the camera world position whenever you set the VP matrix, viewport, etc. See SPCameraWorld.
The RSP does: s16 dotProduct = dot(vertex normal, camera pos - vertex pos); dotProduct = abs(dotProduct); // 0 = points to side, 7FFF = points at or away s32 factor = ((scale * dotProduct) >> 15) + offset; s16 result = clamp(factor << 8, 0, 7FFF); color_or_alpha = result >> 7;
At dotMax, color_or_alpha = FF, result = 7F80, factor = 7F At dotMin, color_or_alpha = 00, result = 0, factor = 0 7F = ((scale * dotMax) >> 15) + offset 00 = ((scale * dotMin) >> 15) + offset Subtract: 7F = (scale * (dotMax - dotMin)) >> 15 3F8000 = scale * (dotMax - dotMin) scale = 3F8000 / (dotMax - dotMin) <– offset = -(((3F8000 / (dotMax - dotMin)) * dotMin) >> 15) offset = -((7F * dotMin) / (dotMax - dotMin)) <–
To convert in the opposite direction: ((7F - offset) << 15) / scale = dotMax ((00 - offset) << 15) / scale = dotMin
#define gsSPFresnelOffset | ( | offset | ) | gsMoveHalfwd(G_MW_FX, G_MWO_FRESNEL_OFFSET, offset) |
Fresnel - Feature suggested by thecozies Enabled with the G_FRESNEL bit in geometry mode. The dot product between a vertex normal and the vector from the vertex to the camera is computed. The offset and scale here convert this to a shade alpha value. This is useful for making surfaces fade between transparent when viewed straight-on and opaque when viewed at a large angle, or for applying a fake "outline" around the border of meshes.
If using Fresnel, you need to set the camera world position whenever you set the VP matrix, viewport, etc. See SPCameraWorld.
The RSP does: s16 dotProduct = dot(vertex normal, camera pos - vertex pos); dotProduct = abs(dotProduct); // 0 = points to side, 7FFF = points at or away s32 factor = ((scale * dotProduct) >> 15) + offset; s16 result = clamp(factor << 8, 0, 7FFF); color_or_alpha = result >> 7;
At dotMax, color_or_alpha = FF, result = 7F80, factor = 7F At dotMin, color_or_alpha = 00, result = 0, factor = 0 7F = ((scale * dotMax) >> 15) + offset 00 = ((scale * dotMin) >> 15) + offset Subtract: 7F = (scale * (dotMax - dotMin)) >> 15 3F8000 = scale * (dotMax - dotMin) scale = 3F8000 / (dotMax - dotMin) <– offset = -(((3F8000 / (dotMax - dotMin)) * dotMin) >> 15) offset = -((7F * dotMin) / (dotMax - dotMin)) <–
To convert in the opposite direction: ((7F - offset) << 15) / scale = dotMax ((00 - offset) << 15) / scale = dotMin
#define gsSPFresnelScale | ( | scale | ) | gsMoveHalfwd(G_MW_FX, G_MWO_FRESNEL_SCALE, scale) |
Fresnel - Feature suggested by thecozies Enabled with the G_FRESNEL bit in geometry mode. The dot product between a vertex normal and the vector from the vertex to the camera is computed. The offset and scale here convert this to a shade alpha value. This is useful for making surfaces fade between transparent when viewed straight-on and opaque when viewed at a large angle, or for applying a fake "outline" around the border of meshes.
If using Fresnel, you need to set the camera world position whenever you set the VP matrix, viewport, etc. See SPCameraWorld.
The RSP does: s16 dotProduct = dot(vertex normal, camera pos - vertex pos); dotProduct = abs(dotProduct); // 0 = points to side, 7FFF = points at or away s32 factor = ((scale * dotProduct) >> 15) + offset; s16 result = clamp(factor << 8, 0, 7FFF); color_or_alpha = result >> 7;
At dotMax, color_or_alpha = FF, result = 7F80, factor = 7F At dotMin, color_or_alpha = 00, result = 0, factor = 0 7F = ((scale * dotMax) >> 15) + offset 00 = ((scale * dotMin) >> 15) + offset Subtract: 7F = (scale * (dotMax - dotMin)) >> 15 3F8000 = scale * (dotMax - dotMin) scale = 3F8000 / (dotMax - dotMin) <– offset = -(((3F8000 / (dotMax - dotMin)) * dotMin) >> 15) offset = -((7F * dotMin) / (dotMax - dotMin)) <–
To convert in the opposite direction: ((7F - offset) << 15) / scale = dotMax ((00 - offset) << 15) / scale = dotMin
#define gsSPGeometryMode | ( | c, | |
s ) |
One gSPGeometryMode(pkt,c,s) GBI is equal to these two GBIs.
gSPClearGeometryMode(pkt,c) gSPSetGeometryMode(pkt,s)
gSPLoadGeometryMode(pkt, word) sets GeometryMode directly.
#define gsSPLight | ( | l, | |
n ) gsDma2p( G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, _LIGHT_TO_OFFSET(n)) |
l should point to a Light struct. n should be an integer 1-9 to load lights 0-8. Can also load Ambient lights to lights 0-8 with this. However, if you have 9 directional / point lights, you must use SPAmbient to load light 9 (LIGHT_10) with an ambient light. (That is, the memory for light 9 (LIGHT_10) is only sizeof(Ambient), so if you load this with SPLight, it will overwrite other DMEM and corrupt unrelated things.) New code should not generally use SPLight, and instead use SPSetLights to set all lights in one memory transaction.
#define gsSPLightColor | ( | n, | |
col ) |
gSPLightColor changes the color of a directional light without an additional DMA transfer. col is a 32 bit word where (col >> 24) & 0xFF is red, (col >> 16) & 0xFF is green, and (col >> 8) & 0xFF is blue. (col & 0xFF) is ignored and masked to zero. n should be an integer 1-10 to apply to light 0-9.
#define gsSPLightToRDP | ( | light, | |
alpha, | |||
word0 ) |
Send the color of the specified light to one of the RDP's color registers. light is the index of a light in the RSP counting from the end, i.e. 0 is the ambient light, 1 is the last directional / point light, etc. The RGB color of the selected light is combined with the alpha specified in this command as word 1 of a RDP command, and word 0 is specified in this command. Specialized versions are provided below for prim color and fog color, because these are the two versions needed for cel shading, but any RDP color command could be specified this way.
#define gsSPLoadUcodeEx | ( | uc_start, | |
uc_dstart, | |||
uc_dsize ) |
gSPLoadUcode RSP loads specified ucode.
uc_start = ucode text section start uc_dstart = ucode data section start
#define gsSPLookAt | ( | la | ) | gsDma2p( G_MOVEMEM, (la), sizeof(LookAt), G_MV_LIGHT, 8) |
Reflection/Hiliting Macros. la is the address of a LookAt struct.
#define gsSPLookAtX | ( | l | ) | gsSPLookAt(l) |
These versions are deprecated, please use g*SPLookAt. The two directions cannot be set independently anymore as they both fit within one memory word. (They could be set with moveword, but then the values would have to be within the command itself, not at a memory address.) This deprecated version has the X command set both (assuming l is the name / address of a LookAt struct) and has the Y command as a SP no-op.
#define gsSPLookAtY | ( | l | ) | gsSPNoOp() |
These versions are deprecated, please use g*SPLookAt. The two directions cannot be set independently anymore as they both fit within one memory word. (They could be set with moveword, but then the values would have to be within the command itself, not at a memory address.) This deprecated version has the X command set both (assuming l is the name / address of a LookAt struct) and has the Y command as a SP no-op.
#define gsSPMatrix | ( | m, | |
p ) gsDma2p( G_MTX, (m), sizeof(Mtx), (p) ^ G_MTX_PUSH ^ G_MTX_LOAD, 0) |
macro which inserts a matrix operation in a static display list.
It inserts a matrix operation in the display list. The parameters allow you to select which matrix stack to use (projection or model view), whether to load or multiply, and whether or not to push the matrix stack. The following parameters are bitwise OR'ed together:
The legacy parameters G_MTX_MODELVIEW and G_MTX_PROJECTION are also supported, but in F3DEX3 you should always multiply the view matrix with the projection matrix as G_MTX_VIEWPROJECTION, and only put model matrices in the G_MTX_MODEL stack.
The format of the fixed-point matrices may seem a little awkward to the application programmer because it is optimized for the RSP geometry engine. This unusual format is hidden in the graphics utility libraries and not usually exposed to the application programmer, but in some cases (static matrix declarations or direct element manipulation) it is necessary to understand the format.
The integer and fractional components of the matrix elements are separated. The first 8 words (16 shorts) hold the 16-bit integer elements, the second 8 words (16 shorts) hold the 16-bit fractional elements. The fact that the Mtx type is declared as a long [4][4] array is slightly misleading. For example, to declare a static identity matrix, use code similar to this:
To force the translation elements of a matrix to be (10.5, 20.5, 30.5), use code similar to this:
Matrix multiplication in the RSP geometry engine is done using 32-bit integer arithmetic, in s15.16 format (16 integer, 16 fractional bits, in other words representing -32768.0 to 32767.999985 with a resolution of about 0.000015). A 32 x 32 bit multiply results in a 64-bit number. Only the middle 32 bits of this 64-bit result are kept for the new matrix, to preserve the s15.16 format.
A typical game object's transformation will have a scale around the range of 1/100, a rotation which is always values -1.0 to 1.0, and a translation around the range of 1000. When producing a final transformation matrix, you will typically compose (multiply) one scale, multiple rotations (for limbs), and finally one translation. Each matrix multiply on the RSP will lose precision, especially if the scale has been applied before the rotations.
Therefore, your game should usually maintain a matrix stack on the CPU in floating point, and once you have a final model matrix for each limb / object, convert it to fixed point and load it to the RSP. Occasional uses of G_MTX_MUL, such as multiplying view * projection or for HUD elements, are okay. Both SM64 and OoT already operate this way.
Your game generally should not use G_MTX_PUSH or SPPopMatrix*, even in a scene graph style engine like SM64.
If you have taken the advice above to just compute and upload final model matrices, there is no need to use push or pop–you'll always just do a single load before rendering any model. If you need to return to a previous transformation matrix, just upload that already-computed matrix again. Again, both SM64 and OoT already do this.
In F3DEX3, the code for G_MTX_PUSH and SPPopMatrix* is moved to overlay 3, meaning these operations will be slower on average than in F3DEX2.
m | is the pointer to the 4x4 fixed-point matrix (see note above about format) |
p | are the bit OR'd parameters to the matrix macro (G_MTX_MODEL, G_MTX_VIEWPROJECTION, G_MTX_MUL, G_MTX_LOAD, G_MTX_NOPUSH, G_MTX_PUSH) |
#define gsSPMemset | ( | dram, | |
value, | |||
size ) |
Use RSP DMAs to set a region of memory to a repeated 16-bit value. This can clear the color framebuffer or Z-buffer faster than the RDP can in fill mode. SPMemset overwrites the DMEM vertex buffer, so vertices loaded before this command cannot be used after it (though this would not normally be done).
dram: Segmented or physical start address. Must be aligned to 16 bytes. value: 16-bit value to fill the memory with. e.g. 0 for color, 0xFFFC for Z. size: Size in bytes to fill, must be nonzero and a multiple of 16 bytes.
#define gsSPModifyVertex | ( | vtx, | |
where, | |||
val ) |
You can use this macro to modify certain sections of a vertex after it has been sent to the RSP (by the gSPVertex macro).
This is an advanced macro. You need a good understanding of how vertices work in the RSP microcode before you use this macro (refer to gSPVertex).
You can use this macro to modify certain sections of a vertex after it has been sent to the RSP (by the gSPVertex macro). This is useful for vertices that are shared between two or more triangles that must have different properties when associated with one triangle versus the other triangle.
For example, you might have two adjacent triangles that both need smooth-shaded color, but one is smooth-shaded red-to-yellow and the other is smooth-shaded green-to-cyan. In this case, the vertex that is shared by both triangles is sent with red/yellow color by using the gSPVertex macro. The first triangle is drawn. Then, the gSPModifyVertex macro is used to change the color to green/cyan, and the second triangle is drawn.
The primary use of the gSPModifyVertex macro is to modify the texture coordinate of a vertex so that a vertex that is shared by two triangles with different textures and different texture coordinate spaces can contain the texture coordinate for the first texture and then be modified to contain the texture coordinate for the second texture.
It is faster to use the gSPModifyVertex macro than to send a new vertex macro with a different but similar vertex because no transformations or lighting are done to the vertex when you use the gSPModifyVertex macro.
The where argument specifies which part of the vertex is to be modified. It can hold one of the following values:
The S and T coordinates supplied in the gSPModifyVertex macro are never multiplied by the texture scale (from the gSPTexture macro), so you must pre-scale them before sending them. For example, if you want a texture scale of 1/2 (0x8000), make the S and T values sent with the gSPModifyVertex macro half the value of the equivalent values used with the gSPVertex macro.
To share a vertex between two triangles with different textures and texture coordinates, use this code:
vtx | specifies which of the RSP's vertices (0-55) to modify |
where | specifies which part of the vertex to modify (G_MWO_POINT_RGBA, G_MWO_POINT_ST, G_MWO_POINT_XYSCREEN or G_MWO_POINT_ZSCREEN) |
val | is the new value for the part of the vertex to be modified (a 32 bit integer number) |
#define gsSPNumLights | ( | n | ) | gsMoveWd( G_MW_NUMLIGHT, G_MWO_NUMLIGHT, NUML(n)) |
Number of directional / point lights, in the range 0-9. There is also always one ambient light not counted in this number. See also ENABLE_POINT_LIGHTS.
#define gsSPOcclusionPlane | ( | o | ) |
Set the occlusion plane. This is a quadrilateral in 3D space where all geometry behind it is culled. You should create occlusion plane candidates just behind walls and other large objects, and have your game engine pick the most optimal one every frame to send to the RSP.
Computing the coefficients for the occlusion plane is far too complicated to explain here. The reference implementation guOcclusionPlane
is provided separately.
o is the address of an OcclusionPlane struct
#define gsSPPopMatrix | ( | mtx | ) | gsSPPopMatrixN( (mtx), 1) |
macro which pops one matrix from a matrix stack in a static display list.
This is just SPPopMatrixN with num=1:
It pops num
matrices from the stack.
mtx | is the flag field that identifies which matrix stack to pop:
|
num | is the number of matrices to pop |
#define gsSPPopMatrixN | ( | mtx, | |
num ) gsDma2p( G_POPMTX, (num) * 64, 64, (mtx) + G_MV_MMTX, 0) |
macro which pops multiple matrices from a matrix stack.
It pops num
matrices from the stack.
mtx | is the flag field that identifies which matrix stack to pop:
|
num | is the number of matrices to pop |
#define gsSPSetLights | ( | n, | |
name ) |
Set all your scene's lights (directional/point + ambient) with one memory transaction. n is the number of directional / point lights, from 0 to 9. There is also always an ambient light. If there are point lights, set ENABLE_POINT_LIGHTS in n via logical or (i.e. set n to (numLights | ENABLE_POINT_LIGHTS)) name should be the NAME of a Lights struct (NOT A POINTER) filled in with all the lighting data. You can use the gdSPDef* macros to fill in the struct or just do it manually. Example: Lights2 myLights; // 2 dir/pos + 1 ambient gSPSetLights(POLY_OPA_DISP++, 2, myLights);
If you need to use a pointer, e.g. if the number of lights is variable at runtime: Light *lights = memory_allocate((numLights + 1) * sizeof(Light)); lights[0].p.pos = ...; lights[1].l.dir = ...; ... lights[numLights].l.col = ambient_color(); gSPSetLights(POLY_OPA_DISP++, ENABLE_POINT_LIGHTS | numLights, *lights); // <- NOTE DEREFERENCE
If you're wondering why this macro takes a name / dereference instead of a pointer, it's for backwards compatibility.
#define gsSPTexture | ( | s, | |
t, | |||
level, | |||
tile, | |||
on ) |
Macros to turn texture on/off
#define gsSPTextureL | ( | s, | |
t, | |||
level, | |||
bowtie, | |||
tile, | |||
on ) gsSPTexture(s, t, level, tile, on) |
The bowtie value is a workaround for a bug in HW V1, and is not supported by F3DEX2, let alone F3DEX3.
#define gsSPTriFan | ( | v1, | |
v2, | |||
v3, | |||
v4, | |||
v5, | |||
v6, | |||
v7 ) |
5 Triangles in fan arrangement. Draws the following tris: v3-v1-v2, v4-v1-v3, v5-v1-v4, v6-v1-v5, v7-v1-v6 Otherwise works the same as
#define gsSPTriSnake | ( | i1, | |
i2, | |||
i3, | |||
i4, | |||
i4d, | |||
i5, | |||
i5d, | |||
i6, | |||
i6d, | |||
i7, | |||
i7d ) |
Triangle snake is F3DEX3's accelerated triangles command. It is a generalized form of a triangle strip or fan, which can represent any sequential chain of connected triangles by encoding which side of the current triangle the next triangle attaches to. This allows the chain of triangles to "snake" around and double back next to itself, unlike a triangle strip. For more information on the design, see Triangle Snake in the documentation.
The drawing algorithm is:
For example, after drawing the first triangle i3-i1-i2, if i4 is G_SNAKE_RIGHT, the snake turns right and draws i4-i3-i2: 3 –<– 4 /'\ '/ (winding order and / \ / first vertex for flat / \ / shading are marked) 1 -->– 2 Conversely, after the first triangle i3-i1-i2, if i4 is G_SNAKE_LEFT, the snake turns left and draws i4-i1-i3: 4 –<– 3 \' /'\ \ / \ \ / \ 1 -->– 2 If the snake turns in the same direction repeatedly, it will coil up, forming a triangle fan. If it slithers left and right alternately, this will form a triangle strip. Any combination of these is also possible. In particular, a useful shape is a triangle strip for a few tris, then a tri fan for a couple tris to "turn around", then another tri strip alongside the first, and so on. This shape can cover almost all tris of a typical surface with a single snake, except for tris which have two unconnected edges which can only be the first or last tris of the snake.
Logical-OR G_SNAKE_LAST into the last valid index of the snake. This index still needs a valid G_SNAKE_LEFT or G_SNAKE_RIGHT for its direction. However, for all indices after this, you can fill the index and direction parameters with 0s.
#define gsSPTriStrip | ( | v1, | |
v2, | |||
v3, | |||
v4, | |||
v5, | |||
v6, | |||
v7 ) |
5 Triangles in strip arrangement. Draws the following tris: v3-v1-v2, v4-v3-v2, v5-v3-v4, v6-v5-v4, v7-v5-v6 To draw fewer than 5 tris, set indices to -1 from the right; for example to draw 4 tris, set v7 to -1, or to draw 3 tris set v6 to -1.
#define gsSPVertex | ( | v, | |
n, | |||
v0 ) |
macro which loads an internal vertex buffer in the RSP with points that are used by gSP1Triangle macros to generate polygons in a static display list.
It loads an internal vertex buffer in the RSP with points that are used by gSP1Triangle macros to generate polygons. This vertex cache can hold up to 56 vertices, and the vertex loading can begin at any entry (index) within the cache. The vertex coordinates (x,y,z) are encoded in signed 2's complement, 16-bit integers. The texture coordinates (s,t) are encoded in S10.5 format. A vertex either has a color or a normal (for shading). These values are 8-bit values. The colors and alphas are treated as 8-bit unsigned values (0-255), but the normals are treated as 8-bit signed values (-128 to 127). Therefore, the appropriate member of the union to use (.v. or .n.) depends on whether you are using colors or normals.
Normal coordinates range from -1.0 to 1.0. A value of -1.0 is represented as -128, and a value of 1.0 is represented as 128, but because the maximum positive value of a signed byte is 127, a value of 1.0 can't really be represented. Therefore, 0.992 is the maximum representable positive value, which is good enough for this purpose.
The flag value is used for the packed normals feature to store normals with octahedral encoding.
The coordinates (x,y,z) are transformed using the current 4x4 projection and model view matrices, and (s,t) are transformed using the scale defined by gSPTexture.
To load vertex cache entry 2,3,4, use this code:
v | is the pointer to the vertex list (segment address) |
n | is the number of vertices |
v0 | is the load vertex by index vo(0~55) in vertex buffer |
#define LIGHT_1 1 |
There is also no need to use these macros.
#define LIGHT_TYPE_DIR 0 |
Standard directional light. Equivalent to kc = 0.
#define LIGHT_TYPE_POINT | ( | kc | ) | kc |
Identifies the light as a point light, with the given kc coefficient (nonzero).
#define NUMLIGHTS_0 0 |
F3DEX3 properly supports zero lights, so there is no need to use these macros anymore.
typedef long int Mtx_t[4][4] |
4x4 matrix, fixed point s15.16 format. First 8 words are integer portion of the 4x4 matrix Last 8 words are the fraction portion of the 4x4 matrix