mirror of https://github.com/PrimeDecomp/prime.git
253 lines
7.8 KiB
C++
253 lines
7.8 KiB
C++
#include "MetroidPrime/CRainSplashGenerator.hpp"
|
|
|
|
#include "MetroidPrime/CEnvFxManager.hpp"
|
|
#include "MetroidPrime/CStateManager.hpp"
|
|
#include "MetroidPrime/CWorld.hpp"
|
|
|
|
#include "Kyoto/Graphics/CGX.hpp"
|
|
#include "Kyoto/Graphics/CGraphics.hpp"
|
|
|
|
#include "rstl/math.hpp"
|
|
|
|
GXVtxDescList vtxDescv[] = {
|
|
{GX_VA_POS, GX_DIRECT},
|
|
{GX_VA_CLR0, GX_DIRECT},
|
|
{GX_VA_NULL, GX_NONE},
|
|
};
|
|
|
|
int CRainSplashGenerator::GetNextBestPt(int pt, const CVector3f* vertices, const CVector3f* normals,
|
|
int count, CRandom16& rand, float minZ) {
|
|
const CVector3f& refVert = vertices[pt];
|
|
float maxDist = 0.f;
|
|
int nextPt = pt;
|
|
for (int i = 0; i < 3; ++i) {
|
|
bool goodNorm = false;
|
|
const int idx = rand.Range(0, count - 1);
|
|
const CVector3f& vert = vertices[idx];
|
|
const CVector3f& norm = normals[idx];
|
|
|
|
const float distSq = (refVert - vert).MagSquared();
|
|
const float normDot = CVector3f::Dot(norm, CVector3f::Up());
|
|
|
|
if (0.0f <= normDot && normDot <= 1.0f) {
|
|
goodNorm = true;
|
|
}
|
|
|
|
bool goodZ = minZ < 0.0f || minZ < vert.GetZ();
|
|
|
|
if (distSq > maxDist && goodNorm && goodZ) {
|
|
nextPt = idx;
|
|
maxDist = distSq;
|
|
}
|
|
}
|
|
return nextPt;
|
|
}
|
|
|
|
CRainSplashGenerator::CRainSplashGenerator(const CVector3f& scale, int maxSplashes, int genRate,
|
|
float minZ, float alpha)
|
|
: x14_scale(scale)
|
|
, x20_generateTimer(0.f)
|
|
, x28_dt(0.f)
|
|
, x2c_minZ(minZ)
|
|
, x30_alpha(alpha > 1.0f ? 255.f : alpha * 255.f)
|
|
, x34_curPoint(0)
|
|
, x38_queueTail(0)
|
|
, x3c_queueHead(0)
|
|
, x40_queueSize(0)
|
|
, x44_genRate(rstl::min_val(maxSplashes, genRate)) {
|
|
x0_rainSplashes.reserve(maxSplashes);
|
|
for (int i = 0; i < maxSplashes; ++i)
|
|
x0_rainSplashes.push_back(SRainSplash());
|
|
}
|
|
|
|
// CRainSplashGenerator::SSplashLine::SSplashLine() {}
|
|
|
|
void CRainSplashGenerator::AddPoint(const CVector3f& pos) {
|
|
if (x38_queueTail >= x0_rainSplashes.size())
|
|
x38_queueTail = 0;
|
|
x0_rainSplashes[x38_queueTail].SetPoint(pos);
|
|
x40_queueSize += 1;
|
|
x38_queueTail += 1;
|
|
}
|
|
|
|
void CRainSplashGenerator::GeneratePoints(const CVector3f* vertices, const CVector3f* normals, int count) {
|
|
if (!x48_25_raining)
|
|
return;
|
|
|
|
if (x20_generateTimer > x24_generateInterval) {
|
|
int pt = x34_curPoint;
|
|
for (int i = 0; i < x44_genRate; ++i) {
|
|
if (x40_queueSize >= x0_rainSplashes.size())
|
|
break;
|
|
pt = GetNextBestPt(x34_curPoint, vertices, normals, count, x10_random, x2c_minZ);
|
|
AddPoint(CVector3f::ByElementMultiply(x14_scale, vertices[pt]));
|
|
}
|
|
x34_curPoint = pt;
|
|
x20_generateTimer = 0.f;
|
|
}
|
|
}
|
|
|
|
void CRainSplashGenerator::UpdateRainSplashRange(CStateManager& mgr, int start, int end, float dt) {
|
|
for (int i = start; i < end; ++i) {
|
|
SRainSplash& set = x0_rainSplashes[i];
|
|
set.Update(dt, mgr);
|
|
if (!set.IsActive()) {
|
|
x40_queueSize -= 1;
|
|
x3c_queueHead += 1;
|
|
if (x3c_queueHead >= x0_rainSplashes.size())
|
|
x3c_queueHead = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CRainSplashGenerator::UpdateRainSplashes(CStateManager& mgr, float magnitude, float dt) {
|
|
x20_generateTimer += dt;
|
|
x24_generateInterval = 1.f / (70.f * magnitude);
|
|
if (x40_queueSize > 0) {
|
|
if (x38_queueTail <= x3c_queueHead) {
|
|
UpdateRainSplashRange(mgr, x3c_queueHead, int(x0_rainSplashes.size()), dt);
|
|
UpdateRainSplashRange(mgr, 0, x38_queueTail, dt);
|
|
} else {
|
|
UpdateRainSplashRange(mgr, x3c_queueHead, x38_queueTail, dt);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CRainSplashGenerator::Update(float dt, CStateManager& mgr) {
|
|
EEnvFxType neededFx = mgr.GetWorld()->GetNeededEnvFx();
|
|
x28_dt = dt;
|
|
x48_25_raining = false;
|
|
if (neededFx != kEFX_None && mgr.GetEnvFxManager()->IsSplashActive()) {
|
|
if (mgr.GetEnvFxManager()->GetRainMagnitude() != 0.f) {
|
|
switch (neededFx) {
|
|
case kEFX_Rain:
|
|
UpdateRainSplashes(mgr, mgr.GetEnvFxManager()->GetRainMagnitude(), dt);
|
|
x48_25_raining = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CRainSplashGenerator::Draw(const CTransform4f& xf) const {
|
|
if (!x48_25_raining) {
|
|
return;
|
|
}
|
|
|
|
DoDraw(xf);
|
|
}
|
|
|
|
void CRainSplashGenerator::DoDraw(const CTransform4f& xf) const {
|
|
if (!(x28_dt <= 0.0f)) {
|
|
CGX::SetVtxDescv(vtxDescv);
|
|
CGX::SetNumChans(1);
|
|
CGX::SetNumTevStages(1);
|
|
CGX::SetChanCtrl(CGX::Channel0, false, GX_SRC_VTX, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE,
|
|
GX_AF_NONE);
|
|
CGX::SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
|
|
CGX::SetNumTexGens(0);
|
|
CGX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
|
|
CGX::SetZMode(true, GX_LEQUAL, false);
|
|
CGraphics::SetTevOp(kTS_Stage0, CGraphics::kEnvPassthru);
|
|
CGraphics::SetTevOp(kTS_Stage1, CGraphics::kEnvPassthru);
|
|
CGraphics::SetModelMatrix(xf);
|
|
if (x40_queueSize > 0) {
|
|
if (x38_queueTail <= x3c_queueHead) {
|
|
for (int i = x3c_queueHead; i < x0_rainSplashes.size(); ++i) {
|
|
const SRainSplash& splash = x0_rainSplashes[i];
|
|
splash.Draw(x30_alpha, x28_dt, splash.x64_pos);
|
|
}
|
|
for (int i = 0; i < x38_queueTail; ++i) {
|
|
const SRainSplash& splash = x0_rainSplashes[i];
|
|
splash.Draw(x30_alpha, x28_dt, splash.x64_pos);
|
|
}
|
|
} else {
|
|
for (int i = x3c_queueHead; i < x38_queueTail; ++i) {
|
|
const SRainSplash& splash = x0_rainSplashes[i];
|
|
splash.Draw(x30_alpha, x28_dt, splash.x64_pos);
|
|
}
|
|
}
|
|
}
|
|
CGX::SetLineWidth(6, GX_TO_ZERO);
|
|
}
|
|
}
|
|
|
|
void CRainSplashGenerator::SSplashLine::SetActive() { x16_active = true; }
|
|
|
|
void CRainSplashGenerator::SSplashLine::Update(float dt, CStateManager& mgr) {
|
|
if (!x16_active)
|
|
return;
|
|
if (x0_t <= 0.8f) {
|
|
x14_ = u8(5.f * (1.f - x0_t) + 3.f * x0_t);
|
|
x0_t += dt * xc_speed;
|
|
} else if (x15_length != 0) {
|
|
x15_length -= 1;
|
|
} else {
|
|
x16_active = false;
|
|
xc_speed = mgr.Random()->Range(4.0f, 8.0f);
|
|
x10_zParabolaHeight = mgr.Random()->Range(0.015625f, 0.03125f);
|
|
x4_xEnd = mgr.Random()->Range(-0.125f, 0.125f);
|
|
x8_yEnd = mgr.Random()->Range(-0.125f, 0.125f);
|
|
x15_length = u8(mgr.Random()->Range(1, 2));
|
|
}
|
|
}
|
|
|
|
void CRainSplashGenerator::SSplashLine::Draw(float alpha, float dt, const CVector3f& pos) const {
|
|
if (x0_t > 0.f) {
|
|
float delta = dt * xc_speed;
|
|
float vt = x0_t - delta * x15_length;
|
|
if (vt < 0.0f) {
|
|
vt = 0.0f;
|
|
}
|
|
int vertCount = int((x0_t - vt) / delta + 1.f);
|
|
|
|
CGX::SetLineWidth(x14_ * 6, GX_TO_ZERO);
|
|
CGX::Begin(GX_LINESTRIP, GX_VTXFMT0, vertCount);
|
|
|
|
int i = 0;
|
|
while (i < vertCount) {
|
|
GXPosition3f32(vt * x4_xEnd + pos.GetX(), vt * x8_yEnd + pos.GetY(),
|
|
-4.f * vt * (vt - 1.f) * x10_zParabolaHeight + pos.GetZ());
|
|
uint a = vt * alpha;
|
|
vt += delta;
|
|
i++;
|
|
GXColor1u32(a | 0xffffff00);
|
|
}
|
|
CGX::End();
|
|
}
|
|
}
|
|
|
|
CRainSplashGenerator::SRainSplash::SRainSplash()
|
|
: x0_lines(SSplashLine()), x64_pos(CVector3f::Zero()), x70_(0.0f) {}
|
|
|
|
void CRainSplashGenerator::SRainSplash::Update(float dt, CStateManager& mgr) {
|
|
for (rstl::reserved_vector< SSplashLine, 4 >::iterator it = x0_lines.begin();
|
|
it != x0_lines.end(); +it)
|
|
it->Update(dt, mgr);
|
|
}
|
|
|
|
void CRainSplashGenerator::SRainSplash::Draw(float alpha, float dt, const CVector3f& pos) const {
|
|
for (rstl::reserved_vector< SSplashLine, 4 >::const_iterator it = x0_lines.begin();
|
|
it != x0_lines.end(); +it) {
|
|
it->Draw(alpha, dt, pos);
|
|
}
|
|
}
|
|
|
|
bool CRainSplashGenerator::SRainSplash::IsActive() const {
|
|
bool ret = false;
|
|
for (rstl::reserved_vector< SSplashLine, 4 >::const_iterator it = x0_lines.begin();
|
|
it != x0_lines.end(); +it) {
|
|
ret |= it->x16_active;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void CRainSplashGenerator::SRainSplash::SetPoint(const CVector3f& pos) {
|
|
for (rstl::reserved_vector< SSplashLine, 4 >::iterator it = x0_lines.begin();
|
|
it != x0_lines.end(); +it)
|
|
it->SetActive();
|
|
x64_pos = pos;
|
|
}
|