#include "Runtime/Graphics/Shaders/CModelShaders.hpp" #include "Runtime/CStopwatch.hpp" #include "Runtime/Graphics/CLight.hpp" #include "Runtime/Graphics/CTexture.hpp" #include "Runtime/Graphics/CBooRenderer.hpp" #include "Runtime/GameGlobalObjects.hpp" #include "hecl/Backend.hpp" #include "zeus/CAABox.hpp" namespace urde { void CModelShaders::FragmentUniform::ActivateLights(const std::vector& lts) { ambient = zeus::skClear; 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 >= lights.size()) { continue; } CModelShaders::Light& lightOut = lights[curLight++]; lightOut.pos = CGraphics::g_CameraMatrix * light.GetPosition(); lightOut.dir = (CGraphics::g_CameraMatrix.basis * light.GetDirection()).normalized(); 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 < lights.size(); ++curLight) { CModelShaders::Light& lightOut = lights[curLight]; lightOut.pos = zeus::skZero3f; lightOut.dir = zeus::skDown; lightOut.color = zeus::skClear; lightOut.linAtt[0] = 1.f; lightOut.linAtt[1] = 0.f; lightOut.linAtt[2] = 0.f; lightOut.angAtt[0] = 1.f; lightOut.angAtt[1] = 0.f; lightOut.angAtt[2] = 0.f; } } using TexCoordSource = hecl::Backend::TexCoordSource; constexpr std::array ThermalTextures{{ {TexCoordSource::Normal, 7, true}, }}; constexpr std::array BallFadeTextures{{ {TexCoordSource::Position, 0, false}, // ID tex {TexCoordSource::Position, 0, false}, // Sphere ramp {TexCoordSource::Position, 1, false}, // TXTR_BallFade }}; constexpr std::array WorldShadowTextures{{ {TexCoordSource::Position, 7, false}, // Shadow tex }}; constexpr std::array DisintegrateTextures{{ {TexCoordSource::Position, 0, false}, // Ashy tex {TexCoordSource::Position, 1, false}, // Ashy tex }}; static std::array g_ExtensionSlots{{ /* Default solid shading */ {}, /* Normal lit shading */ {0, nullptr, hecl::Backend::BlendFactor::Original, hecl::Backend::BlendFactor::Original, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::Backface, false, false, true}, /* Thermal Visor shading */ {1, ThermalTextures.data(), hecl::Backend::BlendFactor::One, hecl::Backend::BlendFactor::One, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::Backface, false, false, false, true}, /* Forced alpha shading */ {0, nullptr, hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::InvSrcAlpha, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::Backface, false, false, true}, /* Forced additive shading */ {0, nullptr, hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::One, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::Backface, false, false, true}, /* Solid color */ {0, nullptr, hecl::Backend::BlendFactor::One, hecl::Backend::BlendFactor::Zero, hecl::Backend::ZTest::LEqual, hecl::Backend::CullMode::Backface, false, false, false}, /* Solid color additive */ {0, nullptr, hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::One, hecl::Backend::ZTest::LEqual, hecl::Backend::CullMode::Backface, true, false, true}, /* Alpha-only Solid color frontface cull, LEqual */ {0, nullptr, hecl::Backend::BlendFactor::Zero, hecl::Backend::BlendFactor::One, hecl::Backend::ZTest::LEqual, hecl::Backend::CullMode::Frontface, false, true, false}, /* Alpha-only Solid color frontface cull, Always, No Z-write */ {0, nullptr, hecl::Backend::BlendFactor::Zero, hecl::Backend::BlendFactor::One, hecl::Backend::ZTest::None, hecl::Backend::CullMode::Frontface, true, true, false}, /* Alpha-only Solid color backface cull, LEqual */ {0, nullptr, hecl::Backend::BlendFactor::Zero, hecl::Backend::BlendFactor::One, hecl::Backend::ZTest::LEqual, hecl::Backend::CullMode::Backface, false, true, false}, /* Alpha-only Solid color backface cull, Greater, No Z-write */ {0, nullptr, hecl::Backend::BlendFactor::Zero, hecl::Backend::BlendFactor::One, hecl::Backend::ZTest::Greater, hecl::Backend::CullMode::Backface, true, true, false}, /* MorphBall shadow shading */ {3, BallFadeTextures.data(), hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::InvSrcAlpha, hecl::Backend::ZTest::Equal, hecl::Backend::CullMode::Backface, false, false, true, false, true}, /* World shadow shading (modified lighting) */ {1, WorldShadowTextures.data(), hecl::Backend::BlendFactor::Original, hecl::Backend::BlendFactor::Original, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::Backface, false, false, true}, /* Forced alpha shading without culling */ {0, nullptr, hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::InvSrcAlpha, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::None, false, false, true}, /* Forced additive shading without culling */ {0, nullptr, hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::One, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::None, false, false, true}, /* Forced alpha shading without Z-write */ {0, nullptr, hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::InvSrcAlpha, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::Original, true, false, true}, /* Forced additive shading without Z-write */ {0, nullptr, hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::One, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::Original, true, false, true}, /* Forced alpha shading without culling or Z-write */ {0, nullptr, hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::InvSrcAlpha, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::None, true, false, true}, /* Forced additive shading without culling or Z-write */ {0, nullptr, hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::One, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::None, true, false, true}, /* Depth GEqual no Z-write */ {0, nullptr, hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::InvSrcAlpha, hecl::Backend::ZTest::GEqual, hecl::Backend::CullMode::Backface, true, false, true}, /* Disintegration */ {2, DisintegrateTextures.data(), hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::InvSrcAlpha, hecl::Backend::ZTest::LEqual, hecl::Backend::CullMode::Original, false, false, true, false, false, true}, /* Forced additive shading without culling or Z-write and greater depth test */ {0, nullptr, hecl::Backend::BlendFactor::SrcAlpha, hecl::Backend::BlendFactor::One, hecl::Backend::ZTest::Greater, hecl::Backend::CullMode::None, true, false, true}, /* Thermal cold shading */ {0, nullptr, hecl::Backend::BlendFactor::Original, hecl::Backend::BlendFactor::Original, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::Original, false, false, true, false, false, false, true}, /* Normal lit shading with alpha */ {0, nullptr, hecl::Backend::BlendFactor::Original, hecl::Backend::BlendFactor::Original, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::Backface}, /* Normal lit shading with alpha without Z-write or depth test */ {0, nullptr, hecl::Backend::BlendFactor::Original, hecl::Backend::BlendFactor::Original, hecl::Backend::ZTest::None, hecl::Backend::CullMode::Backface, true}, /* Normal lit shading with cube reflection */ {0, nullptr, hecl::Backend::BlendFactor::Original, hecl::Backend::BlendFactor::Original, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::Backface, false, false, true}, /* Normal lit shading with cube reflection and world shadow */ {1, WorldShadowTextures.data(), hecl::Backend::BlendFactor::Original, hecl::Backend::BlendFactor::Original, hecl::Backend::ZTest::Original, hecl::Backend::CullMode::Backface, false, false, true}, }}; // TODO: put somewhere common template class Flags { public: using MaskType = typename std::underlying_type::type; // constructors constexpr Flags() noexcept : m_mask(0) {} constexpr Flags(BitType bit) noexcept : m_mask(static_cast(bit)) {} constexpr Flags(Flags const& rhs) noexcept : m_mask(rhs.m_mask) {} constexpr explicit Flags(MaskType flags) noexcept : m_mask(flags) {} // relational operators auto operator<=>(Flags const&) const = default; // logical operator constexpr bool operator!() const noexcept { return !m_mask; } // bitwise operators constexpr Flags operator&(Flags const& rhs) const noexcept { return Flags(m_mask & rhs.m_mask); } constexpr Flags operator|(Flags const& rhs) const noexcept { return Flags(m_mask | rhs.m_mask); } constexpr Flags operator^(Flags const& rhs) const noexcept { return Flags(m_mask ^ rhs.m_mask); } // assignment operators constexpr Flags& operator=(Flags const& rhs) noexcept { m_mask = rhs.m_mask; return *this; } constexpr Flags& operator|=(Flags const& rhs) noexcept { m_mask |= rhs.m_mask; return *this; } constexpr Flags& operator&=(Flags const& rhs) noexcept { m_mask &= rhs.m_mask; return *this; } constexpr Flags& operator^=(Flags const& rhs) noexcept { m_mask ^= rhs.m_mask; return *this; } // cast operators explicit constexpr operator bool() const noexcept { return !!m_mask; } explicit constexpr operator MaskType() const noexcept { return m_mask; } private: MaskType m_mask; }; enum class CCubeMaterialFlagBits : u32 { fKonstValues = 0x8, fDepthSorting = 0x10, fAlphaTest = 0x20, fSamusReflection = 0x40, fDepthWrite = 0x80, fSamusReflectionSurfaceEye = 0x100, fShadowOccluderMesh = 0x200, fSamusReflectionIndirectTexture = 0x400, fLightmap = 0x800, fLightmapUvArray = 0x2000, fTextureSlotMask = 0xffff0000 }; using CCubeMaterialFlags = Flags; enum class CCubeMaterialVatAttribute : u32 { Position = 0, Normal = 2, Color0 = 4, Color1 = 8, Tex0 = 10, Tex1 = 12, Tex2 = 14, Tex3 = 16, Tex4 = 18, Tex5 = 20, Tex6 = 22, }; enum class CCubeMaterialVatAttributeType : u32 { None = 0, Direct = 1, Index8 = 2, Index16 = 3 }; class CCubeMaterialVatFlags { u32 m_flags; public: constexpr CCubeMaterialVatFlags() noexcept : m_flags(0) {} CCubeMaterialVatAttributeType GetAttributeType(CCubeMaterialVatAttribute attribute) const noexcept { return CCubeMaterialVatAttributeType((m_flags >> u32(attribute)) & 0x3); } void SetAttributeType(CCubeMaterialVatAttribute attribute, CCubeMaterialVatAttributeType type) noexcept { m_flags &= ~(u32(0x3) << u32(attribute)); m_flags |= u32(type) << u32(attribute); } }; struct CCubeSurface { float x0_center[3]; u32 xc_matIdx; u16 x10_fixedPointRange; u16 x12_dlSize; struct CCubeModel* x14_parentModel; CCubeSurface* x18_nextSurface; u32 x1c_extraSize; float x20_normal[3]; }; struct CMetroidModelInstance { u32 x0_visorFlags; zeus::CTransform x4_worldXf; zeus::CAABox x34_worldAABB; void* x4c_firstGeomData; std::vector x50_surfacePtrs; void* x60_positions; void* x64_normals; void* x68_vtxColors; void* x6c_floatUVs; void* x70_shortUVs; // CMetroidModelInstance(void* modelHeader, void* firstGeomPtr, void* positions, void* normals, void* vtxColors, // void* floatUVs, void* shortUVs, const std::vector& surfacePtrs); }; struct STexReference { u32 x0_; u32 x4_; CTexture* x8_tex; }; struct CCubeModel { static bool sUsingPackedLightmaps; const std::vector& x0_surfacePtrs; void* x4_firstGeomData; void* x8_positions; void* xc_normals; void* x10_vtxColors; void* x14_floatUVs; void* x18_shortUVs; const std::vector& x1c_texs; zeus::CAABox x20_worldAABB; CCubeSurface* x38_firstUnsortedSurf = nullptr; CCubeSurface* x3c_firstSortedSurf = nullptr; bool x40_24_someFlag; bool x40_25 = false; u8 x41_visorFlags; u32 x44_idx = 0; // CCubeModel(const std::vector& surfacePtrs, const std::vector& texs, // void* firstGeomData, void* positions, void* normals, void* vtxColors, // void* floatUVs, void* shortUVs, const zeus::CAABox& worldAABB, u8 visorFlags, // bool someFlag, u32 surfaceIdx); void SetUsingPackedLightmaps() const {} }; static const u8* sLastMaterialCached = nullptr; static bool sRenderModelBlack = false; static u32 sLastMaterialUnique = 0; static bool kShadowMapsEnabled = false; enum class CModelFlagsFlagBits : u16 { fDepthTest = 0x1, fDepthWrite = 0x2, fDrawWithoutTexLock = 0x4, kDepthGreater = 0x8, fDepthNonInclusive = 0x10, }; using CModelFlagsFlags = Flags; using namespace hsh::pipeline; constexpr hsh::sampler Samp; constexpr hsh::sampler ClampSamp(hsh::Linear, hsh::Linear, hsh::Linear, hsh::ClampToBorder, hsh::ClampToBorder, hsh::ClampToBorder, 0.f, hsh::Never, hsh::OpaqueWhite); constexpr hsh::sampler ClampEdgeSamp(hsh::Linear, hsh::Linear, hsh::Linear, hsh::ClampToEdge, hsh::ClampToEdge, hsh::ClampToEdge); constexpr hsh::sampler ReflectSamp(hsh::Linear, hsh::Linear, hsh::Linear, hsh::ClampToBorder, hsh::ClampToBorder, hsh::ClampToBorder); template struct DynReflectionTex { using type = hsh::texture2d; }; template <> struct DynReflectionTex { using type = hsh::texturecube; }; template using DynReflectionTexType = typename DynReflectionTex::type; using BlendMaterial = hecl::blender::Material; using MaterialBlendMode = BlendMaterial::BlendMode; using MaterialTexCoordSource = BlendMaterial::TexCoordSource; struct NoColorValue { static constexpr bool Constant = false; }; template struct ColorValue { static constexpr bool Constant = true; static constexpr hsh::float3 RGB{R, G, B}; static constexpr float A_ = A; }; template struct PassTraits { static constexpr MaterialTexCoordSource Source_ = Source; static constexpr bool Normalize_ = Normalize; static constexpr int MtxIdx_ = MtxIdx; static constexpr bool SampleAlpha_ = SampleAlpha; using ConstantColor_ = ConstantColor; }; template constexpr hsh::BlendFactor MaterialBlendModeSrcFactor = hsh::One; template <> constexpr hsh::BlendFactor MaterialBlendModeSrcFactor = hsh::SrcAlpha; template <> constexpr hsh::BlendFactor MaterialBlendModeSrcFactor = hsh::SrcAlpha; template constexpr hsh::BlendFactor MaterialBlendModeDstFactor = hsh::Zero; template <> constexpr hsh::BlendFactor MaterialBlendModeDstFactor = hsh::InvSrcAlpha; template <> constexpr hsh::BlendFactor MaterialBlendModeDstFactor = hsh::One; template constexpr hsh::ColorComponentFlags ColorUpdateFlags = hsh::ColorComponentFlags((ColorUpdate ? hsh::CC_Red | hsh::CC_Green | hsh::CC_Blue : 0) | (AlphaUpdate ? hsh::CC_Alpha : 0)); template struct CModelShadersColorAttachmentBase : color_attachment, MaterialBlendModeDstFactor, hsh::Add, DstAlpha ? hsh::ConstAlpha : hsh::Zero, hsh::Zero, hsh::Add, ColorUpdateFlags> {}; template struct CModelShadersColorAttachmentOverride : color_attachment> {}; template struct CModelShadersColorAttachment0To4 : CModelShadersColorAttachmentBase {}; template struct CModelShadersColorAttachment0To4 : CModelShadersColorAttachmentOverride {}; template struct CModelShadersColorAttachment5To6 : CModelShadersColorAttachmentBase {}; template struct CModelShadersColorAttachment5To6 : CModelShadersColorAttachmentOverride {}; template struct CModelShadersColorAttachment5To6 : CModelShadersColorAttachmentOverride {}; template struct CModelShadersColorAttachment7To8 : CModelShadersColorAttachmentBase {}; template struct CModelShadersColorAttachment7To8 : CModelShadersColorAttachmentOverride {}; template struct CModelShadersColorAttachment7To8 : CModelShadersColorAttachmentOverride {}; template struct CModelShadersColorAttachment : color_attachment<> {}; template struct CModelShadersColorAttachment : CModelShadersColorAttachment0To4 {}; template struct CModelShadersColorAttachment : CModelShadersColorAttachment0To4 {}; template struct CModelShadersColorAttachment : CModelShadersColorAttachment0To4 {}; template struct CModelShadersColorAttachment : CModelShadersColorAttachment0To4 {}; template struct CModelShadersColorAttachment : CModelShadersColorAttachment0To4 {}; template struct CModelShadersColorAttachment : CModelShadersColorAttachment5To6 {}; template struct CModelShadersColorAttachment : CModelShadersColorAttachment5To6 {}; template struct CModelShadersColorAttachment : CModelShadersColorAttachment7To8 {}; template struct CModelShadersColorAttachment : CModelShadersColorAttachment7To8 {}; template struct CModelShadersPipelineConfig : pipeline< CModelShadersColorAttachment, depth_compare, depth_write, early_depth_stencil<(MaterialFlags & u16(CCubeMaterialFlagBits::fAlphaTest)) == 0>> { static constexpr bool AlphaTest = MaterialFlags & u16(CCubeMaterialFlagBits::fAlphaTest); }; template struct CModelShadersPipeline : CModelShadersPipelineConfig { CModelShadersPipeline(hsh::uniform_buffer> vu, hsh::uniform_buffer fragu HSH_VAR_STAGE(fragment), hsh::uniform_buffer> tcgu, hsh::uniform_buffer refu, hsh::texture2d Lightmap, hsh::texture2d Diffuse, hsh::texture2d Emissive, hsh::texture2d Specular, hsh::texture2d ExtendedSpecular, hsh::texture2d Reflection, hsh::texture2d Alpha, hsh::texture2d ReflectionIndTex, hsh::texture2d ExtTex0, hsh::texture2d ExtTex1, hsh::texture2d ExtTex2, DynReflectionTexType dynReflection, hsh::vertex_buffer> vd) { hsh::float4 mvPos; hsh::float4 mvNorm; hsh::float4 objPos; hsh::float4 objNorm; if constexpr (NSkinSlots != 0) { objPos = hsh::float4(0.f); objNorm = hsh::float4(0.f); for (uint32_t i = 0; i < NSkinSlots; ++i) { objPos += (vu->objs[i] * hsh::float4(vd->posIn, 1.f)) * vd->weightIn[i / 4][i % 4]; objNorm += (vu->objsInv[i] * hsh::float4(vd->normIn, 1.f)) * vd->weightIn[i / 4][i % 4]; } objPos[3] = 1.f; objNorm = hsh::float4(hsh::normalize(objNorm.xyz()), 0.f); mvPos = vu->mv * objPos; mvNorm = hsh::float4(hsh::normalize((vu->mvInv * objNorm).xyz()), 0.f); this->position = vu->proj * mvPos; } else { objPos = hsh::float4(vd->posIn, 1.f); objNorm = hsh::float4(vd->normIn, 0.f); mvPos = vu->mv * objPos; mvNorm = vu->mvInv * objNorm; this->position = vu->proj * mvPos; } hsh::float2 LightmapUv = hsh::float2(0.f); hsh::float2 DiffuseUv = hsh::float2(0.f); hsh::float2 EmissiveUv = hsh::float2(0.f); hsh::float2 SpecularUv = hsh::float2(0.f); hsh::float2 ExtendedSpecularUv = hsh::float2(0.f); hsh::float2 ReflectionUv = hsh::float2(0.f); hsh::float2 AlphaUv = hsh::float2(0.f); hsh::float2 ShadowUv = hsh::float2(0.f); hsh::float2 DynReflectionUv = hsh::float2(0.f); hsh::float2 DynReflectionIndUv = hsh::float2(0.f); hsh::float2 ExtUv0 = hsh::float2(0.f); hsh::float2 ExtUv1 = hsh::float2(0.f); hsh::float2 ExtUv2 = hsh::float2(0.f); #define EMIT_TCG(Pass) \ if constexpr (Pass##Traits::Source_ != MaterialTexCoordSource::Invalid) { \ if constexpr (Pass##Traits::MtxIdx_ >= 0) { \ hsh::float4 src; \ switch (Pass##Traits::Source_) { \ case MaterialTexCoordSource::Position: \ src = hsh::float4(objPos.xyz(), 1.f); \ break; \ case MaterialTexCoordSource::Normal: \ src = hsh::float4(objNorm.xyz(), 1.f); \ break; \ case MaterialTexCoordSource::Tex0: \ src = hsh::float4(vd->uvIn[0], 0.f, 1.f); \ break; \ case MaterialTexCoordSource::Tex1: \ src = hsh::float4(vd->uvIn[1], 0.f, 1.f); \ break; \ case MaterialTexCoordSource::Tex2: \ src = hsh::float4(vd->uvIn[2], 0.f, 1.f); \ break; \ case MaterialTexCoordSource::Tex3: \ src = hsh::float4(vd->uvIn[3], 0.f, 1.f); \ break; \ case MaterialTexCoordSource::Tex4: \ src = hsh::float4(vd->uvIn[4], 0.f, 1.f); \ break; \ case MaterialTexCoordSource::Tex5: \ src = hsh::float4(vd->uvIn[5], 0.f, 1.f); \ break; \ case MaterialTexCoordSource::Tex6: \ src = hsh::float4(vd->uvIn[6], 0.f, 1.f); \ break; \ case MaterialTexCoordSource::Tex7: \ src = hsh::float4(vd->uvIn[7], 0.f, 1.f); \ break; \ case MaterialTexCoordSource::Invalid: \ break; \ } \ hsh::float3 tmp = ((*tcgu)[Pass##Traits::MtxIdx_].mtx * src).xyz(); \ if constexpr (Pass##Traits::Normalize_) \ tmp = hsh::normalize(tmp); \ hsh::float4 tmpProj = (*tcgu)[Pass##Traits::MtxIdx_].postMtx * hsh::float4(tmp, 1.f); \ Pass##Uv = (tmpProj / tmpProj.w).xy(); \ } else { \ switch (Pass##Traits::Source_) { \ case MaterialTexCoordSource::Position: \ Pass##Uv = objPos.xy(); \ break; \ case MaterialTexCoordSource::Normal: \ Pass##Uv = objNorm.xy(); \ break; \ case MaterialTexCoordSource::Tex0: \ Pass##Uv = vd->uvIn[0]; \ break; \ case MaterialTexCoordSource::Tex1: \ Pass##Uv = vd->uvIn[1]; \ break; \ case MaterialTexCoordSource::Tex2: \ Pass##Uv = vd->uvIn[2]; \ break; \ case MaterialTexCoordSource::Tex3: \ Pass##Uv = vd->uvIn[3]; \ break; \ case MaterialTexCoordSource::Tex4: \ Pass##Uv = vd->uvIn[4]; \ break; \ case MaterialTexCoordSource::Tex5: \ Pass##Uv = vd->uvIn[5]; \ break; \ case MaterialTexCoordSource::Tex6: \ Pass##Uv = vd->uvIn[6]; \ break; \ case MaterialTexCoordSource::Tex7: \ Pass##Uv = vd->uvIn[7]; \ break; \ case MaterialTexCoordSource::Invalid: \ break; \ } \ } \ } EMIT_TCG(Lightmap) EMIT_TCG(Diffuse) EMIT_TCG(Emissive) EMIT_TCG(Specular) EMIT_TCG(ExtendedSpecular) EMIT_TCG(Reflection) EMIT_TCG(Alpha) if constexpr ((MaterialFlags & (u32(CCubeMaterialFlagBits::fSamusReflection) | u32(CCubeMaterialFlagBits::fSamusReflectionIndirectTexture))) != 0) { DynReflectionIndUv = hsh::normalize((refu->indMtx * hsh::float4(objPos.xyz(), 1.f)).xz()) * hsh::float2(0.5f) + hsh::float2(0.5f); DynReflectionUv = (refu->reflectMtx * hsh::float4(objPos.xyz(), 1.f)).xy(); } hsh::float3 lighting; switch (Post) { case EPostType::ThermalHot: case EPostType::ThermalCold: case EPostType::Solid: case EPostType::MBShadow: lighting = hsh::float3(1.f); break; default: lighting = fragu->ambient.xyz(); for (int i = 0; i < URDE_MAX_LIGHTS; ++i) { hsh::float3 delta = mvPos.xyz() - fragu->lights[i].pos; float dist = hsh::length(delta); hsh::float3 deltaNorm = delta / dist; float angDot = hsh::max(hsh::dot(deltaNorm, fragu->lights[i].dir), 0.f); float att = 1.f / (fragu->lights[i].linAtt[2] * dist * dist + fragu->lights[i].linAtt[1] * dist + fragu->lights[i].linAtt[0]); float angAtt = fragu->lights[i].angAtt[2] * angDot * angDot + fragu->lights[i].angAtt[1] * angDot + fragu->lights[i].angAtt[0]; hsh::float3 thisColor = fragu->lights[i].color.xyz() * angAtt * att * hsh::max(hsh::dot(-deltaNorm, mvNorm.xyz()), 0.f); if (WorldShadow && i == 0) thisColor *= ExtTex0.sample(ShadowUv, ClampSamp).x; lighting += thisColor; } lighting = hsh::saturate(lighting); break; } hsh::float3 DynReflectionSample HSH_VAR_STAGE(fragment); if constexpr ((MaterialFlags & u32(CCubeMaterialFlagBits::fSamusReflectionIndirectTexture)) != 0) { DynReflectionSample = dynReflection .template sample((ReflectionIndTex.sample(DynReflectionIndUv, Samp).xw() - hsh::float2(0.5f)) * hsh::float2(0.5f) + DynReflectionUv, ReflectSamp) .xyz() * refu->reflectAlpha; } else if constexpr ((MaterialFlags & u32(CCubeMaterialFlagBits::fSamusReflection)) != 0) { DynReflectionSample = dynReflection.template sample(DynReflectionUv, ReflectSamp).xyz() * refu->reflectAlpha; } else { DynReflectionSample = hsh::float3(0.f); } const hsh::float3 kRGBToYPrime = hsh::float3(0.257f, 0.504f, 0.098f); #define Sample(Pass) \ (Pass##Traits::SampleAlpha_ \ ? (Pass##Traits::ConstantColor_::Constant ? hsh::float3(Pass##Traits::ConstantColor_::A_) \ : hsh::float3(Pass.sample(Pass##Uv, Samp).w)) \ : (Pass##Traits::ConstantColor_::Constant ? Pass##Traits::ConstantColor_::RGB \ : Pass.sample(Pass##Uv, Samp).xyz())) #define SampleAlpha(Pass) \ (Pass##Traits::SampleAlpha_ \ ? (Pass##Traits::ConstantColor_::Constant ? Pass##Traits::ConstantColor_::A_ \ : Pass.sample(Pass##Uv, Samp).w) \ : (Pass##Traits::ConstantColor_::Constant ? hsh::dot(Pass##Traits::ConstantColor_::RGB, kRGBToYPrime) \ : hsh::dot(Pass.sample(Pass##Uv, Samp).xyz(), kRGBToYPrime))) switch (Type) { case BlendMaterial::ShaderType::Invalid: this->color_out[0] = hsh::float4(Sample(Diffuse), SampleAlpha(Alpha)); break; case BlendMaterial::ShaderType::RetroShader: this->color_out[0] = hsh::float4( (Sample(Lightmap) * fragu->lightmapMul.xyz() + lighting) * Sample(Diffuse) + Sample(Emissive) + (Sample(Specular) + Sample(ExtendedSpecular) * lighting) * Sample(Reflection) + DynReflectionSample, SampleAlpha(Alpha)); break; case BlendMaterial::ShaderType::RetroDynamicShader: this->color_out[0] = hsh::float4( (Sample(Lightmap) * fragu->lightmapMul.xyz() + lighting) * (Sample(Diffuse) + Sample(Emissive)) * fragu->lightmapMul.xyz() + (Sample(Specular) + Sample(ExtendedSpecular) * lighting) * Sample(Reflection) + DynReflectionSample, SampleAlpha(Alpha)); break; case BlendMaterial::ShaderType::RetroDynamicAlphaShader: this->color_out[0] = hsh::float4( (Sample(Lightmap) * fragu->lightmapMul.xyz() + lighting) * (Sample(Diffuse) + Sample(Emissive)) * fragu->lightmapMul.xyz() + (Sample(Specular) + Sample(ExtendedSpecular) * lighting) * Sample(Reflection) + DynReflectionSample, SampleAlpha(Alpha) * fragu->lightmapMul.w); break; case BlendMaterial::ShaderType::RetroDynamicCharacterShader: this->color_out[0] = hsh::float4( (Sample(Lightmap) + lighting) * Sample(Diffuse) + Sample(Emissive) * fragu->lightmapMul.xyz() + (Sample(Specular) + Sample(ExtendedSpecular) * lighting) * Sample(Reflection) + DynReflectionSample, SampleAlpha(Alpha)); break; } FOG_SHADER(fragu->fog) if constexpr (Post == EPostType::Normal) { if constexpr (this->DstColorBlendFactor<0> == hsh::One) this->color_out[0] = hsh::float4(hsh::lerp(this->color_out[0], hsh::float4(0.f), fogZ).xyz(), this->color_out[0].w); else this->color_out[0] = hsh::float4(hsh::lerp(this->color_out[0], fragu->fog.m_color, fogZ).xyz(), this->color_out[0].w); if constexpr (GameBlendMode == 2) { if (this->DstColorBlendFactor<0> != hsh::One) this->color_out[0] += fragu->flagsColor; } else if constexpr (GameBlendMode != 0) { this->color_out[0] *= fragu->flagsColor; } } else if constexpr (Post == EPostType::ThermalHot) { this->color_out[0] = hsh::float4(ExtTex0.sample(ExtUv0, Samp).x) * fragu->flagsColor + fragu->ambient; } else if constexpr (Post == EPostType::ThermalCold) { this->color_out[0] *= hsh::float4(0.75f); } else if constexpr (Post == EPostType::Solid) { this->color_out[0] = fragu->flagsColor; } else if constexpr (Post == EPostType::MBShadow) { float idTexel = ExtTex0.sample(ExtUv0, Samp).w; float sphereTexel = ExtTex1.sample(ExtUv1, ClampEdgeSamp).x; float fadeTexel = ExtTex2.sample(ExtUv2, ClampEdgeSamp).w; float val = ((hsh::abs(idTexel - fragu->ambient.x) < 0.001f) ? (hsh::dot(mvNorm.xyz(), fragu->flagsColor.xyz()) * fragu->flagsColor.w) : 0.f) * sphereTexel * fadeTexel; this->color_out[0] = hsh::float4(0.f, 0.f, 0.f, val); } else if constexpr (Post == EPostType::Disintegrate) { hsh::float4 texel0 = ExtTex0.sample(ExtUv0, Samp); hsh::float4 texel1 = ExtTex0.sample(ExtUv1, Samp); this->color_out[0] = hsh::lerp(hsh::float4(0.f), texel1, texel0); this->color_out[0] = hsh::float4(fragu->flagsColor.xyz() + this->color_out[0].xyz(), this->color_out[0].w); if constexpr (this->DstColorBlendFactor<0> == hsh::One) this->color_out[0] = hsh::float4(hsh::lerp(this->color_out[0], hsh::float4(0.f), fogZ).xyz(), this->color_out[0].w); else this->color_out[0] = hsh::float4(hsh::lerp(this->color_out[0], fragu->fog.m_color, fogZ).xyz(), this->color_out[0].w); } if (this->AlphaTest && this->color_out[0].w < 0.25f) hsh::discard(); } }; #if 0 template #endif hsh::binding& CModelShaders::SetCurrent(const CModelFlags& modelFlags, const CBooSurface& surface, const CBooModel& model) { const auto& material = model.x4_matSet->materials[surface.m_data.matIdx]; const auto& vtxFmt = model.m_vtxFmt; material.chunks m_dataBind.hsh_bind(CModelShadersPipeline()); return m_dataBind; } struct CCubeMaterial { const u8* x0_data; static u32 sReflectionType; static const CCubeModel* sLastModelCached; static const CCubeModel* sRenderingModel; static float gReflectionAlpha; #if 0 CCubeMaterialFlags flags; u32 texCount; // tex indices here CCubeMaterialVatFlags vatFlags; u32 groupIndex; u32 kColorCount; // kcolors here u16 destBlendFactor; u16 srcBlendFactor; u32 reflectionIndTexIdx; u32 colorChanCount; // cc here u32 tevStageCount; #endif void SetCurrentBlack() const { u32 texCount = *reinterpret_cast(x0_data + 4); CCubeMaterialFlags flags = *reinterpret_cast(x0_data); CCubeMaterialVatFlags vatFlags = *reinterpret_cast(x0_data + 8 + 4 * texCount); if (flags & (CCubeMaterialFlags(CCubeMaterialFlagBits::fDepthSorting) | CCubeMaterialFlags(CCubeMaterialFlagBits::fAlphaTest))) {} } static void EnsureViewDepStateCached(const CCubeSurface* surf) {} static void SetupBlendMode(u32 blendFactors, const CModelFlags& modelFlags, bool alphaTest) { u16 newSrcFactor = blendFactors & 0xffff; u16 newDstFactor = blendFactors >> 16 & 0xffff; if (alphaTest) { // GXSetAlphaCompare(6, 64, 1, 0, 0); // GXSetZCompLoc(0); newSrcFactor = 1; newDstFactor = 0; } else { // GXSetAlphaCompare(7, 0, 1, 7, 0); // GXSetZCompLoc(1); } if (modelFlags.x0_blendMode > 4) { if (newSrcFactor == 1) { newSrcFactor = 4; if (newDstFactor == 0) { newDstFactor = modelFlags.x0_blendMode > 6 ? 1 : 5; } } } // set fog color zero if dst blend zero // set blend factors } static void HandleDepth(u16 modelFlags, CCubeMaterialFlags materialFlags) { u32 compare = 0; if ((modelFlags & 0x1) == 0) { compare = 7; // ALWAYS } else { if ((modelFlags & 0x8) != 0) { compare = (modelFlags & 0x10) != 0 ? 4 : 6; // GREATER or GEQUAL } else { compare = (modelFlags & 0x10) != 0 ? 1 : 3; // LESS or LEQUAL } } bool depthWrite = ((modelFlags & 0x2) == 0x2 && materialFlags & CCubeMaterialFlagBits::fDepthWrite); } static u32 HandleColorChannels(u32 chanCount, u32 firstChan) { return 0; } static void HandleTev(u32 tevCur, const u8* materialDataCur, const u32* texMapTexCoordFlags, bool shadowMapsEnabled) { } static u32 HandleAnimatedUV(const u32* uvAnim, u32 texMtx, u32 pttTexMtx) { return 0; } static void HandleTransparency(u32& finalTevCount, u32& finalKColorCount, const CModelFlags& modelFlags, u32 blendFactors, u32& finalCCFlags, u32& finalACFlags) { if (modelFlags.x0_blendMode == 2) { u16 dstFactor = blendFactors >> 16 & 0xffff; if (dstFactor == 1) return; } if (modelFlags.x0_blendMode == 3) { // Stage outputting splatted KAlpha as color to reg0 // GXSetTevColorIn(finalTevCount, TEVCOLORARG_ZERO, TEVCOLORARG_ZERO, TEVCOLORARG_ZERO, TEVCOLORARG_KONST); // GXSetTevAlphaIn(finalTevCount, TEVALPHAARG_ZERO, TEVALPHAARG_ZERO, TEVALPHAARG_ZERO, TEVALPHAARG_APREV); // GXSetTevColorOp(finalTevCount, 0, 0, 0, 1, 1); // ColorReg0 // GXSetTevKColorSel(finalTevCount, finalKColorCount+28); // GXSetTevAlphaOp(finalTevCount, 0, 0, 0, 1, 0); // AlphaRegPrev // GXSetTevOrder(finalTevCount, 255, 255, 255); // GXSetTevDirect(finalTevCount); // Stage interpolating from splatted KAlpha using KColor // GXSetTevColorIn(finalTevCount + 1, TEVCOLORARG_CPREV, TEVCOLORARG_C0, TEVCOLORARG_KONST, TEVCOLORARG_ZERO); // GXSetTevAlphaIn(finalTevCount + 1, TEVALPHAARG_ZERO, TEVALPHAARG_ZERO, TEVALPHAARG_ZERO, TEVALPHAARG_APREV); // GXSetTevKColorSel(finalTevCount, finalKColorCount+12); // SetStandardTevColorAlphaOp(finalTevCount + 1); // GXSetTevDirect(finalTevCount + 1); // GXSetTevOrder(finalTevCount + 1, 255, 255, 255); // GXSetTevKColor(finalKColorCount, modelFlags.x4_color); finalKColorCount += 1; finalTevCount += 2; } else { // Mul KAlpha u32 tevAlpha = 0x000380C7; // TEVALPHAARG_ZERO, TEVALPHAARG_KONST, TEVALPHAARG_APREV, TEVALPHAARG_ZERO if (modelFlags.x0_blendMode == 8) { // Set KAlpha tevAlpha = 0x00031CE7; // TEVALPHAARG_ZERO, TEVALPHAARG_ZERO, TEVALPHAARG_ZERO, TEVALPHAARG_KONST } // Mul KColor u32 tevColor = 0x000781CF; // TEVCOLORARG_ZERO, TEVCOLORARG_KONST, TEVCOLORARG_CPREV, TEVCOLORARG_ZERO if (modelFlags.x0_blendMode == 2) { // Add KColor tevColor = 0x0007018F; // TEVCOLORARG_ZERO, TEVCOLORARG_ONE, TEVCOLORARG_CPREV, TEVCOLORARG_KONST } // GXSetTevColorIn(finalTevCount) // GXSetTevAlphaIn(finalTevCount) // SetStandardTevColorAlphaOp(finalTevCount); finalCCFlags = 0x100; // Just clamp, output prev reg finalACFlags = 0x100; // GXSetTevDirect(finalTevCount); // GXSetTevOrder(finalTevCount, 255, 255, 255); // GXSetTevKColor(finalKColorCount, modelFlags.x4_color); // GXSetTevKColorSel(finalTevCount, finalKColorCount+12); // GXSetTevKAlphaSel(finalTevCount, finalKColorCount+28); finalTevCount += 1; finalKColorCount += 1; } } static u32 HandleReflection(bool usesTevReg2, u32 indTexSlot, u32 r5, u32 finalTevCount, u32 texCount, u32 tcgCount, u32 finalKColorCount, u32& finalCCFlags, u32& finalACFlags) { return 0; } static void DoModelShadow(u32 texCount, u32 tcgCount) {} static void DoPassthru(u32 finalTevCount) {} void SetCurrent(const CModelFlags& modelFlags, const CCubeSurface& surface, const CCubeModel& model) const { if (sLastMaterialCached == x0_data) { switch (sReflectionType) { case 2: break; default: return; case 1: if (sLastModelCached == sRenderingModel) return; break; } } if (sRenderModelBlack) { SetCurrentBlack(); return; } u32 numIndStages = 0; CCubeMaterialFlags flags = *reinterpret_cast(x0_data); const u8* materialDataCur = x0_data; bool reflection = bool(flags & (CCubeMaterialFlags(CCubeMaterialFlagBits::fSamusReflectionSurfaceEye) | CCubeMaterialFlags(CCubeMaterialFlagBits::fSamusReflection))); if (reflection) { sLastMaterialCached = x0_data; sRenderingModel = &model; EnsureViewDepStateCached(flags & CCubeMaterialFlagBits::fSamusReflectionSurfaceEye ? &surface : nullptr); } sRenderingModel = &model; sLastMaterialCached = x0_data; u32 texCount = *reinterpret_cast(materialDataCur + 4); if ((modelFlags.x2_flags & 0x4) == 0) { materialDataCur += 8; for (u32 i = 0; i < texCount; ++i) { u32 texIdx = *reinterpret_cast(materialDataCur); sRenderingModel->x1c_texs[texIdx].x8_tex->Load(i, CTexture::EClampMode::One); materialDataCur += 4; } } else { materialDataCur += (2 + texCount) * 4; } u32 groupIdx = *reinterpret_cast(materialDataCur + 4); if (sLastMaterialUnique != UINT32_MAX && groupIdx == UINT32_MAX && sReflectionType == 0) return; sLastMaterialUnique = groupIdx; CCubeMaterialVatFlags vatFlags = *reinterpret_cast(materialDataCur); // SetVtxDescv_Compressed(vatFlags); materialDataCur += 8; if (bool(flags & CCubeMaterialFlagBits::fLightmapUvArray) != CCubeModel::sUsingPackedLightmaps) { model.SetUsingPackedLightmaps(); } u32 finalKColorCount = 0; if (flags & CCubeMaterialFlagBits::fKonstValues) { u32 konstCount = *reinterpret_cast(materialDataCur); finalKColorCount = konstCount; materialDataCur += 4; for (u32 i = 0; i < konstCount; ++i) { u32 kColor = *reinterpret_cast(materialDataCur); materialDataCur += 4; // Set KColor } } u32 blendFactors = *reinterpret_cast(materialDataCur); materialDataCur += 4; if (g_Renderer->IsInAreaDraw()) { // Blackout fog, additive blend } else { SetupBlendMode(blendFactors, modelFlags, bool(flags & CCubeMaterialFlagBits::fAlphaTest)); } bool indTex = bool(flags & CCubeMaterialFlagBits::fSamusReflectionIndirectTexture); u32 indTexSlot = 0; if (indTex) { indTexSlot = *reinterpret_cast(materialDataCur); materialDataCur += 4; } HandleDepth(modelFlags.x2_flags, flags); u32 chanCount = *reinterpret_cast(materialDataCur); materialDataCur += 4; u32 firstChan = *reinterpret_cast(materialDataCur); materialDataCur += 4 * chanCount; u32 finalNumColorChans = HandleColorChannels(chanCount, firstChan); u32 firstTev = 0; if (kShadowMapsEnabled) firstTev = 2; u32 matTevCount = *reinterpret_cast(materialDataCur); materialDataCur += 4; u32 finalTevCount = matTevCount; const u32* texMapTexCoordFlags = reinterpret_cast(materialDataCur + matTevCount * 20); const u32* tcgs = reinterpret_cast(texMapTexCoordFlags + matTevCount); bool usesTevReg2 = false; u32 finalCCFlags = 0; u32 finalACFlags = 0; if (g_Renderer->IsThermalVisorActive()) { finalTevCount = firstTev + 1; u32 ccFlags = *reinterpret_cast(materialDataCur + 8); finalCCFlags = ccFlags; u32 outputReg = ccFlags >> 9 & 0x3; if (outputReg == 1) { // TevReg0 materialDataCur += 20; texMapTexCoordFlags += 1; finalCCFlags = *reinterpret_cast(materialDataCur + 8); // Set TevReg0 = 0xc0c0c0c0 } finalACFlags = *reinterpret_cast(materialDataCur + 12); HandleTev(firstTev, materialDataCur, texMapTexCoordFlags, kShadowMapsEnabled); usesTevReg2 = false; } else { finalTevCount = firstTev + matTevCount; for (u32 i = firstTev; i < finalTevCount; ++i) { HandleTev(i, materialDataCur, texMapTexCoordFlags, kShadowMapsEnabled && i == firstTev); u32 ccFlags = *reinterpret_cast(materialDataCur + 8); finalCCFlags = ccFlags; finalACFlags = *reinterpret_cast(materialDataCur + 12); u32 outputReg = ccFlags >> 9 & 0x3; if (outputReg == 3) { // TevReg2 usesTevReg2 = true; } materialDataCur += 20; texMapTexCoordFlags += 1; } } u32 tcgCount = 0; if (g_Renderer->IsThermalVisorActive()) { u32 fullTcgCount = *tcgs; tcgCount = std::min(fullTcgCount, 2u); for (u32 i = 0; i < tcgCount; ++i) { // Set TCG } tcgs += fullTcgCount + 1; } else { tcgCount = *tcgs; for (u32 i = 0; i < tcgCount; ++i) { // Set TCG } tcgs += tcgCount + 1; } const u32* uvAnim = tcgs; u32 animCount = uvAnim[1]; uvAnim += 2; u32 texMtx = 30; u32 pttTexMtx = 64; for (u32 i = 0; i < animCount; ++i) { u32 size = HandleAnimatedUV(uvAnim, texMtx, pttTexMtx); if (size == 0) break; uvAnim += size; texMtx += 3; pttTexMtx += 3; } if (modelFlags.x0_blendMode) { HandleTransparency(finalTevCount, finalKColorCount, modelFlags, blendFactors, finalCCFlags, finalACFlags); } if (reflection) { if (gReflectionAlpha > 0.f) { u32 additionalTevs = 0; if (indTex) { additionalTevs = HandleReflection(usesTevReg2, indTexSlot, 0, finalTevCount, texCount, tcgCount, finalKColorCount, finalCCFlags, finalACFlags); numIndStages = 1; tcgCount += 2; } else { additionalTevs = HandleReflection(usesTevReg2, 255, 0, finalTevCount, texCount, tcgCount, finalKColorCount, finalCCFlags, finalACFlags); tcgCount += 1; } texCount += 1; finalTevCount += additionalTevs; finalKColorCount += 1; } else if (((finalCCFlags >> 9) & 0x3) != 0) { DoPassthru(finalTevCount); finalTevCount += 1; } } if (kShadowMapsEnabled) { DoModelShadow(texCount, tcgCount); tcgCount += 1; } // SetNumIndStages(numIndStages); // SetNumTevStages(finalTevCount); // SetNumTexGens(tcgCount); // SetNumColorChans(finalNumColorChans); } }; } // namespace urde