metaforce/Runtime/World/CEnvFxManager.cpp

601 lines
24 KiB
C++

#include "Runtime/World/CEnvFxManager.hpp"
#include "Runtime/CRandom16.hpp"
#include "Runtime/CSimplePool.hpp"
#include "Runtime/CStateManager.hpp"
#include "Runtime/GameGlobalObjects.hpp"
#include "Runtime/Collision/CGameCollision.hpp"
#include "Runtime/Collision/CInternalRayCastStructure.hpp"
#include "Runtime/Graphics/CBooRenderer.hpp"
#include "Runtime/Graphics/CTexture.hpp"
#include "Runtime/World/CActor.hpp"
#include "Runtime/World/CHUDBillboardEffect.hpp"
#include "Runtime/World/CPlayer.hpp"
#include "Runtime/World/CScriptTrigger.hpp"
#include "Runtime/World/CScriptWater.hpp"
#include "Runtime/World/CWorld.hpp"
#include "TCastTo.hpp" // Generated file, do not modify include path
namespace metaforce {
static rstl::reserved_vector<zeus::CVector2f, 256> g_SnowForces;
CEnvFxManagerGrid::CEnvFxManagerGrid(const zeus::CVector2i& position, const zeus::CVector2i& extent,
std::vector<CVectorFixed8_8> initialParticles, int reserve, CEnvFxManager& parent)
: x4_position(position)
, xc_extent(extent)
, x1c_particles(std::move(initialParticles))
, m_instBuf(hsh::create_dynamic_vertex_buffer<CEnvFxShaders::Instance>(reserve))
, m_uniformBuf(hsh::create_dynamic_uniform_buffer<CEnvFxShaders::Uniform>())
, m_lineRenderer(CLineRenderer::EPrimitiveMode::Lines, reserve * 2, parent.x40_txtrEnvGradient->GetBooTexture(), true,
hsh::LEqual) {
x1c_particles.reserve(reserve);
CEnvFxShaders::BuildShaderDataBinding(parent, *this);
}
CEnvFxManager::CEnvFxManager() {
x40_txtrEnvGradient = g_SimplePool->GetObj("TXTR_EnvGradient");
xb58_envRainSplash = g_SimplePool->GetObj("PART_EnvRainSplash");
xb74_txtrSnowFlake = g_SimplePool->GetObj("TXTR_SnowFlake");
xc48_underwaterFlake = g_SimplePool->GetObj("TXTR_UnderwaterFlake");
CRandom16 random(0);
m_fogUniformBuf = hsh::create_dynamic_uniform_buffer<CGraphics::CFogState>();
for (int i = 0; i < 8; ++i)
for (int j = 0; j < 8; ++j)
x50_grids.emplace_back(zeus::CVector2i{j * 2048, i * 2048}, zeus::CVector2i{2048, 2048},
std::vector<CVectorFixed8_8>{}, 171, *this);
for (int i = 0; i < 16; ++i)
xb84_snowZDeltas.emplace_back(0.f, 0.f, random.Range(-2.f, -4.f));
}
void CEnvFxManager::SetSplashEffectRate(float rate, const CStateManager& mgr) {
if (TCastToPtr<CHUDBillboardEffect> splashEffect = mgr.ObjectById(xb68_envRainSplashId))
if (splashEffect->IsElementGen())
splashEffect->GetParticleGen()->SetGeneratorRate(rate);
}
/* Used to be MIDI scale */
static float CalcRainVolume(float f) {
if (f < 0.1f)
return (f / 0.1f * 74.f) / 127.f;
else
return (f / 0.9f * 21.f + 74.f) / 127.f;
}
/* Used to be MIDI scale */
static float CalcRainPitch(float f) { return f - 1.f; }
void CEnvFxManager::UpdateRainSounds(const CStateManager& mgr) {
if (mgr.GetWorld()->GetNeededEnvFx() == EEnvFxType::Rain) {
zeus::CTransform camXf = mgr.GetCameraManager()->GetCurrentCameraTransform(mgr);
float rainVol = CalcRainVolume(x30_fxDensity);
if (!xb6a_rainSoundActive) {
xb6c_leftRainSound =
CSfxManager::AddEmitter(SFXsfx09F0, zeus::skZero3f, zeus::skZero3f, false, true, 0xff, kInvalidAreaId);
xb70_rightRainSound =
CSfxManager::AddEmitter(SFXsfx09F1, zeus::skZero3f, zeus::skZero3f, false, true, 0xff, kInvalidAreaId);
xb6a_rainSoundActive = true;
}
CSfxManager::UpdateEmitter(xb6c_leftRainSound, camXf.origin - camXf.basis[0], camXf.basis[0], rainVol);
CSfxManager::UpdateEmitter(xb70_rightRainSound, camXf.origin + camXf.basis[0], -camXf.basis[0], rainVol);
float rainPitch = CalcRainPitch(x30_fxDensity);
CSfxManager::PitchBend(xb6c_leftRainSound, rainPitch);
CSfxManager::PitchBend(xb70_rightRainSound, rainPitch);
} else if (xb6a_rainSoundActive) {
CSfxManager::RemoveEmitter(xb6c_leftRainSound);
CSfxManager::RemoveEmitter(xb70_rightRainSound);
xb6a_rainSoundActive = false;
}
}
zeus::CVector3f CEnvFxManager::GetParticleBoundsToWorldScale() const {
return (x0_particleBounds.max - x0_particleBounds.min) / 127.f;
}
zeus::CTransform CEnvFxManager::GetParticleBoundsToWorldTransform() const {
return zeus::CTransform::Translate(x18_focusCellPosition) * zeus::CTransform::Translate(zeus::CVector3f(-31.75f)) *
zeus::CTransform::Scale(GetParticleBoundsToWorldScale());
}
void CEnvFxManager::UpdateVisorSplash(CStateManager& mgr, float dt, const zeus::CTransform& camXf) {
EEnvFxType fxType = mgr.GetWorld()->GetNeededEnvFx();
if (xb68_envRainSplashId != kInvalidUniqueId)
if (TCastToPtr<CHUDBillboardEffect> splashEffect = mgr.ObjectById(xb68_envRainSplashId))
mgr.SetActorAreaId(*splashEffect, mgr.GetNextAreaId());
float camUpness = camXf.basis[1].dot(zeus::skUp);
float splashRateFactor;
if (x24_enableSplash)
splashRateFactor = std::max(0.f, camUpness) * x30_fxDensity;
else
splashRateFactor = 0.f;
float forwardRateFactor = 0.f;
if (x24_enableSplash && camUpness >= -0.1f) {
zeus::CVector3f pRelVel = mgr.GetPlayer().GetTransform().transposeRotate(mgr.GetPlayer().GetVelocity());
if (pRelVel.canBeNormalized()) {
float velMag = pRelVel.magnitude();
zeus::CVector3f normRelVel = pRelVel * (1.f / velMag);
forwardRateFactor = std::min(velMag / 60.f, 1.f) * normRelVel.dot(zeus::skForward);
}
}
float additionalFactor;
if (fxType == EEnvFxType::Rain)
additionalFactor = splashRateFactor + forwardRateFactor;
else
additionalFactor = 0.f;
SetSplashEffectRate(xb54_baseSplashRate + additionalFactor, mgr);
xb54_baseSplashRate = 0.f;
}
void CEnvFxManager::MoveWrapCells(s32 moveX, s32 moveY) {
if (moveX == 0 && moveY == 0)
return;
bool r5 = std::fabs(moveX) >= 1.f || std::fabs(moveY) >= 1.f;
s32 moveXMaj = moveX << 11;
s32 moveYMaj = moveY << 11;
for (int i = 0; i < 8; ++i) {
s32 r28 = i - moveY;
for (int j = 0; j < 8; ++j) {
CEnvFxManagerGrid& grid = x50_grids[i * 8 + j];
s32 r3 = j - moveX;
if (!r5) {
CEnvFxManagerGrid& otherGrid = x50_grids[r28 * 8 + r3];
grid.x14_block = otherGrid.x14_block;
} else {
grid.x0_24_blockDirty = true;
}
grid.x4_position =
zeus::CVector2i((moveXMaj + grid.x4_position.x) & 0x3fff, (moveYMaj + grid.x4_position.y) & 0x3fff);
}
}
}
void CEnvFxManager::CalculateSnowForces(const CVectorFixed8_8& zVec,
rstl::reserved_vector<CVectorFixed8_8, 256>& snowForces, EEnvFxType type,
const zeus::CVector3f& oopbtws, float dt) {
if (type == EEnvFxType::Snow) {
CVectorFixed8_8 vecf;
zeus::CVector3f vec;
for (int i = 255; i >= 0; --i) {
const zeus::CVector2f& force = g_SnowForces[i];
zeus::CVector3f delta = zeus::CVector3f(force * dt) * oopbtws;
vec += delta;
CVectorFixed8_8 vecf2(vec);
snowForces.push_back(vecf2 - vecf);
vecf = vecf2;
}
for (int i = 0; i < snowForces.size(); ++i)
snowForces[i] = snowForces[i] + zVec + CVectorFixed8_8(xb84_snowZDeltas[i & 0xf] * dt * oopbtws);
}
}
void CEnvFxManager::BuildBlockObjectList(rstl::reserved_vector<TUniqueId, 1024>& list, CStateManager& mgr) {
for (CEntity* ent : mgr.GetAllObjectList()) {
const TCastToConstPtr<CScriptTrigger> trig = ent;
if (trig && True(trig->GetTriggerFlags() & ETriggerFlags::BlockEnvironmentalEffects)) {
list.push_back(ent->GetUniqueId());
} else if (TCastToConstPtr<CScriptWater>(ent)) {
list.push_back(ent->GetUniqueId());
}
}
}
void CEnvFxManager::UpdateBlockedGrids(CStateManager& mgr, EEnvFxType type, const zeus::CTransform& camXf,
const zeus::CTransform& xf, const zeus::CTransform& invXf) {
zeus::CVector3f playerPos;
if (mgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed)
playerPos = camXf.origin;
else
playerPos = mgr.GetPlayer().GetBallPosition();
zeus::CVector2i localPlayerPos((invXf * playerPos * 256.f).toVec2f());
x2c_lastBlockedGridIdx = -1;
x24_enableSplash = false;
rstl::reserved_vector<TUniqueId, 1024> blockList;
bool blockListBuilt = false;
int blockedGrids = 0;
for (int i = 0; i < x50_grids.size(); ++i) {
CEnvFxManagerGrid& grid = x50_grids[i];
if (blockedGrids < 8 && grid.x0_24_blockDirty) {
if (type == EEnvFxType::UnderwaterFlake) {
grid.x14_block = std::make_pair(true, float(-FLT_MAX));
} else {
constexpr auto filter =
CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid, EMaterialTypes::Trigger},
{EMaterialTypes::ProjectilePassthrough, EMaterialTypes::SeeThrough});
zeus::CVector3f pos =
xf * zeus::CVector3f((grid.x4_position + grid.xc_extent * 0).toVec2f() / 256.f) + zeus::skUp * 500.f;
CRayCastResult result = CGameCollision::RayStaticIntersection(mgr, pos, zeus::skDown, 1000.f, filter);
if (result.IsValid()) {
if (!blockListBuilt) {
BuildBlockObjectList(blockList, mgr);
blockListBuilt = true;
}
for (TUniqueId id : blockList) {
if (TCastToConstPtr<CScriptTrigger> trig = mgr.GetObjectById(id)) {
if (auto tb = trig->GetTouchBounds()) {
CCollidableAABox caabb(*tb, {EMaterialTypes::Trigger});
CRayCastResult result2 = caabb.CastRayInternal({pos, zeus::skDown, 1000.f, {}, filter});
if (result2.IsValid() && result2.GetT() < result.GetT())
result = result2;
}
}
}
}
++blockedGrids;
grid.x14_block = std::make_pair(result.IsValid(), result.GetPoint().z());
}
grid.x0_24_blockDirty = false;
}
zeus::CVector2i gridEnd = grid.x4_position + grid.xc_extent;
if (localPlayerPos.x >= grid.x4_position.x && localPlayerPos.y >= grid.x4_position.y &&
localPlayerPos.x < gridEnd.x && localPlayerPos.y < gridEnd.y && grid.x14_block.first &&
grid.x14_block.second <= playerPos.z()) {
x24_enableSplash = true;
x2c_lastBlockedGridIdx = i;
}
}
}
void CEnvFxManager::CreateNewParticles(EEnvFxType type) {
int maxCellParticleCount;
if (type == EEnvFxType::Snow)
maxCellParticleCount = 0x1c98;
else if (type == EEnvFxType::Rain)
maxCellParticleCount = 0x2af8;
else if (type == EEnvFxType::UnderwaterFlake)
maxCellParticleCount = 0x1fd6;
else
maxCellParticleCount = 0;
maxCellParticleCount >>= 6;
int cellParticleCount = maxCellParticleCount * x30_fxDensity;
static u32 seedStash = 0;
CRandom16 random(seedStash);
for (auto it = x50_grids.rbegin(); it != x50_grids.rend(); ++it) {
if (it->x14_block.first) {
if (cellParticleCount > it->x1c_particles.size()) {
if (cellParticleCount > it->x1c_particles.capacity())
it->x1c_particles.reserve(maxCellParticleCount);
int remCellParticleCount = cellParticleCount - it->x1c_particles.size();
for (int i = 0; i < remCellParticleCount; ++i) {
int x = random.Range(0.f, float(it->xc_extent.x));
int y = random.Range(0.f, float(it->xc_extent.y));
int z = 256.f * random.Range(0.f, 63.f);
it->x1c_particles.emplace_back(x, y, z);
}
} else {
it->x1c_particles.resize(cellParticleCount);
}
}
}
seedStash = random.GetSeed();
}
void CEnvFxManager::UpdateSnowParticles(const rstl::reserved_vector<CVectorFixed8_8, 256>& snowForces) {
for (auto it = x50_grids.rbegin(); it != x50_grids.rend(); ++it) {
int forceIt = int(x28_firstSnowForce);
if (it->x14_block.first) {
for (auto pit = it->x1c_particles.rbegin(); pit != it->x1c_particles.rend(); ++pit) {
const CVectorFixed8_8& force = snowForces[forceIt];
forceIt = (forceIt + 1) & 0xff;
*pit = *pit + force;
pit->z = s16(pit->z & 0x3fff);
}
}
}
}
void CEnvFxManager::UpdateRainParticles(const CVectorFixed8_8& zVec, const zeus::CVector3f& oopbtws, float dt) {
s16 deltaZ = zVec.z + s16(-40.f * dt * oopbtws.z() * 256.f);
for (auto it = x50_grids.rbegin(); it != x50_grids.rend(); ++it)
for (auto pit = it->x1c_particles.rbegin(); pit != it->x1c_particles.rend(); ++pit)
pit->z = s16((pit->z + deltaZ) & 0x3fff);
}
void CEnvFxManager::UpdateUnderwaterParticles(const CVectorFixed8_8& zVec) {
for (auto it = x50_grids.rbegin(); it != x50_grids.rend(); ++it)
for (auto pit = it->x1c_particles.rbegin(); pit != it->x1c_particles.rend(); ++pit)
pit->z = s16((pit->z + zVec.z) & 0x3fff);
}
void CEnvFxManager::Update(float dt, CStateManager& mgr) {
EEnvFxType fxType = mgr.GetWorld()->GetNeededEnvFx();
zeus::CTransform camXf = mgr.GetCameraManager()->GetCurrentCameraTransform(mgr);
if (mgr.GetCameraManager()->GetFluidCounter() != 0) {
x2c_lastBlockedGridIdx = -1;
x24_enableSplash = false;
SetSplashEffectRate(0.f, mgr);
}
UpdateRainSounds(mgr);
UpdateVisorSplash(mgr, dt, camXf);
if (fxType == EEnvFxType::None) {
for (auto it = x50_grids.rbegin(); it != x50_grids.rend(); ++it)
if (it->x14_block.first)
it->x1c_particles = std::vector<CVectorFixed8_8>();
} else {
float densityDelta = x34_targetFxDensity - x30_fxDensity;
float densityDeltaDamper = std::min(std::fabs(densityDelta) / 0.15f, 1.f);
float maxDensityDelta = x38_maxDensityDeltaSpeed / 11000.f * dt;
if (std::fabs(densityDelta) > maxDensityDelta)
densityDelta = (densityDelta > 0.f ? 1.f : -1.f) * maxDensityDelta;
x30_fxDensity += densityDeltaDamper * densityDelta;
zeus::CVector3f pbtws = GetParticleBoundsToWorldScale();
zeus::CVector3f oopbtws = 1.f / pbtws;
zeus::CVector3f forwardPoint = camXf.basis[1] * 23.8125f + camXf.origin;
float modX = std::fmod(forwardPoint.x(), 7.9375f);
float modY = std::fmod(forwardPoint.y(), 7.9375f);
s32 moveX = (x18_focusCellPosition.x() - (forwardPoint.x() - modX)) / 7.9375f;
x18_focusCellPosition.x() = forwardPoint.x() - modX;
s32 moveY = (x18_focusCellPosition.y() - (forwardPoint.y() - modY)) / 7.9375f;
x18_focusCellPosition.y() = forwardPoint.y() - modY;
float deltaZ = x18_focusCellPosition.z() - forwardPoint.z();
x18_focusCellPosition.z() = float(forwardPoint.z());
MoveWrapCells(moveX, moveY);
CVectorFixed8_8 zVec(oopbtws * zeus::CVector3f(0.f, 0.f, deltaZ));
if (fxType == EEnvFxType::UnderwaterFlake)
zVec.z += s16(256.f * 0.5f * dt);
rstl::reserved_vector<CVectorFixed8_8, 256> snowForces;
CalculateSnowForces(zVec, snowForces, fxType, oopbtws, dt);
zeus::CTransform xf = GetParticleBoundsToWorldTransform();
zeus::CTransform invXf = xf.inverse();
UpdateBlockedGrids(mgr, fxType, camXf, xf, invXf);
CreateNewParticles(fxType);
switch (fxType) {
case EEnvFxType::Snow:
UpdateSnowParticles(snowForces);
break;
case EEnvFxType::Rain:
UpdateRainParticles(zVec, oopbtws, dt);
break;
case EEnvFxType::UnderwaterFlake:
UpdateUnderwaterParticles(zVec);
break;
default:
break;
}
if (fxType == EEnvFxType::Snow)
x28_firstSnowForce = std::fmod(1.f + x28_firstSnowForce, 256.f);
else
x28_firstSnowForce = std::fmod(0.125f + x28_firstSnowForce, 256.f);
}
}
static zeus::CColor GetFlakeColor(const zeus::CMatrix4f& mvp, const CEnvFxShaders::Instance& inst) {
float screenHeight =
std::fabs(mvp.multiplyOneOverW(inst.positions[1]).y() - mvp.multiplyOneOverW(inst.positions[0]).y()) / 2.f;
screenHeight -= (32.f / 480.f);
screenHeight /= (32.f / 480.f);
return zeus::CColor(1.f - zeus::clamp(0.f, screenHeight, 1.f), 1.f);
}
void CEnvFxManagerGrid::RenderSnowParticles(const zeus::CTransform& camXf) {
const zeus::CVector3f xVec = 0.2f * camXf.basis[0];
const zeus::CVector3f zVec = 0.2f * camXf.basis[2];
const zeus::CMatrix4f mvp = CGraphics::GetPerspectiveProjectionMatrix(false) * CGraphics::g_GXModelView.toMatrix4f();
auto* bufOut = m_instBuf.map();
for (const auto& particle : x1c_particles) {
bufOut->positions[0] = particle.toVec3f();
bufOut->uvs[0] = zeus::CVector2f(0.f, 0.f);
bufOut->positions[1] = bufOut->positions[0] + zVec;
bufOut->uvs[1] = zeus::CVector2f(0.f, 1.f);
bufOut->positions[3] = bufOut->positions[1] + xVec;
bufOut->uvs[3] = zeus::CVector2f(1.f, 1.f);
bufOut->positions[2] = bufOut->positions[3] - zVec;
bufOut->uvs[2] = zeus::CVector2f(1.f, 0.f);
bufOut->color = GetFlakeColor(mvp, *bufOut);
++bufOut;
}
m_instBuf.unmap();
m_snowBinding.draw_instanced(0, 4, x1c_particles.size());
}
void CEnvFxManagerGrid::RenderRainParticles(const zeus::CTransform& camXf) {
m_lineRenderer.Reset();
const float zOffset = 2.f * (1.f - std::fabs(camXf.basis[2].dot(zeus::skUp))) + 1.f;
const zeus::CColor color0(1.f, 10.f / 15.f);
for (const auto& particle : x1c_particles) {
const zeus::CVector3f pos0 = particle.toVec3f();
zeus::CVector3f pos1 = pos0;
pos1.z() += zOffset;
const float uvy0 = pos0.z() * 10.f + m_uvyOffset;
const float uvy1 = pos1.z() * 10.f + m_uvyOffset;
m_lineRenderer.AddVertex(pos0, zeus::skWhite, 1.f, {0.f, uvy0});
m_lineRenderer.AddVertex(pos1, zeus::skClear, 1.f, {0.f, uvy1});
}
m_lineRenderer.Render(g_Renderer->IsThermalVisorHotPass(), zeus::CColor(1.f, 0.15f));
}
void CEnvFxManagerGrid::RenderUnderwaterParticles(const zeus::CTransform& camXf) {
const zeus::CVector3f xVec = 0.5f * camXf.basis[0];
const zeus::CVector3f zVec = 0.5f * camXf.basis[2];
const zeus::CMatrix4f mvp = CGraphics::GetPerspectiveProjectionMatrix(false) * CGraphics::g_GXModelView.toMatrix4f();
auto* bufOut = m_instBuf.map();
for (const auto& particle : x1c_particles) {
bufOut->positions[0] = particle.toVec3f();
bufOut->uvs[0] = zeus::CVector2f(0.f, 0.f);
bufOut->positions[1] = bufOut->positions[0] + zVec;
bufOut->uvs[1] = zeus::CVector2f(0.f, 1.f);
bufOut->positions[3] = bufOut->positions[1] + xVec;
bufOut->uvs[3] = zeus::CVector2f(1.f, 1.f);
bufOut->positions[2] = bufOut->positions[3] - zVec;
bufOut->uvs[2] = zeus::CVector2f(1.f, 0.f);
bufOut->color = GetFlakeColor(mvp, *bufOut);
++bufOut;
}
m_instBuf.unmap();
m_underwaterBinding.draw_instanced(0, 4, x1c_particles.size());
}
void CEnvFxManagerGrid::Render(const zeus::CTransform& xf, const zeus::CTransform& invXf, const zeus::CTransform& camXf,
float fxDensity, EEnvFxType fxType, CEnvFxManager& parent) {
if (!x1c_particles.empty() && x14_block.first) {
CGraphics::SetModelMatrix(xf * zeus::CTransform::Translate(x4_position.toVec2f() / 256.f));
parent.m_uniformData.mv = CGraphics::g_GXModelView.toMatrix4f();
parent.m_uniformData.proj = CGraphics::GetPerspectiveProjectionMatrix(true);
switch (fxType) {
case EEnvFxType::Snow:
case EEnvFxType::Rain: {
zeus::CMatrix4f envTexMtx(true);
envTexMtx[2][1] = 10.f;
envTexMtx[3][1] = 0.5f - (invXf * (zeus::skUp * x14_block.second)).z() * 10.f;
m_uvyOffset = envTexMtx[3][1];
parent.m_uniformData.envMtx = envTexMtx;
break;
}
default:
break;
}
m_uniformBuf.load(parent.m_uniformData);
switch (fxType) {
case EEnvFxType::Snow:
RenderSnowParticles(camXf);
break;
case EEnvFxType::Rain:
RenderRainParticles(camXf);
break;
case EEnvFxType::UnderwaterFlake:
RenderUnderwaterParticles(camXf);
break;
default:
break;
}
}
}
void CEnvFxManager::SetupSnowTevs(const CStateManager& mgr) {
mgr.GetCameraManager()->GetCurrentCamera(mgr);
if (mgr.GetCameraManager()->GetFluidCounter() != 0) {
g_Renderer->SetWorldFog(ERglFogMode::PerspExp, 0.f, 35.f, zeus::skBlack);
m_uniformData.moduColor = zeus::CColor(1.f, 0.5f);
} else {
g_Renderer->SetWorldFog(ERglFogMode::PerspLin, 52.f, 57.f, zeus::skBlack);
}
// Blend One One
// 2 stages
// xb74_txtrSnowFlake
// Texcoord0: 2x4, TEX0, GX_IDENTITY, no norm, GX_PTTIDENTITY
// 0: Standard alpha, map0, tcg0
// Color: Zero, Konst, TexC, Zero
// Alpha: Zero, Konst, TexA, Zero
// x40_txtrEnvGradient
// Texcoord1: 2x4, POS, GX_TEXMTX5, no norm, GX_PTTIDENTITY
// 0: Standard alpha, map0, tcg0
// Color: Zero, TexC, CPrev, Zero
// Alpha: Zero, Zero, Zero, TexA
}
void CEnvFxManager::SetupRainTevs() const {
// Line-width 1px
// Blend SrcAlpha One
// x40_txtrEnvGradient
// Texcoord0: 2x4, POS, GX_TEXMTX5, no norm, GX_PTTIDENTITY
// 0: Standard alpha, map0, tcg0
// Color: Zero, Zero, Zero, TexC
// Alpha: Zero, RasA, Konst, Zero
// Ras vertex color
// KAlpha 0.15
}
void CEnvFxManager::SetupUnderwaterTevs(const zeus::CTransform& invXf, const CStateManager& mgr) const {
// Blend SrcAlpha InvSrcAlpha
// xc48_underwaterFlake
// Texcoord0: 2x4, TEX0, GX_IDENTITY, no norm, GX_PTTIDENTITY
// Color: Zero, Zero, Zero, TexC
// Alpha: Zero, Zero, Zero, TexA
float waterTop = FLT_MAX;
for (CEntity* ent : mgr.GetAllObjectList())
if (TCastToPtr<CScriptWater> water = ent)
if (auto tb = water->GetTouchBounds())
waterTop = std::min(waterTop, float(tb->max.z()));
zeus::CVector3f localWaterTop = invXf * (zeus::skUp * waterTop);
zeus::CMatrix4f envTexMtx(true);
envTexMtx[2][1] = -10.f;
envTexMtx[3][1] = localWaterTop.z() * -10.f + 0.5f;
// Load into texmtx5
// x40_txtrEnvGradient
// Texcoord1: 2x4, POS, GX_TEXMTX5, no norm, GX_PTTIDENTITY
// MTX: y-scale -10.0 of Z, y-trans -10.0 * ()
// 1: Standard alpha, map1, tcg1
// Color: Zero, One, CPrev, Zero
// Alpha: Zero, TexA, APrev, Zero
// Swap: RGBR
}
void CEnvFxManager::Render(const CStateManager& mgr) {
EEnvFxType fxType = mgr.GetWorld()->GetNeededEnvFx();
if (fxType != EEnvFxType::None) {
if (mgr.GetPlayer().GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Unmorphed ||
(mgr.GetPlayerState()->GetCurrentVisor() != CPlayerState::EPlayerVisor::Thermal &&
(fxType != EEnvFxType::Snow || mgr.GetPlayerState()->GetCurrentVisor() != CPlayerState::EPlayerVisor::XRay))) {
SCOPED_GRAPHICS_DEBUG_GROUP("CEnvFxManager::Render", zeus::skCyan);
// No Cull
// ZTest, No ZWrite
zeus::CTransform xf = GetParticleBoundsToWorldTransform();
zeus::CTransform invXf = xf.inverse();
zeus::CTransform camXf = mgr.GetCameraManager()->GetCurrentCameraTransform(mgr);
m_uniformData.moduColor = zeus::skWhite;
switch (fxType) {
case EEnvFxType::Snow:
SetupSnowTevs(mgr);
break;
case EEnvFxType::Rain:
SetupRainTevs();
break;
case EEnvFxType::UnderwaterFlake:
SetupUnderwaterTevs(invXf, mgr);
break;
default:
break;
}
m_fogUniformBuf.load(CGraphics::g_Fog);
for (auto& grid : x50_grids)
grid.Render(xf, invXf, camXf, x30_fxDensity, fxType, *this);
// Backface cull
}
}
}
void CEnvFxManager::AsyncLoadResources(CStateManager& mgr) {
xb68_envRainSplashId = mgr.AllocateUniqueId();
auto* effect =
new CHUDBillboardEffect(xb58_envRainSplash, {}, xb68_envRainSplashId, true, "VisorRainSplashes",
CHUDBillboardEffect::GetNearClipDistance(mgr), CHUDBillboardEffect::GetScaleForPOV(mgr),
zeus::skWhite, zeus::skOne3f, zeus::skZero3f);
effect->SetRunIndefinitely(true);
mgr.AddObject(effect);
}
void CEnvFxManager::SetFxDensity(s32 val, float density) {
x34_targetFxDensity = density;
x38_maxDensityDeltaSpeed = val;
}
void CEnvFxManager::AreaLoaded() {
for (CEnvFxManagerGrid& grid : x50_grids)
grid.x0_24_blockDirty = true;
}
void CEnvFxManager::Cleanup() {
xb68_envRainSplashId = kInvalidUniqueId;
xb6a_rainSoundActive = false;
xb6c_leftRainSound.reset();
xb70_rightRainSound.reset();
}
void CEnvFxManager::Initialize() {
const SObjectTag* tag = g_ResFactory->GetResourceIdByName("DUMB_SnowForces");
std::unique_ptr<u8[]> data = g_ResFactory->LoadResourceSync(*tag);
athena::io::MemoryReader r(data.get(), 2048);
for (int i = 0; i < 256; ++i)
g_SnowForces.push_back(r.readVec2fBig());
}
} // namespace metaforce