mirror of https://github.com/AxioDL/metaforce.git
605 lines
23 KiB
C++
605 lines
23 KiB
C++
#include "Runtime/World/CScriptWater.hpp"
|
|
|
|
#include <array>
|
|
|
|
#include "Runtime/CSimplePool.hpp"
|
|
#include "Runtime/CStateManager.hpp"
|
|
#include "Runtime/GameGlobalObjects.hpp"
|
|
#include "Runtime/Camera/CGameCamera.hpp"
|
|
#include "Runtime/Graphics/CCubeRenderer.hpp"
|
|
#include "Runtime/World/CFluidPlaneGPU.hpp"
|
|
#include "Runtime/World/CWorld.hpp"
|
|
|
|
#include "TCastTo.hpp" // Generated file, do not modify include path
|
|
|
|
namespace metaforce {
|
|
constexpr std::array kSplashScales{
|
|
1.0f, 3.0f, 0.709f, 1.19f, 0.709f, 1.f,
|
|
};
|
|
|
|
CScriptWater::CScriptWater(
|
|
CStateManager& mgr, TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CVector3f& pos,
|
|
const zeus::CAABox& box, const metaforce::CDamageInfo& dInfo, zeus::CVector3f& orientedForce,
|
|
ETriggerFlags triggerFlags, bool thermalCold, bool allowRender, CAssetId patternMap1, CAssetId patternMap2,
|
|
CAssetId colorMap, CAssetId bumpMap, CAssetId envMap, CAssetId envBumpMap, CAssetId unusedMap,
|
|
const zeus::CVector3f& bumpLightDir, float bumpScale, float morphInTime, float morphOutTime, bool active,
|
|
EFluidType fluidType, bool b4, float alpha, const CFluidUVMotion& uvMot, float turbSpeed, float turbDistance,
|
|
float turbFreqMax, float turbFreqMin, float turbPhaseMax, float turbPhaseMin, float turbAmplitudeMax,
|
|
float turbAmplitudeMin, const zeus::CColor& splashColor, const zeus::CColor& insideFogColor,
|
|
CAssetId splashParticle1, CAssetId splashParticle2, CAssetId splashParticle3, CAssetId visorRunoffParticle,
|
|
CAssetId unmorphVisorRunoffparticle, s32 visorRunoffSfx, s32 unmorphVisorRunoffSfx, s32 splashSfx1, s32 splashSfx2,
|
|
s32 splashSfx3, float tileSize, u32 tileSubdivisions, float specularMin, float specularMax, float reflectionSize,
|
|
float rippleIntensity, float reflectionBlend, float fogBias, float fogMagnitude, float fogSpeed,
|
|
const zeus::CColor& fogColor, CAssetId lightmapId, float unitsPerLightmapTexel, float alphaInTime,
|
|
float alphaOutTime, u32, u32, bool, s32, s32, std::unique_ptr<u32[]>&& u32Arr)
|
|
: CScriptTrigger(uid, name, info, pos, box, dInfo, orientedForce, triggerFlags, active, false, false)
|
|
, x1b8_positionMorphed(pos)
|
|
, x1c4_extentMorphed(box.max - box.min)
|
|
, x1d0_morphInTime(morphInTime)
|
|
, x1d4_positionOrig(pos)
|
|
, x1e0_extentOrig(box.max - box.min)
|
|
, x1ec_damageOrig(dInfo.GetDamage())
|
|
, x1f0_damageMorphed(dInfo.GetDamage())
|
|
, x1f4_morphOutTime(morphOutTime)
|
|
, x214_fogBias(fogBias)
|
|
, x218_fogMagnitude(fogMagnitude)
|
|
, x21c_origFogBias(fogBias)
|
|
, x220_origFogMagnitude(fogMagnitude)
|
|
, x224_fogSpeed(fogSpeed)
|
|
, x228_fogColor(fogColor)
|
|
, x22c_splashParticle1Id(splashParticle1)
|
|
, x230_splashParticle2Id(splashParticle2)
|
|
, x234_splashParticle3Id(splashParticle3)
|
|
, x238_visorRunoffParticleId(visorRunoffParticle)
|
|
, x24c_unmorphVisorRunoffParticleId(unmorphVisorRunoffparticle)
|
|
, x260_visorRunoffSfx(CSfxManager::TranslateSFXID(visorRunoffSfx))
|
|
, x262_unmorphVisorRunoffSfx(CSfxManager::TranslateSFXID(unmorphVisorRunoffSfx))
|
|
, x2a4_splashColor(splashColor)
|
|
, x2a8_insideFogColor(insideFogColor)
|
|
, x2ac_alphaInTime(alphaInTime)
|
|
, x2b0_alphaOutTime(alphaOutTime)
|
|
, x2b4_alphaInRecip((alphaInTime != 0.f) ? 1.f / alphaInTime : 0.f)
|
|
, x2b8_alphaOutRecip((alphaOutTime != 0.f) ? 1.f / alphaOutTime : 0.f)
|
|
, x2bc_alpha(alpha)
|
|
, x2c0_tileSize(tileSize)
|
|
, x2e8_24_b4(b4)
|
|
, x2e8_27_allowRender(allowRender) {
|
|
const zeus::CAABox triggerAABB = GetTriggerBoundsWR();
|
|
x2c4_gridDimX = u32((x2c0_tileSize + triggerAABB.max.x() - triggerAABB.min.x() - 0.01f) / x2c0_tileSize);
|
|
x2c8_gridDimY = u32((x2c0_tileSize + triggerAABB.max.y() - triggerAABB.min.y() - 0.01f) / x2c0_tileSize);
|
|
x2cc_gridCellCount = (x2c4_gridDimX + 1) * (x2c8_gridDimY + 1);
|
|
|
|
uint32_t maxPatchSize;
|
|
// if (CGraphics::g_BooFactory->isTessellationSupported(maxPatchSize)) {
|
|
// x1b4_fluidPlane = std::make_unique<CFluidPlaneGPU>(
|
|
// patternMap1, patternMap2, colorMap, bumpMap, envMap, envBumpMap, lightmapId, unitsPerLightmapTexel, tileSize,
|
|
// tileSubdivisions * 2, fluidType, x2bc_alpha, bumpLightDir, bumpScale, uvMot, turbSpeed, turbDistance,
|
|
// turbFreqMax, turbFreqMin, turbPhaseMax, turbPhaseMin, turbAmplitudeMax, turbAmplitudeMin, specularMin,
|
|
// specularMax, reflectionBlend, reflectionSize, rippleIntensity,
|
|
// x2cc_gridCellCount * ((std::max(u32(2), tileSubdivisions * 2) * 4 + 2) * 4));
|
|
// } else {
|
|
x1b4_fluidPlane = std::make_unique<CFluidPlaneCPU>(
|
|
patternMap1, patternMap2, colorMap, bumpMap, envMap, envBumpMap, lightmapId, unitsPerLightmapTexel, tileSize,
|
|
tileSubdivisions, fluidType, x2bc_alpha, bumpLightDir, bumpScale, uvMot, turbSpeed, turbDistance, turbFreqMax,
|
|
turbFreqMin, turbPhaseMax, turbPhaseMin, turbAmplitudeMax, turbAmplitudeMin, specularMin, specularMax,
|
|
reflectionBlend, reflectionSize, rippleIntensity,
|
|
x2cc_gridCellCount * ((std::max(u32(2), tileSubdivisions) * 4 + 2) * 4));
|
|
// }
|
|
u32Arr.reset();
|
|
x264_splashEffects.resize(3);
|
|
if (x22c_splashParticle1Id.IsValid()) {
|
|
x264_splashEffects[0].emplace(g_SimplePool->GetObj({FOURCC('PART'), x22c_splashParticle1Id}));
|
|
}
|
|
if (x230_splashParticle2Id.IsValid()) {
|
|
x264_splashEffects[1].emplace(g_SimplePool->GetObj({FOURCC('PART'), x230_splashParticle2Id}));
|
|
}
|
|
if (x234_splashParticle3Id.IsValid()) {
|
|
x264_splashEffects[2].emplace(g_SimplePool->GetObj({FOURCC('PART'), x234_splashParticle3Id}));
|
|
}
|
|
if (x238_visorRunoffParticleId.IsValid()) {
|
|
x23c_visorRunoffEffect.emplace(g_SimplePool->GetObj({FOURCC('PART'), x238_visorRunoffParticleId}));
|
|
}
|
|
if (x24c_unmorphVisorRunoffParticleId.IsValid()) {
|
|
x250_unmorphVisorRunoffEffect.emplace(g_SimplePool->GetObj({FOURCC('PART'), x24c_unmorphVisorRunoffParticleId}));
|
|
}
|
|
x298_splashSounds.push_back(CSfxManager::TranslateSFXID(splashSfx1));
|
|
x298_splashSounds.push_back(CSfxManager::TranslateSFXID(splashSfx2));
|
|
x298_splashSounds.push_back(CSfxManager::TranslateSFXID(splashSfx3));
|
|
SetCalculateLighting(true);
|
|
if (lightmapId.IsValid()) {
|
|
x90_actorLights->DisableAreaLights();
|
|
}
|
|
x90_actorLights->SetMaxDynamicLights(4);
|
|
x90_actorLights->SetCastShadows(false);
|
|
x90_actorLights->SetAmbienceGenerated(false);
|
|
x90_actorLights->SetFindNearestDynamicLights(true);
|
|
x148_24_detectCamera = true;
|
|
CScriptWater::CalculateRenderBounds();
|
|
xe6_27_thermalVisorFlags = u8(thermalCold ? 2 : 1);
|
|
if (!x30_24_active) {
|
|
x2bc_alpha = 0.f;
|
|
x214_fogBias = 0.f;
|
|
x218_fogMagnitude = 0.f;
|
|
}
|
|
SetupGrid(true);
|
|
}
|
|
|
|
void CScriptWater::SetupGrid(bool recomputeClipping) {
|
|
const zeus::CAABox triggerAABB = GetTriggerBoundsWR();
|
|
const auto dimX = u32((triggerAABB.max.x() - triggerAABB.min.x() + x2c0_tileSize) / x2c0_tileSize);
|
|
const auto dimY = u32((triggerAABB.max.y() - triggerAABB.min.y() + x2c0_tileSize) / x2c0_tileSize);
|
|
x2e4_computedGridCellCount = x2cc_gridCellCount = (dimX + 1) * (dimY + 1);
|
|
x2dc_vertIntersects.reset();
|
|
if (!x2d8_tileIntersects || dimX != x2c4_gridDimX || dimY != x2c8_gridDimY) {
|
|
x2d8_tileIntersects.reset(new bool[x2cc_gridCellCount]);
|
|
}
|
|
x2c4_gridDimX = dimX;
|
|
x2c8_gridDimY = dimY;
|
|
for (int i = 0; i < x2c8_gridDimY; ++i) {
|
|
for (int j = 0; j < x2c4_gridDimX; ++j) {
|
|
x2d8_tileIntersects[i * x2c4_gridDimX + j] = true;
|
|
}
|
|
}
|
|
if (!x2e0_patchIntersects || x2d0_patchDimX != 0 || x2d4_patchDimY != 0) {
|
|
x2e0_patchIntersects.reset(new u8[32]);
|
|
}
|
|
for (int i = 0; i < 32; ++i) {
|
|
x2e0_patchIntersects[i] = 1;
|
|
}
|
|
x2d4_patchDimY = 0;
|
|
x2d0_patchDimX = 0;
|
|
x2e8_28_recomputeClipping = recomputeClipping;
|
|
}
|
|
|
|
constexpr CMaterialFilter SolidFilter = CMaterialFilter::MakeInclude({EMaterialTypes::Solid});
|
|
|
|
void CScriptWater::SetupGridClipping(CStateManager& mgr, int computeVerts) {
|
|
if (x2e8_28_recomputeClipping) {
|
|
x2e4_computedGridCellCount = 0;
|
|
x2dc_vertIntersects.reset();
|
|
x2e8_28_recomputeClipping = false;
|
|
}
|
|
|
|
if (x2e4_computedGridCellCount >= x2cc_gridCellCount) {
|
|
return;
|
|
}
|
|
|
|
if (!x2dc_vertIntersects) {
|
|
x2dc_vertIntersects.reset(new bool[(x2c4_gridDimX + 1) * (x2c8_gridDimY + 1)]);
|
|
}
|
|
|
|
const zeus::CAABox triggerBounds = GetTriggerBoundsWR();
|
|
zeus::CVector3f basePos = triggerBounds.min;
|
|
basePos.z() = triggerBounds.max.z() + 0.8f;
|
|
auto gridDiv = std::div(x2e4_computedGridCellCount, x2c4_gridDimX + 1);
|
|
float yOffset = x2c0_tileSize * gridDiv.quot;
|
|
float xOffset = x2c0_tileSize * gridDiv.rem;
|
|
float mag = std::min(120.f, 2.f * (x130_bounds.max.z() - x130_bounds.min.z()) + 0.8f);
|
|
for (int i = x2e4_computedGridCellCount; i < std::min(x2e4_computedGridCellCount + computeVerts, x2cc_gridCellCount);
|
|
++i) {
|
|
zeus::CVector3f pos = basePos;
|
|
pos.x() += xOffset;
|
|
pos.y() += yOffset;
|
|
x2dc_vertIntersects[i] = mgr.RayStaticIntersection(pos, zeus::skDown, mag, SolidFilter).IsValid();
|
|
gridDiv.rem += 1;
|
|
xOffset += x2c0_tileSize;
|
|
if (gridDiv.rem > x2c4_gridDimX) {
|
|
yOffset += x2c0_tileSize;
|
|
xOffset = 0.f;
|
|
gridDiv.rem = 0;
|
|
}
|
|
}
|
|
x2e4_computedGridCellCount += computeVerts;
|
|
if (x2e4_computedGridCellCount < x2cc_gridCellCount) {
|
|
return;
|
|
}
|
|
|
|
x2e4_computedGridCellCount = x2cc_gridCellCount;
|
|
x2d8_tileIntersects.reset(new bool[x2cc_gridCellCount]);
|
|
|
|
for (int i = 0; i < x2c8_gridDimY; ++i) {
|
|
const int rowBase = x2c4_gridDimX * i;
|
|
const int nextRowBase = (x2c4_gridDimX + 1) * i;
|
|
for (int j = 0; j < x2c4_gridDimX; ++j) {
|
|
x2d8_tileIntersects[rowBase + j] = x2dc_vertIntersects[nextRowBase + j] ||
|
|
x2dc_vertIntersects[nextRowBase + j + 1] ||
|
|
x2dc_vertIntersects[nextRowBase + j + x2c4_gridDimX + 1] ||
|
|
x2dc_vertIntersects[nextRowBase + j + x2c4_gridDimX + 2];
|
|
}
|
|
}
|
|
|
|
const int tilesPerPatch = std::min(42 / x1b4_fluidPlane->GetTileSubdivisions(), 7);
|
|
x2d0_patchDimX = (tilesPerPatch + x2c4_gridDimX - 1) / tilesPerPatch;
|
|
x2d4_patchDimY = (tilesPerPatch + x2c8_gridDimY - 1) / tilesPerPatch;
|
|
x2e0_patchIntersects.reset(new u8[x2d0_patchDimX * x2d4_patchDimY]);
|
|
|
|
int curTileY = 0;
|
|
int nextTileY;
|
|
for (int i = 0; i < x2d4_patchDimY; ++i, curTileY = nextTileY) {
|
|
nextTileY = curTileY + tilesPerPatch;
|
|
int curTileX = 0;
|
|
const int rowBase = x2d0_patchDimX * i;
|
|
for (int j = 0; j < x2d0_patchDimX; ++j) {
|
|
int nextTileX = curTileX + tilesPerPatch;
|
|
bool allClear = true;
|
|
bool allIntersections = true;
|
|
for (int k = curTileY; k < std::min(nextTileY, x2c8_gridDimY); ++k) {
|
|
if (!allClear && !allIntersections) {
|
|
break;
|
|
}
|
|
for (int l = curTileX; l < std::min(nextTileX, x2c4_gridDimX); ++l) {
|
|
if (x2d8_tileIntersects[k * x2c4_gridDimX + l]) {
|
|
allClear = false;
|
|
if (!allIntersections) {
|
|
break;
|
|
}
|
|
} else {
|
|
allIntersections = false;
|
|
if (!allClear) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
u8 flag = 2;
|
|
if (allIntersections) {
|
|
flag = 1;
|
|
} else if (allClear) {
|
|
flag = 0;
|
|
}
|
|
|
|
x2e0_patchIntersects[rowBase + j] = flag;
|
|
curTileX += tilesPerPatch;
|
|
}
|
|
}
|
|
|
|
x2dc_vertIntersects.reset();
|
|
}
|
|
|
|
void CScriptWater::UpdateSplashInhabitants(CStateManager& mgr) {
|
|
for (auto it = x1fc_waterInhabitants.begin(); it != x1fc_waterInhabitants.end();) {
|
|
auto& inhab = *it;
|
|
const TCastToPtr<CActor> act = mgr.ObjectById(inhab.first);
|
|
bool intersects = false;
|
|
if (act) {
|
|
if (const auto tb = act->GetTouchBounds()) {
|
|
const zeus::CAABox thisTb = GetTriggerBoundsWR();
|
|
if (tb->min.z() <= thisTb.max.z() && tb->max.z() >= thisTb.max.z()) {
|
|
intersects = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (act && inhab.second) {
|
|
if (intersects) {
|
|
act->FluidFXThink(EFluidState::InFluid, *this, mgr);
|
|
}
|
|
mgr.SendScriptMsg(act.GetPtr(), GetUniqueId(), EScriptObjectMessage::UpdateSplashInhabitant);
|
|
inhab.second = false;
|
|
} else {
|
|
it = x1fc_waterInhabitants.erase(it);
|
|
if (act) {
|
|
if (intersects) {
|
|
act->FluidFXThink(EFluidState::LeftFluid, *this, mgr);
|
|
}
|
|
mgr.SendScriptMsg(act.GetPtr(), GetUniqueId(), EScriptObjectMessage::RemoveSplashInhabitant);
|
|
}
|
|
continue;
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
|
|
void CScriptWater::Accept(IVisitor& visitor) { visitor.Visit(this); }
|
|
|
|
void CScriptWater::Think(float dt, CStateManager& mgr) {
|
|
if (!x30_24_active) {
|
|
return;
|
|
}
|
|
|
|
const bool oldCamSubmerged = x148_25_camSubmerged;
|
|
CScriptTrigger::Think(dt, mgr);
|
|
|
|
CGameCamera* curCam = mgr.GetCameraManager()->GetCurrentCamera(mgr);
|
|
if (x148_25_camSubmerged && !oldCamSubmerged) {
|
|
mgr.SendScriptMsg(curCam, x8_uid, EScriptObjectMessage::AddSplashInhabitant);
|
|
} else if (!x148_25_camSubmerged && oldCamSubmerged) {
|
|
mgr.SendScriptMsg(curCam, x8_uid, EScriptObjectMessage::RemoveSplashInhabitant);
|
|
}
|
|
|
|
UpdateSplashInhabitants(mgr);
|
|
|
|
if (x2e8_30_alphaOut) {
|
|
x2bc_alpha -= x2b8_alphaOutRecip * dt * x1b4_fluidPlane->GetAlpha();
|
|
x214_fogBias -= x2b8_alphaOutRecip * dt * x21c_origFogBias;
|
|
x218_fogMagnitude -= x2b8_alphaOutRecip * dt * x220_origFogMagnitude;
|
|
if (x2bc_alpha <= 0.f) {
|
|
x218_fogMagnitude = 0.f;
|
|
x214_fogBias = 0.f;
|
|
x2bc_alpha = 0.f;
|
|
x2e8_30_alphaOut = false;
|
|
}
|
|
} else if (x2e8_29_alphaIn) {
|
|
x2bc_alpha += x2b4_alphaInRecip * dt * x1b4_fluidPlane->GetAlpha();
|
|
x214_fogBias -= x2b4_alphaInRecip * dt * x21c_origFogBias;
|
|
x218_fogMagnitude -= x2b4_alphaInRecip * dt * x220_origFogMagnitude;
|
|
if (x2bc_alpha > x1b4_fluidPlane->GetAlpha()) {
|
|
x2bc_alpha = x1b4_fluidPlane->GetAlpha();
|
|
x214_fogBias = x21c_origFogBias;
|
|
x218_fogMagnitude = x220_origFogMagnitude;
|
|
x2e8_29_alphaIn = false;
|
|
}
|
|
}
|
|
|
|
if (x2e8_26_morphing) {
|
|
bool stillMorphing = true;
|
|
if (x2e8_25_morphIn) {
|
|
x1f8_morphFactor += dt / x1d0_morphInTime;
|
|
if (x1f8_morphFactor > 1.f) {
|
|
x1f8_morphFactor = 1.f;
|
|
stillMorphing = false;
|
|
}
|
|
} else {
|
|
x1f8_morphFactor -= dt / x1f4_morphOutTime;
|
|
if (x1f8_morphFactor < 0.f) {
|
|
x1f8_morphFactor = 0.f;
|
|
stillMorphing = false;
|
|
}
|
|
}
|
|
|
|
SetTranslation(zeus::CVector3f::lerp(x1d4_positionOrig, x1b8_positionMorphed, x1f8_morphFactor));
|
|
const zeus::CVector3f lerpExtent = zeus::CVector3f::lerp(x1e0_extentOrig, x1c4_extentMorphed, x1f8_morphFactor);
|
|
x130_bounds = zeus::CAABox(lerpExtent * -0.5f, lerpExtent * 0.5f);
|
|
CalculateRenderBounds();
|
|
|
|
if (!stillMorphing) {
|
|
SetMorphing(false);
|
|
} else {
|
|
SetupGrid(false);
|
|
}
|
|
}
|
|
|
|
SetupGridClipping(mgr, 4);
|
|
}
|
|
|
|
void CScriptWater::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId other, CStateManager& mgr) {
|
|
switch (msg) {
|
|
case EScriptObjectMessage::Next:
|
|
if (!x30_24_active) {
|
|
break;
|
|
}
|
|
x2e8_25_morphIn = !x2e8_25_morphIn;
|
|
if (x2e8_25_morphIn) {
|
|
for (const SConnection& conn : x20_conns) {
|
|
if (conn.x0_state != EScriptObjectState::Play || conn.x4_msg != EScriptObjectMessage::Activate) {
|
|
continue;
|
|
}
|
|
const auto list = mgr.GetIdListForScript(conn.x8_objId);
|
|
if (list.first == mgr.GetIdListEnd()) {
|
|
continue;
|
|
}
|
|
if (const TCastToConstPtr<CScriptTrigger> trig = mgr.GetObjectById(list.first->second)) {
|
|
x1b8_positionMorphed = trig->GetTranslation();
|
|
x1c4_extentMorphed = trig->GetTriggerBoundsOR().max - trig->GetTriggerBoundsOR().min;
|
|
x1f0_damageMorphed = trig->GetDamageInfo().GetDamage();
|
|
x1d4_positionOrig = GetTranslation();
|
|
x1e0_extentOrig = x130_bounds.max - x130_bounds.min;
|
|
x1ec_damageOrig = x100_damageInfo.GetDamage();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
SetMorphing(true);
|
|
break;
|
|
case EScriptObjectMessage::Activate:
|
|
x2e8_30_alphaOut = false;
|
|
if (std::fabs(x2ac_alphaInTime) < 0.00001f) {
|
|
x2bc_alpha = x1b4_fluidPlane->GetAlpha();
|
|
x214_fogBias = x21c_origFogBias;
|
|
x218_fogMagnitude = x220_origFogMagnitude;
|
|
} else {
|
|
x2e8_29_alphaIn = true;
|
|
}
|
|
break;
|
|
case EScriptObjectMessage::Action:
|
|
x2e8_29_alphaIn = false;
|
|
if (std::fabs(x2b0_alphaOutTime) < 0.00001f) {
|
|
x2bc_alpha = 0.f;
|
|
x214_fogBias = 0.f;
|
|
x218_fogMagnitude = 0.f;
|
|
} else {
|
|
x2e8_30_alphaOut = true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
CScriptTrigger::AcceptScriptMsg(msg, other, mgr);
|
|
}
|
|
|
|
void CScriptWater::PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) {
|
|
if (!x2e8_27_allowRender) {
|
|
xe4_30_outOfFrustum = true;
|
|
return;
|
|
}
|
|
|
|
const zeus::CAABox aabb = GetSortingBounds(mgr);
|
|
xe4_30_outOfFrustum = !frustum.aabbFrustumTest(aabb);
|
|
if (xe4_30_outOfFrustum) {
|
|
return;
|
|
}
|
|
|
|
if (x4_areaId != kInvalidAreaId) {
|
|
if (x90_actorLights->GetMaxAreaLights() && (xe4_29_actorLightsDirty || x90_actorLights->GetIsDirty())) {
|
|
const CGameArea* area = mgr.GetWorld()->GetAreaAlways(x4_areaId);
|
|
if (area->IsPostConstructed()) {
|
|
x90_actorLights->BuildAreaLightList(mgr, *area, GetTriggerBoundsWR());
|
|
xe4_29_actorLightsDirty = false;
|
|
}
|
|
}
|
|
x90_actorLights->BuildDynamicLightList(mgr, GetTriggerBoundsWR());
|
|
}
|
|
x150_frustum = frustum;
|
|
}
|
|
|
|
void CScriptWater::AddToRenderer(const zeus::CFrustum& /*frustum*/, CStateManager& mgr) {
|
|
if (xe4_30_outOfFrustum) {
|
|
return;
|
|
}
|
|
|
|
const zeus::CPlane plane(zeus::skUp, x34_transform.origin.z() + x130_bounds.max.z());
|
|
const zeus::CAABox renderBounds = GetSortingBounds(mgr);
|
|
mgr.AddDrawableActorPlane(*this, plane, renderBounds);
|
|
}
|
|
|
|
void CScriptWater::Render(CStateManager& mgr) {
|
|
if (x30_24_active && !xe4_30_outOfFrustum) {
|
|
const float zOffset = 0.5f * (x9c_renderBounds.max.z() + x9c_renderBounds.min.z()) - x34_transform.origin.z();
|
|
const zeus::CAABox aabb = x9c_renderBounds.getTransformedAABox(zeus::CTransform::Translate(
|
|
-x34_transform.origin.x(), -x34_transform.origin.y(), -x34_transform.origin.z() - zOffset));
|
|
zeus::CTransform xf = x34_transform;
|
|
xf.origin.z() += zOffset;
|
|
const zeus::CVector3f areaCenter = mgr.GetWorld()->GetAreaAlways(mgr.GetNextAreaId())->GetAABB().center();
|
|
const std::optional<CRippleManager> rippleMan(mgr.GetFluidPlaneManager()->GetRippleManager());
|
|
x1b4_fluidPlane->Render(mgr, x2bc_alpha, aabb, xf, mgr.GetWorld()->GetAreaAlways(x4_areaId)->GetTransform(), false,
|
|
x150_frustum, rippleMan, x8_uid, x2d8_tileIntersects.get(), x2c4_gridDimX, x2c8_gridDimY,
|
|
areaCenter);
|
|
if (x214_fogBias != 0.f) {
|
|
if (mgr.GetPlayerState()->CanVisorSeeFog(mgr)) {
|
|
const float fogLevel = mgr.IntegrateVisorFog(
|
|
x218_fogMagnitude * std::sin(x224_fogSpeed * CGraphics::GetSecondsMod900()) + x214_fogBias);
|
|
if (fogLevel > 0.f) {
|
|
zeus::CAABox fogBox = GetTriggerBoundsWR();
|
|
fogBox.min.z() = float(fogBox.max.z());
|
|
fogBox.max.z() += fogLevel;
|
|
const zeus::CTransform modelXf =
|
|
zeus::CTransform::Translate(fogBox.center()) * zeus::CTransform::Scale((fogBox.max - fogBox.min) * 0.5f);
|
|
const zeus::CAABox renderAABB(zeus::skNegOne3f, zeus::skOne3f);
|
|
CGraphics::SetModelMatrix(modelXf);
|
|
g_Renderer->SetAmbientColor(zeus::skWhite);
|
|
g_Renderer->RenderFogVolume(x228_fogColor, renderAABB, nullptr, nullptr);
|
|
}
|
|
}
|
|
}
|
|
CGraphics::DisableAllLights();
|
|
}
|
|
CActor::Render(mgr);
|
|
}
|
|
|
|
void CScriptWater::Touch(CActor& otherAct, CStateManager& mgr) {
|
|
if (!x30_24_active) {
|
|
return;
|
|
}
|
|
|
|
CScriptTrigger::Touch(otherAct, mgr);
|
|
if (otherAct.GetMaterialList().HasMaterial(EMaterialTypes::Trigger)) {
|
|
return;
|
|
}
|
|
|
|
for (auto& inhab : x1fc_waterInhabitants)
|
|
if (inhab.first == otherAct.GetUniqueId()) {
|
|
inhab.second = true;
|
|
return;
|
|
}
|
|
|
|
const auto touchBounds = otherAct.GetTouchBounds();
|
|
if (!touchBounds) {
|
|
return;
|
|
}
|
|
|
|
x1fc_waterInhabitants.emplace_back(otherAct.GetUniqueId(), true);
|
|
const float triggerMaxZ = GetTriggerBoundsWR().max.z();
|
|
if (touchBounds->min.z() <= triggerMaxZ && touchBounds->max.z() >= triggerMaxZ) {
|
|
otherAct.FluidFXThink(EFluidState::EnteredFluid, *this, mgr);
|
|
}
|
|
|
|
mgr.SendScriptMsg(&otherAct, x8_uid, EScriptObjectMessage::AddSplashInhabitant);
|
|
}
|
|
|
|
void CScriptWater::CalculateRenderBounds() {
|
|
zeus::CVector3f aabbMin = x130_bounds.min;
|
|
aabbMin.z() = x130_bounds.max.z() - 1.f;
|
|
zeus::CVector3f aabbMax = x130_bounds.max;
|
|
aabbMax.z() += 1.f;
|
|
const zeus::CVector3f transAABBMin = aabbMin + GetTranslation();
|
|
const zeus::CVector3f transAABBMax = aabbMax + GetTranslation();
|
|
x9c_renderBounds = zeus::CAABox(transAABBMin, transAABBMax);
|
|
}
|
|
|
|
zeus::CAABox CScriptWater::GetSortingBounds(const CStateManager& mgr) const {
|
|
zeus::CVector3f max = x9c_renderBounds.max;
|
|
max.z() = std::max(float(max.z()), x9c_renderBounds.max.z() - 1.f + x214_fogBias + x218_fogMagnitude);
|
|
return {x9c_renderBounds.min, max};
|
|
}
|
|
|
|
EWeaponCollisionResponseTypes CScriptWater::GetCollisionResponseType(const zeus::CVector3f&, const zeus::CVector3f&,
|
|
const CWeaponMode&, EProjectileAttrib) const {
|
|
return EWeaponCollisionResponseTypes::Water;
|
|
}
|
|
|
|
u16 CScriptWater::GetSplashSound(float mag) const { return x298_splashSounds[GetSplashIndex(mag)]; }
|
|
|
|
const std::optional<TLockedToken<CGenDescription>>& CScriptWater::GetSplashEffect(float mag) const {
|
|
return x264_splashEffects[GetSplashIndex(mag)];
|
|
}
|
|
|
|
float CScriptWater::GetSplashEffectScale(float dt) const {
|
|
if (std::fabs(dt - 1.f) < 0.00001f) {
|
|
return kSplashScales[5];
|
|
}
|
|
|
|
const u32 idx = GetSplashIndex(dt);
|
|
const float s = dt - std::floor(dt * 3.f);
|
|
return ((1.f - s) * (s * kSplashScales[idx * 2])) + kSplashScales[idx];
|
|
}
|
|
|
|
u32 CScriptWater::GetSplashIndex(float mag) const {
|
|
const auto idx = u32(mag * 3.f);
|
|
return (idx < 3 ? idx : idx - 1);
|
|
}
|
|
|
|
void CScriptWater::SetMorphing(bool m) {
|
|
if (m == x2e8_26_morphing) {
|
|
return;
|
|
}
|
|
|
|
x2e8_26_morphing = m;
|
|
SetupGrid(!m);
|
|
}
|
|
|
|
const CScriptWater* CScriptWater::GetNextConnectedWater(const CStateManager& mgr) const {
|
|
for (const SConnection& conn : x20_conns) {
|
|
if (conn.x0_state != EScriptObjectState::Play || conn.x4_msg != EScriptObjectMessage::Activate) {
|
|
continue;
|
|
}
|
|
const auto its = mgr.GetIdListForScript(conn.x8_objId);
|
|
if (its.first != mgr.GetIdListEnd()) {
|
|
if (const TCastToConstPtr<CScriptWater> water = mgr.GetObjectById(its.first->second)) {
|
|
return water.GetPtr();
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool CScriptWater::CanRippleAtPoint(const zeus::CVector3f& point) const {
|
|
if (!x2d8_tileIntersects) {
|
|
return true;
|
|
}
|
|
|
|
const auto xTile = int((point.x() - GetTriggerBoundsWR().min.x()) / x2c0_tileSize);
|
|
if (xTile < 0 || xTile >= x2c4_gridDimX) {
|
|
return false;
|
|
}
|
|
|
|
const auto yTile = int((point.y() - GetTriggerBoundsWR().min.y()) / x2c0_tileSize);
|
|
if (yTile < 0 || yTile >= x2c8_gridDimY) {
|
|
return false;
|
|
}
|
|
|
|
return x2d8_tileIntersects[yTile * x2c4_gridDimX + xTile];
|
|
}
|
|
} // namespace metaforce
|