F3DEX3
|
Modded GBI for use with F3DEX3 custom microcode. More...
#include "ultra64/mbi.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_MODELVIEW 0x00 /* matrix types */ |
specifies whether the matrix operation will be performed on the projection or the model view matrix. | |
#define | G_MTX_PROJECTION 0x04 |
specifies whether the matrix operation will be performed on the projection or the model view matrix. | |
#define | G_MTX_MUL 0x00 /* concat or load */ |
concatenates the matrix (m) with the top of the matrix stack. | |
#define | G_MTX_LOAD 0x02 |
loads the matrix (m) onto the top of the matrix stack. | |
#define | G_MTX_NOPUSH 0x00 /* push or not */ |
specifies do not push the matrix stack prior to matrix operations | |
#define | G_MTX_PUSH 0x01 |
specifies push the matrix stack prior to matrix operations | |
#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 | gSPMatrix(pkt, m, p) gDma2p((pkt),G_MTX, (m), sizeof(Mtx), (p) ^ G_MTX_PUSH, 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, 0) |
macro which inserts a matrix operation in a static display list. | |
#define | gSPPopMatrixN(pkt, n, num) gDma2p((pkt), G_POPMTX, (num) * 64, 64, 2, 0) |
macro which pops one of the matrix stacks at the end display list. | |
#define | gsSPPopMatrixN(n, num) gsDma2p( G_POPMTX, (num) * 64, 64, 2, 0) |
macro which pops one of the matrix stacks in a static display list. | |
#define | gSPPopMatrix(pkt, n) gSPPopMatrixN((pkt), (n), 1) |
macro which pops one of the matrix stacks at the end display list. | |
#define | gsSPPopMatrix(n) gsSPPopMatrixN( (n), 1) |
macro which pops one of the matrix stacks 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(pkt, dram, value, size) |
#define | gImmp0(pkt, c) |
#define | gsImmp0(c) |
#define | gSP1Triangle(pkt, v0, v1, v2, flag) |
#define | gsSP1Triangle(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 | gSPTriStrip(pkt, v1, v2, v3, v4, v5, v6, v7) _gSP5Triangles(pkt, G_TRISTRIP, v1, v2, v3, v4, v5, v6, v7) |
#define | gsSPTriStrip(v1, v2, v3, v4, v5, v6, v7) _gsSP5Triangles(G_TRISTRIP, v1, v2, v3, v4, v5, v6, v7) |
#define | gSPTriFan(pkt, v1, v2, v3, v4, v5, v6, v7) _gSP5Triangles(pkt, G_TRIFAN, v1, v2, v3, v4, v5, v6, v7) |
#define | gsSPTriFan(v1, v2, v3, v4, v5, v6, v7) _gsSP5Triangles(G_TRIFAN, 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 | gSPNormalsMode(pkt, mode) gMoveHalfwd(pkt, G_MW_FX, G_MWO_NORMALS_MODE, (mode) & 0xFF) |
#define | gsSPNormalsMode(mode) gsMoveHalfwd(G_MW_FX, G_MWO_NORMALS_MODE, (mode) & 0xFF) |
#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 | gSPMITMatrix(pkt, mit) gDma2p((pkt), G_MOVEMEM, (mit), sizeof(MITMtx), G_MV_MMTX, 0x80) |
#define | gsSPMITMatrix(mtx) gsDma2p( G_MOVEMEM, (mit), sizeof(MITMtx), G_MV_MMTX, 0x80) |
#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 | 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 | 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 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_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 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 gImmp0 | ( | pkt, | |
c ) |
#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 gsImmp0 | ( | c | ) |
RSP short command (no DMA required) macros
#define gSP1Quadrangle | ( | pkt, | |
v0, | |||
v1, | |||
v2, | |||
v3, | |||
flag ) |
#define gSP1Triangle | ( | pkt, | |
v0, | |||
v1, | |||
v2, | |||
flag ) |
#define gSP2Triangles | ( | pkt, | |
v00, | |||
v01, | |||
v02, | |||
flag0, | |||
v10, | |||
v11, | |||
v12, | |||
flag1 ) |
#define gSPAlphaCompareCull | ( | pkt, | |
mode, | |||
thresh ) |
Alpha compare culling. Optimization for cel shading, could also be used for other scenarios where lots of tris are being drawn with alpha compare.
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 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 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 there is no MVP matrix in F3DEX3.
#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, 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), where to load or concatenate, and whether or not to push the matrix stack. The following parameters are bit OR'ed together:
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:
For example, to retain maximum precision, the number ranges must be similar. Large-scale and translate parameters can decrease the transformation precision. Because rotation and projection matrices require quite a bit of fractional accuracy, these fractions may get tossed out if multiplied against large integer numbers.
Each concatenation results in the rounding of the LSB of each matrix term. This means that each concatenation injects 1/2 LSB of error into the matrix. To keep full precision, concatenate matrices in floating-point on the processor and just load the result into the RSP.
Each G_MTX_MODELVIEW matrix operation has an implicit matrix multiplication even if you specify G_MTX_LOAD. This is the combined model view (M) and projection (P) matrix that is necessary for the vertex transformation to use a single matrix during transformation.
You can optimize this by concatenating modeling matrices on the CPU and then putting the viewing (V) and projection matrices on the projection stack. By doing this, you only incur the single MxVP matrix concatenation each time you load a modeling matrix. Furthermore, the application has more information on how to do a cheap hack for modeling matrix concatenation. For example, if you want to combine a single axis rotation with a translation, just place the coefficients in the correct entries of the resulting matrix.
m | is the pointer to the 4x4 fixed-point matrix (see note below about format) |
p | are the bit OR'd parameters to the matrix macro (G_MTX_PROJECTION, G_MTX_MODELVIEW, G_MTX_MUL, G_MTX_LOAD, G_MTX_NOPUSH) |
#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 gSPMITMatrix | ( | pkt, | |
mit ) gDma2p((pkt), G_MOVEMEM, (mit), sizeof(MITMtx), G_MV_MMTX, 0x80) |
See SPNormalsMode. mtx is the address of a MITMtx (M inverse transpose).
The matrix values must be scaled down so that the matrix norm is <= 1, i.e. multiplying this matrix by any vector length <= 1 must produce a vector with length <= 1. Normally, M scales things down substantially, so M inverse transpose natively would scale them up substantially; you need to apply a constant scale to counteract this. One easy way to do this is compute M inverse transpose normally, then scale it so until the maximum absolute value of any element is 0.5. Because of this scaling, you can also skip the part of the inverse computation where you compute the determinant and divide by it, cause you're going to rescale it arbitrarily anyway.
#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 gSPNormalsMode | ( | pkt, | |
mode ) gMoveHalfwd(pkt, G_MW_FX, G_MWO_NORMALS_MODE, (mode) & 0xFF) |
Normals mode: How to handle transformation of vertex normals from model to world space for lighting.
If mode = G_NORMALS_MODE_FAST, transforms normals from model space to world space with the M matrix. This is correct if the object's transformation matrix stack only included translations, rotations, and uniform scale (i.e. same scale in X, Y, and Z); otherwise, if the transformation matrix has nonuniform scale or shear, the lighting on the object will be somewhat distorted.
If mode = G_NORMALS_MODE_AUTO, transforms normals from model space to world space with M inverse transpose, which renders lighting correctly for the object regardless of its transformation matrix (nonuniform scale or shear is okay). Whenever vertices are drawn with lighting enabled after M has been changed, computes M inverse transpose from M. This requires swapping to overlay 4 for M inverse transpose and then back to overlay 2 for lighting, which produces roughly 3.5 us of extra DRAM traffic. This performance penalty happens effectively once per matrix, which is once per normal object or separated limb or about twice per flex skeleton limb. So in a scene with lots of complex skeletons, this may have a noticeable performance impact.
If mode = G_NORMALS_MODE_MANUAL, uses M inverse transpose for correct results like G_NORMALS_MODE_AUTO, but it never internally computes M inverse transpose. You have to upload M inverse transpose to the RSP using SPMITMatrix every time you change the M matrix. The DRAM traffic for the extra matrix uploads is much smaller than the overlay swaps, so if you can efficiently compute M inverse transpose on the CPU, this may be faster than G_NORMALS_MODE_AUTO.
Recommended to leave this set to G_NORMALS_MODE_FAST generally, and only set it to G_NORMALS_MODE_AUTO for specific objects at times when they actually have a nonuniform scale. For example, G_NORMALS_MODE_FAST for Mario generally, but G_NORMALS_MODE_AUTO temporarily while he is squashed.
#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.
#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, | |
n ) gSPPopMatrixN((pkt), (n), 1) |
macro which pops one of the matrix stacks at the end display list.
It pops one of the matrix stacks. The model view stack can be up to 10 matrices deep. The projection stack is 1 matrix deep, so it cannot be popped.
n | is the flag field that identifies which matrix stack to pop:
|
#define gSPPopMatrixN | ( | pkt, | |
n, | |||
num ) gDma2p((pkt), G_POPMTX, (num) * 64, 64, 2, 0) |
macro which pops one of the matrix stacks at the end display list.
It pops num
of the matrix stacks. The model view stack can be up to 10 matrices deep. The projection stack is 1 matrix deep, so it cannot be popped.
n | 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. 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++, 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 ) _gSP5Triangles(pkt, G_TRIFAN, v1, v2, v3, v4, v5, v6, v7) |
5 Triangles in fan arrangement. Draws the following tris: v1-v2-v3, v1-v3-v4, v1-v4-v5, v1-v5-v6, v1-v6-v7 Otherwise works the same as SPTriStrip, see above.
#define gSPTriStrip | ( | pkt, | |
v1, | |||
v2, | |||
v3, | |||
v4, | |||
v5, | |||
v6, | |||
v7 ) _gSP5Triangles(pkt, G_TRISTRIP, v1, v2, v3, v4, v5, v6, v7) |
5 Triangles in strip arrangement. Draws the following tris: v1-v2-v3, v3-v2-v4, v3-v4-v5, v5-v4-v6, v5-v6-v7 If you want to draw fewer tris, set indices to -1 from the right. e.g. to draw 4 tris, set v7 to -1; to draw 3 tris, set v6 to -1 Note that any set of 3 adjacent tris can be drawn with either SPTriStrip or SPTriFan. For arbitrary sets of 4 adjacent tris, four out of five of them can be drawn with one of SPTriStrip or SPTriFan. The 4-triangle formation which can't be drawn with either command looks like the Triforce.
#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 ) |
1 Triangle
#define gsSP2Triangles | ( | v00, | |
v01, | |||
v02, | |||
flag0, | |||
v10, | |||
v11, | |||
v12, | |||
flag1 ) |
2 Triangles
#define gsSPAlphaCompareCull | ( | mode, | |
thresh ) |
Alpha compare culling. Optimization for cel shading, could also be used for other scenarios where lots of tris are being drawn with alpha compare.
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 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 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 there is no MVP matrix in F3DEX3.
#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, 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), where to load or concatenate, and whether or not to push the matrix stack. The following parameters are bit OR'ed together:
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:
For example, to retain maximum precision, the number ranges must be similar. Large-scale and translate parameters can decrease the transformation precision. Because rotation and projection matrices require quite a bit of fractional accuracy, these fractions may get tossed out if multiplied against large integer numbers.
Each concatenation results in the rounding of the LSB of each matrix term. This means that each concatenation injects 1/2 LSB of error into the matrix. To keep full precision, concatenate matrices in floating-point on the processor and just load the result into the RSP.
Each G_MTX_MODELVIEW matrix operation has an implicit matrix multiplication even if you specify G_MTX_LOAD. This is the combined model view (M) and projection (P) matrix that is necessary for the vertex transformation to use a single matrix during transformation.
You can optimize this by concatenating modeling matrices on the CPU and then putting the viewing (V) and projection matrices on the projection stack. By doing this, you only incur the single MxVP matrix concatenation each time you load a modeling matrix. Furthermore, the application has more information on how to do a cheap hack for modeling matrix concatenation. For example, if you want to combine a single axis rotation with a translation, just place the coefficients in the correct entries of the resulting matrix.
m | is the pointer to the 4x4 fixed-point matrix (see note below about format) |
p | are the bit OR'd parameters to the matrix macro (G_MTX_PROJECTION, G_MTX_MODELVIEW, G_MTX_MUL, G_MTX_LOAD, G_MTX_NOPUSH) |
#define gsSPMemset | ( | 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 gsSPMITMatrix | ( | mtx | ) | gsDma2p( G_MOVEMEM, (mit), sizeof(MITMtx), G_MV_MMTX, 0x80) |
See SPNormalsMode. mtx is the address of a MITMtx (M inverse transpose).
The matrix values must be scaled down so that the matrix norm is <= 1, i.e. multiplying this matrix by any vector length <= 1 must produce a vector with length <= 1. Normally, M scales things down substantially, so M inverse transpose natively would scale them up substantially; you need to apply a constant scale to counteract this. One easy way to do this is compute M inverse transpose normally, then scale it so until the maximum absolute value of any element is 0.5. Because of this scaling, you can also skip the part of the inverse computation where you compute the determinant and divide by it, cause you're going to rescale it arbitrarily anyway.
#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 gsSPNormalsMode | ( | mode | ) | gsMoveHalfwd(G_MW_FX, G_MWO_NORMALS_MODE, (mode) & 0xFF) |
Normals mode: How to handle transformation of vertex normals from model to world space for lighting.
If mode = G_NORMALS_MODE_FAST, transforms normals from model space to world space with the M matrix. This is correct if the object's transformation matrix stack only included translations, rotations, and uniform scale (i.e. same scale in X, Y, and Z); otherwise, if the transformation matrix has nonuniform scale or shear, the lighting on the object will be somewhat distorted.
If mode = G_NORMALS_MODE_AUTO, transforms normals from model space to world space with M inverse transpose, which renders lighting correctly for the object regardless of its transformation matrix (nonuniform scale or shear is okay). Whenever vertices are drawn with lighting enabled after M has been changed, computes M inverse transpose from M. This requires swapping to overlay 4 for M inverse transpose and then back to overlay 2 for lighting, which produces roughly 3.5 us of extra DRAM traffic. This performance penalty happens effectively once per matrix, which is once per normal object or separated limb or about twice per flex skeleton limb. So in a scene with lots of complex skeletons, this may have a noticeable performance impact.
If mode = G_NORMALS_MODE_MANUAL, uses M inverse transpose for correct results like G_NORMALS_MODE_AUTO, but it never internally computes M inverse transpose. You have to upload M inverse transpose to the RSP using SPMITMatrix every time you change the M matrix. The DRAM traffic for the extra matrix uploads is much smaller than the overlay swaps, so if you can efficiently compute M inverse transpose on the CPU, this may be faster than G_NORMALS_MODE_AUTO.
Recommended to leave this set to G_NORMALS_MODE_FAST generally, and only set it to G_NORMALS_MODE_AUTO for specific objects at times when they actually have a nonuniform scale. For example, G_NORMALS_MODE_FAST for Mario generally, but G_NORMALS_MODE_AUTO temporarily while he is squashed.
#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.
#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 | ( | n | ) | gsSPPopMatrixN( (n), 1) |
macro which pops one of the matrix stacks in a static display list.
It pops one of the matrix stacks. The model view stack can be up to 10 matrices deep. The projection stack is 1 matrix deep, so it cannot be popped.
n | is the flag field that identifies which matrix stack to pop:
|
#define gsSPPopMatrixN | ( | n, | |
num ) gsDma2p( G_POPMTX, (num) * 64, 64, 2, 0) |
macro which pops one of the matrix stacks in a static display list.
It pops num
of the matrix stacks. The model view stack can be up to 10 matrices deep. The projection stack is 1 matrix deep, so it cannot be popped.
n | 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. 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++, 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 ) _gsSP5Triangles(G_TRIFAN, v1, v2, v3, v4, v5, v6, v7) |
5 Triangles in fan arrangement. Draws the following tris: v1-v2-v3, v1-v3-v4, v1-v4-v5, v1-v5-v6, v1-v6-v7 Otherwise works the same as SPTriStrip, see above.
#define gsSPTriStrip | ( | v1, | |
v2, | |||
v3, | |||
v4, | |||
v5, | |||
v6, | |||
v7 ) _gsSP5Triangles(G_TRISTRIP, v1, v2, v3, v4, v5, v6, v7) |
5 Triangles in strip arrangement. Draws the following tris: v1-v2-v3, v3-v2-v4, v3-v4-v5, v5-v4-v6, v5-v6-v7 If you want to draw fewer tris, set indices to -1 from the right. e.g. to draw 4 tris, set v7 to -1; to draw 3 tris, set v6 to -1 Note that any set of 3 adjacent tris can be drawn with either SPTriStrip or SPTriFan. For arbitrary sets of 4 adjacent tris, four out of five of them can be drawn with one of SPTriStrip or SPTriFan. The 4-triangle formation which can't be drawn with either command looks like the Triforce.
#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 |
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