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.
You call
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
fungos: The
GX_TG_BUMPx
are to use with embossed style bump texture coordinates and for this type, the valid tgen_src parameters are onlyGX_TG_TEXCOORD0-6
(*note) and mtxsrc is not used.The
GX_TG_SRTG
is 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 parametertgen_src
can be onlyGX_TG_COLOR0
orGX_TG_COLOR1
and
mtxsrc
is 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_TEXCOORDx
andGX_TG_TEXx
. MaybeGX_TG_TEX0-7
are the real texture coordinates input (note, this is from 0 to 7, that are 8 total textures) andGX_TG_TEXCOORD0-6
may be only used with thatGX_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 a
, b
, c
and d
, the output is called out
.
GX_SetNumTevStages
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.
GX_SetTevColorOp
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_TEVSTAGE0
to 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
clamp
is GX_TRUE
or GX_FALSE
.
After the tev operation has executed, the output is modified by bias
, scale
and 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 GX_TEV_ADD
or GX_TEV_SUB
, bias
, scale
and 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.
fungos:
GX_TEV_ADD
you got right andGX_TEV_SUB
a 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 ‘-’ (GX_TEV_SUB
).there are two cases, when using
GX_SetTevColorOp
you’re right (GX_TEV_COMP_RGB8_[GT|EQ]
), it will compare each component independently, but when usingGX_SetTevAlphaOp
it will compare the alpha component only. But the rules is slightly different than what you said (lets useGX_TEV_COMP_R8_GT
as 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)
GX_SetTevColorIn
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.
a
, b
, c
and d
specify which is loaded into a
, b
, c
and 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 b
, c
or 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 GX_SetTevOrder
?).
GX_SetTevOrder
If you use GX_CC_TEXC
, GX_CC_TEXA
, GX_CC_RASC
or GX_CC_RASA
in 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.
texmap
is
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 tevstage
. Pass 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 COLOR0
and COLOR1
are simply the primary and secondary vertex color, but I don’t know how the bump stuff works.
GX_SetTevKColorSel
If 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.
GX_SetTevColor
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.
r
, g
, b
and 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.
By thakis@users.sourceforge.net.