2
0
mirror of https://github.com/AxioDL/metaforce.git synced 2025-05-14 18:31:20 +00:00
metaforce/Runtime/World/CFluidPlaneCPU.cpp
Lioncash 14f6dd2cd8 CFluidUVMotion: Return std::array by value from CalculateFluidTextureOffset()
Same behavior, but allows for easier use of API and makes it harder to
misuse. It also makes it easier for analysis to determine out of bounds,
given we leverage std::array rather than a pointer that causes arrays to
decay and lose their size information.
2020-04-12 09:23:52 -04:00

875 lines
40 KiB
C++

#include "Runtime/World/CFluidPlaneCPU.hpp"
#include "Runtime/CSimplePool.hpp"
#include "Runtime/CStateManager.hpp"
#include "Runtime/GameGlobalObjects.hpp"
#include "Runtime/Camera/CGameCamera.hpp"
#include "Runtime/World/CFluidPlaneManager.hpp"
#include "Runtime/World/CScriptWater.hpp"
#include "Runtime/World/CWorld.hpp"
#include "TCastTo.hpp" // Generated file, do not modify include path
#define kTableSize 2048
namespace urde {
CFluidPlaneCPU::CTurbulence::CTurbulence(float speed, float distance, float freqMax, float freqMin, float phaseMax,
float phaseMin, float amplitudeMax, float amplitudeMin)
: x0_speed(speed)
, x4_distance(distance)
, x8_freqMax(freqMax)
, xc_freqMin(freqMin)
, x10_phaseMax(phaseMax)
, x14_phaseMin(phaseMin)
, x18_amplitudeMax(amplitudeMax)
, x1c_amplitudeMin(amplitudeMin)
, x2c_ooTurbSpeed(1.f / x0_speed)
, x30_ooTurbDistance(1.f / x4_distance) {
if (x18_amplitudeMax != 0.f || x1c_amplitudeMin != 0.f) {
x24_tableCount = kTableSize;
x28_heightSelPitch = x24_tableCount;
x20_table.reset(new float[x24_tableCount]);
float anglePitch = 2.f * M_PIF / x28_heightSelPitch;
float freqConstant = 0.5f * (x8_freqMax + xc_freqMin);
float freqLinear = 0.5f * (x8_freqMax - xc_freqMin);
float phaseConstant = 0.5f * (x10_phaseMax + x14_phaseMin);
float phaseLinear = 0.5f * (x10_phaseMax - x14_phaseMin);
float amplitudeConstant = 0.5f * (x18_amplitudeMax + x1c_amplitudeMin);
float amplitudeLinear = 0.5f * (x18_amplitudeMax - x1c_amplitudeMin);
float curAng = 0.f;
for (int i = 0; i < x24_tableCount; ++i, curAng += anglePitch) {
float angCos = std::cos(curAng);
x20_table[i] = (amplitudeLinear * angCos + amplitudeConstant) *
std::sin((freqLinear * angCos + freqConstant) * curAng + (phaseLinear * angCos + phaseConstant));
}
x34_hasTurbulence = true;
}
}
CFluidPlaneCPU::CFluidPlaneCPU(CAssetId texPattern1, CAssetId texPattern2, CAssetId texColor, CAssetId bumpMap,
CAssetId envMap, CAssetId envBumpMap, CAssetId lightMap, float unitsPerLightmapTexel,
float tileSize, u32 tileSubdivisions, EFluidType fluidType, float alpha,
const zeus::CVector3f& bumpLightDir, float bumpScale, const CFluidUVMotion& mot,
float turbSpeed, float turbDistance, float turbFreqMax, float turbFreqMin,
float turbPhaseMax, float turbPhaseMin, float turbAmplitudeMax, float turbAmplitudeMin,
float specularMin, float specularMax, float reflectionBlend, float reflectionSize,
float rippleIntensity, u32 maxVertCount)
: CFluidPlane(texPattern1, texPattern2, texColor, alpha, fluidType, rippleIntensity, mot)
, xa0_texIdBumpMap(bumpMap)
, xa4_texIdEnvMap(envMap)
, xa8_texIdEnvBumpMap(envBumpMap)
, xac_texId4(lightMap)
, xf0_bumpLightDir(bumpLightDir)
, xfc_bumpScale(bumpScale)
, x100_tileSize(tileSize)
, x104_tileSubdivisions(tileSubdivisions & ~0x1)
, x108_rippleResolution(x100_tileSize / float(x104_tileSubdivisions))
, x10c_specularMin(specularMin)
, x110_specularMax(specularMax)
, x114_reflectionBlend(reflectionBlend)
, x118_reflectionSize(reflectionSize)
, x11c_unitsPerLightmapTexel(unitsPerLightmapTexel)
, x120_turbulence(turbSpeed, turbDistance, turbFreqMax, turbFreqMin, turbPhaseMax, turbPhaseMin, turbAmplitudeMax,
turbAmplitudeMin)
, m_maxVertCount(maxVertCount) {
if (g_ResFactory->GetResourceTypeById(xa0_texIdBumpMap) == FOURCC('TXTR'))
xb0_bumpMap = g_SimplePool->GetObj(SObjectTag{FOURCC('TXTR'), xa0_texIdBumpMap});
if (g_ResFactory->GetResourceTypeById(xa4_texIdEnvMap) == FOURCC('TXTR'))
xc0_envMap = g_SimplePool->GetObj(SObjectTag{FOURCC('TXTR'), xa4_texIdEnvMap});
if (g_ResFactory->GetResourceTypeById(xa8_texIdEnvBumpMap) == FOURCC('TXTR'))
xd0_envBumpMap = g_SimplePool->GetObj(SObjectTag{FOURCC('TXTR'), xa8_texIdEnvBumpMap});
if (g_ResFactory->GetResourceTypeById(xac_texId4) == FOURCC('TXTR'))
xe0_lightmap = g_SimplePool->GetObj(SObjectTag{FOURCC('TXTR'), xac_texId4});
}
void CFluidPlaneCPU::CreateRipple(const CRipple& ripple, CStateManager& mgr) {}
void CFluidPlaneCPU::CalculateLightmapMatrix(const zeus::CTransform& areaXf, const zeus::CTransform& xf,
const zeus::CAABox& aabb, zeus::CMatrix4f& mtxOut) const {
int width = GetLightMap().GetWidth();
int height = GetLightMap().GetHeight();
zeus::CTransform toLocal = areaXf.getRotation().inverse();
zeus::CAABox areaLocalAABB = aabb.getTransformedAABox(toLocal);
float f26 = (areaLocalAABB.max.x() - areaLocalAABB.min.x()) / (width * x11c_unitsPerLightmapTexel);
float f25 = (areaLocalAABB.max.y() - areaLocalAABB.min.y()) / (height * x11c_unitsPerLightmapTexel);
float f24 = (1.f + std::fmod(areaLocalAABB.min.x() + xf.origin.x(), x11c_unitsPerLightmapTexel)) / width;
float f23 = (2.f - std::fmod(areaLocalAABB.max.x() + xf.origin.x(), x11c_unitsPerLightmapTexel)) / width;
float f29 = (1.f + std::fmod(areaLocalAABB.min.y() + xf.origin.y(), x11c_unitsPerLightmapTexel)) / height;
float f6 = (2.f - std::fmod(areaLocalAABB.max.y() + xf.origin.y(), x11c_unitsPerLightmapTexel)) / height;
float scaleX = (f26 - f24 - f23) / (areaLocalAABB.max.x() - areaLocalAABB.min.x());
float scaleY = -(f25 - f29 - f6) / (areaLocalAABB.max.y() - areaLocalAABB.min.y());
float offX = f24 + f26 * -areaLocalAABB.min.x() / (areaLocalAABB.max.x() - areaLocalAABB.min.x());
float offY = f25 * areaLocalAABB.min.y() / (areaLocalAABB.max.y() - areaLocalAABB.min.y()) - f6;
mtxOut = (zeus::CTransform(zeus::CMatrix3f(zeus::CVector3f(scaleX, scaleY, 0.f)), zeus::CVector3f(offX, offY, 0.f)) *
toLocal)
.toMatrix4f();
}
static bool sSineWaveInitialized = false;
static float sGlobalSineWave[256] = {};
static const float* InitializeSineWave() {
if (sSineWaveInitialized)
return sGlobalSineWave;
for (int i = 0; i < 256; ++i)
sGlobalSineWave[i] = std::sin(2.f * M_PIF * (i / 256.f));
sSineWaveInitialized = true;
return sGlobalSineWave;
}
#define kEnableWaterBumpMaps true
CFluidPlaneShader::RenderSetupInfo CFluidPlaneCPU::RenderSetup(const CStateManager& mgr, float alpha,
const zeus::CTransform& xf,
const zeus::CTransform& areaXf, const zeus::CAABox& aabb,
const CScriptWater* water) {
CFluidPlaneShader::RenderSetupInfo out;
const float uvT = mgr.GetFluidPlaneManager()->GetUVT();
const bool hasBumpMap = HasBumpMap() && kEnableWaterBumpMaps;
bool doubleLightmapBlend = false;
const bool hasEnvMap = mgr.GetCameraManager()->GetFluidCounter() == 0 && HasEnvMap();
const bool hasEnvBumpMap = HasEnvBumpMap();
InitializeSineWave();
CGraphics::SetModelMatrix(xf);
if (hasBumpMap) {
// Build 50% grey directional light with xf0_bumpLightDir and load into LIGHT_3
// Light 3 in channel 1
// Vertex colors in channel 0
out.lights.resize(4);
out.lights[3] = CLight::BuildDirectional(xf0_bumpLightDir, zeus::skGrey);
} else {
// Normal light mask in channel 1
// Vertex colors in channel 0
out.lights = water->GetActorLights()->BuildLightVector();
}
int curTex = 3;
if (hasBumpMap) {
// Load into next
curTex++;
}
if (hasEnvMap) {
// Load into next
curTex++;
}
if (hasEnvBumpMap) {
// Load into next
curTex++;
}
const auto fluidUVs = x4c_uvMotion.CalculateFluidTextureOffset(uvT);
out.texMtxs[0][0][0] = x4c_uvMotion.GetFluidLayers()[1].GetUVScale();
out.texMtxs[0][1][1] = x4c_uvMotion.GetFluidLayers()[1].GetUVScale();
out.texMtxs[0][3][0] = fluidUVs[1][0];
out.texMtxs[0][3][1] = fluidUVs[1][1];
out.texMtxs[1][0][0] = x4c_uvMotion.GetFluidLayers()[2].GetUVScale();
out.texMtxs[1][1][1] = x4c_uvMotion.GetFluidLayers()[2].GetUVScale();
out.texMtxs[1][3][0] = fluidUVs[2][0];
out.texMtxs[1][3][1] = fluidUVs[2][1];
out.texMtxs[2][0][0] = x4c_uvMotion.GetFluidLayers()[0].GetUVScale();
out.texMtxs[2][1][1] = x4c_uvMotion.GetFluidLayers()[0].GetUVScale();
out.texMtxs[2][3][0] = fluidUVs[0][0];
out.texMtxs[2][3][1] = fluidUVs[0][1];
// Load normal mtx 0 with
out.normMtx = (zeus::CTransform::Scale(xfc_bumpScale) * CGraphics::g_ViewMatrix.getRotation().inverse()).toMatrix4f();
// Setup TCGs
int nextTexMtx = 3;
if (hasEnvBumpMap) {
float pttScale;
if (hasEnvMap)
pttScale = 0.5f * (1.f - x118_reflectionSize);
else
pttScale = g_tweakGame->GetFluidEnvBumpScale() * x4c_uvMotion.GetFluidLayers()[0].GetUVScale();
// Load GX_TEXMTX3 with identity
zeus::CMatrix4f& texMtx = out.texMtxs[nextTexMtx++];
texMtx[0][0] = pttScale;
texMtx[1][1] = pttScale;
texMtx[3][0] = 0.5f;
texMtx[3][1] = 0.5f;
// Load GX_PTTEXMTX0 with scale of pttScale
// Next: GX_TG_MTX2x4 GX_TG_NRM, GX_TEXMTX3, true, GX_PTTEXMTX0
out.indScale = 0.5f * (hasEnvMap ? x118_reflectionSize : 1.f);
// Load ind mtx with scale of (indScale, -indScale)
// Load envBumpMap into ind stage 0 with previous TCG
}
if (hasEnvMap) {
float scale = std::max(aabb.max.x() - aabb.min.x(), aabb.max.y() - aabb.min.y());
zeus::CMatrix4f& texMtx = out.texMtxs[nextTexMtx++];
texMtx[0][0] = 1.f / scale;
texMtx[1][1] = 1.f / scale;
zeus::CVector3f center = aabb.center();
texMtx[3][0] = 0.5f + -center.x() / scale;
texMtx[3][1] = 0.5f + -center.y() / scale;
// Next: GX_TG_MTX2x4 GX_TG_POS, mtxNext, false, GX_PTIDENTITY
}
if (HasLightMap()) {
float lowLightBlend = 1.f;
const CGameArea* area = mgr.GetWorld()->GetAreaAlways(mgr.GetNextAreaId());
float lightLevel = area->GetPostConstructed()->x1128_worldLightingLevel;
const CScriptWater* nextWater = water->GetNextConnectedWater(mgr);
if (std::fabs(water->GetMorphFactor()) < 0.00001f || !nextWater || !nextWater->GetFluidPlane().HasLightMap()) {
// Load lightmap
CalculateLightmapMatrix(areaXf, xf, aabb, out.texMtxs[nextTexMtx++]);
// Next: GX_TG_MTX2x4 GX_TG_POS, mtxNext, false, GX_PTIDENTITY
} else if (nextWater && nextWater->GetFluidPlane().HasLightMap()) {
if (std::fabs(water->GetMorphFactor() - 1.f) < 0.00001f) {
// Load lightmap
CalculateLightmapMatrix(areaXf, xf, aabb, out.texMtxs[nextTexMtx++]);
// Next: GX_TG_MTX2x4 GX_TG_POS, mtxNext, false, GX_PTIDENTITY
} else {
// Load lightmap
CalculateLightmapMatrix(areaXf, xf, aabb, out.texMtxs[nextTexMtx++]);
// Next: GX_TG_MTX2x4 GX_TG_POS, mtxNext, false, GX_PTIDENTITY
// Load lightmap
CalculateLightmapMatrix(areaXf, xf, aabb, out.texMtxs[nextTexMtx++]);
// Next: GX_TG_MTX2x4 GX_TG_POS, mtxNext, false, GX_PTIDENTITY
float lum = lightLevel * water->GetMorphFactor();
out.kColors[3] = zeus::CColor(lum, 1.f);
lowLightBlend = (1.f - water->GetMorphFactor()) / (1.f - lum);
doubleLightmapBlend = true;
}
}
out.kColors[2] = zeus::CColor(lowLightBlend * lightLevel, 1.f);
}
float waterPlaneOrthoDot = xf.transposeRotate(zeus::skUp)
.dot(CGraphics::g_ViewMatrix.inverse().transposeRotate(zeus::skForward));
if (waterPlaneOrthoDot < 0.f)
waterPlaneOrthoDot = -waterPlaneOrthoDot;
out.kColors[0] =
zeus::CColor((1.f - waterPlaneOrthoDot) * (x110_specularMax - x10c_specularMin) + x10c_specularMin, alpha);
out.kColors[1] = zeus::CColor(x114_reflectionBlend, 1.f);
if (!m_shader || m_cachedDoubleLightmapBlend != doubleLightmapBlend ||
m_cachedAdditive != (mgr.GetThermalDrawFlag() == EThermalDrawFlag::Hot)) {
m_cachedDoubleLightmapBlend = doubleLightmapBlend;
m_cachedAdditive = mgr.GetThermalDrawFlag() == EThermalDrawFlag::Hot;
m_shader.emplace(x44_fluidType, x10_texPattern1, x20_texPattern2, x30_texColor, xb0_bumpMap, xc0_envMap,
xd0_envBumpMap, xe0_lightmap,
m_tessellation ? CFluidPlaneManager::RippleMapTex : boo::ObjToken<boo::ITextureS>{},
m_cachedDoubleLightmapBlend, m_cachedAdditive, m_maxVertCount);
}
return out;
}
int CFluidPlaneRender::numTilesInHField;
int CFluidPlaneRender::numSubdivisionsInTile;
int CFluidPlaneRender::numSubdivisionsInHField;
bool CFluidPlaneCPU::PrepareRipple(const CRipple& ripple, const CFluidPlaneRender::SPatchInfo& info,
CFluidPlaneRender::SRippleInfo& rippleOut) {
auto lifeIdx = int((1.f - (ripple.GetTimeFalloff() - ripple.GetTime()) / ripple.GetTimeFalloff()) * 64.f);
float dist = CFluidPlaneManager::RippleMaxs[lifeIdx] * (ripple.GetDistanceFalloff() / 256.f);
dist *= dist;
if (dist != 0)
dist = std::sqrt(dist);
dist = info.x24_ooRippleResolution * dist + 1.f;
float centerX = info.x24_ooRippleResolution * (ripple.GetCenter().x() - info.xc_globalMin.x());
float centerY = info.x24_ooRippleResolution * (ripple.GetCenter().y() - info.xc_globalMin.y());
int fromX = int(centerX - dist) - 1;
int toX = int(centerX + dist) + 1;
int fromY = int(centerY - dist) - 1;
int toY = int(centerY + dist) + 1;
rippleOut.x4_fromX = std::max(0, fromX);
rippleOut.x8_toX = std::min(int(info.x0_xSubdivs), toX);
rippleOut.xc_fromY = std::max(0, fromY);
rippleOut.x10_toY = std::min(int(info.x1_ySubdivs), toY);
rippleOut.x14_gfromX = std::max(rippleOut.x14_gfromX, fromX);
rippleOut.x18_gtoX = std::min(rippleOut.x18_gtoX, toX);
rippleOut.x1c_gfromY = std::max(rippleOut.x1c_gfromY, fromY);
rippleOut.x20_gtoY = std::min(rippleOut.x20_gtoY, toY);
return !(rippleOut.x14_gfromX > rippleOut.x18_gtoX || rippleOut.x1c_gfromY > rippleOut.x20_gtoY);
}
void CFluidPlaneCPU::ApplyTurbulence(float t, CFluidPlaneRender::SHFieldSample (&heights)[46][46],
const u8 (&flags)[9][9], const float sineWave[256],
const CFluidPlaneRender::SPatchInfo& info,
const zeus::CVector3f& areaCenter) const {
if (!HasTurbulence()) {
memset(&heights, 0, sizeof(heights));
return;
}
float scaledT = t * GetOOTurbulenceSpeed();
float curY = info.x4_localMin.y() - info.x18_rippleResolution - areaCenter.y();
int xDivs = (info.x0_xSubdivs + CFluidPlaneRender::numSubdivisionsInTile - 4) /
CFluidPlaneRender::numSubdivisionsInTile * CFluidPlaneRender::numSubdivisionsInTile +
2;
int yDivs = (info.x1_ySubdivs + CFluidPlaneRender::numSubdivisionsInTile - 4) /
CFluidPlaneRender::numSubdivisionsInTile * CFluidPlaneRender::numSubdivisionsInTile +
2;
for (int i = 0; i <= yDivs; ++i) {
float curYSq = curY * curY;
float curX = info.x4_localMin.x() - info.x18_rippleResolution - areaCenter.x();
for (int j = 0; j <= xDivs; ++j) {
float distFac = curX * curX + curYSq;
if (distFac != 0.f)
distFac = std::sqrt(distFac);
heights[i][j].height = GetTurbulenceHeight(GetOOTurbulenceDistance() * distFac + scaledT);
curX += info.x18_rippleResolution;
}
curY += info.x18_rippleResolution;
}
}
void CFluidPlaneCPU::ApplyRipple(const CFluidPlaneRender::SRippleInfo& rippleInfo,
CFluidPlaneRender::SHFieldSample (&heights)[46][46], u8 (&flags)[9][9],
const float sineWave[256], const CFluidPlaneRender::SPatchInfo& info) const {
float lookupT = 256.f *
(1.f - rippleInfo.x0_ripple.GetTime() * rippleInfo.x0_ripple.GetOOTimeFalloff() *
rippleInfo.x0_ripple.GetOOTimeFalloff()) *
rippleInfo.x0_ripple.GetFrequency();
auto lifeIdx = int(64.f * rippleInfo.x0_ripple.GetTime() * rippleInfo.x0_ripple.GetOOTimeFalloff());
float distMul = rippleInfo.x0_ripple.GetDistanceFalloff() / 255.f;
float minDist = CFluidPlaneManager::RippleMins[lifeIdx] * distMul;
float minDistSq = minDist * minDist;
if (minDistSq != 0.f)
minDist = std::sqrt(minDistSq);
float maxDist = CFluidPlaneManager::RippleMaxs[lifeIdx] * distMul;
float maxDistSq = maxDist * maxDist;
if (maxDistSq != 0.f)
maxDist = std::sqrt(maxDistSq);
int fromY =
(rippleInfo.x1c_gfromY + CFluidPlaneRender::numSubdivisionsInTile - 1) / CFluidPlaneRender::numSubdivisionsInTile;
int fromX =
(rippleInfo.x14_gfromX + CFluidPlaneRender::numSubdivisionsInTile - 1) / CFluidPlaneRender::numSubdivisionsInTile;
int toY =
(rippleInfo.x20_gtoY + CFluidPlaneRender::numSubdivisionsInTile - 1) / CFluidPlaneRender::numSubdivisionsInTile;
int toX =
(rippleInfo.x18_gtoX + CFluidPlaneRender::numSubdivisionsInTile - 1) / CFluidPlaneRender::numSubdivisionsInTile;
float curY = rippleInfo.x0_ripple.GetCenter().y() - info.xc_globalMin.y() -
(0.5f * info.x14_tileSize + (fromY - 1) * info.x14_tileSize);
int curGridY = info.x2a_gridDimX * (info.x2e_tileY + fromY - 1);
int startGridX = (info.x28_tileX + fromX - 1);
int gridCells = info.x2a_gridDimX * info.x2c_gridDimY;
float distFalloff = 64.f * rippleInfo.x0_ripple.GetOODistanceFalloff();
int curYDiv = rippleInfo.xc_fromY;
for (int i = fromY; i <= toY; ++i, curY -= info.x14_tileSize) {
int nextYDiv = (i + 1) * CFluidPlaneRender::numSubdivisionsInTile;
float curYSq = curY * curY;
int curGridX = startGridX;
int curXDiv = rippleInfo.x4_fromX;
float curX = rippleInfo.x0_ripple.GetCenter().x() - info.xc_globalMin.x() -
(0.5f * info.x14_tileSize + (fromX - 1) * info.x14_tileSize);
for (int j = fromX; j <= toX; ++j, curX -= info.x14_tileSize, ++curGridX) {
float dist = curX * curX + curYSq;
if (dist != 0.f)
dist = std::sqrt(dist);
if (maxDist < dist - info.x1c_tileHypRadius || minDist > dist + info.x1c_tileHypRadius)
continue;
bool addedRipple = false;
int nextXDiv = (j + 1) * CFluidPlaneRender::numSubdivisionsInTile;
float curXMod =
(rippleInfo.x0_ripple.GetCenter().x() - info.xc_globalMin.x()) - info.x18_rippleResolution * curXDiv;
float curYMod =
(rippleInfo.x0_ripple.GetCenter().y() - info.xc_globalMin.y()) - info.x18_rippleResolution * curYDiv;
if (!info.x30_gridFlags || (info.x30_gridFlags && curGridY >= 0 && curGridY < gridCells && curGridX >= 0 &&
curGridX < info.x2a_gridDimX && info.x30_gridFlags[curGridX + curGridY])) {
for (int k = curYDiv; k <= std::min(rippleInfo.x10_toY, nextYDiv - 1);
++k, curYMod -= info.x18_rippleResolution) {
float tmpXMod = curXMod;
float curYModSq = curYMod * curYMod;
for (int l = curXDiv; l <= std::min(rippleInfo.x8_toX, nextXDiv - 1);
++l, tmpXMod -= info.x18_rippleResolution) {
float divDistSq = tmpXMod * tmpXMod + curYModSq;
if (divDistSq < minDistSq || divDistSq > maxDistSq)
continue;
if (m_tessellation) {
/* This will be evaluated in tessellation shader instead */
addedRipple = true;
break;
}
float divDist = (divDistSq != 0.f) ? std::sqrt(divDistSq) : 0.f;
if (u8 rippleV = CFluidPlaneManager::RippleValues[lifeIdx][int(divDist * distFalloff)]) {
heights[k][l].height += rippleV * rippleInfo.x0_ripple.GetLookupAmplitude() *
sineWave[int(divDist * rippleInfo.x0_ripple.GetLookupPhase() + lookupT) & 0xff];
} else {
heights[k][l].height += 0.f;
}
addedRipple = true;
}
}
if (addedRipple)
flags[i][j] = 0x1f;
} else {
int yMin = nextYDiv - 1;
int yMax = nextYDiv - CFluidPlaneRender::numSubdivisionsInTile + 1;
int xMin = nextXDiv - 1;
int xMax = nextXDiv - CFluidPlaneRender::numSubdivisionsInTile + 1;
if (curGridX >= 0.f && curGridX < info.x2a_gridDimX && curGridY - info.x2a_gridDimX >= 0 &&
!info.x30_gridFlags[curGridX + curGridY - info.x2a_gridDimX])
yMax -= 2;
if (curGridX >= 0.f && curGridX < info.x2a_gridDimX && curGridY + info.x2a_gridDimX < gridCells &&
!info.x30_gridFlags[curGridX + info.x2a_gridDimX])
yMin += 2;
if (curGridY >= 0 && curGridY < info.x2c_gridDimY && curGridX > 0 && !info.x30_gridFlags[curGridX - 1])
xMax -= 2;
if (curGridY >= 0 && curGridY < info.x2c_gridDimY && curGridX + 1 < info.x2a_gridDimX &&
!info.x30_gridFlags[curGridX + 1])
xMin += 2;
for (int k = curYDiv; k <= std::min(rippleInfo.x10_toY, nextYDiv - 1);
++k, curYMod -= info.x18_rippleResolution) {
float tmpXMod = curXMod;
float curYModSq = curYMod * curYMod;
for (int l = curXDiv; l <= std::min(rippleInfo.x8_toX, nextXDiv - 1);
++l, tmpXMod -= info.x18_rippleResolution) {
if (k <= yMax || k >= yMin || l <= xMax || l >= xMin) {
float divDistSq = tmpXMod * tmpXMod + curYModSq;
if (divDistSq < minDistSq || divDistSq > maxDistSq)
continue;
if (m_tessellation) {
/* This will be evaluated in tessellation shader instead */
addedRipple = true;
break;
}
float divDist = (divDistSq != 0.f) ? std::sqrt(divDistSq) : 0.f;
if (u8 rippleV = CFluidPlaneManager::RippleValues[lifeIdx][int(divDist * distFalloff)]) {
heights[k][l].height += rippleV * rippleInfo.x0_ripple.GetLookupAmplitude() *
sineWave[int(divDist * rippleInfo.x0_ripple.GetLookupPhase() + lookupT) & 0xff];
} else {
heights[k][l].height += 0.f;
}
addedRipple = true;
}
}
if (m_tessellation && addedRipple)
break;
}
if (addedRipple)
flags[i][j] = 0xf;
}
curXDiv = nextXDiv;
}
curYDiv = nextYDiv;
curGridY += info.x2a_gridDimX;
}
}
void CFluidPlaneCPU::ApplyRipples(const rstl::reserved_vector<CFluidPlaneRender::SRippleInfo, 32>& rippleInfos,
CFluidPlaneRender::SHFieldSample (&heights)[46][46], u8 (&flags)[9][9],
const float sineWave[256], const CFluidPlaneRender::SPatchInfo& info) const {
for (const CFluidPlaneRender::SRippleInfo& rippleInfo : rippleInfos)
ApplyRipple(rippleInfo, heights, flags, sineWave, info);
for (int i = 0; i < CFluidPlaneRender::numTilesInHField; ++i)
flags[0][i + 1] |= 1;
for (int i = 0; i < CFluidPlaneRender::numTilesInHField; ++i)
flags[i + 1][0] |= 8;
for (int i = 0; i < CFluidPlaneRender::numTilesInHField; ++i)
flags[i + 1][CFluidPlaneRender::numTilesInHField + 1] |= 4;
for (int i = 0; i < CFluidPlaneRender::numTilesInHField; ++i)
flags[CFluidPlaneRender::numTilesInHField + 1][i + 1] |= 2;
}
void CFluidPlaneCPU::UpdatePatchNoNormals(CFluidPlaneRender::SHFieldSample (&heights)[46][46], const u8 (&flags)[9][9],
const CFluidPlaneRender::SPatchInfo& info) {
for (int i = 1; i <= (info.x1_ySubdivs + CFluidPlaneRender::numSubdivisionsInTile - 2) /
CFluidPlaneRender::numSubdivisionsInTile;
++i) {
int r10 = i * CFluidPlaneRender::numSubdivisionsInTile + 1;
int r9 = std::max(0, r10 - CFluidPlaneRender::numSubdivisionsInTile);
int x24 = std::min(r10, info.x1_ySubdivs + 1);
for (int j = 1; j <= (info.x0_xSubdivs + CFluidPlaneRender::numSubdivisionsInTile - 2) /
CFluidPlaneRender::numSubdivisionsInTile;
++j) {
int r29 = j * CFluidPlaneRender::numSubdivisionsInTile + 1;
int r11 = std::max(0, r29 - CFluidPlaneRender::numSubdivisionsInTile);
int x28 = std::min(r29, info.x0_xSubdivs + 1);
if ((flags[i][j] & 0x1f) == 0x1f) {
for (int k = r9; k < x24; ++k) {
for (int l = r11; l < x28; ++l) {
CFluidPlaneRender::SHFieldSample& sample = heights[k][l];
if (sample.height > 0.f)
sample.wavecapIntensity = u8(std::min(255, int(info.x38_wavecapIntensityScale * sample.height)));
else
sample.wavecapIntensity = 0;
}
}
} else {
if (i > 0 && i < CFluidPlaneRender::numTilesInHField + 1 && j > 0 &&
j < CFluidPlaneRender::numTilesInHField + 1) {
int halfSubdivs = CFluidPlaneRender::numSubdivisionsInTile / 2;
CFluidPlaneRender::SHFieldSample& sample = heights[halfSubdivs + r9][halfSubdivs + r11];
if (sample.height > 0.f)
sample.wavecapIntensity = u8(std::min(255, int(info.x38_wavecapIntensityScale * sample.height)));
else
sample.wavecapIntensity = 0;
}
if (i != 0) {
for (int l = r11; l < x28; ++l) {
CFluidPlaneRender::SHFieldSample& sample = heights[r9][l];
if (sample.height > 0.f)
sample.wavecapIntensity = u8(std::min(255, int(info.x38_wavecapIntensityScale * sample.height)));
else
sample.wavecapIntensity = 0;
}
}
if (j != 0) {
for (int k = r9 + 1; k < x24; ++k) {
CFluidPlaneRender::SHFieldSample& sample = heights[k][r11];
if (sample.height > 0.f)
sample.wavecapIntensity = u8(std::min(255, int(info.x38_wavecapIntensityScale * sample.height)));
else
sample.wavecapIntensity = 0;
}
}
}
}
}
}
void CFluidPlaneCPU::UpdatePatchWithNormals(CFluidPlaneRender::SHFieldSample (&heights)[46][46],
const u8 (&flags)[9][9], const CFluidPlaneRender::SPatchInfo& info) {
float normalScale = -(2.f * info.x18_rippleResolution);
float nz = 0.25f * 2.f * info.x18_rippleResolution;
int curGridY = info.x2e_tileY * info.x2a_gridDimX - 1 + info.x28_tileX;
for (int i = 1; i <= (info.x1_ySubdivs + CFluidPlaneRender::numSubdivisionsInTile - 2) /
CFluidPlaneRender::numSubdivisionsInTile;
++i, curGridY += info.x2a_gridDimX) {
int r11 = i * CFluidPlaneRender::numSubdivisionsInTile + 1;
int r9 = std::max(0, r11 - CFluidPlaneRender::numSubdivisionsInTile);
int x38 = std::min(r11, info.x1_ySubdivs + 1);
for (int j = 1; j <= (info.x0_xSubdivs + CFluidPlaneRender::numSubdivisionsInTile - 2) /
CFluidPlaneRender::numSubdivisionsInTile;
++j) {
int r12 = j * CFluidPlaneRender::numSubdivisionsInTile + 1;
int x3c = std::min(r12, info.x0_xSubdivs + 1);
r12 -= CFluidPlaneRender::numSubdivisionsInTile;
if ((flags[i][j] & 0x1f) == 0x1f) {
for (int k = r9; k < x38; ++k) {
for (int l = r12; l < x3c; ++l) {
CFluidPlaneRender::SHFieldSample& sample = heights[k][l];
CFluidPlaneRender::SHFieldSample& up = heights[k + 1][l];
CFluidPlaneRender::SHFieldSample& down = heights[k - 1][l];
CFluidPlaneRender::SHFieldSample& right = heights[k][l + 1];
CFluidPlaneRender::SHFieldSample& left = heights[k][l - 1];
float nx = (right.height - left.height) * normalScale;
float ny = (up.height - down.height) * normalScale;
float normalizer = ny * ny + nx * nx + nz * nz;
if (normalizer != 0.f)
normalizer = std::sqrt(normalizer);
normalizer = 63.f / normalizer;
sample.nx = s8(nx * normalizer);
sample.ny = s8(ny * normalizer);
sample.nz = s8(nz * normalizer);
if (sample.height > 0.f)
sample.wavecapIntensity = u8(std::min(255, int(info.x38_wavecapIntensityScale * sample.height)));
else
sample.wavecapIntensity = 0;
}
}
} else {
if (!info.x30_gridFlags || info.x30_gridFlags[curGridY + j]) {
if (i > 0 && i < CFluidPlaneRender::numTilesInHField + 1 && j > 0 &&
j < CFluidPlaneRender::numTilesInHField + 1) {
int halfSubdivs = CFluidPlaneRender::numSubdivisionsInTile / 2;
int k = halfSubdivs + r9;
int l = halfSubdivs + r12;
CFluidPlaneRender::SHFieldSample& sample = heights[k][l];
CFluidPlaneRender::SHFieldSample& up = heights[k + 1][l];
CFluidPlaneRender::SHFieldSample& down = heights[k - 1][l];
CFluidPlaneRender::SHFieldSample& right = heights[k][l + 1];
CFluidPlaneRender::SHFieldSample& left = heights[k][l - 1];
float nx = (right.height - left.height) * normalScale;
float ny = (up.height - down.height) * normalScale;
float normalizer = ny * ny + nx * nx + nz * nz;
if (normalizer != 0.f)
normalizer = std::sqrt(normalizer);
normalizer = 63.f / normalizer;
sample.nx = s8(nx * normalizer);
sample.ny = s8(ny * normalizer);
sample.nz = s8(nz * normalizer);
if (sample.height > 0.f)
sample.wavecapIntensity = u8(std::min(255, int(info.x38_wavecapIntensityScale * sample.height)));
else
sample.wavecapIntensity = 0;
}
}
if (j != 0 && i != 0) {
if ((flags[i][j] & 2) != 0 || (flags[i - 1][j] & 1) != 0 || (flags[i][j] & 4) != 0 ||
(flags[i][j - 1] & 8) != 0) {
for (int l = r12; l < x3c; ++l) {
CFluidPlaneRender::SHFieldSample& sample = heights[r9][l];
CFluidPlaneRender::SHFieldSample& up = heights[r9 + 1][l];
CFluidPlaneRender::SHFieldSample& down = heights[r9 - 1][l];
CFluidPlaneRender::SHFieldSample& right = heights[r9][l + 1];
CFluidPlaneRender::SHFieldSample& left = heights[r9][l - 1];
float nx = (right.height - left.height) * normalScale;
float ny = (up.height - down.height) * normalScale;
float normalizer = ny * ny + nx * nx + nz * nz;
if (normalizer != 0.f)
normalizer = std::sqrt(normalizer);
normalizer = 63.f / normalizer;
sample.nx = s8(nx * normalizer);
sample.ny = s8(ny * normalizer);
sample.nz = s8(nz * normalizer);
if (sample.height > 0.f)
sample.wavecapIntensity = u8(std::min(255, int(info.x38_wavecapIntensityScale * sample.height)));
else
sample.wavecapIntensity = 0;
}
for (int k = r9; k < x38; ++k) {
CFluidPlaneRender::SHFieldSample& sample = heights[k][r12];
CFluidPlaneRender::SHFieldSample& up = heights[k + 1][r12];
CFluidPlaneRender::SHFieldSample& down = heights[k - 1][r12];
CFluidPlaneRender::SHFieldSample& right = heights[k][r12 + 1];
CFluidPlaneRender::SHFieldSample& left = heights[k][r12 - 1];
float nx = (right.height - left.height) * normalScale;
float ny = (up.height - down.height) * normalScale;
float normalizer = ny * ny + nx * nx + nz * nz;
if (normalizer != 0.f)
normalizer = std::sqrt(normalizer);
normalizer = 63.f / normalizer;
sample.nx = s8(nx * normalizer);
sample.ny = s8(ny * normalizer);
sample.nz = s8(nz * normalizer);
if (sample.height > 0.f)
sample.wavecapIntensity = u8(std::min(255, int(info.x38_wavecapIntensityScale * sample.height)));
else
sample.wavecapIntensity = 0;
}
} else {
CFluidPlaneRender::SHFieldSample& sample = heights[r9][r12];
CFluidPlaneRender::SHFieldSample& up = heights[r9 + 1][r12];
CFluidPlaneRender::SHFieldSample& down = heights[r9 - 1][r12];
CFluidPlaneRender::SHFieldSample& right = heights[r9][r12 + 1];
CFluidPlaneRender::SHFieldSample& left = heights[r9][r12 - 1];
float nx = (right.height - left.height) * normalScale;
float ny = (up.height - down.height) * normalScale;
float normalizer = ny * ny + nx * nx + nz * nz;
if (normalizer != 0.f)
normalizer = std::sqrt(normalizer);
normalizer = 63.f / normalizer;
sample.nx = s8(nx * normalizer);
sample.ny = s8(ny * normalizer);
sample.nz = s8(nz * normalizer);
if (sample.height > 0.f)
sample.wavecapIntensity = u8(std::min(255, int(info.x38_wavecapIntensityScale * sample.height)));
else
sample.wavecapIntensity = 0;
}
}
}
}
}
}
bool CFluidPlaneCPU::UpdatePatch(float time, const CFluidPlaneRender::SPatchInfo& info,
CFluidPlaneRender::SHFieldSample (&heights)[46][46], u8 (&flags)[9][9],
const zeus::CVector3f& areaCenter,
const std::optional<CRippleManager>& rippleManager, int fromX, int toX,
int fromY, int toY) const {
rstl::reserved_vector<CFluidPlaneRender::SRippleInfo, 32> rippleInfos;
if (rippleManager) {
for (const CRipple& ripple : rippleManager->GetRipples()) {
if (ripple.GetTime() >= ripple.GetTimeFalloff())
continue;
CFluidPlaneRender::SRippleInfo rippleInfo(ripple, fromX, toX, fromY, toY);
if (PrepareRipple(ripple, info, rippleInfo))
rippleInfos.push_back(rippleInfo);
}
}
if (rippleInfos.empty())
return true;
ApplyTurbulence(time, heights, flags, sGlobalSineWave, info, areaCenter);
ApplyRipples(rippleInfos, heights, flags, sGlobalSineWave, info);
/* No further action necessary if using tessellation shaders */
if (m_tessellation)
return false;
if (info.x37_normalMode == CFluidPlaneRender::NormalMode::NoNormals)
UpdatePatchNoNormals(heights, flags, info);
else
UpdatePatchWithNormals(heights, flags, info);
return false;
}
/* Used to be part of locked cache
* These are too big for stack allocation */
static CFluidPlaneRender::SHFieldSample lc_heights[46][46] = {};
static u8 lc_flags[9][9] = {};
void CFluidPlaneCPU::Render(const CStateManager& mgr, float alpha, const zeus::CAABox& aabb, const zeus::CTransform& xf,
const zeus::CTransform& areaXf, bool noNormals, const zeus::CFrustum& frustum,
const std::optional<CRippleManager>& rippleManager, TUniqueId waterId,
const bool* gridFlags, u32 gridDimX, u32 gridDimY,
const zeus::CVector3f& areaCenter) {
SCOPED_GRAPHICS_DEBUG_GROUP("CFluidPlaneCPU::Render", zeus::skCyan);
TCastToConstPtr<CScriptWater> water = mgr.GetObjectById(waterId);
CFluidPlaneShader::RenderSetupInfo setupInfo = RenderSetup(mgr, alpha, xf, areaXf, aabb, water.GetPtr());
//if (!m_shader->isReady())
// return;
CFluidPlaneRender::NormalMode normalMode;
if (xb0_bumpMap && kEnableWaterBumpMaps)
normalMode = CFluidPlaneRender::NormalMode::NBT;
else if (!noNormals)
normalMode = CFluidPlaneRender::NormalMode::Normals;
else
normalMode = CFluidPlaneRender::NormalMode::NoNormals;
// Set Position and color format
switch (normalMode) {
case CFluidPlaneRender::NormalMode::NBT:
// Set NBT format
break;
case CFluidPlaneRender::NormalMode::Normals:
// Set Normal format
break;
default:
break;
}
float rippleResolutionRecip = 1.f / x108_rippleResolution;
CFluidPlaneRender::numSubdivisionsInTile = x104_tileSubdivisions;
CFluidPlaneRender::numTilesInHField = std::min(7, 42 / CFluidPlaneRender::numSubdivisionsInTile);
CFluidPlaneRender::numSubdivisionsInHField =
CFluidPlaneRender::numTilesInHField * CFluidPlaneRender::numSubdivisionsInTile;
zeus::CVector2f ripplePitch(x108_rippleResolution * CFluidPlaneRender::numSubdivisionsInHField);
// Amount to shift intensity values right (for added wavecap color)
int redShift = 0;
int greenShift = 0;
int blueShift = 0;
float wavecapIntensityScale = g_tweakGame->GetWavecapIntensityNormal();
switch (x44_fluidType) {
case EFluidType::PoisonWater:
wavecapIntensityScale = g_tweakGame->GetWavecapIntensityPoison();
redShift = 1;
blueShift = 1;
break;
case EFluidType::Lava:
case EFluidType::ThickLava:
wavecapIntensityScale = g_tweakGame->GetWavecapIntensityLava();
blueShift = 8;
greenShift = 8;
break;
default:
break;
}
if (water) {
float cameraPenetration =
mgr.GetCameraManager()->GetCurrentCamera(mgr)->GetTranslation().dot(zeus::skUp) -
water->GetTriggerBoundsWR().max.z();
wavecapIntensityScale *= (cameraPenetration >= 0.5f || cameraPenetration < 0.f) ? 1.f : 2.f * cameraPenetration;
}
u32 patchDimX = (water && water->GetPatchDimensionX()) ? water->GetPatchDimensionX() : 128;
u32 patchDimY = (water && water->GetPatchDimensionY()) ? water->GetPatchDimensionY() : 128;
m_verts.clear();
m_pVerts.clear();
if (m_tessellation) {
/* Additional uniform data for tessellation evaluation shader */
zeus::CColor colorMul;
colorMul.r() = wavecapIntensityScale / 255.f / float(1 << redShift);
colorMul.g() = wavecapIntensityScale / 255.f / float(1 << greenShift);
colorMul.b() = wavecapIntensityScale / 255.f / float(1 << blueShift);
m_shader->prepareDraw(setupInfo, xf.origin, *rippleManager, colorMul, x108_rippleResolution / 4.f);
} else {
m_shader->prepareDraw(setupInfo);
}
u32 tileY = 0;
float curY = aabb.min.y();
for (int i = 0; curY < aabb.max.y() && i < patchDimY; ++i) {
u32 tileX = 0;
float curX = aabb.min.x();
float _remDivsY = (aabb.max.y() - curY) * rippleResolutionRecip;
for (int j = 0; curX < aabb.max.x() && j < patchDimX; ++j) {
if (u8 renderFlags = water->GetPatchRenderFlags(j, i)) {
s16 remDivsX = std::min(s16((aabb.max.x() - curX) * rippleResolutionRecip),
s16(CFluidPlaneRender::numSubdivisionsInHField));
s16 remDivsY = std::min(s16(_remDivsY), s16(CFluidPlaneRender::numSubdivisionsInHField));
zeus::CVector3f localMax(x108_rippleResolution * remDivsX + curX, x108_rippleResolution * remDivsY + curY,
aabb.max.z());
zeus::CVector3f localMin(curX, curY, aabb.min.z());
zeus::CAABox testaabb(localMin + xf.origin, localMax + xf.origin);
if (frustum.aabbFrustumTest(testaabb)) {
CFluidPlaneRender::SPatchInfo info(localMin, localMax, xf.origin, x108_rippleResolution, x100_tileSize,
wavecapIntensityScale, CFluidPlaneRender::numSubdivisionsInHField,
normalMode, redShift, greenShift, blueShift, tileX, gridDimX, gridDimY,
tileY, gridFlags);
int fromX = tileX != 0 ? (2 - CFluidPlaneRender::numSubdivisionsInTile) : 0;
int toX;
if (tileX != gridDimX - 1)
toX = info.x0_xSubdivs + (CFluidPlaneRender::numSubdivisionsInTile - 2);
else
toX = info.x0_xSubdivs;
int fromY = tileY != 0 ? (2 - CFluidPlaneRender::numSubdivisionsInTile) : 0;
int toY;
if (tileY != gridDimY - 1)
toY = info.x1_ySubdivs + (CFluidPlaneRender::numSubdivisionsInTile - 2);
else
toY = info.x1_ySubdivs;
bool noRipples = UpdatePatch(mgr.GetFluidPlaneManager()->GetUVT(), info, lc_heights, lc_flags, areaCenter,
rippleManager, fromX, toX, fromY, toY);
RenderPatch(info, lc_heights, lc_flags, noRipples, renderFlags == 1, m_verts, m_pVerts);
}
}
curX += ripplePitch.x();
tileX += CFluidPlaneRender::numTilesInHField;
}
curY += ripplePitch.y();
tileY += CFluidPlaneRender::numTilesInHField;
}
m_shader->loadVerts(m_verts, m_pVerts);
m_shader->doneDrawing();
}
} // namespace urde