diff --git a/DataSpec/DNACommon/ANCS.cpp b/DataSpec/DNACommon/ANCS.cpp index 3ad2eabc9..5981c402b 100644 --- a/DataSpec/DNACommon/ANCS.cpp +++ b/DataSpec/DNACommon/ANCS.cpp @@ -33,6 +33,7 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE); if (force || cmdlPath.isNone()) { + cmdlPath.makeDirChain(false); if (!conn.createBlend(cmdlPath, hecl::blender::BlendType::Mesh)) return false; @@ -70,6 +71,7 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE); if (force || cmdlPath.isNone()) { + cmdlPath.makeDirChain(false); if (!conn.createBlend(cmdlPath, hecl::blender::BlendType::Mesh)) return false; diff --git a/DataSpec/DNACommon/CMDL.cpp b/DataSpec/DNACommon/CMDL.cpp index 114cbf37c..d80d8206a 100644 --- a/DataSpec/DNACommon/CMDL.cpp +++ b/DataSpec/DNACommon/CMDL.cpp @@ -652,6 +652,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, { MaterialSet matSet; matSet.read(reader); + matSet.ensureTexturesExtracted(pakRouter); GetVertexAttributes(matSet, vertAttribs); } } diff --git a/DataSpec/DNAMP1/CMDLMaterials.hpp b/DataSpec/DNAMP1/CMDLMaterials.hpp index ca863b349..7a00f411a 100644 --- a/DataSpec/DNAMP1/CMDLMaterials.hpp +++ b/DataSpec/DNAMP1/CMDLMaterials.hpp @@ -23,6 +23,25 @@ struct MaterialSet : BigDNA void addTexture(const UniqueID32& id) {textureIDs.push_back(id); ++textureCount;} void addMaterialEndOff(atUint32 off) {materialEndOffs.push_back(off); ++materialCount;} + + template + void ensureTexturesExtracted(PAKRouter& pakRouter) const + { + for (const auto& id : textureIDs) + { + const nod::Node* node; + const PAK::Entry* texEntry = pakRouter.lookupEntry(id, &node); + if (!texEntry) + continue; + hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry); + if (txtrPath.isNone()) + { + txtrPath.makeDirChain(false); + PAKEntryReadStream rs = texEntry->beginReadStream(*node); + TXTR::Extract(rs, txtrPath); + } + } + } } head; struct Material : BigDNA @@ -352,6 +371,10 @@ struct MaterialSet : BigDNA } } + void ensureTexturesExtracted(PAKRouter& pakRouter) const + { + head.ensureTexturesExtracted(pakRouter); + } }; struct HMDLMaterialSet : BigDNA diff --git a/DataSpec/DNAMP2/CMDLMaterials.hpp b/DataSpec/DNAMP2/CMDLMaterials.hpp index b05fd4f9e..58868a112 100644 --- a/DataSpec/DNAMP2/CMDLMaterials.hpp +++ b/DataSpec/DNAMP2/CMDLMaterials.hpp @@ -71,6 +71,11 @@ struct MaterialSet : BigDNA { DNACMDL::ReadMaterialSetToBlender_1_2(os, *this, pakRouter, entry, setIdx); } + + void ensureTexturesExtracted(PAKRouter& pakRouter) const + { + head.ensureTexturesExtracted(pakRouter); + } }; } diff --git a/DataSpec/DNAMP3/CMDLMaterials.hpp b/DataSpec/DNAMP3/CMDLMaterials.hpp index 02e102105..df1956a1b 100644 --- a/DataSpec/DNAMP3/CMDLMaterials.hpp +++ b/DataSpec/DNAMP3/CMDLMaterials.hpp @@ -198,6 +198,8 @@ struct MaterialSet : BigDNA { DNACMDL::ReadMaterialSetToBlender_3(os, *this, pakRouter, entry, setIdx); } + + void ensureTexturesExtracted(PAKRouter& pakRouter) const {} }; } diff --git a/Runtime/Audio/CSfxManager.cpp b/Runtime/Audio/CSfxManager.cpp index de9a16186..e970caace 100644 --- a/Runtime/Audio/CSfxManager.cpp +++ b/Runtime/Audio/CSfxManager.cpp @@ -3,8 +3,8 @@ namespace urde { -static TLockedToken> mpSfxTranslationTableTok; -std::vector* CSfxManager::mpSfxTranslationTable = nullptr; +static TLockedToken> mpSfxTranslationTableTok; +std::vector* CSfxManager::mpSfxTranslationTable = nullptr; static amuse::EffectReverbHiInfo s_ReverbHiQueued; static amuse::EffectChorusInfo s_ChorusQueued; @@ -20,12 +20,12 @@ CFactoryFnReturn FAudioTranslationTableFactory(const SObjectTag& tag, CInputStre const CVParamTransfer& vparms, CObjectReference* selfRef) { - std::unique_ptr> obj = std::make_unique>(); + std::unique_ptr> obj = std::make_unique>(); u32 count = in.readUint32Big(); obj->reserve(count); for (u32 i=0 ; ipush_back(in.readUint16Big()); - return TToken>::GetIObjObjectFor(std::move(obj)); + return TToken>::GetIObjObjectFor(std::move(obj)); } CSfxManager::CSfxChannel CSfxManager::m_channels[4]; @@ -379,8 +379,8 @@ u16 CSfxManager::TranslateSFXID(u16 id) if (index >= mpSfxTranslationTable->size()) return 0; - s16 ret = mpSfxTranslationTable->at(index); - if (ret == -1) + u16 ret = (*mpSfxTranslationTable)[index]; + if (ret == 0xffff) return 0; return ret; } @@ -747,7 +747,7 @@ void CSfxManager::Update(float dt) void CSfxManager::Shutdown() { mpSfxTranslationTable = nullptr; - mpSfxTranslationTableTok = TLockedToken>{}; + mpSfxTranslationTableTok = TLockedToken>{}; StopAndRemoveAllEmitters(); DisableAuxCallback(); } diff --git a/Runtime/Audio/CSfxManager.hpp b/Runtime/Audio/CSfxManager.hpp index 9e0f513f5..5d1ba79c4 100644 --- a/Runtime/Audio/CSfxManager.hpp +++ b/Runtime/Audio/CSfxManager.hpp @@ -11,7 +11,7 @@ namespace urde class CSfxManager { - static std::vector* mpSfxTranslationTable; + static std::vector* mpSfxTranslationTable; public: enum class ESfxChannels diff --git a/Runtime/CStateManager.cpp b/Runtime/CStateManager.cpp index f6c6393c4..6f5b30d71 100644 --- a/Runtime/CStateManager.cpp +++ b/Runtime/CStateManager.cpp @@ -2434,9 +2434,6 @@ void CStateManager::SetCurrentAreaId(TAreaId aid) x850_world->GetMapWorld()->RecalculateWorldSphere(*x8c0_mapWorldInfo, *x850_world); } -CEntity* CStateManager::ObjectById(TUniqueId uid) { return GetAllObjectList().GetObjectById(uid); } -const CEntity* CStateManager::GetObjectById(TUniqueId uid) const { return GetAllObjectList().GetObjectById(uid); } - void CStateManager::AreaUnloaded(TAreaId) { // Intentionally empty diff --git a/Runtime/CStateManager.hpp b/Runtime/CStateManager.hpp index 1a414f68e..6d9c0a7b7 100644 --- a/Runtime/CStateManager.hpp +++ b/Runtime/CStateManager.hpp @@ -361,8 +361,8 @@ public: void UpdateRoomAcoustics(TAreaId); TAreaId GetNextAreaId() const { return x8cc_nextAreaId; } void SetCurrentAreaId(TAreaId); - CEntity* ObjectById(TUniqueId uid); - const CEntity* GetObjectById(TUniqueId uid) const; + CEntity* ObjectById(TUniqueId uid) const { return GetAllObjectList().GetObjectById(uid); } + const CEntity* GetObjectById(TUniqueId uid) const { return GetAllObjectList().GetObjectById(uid); } void AreaUnloaded(TAreaId); void PrepareAreaUnload(TAreaId); void AreaLoaded(TAreaId); diff --git a/Runtime/Graphics/CBooRenderer.cpp b/Runtime/Graphics/CBooRenderer.cpp index 6b03450f3..1bdebc1dc 100644 --- a/Runtime/Graphics/CBooRenderer.cpp +++ b/Runtime/Graphics/CBooRenderer.cpp @@ -1067,7 +1067,7 @@ void CBooRenderer::AddPlaneObject(const void* obj, const zeus::CAABox& aabb, con float farDist = xb0_viewPlane.pointToPlaneDist(farPoint); if (closeDist >= 0.f || farDist >= 0.f) { - bool zOnly = plane.normal().isZero(); + bool zOnly = plane.normal() == zeus::CVector3f::skUp; bool invert; if (zOnly) invert = CGraphics::g_ViewMatrix.origin.z >= plane.d; diff --git a/Runtime/Graphics/CDrawable.hpp b/Runtime/Graphics/CDrawable.hpp index 5aba17753..a5d9337cf 100644 --- a/Runtime/Graphics/CDrawable.hpp +++ b/Runtime/Graphics/CDrawable.hpp @@ -9,8 +9,8 @@ enum class EDrawableType : u16 { WorldSurface, Particle, - UnsortedCallback, - SortedCallback, + Actor, + SimpleShadow, Decal }; diff --git a/Runtime/Graphics/CMakeLists.txt b/Runtime/Graphics/CMakeLists.txt index 0e85e28a0..9d7465965 100644 --- a/Runtime/Graphics/CMakeLists.txt +++ b/Runtime/Graphics/CMakeLists.txt @@ -43,6 +43,7 @@ set(GRAPHICS_SOURCES Shaders/CParticleSwooshShaders.hpp Shaders/CParticleSwooshShaders.cpp Shaders/CFluidPlaneShader.hpp Shaders/CFluidPlaneShader.cpp Shaders/CAABoxShader.hpp Shaders/CAABoxShader.cpp - Shaders/CWorldShadowShader.hpp Shaders/CWorldShadowShader.cpp) + Shaders/CWorldShadowShader.hpp Shaders/CWorldShadowShader.cpp + Shaders/CEnvFxShaders.hpp Shaders/CEnvFxShaders.cpp) runtime_add_list(Graphics GRAPHICS_SOURCES) diff --git a/Runtime/Graphics/CRainSplashGenerator.cpp b/Runtime/Graphics/CRainSplashGenerator.cpp index c6a93eeb9..2a3f8c8db 100644 --- a/Runtime/Graphics/CRainSplashGenerator.cpp +++ b/Runtime/Graphics/CRainSplashGenerator.cpp @@ -170,7 +170,7 @@ void CRainSplashGenerator::Update(float dt, CStateManager& mgr) EEnvFxType neededFx = mgr.GetWorld()->GetNeededEnvFx(); x28_dt = dt; x48_25_raining = false; - if (neededFx != EEnvFxType::None && mgr.GetEnvFxManager()->GetX24() && + if (neededFx != EEnvFxType::None && mgr.GetEnvFxManager()->IsSplashActive() && mgr.GetEnvFxManager()->GetRainMagnitude() != 0.f && neededFx == EEnvFxType::Rain) { UpdateRainSplashes(mgr, mgr.GetEnvFxManager()->GetRainMagnitude(), dt); diff --git a/Runtime/Graphics/Shaders/CEnvFxShaders.cpp b/Runtime/Graphics/Shaders/CEnvFxShaders.cpp new file mode 100644 index 000000000..1457504fb --- /dev/null +++ b/Runtime/Graphics/Shaders/CEnvFxShaders.cpp @@ -0,0 +1,45 @@ +#include "CEnvFxShaders.hpp" +#include "hecl/Pipeline.hpp" +#include "World/CEnvFxManager.hpp" + +namespace urde +{ +boo::ObjToken CEnvFxShaders::m_snowPipeline; +boo::ObjToken CEnvFxShaders::m_underwaterPipeline; + +void CEnvFxShaders::Initialize() +{ + m_snowPipeline = hecl::conv->convert(Shader_CEnvFxSnowShader{}); + m_underwaterPipeline = hecl::conv->convert(Shader_CEnvFxUnderwaterShader{}); +} + +void CEnvFxShaders::Shutdown() +{ + m_snowPipeline.reset(); + m_underwaterPipeline.reset(); +} + +void CEnvFxShaders::BuildShaderDataBinding(boo::IGraphicsDataFactory::Context& ctx, + CEnvFxManager& fxMgr, CEnvFxManagerGrid& grid) +{ + auto uBufInfo = grid.m_uniformBuf.getBufferInfo(); + auto iBufInfo = grid.m_instBuf.getBufferInfo(); + boo::ObjToken uniforms[] = {uBufInfo.first.get(), + fxMgr.m_fogUniformBuf.get()}; + size_t ubufOffsets[] = {uBufInfo.second, 0}; + size_t ubufSizes[] = {256, 256}; + boo::PipelineStage uniformStages[] = {boo::PipelineStage::Vertex, boo::PipelineStage::Fragment}; + boo::ObjToken textures[] = {fxMgr.xb74_txtrSnowFlake->GetBooTexture(), + fxMgr.x40_txtrEnvGradient->GetBooTexture()}; + grid.m_snowBinding = ctx.newShaderDataBinding(m_snowPipeline, nullptr, + iBufInfo.first.get(), nullptr, 2, uniforms, + uniformStages, ubufOffsets, ubufSizes, + 2, textures, nullptr, nullptr, 0, iBufInfo.second); + textures[0] = fxMgr.xc48_underwaterFlake->GetBooTexture(); + grid.m_underwaterBinding = ctx.newShaderDataBinding(m_underwaterPipeline, nullptr, + iBufInfo.first.get(), nullptr, 2, uniforms, + uniformStages, ubufOffsets, ubufSizes, + 2, textures, nullptr, nullptr, 0, iBufInfo.second); +} + +} diff --git a/Runtime/Graphics/Shaders/CEnvFxShaders.hpp b/Runtime/Graphics/Shaders/CEnvFxShaders.hpp new file mode 100644 index 000000000..7892c8983 --- /dev/null +++ b/Runtime/Graphics/Shaders/CEnvFxShaders.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "Graphics/CGraphics.hpp" +#include "boo/graphicsdev/IGraphicsDataFactory.hpp" + +namespace urde +{ +class CEnvFxManager; +class CEnvFxManagerGrid; + +class CEnvFxShaders +{ +public: + struct Instance + { + zeus::CVector3f positions[4]; + zeus::CColor color; + zeus::CVector2f uvs[4]; + }; + struct Uniform + { + zeus::CMatrix4f mvp; + zeus::CMatrix4f envMtx; + zeus::CColor moduColor; + }; + +private: + static boo::ObjToken m_snowPipeline; + static boo::ObjToken m_underwaterPipeline; + +public: + static void Initialize(); + static void Shutdown(); + static void BuildShaderDataBinding(boo::IGraphicsDataFactory::Context& ctx, + CEnvFxManager& fxMgr, CEnvFxManagerGrid& grid); +}; + +} diff --git a/Runtime/MP1/MP1.cpp b/Runtime/MP1/MP1.cpp index a3354035b..427f34b63 100644 --- a/Runtime/MP1/MP1.cpp +++ b/Runtime/MP1/MP1.cpp @@ -18,6 +18,7 @@ #include "Graphics/Shaders/CAABoxShader.hpp" #include "Graphics/Shaders/CWorldShadowShader.hpp" #include "Graphics/Shaders/CParticleSwooshShaders.hpp" +#include "Graphics/Shaders/CEnvFxShaders.hpp" #include "NESEmulator/CNESShader.hpp" #include "Audio/CStreamAudioManager.hpp" #include "CGBASupport.hpp" @@ -286,6 +287,7 @@ CMain::BooSetter::BooSetter(boo::IGraphicsDataFactory* factory, CTextSupportShader::Initialize(); CScanLinesFilter::Initialize(); CRandomStaticFilter::Initialize(); + CEnvFxShaders::Initialize(); CNESShader::Initialize(); } @@ -942,6 +944,7 @@ void CMain::Shutdown() CTextSupportShader::Shutdown(); CScanLinesFilter::Shutdown(); CRandomStaticFilter::Shutdown(); + CEnvFxShaders::Shutdown(); CFluidPlaneShader::Shutdown(); CFluidPlaneManager::RippleMapTex.reset(); CNESShader::Shutdown(); diff --git a/Runtime/MP1/MP1.hpp b/Runtime/MP1/MP1.hpp index 00c9ea13f..65e84654c 100644 --- a/Runtime/MP1/MP1.hpp +++ b/Runtime/MP1/MP1.hpp @@ -110,6 +110,7 @@ public: x4_resFactory->LoadOriginalIDs(*xcc_simplePool); LoadStringTable(); m_renderer.reset(AllocateRenderer(*xcc_simplePool, *x4_resFactory)); + CEnvFxManager::Initialize(); CScriptMazeNode::LoadMazeSeeds(); } diff --git a/Runtime/World/CEnvFxManager.cpp b/Runtime/World/CEnvFxManager.cpp index 172d81660..05f47dfa8 100644 --- a/Runtime/World/CEnvFxManager.cpp +++ b/Runtime/World/CEnvFxManager.cpp @@ -1,47 +1,656 @@ #include "CEnvFxManager.hpp" #include "Graphics/CTexture.hpp" #include "CActor.hpp" +#include "GameGlobalObjects.hpp" +#include "CSimplePool.hpp" +#include "CRandom16.hpp" +#include "CStateManager.hpp" +#include "TCastTo.hpp" +#include "CHUDBillboardEffect.hpp" +#include "World/CWorld.hpp" +#include "World/CPlayer.hpp" +#include "Collision/CGameCollision.hpp" +#include "Collision/CInternalRayCastStructure.hpp" +#include "World/CScriptTrigger.hpp" +#include "World/CScriptWater.hpp" +#include "Graphics/CBooRenderer.hpp" namespace urde { +static rstl::reserved_vector g_SnowForces; + +CEnvFxManagerGrid::CEnvFxManagerGrid(const zeus::CVector2i& position, const zeus::CVector2i& extent, + const std::vector& initialParticles, int reserve, + CEnvFxManager& parent, boo::IGraphicsDataFactory::Context& ctx) +: x4_position(position), xc_extent(extent), x1c_particles(initialParticles), + m_instBuf(parent.m_instPool.allocateBlock(CGraphics::g_BooFactory, reserve)), + m_uniformBuf(parent.m_uniformPool.allocateBlock(CGraphics::g_BooFactory)), + m_lineRenderer(ctx, CLineRenderer::EPrimitiveMode::Lines, reserve * 2, + parent.x40_txtrEnvGradient->GetBooTexture(), true, true) +{ + x1c_particles.reserve(reserve); + CEnvFxShaders::BuildShaderDataBinding(ctx, parent, *this); +} + CEnvFxManager::CEnvFxManager() { - + x40_txtrEnvGradient = g_SimplePool->GetObj("TXTR_EnvGradient"); + x40_txtrEnvGradient->GetBooTexture()->setClampMode(boo::TextureClampMode::ClampToEdge); + xb58_envRainSplash = g_SimplePool->GetObj("PART_EnvRainSplash"); + xb74_txtrSnowFlake = g_SimplePool->GetObj("TXTR_SnowFlake"); + xc48_underwaterFlake = g_SimplePool->GetObj("TXTR_UnderwaterFlake"); + CRandom16 random(0); + CGraphics::CommitResources([this](boo::IGraphicsDataFactory::Context& ctx) + { + m_fogUniformBuf = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(CGraphics::g_Fog), 1); + 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{}, 171, *this, ctx); + return true; + } BooTrace); + for (int i = 0; i < 16; ++i) + xb84_snowZDeltas.emplace_back(0.f, 0.f, random.Range(-2.f, -4.f)); } -void CEnvFxManager::Update(float, const CStateManager&) +void CEnvFxManager::SetSplashEffectRate(float rate, const CStateManager& mgr) +{ + if (TCastToPtr splashEffect = mgr.ObjectById(xb68_envRainSplashId)) + if (splashEffect->GetX104_26()) + 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::CVector3f::skZero, zeus::CVector3f::skZero, + false, true, 0xff, kInvalidAreaId); + xb70_rightRainSound = CSfxManager::AddEmitter(SFXsfx09F1, + zeus::CVector3f::skZero, zeus::CVector3f::skZero, + 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 splashEffect = mgr.ObjectById(xb68_envRainSplashId)) + mgr.SetActorAreaId(*splashEffect, mgr.GetNextAreaId()); + float camUpness = camXf.basis[1].dot(zeus::CVector3f::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::CVector3f::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& 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& list, CStateManager& mgr) +{ + for (CEntity* ent : mgr.GetAllObjectList()) + { + if (TCastToPtr trig = ent) + { + if ((trig->GetTriggerFlags() & ETriggerFlags::BlockEnvironmentalEffects) != ETriggerFlags::None) + list.push_back(ent->GetUniqueId()); + } + else if (TCastToPtr water = 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 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 + { + CMaterialFilter 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::CVector3f::skUp * 500.f; + CRayCastResult result = + CGameCollision::RayStaticIntersection(mgr, pos, zeus::CVector3f::skDown, 1000.f, filter); + if (result.IsValid()) + { + if (!blockListBuilt) + { + BuildBlockObjectList(blockList, mgr); + blockListBuilt = true; + } + for (TUniqueId id : blockList) + { + if (TCastToConstPtr trig = mgr.GetObjectById(id)) + { + if (auto tb = trig->GetTouchBounds()) + { + CCollidableAABox caabb(*tb, {EMaterialTypes::Trigger}); + CRayCastResult result2 = + caabb.CastRayInternal({pos, zeus::CVector3f::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& 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(); + } + 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.x, 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 = 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 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); + } +} + +void CEnvFxManagerGrid::RenderSnowParticles(const zeus::CTransform& camXf) const +{ + zeus::CVector3f xVec = 0.2f * camXf.basis[0]; + zeus::CVector3f zVec = 0.2f * camXf.basis[2]; +} + +void CEnvFxManagerGrid::RenderRainParticles(const zeus::CTransform& camXf) const +{ + m_lineRenderer.Reset(); + float zOffset = 2.f * (1.f - std::fabs(camXf.basis[2].dot(zeus::CVector3f::skUp))) + 1.f; + zeus::CColor color0(1.f, 10.f / 15.f); + for (const auto& particle : x1c_particles) + { + zeus::CVector3f pos0 = particle.toVec3f(); + zeus::CVector3f pos1 = pos0; + pos1.z += zOffset; + float uvy0 = pos0.z * 10.f + m_uvyOffset; + float uvy1 = pos1.z * 10.f + m_uvyOffset; + m_lineRenderer.AddVertex(pos0, zeus::CColor::skWhite, 1.f, {0.f, uvy0}); + m_lineRenderer.AddVertex(pos1, zeus::CColor::skClear, 1.f, {0.f, uvy1}); + } + m_lineRenderer.Render(zeus::CColor(1.f, 0.15f)); +} + +void CEnvFxManagerGrid::RenderUnderwaterParticles(const zeus::CTransform& camXf) const { } -void CEnvFxManager::Render(const CStateManager& mgr) +void CEnvFxManagerGrid::Render(const zeus::CTransform& xf, const zeus::CTransform& invXf, + const zeus::CTransform& camXf, float fxDensity, EEnvFxType fxType, + const CEnvFxManager& parent) const { + if (!x1c_particles.empty() && x14_block.first) + { + CGraphics::SetModelMatrix(xf * zeus::CTransform::Translate(x4_position.toVec2f() / 256.f)); + parent.m_uniformData.mvp = + CGraphics::GetPerspectiveProjectionMatrix(true) * + CGraphics::g_GXModelView.toMatrix4f(); + switch (fxType) + { + case EEnvFxType::Snow: + case EEnvFxType::Rain: + { + zeus::CMatrix4f envTexMtx(true); + envTexMtx[2][1] = 10.f; + envTexMtx[3][1] = 0.5f - (invXf * (zeus::CVector3f::skUp * x14_block.second)).z * 10.f; + m_uvyOffset = envTexMtx[3][1]; + parent.m_uniformData.envMtx = envTexMtx; + break; + } + default: + break; + } + m_uniformBuf.access() = 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) const +{ + mgr.GetCameraManager()->GetCurrentCamera(mgr); + if (mgr.GetCameraManager()->GetFluidCounter() != 0) + { + g_Renderer->SetWorldFog(ERglFogMode::PerspExp, 0.f, 35.f, zeus::CColor::skBlack); + m_uniformData.moduColor = zeus::CColor(1.f, 0.5f); + } + else + { + g_Renderer->SetWorldFog(ERglFogMode::PerspLin, 52.f, 57.f, zeus::CColor::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 water = ent) + if (auto tb = water->GetTouchBounds()) + waterTop = std::min(waterTop, tb->max.z); + zeus::CVector3f localWaterTop = invXf * (zeus::CVector3f::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) const +{ + 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))) + { + // 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::CColor::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, sizeof(CGraphics::g_Fog)); + for (const auto& grid : x50_grids) + grid.Render(xf, invXf, camXf, x30_fxDensity, fxType, *this); + // Backface cull + } + } } void CEnvFxManager::AsyncLoadResources(CStateManager& mgr) { + xb68_envRainSplashId = mgr.AllocateUniqueId(); + CHUDBillboardEffect* effect = + new CHUDBillboardEffect(xb58_envRainSplash, {}, xb68_envRainSplashId, true, "VisorRainSplashes", + CHUDBillboardEffect::GetNearClipDistance(mgr), + CHUDBillboardEffect::GetScaleForPOV(mgr), zeus::CColor::skWhite, + zeus::CVector3f::skOne, zeus::CVector3f::skZero); + effect->SetX104_27(true); + mgr.AddObject(effect); } void CEnvFxManager::SetFxDensity(s32 val, float density) { - x34_fxDensity = density; - x38_ = val; + x34_targetFxDensity = density; + x38_maxDensityDeltaSpeed = val; } void CEnvFxManager::AreaLoaded() { for (CEnvFxManagerGrid& grid : x50_grids) - grid.x0_24_ = true; + grid.x0_24_blockDirty = true; } void CEnvFxManager::Cleanup() { xb68_envRainSplashId = kInvalidUniqueId; - xb6a_ = false; - xb6c_ = 0; - xb70_ = 0; + xb6a_rainSoundActive = false; + xb6c_leftRainSound.reset(); + xb70_rightRainSound.reset(); +} + +void CEnvFxManager::Initialize() +{ + const SObjectTag* tag = g_ResFactory->GetResourceIdByName("DUMB_SnowForces"); + std::unique_ptr 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()); } } diff --git a/Runtime/World/CEnvFxManager.hpp b/Runtime/World/CEnvFxManager.hpp index 28d4502d0..0c855c656 100644 --- a/Runtime/World/CEnvFxManager.hpp +++ b/Runtime/World/CEnvFxManager.hpp @@ -4,6 +4,11 @@ #include "CToken.hpp" #include "zeus/CAABox.hpp" #include "Particle/CGenDescription.hpp" +#include "Audio/CSfxManager.hpp" +#include "Graphics/Shaders/CEnvFxShaders.hpp" +#include "Graphics/CLineRenderer.hpp" +#include "hecl/VertexBufferPool.hpp" +#include "hecl/UniformBufferPool.hpp" namespace urde { @@ -15,7 +20,8 @@ enum class EEnvFxType { None, Snow, - Rain + Rain, + UnderwaterFlake }; enum class EPhazonType @@ -27,64 +33,132 @@ enum class EPhazonType class CVectorFixed8_8 { - //u16 x0_[3]; +public: + union { + struct + { + s16 x, y, z; + }; + s16 v[3]; + }; + CVectorFixed8_8() { x = y = z = 0; } + CVectorFixed8_8(s16 xi, s16 yi, s16 zi) { x = xi; y = yi; z = zi; } + CVectorFixed8_8(const zeus::CVector3f& vec) + { + x = s16(vec.x * 256.f); + y = s16(vec.y * 256.f); + z = s16(vec.z * 256.f); + } + CVectorFixed8_8 operator+(const CVectorFixed8_8& other) const + { + return {s16(x + other.x), s16(y + other.y), s16(z + other.z)}; + } + CVectorFixed8_8 operator-(const CVectorFixed8_8& other) const + { + return {s16(x - other.x), s16(y - other.y), s16(z - other.z)}; + } + zeus::CVector3f toVec3f() const + { + return {x / 256.f, y / 256.f, z / 256.f}; + } }; class CEnvFxManagerGrid { friend class CEnvFxManager; - bool x0_24_ = true; - zeus::CVector2i x4_; - zeus::CVector2i xc_; - std::pair x14_ = {false, FLT_MAX}; - std::vector x1c_; + friend class CEnvFxShaders; + bool x0_24_blockDirty = true; + zeus::CVector2i x4_position; /* 8.8 fixed point */ + zeus::CVector2i xc_extent; /* 8.8 fixed point */ + std::pair x14_block = {false, FLT_MAX}; /* Blocked-bool, Z-coordinate */ + std::vector x1c_particles; + + mutable hecl::VertexBufferPool::Token m_instBuf; + mutable hecl::UniformBufferPool::Token m_uniformBuf; + mutable CLineRenderer m_lineRenderer; + + boo::ObjToken m_snowBinding; + boo::ObjToken m_underwaterBinding; + + mutable float m_uvyOffset = 0.f; + + void RenderSnowParticles(const zeus::CTransform& camXf) const; + void RenderRainParticles(const zeus::CTransform& camXf) const; + void RenderUnderwaterParticles(const zeus::CTransform& camXf) const; public: - CEnvFxManagerGrid(const zeus::CVector2i& a, const zeus::CVector2i& b, - const std::vector& vec, int reserve) - : x4_(a), xc_(b), x1c_(vec) - { - x1c_.reserve(reserve); - } + CEnvFxManagerGrid(const zeus::CVector2i& position, const zeus::CVector2i& extent, + const std::vector& initialParticles, int reserve, + CEnvFxManager& parent, boo::IGraphicsDataFactory::Context& ctx); + void Render(const zeus::CTransform& xf, const zeus::CTransform& invXf, + const zeus::CTransform& camXf, float fxDensity, EEnvFxType fxType, + const CEnvFxManager& parent) const; }; class CEnvFxManager { - zeus::CAABox x0_ = zeus::CAABox(-63.5, 63.5); - zeus::CVector3f x18_ = zeus::CVector3f::skZero; - bool x24_ = false; - float x28_ = 0.f; - u32 x2c_ = -1; - float x30_rainMagnitude = 0.f; - float x34_fxDensity = 0.f; - float x38_ = 0.f; - u8 x3c = 0; - + friend class CEnvFxManagerGrid; + friend class CEnvFxShaders; + zeus::CAABox x0_particleBounds = zeus::CAABox(-63.5f, 63.5f); + zeus::CVector3f x18_focusCellPosition = zeus::CVector3f::skZero; + bool x24_enableSplash = false; + float x28_firstSnowForce = 0.f; + s32 x2c_lastBlockedGridIdx = -1; + float x30_fxDensity = 0.f; + float x34_targetFxDensity = 0.f; + float x38_maxDensityDeltaSpeed = 0.f; + //bool x3c_snowflakeTextureMipBlanked = false; /* Shader simulates this texture mod */ + TLockedToken x40_txtrEnvGradient; rstl::reserved_vector x50_grids; - - float xb54_; + float xb54_baseSplashRate; TLockedToken xb58_envRainSplash; bool xb64_ = true; TUniqueId xb68_envRainSplashId = kInvalidUniqueId; - bool xb6a_ = false; - u32 xb6c_ = 0; - u32 xb70_ = 0; + bool xb6a_rainSoundActive = false; + CSfxHandle xb6c_leftRainSound; + CSfxHandle xb70_rightRainSound; + TLockedToken xb74_txtrSnowFlake; + bool xb80_ = true; + rstl::reserved_vector xb84_snowZDeltas; + TLockedToken xc48_underwaterFlake; + bool xc54_ = true; - void SetupSnowTevs(); - void SetupRainTevs(); + hecl::VertexBufferPool m_instPool; + hecl::UniformBufferPool m_uniformPool; + mutable CEnvFxShaders::Uniform m_uniformData; + boo::ObjToken m_fogUniformBuf; + + void SetSplashEffectRate(float f, const CStateManager& mgr); + void UpdateRainSounds(const CStateManager& mgr); + zeus::CVector3f GetParticleBoundsToWorldScale() const; + zeus::CTransform GetParticleBoundsToWorldTransform() const; + void UpdateVisorSplash(CStateManager& mgr, float dt, const zeus::CTransform& camXf); + void MoveWrapCells(s32, s32); + void CalculateSnowForces(const CVectorFixed8_8& zVec, rstl::reserved_vector& snowForces, + EEnvFxType type, const zeus::CVector3f& oopbtws, float dt); + static void BuildBlockObjectList(rstl::reserved_vector& list, CStateManager& mgr); + void UpdateBlockedGrids(CStateManager& mgr, EEnvFxType type, const zeus::CTransform& camXf, + const zeus::CTransform& xf, const zeus::CTransform& invXf); + void CreateNewParticles(EEnvFxType type); + void UpdateSnowParticles(const rstl::reserved_vector& snowForces); + void UpdateRainParticles(const CVectorFixed8_8& zVec, const zeus::CVector3f& oopbtws, float dt); + void UpdateUnderwaterParticles(const CVectorFixed8_8& zVec); + void SetupSnowTevs(const CStateManager& mgr) const; + void SetupRainTevs() const; + void SetupUnderwaterTevs(const zeus::CTransform& invXf, const CStateManager& mgr) const; public: CEnvFxManager(); void AsyncLoadResources(CStateManager& mgr); - void Update(float, const CStateManager&); - void Render(const CStateManager& mgr); + void Update(float, CStateManager& mgr); + void Render(const CStateManager& mgr) const; void SetFxDensity(s32, float); - void MoveWrapCells(s32, s32); - void GetParticleBoundsToWorldScale() const; void AreaLoaded(); - void SetXB54(float f) { xb54_ = f; } - bool GetX24() const { return x24_; } - float GetRainMagnitude() const { return x30_rainMagnitude; } + void SetSplashRate(float f) { xb54_baseSplashRate = f; } + bool IsSplashActive() const { return x24_enableSplash; } + float GetRainMagnitude() const { return x30_fxDensity; } void Cleanup(); + + static void Initialize(); }; } diff --git a/Runtime/World/CHUDBillboardEffect.hpp b/Runtime/World/CHUDBillboardEffect.hpp index b7735e198..bdac2ae3f 100644 --- a/Runtime/World/CHUDBillboardEffect.hpp +++ b/Runtime/World/CHUDBillboardEffect.hpp @@ -31,6 +31,9 @@ public: const zeus::CColor& color, const zeus::CVector3f& v1, const zeus::CVector3f& v2); ~CHUDBillboardEffect(); void Accept(IVisitor& visitor); + bool GetX104_26() const { return x104_26_; } + void SetX104_27(bool b) { x104_27_ = b; } + CParticleGen* GetParticleGen() const { return xe8_generator.get(); } static float GetNearClipDistance(CStateManager& mgr); static zeus::CVector3f GetScaleForPOV(CStateManager& mgr); diff --git a/Runtime/World/CMorphBall.cpp b/Runtime/World/CMorphBall.cpp index 53f3b72d6..29d1bd9b8 100644 --- a/Runtime/World/CMorphBall.cpp +++ b/Runtime/World/CMorphBall.cpp @@ -1258,7 +1258,7 @@ void CMorphBall::UpdateEffects(float dt, CStateManager& mgr) bool emitRainWake = (x0_player.GetPlayerMovementState() == CPlayer::EPlayerMovementState::OnGround && mgr.GetWorld()->GetNeededEnvFx() == EEnvFxType::Rain && mgr.GetEnvFxManager()->GetRainMagnitude() > 0.f && - mgr.GetEnvFxManager()->GetX24()); + mgr.GetEnvFxManager()->IsSplashActive()); x1bc8_wakeEffectGens[7]->SetParticleEmission(emitRainWake); float rainGenRate = std::min(mgr.GetEnvFxManager()->GetRainMagnitude() * 2.f * x0_player.x4fc_flatMoveSpeed / x0_player.GetBallMaxVelocity(), 1.f); diff --git a/Runtime/World/CPlayer.cpp b/Runtime/World/CPlayer.cpp index 1de515865..38cd508d7 100644 --- a/Runtime/World/CPlayer.cpp +++ b/Runtime/World/CPlayer.cpp @@ -933,7 +933,7 @@ void CPlayer::FluidFXThink(EFluidState state, CScriptWater& water, CStateManager if (water.GetFluidPlane().GetFluidType() == EFluidType::NormalWater) { float velMag = mgr.GetPlayer().GetVelocity().magnitude() / 10.f; - mgr.GetEnvFxManager()->SetXB54(10.f * std::max(1.f, velMag)); + mgr.GetEnvFxManager()->SetSplashRate(10.f * std::max(1.f, velMag)); } } break; diff --git a/Runtime/World/CScriptEffect.cpp b/Runtime/World/CScriptEffect.cpp index fe2ff36b7..6ea511359 100644 --- a/Runtime/World/CScriptEffect.cpp +++ b/Runtime/World/CScriptEffect.cpp @@ -147,15 +147,15 @@ void CScriptEffect::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CSt case EScriptObjectMessage::InitializedInArea: for (const SConnection& conn : x20_conns) { - if (!(conn.x0_state == EScriptObjectState::Active && conn.x4_msg == EScriptObjectMessage::Deactivate) && - !(conn.x0_state == EScriptObjectState::Modify && conn.x4_msg == EScriptObjectMessage::Activate)) - continue; - - auto search = mgr.GetIdListForScript(conn.x8_objId); - for (auto it = search.first; it != search.second; ++it) + if ((conn.x0_state == EScriptObjectState::Modify && conn.x4_msg == EScriptObjectMessage::Follow) || + (conn.x0_state == EScriptObjectState::InheritBounds && conn.x4_msg == EScriptObjectMessage::Activate)) { - if (TCastToConstPtr(mgr.GetObjectById(it->second))) - x13c_triggerId = it->second; + auto search = mgr.GetIdListForScript(conn.x8_objId); + for (auto it = search.first; it != search.second; ++it) + { + if (TCastToConstPtr(mgr.GetObjectById(it->second))) + x13c_triggerId = it->second; + } } } break; diff --git a/Runtime/World/CScriptSteam.cpp b/Runtime/World/CScriptSteam.cpp index aded0a6a5..fd021968a 100644 --- a/Runtime/World/CScriptSteam.cpp +++ b/Runtime/World/CScriptSteam.cpp @@ -55,7 +55,7 @@ void CScriptSteam::Think(float dt, CStateManager& mgr) float distance = (mag >= x164_ ? 0.f : std::cos((1.5707964f * mag) * x168_) * x158_); mgr.Player()->SetVisorSteam(distance, x15c_alphaInDur, x160_alphaOutDur, x154_texture, x150_); if (x150_) - mgr.GetEnvFxManager()->SetXB54(2.f * distance); + mgr.GetEnvFxManager()->SetSplashRate(2.f * distance); } else mgr.Player()->SetVisorSteam(0.f, x15c_alphaInDur, x160_alphaOutDur, CAssetId(), x150_); diff --git a/Shaders/CEnvFxShaders.shader b/Shaders/CEnvFxShaders.shader new file mode 100644 index 000000000..4ecf0a024 --- /dev/null +++ b/Shaders/CEnvFxShaders.shader @@ -0,0 +1,297 @@ +#shader CEnvFxSnowShader +#instattribute position4 0 +#instattribute position4 1 +#instattribute position4 2 +#instattribute position4 3 +#instattribute color +#instattribute uv4 0 +#instattribute uv4 1 +#instattribute uv4 2 +#instattribute uv4 3 +#srcfac one +#dstfac one +#primitive tristrips +#depthtest lequal +#depthwrite false +#alphawrite false +#culling none + +#vertex glsl +layout(location=0) in vec4 posIn[4]; +layout(location=4) in vec4 colorIn; +layout(location=5) in vec4 uvsIn[4]; + +UBINDING0 uniform EnvFxUniform +{ + mat4 mv; + mat4 proj; + mat4 envMtx; + vec4 moduColor; +}; + +struct VertToFrag +{ + vec4 mvPos; + vec4 color; + vec2 uvFlake; + vec2 uvEnv; +}; + +SBINDING(0) out VertToFrag vtf; +void main() +{ + vec4 pos = posIn[gl_VertexID]; + vtf.color = colorIn * moduColor; + vtf.uvFlake = uvsIn[gl_VertexID].xy; + vtf.uvEnv = (envMtx * pos).xy; + vtf.mvPos = mv * pos; + gl_Position = proj * vtf.mvPos; +} + +#fragment glsl +struct VertToFrag +{ + vec4 mvPos; + vec4 color; + vec2 uvFlake; + vec2 uvEnv; +}; + +SBINDING(0) in VertToFrag vtf; +layout(location=0) out vec4 colorOut; +TBINDING0 uniform sampler2D texFlake; +TBINDING1 uniform sampler2D texEnv; + +UBINDING1 uniform FogUniform +{ + int mode; + vec4 color; + float rangeScale; + float start; +}; + +vec4 MainPostFunc(vec4 colorIn) +{ + float fogZ, temp; + switch (mode) + { + case 2: + fogZ = (-vtf.mvPos.z - start) * rangeScale; + break; + case 4: + fogZ = 1.0 - exp2(-8.0 * (-vtf.mvPos.z - start) * rangeScale); + break; + case 5: + temp = (-vtf.mvPos.z - start) * rangeScale; + fogZ = 1.0 - exp2(-8.0 * temp * temp); + break; + case 6: + fogZ = exp2(-8.0 * (start + vtf.mvPos.z) * rangeScale); + break; + case 7: + temp = (start + vtf.mvPos.z) * rangeScale; + fogZ = exp2(-8.0 * temp * temp); + break; + default: + fogZ = 0.0; + break; + } + return vec4(mix(colorIn, color, clamp(fogZ, 0.0, 1.0)).rgb, colorIn.a); +} + +void main() +{ + colorOut = MainPostFunc(vtf.color * texture(texFlake, vtf.uvFlake) * texture(texEnv, vtf.uvEnv)); +} + +#vertex hlsl +struct VertData +{ + float4 posIn[4] : POSITION; + float4 colorIn : COLOR; + float4 uvsIn[4] : UV; +}; + +cbuffer EnvFxUniform : register(b0) +{ + float4x4 mv; + float4x4 proj; + float4x4 envMtx; + float4 moduColor; +}; + +struct VertToFrag +{ + float4 position : SV_Position; + float4 mvPos : POSITION; + float4 color : COLOR; + float2 uvFlake : UV0; + float2 uvEnv : UV1; +}; + +VertToFrag main(in VertData v, in uint vertId : SV_VertexID) +{ + VertToFrag vtf; + vtf.color = v.colorIn * moduColor; + vtf.uvFlake = v.uvsIn[vertId].xy; + vtf.uvEnv = mul(envMtx, v.posIn[vertId]).xy; + vtf.mvPos = mul(mv, v.posIn[vertId]); + vtf.position = mul(proj, vtf.mvPos); + return vtf; +} + +#fragment hlsl +SamplerState samp : register(s0); +SamplerState sampClamp : register(s3); +Texture2D texFlake : register(t0); +Texture2D texEnv : register(t1); +struct VertToFrag +{ + float4 position : SV_Position; + float4 mvPos : POSITION; + float4 color : COLOR; + float2 uvFlake : UV0; + float2 uvEnv : UV1; +}; + +cbuffer FogUniform : register(b1) +{ + int mode; + float4 color; + float rangeScale; + float start; +}; + +static float4 MainPostFunc(in VertToFrag vtf, float4 colorIn) +{ + float fogZ, temp; + switch (mode) + { + case 2: + fogZ = (-vtf.mvPos.z - start) * rangeScale; + break; + case 4: + fogZ = 1.0 - exp2(-8.0 * (-vtf.mvPos.z - start) * rangeScale); + break; + case 5: + temp = (-vtf.mvPos.z - start) * rangeScale; + fogZ = 1.0 - exp2(-8.0 * temp * temp); + break; + case 6: + fogZ = exp2(-8.0 * (start + vtf.mvPos.z) * rangeScale); + break; + case 7: + temp = (start + vtf.mvPos.z) * rangeScale; + fogZ = exp2(-8.0 * temp * temp); + break; + default: + fogZ = 0.0; + break; + } + return float4(lerp(colorIn, color, saturate(fogZ)).rgb, colorIn.a); +} + +float4 main(in VertToFrag vtf) : SV_Target0 +{ + return MainPostFunc(vtf, vtf.color * texFlake.Sample(samp, vtf.uvFlake) * texEnv.Sample(sampClamp, vtf.uvEnv)); +} + +#vertex metal +struct VertData +{ + float4 posIn[4]; + float4 colorIn; + float4 uvsIn[4]; +}; + +struct EnvFxUniform +{ + float4x4 mv; + float4x4 proj; + float4x4 envMtx; + float4 moduColor; +}; + +struct VertToFrag +{ + float4 position [[ position ]]; + float4 mvPos; + float4 color; + float2 uvFlake; + float2 uvEnv; +}; + +vertex VertToFrag vmain(constant VertData* va [[ buffer(1) ]], + uint vertId [[ vertex_id ]], uint instId [[ instance_id ]], + constant EnvFxUniform& particle [[ buffer(2) ]]) +{ + VertToFrag vtf; + constant VertData& v = va[instId]; + vtf.color = v.colorIn * particle.moduColor; + vtf.uvFlake = v.uvsIn[vertId].xy; + vtf.uvEnv = (envMtx * v.posIn[vertId]).xy; + vtf.mvPos = particle.mv * v.posIn[vertId]; + vtf.position = particle.proj * vtf.mvPos; + return vtf; +} + +#fragment metal +struct VertToFrag +{ + float4 position [[ position ]]; + float4 mvPos; + float4 color; + float2 uvFlake; + float2 uvEnv; +}; + +struct FogUniform +{ + int mode; + float4 color; + float rangeScale; + float start; +}; + +float4 MainPostFunc(thread VertToFrag& vtf, constant FogUniform& fu, float4 colorIn) +{ + float fogZ, temp; + switch (lu.mode) + { + case 2: + fogZ = (-vtf.mvPos.z - fu.start) * fu.rangeScale; + break; + case 4: + fogZ = 1.0 - exp2(-8.0 * (-vtf.mvPos.z - fu.start) * fu.rangeScale); + break; + case 5: + temp = (-vtf.mvPos.z - fu.start) * fu.rangeScale; + fogZ = 1.0 - exp2(-8.0 * temp * temp); + break; + case 6: + fogZ = exp2(-8.0 * (fu.start + vtf.mvPos.z) * fu.rangeScale); + break; + case 7: + temp = (fu.start + vtf.mvPos.z) * fu.rangeScale; + fogZ = exp2(-8.0 * temp * temp); + break; + default: + fogZ = 0.0; + break; + } + return float4(mix(colorIn, fu.color, saturate(fogZ)).rgb, colorIn.a); +} + +fragment float4 fmain(VertToFrag vtf [[ stage_in ]], + sampler samp [[ sampler(0) ]], + sampler sampClamp [[ sampler(3) ]], + constant FogUniform& fu [[ buffer(3) ]], + texture2d texFlake [[ texture(0) ]], + texture2d texEnv [[ texture(1) ]]) +{ + return MainPostFunc(vtf, fu, vtf.color * texFlake.sample(samp, vtf.uvFlake) * texEnv.sample(sampClamp, vtf.uvEnv)); +} + +#shader CEnvFxUnderwaterShader : CEnvFxSnowShader +#srcfac srcalpha +#dstfac invsrcalpha diff --git a/Shaders/CMakeLists.txt b/Shaders/CMakeLists.txt index 98f9c9f94..0e21019da 100644 --- a/Shaders/CMakeLists.txt +++ b/Shaders/CMakeLists.txt @@ -10,6 +10,7 @@ add_shader(CColoredQuadFilter) add_shader(CDecalShaders) add_shader(CElementGenShaders) add_shader(CEnergyBarShader) +add_shader(CEnvFxShaders) add_shader(CFogVolumeFilter) add_shader(CFogVolumePlaneShader) add_shader(CLineRendererShaders) diff --git a/hecl b/hecl index 7561dd66e..26541cd2b 160000 --- a/hecl +++ b/hecl @@ -1 +1 @@ -Subproject commit 7561dd66e6ab5f5b7d4f94fc7bf732653ce104fb +Subproject commit 26541cd2b460592049b75b9612562fa6b7d01cc5 diff --git a/specter b/specter index 8d29c1449..6475d4ad2 160000 --- a/specter +++ b/specter @@ -1 +1 @@ -Subproject commit 8d29c1449c4e661449c6b666c2fe17ce35dff023 +Subproject commit 6475d4ad284a6106f55313f86d8fdc2ac13a381b