Document SPLEmitter_EmitParticles

This commit is contained in:
Fexty12573 2024-08-02 14:47:23 +02:00
parent 98ca0c745a
commit 616005990d
10 changed files with 361 additions and 338 deletions

View File

@ -39,17 +39,17 @@ typedef struct SPLEmitter {
UnkSPLStruct7 state;
VecFx32 position;
VecFx32 velocity;
VecFx32 unk_B0;
VecFx32 particleInitVelocity;
u16 age;
fx16 unk_BE;
fx16 emissionCountFractional; // fractional part of the number of particles to emit (doesn't seem to be used)
VecFx16 axis;
u16 unk_C6;
fx32 unk_C8;
fx32 unk_CC;
fx32 emissionCount;
fx32 radius;
fx32 length;
fx32 unk_D4;
fx32 unk_D8;
fx32 unk_DC;
fx32 initVelPositionAmplifier; // amplifies the initial velocity of the particles based on their position
fx32 initVelAxisAmplifier; // amplifies the initial velocity of the particles based on the emitter's axis
fx32 baseScale; // base scale of the particles
u16 particleLifeTime;
GXRgb color;
fx32 collisionPlaneHeight;
@ -59,12 +59,12 @@ typedef struct SPLEmitter {
fx16 unk_EE;
struct {
u32 emissionInterval : 8; // number of frames between particle emissions
u32 unk_01_0 : 8;
u32 baseAlpha : 8;
u32 updateCycle : 3; // 0 = every frame, 1 = cycle A, 2 = cycle B, cycles A and B alternate
u32 unk_02_3 : 13;
} misc;
VecFx16 unk_F4;
VecFx16 unk_FA;
VecFx16 crossAxis1;
VecFx16 crossAxis2;
SPLEmitterUpdateCallback updateCallback;
void * unk_104;
union {

View File

@ -36,13 +36,13 @@ void sub_020A1A94(SPLParticle *ptcl, SPLResource *res, int lifeRate); // spl_tex
void sub_020A1A48(SPLParticle *ptcl, SPLResource *res, int lifeRate); // spl_chld_scl_out
void sub_020A19F0(SPLParticle *ptcl, SPLResource *res, int lifeRate); // spl_chld_alp_out
void sub_020A08DC(SPLEmitter *emtr, SPLList *list);
void SPLEmitter_EmitParticles(SPLEmitter *emtr, SPLParticleList *list);
void sub_020A05BC(SPLParticle *ptcl, SPLEmitter *emtr, SPLList *list);
void SPLEmitter_Update(SPLManager *mgr, SPLEmitter *emtr);
void SPLManager_DoDraw(SPLManager *mgr);
void SPLEmitter_Init(SPLEmitter *emtr, SPLResource *res, const VecFx32 *pos);
void SPLEmitter_Emit(SPLEmitter *emtr, SPLList *list);
void SPLEmitter_Emit(SPLEmitter *emtr, SPLParticleList *list);
static inline void SPLParticleList_PushFront(SPLParticleList *list, SPLParticle *ptcl)
{

View File

@ -8,7 +8,7 @@
typedef struct SPLParticle {
struct SPLParticle *next;
struct SPLParticle *prev;
VecFx32 position;
VecFx32 position; // position of the particle, relative to the emitter
VecFx32 velocity;
u16 rotation;
s16 angularVelocity;
@ -22,7 +22,7 @@ typedef struct SPLParticle {
u16 lifeTimeFactor;
struct {
u16 unk_00 : 8;
u16 texture : 8; // Index of the current texture in the resource
// A value between 0 and 255 that is added to the life rate of the particle.
// This is used only for looping particles, so particles spawned at the same time
@ -30,13 +30,13 @@ typedef struct SPLParticle {
u16 lifeRateOffset : 8;
} misc;
struct {
u16 unk_00_0 : 5;
u16 unk_00_5 : 5;
u16 baseAlpha : 5;
u16 animAlpha : 5;
u16 currentPolygonID : 6;
} unk_2E;
fx32 unk_30;
} visibility;
fx32 baseScale;
fx16 unk_34;
GXRgb unk_36;
GXRgb color;
VecFx32 emitterPos;
} SPLParticle; // size=0x44

View File

@ -5,6 +5,27 @@
#include "struct_defs/struct_020147B8.h"
#define SPL_TEX_ANIM_MAX_FRAMES 8
enum SPLCircleAxis {
SPL_CIRCLE_AXIS_Z = 0,
SPL_CIRCLE_AXIS_Y = 1,
SPL_CIRCLE_AXIS_X = 2,
SPL_CIRCLE_AXIS_EMITTER = 3, // The emitter's axis
};
enum SPLEmissionType {
SPL_EMISSION_TYPE_POINT = 0, // A single point
SPL_EMISSION_TYPE_SPHERE_SURFACE, // Any point on the surface of a sphere
SPL_EMISSION_TYPE_CIRCLE_BORDER, // Any point on a circle around an axis (specified by flags)
SPL_EMISSION_TYPE_CIRCLE_BORDER_UNIFORM, // Uniform distribution of points on a circle around an axis (specified by flags)
SPL_EMISSION_TYPE_SPHERE, // Any point inside a sphere
SPL_EMISSION_TYPE_CIRCLE, // Any point inside a circle around an axis (specified by flags)
SPL_EMISSION_TYPE_CYLINDER_SURFACE, // Any point on the surface of a cylinder
SPL_EMISSION_TYPE_CYLINDER, // Any point inside a cylinder
SPL_EMISSION_TYPE_HEMISPHERE_SURFACE, // Any point on the surface of a hemisphere (direction specified by flags)
SPL_EMISSION_TYPE_HEMISPHERE, // Any point inside a hemisphere (direction specified by flags)
};
typedef struct SPLArcHdr {
u32 magic;
@ -21,14 +42,14 @@ typedef struct SPLArcHdr {
typedef union SPLResourceFlags {
u32 all;
struct {
u32 unk_04_0 : 4;
u32 emissionType : 4; // Maps to SPLEmissionType
u32 drawType : 2;
u32 unk_04_6 : 2;
u32 circleAxis : 2; // Maps to SPLCircleAxis
u32 hasScaleAnim : 1;
u32 hasColorAnim : 1;
u32 hasAlphaAnim : 1;
u32 hasTexAnim : 1;
u32 unk_05_4 : 1;
u32 hasRotation : 1;
u32 unk_05_5 : 1;
// Whether the emitter manages itself or not.
// If set, the emitter will automatically terminate when it reaches the end of its life
@ -38,7 +59,7 @@ typedef union SPLResourceFlags {
u32 hasChildResource : 1;
u32 unk_06_1 : 2;
u32 unk_06_3 : 1;
u32 unk_06_4 : 1;
u32 randomizeLoopedAnim : 1;
u32 drawChildrenFirst : 1; // If set, child particles will be rendered before parent particles
u32 hideParent : 1; // If set, only child particles will be rendered
u32 unk_06_7 : 1;
@ -90,35 +111,39 @@ typedef union {
typedef struct SPLResourceHeader {
SPLResourceFlags flags;
VecFx32 emitterBasePos;
fx32 unk_10;
fx32 emissionCount; // Number of particles to emit per emission interval
fx32 unk_14;
fx32 unk_18;
VecFx16 unk_1C;
GXRgb unk_22;
GXRgb color;
fx32 unk_24;
fx32 unk_28;
fx32 unk_2C;
fx16 unk_30;
u16 startDelay; // Delay, in frames, before the emitter starts emitting particles
s16 unk_34;
s16 unk_36;
s16 minRotation;
s16 maxRotation;
u16 unk_38;
u16 reserved_3A;
u16 emitterLifeTime;
u16 particleLifeTime;
// All of these values are mapped to the range [0, 1]
// They are used to attenuate the particle's properties at initialization,
// acting as a sort of randomization factor which scales down the initial values
struct {
u32 unk_00_0 : 8;
u32 unk_01_0 : 8;
u32 unk_02_0 : 8;
u32 reserved_03_0 : 8;
} unk_44;
u32 baseScale : 8; // Damping factor for the base scale of the particles (0 = no damping)
u32 lifeTime : 8;
u32 initVel : 8; // Attenuation factor for the initial velocity of the particles (0 = no attenuation)
u32 : 8;
} randomAttenuation;
struct {
u32 unk_00_0 : 8;
u32 unk_01_0 : 8;
u32 airResistance : 8;
u32 textureIndex : 8;
u32 unk_04_0 : 8;
u32 loopFrames : 8;
u32 unk_05_0 : 16;
u32 unk_07_0 : 2;
u32 unk_07_2 : 2;
@ -150,8 +175,8 @@ typedef struct SPLScaleAnim {
} SPLScaleAnim; // size=0xc
typedef struct SPLColorAnim {
GXRgb unk_00;
GXRgb unk_02;
GXRgb startColor;
GXRgb endColor;
UnkSPLUnion4 unk_04;
struct {
u16 unk_00_0 : 1;
@ -182,14 +207,14 @@ typedef struct SPLAlphaAnim {
} SPLAlphaAnim; // size=0x8
typedef struct SPLTexAnim {
u8 unk_00[8];
u8 textures[SPL_TEX_ANIM_MAX_FRAMES];
struct {
u32 unk_00_0 : 8;
u32 frameCount : 8;
u32 unk_01_0 : 8;
u32 unk_02_0 : 1;
u32 randomizeInit : 1; // Randomize the initial texture frame
u32 unk_02_1 : 1;
u32 reserved_02_2 : 14;
} unk_08;
} param;
} SPLTexAnim;
typedef struct SPLChildResource {

View File

@ -68,27 +68,27 @@ void SPLEmitter_Init(SPLEmitter *emtr, SPLResource *res, const VecFx32 *pos)
emtr->position.y = pos->y + emtr->resource->header->emitterBasePos.y;
emtr->position.z = pos->z + emtr->resource->header->emitterBasePos.z;
emtr->unk_B0.x = 0;
emtr->unk_B0.y = 0;
emtr->unk_B0.z = 0;
emtr->particleInitVelocity.x = 0;
emtr->particleInitVelocity.y = 0;
emtr->particleInitVelocity.z = 0;
emtr->velocity.x = emtr->velocity.y = emtr->velocity.z = 0;
emtr->age = 0;
emtr->unk_BE = 0;
emtr->emissionCountFractional = 0;
emtr->axis = emtr->resource->header->unk_1C;
emtr->unk_C6 = emtr->resource->header->unk_38;
emtr->unk_C8 = emtr->resource->header->unk_10;
emtr->unk_CC = emtr->resource->header->unk_14;
emtr->emissionCount = emtr->resource->header->emissionCount;
emtr->radius = emtr->resource->header->unk_14;
emtr->length = emtr->resource->header->unk_18;
emtr->unk_D4 = emtr->resource->header->unk_24;
emtr->unk_D8 = emtr->resource->header->unk_28;
emtr->unk_DC = emtr->resource->header->unk_2C;
emtr->initVelPositionAmplifier = emtr->resource->header->unk_24;
emtr->initVelAxisAmplifier = emtr->resource->header->unk_28;
emtr->baseScale = emtr->resource->header->unk_2C;
emtr->particleLifeTime = emtr->resource->header->particleLifeTime;
emtr->color = GX_RGB(31, 31, 31);
emtr->misc.emissionInterval = emtr->resource->header->misc.unk_00_0;
emtr->misc.unk_01_0 = emtr->resource->header->misc.unk_01_0;
emtr->misc.baseAlpha = emtr->resource->header->misc.unk_01_0;
emtr->misc.updateCycle = 0;
emtr->misc.unk_02_3 = 0;
emtr->collisionPlaneHeight = FX32_MIN;
@ -154,7 +154,7 @@ void SPLEmitter_Update(SPLManager *mgr, SPLEmitter *emtr)
if (header->emitterLifeTime == 0 || emtr->age < header->emitterLifeTime) {
if (emtr->age % emtr->misc.emissionInterval == 0) {
if (!emtr->state.terminate && !emtr->state.emissionPaused && emtr->state.started) {
sub_020A08DC(emtr, (SPLList *)(&mgr->inactiveParticles));
SPLEmitter_EmitParticles(emtr, &mgr->inactiveParticles);
}
}
}
@ -174,9 +174,9 @@ void SPLEmitter_Update(SPLManager *mgr, SPLEmitter *emtr)
animFuncs[animCount++].loop = res->alphaAnim->unk_02.unk_01_0;
}
if (resFlags.hasTexAnim && !res->texAnim->unk_08.unk_02_0) { // TexAnim
if (resFlags.hasTexAnim && !res->texAnim->param.randomizeInit) { // TexAnim
animFuncs[animCount].func = sub_020A1A94;
animFuncs[animCount++].loop = res->texAnim->unk_08.unk_02_1;
animFuncs[animCount++].loop = res->texAnim->param.unk_02_1;
}
for (ptcl = emtr->particles.first; ptcl != NULL; ptcl = next) {
@ -231,9 +231,9 @@ void SPLEmitter_Update(SPLManager *mgr, SPLEmitter *emtr)
}
if (emtr->resource->header->flags.hasFixedPolygonID) {
ptcl->unk_2E.currentPolygonID = mgr->polygonID.fix;
ptcl->visibility.currentPolygonID = mgr->polygonID.fix;
} else {
ptcl->unk_2E.currentPolygonID = mgr->polygonID.current;
ptcl->visibility.currentPolygonID = mgr->polygonID.current;
mgr->polygonID.current += 1;
if (mgr->polygonID.current > mgr->polygonID.max) {
@ -300,9 +300,9 @@ void SPLEmitter_Update(SPLManager *mgr, SPLEmitter *emtr)
ptcl->position.z += ptcl->velocity.z + emtr->velocity.z;
if (emtr->resource->header->flags.childHasFixedPolygonID) {
ptcl->unk_2E.currentPolygonID = mgr->polygonID.fix;
ptcl->visibility.currentPolygonID = mgr->polygonID.fix;
} else {
ptcl->unk_2E.currentPolygonID = mgr->polygonID.current;
ptcl->visibility.currentPolygonID = mgr->polygonID.current;
mgr->polygonID.current += 1;
if (mgr->polygonID.current > mgr->polygonID.max) {
@ -355,7 +355,7 @@ static void SPLManager_DrawParticles(SPLManager *mgr)
SetTexFunc setTexFunc = header->flags.hasTexAnim ? SPLUtil_SetTexture : SPLUtil_SetTexture_Stub;
for (SPLParticle *ptcl = emtr->particles.first; ptcl != NULL; ptcl = ptcl->next) {
setTexFunc(mgr->textures + ptcl->misc.unk_00);
setTexFunc(mgr->textures + ptcl->misc.texture);
drawFunc(mgr, ptcl);
}
}
@ -414,16 +414,16 @@ void SPLManager_DoDraw(SPLManager *mgr)
}
}
void SPLEmitter_Emit(SPLEmitter *emtr, SPLList *list)
void SPLEmitter_Emit(SPLEmitter *emtr, SPLParticleList *list)
{
sub_020A08DC(emtr, list);
SPLEmitter_EmitParticles(emtr, list);
}
void SPLUtil_SetCylinderEmitterDir(SPLEmitter *emtr, VecFx32 *p1, VecFx32 *p2)
{
VecFx32 vex;
if (emtr->resource->header->flags.unk_04_0 == 6 || emtr->resource->header->flags.unk_04_0 == 7) {
if (emtr->resource->header->flags.emissionType == 6 || emtr->resource->header->flags.emissionType == 7) {
emtr->position.x = (p2->x + p1->x) / 2;
emtr->position.y = (p2->y + p1->y) / 2;
emtr->position.z = (p2->z + p1->z) / 2;

View File

@ -426,7 +426,7 @@ void SPLManager_DeleteAllEmitters(SPLManager *mgr)
void SPL_Emit(SPLManager *mgr, SPLEmitter *emtr)
{
SPLEmitter_Emit(emtr, (SPLList *)&mgr->inactiveParticles);
SPLEmitter_Emit(emtr, &mgr->inactiveParticles);
}
void SPL_EmitAt(SPLManager *mgr, SPLEmitter *emtr, VecFx32 *pos)
@ -434,5 +434,5 @@ void SPL_EmitAt(SPLManager *mgr, SPLEmitter *emtr, VecFx32 *pos)
emtr->position.x = pos->x + emtr->resource->header->emitterBasePos.x;
emtr->position.y = pos->y + emtr->resource->header->emitterBasePos.y;
emtr->position.z = pos->z + emtr->resource->header->emitterBasePos.z;
SPLEmitter_Emit(emtr, (SPLList *)&mgr->inactiveParticles);
SPLEmitter_Emit(emtr, &mgr->inactiveParticles);
}

View File

@ -135,18 +135,18 @@ void sub_0209FF0C(SPLManager *mgr, SPLParticle *ptcl)
aspect = mgr->renderState.emitter->resource->header->unk_30;
animScale = ptcl->unk_34;
ptclCol = ptcl->unk_36;
ptclCol = ptcl->color;
emtrCol = emtr->color;
scaleAnimDirect = resBase->misc.unk_07_4;
fx32 alpha = ptcl->unk_2E.unk_00_0 * (ptcl->unk_2E.unk_00_5 + 1) >> 5;
fx32 alpha = ptcl->visibility.baseAlpha * (ptcl->visibility.animAlpha + 1) >> 5;
G3_PolygonAttr(
GX_LIGHTMASK_NONE,
GX_POLYGONMODE_MODULATE,
GX_CULL_NONE,
ptcl->unk_2E.currentPolygonID,
ptcl->visibility.currentPolygonID,
alpha,
mgr->unk_3C);
@ -156,7 +156,7 @@ void sub_0209FF0C(SPLManager *mgr, SPLParticle *ptcl)
return;
}
sclY = ptcl->unk_30;
sclY = ptcl->baseScale;
sclX = FX_MUL(sclY, aspect);
if (scaleAnimDirect == 0) {
@ -240,13 +240,13 @@ void sub_0209FAB8(SPLManager *mgr, SPLParticle *ptcl)
cmr = mgr->renderState.viewMatrix;
aspect = mgr->renderState.emitter->resource->header->unk_30;
fx32 alpha = (fx32)(ptcl->unk_2E.unk_00_0 * (ptcl->unk_2E.unk_00_5 + 1)) >> 5;
fx32 alpha = (fx32)(ptcl->visibility.baseAlpha * (ptcl->visibility.animAlpha + 1)) >> 5;
G3_PolygonAttr(
GX_LIGHTMASK_NONE,
GX_POLYGONMODE_MODULATE,
GX_CULL_NONE,
ptcl->unk_2E.currentPolygonID,
ptcl->visibility.currentPolygonID,
alpha,
mgr->unk_3C);
@ -256,7 +256,7 @@ void sub_0209FAB8(SPLManager *mgr, SPLParticle *ptcl)
return;
}
sclY = ptcl->unk_30;
sclY = ptcl->baseScale;
sclX = FX_MUL(sclY, aspect);
switch (mgr->renderState.emitter->resource->header->misc.unk_07_4) {
@ -325,7 +325,7 @@ void sub_0209FAB8(SPLManager *mgr, SPLParticle *ptcl)
G3_MultMtx43(&load);
}
GXRgb colA = ptcl->unk_36;
GXRgb colA = ptcl->color;
GXRgb colB = mgr->renderState.emitter->color;
G3_Color(GX_RGB(
GX_RGB_R_(colA) * GX_RGB_R_(colB) >> 5,
@ -351,13 +351,13 @@ void sub_0209F3D0(SPLManager *mgr, SPLParticle *ptcl)
cmr = mgr->renderState.viewMatrix;
aspect = mgr->renderState.emitter->resource->header->unk_30;
fx32 alpha = (fx32)(ptcl->unk_2E.unk_00_0 * (ptcl->unk_2E.unk_00_5 + 1)) >> 5;
fx32 alpha = (fx32)(ptcl->visibility.baseAlpha * (ptcl->visibility.animAlpha + 1)) >> 5;
G3_PolygonAttr(
GX_LIGHTMASK_NONE,
GX_POLYGONMODE_MODULATE,
GX_CULL_NONE,
ptcl->unk_2E.currentPolygonID,
ptcl->visibility.currentPolygonID,
alpha,
mgr->unk_3C);
@ -367,7 +367,7 @@ void sub_0209F3D0(SPLManager *mgr, SPLParticle *ptcl)
return;
}
sclY = ptcl->unk_30;
sclY = ptcl->baseScale;
sclX = FX_MUL(sclY, aspect);
switch (mgr->renderState.emitter->resource->header->misc.unk_07_4) {
@ -480,7 +480,7 @@ void sub_0209F3D0(SPLManager *mgr, SPLParticle *ptcl)
G3_MultMtx43(&load);
}
GXRgb colA = ptcl->unk_36;
GXRgb colA = ptcl->color;
GXRgb colB = mgr->renderState.emitter->color;
G3_Color(GX_RGB(
GX_RGB_R_(colA) * GX_RGB_R_(colB) >> 5,
@ -506,13 +506,13 @@ void sub_0209ECF0(SPLManager *mgr, SPLParticle *ptcl)
cmr = mgr->renderState.viewMatrix;
aspect = mgr->renderState.emitter->resource->header->unk_30;
fx32 alpha = (fx32)(ptcl->unk_2E.unk_00_0 * (ptcl->unk_2E.unk_00_5 + 1)) >> 5;
fx32 alpha = (fx32)(ptcl->visibility.baseAlpha * (ptcl->visibility.animAlpha + 1)) >> 5;
G3_PolygonAttr(
GX_LIGHTMASK_NONE,
GX_POLYGONMODE_MODULATE,
GX_CULL_NONE,
ptcl->unk_2E.currentPolygonID,
ptcl->visibility.currentPolygonID,
alpha,
mgr->unk_3C);
@ -522,7 +522,7 @@ void sub_0209ECF0(SPLManager *mgr, SPLParticle *ptcl)
return;
}
sclY = ptcl->unk_30;
sclY = ptcl->baseScale;
sclX = FX_MUL(sclY, aspect);
switch (mgr->renderState.emitter->resource->header->misc.unk_07_4) {
@ -635,7 +635,7 @@ void sub_0209ECF0(SPLManager *mgr, SPLParticle *ptcl)
G3_MultMtx43(&load);
}
GXRgb colA = ptcl->unk_36;
GXRgb colA = ptcl->color;
GXRgb colB = mgr->renderState.emitter->color;
G3_Color(GX_RGB(
GX_RGB_R_(colA) * GX_RGB_R_(colB) >> 5,
@ -656,13 +656,13 @@ void sub_0209E9A0(SPLManager *mgr, SPLParticle *ptcl)
fx32 alpha;
SPLResourceHeader *resBase;
alpha = (fx32)(ptcl->unk_2E.unk_00_0 * (ptcl->unk_2E.unk_00_5 + 1)) >> 5;
alpha = (fx32)(ptcl->visibility.baseAlpha * (ptcl->visibility.animAlpha + 1)) >> 5;
G3_PolygonAttr(
GX_LIGHTMASK_NONE,
GX_POLYGONMODE_MODULATE,
GX_CULL_NONE,
ptcl->unk_2E.currentPolygonID,
ptcl->visibility.currentPolygonID,
alpha,
mgr->unk_3C);
@ -674,7 +674,7 @@ void sub_0209E9A0(SPLManager *mgr, SPLParticle *ptcl)
Unk_02100DA8[mgr->renderState.emitter->resource->header->flags.unk_06_1](FX_SinIdx(ptcl->rotation), FX_CosIdx(ptcl->rotation), &rotMtx);
sclY = ptcl->unk_30;
sclY = ptcl->baseScale;
resBase = mgr->renderState.emitter->resource->header;
sclX = FX_MUL(sclY, resBase->unk_30);
@ -717,7 +717,7 @@ void sub_0209E9A0(SPLManager *mgr, SPLParticle *ptcl)
G3_MultMtx43(&load);
}
GXRgb colA = ptcl->unk_36;
GXRgb colA = ptcl->color;
GXRgb colB = mgr->renderState.emitter->color;
G3_Color(GX_RGB(
GX_RGB_R_(colA) * GX_RGB_R_(colB) >> 5,
@ -739,13 +739,13 @@ void sub_0209E650(SPLManager *mgr, SPLParticle *ptcl)
fx32 alpha;
SPLResourceHeader *resBase;
alpha = ptcl->unk_2E.unk_00_0 * (ptcl->unk_2E.unk_00_5 + 1) >> 5;
alpha = ptcl->visibility.baseAlpha * (ptcl->visibility.animAlpha + 1) >> 5;
G3_PolygonAttr(
GX_LIGHTMASK_NONE,
GX_POLYGONMODE_MODULATE,
GX_CULL_NONE,
ptcl->unk_2E.currentPolygonID,
ptcl->visibility.currentPolygonID,
alpha,
mgr->unk_3C);
@ -757,7 +757,7 @@ void sub_0209E650(SPLManager *mgr, SPLParticle *ptcl)
Unk_02100DA8[mgr->renderState.emitter->resource->childResource->flags.unk_03_1](FX_SinIdx(ptcl->rotation), FX_CosIdx(ptcl->rotation), &rotMtx);
sclY = ptcl->unk_30;
sclY = ptcl->baseScale;
resBase = mgr->renderState.emitter->resource->header;
sclX = FX_MUL(sclY, resBase->unk_30);
@ -798,7 +798,7 @@ void sub_0209E650(SPLManager *mgr, SPLParticle *ptcl)
G3_MultMtx43(&load);
}
GXRgb colA = ptcl->unk_36;
GXRgb colA = ptcl->color;
GXRgb colB = mgr->renderState.emitter->color;
G3_Color(GX_RGB(
GX_RGB_R_(colA) * GX_RGB_R_(colB) >> 5,
@ -821,13 +821,13 @@ void sub_0209E1D4(SPLManager *mgr, SPLParticle *ptcl)
SPLEmitter *emtr;
GXRgb colA, colB;
alpha = ptcl->unk_2E.unk_00_0 * (ptcl->unk_2E.unk_00_5 + 1) >> 5;
alpha = ptcl->visibility.baseAlpha * (ptcl->visibility.animAlpha + 1) >> 5;
G3_PolygonAttr(
GX_LIGHTMASK_NONE,
GX_POLYGONMODE_MODULATE,
GX_CULL_NONE,
ptcl->unk_2E.currentPolygonID,
ptcl->visibility.currentPolygonID,
alpha,
mgr->unk_3C);
@ -876,7 +876,7 @@ void sub_0209E1D4(SPLManager *mgr, SPLParticle *ptcl)
MTX_Concat43(&rotMat, &mat, &rotMat);
resBase = mgr->renderState.emitter->resource->header;
scaleY = ptcl->unk_30;
scaleY = ptcl->baseScale;
scaleX = FX_MUL(scaleY, resBase->unk_30);
switch (resBase->misc.unk_07_4) {
@ -917,7 +917,7 @@ void sub_0209E1D4(SPLManager *mgr, SPLParticle *ptcl)
G3_MultMtx43(&transform);
}
colA = ptcl->unk_36;
colA = ptcl->color;
colB = mgr->renderState.emitter->color;
G3_Color(GX_RGB(
GX_RGB_R_(colA) * GX_RGB_R_(colB) >> 5,
@ -939,13 +939,13 @@ void sub_0209DD54(SPLManager *mgr, SPLParticle *ptcl)
fx32 dot, scaleX, scaleY, scaleZ, alpha, tmp;
SPLResourceHeader *resBase;
alpha = ptcl->unk_2E.unk_00_0 * (ptcl->unk_2E.unk_00_5 + 1) >> 5;
alpha = ptcl->visibility.baseAlpha * (ptcl->visibility.animAlpha + 1) >> 5;
G3_PolygonAttr(
GX_LIGHTMASK_NONE,
GX_POLYGONMODE_MODULATE,
GX_CULL_NONE,
ptcl->unk_2E.currentPolygonID,
ptcl->visibility.currentPolygonID,
alpha,
mgr->unk_3C);
@ -994,7 +994,7 @@ void sub_0209DD54(SPLManager *mgr, SPLParticle *ptcl)
MTX_Concat43(&rotMtx, &mat, &rotMtx);
resBase = mgr->renderState.emitter->resource->header;
scaleY = ptcl->unk_30;
scaleY = ptcl->baseScale;
scaleX = FX_MUL(scaleY, resBase->unk_30);
switch (resBase->misc.unk_07_4) {
@ -1035,7 +1035,7 @@ void sub_0209DD54(SPLManager *mgr, SPLParticle *ptcl)
G3_MultMtx43(&transform);
}
GXRgb colA = ptcl->unk_36;
GXRgb colA = ptcl->color;
GXRgb colB = mgr->renderState.emitter->color;
G3_Color(GX_RGB(
GX_RGB_R_(colA) * GX_RGB_R_(colB) >> 5,

View File

@ -1,44 +1,45 @@
#include "nitro/types.h"
#include <nitro/fx/fx.h>
#include <nitro/fx/fx_const.h>
#include <nitro/fx/fx_trig.h>
#include "spl_emitter.h"
#include "spl_internal.h"
#include "spl_list.h"
#include "spl_particle.h"
#include "spl_random.h"
#include "spl_resource.h"
#define IS_IN_RANGE(x, min, max) (((x) - (min)) <= ((max) - (min)))
static void SPLEmitter_ComputeOrthogonalAxes(SPLEmitter *emtr);
static void SPLUtil_TiltCoordinates(VecFx32 *ptclPos, VecFx32 *pos, SPLEmitter *emtr);
static VecFx16 sUpVector = { 0, FX16_ONE, 0 };
static void sub_020A1768(SPLEmitter *emtr);
static void sub_020A1608(VecFx32 *ptclPos, VecFx32 *pos, SPLEmitter *emtr);
VecFx16 Unk_02100DB0 = { 0, FX16_ONE, 0 };
static void sub_020A1768(SPLEmitter *emtr)
static void SPLEmitter_ComputeOrthogonalAxes(SPLEmitter *emtr)
{
VecFx16 vec, axis;
vec = Unk_02100DB0;
vec = sUpVector;
switch (emtr->resource->header->flags.unk_04_6) {
case 2:
switch (emtr->resource->header->flags.circleAxis) {
case SPL_CIRCLE_AXIS_X:
axis.x = FX32_ONE;
axis.y = 0;
axis.z = 0;
break;
case 1:
case SPL_CIRCLE_AXIS_Y:
axis.x = 0;
axis.y = FX32_ONE;
axis.z = 0;
break;
case 0:
case SPL_CIRCLE_AXIS_Z:
axis.x = 0;
axis.y = 0;
axis.z = FX32_ONE;
break;
default:
default: // SPL_CIRCLE_AXIS_EMITTER
VEC_Fx16Normalize(&emtr->axis, &axis);
break;
}
@ -50,240 +51,237 @@ static void sub_020A1768(SPLEmitter *emtr)
vec.z = 0;
}
emtr->unk_F4.x = FX_MUL(axis.y, vec.z) - FX_MUL(axis.z, vec.y);
emtr->unk_F4.y = FX_MUL(axis.z, vec.x) - FX_MUL(axis.x, vec.z);
emtr->unk_F4.z = FX_MUL(axis.x, vec.y) - FX_MUL(axis.y, vec.x);
emtr->crossAxis1.x = FX_MUL(axis.y, vec.z) - FX_MUL(axis.z, vec.y);
emtr->crossAxis1.y = FX_MUL(axis.z, vec.x) - FX_MUL(axis.x, vec.z);
emtr->crossAxis1.z = FX_MUL(axis.x, vec.y) - FX_MUL(axis.y, vec.x);
emtr->unk_FA.x = FX_MUL(axis.y, emtr->unk_F4.z) - FX_MUL(axis.z, emtr->unk_F4.y);
emtr->unk_FA.y = FX_MUL(axis.z, emtr->unk_F4.x) - FX_MUL(axis.x, emtr->unk_F4.z);
emtr->unk_FA.z = FX_MUL(axis.x, emtr->unk_F4.y) - FX_MUL(axis.y, emtr->unk_F4.x);
emtr->crossAxis2.x = FX_MUL(axis.y, emtr->crossAxis1.z) - FX_MUL(axis.z, emtr->crossAxis1.y);
emtr->crossAxis2.y = FX_MUL(axis.z, emtr->crossAxis1.x) - FX_MUL(axis.x, emtr->crossAxis1.z);
emtr->crossAxis2.z = FX_MUL(axis.x, emtr->crossAxis1.y) - FX_MUL(axis.y, emtr->crossAxis1.x);
VEC_Fx16Normalize(&emtr->unk_F4, &emtr->unk_F4);
VEC_Fx16Normalize(&emtr->unk_FA, &emtr->unk_FA);
VEC_Fx16Normalize(&emtr->crossAxis1, &emtr->crossAxis1);
VEC_Fx16Normalize(&emtr->crossAxis2, &emtr->crossAxis2);
}
static void sub_020A1608(VecFx32 *ptclPos, VecFx32 *pos, SPLEmitter *emtr)
// Tilt the coordinates of a particle based on the emitter's axis
static void SPLUtil_TiltCoordinates(VecFx32 *ptclPos, VecFx32 *pos, SPLEmitter *emtr)
{
VecFx16 vec;
VEC_Fx16CrossProduct(&emtr->unk_F4, &emtr->unk_FA, &vec);
VEC_Fx16CrossProduct(&emtr->crossAxis1, &emtr->crossAxis2, &vec);
VEC_Fx16Normalize(&vec, &vec);
ptclPos->x = FX_MUL(pos->x, emtr->unk_F4.x) + FX_MUL(pos->y, emtr->unk_FA.x) + FX_MUL(pos->z, vec.x);
ptclPos->y = FX_MUL(pos->x, emtr->unk_F4.y) + FX_MUL(pos->y, emtr->unk_FA.y) + FX_MUL(pos->z, vec.y);
ptclPos->z = FX_MUL(pos->x, emtr->unk_F4.z) + FX_MUL(pos->y, emtr->unk_FA.z) + FX_MUL(pos->z, vec.z);
ptclPos->x = FX_MUL(pos->x, emtr->crossAxis1.x) + FX_MUL(pos->y, emtr->crossAxis2.x) + FX_MUL(pos->z, vec.x);
ptclPos->y = FX_MUL(pos->x, emtr->crossAxis1.y) + FX_MUL(pos->y, emtr->crossAxis2.y) + FX_MUL(pos->z, vec.y);
ptclPos->z = FX_MUL(pos->x, emtr->crossAxis1.z) + FX_MUL(pos->y, emtr->crossAxis2.z) + FX_MUL(pos->z, vec.z);
}
void sub_020A08DC(SPLEmitter *emtr, SPLList *list)
void SPLEmitter_EmitParticles(SPLEmitter *emtr, SPLParticleList *list)
{
SPLResource *res;
SPLResourceHeader *resBase;
int i, curGenNum;
SPLParticle *ptcl;
fx32 magPos;
fx32 magAxis;
SPLResource *res = emtr->resource;
SPLResourceHeader *header = res->header;
res = emtr->resource;
resBase = res->header;
int i = 0;
int emitCountDec = emtr->emissionCount + FX32_CAST(emtr->emissionCountFractional);
int totalEmissions = emitCountDec >> FX32_SHIFT;
int emission = 0;
emtr->emissionCountFractional = emitCountDec & FX32_DEC_MASK;
int temp = emtr->unk_C8 + FX32_CAST(emtr->unk_BE);
curGenNum = temp >> FX32_SHIFT;
emtr->unk_BE = temp & FX32_DEC_MASK;
u32 initType = resBase->flags.unk_04_0;
if (initType == 2 || initType == 3 || IS_IN_RANGE(initType, 5, 9)) {
sub_020A1768(emtr);
enum SPLEmissionType emitType = header->flags.emissionType;
if (emitType == SPL_EMISSION_TYPE_CIRCLE_BORDER ||
emitType == SPL_EMISSION_TYPE_CIRCLE_BORDER_UNIFORM ||
emitType == SPL_EMISSION_TYPE_CIRCLE ||
emitType == SPL_EMISSION_TYPE_CYLINDER_SURFACE ||
emitType == SPL_EMISSION_TYPE_CYLINDER ||
emitType == SPL_EMISSION_TYPE_HEMISPHERE_SURFACE ||
emitType == SPL_EMISSION_TYPE_HEMISPHERE) {
SPLEmitter_ComputeOrthogonalAxes(emtr);
}
i = 0;
if (i < curGenNum) {
fx32 genNum = 0;
do {
ptcl = (SPLParticle *)SPLList_PopFront(list);
for (i = 0; i < totalEmissions; i++) {
SPLParticle *ptcl = SPLParticleList_PopFront(list);
if (ptcl == NULL) {
return;
}
if (ptcl == NULL) {
return;
SPLParticleList_PushFront(&emtr->particles, ptcl);
switch (header->flags.emissionType) {
case SPL_EMISSION_TYPE_POINT:
ptcl->position.x = ptcl->position.y = ptcl->position.z = 0;
break;
case SPL_EMISSION_TYPE_SPHERE_SURFACE:
SPLRandom_VecFx32(&ptcl->position);
ptcl->position.x = FX_MUL(ptcl->position.x, emtr->radius);
ptcl->position.y = FX_MUL(ptcl->position.y, emtr->radius);
ptcl->position.z = FX_MUL(ptcl->position.z, emtr->radius);
break;
case SPL_EMISSION_TYPE_CIRCLE_BORDER: {
VecFx32 pos;
SPLRandom_VecFx32_XY(&pos);
pos.x = FX_MUL(pos.x, emtr->radius);
pos.y = FX_MUL(pos.y, emtr->radius);
pos.z = 0;
SPLUtil_TiltCoordinates(&ptcl->position, &pos, emtr);
} break;
case SPL_EMISSION_TYPE_CIRCLE_BORDER_UNIFORM: {
VecFx32 pos;
int idx = (emission * FX32_CONST(16)) / totalEmissions;
emission += 1;
fx32 sin = FX_SinIdx(idx);
fx32 cos = FX_CosIdx(idx);
pos.x = FX_MUL(sin, emtr->radius);
pos.y = FX_MUL(cos, emtr->radius);
pos.z = 0;
SPLUtil_TiltCoordinates(&ptcl->position, &pos, emtr);
} break;
case SPL_EMISSION_TYPE_SPHERE:
SPLRandom_VecFx32(&ptcl->position);
ptcl->position.x = FX_MUL(FX_MUL(ptcl->position.x, emtr->radius), SPLRandom_RangeFX32(FX32_ONE));
ptcl->position.y = FX_MUL(FX_MUL(ptcl->position.y, emtr->radius), SPLRandom_RangeFX32(FX32_ONE));
ptcl->position.z = FX_MUL(FX_MUL(ptcl->position.z, emtr->radius), SPLRandom_RangeFX32(FX32_ONE));
break;
case SPL_EMISSION_TYPE_CIRCLE: {
VecFx32 pos;
SPLRandom_VecFx32_XY(&pos);
pos.x = FX_MUL(FX_MUL(pos.x, emtr->radius), SPLRandom_RangeFX32(FX32_ONE));
pos.y = FX_MUL(FX_MUL(pos.y, emtr->radius), SPLRandom_RangeFX32(FX32_ONE));
SPLUtil_TiltCoordinates(&ptcl->position, &pos, emtr);
} break;
case SPL_EMISSION_TYPE_HEMISPHERE_SURFACE: {
VecFx32 pos;
VecFx16 emitterUp;
SPLRandom_VecFx32(&ptcl->position);
VEC_Fx16CrossProduct(&emtr->crossAxis1, &emtr->crossAxis2, &emitterUp);
pos.x = emitterUp.x;
pos.y = emitterUp.y;
pos.z = emitterUp.z;
if (VEC_DotProduct(&pos, &ptcl->position) <= 0) {
ptcl->position.x = -ptcl->position.x;
ptcl->position.y = -ptcl->position.y;
ptcl->position.z = -ptcl->position.z;
}
SPLList_PushFront((SPLList *)&emtr->particles, (SPLNode *)ptcl);
ptcl->position.x = FX_MUL(ptcl->position.x, emtr->radius);
ptcl->position.y = FX_MUL(ptcl->position.y, emtr->radius);
ptcl->position.z = FX_MUL(ptcl->position.z, emtr->radius);
} break;
switch (resBase->flags.unk_04_0) {
case 0:
ptcl->position.x = ptcl->position.y = ptcl->position.z = 0;
break;
case 1:
SPLRandom_VecFx32(&ptcl->position);
ptcl->position.x = FX_MUL(ptcl->position.x, emtr->unk_CC);
ptcl->position.y = FX_MUL(ptcl->position.y, emtr->unk_CC);
ptcl->position.z = FX_MUL(ptcl->position.z, emtr->unk_CC);
break;
case 2: {
VecFx32 pos;
SPLRandom_VecFx32_XY(&pos);
pos.x = FX_MUL(pos.x, emtr->unk_CC);
pos.y = FX_MUL(pos.y, emtr->unk_CC);
pos.z = 0;
sub_020A1608(&ptcl->position, &pos, emtr);
} break;
case 3: {
VecFx32 pos;
int idx = genNum / curGenNum;
genNum += 0x10000;
fx32 sin = FX_SinIdx(idx);
fx32 cos = FX_CosIdx(idx);
pos.x = FX_MUL(sin, emtr->unk_CC);
pos.y = FX_MUL(cos, emtr->unk_CC);
pos.z = 0;
sub_020A1608(&ptcl->position, &pos, emtr);
} break;
case 4:
SPLRandom_VecFx32(&ptcl->position);
ptcl->position.x = FX_MUL(FX_MUL(ptcl->position.x, emtr->unk_CC), SPLRandom_RangeFX32(FX32_ONE));
ptcl->position.y = FX_MUL(FX_MUL(ptcl->position.y, emtr->unk_CC), SPLRandom_RangeFX32(FX32_ONE));
ptcl->position.z = FX_MUL(FX_MUL(ptcl->position.z, emtr->unk_CC), SPLRandom_RangeFX32(FX32_ONE));
break;
case 5: {
VecFx32 pos;
SPLRandom_VecFx32_XY(&pos);
pos.x = FX_MUL(FX_MUL(pos.x, emtr->unk_CC), SPLRandom_RangeFX32(FX32_ONE));
pos.y = FX_MUL(FX_MUL(pos.y, emtr->unk_CC), SPLRandom_RangeFX32(FX32_ONE));
sub_020A1608(&ptcl->position, &pos, emtr);
} break;
case 8: {
VecFx32 pos;
VecFx16 tmpUnitVec;
SPLRandom_VecFx32(&ptcl->position);
VEC_Fx16CrossProduct(&emtr->unk_F4, &emtr->unk_FA, &tmpUnitVec);
pos.x = tmpUnitVec.x;
pos.y = tmpUnitVec.y;
pos.z = tmpUnitVec.z;
if (VEC_DotProduct(&pos, &ptcl->position) <= 0) {
ptcl->position.x = -ptcl->position.x;
ptcl->position.y = -ptcl->position.y;
ptcl->position.z = -ptcl->position.z;
}
ptcl->position.x = FX_MUL(ptcl->position.x, emtr->unk_CC);
ptcl->position.y = FX_MUL(ptcl->position.y, emtr->unk_CC);
ptcl->position.z = FX_MUL(ptcl->position.z, emtr->unk_CC);
} break;
case 9: {
VecFx32 pos;
VecFx16 tmpUnitVec;
SPLRandom_VecFx32(&ptcl->position);
VEC_Fx16CrossProduct(&emtr->unk_F4, &emtr->unk_FA, &tmpUnitVec);
pos.x = tmpUnitVec.x;
pos.y = tmpUnitVec.y;
pos.z = tmpUnitVec.z;
if (VEC_DotProduct(&pos, &ptcl->position) < 0) {
ptcl->position.x = -ptcl->position.x;
ptcl->position.y = -ptcl->position.y;
ptcl->position.z = -ptcl->position.z;
}
ptcl->position.x = FX_MUL(FX_MUL(ptcl->position.x, emtr->unk_CC), (SPLRandom_RangeFX32(FX32_ONE) >> 1) + FX32_HALF);
ptcl->position.y = FX_MUL(FX_MUL(ptcl->position.y, emtr->unk_CC), (SPLRandom_RangeFX32(FX32_ONE) >> 1) + FX32_HALF);
ptcl->position.z = FX_MUL(FX_MUL(ptcl->position.z, emtr->unk_CC), (SPLRandom_RangeFX32(FX32_ONE) >> 1) + FX32_HALF);
} break;
case 6: {
VecFx32 pos;
SPLRandom_VecFx32_XY(&ptcl->velocity);
pos.x = FX_MUL(ptcl->velocity.x, emtr->unk_CC);
pos.y = FX_MUL(ptcl->velocity.y, emtr->unk_CC);
pos.z = SPLRandom_RangeFX32(emtr->length);
sub_020A1608(&ptcl->position, &pos, emtr);
} break;
case 7: {
VecFx32 pos;
SPLRandom_VecFx32_XY(&ptcl->velocity);
pos.x = FX_MUL(FX_MUL(ptcl->velocity.x, emtr->unk_CC), SPLRandom_RangeFX32(FX32_ONE));
pos.y = FX_MUL(FX_MUL(ptcl->velocity.y, emtr->unk_CC), SPLRandom_RangeFX32(FX32_ONE));
pos.z = SPLRandom_RangeFX32(emtr->length);
sub_020A1608(&ptcl->position, &pos, emtr);
} break;
case SPL_EMISSION_TYPE_HEMISPHERE: {
VecFx32 pos;
VecFx16 tmpUnitVec;
SPLRandom_VecFx32(&ptcl->position);
VEC_Fx16CrossProduct(&emtr->crossAxis1, &emtr->crossAxis2, &tmpUnitVec);
pos.x = tmpUnitVec.x;
pos.y = tmpUnitVec.y;
pos.z = tmpUnitVec.z;
if (VEC_DotProduct(&pos, &ptcl->position) < 0) {
ptcl->position.x = -ptcl->position.x;
ptcl->position.y = -ptcl->position.y;
ptcl->position.z = -ptcl->position.z;
}
magPos = SPLRandom_DoubleScaledRangeFX32(emtr->unk_D4, resBase->unk_44.unk_02_0);
magAxis = SPLRandom_DoubleScaledRangeFX32(emtr->unk_D8, resBase->unk_44.unk_02_0);
ptcl->position.x = FX_MUL(FX_MUL(ptcl->position.x, emtr->radius), (SPLRandom_RangeFX32(FX32_ONE) >> 1) + FX32_HALF);
ptcl->position.y = FX_MUL(FX_MUL(ptcl->position.y, emtr->radius), (SPLRandom_RangeFX32(FX32_ONE) >> 1) + FX32_HALF);
ptcl->position.z = FX_MUL(FX_MUL(ptcl->position.z, emtr->radius), (SPLRandom_RangeFX32(FX32_ONE) >> 1) + FX32_HALF);
} break;
VecFx32 posNorm;
if (resBase->flags.unk_04_0 == 6) {
VecFx32 tmp;
tmp.x = FX_MUL(ptcl->velocity.x, emtr->unk_F4.x) + FX_MUL(ptcl->velocity.y, emtr->unk_FA.x);
tmp.y = FX_MUL(ptcl->velocity.x, emtr->unk_F4.y) + FX_MUL(ptcl->velocity.y, emtr->unk_FA.y);
tmp.z = FX_MUL(ptcl->velocity.x, emtr->unk_F4.z) + FX_MUL(ptcl->velocity.y, emtr->unk_FA.z);
case SPL_EMISSION_TYPE_CYLINDER_SURFACE: {
VecFx32 pos;
SPLRandom_VecFx32_XY(&ptcl->velocity);
pos.x = FX_MUL(ptcl->velocity.x, emtr->radius);
pos.y = FX_MUL(ptcl->velocity.y, emtr->radius);
pos.z = SPLRandom_RangeFX32(emtr->length);
SPLUtil_TiltCoordinates(&ptcl->position, &pos, emtr);
} break;
VEC_Normalize(&tmp, &posNorm);
} else if (ptcl->position.x == 0 && ptcl->position.y == 0 && ptcl->position.z == 0) {
SPLRandom_VecFx32(&posNorm);
} else {
VEC_Normalize(&ptcl->position, &posNorm);
}
case SPL_EMISSION_TYPE_CYLINDER: {
VecFx32 pos;
SPLRandom_VecFx32_XY(&ptcl->velocity);
pos.x = FX_MUL(FX_MUL(ptcl->velocity.x, emtr->radius), SPLRandom_RangeFX32(FX32_ONE));
pos.y = FX_MUL(FX_MUL(ptcl->velocity.y, emtr->radius), SPLRandom_RangeFX32(FX32_ONE));
pos.z = SPLRandom_RangeFX32(emtr->length);
SPLUtil_TiltCoordinates(&ptcl->position, &pos, emtr);
} break;
}
ptcl->velocity.x = FX_MUL(posNorm.x, magPos) + FX_MUL(emtr->axis.x, magAxis) + emtr->unk_B0.x;
ptcl->velocity.y = FX_MUL(posNorm.y, magPos) + FX_MUL(emtr->axis.y, magAxis) + emtr->unk_B0.y;
ptcl->velocity.z = FX_MUL(posNorm.z, magPos) + FX_MUL(emtr->axis.z, magAxis) + emtr->unk_B0.z;
fx32 magPos = SPLRandom_DoubleScaledRangeFX32(emtr->initVelPositionAmplifier, header->randomAttenuation.initVel);
fx32 magAxis = SPLRandom_DoubleScaledRangeFX32(emtr->initVelAxisAmplifier, header->randomAttenuation.initVel);
ptcl->emitterPos = emtr->position;
VecFx32 posNorm;
if (header->flags.emissionType == SPL_EMISSION_TYPE_CYLINDER_SURFACE) {
VecFx32 tmp;
tmp.x = FX_MUL(ptcl->velocity.x, emtr->crossAxis1.x) + FX_MUL(ptcl->velocity.y, emtr->crossAxis2.x);
tmp.y = FX_MUL(ptcl->velocity.x, emtr->crossAxis1.y) + FX_MUL(ptcl->velocity.y, emtr->crossAxis2.y);
tmp.z = FX_MUL(ptcl->velocity.x, emtr->crossAxis1.z) + FX_MUL(ptcl->velocity.y, emtr->crossAxis2.z);
ptcl->unk_30 = SPLRandom_DoubleScaledRangeFX32(emtr->unk_DC, resBase->unk_44.unk_00_0);
ptcl->unk_34 = FX32_ONE;
VEC_Normalize(&tmp, &posNorm);
} else if (ptcl->position.x == 0 && ptcl->position.y == 0 && ptcl->position.z == 0) {
SPLRandom_VecFx32(&posNorm);
} else {
VEC_Normalize(&ptcl->position, &posNorm);
}
if (resBase->flags.hasColorAnim && res->colorAnim->unk_08.unk_00_0) {
u16 clr[3];
u32 index = SPLRandom_S32(12);
clr[0] = res->colorAnim->unk_00;
clr[1] = resBase->unk_22;
clr[2] = res->colorAnim->unk_02;
ptcl->unk_36 = clr[index % 3];
} else {
ptcl->unk_36 = resBase->unk_22;
}
ptcl->velocity.x = FX_MUL(posNorm.x, magPos) + FX_MUL(emtr->axis.x, magAxis) + emtr->particleInitVelocity.x;
ptcl->velocity.y = FX_MUL(posNorm.y, magPos) + FX_MUL(emtr->axis.y, magAxis) + emtr->particleInitVelocity.y;
ptcl->velocity.z = FX_MUL(posNorm.z, magPos) + FX_MUL(emtr->axis.z, magAxis) + emtr->particleInitVelocity.z;
ptcl->unk_2E.unk_00_0 = emtr->misc.unk_01_0;
ptcl->unk_2E.unk_00_5 = 31;
ptcl->emitterPos = emtr->position;
if (resBase->flags.unk_05_5) {
ptcl->rotation = SPLRandom_S32(32);
} else {
ptcl->rotation = emtr->unk_C6;
}
ptcl->baseScale = SPLRandom_DoubleScaledRangeFX32(emtr->baseScale, header->randomAttenuation.baseScale);
ptcl->unk_34 = FX32_ONE;
if (resBase->flags.unk_05_4) {
ptcl->angularVelocity = (u32)SPLRandom_BetweenFX32(resBase->unk_34, resBase->unk_36) >> FX32_SHIFT;
} else {
ptcl->angularVelocity = 0;
}
if (header->flags.hasColorAnim && res->colorAnim->unk_08.unk_00_0) {
u16 clr[3];
u32 index = SPLRandom_S32(12);
clr[0] = res->colorAnim->startColor;
clr[1] = header->color;
clr[2] = res->colorAnim->endColor;
ptcl->color = clr[index % 3];
} else {
ptcl->color = header->color;
}
ptcl->lifeTime = SPLRandom_ScaledRangeFX32(emtr->particleLifeTime, resBase->unk_44.unk_01_0) + 1;
ptcl->age = 0;
ptcl->visibility.baseAlpha = emtr->misc.baseAlpha;
ptcl->visibility.animAlpha = 31;
if (resBase->flags.hasTexAnim && res->texAnim->unk_08.unk_02_0) {
ptcl->misc.unk_00 = res->texAnim->unk_00[SPLRandom_U32(12) % res->texAnim->unk_08.unk_00_0];
} else if (resBase->flags.hasTexAnim && !res->texAnim->unk_08.unk_02_0) {
ptcl->misc.unk_00 = res->texAnim->unk_00[0];
} else {
ptcl->misc.unk_00 = resBase->misc.textureIndex;
}
if (header->flags.unk_05_5) {
ptcl->rotation = SPLRandom_S32(32);
} else {
ptcl->rotation = emtr->unk_C6;
}
ptcl->loopTimeFactor = 0xFFFF / res->header->misc.unk_04_0;
ptcl->lifeTimeFactor = 0xFFFF / ptcl->lifeTime;
if (header->flags.hasRotation) {
ptcl->angularVelocity = (u32)SPLRandom_BetweenFX32(header->minRotation, header->maxRotation) >> FX32_SHIFT;
} else {
ptcl->angularVelocity = 0;
}
ptcl->misc.lifeRateOffset = 0;
ptcl->lifeTime = SPLRandom_ScaledRangeFX32(emtr->particleLifeTime, header->randomAttenuation.lifeTime) + 1; // Life is always at least 1 frame
ptcl->age = 0;
if (resBase->flags.unk_06_4) {
ptcl->misc.lifeRateOffset = (u8)SPLRandom_S32(8);
}
i++;
} while (i < curGenNum);
if (header->flags.hasTexAnim && res->texAnim->param.randomizeInit) {
ptcl->misc.texture = res->texAnim->textures[SPLRandom_U32(12) % res->texAnim->param.frameCount];
} else if (header->flags.hasTexAnim && !res->texAnim->param.randomizeInit) {
ptcl->misc.texture = res->texAnim->textures[0];
} else {
ptcl->misc.texture = header->misc.textureIndex;
}
ptcl->loopTimeFactor = 0xFFFF / res->header->misc.loopFrames;
ptcl->lifeTimeFactor = 0xFFFF / ptcl->lifeTime;
ptcl->misc.lifeRateOffset = 0;
if (header->flags.randomizeLoopedAnim) {
ptcl->misc.lifeRateOffset = (u8)SPLRandom_S32(8);
}
}
}
@ -322,19 +320,19 @@ void sub_020A05BC(SPLParticle *ptcl, SPLEmitter *emtr, SPLList *list)
// `unk_08.unk_00_0` and `unk_08.unk_01_0` in `UnkSPLStruct14`
// could just be `u8 unk_08;` and `u8 unk_09;`
// instead of an inner struct
int idk = ptcl->unk_30 * ptcl->unk_34 >> FX32_SHIFT;
chld->unk_30 = idk * (chldRes->unk_08.unk_01_0 + 1) >> 6;
int idk = ptcl->baseScale * ptcl->unk_34 >> FX32_SHIFT;
chld->baseScale = idk * (chldRes->unk_08.unk_01_0 + 1) >> 6;
chld->unk_34 = FX32_ONE;
if (chldRes->flags.unk_02_6) {
chld->unk_36 = chldRes->unk_0A;
chld->color = chldRes->unk_0A;
} else {
chld->unk_36 = ptcl->unk_36;
chld->color = ptcl->color;
}
chld->unk_2E.unk_00_0 = (ptcl->unk_2E.unk_00_0 * (ptcl->unk_2E.unk_00_5 + 1)) >> 5;
chld->unk_2E.unk_00_5 = 31;
chld->visibility.baseAlpha = (ptcl->visibility.baseAlpha * (ptcl->visibility.animAlpha + 1)) >> 5;
chld->visibility.animAlpha = 31;
switch (chldRes->flags.unk_02_3) {
case 0:
@ -353,7 +351,7 @@ void sub_020A05BC(SPLParticle *ptcl, SPLEmitter *emtr, SPLList *list)
chld->lifeTime = chldRes->unk_06;
chld->age = 0;
chld->misc.unk_00 = chldRes->misc.textureIndex;
chld->misc.texture = chldRes->misc.textureIndex;
chld->loopTimeFactor = 0xFFFF / (ptcl->lifeTime / 2);
chld->lifeTimeFactor = 0xFFFF / ptcl->lifeTime;

View File

@ -41,53 +41,53 @@ void sub_020A1BD4(SPLParticle *ptcl, SPLResource *res, int lifeRate)
in = colorAnim->unk_04.unk_04_0;
if (lifeRate < in) {
ptcl->unk_36 = colorAnim->unk_00;
ptcl->color = colorAnim->startColor;
} else if (lifeRate < peak) {
int peakR = GX_RGB_R(resBase->unk_22);
int startR = GX_RGB_R(colorAnim->unk_00);
int peakR = GX_RGB_R(resBase->color);
int startR = GX_RGB_R(colorAnim->startColor);
int peakG = GX_RGB_G(resBase->unk_22);
int startG = GX_RGB_G(colorAnim->unk_00);
int peakG = GX_RGB_G(resBase->color);
int startG = GX_RGB_G(colorAnim->startColor);
int peakB = GX_RGB_B(resBase->unk_22);
int startB = GX_RGB_B(colorAnim->unk_00);
int peakB = GX_RGB_B(resBase->color);
int startB = GX_RGB_B(colorAnim->startColor);
if (colorAnim->unk_08.unk_00_2) { // interpolate
int a = lifeRate - in;
int b = peak - in;
ptcl->unk_36 = GX_RGB(
ptcl->color = GX_RGB(
startR + (a * (int)(peakR - startR)) / b,
startG + (a * (int)(peakG - startG)) / b,
startB + (a * (int)(peakB - startB)) / b
);
} else {
ptcl->unk_36 = GX_RGB(peakR, peakG, peakB);
ptcl->color = GX_RGB(peakR, peakG, peakB);
}
} else if (lifeRate < out) {
int peakR = GX_RGB_R(resBase->unk_22);
int endR = GX_RGB_R(colorAnim->unk_02);
int peakR = GX_RGB_R(resBase->color);
int endR = GX_RGB_R(colorAnim->endColor);
int peakG = GX_RGB_G(resBase->unk_22);
int endG = GX_RGB_G(colorAnim->unk_02);
int peakG = GX_RGB_G(resBase->color);
int endG = GX_RGB_G(colorAnim->endColor);
int peakB = GX_RGB_B(resBase->unk_22);
int endB = GX_RGB_B(colorAnim->unk_02);
int peakB = GX_RGB_B(resBase->color);
int endB = GX_RGB_B(colorAnim->endColor);
if (colorAnim->unk_08.unk_00_2) { // interpolate
int a = lifeRate - peak;
int b = out - peak;
ptcl->unk_36 = GX_RGB(
ptcl->color = GX_RGB(
peakR + (a * (int)(endR - peakR)) / b,
peakG + (a * (int)(endG - peakG)) / b,
peakB + (a * (int)(endB - peakB)) / b
);
} else {
ptcl->unk_36 = GX_RGB(endR, endG, endB);
ptcl->color = GX_RGB(endR, endG, endB);
}
} else {
ptcl->unk_36 = colorAnim->unk_02;
ptcl->color = colorAnim->endColor;
}
}
@ -108,15 +108,15 @@ void sub_020A1AF8(SPLParticle *ptcl, SPLResource *res, int lifeRate)
x += alphaAnim->unk_00.val2_01_2;
}
ptcl->unk_2E.unk_00_5 = SPLRandom_ScaledRangeFX32(x, alphaAnim->unk_02.unk_00_0);
ptcl->visibility.animAlpha = SPLRandom_ScaledRangeFX32(x, alphaAnim->unk_02.unk_00_0);
}
void sub_020A1A94(SPLParticle *ptcl, SPLResource *res, int lifeRate)
{
SPLTexAnim *texAnim = res->texAnim;
for (int i = 0; i < texAnim->unk_08.unk_00_0; i++) {
if (lifeRate < texAnim->unk_08.unk_01_0 * (i + 1)) {
ptcl->misc.unk_00 = texAnim->unk_00[i];
for (int i = 0; i < texAnim->param.frameCount; i++) {
if (lifeRate < texAnim->param.unk_01_0 * (i + 1)) {
ptcl->misc.texture = texAnim->textures[i];
return;
}
}
@ -129,5 +129,5 @@ void sub_020A1A48(SPLParticle *ptcl, SPLResource *res, int lifeRate)
void sub_020A19F0(SPLParticle *ptcl, SPLResource *res, int lifeRate)
{
ptcl->unk_2E.unk_00_5 = ((255 - lifeRate) * 31) / 255;
ptcl->visibility.animAlpha = ((255 - lifeRate) * 31) / 255;
}

View File

@ -652,7 +652,7 @@ void sub_02014798(SPLEmitter *param0, VecFx16 *param1)
void sub_020147B0(SPLEmitter *param0, fx32 param1)
{
param0->resource->header->unk_10 = param1;
param0->resource->header->emissionCount = param1;
}
enum {