perfect_dark/src/lib/model.c

4405 lines
109 KiB
C

#include <ultra64.h>
#include "constants.h"
#include "game/game_096700.h"
#include "game/acosfasinf.h"
#include "game/quaternion.h"
#include "game/camera.h"
#include "game/floor.h"
#include "game/ceil.h"
#include "game/tex.h"
#include "game/gfxmemory.h"
#include "game/bg.h"
#include "game/file.h"
#include "bss.h"
#include "lib/rng.h"
#include "lib/mtx.h"
#include "lib/anim.h"
#include "lib/model.h"
#include "data.h"
#include "types.h"
/**
* -- Model Definitions --
*
* A model definition is a representation of a model file.
* The term is sometimes abbreviated to modeldef.
*
* A single model definition can be shared by multiple model instances.
* For characters, their body is one model instance and their head is another
* instance, so guards who share the same outfit can share the same body
* modeldef, and guards who have the same head can share the same head modeldef.
*
* A modeldef contains a tree of nodes. A node can have any number of siblings,
* as well as children and grandchildren and so on. Each node has a type, and
* each node can contain data specific to that type.
*
* Some common node types are:
* - Distance nodes, which can turn on/off their child nodes based on distance
* to the camera.
* - Toggle nodes, which can turn on/off their child nodes by other places in
* the game (for example, hiding Cass's necklace from her model).
* - Headspot nodes, which mark a point on a body model where a head can be
* attached.
* - Displaylist nodes, which contain GBI bytecode for rendering.
* - Bbox (bounding box) nodes, which are commonly used for shield hit detection.
*
* The data for these nodes is referred to as read-only data (rodata).
* It's read-only because changing it would affect all model instances that use
* this modeldef, which is typically undesired.
*
* Modeldefs can contain a parts list. The parts list is a mapping of part
* numbers to certain nodes within the tree. They allow the game to quickly get
* a pointer to a node by part number, without having to iterate through the
* tree to find it. A part number can map to a node of any type, but the game
* assumes certain numbers map to certain types. For example, it assumes that
* the part number for Cass's necklace maps to a toggle node.
*
* -- Model Instances --
*
* Model instances are usually just referred to as "models" in decomp.
*
* Each model instance contains a pointer to its modeldef and can have an
* animation applied to it, among other things.
*
* Model instances also have read-write data (rwdata). rwdata stores
* instance-specific node data, such as position/rotation, toggle state and the
* chosen head. Each instance has a pointer to its rwdata allocation, and each
* node in the model definition contains a rwdataindex which is its index in any
* instance's rwdata allocation. The node rwdata for an instance can be found by
* adding the two together.
*
* When working with a model, its common to detach and reattach nodes in the
* model definition's node tree. For example, a distance node may enable or
* disable its children, toggle nodes may be enabled or disabled, and heads may
* be replaced as needed. This is all done by clearing or populating a node's
* child pointer in the model definition. This leaves the modeldef in an
* inconsistent state, so any time a model instance is being worked on it needs
* to re-apply its relations to the modeldef. Some of these relations can be
* calculated on the fly (such as distance nodes) while others are saved in
* rwdata (such as the selected head).
*/
#if VERSION >= VERSION_PAL_BETA
u8 var8005efb0_2 = 0;
#endif
u32 var8005efb0 = 0;
bool g_ModelDistanceDisabled = false;
f32 g_ModelDistanceScale = 1;
bool var8005efbc = false;
f32 var8005efc0 = 0;
bool (*var8005efc4)(struct model *model, struct modelnode *node) = NULL;
#if VERSION >= VERSION_PAL_BETA
bool var8005efd8_2 = false;
#endif
Vtx *(*g_ModelVtxAllocatorFunc)(s32 numvertices) = NULL;
void (*g_ModelJointPositionedFunc)(s32 mtxindex, Mtxf *mtx) = NULL;
void modelSetDistanceChecksDisabled(bool disabled)
{
g_ModelDistanceDisabled = disabled;
}
void modelSetDistanceScale(f32 scale)
{
g_ModelDistanceScale = scale;
}
void modelSetVtxAllocatorFunc(Vtx *(*fn)(s32 numvertices))
{
g_ModelVtxAllocatorFunc = fn;
}
/**
* Ascend the node's parents until a node type is found which supports an mtx.
* Return the instance-local mtx index for that node.
*
* Position nodes support up to 3 matrices. In this case the desired one can be
* specified with arg1.
*/
s32 modelFindNodeMtxIndex(struct modelnode *node, s32 arg1)
{
s32 index;
union modelrodata *rodata1;
union modelrodata *rodata2;
union modelrodata *rodata3;
while (node) {
switch (node->type & 0xff) {
case MODELNODETYPE_CHRINFO:
rodata1 = node->rodata;
return rodata1->chrinfo.mtxindex;
case MODELNODETYPE_POSITION:
rodata2 = node->rodata;
return rodata2->position.mtxindexes[arg1 == 0x200 ? 2 : (arg1 == 0x100 ? 1 : 0)];
case MODELNODETYPE_POSITIONHELD:
rodata3 = node->rodata;
return rodata3->positionheld.mtxindex;
}
node = node->parent;
}
return -1;
}
Mtxf *modelFindNodeMtx(struct model *model, struct modelnode *node, s32 arg2)
{
s32 index = modelFindNodeMtxIndex(node, arg2);
if (index >= 0) {
return &model->matrices[index];
}
return NULL;
}
Mtxf *modelGetRootMtx(struct model *model)
{
return modelFindNodeMtx(model, model->definition->rootnode, 0);
}
struct modelnode *modelFindNodeByMtxIndex(struct model *model, s32 mtxindex)
{
struct modelnode *node = model->definition->rootnode;
union modelrodata *rodata1;
union modelrodata *rodata2;
union modelrodata *rodata3;
while (node) {
switch (node->type & 0xff) {
case MODELNODETYPE_CHRINFO:
rodata1 = node->rodata;
if (mtxindex == rodata1->chrinfo.mtxindex) {
return node;
}
break;
case MODELNODETYPE_POSITION:
rodata2 = node->rodata;
if (mtxindex == rodata2->position.mtxindexes[0]
|| mtxindex == rodata2->position.mtxindexes[1]
|| mtxindex == rodata2->position.mtxindexes[2]) {
return node;
}
break;
case MODELNODETYPE_POSITIONHELD:
rodata3 = node->rodata;
if (mtxindex == rodata3->positionheld.mtxindex) {
return node;
}
break;
}
if (node->child) {
node = node->child;
} else {
while (node) {
if (node && node->next) {
node = node->next;
break;
}
node = node->parent;
}
}
}
return NULL;
}
struct modelnode *modelNodeFindMtxNode(struct modelnode *node)
{
while (node) {
u32 type = node->type & 0xff;
if (type == MODELNODETYPE_CHRINFO
|| type == MODELNODETYPE_POSITION
|| type == MODELNODETYPE_POSITIONHELD) {
break;
}
node = node->parent;
}
return node;
}
struct modelnode *modelNodeFindParentMtxNode(struct modelnode *node)
{
while ((node = node->parent)) {
u32 type = node->type & 0xff;
if (type == MODELNODETYPE_CHRINFO
|| type == MODELNODETYPE_POSITION
|| type == MODELNODETYPE_POSITIONHELD) {
break;
}
}
return node;
}
struct modelnode *modelNodeFindChildMtxNode(struct modelnode *basenode)
{
struct modelnode *node = basenode->child;
while (node) {
u32 type = node->type & 0xff;
if (type == MODELNODETYPE_CHRINFO
|| type == MODELNODETYPE_POSITION
|| type == MODELNODETYPE_POSITIONHELD) {
break;
}
if (node->child) {
node = node->child;
} else {
while (node) {
if (node == basenode) {
node = NULL;
break;
}
if (node->next) {
node = node->next;
break;
}
node = node->parent;
}
}
}
return node;
}
struct modelnode *modelNodeFindChildOrParentMtxNode(struct modelnode *basenode)
{
struct modelnode *node = basenode;
struct modelnode *next;
u32 type;
while (node) {
if (node != basenode && node->child) {
node = node->child;
} else {
while (node) {
if (node != basenode) {
type = node->type & 0xff;
if (type == MODELNODETYPE_CHRINFO
|| type == MODELNODETYPE_POSITION
|| type == MODELNODETYPE_POSITIONHELD) {
node = NULL;
break;
}
}
if (node->next) {
node = node->next;
break;
}
node = node->parent;
}
if (!node) {
break;
}
}
type = node->type & 0xff;
if (type == MODELNODETYPE_CHRINFO
|| type == MODELNODETYPE_POSITION
|| type == MODELNODETYPE_POSITIONHELD) {
break;
}
}
return node;
}
struct modelnode *modelGetPart(struct modeldef *modeldef, s32 partnum)
{
s32 upper;
s32 lower;
u32 i;
s16 *partnums;
if (modeldef->numparts == 0) {
return NULL;
}
partnums = (s16 *)&modeldef->parts[modeldef->numparts];
lower = 0;
upper = modeldef->numparts;
while (upper >= lower) {
i = (lower + upper) / 2;
if (partnum == partnums[i]) {
return modeldef->parts[i];
}
if (partnum < partnums[i]) {
upper = i - 1;
} else {
lower = i + 1;
}
}
return NULL;
}
void *modelGetPartRodata(struct modeldef *modeldef, s32 partnum)
{
struct modelnode *node = modelGetPart(modeldef, partnum);
if (node) {
return node->rodata;
}
return NULL;
}
f32 modelGetScreenDistance(struct model *model)
{
Mtxf *mtx = modelGetRootMtx(model);
if (mtx) {
return -mtx->m[3][2];
}
return 0;
}
#if VERSION >= VERSION_NTSC_1_0
// ntsc-beta has this function in another file
void *modelGetNodeRwData(struct model *model, struct modelnode *node)
{
u32 index = 0;
u32 *rwdatas = model->rwdatas;
switch (node->type & 0xff) {
case MODELNODETYPE_CHRINFO:
index = node->rodata->chrinfo.rwdataindex;
break;
case MODELNODETYPE_DL:
index = node->rodata->dl.rwdataindex;
break;
case MODELNODETYPE_DISTANCE:
index = node->rodata->distance.rwdataindex;
break;
case MODELNODETYPE_TOGGLE:
index = node->rodata->toggle.rwdataindex;
break;
case MODELNODETYPE_REORDER:
index = node->rodata->reorder.rwdataindex;
break;
case MODELNODETYPE_0B:
index = node->rodata->type0b.rwdataindex;
break;
case MODELNODETYPE_CHRGUNFIRE:
index = node->rodata->chrgunfire.rwdataindex;
break;
case MODELNODETYPE_HEADSPOT:
index = node->rodata->headspot.rwdataindex;
break;
}
while (node->parent) {
node = node->parent;
if ((node->type & 0xff) == MODELNODETYPE_HEADSPOT) {
struct modelrwdata_headspot *tmp = modelGetNodeRwData(model, node);
rwdatas = tmp->rwdatas;
break;
}
}
return &rwdatas[index];
}
#endif
void modelNodeGetPosition(struct model *model, struct modelnode *node, struct coord *pos)
{
switch (node->type & 0xff) {
case MODELNODETYPE_CHRINFO:
{
struct modelrwdata_chrinfo *rwdata = modelGetNodeRwData(model, node);
pos->x = rwdata->pos.x;
pos->y = rwdata->pos.y;
pos->z = rwdata->pos.z;
}
break;
case MODELNODETYPE_POSITION:
{
struct modelrodata_position *rodata = &node->rodata->position;
pos->x = rodata->pos.x;
pos->y = rodata->pos.y;
pos->z = rodata->pos.z;
}
break;
case MODELNODETYPE_POSITIONHELD:
{
struct modelrodata_positionheld *rodata = &node->rodata->positionheld;
pos->x = rodata->pos.x;
pos->y = rodata->pos.y;
pos->z = rodata->pos.z;
}
break;
default:
pos->x = 0;
pos->y = 0;
pos->z = 0;
break;
}
}
void modelNodeSetPosition(struct model *model, struct modelnode *node, struct coord *pos)
{
switch (node->type & 0xff) {
case MODELNODETYPE_CHRINFO:
{
struct modelrwdata_chrinfo *rwdata = modelGetNodeRwData(model, node);
struct coord diff[1];
diff[0].x = pos->x - rwdata->pos.x;
diff[0].z = pos->z - rwdata->pos.z;
rwdata->pos.x = pos->x;
rwdata->pos.y = pos->y;
rwdata->pos.z = pos->z;
rwdata->unk24.x += diff[0].x; rwdata->unk24.z += diff[0].z;
rwdata->unk34.x += diff[0].x; rwdata->unk34.z += diff[0].z;
rwdata->unk40.x += diff[0].x; rwdata->unk40.z += diff[0].z;
rwdata->unk4c.x += diff[0].x; rwdata->unk4c.z += diff[0].z;
}
break;
case MODELNODETYPE_POSITION:
{
struct modelrodata_position *rodata = &node->rodata->position;
rodata->pos.x = pos->x;
rodata->pos.y = pos->y;
rodata->pos.z = pos->z;
}
break;
case MODELNODETYPE_POSITIONHELD:
{
struct modelrodata_positionheld *rodata = &node->rodata->positionheld;
rodata->pos.x = pos->x;
rodata->pos.y = pos->y;
rodata->pos.z = pos->z;
}
break;
}
}
void modelGetRootPosition(struct model *model, struct coord *pos)
{
modelNodeGetPosition(model, model->definition->rootnode, pos);
}
void modelSetRootPosition(struct model *model, struct coord *pos)
{
modelNodeSetPosition(model, model->definition->rootnode, pos);
}
void modelNodeGetModelRelativePosition(struct model *model, struct modelnode *node, struct coord *pos)
{
pos->x = 0;
pos->y = 0;
pos->z = 0;
while (node) {
struct coord nodepos;
u32 type = node->type & 0xff;
if (type == MODELNODETYPE_CHRINFO
|| type == MODELNODETYPE_POSITION
|| type == MODELNODETYPE_POSITIONHELD) {
modelNodeGetPosition(model, node, &nodepos);
pos->x += nodepos.x;
pos->y += nodepos.y;
pos->z += nodepos.z;
}
node = node->parent;
}
}
f32 modelGetChrRotY(struct model *model)
{
if ((model->definition->rootnode->type & 0xff) == MODELNODETYPE_CHRINFO) {
union modelrwdata *rwdata = modelGetNodeRwData(model, model->definition->rootnode);
return rwdata->chrinfo.yrot;
}
return 0;
}
void modelSetChrRotY(struct model *model, f32 angle)
{
if ((model->definition->rootnode->type & 0xff) == MODELNODETYPE_CHRINFO) {
struct modelrwdata_chrinfo *rwdata = modelGetNodeRwData(model, model->definition->rootnode);
f32 diff = angle - rwdata->yrot;
if (diff < 0) {
diff += M_BADTAU;
}
rwdata->unk30 += diff;
if (rwdata->unk30 >= M_BADTAU) {
rwdata->unk30 -= M_BADTAU;
}
rwdata->unk20 += diff;
if (rwdata->unk20 >= M_BADTAU) {
rwdata->unk20 -= M_BADTAU;
}
rwdata->yrot = angle;
}
}
void modelSetScale(struct model *model, f32 scale)
{
model->scale = scale;
}
void modelSetAnimScale(struct model *model, f32 scale)
{
if (model->anim) {
model->anim->animscale = scale;
}
}
f32 modelGetEffectiveScale(struct model *model)
{
return model->definition->scale * model->scale;
}
void modelTweenPos(struct coord *curpos, struct coord *goalpos, f32 frac)
{
curpos->x += (goalpos->x - curpos->x) * frac;
curpos->y += (goalpos->y - curpos->y) * frac;
curpos->z += (goalpos->z - curpos->z) * frac;
}
f32 modelTweenRotAxis(f32 curangle, f32 goalangle, f32 mult)
{
f32 diff = goalangle - curangle;
if (goalangle < curangle) {
diff += M_BADTAU;
}
if (diff < M_PI) {
curangle += diff * mult;
if (curangle >= M_BADTAU) {
curangle -= M_BADTAU;
}
} else {
curangle -= (M_BADTAU - diff) * mult;
if (curangle < 0) {
curangle += M_BADTAU;
}
}
return curangle;
}
void modelTweenRot(struct coord *currot, struct coord *goalrot, f32 mult)
{
currot->x = modelTweenRotAxis(currot->x, goalrot->x, mult);
currot->y = modelTweenRotAxis(currot->y, goalrot->y, mult);
currot->z = modelTweenRotAxis(currot->z, goalrot->z, mult);
}
void modelUpdateChrInfo(struct model *model, struct modelnode *node)
{
union modelrwdata *rwdata;
struct anim *anim = model->anim;
struct coord sp34;
struct coord sp28;
f32 frac;
if (!anim) {
return;
}
rwdata = modelGetNodeRwData(model, node);
if (rwdata->chrinfo.unk00) {
return;
}
sp34.x = rwdata->chrinfo.unk34.x;
sp34.y = rwdata->chrinfo.unk34.y;
sp34.z = rwdata->chrinfo.unk34.z;
rwdata->chrinfo.yrot = rwdata->chrinfo.unk30;
if (g_Vars.in_cutscene && anim->speed > 0.0f) {
#if VERSION >= VERSION_PAL_BETA
frac = floorf(anim->frac / anim->speed + 0.01f) * anim->speed;
#else
frac = floorf(anim->frac / anim->speed) * anim->speed;
#endif
} else {
frac = anim->frac;
}
if (frac != 0.0f && rwdata->chrinfo.unk01) {
modelTweenPos(&sp34, &rwdata->chrinfo.unk24, frac);
rwdata->chrinfo.yrot = modelTweenRotAxis(rwdata->chrinfo.unk30, rwdata->chrinfo.unk20, frac);
}
if (anim->animnum2 || anim->fracmerge) {
if (rwdata->chrinfo.unk02) {
f32 y = rwdata->chrinfo.unk4c.y;
if (anim->frac2 != 0.0f) {
y += (rwdata->chrinfo.unk40.y - y) * anim->frac2;
}
sp34.y += (y - sp34.y) * anim->fracmerge;
}
}
if (anim->unk70 == NULL) {
rwdata->chrinfo.pos.x = sp34.x;
rwdata->chrinfo.pos.y = rwdata->chrinfo.ground + sp34.f[1];
rwdata->chrinfo.pos.z = sp34.z;
} else {
sp28.x = sp34.x;
sp28.y = sp34.y;
sp28.z = sp34.z;
if (anim->unk70(model, &rwdata->chrinfo.pos, &sp28, &rwdata->chrinfo.ground)) {
rwdata->chrinfo.pos.x = sp28.x;
rwdata->chrinfo.pos.y = rwdata->chrinfo.ground + sp28.f[1];
rwdata->chrinfo.pos.z = sp28.z;
sp34.x = sp28.x - sp34.x;
sp34.z = sp28.z - sp34.z;
rwdata->chrinfo.unk34.x += sp34.x;
rwdata->chrinfo.unk34.z += sp34.z;
if (rwdata->chrinfo.unk01) {
rwdata->chrinfo.unk24.x += sp34.x;
rwdata->chrinfo.unk24.z += sp34.z;
}
if (rwdata->chrinfo.unk02) {
rwdata->chrinfo.unk4c.x += sp34.x;
rwdata->chrinfo.unk4c.z += sp34.z;
rwdata->chrinfo.unk40.x += sp34.x;
rwdata->chrinfo.unk40.z += sp34.z;
}
}
}
}
void modelUpdateInfo(struct model *model)
{
struct modelnode *node = model->definition->rootnode;
if (node && (node->type & 0xff) == MODELNODETYPE_CHRINFO) {
modelUpdateChrInfo(model, node);
}
}
void modelUpdateChrNodeMtx(struct modelrenderdata *arg0, struct model *model, struct modelnode *node)
{
struct anim *anim = model->anim;
union modelrodata *rodata = node->rodata;
union modelrwdata *rwdata = modelGetNodeRwData(model, node);
f32 scale = model->scale;
struct coord *sp254 = &rwdata->chrinfo.pos;
f32 sp250 = rwdata->chrinfo.yrot;
Mtxf *sp24c;
u32 stack1;
Mtxf *mtx = &model->matrices[rodata->chrinfo.mtxindex];
s32 animpart = rodata->chrinfo.animpart;
struct skeleton *skel = model->definition->skel;
struct coord rot1;
struct coord translate1;
struct coord scale1;
Mtxf sp1d8;
Mtxf sp198;
Mtxf sp158;
f32 sp154;
struct coord rot2;
struct coord translate2;
struct coord scale2;
struct coord rot3;
struct coord translate3;
struct coord scale3;
f32 spfc[4];
f32 spec[4];
f32 spdc[4];
struct coord rot4;
struct coord translate4;
struct coord scale4;
Mtxf sp78;
Mtxf sp38;
if (rodata->chrinfo.mtxindex);
if (node->parent) {
sp24c = modelFindNodeMtx(model, node->parent, 0);
} else {
sp24c = arg0->unk00;
}
animGetRotTranslateScale(animpart, anim->flip, skel, anim->animnum, anim->frameslot1, &rot1, &translate1, &scale1);
if (g_Vars.in_cutscene && anim->speed > 0) {
#if VERSION >= VERSION_PAL_BETA
sp154 = floorf(anim->frac / anim->speed + 0.01f) * anim->speed;
#else
sp154 = floorf(anim->frac / anim->speed) * anim->speed;
#endif
} else {
sp154 = anim->frac;
}
if (sp154 != 0.0f) {
animGetRotTranslateScale(animpart, anim->flip, skel, anim->animnum, anim->frameslot2, &rot2, &translate2, &scale2);
modelTweenRot(&rot1, &rot2, sp154);
}
if (anim->fracmerge != 0.0f) {
animGetRotTranslateScale(animpart, anim->flip2, skel, anim->animnum2, anim->frameslot3, &rot3, &translate3, &scale3);
if (anim->frac2 != 0.0f) {
animGetRotTranslateScale(animpart, anim->flip2, skel, anim->animnum2, anim->frameslot4, &rot4, &translate4, &scale4);
modelTweenRot(&rot3, &rot4, anim->frac2);
}
if ((g_Anims[anim->animnum].flags & ANIMFLAG_ABSOLUTETRANSLATION) && (g_Anims[anim->animnum2].flags & ANIMFLAG_ABSOLUTETRANSLATION) == 0) {
mtx4LoadYRotation(rwdata->chrinfo.yrot, &sp78);
mtx4LoadRotation(&rot3, &sp38);
mtx00015be0(&sp78, &sp38);
quaternion0f097044(&sp38, spec);
} else {
quaternion0f096ca0(&rot3, spec);
}
quaternion0f096ca0(&rot1, spfc);
quaternion0f0976c0(spfc, spec);
quaternionSlerp(spfc, spec, anim->fracmerge, spdc);
quaternionToMtx(spdc, &sp1d8);
} else {
mtx4LoadRotation(&rot1, &sp1d8);
}
if (g_Anims[anim->animnum].flags & ANIMFLAG_ABSOLUTETRANSLATION) {
mtx4LoadTranslation(sp254, &sp198);
} else {
if (rwdata->chrinfo.unk18 != 0.0f) {
sp250 = modelTweenRotAxis(sp250, rwdata->chrinfo.unk1c, rwdata->chrinfo.unk18);
}
mtx4LoadYRotationWithTranslation(sp254, sp250, &sp198);
}
mtx00015be4(&sp198, &sp1d8, &sp158);
if (scale != 1.0f) {
mtx00015f4c(scale, &sp158);
}
if (sp24c) {
mtx00015be4(sp24c, &sp158, mtx);
} else {
mtx4Copy(&sp158, mtx);
}
}
void modelPositionJointUsingVecRot(struct modelrenderdata *renderdata, struct model *model, struct modelnode *node, struct coord *rot, struct coord *pos, bool allowscale, struct coord *arg6)
{
s32 nodetype = node->type;
struct modelrodata_position *rodata = &node->rodata->position;
Mtxf *rendermtx;
u32 stack;
Mtxf mtx68;
s32 mtxindex0 = rodata->mtxindex0;
s32 mtxindex1 = rodata->mtxindex1;
s32 mtxindex2 = rodata->mtxindex2;
Mtxf *matrices = model->matrices;
if (node->parent != NULL) {
rendermtx = modelFindNodeMtx(model, node->parent, 0);
} else {
rendermtx = renderdata->unk00;
}
if (rendermtx != NULL) {
Mtxf *nodemtx = &matrices[mtxindex0];
mtx4LoadRotationAndTranslation(pos, rot, &mtx68);
if (allowscale && model->scale != 1.0f) {
mtx00015f04(model->scale, &mtx68);
}
if (arg6->x != 1.0f) {
mtx00015df0(arg6->x, &mtx68);
}
if (arg6->y != 1.0f) {
mtx00015e4c(arg6->y, &mtx68);
}
if (arg6->z != 1.0f) {
mtx00015ea8(arg6->z, &mtx68);
}
mtx00015be4(rendermtx, &mtx68, nodemtx);
if (g_ModelJointPositionedFunc != NULL) {
g_ModelJointPositionedFunc(mtxindex0, nodemtx);
}
} else {
Mtxf *nodemtx = &matrices[mtxindex0];
mtx4LoadRotationAndTranslation(pos, rot, nodemtx);
if (allowscale && model->scale != 1.0f) {
mtx00015f04(model->scale, nodemtx);
}
if (arg6->x != 1.0f) {
mtx00015df0(arg6->x, nodemtx);
}
if (arg6->y != 1.0f) {
mtx00015e4c(arg6->y, nodemtx);
}
if (arg6->z != 1.0f) {
mtx00015ea8(arg6->z, nodemtx);
}
}
if (nodetype & MODELNODETYPE_0100) {
Mtxf *nodemtx = &matrices[mtxindex1];
f32 sp3c[4];
f32 sp2c[4];
quaternion0f096ca0(rot, sp3c);
quaternion0f097518(sp3c, 0.5f, sp2c);
if (rendermtx != NULL) {
quaternionToTransformMtx(pos, sp2c, &mtx68);
mtx00015be4(rendermtx, &mtx68, nodemtx);
} else {
quaternionToTransformMtx(pos, sp2c, nodemtx);
}
}
if (nodetype & MODELNODETYPE_0200) {
Mtxf *finalmtx = rendermtx ? &mtx68 : &matrices[mtxindex2];
f32 roty = rot->y;
if (roty < M_PI) {
roty *= 0.5f;
} else {
roty = M_BADTAU - (M_BADTAU - roty) * 0.5f;
}
mtx4LoadYRotation(roty, finalmtx);
if (roty >= M_PI) {
roty = M_BADTAU - roty;
}
if (roty < 0.890118f) { // 51 degrees
roty = func0f096700(roty);
} else {
roty = 1.5f;
}
mtx00015edc(roty, finalmtx);
mtx4SetTranslation(pos, finalmtx);
if (rendermtx != NULL) {
Mtxf *nodemtx = &matrices[mtxindex2];
mtx00015be4(rendermtx, finalmtx, nodemtx);
}
}
}
void modelPositionJointUsingQuatRot(struct modelrenderdata *renderdata, struct model *model, struct modelnode *node, f32 rot[4], struct coord *pos, struct coord *arg5)
{
s32 nodetype = node->type;
struct modelrodata_position *rodata = &node->rodata->position;
Mtxf *rendermtx;
u32 stack;
Mtxf mtx58;
s32 mtxindex0 = rodata->mtxindex0;
s32 mtxindex1 = rodata->mtxindex1;
s32 mtxindex2 = rodata->mtxindex2;
Mtxf *matrices = model->matrices;
if (node->parent != NULL) {
rendermtx = modelFindNodeMtx(model, node->parent, 0);
} else {
rendermtx = renderdata->unk00;
}
if (rendermtx != NULL) {
Mtxf *nodemtx = &matrices[mtxindex0];
quaternionToTransformMtx(pos, rot, &mtx58);
if (arg5->x != 1.0f) {
mtx00015df0(arg5->x, &mtx58);
}
if (arg5->y != 1.0f) {
mtx00015e4c(arg5->y, &mtx58);
}
if (arg5->z != 1.0f) {
mtx00015ea8(arg5->z, &mtx58);
}
mtx00015be4(rendermtx, &mtx58, nodemtx);
if (g_ModelJointPositionedFunc != NULL) {
g_ModelJointPositionedFunc(mtxindex0, nodemtx);
}
} else {
Mtxf *nodemtx = &matrices[mtxindex0];
quaternionToTransformMtx(pos, rot, nodemtx);
if (arg5->x != 1.0f) {
mtx00015df0(arg5->x, nodemtx);
}
if (arg5->y != 1.0f) {
mtx00015e4c(arg5->y, nodemtx);
}
if (arg5->z != 1.0f) {
mtx00015ea8(arg5->z, nodemtx);
}
}
if (nodetype & MODELNODETYPE_0100) {
Mtxf *nodemtx = &matrices[mtxindex1];
f32 sp2c[4];
quaternion0f097518(rot, 0.5f, sp2c);
if (rendermtx != NULL) {
quaternionToTransformMtx(pos, sp2c, &mtx58);
mtx00015be4(rendermtx, &mtx58, nodemtx);
} else {
quaternionToTransformMtx(pos, sp2c, nodemtx);
}
}
if (nodetype & MODELNODETYPE_0200) {
Mtxf *finalmtx = rendermtx ? &mtx58 : &matrices[mtxindex2];
f32 roty = 2.0f * acosf(rot[0]);
if (roty < M_PI) {
roty *= 0.5f;
} else {
roty = M_BADTAU - (M_BADTAU - roty) * 0.5f;
}
mtx4LoadYRotation(roty, finalmtx);
if (roty >= M_PI) {
roty = M_BADTAU - roty;
}
if (roty < 0.890118f) { // 51 degrees
roty = func0f096700(roty);
} else {
roty = 1.5f;
}
mtx00015edc(roty, finalmtx);
mtx4SetTranslation(pos, finalmtx);
if (rendermtx != NULL) {
Mtxf *nodemtx = &matrices[mtxindex2];
mtx00015be4(rendermtx, finalmtx, nodemtx);
}
}
}
void modelUpdatePositionNodeMtx(struct modelrenderdata *renderdata, struct model *model, struct modelnode *node)
{
struct anim *anim;
struct modelrodata_position *rodata = &node->rodata->position;
s32 animpart;
struct skeleton *skel;
struct coord rot1;
struct coord translate1;
struct coord scale1;
bool sp128;
Mtxf spe8;
Mtxf *mtx;
f32 spe0;
struct coord rot2;
struct coord translate2;
struct coord scale2;
struct coord rot3;
struct coord translate3;
struct coord scale3;
f32 sp88[4];
f32 sp78[4];
f32 sp68[4];
struct coord rot4;
struct coord translate4;
struct coord scale4;
anim = model->anim;
if (anim != NULL) {
animpart = rodata->part;
skel = model->definition->skel;
if (anim->animnum != 0) {
sp128 = (g_Anims[anim->animnum].flags & ANIMFLAG_ABSOLUTETRANSLATION) && node == model->definition->rootnode;
animGetRotTranslateScale(animpart, anim->flip, skel, anim->animnum, anim->frameslot1, &rot1, &translate1, &scale1);
if (g_Vars.in_cutscene && anim->speed > 0.0f) {
#if VERSION >= VERSION_PAL_BETA
spe0 = floorf(anim->frac / anim->speed + 0.0099999997764826f) * anim->speed;
#else
spe0 = floorf(anim->frac / anim->speed) * anim->speed;
#endif
} else {
spe0 = anim->frac;
}
if (spe0 != 0.0f) {
animGetRotTranslateScale(animpart, anim->flip, skel, anim->animnum, anim->frameslot2, &rot2, &translate2, &scale2);
modelTweenRot(&rot1, &rot2, spe0);
#if VERSION >= VERSION_PAL_BETA
if (sp128 || var8005efd8_2)
#else
if (sp128)
#endif
{
modelTweenPos(&translate1, &translate2, spe0);
}
}
} else {
translate1.f[0] = translate1.f[1] = translate1.f[2] = 0.0f;
rot1.f[0] = rot1.f[1] = rot1.f[2] = 0.0f;
scale1.f[0] = scale1.f[1] = scale1.f[2] = 1.0f;
sp128 = false;
}
if (anim->fracmerge != 0.0f) {
animGetRotTranslateScale(animpart, anim->flip2, skel, anim->animnum2, anim->frameslot3, &rot3, &translate3, &scale3);
if (anim->frac2 != 0.0f) {
animGetRotTranslateScale(animpart, anim->flip2, skel, anim->animnum2, anim->frameslot4, &rot4, &translate4, &scale4);
modelTweenRot(&rot3, &rot4, anim->frac2);
}
quaternion0f096ca0(&rot1, sp88);
quaternion0f096ca0(&rot3, sp78);
quaternion0f0976c0(sp88, sp78);
quaternionSlerp(sp88, sp78, anim->fracmerge, sp68);
if (translate1.f[0] != 0.0f || translate1.f[1] != 0.0f || translate1.f[2] != 0.0f) {
translate1.x *= anim->animscale;
translate1.y *= anim->animscale;
translate1.z *= anim->animscale;
if (node != model->definition->rootnode) {
translate1.x += rodata->pos.x;
translate1.y += rodata->pos.y;
translate1.z += rodata->pos.z;
}
modelPositionJointUsingQuatRot(renderdata, model, node, sp68, &translate1, &scale1);
} else if (node != model->definition->rootnode) {
modelPositionJointUsingQuatRot(renderdata, model, node, sp68, &rodata->pos, &scale1);
} else {
modelPositionJointUsingQuatRot(renderdata, model, node, sp68, &translate1, &scale1);
}
} else if (sp128) {
f32 mult = bgGetStageTranslationThing();
translate1.x *= mult;
translate1.y *= mult;
translate1.z *= mult;
modelPositionJointUsingVecRot(renderdata, model, node, &rot1, &translate1, true, &scale1);
} else if (translate1.f[0] != 0.0f || translate1.f[1] != 0.0f || translate1.f[2] != 0.0f) {
translate1.x *= anim->animscale;
translate1.y *= anim->animscale;
translate1.z *= anim->animscale;
if (node != model->definition->rootnode) {
translate1.x += rodata->pos.x;
translate1.y += rodata->pos.y;
translate1.z += rodata->pos.z;
}
modelPositionJointUsingVecRot(renderdata, model, node, &rot1, &translate1, false, &scale1);
} else if (node != model->definition->rootnode) {
modelPositionJointUsingVecRot(renderdata, model, node, &rot1, &rodata->pos, false, &scale1);
} else {
modelPositionJointUsingVecRot(renderdata, model, node, &rot1, &translate1, false, &scale1);
}
} else {
if (node->parent) {
mtx = modelFindNodeMtx(model, node->parent, 0);
} else {
mtx = renderdata->unk00;
}
if (mtx) {
mtx4LoadTranslation(&rodata->pos, &spe8);
mtx00015be4(mtx, &spe8, &model->matrices[rodata->mtxindex0]);
} else {
mtx4LoadTranslation(&rodata->pos, &model->matrices[rodata->mtxindex0]);
}
}
}
void modelUpdatePositionHeldNodeMtx(struct modelrenderdata *arg0, struct model *model, struct modelnode *node)
{
union modelrodata *rodata = node->rodata;
Mtxf *sp68;
Mtxf sp28;
s32 mtxindex = rodata->positionheld.mtxindex;
Mtxf *matrices = model->matrices;
if (node->parent) {
sp68 = modelFindNodeMtx(model, node->parent, 0);
} else {
sp68 = arg0->unk00;
}
if (sp68) {
mtx4LoadTranslation(&rodata->positionheld.pos, &sp28);
mtx00015be4(sp68, &sp28, &matrices[mtxindex]);
} else {
mtx4LoadTranslation(&rodata->positionheld.pos, &matrices[mtxindex]);
}
}
/**
* For a distance node, set its target to visible based on distance.
*/
void modelUpdateDistanceRelations(struct model *model, struct modelnode *node)
{
union modelrodata *rodata = node->rodata;
union modelrwdata *rwdata = modelGetNodeRwData(model, node);
Mtxf *mtx = modelFindNodeMtx(model, node, 0);
f32 distance;
if (g_ModelDistanceDisabled || !mtx) {
distance = 0;
} else {
distance = -mtx->m[3][2] * camGetLodScaleZ();
if (g_ModelDistanceScale != 1) {
distance *= g_ModelDistanceScale;
}
}
if (distance > rodata->distance.near * model->scale || rodata->distance.near == 0) {
if (distance <= rodata->distance.far * model->scale) {
rwdata->distance.visible = true;
node->child = rodata->distance.target;
return;
}
}
rwdata->distance.visible = false;
node->child = NULL;
}
void modelApplyDistanceRelations(struct model *model, struct modelnode *node)
{
struct modelrodata_distance *rodata = &node->rodata->distance;
struct modelrwdata_distance *rwdata = modelGetNodeRwData(model, node);
if (rwdata->visible) {
node->child = rodata->target;
} else {
node->child = NULL;
}
}
void modelApplyToggleRelations(struct model *model, struct modelnode *node)
{
struct modelrodata_toggle *rodata = &node->rodata->toggle;
struct modelrwdata_toggle *rwdata = modelGetNodeRwData(model, node);
if (rwdata->visible) {
node->child = rodata->target;
} else {
node->child = NULL;
}
}
/**
* Attach a head model to its placeholder on the body model.
*
* The given modelnode is assumed to be of type MODELNODETYPE_HEADSPOT.
*/
void modelApplyHeadRelations(struct model *model, struct modelnode *bodynode)
{
struct modelrwdata_headspot *rwdata = modelGetNodeRwData(model, bodynode);
if (rwdata->headmodeldef) {
struct modelnode *headnode = rwdata->headmodeldef->rootnode;
bodynode->child = headnode;
while (headnode) {
headnode->parent = bodynode;
headnode = headnode->next;
}
}
}
void modelApplyReorderRelationsByArg(struct modelnode *basenode, bool reverse)
{
union modelrodata *rodata = basenode->rodata;
struct modelnode *node1;
struct modelnode *node2;
struct modelnode *loopnode;
if (reverse) {
node1 = rodata->reorder.unk18;
node2 = rodata->reorder.unk1c;
} else {
node1 = rodata->reorder.unk1c;
node2 = rodata->reorder.unk18;
}
if (node1) {
// I think what's happening here is there's two groups of siblings,
// where node1 and node2 are the head nodes. Either group can be first,
// and this is ensuring the node1 group is first.
// Note that node2 might be NULL.
basenode->child = node1;
node1->prev = NULL;
// Skip through node1's siblings until node2 is found or the end is
// reached
loopnode = node1;
while (loopnode->next && loopnode->next != node2) {
loopnode = loopnode->next;
}
loopnode->next = node2;
if (node2) {
// Append node2 and its siblings to node1's siblings
node2->prev = loopnode;
loopnode = node2;
while (loopnode->next && loopnode->next != node1) {
loopnode = loopnode->next;
}
loopnode->next = NULL;
}
} else {
basenode->child = node2;
if (node2) {
node2->prev = NULL;
}
}
}
void modelApplyReorderRelations(struct model *model, struct modelnode *node)
{
union modelrwdata *rwdata = modelGetNodeRwData(model, node);
modelApplyReorderRelationsByArg(node, rwdata->reorder.reverse);
}
void modelUpdateReorderRelations(struct model *model, struct modelnode *node)
{
union modelrodata *rodata = node->rodata;
union modelrwdata *rwdata = modelGetNodeRwData(model, node);
Mtxf *mtx = modelFindNodeMtx(model, node, 0);
struct coord sp38;
struct coord sp2c;
f32 tmp;
if (rodata->reorder.side == 0) {
sp38.x = rodata->reorder.unk0c[0];
sp38.y = rodata->reorder.unk0c[1];
sp38.z = rodata->reorder.unk0c[2];
mtx4RotateVecInPlace(mtx, &sp38);
} else if (rodata->reorder.side == 2) {
sp38.x = mtx->m[1][0] * rodata->reorder.unk0c[1];
sp38.y = mtx->m[1][1] * rodata->reorder.unk0c[1];
sp38.z = mtx->m[1][2] * rodata->reorder.unk0c[1];
} else if (rodata->reorder.side == 3) {
sp38.x = mtx->m[2][0] * rodata->reorder.unk0c[2];
sp38.y = mtx->m[2][1] * rodata->reorder.unk0c[2];
sp38.z = mtx->m[2][2] * rodata->reorder.unk0c[2];
} else if (rodata->reorder.side == 1) {
sp38.x = mtx->m[0][0] * rodata->reorder.unk0c[0];
sp38.y = mtx->m[0][1] * rodata->reorder.unk0c[0];
sp38.z = mtx->m[0][2] * rodata->reorder.unk0c[0];
}
sp2c.x = rodata->reorder.unk00;
sp2c.y = rodata->reorder.unk04;
sp2c.z = rodata->reorder.unk08;
mtx4TransformVecInPlace(mtx, &sp2c);
tmp = sp38.f[0] * sp2c.f[0] + sp38.f[1] * sp2c.f[1] + sp38.f[2] * sp2c.f[2];
if (tmp < 0) {
rwdata->reorder.reverse = true;
} else {
rwdata->reorder.reverse = false;
}
modelApplyReorderRelations(model, node);
}
/**
* Update a model definition's node relations based on the rwdata from the given
* model instance, recalculating the rwdata where possible.
*
* However, several node types are not descended into, and toggle nodes are not
* applied to the model definition.
*/
void modelUpdateRelationsQuick(struct model *model, struct modelnode *parent)
{
struct modelnode *node = parent->child;
if (parent);
while (node) {
s32 type = node->type & 0xff;
bool dochildren = true;
switch (type) {
case MODELNODETYPE_CHRINFO:
case MODELNODETYPE_POSITION:
case MODELNODETYPE_0B:
case MODELNODETYPE_CHRGUNFIRE:
case MODELNODETYPE_0D:
case MODELNODETYPE_0E:
case MODELNODETYPE_0F:
case MODELNODETYPE_POSITIONHELD:
dochildren = false;
break;
case MODELNODETYPE_DISTANCE:
modelUpdateDistanceRelations(model, node);
break;
case MODELNODETYPE_REORDER:
modelUpdateReorderRelations(model, node);
break;
case MODELNODETYPE_HEADSPOT:
modelApplyHeadRelations(model, node);
break;
case MODELNODETYPE_DL:
break;
}
if (dochildren && node->child) {
node = node->child;
} else {
while (node) {
if (node == parent->parent) {
node = NULL;
break;
}
if (node->next) {
node = node->next;
break;
}
node = node->parent;
}
}
}
}
/**
* Update a model definition's node relations based on the rwdata from the given
* model instance, recalculating the rwdata where possible.
*/
void modelUpdateRelations(struct model *model)
{
struct modelnode *node = model->definition->rootnode;
while (node) {
u32 type = node->type & 0xff;
switch (type) {
case MODELNODETYPE_DISTANCE:
modelUpdateDistanceRelations(model, node);
break;
case MODELNODETYPE_REORDER:
modelUpdateReorderRelations(model, node);
break;
case MODELNODETYPE_TOGGLE:
modelApplyToggleRelations(model, node);
break;
case MODELNODETYPE_HEADSPOT:
modelApplyHeadRelations(model, node);
break;
case MODELNODETYPE_CHRINFO:
case MODELNODETYPE_DL:
default:
break;
}
if (node->child) {
node = node->child;
} else {
while (node) {
if (node->next) {
node = node->next;
break;
}
node = node->parent;
}
}
}
}
void modelUpdateMatrices(struct modelrenderdata *arg0, struct model *model)
{
struct modelnode *node = model->definition->rootnode;
while (node) {
u32 type = node->type & 0xff;
switch (type) {
case MODELNODETYPE_CHRINFO:
modelUpdateChrNodeMtx(arg0, model, node);
break;
case MODELNODETYPE_POSITION:
modelUpdatePositionNodeMtx(arg0, model, node);
break;
case MODELNODETYPE_POSITIONHELD:
modelUpdatePositionHeldNodeMtx(arg0, model, node);
break;
case MODELNODETYPE_DISTANCE:
modelUpdateDistanceRelations(model, node);
break;
case MODELNODETYPE_REORDER:
modelUpdateReorderRelations(model, node);
break;
case MODELNODETYPE_TOGGLE:
modelApplyToggleRelations(model, node);
break;
case MODELNODETYPE_HEADSPOT:
modelApplyHeadRelations(model, node);
break;
case MODELNODETYPE_DL:
default:
break;
}
if (node->child) {
node = node->child;
} else {
while (node) {
if (node->next) {
node = node->next;
break;
}
node = node->parent;
}
}
}
}
void modelSetMatrices(struct modelrenderdata *renderdata, struct model *model)
{
model->matrices = renderdata->unk10;
renderdata->unk10 += model->definition->nummatrices;
#if VERSION >= VERSION_PAL_BETA
if (var8005efb0_2 || !modelasm00018680(renderdata, model)) {
modelUpdateMatrices(renderdata, model);
}
#else
if (!modelasm00018680(renderdata, model)) {
modelUpdateMatrices(renderdata, model);
}
#endif
}
void modelSetMatricesWithAnim(struct modelrenderdata *renderdata, struct model *model)
{
struct anim *anim = model->anim;
f32 speed;
f32 frac;
f32 frac2;
if (anim && anim->animnum) {
if (PLAYERCOUNT() >= 2) {
frac = anim->frac;
frac2 = anim->frac2;
speed = anim->speed;
if (speed < 0) {
speed = -speed;
}
if (speed > 0.5f) {
anim->frac = 0;
anim->frac2 = 0;
}
}
animLoadHeader(anim->animnum);
anim->frameslot1 = animLoadFrame(anim->animnum, anim->framea);
if (anim->frac != 0) {
anim->frameslot2 = animLoadFrame(anim->animnum, anim->frameb);
}
if (anim->animnum2) {
animLoadHeader(anim->animnum2);
anim->frameslot3 = animLoadFrame(anim->animnum2, anim->frame2a);
if (anim->frac2 != 0) {
anim->frameslot4 = animLoadFrame(anim->animnum2, anim->frame2b);
}
}
animForgetFrameBirths();
}
modelSetMatrices(renderdata, model);
if (PLAYERCOUNT() >= 2 && anim && anim->animnum) {
anim->frac = frac;
anim->frac2 = frac2;
}
}
s16 modelGetAnimNum(struct model *model)
{
if (model->anim) {
return model->anim->animnum;
}
return 0;
}
bool modelIsFlipped(struct model *model)
{
if (model->anim) {
return model->anim->flip;
}
return false;
}
f32 modelGetCurAnimFrame(struct model *model)
{
if (model->anim) {
return model->anim->frame;
}
return 0;
}
f32 modelGetAnimEndFrame(struct model *model)
{
struct anim *anim = model->anim;
if (anim) {
if (anim->endframe >= 0) {
return anim->endframe;
}
if (anim->animnum) {
return animGetNumFrames(anim->animnum) - 1;
} else {
return 0;
}
}
return 0;
}
s32 modelGetNumAnimFrames(struct model *model)
{
if (model->anim) {
return animGetNumFrames(modelGetAnimNum(model));
}
return 0;
}
f32 modelGetAnimSpeed(struct model *model)
{
if (model->anim) {
return model->anim->speed;
}
return 1;
}
f32 modelGetAbsAnimSpeed(struct model *model)
{
f32 speed;
if (model->anim) {
speed = model->anim->speed;
if (speed < 0) {
speed = -speed;
}
return speed;
}
return 1;
}
f32 modelGetEffectiveAnimSpeed(struct model *model)
{
if (model->anim) {
return modelGetAnimSpeed(model) * model->anim->playspeed;
}
return 1;
}
/**
* Constrain the given frame number to the bounds of the animation, unless the
* animation is looping in which case wrap it to the other side.
*/
s32 modelConstrainOrWrapAnimFrame(s32 frame, s16 animnum, f32 endframe)
{
if (frame < 0) {
if (var8005efbc || (g_Anims[animnum].flags & ANIMFLAG_LOOP)) {
frame = animGetNumFrames(animnum) - (-frame % animGetNumFrames(animnum));
} else {
frame = 0;
}
} else if (endframe >= 0 && frame > (s32)endframe) {
frame = ceil(endframe);
} else if (frame >= animGetNumFrames(animnum)) {
if (var8005efbc || (g_Anims[animnum].flags & ANIMFLAG_LOOP)) {
frame = frame % animGetNumFrames(animnum);
} else {
frame = animGetNumFrames(animnum) - 1;
}
}
return frame;
}
void modelCopyAnimForMerge(struct model *model, f32 merge)
{
struct anim *anim = model->anim;
struct modelnode *node;
u32 nodetype;
if (anim) {
if (merge > 0 && anim->animnum) {
if (anim->animnum2 && anim->fracmerge == 1) {
return;
}
node = model->definition->rootnode;
nodetype = node->type & 0xff;
anim->frame2 = anim->frame;
anim->frac2 = anim->frac;
anim->animnum2 = anim->animnum;
anim->flip2 = anim->flip;
anim->frame2a = anim->framea;
anim->frame2b = anim->frameb;
anim->speed2 = anim->speed;
anim->newspeed2 = anim->newspeed;
anim->oldspeed2 = anim->oldspeed;
anim->timespeed2 = anim->timespeed;
anim->elapsespeed2 = anim->elapsespeed;
anim->endframe2 = anim->endframe;
if (nodetype == MODELNODETYPE_CHRINFO) {
struct modelrwdata_chrinfo *rwdata = modelGetNodeRwData(model, node);
rwdata->unk02 = 1;
rwdata->unk4c.x = rwdata->unk34.x;
rwdata->unk4c.y = rwdata->unk34.y;
rwdata->unk4c.z = rwdata->unk34.z;
rwdata->unk40.x = rwdata->unk24.x;
rwdata->unk40.y = rwdata->unk24.y;
rwdata->unk40.z = rwdata->unk24.z;
}
} else {
anim->animnum2 = 0;
}
}
}
void modelSetAnimation2(struct model *model, s16 animnum, s32 flip, f32 fstartframe, f32 speed, f32 merge)
{
struct anim *anim = model->anim;
if (anim) {
s32 isfirstanim = !anim->animnum;
s32 type;
if (anim->animnum2) {
anim->timemerge = merge;
anim->elapsemerge = 0;
anim->fracmerge = 1;
} else {
anim->timemerge = 0;
anim->fracmerge = 0;
}
anim->animnum = animnum;
anim->flip = flip;
anim->endframe = -1;
anim->speed = speed;
anim->timespeed = 0;
modelSetAnimFrame(model, fstartframe);
anim->looping = false;
type = model->definition->rootnode->type & 0xff;
if (type == MODELNODETYPE_CHRINFO) {
u32 stack;
struct modelrodata_chrinfo *rodata = &model->definition->rootnode->rodata->chrinfo;
struct modelrwdata_chrinfo *rwdata = (struct modelrwdata_chrinfo *) modelGetNodeRwData(model, model->definition->rootnode);
s32 animpart = rodata->animpart;
struct skeleton *skel = model->definition->skel;
f32 scale;
f32 sp98;
f32 sp94;
struct coord translate = {0, 0, 0};
f32 sp84;
u8 frameslot;
struct coord rot1;
struct coord scale1;
f32 sp64;
struct coord sp58;
struct coord sp4c;
f32 angle;
f32 y;
f32 x;
f32 z;
if (g_Anims[anim->animnum].flags & ANIMFLAG_ABSOLUTETRANSLATION) {
sp64 = bgGetStageTranslationThing();
animLoadHeader(anim->animnum);
frameslot = animLoadFrame(anim->animnum, anim->framea);
animForgetFrameBirths();
animGetRotTranslateScale(animpart, anim->flip, skel, anim->animnum, frameslot, &rot1, &translate, &scale1);
rwdata->unk34.x = translate.x * sp64;
rwdata->unk34.y = translate.y * sp64;
rwdata->unk34.z = translate.z * sp64;
rwdata->unk30 = rwdata->yrot;
if (anim->frac == 0) {
rwdata->unk01 = 0;
} else {
animLoadHeader(anim->animnum);
frameslot = animLoadFrame(anim->animnum, anim->frameb);
animForgetFrameBirths();
animGetRotTranslateScale(animpart, anim->flip, skel, anim->animnum, frameslot, &rot1, &translate, &scale1);
rwdata->unk24.x = translate.x * sp64;
rwdata->unk24.y = translate.y * sp64;
rwdata->unk24.z = translate.z * sp64;
rwdata->unk20 = rwdata->yrot;
rwdata->unk01 = 1;
}
} else {
sp84 = animGetTranslateAngle(animpart, anim->flip, skel, anim->animnum, anim->frameb, &translate, anim->average);
scale = model->scale * anim->animscale;
if (scale != 1) {
translate.x *= scale;
translate.y *= scale;
translate.z *= scale;
}
if (anim->average) {
translate.y = rwdata->pos.y - rwdata->ground;
}
sp98 = cosf(rwdata->yrot);
sp94 = sinf(rwdata->yrot);
if (anim->frac == 0) {
rwdata->unk34.x = rwdata->pos.f[0];
rwdata->unk34.y = rwdata->pos.f[1] - rwdata->ground;
rwdata->unk34.z = rwdata->pos.f[2];
rwdata->unk30 = rwdata->yrot;
sp58.x = rwdata->unk34.f[0] + translate.f[0] * sp98 + translate.f[2] * sp94;
sp58.y = translate.f[1];
sp58.z = rwdata->unk34.f[2] - translate.f[0] * sp94 + translate.f[2] * sp98;
rwdata->unk24.x = sp58.f[0];
rwdata->unk24.y = sp58.f[1];
rwdata->unk24.z = sp58.f[2];
if (rwdata->unk18 == 0) {
rwdata->unk20 = rwdata->unk30 + sp84;
if (rwdata->unk20 >= M_BADTAU) {
rwdata->unk20 -= M_BADTAU;
}
}
rwdata->unk01 = 1;
} else {
x = translate.f[0] * sp98 + translate.f[2] * sp94;
y = translate.f[1];
z = -translate.f[0] * sp94 + translate.f[2] * sp98;
sp4c.f[0] = rwdata->pos.f[0] + x * (1 - anim->frac);
sp4c.f[1] = y;
sp4c.f[2] = rwdata->pos.f[2] + z * (1 - anim->frac);
rwdata->unk24.f[0] = sp4c.f[0];
rwdata->unk24.f[1] = sp4c.f[1];
rwdata->unk24.f[2] = sp4c.f[2];
rwdata->unk34.f[0] = rwdata->unk24.f[0] - x;
rwdata->unk34.f[1] = (rwdata->pos.f[1] - rwdata->ground) - (y - (rwdata->pos.f[1] - rwdata->ground)) * anim->frac / (1 - anim->frac);
rwdata->unk34.f[2] = rwdata->unk24.f[2] - z;
angle = rwdata->yrot - sp84;
if (angle < 0) {
angle += M_BADTAU;
}
rwdata->unk30 = modelTweenRotAxis(rwdata->yrot, angle, anim->frac);
if (rwdata->unk18 == 0) {
rwdata->unk20 = rwdata->unk30 + sp84;
if (rwdata->unk20 >= M_BADTAU) {
rwdata->unk20 -= M_BADTAU;
}
}
rwdata->unk01 = 1;
}
if (isfirstanim) {
rwdata->unk34.f[1] = rwdata->unk24.f[1];
}
}
}
}
}
bool modelIsAnimMerging(struct model *model)
{
if (model && model->anim && model->anim->animnum2
&& model->anim->fracmerge != 0 && model->anim->fracmerge != 1) {
return true;
}
return false;
}
void modelSetAnimationWithMerge(struct model *model, s16 animnum, u32 flip, f32 startframe, f32 speed, f32 timemerge, bool domerge)
{
if (model) {
if (model->anim && model->anim->animnum
&& (g_Anims[model->anim->animnum].flags & ANIMFLAG_ABSOLUTETRANSLATION)
&& (g_Anims[animnum].flags & ANIMFLAG_ABSOLUTETRANSLATION) == 0) {
timemerge = 0;
}
if (domerge) {
modelCopyAnimForMerge(model, timemerge);
}
modelSetAnimation2(model, animnum, flip, startframe, speed, timemerge);
}
}
void modelSetAnimation(struct model *model, s16 animnum, s32 flip, f32 startframe, f32 speed, f32 merge)
{
if (model) {
if (model->anim && model->anim->animnum
&& (g_Anims[model->anim->animnum].flags & ANIMFLAG_ABSOLUTETRANSLATION)
&& (g_Anims[animnum].flags & ANIMFLAG_ABSOLUTETRANSLATION) == 0) {
merge = 0;
}
modelCopyAnimForMerge(model, merge);
modelSetAnimation2(model, animnum, flip, startframe, speed, merge);
}
}
void modelCopyAnimData(struct model *src, struct model *dst)
{
if (src->anim && dst->anim) {
*dst->anim = *src->anim;
}
}
void modelSetAnimLooping(struct model *model, f32 loopframe, f32 loopmerge)
{
if (model->anim) {
model->anim->looping = true;
model->anim->loopframe = loopframe;
model->anim->loopmerge = loopmerge;
}
}
void modelSetAnimEndFrame(struct model *model, f32 endframe)
{
struct anim *anim = model->anim;
if (anim) {
if (anim->animnum && endframe < animGetNumFrames(anim->animnum) - 1) {
anim->endframe = endframe;
} else {
anim->endframe = -1;
}
}
}
void modelSetAnimFlipFunction(struct model *model, void *callback)
{
if (model->anim) {
model->anim->flipfunc = callback;
}
}
#if VERSION < VERSION_NTSC_1_0
void modelSetAnimUnk6c(struct model *model, s32 value)
{
if (model->anim) {
model->anim->unk6c = value;
}
}
#endif
void modelSetAnimSpeed(struct model *model, f32 speed, f32 startframe)
{
struct anim *anim = model->anim;
if (anim) {
if (startframe > 0) {
anim->timespeed = startframe;
anim->newspeed = speed;
anim->elapsespeed = 0;
anim->oldspeed = anim->speed;
} else {
anim->speed = speed;
anim->timespeed = 0;
}
}
}
void modelSetAnimSpeedAuto(struct model *model, f32 arg1, f32 startframe)
{
struct anim *anim = model->anim;
f32 tmp;
f32 speed;
if (anim) {
if (anim->frame <= arg1) {
tmp = arg1 - anim->frame;
} else {
tmp = animGetNumFrames(anim->animnum) - anim->frame + arg1;
}
speed = anim->speed + (tmp + tmp) / startframe;
modelSetAnimSpeed(model, speed, startframe);
}
}
void modelSetAnimPlaySpeed(struct model *model, f32 speed, f32 startframe)
{
struct anim *anim = model->anim;
if (anim) {
if (startframe > 0) {
anim->timeplay = startframe;
anim->newplay = speed;
anim->elapseplay = 0;
anim->oldplay = anim->playspeed;
} else {
anim->playspeed = speed;
anim->timeplay = 0;
}
}
}
void modelSetAnim70(struct model *model, void *callback)
{
if (model->anim) {
model->anim->unk70 = callback;
}
}
void modelSetAnimFrame(struct model *model, f32 frame)
{
s32 framea;
s32 frameb;
bool forwards;
struct anim *anim = model->anim;
if (anim) {
framea = floor(frame);
forwards = anim->speed >= 0;
frameb = (forwards ? framea + 1 : framea - 1);
anim->framea = modelConstrainOrWrapAnimFrame(framea, anim->animnum, anim->endframe);
anim->frameb = modelConstrainOrWrapAnimFrame(frameb, anim->animnum, anim->endframe);
if (anim->framea == anim->frameb) {
anim->frac = 0;
anim->frame = anim->framea;
} else if (forwards) {
anim->frac = frame - framea;
anim->frame = anim->framea + anim->frac;
} else {
anim->frac = 1 - (frame - frameb);
anim->frame = anim->frameb + (1 - anim->frac);
}
}
}
void modelSetAnimFrame2(struct model *model, f32 frame1, f32 frame2)
{
struct anim *anim = model->anim;
if (anim) {
modelSetAnimFrame(model, frame1);
if (anim->animnum2) {
s32 framea = floor(frame2);
s32 frameb;
bool forwards = anim->speed2 >= 0;
frameb = (forwards ? framea + 1 : framea - 1);
anim->frame2a = modelConstrainOrWrapAnimFrame(framea, anim->animnum2, anim->endframe2);
anim->frame2b = modelConstrainOrWrapAnimFrame(frameb, anim->animnum2, anim->endframe2);
if (anim->frame2a == anim->frame2b) {
anim->frac2 = 0;
anim->frame2 = anim->frame2a;
} else if (forwards) {
anim->frac2 = frame2 - framea;
anim->frame2 = anim->frame2a + anim->frac2;
} else {
anim->frac2 = 1 - (frame2 - frameb);
anim->frame2 = anim->frame2b + (1 - anim->frac2);
}
}
}
}
bool g_ModelAnimMergingEnabled = true;
void modelSetAnimMergingEnabled(bool value)
{
g_ModelAnimMergingEnabled = value;
}
bool modelIsAnimMergingEnabled(void)
{
return g_ModelAnimMergingEnabled;
}
void modelSetAnimFrame2WithChrStuff(struct model *model, f32 curframe, f32 endframe, f32 curframe2, f32 endframe2)
{
struct anim *anim = model->anim;
if (anim != NULL) {
struct modelnode *rootnode = model->definition->rootnode;
u16 nodetype = rootnode->type;
if ((nodetype & 0xff) == MODELNODETYPE_CHRINFO) {
struct modelrodata_chrinfo *rodata = &rootnode->rodata->chrinfo;
struct modelrwdata_chrinfo *rwdata = modelGetNodeRwData(model, rootnode);
if (rwdata->unk00 == 0) {
s32 animpart = rodata->animpart;
struct skeleton *skel = model->definition->skel;
f32 scale = model->scale * anim->animscale;
f32 sine;
f32 cosine;
struct coord translate = {0, 0, 0};
u8 frameslot;
f32 f20;
s32 floorcur;
s32 floorend;
struct coord spe0;
f32 f30;
struct coord spd0;
f32 spcc;
s32 spc8;
bool forwards;
f32 absspeed;
f32 absspeed2;
f32 f22;
s32 s0frame;
struct coord rot1;
struct coord scale1;
struct coord sp90;
u32 stack;
spe0.x = rwdata->unk34.x;
spe0.y = rwdata->unk34.y;
spe0.z = rwdata->unk34.z;
f30 = rwdata->unk30;
spd0.x = rwdata->unk24.x;
spd0.y = rwdata->unk24.y;
spd0.z = rwdata->unk24.z;
spcc = rwdata->unk20;
spc8 = rwdata->unk01;
absspeed = anim->speed;
if (absspeed < 0.0f) {
absspeed = -absspeed;
}
absspeed2 = anim->speed2;
if (absspeed2 < 0.0f) {
absspeed2 = -absspeed2;
}
forwards = false;
if (curframe <= endframe) {
forwards = true;
}
if (forwards) {
floorcur = floor(curframe) + 1;
floorend = floor(endframe);
} else {
floorcur = ceil(curframe) - 1;
floorend = ceil(endframe);
}
if (g_Anims[anim->animnum].flags & ANIMFLAG_ABSOLUTETRANSLATION) {
f20 = bgGetStageTranslationThing();
if (floorend != anim->framea) {
s0frame = modelConstrainOrWrapAnimFrame(floorend, anim->animnum, anim->endframe);
anim->framea = s0frame;
if (spc8 && floorend == anim->frameb) {
spe0.x = spd0.x;
spe0.y = spd0.y;
spe0.z = spd0.z;
} else {
animLoadHeader(anim->animnum);
frameslot = animLoadFrame(anim->animnum, s0frame);
animForgetFrameBirths();
animGetRotTranslateScale(animpart, anim->flip, skel, anim->animnum, frameslot, &rot1, &translate, &scale1);
spe0.x = translate.x * f20;
spe0.y = translate.y * f20;
spe0.z = translate.z * f20;
}
floorcur = floorend;
if (forwards) {
floorcur++;
} else {
floorcur--;
}
s0frame = modelConstrainOrWrapAnimFrame(floorcur, anim->animnum, anim->endframe);
anim->frameb = s0frame;
animLoadHeader(anim->animnum);
frameslot = animLoadFrame(anim->animnum, s0frame);
animForgetFrameBirths();
animGetRotTranslateScale(animpart, anim->flip, skel, anim->animnum, frameslot, &rot1, &translate, &scale1);
spc8 = true;
spd0.x = translate.x * f20;
spd0.y = translate.y * f20;
spd0.z = translate.z * f20;
}
} else {
while (true) {
if (forwards) {
if (floorend < floorcur) {
break;
}
} else {
if (floorend > floorcur) {
break;
}
}
s0frame = modelConstrainOrWrapAnimFrame(floorcur, anim->animnum, anim->endframe);
anim->framea = s0frame;
if (spc8) {
spe0.x = spd0.x;
spe0.y = spd0.y;
spe0.z = spd0.z;
if (rwdata->unk18 == 0.0f) {
f30 = spcc;
}
} else {
f22 = animGetTranslateAngle(animpart, anim->flip, skel, anim->animnum, s0frame, &translate, anim->average);
if (scale != 1.0f) {
translate.x *= scale;
translate.y *= scale;
translate.z *= scale;
}
if (!forwards) {
translate.x = -translate.x;
translate.z = -translate.z;
if (f22 > 0.0f) {
f22 = M_BADTAU - f22;
}
}
if (anim->average) {
translate.y = rwdata->pos.y - rwdata->ground;
}
cosine = cosf(rwdata->yrot);
sine = sinf(rwdata->yrot);
spe0.x += translate.x * cosine + translate.z * sine;
spe0.y = translate.y;
spe0.z += (-translate.x * sine) + (translate.z * cosine);
if (rwdata->unk18 == 0.0f) {
f30 += f22;
if (f30 >= M_BADTAU) {
f30 -= M_BADTAU;
}
}
}
if (forwards) {
floorcur++;
} else {
floorcur--;
}
s0frame = modelConstrainOrWrapAnimFrame(floorcur, anim->animnum, anim->endframe);
anim->frameb = s0frame;
if (anim->frameb != anim->framea) {
f22 = animGetTranslateAngle(animpart, anim->flip, skel, anim->animnum, s0frame, &translate, anim->average);
spc8 = true;
if (scale != 1.0f) {
translate.x *= scale;
translate.y *= scale;
translate.z *= scale;
}
if (!forwards) {
translate.x = -translate.x;
translate.z = -translate.z;
if (f22 > 0.0f) {
f22 = M_BADTAU - f22;
}
}
if (anim->average) {
translate.y = rwdata->unk34.y;
}
cosine = cosf(rwdata->unk30);
sine = sinf(rwdata->unk30);
if (g_ModelAnimMergingEnabled && anim->animnum2) {
spd0.x = translate.x * cosine + translate.f[2] * sine;
spd0.z = -translate.x * sine + translate.f[2] * cosine;
if (absspeed > 0.0f) {
f32 f0 = anim->fracmerge - anim->playspeed / (absspeed * anim->timemerge);
if (f0 < 0.0f) {
f0 = 0.0f;
}
f0 = (f0 + anim->fracmerge) / 2.0f;
if (1);
sp90.f[0] = (rwdata->unk40.x - rwdata->unk4c.x) * absspeed2 / absspeed;
sp90.f[2] = (rwdata->unk40.z - rwdata->unk4c.z) * absspeed2 / absspeed;
spd0.x += (sp90.x - spd0.x) * f0;
spd0.z += (sp90.z - spd0.z) * f0;
} else {
spd0.x += (rwdata->unk40.x - rwdata->unk4c.x) * anim->fracmerge;
spd0.z += (rwdata->unk40.z - rwdata->unk4c.z) * anim->fracmerge;
}
spd0.x += spe0.x;
spd0.z += spe0.z;
spd0.y = translate.y;
} else {
spd0.x = spe0.x + translate.x * cosine + translate.f[2] * sine;
spd0.y = translate.y;
spd0.z = spe0.z - translate.x * sine + translate.f[2] * cosine;
}
if (rwdata->unk5c > 0.0f && absspeed > 0.0f) {
f32 increment = 1.0f / absspeed;
if (increment > rwdata->unk5c) {
increment = rwdata->unk5c;
rwdata->unk5c = 0.0f;
} else {
rwdata->unk5c -= increment;
}
f22 += rwdata->unk58 * increment;
if (f22 < 0.0f) {
f22 += M_BADTAU;
} else if (f22 >= M_BADTAU) {
f22 -= M_BADTAU;
}
}
if (rwdata->unk18 == 0.0f) {
spcc = f30 + f22;
if (spcc >= M_BADTAU) {
spcc -= M_BADTAU;
}
}
}
}
}
rwdata->unk34.x = spe0.x;
rwdata->unk34.y = spe0.y;
rwdata->unk34.z = spe0.z;
rwdata->unk30 = f30;
rwdata->unk24.x = spd0.f[0];
rwdata->unk24.y = spd0.f[1];
rwdata->unk24.z = spd0.f[2];
rwdata->unk20 = spcc;
rwdata->unk01 = spc8;
if (anim->framea == anim->frameb) {
anim->frac = 0.0f;
anim->frame = anim->framea;
} else if (forwards) {
anim->frac = endframe - floorend;
anim->frame = anim->framea + anim->frac;
} else {
anim->frac = floorend - endframe;
anim->frame = anim->frameb + (1.0f - anim->frac);
}
if (anim->animnum2 && (g_Anims[anim->animnum].flags & ANIMFLAG_ABSOLUTETRANSLATION) == 0) {
s32 floorcur2 = floor(curframe2);
s32 floorend2 = floor(endframe2);
if ((forwards && floorcur2 < floorend2) || (!forwards && floorend2 < floorcur2)) {
if (rwdata->unk02 != 0) {
rwdata->unk4c.f[1] = rwdata->unk40.f[1];
} else {
rwdata->unk4c.f[1] = rwdata->unk34.f[1];
}
anim->frame2a = modelConstrainOrWrapAnimFrame(floorend2, anim->animnum2, anim->endframe2);
s0frame = modelConstrainOrWrapAnimFrame(floorend2 + 1, anim->animnum2, anim->endframe2);
anim->frame2b = s0frame;
animGetTranslateAngle(animpart, anim->flip2, skel, anim->animnum2, s0frame, &translate, anim->average);
if (scale != 1.0f) {
translate.y *= scale;
}
if (anim->average) {
translate.y = rwdata->unk4c.y;
}
rwdata->unk40.y = translate.y;
rwdata->unk02 = 1;
}
if (forwards) {
anim->frac2 = endframe2 - floorend2;
anim->frame2 = anim->frame2a + anim->frac2;
} else {
anim->frac2 = 1.0f - (endframe2 - floorend2);
anim->frame2 = anim->frame2b + (1.0f - anim->frac2);
}
} else {
rwdata->unk02 = 0;
}
} else {
modelSetAnimFrame2(model, endframe, endframe2);
}
} else {
modelSetAnimFrame2(model, endframe, endframe2);
}
}
}
void modelTickAnimQuarterSpeed(struct model *model, s32 lvupdate240, bool arg2)
{
f32 frame;
f32 frame2;
f32 speed;
f32 speed2;
f32 startframe;
f32 endframe;
f32 realendframe;
struct anim *anim = model->anim;
if (anim && lvupdate240 > 0) {
frame = anim->frame;
frame2 = anim->frame2;
for (; lvupdate240 > 0; lvupdate240--) {
if (anim->timeplay > 0) {
anim->elapseplay += 0.25f;
if (anim->elapseplay < anim->timeplay) {
anim->playspeed = anim->oldplay + (anim->newplay - anim->oldplay) * anim->elapseplay / anim->timeplay;
} else {
anim->timeplay = 0;
anim->playspeed = anim->newplay;
}
}
if (anim->timemerge > 0) {
anim->elapsemerge += anim->playspeed * 0.25f;
if (anim->elapsemerge == 0) {
anim->fracmerge = 1;
} else {
if (anim->elapsemerge < anim->timemerge) {
anim->fracmerge = (anim->timemerge - anim->elapsemerge) / anim->timemerge;
} else {
anim->timemerge = 0;
anim->fracmerge = 0;
anim->animnum2 = 0;
}
}
}
if (anim->timespeed > 0) {
anim->elapsespeed += anim->playspeed * 0.25f;
if (anim->elapsespeed < anim->timespeed) {
anim->speed = anim->oldspeed + (anim->newspeed - anim->oldspeed) * anim->elapsespeed / anim->timespeed;
} else {
anim->timespeed = 0;
anim->speed = anim->newspeed;
}
}
speed = anim->speed;
frame += anim->playspeed * speed * 0.25f;
if (anim->animnum2) {
if (anim->timespeed2 > 0) {
anim->elapsespeed2 += anim->playspeed * 0.25f;
if (anim->elapsespeed2 < anim->timespeed2) {
anim->speed2 = anim->oldspeed2 + (anim->newspeed2 - anim->oldspeed2) * anim->elapsespeed2 / anim->timespeed2;
} else {
anim->timespeed2 = 0;
anim->speed2 = anim->newspeed2;
}
}
speed2 = anim->speed2;
frame2 += anim->playspeed * speed2 * 0.25f;
}
if (anim->looping) {
realendframe = anim->endframe;
if (speed >= 0) {
endframe = animGetNumFrames(anim->animnum) - 1;
startframe = anim->loopframe;
if (realendframe >= 0 && endframe > realendframe) {
endframe = realendframe;
}
} else {
endframe = anim->loopframe;
startframe = animGetNumFrames(anim->animnum) - 1;
if (realendframe >= 0 && startframe > realendframe) {
startframe = realendframe;
}
}
if ((speed >= 0 && frame >= endframe) || (speed < 0 && frame <= endframe)) {
f32 prevnewspeed = anim->newspeed;
f32 prevoldspeed = anim->oldspeed;
f32 prevtimespeed = anim->timespeed;
f32 prevelapsespeed = anim->elapsespeed;
if (arg2) {
modelSetAnimFrame2WithChrStuff(model, anim->frame, endframe, 0, 0);
} else {
modelSetAnimFrame2(model, endframe, 0);
}
modelSetAnimation(model, anim->animnum, anim->flip, startframe, anim->speed, anim->loopmerge);
anim->looping = true;
anim->endframe = realendframe;
anim->newspeed = prevnewspeed;
anim->oldspeed = prevoldspeed;
anim->timespeed = prevtimespeed;
anim->elapsespeed = prevelapsespeed;
frame2 = frame;
frame = startframe + frame - endframe;
if (anim->flipfunc) {
anim->flipfunc();
}
}
}
}
if (arg2) {
if (anim->animnum2) {
modelSetAnimFrame2WithChrStuff(model, anim->frame, frame, anim->frame2, frame2);
} else {
modelSetAnimFrame2WithChrStuff(model, anim->frame, frame, 0, 0);
}
} else {
if (anim->animnum2) {
modelSetAnimFrame2(model, frame, frame2);
} else {
modelSetAnimFrame2(model, frame, 0);
}
}
}
}
#if VERSION < VERSION_PAL_BETA
/**
* This is identical to the above function but removes the 0.25f multipliers.
*/
void modelTickAnim(struct model *model, s32 lvupdate240, bool arg2)
{
f32 frame;
f32 frame2;
f32 speed;
f32 speed2;
f32 startframe;
f32 endframe;
f32 realendframe;
struct anim *anim = model->anim;
if (anim && lvupdate240 > 0) {
frame = anim->frame;
frame2 = anim->frame2;
for (; lvupdate240 > 0; lvupdate240--) {
if (anim->timeplay > 0) {
anim->elapseplay++;
if (anim->elapseplay < anim->timeplay) {
anim->playspeed = anim->oldplay + (anim->newplay - anim->oldplay) * anim->elapseplay / anim->timeplay;
} else {
anim->timeplay = 0;
anim->playspeed = anim->newplay;
}
}
if (anim->timemerge > 0) {
anim->elapsemerge += anim->playspeed;
if (anim->elapsemerge == 0) {
anim->fracmerge = 1;
} else {
if (anim->elapsemerge < anim->timemerge) {
anim->fracmerge = (anim->timemerge - anim->elapsemerge) / anim->timemerge;
} else {
anim->timemerge = 0;
anim->fracmerge = 0;
anim->animnum2 = 0;
}
}
}
if (anim->timespeed > 0) {
anim->elapsespeed += anim->playspeed;
if (anim->elapsespeed < anim->timespeed) {
anim->speed = anim->oldspeed + (anim->newspeed - anim->oldspeed) * anim->elapsespeed / anim->timespeed;
} else {
anim->timespeed = 0;
anim->speed = anim->newspeed;
}
}
speed = anim->speed;
frame += anim->playspeed * speed;
if (anim->animnum2) {
if (anim->timespeed2 > 0) {
anim->elapsespeed2 += anim->playspeed;
if (anim->elapsespeed2 < anim->timespeed2) {
anim->speed2 = anim->oldspeed2 + (anim->newspeed2 - anim->oldspeed2) * anim->elapsespeed2 / anim->timespeed2;
} else {
anim->timespeed2 = 0;
anim->speed2 = anim->newspeed2;
}
}
speed2 = anim->speed2;
frame2 += anim->playspeed * speed2;
}
if (anim->looping) {
realendframe = anim->endframe;
if (speed >= 0) {
endframe = animGetNumFrames(anim->animnum) - 1;
startframe = anim->loopframe;
if (realendframe >= 0 && endframe > realendframe) {
endframe = realendframe;
}
} else {
endframe = anim->loopframe;
startframe = animGetNumFrames(anim->animnum) - 1;
if (realendframe >= 0 && startframe > realendframe) {
startframe = realendframe;
}
}
if ((speed >= 0 && frame >= endframe) || (speed < 0 && frame <= endframe)) {
f32 prevnewspeed = anim->newspeed;
f32 prevoldspeed = anim->oldspeed;
f32 prevtimespeed = anim->timespeed;
f32 prevelapsespeed = anim->elapsespeed;
if (arg2) {
modelSetAnimFrame2WithChrStuff(model, anim->frame, endframe, 0, 0);
} else {
modelSetAnimFrame2(model, endframe, 0);
}
modelSetAnimation(model, anim->animnum, anim->flip, startframe, anim->speed, anim->loopmerge);
anim->looping = true;
anim->endframe = realendframe;
anim->newspeed = prevnewspeed;
anim->oldspeed = prevoldspeed;
anim->timespeed = prevtimespeed;
anim->elapsespeed = prevelapsespeed;
frame2 = frame;
frame = startframe + frame - endframe;
if (anim->flipfunc) {
anim->flipfunc();
}
}
}
}
if (arg2) {
if (anim->animnum2) {
modelSetAnimFrame2WithChrStuff(model, anim->frame, frame, anim->frame2, frame2);
} else {
modelSetAnimFrame2WithChrStuff(model, anim->frame, frame, 0, 0);
}
} else {
if (anim->animnum2) {
modelSetAnimFrame2(model, frame, frame2);
} else {
modelSetAnimFrame2(model, frame, 0);
}
}
}
}
#endif
void modelApplyRenderModeType1(struct modelrenderdata *renderdata)
{
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_1CYCLE);
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_AA_ZB_OPA_SURF, G_RM_AA_ZB_OPA_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_AA_OPA_SURF, G_RM_AA_OPA_SURF2);
}
gDPSetCombineMode(renderdata->gdl++, G_CC_MODULATEIA, G_CC_MODULATEIA);
}
void modelApplyRenderModeType3(struct modelrenderdata *renderdata, bool arg1)
{
if (renderdata->unk30 == 7) {
if (arg1) {
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
gDPSetFogColorViaWord(renderdata->gdl++, renderdata->fogcolour);
gDPSetEnvColorViaWord(renderdata->gdl++, renderdata->envcolour | 0xff);
gDPSetCombineMode(renderdata->gdl++, G_CC_CUSTOM_17, G_CC_CUSTOM_18);
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_OPA_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_OPA_SURF2);
}
} else {
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_XLU_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_XLU_SURF2);
}
}
} else if (renderdata->unk30 == 8) {
if (arg1) {
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
gDPSetFogColorViaWord(renderdata->gdl++, renderdata->fogcolour);
gDPSetEnvColorViaWord(renderdata->gdl++, renderdata->envcolour);
gDPSetCombineMode(renderdata->gdl++, G_CC_CUSTOM_19, G_CC_CUSTOM_18);
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_XLU_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_XLU_SURF2);
}
}
} else if (renderdata->unk30 == 9) {
if ((renderdata->envcolour & 0xff) == 0) {
if (arg1) {
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
gDPSetFogColorViaWord(renderdata->gdl++, renderdata->fogcolour);
gDPSetEnvColorViaWord(renderdata->gdl++, 0xffffffff);
gDPSetPrimColor(renderdata->gdl++, 0, 0, 0, 0, 0, (renderdata->envcolour >> 8) & 0xff);
gDPSetCombineMode(renderdata->gdl++, G_CC_TRILERP, G_CC_CUSTOM_20);
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_OPA_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_OPA_SURF2);
}
} else {
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_XLU_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_XLU_SURF2);
}
}
} else {
if (arg1) {
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
gDPSetFogColorViaWord(renderdata->gdl++, renderdata->fogcolour);
gDPSetEnvColor(renderdata->gdl++, 0, 0, 0, renderdata->envcolour);
gDPSetCombineMode(renderdata->gdl++, G_CC_CUSTOM_21, G_CC_CUSTOM_18);
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_TEX_EDGE2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_TEX_EDGE2);
}
} else {
gDPSetPrimColor(renderdata->gdl++, 0, 0, 0, 0, 0, (renderdata->envcolour >> 8) & 0xff);
gDPSetCombineMode(renderdata->gdl++, G_CC_CUSTOM_22, G_CC_CUSTOM_23);
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_TEX_EDGE2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_TEX_EDGE2);
}
}
}
} else if (renderdata->unk30 == 4) {
if (arg1) {
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
gDPSetFogColorViaWord(renderdata->gdl++, renderdata->envcolour);
gDPSetCombineMode(renderdata->gdl++, G_CC_TRILERP, G_CC_MODULATEIA2);
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_OPA_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_OPA_SURF2);
}
} else {
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_XLU_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_XLU_SURF2);
}
}
} else if (renderdata->unk30 == 5) {
u8 alpha;
if (arg1) {
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
gDPSetFogColorViaWord(renderdata->gdl++, renderdata->fogcolour);
alpha = renderdata->envcolour & 0xff;
if (alpha < 255) {
gDPSetEnvColor(renderdata->gdl++, 0xff, 0xff, 0xff, alpha);
if (renderdata->envcolour & 0xff00) {
gDPSetCombineMode(renderdata->gdl++, G_CC_CUSTOM_24, G_CC_MODULATEIA2);
} else {
gDPSetCombineMode(renderdata->gdl++, G_CC_CUSTOM_25, G_CC_MODULATEIA2);
}
} else {
gDPSetCombineMode(renderdata->gdl++, G_CC_TRILERP, G_CC_MODULATEIA2);
}
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_XLU_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_XLU_SURF2);
}
} else {
alpha = renderdata->envcolour & 0xff;
if (alpha < 255) {
gDPSetCombineMode(renderdata->gdl++, G_CC_CUSTOM_26, G_CC_MODULATEIA2);
} else {
gDPSetCombineMode(renderdata->gdl++, G_CC_TRILERP, G_CC_MODULATEIA2);
}
}
} else {
if (arg1) {
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
gDPSetCombineMode(renderdata->gdl++, G_CC_TRILERP, G_CC_MODULATEIA2);
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_PASS, G_RM_AA_ZB_OPA_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_PASS, G_RM_AA_OPA_SURF2);
}
} else {
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_PASS, G_RM_AA_ZB_XLU_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_PASS, G_RM_AA_XLU_SURF2);
}
}
}
}
void modelApplyRenderModeType4(struct modelrenderdata *renderdata, bool arg1)
{
if (renderdata->unk30 == 7) {
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
gDPSetFogColorViaWord(renderdata->gdl++, renderdata->fogcolour);
gDPSetEnvColorViaWord(renderdata->gdl++, renderdata->envcolour | 0x000000ff);
gDPSetCombineMode(renderdata->gdl++, G_CC_CUSTOM_17, G_CC_CUSTOM_18);
if (arg1) {
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_OPA_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_OPA_SURF2);
}
} else {
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_XLU_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_XLU_SURF2);
}
}
} else if (renderdata->unk30 == 8) {
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
gDPSetFogColorViaWord(renderdata->gdl++, renderdata->fogcolour);
gDPSetEnvColorViaWord(renderdata->gdl++, renderdata->envcolour);
gDPSetCombineMode(renderdata->gdl++, G_CC_CUSTOM_19, G_CC_CUSTOM_18);
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_XLU_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_XLU_SURF2);
}
} else if (renderdata->unk30 == 9) {
if ((renderdata->envcolour & 0xff) == 0) {
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
gDPSetFogColorViaWord(renderdata->gdl++, renderdata->fogcolour);
gDPSetEnvColorViaWord(renderdata->gdl++, 0xffffffff);
gDPSetPrimColor(renderdata->gdl++, 0, 0, 0, 0, 0, (renderdata->envcolour >> 8) & 0xff);
if (arg1) {
gDPSetCombineMode(renderdata->gdl++, G_CC_TRILERP, G_CC_CUSTOM_20);
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_OPA_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_OPA_SURF2);
}
} else {
gDPSetCombineMode(renderdata->gdl++, G_CC_TRILERP, G_CC_CUSTOM_20);
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_XLU_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_XLU_SURF2);
}
}
} else {
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
gDPSetFogColorViaWord(renderdata->gdl++, renderdata->fogcolour);
gDPSetEnvColorViaWord(renderdata->gdl++, renderdata->envcolour & 0xff);
if (arg1) {
gDPSetCombineMode(renderdata->gdl++, G_CC_CUSTOM_21, G_CC_CUSTOM_18);
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_TEX_EDGE2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_TEX_EDGE2);
}
} else {
gDPSetPrimColor(renderdata->gdl++, 0, 0, 0, 0, 0, (renderdata->envcolour >> 8) & 0xff);
gDPSetCombineMode(renderdata->gdl++, G_CC_CUSTOM_22, G_CC_CUSTOM_23);
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_TEX_EDGE2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_TEX_EDGE2);
}
}
}
} else if (renderdata->unk30 == 4) {
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
gDPSetFogColorViaWord(renderdata->gdl++, renderdata->envcolour);
gDPSetCombineMode(renderdata->gdl++, G_CC_TRILERP, G_CC_MODULATEIA2);
if (arg1) {
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_OPA_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_OPA_SURF2);
}
} else {
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_XLU_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_XLU_SURF2);
}
}
} else if (renderdata->unk30 == 5) {
u8 alpha;
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
gDPSetFogColorViaWord(renderdata->gdl++, renderdata->fogcolour);
alpha = renderdata->envcolour & 0xff;
if (alpha < 255) {
gDPSetEnvColor(renderdata->gdl++, 0xff, 0xff, 0xff, alpha);
if (arg1) {
if (renderdata->envcolour & 0xff00) {
gDPSetCombineMode(renderdata->gdl++, G_CC_CUSTOM_24, G_CC_MODULATEIA2);
} else {
gDPSetCombineMode(renderdata->gdl++, G_CC_CUSTOM_25, G_CC_MODULATEIA2);
}
} else {
gDPSetCombineMode(renderdata->gdl++, G_CC_CUSTOM_26, G_CC_MODULATEIA2);
}
} else {
gDPSetCombineMode(renderdata->gdl++, G_CC_TRILERP, G_CC_MODULATEIA2);
}
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_XLU_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_XLU_SURF2);
}
} else {
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
gDPSetFogColorViaWord(renderdata->gdl++, 0xffffff00);
gDPSetCombineMode(renderdata->gdl++, G_CC_TRILERP, G_CC_MODULATEIA2);
if (arg1) {
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_OPA_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_OPA_SURF2);
}
} else {
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_ZB_XLU_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_FOG_PRIM_A, G_RM_AA_XLU_SURF2);
}
}
}
}
void modelApplyRenderModeType2(struct modelrenderdata *renderdata)
{
gDPPipeSync(renderdata->gdl++);
gDPSetCycleType(renderdata->gdl++, G_CYC_2CYCLE);
if (renderdata->zbufferenabled) {
gDPSetRenderMode(renderdata->gdl++, G_RM_PASS, G_RM_AA_ZB_OPA_SURF2);
} else {
gDPSetRenderMode(renderdata->gdl++, G_RM_PASS, G_RM_AA_OPA_SURF2);
}
gDPSetCombineMode(renderdata->gdl++, G_CC_TRILERP, G_CC_MODULATEIA2);
}
void modelApplyCullMode(struct modelrenderdata *renderdata)
{
if (renderdata->cullmode == CULLMODE_NONE) {
gSPClearGeometryMode(renderdata->gdl++, G_CULL_BOTH);
} else if (renderdata->cullmode == CULLMODE_FRONT) {
gSPSetGeometryMode(renderdata->gdl++, G_CULL_FRONT);
} else if (renderdata->cullmode == CULLMODE_BACK) {
gSPSetGeometryMode(renderdata->gdl++, G_CULL_BACK);
}
}
void modelRenderNodeGundl(struct modelrenderdata *renderdata, struct model *model, struct modelnode *node)
{
struct modelrodata_gundl *rodata = &node->rodata->gundl;
if (var8005efc4 && !var8005efc4(model, node)) {
return;
}
if ((renderdata->flags & MODELRENDERFLAG_OPA) && rodata->opagdl) {
gSPSegment(renderdata->gdl++, SPSEGMENT_MODEL_COL1, osVirtualToPhysical(rodata->baseaddr));
if (renderdata->cullmode) {
modelApplyCullMode(renderdata);
}
switch (rodata->unk12) {
case 1:
modelApplyRenderModeType1(renderdata);
break;
case 3:
modelApplyRenderModeType3(renderdata, true);
break;
case 4:
modelApplyRenderModeType4(renderdata, true);
break;
case 2:
modelApplyRenderModeType2(renderdata);
break;
}
gSPDisplayList(renderdata->gdl++, rodata->opagdl);
if (rodata->unk12 == 3 && rodata->xlugdl) {
modelApplyRenderModeType3(renderdata, false);
gSPDisplayList(renderdata->gdl++, rodata->xlugdl);
}
}
if ((renderdata->flags & MODELRENDERFLAG_XLU) && rodata->opagdl && rodata->unk12 == 4 && rodata->xlugdl) {
gSPSegment(renderdata->gdl++, SPSEGMENT_MODEL_COL1, osVirtualToPhysical(rodata->baseaddr));
if (renderdata->cullmode) {
modelApplyCullMode(renderdata);
}
modelApplyRenderModeType4(renderdata, false);
gSPDisplayList(renderdata->gdl++, rodata->xlugdl);
}
}
void modelRenderNodeDl(struct modelrenderdata *renderdata, struct model *model, struct modelnode *node)
{
union modelrodata *rodata = node->rodata;
if (var8005efc4 && !var8005efc4(model, node)) {
return;
}
if (renderdata->flags & MODELRENDERFLAG_OPA) {
union modelrwdata *rwdata = modelGetNodeRwData(model, node);
if (rwdata->dl.gdl) {
gSPSegment(renderdata->gdl++, SPSEGMENT_MODEL_COL1, osVirtualToPhysical(rodata->dl.colours));
if (renderdata->cullmode) {
modelApplyCullMode(renderdata);
}
switch (rodata->dl.mcount) {
case 1:
modelApplyRenderModeType1(renderdata);
break;
case 3:
modelApplyRenderModeType3(renderdata, true);
break;
case 4:
modelApplyRenderModeType4(renderdata, true);
break;
case 2:
modelApplyRenderModeType2(renderdata);
break;
}
gSPSegment(renderdata->gdl++, SPSEGMENT_MODEL_VTX, osVirtualToPhysical(rwdata->dl.vertices));
gSPSegment(renderdata->gdl++, SPSEGMENT_MODEL_COL2, osVirtualToPhysical(rwdata->dl.colours));
gSPDisplayList(renderdata->gdl++, rwdata->dl.gdl);
if (rodata->dl.mcount == 3 && rodata->dl.xlugdl) {
modelApplyRenderModeType3(renderdata, false);
gSPDisplayList(renderdata->gdl++, rodata->dl.xlugdl);
}
}
}
if (renderdata->flags & MODELRENDERFLAG_XLU) {
union modelrwdata *rwdata = modelGetNodeRwData(model, node);
if (rwdata->dl.gdl && rodata->dl.mcount == 4 && rodata->dl.xlugdl) {
gSPSegment(renderdata->gdl++, SPSEGMENT_MODEL_COL1, osVirtualToPhysical(rodata->dl.colours));
if (renderdata->cullmode) {
modelApplyCullMode(renderdata);
}
gSPSegment(renderdata->gdl++, SPSEGMENT_MODEL_VTX, osVirtualToPhysical(rwdata->dl.vertices));
gSPSegment(renderdata->gdl++, SPSEGMENT_MODEL_COL2, osVirtualToPhysical(rwdata->dl.colours));
modelApplyRenderModeType4(renderdata, false);
gSPDisplayList(renderdata->gdl++, rodata->dl.xlugdl);
}
}
}
/**
* Star gunfire is a muzzle flash in a first person perspective, where the
* muzzle flash has 3 or 4 "arms" that flare out from the main body.
*
* Some weapons that use this are the Cyclone, Dragon, K7 Avenger, AR34,
* SuperDragon and RC-P45.
*
* This function reads vertices from the model definition, tweaks them randomly,
* writes them to a newly allocated vertices table and queues the node's
* displaylist to the renderdata's DL.
*/
void modelRenderNodeStarGunfire(struct modelrenderdata *renderdata, struct modelnode *node)
{
if (renderdata->flags & MODELRENDERFLAG_XLU) {
struct modelrodata_stargunfire *rodata = &node->rodata->stargunfire;
s32 i;
if (rodata->gdl) {
Vtx *src = (Vtx *) rodata->vertices;
Vtx *dst = g_ModelVtxAllocatorFunc(rodata->unk00 * 4);
gSPSegment(renderdata->gdl++, SPSEGMENT_MODEL_VTX, osVirtualToPhysical(dst));
gSPSegment(renderdata->gdl++, SPSEGMENT_MODEL_COL2, osVirtualToPhysical((void *)ALIGN8((uintptr_t)&rodata->vertices[rodata->unk00 << 2])));
gSPSegment(renderdata->gdl++, SPSEGMENT_MODEL_COL1, osVirtualToPhysical(rodata->baseaddr));
gDPSetFogColor(renderdata->gdl++, 0x00, 0x00, 0x00, 0x00);
gSPDisplayList(renderdata->gdl++, rodata->gdl);
for (i = 0; i < rodata->unk00; i++) {
u16 rand1 = (random() << 10) & 0xffff;
s32 s4 = ((coss(rand1) << 5) * 181) >> 18;
s32 s3 = ((sins(rand1) << 5) * 181) >> 18;
s32 s1 = random() >> 31;
s32 mult = 0x10000 - (random() & 0x3fff);
s32 corner1 = 0x200 + s3;
s32 corner2 = 0x200 - s3;
s32 corner3 = 0x200 - s4;
s32 corner4 = 0x200 + s4;
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
dst[3] = src[3];
dst[0].s = corner3;
dst[0].t = corner2;
dst[0].x = (src[(s1 + 0) % 4].x * mult) >> 16;
dst[0].y = (src[(s1 + 0) % 4].y * mult) >> 16;
dst[0].z = (src[(s1 + 0) % 4].z * mult) >> 16;
dst[1].s = corner1;
dst[1].t = corner3;
dst[1].x = (src[(s1 + 1) % 4].x * mult) >> 16;
dst[1].y = (src[(s1 + 1) % 4].y * mult) >> 16;
dst[1].z = (src[(s1 + 1) % 4].z * mult) >> 16;
dst[2].s = corner4;
dst[2].t = corner1;
dst[2].x = (src[(s1 + 2) % 4].x * mult) >> 16;
dst[2].y = (src[(s1 + 2) % 4].y * mult) >> 16;
dst[2].z = (src[(s1 + 2) % 4].z * mult) >> 16;
dst[3].s = corner2;
dst[3].t = corner4;
dst[3].x = (src[(s1 + 3) % 4].x * mult) >> 16;
dst[3].y = (src[(s1 + 3) % 4].y * mult) >> 16;
dst[3].z = (src[(s1 + 3) % 4].z * mult) >> 16;
src += 4;
dst += 4;
}
}
}
}
void modelSelectTexture(struct modelrenderdata *renderdata, struct textureconfig *tconfig, s32 arg2)
{
texSelect(&renderdata->gdl, tconfig, arg2, renderdata->zbufferenabled, 2, 1, NULL);
}
void modelRenderNodeChrGunfire(struct modelrenderdata *renderdata, struct model *model, struct modelnode *node)
{
u32 stack[3];
f32 negspc0;
struct modelrodata_chrgunfire *rodata = &node->rodata->chrgunfire;
union modelrwdata *rwdata = modelGetNodeRwData(model, node);
Vtx *vertices;
f32 spf0;
f32 spec;
struct coord spe0;
f32 spdc;
f32 spd8;
f32 rot2;
f32 spd0;
f32 spcc;
f32 spc8;
f32 spc4;
f32 spc0;
f32 spbc;
f32 negspcc;
f32 negspc8;
f32 scale;
Mtxf *mtx;
f32 tmp;
struct coord sp9c;
struct coord sp90;
Vtx vtxtemplate = {0};
Col colourtemplate = {0xffffffff};
struct textureconfig *tconfig;
Col *colours;
f32 distance;
if ((renderdata->flags & MODELRENDERFLAG_XLU) && rwdata->chrgunfire.visible) {
s32 index = modelFindNodeMtxIndex(node, 0);
mtx = &model->matrices[index];
spe0.x = -(rodata->pos.f[0] * mtx->m[0][0] + rodata->pos.f[1] * mtx->m[1][0] + rodata->pos.f[2] * mtx->m[2][0] + mtx->m[3][0]);
spe0.y = -(rodata->pos.f[0] * mtx->m[0][1] + rodata->pos.f[1] * mtx->m[1][1] + rodata->pos.f[2] * mtx->m[2][1] + mtx->m[3][1]);
spe0.z = -(rodata->pos.f[0] * mtx->m[0][2] + rodata->pos.f[1] * mtx->m[1][2] + rodata->pos.f[2] * mtx->m[2][2] + mtx->m[3][2]);
distance = sqrtf(spe0.f[0] * spe0.f[0] + spe0.f[1] * spe0.f[1] + spe0.f[2] * spe0.f[2]);
if (distance > 0) {
f32 tmp = 1 / (model->scale * distance);
spe0.f[0] *= tmp;
spe0.f[1] *= tmp;
spe0.f[2] *= tmp;
} else {
spe0.f[0] = 0;
spe0.f[1] = 0;
spe0.f[2] = 1 / model->scale;
}
spec = acosf(spe0.f[0] * mtx->m[1][0] + spe0.f[1] * mtx->m[1][1] + spe0.f[2] * mtx->m[1][2]);
spf0 = acosf(-(spe0.f[0] * mtx->m[2][0] + spe0.f[1] * mtx->m[2][1] + spe0.f[2] * mtx->m[2][2]) / sinf(spec));
tmp = -(spe0.f[0] * mtx->m[0][0] + spe0.f[1] * mtx->m[0][1] + spe0.f[2] * mtx->m[0][2]);
if (tmp < 0) {
spf0 = M_BADTAU - spf0;
}
spdc = cosf(spf0);
spd8 = sinf(spf0);
rot2 = cosf(spec);
spd0 = sinf(spec);
scale = 0.75f + (random() % 128) * (1.0f / 256.0f); // 0.75 to 1.25
sp9c.f[0] = rodata->dim.f[0] * scale;
sp9c.f[1] = rodata->dim.f[1] * scale;
sp9c.f[2] = rodata->dim.f[2] * scale;
spcc = sp9c.f[0] * spdc * 0.5f;
spc8 = sp9c.f[2] * spd8 * 0.5f;
spc4 = sp9c.f[1] * spd0 * 0.5f;
spc0 = sp9c.f[0] * rot2 * spd8 * 0.5f;
spbc = sp9c.f[2] * rot2 * spdc * 0.5f;
negspcc = -spcc;
negspc8 = -spc8;
negspc0 = -spc0;
sp90.f[0] = rodata->pos.f[0] - sp9c.f[0] * 0.5f;
sp90.f[1] = rodata->pos.f[1];
sp90.f[2] = rodata->pos.f[2];
vertices = g_ModelVtxAllocatorFunc(4);
colours = (Col *) gfxAllocateColours(1);
vertices[0] = vtxtemplate;
vertices[1] = vtxtemplate;
vertices[2] = vtxtemplate;
vertices[3] = vtxtemplate;
colours[0] = colourtemplate;
vertices[0].x = sp90.f[0] + negspcc + negspc0;
vertices[0].y = sp90.f[1] - spc4;
vertices[0].z = sp90.f[2] - negspc8 + -spbc;
vertices[1].x = sp90.f[0] + negspcc - negspc0;
vertices[1].y = sp90.f[1] + spc4;
vertices[1].z = sp90.f[2] - negspc8 - -spbc;
vertices[2].x = sp90.f[0] - negspcc - negspc0;
vertices[2].y = sp90.f[1] + spc4;
vertices[2].z = sp90.f[2] + negspc8 - -spbc;
vertices[3].x = sp90.f[0] - negspcc + negspc0;
vertices[3].y = sp90.f[1] - spc4;
vertices[3].z = sp90.f[2] + negspc8 + -spbc;
gSPSegment(renderdata->gdl++, SPSEGMENT_MODEL_COL1, osVirtualToPhysical(rodata->baseaddr));
if (rodata->texture) {
s32 centre;
u16 sp62;
s32 sp5c;
s32 sp58;
tconfig = rodata->texture;
sp62 = (random() * 1024) & 0xffff;
sp5c = (coss(sp62) * tconfig->width * 0xb5) >> 18;
sp58 = (sins(sp62) * tconfig->width * 0xb5) >> 18;
centre = tconfig->width << 4;
vertices[0].s = centre - sp5c;
vertices[0].t = centre - sp58;
vertices[1].s = centre + sp58;
vertices[1].t = centre - sp5c;
vertices[2].s = centre + sp5c;
vertices[2].t = centre + sp58;
vertices[3].s = centre - sp58;
vertices[3].t = centre + sp5c;
modelSelectTexture(renderdata, tconfig, 4);
} else {
modelSelectTexture(renderdata, NULL, 1);
}
gSPSetGeometryMode(renderdata->gdl++, G_CULL_BACK);
gSPMatrix(renderdata->gdl++, osVirtualToPhysical(mtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPColor(renderdata->gdl++, osVirtualToPhysical(colours), 1);
gSPVertex(renderdata->gdl++, osVirtualToPhysical(vertices), 4, 0);
gSPTri2(renderdata->gdl++, 0, 1, 2, 2, 3, 0);
}
}
void modelRender(struct modelrenderdata *renderdata, struct model *model)
{
union modelrodata *rodata;
union modelrwdata *rwdata;
u32 type;
struct modelnode *node = model->definition->rootnode;
gSPSegment(renderdata->gdl++, SPSEGMENT_MODEL_MTX, osVirtualToPhysical(model->matrices));
while (node) {
type = node->type & 0xff;
switch (type) {
case MODELNODETYPE_DISTANCE:
case MODELNODETYPE_TOGGLE:
rodata = node->rodata;
rwdata = modelGetNodeRwData(model, node);
switch (type) {
case MODELNODETYPE_DISTANCE:
node->child = rwdata->distance.visible ? rodata->distance.target : NULL;
break;
case MODELNODETYPE_TOGGLE:
node->child = rwdata->toggle.visible ? rodata->toggle.target : NULL;
break;
}
break;
case MODELNODETYPE_HEADSPOT:
rwdata = modelGetNodeRwData(model, node);
if (rwdata->headspot.headmodeldef) {
struct modelnode *loopnode = rwdata->headspot.headmodeldef->rootnode;
node->child = loopnode;
while (loopnode) {
loopnode->parent = node;
loopnode = loopnode->next;
}
}
break;
case MODELNODETYPE_REORDER:
modelApplyReorderRelations(model, node);
break;
case MODELNODETYPE_CHRGUNFIRE:
modelRenderNodeChrGunfire(renderdata, model, node);
break;
case MODELNODETYPE_GUNDL:
modelRenderNodeGundl(renderdata, model, node);
break;
case MODELNODETYPE_DL:
modelRenderNodeDl(renderdata, model, node);
break;
case MODELNODETYPE_STARGUNFIRE:
modelRenderNodeStarGunfire(renderdata, node);
break;
case MODELNODETYPE_CHRINFO:
default:
break;
}
if (node->child) {
node = node->child;
} else {
while (node) {
if (node->next) {
node = node->next;
break;
}
node = node->parent;
}
}
}
}
bool modelTestBboxNodeForHit(struct modelrodata_bbox *bbox, Mtxf *mtx, struct coord *arg2, struct coord *arg3)
{
f32 xthingx;
f32 xthingy;
f32 xthingz;
u32 stack1[3];
f32 xsum1;
f32 xsum2;
f32 xsum3;
f32 f0;
u32 stack;
f32 ythingx;
f32 ythingy;
f32 ythingz;
u32 stack2[3];
f32 ysum1;
f32 ysum2;
f32 ysum3;
f32 mult1;
f32 mult2;
f32 bestsum2;
f32 bestsum1;
f32 anotherbestsum3;
f32 anotherbestsum1;
f32 xmin = bbox->xmin;
f32 xmax = bbox->xmax;
f32 ymin = bbox->ymin;
f32 ymax = bbox->ymax;
f32 zmin = bbox->zmin;
f32 zmax = bbox->zmax;
f32 mult3;
f32 mult4;
f32 zthingx;
f32 zthingy;
f32 zthingz;
u32 stack3[3];
f32 zsum1;
f32 zsum2;
f32 zsum3;
if (var8005efc0 != 0.0f) {
xmin -= var8005efc0;
xmax += var8005efc0;
ymin -= var8005efc0;
ymax += var8005efc0;
zmin -= var8005efc0;
zmax += var8005efc0;
}
// x
xthingx = mtx->m[0][0] * mtx->m[0][0];
xthingy = mtx->m[0][1] * mtx->m[0][1];
xthingz = mtx->m[0][2] * mtx->m[0][2];
xsum1 = mtx->m[0][0] * arg3->f[0] + mtx->m[0][1] * arg3->f[1] + mtx->m[0][2] * arg3->f[2];
xsum2 = mtx->m[0][0] * (arg2->f[0] - mtx->m[3][0]) + mtx->m[0][1] * (arg2->f[1] - mtx->m[3][1]) + mtx->m[0][2] * (arg2->f[2] - mtx->m[3][2]);
f0 = -(xthingx + xthingy + xthingz) * xmax;
xsum3 = -(xsum2 + f0);
f0 = -(xthingx + xthingy + xthingz) * xmin;
xsum2 = -(xsum2 + f0);
if (xsum1 < 0.0f) {
xsum1 = -xsum1;
xsum2 = -xsum2;
xsum3 = -xsum3;
}
if (xsum2 < 0.0f && xsum3 < 0.0f) {
return false;
}
if (xsum3 < xsum2) {
f32 tmp = xsum2;
xsum2 = xsum3;
xsum3 = tmp;
}
// y
ythingx = mtx->m[1][0] * mtx->m[1][0];
ythingy = mtx->m[1][1] * mtx->m[1][1];
ythingz = mtx->m[1][2] * mtx->m[1][2];
ysum1 = mtx->m[1][0] * arg3->f[0] + mtx->m[1][1] * arg3->f[1] + mtx->m[1][2] * arg3->f[2];
ysum2 = mtx->m[1][0] * (arg2->f[0] - mtx->m[3][0]) + mtx->m[1][1] * (arg2->f[1] - mtx->m[3][1]) + mtx->m[1][2] * (arg2->f[2] - mtx->m[3][2]);
f0 = -(ythingx + ythingy + ythingz) * ymax;
ysum3 = -(ysum2 + f0);
f0 = -(ythingx + ythingy + ythingz) * ymin;
ysum2 = -(ysum2 + f0);
if (ysum1 < 0.0f) {
ysum1 = -ysum1;
ysum2 = -ysum2;
ysum3 = -ysum3;
}
if (ysum2 < 0.0f && ysum3 < 0.0f) {
return false;
}
if (ysum3 < ysum2) {
f32 tmp = ysum2;
ysum2 = ysum3;
ysum3 = tmp;
}
// Do x and y comparison things
mult1 = ysum2 * xsum1;
mult2 = xsum2 * ysum1;
mult3 = xsum3 * ysum1;
mult4 = ysum3 * xsum1;
if (mult1 < mult2) {
if (mult4 < mult2) {
return false;
}
bestsum2 = xsum2;
bestsum1 = xsum1;
} else {
if (mult3 < mult1) {
return false;
}
bestsum2 = ysum2;
bestsum1 = ysum1;
}
if (mult3 < mult4) {
anotherbestsum3 = xsum3;
anotherbestsum1 = xsum1;
} else {
anotherbestsum3 = ysum3;
anotherbestsum1 = ysum1;
}
// z
zthingx = mtx->m[2][0] * mtx->m[2][0];
zthingy = mtx->m[2][1] * mtx->m[2][1];
zthingz = mtx->m[2][2] * mtx->m[2][2];
zsum1 = mtx->m[2][0] * arg3->f[0] + mtx->m[2][1] * arg3->f[1] + mtx->m[2][2] * arg3->f[2];
zsum2 = mtx->m[2][0] * (arg2->f[0] - mtx->m[3][0]) + mtx->m[2][1] * (arg2->f[1] - mtx->m[3][1]) + mtx->m[2][2] * (arg2->f[2] - mtx->m[3][2]);
f0 = -(zthingx + zthingy + zthingz) * zmax;
zsum3 = -(zsum2 + f0);
f0 = -(zthingx + zthingy + zthingz) * zmin;
zsum2 = -(zsum2 + f0);
if (zsum1 < 0.0f) {
zsum1 = -zsum1;
zsum2 = -zsum2;
zsum3 = -zsum3;
}
if (zsum2 < 0.0f && zsum3 < 0.0f) {
return false;
}
if (zsum3 < zsum2) {
f32 tmp = zsum2;
zsum2 = zsum3;
zsum3 = tmp;
}
// Do z comparison things with the result of the x/y comparison thing
if (bestsum2 * zsum1 < zsum2 * bestsum1) {
if (anotherbestsum3 * zsum1 < zsum2 * anotherbestsum1) {
return false;
}
} else {
if (zsum3 * bestsum1 < bestsum2 * zsum1) {
return false;
}
}
return true;
}
/**
* Note that this only checks bbox nodes.
* This is okay for most objects as well as shielded chrs.
* For non-shielded chrs, an accurate polygon test is done elsewhere.
*/
s32 modelTestForHit(struct model *model, struct coord *arg1, struct coord *arg2, struct modelnode **startnode)
{
struct modelnode *node;
bool dochildren = true;
Mtxf *mtx;
union modelrodata *rodata;
union modelrwdata *rwdata;
u32 type;
if (model);
if (*startnode) {
node = *startnode;
*startnode = NULL;
} else {
node = model->definition->rootnode;
}
while (node) {
if (dochildren && node->child) {
node = node->child;
} else {
while (node) {
if (node->next) {
node = node->next;
break;
}
node = node->parent;
}
if (!node) {
break;
}
}
dochildren = true;
type = node->type & 0xff;
switch (type) {
case MODELNODETYPE_BBOX:
rodata = node->rodata;
mtx = modelFindNodeMtx(model, node, 0);
if (modelTestBboxNodeForHit(&rodata->bbox, mtx, arg1, arg2)) {
*startnode = node;
return rodata->bbox.hitpart;
}
dochildren = false;
break;
case MODELNODETYPE_DISTANCE:
rodata = node->rodata;
rwdata = modelGetNodeRwData(model, node);
node->child = rwdata->distance.visible ? rodata->distance.target : NULL;
break;
case MODELNODETYPE_TOGGLE:
rodata = node->rodata;
rwdata = modelGetNodeRwData(model, node);
node->child = rwdata->toggle.visible ? rodata->toggle.target : NULL;
break;
case MODELNODETYPE_HEADSPOT:
rwdata = modelGetNodeRwData(model, node);
if (rwdata->headspot.headmodeldef) {
struct modelnode *loopnode = rwdata->headspot.headmodeldef->rootnode;
node->child = loopnode;
while (loopnode) {
loopnode->parent = node;
loopnode = loopnode->next;
}
}
break;
case MODELNODETYPE_CHRINFO:
case MODELNODETYPE_DL:
default:
break;
}
}
return 0;
}
#define PROMOTE(var) \
if (var) \
var = (void *)((uintptr_t)var + diff)
void modelPromoteNodeOffsetsToPointers(struct modelnode *node, u32 vma, u32 fileramaddr)
{
union modelrodata *rodata;
s32 diff = fileramaddr - vma;
while (node) {
u32 type = node->type & 0xff;
PROMOTE(node->rodata);
PROMOTE(node->parent);
PROMOTE(node->next);
PROMOTE(node->prev);
PROMOTE(node->child);
switch (type) {
case MODELNODETYPE_CHRINFO:
break;
case MODELNODETYPE_POSITION:
break;
case MODELNODETYPE_GUNDL:
rodata = node->rodata;
PROMOTE(rodata->gundl.vertices);
rodata->gundl.baseaddr = (void *)fileramaddr;
break;
case MODELNODETYPE_DL:
rodata = node->rodata;
PROMOTE(rodata->dl.vertices);
rodata->dl.colours = (void *)fileramaddr;
break;
case MODELNODETYPE_DISTANCE:
rodata = node->rodata;
PROMOTE(rodata->distance.target);
node->child = rodata->distance.target;
break;
case MODELNODETYPE_TOGGLE:
rodata = node->rodata;
PROMOTE(rodata->toggle.target);
break;
case MODELNODETYPE_REORDER:
rodata = node->rodata;
PROMOTE(rodata->reorder.unk18);
PROMOTE(rodata->reorder.unk1c);
break;
case MODELNODETYPE_11:
rodata = node->rodata;
PROMOTE(rodata->type11.unk14);
break;
case MODELNODETYPE_0B:
rodata = node->rodata;
PROMOTE(rodata->type0b.unk3c);
rodata->type0b.baseaddr = (void *)fileramaddr;
break;
case MODELNODETYPE_CHRGUNFIRE:
rodata = node->rodata;
PROMOTE(rodata->chrgunfire.texture);
rodata->chrgunfire.baseaddr = (void *)fileramaddr;
break;
case MODELNODETYPE_0D:
rodata = node->rodata;
PROMOTE(rodata->type0d.unk10);
PROMOTE(rodata->type0d.unk14);
rodata->type0d.baseaddr = (void *)fileramaddr;
break;
case MODELNODETYPE_STARGUNFIRE:
rodata = node->rodata;
PROMOTE(rodata->stargunfire.vertices);
rodata->stargunfire.baseaddr = (void *)fileramaddr;
break;
default:
break;
}
if (node->child) {
node = node->child;
} else {
while (node) {
if (node->next) {
node = node->next;
break;
}
node = node->parent;
}
}
}
}
/**
* Convert a model file's file-relative offsets to global pointers,
* and sort the part numbers list so they can be looked up using bisection.
*
* Offsets in model files are based from virtual memory address 0x0f000000.
* This vma address is specified as an argument to the function.
*/
void modelPromoteOffsetsToPointers(struct modeldef *modeldef, u32 vma, uintptr_t fileramaddr)
{
s32 diff = fileramaddr - vma;
s32 i;
s16 *partnums;
PROMOTE(modeldef->rootnode);
PROMOTE(modeldef->parts);
PROMOTE(modeldef->texconfigs);
for (i = 0; i < modeldef->numparts; i++) {
PROMOTE(modeldef->parts[i]);
}
modelPromoteNodeOffsetsToPointers(modeldef->rootnode, vma, fileramaddr);
// Sort parts by part number so they can be bisected during lookup
partnums = (s16 *)&modeldef->parts[modeldef->numparts];
if (modeldef->numparts) {
struct modelnode *tmpnode;
s16 tmpnum;
bool changed;
do {
changed = false;
for (i = 0; i < modeldef->numparts - 1; i++) {
if (partnums[i] > partnums[i + 1]) {
tmpnum = partnums[i];
partnums[i] = partnums[i + 1];
partnums[i + 1] = tmpnum;
tmpnode = modeldef->parts[i];
modeldef->parts[i] = modeldef->parts[i + 1];
modeldef->parts[i + 1] = tmpnode;
changed = true;
}
}
} while (changed == true);
}
}
s32 modelCalculateRwDataIndexes(struct modelnode *basenode)
{
u16 len = 0;
struct modelnode *node = basenode;
union modelrodata *rodata;
while (node) {
u32 type = node->type & 0xff;
switch (type) {
case MODELNODETYPE_CHRINFO:
rodata = node->rodata;
rodata->chrinfo.rwdataindex = len;
len += sizeof(struct modelrwdata_chrinfo) / 4;
break;
case MODELNODETYPE_DISTANCE:
rodata = node->rodata;
rodata->distance.rwdataindex = len;
len += sizeof(struct modelrwdata_distance) / 4;
node->child = rodata->distance.target;
break;
case MODELNODETYPE_TOGGLE:
rodata = node->rodata;
rodata->toggle.rwdataindex = len;
len += sizeof(struct modelrwdata_toggle) / 4;
node->child = rodata->toggle.target;
break;
case MODELNODETYPE_HEADSPOT:
rodata = node->rodata;
rodata->headspot.rwdataindex = len;
len += sizeof(struct modelrwdata_headspot) / 4;
node->child = NULL;
break;
case MODELNODETYPE_REORDER:
rodata = node->rodata;
rodata->reorder.rwdataindex = len;
len += sizeof(struct modelrwdata_reorder) / 4;
modelApplyReorderRelationsByArg(node, false);
break;
case MODELNODETYPE_0B:
rodata = node->rodata;
rodata->type0b.rwdataindex = len;
len += sizeof(struct modelrwdata_0b) / 4;
break;
case MODELNODETYPE_CHRGUNFIRE:
rodata = node->rodata;
rodata->chrgunfire.rwdataindex = len;
len += sizeof(struct modelrwdata_chrgunfire) / 4;
break;
case MODELNODETYPE_DL:
rodata = node->rodata;
rodata->dl.rwdataindex = len;
len += sizeof(struct modelrwdata_dl) / 4;
break;
default:
break;
}
if (node->child) {
node = node->child;
} else {
while (node) {
if (node == basenode->parent) {
node = NULL;
break;
}
if (node->next) {
node = node->next;
break;
}
node = node->parent;
}
}
}
return len;
}
void modelAllocateRwData(struct modeldef *modeldef)
{
modeldef->rwdatalen = modelCalculateRwDataIndexes(modeldef->rootnode);
}
void modelInitRwData(struct model *model, struct modelnode *startnode)
{
struct modelnode *node = startnode;
union modelrodata *rodata;
union modelrwdata *rwdata;
while (node) {
u32 type = node->type & 0xff;
switch (type) {
case MODELNODETYPE_CHRINFO:
rwdata = modelGetNodeRwData(model, node);
rwdata->chrinfo.unk00 = 0;
rwdata->chrinfo.ground = 0;
rwdata->chrinfo.pos.x = 0;
rwdata->chrinfo.pos.y = 0;
rwdata->chrinfo.pos.z = 0;
rwdata->chrinfo.yrot = 0;
rwdata->chrinfo.unk18 = 0;
rwdata->chrinfo.unk1c = 0;
rwdata->chrinfo.unk01 = 0;
rwdata->chrinfo.unk34.x = 0;
rwdata->chrinfo.unk34.y = 0;
rwdata->chrinfo.unk34.z = 0;
rwdata->chrinfo.unk30 = 0;
rwdata->chrinfo.unk24.x = 0;
rwdata->chrinfo.unk24.y = 0;
rwdata->chrinfo.unk24.z = 0;
rwdata->chrinfo.unk20 = 0;
rwdata->chrinfo.unk02 = 0;
rwdata->chrinfo.unk4c.x = 0;
rwdata->chrinfo.unk4c.y = 0;
rwdata->chrinfo.unk4c.z = 0;
rwdata->chrinfo.unk40.x = 0;
rwdata->chrinfo.unk40.y = 0;
rwdata->chrinfo.unk40.z = 0;
rwdata->chrinfo.unk5c = 0;
break;
case MODELNODETYPE_DISTANCE:
rodata = node->rodata;
rwdata = modelGetNodeRwData(model, node);
rwdata->distance.visible = false;
node->child = rodata->distance.target;
break;
case MODELNODETYPE_TOGGLE:
rodata = node->rodata;
rwdata = modelGetNodeRwData(model, node);
rwdata->toggle.visible = true;
node->child = rodata->toggle.target;
break;
case MODELNODETYPE_HEADSPOT:
rwdata = modelGetNodeRwData(model, node);
rwdata->headspot.headmodeldef = NULL;
rwdata->headspot.rwdatas = NULL;
break;
case MODELNODETYPE_REORDER:
rwdata = modelGetNodeRwData(model, node);
rwdata->reorder.reverse = false;
modelApplyReorderRelations(model, node);
break;
case MODELNODETYPE_0B:
rwdata = modelGetNodeRwData(model, node);
rwdata->type0b.unk00 = 0;
break;
case MODELNODETYPE_CHRGUNFIRE:
rwdata = modelGetNodeRwData(model, node);
rwdata->chrgunfire.visible = false;
break;
case MODELNODETYPE_DL:
rodata = node->rodata;
rwdata = modelGetNodeRwData(model, node);
rwdata->dl.vertices = rodata->dl.vertices;
rwdata->dl.gdl = rodata->dl.opagdl;
rwdata->dl.colours = (void *) ALIGN8((uintptr_t)(rodata->dl.vertices + rodata->dl.numvertices));
if (rodata->dl.numvertices);
if (rodata->dl.numvertices);
if (rodata->dl.numvertices);
break;
default:
break;
}
if (node->child) {
node = node->child;
} else {
while (node) {
if (node == startnode->parent) {
node = NULL;
break;
}
if (node->next) {
node = node->next;
break;
}
node = node->parent;
}
}
}
}
void modelInit(struct model *model, struct modeldef *modeldef, u32 *rwdatas, bool resetanim)
{
struct modelnode *node;
model->unk00 = 0;
model->definition = modeldef;
model->rwdatas = rwdatas;
model->rwdatalen = -1;
model->scale = 1;
model->attachedtomodel = NULL;
model->attachedtonode = NULL;
node = modeldef->rootnode;
while (node) {
u32 type = node->type & 0xff;
if (type == MODELNODETYPE_HEADSPOT) {
model->unk00 |= 1;
}
if (node->child) {
node = node->child;
} else {
while (node) {
if (node == modeldef->rootnode->parent) {
node = NULL;
break;
}
if (node->next) {
node = node->next;
break;
}
node = node->parent;
}
}
}
if (rwdatas != NULL) {
modelInitRwData(model, modeldef->rootnode);
}
if (resetanim) {
model->anim = NULL;
}
}
void animInit(struct anim *anim)
{
anim->animnum = 0;
anim->animnum2 = 0;
anim->looping = 0;
anim->flipfunc = NULL;
anim->unk6c = 0;
anim->unk70 = NULL;
anim->average = 0;
anim->frac = 0;
anim->timespeed = 0;
anim->frac2 = 0;
anim->timespeed2 = 0;
anim->fracmerge = 0;
anim->timemerge = 0;
anim->timeplay = 0;
anim->endframe = -1;
anim->endframe2 = -1;
anim->speed = 1;
anim->speed2 = 1;
anim->playspeed = 1;
anim->animscale = 1;
}
void modelAttachHead(struct model *bodymode, struct modeldef *bodymodeldef, struct modelnode *headspotnode, struct modeldef *headmodeldef)
{
struct modelrwdata_headspot *rwdata = modelGetNodeRwData(bodymode, headspotnode);
struct modelnode *node;
rwdata->headmodeldef = headmodeldef;
rwdata->rwdatas = &bodymode->rwdatas[bodymodeldef->rwdatalen];
headspotnode->child = headmodeldef->rootnode;
node = headspotnode->child;
while (node) {
node->parent = headspotnode;
node = node->next;
}
bodymodeldef->rwdatalen += modelCalculateRwDataIndexes(headspotnode->child);
}
/**
* This function can be called repeatedly to iterate a model's display lists.
*
* On the first call, the value passed as nodeptr should point to a NULL value.
* Each time the function is called, it will update *gdlptr to point to the next
* display list, and will update *nodeptr to point to the current node. On
* subsequent calls, the same values should be passed as nodeptr and gdlptr so
* the function can continue correctly.
*
* Note that some node types support multiple display lists, so the function
* may return the same node while it iterates the display lists for that node.
*/
void modelIterateDisplayLists(struct modeldef *modeldef, struct modelnode **nodeptr, Gfx **gdlptr)
{
struct modelnode *node = *nodeptr;
union modelrodata *rodata;
Gfx *gdl = NULL;
if (node == NULL) {
node = modeldef->rootnode;
}
while (node) {
u32 type = node->type & 0xff;
switch (type) {
case MODELNODETYPE_GUNDL:
rodata = node->rodata;
if (node != *nodeptr) {
gdl = rodata->gundl.opagdl;
} else if (rodata->gundl.xlugdl != *gdlptr) {
gdl = rodata->gundl.xlugdl;
}
break;
case MODELNODETYPE_DL:
rodata = node->rodata;
if (node != *nodeptr) {
gdl = rodata->dl.opagdl;
} else if (rodata->dl.xlugdl != *gdlptr) {
gdl = rodata->dl.xlugdl;
}
break;
case MODELNODETYPE_STARGUNFIRE:
rodata = node->rodata;
if (node != *nodeptr) {
gdl = rodata->stargunfire.gdl;
}
break;
case MODELNODETYPE_DISTANCE:
rodata = node->rodata;
node->child = rodata->distance.target;
break;
case MODELNODETYPE_TOGGLE:
rodata = node->rodata;
node->child = rodata->toggle.target;
break;
case MODELNODETYPE_REORDER:
modelApplyReorderRelationsByArg(node, true);
break;
}
if (gdl) {
break;
}
if (node->child) {
node = node->child;
} else {
while (node) {
if (node->next) {
node = node->next;
break;
}
node = node->parent;
}
}
}
*gdlptr = gdl;
*nodeptr = node;
}
void modelNodeReplaceGdl(struct modeldef *modeldef, struct modelnode *node, Gfx *find, Gfx *replacement)
{
union modelrodata *rodata;
u32 type = node->type & 0xff;
switch (type) {
case MODELNODETYPE_GUNDL:
rodata = node->rodata;
if (rodata->gundl.opagdl == find) {
rodata->gundl.opagdl = replacement;
return;
}
if (rodata->gundl.xlugdl == find) {
rodata->gundl.xlugdl = replacement;
return;
}
break;
case MODELNODETYPE_DL:
rodata = node->rodata;
if (rodata->dl.opagdl == find) {
rodata->dl.opagdl = replacement;
return;
}
if (rodata->dl.xlugdl == find) {
rodata->dl.xlugdl = replacement;
return;
}
break;
case MODELNODETYPE_STARGUNFIRE:
rodata = node->rodata;
if (rodata->stargunfire.gdl == find) {
rodata->stargunfire.gdl = replacement;
return;
}
break;
}
}