Implement CWorldShadow and texture clamp mode

This commit is contained in:
Jack Andersen 2017-09-30 18:26:46 -10:00
parent 484a4900a0
commit 30ae347420
38 changed files with 915 additions and 110 deletions

View File

@ -33,7 +33,7 @@ boo::GraphicsDataToken InitializeBadging(specter::ViewResources& viewRes)
specter::IconAtlas<1, 1> atlas;
atlas.initializeAtlas(ctx.newStaticTexture(width, height, mips,
boo::TextureFormat::RGBA8,
boo::TextureFormat::RGBA8, boo::TextureClampMode::Repeat,
texels.get(), destSz));
g_BadgeIcon = atlas.getIcon(0, 0);
return true;

View File

@ -32,6 +32,7 @@ boo::GraphicsDataToken InitializeIcons(specter::ViewResources& viewRes)
{
g_IconAtlas.initializeAtlas(ctx.newStaticTexture(width, height, mips,
boo::TextureFormat::RGBA8,
boo::TextureClampMode::Repeat,
texels.get(), destSz));
return true;
});

View File

@ -694,7 +694,7 @@ void CStateManager::DrawWorld() const
{
const CGameArea& area = *areaArr[i];
SetupFogForArea(area);
g_Renderer->EnablePVS(&pvsArr[i], area.x4_selfIdx);
g_Renderer->EnablePVS(pvsArr[i], area.x4_selfIdx);
g_Renderer->SetWorldLightFadeLevel(area.GetPostConstructed()->x1128_worldLightingLevel);
g_Renderer->DrawUnsortedGeometry(area.x4_selfIdx, mask, targetMask);
}
@ -767,7 +767,7 @@ void CStateManager::DrawWorld() const
if (xf7c_projectedShadow)
xf7c_projectedShadow->Render(*this);
g_Renderer->EnablePVS(&pvs, area.x4_selfIdx);
g_Renderer->EnablePVS(pvs, area.x4_selfIdx);
g_Renderer->DrawSortedGeometry(area.x4_selfIdx, mask, targetMask);
}
@ -801,7 +801,7 @@ void CStateManager::DrawWorld() const
const CGameArea& area = *areaArr[i];
CPVSVisSet& pvs = pvsArr[i];
g_Renderer->EnablePVS(&pvs, area.x4_selfIdx);
g_Renderer->EnablePVS(pvs, area.x4_selfIdx);
g_Renderer->DrawUnsortedGeometry(area.x4_selfIdx, mask, 0x20);
g_Renderer->DrawAreaGeometry(area.x4_selfIdx, mask, 0x10);
}
@ -832,7 +832,7 @@ void CStateManager::DrawWorld() const
++const_cast<CStateManager&>(*this).x8dc_objectDrawToken;
g_Renderer->EnablePVS(&pvs, area.x4_selfIdx);
g_Renderer->EnablePVS(pvs, area.x4_selfIdx);
g_Renderer->DrawSortedGeometry(area.x4_selfIdx, mask, 0x10);
}

View File

@ -523,6 +523,12 @@ std::vector<CLight> CActorLights::BuildLightVector() const
{
lights.push_back(*it);
}
if (x29c_shadowLightArrIdx > 0)
{
/* Ensure shadow light comes first for shader extension */
std::swap(lights[0], lights[x29c_shadowLightArrIdx]);
}
}
for (const CLight& light : x144_dynamicLights)

View File

@ -589,7 +589,7 @@ void CBooRenderer::GenerateFogVolumeRampTex(boo::IGraphicsDataFactory::Context&
}
}
x1b8_fogVolumeRamp = ctx.newStaticTexture(FOGVOL_RAMP_RES, FOGVOL_RAMP_RES, 1,
boo::TextureFormat::I8, data[0],
boo::TextureFormat::I8, boo::TextureClampMode::Repeat, data[0],
FOGVOL_RAMP_RES * FOGVOL_RAMP_RES);
}
@ -606,7 +606,7 @@ void CBooRenderer::GenerateSphereRampTex(boo::IGraphicsDataFactory::Context& ctx
}
}
x220_sphereRamp = ctx.newStaticTexture(SPHERE_RAMP_RES, SPHERE_RAMP_RES, 1,
boo::TextureFormat::I8, data[0],
boo::TextureFormat::I8, boo::TextureClampMode::Repeat, data[0],
SPHERE_RAMP_RES * SPHERE_RAMP_RES);
}
@ -676,7 +676,7 @@ CBooRenderer::CBooRenderer(IObjectStore& store, IFactory& resFac)
{
GenerateFogVolumeRampTex(ctx);
GenerateSphereRampTex(ctx);
m_ballShadowId = ctx.newRenderTexture(m_ballShadowIdW, m_ballShadowIdH, true, false);
m_ballShadowId = ctx.newRenderTexture(m_ballShadowIdW, m_ballShadowIdH, boo::TextureClampMode::Repeat, 1, 0);
GenerateScanLinesVBO(ctx);
return true;
});
@ -734,9 +734,9 @@ void CBooRenderer::AddStaticGeometry(const std::vector<CMetroidModelInstance>* g
}
}
void CBooRenderer::EnablePVS(const CPVSVisSet* set, u32 areaIdx)
void CBooRenderer::EnablePVS(const CPVSVisSet& set, u32 areaIdx)
{
xc8_pvs.emplace(*set);
xc8_pvs.emplace(set);
xe0_pvsAreaIdx = areaIdx;
}

View File

@ -205,7 +205,7 @@ public:
std::list<CAreaListItem>::iterator FindStaticGeometry(const std::vector<CMetroidModelInstance>*);
void AddStaticGeometry(const std::vector<CMetroidModelInstance>*, const CAreaRenderOctTree*, int areaIdx);
void EnablePVS(const CPVSVisSet*, u32);
void EnablePVS(const CPVSVisSet&, u32);
void DisablePVS();
void RemoveStaticGeometry(const std::vector<CMetroidModelInstance>*);
void DrawAreaGeometry(int areaIdx, int mask, int targetMask);
@ -279,7 +279,7 @@ public:
boo::IGraphicsBuffer* GetScanLinesEvenVBO() const {return m_scanLinesEvenVBO;}
boo::IGraphicsBuffer* GetScanLinesOddVBO() const {return m_scanLinesOddVBO;}
void BindMainDrawTarget() {CGraphics::g_BooMainCommandQueue->setRenderTarget(CGraphics::g_SpareTexture);}
static void BindMainDrawTarget() {CGraphics::g_BooMainCommandQueue->setRenderTarget(CGraphics::g_SpareTexture);}
void BindReflectionDrawTarget() {CGraphics::g_BooMainCommandQueue->setRenderTarget(x14c_reflectionTex);}
void BindBallShadowIdTarget()
{

View File

@ -21,7 +21,8 @@ if(WIN32)
Shaders/CElementGenShadersHLSL.cpp
Shaders/CParticleSwooshShadersHLSL.cpp
Shaders/CFluidPlaneShaderHLSL.cpp
Shaders/CAABoxShaderHLSL.cpp)
Shaders/CAABoxShaderHLSL.cpp
Shaders/CWorldShadowShaderHLSL.cpp)
elseif(BOO_HAS_METAL)
set(PLAT_SRCS
Shaders/CLineRendererShadersMetal.cpp
@ -45,7 +46,8 @@ elseif(BOO_HAS_METAL)
Shaders/CElementGenShadersMetal.cpp
Shaders/CParticleSwooshShadersMetal.cpp
Shaders/CFluidPlaneShaderMetal.cpp
Shaders/CAABoxShaderMetal.cpp)
Shaders/CAABoxShaderMetal.cpp
Shaders/CWorldShadowShaderMetal.cpp)
endif()
set(GRAPHICS_SOURCES
@ -92,6 +94,7 @@ set(GRAPHICS_SOURCES
Shaders/CParticleSwooshShaders.hpp Shaders/CParticleSwooshShaders.cpp Shaders/CParticleSwooshShadersGLSL.cpp
Shaders/CFluidPlaneShader.hpp Shaders/CFluidPlaneShader.cpp Shaders/CFluidPlaneShaderGLSL.cpp
Shaders/CAABoxShader.hpp Shaders/CAABoxShader.cpp Shaders/CAABoxShaderGLSL.cpp
Shaders/CWorldShadowShader.hpp Shaders/CWorldShadowShader.cpp Shaders/CWorldShadowShaderGLSL.cpp
${PLAT_SRCS})
runtime_add_list(Graphics GRAPHICS_SOURCES)

View File

@ -144,6 +144,8 @@ private:
boo::ITexture* m_txtrOverrides[8] = {};
boo::ITexture* m_lastDrawnShadowMap = nullptr;
ModelInstance* PushNewModelInstance();
void DrawAlphaSurfaces(const CModelFlags& flags) const;
void DrawNormalSurfaces(const CModelFlags& flags) const;
@ -209,6 +211,18 @@ public:
static void KillCachedViewDepState();
static void EnsureViewDepStateCached(const CBooModel& model, const CBooSurface* surf,
zeus::CMatrix4f* mtxsOut, float& alphaOut);
static boo::ITexture* g_shadowMap;
static zeus::CTransform g_shadowTexXf;
static void EnableShadowMaps(boo::ITexture* map, const zeus::CTransform& texXf)
{
g_shadowMap = map;
g_shadowTexXf = texXf;
}
static void DisableShadowMaps()
{
g_shadowMap = nullptr;
}
};
class CModel

View File

@ -123,6 +123,9 @@ void CBooModel::EnsureViewDepStateCached(const CBooModel& model, const CBooSurfa
}
}
boo::ITexture* CBooModel::g_shadowMap = nullptr;
zeus::CTransform CBooModel::g_shadowTexXf;
CBooModel::~CBooModel()
{
if (m_prev)
@ -334,7 +337,6 @@ CBooModel::ModelInstance* CBooModel::PushNewModelInstance()
texs[texCount++] = tex.GetObj()->GetBooTexture();
}
}
texs[7] = g_Renderer->x220_sphereRamp;
if (skinBankCount)
{
@ -380,6 +382,7 @@ CBooModel::ModelInstance* CBooModel::PushNewModelInstance()
if (idx == EExtendedShader::Thermal)
{
texCount = 8;
texs[7] = g_Renderer->x220_sphereRamp;
ltexs = texs;
}
else if (idx == EExtendedShader::MorphBallShadow)
@ -387,6 +390,12 @@ CBooModel::ModelInstance* CBooModel::PushNewModelInstance()
texCount = 3;
ltexs = mbShadowTexs;
}
else if (idx == EExtendedShader::WorldShadow)
{
texCount = 8;
texs[7] = g_shadowMap;
ltexs = texs;
}
else if (useReflection)
{
texCount = mat.textureIdxs.size() + 1;
@ -698,31 +707,47 @@ void CBooModel::UVAnimationBuffer::Update(u8*& bufOut, const MaterialSet* matSet
return;
}
/* Special Mode0 matrix for exclusive Thermal Visor use */
std::experimental::optional<std::array<zeus::CMatrix4f, 2>> thermalMtxOut;
std::experimental::optional<std::array<zeus::CMatrix4f, 2>> specialMtxOut;
if (flags.m_extendedShader == EExtendedShader::Thermal)
{
thermalMtxOut.emplace();
/* Special Mode0 matrix for exclusive Thermal Visor use */
specialMtxOut.emplace();
zeus::CMatrix4f& texMtxOut = (*thermalMtxOut)[0];
zeus::CMatrix4f& texMtxOut = (*specialMtxOut)[0];
texMtxOut = CGraphics::g_GXModelViewInvXpose.toMatrix4f();
texMtxOut.vec[3].zeroOut();
texMtxOut.vec[3].w = 1.f;
zeus::CMatrix4f& postMtxOut = (*thermalMtxOut)[1];
zeus::CMatrix4f& postMtxOut = (*specialMtxOut)[1];
postMtxOut.vec[0].x = 0.5f;
postMtxOut.vec[1].y = 0.5f;
postMtxOut.vec[3].x = 0.5f;
postMtxOut.vec[3].y = 0.5f;
}
else if (flags.m_extendedShader == EExtendedShader::WorldShadow)
{
/* Special matrix for mapping world shadow */
specialMtxOut.emplace();
zeus::CMatrix4f mat = g_shadowTexXf.toMatrix4f();
zeus::CMatrix4f& texMtxOut = (*specialMtxOut)[0];
texMtxOut[0][0] = mat[0][0];
texMtxOut[1][0] = mat[1][0];
texMtxOut[2][0] = mat[2][0];
texMtxOut[3][0] = mat[3][0];
texMtxOut[0][1] = mat[0][2];
texMtxOut[1][1] = mat[1][2];
texMtxOut[2][1] = mat[2][2];
texMtxOut[3][1] = mat[3][2];
}
for (const MaterialSet::Material& mat : matSet->materials)
{
if (thermalMtxOut)
if (specialMtxOut)
{
std::array<zeus::CMatrix4f, 2>* mtxs = reinterpret_cast<std::array<zeus::CMatrix4f, 2>*>(bufOut);
mtxs[7][0] = (*thermalMtxOut)[0];
mtxs[7][1] = (*thermalMtxOut)[1];
mtxs[7][0] = (*specialMtxOut)[0];
mtxs[7][1] = (*specialMtxOut)[1];
}
u8* bufOrig = bufOut;
for (const UVAnimation& anim : mat.uvAnims)
@ -744,6 +769,14 @@ boo::IGraphicsBufferD* CBooModel::UpdateUniformData(const CModelFlags& flags,
weightVecCount = model->m_hmdlMeta.weightCount;
}
/* Invalidate instances if new shadow being drawn */
if (flags.m_extendedShader == EExtendedShader::WorldShadow &&
m_lastDrawnShadowMap != g_shadowMap)
{
const_cast<CBooModel*>(this)->m_lastDrawnShadowMap = g_shadowMap;
const_cast<CBooModel*>(this)->m_instances.clear();
}
const ModelInstance* inst;
if (m_instances.size() <= m_uniUpdateCount)
{

View File

@ -449,18 +449,14 @@ CMoviePlayer::CMoviePlayer(const char* path, float preLoadSeconds, bool loop, bo
if (deinterlace)
{
/* urde addition: this way interlaced THPs don't look horrible */
set.Y[0] = ctx.newDynamicTexture(x6c_videoInfo.width,
x6c_videoInfo.height / 2,
boo::TextureFormat::I8);
set.Y[1] = ctx.newDynamicTexture(x6c_videoInfo.width,
x6c_videoInfo.height / 2,
boo::TextureFormat::I8);
set.U = ctx.newDynamicTexture(x6c_videoInfo.width / 2,
x6c_videoInfo.height / 2,
boo::TextureFormat::I8);
set.V = ctx.newDynamicTexture(x6c_videoInfo.width / 2,
x6c_videoInfo.height / 2,
boo::TextureFormat::I8);
set.Y[0] = ctx.newDynamicTexture(x6c_videoInfo.width, x6c_videoInfo.height / 2,
boo::TextureFormat::I8, boo::TextureClampMode::Repeat);
set.Y[1] = ctx.newDynamicTexture(x6c_videoInfo.width, x6c_videoInfo.height / 2,
boo::TextureFormat::I8, boo::TextureClampMode::Repeat);
set.U = ctx.newDynamicTexture(x6c_videoInfo.width / 2, x6c_videoInfo.height / 2,
boo::TextureFormat::I8, boo::TextureClampMode::Repeat);
set.V = ctx.newDynamicTexture(x6c_videoInfo.width / 2, x6c_videoInfo.height / 2,
boo::TextureFormat::I8, boo::TextureClampMode::Repeat);
boo::IGraphicsBuffer* bufs[] = {m_blockBuf};
for (int j=0 ; j<2 ; ++j)
@ -474,15 +470,12 @@ CMoviePlayer::CMoviePlayer(const char* path, float preLoadSeconds, bool loop, bo
else
{
/* normal progressive presentation */
set.Y[0] = ctx.newDynamicTexture(x6c_videoInfo.width,
x6c_videoInfo.height,
boo::TextureFormat::I8);
set.U = ctx.newDynamicTexture(x6c_videoInfo.width / 2,
x6c_videoInfo.height / 2,
boo::TextureFormat::I8);
set.V = ctx.newDynamicTexture(x6c_videoInfo.width / 2,
x6c_videoInfo.height / 2,
boo::TextureFormat::I8);
set.Y[0] = ctx.newDynamicTexture(x6c_videoInfo.width, x6c_videoInfo.height,
boo::TextureFormat::I8, boo::TextureClampMode::Repeat);
set.U = ctx.newDynamicTexture(x6c_videoInfo.width / 2, x6c_videoInfo.height / 2,
boo::TextureFormat::I8, boo::TextureClampMode::Repeat);
set.V = ctx.newDynamicTexture(x6c_videoInfo.width / 2, x6c_videoInfo.height / 2,
boo::TextureFormat::I8, boo::TextureClampMode::Repeat);
boo::IGraphicsBuffer* bufs[] = {m_blockBuf};
boo::ITexture* texs[] = {set.Y[0], set.U, set.V};

View File

@ -19,6 +19,14 @@ class CPVSAreaSet
const u8* x1c_lightLeaves;
CPVSVisOctree x20_octree;
CPVSVisSet _GetLightSet(u32 lightIdx) const
{
CPVSVisSet ret;
ret.SetFromMemory(x20_octree.GetTotalBits(), x20_octree.GetLightBits(),
x1c_lightLeaves + x10_leafSize * lightIdx);
return ret;
}
public:
CPVSAreaSet(const u8* data, u32 len);
u32 GetNumFeatures() const { return x0_numFeatures; }
@ -28,6 +36,8 @@ public:
bool Has2ndLayerLights() const { return x8_num2ndLights != 0; }
u32 GetEntityIdByIndex(int idx) const { return x18_entityIndex[idx]; }
const CPVSVisOctree& GetVisOctree() const { return x20_octree; }
CPVSVisSet Get1stLightSet(u32 lightIdx) const { return _GetLightSet(x8_num2ndLights + lightIdx); }
CPVSVisSet Get2ndLightSet(u32 lightIdx) const { return _GetLightSet(lightIdx); }
};
}

View File

@ -115,7 +115,7 @@ void CTexture::BuildI4FromGCN(CInputStream& in)
m_booToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::RGBA8,
buf.get(), texelCount * 4);
boo::TextureClampMode::Repeat, buf.get(), texelCount * 4);
return true;
});
}
@ -163,7 +163,7 @@ void CTexture::BuildI8FromGCN(CInputStream& in)
m_booToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::RGBA8,
buf.get(), texelCount * 4);
boo::TextureClampMode::Repeat, buf.get(), texelCount * 4);
return true;
});
}
@ -212,7 +212,7 @@ void CTexture::BuildIA4FromGCN(CInputStream& in)
m_booToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::RGBA8,
buf.get(), texelCount * 4);
boo::TextureClampMode::Repeat, buf.get(), texelCount * 4);
return true;
});
}
@ -261,7 +261,7 @@ void CTexture::BuildIA8FromGCN(CInputStream& in)
m_booToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::RGBA8,
buf.get(), texelCount * 4);
boo::TextureClampMode::Repeat, buf.get(), texelCount * 4);
return true;
});
}
@ -369,7 +369,7 @@ void CTexture::BuildC4FromGCN(CInputStream& in)
m_booToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::RGBA8,
buf.get(), texelCount * 4);
boo::TextureClampMode::Repeat, buf.get(), texelCount * 4);
return true;
});
}
@ -413,7 +413,7 @@ void CTexture::BuildC8FromGCN(CInputStream& in)
m_booToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::RGBA8,
buf.get(), texelCount * 4);
boo::TextureClampMode::Repeat, buf.get(), texelCount * 4);
return true;
});
}
@ -465,7 +465,7 @@ void CTexture::BuildRGB565FromGCN(CInputStream& in)
m_booToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::RGBA8,
buf.get(), texelCount * 4);
boo::TextureClampMode::Repeat, buf.get(), texelCount * 4);
return true;
});
}
@ -522,7 +522,7 @@ void CTexture::BuildRGB5A3FromGCN(CInputStream& in)
m_booToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::RGBA8,
buf.get(), texelCount * 4);
boo::TextureClampMode::Repeat, buf.get(), texelCount * 4);
return true;
});
}
@ -579,7 +579,7 @@ void CTexture::BuildRGBA8FromGCN(CInputStream& in)
m_booToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::RGBA8,
buf.get(), texelCount * 4);
boo::TextureClampMode::Repeat, buf.get(), texelCount * 4);
return true;
});
}
@ -643,7 +643,7 @@ void CTexture::BuildDXT1FromGCN(CInputStream& in)
m_booToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::DXT1,
buf.get(), blockCount * 8);
boo::TextureClampMode::Repeat, buf.get(), blockCount * 8);
return true;
});
}
@ -659,7 +659,7 @@ void CTexture::BuildRGBA8(const void* data, size_t length)
m_booToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::RGBA8,
data, expectedSize);
boo::TextureClampMode::Repeat, data, expectedSize);
return true;
});
}
@ -677,9 +677,9 @@ void CTexture::BuildC8(const void* data, size_t length)
const u8* paletteTexels = reinterpret_cast<const u8*>(data) + 4;
const u8* texels = reinterpret_cast<const u8*>(data) + 4 + nentries * 4;
m_paletteTex = ctx.newStaticTexture(nentries, 1, 1, boo::TextureFormat::RGBA8,
paletteTexels, nentries * 4);
boo::TextureClampMode::Repeat, paletteTexels, nentries * 4);
m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::I8,
texels, texelCount);
boo::TextureClampMode::Repeat, texels, texelCount);
return true;
});
}
@ -781,7 +781,7 @@ void CTexture::BuildC8Font(const void* data, EFontType ftype)
m_booToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_booTex = ctx.newStaticArrayTexture(x4_w, x6_h, layerCount, x8_mips, boo::TextureFormat::RGBA8,
buf.get(), texelCount * layerCount * 4);
boo::TextureClampMode::Repeat, buf.get(), texelCount * layerCount * 4);
return true;
});
}

View File

@ -45,7 +45,7 @@ public:
virtual ~IRenderer() = default;
virtual void AddStaticGeometry(const std::vector<CMetroidModelInstance>*, const CAreaRenderOctTree*, int)=0;
virtual void EnablePVS(const CPVSVisSet*, u32)=0;
virtual void EnablePVS(const CPVSVisSet&, u32)=0;
virtual void DisablePVS()=0;
virtual void RemoveStaticGeometry(const std::vector<CMetroidModelInstance>*)=0;
virtual void DrawAreaGeometry(int areaIdx, int mask, int targetMask)=0;

View File

@ -1,6 +1,4 @@
#include "CColoredQuadFilter.hpp"
#include "TMultiBlendShader.hpp"
#include "Graphics/CTexture.hpp"
namespace urde
{

View File

@ -1,6 +1,4 @@
#include "CColoredQuadFilter.hpp"
#include "TMultiBlendShader.hpp"
#include "Graphics/CTexture.hpp"
namespace urde
{

View File

@ -1,6 +1,4 @@
#include "CColoredQuadFilter.hpp"
#include "TMultiBlendShader.hpp"
#include "Graphics/CTexture.hpp"
namespace urde
{

View File

@ -87,6 +87,11 @@ const hecl::Backend::TextureInfo CModelShaders::BallFadeTextures[] =
{hecl::Backend::TexGenSrc::Position, 2, 0, 1, false} // TXTR_BallFade
};
const hecl::Backend::TextureInfo CModelShaders::WorldShadowTextures[] =
{
{hecl::Backend::TexGenSrc::Position, 7, 0, 7, false} // Shadow tex
};
CModelShaders::CModelShaders(const hecl::Runtime::FileStoreManager& storeMgr,
boo::IGraphicsDataFactory* gfxFactory)
: m_shaderCache(storeMgr, gfxFactory, GetShaderExtensions(gfxFactory->platform())) {}

View File

@ -25,7 +25,8 @@ enum EExtendedShader : uint8_t
SolidColorFrontfaceCullAlwaysAlphaOnly, // No Z-write or test
SolidColorBackfaceCullLEqualAlphaOnly,
SolidColorBackfaceCullGreaterAlphaOnly, // No Z-write
MorphBallShadow
MorphBallShadow,
WorldShadow
};
class CModelShaders
@ -39,6 +40,7 @@ class CModelShaders
static hecl::Runtime::ShaderCacheExtensions GetShaderExtensionsMetal(boo::IGraphicsDataFactory::Platform plat);
static const hecl::Backend::TextureInfo ThermalTextures[];
static const hecl::Backend::TextureInfo BallFadeTextures[];
static const hecl::Backend::TextureInfo WorldShadowTextures[];
public:
struct Light
{

View File

@ -52,6 +52,66 @@ static const char* LightingGLSL =
" return clamp(ret, vec4(0.0,0.0,0.0,0.0), vec4(1.0,1.0,1.0,1.0));\n"
"}\n";
static const char* LightingShadowGLSL =
"struct Light\n"
"{\n"
" vec4 pos;\n"
" vec4 dir;\n"
" vec4 color;\n"
" vec4 linAtt;\n"
" vec4 angAtt;\n"
"};\n"
"struct Fog\n"
"{\n"
" vec4 color;\n"
" float rangeScale;\n"
" float start;\n"
"};\n"
"\n"
"UBINDING2 uniform LightingUniform\n"
"{\n"
" Light lights[" _XSTR(URDE_MAX_LIGHTS) "];\n"
" vec4 ambient;\n"
" vec4 colorReg0;\n"
" vec4 colorReg1;\n"
" vec4 colorReg2;\n"
" vec4 mulColor;\n"
" Fog fog;\n"
"};\n"
"\n"
"vec4 LightingShadowFunc(vec4 mvPosIn, vec4 mvNormIn)\n"
"{\n"
" vec4 ret = ambient;\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"
" texture(extTex0, vtf.extTcgs[0]).r;\n"
" \n"
" for (int i=1 ; 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";
static const char* MainPostGLSL =
"vec4 MainPostFunc(vec4 colorIn)\n"
"{\n"
@ -190,6 +250,20 @@ CModelShaders::GetShaderExtensionsGLSL(boo::IGraphicsDataFactory::Platform plat)
hecl::Backend::ZTest::Equal,
false, false, false, true);
/* MorphBall shadow shading */
ext.registerExtensionSlot({}, {MBShadowPostGLSL, "MBShadowPostFunc"},
3, MBShadowBlockNames, 3, BallFadeTextures,
hecl::Backend::BlendFactor::SrcAlpha,
hecl::Backend::BlendFactor::InvSrcAlpha,
hecl::Backend::ZTest::Equal,
false, false, false, true);
/* World shadow shading (modified lighting) */
ext.registerExtensionSlot({LightingShadowGLSL, "LightingShadowFunc"}, {MainPostGLSL, "MainPostFunc"},
3, BlockNames, 1, WorldShadowTextures, hecl::Backend::BlendFactor::Original,
hecl::Backend::BlendFactor::Original, hecl::Backend::ZTest::Original,
false, false, false, true);
return ext;
}

View File

@ -30,7 +30,7 @@ static const char* LightingHLSL =
" Fog fog;\n"
"};\n"
"\n"
"static float4 LightingFunc(float4 mvPosIn, float4 mvNormIn)\n"
"static float4 LightingFunc(float4 mvPosIn, float4 mvNormIn, in VertToFrag vtf)\n"
"{\n"
" float4 ret = ambient;\n"
" \n"
@ -51,6 +51,66 @@ static const char* LightingHLSL =
" return saturate(ret);\n"
"}\n";
static const char* LightingShadowHLSL =
"struct Light\n"
"{\n"
" float4 pos;\n"
" float4 dir;\n"
" float4 color;\n"
" float4 linAtt;\n"
" float4 angAtt;\n"
"};\n"
"struct Fog\n"
"{\n"
" float4 color;\n"
" float rangeScale;\n"
" float start;\n"
"};\n"
"\n"
"cbuffer LightingUniform : register(b2)\n"
"{\n"
" Light lights[" _XSTR(URDE_MAX_LIGHTS) "];\n"
" float4 ambient;\n"
" float4 colorReg0;\n"
" float4 colorReg1;\n"
" float4 colorReg2;\n"
" float4 mulColor;\n"
" Fog fog;\n"
"};\n"
"\n"
"static float4 LightingShadowFunc(float4 mvPosIn, float4 mvNormIn, in VertToFrag vtf)\n"
"{\n"
" float4 ret = ambient;\n"
" \n"
" float3 delta = mvPosIn.xyz - lights[i].pos.xyz;\n"
" float dist = length(delta);\n"
" float angDot = saturate(dot(normalize(delta), lights[i].dir.xyz));\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 * saturate(angAtt) * att * saturate(dot(normalize(-delta), mvNormIn.xyz)) *\n"
" extTex0.Sample(clampSamp, vtf.extTcgs[0]).r;\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 = saturate(dot(normalize(delta), lights[i].dir.xyz));\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 * saturate(angAtt) * att * saturate(dot(normalize(-delta), mvNormIn.xyz));\n"
" }\n"
" \n"
" return saturate(ret);\n"
"}\n";
static const char* MainPostHLSL =
"static float4 MainPostFunc(in VertToFrag vtf, float4 colorIn)\n"
"{\n"
@ -173,6 +233,12 @@ CModelShaders::GetShaderExtensionsHLSL(boo::IGraphicsDataFactory::Platform plat)
hecl::Backend::ZTest::Equal,
false, false, false, true);
/* World shadow shading (modified lighting) */
ext.registerExtensionSlot({LightingShadowHLSL, "LightingShadowFunc"}, {MainPostHLSL, "MainPostFunc"},
0, nullptr, 1, WorldShadowTextures, hecl::Backend::BlendFactor::Original,
hecl::Backend::BlendFactor::Original, hecl::Backend::ZTest::Original,
false, false, false, true);
return ext;
}

View File

@ -30,7 +30,7 @@ static const char* LightingMetal =
" Fog fog;\n"
"};\n"
"\n"
"static float4 LightingFunc(constant LightingUniform& lu, float4 mvPosIn, float4 mvNormIn)\n"
"static float4 LightingFunc(constant LightingUniform& lu, float4 mvPosIn, float4 mvNormIn, thread VertToFrag& vtf)\n"
"{\n"
" float4 ret = lu.ambient;\n"
" \n"
@ -51,6 +51,67 @@ static const char* LightingMetal =
" return saturate(ret);\n"
"}\n";
static const char* LightingShadowMetal =
"struct Light\n"
"{\n"
" float4 pos;\n"
" float4 dir;\n"
" float4 color;\n"
" float4 linAtt;\n"
" float4 angAtt;\n"
"};\n"
"struct Fog\n"
"{\n"
" float4 color;\n"
" float rangeScale;\n"
" float start;\n"
"};\n"
"\n"
"struct LightingUniform\n"
"{\n"
" Light lights[" _XSTR(URDE_MAX_LIGHTS) "];\n"
" float4 ambient;\n"
" float4 colorReg0;\n"
" float4 colorReg1;\n"
" float4 colorReg2;\n"
" float4 mulColor;\n"
" Fog fog;\n"
"};\n"
"\n"
"static float4 EXTLightingShadowFunc(constant LightingUniform& lu, float4 mvPosIn, float4 mvNormIn,\n"
" thread VertToFrag& vtf, texture2d<float> extTex0)\n"
"{\n"
" float4 ret = lu.ambient;\n"
" \n"
" float3 delta = mvPosIn.xyz - lu.lights[i].pos.xyz;\n"
" float dist = length(delta);\n"
" float angDot = saturate(dot(normalize(delta), lu.lights[i].dir.xyz));\n"
" float att = 1.0 / (lu.lights[i].linAtt[2] * dist * dist +\n"
" lu.lights[i].linAtt[1] * dist +\n"
" lu.lights[i].linAtt[0]);\n"
" float angAtt = lu.lights[i].angAtt[2] * angDot * angDot +\n"
" lu.lights[i].angAtt[1] * angDot +\n"
" lu.lights[i].angAtt[0];\n"
" ret += lu.lights[i].color * saturate(angAtt) * att * saturate(dot(normalize(-delta), mvNormIn.xyz)) *\n"
" extTex0.sample(clampSamp, vtf.extTcgs0);\n"
" \n"
" for (int i=1 ; i<" _XSTR(URDE_MAX_LIGHTS) " ; ++i)\n"
" {\n"
" float3 delta = mvPosIn.xyz - lu.lights[i].pos.xyz;\n"
" float dist = length(delta);\n"
" float angDot = saturate(dot(normalize(delta), lu.lights[i].dir.xyz));\n"
" float att = 1.0 / (lu.lights[i].linAtt[2] * dist * dist +\n"
" lu.lights[i].linAtt[1] * dist +\n"
" lu.lights[i].linAtt[0]);\n"
" float angAtt = lu.lights[i].angAtt[2] * angDot * angDot +\n"
" lu.lights[i].angAtt[1] * angDot +\n"
" lu.lights[i].angAtt[0];\n"
" ret += lu.lights[i].color * saturate(angAtt) * att * saturate(dot(normalize(-delta), mvNormIn.xyz));\n"
" }\n"
" \n"
" return saturate(ret);\n"
"}\n";
static const char* MainPostMetal =
"float4 MainPostFunc(thread VertToFrag& vtf, constant LightingUniform& lu, float4 colorIn)\n"
"{\n"
@ -179,6 +240,12 @@ CModelShaders::GetShaderExtensionsMetal(boo::IGraphicsDataFactory::Platform plat
hecl::Backend::ZTest::Equal,
false, false, false, true);
/* World shadow shading (modified lighting) */
ext.registerExtensionSlot({LightingShadowMetal, "EXTLightingShadowFunc"}, {MainPostMetal, "MainPostFunc"},
1, BlockNames, 1, WorldShadowTextures, hecl::Backend::BlendFactor::Original,
hecl::Backend::BlendFactor::Original, hecl::Backend::ZTest::Original,
false, false, false, true);
return ext;
}

View File

@ -27,8 +27,8 @@ void CSpaceWarpFilter::GenerateWarpRampTex(boo::IGraphicsDataFactory::Context& c
}
}
m_warpTex = ctx.newStaticTexture(WARP_RAMP_RES+1, WARP_RAMP_RES+1, 1,
boo::TextureFormat::RGBA8, data[0],
(WARP_RAMP_RES+1) * (WARP_RAMP_RES+1) * 4);
boo::TextureFormat::RGBA8, boo::TextureClampMode::Repeat,
data[0], (WARP_RAMP_RES+1) * (WARP_RAMP_RES+1) * 4);
}
CSpaceWarpFilter::CSpaceWarpFilter()

View File

@ -8,7 +8,7 @@ CThermalColdFilter::CThermalColdFilter()
{
m_token = CGraphics::g_BooFactory->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
m_shiftTex = ctx.newDynamicTexture(8, 4, boo::TextureFormat::RGBA8);
m_shiftTex = ctx.newDynamicTexture(8, 4, boo::TextureFormat::RGBA8, boo::TextureClampMode::Repeat);
struct Vert
{

View File

@ -0,0 +1,59 @@
#include "CWorldShadowShader.hpp"
namespace urde
{
CWorldShadowShader::CWorldShadowShader(u32 w, u32 h)
: m_w(w), m_h(h) {}
void CWorldShadowShader::_buildTex(boo::IGraphicsDataFactory::Context& ctx)
{
m_tex = ctx.newRenderTexture(m_w, m_h, boo::TextureClampMode::ClampToWhite, 1, 0);
}
void CWorldShadowShader::bindRenderTarget()
{
CGraphics::g_BooMainCommandQueue->setRenderTarget(m_tex);
}
void CWorldShadowShader::drawBase(float extent)
{
zeus::CVector3f verts[] = {{-extent, 0.f, extent}, {extent, 0.f, extent},
{-extent, 0.f, -extent}, {extent, 0.f, -extent}};
m_vbo->load(verts, sizeof(zeus::CVector3f) * 4);
m_uniform.m_matrix = CGraphics::GetPerspectiveProjectionMatrix(true) * CGraphics::g_GXModelView.toMatrix4f();
m_uniform.m_color = zeus::CColor::skWhite;
m_uniBuf->load(&m_uniform, sizeof(m_uniform));
CGraphics::SetShaderDataBinding(m_zDataBind);
CGraphics::DrawArray(0, 4);
}
void CWorldShadowShader::lightenShadow()
{
m_uniform.m_color = zeus::CColor(1.f, 0.25f);
m_uniBuf->load(&m_uniform, sizeof(m_uniform));
CGraphics::SetShaderDataBinding(m_dataBind);
CGraphics::DrawArray(0, 4);
}
void CWorldShadowShader::blendPreviousShadow()
{
if (!m_prevQuad)
m_prevQuad.emplace(EFilterType::Blend, m_tex);
m_prevQuad->draw({1.f, 0.85f}, 1.f);
}
void CWorldShadowShader::resolveTexture()
{
boo::SWindowRect rect = {0, 0, m_w, m_h};
CGraphics::g_BooMainCommandQueue->resolveBindTexture(m_tex, rect, false, 0, true, false);
}
void CWorldShadowShader::Shutdown() {}
URDE_SPECIALIZE_SHADER(CWorldShadowShader)
}

View File

@ -0,0 +1,55 @@
#ifndef URDE_CWORLDSHADOWSHADER_HPP
#define URDE_CWORLDSHADOWSHADER_HPP
#include "TShader.hpp"
#include "CColoredQuadFilter.hpp"
#include "CTexturedQuadFilter.hpp"
namespace urde
{
class CWorldShadowShader
{
friend class CWorldShadowShaderGLDataBindingFactory;
friend class CWorldShadowShaderVulkanDataBindingFactory;
friend class CWorldShadowShaderD3DDataBindingFactory;
friend class CWorldShadowShaderMetalDataBindingFactory;
boo::ITextureR* m_tex;
std::experimental::optional<CTexturedQuadFilter> m_prevQuad;
u32 m_w, m_h;
struct Uniform
{
zeus::CMatrix4f m_matrix;
zeus::CColor m_color;
};
boo::GraphicsDataToken m_token;
boo::IGraphicsBufferD* m_vbo;
boo::IGraphicsBufferD* m_uniBuf;
boo::IShaderDataBinding* m_dataBind = nullptr;
boo::IShaderDataBinding* m_zDataBind = nullptr;
Uniform m_uniform;
void _buildTex(boo::IGraphicsDataFactory::Context& ctx);
public:
CWorldShadowShader(u32 w, u32 h);
void bindRenderTarget();
void drawBase(float extent);
void lightenShadow();
void blendPreviousShadow();
void resolveTexture();
u32 GetWidth() const { return m_w; }
u32 GetHeight() const { return m_h; }
boo::ITexture* GetTexture() const { return m_tex; }
using _CLS = CWorldShadowShader;
#include "TShaderDecl.hpp"
};
}
#endif // URDE_CWORLDSHADOWSHADER_HPP

View File

@ -0,0 +1,129 @@
#include "CWorldShadowShader.hpp"
namespace urde
{
static const char* VS =
"#version 330\n"
BOO_GLSL_BINDING_HEAD
"layout(location=0) in vec4 posIn;\n"
"\n"
"UBINDING0 uniform ColoredQuadUniform\n"
"{\n"
" mat4 xf;\n"
" vec4 color;\n"
"};\n"
"\n"
"struct VertToFrag\n"
"{\n"
" vec4 color;\n"
"};\n"
"\n"
"SBINDING(0) out VertToFrag vtf;\n"
"void main()\n"
"{\n"
" vtf.color = color;\n"
" gl_Position = xf * vec4(posIn.xyz, 1.0);\n"
"}\n";
static const char* FS =
"#version 330\n"
BOO_GLSL_BINDING_HEAD
"struct VertToFrag\n"
"{\n"
" vec4 color;\n"
"};\n"
"\n"
"SBINDING(0) in VertToFrag vtf;\n"
"layout(location=0) out vec4 colorOut;\n"
"void main()\n"
"{\n"
" colorOut = vtf.color;\n"
"}\n";
URDE_DECL_SPECIALIZE_SHADER(CWorldShadowShader)
static boo::IVertexFormat* s_VtxFmt = nullptr;
static boo::IShaderPipeline* s_Pipeline = nullptr;
static boo::IShaderPipeline* s_ZPipeline = nullptr;
struct CWorldShadowShaderGLDataBindingFactory : TShader<CWorldShadowShader>::IDataBindingFactory
{
boo::IShaderDataBinding* BuildShaderDataBinding(boo::IGraphicsDataFactory::Context& ctx,
CWorldShadowShader& filter)
{
boo::GLDataFactory::Context& cctx = static_cast<boo::GLDataFactory::Context&>(ctx);
const boo::VertexElementDescriptor VtxVmt[] =
{
{filter.m_vbo, nullptr, boo::VertexSemantic::Position4}
};
boo::IGraphicsBuffer* bufs[] = {filter.m_uniBuf};
boo::PipelineStage stages[] = {boo::PipelineStage::Vertex};
boo::IVertexFormat* vtxFmt = ctx.newVertexFormat(1, VtxVmt);
filter.m_dataBind = cctx.newShaderDataBinding(s_Pipeline,
vtxFmt, filter.m_vbo, nullptr, nullptr,
1, bufs, stages, nullptr, nullptr, 0, nullptr, nullptr, nullptr);
filter.m_zDataBind = cctx.newShaderDataBinding(s_ZPipeline,
vtxFmt, filter.m_vbo, nullptr, nullptr,
1, bufs, stages, nullptr, nullptr, 0, nullptr, nullptr, nullptr);
filter._buildTex(ctx);
return nullptr;
}
};
#if BOO_HAS_VULKAN
struct CWorldShadowShaderVulkanDataBindingFactory : TShader<CWorldShadowShader>::IDataBindingFactory
{
boo::IShaderDataBinding* BuildShaderDataBinding(boo::IGraphicsDataFactory::Context& ctx,
CWorldShadowShader& filter)
{
boo::VulkanDataFactory::Context& cctx = static_cast<boo::VulkanDataFactory::Context&>(ctx);
boo::IGraphicsBuffer* bufs[] = {filter.m_uniBuf};
filter.m_dataBind = cctx.newShaderDataBinding(s_Pipeline, s_VtxFmt,
filter.m_vbo, nullptr, nullptr, 1, bufs,
nullptr, nullptr, nullptr, 0, nullptr, nullptr, nullptr);
filter.m_zDataBind = cctx.newShaderDataBinding(s_ZPipeline, s_VtxFmt,
filter.m_vbo, nullptr, nullptr, 1, bufs,
nullptr, nullptr, nullptr, 0, nullptr, nullptr, nullptr);
filter._buildTex(ctx);
return nullptr;
}
};
#endif
TShader<CWorldShadowShader>::IDataBindingFactory*
CWorldShadowShader::Initialize(boo::GLDataFactory::Context& ctx)
{
const char* uniNames[] = {"ColoredQuadUniform"};
s_Pipeline = ctx.newShaderPipeline(VS, FS, 0, nullptr, 1, uniNames, boo::BlendFactor::SrcAlpha,
boo::BlendFactor::InvSrcAlpha, boo::Primitive::TriStrips,
boo::ZTest::None, false, true, false, boo::CullMode::None);
s_ZPipeline = ctx.newShaderPipeline(VS, FS, 0, nullptr, 1, uniNames, boo::BlendFactor::SrcAlpha,
boo::BlendFactor::InvSrcAlpha, boo::Primitive::TriStrips,
boo::ZTest::LEqual, true, true, false, boo::CullMode::None);
return new CWorldShadowShaderGLDataBindingFactory;
}
#if BOO_HAS_VULKAN
TShader<CWorldShadowShader>::IDataBindingFactory*
CWorldShadowShader::Initialize(boo::VulkanDataFactory::Context& ctx)
{
const boo::VertexElementDescriptor VtxVmt[] =
{
{nullptr, nullptr, boo::VertexSemantic::Position4}
};
s_VtxFmt = ctx.newVertexFormat(1, VtxVmt);
s_Pipeline = ctx.newShaderPipeline(VS, FS, s_VtxFmt, boo::BlendFactor::SrcAlpha,
boo::BlendFactor::InvSrcAlpha, boo::Primitive::TriStrips,
boo::ZTest::None, false, true, false, boo::CullMode::None);
s_ZPipeline = ctx.newShaderPipeline(VS, FS, s_VtxFmt, boo::BlendFactor::SrcAlpha,
boo::BlendFactor::InvSrcAlpha, boo::Primitive::TriStrips,
boo::ZTest::LEqual, true, true, false, boo::CullMode::None);
return new CWorldShadowShaderVulkanDataBindingFactory;
}
#endif
}

View File

@ -0,0 +1,89 @@
#include "CWorldShadowShader.hpp"
namespace urde
{
static const char* VS =
"struct VertData\n"
"{\n"
" float4 posIn : POSITION;\n"
"};\n"
"\n"
"cbuffer ColoredQuadUniform : register(b0)\n"
"{\n"
" float4x4 xf;\n"
" float4 color;\n"
"};\n"
"\n"
"struct VertToFrag\n"
"{\n"
" float4 position : SV_Position;\n"
" float4 color : COLOR;\n"
"};\n"
"\n"
"VertToFrag main(in VertData v)\n"
"{\n"
" VertToFrag vtf;\n"
" vtf.color = color;\n"
" vtf.position = mul(xf, float4(v.posIn.xyz, 1.0));\n"
" return vtf;\n"
"}\n";
static const char* FS =
"struct VertToFrag\n"
"{\n"
" float4 position : SV_Position;\n"
" float4 color : COLOR;\n"
"};\n"
"\n"
"float4 main(in VertToFrag vtf) : SV_Target0\n"
"{\n"
" return vtf.color;\n"
"}\n";
URDE_DECL_SPECIALIZE_SHADER(CWorldShadowShader)
static boo::IVertexFormat* s_VtxFmt = nullptr;
static boo::IShaderPipeline* s_Pipeline = nullptr;
static boo::IShaderPipeline* s_ZPipeline = nullptr;
struct CWorldShadowShaderD3DDataBindingFactory : TShader<CWorldShadowShader>::IDataBindingFactory
{
boo::IShaderDataBinding* BuildShaderDataBinding(boo::IGraphicsDataFactory::Context& ctx,
CWorldShadowShader& filter)
{
boo::ID3DDataFactory::Context& cctx = static_cast<boo::ID3DDataFactory::Context&>(ctx);
boo::IGraphicsBuffer* bufs[] = {filter.m_uniBuf};
filter.m_dataBind = cctx.newShaderDataBinding(s_Pipeline,
nullptr, nullptr, nullptr, s_VtxFmt,
filter.m_vbo, nullptr, nullptr, 1, bufs,
nullptr, nullptr, nullptr, 0, nullptr, nullptr, nullptr);
filter.m_zDataBind = cctx.newShaderDataBinding(s_ZPipeline,
nullptr, nullptr, nullptr, s_VtxFmt,
filter.m_vbo, nullptr, nullptr, 1, bufs,
nullptr, nullptr, nullptr, 0, nullptr, nullptr, nullptr);
filter._buildTex(ctx);
return nullptr;
}
};
TShader<CWorldShadowShader>::IDataBindingFactory*
CWorldShadowShader::Initialize(boo::ID3DDataFactory::Context& ctx)
{
const boo::VertexElementDescriptor VtxVmt[] =
{
{nullptr, nullptr, boo::VertexSemantic::Position4}
};
s_VtxFmt = ctx.newVertexFormat(1, VtxVmt);
s_Pipeline = ctx.newShaderPipeline(VS, FS, s_VtxFmt, boo::BlendFactor::SrcAlpha,
boo::BlendFactor::InvSrcAlpha, boo::Primitive::TriStrips,
boo::ZTest::None, false, true, false, boo::CullMode::None);
s_ZPipeline = ctx.newShaderPipeline(VS, FS, s_VtxFmt, boo::BlendFactor::SrcAlpha,
boo::BlendFactor::InvSrcAlpha, boo::Primitive::TriStrips,
boo::ZTest::LEqual, true, true, false, boo::CullMode::None);
return new CWorldShadowShaderD3DDataBindingFactory;
}
}

View File

@ -0,0 +1,91 @@
#include "CWorldShadowShader.hpp"
namespace urde
{
static const char* VS =
"#include <metal_stdlib>\n"
"using namespace metal;\n"
"struct VertData\n"
"{\n"
" float4 posIn [[ attribute(0) ]];\n"
"};\n"
"\n"
"struct ColoredQuadUniform\n"
"{\n"
" float4x4 xf;\n"
" float4 color;\n"
"};\n"
"\n"
"struct VertToFrag\n"
"{\n"
" float4 position [[ position ]];\n"
" float4 color;\n"
"};\n"
"\n"
"vertex VertToFrag vmain(VertData v [[ stage_in ]], constant ColoredQuadUniform& cqu [[ buffer(2) ]])\n"
"{\n"
" VertToFrag vtf;\n"
" vtf.color = cqu.color;\n"
" vtf.position = cqu.xf * float4(v.posIn.xyz, 1.0);\n"
" return vtf;\n"
"}\n";
static const char* FS =
"#include <metal_stdlib>\n"
"using namespace metal;\n"
"struct VertToFrag\n"
"{\n"
" float4 position [[ position ]];\n"
" float4 color;\n"
"};\n"
"\n"
"fragment float4 fmain(VertToFrag vtf [[ stage_in ]])\n"
"{\n"
" return vtf.color;\n"
"}\n";
URDE_DECL_SPECIALIZE_SHADER(CWorldShadowShader)
static boo::IVertexFormat* s_VtxFmt = nullptr;
static boo::IShaderPipeline* s_Pipeline = nullptr;
static boo::IShaderPipeline* s_ZPipeline = nullptr;
struct CWorldShadowShaderMetalDataBindingFactory : TShader<CWorldShadowShader>::IDataBindingFactory
{
boo::IShaderDataBinding* BuildShaderDataBinding(boo::IGraphicsDataFactory::Context& ctx,
CWorldShadowShader& filter)
{
boo::MetalDataFactory::Context& cctx = static_cast<boo::MetalDataFactory::Context&>(ctx);
boo::IGraphicsBuffer* bufs[] = {filter.m_uniBuf};
filter.m_dataBind = cctx.newShaderDataBinding(s_Pipeline, s_VtxFmt,
filter.m_vbo, nullptr, nullptr, 1, bufs,
nullptr, nullptr, nullptr, 0, nullptr, nullptr, nullptr);
filter.m_zDataBind = cctx.newShaderDataBinding(s_ZPipeline, s_VtxFmt,
filter.m_vbo, nullptr, nullptr, 1, bufs,
nullptr, nullptr, nullptr, 0, nullptr, nullptr, nullptr);
filter._buildTex(ctx);
return nullptr;
}
};
TShader<CWorldShadowShader>::IDataBindingFactory*
CWorldShadowShader::Initialize(boo::MetalDataFactory::Context& ctx)
{
const boo::VertexElementDescriptor VtxVmt[] =
{
{nullptr, nullptr, boo::VertexSemantic::Position4}
};
s_VtxFmt = ctx.newVertexFormat(1, VtxVmt);
s_Pipeline = ctx.newShaderPipeline(VS, FS, s_VtxFmt, CGraphics::g_ViewportSamples, boo::BlendFactor::SrcAlpha,
boo::BlendFactor::InvSrcAlpha, boo::Primitive::TriStrips,
boo::ZTest::None, false, true, false, boo::CullMode::None);
s_ZPipeline = ctx.newShaderPipeline(VS, FS, s_VtxFmt, CGraphics::g_ViewportSamples, boo::BlendFactor::SrcAlpha,
boo::BlendFactor::InvSrcAlpha, boo::Primitive::TriStrips,
boo::ZTest::LEqual, true, true, false, boo::CullMode::None);
return new CWorldShadowShaderMetalDataBindingFactory;
}
}

View File

@ -16,6 +16,7 @@
#include "Graphics/Shaders/CRandomStaticFilter.hpp"
#include "Graphics/Shaders/CFluidPlaneShader.hpp"
#include "Graphics/Shaders/CAABoxShader.hpp"
#include "Graphics/Shaders/CWorldShadowShader.hpp"
#include "Character/CCharLayoutInfo.hpp"
#include "Audio/CStreamAudioManager.hpp"
#include "CGBASupport.hpp"
@ -36,6 +37,7 @@ URDE_DECL_SPECIALIZE_SHADER(CRadarPaintShader)
URDE_DECL_SPECIALIZE_SHADER(CMapSurfaceShader)
URDE_DECL_SPECIALIZE_SHADER(CPhazonSuitFilter)
URDE_DECL_SPECIALIZE_SHADER(CAABoxShader)
URDE_DECL_SPECIALIZE_SHADER(CWorldShadowShader)
URDE_DECL_SPECIALIZE_MULTI_BLEND_SHADER(CColoredQuadFilter)
URDE_DECL_SPECIALIZE_MULTI_BLEND_SHADER(CTexturedQuadFilter)
URDE_DECL_SPECIALIZE_MULTI_BLEND_SHADER(CTexturedQuadFilterAlpha)
@ -233,6 +235,7 @@ CMain::BooSetter::BooSetter(boo::IGraphicsDataFactory* factory,
TShader<CMapSurfaceShader>::Initialize();
TShader<CPhazonSuitFilter>::Initialize();
TShader<CAABoxShader>::Initialize();
TShader<CWorldShadowShader>::Initialize();
TMultiBlendShader<CColoredQuadFilter>::Initialize();
TMultiBlendShader<CTexturedQuadFilter>::Initialize();
TMultiBlendShader<CTexturedQuadFilterAlpha>::Initialize();
@ -368,6 +371,7 @@ void CMain::Shutdown()
TShader<CMapSurfaceShader>::Shutdown();
TShader<CPhazonSuitFilter>::Shutdown();
TShader<CAABoxShader>::Shutdown();
TShader<CWorldShadowShader>::Shutdown();
TMultiBlendShader<CColoredQuadFilter>::Shutdown();
TMultiBlendShader<CTexturedQuadFilter>::Shutdown();
TMultiBlendShader<CTexturedQuadFilterAlpha>::Shutdown();

View File

@ -2470,19 +2470,14 @@ static const CModelFlags kHandHoloFlag = {1, 0, 3, zeus::CColor(0.75f, 0.5f, 0.f
void CPlayerGun::Render(const CStateManager& mgr, const zeus::CVector3f& pos, const CModelFlags& flags) const
{
CGraphics::CProjectionState projState = CGraphics::GetProjectionState();
CModelFlags useFlags;
CModelFlags useFlags = flags;
if (x0_lights.HasShadowLight())
useFlags.m_extendedShader = EExtendedShader::WorldShadow;
CModelFlags beamFlags = useFlags;
if (mgr.GetPlayerState()->GetCurrentVisor() == CPlayerState::EPlayerVisor::Thermal)
{
useFlags = kThermalFlags[int(x310_currentBeam)];
}
else
{
if (x835_26_phazonBeamMorphing)
useFlags = CModelFlags(1, 0, 3, zeus::CColor::lerp(zeus::CColor::skWhite, zeus::CColor::skBlack,
x39c_phazonMorphT));
else
useFlags = flags;
}
beamFlags = kThermalFlags[int(x310_currentBeam)];
else if (x835_26_phazonBeamMorphing)
beamFlags.x4_color = zeus::CColor::lerp(zeus::CColor::skWhite, zeus::CColor::skBlack, x39c_phazonMorphT);
const CGameCamera* cam = mgr.GetCameraManager()->GetCurrentCamera(mgr);
CGraphics::SetDepthRange(0.03125f, 0.125f);
@ -2525,8 +2520,8 @@ void CPlayerGun::Render(const CStateManager& mgr, const zeus::CVector3f& pos, co
mgr.GetPlayerState()->GetCurrentVisor() == CPlayerState::EPlayerVisor::Thermal ?
kHandThermalFlag : kHandHoloFlag);
}
DrawArm(mgr, pos, flags);
x72c_currentBeam->Draw(drawSuitArm, mgr, offsetWorldXf, useFlags, &x0_lights);
DrawArm(mgr, pos, useFlags);
x72c_currentBeam->Draw(drawSuitArm, mgr, offsetWorldXf, beamFlags, &x0_lights);
x82c_shadow->DisableModelProjectedShadow();
break;
case CGunMorph::EGunState::InWipeDone:
@ -2545,7 +2540,7 @@ void CPlayerGun::Render(const CStateManager& mgr, const zeus::CVector3f& pos, co
x82c_shadow->EnableModelProjectedShadow(offsetWorldXf, x0_lights.GetShadowLightArrIndex(), 2.15f);
CGraphics::SetModelMatrix(morphXf);
DrawClipCube(x6c8_hologramClipCube);
x72c_currentBeam->Draw(drawSuitArm, mgr, offsetWorldXf, useFlags, &x0_lights);
x72c_currentBeam->Draw(drawSuitArm, mgr, offsetWorldXf, beamFlags, &x0_lights);
x82c_shadow->DisableModelProjectedShadow();
}
else
@ -2556,7 +2551,7 @@ void CPlayerGun::Render(const CStateManager& mgr, const zeus::CVector3f& pos, co
x72c_currentBeam->DrawHologram(mgr, offsetWorldXf, CModelFlags(0, 0, 3, zeus::CColor::skWhite));
if (x0_lights.HasShadowLight())
x82c_shadow->EnableModelProjectedShadow(offsetWorldXf, x0_lights.GetShadowLightArrIndex(), 2.15f);
DrawArm(mgr, pos, flags);
DrawArm(mgr, pos, useFlags);
x82c_shadow->DisableModelProjectedShadow();
}
break;

View File

@ -1382,7 +1382,7 @@ void CMorphBall::ComputeBoostBallMovement(const CFinalInput& input, CStateManage
if (x1de8_boostChargeTime >= g_tweakBall->GetBoostBallMinChargeTime())
{
if (GetBallBoostState() == EBallBoostState::Zero)
if (GetBallBoostState() == EBallBoostState::BoostAvailable)
{
if (GetIsInHalfPipeMode() || x1df8_27_ballCloseToCollision)
{
@ -1395,7 +1395,7 @@ void CMorphBall::ComputeBoostBallMovement(const CFinalInput& input, CStateManage
CancelBoosting();
}
}
else if (GetBallBoostState() == EBallBoostState::One)
else if (GetBallBoostState() == EBallBoostState::BoostDisabled)
{
x0_player.SetTransform(zeus::lookAt(x0_player.GetTranslation(),
x0_player.GetTranslation() + GetBallToWorld().basis[1]));
@ -1726,7 +1726,10 @@ void CMorphBall::Render(const CStateManager& mgr, const CActorLights* lights) co
if (x1c34_boostLightFactor != 1.f)
{
if (lights->HasShadowLight())
{
x1c14_worldShadow->EnableModelProjectedShadow(ballToWorld, lights->GetShadowLightArrIndex(), 1.f);
flags.m_extendedShader = EExtendedShader::WorldShadow;
}
x58_ballModel->Render(mgr, ballToWorld, lights, flags);
x1c14_worldShadow->DisableModelProjectedShadow();
}
@ -1798,7 +1801,10 @@ void CMorphBall::Render(const CStateManager& mgr, const CActorLights* lights) co
if (tmp != 1.f)
{
if (lights->HasShadowLight())
{
x1c14_worldShadow->EnableModelProjectedShadow(ballToWorld, lights->GetShadowLightArrIndex(), 1.f);
sflags.m_extendedShader = EExtendedShader::WorldShadow;
}
x60_spiderBallGlassModel->Render(mgr, ballToWorld, x1c18_actorLights.get(), sflags);
x1c14_worldShadow->DisableModelProjectedShadow();
}

View File

@ -28,8 +28,8 @@ class CMorphBall
public:
enum class EBallBoostState
{
Zero,
One
BoostAvailable,
BoostDisabled
};
enum class ESpiderBallState
@ -40,8 +40,8 @@ public:
enum class EBombJumpState
{
Zero,
One
BombJumpAvailable,
BombJumpDisabled
};
private:
struct CSpiderBallElectricityManager
@ -167,8 +167,8 @@ private:
u16 x1e34_rollSfx = 0xffff;
u16 x1e36_landSfx = 0xffff;
u32 x1e38_wallSparkFrameCountdown = 0;
EBallBoostState x1e3c_boostState = EBallBoostState::Zero;
EBombJumpState x1e40_bombJumpState = EBombJumpState::Zero;
EBallBoostState x1e3c_boostState = EBallBoostState::BoostAvailable;
EBombJumpState x1e40_bombJumpState = EBombJumpState::BombJumpAvailable;
float x1e44_damageEffect = 0.f;
float x1e48_damageEffectDecaySpeed = 0.f;
float x1e4c_damageTime = 0.f;
@ -278,6 +278,7 @@ public:
EBallBoostState GetBallBoostState() const { return x1e3c_boostState; }
void SetBallBoostState(EBallBoostState state) { x1e3c_boostState = state; }
EBombJumpState GetBombJumpState() const { return x1e40_bombJumpState; }
void SetBombJumpState(EBombJumpState state) { x1e40_bombJumpState = state; }
void TakeDamage(float dam);
void DrawBallShadow(const CStateManager& mgr);
void DeleteBallShadow();

View File

@ -5395,7 +5395,7 @@ void CPlayer::Teleport(const zeus::CTransform& xf, CStateManager& mgr, bool rese
void CPlayer::BombJump(const zeus::CVector3f& pos, CStateManager& mgr)
{
if (x2f8_morphBallState == EPlayerMorphBallState::Morphed &&
x768_morphball->GetBombJumpState() != CMorphBall::EBombJumpState::One)
x768_morphball->GetBombJumpState() != CMorphBall::EBombJumpState::BombJumpDisabled)
{
zeus::CVector3f posToBall =
GetTranslation() + zeus::CVector3f(0.f, 0.f, g_tweakPlayer->GetPlayerBallHalfExtent()) - pos;

View File

@ -33,9 +33,9 @@ private:
public:
CWorldLight(const CWorldLight&) = default;
CWorldLight(CInputStream& in);
ELightType GetLightType() const;
const zeus::CVector3f& GetDirection() const;
const zeus::CVector3f& GetPosition() const;
EWorldLightType GetLightType() const { return x0_type; }
const zeus::CVector3f& GetDirection() const { return x1c_direction; }
const zeus::CVector3f& GetPosition() const { return x10_position; }
bool DoesCastShadows() const { return x34_castShadows; }
CLight GetAsCGraphicsLight() const;

View File

@ -1,31 +1,128 @@
#include "CWorldShadow.hpp"
#include "CWorld.hpp"
#include "CStateManager.hpp"
#include "Graphics/CBooRenderer.hpp"
namespace urde
{
CWorldShadow::CWorldShadow(u32, u32, bool)
{
}
CWorldShadow::CWorldShadow(u32 w, u32 h, bool rgba8)
: m_shader(w, h) {}
void CWorldShadow::EnableModelProjectedShadow(const zeus::CTransform& pos, s32 lightIdx, float f1)
{
zeus::CTransform texTransform = zeus::lookAt(zeus::CVector3f::skZero, x74_lightPos - x68_objPos);
zeus::CTransform posXf = pos;
posXf.origin = zeus::CVector3f::skZero;
texTransform = posXf.inverse() * texTransform;
texTransform = (texTransform * zeus::CTransform::Scale(float(M_SQRT2) * x64_objHalfExtent * f1)).inverse();
texTransform = zeus::CTransform::Translate(0.5f, 0.f, 0.5f) * texTransform;
CBooModel::EnableShadowMaps(m_shader.GetTexture(), texTransform);
}
void CWorldShadow::DisableModelProjectedShadow()
{
CBooModel::DisableShadowMaps();
}
void CWorldShadow::BuildLightShadowTexture(const CStateManager& mgr, TAreaId aid, s32 lightIdx,
const zeus::CAABox& aabb, bool b1, bool b2)
const zeus::CAABox& aabb, bool motionBlur, bool lighten)
{
if (x80_aid != aid || x84_lightIdx != lightIdx)
{
x88_blurReset = true;
x80_aid = aid;
x84_lightIdx = lightIdx;
}
if (aid != kInvalidAreaId)
{
const CGameArea* area = mgr.GetWorld()->GetAreaAlways(aid);
if (area->IsPostConstructed())
{
const CWorldLight& light = area->GetPostConstructed()->x60_lightsA[lightIdx];
zeus::CVector3f centerPoint = aabb.center();
if (const CPVSAreaSet* pvs = area->GetAreaVisSet())
{
CPVSVisSet lightSet = pvs->Get1stLightSet(lightIdx);
g_Renderer->EnablePVS(lightSet, aid);
}
else
{
CPVSVisSet visSet;
visSet.Reset(EPVSVisSetState::OutOfBounds);
g_Renderer->EnablePVS(visSet, aid);
}
zeus::CVector3f lightToPoint = centerPoint - light.GetPosition();
x64_objHalfExtent = (aabb.max - centerPoint).magnitude();
float distance = lightToPoint.magnitude();
float fov = zeus::radToDeg(std::atan2(x64_objHalfExtent, distance)) * 2.f;
if (fov >= 0.00001f)
{
lightToPoint.normalize();
x4_view = zeus::lookAt(light.GetPosition(), centerPoint, zeus::CVector3f::skDown);
x68_objPos = centerPoint;
x74_lightPos = light.GetPosition();
CGraphics::SetViewPointMatrix(x4_view);
zeus::CFrustum frustum;
frustum.updatePlanes(x4_view, zeus::SProjPersp(
zeus::degToRad(fov), 1.f, 0.1f, distance + x64_objHalfExtent));
g_Renderer->SetClippingPlanes(frustum);
g_Renderer->SetPerspective(fov, m_shader.GetWidth(), m_shader.GetHeight(), 0.1f, 1000.f);
SViewport backupVp = g_Viewport;
zeus::CVector2f backupDepthRange = CGraphics::g_CachedDepthRange;
m_shader.bindRenderTarget();
g_Renderer->SetViewport(0, 0, m_shader.GetWidth(), m_shader.GetHeight());
CGraphics::SetDepthRange(0.f, 1.f);
x34_model = zeus::lookAt(centerPoint - zeus::CVector3f(0.f, 0.f, 0.1f), light.GetPosition());
CGraphics::SetModelMatrix(x34_model);
m_shadowViewProj = CGraphics::GetPerspectiveProjectionMatrix(false) *
CGraphics::g_CameraMatrix.toMatrix4f();
float extent = float(M_SQRT2) * x64_objHalfExtent;
/* Depth test and write */
/* Color white 100% alpha */
m_shader.drawBase(extent);
CGraphics::SetModelMatrix(zeus::CTransform::Identity());
CBooModel::SetDrawingOccluders(true);
g_Renderer->PrepareDynamicLights({});
g_Renderer->DrawUnsortedGeometry(aid, 0, 0);
CBooModel::SetDrawingOccluders(false);
if (lighten)
{
CGraphics::SetModelMatrix(x34_model);
/* No depth test or write */
/* Color white 25% alpha */
m_shader.lightenShadow();
}
if (motionBlur && !x88_blurReset)
{
/* No depth test or write */
/* Color white 85% alpha */
/* Draw in shadow texture */
m_shader.blendPreviousShadow();
}
x88_blurReset = false;
m_shader.resolveTexture();
CBooRenderer::BindMainDrawTarget();
g_Renderer->SetViewport(backupVp.x0_left, backupVp.x4_top, backupVp.x8_width, backupVp.xc_height);
CGraphics::SetDepthRange(backupDepthRange[0], backupDepthRange[1]);
}
}
}
}
void CWorldShadow::ResetBlur()
{
x88_blurReset = true;
}
}

View File

@ -3,6 +3,7 @@
#include "RetroTypes.hpp"
#include "zeus/CAABox.hpp"
#include "Graphics/Shaders/CWorldShadowShader.hpp"
namespace urde
{
@ -10,12 +11,22 @@ class CStateManager;
class CWorldShadow
{
CWorldShadowShader m_shader;
zeus::CTransform x4_view;
zeus::CTransform x34_model;
float x64_objHalfExtent = 1.f;
zeus::CVector3f x68_objPos = {0.f, 1.f, 0.f};
zeus::CVector3f x74_lightPos;
TAreaId x80_aid;
s32 x84_lightIdx = -1;
bool x88_blurReset = true;
zeus::CMatrix4f m_shadowViewProj;
public:
CWorldShadow(u32, u32, bool);
CWorldShadow(u32 w, u32 h, bool rgba8);
void EnableModelProjectedShadow(const zeus::CTransform& pos, s32 lightIdx, float f1);
void DisableModelProjectedShadow();
void BuildLightShadowTexture(const CStateManager& mgr, TAreaId aid, s32 lightIdx,
const zeus::CAABox& aabb, bool b1, bool b2);
const zeus::CAABox& aabb, bool motionBlur, bool lighten);
void ResetBlur();
};

2
hecl

@ -1 +1 @@
Subproject commit 0270b2c253785221367af1ca414fca52f3867105
Subproject commit 6f86b576ed28e44740c65aaeb330b58ef6b06b13

@ -1 +1 @@
Subproject commit 6a89d8c22b9dfc83b93baee1344096fca7008af9
Subproject commit 02e557c7df6304e1f6143ef2f6ecae0fb152c6e1