Files
perfect_dark/src/game/tex.c
T

1180 lines
28 KiB
C

#include <ultra64.h>
#include "constants.h"
#include "game/dyntex.h"
#include "game/tex.h"
#include "game/texdecompress.h"
#include "bss.h"
#include "data.h"
#include "gbiex.h"
#include "textures.h"
#include "types.h"
#include "platform.h"
#define TXMODE_WRAP 0
#define TXMODE_CLAMP 1
#define TXMODE_MIRROR 2
struct tilestate {
bool inuse;
s32 format;
s32 depth;
s32 line;
s32 tmem;
s32 smode;
s32 tmode;
s32 masks;
s32 maskt;
s32 shifts;
s32 shiftt;
};
struct tilesize {
bool inuse;
s32 uls;
s32 ult;
s32 lrs;
s32 lrt;
};
s32 g_TexLutMode;
u32 var800ab5b4;
struct tilestate g_TexTileStates[8];
struct tilesize g_TexTileSizes[8];
#ifndef PLATFORM_N64
u32 g_TexFilter2D = G_TF_BILERP;
#endif
// Default
u16 g_SurfaceTypeDefaultSounds[] = { SFX_HIT_STONE_8087, SFX_HIT_STONE_8088 };
u8 g_SurfaceTypeDefaultTexes[] = { WALLHITTEX_BULLET2 };
struct surfacetype g_SurfaceTypeDefault = {
g_SurfaceTypeDefaultSounds, g_SurfaceTypeDefaultTexes, 2, 1,
};
// Stone
u16 g_SurfaceTypeStoneSounds[] = { SFX_HIT_STONE_8087, SFX_HIT_STONE_8088 };
u8 g_SurfaceTypeStoneTexes[] = { WALLHITTEX_BULLET1 };
struct surfacetype g_SurfaceTypeStone = {
g_SurfaceTypeStoneSounds, g_SurfaceTypeStoneTexes, 2, 1,
};
// Wood
u16 g_SurfaceTypeWoodSounds[] = { SFX_HIT_WOOD_807E, SFX_HIT_WOOD_807F };
u8 g_SurfaceTypeWoodTexes[] = { WALLHITTEX_WOOD };
struct surfacetype g_SurfaceTypeWood = {
g_SurfaceTypeWoodSounds, g_SurfaceTypeWoodTexes, 2, 1,
};
// Metal
u16 g_SurfaceTypeMetalSounds[] = { SFX_HIT_METAL_8079, SFX_HIT_METAL_807B };
u8 g_SurfaceTypeMetalTexes[] = { WALLHITTEX_METAL };
struct surfacetype g_SurfaceTypeMetal = {
g_SurfaceTypeMetalSounds, g_SurfaceTypeMetalTexes, 2, 1,
};
// Glass
u16 g_SurfaceTypeGlassSounds[] = { SFX_HIT_GLASS };
u8 g_SurfaceTypeGlassTexes[] = { WALLHITTEX_GLASS1, WALLHITTEX_GLASS2, WALLHITTEX_GLASS3 };
struct surfacetype g_SurfaceTypeGlass = {
g_SurfaceTypeGlassSounds, g_SurfaceTypeGlassTexes, 1, 3,
};
// Snow
u16 g_SurfaceTypeSnowSounds[] = { SFX_HIT_SNOW };
u8 g_SurfaceTypeSnowTexes[] = { WALLHITTEX_BULLET1 };
struct surfacetype g_SurfaceTypeSnow = {
g_SurfaceTypeSnowSounds, g_SurfaceTypeSnowTexes, 1, 1,
};
// Dirt
u16 g_SurfaceTypeDirtSounds[] = { SFX_HIT_DIRT_8084, SFX_HIT_DIRT_8085 };
u8 g_SurfaceTypeDirtTexes[] = { WALLHITTEX_SOFT };
struct surfacetype g_SurfaceTypeDirt = {
g_SurfaceTypeDirtSounds, g_SurfaceTypeDirtTexes, 2, 1,
};
// Mud
u16 g_SurfaceTypeMudSounds[] = { SFX_HIT_MUD_8081, SFX_HIT_MUD_8082, SFX_HIT_MUD_8083 };
u8 g_SurfaceTypeMudTexes[] = { WALLHITTEX_SOFT };
struct surfacetype g_SurfaceTypeMud = {
g_SurfaceTypeMudSounds, g_SurfaceTypeMudTexes, 3, 1,
};
// Tile
u16 g_SurfaceTypeTileSounds[] = { SFX_HIT_TILE };
u8 g_SurfaceTypeTileTexes[] = { WALLHITTEX_BULLET1 };
struct surfacetype g_SurfaceTypeTile = {
g_SurfaceTypeTileSounds, g_SurfaceTypeTileTexes, 1, 1,
};
// Metal obj
u16 g_SurfaceTypeMetalObjSounds[] = { SFX_HIT_METALOBJ_8089, SFX_HIT_METALOBJ_808A };
u8 g_SurfaceTypeMetalObjTexes[] = { WALLHITTEX_BULLET1, WALLHITTEX_BULLET2 };
struct surfacetype g_SurfaceTypeMetalObj = {
g_SurfaceTypeMetalObjSounds, g_SurfaceTypeMetalObjTexes, 2, 2,
};
// Chr
u16 g_SurfaceTypeChrSounds[] = { SFX_HIT_CHR };
u8 g_SurfaceTypeChrTexes[] = { WALLHITTEX_SOFT };
struct surfacetype g_SurfaceTypeChr = {
g_SurfaceTypeChrSounds, g_SurfaceTypeChrTexes, 1, 1,
};
// Glass XLU
u16 g_SurfaceTypeGlassXluSounds[] = { SFX_HIT_GLASS };
u8 g_SurfaceTypeGlassXluTexes[] = { WALLHITTEX_GLASS1, WALLHITTEX_GLASS2, WALLHITTEX_GLASS3 };
struct surfacetype g_SurfaceTypeGlassXlu = {
g_SurfaceTypeGlassXluSounds, g_SurfaceTypeGlassXluTexes, 1, 3,
};
// None
struct surfacetype g_SurfaceTypeNone = {
NULL, NULL, 0, 0,
};
// Shallow water
u16 g_SurfaceTypeShallowWaterSounds[] = { SFX_HIT_WATER };
u8 g_SurfaceTypeShallowWaterTexes[] = { WALLHITTEX_WATER };
struct surfacetype g_SurfaceTypeShallowWater = {
g_SurfaceTypeShallowWaterSounds, g_SurfaceTypeShallowWaterTexes, 1, 1,
};
// Deep water
u16 g_SurfaceTypeDeepWaterSounds[] = { SFX_HIT_WATER };
u8 g_SurfaceTypeDeepWaterTexes[] = { WALLHITTEX_WATER };
struct surfacetype g_SurfaceTypeDeepWater = {
g_SurfaceTypeDeepWaterSounds, g_SurfaceTypeDeepWaterTexes, 1, 1,
};
struct surfacetype *g_SurfaceTypes[] = {
/* 0*/ &g_SurfaceTypeDefault,
/* 1*/ &g_SurfaceTypeStone,
/* 2*/ &g_SurfaceTypeWood,
/* 3*/ &g_SurfaceTypeMetal,
/* 4*/ &g_SurfaceTypeGlass,
/* 5*/ &g_SurfaceTypeShallowWater,
/* 6*/ &g_SurfaceTypeSnow,
/* 7*/ &g_SurfaceTypeDirt,
/* 8*/ &g_SurfaceTypeMud,
/* 9*/ &g_SurfaceTypeTile,
/*10*/ &g_SurfaceTypeMetalObj,
/*11*/ &g_SurfaceTypeChr,
/*12*/ &g_SurfaceTypeGlassXlu,
/*13*/ &g_SurfaceTypeNone,
/*14*/ &g_SurfaceTypeDeepWater,
};
char *var80084494[] = {
"default",
"stone",
"wood",
"metal",
"glass",
"shallow water",
"snow",
"dirt",
"mud",
"tile",
"metalobj",
"chr",
"glass xlu",
"no hit",
"deep water",
};
const char var7f1b7c24[] = "";
const char var7f1b7c28[] = "";
const char var7f1b7c2c[] = "";
bool g_TexPipeSynced = false;
u32 var800844d4 = 0x00000000;
u32 var800844d8 = 0x00000000;
u32 var800844dc = 0x00000000;
u32 var800844e0 = 0x00000103;
u32 var800844e4 = 0x00000000;
u32 var800844e8 = 0x00000000;
u32 var800844ec = 0x00000000;
void tex0f173a00(void)
{
// empty
}
void texResetTiles(void)
{
s32 i;
for (i = 0; i < ARRAYCOUNT(g_TexTileStates); i++) {
g_TexTileStates[i].inuse = false;
g_TexTileSizes[i].inuse = false;
}
g_TexLutMode = -1;
}
bool texTrySetLutMode(u32 lutmode)
{
if (g_TexLutMode == lutmode) {
return false;
}
g_TexLutMode = lutmode;
return true;
}
bool texTrySetTileState(s32 tile, s32 format, s32 depth, s32 line, s32 tmem, s32 smode, s32 tmode, s32 masks, s32 maskt, s32 shifts, s32 shiftt)
{
bool changed = false;
if (g_TexTileStates[tile].inuse == false
|| format != g_TexTileStates[tile].format
|| depth != g_TexTileStates[tile].depth
|| line != g_TexTileStates[tile].line
|| tmem != g_TexTileStates[tile].tmem
|| smode != g_TexTileStates[tile].smode
|| tmode != g_TexTileStates[tile].tmode
|| masks != g_TexTileStates[tile].masks
|| maskt != g_TexTileStates[tile].maskt
|| shifts != g_TexTileStates[tile].shifts
|| shiftt != g_TexTileStates[tile].shiftt) {
changed = true;
g_TexTileStates[tile].inuse = true;
g_TexTileStates[tile].format = format;
g_TexTileStates[tile].depth = depth;
g_TexTileStates[tile].line = line;
g_TexTileStates[tile].tmem = tmem;
g_TexTileStates[tile].smode = smode;
g_TexTileStates[tile].tmode = tmode;
g_TexTileStates[tile].masks = masks;
g_TexTileStates[tile].maskt = maskt;
g_TexTileStates[tile].shifts = shifts;
g_TexTileStates[tile].shiftt = shiftt;
}
return changed;
}
bool texTrySetTileSize(s32 tile, s32 uls, s32 ult, s32 lrs, s32 lrt)
{
bool changed = false;
if (g_TexTileSizes[tile].inuse == false
|| uls != g_TexTileSizes[tile].uls
|| ult != g_TexTileSizes[tile].ult
|| lrs != g_TexTileSizes[tile].lrs
|| lrt != g_TexTileSizes[tile].lrt) {
changed = true;
g_TexTileSizes[tile].inuse = true;
g_TexTileSizes[tile].uls = uls;
g_TexTileSizes[tile].ult = ult;
g_TexTileSizes[tile].lrs = lrs;
g_TexTileSizes[tile].lrt = lrt;
}
return changed;
}
s32 texGetWidthAtLod(struct tex *tex, s32 lod)
{
s32 i;
s32 width = tex->width;
if (lod == 0) {
return width;
}
if (tex->hasloddata) {
for (i = 0; i < g_TexCacheCount; i++) {
if (tex->texturenum == g_TexCacheItems[i].texturenum) {
return g_TexCacheItems[i].widths[lod - 1];
}
}
return 1;
}
for (i = 0; i < lod; i++) {
width = (width + 1) >> 1;
}
return width;
}
s32 texGetHeightAtLod(struct tex *tex, s32 lod)
{
s32 i;
s32 height = tex->height;
if (lod == 0) {
return height;
}
if (tex->hasloddata) {
for (i = 0; i < g_TexCacheCount; i++) {
if (tex->texturenum == g_TexCacheItems[i].texturenum) {
return g_TexCacheItems[i].heights[lod - 1];
}
}
return 1;
}
for (i = 0; i < lod; i++) {
height = (height + 1) >> 1;
}
return height;
}
s32 texGetLineSizeInBytes(struct tex *tex, s32 lod)
{
s32 depth = tex->depth;
s32 width = texGetWidthAtLod(tex, lod);
if (depth == G_IM_SIZ_32b) {
return (width + 3) / 4;
}
if (depth == G_IM_SIZ_16b) {
return (width + 3) / 4;
}
if (depth == G_IM_SIZ_8b) {
return (width + 7) / 8;
}
return (width + 15) / 16;
}
s32 texGetSizeInBytes(struct tex *tex, s32 lod)
{
return texGetHeightAtLod(tex, lod) * texGetLineSizeInBytes(tex, lod);
}
void texGetDepthAndSize(struct tex *tex, s32 *deptharg, s32 *lenarg)
{
s32 depth = tex->depth;
s32 numlods = tex->numlods ? tex->numlods : 1;
s32 lod;
*lenarg = 0;
if (depth == G_IM_SIZ_32b) {
*deptharg = G_IM_SIZ_32b;
} else if (depth == G_IM_SIZ_16b) {
*deptharg = G_IM_SIZ_16b;
} else if (depth == G_IM_SIZ_8b) {
*deptharg = G_IM_SIZ_16b;
} else {
*deptharg = G_IM_SIZ_16b;
}
for (lod = 0; lod < numlods; lod++) {
*lenarg += texGetSizeInBytes(tex, lod) * 4;
}
}
s32 texDimensionToMask(s32 dimension)
{
s32 i = 0;
dimension--;
while (dimension > 0 && i < 8) {
dimension >>= 1;
i++;
}
return i;
}
s32 texModeToGbiMode(s32 txmode)
{
if (txmode == TXMODE_CLAMP) {
return G_TX_CLAMP;
}
if (txmode == TXMODE_MIRROR) {
return G_TX_MIRROR;
}
return G_TX_WRAP;
}
Gfx *texWriteTileFromDefinition(Gfx *gdl, struct tex *tex, s32 offset, s32 shifts, s32 shiftt, s32 min)
{
struct texture *s0 = &g_Textures[tex->texturenum];
s32 masks;
s32 maskt;
s32 line;
u32 stack[4];
s32 uls;
s32 ult;
s32 lrs;
s32 lrt;
masks = texDimensionToMask(tex->width);
maskt = texDimensionToMask(tex->height);
line = texGetLineSizeInBytes(tex, 0);
gDPSetPrimColorViaWord(gdl++, min, 0, 0xffffffff);
if (texTrySetLutMode(tex->lutmodeindex << G_MDSFT_TEXTLUT)) {
gDPSetTextureLUT(gdl++, tex->lutmodeindex << G_MDSFT_TEXTLUT);
}
if (texTrySetTileState(0, tex->gbiformat, tex->depth, line, s0->unk04_00 + line * s0->unk04_04, 0, 0, masks - s0->unk04_08, maskt - s0->unk04_0c, shifts, shiftt)) {
gDPSetTile(gdl++, tex->gbiformat, tex->depth, line, s0->unk04_00 + line * s0->unk04_04, 0, 0,
texModeToGbiMode(TXMODE_WRAP), maskt - s0->unk04_0c, shiftt,
texModeToGbiMode(TXMODE_WRAP), masks - s0->unk04_08, shifts);
}
uls = (offset == 2 && !tex->hasloddata ? 2 : 0) + 0;
ult = (offset == 2 && !tex->hasloddata ? 2 : 0) + 0;
lrs = (offset == 2 && !tex->hasloddata ? 2 : 0) + ((tex->width - 1) << 2);
lrt = (offset == 2 && !tex->hasloddata ? 2 : 0) + ((tex->height - 1) << 2);
if (texTrySetTileSize(0, uls, ult, lrs, lrt)) {
gDPSetTileSize(gdl++, 0, uls, ult, lrs, lrt);
}
return gdl;
}
/**
* Write a gSPTexture command with the lowest detail LOD number.
*
* If texcmd is provided then it will be used as a reference, otherwise a new
* command is written with some assumed arguments.
*
* If append is true then the command will be appended to the given gdl,
* otherwise the existing texcmd will be patched in place.
*/
Gfx *texWriteTextureCmd(Gfx *gdl, Gfx *texcmd, struct tex *tex, bool append)
{
s32 lod = tex->numlods ? tex->numlods - 1 : 0;
if (append) {
if (texcmd != NULL) {
// Copy the gSPTexture command, but with the new lod
u32 w0 = (texcmd->words.w0 & ~0x3800) | (lod << 11);
if (w0 != texcmd->words.w0) {
gdl->words.w0 = w0;
gdl->words.w1 = texcmd->words.w1;
gdl++;
}
} else {
gSPTexture(gdl++, 0xffff, 0xffff, lod, G_TX_RENDERTILE, G_ON);
}
} else {
texcmd->words.w0 &= ~0x3800;
texcmd->words.w0 |= lod << 11;
}
return gdl;
}
Gfx *texWriteLoadToTmemAddr(Gfx *gdl, struct tex *tex, s32 tmemoffset)
{
s32 depth;
s32 len;
texGetDepthAndSize(tex, &depth, &len);
if (tex->lutmodeindex == 0) {
gDPSetTextureImage(gdl++, tex->gbiformat, depth, 1, tex->data);
if (!g_TexPipeSynced) {
gDPPipeSync(gdl++);
g_TexPipeSynced = true;
}
if (depth == G_IM_SIZ_16b && tmemoffset == 0) {
gDPLoadSync(gdl++);
gDPLoadBlock(gdl++, G_TX_LOADTILE, 0, 0, len - 1, 0);
} else {
if (texTrySetTileState(5, 0, depth, 0, tmemoffset, 0, 0, 0, 0, 0, 0)) {
gDPSetTile(gdl++, G_IM_FMT_RGBA, depth, 0, tmemoffset, 5, 0,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD);
}
gDPLoadSync(gdl++);
gDPLoadBlock(gdl++, 5, 0, 0, len - 1, 0);
}
} else {
gDPSetTextureImage(gdl++, tex->gbiformat, depth, 1, tex->data);
if (!g_TexPipeSynced) {
gDPPipeSync(gdl++);
g_TexPipeSynced = true;
}
if (depth == G_IM_SIZ_16b && tmemoffset == 0) {
gDPLoadSync(gdl++);
gDPLoadBlock(gdl++, G_TX_LOADTILE, 0, 0, len - 1, 0);
} else {
if (texTrySetTileState(5, 0, depth, 0, tmemoffset, 0, 0, 0, 0, 0, 0)) {
gDPSetTile(gdl++, G_IM_FMT_RGBA, depth, 0, tmemoffset, 5, 0,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD);
}
gDPLoadSync(gdl++);
gDPLoadBlock(gdl++, 5, 0, 0, len - 1, 0);
}
{
s32 tmp = len;
s32 a2 = (u32)(0x3ff - tex->unk0a) < len ? (u32)(0x3ff - tex->unk0a) : 0;
tmp -= a2;
gDPLoadSync(gdl++);
gDPLoadTLUT06(gdl++, tmp, a2, tex->unk0a + tmp, a2);
}
}
return gdl;
}
Gfx *texWriteTileLods(Gfx *gdl, struct tex *tex, s32 smode, s32 tmode, s32 offset, s32 starttile, s32 numlodsarg, u32 tmemoffsetarg)
{
u32 tmemoffset;
s32 numlods;
s32 tile;
tmemoffset = tmemoffsetarg;
numlods = tex->numlods;
if (numlodsarg >= 0 && numlodsarg < numlods) {
numlods = numlodsarg;
}
for (tile = starttile; tile < numlods + starttile && tile < 6; tile++) {
s32 stack[2];
s32 lod = tile - starttile;
s32 masks = texDimensionToMask(texGetWidthAtLod(tex, lod));
s32 maskt = texDimensionToMask(texGetHeightAtLod(tex, lod));
s32 line = texGetLineSizeInBytes(tex, lod);
s32 uls;
s32 ult;
s32 lrs;
s32 lrt;
s32 bytes = texGetSizeInBytes(tex, lod);
bool hasloddata = tex->hasloddata;
if (texTrySetLutMode(tex->lutmodeindex << G_MDSFT_TEXTLUT)) {
gDPSetTextureLUT(gdl++, tex->lutmodeindex << G_MDSFT_TEXTLUT);
}
if (texTrySetTileState(tile, tex->gbiformat, tex->depth, line, tmemoffset, smode, tmode, masks, maskt, lod, lod)) {
gDPSetTile(gdl++, tex->gbiformat, tex->depth, line, tmemoffset, tile, 0,
texModeToGbiMode(tmode), maskt, lod,
texModeToGbiMode(smode), masks, tile - starttile);
}
uls = (offset == 2 && hasloddata == false ? 2 : 0) + 0;
ult = (offset == 2 && hasloddata == false ? 2 : 0) + 0;
lrs = ((texGetWidthAtLod(tex, lod) - 1) << 2) + (offset == 2 && hasloddata == false ? 2 : 0);
lrt = ((texGetHeightAtLod(tex, lod) - 1) << 2) + (offset == 2 && hasloddata == false ? 2 : 0);
if (texTrySetTileSize(tile, uls, ult, lrs, lrt)) {
gDPSetTileSize(gdl++, tile, uls, ult, lrs, lrt);
}
tmemoffset += bytes;
}
return gdl;
}
Gfx *texWriteLoadToTmemZero(Gfx *gdl, struct tex *tex)
{
s32 depth;
s32 len;
texGetDepthAndSize(tex, &depth, &len);
if (tex->lutmodeindex == 0) {
gDPSetTextureImage(gdl++, tex->gbiformat, depth, 1, tex->data);
if (!g_TexPipeSynced) {
gDPPipeSync(gdl++);
g_TexPipeSynced = true;
}
if (depth == G_IM_SIZ_16b) {
gDPLoadSync(gdl++);
gDPLoadBlock(gdl++, G_TX_LOADTILE, 0, 0, len - 1, 0);
} else {
if (texTrySetTileState(5, 0, depth, 0, 0, 0, 0, 0, 0, 0, 0)) {
gDPSetTile(gdl++, G_IM_FMT_RGBA, depth, 0, 0x0000, 5, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD);
}
gDPLoadSync(gdl++);
gDPLoadBlock(gdl++, 5, 0, 0, len - 1, 0);
}
} else {
gDPSetTextureImage(gdl++, tex->gbiformat, depth, 1, tex->data);
if (!g_TexPipeSynced) {
gDPPipeSync(gdl++);
g_TexPipeSynced = true;
}
if (depth == G_IM_SIZ_16b) {
gDPLoadSync(gdl++);
gDPLoadBlock(gdl++, G_TX_LOADTILE, 0, 0, len - 1, 0);
} else {
if (texTrySetTileState(5, 0, depth, 0, 0, 0, 0, 0, 0, 0, 0)) {
gDPSetTile(gdl++, G_IM_FMT_RGBA, depth, 0, 0x0000, 5, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD);
}
gDPLoadSync(gdl++);
gDPLoadBlock(gdl++, 5, 0, 0, len - 1, 0);
}
{
s32 tmp = len;
s32 a2 = (u32)(0x3ff - tex->unk0a) < len ? (u32)(0x3ff - tex->unk0a) : 0;
tmp -= a2;
gDPLoadSync(gdl++);
gDPLoadTLUT06(gdl++, tmp, a2, tex->unk0a + tmp, a2);
}
}
return gdl;
}
Gfx *texWriteTile(Gfx *gdl, struct tex *tex, s32 smode, s32 tmode, s32 offset, s32 tile)
{
s32 masks;
s32 maskt;
s32 line;
s32 uls;
s32 ult;
s32 lrs;
s32 lrt;
bool hasloddata;
masks = texDimensionToMask(tex->width);
maskt = texDimensionToMask(tex->height);
line = texGetLineSizeInBytes(tex, 0);
hasloddata = tex->hasloddata;
if (texTrySetLutMode(tex->lutmodeindex << G_MDSFT_TEXTLUT)) {
gDPSetTextureLUT(gdl++, tex->lutmodeindex << G_MDSFT_TEXTLUT);
}
if (texTrySetTileState(tile, tex->gbiformat, tex->depth, line, 0, smode, tmode, masks, maskt, 0, 0)) {
gDPSetTile(gdl++, tex->gbiformat, tex->depth, line, 0x0000, tile, 0,
texModeToGbiMode(tmode), maskt, G_TX_NOLOD,
texModeToGbiMode(smode), masks, G_TX_NOLOD);
}
uls = (offset == 2 && hasloddata == false ? 2 : 0) + 0;
ult = (offset == 2 && hasloddata == false ? 2 : 0) + 0;
lrs = (offset == 2 && hasloddata == false ? 2 : 0) + ((tex->width - 1) << 2);
lrt = (offset == 2 && hasloddata == false ? 2 : 0) + ((tex->height - 1) << 2);
if (texTrySetTileSize(tile, uls, ult, lrs, lrt)) {
gDPSetTileSize(gdl++, tile, uls, ult, lrs, lrt);
}
return gdl;
}
Gfx *texHandleType2(Gfx *gdl, struct tex *tex, s32 smode, s32 tmode, s32 offset, bool flag)
{
s32 tile = 0;
gdl = texWriteLoadToTmemAddr(gdl, tex, 0);
if (flag) {
gdl = texWriteTileLods(gdl, tex, smode, tmode, offset, tile, 1, 0);
tile++;
}
gdl = texWriteTileLods(gdl, tex, smode, tmode, offset, tile, -1, 0);
tile += tex->numlods;
if (!flag && tex->numlods == 1) {
gdl = texWriteTileLods(gdl, tex, smode, tmode, offset, tile, -1, 0);
tile += tex->numlods;
}
gDPPipeSync(gdl++);
g_TexPipeSynced = true;
return gdl;
}
Gfx *texHandleType1(Gfx *gdl, struct tex *tex1, s32 smode, s32 tmode, s32 offset, struct tex *tex2, s32 shifts, s32 shiftt, s32 min, bool flag)
{
s32 size = texGetSizeInBytes(tex2, 0);
s32 tile = 0;
gdl = texWriteLoadToTmemZero(gdl, tex2);
gDPTileSync(gdl++);
gdl = texWriteLoadToTmemAddr(gdl, tex1, size);
gdl = texWriteTileFromDefinition(gdl, tex2, offset, shifts, shiftt, min);
tile++;
if (flag) {
gdl = texWriteTileLods(gdl, tex1, smode, tmode, offset, tile, 1, size);
tile++;
}
gdl = texWriteTileLods(gdl, tex1, smode, tmode, offset, tile, -1, size);
tile += tex1->numlods;
if (!flag && tex1->numlods == 1) {
gdl = texWriteTileLods(gdl, tex1, smode, tmode, offset, tile, -1, size);
tile += tex1->numlods;
}
gDPPipeSync(gdl++);
g_TexPipeSynced = true;
return gdl;
}
Gfx *texHandleType0(Gfx *gdl, struct tex *tex, s32 smode, s32 tmode, s32 offset, s32 shifts, s32 shiftt, s32 min, bool flag)
{
s32 tile = 0;
gdl = texWriteLoadToTmemAddr(gdl, tex, 0);
gdl = texWriteTileFromDefinition(gdl, tex, offset, shifts, shiftt, min);
tile++;
if (flag) {
gdl = texWriteTileLods(gdl, tex, smode, tmode, offset, tile, 1, 0);
tile++;
}
gdl = texWriteTileLods(gdl, tex, smode, tmode, offset, tile, -1, 0);
tile += tex->numlods;
if (!flag && tex->numlods == 1) {
gdl = texWriteTileLods(gdl, tex, smode, tmode, offset, tile, -1, 0);
tile += tex->numlods;
}
gDPPipeSync(gdl++);
g_TexPipeSynced = true;
return gdl;
}
Gfx *texHandleType4(Gfx *gdl, struct tex *tex, s32 smode, s32 tmode, s32 offset)
{
gdl = texWriteLoadToTmemZero(gdl, tex);
gdl = texWriteTile(gdl, tex, smode, tmode, offset, 0);
gDPPipeSync(gdl++);
g_TexPipeSynced = true;
return gdl;
}
Gfx *texHandleType3(Gfx *gdl, struct tex *tex, s32 smode, s32 tmode, s32 offset)
{
gdl = texWriteLoadToTmemZero(gdl, tex);
gdl = texWriteTile(gdl, tex, smode, tmode, offset, 0);
gdl = texWriteTile(gdl, tex, smode, tmode, offset, 1);
gDPPipeSync(gdl++);
g_TexPipeSynced = true;
return gdl;
}
/**
* Read the instart displaylist and write the same displaylist to outstart,
* but substitute the special C0 commands with ones that the RSP will understand,
* and load textures from the ROM as they are found in the displaylist.
*
* The C0 command only exists in asset files. It has no implementation in the RSP.
* The command packs together several texture-related commands. It's likely done
* for ease of development, as it allows asset designers to spam its use everywhere
* in the displaylist rather than have to keep track of the RSP state.
* The code that expands the C0 commands keeps track of RSP state and avoids
* emitting duplicate or redundant commands.
*/
s32 texLoadFromGdl(Gfx *instart, s32 gdlsizeinbytes, Gfx *outstart, struct texpool *pool, u8 *vtxstart)
{
struct tex *tex1;
struct tex *tex2;
Gfx *texcmd;
s32 numcmdsremaining;
s32 smode;
s32 tmode;
s32 offset;
s32 shifts;
s32 shiftt;
s32 min;
bool flag;
s32 j;
bool appendtex;
u8 animated;
Gfx *ingdl;
Gfx *outgdl;
u32 spf4;
s32 texturenum;
s32 texturenum2;
bool spe8;
s32 spe4;
s32 spe0;
Vtx *spa0[16];
u8 sp90[16];
s32 i;
texcmd = NULL;
appendtex = true;
animated = false;
spe8 = false;
spe4 = false;
g_TexPipeSynced = false;
spf4 = 0;
ingdl = instart;
outgdl = outstart;
#ifdef PLATFORM_64BIT
numcmdsremaining = gdlsizeinbytes >> 4;
#else
numcmdsremaining = gdlsizeinbytes >> 3;
#endif
texResetTiles();
spe0 = dyntexHasRoom();
if (spe0) {
for (j = 0; j < ARRAYCOUNT(sp90); j++) {
sp90[j] = 0;
}
}
if (pool == NULL) {
pool = &g_TexSharedPool;
}
while (numcmdsremaining > 0) {
switch (ingdl->texture.cmd) {
case G_NOOP: // 0xc0, repurposed
spe4 = true;
if (animated) {
spe8 = true;
}
texturenum = ingdl->words.w1 & 0xfff;
flag = ingdl->words.w0 & 0x200;
texLoadFromTextureNum(texturenum, pool);
tex1 = texFindInPool(texturenum, pool);
if (tex1 != NULL) {
spf4 = tex1->unk0c_03;
} else {
spf4 = 0;
}
if (tex1 != NULL) {
outgdl = texWriteTextureCmd(outgdl, texcmd, tex1, appendtex);
appendtex = false;
animated = false;
switch (ingdl->unkc0.subcmd) {
case 0:
min = (ingdl->words.w1 >> 24) & 0xff;
smode = (ingdl->words.w0 >> 22) & 3;
tmode = (ingdl->words.w0 >> 20) & 3;
offset = (ingdl->words.w0 >> 18) & 3;
shifts = (ingdl->words.w0 >> 14) & 0xf;
shiftt = (ingdl->words.w0 >> 10) & 0xf;
outgdl = texHandleType0(outgdl, tex1, smode, tmode, offset, shifts, shiftt, min, flag);
break;
case 1:
texturenum2 = (ingdl->words.w1 >> 12) & 0xfff;
texLoadFromTextureNum(texturenum2, pool);
tex2 = texFindInPool(texturenum2, pool);
if (tex2 != NULL) {
min = (ingdl->words.w1 >> 24) & 0xff;
smode = (ingdl->words.w0 >> 22) & 3;
tmode = (ingdl->words.w0 >> 20) & 3;
offset = (ingdl->words.w0 >> 18) & 3;
shifts = (ingdl->words.w0 >> 14) & 0xf;
shiftt = (ingdl->words.w0 >> 10) & 0xf;
outgdl = texHandleType1(outgdl, tex1, smode, tmode, offset, tex2, shifts, shiftt, min, flag);
}
break;
case 2:
smode = (ingdl->words.w0 >> 22) & 3;
tmode = (ingdl->words.w0 >> 20) & 3;
offset = (ingdl->words.w0 >> 18) & 3;
outgdl = texHandleType2(outgdl, tex1, smode, tmode, offset, flag);
break;
case 3:
smode = (ingdl->words.w0 >> 22) & 3;
tmode = (ingdl->words.w0 >> 20) & 3;
offset = (ingdl->words.w0 >> 18) & 3;
outgdl = texHandleType3(outgdl, tex1, smode, tmode, offset);
break;
case 4:
smode = (ingdl->words.w0 >> 22) & 3;
tmode = (ingdl->words.w0 >> 20) & 3;
offset = (ingdl->words.w0 >> 18) & 3;
outgdl = texHandleType4(outgdl, tex1, smode, tmode, offset);
break;
}
if (spe0 != 0) {
// Deep Sea - green river under floor
if (texturenum == TEXTURE_06CB) {
dyntexSetCurrentType(DYNTEXTYPE_RIVER);
animated = true;
}
// Deep Sea - juice that flows inside SA megaweapon
// Attack Ship - juice that flows inside engine power node
if (texturenum == TEXTURE_0A6A) {
dyntexSetCurrentType(DYNTEXTYPE_POWERJUICE);
animated = true;
}
// Deep Sea - white rings around SA megaweapon node
// Attack Ship - white rings around engine power node
if (texturenum == TEXTURE_0A69) {
dyntexSetCurrentType(DYNTEXTYPE_POWERRING);
animated = true;
}
// Deep Sea - teleport
if (texturenum == TEXTURE_06E2) {
dyntexSetCurrentType(DYNTEXTYPE_TELEPORTAL);
animated = true;
}
// 01c7 - Air Base - distant water
// 01c7 - Investigation - puddle behind glass near shield
// 0dae - Chicago - canal
// 0dae - Villa - shallow water
// 0dae - Sewers (MP)
if (texturenum == TEXTURE_01C7 || texturenum == TEXTURE_0DAE) {
dyntexSetCurrentType(DYNTEXTYPE_RIVER);
animated = true;
}
// Air Force One - Monitor
if (texturenum == TEXTURE_029B) {
dyntexSetCurrentType(DYNTEXTYPE_MONITOR);
animated = true;
}
// Villa - deep water
// Complex - water
if (texturenum == TEXTURE_090F) {
dyntexSetCurrentType(DYNTEXTYPE_OCEAN);
animated = true;
}
// Attack Ship - triangular arrows
if (texturenum == TEXTURE_0A42) {
dyntexSetCurrentType(DYNTEXTYPE_ARROWS);
animated = true;
}
}
}
ingdl++;
break;
case G_VTX:
{
s32 start;
s32 count;
u32 offset;
Vtx *vtx;
if (spe0) {
start = ingdl->bytes[GFX_W0_BYTE(1)] & 0xf;
count = ingdl->vtx.unk08 + 1;
vtx = (Vtx *)(UNSEGADDR(ingdl->dma.addr) & 0x00ffffff);
for (i = start; i < start + count; i++) {
if (animated && sp90[i]) {
dyntexAddVertex(spa0[i]);
sp90[i] = 0;
}
spa0[i] = vtx;
vtx++;
}
}
if (spf4 && vtxstart) {
s32 count = ingdl->vtx.unk08 + 1;
Vtx *vtx;
s32 i;
offset = UNSEGADDR(ingdl->dma.addr) & 0x00ffffff;
vtx = (Vtx *) (vtxstart + offset);
for (i = 0; i < count; i++) {
vtx[i].s >>= 1;
vtx[i].t >>= 1;
}
}
}
*outgdl = *ingdl;
outgdl++;
ingdl++;
break;
case G_RDPPIPESYNC:
g_TexPipeSynced = true;
*outgdl = *ingdl;
outgdl++;
ingdl++;
break;
case (u8) G_TRI4:
case (u8) G_TRI1:
if (animated) {
if (ingdl->texture.cmd == (u8) G_TRI1) {
sp90[ingdl->tri.tri.v[GFX_TRI_VTX(0)] / 10] = 1;
sp90[ingdl->tri.tri.v[GFX_TRI_VTX(1)] / 10] = 1;
sp90[ingdl->tri.tri.v[GFX_TRI_VTX(2)] / 10] = 1;
} else {
Gfx *tmp = ingdl;
if (tmp->tri4.x1 != tmp->tri4.y1 || tmp->tri4.z1 != tmp->tri4.y1) {
sp90[tmp->tri4.x1] = 1;
sp90[tmp->tri4.y1] = 1;
sp90[tmp->tri4.z1] = 1;
}
if (tmp->tri4.x2 != tmp->tri4.y2 || tmp->tri4.z2 != tmp->tri4.y2) {
sp90[tmp->tri4.x2] = 1;
sp90[tmp->tri4.y2] = 1;
sp90[tmp->tri4.z2] = 1;
}
if (tmp->tri4.x3 != tmp->tri4.y3 || tmp->tri4.z3 != tmp->tri4.y3) {
sp90[tmp->tri4.x3] = 1;
sp90[tmp->tri4.y3] = 1;
sp90[tmp->tri4.z3] = 1;
}
if (tmp->tri4.x4 != tmp->tri4.y4 || tmp->tri4.z4 != tmp->tri4.y4) {
sp90[tmp->tri4.x4] = 1;
sp90[tmp->tri4.y4] = 1;
sp90[tmp->tri4.z4] = 1;
}
}
}
appendtex = true;
g_TexPipeSynced = false;
*outgdl = *ingdl;
outgdl++;
ingdl++;
break;
case (u8) G_TEXTURE:
spe4 = true;
if (animated) {
spe8 = true;
}
animated = false;
appendtex = false;
texcmd = outgdl;
*outgdl = *ingdl;
outgdl++;
ingdl++;
break;
case (u8) G_SETOTHERMODE_H:
*outgdl = *ingdl;
outgdl++;
ingdl++;
break;
default:
*outgdl = *ingdl;
outgdl++;
ingdl++;
break;
}
numcmdsremaining--;
if (spe4 || numcmdsremaining <= 0) {
spe4 = false;
if (spe8 || animated) {
s32 i;
spe8 = false;
for (i = 0; i < ARRAYCOUNT(sp90); i++) {
if (sp90[i]) {
dyntexAddVertex(spa0[i]);
sp90[i] = 0;
}
}
}
}
}
return (uintptr_t) outgdl - (uintptr_t) outstart;
}
void texCopyGdls(Gfx *src, Gfx *dst, s32 count)
{
#ifdef PLATFORM_64BIT
count = (count >> 4);
#else
count = (count >> 3);
#endif
src = src + (count - 1);
dst = dst + (count - 1);
while (count--) {
*dst = *src;
dst--;
src--;
}
}