Update 20090502: I received a mail from “fungos” with a few corrections. I don’t have time to check what (s)he writes, but it sounds right, so I include the feedback as blockquotes below.
This describes how some parts of Flipper (the graphics chip of the Gamecube). I’ll probably add more descriptions if you want. I don’t describe how indirect tev works, because I don’t know that. If you know, tell me :-) If some of the things discussed in this document are wrong, let me know as well.
This document would not have been possible without libogc, especially gx.h and gx.c.
You don’t always need explicit texture coordinates if you want to use a texture. For example, you could compute texture coordinates from the vertex positions, or you could use one set of texture coordinates for different texture units. Texgen (‘texture coord generation’) is a means to accomplish that. This section discusses the texgen unit.
void GX_SetNumTexGens(u32 nr);
to specify for how many texture units you want to enable texgen for.
To control how texture coordinates are computed, you call
void GX_SetTexCoordGen(u16 texcoord,u32 tgen_typ,u32 tgen_src,u32 mtxsrc);
texcoord can be one of
GX_TEXCOORD0 GX_TEXCOORD1 GX_TEXCOORD2 GX_TEXCOORD3 GX_TEXCOORD4 GX_TEXCOORD5 GX_TEXCOORD6 GX_TEXCOORD7
and specifies texgen for which texture coordinate you’re going to configure.
tgen_typ is used to specify how texture coordinates are generated from the inputs. I don’t really know how all these functions work, but I provide a few guesses. I have no idea at all how the
BUMP stuff works.
GX_TG_MTX3x4 output = 3x4matrix * input (u, v, w generated) GX_TG_MTX2x4 output = 2x4matrix * input (u, v generated) GX_TG_BUMP0 GX_TG_BUMP1 GX_TG_BUMP2 GX_TG_BUMP3 GX_TG_BUMP4 GX_TG_BUMP5 GX_TG_BUMP6 GX_TG_BUMP7 GX_TG_SRTG I believe this is 'spherical reflection coord generation' like implemented in OpenGL
GX_TG_BUMPxare to use with embossed style bump texture coordinates and for this type, the valid tgen_src parameters are only
GX_TG_TEXCOORD0-6(*note) and mtxsrc is not used.
GX_TG_SRTGis way different than what you thought :) it uses components from color channel output, that is, the S texture coordinate is generated by using R red component and the T texture coordinate is generated by using G green component (both 8-bits), the parameter
tgen_srccan be only
mtxsrcis not used too. I think this is used to generate cel-shading/toon effect from a 1D texture.
tgen_src is used to tell the texgen unit what is the input for the generation function. I don’t know what the
TG_TEXn constants are for, the rest is self-explanatory.
GX_TG_POS GX_TG_NRM GX_TG_BINRM GX_TG_TANGENT GX_TG_TEX0 GX_TG_TEX1 GX_TG_TEX2 GX_TG_TEX3 GX_TG_TEX4 GX_TG_TEX5 GX_TG_TEX6 GX_TG_TEX7 GX_TG_TEXCOORD0 GX_TG_TEXCOORD1 GX_TG_TEXCOORD2 GX_TG_TEXCOORD3 GX_TG_TEXCOORD4 GX_TG_TEXCOORD5 GX_TG_TEXCOORD6 GX_TG_COLOR0 GX_TG_COLOR1
fungos: I think that there is a confusion between
GX_TG_TEX0-7are the real texture coordinates input (note, this is from 0 to 7, that are 8 total textures) and
GX_TG_TEXCOORD0-6may be only used with that
GX_TG_BUMPx. I don’t understand that yet and it is very hard to follow it.
mtxsrc is one of the matrix identifiers and may be one of
GX_PNMTX0 GX_PNMTX1 GX_PNMTX2 GX_PNMTX3 GX_PNMTX4 GX_PNMTX5 GX_PNMTX6 GX_PNMTX7 GX_PNMTX8 GX_PNMTX9 GX_TEXMTX0 GX_TEXMTX1 GX_TEXMTX2 GX_TEXMTX3 GX_TEXMTX4 GX_TEXMTX5 GX_TEXMTX6 GX_TEXMTX7 GX_TEXMTX8 GX_TEXMTX9 GX_IDENTITY GX_DTTMTX0 GX_DTTMTX1 GX_DTTMTX2 GX_DTTMTX3 GX_DTTMTX4 GX_DTTMTX5 GX_DTTMTX6 GX_DTTMTX7 GX_DTTMTX8 GX_DTTMTX9 GX_DTTMTX10 GX_DTTMTX11 GX_DTTMTX12 GX_DTTMTX13 GX_DTTMTX14 GX_DTTMTX15 GX_DTTMTX16 GX_DTTMTX17 GX_DTTMTX18 GX_DTTMTX19 GX_DTTIDENTITY
(perhaps not all of them are valid)
There is another function to control texgen that provides a few more options:
void GX_SetTexCoordGen2(u16 texcoord,u32 tgen_typ,u32 tgen_src,u32 mtxsrc, u32 normalize,u32 postmtx);
This is like
GX_SetTexCoordGen with a few additional parameters. I don’t know what this does (I guess it does the same as
GX_SetTexCoordGen, but optionally normalizes the generated texture coordinate to unit length and multiplies it with a post processing matrix).
The Gamecube doesn’t really have a programmable graphics pipeline, only a set of 16 TEV stages that can combine colors in a fixed way. A single TEV stage has 4 inputs and 1 output. The output is calculated by selecting one of several fixed functions on the inputs. In only discuss the ‘color’ functions, the alpha functions are very similar. You should be able to figure them out yourself by looking at gx.h.
In the remainder, the 4 inputs of a tev a called
d, the output is called
You configure how many of the 16 tev stages are used by calling
void GX_SetNumTevStages(u8 num);
For color computation, the first
num tev stages are executed. The color which ends up in the ‘previous color register’ at the end of these
num stages is used as the color for the current pixel.
The central function to configure a tev unit is
void GX_SetTevColorOp(u8 tevstage,u8 tevop,u8 tevbias,u8 tevscale,u8 clamp,u8 tevregid);
tevstage is one of
GX_TEVSTAGE15 and identifies the tev unit you want to configure.
tevop is one of the following functions:
GX_TEV_ADD out = a*(1 - c) + b*c + d GX_TEV_SUB out = -a*(1 - c) - b*c + d (not 100% sure with this, please confirm) GX_TEV_COMP_R8_GT if(a.r > b.r) out = c; else out = d; GX_TEV_COMP_R8_EQ if(a.r == b.r) out = c; else out = d; GX_TEV_COMP_GR16_GT GX_TEV_COMP_GR16_EQ same as r8, but use gr as 16 bit value for comparing GX_TEV_COMP_BGR24_GT GX_TEV_COMP_BGR24_EQ same as r8, but use bgr as 24 bit value for comparing GX_TEV_COMP_RGB8_GT GX_TEV_COMP_RGB8_EQ Not sure with this, but I think this compares rgb independently. Can you confirm this? (this is not done correctly in bmdview's source)
and controls which operation the tev stage will perform (discussed in more detail below).
tevbias is one of
GX_TB_ZERO GX_TB_ADDHALF GX_TB_SUBHALF
tevscale is one of
GX_CS_SCALE_1 GX_CS_SCALE_2 GX_CS_SCALE_4 GX_CS_DIVIDE_2
After the tev operation has executed, the output is modified by
clamp as follows (in this order):
out = out + bias; //for each component; bias is 0, 0.5 or -0.5 out = out * scale; //scale is 1, 2, 4 or 0.5 if(clamp) clamp out to [0.0, 1.0] in each component
If tev op is not
clamp shall be ignored.
tevregid is one of
GX_TEVPREV regPrev.rgb = out.rgb GX_TEVREG0 reg0.rgb = out.rgb GX_TEVREG1 reg1.rgb = out.rgb GX_TEVREG2 reg2.rgb = out.rgb
and specifies in which register
out is written.
GX_TEV_ADDyou got right and
GX_TEV_SUBa little wrong, we can generalize it this way:
output register = (d (tevop) ((1.0-c)*a + c*b) + tevbias) * tevscale
where (tevop) is ‘+’ (
GX_TEV_ADD) or ‘-’ (
there are two cases, when using
GX_SetTevColorOpyou’re right (
GX_TEV_COMP_RGB8_[GT|EQ]), it will compare each component independently, but when using
GX_SetTevAlphaOpit will compare the alpha component only. But the rules is slightly different than what you said (lets use
GX_TEV_COMP_R8_GTas a sample):
if (a.r > b.r) out = c; else out = d;
if think is this way:
d + ((a.r > b.r) ? c : 0)
So, the entire rule table is:
GX_TEV_COMP_R8_GT d + ((a.r > b.r) ? c : 0) GX_TEV_COMP_R8_EQ d + ((a.r == b.r) ? c : 0) GX_TEV_COMP_GR16_GT d + ((a.gr > b.gr) ? c : 0) GX_TEV_COMP_GR16_EQ d + ((a.gr == b.gr) ? c : 0) GX_TEV_COMP_BGR24_GT d + ((a.bgr > b.bgr]) ? c : 0) GX_TEV_COMP_BGR24_EQ d + ((a.bgr == b.bgr) ? c : 0) GX_TEV_COMP_RGB8_GT r: d.r + ((a.r > b.r) ? c.r : 0) g: d.g + ((a.g > b.g) ? c.g : 0) b: d.b + ((a.b > b.b) ? c.b : 0) GX_TEV_COMP_RGB8_EQ r: d.r + ((a.r == b.b) ? c.r : 0) g: d.g + ((a.g == b.b) ? c.g : 0) b: d.b + ((a.b == b.b) ? c.b : 0) GX_TEV_COMP_A8_GT d + ((a.a > b.a) ? c : 0) GX_TEV_COMP_A8_EQ d + ((a.a > b.a) ? c : 0)
To control which are the 4 inputs of a tev stage, you call
void GX_SetTevColorIn(u8 tevstage,u8 a,u8 b,u8 c,u8 d);
tevstage is again a tev stage identifier.
d specify which is loaded into
d in the equations above:
GX_CC_CPREV a.rgb = regPrev.rgb GX_CC_APREV a.rgb = regPrev.aaa GX_CC_C0 a.rgb = reg0.rgb GX_CC_A0 a.rgb = reg0.aaa GX_CC_C1 a.rgb = reg1.rgb GX_CC_A1 a.rgb = reg1.aaa GX_CC_C2 a.rgb = reg2.rgb GX_CC_A2 a.rgb = reg2.aaa GX_CC_TEXC a.rgb = textureBoundToTev.rgb (see GX_SetTevOrder below) GX_CC_TEXA a.rgb = textureBoundToTev.rgb (see GX_SetTevOrder below) GX_CC_RASC a.rgb = rasColor.rgb (see below) GX_CC_RASA a.rgb = rasColor.aaa (see below) GX_CC_ONE a.rgb = (1.0, 1.0, 1.0) GX_CC_HALF a.rgb = (0.5, 0.5, 0.5) GX_CC_KONST a.rgb = konst (see GX_SetTevKColorSel below) GX_CC_ZERO a.rgb = (0.0, 0.0, 0.0)
In the list above,
a is of course only used as an example — could be
d as well :-P
rasColor is the color which is passed from the ‘vertex shader’, usually this is simply the vertex color. More information later if you need it (TODO: see
If you use
GX_SetTevColorIn, you use
void GX_SetTevOrder(u8 tevstage,u8 texcoord,u32 texmap,u8 color);
to specify which texture is used for the texture input and which color for the ras color.
tevstage is the usual tev stage identifier.
texcoord is one of
GX_TEXCOORD0 GX_TEXCOORD1 GX_TEXCOORD2 GX_TEXCOORD3 GX_TEXCOORD4 GX_TEXCOORD5 GX_TEXCOORD6 GX_TEXCOORD7 GX_TEXCOORDNULL
and tells the tev unit which texture coordinate channel is used to access the texture. Note: The texture coordinate after texgen is used (texgen is not described in this document yet — for now pretend that this makes no difference). Use
GX_TEXCOORDNULL if you don’t need a texture input for the tev unit.
GX_TEXMAP0 GX_TEXMAP1 GX_TEXMAP2 GX_TEXMAP3 GX_TEXMAP4 GX_TEXMAP5 GX_TEXMAP6 GX_TEXMAP7 GX_TEXMAP_NULL
and specifies which texture image should be used as input for
GX_TEXMAP_NULL if the texture input is not used in the current tev.
I’m pretty sure that
color is one of
GX_COLOR0 GX_COLOR1 GX_ALPHA0 GX_ALPHA1 GX_COLOR0A0 GX_COLOR1A1 GX_COLORZERO GX_BUMP GX_BUMPN GX_COLORNULL
but I don’t really know what all these constants mean. The do specify what is used as ras color in the tev. I guess
COLOR1 are simply the primary and secondary vertex color, but I don’t know how the bump stuff works.
GX_CC_KONST is used as one of the 4 inputs to a tev stage in
GX_SetTevColorIn, you specify which constant is used with
void GX_SetTevKColorSel(u8 tevstage,u8 sel);
tevstage is the usual tev stage identifier.
sel is one of
GX_TEV_KCSEL_1 GX_TEV_KCSEL_7_8 GX_TEV_KCSEL_3_4 GX_TEV_KCSEL_5_8 GX_TEV_KCSEL_1_2 GX_TEV_KCSEL_3_8 GX_TEV_KCSEL_1_4 GX_TEV_KCSEL_1_8 GX_TEV_KCSEL_K0 GX_TEV_KCSEL_K1 GX_TEV_KCSEL_K2 GX_TEV_KCSEL_K3 GX_TEV_KCSEL_K0_R GX_TEV_KCSEL_K1_R GX_TEV_KCSEL_K2_R GX_TEV_KCSEL_K3_R GX_TEV_KCSEL_K0_G GX_TEV_KCSEL_K1_G GX_TEV_KCSEL_K2_G GX_TEV_KCSEL_K3_G GX_TEV_KCSEL_K0_B GX_TEV_KCSEL_K1_B GX_TEV_KCSEL_K2_B GX_TEV_KCSEL_K3_B GX_TEV_KCSEL_K0_A GX_TEV_KCSEL_K1_A GX_TEV_KCSEL_K2_A GX_TEV_KCSEL_K3_A
The first few functions are simply the constants 1.0, 7.0/8.0, …, 1.0/8.0. The other constants select which of the 4 constant color registers are used. You load values in the constant color registers with XXX.
To load a color into a tev register, you call
void GX_SetTevColor(u8 tev_regid,GXColor color);
tev_regid is a tev register identifier — these registers are shared by all tev stages. Valid values are
TODO (I guess values from 0 to 3 for the 4 constant color registers)
color is the color to load into
tev_regid. Who would have thought?
No bmd/bdl file I know of uses the stuff described in this section. It’s not that important to emulate it I guess ;-)
You can swizzle the ras color and the tex colors before they are given to the tev stages. For this, there are four different swap mode tables,
GX_TEV_SWAP0 GX_TEV_SWAP1 GX_TEV_SWAP2 GX_TEV_SWAP3
which are configures via GX_SetTevSwapModeTable (below). To use these tables, you call
void GX_SetTevSwapMode(u8 tevstage,u8 ras_sel,u8 tex_sel);
tevstage is the usual tev stage id.
ras_sel is the swap mode table identifier to use for the ras color.
tex_sel is the swap mode table identifier to use for the tex color.
To configure what a swap mode table does, you call
void GX_SetTevSwapModeTable(u8 swapid,u8 r,u8 g,u8 b,u8 a);
swapid is a swap mode table identifier.
a specify which channel of the original color should be passed to the tev stage as r, g, b or a. Valid values are
GX_CH_RED GX_CH_GREEN GX_CH_BLUE GX_CH_ALPHA
TODO. For now,
void GX_SetTevOp(u8 tevstage,u8 mode) from gx.c is a good example.