#include "Runtime/Graphics/Shaders/CFluidPlaneShader.hpp" #include "Runtime/World/CRipple.hpp" #include "Runtime/World/CRippleManager.hpp" #include namespace metaforce { CFluidPlaneShader::Cache CFluidPlaneShader::_cache = {}; u16 CFluidPlaneShader::Cache::MakeCacheKey(const SFluidPlaneShaderInfo& info) { u16 ret = 0; switch (info.m_type) { case EFluidType::NormalWater: case EFluidType::PhazonFluid: case EFluidType::Four: if (info.m_hasLightmap) { ret |= 1 << 2; if (info.m_doubleLightmapBlend) ret |= 1 << 3; } if (!info.m_hasEnvMap && info.m_hasEnvBumpMap) ret |= 1 << 4; if (info.m_hasEnvMap) ret |= 1 << 5; break; case EFluidType::PoisonWater: ret |= 1; if (info.m_hasLightmap) { ret |= 1 << 2; if (info.m_doubleLightmapBlend) ret |= 1 << 3; } if (info.m_hasEnvBumpMap) ret |= 1 << 4; break; case EFluidType::Lava: ret |= 2; if (info.m_hasBumpMap) ret |= 1 << 2; break; case EFluidType::ThickLava: ret |= 3; if (info.m_hasBumpMap) ret |= 1 << 2; break; } if (info.m_hasPatternTex1) ret |= 1 << 6; if (info.m_hasPatternTex2) ret |= 1 << 7; if (info.m_hasColorTex) ret |= 1 << 8; if (info.m_additive) ret |= 1 << 9; return ret; } u16 CFluidPlaneShader::Cache::MakeCacheKey(const SFluidPlaneDoorShaderInfo& info) { u16 ret = 0; if (info.m_hasPatternTex1) ret |= 1 << 0; if (info.m_hasPatternTex2) ret |= 1 << 1; if (info.m_hasColorTex) ret |= 1 << 2; return ret; } template <> CFluidPlaneShader::ShaderPair CFluidPlaneShader::Cache::GetOrBuildShader(const SFluidPlaneShaderInfo& info) { OPTICK_EVENT(); u16 key = MakeCacheKey(info); auto& slot = CacheSlot(info, key); if (slot.m_regular) return slot; slot.m_regular = hecl::conv->convert(Shader_CFluidPlaneShader{info, false}); if (info.m_tessellation) slot.m_tessellation = hecl::conv->convert(Shader_CFluidPlaneShader{info, true}); return slot; } template <> CFluidPlaneShader::ShaderPair CFluidPlaneShader::Cache::GetOrBuildShader(const SFluidPlaneDoorShaderInfo& info) { OPTICK_EVENT(); u16 key = MakeCacheKey(info); auto& slot = CacheSlot(info, key); if (slot.m_regular) return slot; slot.m_regular = hecl::conv->convert(Shader_CFluidPlaneDoorShader{info}); return slot; } void CFluidPlaneShader::Cache::Clear() { for (auto& p : m_cache) p.reset(); for (auto& p : m_doorCache) p.reset(); } void CFluidPlaneShader::PrepareBinding(u32 maxVertCount) { CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) { m_vbo = ctx.newDynamicBuffer(boo::BufferUse::Vertex, sizeof(Vertex), maxVertCount); if (m_pipelines.m_tessellation) { m_pvbo = ctx.newDynamicBuffer(boo::BufferUse::Vertex, sizeof(PatchVertex), maxVertCount); } m_uniBuf = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(Uniform), 1); const std::array, 3> ubufs{{ m_uniBuf.get(), m_uniBuf.get(), m_uniBuf.get(), }}; constexpr std::array ubufStages{ boo::PipelineStage::Vertex, boo::PipelineStage::Vertex, boo::PipelineStage::Fragment, }; constexpr std::array ubufOffs{ 0, 0, 1280, }; constexpr std::array ubufSizes{ 1280, 1280, sizeof(CModelShaders::LightingUniform), }; size_t texCount = 0; std::array, 8> texs; if (m_patternTex1) { texs[texCount++] = m_patternTex1->GetBooTexture(); } if (m_patternTex2) { texs[texCount++] = m_patternTex2->GetBooTexture(); } if (m_colorTex) { texs[texCount++] = m_colorTex->GetBooTexture(); } if (m_bumpMap) { texs[texCount++] = m_bumpMap->GetBooTexture(); } if (m_envMap) { texs[texCount++] = m_envMap->GetBooTexture(); } if (m_envBumpMap) { texs[texCount++] = m_envBumpMap->GetBooTexture(); } if (m_lightmap) { texs[texCount++] = m_lightmap->GetBooTexture(); } auto regular = ctx.newShaderDataBinding(m_pipelines.m_regular, m_vbo.get(), nullptr, nullptr, ubufs.size(), ubufs.data(), ubufStages.data(), ubufOffs.data(), ubufSizes.data(), texCount, texs.data(), nullptr, nullptr); boo::ObjToken tessellation; if (m_pipelines.m_tessellation) { texs[texCount++] = m_rippleMap.get(); tessellation = ctx.newShaderDataBinding(m_pipelines.m_tessellation, m_pvbo.get(), nullptr, nullptr, ubufs.size(), ubufs.data(), ubufStages.data(), ubufOffs.data(), ubufSizes.data(), texCount, texs.data(), nullptr, nullptr); } m_dataBind = {regular, tessellation}; return true; } BooTrace); } void CFluidPlaneShader::Shutdown() { _cache.Clear(); } CFluidPlaneShader::CFluidPlaneShader(EFluidType type, const TLockedToken& patternTex1, const TLockedToken& patternTex2, const TLockedToken& colorTex, const TLockedToken& bumpMap, const TLockedToken& envMap, const TLockedToken& envBumpMap, const TLockedToken& lightmap, const boo::ObjToken& rippleMap, bool doubleLightmapBlend, bool additive, u32 maxVertCount) : m_patternTex1(patternTex1) , m_patternTex2(patternTex2) , m_colorTex(colorTex) , m_bumpMap(bumpMap) , m_envMap(envMap) , m_envBumpMap(envBumpMap) , m_lightmap(lightmap) , m_rippleMap(rippleMap) { SFluidPlaneShaderInfo shaderInfo(type, m_patternTex1.HasReference(), m_patternTex2.HasReference(), m_colorTex.HasReference(), m_bumpMap.HasReference(), m_envMap.HasReference(), m_envBumpMap.HasReference(), m_lightmap.HasReference(), m_rippleMap.operator bool(), doubleLightmapBlend, additive); m_pipelines = _cache.GetOrBuildShader(shaderInfo); PrepareBinding(maxVertCount); } CFluidPlaneShader::CFluidPlaneShader(const TLockedToken& patternTex1, const TLockedToken& patternTex2, const TLockedToken& colorTex, u32 maxVertCount) : m_patternTex1(patternTex1), m_patternTex2(patternTex2), m_colorTex(colorTex) { SFluidPlaneDoorShaderInfo shaderInfo(m_patternTex1.HasReference(), m_patternTex2.HasReference(), m_colorTex.HasReference()); m_pipelines = _cache.GetOrBuildShader(shaderInfo); PrepareBinding(maxVertCount); } void CFluidPlaneShader::prepareDraw(const RenderSetupInfo& info) { Uniform& uni = *reinterpret_cast(m_uniBuf->map(sizeof(Uniform))); uni.m_mv = CGraphics::g_GXModelView.toMatrix4f(); uni.m_mvNorm = info.normMtx; uni.m_proj = CGraphics::GetPerspectiveProjectionMatrix(true); uni.m_texMtxs = info.texMtxs; uni.m_lighting.ActivateLights(info.lights); for (size_t i = 0; i < uni.m_lighting.colorRegs.size(); ++i) { uni.m_lighting.colorRegs[i] = info.kColors[i]; } uni.m_lighting.mulColor = info.kColors[3]; uni.m_lighting.fog = CGraphics::g_Fog; uni.m_pad2.x() = info.indScale; m_uniBuf->unmap(); } void CFluidPlaneShader::prepareDraw(const RenderSetupInfo& info, const zeus::CVector3f& waterCenter, const CRippleManager& rippleManager, const zeus::CColor& colorMul, float rippleNormResolution) { Uniform& uni = *reinterpret_cast(m_uniBuf->map(sizeof(Uniform))); uni.m_mv = CGraphics::g_GXModelView.toMatrix4f(); uni.m_mvNorm = info.normMtx; uni.m_proj = CGraphics::GetPerspectiveProjectionMatrix(true); uni.m_texMtxs = info.texMtxs; size_t i = 0; for (const CRipple& ripple : rippleManager.GetRipples()) { assert(i < uni.m_ripple.size() && "Too many ripples"); Ripple& rOut = uni.m_ripple[i++]; if (ripple.GetTime() >= ripple.GetTimeFalloff()) { rOut.center.zeroOut(); rOut.params.zeroOut(); continue; } zeus::CVector3f localPos = ripple.GetCenter() - waterCenter; rOut.center.x() = float(localPos.x()); rOut.center.y() = float(localPos.y()); rOut.center.z() = ripple.GetTime() * ripple.GetOOTimeFalloff(); rOut.center.w() = ripple.GetOODistanceFalloff(); rOut.params.x() = ripple.GetAmplitude(); rOut.params.y() = ripple.GetPhase(); rOut.params.z() = (1.f - ripple.GetTime() * ripple.GetOOTimeFalloff() * ripple.GetOOTimeFalloff()) * ripple.GetFrequency(); } uni.m_colorMul = colorMul; uni.m_pad[0].x() = rippleNormResolution; uni.m_lighting.ActivateLights(info.lights); for (i = 0; i < uni.m_lighting.colorRegs.size(); ++i) { uni.m_lighting.colorRegs[i] = info.kColors[i]; } uni.m_lighting.mulColor = info.kColors[3]; uni.m_lighting.fog = CGraphics::g_Fog; uni.m_pad2.x() = info.indScale; m_uniBuf->unmap(); } void CFluidPlaneShader::loadVerts(const std::vector& verts, const std::vector& pVerts) { m_vbo->load(verts.data(), verts.size() * sizeof(Vertex)); if (m_pvbo) m_pvbo->load(pVerts.data(), pVerts.size() * sizeof(PatchVertex)); } } // namespace metaforce