From 3650a58a1ee89bb57735a07c7bf7cc8910c63602 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Mon, 7 Aug 2017 20:03:57 -1000 Subject: [PATCH] Initial CFluidPlane implementation --- .idea/misc.xml | 2 +- DataSpec/DNACommon/Tweaks/ITweakGame.hpp | 1 + DataSpec/DNAMP1/Tweaks/CTweakGame.hpp | 3 +- Runtime/CStateManager.cpp | 7 +- Runtime/CStateManager.hpp | 1 + Runtime/Character/CActorLights.cpp | 30 +- Runtime/Character/CActorLights.hpp | 1 + Runtime/Graphics/CMakeLists.txt | 7 +- Runtime/Graphics/CModelBoo.cpp | 45 +- .../Graphics/Shaders/CFluidPlaneShader.cpp | 199 +++++++ .../Graphics/Shaders/CFluidPlaneShader.hpp | 110 ++++ .../Shaders/CFluidPlaneShaderGLSL.cpp | 559 ++++++++++++++++++ .../Shaders/CFluidPlaneShaderHLSL.cpp | 4 + .../Shaders/CFluidPlaneShaderMetal.cpp | 480 +++++++++++++++ Runtime/Graphics/Shaders/CModelShaders.cpp | 48 ++ Runtime/Graphics/Shaders/CModelShaders.hpp | 4 +- Runtime/World/CActor.hpp | 1 + Runtime/World/CFluidPlane.hpp | 10 +- Runtime/World/CFluidPlaneCPU.cpp | 240 +++++++- Runtime/World/CFluidPlaneCPU.hpp | 28 +- Runtime/World/CFluidPlaneManager.cpp | 55 +- Runtime/World/CFluidPlaneManager.hpp | 22 +- Runtime/World/CFluidUVMotion.cpp | 2 +- Runtime/World/CFluidUVMotion.hpp | 12 +- Runtime/World/CPlayer.cpp | 10 +- Runtime/World/CScriptWater.cpp | 15 + Runtime/World/CScriptWater.hpp | 4 +- 27 files changed, 1801 insertions(+), 99 deletions(-) create mode 100644 Runtime/Graphics/Shaders/CFluidPlaneShader.cpp create mode 100644 Runtime/Graphics/Shaders/CFluidPlaneShader.hpp create mode 100644 Runtime/Graphics/Shaders/CFluidPlaneShaderGLSL.cpp create mode 100644 Runtime/Graphics/Shaders/CFluidPlaneShaderHLSL.cpp create mode 100644 Runtime/Graphics/Shaders/CFluidPlaneShaderMetal.cpp diff --git a/.idea/misc.xml b/.idea/misc.xml index 79b3c9483..674375dd3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/DataSpec/DNACommon/Tweaks/ITweakGame.hpp b/DataSpec/DNACommon/Tweaks/ITweakGame.hpp index 8f6dc8b80..4dae5ce70 100644 --- a/DataSpec/DNACommon/Tweaks/ITweakGame.hpp +++ b/DataSpec/DNACommon/Tweaks/ITweakGame.hpp @@ -12,6 +12,7 @@ struct ITweakGame : ITweak virtual bool GetSplashScreensDisabled() const = 0; virtual float GetFirstPersonFOV() const = 0; virtual float GetPressStartDelay() const = 0; + virtual float GetFluidEnvBumpScale() const = 0; virtual float GetHardModeDamageMultiplier() const = 0; virtual float GetHardModeWeaponMultiplier() const = 0; }; diff --git a/DataSpec/DNAMP1/Tweaks/CTweakGame.hpp b/DataSpec/DNAMP1/Tweaks/CTweakGame.hpp index 31268c17f..dac212252 100644 --- a/DataSpec/DNAMP1/Tweaks/CTweakGame.hpp +++ b/DataSpec/DNAMP1/Tweaks/CTweakGame.hpp @@ -26,7 +26,7 @@ struct CTweakGame : ITweakGame Value x40_unknown10; Value x44_unknown11; Value x48_unknown12; - Value x4c_unknown13; + Value x4c_fluidEnvBumpScale; Value x50_unknown14; Value x54_unknown15; Value x58_unknown16; @@ -38,6 +38,7 @@ struct CTweakGame : ITweakGame virtual bool GetSplashScreensDisabled() const { return x2b_splashScreensDisabled; } virtual float GetFirstPersonFOV() const { return x24_fov; } virtual float GetPressStartDelay() const { return x30_presStartDelay; } + virtual float GetFluidEnvBumpScale() const { return x4c_fluidEnvBumpScale; } virtual float GetHardModeDamageMultiplier() const { return x60_hardmodeDamageMult; } virtual float GetHardModeWeaponMultiplier() const { return x64_hardmodeWeaponMult; } CTweakGame() = default; diff --git a/Runtime/CStateManager.cpp b/Runtime/CStateManager.cpp index 14b0f1e40..5d36ac46d 100644 --- a/Runtime/CStateManager.cpp +++ b/Runtime/CStateManager.cpp @@ -1055,7 +1055,7 @@ void CStateManager::SendScriptMsg(TUniqueId src, TEditorId dest, EScriptObjectMe { CEntity* ent = ObjectById(src); auto search = GetIdListForScript(dest); - if (ent && search.first != x890_scriptIdMap.cend() && search.second != x890_scriptIdMap.cend()) + if (ent && search.first != x890_scriptIdMap.cend()) { for (auto it = search.first; it != search.second; ++it) { @@ -1137,7 +1137,10 @@ TUniqueId CStateManager::GetIdForScript(TEditorId id) const std::pair::const_iterator, std::multimap::const_iterator> CStateManager::GetIdListForScript(TEditorId id) const { - return x890_scriptIdMap.equal_range(id); + auto ret = x890_scriptIdMap.equal_range(id); + if (ret.first->first != id) + ret.first = x890_scriptIdMap.cend(); + return ret; } void CStateManager::LoadScriptObjects(TAreaId aid, CInputStream& in, std::vector& idsOut) diff --git a/Runtime/CStateManager.hpp b/Runtime/CStateManager.hpp index c3d67d658..5db2cdabd 100644 --- a/Runtime/CStateManager.hpp +++ b/Runtime/CStateManager.hpp @@ -315,6 +315,7 @@ public: std::pair::const_iterator, std::multimap::const_iterator> GetIdListForScript(TEditorId) const; + std::multimap::const_iterator GetIdListEnd() const { return x890_scriptIdMap.cend(); }; void LoadScriptObjects(TAreaId, CInputStream& in, std::vector& idsOut); void InitializeScriptObjects(const std::vector& objIds); std::pair LoadScriptObject(TAreaId, EScriptObjectType, u32, CInputStream& in); diff --git a/Runtime/Character/CActorLights.cpp b/Runtime/Character/CActorLights.cpp index c1c328145..8215bdd58 100644 --- a/Runtime/Character/CActorLights.cpp +++ b/Runtime/Character/CActorLights.cpp @@ -503,19 +503,9 @@ void CActorLights::BuildDynamicLightList(const CStateManager& mgr, const zeus::C } } -void CActorLights::ActivateLights(CBooModel& model) const +std::vector CActorLights::BuildLightVector() const { std::vector lights; - if (x298_28_inArea) - { - if (!x298_26_hasAreaLights || !x299_26_) - { - g_Renderer->SetAmbientColor(zeus::CColor::skWhite); - model.ActivateLights(lights); - return; - } - } - lights.push_back(CLight::BuildLocalAmbient(zeus::CVector3f::skZero, x288_ambientColor)); if (x0_areaLights.size()) @@ -538,6 +528,24 @@ void CActorLights::ActivateLights(CBooModel& model) const for (const CLight& light : x144_dynamicLights) lights.push_back(light); + return lights; +} + +void CActorLights::ActivateLights(CBooModel& model) const +{ + std::vector lights; + + if (x298_28_inArea) + { + if (!x298_26_hasAreaLights || !x299_26_) + { + g_Renderer->SetAmbientColor(zeus::CColor::skWhite); + model.ActivateLights(lights); + return; + } + } + + lights = BuildLightVector(); model.ActivateLights(lights); if (x298_31_disableWorldLights) diff --git a/Runtime/Character/CActorLights.hpp b/Runtime/Character/CActorLights.hpp index b831fd474..48770efc5 100644 --- a/Runtime/Character/CActorLights.hpp +++ b/Runtime/Character/CActorLights.hpp @@ -71,6 +71,7 @@ public: void BuildFaceLightList(const CStateManager& mgr, const CGameArea& area, const zeus::CAABox& aabb); bool BuildAreaLightList(const CStateManager& mgr, const CGameArea& area, const zeus::CAABox& aabb); void BuildDynamicLightList(const CStateManager& mgr, const zeus::CAABox& aabb); + std::vector BuildLightVector() const; void ActivateLights(CBooModel& model) const; void SetCastShadows(bool v) { x298_25_castShadows = v; } void SetFindShadowLight(bool v) { x298_27_findShadowLight = v; } diff --git a/Runtime/Graphics/CMakeLists.txt b/Runtime/Graphics/CMakeLists.txt index 8da2d0b47..d29a791ce 100644 --- a/Runtime/Graphics/CMakeLists.txt +++ b/Runtime/Graphics/CMakeLists.txt @@ -19,7 +19,8 @@ if(WIN32) Shaders/CScanLinesFilterHLSL.cpp Shaders/CRandomStaticFilterHLSL.cpp Shaders/CElementGenShadersHLSL.cpp - Shaders/CParticleSwooshShadersHLSL.cpp) + Shaders/CParticleSwooshShadersHLSL.cpp + Shaders/CFluidPlaneShaderHLSL.cpp) elseif(BOO_HAS_METAL) set(PLAT_SRCS Shaders/CLineRendererShadersMetal.cpp @@ -41,7 +42,8 @@ elseif(BOO_HAS_METAL) Shaders/CScanLinesFilterMetal.cpp Shaders/CRandomStaticFilterMetal.cpp Shaders/CElementGenShadersMetal.cpp - Shaders/CParticleSwooshShadersMetal.cpp) + Shaders/CParticleSwooshShadersMetal.cpp + Shaders/CFluidPlaneShaderMetal.cpp) endif() set(GRAPHICS_SOURCES @@ -85,6 +87,7 @@ set(GRAPHICS_SOURCES Shaders/CRandomStaticFilter.hpp Shaders/CRandomStaticFilter.cpp Shaders/CRandomStaticFilterGLSL.cpp Shaders/CElementGenShaders.hpp Shaders/CElementGenShaders.cpp Shaders/CElementGenShadersGLSL.cpp Shaders/CParticleSwooshShaders.hpp Shaders/CParticleSwooshShaders.cpp Shaders/CParticleSwooshShadersGLSL.cpp + Shaders/CFluidPlaneShader.hpp Shaders/CFluidPlaneShader.cpp Shaders/CFluidPlaneShaderGLSL.cpp ${PLAT_SRCS}) runtime_add_list(Graphics GRAPHICS_SOURCES) diff --git a/Runtime/Graphics/CModelBoo.cpp b/Runtime/Graphics/CModelBoo.cpp index e96a3d8ac..86bab39bd 100644 --- a/Runtime/Graphics/CModelBoo.cpp +++ b/Runtime/Graphics/CModelBoo.cpp @@ -390,50 +390,7 @@ void CBooModel::MakeTexturesFromMats(std::vector>& toksOu void CBooModel::ActivateLights(const std::vector& lights) { - m_lightingData.ambient = zeus::CColor::skBlack; - size_t curLight = 0; - - for (const CLight& light : lights) - { - switch (light.x1c_type) - { - case ELightType::LocalAmbient: - m_lightingData.ambient += light.x18_color; - break; - case ELightType::Point: - case ELightType::Spot: - case ELightType::Custom: - case ELightType::Directional: - { - if (curLight >= URDE_MAX_LIGHTS) - continue; - CModelShaders::Light& lightOut = m_lightingData.lights[curLight++]; - lightOut.pos = CGraphics::g_CameraMatrix * light.x0_pos; - lightOut.dir = CGraphics::g_CameraMatrix.basis * light.xc_dir; - lightOut.dir.normalize(); - lightOut.color = light.x18_color; - lightOut.linAtt[0] = light.x24_distC; - lightOut.linAtt[1] = light.x28_distL; - lightOut.linAtt[2] = light.x2c_distQ; - lightOut.angAtt[0] = light.x30_angleC; - lightOut.angAtt[1] = light.x34_angleL; - lightOut.angAtt[2] = light.x38_angleQ; - - if (light.x1c_type == ELightType::Directional) - lightOut.pos = (-lightOut.dir) * 1048576.f; - break; - } - default: break; - } - } - - for (; curLight(ctx), info); + break; +#if _WIN32 + case boo::IGraphicsDataFactory::Platform::D3D11: + case boo::IGraphicsDataFactory::Platform::D3D12: + slot.second = BuildShader(static_cast(ctx), info); + break; +#endif +#if BOO_HAS_METAL + case boo::IGraphicsDataFactory::Platform::Metal: + slot.second = BuildShader(static_cast(ctx), info); + break; +#endif +#if BOO_HAS_VULKAN + case boo::IGraphicsDataFactory::Platform::Vulkan: + slot.second = BuildShader(static_cast(ctx), info); + break; +#endif + default: break; + } + return true; + }); + + return slot.second; +} + +CFluidPlaneShader::CFluidPlaneShader(CFluidPlane::EFluidType type, + const std::experimental::optional>& patternTex1, + const std::experimental::optional>& patternTex2, + const std::experimental::optional>& colorTex, + const std::experimental::optional>& bumpMap, + const std::experimental::optional>& envMap, + const std::experimental::optional>& envBumpMap, + const std::experimental::optional>& lightmap, + bool doubleLightmapBlend, bool additive) +: m_patternTex1(patternTex1), + m_patternTex2(patternTex2), + m_colorTex(colorTex), + m_bumpMap(bumpMap), + m_envMap(envMap), + m_envBumpMap(envBumpMap), + m_lightmap(lightmap) +{ + SFluidPlaneShaderInfo shaderInfo(type, + m_patternTex1.operator bool(), + m_patternTex2.operator bool(), + m_colorTex.operator bool(), + m_bumpMap.operator bool(), + m_envMap.operator bool(), + m_envBumpMap.operator bool(), + m_lightmap.operator bool(), + doubleLightmapBlend, additive); + boo::IShaderPipeline* pipeline = _cache.GetOrBuildShader(shaderInfo); + + m_gfxTok = CGraphics::CommitResources( + [&](boo::IGraphicsDataFactory::Context& ctx) + { + m_vbo = ctx.newDynamicBuffer(boo::BufferUse::Vertex, sizeof(Vertex), 999); // TODO: Figure out how many + m_uniBuf = ctx.newDynamicBuffer(boo::BufferUse::Uniform, 1024, 1); + + switch (ctx.platform()) + { + case boo::IGraphicsDataFactory::Platform::OpenGL: + m_dataBind = BuildBinding(static_cast(ctx), pipeline); + break; +#if _WIN32 + case boo::IGraphicsDataFactory::Platform::D3D11: + case boo::IGraphicsDataFactory::Platform::D3D12: + m_dataBind = BuildBinding(static_cast(ctx), pipeline); + break; +#endif +#if BOO_HAS_METAL + case boo::IGraphicsDataFactory::Platform::Metal: + m_dataBind = BuildBinding(static_cast(ctx), pipeline); + break; +#endif +#if BOO_HAS_VULKAN + case boo::IGraphicsDataFactory::Platform::Vulkan: + m_dataBind = BuildBinding(static_cast(ctx), pipeline); + break; +#endif + default: break; + } + return true; + }); +} + +void CFluidPlaneShader::draw(const zeus::CMatrix4f texMtxs[6], const zeus::CMatrix4f& normMtx, float indScale, + const std::vector& lights, const zeus::CColor kColors[4]) +{ + Uniform& uni = *reinterpret_cast(m_uniBuf->map(sizeof(Uniform))); + uni.m_mv = CGraphics::g_GXModelView.toMatrix4f(); + uni.m_mvNorm = normMtx; + uni.m_proj = CGraphics::GetPerspectiveProjectionMatrix(true); + for (int i=0 ; i<6 ; ++i) + uni.m_texMtxs[i] = texMtxs[i]; + uni.m_lighting.ActivateLights(lights); + for (int i=0 ; i<3 ; ++i) + uni.m_lighting.colorRegs[i] = kColors[i]; + uni.m_lighting.mulColor = kColors[3]; + uni.m_lighting.fog.m_rangeScale = indScale; + m_uniBuf->unmap(); + CGraphics::SetShaderDataBinding(m_dataBind); + CGraphics::DrawArray(0, 0); +} + +} diff --git a/Runtime/Graphics/Shaders/CFluidPlaneShader.hpp b/Runtime/Graphics/Shaders/CFluidPlaneShader.hpp new file mode 100644 index 000000000..9f5b9fc34 --- /dev/null +++ b/Runtime/Graphics/Shaders/CFluidPlaneShader.hpp @@ -0,0 +1,110 @@ +#ifndef CFLUIDPLANESHADER_HPP +#define CFLUIDPLANESHADER_HPP + +#include "RetroTypes.hpp" +#include "boo/graphicsdev/IGraphicsDataFactory.hpp" +#include "Runtime/World/CFluidPlane.hpp" +#include "CModelShaders.hpp" +#include "boo/graphicsdev/GL.hpp" +#include "boo/graphicsdev/D3D.hpp" +#include "boo/graphicsdev/Metal.hpp" +#include "boo/graphicsdev/Vulkan.hpp" + +namespace urde +{ + +struct SFluidPlaneShaderInfo +{ + CFluidPlane::EFluidType m_type; + bool m_hasPatternTex1; + bool m_hasPatternTex2; + bool m_hasColorTex; + bool m_hasBumpMap; + bool m_hasEnvMap; + bool m_hasEnvBumpMap; + bool m_hasLightmap; + bool m_doubleLightmapBlend; + bool m_additive; + + SFluidPlaneShaderInfo(CFluidPlane::EFluidType type, bool hasPatternTex1, bool hasPatternTex2, bool hasColorTex, + bool hasBumpMap, bool hasEnvMap, bool hasEnvBumpMap, bool hasLightmap, + bool doubleLightmapBlend, bool additive) + : m_type(type), m_hasPatternTex1(hasPatternTex1), m_hasPatternTex2(hasPatternTex2), m_hasColorTex(hasColorTex), + m_hasBumpMap(hasBumpMap), m_hasEnvMap(hasEnvMap), m_hasEnvBumpMap(hasEnvBumpMap), m_hasLightmap(hasLightmap), + m_doubleLightmapBlend(doubleLightmapBlend), m_additive(additive) + {} +}; + +class CFluidPlaneShader +{ + class Cache + { + std::pair m_cache[1024] = {}; + static u16 MakeCacheKey(const SFluidPlaneShaderInfo& info); + public: + boo::IShaderPipeline* GetOrBuildShader(const SFluidPlaneShaderInfo& info); + }; + static Cache _cache; + + struct Vertex + { + zeus::CVector3f m_pos; + zeus::CVector3f m_norm; + zeus::CVector3f m_binorm; + zeus::CVector3f m_tangent; + zeus::CColor m_color; + }; + + struct Uniform + { + zeus::CMatrix4f m_mv; + zeus::CMatrix4f m_mvNorm; + zeus::CMatrix4f m_proj; + zeus::CMatrix4f m_texMtxs[9]; // Pad out to 768 bytes + CModelShaders::LightingUniform m_lighting; + }; + + std::experimental::optional> m_patternTex1; + std::experimental::optional> m_patternTex2; + std::experimental::optional> m_colorTex; + std::experimental::optional> m_bumpMap; + std::experimental::optional> m_envMap; + std::experimental::optional> m_envBumpMap; + std::experimental::optional> m_lightmap; + boo::GraphicsDataToken m_gfxTok; + boo::IGraphicsBufferD* m_vbo; + boo::IGraphicsBufferD* m_uniBuf; + boo::IShaderDataBinding* m_dataBind; + + static boo::IShaderPipeline* BuildShader(boo::GLDataFactory::Context& ctx, const SFluidPlaneShaderInfo& info); + boo::IShaderDataBinding* BuildBinding(boo::GLDataFactory::Context& ctx, boo::IShaderPipeline* pipeline); +#if _WIN32 + static boo::IShaderPipeline* BuildShader(boo::ID3DDataFactory::Context& ctx, const SFluidPlaneShaderInfo& info); + boo::IShaderDataBinding* BuildBinding(boo::ID3DDataFactory::Context& ctx, boo::IShaderPipeline* pipeline); +#endif +#if BOO_HAS_METAL + static boo::IShaderPipeline* BuildShader(boo::MetalDataFactory::Context& ctx, const SFluidPlaneShaderInfo& info); + boo::IShaderDataBinding* BuildBinding(boo::MetalDataFactory::Context& ctx, boo::IShaderPipeline* pipeline); +#endif +#if BOO_HAS_VULKAN + static boo::IShaderPipeline* BuildShader(boo::VulkanDataFactory::Context& ctx, const SFluidPlaneShaderInfo& info); + boo::IShaderDataBinding* BuildBinding(boo::VulkanDataFactory::Context& ctx, boo::IShaderPipeline* pipeline); +#endif + +public: + CFluidPlaneShader(CFluidPlane::EFluidType type, + const std::experimental::optional>& patternTex1, + const std::experimental::optional>& patternTex2, + const std::experimental::optional>& colorTex, + const std::experimental::optional>& bumpMap, + const std::experimental::optional>& envMap, + const std::experimental::optional>& envBumpMap, + const std::experimental::optional>& lightmap, + bool doubleLightmapBlend, bool additive); + void draw(const zeus::CMatrix4f texMtxs[6], const zeus::CMatrix4f& normMtx, float indScale, + const std::vector& lights, const zeus::CColor kColors[4]); +}; + +} + +#endif // CFLUIDPLANESHADER_HPP diff --git a/Runtime/Graphics/Shaders/CFluidPlaneShaderGLSL.cpp b/Runtime/Graphics/Shaders/CFluidPlaneShaderGLSL.cpp new file mode 100644 index 000000000..575bb6e39 --- /dev/null +++ b/Runtime/Graphics/Shaders/CFluidPlaneShaderGLSL.cpp @@ -0,0 +1,559 @@ +#include "CFluidPlaneShader.hpp" + +namespace urde +{ + +static const char* VS = +"#version 330\n" +BOO_GLSL_BINDING_HEAD +"layout(location=0) in vec4 posIn;\n" +"layout(location=1) in vec4 normalIn;\n" +"layout(location=2) in vec4 binormalIn;\n" +"layout(location=3) in vec4 tangentIn;\n" +"layout(location=4) in vec4 colorIn;\n" +"\n" +"UBINDING0 uniform FluidPlaneUniform\n" +"{\n" +" mat4 mv;\n" +" mat4 mvNorm;\n" +" mat4 proj;\n" +" mat4 texMtxs[6];\n" +"};\n" +"\n" +"struct VertToFrag\n" +"{\n" +" vec4 mvPos;\n" +" vec4 mvNorm;\n" +" vec4 mvBinorm;\n" +" vec4 mvTangent;\n" +" vec4 color;\n" +" vec2 uvs[7];\n" +"};\n" +"\n" +"SBINDING(0) out VertToFrag vtf;\n" +"void main()\n" +"{\n" +" vtf.mvPos = mv * vec4(posIn.xyz, 1.0);\n" +" gl_Position = proj * vtf.mvPos;\n" +" vtf.mvNorm = mvNorm * normalIn;\n" +" vtf.mvBinorm = mvNorm * binormalIn;\n" +" vtf.mvTangent = mvNorm * tangentIn;\n" +" vtf.color = colorIn;\n" +" vtf.uvs[0] = (texMtxs[0] * posIn).xy;\n" +" vtf.uvs[1] = (texMtxs[1] * posIn).xy;\n" +" vtf.uvs[2] = (texMtxs[2] * posIn).xy;\n" +"%s" // Additional TCGs here +"}\n"; + +static const char* FS = +"#version 330\n" +BOO_GLSL_BINDING_HEAD +"\n" +"struct Light\n" +"{\n" +" vec4 pos;\n" +" vec4 dir;\n" +" vec4 color;\n" +" vec4 linAtt;\n" +" vec4 angAtt;\n" +"};\n" +"struct Fog\n" // Reappropriated for indirect texture scaling +"{\n" +" vec4 color;\n" +" float indScale;\n" +" float start;\n" +"};\n" +"\n" +"UBINDING2 uniform LightingUniform\n" +"{\n" +" Light lights[" _XSTR(URDE_MAX_LIGHTS) "];\n" +" vec4 ambient;\n" +" vec4 kColor0;\n" +" vec4 kColor1;\n" +" vec4 kColor2;\n" +" vec4 kColor3;\n" +" Fog fog;\n" +"};\n" +"\n" +"vec4 LightingFunc(vec4 mvPosIn, vec4 mvNormIn)\n" +"{\n" +" vec4 ret = ambient;\n" +" \n" +" for (int i=0 ; i<" _XSTR(URDE_MAX_LIGHTS) " ; ++i)\n" +" {\n" +" vec3 delta = mvPosIn.xyz - lights[i].pos.xyz;\n" +" float dist = length(delta);\n" +" float angDot = clamp(dot(normalize(delta), lights[i].dir.xyz), 0.0, 1.0);\n" +" float att = 1.0 / (lights[i].linAtt[2] * dist * dist +\n" +" lights[i].linAtt[1] * dist +\n" +" lights[i].linAtt[0]);\n" +" float angAtt = lights[i].angAtt[2] * angDot * angDot +\n" +" lights[i].angAtt[1] * angDot +\n" +" lights[i].angAtt[0];\n" +" ret += lights[i].color * clamp(angAtt, 0.0, 1.0) * att * clamp(dot(normalize(-delta), mvNormIn.xyz), 0.0, 1.0);\n" +" }\n" +" \n" +" return clamp(ret, vec4(0.0,0.0,0.0,0.0), vec4(1.0,1.0,1.0,1.0));\n" +"}\n" +"\n" +"struct VertToFrag\n" +"{\n" +" vec4 mvPos;\n" +" vec4 mvNorm;\n" +" vec4 mvBinorm;\n" +" vec4 mvTangent;\n" +" vec4 color;\n" +" vec2 uvs[7];\n" +"};\n" +"\n" +"SBINDING(0) in VertToFrag vtf;\n" +"layout(location=0) out vec4 colorOut;\n" +"%s" // Textures here +"void main()\n" +"{\n" +" vec4 lighting = LightingFunc(vtf.mvPos, normalize(vtf.mvNorm));\n" +"%s" // Combiner expression here +"}\n"; + +static void _BuildShader(std::string& finalVS, std::string& finalFS, int& nextTex, const char* texNames[7], + const SFluidPlaneShaderInfo& info) +{ + std::string additionalTCGs; + std::string textures; + std::string combiner; + int nextTCG = 3; + int nextMtx = 4; + int bumpMapUv, envBumpMapUv, envMapUv, lightmapUv; + + if (info.m_hasPatternTex1) + { + texNames[nextTex] = "patternTex1"; + textures += hecl::Format("TBINDING%d uniform sampler2D patternTex1;\n", nextTex++); + } + if (info.m_hasPatternTex2) + { + texNames[nextTex] = "patternTex2"; + textures += hecl::Format("TBINDING%d uniform sampler2D patternTex2;\n", nextTex++); + } + if (info.m_hasColorTex) + { + texNames[nextTex] = "colorTex"; + textures += hecl::Format("TBINDING%d uniform sampler2D colorTex;\n", nextTex++); + } + if (info.m_hasBumpMap) + { + texNames[nextTex] = "bumpMap"; + textures += hecl::Format("TBINDING%d uniform sampler2D bumpMap;\n", nextTex++); + } + if (info.m_hasEnvMap) + { + texNames[nextTex] = "envMap"; + textures += hecl::Format("TBINDING%d uniform sampler2D envMap;\n", nextTex++); + } + if (info.m_hasEnvBumpMap) + { + texNames[nextTex] = "envBumpMap"; + textures += hecl::Format("TBINDING%d uniform sampler2D envBumpMap;\n", nextTex++); + } + if (info.m_hasLightmap) + { + texNames[nextTex] = "lightMap"; + textures += hecl::Format("TBINDING%d uniform sampler2D lightMap;\n", nextTex++); + } + + if (info.m_hasBumpMap) + { + bumpMapUv = nextTCG; + additionalTCGs += hecl::Format(" vtf.uvs[%d] = (texMtxs[0] * posIn).xy;\n", nextTCG++); + } + if (info.m_hasEnvBumpMap) + { + envBumpMapUv = nextTCG; + additionalTCGs += hecl::Format(" vtf.uvs[%d] = (texMtxs[3] * posIn).xy;\n", nextTCG++); + } + if (info.m_hasEnvMap) + { + envMapUv = nextTCG; + additionalTCGs += hecl::Format(" vtf.uvs[%d] = (texMtxs[%d] * posIn).xy;\n", nextTCG++, nextMtx++); + } + if (info.m_hasLightmap) + { + lightmapUv = nextTCG; + additionalTCGs += hecl::Format(" vtf.uvs[%d] = (texMtxs[%d] * posIn).xy;\n", nextTCG++, nextMtx++); + } + + switch (info.m_type) + { + case CFluidPlane::EFluidType::NormalWater: + case CFluidPlane::EFluidType::Three: + case CFluidPlane::EFluidType::Four: + if (info.m_hasLightmap) + { + combiner += hecl::Format(" vec4 lightMapTexel = texture(lightMap, vtf.uvs[%d]);\n", lightmapUv); + // 0: Tex4TCG, Tex4, doubleLightmapBlend ? NULL : GX_COLOR1A1 + // ZERO, TEX, KONST, doubleLightmapBlend ? ZERO : RAS + // Output reg 2 + // KColor 2 + if (info.m_doubleLightmapBlend) + { + // 1: Tex4TCG2, Tex4, GX_COLOR1A1 + // C2, TEX, KONST, RAS + // Output reg 2 + // KColor 3 + // Tex * K2 + Lighting + combiner += " lighting += mix(lightMapTexel * kColor2, lightMapTexel, kColor3);\n"; + } + else + { + // mix(Tex * K2, Tex, K3) + Lighting + combiner += " lighting += lightMapTexel * kColor2;\n"; + } + } + + // Next: Tex0TCG, Tex0, GX_COLOR1A1 + // ZERO, TEX, KONST, RAS + // Output reg prev + // KColor 0 + + // Next: Tex1TCG, Tex1, GX_COLOR0A0 + // ZERO, TEX, PREV, RAS + // Output reg prev + + // Next: Tex2TCG, Tex2, GX_COLOR1A1 + // ZERO, TEX, hasTex4 ? C2 : RAS, PREV + // Output reg prev + + // (Tex0 * kColor0 + Lighting) * Tex1 + VertColor + Tex2 * Lighting + if (info.m_hasPatternTex2) + { + if (info.m_hasPatternTex1) + combiner += " colorOut = (texture(patternTex1, vtf.uvs[0]) * kColor0 + lighting) *\n" + " texture(patternTex2, vtf.uvs[1]) + vtf.color;\n"; + else + combiner += " colorOut = lighting * texture(patternTex2, vtf.uvs[1]) + vtf.color;\n"; + } + else + { + combiner += " colorOut = vtf.color;\n"; + } + + + if (info.m_hasColorTex && !info.m_hasEnvMap && info.m_hasEnvBumpMap) + { + // Make previous stage indirect, mtx0 + combiner += hecl::Format(" vec2 indUvs = (texture(envBumpMap, vtf.uvs[%d]).ra - vec2(0.5, 0.5)) *\n" + " vec2(fog.indScale, -fog.indScale);", envBumpMapUv); + combiner += " colorOut += texture(colorTex, indUvs + vtf.uvs[2]) * lighting;\n"; + } + else if (info.m_hasEnvMap) + { + // Next: envTCG, envTex, NULL + // PREV, TEX, KONST, ZERO + // Output reg prev + // KColor 1 + + // Make previous stage indirect, mtx0 + if (info.m_hasColorTex) + combiner += " colorOut += texture(colorTex, vtf.uvs[2]) * lighting;\n"; + combiner += hecl::Format(" vec2 indUvs = (texture(envBumpMap, vtf.uvs[%d]).ra - vec2(0.5, 0.5)) *\n" + " vec2(fog.indScale, -fog.indScale);", envBumpMapUv); + combiner += hecl::Format(" colorOut = mix(colorOut, texture(envMap, indUvs + vtf.uvs[%d]), kColor1);\n", + envMapUv); + } + else if (info.m_hasColorTex) + { + combiner += " colorOut += texture(colorTex, vtf.uvs[2]) * lighting;\n"; + } + + break; + + case CFluidPlane::EFluidType::PoisonWater: + if (info.m_hasLightmap) + { + combiner += hecl::Format(" vec4 lightMapTexel = texture(lightMap, vtf.uvs[%d]);\n", lightmapUv); + // 0: Tex4TCG, Tex4, doubleLightmapBlend ? NULL : GX_COLOR1A1 + // ZERO, TEX, KONST, doubleLightmapBlend ? ZERO : RAS + // Output reg 2 + // KColor 2 + if (info.m_doubleLightmapBlend) + { + // 1: Tex4TCG2, Tex4, GX_COLOR1A1 + // C2, TEX, KONST, RAS + // Output reg 2 + // KColor 3 + // Tex * K2 + Lighting + combiner += " lighting += mix(lightMapTexel * kColor2, lightMapTexel, kColor3);\n"; + } + else + { + // mix(Tex * K2, Tex, K3) + Lighting + combiner += " lighting += lightMapTexel * kColor2;\n"; + } + } + + // Next: Tex0TCG, Tex0, GX_COLOR1A1 + // ZERO, TEX, KONST, RAS + // Output reg prev + // KColor 0 + + // Next: Tex1TCG, Tex1, GX_COLOR0A0 + // ZERO, TEX, PREV, RAS + // Output reg prev + + // Next: Tex2TCG, Tex2, GX_COLOR1A1 + // ZERO, TEX, hasTex4 ? C2 : RAS, PREV + // Output reg prev + + // (Tex0 * kColor0 + Lighting) * Tex1 + VertColor + Tex2 * Lighting + if (info.m_hasPatternTex2) + { + if (info.m_hasPatternTex1) + combiner += " colorOut = (texture(patternTex1, vtf.uvs[0]) * kColor0 + lighting) *\n" + " texture(patternTex2, vtf.uvs[1]) + vtf.color;\n"; + else + combiner += " colorOut = lighting * texture(patternTex2, vtf.uvs[1]) + vtf.color;\n"; + } + else + { + combiner += " colorOut = vtf.color;\n"; + } + + if (info.m_hasColorTex) + { + if (info.m_hasEnvBumpMap) + { + // Make previous stage indirect, mtx0 + combiner += hecl::Format(" vec2 indUvs = (texture(envBumpMap, vtf.uvs[%d]).ra - vec2(0.5, 0.5)) *\n" + " vec2(fog.indScale, -fog.indScale);", envBumpMapUv); + combiner += " colorOut += texture(colorTex, indUvs + vtf.uvs[2]) * lighting;\n"; + } + else + { + combiner += " colorOut += texture(colorTex, vtf.uvs[2]) * lighting;\n"; + } + } + + break; + + case CFluidPlane::EFluidType::Lava: + // 0: Tex0TCG, Tex0, GX_COLOR0A0 + // ZERO, TEX, KONST, RAS + // Output reg prev + // KColor 0 + + // 1: Tex1TCG, Tex1, GX_COLOR0A0 + // ZERO, TEX, PREV, RAS + // Output reg prev + + // 2: Tex2TCG, Tex2, NULL + // ZERO, TEX, ONE, PREV + // Output reg prev + + // (Tex0 * kColor0 + VertColor) * Tex1 + VertColor + Tex2 + if (info.m_hasPatternTex2) + { + if (info.m_hasPatternTex1) + combiner += " colorOut = (texture(patternTex1, vtf.uvs[0]) * kColor0 + vtf.color) *\n" + " texture(patternTex2, vtf.uvs[1]) + vtf.color;\n"; + else + combiner += " colorOut = vtf.color * texture(patternTex2, vtf.uvs[1]) + vtf.color;\n"; + } + else + { + combiner += " colorOut = vtf.color;\n"; + } + + if (info.m_hasColorTex) + combiner += " colorOut += texture(colorTex, vtf.uvs[2]);\n"; + + if (info.m_hasBumpMap) + { + // 3: bumpMapTCG, bumpMap, NULL + // ZERO, TEX, ONE, HALF + // Output reg 0, no clamp, no bias + + // 4: bumpMapTCG2, bumpMap, NULL + // ZERO, TEX, ONE, C0 + // Output reg 0, subtract, clamp, no bias + + combiner += " vec3 lightVec = lights[3].pos.xyz - vtf.mvPos.xyz;\n" + " float lx = dot(vtf.mvTangent, lightVec);\n" + " float ly = dot(vtf.mvBinorm, lightVec);\n"; + combiner += hecl::Format(" vec4 emboss1 = texture(bumpMap, vtf.uvs[%d]) + vec4(0.5);\n" + " vec4 emboss2 = texture(bumpMap, vtf.uvs[%d] + vec2(lx, ly));\n", + bumpMapUv, bumpMapUv); + + // 5: NULL, NULL, NULL + // ZERO, PREV, C0, ZERO + // Output reg prev, scale 2, clamp + + // colorOut * clamp(emboss1 + 0.5 - emboss2, 0.0, 1.0) * 2.0 + combiner += "colorOut *= clamp((emboss1 + vec4(0.5) - emboss2) * vec4(2.0), vec4(0.0), vec4(1.0));\n"; + } + + break; + + case CFluidPlane::EFluidType::Five: + // 0: Tex0TCG, Tex0, GX_COLOR0A0 + // ZERO, TEX, KONST, RAS + // Output reg prev + // KColor 0 + + // 1: Tex1TCG, Tex1, GX_COLOR0A0 + // ZERO, TEX, PREV, RAS + // Output reg prev + + // 2: Tex2TCG, Tex2, NULL + // ZERO, TEX, ONE, PREV + // Output reg prev + + // (Tex0 * kColor0 + VertColor) * Tex1 + VertColor + Tex2 + if (info.m_hasPatternTex2) + { + if (info.m_hasPatternTex1) + combiner += " colorOut = (texture(patternTex1, vtf.uvs[0]) * kColor0 + vtf.color) *\n" + " texture(patternTex2, vtf.uvs[1]) + vtf.color;\n"; + else + combiner += " colorOut = vtf.color * texture(patternTex2, vtf.uvs[1]) + vtf.color;\n"; + } + else + { + combiner += " colorOut = vtf.color;\n"; + } + + if (info.m_hasColorTex) + combiner += " colorOut += texture(colorTex, vtf.uvs[2]);\n"; + + if (info.m_hasBumpMap) + { + // 3: bumpMapTCG, bumpMap, NULL + // ZERO, TEX, PREV, ZERO + // Output reg prev, scale 2 + combiner += hecl::Format(" vec4 emboss1 = texture(bumpMap, vtf.uvs[%d]) + vec4(0.5);\n", bumpMapUv); + combiner += "colorOut *= emboss1 * vec4(2.0);\n"; + } + + break; + } + + combiner += " colorOut.a = kColor0.a;\n"; + + finalVS = hecl::Format(VS, additionalTCGs.c_str()); + finalFS = hecl::Format(FS, textures.c_str(), combiner.c_str()); +} + +boo::IShaderPipeline* +CFluidPlaneShader::BuildShader(boo::GLDataFactory::Context& ctx, const SFluidPlaneShaderInfo& info) +{ + int nextTex = 0; + const char* texNames[7] = {}; + std::string finalVS, finalFS; + _BuildShader(finalVS, finalFS, nextTex, texNames, info); + const char* uniNames[] = {"FluidPlaneUniform", "FluidPlaneUniform", "LightingUniform"}; + return ctx.newShaderPipeline(finalVS.c_str(), finalFS.c_str(), size_t(nextTex), texNames, 3, uniNames, + info.m_additive ? boo::BlendFactor::One : boo::BlendFactor::SrcAlpha, + info.m_additive ? boo::BlendFactor::One : boo::BlendFactor::InvSrcAlpha, + boo::Primitive::TriStrips, boo::ZTest::LEqual, false, true, false, + boo::CullMode::None); +} + +#if BOO_HAS_VULKAN +static boo::IVertexFormat* s_vtxFmt = nullptr; + +boo::IShaderPipeline* +CFluidPlaneShader::BuildShader(boo::VulkanDataFactory::Context& ctx, const SFluidPlaneShaderInfo& info) +{ + if (s_vtxFmt == nullptr) + { + boo::VertexElementDescriptor elements[] = + { + {nullptr, nullptr, boo::VertexSemantic::Position4}, + {nullptr, nullptr, boo::VertexSemantic::Normal4, 0}, + {nullptr, nullptr, boo::VertexSemantic::Normal4, 1}, + {nullptr, nullptr, boo::VertexSemantic::Normal4, 2}, + {nullptr, nullptr, boo::VertexSemantic::Color} + }; + s_vtxFmt = ctx.newVertexFormat(5, elements); + } + + int nextTex = 0; + const char* texNames[7] = {}; + std::string finalVS, finalFS; + _BuildShader(finalVS, finalFS, nextTex, texNames, info); + const char* uniNames[] = {"FluidPlaneUniform", "FluidPlaneUniform", "LightingUniform"}; + return ctx.newShaderPipeline(finalVS.c_str(), finalFS.c_str(), size_t(nextTex), texNames, 3, uniNames, + info.m_additive ? boo::BlendFactor::One : boo::BlendFactor::SrcAlpha, + info.m_additive ? boo::BlendFactor::One : boo::BlendFactor::InvSrcAlpha, + boo::Primitive::TriStrips, boo::ZTest::LEqual, false, true, false, + boo::CullMode::None); +} +#endif + +boo::IShaderDataBinding* CFluidPlaneShader::BuildBinding(boo::GLDataFactory::Context& ctx, + boo::IShaderPipeline* pipeline) +{ + boo::VertexElementDescriptor elements[] = + { + {m_vbo, nullptr, boo::VertexSemantic::Position4}, + {m_vbo, nullptr, boo::VertexSemantic::Normal4, 0}, + {m_vbo, nullptr, boo::VertexSemantic::Normal4, 1}, + {m_vbo, nullptr, boo::VertexSemantic::Normal4, 2}, + {m_vbo, nullptr, boo::VertexSemantic::Color} + }; + boo::IVertexFormat* vtxFmt = ctx.newVertexFormat(5, elements); + boo::IGraphicsBuffer* ubufs[] = { m_uniBuf, m_uniBuf, m_uniBuf }; + boo::PipelineStage ubufStages[] = { boo::PipelineStage::Vertex, boo::PipelineStage::Vertex, + boo::PipelineStage::Fragment }; + size_t ubufOffs[] = {0, 0, 768}; + size_t ubufSizes[] = {768, 768, 256}; + size_t texCount = 0; + boo::ITexture* texs[7] = {}; + 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(); + return ctx.newShaderDataBinding(pipeline, vtxFmt, m_vbo, nullptr, nullptr, 1, ubufs, ubufStages, ubufOffs, + ubufSizes, texCount, texs, nullptr, nullptr); +} + +#if BOO_HAS_VULKAN +boo::IShaderDataBinding* CFluidPlaneShader::BuildBinding(boo::VulkanDataFactory::Context& ctx, + boo::IShaderPipeline* pipeline) +{ + boo::IGraphicsBuffer* ubufs[] = { m_uniBuf, m_uniBuf, m_uniBuf }; + boo::PipelineStage ubufStages[] = { boo::PipelineStage::Vertex, boo::PipelineStage::Vertex, + boo::PipelineStage::Fragment }; + size_t ubufOffs[] = {0, 0, 768}; + size_t ubufSizes[] = {768, 768, 256}; + size_t texCount = 0; + boo::ITexture* texs[7] = {}; + 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(); + return ctx.newShaderDataBinding(pipeline, s_vtxFmt, m_vbo, nullptr, nullptr, 1, ubufs, ubufStages, ubufOffs, + ubufSizes, texCount, texs, nullptr, nullptr); +} +#endif + +} \ No newline at end of file diff --git a/Runtime/Graphics/Shaders/CFluidPlaneShaderHLSL.cpp b/Runtime/Graphics/Shaders/CFluidPlaneShaderHLSL.cpp new file mode 100644 index 000000000..df35c523d --- /dev/null +++ b/Runtime/Graphics/Shaders/CFluidPlaneShaderHLSL.cpp @@ -0,0 +1,4 @@ +// +// Created by Jack Andersen on 8/6/17. +// + diff --git a/Runtime/Graphics/Shaders/CFluidPlaneShaderMetal.cpp b/Runtime/Graphics/Shaders/CFluidPlaneShaderMetal.cpp new file mode 100644 index 000000000..e4610184f --- /dev/null +++ b/Runtime/Graphics/Shaders/CFluidPlaneShaderMetal.cpp @@ -0,0 +1,480 @@ +#include "CFluidPlaneShader.hpp" + +namespace urde +{ + +static boo::IVertexFormat* s_vtxFmt = nullptr; + +static const char* VS = +"#include \n" +"using namespace metal;\n" +"struct VertData\n" +"{\n" +" float4 posIn [[ attribute(0) ]];\n" +" float4 normalIn [[ attribute(1) ]];\n" +" float4 binormalIn [[ attribute(2) ]];\n" +" float4 tangentIn [[ attribute(3) ]];\n" +" float4 colorIn [[ attribute(4) ]];\n" +"};\n" +"\n" +"UBINDING0 uniform FluidPlaneUniform\n" +"{\n" +" float4x4 mv;\n" +" float4x4 mvNorm;\n" +" float4x4 proj;\n" +" float4x4 texMtxs[6];\n" +"};\n" +"\n" +"struct VertToFrag\n" +"{\n" +" float4 mvPos;\n" +" float4 mvNorm;\n" +" float4 mvBinorm;\n" +" float4 mvTangent;\n" +" float4 color;\n" +" float2 uvs[7];\n" +"};\n" +"\n" +"vertex VertToFrag vmain(VertData v [[ stage_in ]],\n" +" constant FluidPlaneUniform& fu [[ buffer(2) ]])\n" +"{\n" +" VertToFrag vtf;\n" +" vtf.mvPos = fu.mv * float4(v.posIn.xyz, 1.0);\n" +" gl_Position = fu.proj * vtf.mvPos;\n" +" vtf.mvNorm = fu.mvNorm * v.normalIn;\n" +" vtf.mvBinorm = fu.mvNorm * v.binormalIn;\n" +" vtf.mvTangent = fu.mvNorm * v.tangentIn;\n" +" vtf.color = v.colorIn;\n" +" vtf.uvs[0] = (fu.texMtxs[0] * v.posIn).xy;\n" +" vtf.uvs[1] = (fu.texMtxs[1] * v.posIn).xy;\n" +" vtf.uvs[2] = (fu.texMtxs[2] * v.posIn).xy;\n" +"%s" // Additional TCGs here +" return vtf;\n" +"}\n"; + +static const char* FS = +"#include \n" +"using namespace metal;\n" +"constexpr sampler samp(address::repeat, filter::linear);\n" +"\n" +"struct Light\n" +"{\n" +" float4 pos;\n" +" float4 dir;\n" +" float4 color;\n" +" float4 linAtt;\n" +" float4 angAtt;\n" +"};\n" +"struct Fog\n" // Reappropriated for indirect texture scaling +"{\n" +" float4 color;\n" +" float indScale;\n" +" float start;\n" +"};\n" +"\n" +"struct LightingUniform\n" +"{\n" +" Light lights[" _XSTR(URDE_MAX_LIGHTS) "];\n" +" float4 ambient;\n" +" float4 kColor0;\n" +" float4 kColor1;\n" +" float4 kColor2;\n" +" float4 kColor3;\n" +" Fog fog;\n" +"};\n" +"\n" +"static float4 LightingFunc(float4 mvPosIn, float4 mvNormIn)\n" +"{\n" +" float4 ret = ambient;\n" +" \n" +" for (int i=0 ; i<" _XSTR(URDE_MAX_LIGHTS) " ; ++i)\n" +" {\n" +" float3 delta = mvPosIn.xyz - lights[i].pos.xyz;\n" +" float dist = length(delta);\n" +" float angDot = clamp(dot(normalize(delta), lights[i].dir.xyz), 0.0, 1.0);\n" +" float att = 1.0 / (lights[i].linAtt[2] * dist * dist +\n" +" lights[i].linAtt[1] * dist +\n" +" lights[i].linAtt[0]);\n" +" float angAtt = lights[i].angAtt[2] * angDot * angDot +\n" +" lights[i].angAtt[1] * angDot +\n" +" lights[i].angAtt[0];\n" +" ret += lights[i].color * clamp(angAtt, 0.0, 1.0) * att * clamp(dot(normalize(-delta), mvNormIn.xyz), 0.0, 1.0);\n" +" }\n" +" \n" +" return clamp(ret, float4(0.0,0.0,0.0,0.0), float4(1.0,1.0,1.0,1.0));\n" +"}\n" +"\n" +"struct VertToFrag\n" +"{\n" +" float4 mvPos;\n" +" float4 mvNorm;\n" +" float4 mvBinorm;\n" +" float4 mvTangent;\n" +" float4 color;\n" +" float2 uvs[7];\n" +"};\n" +"\n" +"fragment float4 fmain(VertToFrag vtf [[ stage_in ]],\n" +" constant LightingUniform& lu [[ buffer(4) ]]%s)\n" // Textures here +"{\n" +" float4 lighting = LightingFunc(vtf.mvPos, normalize(vtf.mvNorm));\n" +" float4 colorOut;\n" +"%s" // Combiner expression here +" return colorOut;\n" +"}\n"; + +boo::IShaderPipeline* +CFluidPlaneShader::BuildShader(boo::MetalDataFactory::Context& ctx, const SFluidPlaneShaderInfo& info) +{ + if (s_vtxFmt == nullptr) + { + boo::VertexElementDescriptor elements[] = + { + {nullptr, nullptr, boo::VertexSemantic::Position4}, + {nullptr, nullptr, boo::VertexSemantic::Normal4, 0}, + {nullptr, nullptr, boo::VertexSemantic::Normal4, 1}, + {nullptr, nullptr, boo::VertexSemantic::Normal4, 2}, + {nullptr, nullptr, boo::VertexSemantic::Color} + }; + s_vtxFmt = ctx.newVertexFormat(5, elements); + } + + std::string additionalTCGs; + std::string textures; + std::string combiner; + int nextTex = 0; + int nextTCG = 3; + int nextMtx = 4; + int bumpMapUv, envBumpMapUv, envMapUv, lightmapUv; + + if (info.m_hasPatternTex1) + textures += hecl::Format(",\ntexture2d patternTex1 [[ texture(%d) ]]", nextTex++); + if (info.m_hasPatternTex2) + textures += hecl::Format(",\ntexture2d patternTex2 [[ texture(%d) ]]", nextTex++); + if (info.m_hasColorTex) + textures += hecl::Format(",\ntexture2d colorTex [[ texture(%d) ]]", nextTex++); + if (info.m_hasBumpMap) + textures += hecl::Format(",\ntexture2d bumpMap [[ texture(%d) ]]", nextTex++); + if (info.m_hasEnvMap) + textures += hecl::Format(",\ntexture2d envMap [[ texture(%d) ]]", nextTex++); + if (info.m_hasEnvBumpMap) + textures += hecl::Format(",\ntexture2d envBumpMap [[ texture(%d) ]]", nextTex++); + if (info.m_hasLightmap) + textures += hecl::Format(",\ntexture2d lightMap [[ texture(%d) ]]", nextTex++); + + if (info.m_hasBumpMap) + { + bumpMapUv = nextTCG; + additionalTCGs += hecl::Format(" vtf.uvs[%d] = (fu.texMtxs[0] * v.posIn).xy;\n", nextTCG++); + } + if (info.m_hasEnvBumpMap) + { + envBumpMapUv = nextTCG; + additionalTCGs += hecl::Format(" vtf.uvs[%d] = (fu.texMtxs[3] * v.posIn).xy;\n", nextTCG++); + } + if (info.m_hasEnvMap) + { + envMapUv = nextTCG; + additionalTCGs += hecl::Format(" vtf.uvs[%d] = (fu.texMtxs[%d] * v.posIn).xy;\n", nextTCG++, nextMtx++); + } + if (info.m_hasLightmap) + { + lightmapUv = nextTCG; + additionalTCGs += hecl::Format(" vtf.uvs[%d] = (fu.texMtxs[%d] * v.posIn).xy;\n", nextTCG++, nextMtx++); + } + + switch (info.m_type) + { + case CFluidPlane::EFluidType::NormalWater: + case CFluidPlane::EFluidType::Three: + case CFluidPlane::EFluidType::Four: + if (info.m_hasLightmap) + { + combiner += hecl::Format(" float4 lightMapTexel = lightMap.sample(samp, vtf.uvs[%d]);\n", lightmapUv); + // 0: Tex4TCG, Tex4, doubleLightmapBlend ? NULL : GX_COLOR1A1 + // ZERO, TEX, KONST, doubleLightmapBlend ? ZERO : RAS + // Output reg 2 + // KColor 2 + if (info.m_doubleLightmapBlend) + { + // 1: Tex4TCG2, Tex4, GX_COLOR1A1 + // C2, TEX, KONST, RAS + // Output reg 2 + // KColor 3 + // Tex * K2 + Lighting + combiner += " lighting += mix(lightMapTexel * lu.kColor2, lightMapTexel, lu.kColor3);\n"; + } + else + { + // mix(Tex * K2, Tex, K3) + Lighting + combiner += " lighting += lightMapTexel * lu.kColor2;\n"; + } + } + + // Next: Tex0TCG, Tex0, GX_COLOR1A1 + // ZERO, TEX, KONST, RAS + // Output reg prev + // KColor 0 + + // Next: Tex1TCG, Tex1, GX_COLOR0A0 + // ZERO, TEX, PREV, RAS + // Output reg prev + + // Next: Tex2TCG, Tex2, GX_COLOR1A1 + // ZERO, TEX, hasTex4 ? C2 : RAS, PREV + // Output reg prev + + // (Tex0 * kColor0 + Lighting) * Tex1 + VertColor + Tex2 * Lighting + if (info.m_hasPatternTex2) + { + if (info.m_hasPatternTex1) + combiner += " colorOut = (patternTex1.sample(samp, vtf.uvs[0]) * lu.kColor0 + lighting) *\n" + " patternTex2.sample(samp, vtf.uvs[1]) + vtf.color;\n"; + else + combiner += " colorOut = lighting * patternTex2.sample(samp, vtf.uvs[1]) + vtf.color;\n"; + } + else + { + combiner += " colorOut = vtf.color;\n"; + } + + + if (info.m_hasColorTex && !info.m_hasEnvMap && info.m_hasEnvBumpMap) + { + // Make previous stage indirect, mtx0 + combiner += hecl::Format(" float2 indUvs = (envBumpMap.sample(samp, vtf.uvs[%d]).ra - float2(0.5, 0.5)) *\n" + " float2(lu.fog.indScale, -lu.fog.indScale);", envBumpMapUv); + combiner += " colorOut += colorTex.sample(samp, indUvs + vtf.uvs[2]) * lighting;\n"; + } + else if (info.m_hasEnvMap) + { + // Next: envTCG, envTex, NULL + // PREV, TEX, KONST, ZERO + // Output reg prev + // KColor 1 + + // Make previous stage indirect, mtx0 + if (info.m_hasColorTex) + combiner += " colorOut += colorTex.sample(samp, vtf.uvs[2]) * lighting;\n"; + combiner += hecl::Format(" float2 indUvs = (envBumpMap.sample(samp, vtf.uvs[%d]).ra - float2(0.5, 0.5)) *\n" + " float2(lu.fog.indScale, -lu.fog.indScale);", envBumpMapUv); + combiner += hecl::Format(" colorOut = mix(colorOut, envMap.sample(samp, indUvs + vtf.uvs[%d]), lu.kColor1);\n", + envMapUv); + } + else if (info.m_hasColorTex) + { + combiner += " colorOut += colorTex.sample(samp, vtf.uvs[2]) * lighting;\n"; + } + + break; + + case CFluidPlane::EFluidType::PoisonWater: + if (info.m_hasLightmap) + { + combiner += hecl::Format(" float4 lightMapTexel = lightMap.sample(samp, vtf.uvs[%d]);\n", lightmapUv); + // 0: Tex4TCG, Tex4, doubleLightmapBlend ? NULL : GX_COLOR1A1 + // ZERO, TEX, KONST, doubleLightmapBlend ? ZERO : RAS + // Output reg 2 + // KColor 2 + if (info.m_doubleLightmapBlend) + { + // 1: Tex4TCG2, Tex4, GX_COLOR1A1 + // C2, TEX, KONST, RAS + // Output reg 2 + // KColor 3 + // Tex * K2 + Lighting + combiner += " lighting += mix(lightMapTexel * lu.kColor2, lightMapTexel, lu.kColor3);\n"; + } + else + { + // mix(Tex * K2, Tex, K3) + Lighting + combiner += " lighting += lightMapTexel * lu.kColor2;\n"; + } + } + + // Next: Tex0TCG, Tex0, GX_COLOR1A1 + // ZERO, TEX, KONST, RAS + // Output reg prev + // KColor 0 + + // Next: Tex1TCG, Tex1, GX_COLOR0A0 + // ZERO, TEX, PREV, RAS + // Output reg prev + + // Next: Tex2TCG, Tex2, GX_COLOR1A1 + // ZERO, TEX, hasTex4 ? C2 : RAS, PREV + // Output reg prev + + // (Tex0 * kColor0 + Lighting) * Tex1 + VertColor + Tex2 * Lighting + if (info.m_hasPatternTex2) + { + if (info.m_hasPatternTex1) + combiner += " colorOut = (patternTex1.sample(samp, vtf.uvs[0]) * lu.kColor0 + lighting) *\n" + " patternTex2.sample(samp, vtf.uvs[1]) + vtf.color;\n"; + else + combiner += " colorOut = lighting * patternTex2.sample(samp, vtf.uvs[1]) + vtf.color;\n"; + } + else + { + combiner += " colorOut = vtf.color;\n"; + } + + if (info.m_hasColorTex) + { + if (info.m_hasEnvBumpMap) + { + // Make previous stage indirect, mtx0 + combiner += hecl::Format(" float2 indUvs = (envBumpMap.sample(samp, vtf.uvs[%d]).ra - float2(0.5, 0.5)) *\n" + " float2(lu.fog.indScale, -lu.fog.indScale);", envBumpMapUv); + combiner += " colorOut += colorTex.sample(samp, indUvs + vtf.uvs[2]) * lighting;\n"; + } + else + { + combiner += " colorOut += colorTex.sample(samp, vtf.uvs[2]) * lighting;\n"; + } + } + + break; + + case CFluidPlane::EFluidType::Lava: + // 0: Tex0TCG, Tex0, GX_COLOR0A0 + // ZERO, TEX, KONST, RAS + // Output reg prev + // KColor 0 + + // 1: Tex1TCG, Tex1, GX_COLOR0A0 + // ZERO, TEX, PREV, RAS + // Output reg prev + + // 2: Tex2TCG, Tex2, NULL + // ZERO, TEX, ONE, PREV + // Output reg prev + + // (Tex0 * kColor0 + VertColor) * Tex1 + VertColor + Tex2 + if (info.m_hasPatternTex2) + { + if (info.m_hasPatternTex1) + combiner += " colorOut = (patternTex1.sample(samp, vtf.uvs[0]) * lu.kColor0 + vtf.color) *\n" + " patternTex2.sample(samp, vtf.uvs[1]) + vtf.color;\n"; + else + combiner += " colorOut = vtf.color * patternTex2.sample(samp, vtf.uvs[1]) + vtf.color;\n"; + } + else + { + combiner += " colorOut = vtf.color;\n"; + } + + if (info.m_hasColorTex) + combiner += " colorOut += colorTex.sample(samp, vtf.uvs[2]);\n"; + + if (info.m_hasBumpMap) + { + // 3: bumpMapTCG, bumpMap, NULL + // ZERO, TEX, ONE, HALF + // Output reg 0, no clamp, no bias + + // 4: bumpMapTCG2, bumpMap, NULL + // ZERO, TEX, ONE, C0 + // Output reg 0, subtract, clamp, no bias + + combiner += " float3 lightVec = lights[3].pos.xyz - vtf.mvPos.xyz;\n" + " float lx = dot(vtf.mvTangent.xyz, lightVec);\n" + " float ly = dot(vtf.mvBinorm.xyz, lightVec);\n"; + combiner += hecl::Format(" float4 emboss1 = bumpMap.sample(samp, vtf.uvs[%d]) + float4(0.5);\n" + " float4 emboss2 = bumpMap.sample(samp, vtf.uvs[%d] + float2(lx, ly));\n", + bumpMapUv, bumpMapUv); + + // 5: NULL, NULL, NULL + // ZERO, PREV, C0, ZERO + // Output reg prev, scale 2, clamp + + // colorOut * clamp(emboss1 + 0.5 - emboss2, 0.0, 1.0) * 2.0 + combiner += "colorOut *= clamp((emboss1 + float4(0.5) - emboss2) * float4(2.0), float4(0.0), float4(1.0));\n"; + } + + break; + + case CFluidPlane::EFluidType::Five: + // 0: Tex0TCG, Tex0, GX_COLOR0A0 + // ZERO, TEX, KONST, RAS + // Output reg prev + // KColor 0 + + // 1: Tex1TCG, Tex1, GX_COLOR0A0 + // ZERO, TEX, PREV, RAS + // Output reg prev + + // 2: Tex2TCG, Tex2, NULL + // ZERO, TEX, ONE, PREV + // Output reg prev + + // (Tex0 * kColor0 + VertColor) * Tex1 + VertColor + Tex2 + if (info.m_hasPatternTex2) + { + if (info.m_hasPatternTex1) + combiner += " colorOut = (patternTex1.sample(samp, vtf.uvs[0]) * lu.kColor0 + vtf.color) *\n" + " patternTex2.sample(samp, vtf.uvs[1]) + vtf.color;\n"; + else + combiner += " colorOut = vtf.color * patternTex2.sample(samp, vtf.uvs[1]) + vtf.color;\n"; + } + else + { + combiner += " colorOut = vtf.color;\n"; + } + + if (info.m_hasColorTex) + combiner += " colorOut += colorTex.sample(samp, vtf.uvs[2]);\n"; + + if (info.m_hasBumpMap) + { + // 3: bumpMapTCG, bumpMap, NULL + // ZERO, TEX, PREV, ZERO + // Output reg prev, scale 2 + combiner += hecl::Format(" float4 emboss1 = bumpMap.sample(samp, vtf.uvs[%d]) + float4(0.5);\n", bumpMapUv); + combiner += "colorOut *= emboss1 * float4(2.0);\n"; + } + + break; + } + + combiner += " colorOut.a = kColor0.a;\n"; + + std::string finalVS = hecl::Format(VS, additionalTCGs.c_str()); + std::string finalFS = hecl::Format(FS, textures.c_str(), combiner.c_str()); + + return ctx.newShaderPipeline(finalVS.c_str(), finalFS.c_str(), s_vtxFmt, CGraphics::g_ViewportSamples, + info.m_additive ? boo::BlendFactor::One : boo::BlendFactor::SrcAlpha, + info.m_additive ? boo::BlendFactor::One : boo::BlendFactor::InvSrcAlpha, + boo::Primitive::TriStrips, boo::ZTest::LEqual, false, true, false, + boo::CullMode::None); +} + +boo::IShaderDataBinding* CFluidPlaneShader::BuildBinding(boo::MetalDataFactory::Context& ctx, + boo::IShaderPipeline* pipeline) +{ + boo::IGraphicsBuffer* ubufs[] = { m_uniBuf, m_uniBuf, m_uniBuf }; + boo::PipelineStage ubufStages[] = { boo::PipelineStage::Vertex, boo::PipelineStage::Vertex, + boo::PipelineStage::Fragment }; + size_t ubufOffs[] = {0, 0, 768}; + size_t ubufSizes[] = {768, 768, 256}; + size_t texCount = 0; + boo::ITexture* texs[7] = {}; + 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(); + return ctx.newShaderDataBinding(pipeline, s_vtxFmt, m_vbo, nullptr, nullptr, 1, ubufs, ubufStages, ubufOffs, + ubufSizes, texCount, texs, nullptr, nullptr); +} + +} \ No newline at end of file diff --git a/Runtime/Graphics/Shaders/CModelShaders.cpp b/Runtime/Graphics/Shaders/CModelShaders.cpp index d9b225c44..54cb7e761 100644 --- a/Runtime/Graphics/Shaders/CModelShaders.cpp +++ b/Runtime/Graphics/Shaders/CModelShaders.cpp @@ -1,10 +1,58 @@ #include "CModelShaders.hpp" +#include "Graphics/CLight.hpp" namespace urde { std::experimental::optional CModelShaders::g_ModelShaders; +void CModelShaders::LightingUniform::ActivateLights(const std::vector& lts) +{ + ambient = zeus::CColor::skBlack; + size_t curLight = 0; + + for (const CLight& light : lts) + { + switch (light.GetType()) + { + case ELightType::LocalAmbient: + ambient += light.GetColor(); + break; + case ELightType::Point: + case ELightType::Spot: + case ELightType::Custom: + case ELightType::Directional: + { + if (curLight >= URDE_MAX_LIGHTS) + continue; + CModelShaders::Light& lightOut = lights[curLight++]; + lightOut.pos = CGraphics::g_CameraMatrix * light.GetPosition(); + lightOut.dir = CGraphics::g_CameraMatrix.basis * light.GetDirection(); + lightOut.dir.normalize(); + lightOut.color = light.GetColor(); + lightOut.linAtt[0] = light.GetAttenuationConstant(); + lightOut.linAtt[1] = light.GetAttenuationLinear(); + lightOut.linAtt[2] = light.GetAttenuationQuadratic(); + lightOut.angAtt[0] = light.GetAngleAttenuationConstant(); + lightOut.angAtt[1] = light.GetAngleAttenuationLinear(); + lightOut.angAtt[2] = light.GetAngleAttenuationQuadratic(); + + if (light.GetType() == ELightType::Directional) + lightOut.pos = (-lightOut.dir) * 1048576.f; + break; + } + } + } + + for (; curLight& lts); }; struct ThermalUniform diff --git a/Runtime/World/CActor.hpp b/Runtime/World/CActor.hpp index 150194241..ddaafa6cc 100644 --- a/Runtime/World/CActor.hpp +++ b/Runtime/World/CActor.hpp @@ -170,6 +170,7 @@ public: bool translateId); SAdvancementDeltas UpdateAnimation(float, CStateManager&, bool); void SetActorLights(std::unique_ptr); + const CActorLights* GetActorLights() const { return x90_actorLights.get(); } bool CanDrawStatic() const; bool GetE7_29() const { return xe7_29_; } const CScannableObjectInfo* GetScannableObjectInfo() const; diff --git a/Runtime/World/CFluidPlane.hpp b/Runtime/World/CFluidPlane.hpp index df87a6969..074c92572 100644 --- a/Runtime/World/CFluidPlane.hpp +++ b/Runtime/World/CFluidPlane.hpp @@ -19,15 +19,15 @@ class CFluidPlane public: enum class EFluidType { - Zero, - One, - Two, + NormalWater, + PoisonWater, + Lava, Three, Four, Five }; -private: +protected: ResId x4_texPattern1Id; ResId x8_texPattern2Id; ResId xc_texColorId; @@ -45,7 +45,7 @@ public: virtual void Ripple(float mag, TUniqueId rippler, const zeus::CVector3f& pos, CScriptWater& water, CStateManager& mgr); virtual void Update(); - virtual void Render(const CStateManager& mgr, const zeus::CAABox& aabb, const zeus::CTransform& xf, + virtual void Render(const CStateManager& mgr, float alpha, const zeus::CAABox& aabb, const zeus::CTransform& xf, const zeus::CTransform& areaXf, bool noSubdiv, const zeus::CFrustum& frustum, const std::experimental::optional& rippleManager, TUniqueId waterId, const bool* gridFlags, u32 gridDimX, u32 gridDimY, const zeus::CVector3f& areaCenter) const {} diff --git a/Runtime/World/CFluidPlaneCPU.cpp b/Runtime/World/CFluidPlaneCPU.cpp index 2fc4a8d97..48d120254 100644 --- a/Runtime/World/CFluidPlaneCPU.cpp +++ b/Runtime/World/CFluidPlaneCPU.cpp @@ -1,6 +1,11 @@ #include "CFluidPlaneCPU.hpp" #include "CSimplePool.hpp" #include "GameGlobalObjects.hpp" +#include "CFluidPlaneManager.hpp" +#include "CStateManager.hpp" +#include "CWorld.hpp" +#include "World/CScriptWater.hpp" +#include "TCastTo.hpp" #define kTableSize 2048 @@ -38,19 +43,20 @@ CFluidPlaneCPU::CTurbulence::CTurbulence(float speed, float distance, float freq } CFluidPlaneCPU::CFluidPlaneCPU(ResId texPattern1, ResId texPattern2, ResId texColor, ResId bumpMap, ResId envMap, - ResId envBumpMap, ResId unkMap, float f1, 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 fluidPlaneF2) + ResId envBumpMap, ResId unkMap, 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 fluidPlaneF2) : CFluidPlane(texPattern1, texPattern2, texColor, alpha, fluidType, fluidPlaneF2, mot), xa0_texIdBumpMap(bumpMap), xa4_texIdEnvMap(envMap), xa8_texIdEnvBumpMap(envBumpMap), xac_texId4(unkMap), 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_f1(f1), + x118_reflectionSize(reflectionSize), x11c_unitsPerLightmapTexel(unitsPerLightmapTexel), x120_turbulence(turbSpeed, turbDistance, turbFreqMax, turbFreqMin, turbPhaseMax, turbPhaseMin, turbAmplitudeMax, turbAmplitudeMin) { @@ -61,7 +67,7 @@ CFluidPlaneCPU::CFluidPlaneCPU(ResId texPattern1, ResId texPattern2, ResId texCo if (g_ResFactory->GetResourceTypeById(xa8_texIdEnvBumpMap) == FOURCC('TXTR')) xd0_envBumpMap.emplace(g_SimplePool->GetObj(SObjectTag{FOURCC('TXTR'), xa8_texIdEnvBumpMap})); if (g_ResFactory->GetResourceTypeById(xac_texId4) == FOURCC('TXTR')) - xe0_tex4.emplace(g_SimplePool->GetObj(SObjectTag{FOURCC('TXTR'), xac_texId4})); + xe0_lightmap.emplace(g_SimplePool->GetObj(SObjectTag{FOURCC('TXTR'), xac_texId4})); } void CFluidPlaneCPU::CreateRipple(const CRipple& ripple, CStateManager& mgr) @@ -69,18 +75,228 @@ void CFluidPlaneCPU::CreateRipple(const CRipple& ripple, CStateManager& mgr) } -void CFluidPlaneCPU::RenderSetup(const CStateManager& mgr, float, const zeus::CTransform& xf, - const zeus::CTransform& areaXf, const zeus::CAABox& aabb, CScriptWater* water) const +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(); } -void CFluidPlaneCPU::Render(const CStateManager& mgr, const zeus::CAABox& aabb, const zeus::CTransform& xf, +#define kEnableWaterBumpMaps true + +CFluidPlaneCPU::RenderSetupInfo +CFluidPlaneCPU::RenderSetup(const CStateManager& mgr, float alpha, const zeus::CTransform& xf, + const zeus::CTransform& areaXf, const zeus::CAABox& aabb, const CScriptWater* water) const +{ + RenderSetupInfo out; + + float uvT = mgr.GetFluidPlaneManager()->GetUVT(); + bool hasBumpMap = HasBumpMap() && kEnableWaterBumpMaps; + bool doubleLightmapBlend = false; + bool hasEnvMap = mgr.GetCameraManager()->GetFluidCounter() == 0 && HasEnvMap(); + bool hasEnvBumpMap = HasEnvBumpMap(); + 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 + } + else + { + // Normal light mask in channel 1 + // Vertex colors in channel 0 + } + + if (x10_texPattern1) + { + // Load into 0 + } + else + { + // Load black tex into 0 + } + + if (x20_texPattern2) + { + // Load into 1 + } + else + { + // Load black tex into 1 + } + + if (x30_texColor) + { + // Load into 2 + } + else + { + // Load black tex into 2 + } + + int curTex = 3; + int bumpMapId; + int envMapId; + int envBumpMapId; + int lightmapId; + + if (hasBumpMap) + { + // Load into next + bumpMapId = curTex++; + } + + if (hasEnvMap) + { + // Load into next + envMapId = curTex++; + } + + if (hasEnvBumpMap) + { + // Load into next + envBumpMapId = curTex++; + } + + float fluidUVs[3][2]; + x4c_uvMotion.CalculateFluidTextureOffset(uvT, fluidUVs); + + out.texMtxs[0][0][0] = 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] = 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] = 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++]; + // 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] = 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->GetLightmapDoubleBlendFactor()) < 0.00001f || !nextWater || + !nextWater->GetFluidPlane().HasLightMap()) + { + lightmapId = curTex; + // 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->GetLightmapDoubleBlendFactor() - 1.f) < 0.00001f) + { + lightmapId = curTex; + // Load lightmap + CalculateLightmapMatrix(areaXf, xf, aabb, out.texMtxs[nextTexMtx++]); + // Next: GX_TG_MTX2x4 GX_TG_POS, mtxNext, false, GX_PTIDENTITY + } + else + { + lightmapId = curTex; + // 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->GetLightmapDoubleBlendFactor(); + out.kColors[3] = zeus::CColor(lum, 1.f); + lowLightBlend = (1.f - water->GetLightmapDoubleBlendFactor()) / (1.f - lum); + doubleLightmapBlend = true; + } + } + + out.kColors[2] = zeus::CColor(lowLightBlend * lightLevel, 1.f); + } + + float waterPlaneOrthoDot = xf.transposeRotate(zeus::CVector3f::skUp). + dot(CGraphics::g_ViewMatrix.inverse().transposeRotate(zeus::CVector3f::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); + + // TODO: Detect parameter changes and rebuild if needed + if (!m_shader) + m_shader.emplace(x44_fluidType, + x10_texPattern1, x20_texPattern2, x30_texColor, xb0_bumpMap, xc0_envMap, xd0_envBumpMap, + xe0_lightmap, doubleLightmapBlend, mgr.GetParticleFlags() == 0); + out.lights = water->GetActorLights()->BuildLightVector(); + + return out; +} + +void CFluidPlaneCPU::Render(const CStateManager& mgr, float alpha, const zeus::CAABox& aabb, const zeus::CTransform& xf, const zeus::CTransform& areaXf, bool noSubdiv, const zeus::CFrustum& frustum, const std::experimental::optional& rippleManager, TUniqueId waterId, const bool* gridFlags, u32 gridDimX, u32 gridDimY, const zeus::CVector3f& areaCenter) const { - + TCastToConstPtr water = mgr.GetObjectById(waterId); + RenderSetupInfo setupInfo = RenderSetup(mgr, alpha, xf, areaXf, aabb, water.GetPtr()); + m_shader->draw(setupInfo.texMtxs, setupInfo.normMtx, setupInfo.indScale, setupInfo.lights, setupInfo.kColors); } void CFluidPlaneCPU::RenderCleanup() const diff --git a/Runtime/World/CFluidPlaneCPU.hpp b/Runtime/World/CFluidPlaneCPU.hpp index 1a57ef8f6..4be9c82f7 100644 --- a/Runtime/World/CFluidPlaneCPU.hpp +++ b/Runtime/World/CFluidPlaneCPU.hpp @@ -3,6 +3,7 @@ #include "CFluidPlane.hpp" #include "CRipple.hpp" +#include "Graphics/Shaders/CFluidPlaneShader.hpp" namespace urde { @@ -41,7 +42,7 @@ class CFluidPlaneCPU : public CFluidPlane std::experimental::optional> xb0_bumpMap; std::experimental::optional> xc0_envMap; std::experimental::optional> xd0_envBumpMap; - std::experimental::optional> xe0_tex4; + std::experimental::optional> xe0_lightmap; zeus::CVector3f xf0_bumpLightDir; float xfc_bumpScale; float x100_tileSize; @@ -51,19 +52,32 @@ class CFluidPlaneCPU : public CFluidPlane float x110_specularMax; float x114_reflectionBlend; float x118_reflectionSize; - float x11c_f1; + float x11c_unitsPerLightmapTexel; CTurbulence x120_turbulence; + mutable std::experimental::optional m_shader; + + struct RenderSetupInfo + { + zeus::CMatrix4f texMtxs[6]; + zeus::CMatrix4f normMtx; + float indScale = 1.f; + zeus::CColor kColors[4]; + std::vector lights; + }; public: CFluidPlaneCPU(ResId texPattern1, ResId texPattern2, ResId texColor, ResId bumpMap, ResId envMap, ResId envBumpMap, - ResId unkMap, float f1, float tileSize, u32 tileSubdivisions, EFluidType fluidType, float alpha, + ResId unkMap, 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 fluidPlaneF2); void CreateRipple(const CRipple& ripple, CStateManager& mgr); - void RenderSetup(const CStateManager& mgr, float, const zeus::CTransform& xf, const zeus::CTransform& areaXf, - const zeus::CAABox& aabb, CScriptWater* water) const; - void Render(const CStateManager& mgr, const zeus::CAABox& aabb, const zeus::CTransform& xf, + void CalculateLightmapMatrix(const zeus::CTransform& areaXf, const zeus::CTransform& xf, + const zeus::CAABox& aabb, zeus::CMatrix4f& mtxOut) const; + RenderSetupInfo RenderSetup(const CStateManager& mgr, float, const zeus::CTransform& xf, + const zeus::CTransform& areaXf, const zeus::CAABox& aabb, + const CScriptWater* water) const; + void Render(const CStateManager& mgr, float alpha, const zeus::CAABox& aabb, const zeus::CTransform& xf, const zeus::CTransform& areaXf, bool noSubdiv, const zeus::CFrustum& frustum, const std::experimental::optional& rippleManager, TUniqueId waterId, const bool* gridFlags, u32 gridDimX, u32 gridDimY, const zeus::CVector3f& areaCenter) const; @@ -79,6 +93,8 @@ public: const CTexture& GetEnvMap() const { return **xc0_envMap; } bool HasEnvBumpMap() const { return xd0_envBumpMap.operator bool(); } const CTexture& GetEnvBumpMap() const { return **xd0_envBumpMap; } + bool HasLightMap() const { return xe0_lightmap.operator bool(); } + const CTexture& GetLightMap() const { return **xe0_lightmap; } const zeus::CVector3f& GetBumpLightDir() const { return xf0_bumpLightDir; } float GetTileSize() const { return x100_tileSize; } u32 GetTileSubdivisions() const { return x104_tileSubdivisions; } diff --git a/Runtime/World/CFluidPlaneManager.cpp b/Runtime/World/CFluidPlaneManager.cpp index b29e55514..48eeea9ac 100644 --- a/Runtime/World/CFluidPlaneManager.cpp +++ b/Runtime/World/CFluidPlaneManager.cpp @@ -8,7 +8,8 @@ CFluidPlaneManager::CFluidProfile CFluidPlaneManager::sProfile = {}; CFluidPlaneManager::CFluidPlaneManager() : x0_rippleManager(20, 0.5f) { - + sProfile.Clear(); + SetupRippleMap(); } void CFluidPlaneManager::CFluidProfile::Clear() @@ -43,4 +44,56 @@ void CFluidPlaneManager::CreateSplash(TUniqueId splasher, CStateManager& mgr, co } +u8 CFluidPlaneManager::RippleValues[64][64] = {}; +u8 CFluidPlaneManager::RippleMins[64] = {}; +u8 CFluidPlaneManager::RippleMaxs[64] = {}; + +void CFluidPlaneManager::SetupRippleMap() +{ + float curX = 0.f; + for (int i=0 ; i<64 ; ++i) + { + float curY = 0.f; + float minY = 1.f; + float maxY = 0.f; + for (int j=0 ; j<64 ; ++j) + { + float rVal = 1.f - curY; + float minX = curY; + float maxX = 1.25f * (0.25f * rVal + 0.1f) + curY; + if (curY < 0.f) + minX = 0.f; + else if (maxX > 1.f) + maxX = 1.f; + + float val = 0.f; + if (curX >= minX && curX <= maxX) + { + float t = (curX - minX) / (maxX - minX); + if (t < 0.4f) + val = 2.5f * t; + else if (t > 0.75f) + val = 4.f * (1.f - t); + else + val = 1.f; + } + + auto valA = u8(std::max(int(255.f * val * rVal * rVal) - 1, 0)); + RippleValues[i][j] = valA; + if (valA != 0 && curY < minY) + minY = curY; + if (valA != 0 && curY > maxY) + maxY = curY; + + curY += (1.f / 63.f); + } + + auto valB = u8(std::max(int(255.f * minY) - 1, 0)); + auto valC = u8(std::min(int(255.f * maxY) + 1, 255)); + RippleMins[i] = valB; + RippleMaxs[i] = valC; + curX += (1.f / 63.f); + } +} + } diff --git a/Runtime/World/CFluidPlaneManager.hpp b/Runtime/World/CFluidPlaneManager.hpp index 19ca2b1de..c65664fff 100644 --- a/Runtime/World/CFluidPlaneManager.hpp +++ b/Runtime/World/CFluidPlaneManager.hpp @@ -11,23 +11,43 @@ class CScriptWater; class CFluidPlaneManager { + class CSplashRecord + { + public: + CSplashRecord(TUniqueId id); + void SetTime(float t); + float GetTime() const; + TUniqueId GetUniqueId() const; + }; + CRippleManager x0_rippleManager; - bool x121_; + rstl::reserved_vector x18_splashes; + float x11c_uvT = 0.f; + bool x120_ = false; + bool x121_ = false; + class CFluidProfile { public: void Clear(); }; static CFluidProfile sProfile; + static void SetupRippleMap(); public: + static u8 RippleValues[64][64]; + static u8 RippleMins[64]; + static u8 RippleMaxs[64]; + CFluidPlaneManager(); void StartFrame(bool); void EndFrame() { x121_ = false; } void Update(float dt); + float GetUVT() const { return x11c_uvT; } float GetLastRippleDeltaTime(TUniqueId rippler) const; float GetLastSplashDeltaTime(TUniqueId splasher) const; void CreateSplash(TUniqueId splasher, CStateManager& mgr, const CScriptWater& water, const zeus::CVector3f& pos, float factor, bool); + rstl::reserved_vector& SplashRecords() { return x18_splashes; } }; } diff --git a/Runtime/World/CFluidUVMotion.cpp b/Runtime/World/CFluidUVMotion.cpp index 3d824f70a..141cd2f2b 100644 --- a/Runtime/World/CFluidUVMotion.cpp +++ b/Runtime/World/CFluidUVMotion.cpp @@ -18,7 +18,7 @@ CFluidUVMotion::CFluidUVMotion(float a, float b, const CFluidUVMotion::SFluidLay CFluidUVMotion::CFluidUVMotion(float, float) {} -void CFluidUVMotion::CalculateFluidTextureOffset(float f31, float offsets[3][2]) +void CFluidUVMotion::CalculateFluidTextureOffset(float f31, float offsets[3][2]) const { float f28 = (f31 * x4c_) * zeus::fastCosF(x50_); float f29 = (f31 * x4c_) / zeus::fastSinF(x50_); diff --git a/Runtime/World/CFluidUVMotion.hpp b/Runtime/World/CFluidUVMotion.hpp index 27308b44d..6a3568937 100644 --- a/Runtime/World/CFluidUVMotion.hpp +++ b/Runtime/World/CFluidUVMotion.hpp @@ -22,14 +22,16 @@ public: float x4_a = 0.16666667f; float x8_b = 0.f; float xc_c = 1.f; - float x10_d = 5.f; - float x14_envNormalScale = 0.2f; + float x10_uvMul = 5.f; + float x14_uvScale = 0.2f; SFluidLayerMotion() = default; - SFluidLayerMotion(EFluidUVMotion motion, float a, float b, float c, float d) - : x0_motion(motion), x4_a(1.f / a), x8_b(b), xc_c(c), x10_d(d), x14_envNormalScale(1.f / d) + SFluidLayerMotion(EFluidUVMotion motion, float a, float b, float c, float uvMul) + : x0_motion(motion), x4_a(1.f / a), x8_b(b), xc_c(c), x10_uvMul(uvMul), x14_uvScale(1.f / uvMul) { } + + float GetUVScale() const { return x14_uvScale; } }; private: @@ -43,7 +45,7 @@ public: const rstl::reserved_vector& GetFluidLayers() const { return x0_fluidLayers; } void GetOrientation() const; void GetOOTimeToWrapTexPage() const; - void CalculateFluidTextureOffset(float, float[3][2]); + void CalculateFluidTextureOffset(float, float[3][2]) const; }; } #endif // __URDE_CFLUIDUVMOTION_HPP__ diff --git a/Runtime/World/CPlayer.cpp b/Runtime/World/CPlayer.cpp index 18ddac504..4719914e7 100644 --- a/Runtime/World/CPlayer.cpp +++ b/Runtime/World/CPlayer.cpp @@ -252,7 +252,7 @@ void CPlayer::FluidFXThink(EFluidState state, CScriptWater& water, CStateManager zeus::CVector3f position = x34_transform.origin + posOffset; position.z = water.GetTriggerBoundsWR().max.z; mgr.GetFluidPlaneManager()->CreateSplash(x8_uid, mgr, water, position, 0.3f, true); - if (water.GetFluidPlane().GetFluidType() == CFluidPlane::EFluidType::Zero) + if (water.GetFluidPlane().GetFluidType() == CFluidPlane::EFluidType::NormalWater) { float velMag = mgr.GetPlayer().GetVelocity().magnitude() / 10.f; mgr.GetEnvFxManager()->SetXB54(10.f * std::max(1.f, velMag)); @@ -1710,14 +1710,14 @@ void CPlayer::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CState { switch (water->GetFluidPlane().GetFluidType()) { - case CFluidPlane::EFluidType::Zero: + case CFluidPlane::EFluidType::NormalWater: x2b0_ = 0; break; - case CFluidPlane::EFluidType::Two: + case CFluidPlane::EFluidType::Lava: case CFluidPlane::EFluidType::Five: x2ac_surfaceRestraint = ESurfaceRestraints::Fluid2Or5; break; - case CFluidPlane::EFluidType::One: + case CFluidPlane::EFluidType::PoisonWater: x2b0_ = 0; break; case CFluidPlane::EFluidType::Three: @@ -4399,7 +4399,7 @@ void CPlayer::UpdateSubmerged(CStateManager& mgr) x828_waterLevelOnPlayer = -(zeus::CVector3f::skUp.dot(x34_transform.origin) - water->GetTriggerBoundsWR().max.z); CFluidPlane::EFluidType fluidType = water->GetFluidPlane().GetFluidType(); - x82c_inLava = (fluidType == CFluidPlane::EFluidType::Two || fluidType == CFluidPlane::EFluidType::Five); + x82c_inLava = (fluidType == CFluidPlane::EFluidType::Lava || fluidType == CFluidPlane::EFluidType::Five); CheckSubmerged(); } } diff --git a/Runtime/World/CScriptWater.cpp b/Runtime/World/CScriptWater.cpp index 2ada1b486..ce9dfa3fb 100644 --- a/Runtime/World/CScriptWater.cpp +++ b/Runtime/World/CScriptWater.cpp @@ -1,5 +1,6 @@ #include "CScriptWater.hpp" #include "CStateManager.hpp" +#include "TCastTo.hpp" namespace urde { @@ -87,4 +88,18 @@ u32 CScriptWater::GetSplashIndex(float dt) const u32 idx = dt * 3.f; return (idx < 3 ? idx : idx - 1); } + +const CScriptWater* CScriptWater::GetNextConnectedWater(const CStateManager& mgr) const +{ + for (const SConnection& conn : x20_conns) + { + if (conn.x0_state != EScriptObjectState::Play || conn.x4_msg != EScriptObjectMessage::Activate) + continue; + auto its = mgr.GetIdListForScript(conn.x8_objId); + if (its.first != mgr.GetIdListEnd()) + if (TCastToConstPtr water = mgr.GetObjectById(its.first->second)) + return water.GetPtr(); + } + return nullptr; +} } diff --git a/Runtime/World/CScriptWater.hpp b/Runtime/World/CScriptWater.hpp index 69886c5cc..833100e6c 100644 --- a/Runtime/World/CScriptWater.hpp +++ b/Runtime/World/CScriptWater.hpp @@ -17,7 +17,7 @@ private: std::unique_ptr x1b4_fluidPlane; zeus::CVector3f x1b8_; float x1f4_; - float x1f8_ = 0.f; + float x1f8_lightmapDoubleBlendFactor = 0.f; zeus::CVector3f x1d4_; std::list> x1fc_waterInhabitants; u32 x210_; @@ -97,6 +97,8 @@ public: const std::experimental::optional>& GetVisorRunoffEffect() const { return x250_visorRunoffEffect; } u16 GetVisorRunoffSfx() const { return x262_visorRunoffSfx; } + const CScriptWater* GetNextConnectedWater(const CStateManager& mgr) const; + float GetLightmapDoubleBlendFactor() const { return x1f8_lightmapDoubleBlendFactor; } }; }