papermario/src/imgfx.c

1907 lines
74 KiB
C

#include "common.h"
#include "ld_addrs.h"
#include "sprite.h"
#include "imgfx.h"
#if VERSION_JP // TODO remove once segments are split
extern Addr imgfx_data_ROM_START;
#endif
typedef union ImgFXIntVars {
s32 raw[2][4];
struct {
s32 anim[4];
s32 color[4];
} args;
// type-specific anim args (sharing first 0x10 bytes)
struct {
s32 type;
s32 interval; // always 1 or 2 in practice
s32 step; // always 1 in practice
} anim;
struct {
Vec3i mag;
} wavy;
// type-specific color args (sharing second 0x10 bytes)
struct {
char unk_00[0x10];
s32 r;
s32 g;
s32 b;
s32 a;
} color;
struct {
char unk_00[0x10];
s32 mode;
s32 noiseAmt;
char unk_18[4];
s32 alphaAmt;
} hologram;
struct {
char unk_00[0x10];
ImgFXOverlayTexture* pattern;
s32 alpha;
} overlay;
} ImgFXIntVars;
typedef union ImgFXFloatVars {
f32 raw[2][4];
// type-specific anim state (sharing first 0x10 bytes)
struct {
f32 curFrame;
f32 curIdx;
} anim;
struct {
f32 phase1;
f32 phase2;
f32 phase3;
} wavy;
// type-specific color state (sharing second 0x10 bytes)
struct {
char unk_00[0x10];
f32 posX;
f32 posY;
} overlay;
} ImgFXFloatVars;
typedef struct ImgFXState {
/* 0x00 */ u8 arrayIdx;
/* 0x01 */ u8 meshType;
/* 0x02 */ u8 renderType;
/* 0x03 */ u8 subdivX;
/* 0x04 */ u8 subdivY;
/* 0x05 */ s8 lastAnimCmd;
/* 0x06 */ s8 lastColorCmd;
/* 0x07 */ char unk_07[0x1];
/* 0x08 */ u16 firstVtxIdx;
/* 0x0A */ u16 lastVtxIdx;
/* 0x0C */ u16 unk_0C;
/* 0x0E */ s16 unk_0E;
/* 0x10 */ s16 nextIdx;
/* 0x14 */ s32 flags;
/* 0x18 */ char unk_18[0x4];
/* 0x1C */ ImgFXIntVars ints;
/* 0x3C */ ImgFXFloatVars floats;
/* 0x5C */ Color_RGBA8* colorBuf;
/* 0x60 */ u16 colorBufCount;
/* 0x62 */ char unk_62[0x2];
/* 0x64 */ u8* curAnimOffset;
/* 0x68 */ Vtx* vtxBufs[2];
/* 0x70 */ Gfx* gfxBufs[2];
/* 0x78 */ s32 otherModeL;
} ImgFXState; // size = 0x7C
typedef struct ImgFXCacheEntry {
/* 0x00 */ s32* data;
/* 0x04 */ u8 staleCooldownTimer;
/* 0x05 */ u8 usingContextualHeap;
/* 0x06 */ char unk_06[0x2];
} ImgFXCacheEntry; // size = 0x8
enum ImgFXAnimFlags {
IMGFX_ANIM_FLAG_ABSOLUTE_COORDS = 1, // image-relative (in percent) when unset
};
typedef struct ImgFXRenderMode {
/* 0x0 */ s32 mode1;
/* 0x4 */ s32 mode2;
/* 0x8 */ u8 flags; // only checks TRUE so far. some kind of switch?
} ImgFXRenderMode; // size = 0xC
typedef ImgFXState ImgFXInstanceList[MAX_IMGFX_INSTANCES];
extern HeapNode heap_spriteHead;
BSS ImgFXWorkingTexture ImgFXCurrentTexture;
BSS Vtx* ImgFXVtxBuffers[2];
BSS Vtx* imgfx_vtxBuf;
BSS ImgFXInstanceList* ImgFXInstances;
BSS s8 D_80156958[2];
BSS s32 D_8015695C;
BSS s32 D_80156960[2];
BSS s32 D_80156968[2];
BSS s8 D_80156970;
BSS s32 D_80156974[3];
BSS ImgFXAnimHeader ImgFXAnimHeaders[MAX_IMGFX_INSTANCES];
BSS ImgFXCacheEntry ImgFXDataCache[8];
// Data
ImgFXWorkingTexture* ImgFXCurrentTexturePtr = &ImgFXCurrentTexture;
u16 imgfx_vtxCount = 0;
Lights2 ImgFXLights = gdSPDefLights2(144, 144, 144, 255, 255, 255, 0, 0, 120, 255, 255, 255, 0, 0, 136);
Vp D_8014EE40 = {
.vp = {
.vscale = { 640, 480, 511, 0 },
.vtrans = { 640, 480, 511, 0 },
}
};
Vp D_8014EE50 = {
.vp = {
.vscale = { 640, 480, 511, 0 },
.vtrans = { 640, 480, 512, 0 },
}
};
u16 ImgFXVtxBufferCapacity = 300;
Gfx DefaultImgFXSetupGfx[] = {
gsSPClearGeometryMode(G_CULL_BOTH | G_LIGHTING),
gsSPSetGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH),
gsSPTexture(-1, -1, 0, G_TX_RENDERTILE, G_ON),
gsDPSetAlphaCompare(G_AC_NONE),
gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_ALPHADITHER, 18,
G_AD_DISABLE | G_CD_DISABLE | G_TC_FILT | G_TF_BILERP | G_TP_PERSP),
gsSPEndDisplayList(),
};
//TODO figure out bits
ImgFXRenderMode ImgFXRenderModes[] = {
[IMGFX_RENDER_DEFAULT] { 0x00441208, 0x00111208, 0 },
[IMGFX_RENDER_MULTIPLY_RGB] { 0x00441208, 0x00111208, 0 },
[IMGFX_RENDER_MULTIPLY_ALPHA] { 0x00404B40, 0x00104B40, IMGFX_RENDER_NO_OVERRIDE },
[IMGFX_RENDER_MULTIPLY_RGBA] { 0x00404B40, 0x00104B40, IMGFX_RENDER_NO_OVERRIDE },
[IMGFX_RENDER_MODULATE_PRIM_RGB] { 0x00441208, 0x00111208, 0 },
[IMGFX_RENDER_MODULATE_PRIM_RGBA] { 0x00404B40, 0x00104B40, IMGFX_RENDER_NO_OVERRIDE },
[IMGFX_RENDER_MULTIPLY_SHADE_RGB] { 0x00441208, 0x00111208, 0 },
[IMGFX_RENDER_MULTIPLY_SHADE_ALPHA] { 0x00404B40, 0x00104B40, IMGFX_RENDER_NO_OVERRIDE },
[IMGFX_RENDER_MULTIPLY_SHADE_RGBA] { 0x00404B40, 0x00104B40, IMGFX_RENDER_NO_OVERRIDE },
[IMGFX_RENDER_MODULATE_SHADE_RGB] { 0x00441208, 0x00111208, 0 },
[IMGFX_RENDER_MODULATE_SHADE_RGBA] { 0x00404B40, 0x00104B40, IMGFX_RENDER_NO_OVERRIDE },
[IMGFX_RENDER_ANIM] { 0x00441208, 0x00111208, 0 },
[IMGFX_RENDER_HOLOGRAM] { 0x00404B40, 0x00104B40, IMGFX_RENDER_NO_OVERRIDE },
[IMGFX_RENDER_COLOR_FILL] { 0x00441208, 0x00111208, 0 },
[IMGFX_RENDER_OVERLAY_RGB] { 0x00441208, 0x00111208, 0 },
[IMGFX_RENDER_OVERLAY_RGBA] { 0x00404B40, 0x00104B40, IMGFX_RENDER_NO_OVERRIDE },
[IMGFX_RENDER_UNUSED] { 0x00441208, 0x00111208, 0 },
};
extern Addr shock_header;
extern Addr shiver_header;
extern Addr vertical_pipe_curl_header;
extern Addr horizontal_pipe_curl_header;
extern Addr startle_header;
extern Addr flutter_down_header;
extern Addr unfurl_header;
extern Addr get_in_bed_header;
extern Addr spirit_capture_header;
extern Addr unused_1_header;
extern Addr unused_2_header;
extern Addr unused_3_header;
extern Addr tutankoopa_gather_header;
extern Addr tutankoopa_swirl_2_header;
extern Addr tutankoopa_swirl_1_header;
extern Addr shuffle_cards_header;
extern Addr flip_card_1_header;
extern Addr flip_card_2_header;
extern Addr flip_card_3_header;
extern Addr cymbal_crush_header;
// all relative to imgfx_data_ROM_START
u8* ImgFXAnimOffsets[] = {
[IMGFX_ANIM_SHOCK] shock_header,
[IMGFX_ANIM_SHIVER] shiver_header,
[IMGFX_ANIM_VERTICAL_PIPE_CURL] vertical_pipe_curl_header,
[IMGFX_ANIM_HORIZONTAL_PIPE_CURL] horizontal_pipe_curl_header,
[IMGFX_ANIM_STARTLE] startle_header,
[IMGFX_ANIM_FLUTTER_DOWN] flutter_down_header,
[IMGFX_ANIM_UNFURL] unfurl_header,
[IMGFX_ANIM_GET_IN_BED] get_in_bed_header,
[IMGFX_ANIM_SPIRIT_CAPTURE] spirit_capture_header,
[IMGFX_ANIM_UNUSED_1] unused_1_header,
[IMGFX_ANIM_UNUSED_2] unused_2_header,
[IMGFX_ANIM_UNUSED_3] unused_3_header,
[IMGFX_ANIM_TUTANKOOPA_GATHER] tutankoopa_gather_header,
[IMGFX_ANIM_TUTANKOOPA_SWIRL_2] tutankoopa_swirl_2_header,
[IMGFX_ANIM_TUTANKOOPA_SWIRL_1] tutankoopa_swirl_1_header,
[IMGFX_ANIM_SHUFFLE_CARDS] shuffle_cards_header,
[IMGFX_ANIM_FLIP_CARD_1] flip_card_1_header,
[IMGFX_ANIM_FLIP_CARD_2] flip_card_2_header,
[IMGFX_ANIM_FLIP_CARD_3] flip_card_3_header,
[IMGFX_ANIM_CYMBAL_CRUSH] cymbal_crush_header,
};
void imgfx_cache_instance_data(ImgFXState* state);
void imgfx_clear_instance_data(ImgFXState* state);
void imgfx_init_instance(ImgFXState* state);
void imgfx_make_mesh(ImgFXState* state);
void imgfx_appendGfx_mesh(ImgFXState* state, Matrix4f mtx);
void imgfx_mesh_make_strip(ImgFXState* state);
void imgfx_mesh_make_grid(ImgFXState* state);
//ImgFXAnimHeader* imgfx_load_anim(ImgFXState* state);
void imgfx_mesh_anim_update(ImgFXState* state);
void imgfx_appendGfx_mesh_basic(ImgFXState*, Matrix4f mtx);
void imgfx_appendGfx_mesh_grid(ImgFXState*, Matrix4f mtx);
void imgfx_appendGfx_mesh_anim(ImgFXState*, Matrix4f mtx);
void imgfx_appendGfx_mesh_strip(ImgFXState*, Matrix4f mtx);
void imgfx_wavy_init(ImgFXState* state);
void imgfx_mesh_make_wavy(ImgFXState* state);
void imgfx_mesh_load_colors(ImgFXState* state);
void imgfx_set_vtx_buf_capacity(s16 arg0) {
ImgFXVtxBufferCapacity = arg0;
}
void imgfx_init(void) {
s32 i;
for (i = 0; i < ARRAY_COUNT(ImgFXVtxBuffers); i++) {
ImgFXVtxBuffers[i] = _heap_malloc(&heap_spriteHead, ImgFXVtxBufferCapacity * sizeof(Vtx));
}
ImgFXInstances = (ImgFXInstanceList*)_heap_malloc(&heap_spriteHead, sizeof(ImgFXInstanceList));
for (i = 0; i < ARRAY_COUNT(*ImgFXInstances); i++) {
imgfx_init_instance(&(*ImgFXInstances)[i]);
imgfx_clear_instance_data(&(*ImgFXInstances)[i]);
}
for (i = 0; i < ARRAY_COUNT(D_80156958); i++) {
D_80156958[i] = -1;
D_80156960[i] = 0;
D_80156968[i] = 0;
D_80156970 = 0;
}
for (i = 0; i < ARRAY_COUNT(ImgFXDataCache); i++) {
ImgFXDataCache[i].data = NULL;
ImgFXDataCache[i].staleCooldownTimer = 0;
ImgFXDataCache[i].usingContextualHeap = FALSE;
}
imgfx_vtxCount = 0;
imgfx_vtxBuf = ImgFXVtxBuffers[gCurrentDisplayContextIndex];
}
void func_8013A4D0(void) {
s32 i;
imgfx_vtxBuf = ImgFXVtxBuffers[gCurrentDisplayContextIndex];
imgfx_vtxCount = 0;
imgfx_init_instance(&(*ImgFXInstances)[0]);
(*ImgFXInstances)[0].flags |= IMGFX_FLAG_VALID;
for (i = 1; i < ARRAY_COUNT(*ImgFXInstances); i++) {
if (((*ImgFXInstances)[i].flags & IMGFX_FLAG_VALID) && (*ImgFXInstances)[i].lastAnimCmd != IMGFX_SET_ANIM) {
imgfx_cache_instance_data(&(*ImgFXInstances)[i]);
}
}
for (i = 1; i < ARRAY_COUNT(*ImgFXInstances); i++) {
if ((*ImgFXInstances)[i].flags & IMGFX_FLAG_VALID && (*ImgFXInstances)[i].colorBuf != NULL) {
if ((*ImgFXInstances)[i].lastColorCmd == IMGFX_COLOR_BUF_SET_MULTIPLY) {
continue;
}
if ((*ImgFXInstances)[i].lastColorCmd == IMGFX_COLOR_BUF_SET_MODULATE) {
continue;
}
general_heap_free((*ImgFXInstances)[i].colorBuf);
(*ImgFXInstances)[i].colorBuf = NULL;
(*ImgFXInstances)[i].colorBufCount = 0;
}
}
}
void imgfx_add_to_cache(void* data, s8 usingContextualHeap) {
s32 i;
for (i = 0; i < ARRAY_COUNT(ImgFXDataCache); i++) {
if (ImgFXDataCache[i].data == NULL) {
ImgFXDataCache[i].data = data;
ImgFXDataCache[i].staleCooldownTimer = 4;
ImgFXDataCache[i].usingContextualHeap = usingContextualHeap;
return;
}
}
}
void imgfx_update_cache_impl(void) {
s32 i;
for (i = 0; i < ARRAY_COUNT(ImgFXDataCache); i++) {
if (ImgFXDataCache[i].data != NULL) {
ImgFXDataCache[i].staleCooldownTimer--;
if (ImgFXDataCache[i].staleCooldownTimer == 0) {
if (ImgFXDataCache[i].usingContextualHeap) {
heap_free(ImgFXDataCache[i].data);
ImgFXDataCache[i].data = NULL;
} else {
general_heap_free(ImgFXDataCache[i].data);
ImgFXDataCache[i].data = NULL;
}
ImgFXDataCache[i].staleCooldownTimer = 0;
ImgFXDataCache[i].usingContextualHeap = FALSE;
}
}
}
}
void imgfx_update_cache(void) {
imgfx_update_cache_impl();
}
// request some number of imgfx instances. returns the id of the first assigned instance, or -1 if not enough are free.
s32 imgfx_get_free_instances(s32 count) {
s32 numAssigned;
s32 foundAny;
s32 iPrev;
s32 firstIdx;
s32 i;
numAssigned = 0;
for (i = 1; i < ARRAY_COUNT(*ImgFXInstances); i++) {
if (!((*ImgFXInstances)[i].flags & IMGFX_FLAG_VALID)) {
numAssigned++;
}
}
if (numAssigned < count) {
return -1;
}
firstIdx = 0;
foundAny = FALSE;
numAssigned = 0;
iPrev = -1;
for (i = 1; i < ARRAY_COUNT(*ImgFXInstances); i++) {
if ((*ImgFXInstances)[i].flags & IMGFX_FLAG_VALID) {
continue;
}
if (!foundAny) {
firstIdx = i;
foundAny = TRUE;
} else {
(*ImgFXInstances)[iPrev].nextIdx = i;
}
(*ImgFXInstances)[i].arrayIdx = i;
imgfx_init_instance(&(*ImgFXInstances)[i]);
numAssigned++;
(*ImgFXInstances)[i].flags |= IMGFX_FLAG_VALID;
iPrev = i;
if (numAssigned == count) {
(*ImgFXInstances)[i].nextIdx = -1;
break;
}
}
return firstIdx;
}
void imgfx_release_instance(u32 idx) {
if (idx < MAX_IMGFX_INSTANCES) {
(*ImgFXInstances)[idx].flags = 0;
(*ImgFXInstances)[idx].nextIdx = -1;
}
}
void imgfx_release_instance_chain(u32 idx) {
if (idx < MAX_IMGFX_INSTANCES) {
s32 next;
do {
next = (*ImgFXInstances)[idx].nextIdx;
imgfx_release_instance(idx);
idx = next;
} while (next != -1);
}
}
s32 imgfx_get_next_instance(s32 idx) {
if (idx < 0 || idx >= MAX_IMGFX_INSTANCES) {
return -1;
}
if (idx >= MAX_IMGFX_INSTANCES) {
return 0xFF;
} else {
return (*ImgFXInstances)[idx].nextIdx;
}
}
ImgFXState* imgfx_get_instance(s32 idx) {
return &(*ImgFXInstances)[idx];
}
void imgfx_cache_instance_data(ImgFXState* state) {
if (state->curAnimOffset != NULL) {
state->curAnimOffset = NULL;
}
if (state->vtxBufs[0] != NULL) {
imgfx_add_to_cache(state->vtxBufs[0], TRUE);
state->vtxBufs[0] = NULL;
}
if (state->vtxBufs[1] != NULL) {
imgfx_add_to_cache(state->vtxBufs[1], TRUE);
state->vtxBufs[1] = NULL;
}
if (state->gfxBufs[0] != NULL) {
imgfx_add_to_cache(state->gfxBufs[0], TRUE);
state->gfxBufs[0] = NULL;
}
if (state->gfxBufs[1] != NULL) {
imgfx_add_to_cache(state->gfxBufs[1], TRUE);
state->gfxBufs[1] = NULL;
}
}
void imgfx_clear_instance_data(ImgFXState* state) {
state->curAnimOffset = NULL;
state->vtxBufs[0] = NULL;
state->vtxBufs[1] = NULL;
state->gfxBufs[0] = NULL;
state->gfxBufs[1] = NULL;
state->colorBuf = NULL;
state->colorBufCount = 0;
}
void imgfx_init_instance(ImgFXState* state) {
s32 i;
s32 j;
state->nextIdx = -1;
state->lastAnimCmd = IMGFX_CLEAR;
state->lastColorCmd = IMGFX_CLEAR;
state->flags = 0;
state->meshType = IMGFX_MESH_DEFAULT;
state->renderType = IMGFX_RENDER_DEFAULT;
state->firstVtxIdx = 0;
state->lastVtxIdx = 0;
state->unk_0C = 0;
state->unk_0E = 0;
state->ints.raw[0][3] = 255;
state->ints.raw[1][3] = 255;
state->subdivX = 0;
state->subdivY = 0;
state->firstVtxIdx = 0;
state->lastVtxIdx = 0;
for (i = 0; i < ARRAY_COUNT(state->ints.raw); i++) {
for (j = 0; j < ARRAY_COUNT(state->ints.raw[0]); j++) {
state->ints.raw[i][j] = 0;
}
}
for (i = 0; i < ARRAY_COUNT(state->floats.raw); i++) {
for (j = 0; j < ARRAY_COUNT(state->floats.raw[0]); j++) {
state->floats.raw[i][j] = 0;
}
}
}
void imgfx_update(u32 idx, ImgFXType type, s32 imgfxArg1, s32 imgfxArg2, s32 imgfxArg3, s32 imgfxArg4, s32 flags) {
ImgFXState* state = &(*ImgFXInstances)[idx];
s32 oldFlags;
s32 t1;
u8 r, g, b, a;
if (!(state->flags & IMGFX_FLAG_VALID) || (idx >= MAX_IMGFX_INSTANCES)) {
return;
}
switch (type) {
case IMGFX_CLEAR:
case IMGFX_RESET:
oldFlags = state->flags;
imgfx_cache_instance_data(state);
imgfx_init_instance(state);
state->flags = oldFlags;
state->lastAnimCmd = IMGFX_CLEAR;
state->lastColorCmd = IMGFX_CLEAR;
state->meshType = IMGFX_MESH_DEFAULT;
state->renderType = IMGFX_RENDER_DEFAULT;
state->ints.raw[0][0] = -1;
state->ints.raw[1][0] = -1;
state->flags &= IMGFX_FLAG_VALID;
if (flags != 0) {
state->flags |= flags;
} else {
state->flags |= 0; // required to match
}
return;
case IMGFX_UNK_1:
state->lastAnimCmd = IMGFX_CLEAR;
state->renderType = IMGFX_RENDER_DEFAULT;
state->ints.raw[0][0] = -1;
return;
case IMGFX_UNK_2:
state->lastColorCmd = IMGFX_CLEAR;
state->meshType = IMGFX_MESH_DEFAULT;
state->ints.raw[1][0] = -1;
return;
case IMGFX_ALLOC_COLOR_BUF:
if (state->colorBuf != NULL) {
heap_free(state->colorBuf);
}
state->colorBufCount = imgfxArg1 * 4;
state->colorBuf = heap_malloc(state->colorBufCount);
return;
case IMGFX_OVERLAY:
case IMGFX_OVERLAY_XLU:
if (type == state->lastColorCmd
&& imgfxArg1 == (s32) state->ints.overlay.pattern
&& imgfxArg2 == state->ints.overlay.alpha
) {
// no paramaters have changed
return;
}
break;
case IMGFX_SET_ANIM:
if (state->lastAnimCmd == type
&& state->ints.anim.type == imgfxArg1
&& state->ints.anim.interval == imgfxArg2
&& state->ints.anim.step == imgfxArg3
) {
// no paramaters have changed
return;
}
break;
default:
if (type != IMGFX_HOLOGRAM && state->lastColorCmd == IMGFX_HOLOGRAM) {
state->meshType = IMGFX_MESH_DEFAULT;
state->subdivX = 1;
state->subdivY = 1;
}
break;
}
if (type != IMGFX_SET_ANIM && state->lastAnimCmd == IMGFX_SET_ANIM) {
state->lastAnimCmd = IMGFX_CLEAR;
}
if (type == IMGFX_SET_WAVY || type == IMGFX_SET_ANIM) {
state->lastAnimCmd = type;
state->ints.args.anim[0] = imgfxArg1;
state->ints.args.anim[1] = imgfxArg2;
state->ints.args.anim[2] = imgfxArg3;
state->ints.args.anim[3] = imgfxArg4;
} else if (type >= IMGFX_SET_COLOR && type <= IMGFX_OVERLAY_XLU) {
state->lastColorCmd = type;
state->ints.args.color[0] = imgfxArg1;
state->ints.args.color[1] = imgfxArg2;
state->ints.args.color[2] = imgfxArg3;
state->ints.args.color[3] = imgfxArg4;
}
state->flags &= IMGFX_FLAG_VALID;
if (flags != 0) {
state->flags |= flags;
}
state->meshType = IMGFX_MESH_DEFAULT;
switch (type) {
case IMGFX_RESET:
state->meshType = IMGFX_MESH_DEFAULT;
state->renderType = IMGFX_RENDER_DEFAULT;
break;
case IMGFX_SET_WAVY:
state->subdivX = 4;
state->subdivY = 4;
state->meshType = IMGFX_MESH_GRID_WAVY;
imgfx_wavy_init(state);
break;
case IMGFX_SET_ANIM:
state->meshType = IMGFX_MESH_ANIMATED;
state->renderType = IMGFX_RENDER_ANIM;
state->floats.anim.curFrame = 0.0f;
state->floats.anim.curIdx = 0.0f;
state->flags |= IMGFX_FLAG_200;
break;
case IMGFX_SET_COLOR:
case IMGFX_SET_ALPHA:
case IMGFX_SET_TINT:
if (imgfxArg1 >= 255 && imgfxArg2 >= 255 && imgfxArg3 >= 255 && imgfxArg4 >= 255) {
// no color + no transparency
state->renderType = IMGFX_RENDER_DEFAULT;
} else if (imgfxArg4 >= 255) {
// some color + no transparency
state->renderType = IMGFX_RENDER_MULTIPLY_RGB;
} else if (imgfxArg1 >= 255 && imgfxArg2 >= 255 && imgfxArg3 >= 255) {
// no color + transparency
state->renderType = IMGFX_RENDER_MULTIPLY_ALPHA;
} else {
// some color + transparency
state->renderType = IMGFX_RENDER_MULTIPLY_RGBA;
}
break;
case IMGFX_SET_WHITE_FADE:
case IMGFX_SET_CREDITS_FADE:
if (imgfxArg4 == 255.0) {
state->renderType = IMGFX_RENDER_MODULATE_PRIM_RGB;
} else {
state->renderType = IMGFX_RENDER_MODULATE_PRIM_RGBA;
}
break;
case IMGFX_COLOR_BUF_SET_MULTIPLY:
if (imgfxArg1 < state->colorBufCount) {
// unpack and store color
r = (imgfxArg2 & 0xFF000000) >> 24;
g = (imgfxArg2 & 0xFF0000) >> 16;
b = (imgfxArg2 & 0xFF00) >> 8;
a = (imgfxArg2 & 0xFF);
state->colorBuf[imgfxArg1].r = r;
state->colorBuf[imgfxArg1].g = g;
state->colorBuf[imgfxArg1].b = b;
state->colorBuf[imgfxArg1].a = a;
state->meshType = IMGFX_MESH_DEFAULT;
if (a == 255) {
state->renderType = IMGFX_RENDER_MULTIPLY_SHADE_RGB;
} else {
state->renderType = IMGFX_RENDER_MULTIPLY_SHADE_RGBA;
}
}
break;
case IMGFX_COLOR_BUF_SET_MODULATE:
if (imgfxArg1 < state->colorBufCount) {
// unpack and store color
r = (imgfxArg2 & 0xFF000000) >> 24;
g = (imgfxArg2 & 0xFF0000) >> 16;
b = (imgfxArg2 & 0xFF00) >> 8;
a = (imgfxArg2 & 0xFF);
state->colorBuf[imgfxArg1].r = r;
state->colorBuf[imgfxArg1].g = g;
state->colorBuf[imgfxArg1].b = b;
state->colorBuf[imgfxArg1].a = a;
state->meshType = IMGFX_MESH_DEFAULT;
if (a == 255) {
state->renderType = IMGFX_RENDER_MODULATE_SHADE_RGB;
} else {
state->renderType = IMGFX_RENDER_MODULATE_SHADE_RGBA;
}
}
break;
case IMGFX_HOLOGRAM:
state->renderType = IMGFX_RENDER_HOLOGRAM;
break;
case IMGFX_FILL_COLOR:
state->renderType = IMGFX_RENDER_COLOR_FILL;
break;
case IMGFX_OVERLAY:
case IMGFX_OVERLAY_XLU:
state->meshType = IMGFX_MESH_STRIP;
if (imgfxArg2 >= 255) {
state->renderType = IMGFX_RENDER_OVERLAY_RGB;
} else {
state->renderType = IMGFX_RENDER_OVERLAY_RGBA;
}
state->floats.overlay.posX = 0.0f;
state->floats.overlay.posY = 0.0f;
break;
}
}
void imgfx_set_state_flags(s32 idx, u16 flagBits, s32 mode) {
if ((*ImgFXInstances)[idx].flags & IMGFX_FLAG_VALID) {
if (mode) {
(*ImgFXInstances)[idx].flags |= flagBits;
} else {
(*ImgFXInstances)[idx].flags &= ~flagBits;
}
}
}
s32 imgfx_appendGfx_component(s32 idx, ImgFXTexture* ifxImg, u32 flagBits, Matrix4f mtx) {
ImgFXState* state = &(*ImgFXInstances)[idx];
s32 ret = 0;
if (ifxImg->alpha == 0) {
return 0;
}
state->arrayIdx = idx;
state->flags |= flagBits;
ImgFXCurrentTexturePtr->tex.raster = ifxImg->raster;
ImgFXCurrentTexturePtr->tex.palette = ifxImg->palette;
ImgFXCurrentTexturePtr->tex.width = ifxImg->width;
ImgFXCurrentTexturePtr->tex.height = ifxImg->height;
ImgFXCurrentTexturePtr->tex.xOffset = ifxImg->xOffset;
ImgFXCurrentTexturePtr->tex.yOffset = ifxImg->yOffset;
ImgFXCurrentTexturePtr->unk_18 = 0;
ImgFXCurrentTexturePtr->unk_1E = 0;
ImgFXCurrentTexturePtr->alphaMultiplier = ifxImg->alpha;
if (idx < 0 || idx >= MAX_IMGFX_INSTANCES) {
return 0;
}
if (idx >= MAX_IMGFX_INSTANCES || state == NULL) {
return 0;
}
imgfx_make_mesh(state);
imgfx_appendGfx_mesh(state, mtx);
if (state->flags & IMGFX_FLAG_ANIM_DONE) {
state->ints.raw[0][0] = -1;
state->ints.raw[1][0] = -1;
state->lastAnimCmd = IMGFX_CLEAR;
state->meshType = 0;
state->renderType = IMGFX_RENDER_DEFAULT;
state->flags &= ~(IMGFX_FLAG_ANIM_DONE | IMGFX_FLAG_800 | IMGFX_FLAG_REVERSE_ANIM | IMGFX_FLAG_LOOP_ANIM);
imgfx_cache_instance_data(state);
ret = 1;
} else if (state->flags & IMGFX_FLAG_4000) {
ret = 2;
} else if (state->flags & IMGFX_FLAG_20000) {
state->lastAnimCmd = IMGFX_CLEAR;
state->lastColorCmd = IMGFX_CLEAR;
state->meshType = IMGFX_MESH_DEFAULT;
state->renderType = IMGFX_RENDER_DEFAULT;
state->ints.raw[0][0] = -1;
state->ints.raw[1][0] = -1;
state->flags &= IMGFX_FLAG_VALID;
ret = 1;
}
return ret;
}
void imgfx_make_mesh(ImgFXState* state) {
switch (state->meshType) {
case IMGFX_MESH_GRID_UNUSED:
if (state->ints.raw[1][2] == 0) {
state->subdivX = 1;
state->subdivY = 16;
} else {
state->subdivX = 1;
state->subdivY = 1;
}
// fallthrough
case IMGFX_MESH_GRID_WAVY:
imgfx_mesh_make_grid(state);
break;
case IMGFX_MESH_ANIMATED:
imgfx_mesh_anim_update(state);
break;
case IMGFX_MESH_DEFAULT:
case IMGFX_MESH_STRIP:
imgfx_mesh_make_strip(state);
break;
default:
return;
}
if (state->lastAnimCmd == IMGFX_SET_WAVY) {
imgfx_mesh_make_wavy(state);
}
switch (state->lastColorCmd) {
case IMGFX_COLOR_BUF_SET_MULTIPLY:
case IMGFX_COLOR_BUF_SET_MODULATE:
imgfx_mesh_load_colors(state);
break;
}
}
void imgfx_appendGfx_mesh(ImgFXState* state, Matrix4f mtx) {
s16 skipModeChange = FALSE;
s32 primAlpha = state->ints.color.a;
s32 renderType = state->renderType;
s8 angle1;
s8 angle2;
f32 ifxImgAlpha;
ImgFXRenderMode* renderMode;
s32 mode1;
s32 mode2;
s32 dirX1;
s32 dirZ2;
gDPPipeSync(gMainGfxPos++);
if (!(state->flags & IMGFX_FLAG_SKIP_GFX_SETUP)) {
gSPDisplayList(gMainGfxPos++, DefaultImgFXSetupGfx);
if (state->flags & IMGFX_FLAG_NO_FILTERING) {
gDPSetTextureFilter(gMainGfxPos++, G_TF_POINT);
}
if (state->flags & IMGFX_FLAG_G_CULL_BACK) {
gSPSetGeometryMode(gMainGfxPos++, G_CULL_BACK);
}
if (state->flags & IMGFX_FLAG_G_CULL_FRONT) {
gSPSetGeometryMode(gMainGfxPos++, G_CULL_FRONT);
}
renderMode = &ImgFXRenderModes[state->renderType];
mode1 = renderMode->mode1;
mode2 = renderMode->mode2;
if (renderMode->flags & IMGFX_RENDER_NO_OVERRIDE) {
skipModeChange = TRUE;
}
ifxImgAlpha = (f32) ImgFXCurrentTexturePtr->alphaMultiplier / 255.0;
// some modes dont support alpha < 255 and must be replaced
if (!skipModeChange && (ImgFXCurrentTexturePtr->alphaMultiplier < 255)) {
state->ints.color.a = 255;
switch (state->renderType) {
case IMGFX_RENDER_DEFAULT:
case IMGFX_RENDER_ANIM:
renderType = IMGFX_RENDER_MULTIPLY_ALPHA;
break;
case IMGFX_RENDER_MULTIPLY_RGB:
case IMGFX_RENDER_MODULATE_PRIM_RGB:
renderType = IMGFX_RENDER_MULTIPLY_RGBA;
break;
case IMGFX_RENDER_MODULATE_SHADE_RGB:
renderType = IMGFX_RENDER_MODULATE_SHADE_RGBA;
break;
}
primAlpha = state->ints.color.a * ifxImgAlpha;
//TODO figure out bits
mode1 = 0x404B40;
mode2 = 0x104B40;
skipModeChange = TRUE;
}
if ((state->flags & IMGFX_FLAG_400) && !skipModeChange) {
mode1 &= ~CVG_DST_FULL;
mode2 &= ~CVG_DST_FULL;
mode1 |= (ALPHA_CVG_SEL | IM_RD);
mode2 |= (ALPHA_CVG_SEL | IM_RD);
}
if (state->flags & IMGFX_FLAG_40) {
gSPClearGeometryMode(gMainGfxPos++, G_ZBUFFER);
} else {
gSPSetGeometryMode(gMainGfxPos++, G_ZBUFFER);
if (skipModeChange) {
mode1 |= Z_CMP;
mode2 |= Z_CMP;
} else {
mode1 |= (Z_CMP | Z_UPD);
mode2 |= (Z_CMP | Z_UPD);
}
}
state->otherModeL = mode2;
gDPSetRenderMode(gMainGfxPos++, mode1, mode2);
switch (renderType) {
case IMGFX_RENDER_MULTIPLY_RGB:
// color: texture * prim
// alpha: texture
gDPSetCombineMode(gMainGfxPos++, G_CC_MODULATEIDECALA_PRIM, G_CC_MODULATEIDECALA_PRIM);
gDPSetPrimColor(gMainGfxPos++, 0, 0, state->ints.color.r, state->ints.color.g,
state->ints.color.b, 0);
break;
case IMGFX_RENDER_MULTIPLY_ALPHA:
// color: texture
// alpha: texture * prim
if (primAlpha <= 0) {
return;
}
gDPSetCombineMode(gMainGfxPos++, PM_CC_02, PM_CC_02);
gDPSetPrimColor(gMainGfxPos++, 0, 0, 0, 0, 0, primAlpha);
break;
case IMGFX_RENDER_MULTIPLY_RGBA:
// color: texture * prim
// alpha: texture * prim
if (primAlpha <= 0) {
return;
}
gDPSetCombineMode(gMainGfxPos++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM);
gDPSetPrimColor(gMainGfxPos++, 0, 0, state->ints.color.r, state->ints.color.g,
state->ints.color.b, primAlpha);
break;
case IMGFX_RENDER_MODULATE_PRIM_RGB:
// color: lerp from prim color to 1 based on texture intensity
// alpha: texture
gDPSetCombineMode(gMainGfxPos++, PM_CC_5B, PM_CC_5B);
gDPSetPrimColor(gMainGfxPos++, 0, 0, state->ints.color.r, state->ints.color.g,
state->ints.color.b, 0);
break;
case IMGFX_RENDER_MODULATE_PRIM_RGBA:
// color: lerp from prim color to 1 based on texture intensity
// alpha: texture * vtx
if (primAlpha <= 0) {
return;
}
gDPSetCombineMode(gMainGfxPos++, PM_CC_5C, PM_CC_5C);
gDPSetPrimColor(gMainGfxPos++, 0, 0, state->ints.color.r, state->ints.color.g,
state->ints.color.b, primAlpha);
break;
case IMGFX_RENDER_MULTIPLY_SHADE_RGB:
// color: modulate vtx color by texture intensity
// alpha: texture
gDPSetCombineMode(gMainGfxPos++, G_CC_MODULATEIDECALA, G_CC_MODULATEIDECALA);
gSPSetGeometryMode(gMainGfxPos++, G_SHADE | G_SHADING_SMOOTH);
gSPClearGeometryMode(gMainGfxPos++, G_LIGHTING);
break;
case IMGFX_RENDER_MODULATE_SHADE_RGB:
// color: lerp from vtx color to 1 based on texture intensity
// alpha: texture
gDPSetCombineMode(gMainGfxPos++, PM_CC_5D, PM_CC_5D);
gSPSetGeometryMode(gMainGfxPos++, G_SHADE | G_SHADING_SMOOTH);
gSPClearGeometryMode(gMainGfxPos++, G_LIGHTING);
break;
case IMGFX_RENDER_MULTIPLY_SHADE_ALPHA:
// color: texture
// alpha: texture * vtx color
gDPSetCombineMode(gMainGfxPos++, PM_CC_12, PM_CC_12);
gSPSetGeometryMode(gMainGfxPos++, G_SHADE | G_SHADING_SMOOTH);
gSPClearGeometryMode(gMainGfxPos++, G_LIGHTING);
break;
case IMGFX_RENDER_MULTIPLY_SHADE_RGBA:
// color: modulate vtx color by texture intensity
// alpha: modulate vtx alpha by texture intensity
gDPSetCombineMode(gMainGfxPos++, G_CC_MODULATEIA, G_CC_MODULATEIA);
gSPSetGeometryMode(gMainGfxPos++, G_SHADE | G_SHADING_SMOOTH);
gSPClearGeometryMode(gMainGfxPos++, G_LIGHTING);
break;
case IMGFX_RENDER_MODULATE_SHADE_RGBA:
// color: lerp from vtx color to 1 based on texture intensity
// alpha: vtx color * texture
gDPSetCombineMode(gMainGfxPos++, PM_CC_5E, PM_CC_5E);
gSPSetGeometryMode(gMainGfxPos++, G_SHADE | G_SHADING_SMOOTH);
gSPClearGeometryMode(gMainGfxPos++, G_LIGHTING);
break;
case IMGFX_RENDER_ANIM:
if (state->flags & (IMGFX_FLAG_2000 | IMGFX_FLAG_8000)) {
Camera* currentCam = &gCameras[gCurrentCameraID];
gDPSetCombineMode(gMainGfxPos++, G_CC_MODULATEIDECALA, G_CC_MODULATEIDECALA);
gSPSetGeometryMode(gMainGfxPos++, G_SHADE | G_LIGHTING | G_SHADING_SMOOTH);
angle1 = cosine(currentCam->curYaw) * 120.0f;
angle2 = cosine(currentCam->curYaw + 90.0f) * 120.0f;
dirX1 = -angle1;
dirZ2 = -angle2;
ImgFXLights.l[0].l.dir[0] = dirX1;
ImgFXLights.l[1].l.dir[0] = angle1;
ImgFXLights.l[0].l.dir[2] = angle2;
ImgFXLights.l[1].l.dir[2] = dirZ2;
gSPSetLights2(gMainGfxPos++, ImgFXLights);
break;
}
gDPSetCombineMode(gMainGfxPos++, G_CC_DECALRGBA, G_CC_DECALRGBA);
break;
case IMGFX_RENDER_HOLOGRAM:
if (state->ints.hologram.mode == IMGFX_HOLOGRAM_NOISE) {
primAlpha = state->ints.hologram.alphaAmt * ifxImgAlpha;
// color: blend texure and noise
// alpha: texure * prim
gDPSetCombineMode(gMainGfxPos++, PM_CC_IMGFX_HOLOGRAM, PM_CC_IMGFX_HOLOGRAM);
gDPSetPrimColor(gMainGfxPos++, 0, 0,
state->ints.hologram.noiseAmt,
state->ints.hologram.noiseAmt,
state->ints.hologram.noiseAmt,
primAlpha);
} else if (state->ints.hologram.mode == IMGFX_HOLOGRAM_DITHER) {
primAlpha = state->ints.hologram.alphaAmt * ifxImgAlpha;
// color: texture
// alpha: texture * prim
gDPSetCombineMode(gMainGfxPos++, PM_CC_02, PM_CC_02);
gDPSetPrimColor(gMainGfxPos++, 0, 0, 0, 0, 0, primAlpha);
gDPSetAlphaCompare(gMainGfxPos++, G_AC_DITHER);
} else if (state->ints.hologram.mode == IMGFX_HOLOGRAM_THRESHOLD) {
s32 blendAlpha = state->ints.hologram.alphaAmt + state->ints.hologram.noiseAmt;
if (blendAlpha > 255) {
blendAlpha = 255;
}
primAlpha = state->ints.hologram.alphaAmt * ifxImgAlpha;
// color: texture
// alpha: texture * prim
gDPSetCombineMode(gMainGfxPos++, PM_CC_02, PM_CC_02);
gDPSetAlphaDither(gMainGfxPos++, G_AD_NOISE);
gDPSetAlphaCompare(gMainGfxPos++, G_AC_THRESHOLD);
gDPSetPrimColor(gMainGfxPos++, 0, 0, 0, 0, 0, primAlpha);
gDPSetBlendColor(gMainGfxPos++, 0, 0, 0, blendAlpha);
}
break;
case IMGFX_RENDER_COLOR_FILL:
// color: prim
// alpha: texture
gDPSetCombineMode(gMainGfxPos++, PM_CC_IMGFX_COLOR_FILL, PM_CC_IMGFX_COLOR_FILL);
gDPSetPrimColor(gMainGfxPos++, 0, 0, state->ints.color.r, state->ints.color.g,
state->ints.color.b, 0);
break;
case IMGFX_RENDER_DEFAULT:
case IMGFX_RENDER_OVERLAY_RGB:
case IMGFX_RENDER_UNUSED:
// color: texture
// alpha: texture
gDPSetCombineMode(gMainGfxPos++, G_CC_DECALRGBA, G_CC_DECALRGBA);
break;
case IMGFX_RENDER_OVERLAY_RGBA:
// color: texture
// alpha: texture * prim
gDPSetCombineMode(gMainGfxPos++, PM_CC_02, PM_CC_02);
gDPSetPrimColor(gMainGfxPos++, 0, 0, 0, 0, 0, state->ints.overlay.alpha);
break;
}
}
switch (state->meshType) {
case IMGFX_MESH_DEFAULT:
imgfx_appendGfx_mesh_basic(state, mtx);
break;
case IMGFX_MESH_GRID_WAVY:
case IMGFX_MESH_GRID_UNUSED:
imgfx_appendGfx_mesh_grid(state, mtx);
break;
case IMGFX_MESH_ANIMATED:
imgfx_appendGfx_mesh_anim(state, mtx);
break;
case IMGFX_MESH_STRIP:
imgfx_appendGfx_mesh_basic(state, mtx);
gDPPipeSync(gMainGfxPos++);
imgfx_appendGfx_mesh_strip(state, mtx);
break;
}
gDPPipeSync(gMainGfxPos++);
if (state->renderType == IMGFX_RENDER_HOLOGRAM) {
gDPSetAlphaCompare(gMainGfxPos++, G_AC_NONE);
gDPSetAlphaDither(gMainGfxPos++, G_AD_DISABLE);
}
}
void imgfx_mesh_make_strip(ImgFXState* state) {
s32 offsetY;
s32 offsetX;
s32 stepY;
s32 rightColor;
s32 leftColor;
s32 temp2;
s32 nextY;
stepY = (128 * 32) / ImgFXCurrentTexturePtr->tex.width;
if (stepY > ImgFXCurrentTexturePtr->tex.height) {
stepY = ImgFXCurrentTexturePtr->tex.height;
}
offsetX = ImgFXCurrentTexturePtr->tex.xOffset;
offsetY = ImgFXCurrentTexturePtr->tex.yOffset;
state->firstVtxIdx = imgfx_vtxCount;
// create first pair of vertices to begin the strip
// 'left' side
imgfx_vtxBuf[imgfx_vtxCount].v.ob[0] = offsetX;
imgfx_vtxBuf[imgfx_vtxCount].v.ob[1] = offsetY;
imgfx_vtxBuf[imgfx_vtxCount].v.ob[2] = 0;
imgfx_vtxBuf[imgfx_vtxCount].v.tc[0] = (0 + 256) * 32;
imgfx_vtxBuf[imgfx_vtxCount].v.tc[1] = temp2 = (0 + 256) * 32; // required to match
imgfx_vtxBuf[imgfx_vtxCount].v.cn[0] = 240;
imgfx_vtxBuf[imgfx_vtxCount].v.cn[1] = 240;
imgfx_vtxBuf[imgfx_vtxCount].v.cn[2] = 240;
// 'right' side
imgfx_vtxBuf[imgfx_vtxCount + 1].v.ob[0] = ImgFXCurrentTexturePtr->tex.width + offsetX;
imgfx_vtxBuf[imgfx_vtxCount + 1].v.ob[1] = offsetY;
imgfx_vtxBuf[imgfx_vtxCount + 1].v.ob[2] = 0;
imgfx_vtxBuf[imgfx_vtxCount + 1].v.tc[0] = (ImgFXCurrentTexturePtr->tex.width + 256) * 32;
imgfx_vtxBuf[imgfx_vtxCount + 1].v.tc[1] = temp2;
imgfx_vtxBuf[imgfx_vtxCount + 1].v.cn[0] = 120;
imgfx_vtxBuf[imgfx_vtxCount + 1].v.cn[1] = 120;
imgfx_vtxBuf[imgfx_vtxCount + 1].v.cn[2] = 120;
// create remaining pairs of vertices along the strip
nextY = stepY;
while (TRUE) {
rightColor = (nextY * 120) / ImgFXCurrentTexturePtr->tex.height;
leftColor = rightColor + 120;
imgfx_vtxCount += 2;
// 'left' side
imgfx_vtxBuf[imgfx_vtxCount].v.ob[0] = offsetX;
imgfx_vtxBuf[imgfx_vtxCount].v.ob[1] = offsetY - stepY;
imgfx_vtxBuf[imgfx_vtxCount].v.ob[2] = 0;
imgfx_vtxBuf[imgfx_vtxCount].v.tc[0] = (0 + 256) * 32;
imgfx_vtxBuf[imgfx_vtxCount].v.tc[1] = (nextY + 256) * 32;
imgfx_vtxBuf[imgfx_vtxCount].v.cn[0] = leftColor;
imgfx_vtxBuf[imgfx_vtxCount].v.cn[1] = leftColor;
imgfx_vtxBuf[imgfx_vtxCount].v.cn[2] = leftColor;
// 'right' side
imgfx_vtxBuf[imgfx_vtxCount + 1].v.ob[0] = ImgFXCurrentTexturePtr->tex.width + offsetX;
imgfx_vtxBuf[imgfx_vtxCount + 1].v.ob[1] = offsetY - stepY;
imgfx_vtxBuf[imgfx_vtxCount + 1].v.ob[2] = 0;
imgfx_vtxBuf[imgfx_vtxCount + 1].v.tc[0] = (ImgFXCurrentTexturePtr->tex.width + 256) * 32;
imgfx_vtxBuf[imgfx_vtxCount + 1].v.tc[1] = (nextY + 256) * 32;
imgfx_vtxBuf[imgfx_vtxCount + 1].v.cn[0] = rightColor;
imgfx_vtxBuf[imgfx_vtxCount + 1].v.cn[1] = rightColor;
imgfx_vtxBuf[imgfx_vtxCount + 1].v.cn[2] = rightColor;
if (nextY != ImgFXCurrentTexturePtr->tex.height) {
offsetY -= stepY;
if (ImgFXCurrentTexturePtr->tex.height < nextY + stepY) {
stepY = ImgFXCurrentTexturePtr->tex.height - nextY;
}
} else {
imgfx_vtxCount += 2;
break;
}
nextY += stepY;
}
state->lastVtxIdx = imgfx_vtxCount - 1;
state->subdivX = 1;
state->subdivY = ((state->lastVtxIdx - state->firstVtxIdx) - 1) / 2;
}
void imgfx_mesh_make_grid(ImgFXState* state) {
f32 divSizeX;
f32 divSizeY;
f32 posX;
f32 posY;
f32 texU;
f32 texV;
Vtx* vtx;
s32 i;
state->firstVtxIdx = imgfx_vtxCount;
divSizeX = ImgFXCurrentTexturePtr->tex.width / (f32) state->subdivX;
divSizeY = ImgFXCurrentTexturePtr->tex.height / (f32) state->subdivY;
posY = ImgFXCurrentTexturePtr->tex.yOffset;
texV = 0.0f;
vtx = &imgfx_vtxBuf[imgfx_vtxCount];
for (i = 0; i <= state->subdivY; i++) {
s32 j;
if (i == state->subdivY) {
texV = ImgFXCurrentTexturePtr->tex.height;
posY = ImgFXCurrentTexturePtr->tex.yOffset - ImgFXCurrentTexturePtr->tex.height;
}
posX = ImgFXCurrentTexturePtr->tex.xOffset;
texU = 0.0f;
for (j = 0; j <= state->subdivX; vtx++, j++) {
if (j == state->subdivX) {
texU = ImgFXCurrentTexturePtr->tex.width;
posX = ImgFXCurrentTexturePtr->tex.xOffset + ImgFXCurrentTexturePtr->tex.width;
}
vtx->n.ob[0] = posX;
vtx->n.ob[1] = posY;
vtx->n.ob[2] = 0;
vtx->n.tc[0] = ((s32) texU + 256) * 32;
vtx->n.tc[1] = ((s32) texV + 256) * 32;
imgfx_vtxCount++;
posX += divSizeX;
texU += divSizeX;
}
posY -= divSizeY;
texV += divSizeY;
}
state->lastVtxIdx = imgfx_vtxCount - 1;
}
ImgFXAnimHeader* imgfx_load_anim(ImgFXState* state) {
u8* romStart = (s32) ImgFXAnimOffsets[state->ints.anim.type] + imgfx_data_ROM_START;
ImgFXAnimHeader* anim = &ImgFXAnimHeaders[state->arrayIdx];
if (state->curAnimOffset != romStart) {
u8* romEnd;
s32 i;
state->curAnimOffset = romStart;
dma_copy(state->curAnimOffset, state->curAnimOffset + sizeof(*anim), anim);
if (state->vtxBufs[0] != NULL) {
imgfx_add_to_cache(state->vtxBufs[0], 1);
state->vtxBufs[0] = NULL;
}
if (state->vtxBufs[1] != NULL) {
imgfx_add_to_cache(state->vtxBufs[1], 1);
state->vtxBufs[1] = NULL;
}
if (state->gfxBufs[0] != NULL) {
imgfx_add_to_cache(state->gfxBufs[0], 1);
state->gfxBufs[0] = NULL;
}
if (state->gfxBufs[1] != NULL) {
// imgfx_add_to_cache(state->gfxBufs[1], 1);
romEnd = (u8*) state->gfxBufs[1]; // required to match
imgfx_add_to_cache(state->gfxBufs[1], 1);
state->gfxBufs[1] = NULL;
}
state->vtxBufs[0] = heap_malloc(anim->vtxCount * sizeof(Vtx));
state->vtxBufs[1] = heap_malloc(anim->vtxCount * sizeof(Vtx));
state->gfxBufs[0] = heap_malloc(anim->gfxCount * sizeof(Gfx));
state->gfxBufs[1] = heap_malloc(anim->gfxCount * sizeof(Gfx));
romStart = imgfx_data_ROM_START + (s32)anim->gfxOffset;
romEnd = romStart + anim->gfxCount * sizeof(Gfx);
dma_copy(romStart, romEnd, state->gfxBufs[0]);
dma_copy(romStart, romEnd, state->gfxBufs[1]);
// Search through the state's displaylists for vertex commands
// and adjust their addresses to point into the vertex buffers
for (i = 0; i < ARRAY_COUNT(state->gfxBufs); i++) {
Gfx* gfxBuffer = state->gfxBufs[i];
s32 j = 0;
u32 cmd;
// Loop over the displaylist commands until we hit an ENDDL
do {
u32 w0 = gfxBuffer[j++].words.w0;
cmd = w0 >> 0x18;
// If this command is a vertex command, adjust the vertex buffer address
if (cmd == G_VTX) {
// ImgFXVtx structs are 0xC bytes while Vtx are 0x10, so we need a (4/3) scaling factor
// to compute a new, equivalent Vtx[i] address for an existing ImgFXVtx[i] address.
// Unfortunately, using sizeof here does not match.
gfxBuffer[j-1].words.w1 = ((((s32) gfxBuffer[j-1].words.w1 - (s32) anim->keyframesOffset) / 3) * 4) +
(s32) state->vtxBufs[i];
}
} while (cmd != G_ENDDL);
}
}
return anim;
}
void imgfx_mesh_anim_update(ImgFXState* state) {
s32 absKeyframeInterval;
s32 nextKeyIdx;
s32 curKeyIdx;
ImgFXVtx* curKeyframe = NULL;
ImgFXVtx* nextKeyframe = NULL;
s32 keyframeInterval = state->ints.anim.interval;
s32 animStep = state->ints.anim.step;
s32 curSubframe = state->floats.anim.curFrame;
ImgFXAnimHeader* header = imgfx_load_anim(state);
u8* romStart;
f32 lerpAlpha;
s32 i;
if (header == NULL) {
return;
}
if (state->flags & IMGFX_FLAG_200) {
state->flags &= ~IMGFX_FLAG_200;
if (state->flags & IMGFX_FLAG_REVERSE_ANIM) {
state->floats.anim.curIdx = header->keyframesCount - 1;
}
}
curKeyIdx = state->floats.anim.curIdx;
absKeyframeInterval = abs(keyframeInterval);
if (state->flags & IMGFX_FLAG_4000) {
nextKeyIdx = curKeyIdx;
} else {
if (state->flags & IMGFX_FLAG_REVERSE_ANIM) {
nextKeyIdx = curKeyIdx - 1;
if (nextKeyIdx < 0) {
nextKeyIdx = curKeyIdx;
if (state->flags & IMGFX_FLAG_LOOP_ANIM) {
nextKeyIdx = header->keyframesCount - 1;
}
}
} else {
nextKeyIdx = curKeyIdx + 1;
if (nextKeyIdx == header->keyframesCount) {
nextKeyIdx = curKeyIdx;
if (state->flags & IMGFX_FLAG_LOOP_ANIM) {
nextKeyIdx = 0;
}
}
}
}
// find the current + next keyframe vertex data
curKeyframe = heap_malloc(header->vtxCount * sizeof(ImgFXVtx));
romStart = (u8*)((s32)imgfx_data_ROM_START + (s32) header->keyframesOffset + curKeyIdx * header->vtxCount * sizeof(ImgFXVtx));
dma_copy(romStart, romStart + header->vtxCount * sizeof(ImgFXVtx), curKeyframe);
if (keyframeInterval > 1) {
nextKeyframe = heap_malloc(header->vtxCount * sizeof(*nextKeyframe));
romStart = (u8*)((s32)imgfx_data_ROM_START + (s32) header->keyframesOffset + nextKeyIdx * header->vtxCount * sizeof(ImgFXVtx));
dma_copy(romStart, romStart + header->vtxCount * sizeof(ImgFXVtx), nextKeyframe);
}
lerpAlpha = (f32) curSubframe / (f32) keyframeInterval;
for (i = 0; i < header->vtxCount; i++) {
if (state->meshType != IMGFX_MESH_ANIMATED) {
return;
}
if (keyframeInterval > 1) {
// get vertex position, interpolated between keyframes
if (header->flags & IMGFX_ANIM_FLAG_ABSOLUTE_COORDS) {
state->vtxBufs[gCurrentDisplayContextIndex][i].v.ob[0] = (s16)(curKeyframe[i].ob[0] + (nextKeyframe[i].ob[0] - curKeyframe[i].ob[0]) * lerpAlpha);
state->vtxBufs[gCurrentDisplayContextIndex][i].v.ob[1] = (s16)(curKeyframe[i].ob[1] + (nextKeyframe[i].ob[1] - curKeyframe[i].ob[1]) * lerpAlpha);
state->vtxBufs[gCurrentDisplayContextIndex][i].v.ob[2] = (s16)(curKeyframe[i].ob[2] + (nextKeyframe[i].ob[2] - curKeyframe[i].ob[2]) * lerpAlpha);
} else {
state->vtxBufs[gCurrentDisplayContextIndex][i].v.ob[0] = (s16)(curKeyframe[i].ob[0] + (nextKeyframe[i].ob[0] - curKeyframe[i].ob[0]) * lerpAlpha) * 0.01 * ImgFXCurrentTexturePtr->tex.width;
state->vtxBufs[gCurrentDisplayContextIndex][i].v.ob[1] = (s16)(curKeyframe[i].ob[1] + (nextKeyframe[i].ob[1] - curKeyframe[i].ob[1]) * lerpAlpha) * 0.01 * ImgFXCurrentTexturePtr->tex.height;
state->vtxBufs[gCurrentDisplayContextIndex][i].v.ob[2] = (s16)(curKeyframe[i].ob[2] + (nextKeyframe[i].ob[2] - curKeyframe[i].ob[2]) * lerpAlpha) * 0.01 * ((ImgFXCurrentTexturePtr->tex.width + ImgFXCurrentTexturePtr->tex.height) / 2);
}
// get vertex color
if (state->flags & (IMGFX_FLAG_2000 | IMGFX_FLAG_8000)) {
state->vtxBufs[gCurrentDisplayContextIndex][i].v.cn[0] = (s16)(curKeyframe[i].cn[0] + (nextKeyframe[i].cn[0] - curKeyframe[i].cn[0]) * lerpAlpha);
state->vtxBufs[gCurrentDisplayContextIndex][i].v.cn[1] = (s16)(curKeyframe[i].cn[1] + (nextKeyframe[i].cn[1] - curKeyframe[i].cn[1]) * lerpAlpha);
state->vtxBufs[gCurrentDisplayContextIndex][i].v.cn[2] = (s16)(curKeyframe[i].cn[2] + (nextKeyframe[i].cn[2] - curKeyframe[i].cn[2]) * lerpAlpha);
} else {
state->vtxBufs[gCurrentDisplayContextIndex][i].v.cn[0] =
state->vtxBufs[gCurrentDisplayContextIndex][i].v.cn[1] =
state->vtxBufs[gCurrentDisplayContextIndex][i].v.cn[2] = 240.0 - (curKeyframe[i].tc[0] + curKeyframe[i].tc[1]) * 1.2;
}
} else {
// get vertex position
if (header->flags & IMGFX_ANIM_FLAG_ABSOLUTE_COORDS) {
state->vtxBufs[gCurrentDisplayContextIndex][i].v.ob[0] = curKeyframe[i].ob[0];
state->vtxBufs[gCurrentDisplayContextIndex][i].v.ob[1] = curKeyframe[i].ob[1];
state->vtxBufs[gCurrentDisplayContextIndex][i].v.ob[2] = curKeyframe[i].ob[2];
} else {
state->vtxBufs[gCurrentDisplayContextIndex][i].v.ob[0] = curKeyframe[i].ob[0] * 0.01 * ImgFXCurrentTexturePtr->tex.width;
state->vtxBufs[gCurrentDisplayContextIndex][i].v.ob[1] = curKeyframe[i].ob[1] * 0.01 * ImgFXCurrentTexturePtr->tex.height;
state->vtxBufs[gCurrentDisplayContextIndex][i].v.ob[2] = curKeyframe[i].ob[2] * 0.01 * ((ImgFXCurrentTexturePtr->tex.width + ImgFXCurrentTexturePtr->tex.height) / 2);
}
// get vertex color
if (state->flags & (IMGFX_FLAG_2000 | IMGFX_FLAG_8000)) {
state->vtxBufs[gCurrentDisplayContextIndex][i].v.cn[0] = curKeyframe[i].cn[0];
state->vtxBufs[gCurrentDisplayContextIndex][i].v.cn[1] = curKeyframe[i].cn[1];
state->vtxBufs[gCurrentDisplayContextIndex][i].v.cn[2] = curKeyframe[i].cn[2];
} else {
state->vtxBufs[gCurrentDisplayContextIndex][i].v.cn[0] =
state->vtxBufs[gCurrentDisplayContextIndex][i].v.cn[1] =
state->vtxBufs[gCurrentDisplayContextIndex][i].v.cn[2] = 240.0 - (curKeyframe[i].tc[0] + curKeyframe[i].tc[1]) * 1.2;
}
}
// get vertex tex coords
if (header->flags & IMGFX_ANIM_FLAG_ABSOLUTE_COORDS) {
state->vtxBufs[gCurrentDisplayContextIndex][i].v.tc[0] = (curKeyframe[i].tc[0] + 256) * 32;
state->vtxBufs[gCurrentDisplayContextIndex][i].v.tc[1] = (curKeyframe[i].tc[1] + 256) * 32;
} else {
state->vtxBufs[gCurrentDisplayContextIndex][i].v.tc[0] = ((s32)(curKeyframe[i].tc[0] * 0.01 * ImgFXCurrentTexturePtr->tex.width) + 256) * 32;
state->vtxBufs[gCurrentDisplayContextIndex][i].v.tc[1] = ((s32)(curKeyframe[i].tc[1] * 0.01 * ImgFXCurrentTexturePtr->tex.height) + 256) * 32;
}
}
state->firstVtxIdx = 0;
state->lastVtxIdx = header->vtxCount - 1;
heap_free(curKeyframe);
if (nextKeyframe != NULL) {
heap_free(nextKeyframe);
}
if (animStep == 0 || gGameStatusPtr->frameCounter % animStep != 0) {
return;
}
if (keyframeInterval > 0) {
curSubframe++;
if (curSubframe >= keyframeInterval) {
if (state->flags & IMGFX_FLAG_REVERSE_ANIM) {
curKeyIdx--;
if (curKeyIdx < 0) {
if (state->flags & IMGFX_FLAG_LOOP_ANIM) {
curKeyIdx = header->keyframesCount - 1;
} else {
if (state->flags & IMGFX_FLAG_800) {
curKeyIdx = 0;
state->flags |= IMGFX_FLAG_4000;
} else {
state->flags |= IMGFX_FLAG_ANIM_DONE;
}
}
}
} else {
curKeyIdx++;
if (curKeyIdx >= header->keyframesCount) {
if (state->flags & IMGFX_FLAG_LOOP_ANIM) {
curKeyIdx = 0;
} else {
if (state->flags & IMGFX_FLAG_800) {
curKeyIdx--;
state->flags |= IMGFX_FLAG_4000;
} else {
state->flags |= IMGFX_FLAG_ANIM_DONE;
}
}
}
}
curSubframe = 0;
}
} else if (keyframeInterval < 0) {
if (state->flags & IMGFX_FLAG_REVERSE_ANIM) {
curKeyIdx -= absKeyframeInterval;
if (curKeyIdx < 0) {
if (state->flags & IMGFX_FLAG_LOOP_ANIM) {
curKeyIdx += header->keyframesCount;
} else {
if (state->flags & IMGFX_FLAG_800) {
curKeyIdx = 0;
state->flags |= IMGFX_FLAG_4000;
} else {
state->flags |= IMGFX_FLAG_ANIM_DONE;
}
}
}
} else {
curKeyIdx += absKeyframeInterval;
if (curKeyIdx >= header->keyframesCount) {
if (state->flags & IMGFX_FLAG_LOOP_ANIM) {
curKeyIdx %= header->keyframesCount;
} else {
if (state->flags & IMGFX_FLAG_800) {
curKeyIdx = header->keyframesCount - 1;
state->flags |= IMGFX_FLAG_4000;
} else {
state->flags |= IMGFX_FLAG_ANIM_DONE;
}
}
}
}
}
state->floats.anim.curFrame = curSubframe;
state->floats.anim.curIdx = curKeyIdx;
}
void imgfx_appendGfx_mesh_basic(ImgFXState* state, Matrix4f mtx) {
s32 i;
if (!(state->flags & IMGFX_FLAG_SKIP_TEX_SETUP)) {
gDPSetTextureLUT(gMainGfxPos++, G_TT_RGBA16);
gDPLoadTLUT_pal16(gMainGfxPos++, 0, ImgFXCurrentTexturePtr->tex.palette);
}
i = state->firstVtxIdx;
while (TRUE) {
Camera* cam;
s32 uls = (imgfx_vtxBuf[i + 0].v.tc[0] >> 0x5) - 256;
s32 ult = (imgfx_vtxBuf[i + 0].v.tc[1] >> 0x5) - 256;
s32 lrs = (imgfx_vtxBuf[i + 3].v.tc[0] >> 0x5) - 256;
s32 lrt = (imgfx_vtxBuf[i + 3].v.tc[1] >> 0x5) - 256;
s32 someFlags = IMGFX_FLAG_100000 | IMGFX_FLAG_80000;
s32 alpha;
s32 alpha2;
if (!(state->flags & IMGFX_FLAG_SKIP_TEX_SETUP)) {
if ((gSpriteShadingProfile->flags & SPR_SHADING_FLAG_ENABLED)
&& (state->arrayIdx != 0)
&& (state->flags & someFlags)
&& ( state->renderType == IMGFX_RENDER_DEFAULT
|| state->renderType == IMGFX_RENDER_MULTIPLY_ALPHA
|| state->renderType == IMGFX_RENDER_OVERLAY_RGBA
|| state->renderType == IMGFX_RENDER_MULTIPLY_SHADE_ALPHA)
) {
gDPScrollMultiTile2_4b(gMainGfxPos++,
ImgFXCurrentTexturePtr->tex.raster, G_IM_FMT_CI,
ImgFXCurrentTexturePtr->tex.width, ImgFXCurrentTexturePtr->tex.height, // img size
uls, ult, // top left
lrs - 1, lrt - 1, // bottom right
0, // palette
G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, // clamp wrap mirror
8, 8, // mask
G_TX_NOLOD, G_TX_NOLOD, // shift,
0x100, 0x100); // scroll
gDPSetTile(gMainGfxPos++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 4, 0x100, 2, 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);
gDPSetTileSize(gMainGfxPos++, 2, 0, 0, 252, 0);
alpha = 255;
switch (state->renderType) {
case IMGFX_RENDER_DEFAULT:
break;
case IMGFX_RENDER_MULTIPLY_ALPHA:
case IMGFX_RENDER_OVERLAY_RGBA:
alpha = state->ints.color.a;
break;
case IMGFX_RENDER_MULTIPLY_SHADE_ALPHA:
alpha = -1;
break;
}
if ((gSpriteShadingProfile->flags & SPR_SHADING_FLAG_SET_VIEWPORT)
&& ((*ImgFXInstances)[0].arrayIdx != 0)
&& (state->flags & someFlags)
) {
cam = &gCameras[gCurrentCamID];
if (gGameStatusPtr->isBattle == 2) {
gSPViewport(gMainGfxPos++, &D_8014EE50);
} else {
gSPViewport(gMainGfxPos++, &cam->vpAlt);
}
gDPSetRenderMode(gMainGfxPos++, G_RM_PASS, state->otherModeL);
if (alpha == -1) {
gDPSetCombineMode(gMainGfxPos++, PM_CC_0D, PM_CC_0C);
} else {
gDPSetEnvColor(gMainGfxPos++, 0, 0, 0, alpha);
gDPSetCombineMode(gMainGfxPos++, PM_CC_0B, PM_CC_0C);
}
gSPVertex(gMainGfxPos++, &imgfx_vtxBuf[i], 4, 0);
gSP2Triangles(gMainGfxPos++, 0, 2, 1, 0, 1, 2, 3, 0);
gDPPipeSync(gMainGfxPos++);
}
create_shading_palette(mtx, uls, ult, lrs, lrt, alpha, state->otherModeL);
} else {
gDPScrollTextureTile_4b(gMainGfxPos++,
ImgFXCurrentTexturePtr->tex.raster, G_IM_FMT_CI,
ImgFXCurrentTexturePtr->tex.width, ImgFXCurrentTexturePtr->tex.height, // img size
uls, ult, // top left
lrs - 1, lrt - 1, // bottom right
0, // palette
G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, // clamp wrap mirror
8, 8, // mask
G_TX_NOLOD, G_TX_NOLOD, // shift,
0x100, 0x100); // scroll
if ((gSpriteShadingProfile->flags & SPR_SHADING_FLAG_SET_VIEWPORT)
&& state->arrayIdx != 0
&& (state->flags & someFlags)
) {
alpha2 = 255;
cam = &gCameras[gCurrentCamID];
if (gGameStatusPtr->isBattle == 2) {
gSPViewport(gMainGfxPos++, &D_8014EE50);
} else {
gSPViewport(gMainGfxPos++, &cam->vpAlt);
}
if (alpha2 == 255) {
gDPSetRenderMode(gMainGfxPos++, G_RM_AA_ZB_TEX_EDGE, G_RM_AA_ZB_TEX_EDGE2);
} else {
gDPSetRenderMode(gMainGfxPos++, G_RM_ZB_XLU_SURF, G_RM_ZB_XLU_SURF2);
}
switch (state->renderType) {
case IMGFX_RENDER_DEFAULT:
alpha2 = 255;
break;
case IMGFX_RENDER_MULTIPLY_ALPHA:
case IMGFX_RENDER_OVERLAY_RGBA:
alpha2 = state->ints.color.a;
break;
case IMGFX_RENDER_MULTIPLY_SHADE_ALPHA:
alpha2 = -1;
break;
}
if (alpha2 == -1) {
gDPSetCombineMode(gMainGfxPos++, PM_CC_59, PM_CC_59);
} else {
gDPSetEnvColor(gMainGfxPos++, 0, 0, 0, alpha2);
gDPSetCombineMode(gMainGfxPos++, PM_CC_0A, PM_CC_0A);
}
gSPVertex(gMainGfxPos++, &imgfx_vtxBuf[i], 4, 0);
gSP2Triangles(gMainGfxPos++, 0, 2, 1, 0, 1, 2, 3, 0);
gDPPipeSync(gMainGfxPos++);
if (alpha2 == 255) {
gDPSetRenderMode(gMainGfxPos++, G_RM_AA_ZB_TEX_EDGE, G_RM_AA_ZB_TEX_EDGE2);
} else {
gDPSetRenderMode(gMainGfxPos++, G_RM_ZB_XLU_SURF, G_RM_ZB_XLU_SURF2);
}
gDPSetEnvColor(gMainGfxPos++, 100, 100, 100, 255);
gDPSetPrimColor(gMainGfxPos++, 0, 0, 0, 0, 0, alpha2);
gDPSetCombineMode(gMainGfxPos++, PM_CC_5A, PM_CC_5A);
gDPSetColorDither(gMainGfxPos++, G_CD_MAGICSQ);
}
}
}
if ((gSpriteShadingProfile->flags & SPR_SHADING_FLAG_SET_VIEWPORT)
&& (*ImgFXInstances)[0].arrayIdx != 0
&& (state->flags & someFlags)
) {
cam = &gCameras[gCurrentCamID];
if (gGameStatusPtr->isBattle == 2) {
gSPViewport(gMainGfxPos++, &D_8014EE40);
D_8014EE50.vp.vtrans[0] = D_8014EE40.vp.vtrans[0] + gGameStatusPtr->altViewportOffset.x;
D_8014EE50.vp.vtrans[1] = D_8014EE40.vp.vtrans[1] + gGameStatusPtr->altViewportOffset.y;
} else {
gSPViewport(gMainGfxPos++, &cam->vp);
}
}
gSPVertex(gMainGfxPos++, &imgfx_vtxBuf[i], 4, 0);
gSP2Triangles(gMainGfxPos++, 0, 2, 1, 0, 1, 2, 3, 0);
if (i + 3 >= state->lastVtxIdx) {
break;
}
i += 2;
}
}
void imgfx_appendGfx_mesh_grid(ImgFXState* state, Matrix4f mtx) {
s32 i, j;
s32 firstVtxIdx;
if (!(state->flags & IMGFX_FLAG_SKIP_TEX_SETUP)) {
gDPSetTextureLUT(gMainGfxPos++, G_TT_RGBA16);
gDPLoadTLUT_pal16(gMainGfxPos++, 0, ImgFXCurrentTexturePtr->tex.palette);
}
firstVtxIdx = state->firstVtxIdx;
for (i = 0; i < state->subdivY; i++) {
for (j = 0; j < state->subdivX; j++) {
s32 ulIdx = firstVtxIdx + i * (state->subdivX + 1) + j;
s32 urIdx = firstVtxIdx + i * (state->subdivX + 1) + j + 1;
s32 llIdx = firstVtxIdx + (i + 1) * (state->subdivX + 1) + j;
s32 lrIdx = firstVtxIdx + (i + 1) * (state->subdivX + 1) + j + 1;
if (!(state->flags & IMGFX_FLAG_SKIP_TEX_SETUP)) {
if ((gSpriteShadingProfile->flags & SPR_SHADING_FLAG_ENABLED)
&& (*ImgFXInstances)[0].arrayIdx != 0
&& (state->flags & (IMGFX_FLAG_100000 | IMGFX_FLAG_80000))
&& (state->renderType == IMGFX_RENDER_DEFAULT
|| state->renderType == IMGFX_RENDER_MULTIPLY_ALPHA
|| state->renderType == IMGFX_RENDER_MULTIPLY_SHADE_ALPHA)
) {
s32 alpha = 255;
gDPScrollMultiTile2_4b(gMainGfxPos++,
ImgFXCurrentTexturePtr->tex.raster, G_IM_FMT_CI,
ImgFXCurrentTexturePtr->tex.width, ImgFXCurrentTexturePtr->tex.height, // img size
(imgfx_vtxBuf[ulIdx].v.tc[0] >> 5) - 0x100, (imgfx_vtxBuf[ulIdx].v.tc[1] >> 5) - 0x100, // top left
(imgfx_vtxBuf[lrIdx].v.tc[0] >> 5) - 0x100 - 1, (imgfx_vtxBuf[lrIdx].v.tc[1] >> 5) - 0x100 - 1, // bottom right
0, // palette
G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, // clamp wrap mirror
8, 8, // mask
G_TX_NOLOD, G_TX_NOLOD, // shift,
0x100, 0x100); // scroll
gDPSetTile(gMainGfxPos++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 4, 0x0100, 2, 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);
gDPSetTileSize(gMainGfxPos++, 2, 0, 0, 63 << 2, 0);
switch (state->renderType) {
case IMGFX_RENDER_DEFAULT:
alpha = 255;
break;
case IMGFX_RENDER_MULTIPLY_ALPHA:
alpha = state->ints.color.a;
break;
case IMGFX_RENDER_MULTIPLY_SHADE_ALPHA:
alpha = -1;
break;
}
create_shading_palette(mtx,
(imgfx_vtxBuf[ulIdx].v.tc[0] >> 5) - 0x100, (imgfx_vtxBuf[ulIdx].v.tc[1] >> 5) - 0x100,
(imgfx_vtxBuf[lrIdx].v.tc[0] >> 5) - 0x100, (imgfx_vtxBuf[lrIdx].v.tc[1] >> 5) - 0x100,
alpha, state->otherModeL);
} else {
gDPScrollTextureTile_4b(gMainGfxPos++,
ImgFXCurrentTexturePtr->tex.raster, G_IM_FMT_CI,
ImgFXCurrentTexturePtr->tex.width, ImgFXCurrentTexturePtr->tex.height, // img size
(imgfx_vtxBuf[ulIdx].v.tc[0] >> 5) - 0x100, (imgfx_vtxBuf[ulIdx].v.tc[1] >> 5) - 0x100, // top left
(imgfx_vtxBuf[lrIdx].v.tc[0] >> 5) - 0x100 - 1, (imgfx_vtxBuf[lrIdx].v.tc[1] >> 5) - 0x100 - 1, // bottom right
0, // palette
G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, // clamp wrap mirror
8, 8, // mask
G_TX_NOLOD, G_TX_NOLOD, // shift,
0x100, 0x100); // scroll
}
}
gSPVertex(gMainGfxPos++, &imgfx_vtxBuf[ulIdx], 1, 0);
gSPVertex(gMainGfxPos++, &imgfx_vtxBuf[urIdx], 1, 1);
gSPVertex(gMainGfxPos++, &imgfx_vtxBuf[llIdx], 1, 2);
gSPVertex(gMainGfxPos++, &imgfx_vtxBuf[lrIdx], 1, 3);
gSP2Triangles(gMainGfxPos++, 0, 2, 1, 0, 1, 2, 3, 0);
}
}
}
void imgfx_appendGfx_mesh_anim(ImgFXState* state, Matrix4f mtx) {
if (state->vtxBufs[gCurrentDisplayContextIndex] == NULL || state->gfxBufs[gCurrentDisplayContextIndex] == NULL) {
return;
}
guScale(&gDisplayContext->matrixStack[gMatrixListPos], 0.1f, 0.1f, 0.1f);
gSPMatrix(gMainGfxPos++, VIRTUAL_TO_PHYSICAL(&gDisplayContext->matrixStack[gMatrixListPos++]), G_MTX_PUSH | G_MTX_MUL | G_MTX_MODELVIEW);
if (!(state->flags & IMGFX_FLAG_SKIP_TEX_SETUP)) {
gDPSetTextureLUT(gMainGfxPos++, G_TT_RGBA16);
gDPLoadTLUT_pal16(gMainGfxPos++, 0, ImgFXCurrentTexturePtr->tex.palette);
if ((gSpriteShadingProfile->flags & SPR_SHADING_FLAG_ENABLED)
&& (state->flags & (IMGFX_FLAG_100000 | IMGFX_FLAG_80000))
&& (state->renderType == IMGFX_RENDER_DEFAULT
|| state->renderType == IMGFX_RENDER_MULTIPLY_ALPHA
|| state->renderType == IMGFX_RENDER_MULTIPLY_SHADE_ALPHA
|| state->renderType == IMGFX_RENDER_ANIM)
) {
s32 alpha = 255;
gDPScrollMultiTile2_4b(gMainGfxPos++, ImgFXCurrentTexturePtr->tex.raster, G_IM_FMT_CI,
ImgFXCurrentTexturePtr->tex.width, ImgFXCurrentTexturePtr->tex.height,
0, 0, ImgFXCurrentTexturePtr->tex.width - 1, ImgFXCurrentTexturePtr->tex.height - 1, 0,
G_TX_CLAMP, G_TX_CLAMP, 8, 8, G_TX_NOLOD, G_TX_NOLOD,
256, 256);
gDPSetTile(gMainGfxPos++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 4, 0x0100, 2, 0,
G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD);
gDPSetTileSize(gMainGfxPos++, 2, 0, 0, 252, 0);
switch (state->renderType) {
case IMGFX_RENDER_DEFAULT:
case IMGFX_RENDER_ANIM:
alpha = 255;
break;
case IMGFX_RENDER_MULTIPLY_ALPHA:
alpha = state->ints.color.a;
break;
case IMGFX_RENDER_MULTIPLY_SHADE_ALPHA:
alpha = -1;
break;
}
create_shading_palette(mtx, 0, 0, ImgFXCurrentTexturePtr->tex.width, ImgFXCurrentTexturePtr->tex.height, alpha, state->otherModeL);
} else {
gDPScrollTextureTile_4b(gMainGfxPos++, ImgFXCurrentTexturePtr->tex.raster, G_IM_FMT_CI,
ImgFXCurrentTexturePtr->tex.width, ImgFXCurrentTexturePtr->tex.height,
0, 0, ImgFXCurrentTexturePtr->tex.width - 1, ImgFXCurrentTexturePtr->tex.height - 1, 0,
G_TX_CLAMP, G_TX_CLAMP, 8, 8, G_TX_NOLOD, G_TX_NOLOD,
256, 256);
}
}
gSPDisplayList(gMainGfxPos++, state->gfxBufs[gCurrentDisplayContextIndex]);
gSPPopMatrix(gMainGfxPos++, G_MTX_MODELVIEW);
}
void imgfx_appendGfx_mesh_strip(ImgFXState* state, Matrix4f mtx) {
ImgFXOverlayTexture* ufs = state->ints.overlay.pattern;
s32 shifts = integer_log(ufs->width, 2);
s32 shiftt = integer_log(ufs->height, 2);
s32 uls, ult;
s32 lrs, lrt;
guScale(&gDisplayContext->matrixStack[gMatrixListPos], (f32)ImgFXCurrentTexturePtr->tex.width / 100.0, (f32)ImgFXCurrentTexturePtr->tex.height / 100.0, 1.0f);
gSPMatrix(gMainGfxPos++, VIRTUAL_TO_PHYSICAL(&gDisplayContext->matrixStack[gMatrixListPos++]), G_MTX_PUSH | G_MTX_MUL | G_MTX_MODELVIEW);
gDPSetRenderMode(gMainGfxPos++, G_RM_ZB_XLU_DECAL, G_RM_ZB_XLU_DECAL2);
if (state->renderType == IMGFX_RENDER_OVERLAY_RGBA) {
s32 alpha = state->ints.overlay.alpha;
gDPSetPrimColor(gMainGfxPos++, 0, 0, 0, 0, 0, alpha);
gDPSetCombineMode(gMainGfxPos++, PM_CC_05, PM_CC_05);
} else {
gDPSetCombineMode(gMainGfxPos++, G_CC_MODULATEIA, G_CC_MODULATEIA);
}
gDPSetTextureLUT(gMainGfxPos++, G_TT_RGBA16);
gDPLoadTLUT_pal16(gMainGfxPos++, 0, ufs->palette);
gDPScrollTextureTile_4b(gMainGfxPos++, ufs->raster, G_IM_FMT_CI, ufs->width, ufs->height,
0, 0, ufs->width - 1, ufs->height - 1, 0,
G_TX_WRAP, G_TX_WRAP, shifts, shiftt, G_TX_NOLOD, G_TX_NOLOD,
256, 256);
uls = state->floats.overlay.posX;
ult = state->floats.overlay.posY;
lrs = ufs->width * 4 + state->floats.overlay.posX;
lrt = ufs->height * 4 + state->floats.overlay.posY;
gDPSetTileSize(gMainGfxPos++, G_TX_RENDERTILE, uls, ult, lrs, lrt);
state->floats.overlay.posX = (s32)(state->floats.overlay.posX + ufs->offsetX) % (ufs->width * 4);
state->floats.overlay.posY = (s32)(state->floats.overlay.posY + ufs->offsetY) % (ufs->height * 4);
gSPDisplayList(gMainGfxPos++, ufs->displayList);
gSPPopMatrix(gMainGfxPos++, G_MTX_MODELVIEW);
}
void imgfx_wavy_init(ImgFXState* state) {
state->floats.wavy.phase1 = 0.0f;
state->floats.wavy.phase2 = 50.0f;
state->floats.wavy.phase3 = 30.0f;
}
void imgfx_mesh_make_wavy(ImgFXState* state) {
Vtx* v1;
Vtx* v2;
Vtx* v3;
f32 vx;
f32 vy;
f32 vz;
f32 angle1;
f32 angle2;
f32 angle3;
f32 phase1;
f32 phase2;
f32 phase3;
s32 angleInc;
s32 amt;
s32 sign;
s32 i;
phase1 = (f32) gGameStatusPtr->frameCounter / 10.3;
while (phase1 > 360.0) {
phase1 -= 360.0;
}
phase2 = (f32) (gGameStatusPtr->frameCounter + 40) / 11.2;
while (phase2 > 360.0) {
phase2 -= 360.0;
}
phase3 = (f32) (gGameStatusPtr->frameCounter + 25) / 10.8;
while (phase3 > 360.0) {
phase3 -= 360.0;
}
state->floats.wavy.phase1 = phase1;
state->floats.wavy.phase2 = phase2;
state->floats.wavy.phase3 = phase3;
if (state->floats.wavy.phase1 >= 360.0) {
state->floats.wavy.phase1-= 360.0;
}
if (state->floats.wavy.phase2 >= 360.0) {
state->floats.wavy.phase2 -= 360.0;
}
if (state->floats.wavy.phase3 >= 360.0) {
state->floats.wavy.phase3 -= 360.0;
}
sign = 0;
angleInc = 0;
amt = (state->lastVtxIdx - state->firstVtxIdx) - state->subdivX;
for (i = 0; i < amt; i++) {
angle1 = state->floats.wavy.phase1 + (angleInc * 45) + (sign * 180);
angle2 = state->floats.wavy.phase2 + (angleInc * 45) + (sign * 180);
angle3 = state->floats.wavy.phase3 + (angleInc * 45) + (sign * 180);
//TODO find better match
v1 = (Vtx*)((state->firstVtxIdx + i) * sizeof(Vtx) + (s32)imgfx_vtxBuf);
vx = v1->v.ob[0];
v1->v.ob[0] = (vx + (sin_rad(angle1) * state->ints.wavy.mag.x)); /// @bug? should be sin_deg?
v2 = (Vtx*)((state->firstVtxIdx + i) * sizeof(Vtx) + (s32)imgfx_vtxBuf);
vy = v2->v.ob[1];
v2->v.ob[1] = (vy + (sin_rad(angle2) * state->ints.wavy.mag.y));
v3 = (Vtx*)((state->firstVtxIdx + i) * sizeof(Vtx) + (s32)imgfx_vtxBuf);
vz = v3->v.ob[2];
v3->v.ob[2] = (vz + (sin_rad(angle3) * state->ints.wavy.mag.z));
angleInc++;
if (i % (state->subdivX + 1) == 0) {
angleInc = 0;
sign = !sign;
}
}
}
void imgfx_mesh_load_colors(ImgFXState* state) {
f32 alpha = (f32)ImgFXCurrentTexturePtr->alphaMultiplier / 255.0;
s32 vtxCount = state->lastVtxIdx - state->firstVtxIdx;
s32 i;
for (i = 0; i <= vtxCount; i++) {
imgfx_vtxBuf[state->firstVtxIdx + i].v.cn[0] = state->colorBuf[i].r;
imgfx_vtxBuf[state->firstVtxIdx + i].v.cn[1] = state->colorBuf[i].g;
imgfx_vtxBuf[state->firstVtxIdx + i].v.cn[2] = state->colorBuf[i].b;
imgfx_vtxBuf[state->firstVtxIdx + i].v.cn[3] = state->colorBuf[i].a * alpha;
}
}