diff --git a/include/rwsdk/rpptank.h b/include/rwsdk/rpptank.h index 5abe158bf..c487e717f 100644 --- a/include/rwsdk/rpptank.h +++ b/include/rwsdk/rpptank.h @@ -58,6 +58,8 @@ enum RpPTankLockFlags rpPTANKLOCKREAD = ((int)0x80000000) }; +extern RwInt32 _rpPTankAtomicDataOffset; + #ifdef __cplusplus extern "C" { #endif @@ -76,4 +78,4 @@ extern RpAtomic* RpPTankAtomicUnlock(RpAtomic* atomic); } #endif -#endif \ No newline at end of file +#endif diff --git a/src/SB/Core/x/xParEmitter.h b/src/SB/Core/x/xParEmitter.h index d92d64404..c43de4045 100644 --- a/src/SB/Core/x/xParEmitter.h +++ b/src/SB/Core/x/xParEmitter.h @@ -105,6 +105,8 @@ void xParEmitterInit(void* b, void* tasset); void xParEmitterSetup(xParEmitter* t); void xParEmitterDestroy(); void xParEmitterUpdate(xBase* to, xScene*, float32 dt); -xPar* xParEmitterEmitCustom(xParEmitter* p, float dt, xParEmitterCustomSettings* info); +xPar* xParEmitterEmitCustom(xParEmitter* p, float32 dt, xParEmitterCustomSettings* info); +float32 xParInterpCompute(int32 interp_mode, xParInterp* r, float32 time, int32 time_has_elapsed, + float32 last_val); #endif diff --git a/src/SB/Core/x/xParSys.h b/src/SB/Core/x/xParSys.h index 6987129ed..1b9d6364e 100644 --- a/src/SB/Core/x/xParSys.h +++ b/src/SB/Core/x/xParSys.h @@ -7,6 +7,8 @@ #include +struct xScene; + struct xParSysAsset : xBaseAsset { uint32 type; @@ -33,12 +35,10 @@ struct xParSys : xBase RwTexture* txtr_particle; }; -struct xScene; - void xParSysInit(void* b, void* tasset); void xParSysSetup(xParSys* t); void xParSysExit(xParSys* t); void xParSysRender(xBase* b); void xParSysUpdate(xBase* to, xScene*, float32 dt); -#endif \ No newline at end of file +#endif diff --git a/src/SB/Core/x/xPtankPool.h b/src/SB/Core/x/xPtankPool.h index 1edf4c702..9a53a1d1a 100644 --- a/src/SB/Core/x/xPtankPool.h +++ b/src/SB/Core/x/xPtankPool.h @@ -6,6 +6,10 @@ #include #include +struct iColor_tag; +struct xVec2; +struct xVec3; + enum ptank_group_type { PGT_COLOR_MAT_UV2, @@ -39,8 +43,18 @@ struct ptank_pool void grab_block(ptank_group_type type); }; +// total size: 0x38 +struct ptank_pool__pos_color_size_uv2 : public ptank_pool +{ + xVec3* pos; + iColor_tag* color; + xVec2* size; + xVec2* uv; + int32 stride; +}; + void xPTankPoolSceneEnter(); void xPTankPoolSceneExit(); void xPTankPoolRender(); -#endif \ No newline at end of file +#endif diff --git a/src/SB/Game/zParPTank.cpp b/src/SB/Game/zParPTank.cpp index 5d0eac46a..7ae0c3bea 100644 --- a/src/SB/Game/zParPTank.cpp +++ b/src/SB/Game/zParPTank.cpp @@ -1,3 +1,341 @@ #include "zParPTank.h" +#include +#include #include + +#include "xMemMgr.h" +#include "xParEmitter.h" +#include "xPtankPool.h" +#include "xstransvc.h" +#include "xString.h" +#include "xVec3.h" + +#include "zGame.h" + +// TODO: Update renderware macros and get rid of this +#define RpPTankData(_atomic) ((RpPTankData*)((int8*)_atomic + _rpPTankAtomicDataOffset)) + +struct BubbleData +{ + xVec3 vel; + float life; +}; + +static zParPTank sPTank[7]; +static uint32 sNumPTanks; +static zParPTank* sSparklePTank; +static zParPTank* sBubblePTank; +static zParPTank* sMenuBubblePTank; +static zParPTank* sSnowPTank; +static zParPTank* sSteamPTank; +static float sSparkleAnimTime; +static BubbleData* sBubbleData; +static BubbleData* sMenuBubbleData; +static RwV2d sparkleSize = { 0.3f, 0.3f }; + +namespace +{ + // total size: 0x30 + struct snow_particle_data + { + xVec3 loc; + float32 size; + xVec3 vel; + float32 life; + float32 u; + float32 pad[3]; + }; + + static ptank_pool__pos_color_size_uv2 snow_pool; + static snow_particle_data* snow_particles; +} // namespace + +// Equivalent, float scheduling +void zParPTankSparkleCreate(zParPTank* zp, uint32 max_particles, zParPTankUpdateCallback update) +{ + zp->num_particles = 0; + zp->max_particles = max_particles; + zp->flags = 0; + zp->update = update; + + RwTexture* tex = (RwTexture*)xSTFindAsset(xStrHash("partex0"), NULL); + if (!tex) + { + return; + } + + RwTextureSetFilterMode(tex, rwFILTERLINEAR); + RwRGBA defaultColor = { 0xFF, 0xFF, 0xFF, 0xFF }; + + zp->ptank = RpPTankAtomicCreate(zp->max_particles, 0x10000081, 0); + RwFrame* frame = RwFrameCreate(); + + frame->modelling.at.z = 1.0f; + frame->modelling.up.y = 1.0f; + frame->modelling.right.x = 1.0f; + + frame->modelling.up.x = 0.0f; + frame->modelling.right.z = 0.0f; + frame->modelling.right.y = 0.0f; + + frame->modelling.at.y = 0.0f; + frame->modelling.at.x = 0.0f; + frame->modelling.up.z = 0.0f; + + frame->modelling.pos.z = 0.0f; + frame->modelling.pos.y = 0.0f; + frame->modelling.pos.x = 0.0f; + + frame->modelling.flags |= 0x20003; + RpAtomicSetFrame(zp->ptank, frame); + + // TODO: Not sure what this structure data is supposed to be. + *(RwRGBA*)(((int8*)RpPTankData(zp->ptank)->data) + 0xc4) = defaultColor; + if (zp->ptank->geometry->matList.materials[0]) + { + zp->ptank->geometry->matList.materials[0]->color = + *(RwRGBA*)((int8*)RpPTankData(zp->ptank)->data + 0xc4); + } + *(uint32*)((int8*)RpPTankData(zp->ptank)->data + 0x40) |= 0x2000; + *(RwV2d*)((int8*)RpPTankData(zp->ptank)->data + 0xb8) = sparkleSize; + *(uint32*)((int8*)RpPTankData(zp->ptank)->data + 0x40) |= 0x4000; + + RpMaterialSetTexture(zp->ptank->geometry->matList.materials[0], tex); + + *(uint32*)((int8*)RpPTankData(zp->ptank)->data + 0xa4) = 5; + *(uint32*)((int8*)RpPTankData(zp->ptank)->data + 0xa8) = 2; + *(uint32*)((int8*)RpPTankData(zp->ptank)->data + 0x40) |= 0x10000000; + *(uint32*)((int8*)RpPTankData(zp->ptank)->data + 0xac) = 1; + + sSparkleAnimTime = 0.0f; +} + +void zParPTankSparkleUpdate(zParPTank* zp, float dt) +{ + sSparkleAnimTime += dt; + if (!(sSparkleAnimTime >= 1.0f / 30.0f)) + { + return; + } + + RpPTankLockStruct plock; + RpPTankLockStruct uvlock; + RpPTankAtomicLock(zp->ptank, &plock, 0x1, rpPTANKLOCKWRITE); + RpPTankAtomicLock(zp->ptank, &uvlock, 0x1, rpPTANKLOCKWRITE); + + uint32 plock_base = plock.stride; + uint32 uvlock_base = uvlock.stride; + + RwTexCoords* uv = (RwTexCoords*)uvlock.data; + for (int32 i = 0; i < zp->num_particles; i++) + { + // RwTexCoords* uv = (RwTexCoords*)&uvlock.data[i * uvlock.stride]; + uv->u += 0.125f; + uv->v += 0.125f; + + if (uv->u >= 1.0f) + { + *(RwV3d*)&plock.data[i * plock.stride] = + *(RwV3d*)&plock.data[zp->num_particles * plock.stride]; + + RwTexCoords* end_uv = + (RwTexCoords*)uvlock.data[(zp->num_particles - 1) * uvlock.stride]; + *uv = *end_uv; + *(uv + 1) = *(end_uv + 1); + i--; + zp->num_particles--; + uv = (RwTexCoords*)&uvlock.data[i * uvlock.stride]; + } + } + + RpPTankAtomicUnlock(zp->ptank); + + *(uint32*)((int8*)RpPTankData(zp->ptank)->data + 0x40) |= 0x800000; + *(uint32*)((int8*)RpPTankData(zp->ptank)->data + 0x4) = zp->num_particles; + sSparkleAnimTime -= 1.0f / 30.f; +} + +void zParPTankSpawnSparkles(xVec3*, uint32) +{ +} + +void zParPTankBubbleCreate(zParPTank* zp, uint32 max_particles, zParPTankUpdateCallback update) +{ +} + +void zParPTankMenuBubbleCreate(zParPTank* zp, uint32 max_particles, zParPTankUpdateCallback update) +{ +} + +void zParPTankBubbleUpdate(zParPTank* zp, float dt) +{ +} + +void zParPTankSpawnBubbles(xVec3*, xVec3*, uint32, float, zParPTank*) +{ +} + +void zParPTankSpawnBubbles(xVec3*, xVec3*, uint32, float) +{ +} + +int32 zParPTankBubblesAvailable() +{ + return sBubblePTank->max_particles - sBubblePTank->num_particles; +} + +void zParPTankSpawnMenuBubbles(xVec3*, xVec3*, uint32) +{ +} + +void zParPTankSnowCreate(zParPTank*, unsigned int, zParPTankUpdateCallback) +{ +} + +void zParPTankSnowUpdate(zParPTank*, float) +{ +} + +void zParPTankSpawnSnow(xVec3*, xVec3*, uint32) +{ +} + +void zParPTankSteamCreate(zParPTank* zp, uint32 max_particles, zParPTankUpdateCallback update) +{ +} + +void zParPTankSteamUpdate(zParPTank* zp, float dt) +{ +} + +zParPTank* zParPTankAdd() +{ + return &sPTank[sNumPTanks++]; +} + +// Equivalent: Scheduling +void zParPTankInit() +{ + sNumPTanks = 0; + sSparklePTank = zParPTankAdd(); + zParPTankSparkleCreate(sSparklePTank, 0x80, zParPTankSparkleUpdate); + + sMenuBubblePTank = zParPTankAdd(); + zParPTankMenuBubbleCreate(sMenuBubblePTank, 0x10, zParPTankBubbleUpdate); + sMenuBubblePTank->flags |= 0x2; + + sBubblePTank = zParPTankAdd(); + zParPTankBubbleCreate(sBubblePTank, 0x300, zParPTankBubbleUpdate); + + sSteamPTank = zParPTankAdd(); + zParPTankSteamCreate(sSteamPTank, 0x80, zParPTankSteamUpdate); +} + +// Equivalent: Scheduling +void zParPTankSceneEnter() +{ + sSnowPTank = zParPTankAdd(); + zParPTankSnowCreate(sSnowPTank, 0x400, zParPTankSnowUpdate); +} + +void zParPTankSceneExit() +{ +} + +//Equivalent: Scheduling +void zParPTankExit() +{ + zParPTank* zp = sPTank; + for (int32 i = 0; i < sNumPTanks; i++, zp++) + { + if (zp->ptank == NULL) + { + continue; + } + + RwFrame* tmpframe = *(RwFrame**)&zp->ptank->object.object.parent; + RpAtomicSetFrame(zp->ptank, NULL); + RwFrameDestroy(tmpframe); + RpPTankAtomicDestroy(zp->ptank); + } + + if (sBubbleData) + { + xMemPopTemp(sBubbleData); + } + sBubbleData = NULL; + + if (sMenuBubbleData) + { + xMemPopTemp(sMenuBubbleData); + } + sMenuBubbleData = NULL; +} + +void zParPTankUpdate(float dt) +{ + int32 paused = zGameIsPaused(); + + zParPTank* zp = sPTank; + for (int32 i = 0; i < sNumPTanks; i++, zp++) + { + if ((!paused || zp->flags & 0x2) && zp->update) + { + zp->update(zp, dt); + } + } +} + +void zParPTankRender() +{ + zParPTank* zp = sPTank; + for (int32 i = 0; i < sNumPTanks; i++, zp++) + { + if ((!zGameIsPaused() || zp == sMenuBubblePTank) && zp->ptank) + { + RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)1); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)1); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, 0); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)5); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)2); + + if (zp->num_particles) + { + zp->ptank->renderCallBack(zp->ptank); + } + } + } +} + +int32 zParPTankConvertEmitRate(xParEmitter* pe, float dt) +{ + xParEmitterPropsAsset* prop = pe->prop; + pe->rate_time += dt; + + int32 rate_has_elapsed = (pe->rate_time > prop->rate.freq); + if (prop->rate.freq == 0.0f) + { + pe->rate_time = 0.0f; + } + + while (pe->rate_time > prop->rate.freq) + { + pe->rate_time -= prop->rate.freq; + } + + float32 rate = + xParInterpCompute(pe->rate_mode, &prop->rate, pe->rate_time, rate_has_elapsed, pe->rate); + + pe->rate = rate; + pe->rate_fraction += rate * dt; + pe->rate_fraction_cull += rate * dt; + + int32 count = std::floorf(pe->rate_fraction); + if (count > 0) + { + pe->rate_fraction -= count; + } + + return count; +} diff --git a/src/SB/Game/zParPTank.h b/src/SB/Game/zParPTank.h index c2f424c95..e5bf5e967 100644 --- a/src/SB/Game/zParPTank.h +++ b/src/SB/Game/zParPTank.h @@ -5,6 +5,21 @@ struct xVec3; +struct RpAtomic; +struct zParPTank; +typedef void (*zParPTankUpdateCallback)(zParPTank*, float); + +// total size: 0x14 +struct zParPTank +{ + // 0x2 == Update when paused + uint32 flags; // offset 0x0, size 0x4 + zParPTankUpdateCallback update; // offset 0x4, size 0x4 + RpAtomic* ptank; // offset 0x8, size 0x4 + uint32 num_particles; // offset 0xC, size 0x4 + uint32 max_particles; // offset 0x10, size 0x4 +}; + void zParPTankInit(); void zParPTankSceneEnter(); void zParPTankSceneExit(); @@ -13,6 +28,6 @@ void zParPTankExit(); void zParPTankRender(); void zParPTankUpdate(float32 dt); -extern uint32 gPTankDisable; +uint32 gPTankDisable = 0; #endif