diff --git a/build/gamesource/textures/decal/decals.ini b/build/engine/config/decals/decals.cfg similarity index 87% rename from build/gamesource/textures/decal/decals.ini rename to build/engine/config/decals/decals.cfg index 438710c7aed537951bd0844fc34a9322d07eba3c..5054a8a4802bee12ac478bb04d96107770bbc8da 100644 --- a/build/gamesource/textures/decal/decals.ini +++ b/build/engine/config/decals/decals.cfg @@ -2,7 +2,7 @@ [decal_0] id=0 base_scale=2.0 -tex=decal_test.dds +tex=decal_test tex0=[0,0,64,64] tex1=[64,0,128,64] tex2=[0,64,64,128] @@ -12,7 +12,7 @@ tex3=[64,64,128,128] [decal_1] id=1 base_scale=2.0 -tex=decal_test.dds +tex=decal_test tex0=[128,0,192,64] tex1=[192,0,256,64] tex2=[128,64,192,128] @@ -20,9 +20,9 @@ tex3=[192,64,256,128] ;wood [decal_2] -id=2 +id=4 base_scale=2.0 -tex=decal_test.dds +tex=decal_test tex0=[256,0,320,64] tex1=[320,0,384,64] tex2=[256,64,320,128] @@ -31,9 +31,9 @@ tex4=[384,0,448,64] ;glass [decal_3] -id=3 +id=2 base_scale=2.0 -tex=decal_test.dds +tex=decal_test tex0=[0,128,128,256] tex1=[128,128,256,256] tex2=[256,128,384,256] @@ -44,7 +44,7 @@ tex4=[128,256,256,384] [decal_4] id=7 base_scale=8.0 -tex=decal_test.dds +tex=decal_test tex0=[256,256,384,384] tex1=[384,256,512,384] tex2=[384,128,512,256] diff --git a/build/gamesource/textures/decal/decal_test.dds b/build/engine/textures/decal/decal_test.dds similarity index 100% rename from build/gamesource/textures/decal/decal_test.dds rename to build/engine/textures/decal/decal_test.dds diff --git a/build/engine/textures/decal/decal_test2.dds b/build/engine/textures/decal/decal_test2.dds new file mode 100644 index 0000000000000000000000000000000000000000..497ec0a645bbe369790dd9d2d78db10214bb2d5e Binary files /dev/null and b/build/engine/textures/decal/decal_test2.dds differ diff --git a/build/engine/textures/decal/decal_test3.dds b/build/engine/textures/decal/decal_test3.dds new file mode 100644 index 0000000000000000000000000000000000000000..b068b6500463898c431ee6069992113c5d9bc5de Binary files /dev/null and b/build/engine/textures/decal/decal_test3.dds differ diff --git a/proj/sxanim/vs2013/sxanim.vcxproj b/proj/sxanim/vs2013/sxanim.vcxproj index bd4a7d117b7fa96a3a354af493a33fc234c34eac..07d961364e9c966a0b4e31fd60525a9cc5f672f5 100644 --- a/proj/sxanim/vs2013/sxanim.vcxproj +++ b/proj/sxanim/vs2013/sxanim.vcxproj @@ -181,10 +181,13 @@ <ClCompile Include="..\..\..\source\anim\AnimatedModel.cpp" /> <ClCompile Include="..\..\..\source\anim\AnimatedModelProvider.cpp" /> <ClCompile Include="..\..\..\source\anim\AnimatedModelShared.cpp" /> + <ClCompile Include="..\..\..\source\anim\Decal.cpp" /> + <ClCompile Include="..\..\..\source\anim\DecalProvider.cpp" /> <ClCompile Include="..\..\..\source\anim\dllmain.cpp" /> <ClCompile Include="..\..\..\source\anim\DynamicModel.cpp" /> <ClCompile Include="..\..\..\source\anim\DynamicModelProvider.cpp" /> <ClCompile Include="..\..\..\source\anim\DynamicModelShared.cpp" /> + <ClCompile Include="..\..\..\source\anim\ModelOverlay.cpp" /> <ClCompile Include="..\..\..\source\anim\plugin_main.cpp" /> <ClCompile Include="..\..\..\source\anim\Renderable.cpp" /> <ClCompile Include="..\..\..\source\anim\RenderableVisibility.cpp" /> @@ -194,13 +197,18 @@ <ClInclude Include="..\..\..\source\anim\AnimatedModel.h" /> <ClInclude Include="..\..\..\source\anim\AnimatedModelProvider.h" /> <ClInclude Include="..\..\..\source\anim\AnimatedModelShared.h" /> + <ClInclude Include="..\..\..\source\anim\Decal.h" /> + <ClInclude Include="..\..\..\source\anim\DecalProvider.h" /> <ClInclude Include="..\..\..\source\anim\DynamicModel.h" /> <ClInclude Include="..\..\..\source\anim\DynamicModelProvider.h" /> <ClInclude Include="..\..\..\source\anim\DynamicModelShared.h" /> + <ClInclude Include="..\..\..\source\anim\ModelOverlay.h" /> <ClInclude Include="..\..\..\source\anim\Renderable.h" /> <ClInclude Include="..\..\..\source\anim\RenderableVisibility.h" /> <ClInclude Include="..\..\..\source\anim\Updatable.h" /> <ClInclude Include="..\..\..\source\xcommon\IXScene.h" /> + <ClInclude Include="..\..\..\source\xcommon\resource\IXDecal.h" /> + <ClInclude Include="..\..\..\source\xcommon\resource\IXDecalProvider.h" /> <ClInclude Include="..\..\..\source\xcommon\resource\IXModelProvider.h" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> diff --git a/proj/sxanim/vs2013/sxanim.vcxproj.filters b/proj/sxanim/vs2013/sxanim.vcxproj.filters index 1db63868b08793ae349c2374e6c93b2f85f5ed5b..0a83dc43df590e8feca6d797857386d0f0931fcc 100644 --- a/proj/sxanim/vs2013/sxanim.vcxproj.filters +++ b/proj/sxanim/vs2013/sxanim.vcxproj.filters @@ -49,6 +49,15 @@ <ClCompile Include="..\..\..\source\anim\dllmain.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\..\..\source\anim\DecalProvider.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\source\anim\ModelOverlay.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\source\anim\Decal.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\source\anim\Renderable.h"> @@ -84,5 +93,20 @@ <ClInclude Include="..\..\..\source\xcommon\IXScene.h"> <Filter>Header Files\xcommon</Filter> </ClInclude> + <ClInclude Include="..\..\..\source\xcommon\resource\IXDecalProvider.h"> + <Filter>Header Files\xcommon</Filter> + </ClInclude> + <ClInclude Include="..\..\..\source\xcommon\resource\IXDecal.h"> + <Filter>Header Files\xcommon</Filter> + </ClInclude> + <ClInclude Include="..\..\..\source\anim\DecalProvider.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\..\source\anim\ModelOverlay.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\..\source\anim\Decal.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> </Project> \ No newline at end of file diff --git a/source/anim/Decal.cpp b/source/anim/Decal.cpp new file mode 100644 index 0000000000000000000000000000000000000000..57b7c55063a8fc691446cb49db29d5a9a050ccd9 --- /dev/null +++ b/source/anim/Decal.cpp @@ -0,0 +1,595 @@ +#include "Decal.h" +#include "DynamicModel.h" +#include "DecalProvider.h" + +CDecal::CDecal(IXSceneQuery *pSceneQuery, IXRender *pRender, CDecalProvider *pProvider): + m_pSceneQuery(pSceneQuery), + m_pRender(pRender), + m_pProvider(pProvider) +{ +} + +CDecal::~CDecal() +{ + removeOverlays(); + mem_release(m_pMaterial); +} + +bool XMETHODCALLTYPE CDecal::isEnabled() const +{ + return(m_isEnabled); +} +void XMETHODCALLTYPE CDecal::enable(bool yesNo) +{ + m_isEnabled = yesNo; + TODO("Change state"); +} + +float3 XMETHODCALLTYPE CDecal::getPosition() const +{ + return(m_vPos); +} +void XMETHODCALLTYPE CDecal::setPosition(const float3 &vPos) +{ + m_vPos = vPos; + setDirty(); +} + +SMQuaternion XMETHODCALLTYPE CDecal::getOrientation() const +{ + return(m_qRot); +} +void XMETHODCALLTYPE CDecal::setOrientation(const SMQuaternion &qRot) +{ + m_qRot = qRot; + setDirty(); +} + +float3 XMETHODCALLTYPE CDecal::getLocalBoundMin() const +{ + float3 vBound; + for(UINT i = 0; i < DECAL_POINTS; ++i) + { + if(i == 0) + { + vBound = m_qRot * float3(m_avCorners[0], 0.0f); + } + else + { + vBound = SMVectorMin(vBound, m_qRot * float3(m_avCorners[0], 0.0f)); + } + vBound = SMVectorMin(vBound, m_qRot * float3(m_avCorners[0], m_fHeight)); + } + + return(vBound); +} +float3 XMETHODCALLTYPE CDecal::getLocalBoundMax() const +{ + float3 vBound; + for(UINT i = 0; i < DECAL_POINTS; ++i) + { + if(i == 0) + { + vBound = m_qRot * float3(m_avCorners[0], 0.0f); + } + else + { + vBound = SMVectorMax(vBound, m_qRot * float3(m_avCorners[0], 0.0f)); + } + vBound = SMVectorMax(vBound, m_qRot * float3(m_avCorners[0], m_fHeight)); + } + + return(vBound); +} +SMAABB XMETHODCALLTYPE CDecal::getLocalBound() const +{ + return(SMAABB(getLocalBoundMin(), getLocalBoundMax())); +} + +bool XMETHODCALLTYPE CDecal::rayTest(const float3 &vStart, const float3 &vEnd, float3 *pvOut, float3 *pvNormal, bool isRayInWorldSpace, bool bReturnNearestPoint) +{ + TODO("Implement me!"); + return(false); +} + +void XMETHODCALLTYPE CDecal::setLayer(UINT uLayer) +{ + m_uLayer = uLayer; + setDirty(); +} +UINT XMETHODCALLTYPE CDecal::getLayer() +{ + return(m_uLayer); +} + +void XMETHODCALLTYPE CDecal::setHeight(float fHeight) +{ + m_fHeight = fHeight; + setDirty(); +} +float XMETHODCALLTYPE CDecal::getHeight() +{ + return(m_fHeight); +} + +void XMETHODCALLTYPE CDecal::setTextureRangeU(const float2_t &vRange) +{ + m_vTexRangeU = vRange; +} +float2_t XMETHODCALLTYPE CDecal::getTextureRangeU() +{ + return(m_vTexRangeU); +} + +void XMETHODCALLTYPE CDecal::setTextureRangeV(const float2_t &vRange) +{ + m_vTexRangeV = vRange; +} +float2_t XMETHODCALLTYPE CDecal::getTextureRangeV() +{ + return(m_vTexRangeV); +} + +void CDecal::setMaterial(IXMaterial *pMaterial) +{ + mem_release(m_pMaterial); + m_pMaterial = pMaterial; + add_ref(m_pMaterial); +} +void CDecal::setCorner(UINT uCorner, const float2_t &vCorner) +{ + assert(uCorner < DECAL_POINTS); + if(uCorner < DECAL_POINTS) + { + m_avCorners[uCorner] = vCorner; + setDirty(); + } +} + +void CDecal::update( +#ifdef DECAL_DEBUG_DRAW + IXGizmoRenderer *pDebugRenderer +#endif +) +{ + if(m_isDirty) + { + // cleanup old overlays + removeOverlays(); + + // query for objects + + SMPLANE aPlanes[] = { + SMPlaneNormalize(SMPLANE(float3(m_avCorners[0], 0.0f), float3(m_avCorners[1], 0.0f), float3(m_avCorners[1], m_fHeight))), + SMPlaneNormalize(SMPLANE(float3(m_avCorners[2], 0.0f), float3(m_avCorners[3], 0.0f), float3(m_avCorners[3], m_fHeight))), + + SMPlaneNormalize(SMPLANE(float3(m_avCorners[1], 0.0f), float3(m_avCorners[2], 0.0f), float3(m_avCorners[2], m_fHeight))), + SMPlaneNormalize(SMPLANE(float3(m_avCorners[3], 0.0f), float3(m_avCorners[0], 0.0f), float3(m_avCorners[0], m_fHeight))), + + SMPLANE(0.0f, 0.0f, 1.0f, m_fHeight), + SMPLANE(0.0f, 0.0f, -1.0f, m_fHeight), + }; + + //SMMATRIX mWorld = SMMatrixTranspose(m_qRot.GetMatrix() * SMMatrixTranslation(m_vPos)); + //SMMATRIX mWorld = m_qRot.GetMatrix(); + for(UINT i = 0; i < ARRAYSIZE(aPlanes); ++i) + { + //aPlanes[i] = SMPlaneTransformTI(aPlanes[i], mWorld); + aPlanes[i] = SMPLANE(m_qRot * float3(aPlanes[i]), aPlanes[i].w); + aPlanes[i].w -= SMVector3Dot(aPlanes[i], m_vPos); + } + + IXFrustum *pFrustum; + m_pRender->newFrustum(&pFrustum); + + pFrustum->update(aPlanes, false); + +#ifdef DECAL_DEBUG_DRAW + pDebugRenderer->setColor(float4(1.0f, 1.0f, 0.0f, 1.0f)); + for(UINT i = 0; i < 8; ++i) + { + pDebugRenderer->drawPoint(pFrustum->getPoint(i)); + } + pDebugRenderer->setColor(float4(1.0f, 1.0f, 0.0f, 0.2f)); +#endif + + CDynamicModel **ppObjects = NULL; + m_pSceneQuery->setLayerMask(1 << m_uLayer); + UINT uCount = m_pSceneQuery->execute(pFrustum, (void***)&ppObjects); + + SMPLANE aPlanesObjectSpace[ARRAYSIZE(aPlanes)]; + + // build overlays + for(UINT i = 0; i < uCount; ++i) + { + TODO("Check ppObjects[i]->receivingDecals()"); + + for(UINT j = 0; j < ARRAYSIZE(aPlanes); ++j) + { + aPlanesObjectSpace[j] = SMPLANE(ppObjects[i]->getOrientation().Conjugate() * float3(aPlanes[j]), aPlanes[j].w + SMVector3Dot(aPlanes[j], ppObjects[i]->getPosition())); + } + pFrustum->update(aPlanesObjectSpace, false); + + spawnOverlayForModel(ppObjects[i], pFrustum +#ifdef DECAL_DEBUG_DRAW + , pDebugRenderer +#endif + ); + } + + mem_release(pFrustum); + + m_isDirty = false; + } +} + +void CDecal::onOverlayRemoved(CModelOverlay *pOverlay) +{ + int idx = m_aOverlays.indexOf(pOverlay, [](const Overlay &a, CModelOverlay *pB){ + return(a.pOverlay == pB); + }); + assert(idx >= 0); + if(idx >= 0) + { + m_aOverlays.erase(idx); + m_pProvider->freeOverlay(pOverlay); + if(!m_aOverlays.size()) + { + m_pProvider->onDecalEmptied(this); + } + } +} + +template<typename T> +static T ArrGet(T *pArr, UINT uSize, UINT uIdx, int iStartOffset = 0) +{ + return(pArr[((int)uIdx + iStartOffset) % uSize]); +} + +template<typename T> +static void ArrDel(T *pArr, UINT &uSize, UINT uIdx, int &iStartOffset) +{ + UINT uStartIndex = ((int)uIdx + iStartOffset) % uSize; + --uSize; + if(uStartIndex != uSize) + { + for(UINT i = uStartIndex; i < uSize; ++i) + { + pArr[i] = pArr[i + 1]; + } + } + if(uStartIndex < iStartOffset) + { + --iStartOffset; + } +} + +template<typename T> +static void ArrIns(const T &val, T *pArr, UINT &uSize, UINT uIdx, int &iStartOffset) +{ + UINT uStartIndex = ((int)uIdx + iStartOffset) % uSize; + if(uStartIndex == 0) + { + uStartIndex = uSize; + } + else + { + for(UINT i = uSize; i > uStartIndex; --i) + { + pArr[i] = pArr[i - 1]; + } + } + assert(uStartIndex < 10); + pArr[uStartIndex] = val; + ++uSize; + + if(uStartIndex < iStartOffset) + { + ++iStartOffset; + } +} + +template<typename T> +static void ArrSet(const T &val, T *pArr, UINT &uSize, UINT uIdx, int iStartOffset = 0) +{ + pArr[((int)uIdx + iStartOffset) % uSize] = val; +} + +static bool IsInside(const SMPLANE &plane, const float3_t &vPos) +{ + return(SMVector4Dot(plane, float4(vPos, 1.0f)) > 0.0f); +} + +void XMETHODCALLTYPE CDecal::FinalRelease() +{ + m_pProvider->onDecalReleased(this); +} + +void CDecal::spawnOverlayForModel(CDynamicModel *pModel, IXFrustum *pFrustum +#ifdef DECAL_DEBUG_DRAW + , IXGizmoRenderer *pDebugRenderer +#endif +) +{ + const IXResourceModelStatic *pResource = pModel->getResource()->asStatic(); + if(!pResource) + { + return; + } + + if(pResource->getPrimitiveTopology() != XPT_TRIANGLELIST) + { + LogError("CDecal::spawnOverlayForModel(): Unsupported primitive topology"); + } + + SMQuaternion qDecalToModel = m_qRot * pModel->getOrientation().Conjugate(); + float3 vS = qDecalToModel * float3(1.0f, 0.0f, 0.0f); + float3 vT = qDecalToModel * float3(0.0f, 1.0f, 0.0f); + float3 vN = qDecalToModel * float3(0.0f, 0.0f, 1.0f); + + float2_t sBound; + float2_t tBound; + // center point + float3 vModelSpaceDecalCenter = pModel->getOrientation().Conjugate() * (m_vPos - pModel->getPosition()); + + for(UINT j = 0; j < DECAL_POINTS; ++j) + { + float s = m_avCorners[j].x; + float t = m_avCorners[j].y; + if(j == 0) + { + sBound.x = s; + sBound.y = s; + tBound.x = t; + tBound.y = t; + } + else + { + if(sBound.x > s) + { + sBound.x = s; + } + if(sBound.y < s) + { + sBound.y = s; + } + if(tBound.x > t) + { + tBound.x = t; + } + if(tBound.y < t) + { + tBound.y = t; + } + } + } + float2_t vInvBoundRange = float2(1.0f, 1.0f) / float2(sBound.y - sBound.x, tBound.y - tBound.x); + XResourceModelStaticVertex vtx; + vtx.vTangent = vS; + vtx.vBinorm = vT; + vtx.vNorm = vN; + + float3_t aTempVertices[10]; // 9 - max vertex count of clipped triangle + 1 temp vertex + UINT uVertexCount; + + Array<XResourceModelStaticVertex> aOverlayVertices; + + UINT uSubsets = pResource->getSubsetCount(0); + for(UINT uSubset = 0; uSubset < uSubsets; ++uSubset) + { + TODO("Separate transparent subsets"); + const XResourceModelStaticSubset *pSubset = pResource->getSubset(0, uSubset); + for(UINT i = 0; i < pSubset->iIndexCount; i += 3) + { + float3_t &a = pSubset->pVertices[pSubset->pIndices[i]].vPos; + float3_t &b = pSubset->pVertices[pSubset->pIndices[i + 1]].vPos; + float3_t &c = pSubset->pVertices[pSubset->pIndices[i + 2]].vPos; + + if(pFrustum->polyInFrustum(a, b, c)) + { + aTempVertices[0] = a; + aTempVertices[1] = b; + aTempVertices[2] = c; + uVertexCount = 3; + + //pDebugRenderer->drawPoly(a, b, c); + + // clip by frustum planes + for(UINT j = 0, jl = pFrustum->getPlaneCount(); j < jl && uVertexCount >= 3/* && j < 4*/; ++j) + { + const SMPLANE &plane = pFrustum->getPlaneAt(j); + int iInsideVertex = -1; + // - find inside vertex + for(UINT k = 0; k < uVertexCount; ++k) + { + if(IsInside(plane, aTempVertices[k])) + { + iInsideVertex = (int)k; + break; + } + } + + if(iInsideVertex < 0) + { + // all vertices outside clipping plane + uVertexCount = 0; + break; + } + // - rotate array to make inside vertex first + // - clip + + for(UINT k = 0; k < uVertexCount; ++k) + { + float3_t vCur = ArrGet(aTempVertices, uVertexCount, k, iInsideVertex); + float3_t vNext = ArrGet(aTempVertices, uVertexCount, k + 1, iInsideVertex); + bool bCurInside = IsInside(plane, vCur); + bool bNextInside = IsInside(plane, vNext); + if(bCurInside) + { + if(bNextInside) + { + continue; + } + else + { + // clip + float3 vPt; + bool b = plane.intersectLine(&vPt, vCur, vNext); + assert(b); + // insert after cur + ArrIns((float3_t)vPt, aTempVertices, uVertexCount, k + 1, iInsideVertex); + ++k; + } + } + else + { + if(bNextInside) + { + // clip + float3 vPt; + bool b = plane.intersectLine(&vPt, vCur, vNext); + assert(b); + // replace cur + ArrSet((float3_t)vPt, aTempVertices, uVertexCount, k, iInsideVertex); + } + else + { + // drop cur + ArrDel(aTempVertices, uVertexCount, k, iInsideVertex); + --k; + } + } + } + } + + // put into overlay + if(uVertexCount >= 3) + { + //if(pModel->isStatic()) + //{ + // // transform to world pos + // float3 vPos = pModel->getPosition(); + // SMQuaternion qRot = pModel->getOrientation(); + // for(UINT j = 0; j < uVertexCount; ++j) + // { + // aTempVertices[j] = qRot * aTempVertices[j] + vPos; + // } + //} + + UINT uStartVtx = aOverlayVertices.size(); + for(UINT b = 0, e = uVertexCount - 1; b + 1 <= e - 1; ++b, --e) + { + vtx.vPos = aTempVertices[e]; + aOverlayVertices.push_back(vtx); + + vtx.vPos = aTempVertices[b]; + aOverlayVertices.push_back(vtx); + + vtx.vPos = aTempVertices[e - 1]; + aOverlayVertices.push_back(vtx); + + vtx.vPos = aTempVertices[b + 1]; + aOverlayVertices.push_back(vtx); + } + + float3 vTriN = SMVector3Normalize(SMVector3Cross(b - a, c - a)); + float3 vTriS = vS; + float3 vTriT = vT; + float fCos = SMVector3Dot(vN, vTriN); + if(fCos < 0.2f) + { + float3 vRotAxis = SMVector3Cross(vN, vTriN); + + float fTempS = SMVector3Dot(vRotAxis, vS); + float fTempT = SMVector3Dot(vRotAxis, vT); + + float fSign = max(fTempS, fTempT); + float fAngle = safe_acosf(fCos); + if(fSign < 0.0f) + { + fAngle = SM_2PI - fAngle; + } + + SMQuaternion q(vRotAxis, fAngle); + vTriS = q * vS; + vTriT = q * vT; + } + + for(UINT j = uStartVtx, jl = aOverlayVertices.size(); j < jl; ++j) + { + XResourceModelStaticVertex &vt = aOverlayVertices[j]; + + float3 vPos = vt.vPos - vModelSpaceDecalCenter; + float s = SMVector3Dot(vPos, vTriS); + float t = SMVector3Dot(vPos, vTriT); + vt.vPos = vt.vPos + vTriN * 0.0001f; + + vt.vTex.x = lerpf(m_vTexRangeU.x, m_vTexRangeU.y, clampf((s - sBound.x) * vInvBoundRange.x, 0.0f, 1.0f)); + vt.vTex.y = lerpf(m_vTexRangeV.x, m_vTexRangeV.y, clampf((t - tBound.x) * vInvBoundRange.y, 0.0f, 1.0f)); + } + } + } + } + } + + // aOverlayVertices + CModelOverlay *pOverlay = m_pProvider->allocOverlay(this, m_pMaterial, aOverlayVertices, vN); + m_aOverlays.push_back({pModel, pOverlay}); + + CModelOverlay *pLastOverlay = pModel->getOverlay(); + if(!pLastOverlay) + { + pModel->setOverlay(pOverlay); + } + else + { + while(pLastOverlay->getNextOverlay()) + { + pLastOverlay = pLastOverlay->getNextOverlay(); + } + pLastOverlay->setNextOverlay(pOverlay); + } +} + +void CDecal::removeOverlays() +{ + fora(i, m_aOverlays) + { + Overlay &ovl = m_aOverlays[i]; + + CModelOverlay *pPrev = NULL; + CModelOverlay *pCur = ovl.pModel->getOverlay(); + while(pCur) + { + if(pCur == ovl.pOverlay) + { + if(pPrev) + { + pPrev->setNextOverlay(pCur->getNextOverlay()); + } + else + { + ovl.pModel->setOverlay(pCur->getNextOverlay()); + } + + break; + } + + pPrev = pCur; + pCur = pCur->getNextOverlay(); + } + + m_pProvider->freeOverlay(ovl.pOverlay); + } + + m_aOverlays.clearFast(); +} + +void CDecal::setDirty() +{ + if(!m_isDirty) + { + m_isDirty = true; + m_pProvider->updateDecal(this); + } +} diff --git a/source/anim/Decal.h b/source/anim/Decal.h new file mode 100644 index 0000000000000000000000000000000000000000..cccb6c73ec6ff7a757c16e8b76e112f4675859b2 --- /dev/null +++ b/source/anim/Decal.h @@ -0,0 +1,104 @@ +#ifndef __DECAL_H +#define __DECAL_H + +#include <xcommon/resource/IXDecal.h> +#include <xcommon/IXScene.h> +#include <xcommon/render/IXRender.h> +#include "ModelOverlay.h" + +#define DECAL_POINTS 4 + +// #define DECAL_DEBUG_DRAW + +class CDynamicModel; +class CDecalProvider; + +class CDecal final: public IXUnknownImplementation<IXDecal> +{ +public: + CDecal(IXSceneQuery *pSceneQuery, IXRender *pRender, CDecalProvider *pProvider); + ~CDecal(); + + bool XMETHODCALLTYPE isEnabled() const override; + void XMETHODCALLTYPE enable(bool yesNo) override; + + float3 XMETHODCALLTYPE getPosition() const override; + void XMETHODCALLTYPE setPosition(const float3 &vPos) override; + + SMQuaternion XMETHODCALLTYPE getOrientation() const override; + void XMETHODCALLTYPE setOrientation(const SMQuaternion &qRot) override; + + float3 XMETHODCALLTYPE getLocalBoundMin() const override; + float3 XMETHODCALLTYPE getLocalBoundMax() const override; + SMAABB XMETHODCALLTYPE getLocalBound() const override; + + bool XMETHODCALLTYPE rayTest(const float3 &vStart, const float3 &vEnd, float3 *pvOut = NULL, float3 *pvNormal = NULL, bool isRayInWorldSpace = true, bool bReturnNearestPoint = false) override; + + void XMETHODCALLTYPE setLayer(UINT uLayer) override; + UINT XMETHODCALLTYPE getLayer() override; + + void XMETHODCALLTYPE setHeight(float fHeight) override; + float XMETHODCALLTYPE getHeight() override; + + void XMETHODCALLTYPE setTextureRangeU(const float2_t &vRange) override; + float2_t XMETHODCALLTYPE getTextureRangeU() override; + + void XMETHODCALLTYPE setTextureRangeV(const float2_t &vRange) override; + float2_t XMETHODCALLTYPE getTextureRangeV() override; + + void setMaterial(IXMaterial *pMaterial); + void setCorner(UINT uCorner, const float2_t &vCorner); + + void update( +#ifdef DECAL_DEBUG_DRAW + IXGizmoRenderer *pDebugRenderer +#endif + ); + + // Must be called only for overlays removed with model + void onOverlayRemoved(CModelOverlay *pOverlay); + +private: + IXSceneQuery *m_pSceneQuery = NULL; + IXRender *m_pRender = NULL; + CDecalProvider *m_pProvider = NULL; + + IXMaterial *m_pMaterial = NULL; + + float2_t m_avCorners[4]; + + SMQuaternion m_qRot; + float3_t m_vPos; + + float m_fHeight = 0.1f; + + UINT m_uLayer = 0; + + float2_t m_vTexRangeU = float2_t(0.0f, 1.0f); + float2_t m_vTexRangeV = float2_t(0.0f, 1.0f); + + struct Overlay + { + CDynamicModel *pModel; + CModelOverlay *pOverlay; + }; + Array<Overlay> m_aOverlays; + + bool m_isEnabled = true; + + bool m_isDirty = false; + +private: + void XMETHODCALLTYPE FinalRelease() override; + void spawnOverlayForModel(CDynamicModel *pModel, IXFrustum *pFrustum +#ifdef DECAL_DEBUG_DRAW + , IXGizmoRenderer *pDebugRenderer +#endif + ); + + void removeOverlays(); + + void setDirty(); +}; + +#endif diff --git a/source/anim/DecalProvider.cpp b/source/anim/DecalProvider.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e9f8fd2ab5d6aca9c55c336854b73259adfdae1f --- /dev/null +++ b/source/anim/DecalProvider.cpp @@ -0,0 +1,591 @@ +#include "DecalProvider.h" +//#include <xcommon/IPluginManager.h> +//#include <core/sxcore.h> +#include "ModelOverlay.h" +#include "DynamicModel.h" +#include "DynamicModelProvider.h" +#include <xcommon/IPluginManager.h> +#include "Decal.h" + +CDecalProvider::CDecalProvider(IXCore *pCore, CDynamicModelProvider *pProviderDynamic): + m_pCore(pCore), + m_pProviderDynamic(pProviderDynamic) +{ + m_pMaterialSystem = (IXMaterialSystem*)pCore->getPluginManager()->getInterface(IXMATERIALSYSTEM_GUID); + m_pRenderUtils = (IXRenderUtils*)pCore->getPluginManager()->getInterface(IXRENDERUTILS_GUID); + m_pRender = (IXRender*)pCore->getPluginManager()->getInterface(IXRENDER_GUID); + + IXScene *pScene = (IXScene*)pCore->getPluginManager()->getInterface(IXSCENE_GUID); + IXSceneObjectType *pType = pScene->getObjectType("xDynamic"); + m_pQuery = pType->newQuery(); + mem_release(pType); + + pCore->getConsole()->registerCVar("r_maxdecals", 300, "Max temp decals"); + //pCore->getConsole()->registerCVar("r_max_overlapped_decals", 4, "Max overlapped decals"); + +#ifdef DECAL_DEBUG_DRAW + m_pRenderUtils->newGizmoRenderer(&m_pDebugRenderer); +#endif + + loadDecals(); +} + +CDecalProvider::~CDecalProvider() +{ + fora(i, m_aTempDecals) + { + mem_release(m_aTempDecals[i]); + } + for(UINT i = 0; i < XDT__COUNT; ++i) + { + mem_release(m_aDecalTypes[i].pMaterial); + } + mem_release(m_pQuery); + mem_release(m_pBlendState); + mem_release(m_pDSState); + mem_release(m_pWorldBuffer); +#ifdef DECAL_DEBUG_DRAW + mem_release(m_pDebugRenderer); +#endif +} + +void CDecalProvider::render(CRenderableVisibility *pVisibility) +{ + XPROFILE_FUNCTION(); + + CRenderableVisibility::OverlayData &overlayData = pVisibility->getOverlayData(); + + UINT uVertexCount = overlayData.aVertices.size(); + + if(uVertexCount) + { + if(overlayData.uVertexBufferAllocSize < uVertexCount) + { + overlayData.uVertexBufferAllocSize = uVertexCount; + mem_release(overlayData.pRB); + mem_release(overlayData.pVB); + mem_release(overlayData.pIB); + + overlayData.pVB = m_pDevice->createVertexBuffer(sizeof(XResourceModelStaticVertexGPU) * uVertexCount, GXBUFFER_USAGE_STREAM); + overlayData.pRB = m_pDevice->createRenderBuffer(1, &overlayData.pVB, m_pProviderDynamic->getVertexDeclaration()); + m_pRenderUtils->getQuadIndexBuffer(uVertexCount / 4, &overlayData.pIB); + } + + XResourceModelStaticVertexGPU *pVertices; + if(overlayData.pVB->lock((void**)&pVertices, GXBL_WRITE)) + { + memcpy(pVertices, overlayData.aVertices, sizeof(XResourceModelStaticVertexGPU) * uVertexCount); + overlayData.pVB->unlock(); + } + + m_pProviderDynamic->bindVertexFormat(); + + IGXContext *pCtx = m_pDevice->getThreadContext(); + pCtx->setRenderBuffer(overlayData.pRB); + pCtx->setIndexBuffer(overlayData.pIB); + pCtx->setPrimitiveTopology(GXPT_TRIANGLELIST); + IGXBlendState *pOldBlendState = pCtx->getBlendState(); + pCtx->setBlendState(m_pBlendState); + m_pDevice->getThreadContext()->setVSConstant(m_pWorldBuffer, 1 /* SCR_OBJECT */); + IGXDepthStencilState *pOldDSState = pCtx->getDepthStencilState(); + pCtx->setDepthStencilState(m_pDSState); + + fora(i, overlayData.aSubsets) + { + auto &ss = overlayData.aSubsets[i]; + m_pMaterialSystem->bindMaterial(ss.pMaterial); + pCtx->drawIndexed(ss.uQuadCount * 4, ss.uQuadCount * 2, ss.uStartIndex); + } + + pCtx->setBlendState(pOldBlendState); + mem_release(pOldBlendState); + + pCtx->setDepthStencilState(pOldDSState); + mem_release(pOldDSState); + } + +#ifdef DECAL_DEBUG_DRAW + m_pDebugRenderer->render(false, false); +#endif +} + +void XMETHODCALLTYPE CDecalProvider::shootDecal(XDECAL_TYPE type, const float3 &vWorldPos, const float3 &vNormal, float fScale, const float3 *pvSAxis) +{ + //type = XDT_GLASS; + const DecalType *pType = getDecalType(type); + if(!pType || !pType->pMaterial) + { + return; + } + + fScale *= pType->fBaseScale * 0.0008f; + + IXTexture *pTex = pType->pMaterial->getTexture("txBase"); + if(!pTex) + { + IKeyIterator *pIter = pType->pMaterial->getTexturesIterator(); + if(pIter) + { + pTex = pType->pMaterial->getTexture(pIter->getCurrent()); + } + mem_release(pIter); + } + + UINT uTexWidth = pTex->getWidth(); + UINT uTexHeight = pTex->getHeight(); + + DecalTexRange texRange = {0, 0, uTexWidth, uTexHeight}; + if(pType->aTextures.size()) + { + texRange = pType->aTextures[rand() % pType->aTextures.size()]; + } + + float2_t vSize((float)(texRange.xmax - texRange.xmin) * fScale, (float)(texRange.ymax - texRange.ymin) * fScale); + + float2_t sBound(-vSize.x * 0.5f, vSize.x * 0.5f); + float2_t tBound(-vSize.y * 0.5f, vSize.y * 0.5f); + + //compute basis + SMMATRIX mBasis; + computeBasis(&mBasis, SMVector3Normalize(vNormal), pvSAxis); + + SMQuaternion qOrient = SMQuaternion(mBasis.r[0], mBasis.r[1], mBasis.r[2]).Normalize(); + + //vWorldPos; + //qOrient; + //pType->pMaterial; + float2_t avCorners[] = { + float2_t(sBound.x, tBound.x), + float2_t(sBound.x, tBound.y), + float2_t(sBound.y, tBound.y), + float2_t(sBound.y, tBound.x), + }; + float fHeight = min(vSize.x, vSize.y) * 0.5f + 0.1f; + +#ifdef DECAL_DEBUG_DRAW + m_pDebugRenderer->setPointSize(0.01f); + m_pDebugRenderer->setLineWidth(0.01f); + m_pDebugRenderer->setColor(float4(1.0f, 0.0f, 0.0f, 1.0f)); + m_pDebugRenderer->jumpTo(vWorldPos); + m_pDebugRenderer->lineTo(vWorldPos + mBasis.r[0]); + m_pDebugRenderer->setColor(float4(0.0f, 1.0f, 0.0f, 1.0f)); + m_pDebugRenderer->jumpTo(vWorldPos); + m_pDebugRenderer->lineTo(vWorldPos + mBasis.r[1]); + m_pDebugRenderer->setColor(float4(0.0f, 0.0f, 1.0f, 1.0f)); + m_pDebugRenderer->jumpTo(vWorldPos); + m_pDebugRenderer->lineTo(vWorldPos + mBasis.r[2]); + + m_pDebugRenderer->setColor(float4(1.0f, 1.0f, 1.0f, 1.0f)); + /*for(UINT i = 0; i < ARRAYSIZE(avCorners); ++i) + { + m_pDebugRenderer->drawPoint(qOrient * float3(avCorners[i], -fHeight) + vWorldPos); + m_pDebugRenderer->drawPoint(qOrient * float3(avCorners[i], fHeight) + vWorldPos); + }*/ +#endif + + // create CDecal + CDecal *pDecal = allocDecal(); + pDecal->setMaterial(pType->pMaterial); + for(UINT i = 0; i < DECAL_POINTS; ++i) + { + pDecal->setCorner(i, avCorners[i]); + } + pDecal->setPosition(vWorldPos); + pDecal->setOrientation(qOrient); + pDecal->setHeight(fHeight); + pDecal->setTextureRangeU(float2((float)texRange.xmin, (float)texRange.xmax) / (float)uTexWidth); + pDecal->setTextureRangeV(float2((float)texRange.ymin, (float)texRange.ymax) / (float)uTexHeight); + + addTempDecal(pDecal); +} + +void CDecalProvider::computeVisibility(const float3 &vHintDir, CRenderableVisibility *pVisibility) +{ + Array<CDynamicModel*> &aRenderList = pVisibility->getRenderList(); + + struct TmpOverlay + { + CDynamicModel *pModel; + CModelOverlay *pOverlay; + }; + + Array<TmpOverlay> aOverlays; + + bool useHintDir = !SMIsZero(SMVector3Length2(vHintDir)); + + UINT uVertices = 0; + fora(i, aRenderList) + { + CDynamicModel *pMdl = aRenderList[i]; + CModelOverlay *pOverlay = pMdl->getOverlay(); + while(pOverlay) + { + // skip backface overlays + bool bRender = !useHintDir || pOverlay->getMaterial()->isTwoSided(); + if(!bRender) + { + float3 vNormal = pOverlay->getNormal(); + if(!pMdl->isStatic()) + { + vNormal = pMdl->getOrientation() * vNormal; + } + bRender = SMVector3Dot(vNormal, vHintDir) < 0.0f; + } + if(bRender) + { + aOverlays.push_back({pMdl, pOverlay}); + + uVertices += pOverlay->getVertices().size(); + } + + pOverlay = pOverlay->getNextOverlay(); + } + } + + CRenderableVisibility::OverlayData &overlayData = pVisibility->getOverlayData(); + + Array<XResourceModelStaticVertexGPU> &aVertices = overlayData.aVertices; + aVertices.clearFast(); + aVertices.reserve(uVertices); + Array<CRenderableVisibility::OverlaySubset>& aSubsets = overlayData.aSubsets; + aSubsets.clearFast(); + + aOverlays.quickSort([](const TmpOverlay &a, const TmpOverlay &b){ + return(a.pOverlay->getMaterial() < b.pOverlay->getMaterial()); + }); + + XResourceModelStaticVertex tempVertex; + XResourceModelStaticVertexGPU tempGpuVertex; + CRenderableVisibility::OverlaySubset curSubset = {}; + fora(i, aOverlays) + { + TmpOverlay &overlay = aOverlays[i]; + + if(curSubset.pMaterial != overlay.pOverlay->getMaterial()) + { + if(curSubset.uStartVertex != aVertices.size()) + { + curSubset.uQuadCount = (aVertices.size() - curSubset.uStartVertex) / 4; + aSubsets.push_back(curSubset); + } + curSubset.pMaterial = overlay.pOverlay->getMaterial(); + + curSubset.uStartVertex = aVertices.size(); + curSubset.uStartIndex = aVertices.size() / 4 * 6; + } + + const Array<XResourceModelStaticVertex>& aOverlayVertices = overlay.pOverlay->getVertices(); + bool bNeedTransform = !overlay.pModel->isStatic(); + + float3 vPos = overlay.pModel->getPosition(); + SMQuaternion qRot = overlay.pModel->getOrientation(); + + fora(j, aOverlayVertices) + { + tempVertex = aOverlayVertices[j]; + if(bNeedTransform) + { + // transform vertices + tempVertex.vPos = qRot * tempVertex.vPos + vPos; + tempVertex.vNorm = qRot * tempVertex.vNorm; + tempVertex.vTangent = qRot * tempVertex.vTangent; + tempVertex.vBinorm = qRot * tempVertex.vBinorm; + } + +#define TO_SHORT(v) ((short)((v) * 32767.0f)) + auto &dst = tempGpuVertex; + auto &src = tempVertex; + dst.vPos = src.vPos; + dst.vTex = src.vTex; + dst.vNorm[0] = TO_SHORT(src.vNorm.x); + dst.vNorm[1] = TO_SHORT(src.vNorm.y); + dst.vNorm[2] = TO_SHORT(src.vNorm.z); + dst.vTangent[0] = TO_SHORT(src.vTangent.x); + dst.vTangent[1] = TO_SHORT(src.vTangent.y); + dst.vTangent[2] = TO_SHORT(src.vTangent.z); + dst.vBinorm[0] = TO_SHORT(src.vBinorm.x); + dst.vBinorm[1] = TO_SHORT(src.vBinorm.y); + dst.vBinorm[2] = TO_SHORT(src.vBinorm.z); +#undef TO_SHORT + + aVertices.push_back(tempGpuVertex); + } + } + + if(curSubset.uStartVertex != aVertices.size()) + { + curSubset.uQuadCount = (aVertices.size() - curSubset.uStartVertex) / 4; + aSubsets.push_back(curSubset); + } +} + +void CDecalProvider::setDevice(IGXDevice *pDevice) +{ + m_pDevice = pDevice; + + GXBlendDesc blendDesc; + memset(&blendDesc, 0, sizeof(blendDesc)); + blendDesc.renderTarget[0].u8RenderTargetWriteMask = GXCOLOR_WRITE_ENABLE_RED | GXCOLOR_WRITE_ENABLE_GREEN | GXCOLOR_WRITE_ENABLE_BLUE; + blendDesc.renderTarget[0].blendSrcColor = GXBLEND_DEST_COLOR; + blendDesc.renderTarget[0].blendDestColor = GXBLEND_SRC_COLOR; + blendDesc.renderTarget[0].blendSrcAlpha = GXBLEND_SRC_ALPHA; + blendDesc.renderTarget[0].blendDestAlpha = GXBLEND_INV_SRC_ALPHA; + blendDesc.renderTarget[0].blendOpColor = GXBLEND_OP_ADD; + blendDesc.renderTarget[0].blendOpAlpha = GXBLEND_OP_ADD; + + /*blendDesc.renderTarget[0].blendSrcColor = GXBLEND_DEST_COLOR; + blendDesc.renderTarget[0].blendDestColor = GXBLEND_SRC_COLOR; + blendDesc.renderTarget[0].blendSrcAlpha = GXBLEND_DEST_ALPHA; + blendDesc.renderTarget[0].blendDestAlpha = GXBLEND_SRC_ALPHA; + blendDesc.renderTarget[0].blendOpColor = GXBLEND_OP_ADD; + blendDesc.renderTarget[0].blendOpAlpha = GXBLEND_OP_ADD;*/ + + blendDesc.renderTarget[0].useBlend = TRUE; + + blendDesc.renderTarget[1].blendSrcColor = GXBLEND_ZERO; + blendDesc.renderTarget[1].blendDestColor = GXBLEND_ONE; + + blendDesc.renderTarget[2].blendSrcColor = GXBLEND_ONE; + blendDesc.renderTarget[2].blendDestColor = GXBLEND_ZERO; + + blendDesc.renderTarget[3].blendSrcColor = GXBLEND_ONE; + blendDesc.renderTarget[3].blendDestColor = GXBLEND_ZERO; + + blendDesc.renderTarget[1].useBlend = FALSE; + blendDesc.renderTarget[2].useBlend = FALSE; + blendDesc.renderTarget[3].useBlend = FALSE; + + blendDesc.useIndependentBlend = TRUE; + + m_pBlendState = pDevice->createBlendState(&blendDesc); + + + GXDepthStencilDesc dsDesc = {}; + dsDesc.cmpFuncDepth = GXCMP_GREATER_EQUAL; + dsDesc.useDepthWrite = FALSE; + m_pDSState = pDevice->createDepthStencilState(&dsDesc); + + + m_pWorldBuffer = pDevice->createConstantBuffer(sizeof(SMMATRIX)); + m_pWorldBuffer->update(&SMMatrixIdentity()); +} + +void CDecalProvider::onDecalReleased(CDecal *pDecal) +{ + ScopedSpinLock lock(m_slMemDecals); + m_memDecals.Delete(pDecal); +} + +void CDecalProvider::onDecalEmptied(CDecal *pDecal) +{ + int idx = -1; + { + ScopedSpinLock lock(m_slTempDecals); + + idx = m_aTempDecals.indexOf(pDecal); + if(idx >= 0) + { + m_aTempDecals.erase(idx); + } + } + + if(idx >= 0) + { + mem_release(pDecal); + } +} + +void CDecalProvider::updateDecal(CDecal *pDecal) +{ + add_ref(pDecal); + m_qDecalsForUpdate.push(pDecal); +} + +void CDecalProvider::update() +{ + CDecal *pDecal; + while(m_qDecalsForUpdate.pop(&pDecal)) + { + pDecal->update( +#ifdef DECAL_DEBUG_DRAW + m_pDebugRenderer +#endif + ); + mem_release(pDecal); + } +} + +CDecal* CDecalProvider::allocDecal() +{ + ScopedSpinLock lock(m_slMemDecals); + return(m_memDecals.Alloc(m_pQuery, m_pRender, this)); +} + +CModelOverlay* CDecalProvider::allocOverlay(CDecal *pDecal, IXMaterial *pMaterial, Array<XResourceModelStaticVertex> &aVertices, const float3_t &vNormal) +{ + ScopedSpinLock lock(m_slMemOverlays); + return(m_memOverlays.Alloc(pDecal, pMaterial, aVertices, vNormal)); +} +void CDecalProvider::freeOverlay(CModelOverlay *pOverlay) +{ + ScopedSpinLock lock(m_slMemOverlays); + m_memOverlays.Delete(pOverlay); +} + +void CDecalProvider::loadDecals() +{ + IXConfig *pConfig = m_pCore->newConfig(); + if(pConfig->open("config/decals/decals.cfg")) + { + UINT uSections = pConfig->getSectionCount(); + for(UINT i = 0; i < uSections; ++i) + { + const char *szSection = pConfig->getSectionName(i); + + int id; + if(pConfig->keyExists(szSection, "id") && sscanf(pConfig->getKey(szSection, "id"), "%d", &id)) + { + if(id < 0 || id >= XDT__COUNT) + { + LogError("Incorrect decal type id '%s'\n", szSection); + continue; + } + } + else + { + LogError("Unable to read decal id '%s'\n", szSection); + continue; + } + + const char *szTex = pConfig->getKey(szSection, "tex"); + if(!szTex) + { + LogError("Unable to read decal tex '%s'\n", szSection); + continue; + } + + const char *szScale = pConfig->getKey(szSection, "base_scale"); + if(szScale) + { + sscanf(szScale, "%f", &m_aDecalTypes[id].fBaseScale); + } + + + int j = 0; + + DecalTexRange rng; + char key[64]; + while(sprintf(key, "tex%d", j) && pConfig->keyExists(szSection, key)) + { + if(sscanf(pConfig->getKey(szSection, key), "[%d,%d,%d,%d]", &rng.xmin, &rng.ymin, &rng.xmax, &rng.ymax) != 4) + { + LogError("Unable to read decal tex coords \"%s\" \"%s\"\n", pConfig->getKey(szSection, key), szSection); + } + else + { + m_aDecalTypes[id].aTextures.push_back(rng); + } + j++; + } + + m_pMaterialSystem->loadMaterial(szTex, &m_aDecalTypes[id].pMaterial); + } + } + + mem_release(pConfig); +} + +const CDecalProvider::DecalType* CDecalProvider::getDecalType(XDECAL_TYPE type) +{ + if(type < 0 || type >= XDT__COUNT) + { + LogError("Incorrect decal type %d\n", type); + return(NULL); + } + return(&m_aDecalTypes[type]); +} + +void CDecalProvider::computeBasis(SMMATRIX *pmOut, const float3_t &vSurfaceNormal, const float3 *pvSAxis) +{ + // s, t, textureSpaceNormal (S cross T = textureSpaceNormal(N)) + // + // +---->S + // /| + // / | + // N |T + // + // S = textureSpaceBasis[0] + // T = textureSpaceBasis[1] + // N = textureSpaceBasis[2] + + // Get the surface normal. + pmOut->r[2] = vSurfaceNormal; + + if(pvSAxis) + { + // T = N cross S + pmOut->r[1] = SMVector3Cross(pmOut->r[2], *pvSAxis); + + // Name sure they aren't parallel or antiparallel + // In that case, fall back to the normal algorithm. + if(SMVector3Dot(pmOut->r[1], pmOut->r[1]) > 1e-6) + { + // S = T cross N + pmOut->r[0] = SMVector3Cross(pmOut->r[1], pmOut->r[2]); + + pmOut->r[0] = SMVector3Normalize(pmOut->r[0]); + pmOut->r[1] = SMVector3Normalize(pmOut->r[1]); + return; + } + + // Fall through to the standard algorithm for parallel or antiparallel + } + + // floor/ceiling? + if(fabs(vSurfaceNormal.y) > SIN_45_DEGREES) + { + pmOut->r[0] = float4(1.0f, 0.0f, 0.0f, 0.0f); + + // T = N cross S + pmOut->r[1] = SMVector3Cross(pmOut->r[2], pmOut->r[0]); + + // S = T cross N + pmOut->r[0] = SMVector3Cross(pmOut->r[1], pmOut->r[2]); + } + // wall + else + { + pmOut->r[1] = float4(0.0f, -1.0f, 0.0f, 0.0f); + + // S = T cross N + pmOut->r[0] = SMVector3Cross(pmOut->r[1], pmOut->r[2]); + // T = N cross S + pmOut->r[1] = SMVector3Cross(pmOut->r[2], pmOut->r[0]); + } + + pmOut->r[0] = SMVector3Normalize(pmOut->r[0]); + pmOut->r[1] = SMVector3Normalize(pmOut->r[1]); +} + +void CDecalProvider::addTempDecal(CDecal *pDecal) +{ + ScopedSpinLock lock(m_slTempDecals); + + static const int *r_maxdecals = m_pCore->getConsole()->getPCVarInt("r_maxdecals"); + if(m_aTempDecals.size() > *r_maxdecals) + { + for(UINT i = (UINT)*r_maxdecals, l = m_aTempDecals.size(); i < l; ++i) + { + mem_release(m_aTempDecals[i]); + } + } + m_aTempDecals.reserve(*r_maxdecals); + + TODO("Use ring array for that"); + if(m_aTempDecals.size() == *r_maxdecals) + { + mem_release(m_aTempDecals[0]); + m_aTempDecals.erase(0); + } + m_aTempDecals.push_back(pDecal); +} diff --git a/source/anim/DecalProvider.h b/source/anim/DecalProvider.h new file mode 100644 index 0000000000000000000000000000000000000000..cc1565b623ab3af057ba9aa44c17982173bc1bc3 --- /dev/null +++ b/source/anim/DecalProvider.h @@ -0,0 +1,104 @@ +#ifndef __DECALPROVIDER_H +#define __DECALPROVIDER_H + +#include <xcommon/resource/IXDecalProvider.h> +#include <xcommon/IXCore.h> +#include "RenderableVisibility.h" +//#include <mtrl/IXMaterialSystem.h> +//#include <common/ConcurrentQueue.h> +//#include <xcommon/XEvents.h> +#include <xcommon/IXScene.h> +#include <common/queue.h> +#include "Decal.h" + +#define SIN_45_DEGREES 0.70710678118654752440084436210485f + +class CDecalProvider: public IXUnknownImplementation<IXDecalProvider> +{ +public: + CDecalProvider(IXCore *pCore, CDynamicModelProvider *pProviderDynamic); + ~CDecalProvider(); + + void render(CRenderableVisibility *pVisibility); + + void XMETHODCALLTYPE shootDecal(XDECAL_TYPE type, const float3 &vWorldPos, const float3 &vNormal, float fScale = 1.0f, const float3 *pvSAxis = NULL) override; + + void computeVisibility(const float3 &vHintDir, CRenderableVisibility *pVisibility); + + void setDevice(IGXDevice *pDevice); + + void onDecalReleased(CDecal *pDecal); + + void onDecalEmptied(CDecal *pDecal); + + void updateDecal(CDecal *pDecal); + + void update(); + + CModelOverlay* allocOverlay(CDecal *pDecal, IXMaterial *pMaterial, Array<XResourceModelStaticVertex> &aVertices, const float3_t &vNormal); + void freeOverlay(CModelOverlay *pOverlay); + +private: + struct DecalTexRange + { + int xmin; + int ymin; + int xmax; + int ymax; + }; + + struct DecalType + { + Array<DecalTexRange> aTextures; + float fBaseScale = 1.0f; + IXMaterial *pMaterial = NULL; + }; + + /*struct TempDecal + { + CDecal *pDecal; + };*/ + +private: + IXCore *m_pCore = NULL; + CDynamicModelProvider *m_pProviderDynamic = NULL; + IGXDevice *m_pDevice = NULL; + IXMaterialSystem *m_pMaterialSystem = NULL; + IXRender *m_pRender = NULL; + IXRenderUtils *m_pRenderUtils = NULL; + IXSceneQuery *m_pQuery = NULL; + + IGXConstantBuffer *m_pWorldBuffer = NULL; + + IGXBlendState *m_pBlendState = NULL; + IGXDepthStencilState *m_pDSState = NULL; + + DecalType m_aDecalTypes[XDT__COUNT]; + +#ifdef DECAL_DEBUG_DRAW + IXGizmoRenderer *m_pDebugRenderer = NULL; +#endif + + MemAlloc<CDecal> m_memDecals; + MemAlloc<CModelOverlay> m_memOverlays; + SpinLock m_slMemDecals; + SpinLock m_slMemOverlays; + + Array<CDecal*> m_aTempDecals; + SpinLock m_slTempDecals; + + Queue<CDecal*> m_qDecalsForUpdate; + +private: + void loadDecals(); + + const DecalType* getDecalType(XDECAL_TYPE type); + + void computeBasis(SMMATRIX *pmOut, const float3_t &vSurfaceNormal, const float3 *pvSAxis = NULL); + + CDecal* allocDecal(); + + void addTempDecal(CDecal *pDecal); +}; + +#endif diff --git a/source/anim/DynamicModel.cpp b/source/anim/DynamicModel.cpp index 0a79dbe82ff68b47fe0a024e1a8761ada11a8a6d..4aaf08f7a60b3c21890f8c30c047d24a6d09aa21 100644 --- a/source/anim/DynamicModel.cpp +++ b/source/anim/DynamicModel.cpp @@ -31,6 +31,8 @@ CDynamicModel::~CDynamicModel() mem_release(m_pWorldBuffer); mem_release(m_pColorBuffer); mem_release(m_pSceneObject); + + SAFE_CALL(m_pOverlay, onModelRemoved); } void CDynamicModel::initGPUresources() @@ -209,11 +211,11 @@ void CDynamicModel::_updateAABB() const m_vLocalMin = (float3)SMVectorMin( SMVectorMin(SMVectorMin(vCurrent[0], vCurrent[1]), SMVectorMin(vCurrent[2], vCurrent[3])), SMVectorMin(SMVectorMin(vCurrent[4], vCurrent[5]), SMVectorMin(vCurrent[6], vCurrent[7])) - ); + ); m_vLocalMax = (float3)SMVectorMax( SMVectorMax(SMVectorMax(vCurrent[0], vCurrent[1]), SMVectorMax(vCurrent[2], vCurrent[3])), SMVectorMax(SMVectorMax(vCurrent[4], vCurrent[5]), SMVectorMax(vCurrent[6], vCurrent[7])) - ); + ); m_isLocalAABBvalid = true; } @@ -466,3 +468,19 @@ UINT XMETHODCALLTYPE CDynamicModel::getLayer() { return(m_uLayer); } + +CModelOverlay* CDynamicModel::getOverlay() +{ + return(m_pOverlay); +} + +void CDynamicModel::setOverlay(CModelOverlay *pOverlay) +{ + m_pOverlay = pOverlay; +} + +bool CDynamicModel::isStatic() +{ + TODO("Implement me"); + return(false); +} diff --git a/source/anim/DynamicModel.h b/source/anim/DynamicModel.h index 836ebdda06dc00c7aafa6efa8f93dd7fe363cd42..1cb519e96c223e121cfc501a1f2a83ac3e12176c 100644 --- a/source/anim/DynamicModel.h +++ b/source/anim/DynamicModel.h @@ -4,6 +4,7 @@ #include <xcommon/resource/IXModel.h> #include <xcommon/IXScene.h> #include "DynamicModelShared.h" +#include "ModelOverlay.h" class CDynamicModel final: public IXUnknownImplementation<IXDynamicModel> { @@ -58,7 +59,13 @@ public: void XMETHODCALLTYPE setLayer(UINT uLayer) override; UINT XMETHODCALLTYPE getLayer() override; -protected: + + CModelOverlay* getOverlay(); + void setOverlay(CModelOverlay *pOverlay); + + bool isStatic(); + +private: CDynamicModelProvider *m_pProvider; CDynamicModelShared *m_pShared; IGXDevice *m_pDevice; @@ -81,11 +88,14 @@ protected: mutable float3_t m_vLocalMin; mutable float3_t m_vLocalMax; + UINT m_uLayer = 0; + + CModelOverlay *m_pOverlay = NULL; + +private: void _updateAABB() const; void XMETHODCALLTYPE FinalRelease() override; - - UINT m_uLayer = 0; }; #endif diff --git a/source/anim/DynamicModelProvider.cpp b/source/anim/DynamicModelProvider.cpp index 4ce0af11d6c8855f0963e177aa8165be14fbcbe7..6faeecb85c054049e058a948ea235ac334ee37d8 100644 --- a/source/anim/DynamicModelProvider.cpp +++ b/source/anim/DynamicModelProvider.cpp @@ -541,6 +541,8 @@ void CDynamicModelProvider::computeVisibility(const IXFrustum *pFrustum, const f { XPROFILE_FUNCTION(); + TODO("Make some hints to not to compute stages is not required for render. eg selfillum stage for shadows"); + if(pCamera && pCamera->getProjectionMode() == XCPM_PERSPECTIVE) { FIXME("Use actual target width!"); diff --git a/source/anim/ModelOverlay.cpp b/source/anim/ModelOverlay.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4514a00bb959c085adeb046a8821b47024898ecc --- /dev/null +++ b/source/anim/ModelOverlay.cpp @@ -0,0 +1,42 @@ +#include "ModelOverlay.h" +#include "Decal.h" + +CModelOverlay::CModelOverlay(CDecal *pDecal, IXMaterial *pMaterial, Array<XResourceModelStaticVertex> &aVertices, const float3_t &vNormal): + m_pDecal(pDecal), + m_pMaterial(pMaterial), + m_vNormal(vNormal) +{ + m_aVertices.swap(aVertices); +} + +const Array<XResourceModelStaticVertex>& CModelOverlay::getVertices() +{ + return(m_aVertices); +} + +CModelOverlay* CModelOverlay::getNextOverlay() +{ + return(m_pNextOverlay); +} +void CModelOverlay::setNextOverlay(CModelOverlay *pOverlay) +{ + m_pNextOverlay = pOverlay; +} + +IXMaterial* CModelOverlay::getMaterial() +{ + return(m_pMaterial); +} + +const float3_t& CModelOverlay::getNormal() +{ + return(m_vNormal); +} + +void CModelOverlay::onModelRemoved() +{ + SAFE_CALL(m_pNextOverlay, onModelRemoved); + + //notify decal + m_pDecal->onOverlayRemoved(this); +} diff --git a/source/anim/ModelOverlay.h b/source/anim/ModelOverlay.h new file mode 100644 index 0000000000000000000000000000000000000000..f16fb7c13919bca51a2fcb337cee5441ea75c06c --- /dev/null +++ b/source/anim/ModelOverlay.h @@ -0,0 +1,42 @@ +#ifndef __MODELOVERLAY_H +#define __MODELOVERLAY_H + +#include <xcommon/resource/IXModel.h> +#include <graphix/graphix.h> +#include <mtrl/IXMaterial.h> + +class IXMaterialSystem; +class CDecal; + +class CModelOverlay final +{ +public: + CModelOverlay(CDecal *pDecal, IXMaterial *pMaterial, Array<XResourceModelStaticVertex> &aVertices, const float3_t &vNormal); + + const Array<XResourceModelStaticVertex>& getVertices(); + + CModelOverlay* getNextOverlay(); + void setNextOverlay(CModelOverlay *pOverlay); + + IXMaterial* getMaterial(); + + const float3_t& getNormal(); + + void onModelRemoved(); + +private: + // Model's overlays stored as linked list + CModelOverlay *m_pNextOverlay = NULL; + CDecal *m_pDecal; + + TODO("Use memory pool"); + Array<XResourceModelStaticVertex> m_aVertices; + + IXMaterial *m_pMaterial = NULL; + + float3_t m_vNormal; + + bool m_isTransparent = false; +}; + +#endif diff --git a/source/anim/Renderable.cpp b/source/anim/Renderable.cpp index 81ac9ab42dbb1d20f0597036e2df28da16bcfc76..52ad958bccb76178cbb80d6718cf70d4f22c2fed 100644 --- a/source/anim/Renderable.cpp +++ b/source/anim/Renderable.cpp @@ -1,10 +1,12 @@ #include "Renderable.h" #include "RenderableVisibility.h" +#include "DecalProvider.h" -CRenderable::CRenderable(ID idPlugin, CAnimatedModelProvider *pProviderAnimated, CDynamicModelProvider *pProviderDynamic): +CRenderable::CRenderable(ID idPlugin, CAnimatedModelProvider *pProviderAnimated, CDynamicModelProvider *pProviderDynamic, CDecalProvider *pProviderDecal): m_idPlugin(idPlugin), m_pAnimatedModelProvider(pProviderAnimated), - m_pDynamicModelProvider(pProviderDynamic) + m_pDynamicModelProvider(pProviderDynamic), + m_pDecalProvider(pProviderDecal) { } @@ -40,6 +42,7 @@ void XMETHODCALLTYPE CRenderable::renderStage(X_RENDER_STAGE stage, IXRenderable case XRS_GBUFFER: m_pAnimatedModelProvider->render(pVis); m_pDynamicModelProvider->render(false, pVis); + m_pDecalProvider->render(pVis); break; case XRS_SHADOWS: m_pAnimatedModelProvider->render(pVis); @@ -89,6 +92,7 @@ void XMETHODCALLTYPE CRenderable::startup(IXRender *pRender, IXMaterialSystem *p m_pAnimatedModelProvider->setDevice(m_pDevice); m_pDynamicModelProvider->setDevice(m_pDevice); + m_pDecalProvider->setDevice(m_pDevice); } void XMETHODCALLTYPE CRenderable::shutdown() { @@ -96,7 +100,7 @@ void XMETHODCALLTYPE CRenderable::shutdown() void XMETHODCALLTYPE CRenderable::newVisData(IXRenderableVisibility **ppVisibility) { - *ppVisibility = new CRenderableVisibility(m_idPlugin, m_pAnimatedModelProvider, m_pDynamicModelProvider); + *ppVisibility = new CRenderableVisibility(m_idPlugin, m_pAnimatedModelProvider, m_pDynamicModelProvider, m_pDecalProvider); } IXMaterialSystem* CRenderable::getMaterialSystem() diff --git a/source/anim/Renderable.h b/source/anim/Renderable.h index cafcc2fbfc260553f1c8f3cf5f857b0b7919a2e8..85f03cc5139e0e8d2d3a4b34f849466849376c73 100644 --- a/source/anim/Renderable.h +++ b/source/anim/Renderable.h @@ -9,7 +9,7 @@ class CRenderable: public IXUnknownImplementation<IXRenderable> { public: - CRenderable(ID idPlugin, CAnimatedModelProvider *pProviderAnimated, CDynamicModelProvider *pProviderDynamic); + CRenderable(ID idPlugin, CAnimatedModelProvider *pProviderAnimated, CDynamicModelProvider *pProviderDynamic, CDecalProvider *pProviderDecal); XIMPLEMENT_VERSION(IXRENDERABLE_VERSION); @@ -56,6 +56,7 @@ protected: CAnimatedModelProvider *m_pAnimatedModelProvider = NULL; CDynamicModelProvider *m_pDynamicModelProvider = NULL; + CDecalProvider *m_pDecalProvider = NULL; }; #endif diff --git a/source/anim/RenderableVisibility.cpp b/source/anim/RenderableVisibility.cpp index d37373b47f114ba2fe3b7f3101020a0965ec53a3..f012c9831787a31e0f85b2e09e058b7df230044b 100644 --- a/source/anim/RenderableVisibility.cpp +++ b/source/anim/RenderableVisibility.cpp @@ -1,11 +1,13 @@ #include "RenderableVisibility.h" #include "AnimatedModelProvider.h" #include "DynamicModelProvider.h" +#include "DecalProvider.h" -CRenderableVisibility::CRenderableVisibility(ID idPlugin, CAnimatedModelProvider *pProviderAnimated, CDynamicModelProvider *pProviderDynamic): +CRenderableVisibility::CRenderableVisibility(ID idPlugin, CAnimatedModelProvider *pProviderAnimated, CDynamicModelProvider *pProviderDynamic, CDecalProvider *pProviderDecal): m_idPlugin(idPlugin), m_pProviderAnimated(pProviderAnimated), - m_pProviderDynamic(pProviderDynamic) + m_pProviderDynamic(pProviderDynamic), + m_pProviderDecal(pProviderDecal) { } @@ -38,6 +40,7 @@ void CRenderableVisibility::updateForCamera(IXCamera *pCamera, const IXRenderabl IXFrustum *pFrustum = pCamera->getFrustum(); m_pProviderAnimated->computeVisibility(pFrustum, this, pCamera->getLayerMask(), pRef); m_pProviderDynamic->computeVisibility(pFrustum, pCamera->getLook(), this, pCamera->getLayerMask(), pRef, pCamera); + m_pProviderDecal->computeVisibility(pCamera->getLook(), this); mem_release(pFrustum); } @@ -52,6 +55,7 @@ void CRenderableVisibility::updateForFrustum(const IXFrustum *pFrustum, UINT bmL m_pProviderAnimated->computeVisibility(pFrustum, this, bmLayers, pRef); m_pProviderDynamic->computeVisibility(pFrustum, float3(), this, bmLayers, pRef); + m_pProviderDecal->computeVisibility(float3(), this); } static void SortRenderList(Array<CDynamicModel*> &aList) @@ -139,7 +143,8 @@ void CRenderableVisibility::append(const IXRenderableVisibility *pOther_) MergeArrays(m_aSelfillumList, pOther->m_aSelfillumList); SortRenderList(m_aSelfillumList); - //! @todo implement for transparency too! + TODO("Implement for transparency too!"); + TODO("Implement for decals too!"); } void CRenderableVisibility::substract(const IXRenderableVisibility *pOther_) @@ -159,7 +164,8 @@ void CRenderableVisibility::substract(const IXRenderableVisibility *pOther_) } } - //! @todo implement for transparency too! + TODO("Implement for transparency too!"); + TODO("Implement for decals too!"); } void CRenderableVisibility::setItemCount(UINT uCount) @@ -257,3 +263,8 @@ Array<CDynamicModel*>& CRenderableVisibility::getTransparentList() { return(m_aTransparentList); } + +CRenderableVisibility::OverlayData& CRenderableVisibility::getOverlayData() +{ + return(m_overlayData); +} diff --git a/source/anim/RenderableVisibility.h b/source/anim/RenderableVisibility.h index 6d96b4998ce3e48771a923a55da214e68c183b2e..8032e469b6fd1bdb59f61f1b95513719956feac8 100644 --- a/source/anim/RenderableVisibility.h +++ b/source/anim/RenderableVisibility.h @@ -2,14 +2,16 @@ #define __RENDERABLE_VISIBILITY_H #include <xcommon/IXRenderable.h> +#include <xcommon/resource/IXResourceModel.h> class CAnimatedModelProvider; class CDynamicModelProvider; +class CDecalProvider; class CDynamicModel; class CRenderableVisibility final: public IXUnknownImplementation<IXRenderableVisibility> { public: - CRenderableVisibility(ID idPlugin, CAnimatedModelProvider *m_pProviderAnimated, CDynamicModelProvider *m_pProviderDynamic); + CRenderableVisibility(ID idPlugin, CAnimatedModelProvider *pProviderAnimated, CDynamicModelProvider *pProviderDynamic, CDecalProvider *pProviderDecal); ~CRenderableVisibility(); ID getPluginId() const override; @@ -40,6 +42,24 @@ public: UINT uLod; IXMaterial *pMaterial; }; + + struct OverlaySubset + { + IXMaterial *pMaterial; + UINT uStartVertex; + UINT uStartIndex; + UINT uQuadCount; + }; + + struct OverlayData + { + Array<XResourceModelStaticVertexGPU> aVertices; + Array<OverlaySubset> aSubsets; + IGXVertexBuffer *pVB = NULL; + IGXRenderBuffer *pRB = NULL; + IGXIndexBuffer *pIB = NULL; + UINT uVertexBufferAllocSize = 0; + }; void setItemCount(UINT uCount); item_s* getItem(UINT uIndex); @@ -57,16 +77,18 @@ public: Array<CDynamicModel*>& getRenderList(); Array<CDynamicModel*>& getTransparentList(); Array<CDynamicModel*>& getSelfillumList(); + OverlayData& getOverlayData(); IXOcclusionCuller* getCuller() { return(m_pOcclusionCuller); } -protected: +private: ID m_idPlugin; CAnimatedModelProvider *m_pProviderAnimated; CDynamicModelProvider *m_pProviderDynamic; + CDecalProvider *m_pProviderDecal; IXOcclusionCuller *m_pOcclusionCuller = NULL; Array<item_s> m_aItems; @@ -75,6 +97,8 @@ protected: Array<CDynamicModel*> m_aRenderList; Array<CDynamicModel*> m_aTransparentList; Array<CDynamicModel*> m_aSelfillumList; + + OverlayData m_overlayData; }; #endif diff --git a/source/anim/Updatable.cpp b/source/anim/Updatable.cpp index d04c08cd13b194c8bac23e7493ff8084328d2471..2a48fd286fed63e6c019b14c508c3ed413305dd1 100644 --- a/source/anim/Updatable.cpp +++ b/source/anim/Updatable.cpp @@ -1,8 +1,9 @@ #include "Updatable.h" -CUpdatable::CUpdatable(CAnimatedModelProvider *pAnimatedModelProvider, CDynamicModelProvider *pDynamicModelProvider): +CUpdatable::CUpdatable(CAnimatedModelProvider *pAnimatedModelProvider, CDynamicModelProvider *pDynamicModelProvider, CDecalProvider *pDecalProvider): m_pAnimatedModelProvider(pAnimatedModelProvider), - m_pDynamicModelProvider(pDynamicModelProvider) + m_pDynamicModelProvider(pDynamicModelProvider), + m_pDecalProvider(pDecalProvider) { } @@ -23,6 +24,8 @@ ID CUpdatable::run(float fDelta) m_pDynamicModelProvider->update(); + m_pDecalProvider->update(); + return(-1); } @@ -30,4 +33,5 @@ void CUpdatable::sync() { m_pAnimatedModelProvider->sync(); m_pDynamicModelProvider->sync(); + //m_pDecalProvider->update(); } diff --git a/source/anim/Updatable.h b/source/anim/Updatable.h index 7123026acff06ca4a1edf9f3efeabeeec7ecc1e1..54089837a4775ebad5c966dc60b1d1c316b9545d 100644 --- a/source/anim/Updatable.h +++ b/source/anim/Updatable.h @@ -4,11 +4,12 @@ #include <xcommon/IXUpdatable.h> #include "AnimatedModelProvider.h" #include "DynamicModelProvider.h" +#include "DecalProvider.h" class CUpdatable: public IXUnknownImplementation<IXUpdatable> { public: - CUpdatable(CAnimatedModelProvider *pAnimatedModelProvider, CDynamicModelProvider *pDynamicModelProvider); + CUpdatable(CAnimatedModelProvider *pAnimatedModelProvider, CDynamicModelProvider *pDynamicModelProvider, CDecalProvider *pDecalProvider); UINT startup() override; void shutdown() override; @@ -19,6 +20,7 @@ public: protected: CAnimatedModelProvider *m_pAnimatedModelProvider; CDynamicModelProvider *m_pDynamicModelProvider; + CDecalProvider *m_pDecalProvider; }; #endif diff --git a/source/anim/plugin_main.cpp b/source/anim/plugin_main.cpp index 307928b5c6c0bcc1ad61feda2a988a7dda710a94..d5dfadd466d5f49a17dc3b4d3fb3c899dd03e17e 100644 --- a/source/anim/plugin_main.cpp +++ b/source/anim/plugin_main.cpp @@ -4,6 +4,7 @@ #include "Updatable.h" #include "AnimatedModelProvider.h" #include "DynamicModelProvider.h" +#include "DecalProvider.h" class CLevelSizeEventListener final: public IEventListener<XEventLevelSize> { @@ -57,8 +58,9 @@ public: { m_pAnimatedModelProvider = new CAnimatedModelProvider(m_pCore); m_pDynamicModelProvider = new CDynamicModelProvider(m_pCore); - m_pRenderable = new CRenderable(getID(), m_pAnimatedModelProvider, m_pDynamicModelProvider); - m_pUpdatable = new CUpdatable(m_pAnimatedModelProvider, m_pDynamicModelProvider); + m_pDecalProvider = new CDecalProvider(m_pCore, m_pDynamicModelProvider); + m_pRenderable = new CRenderable(getID(), m_pAnimatedModelProvider, m_pDynamicModelProvider, m_pDecalProvider); + m_pUpdatable = new CUpdatable(m_pAnimatedModelProvider, m_pDynamicModelProvider, m_pDecalProvider); m_pLevelSizeEventListener = new CLevelSizeEventListener(m_pAnimatedModelProvider, m_pDynamicModelProvider); m_pLevelLoadEventListener = new CLoadLevelEventListener(m_pRenderable); @@ -82,11 +84,12 @@ public: mem_delete(m_pUpdatable); mem_delete(m_pAnimatedModelProvider); mem_delete(m_pDynamicModelProvider); +// mem_delete(m_pDecalProvider); } UINT XMETHODCALLTYPE getInterfaceCount() override { - return(4); + return(5); } const XGUID* XMETHODCALLTYPE getInterfaceGUID(UINT id) override { @@ -105,6 +108,9 @@ public: case 3: s_guid = IXDYNAMICMODELPROVIDER_GUID; break; + case 4: + s_guid = IXDECALPROVIDER_GUID; + break; default: return(NULL); } @@ -149,6 +155,15 @@ public: add_ref(m_pDynamicModelProvider); *ppOut = m_pDynamicModelProvider; break; + + case 4: + if(!m_pDecalProvider) + { + init(); + } + add_ref(m_pDecalProvider); + *ppOut = m_pDecalProvider; + break; default: *ppOut = NULL; @@ -161,6 +176,7 @@ protected: IXCore *m_pCore = NULL; CAnimatedModelProvider *m_pAnimatedModelProvider = NULL; CDynamicModelProvider *m_pDynamicModelProvider = NULL; + CDecalProvider *m_pDecalProvider = NULL; CLevelSizeEventListener *m_pLevelSizeEventListener = NULL; CLoadLevelEventListener *m_pLevelLoadEventListener = NULL; }; diff --git a/source/common b/source/common index 8103fff484d6dd1f7006973af8bf581334d8ea1e..ef2606734c2075678c992d5806d952d4c835d853 160000 --- a/source/common +++ b/source/common @@ -1 +1 @@ -Subproject commit 8103fff484d6dd1f7006973af8bf581334d8ea1e +Subproject commit ef2606734c2075678c992d5806d952d4c835d853 diff --git a/source/game/BaseAmmo.cpp b/source/game/BaseAmmo.cpp index 8714102d3348320990b7f1ca63891f49ba742bac..35abac1eea8a590650ec7e3abe5d2d9420f35cb7 100644 --- a/source/game/BaseAmmo.cpp +++ b/source/game/BaseAmmo.cpp @@ -9,11 +9,10 @@ See the license in LICENSE #include "BaseTool.h" #include <particles/sxparticles.h> #include <mtrl/IXMaterial.h> -#include <decals/sxdecals.h> //#include <BulletCollision/NarrowPhaseCollision/btRaycastCallback.h> //! TODO Reimplement me! -#define SMtrl_MtlGetPhysicMaterial(id) MTLTYPE_PHYSIC_METAL +#define SMtrl_MtlGetPhysicMaterial(id) MTLTYPE_PHYSIC_CONCRETE #define SMtrl_MtlGetDurability(id) 1.0f #define SMtrl_MtlGetDensity(id) 1.0f #define SMtrl_MtlGetHitChance(id) 1.0f @@ -107,8 +106,7 @@ void CBaseAmmo::fire(const float3 &_vStart, const float3 &_vDir, CBaseCharacter }); for(int i = 0, l = aHitPoints.size(); i < l; ++i) { - - ID idMtl = -1; // SPhysics_GetMtlID(aHitPoints[i].pCollisionObject, &aHitPoints[i].shapeInfo); + ID idMtl = 0; // SPhysics_GetMtlID(aHitPoints[i].pCollisionObject, &aHitPoints[i].shapeInfo); if(ID_VALID(idMtl) && !aHitPoints[i].isExit) { float fHitChance = SMtrl_MtlGetHitChance(idMtl); @@ -184,6 +182,15 @@ void CBaseAmmo::fire(const float3 &_vStart, const float3 &_vDir, CBaseCharacter // restart fire with new dir and speed float3 vStart2 = aHitPoints[i].vPosition + SMVector3Normalize(vDir) * 0.001f; + for(int j = i + 1; j < l; ++j) + { + if(aHitPoints[j].isExit) + { + // restart at exit point + vStart2 = aHitPoints[i].vPosition + SMVector3Normalize(vDir) * 0.001f; + } + } + vDir = vNewDir; fSpeed = fNewSpeed; @@ -240,26 +247,26 @@ void CBaseAmmo::shootDecal(const float3 &vPos, const float3 &vNormal, ID idMtl) if(ID_VALID(idMtl)) { MTLTYPE_PHYSIC type = SMtrl_MtlGetPhysicMaterial(idMtl); - DECAL_TYPE decalType = DECAL_TYPE_CONCRETE; + XDECAL_TYPE decalType = XDT_CONCRETE; switch(type) { case MTLTYPE_PHYSIC_METAL: - decalType = DECAL_TYPE_METAL; + decalType = XDT_METAL; break; case MTLTYPE_PHYSIC_FLESH: - decalType = DECAL_TYPE_FLESH; + decalType = XDT_FLESH; break; case MTLTYPE_PHYSIC_GROUD_SAND: - decalType = DECAL_TYPE_EARTH; + decalType = XDT_EARTH; break; case MTLTYPE_PHYSIC_PLASTIC: - decalType = DECAL_TYPE_PLASTIC; + decalType = XDT_PLASTIC; break; case MTLTYPE_PHYSIC_TREE: - decalType = DECAL_TYPE_WOOD; + decalType = XDT_WOOD; break; } - SXDecals_ShootDecal(decalType, vPos, vNormal); + SAFE_CALL(GetDecalProvider(), shootDecal, decalType, vPos, vNormal); //SPE_EffectPlayByName("create_decal_test", &aHitPoints[i].vPosition, &aHitPoints[i].vNormal); } @@ -267,7 +274,7 @@ void CBaseAmmo::shootDecal(const float3 &vPos, const float3 &vNormal, ID idMtl) void CBaseAmmo::shootBlood(const float3 &vPos, const float3 &vNormal) { - SXDecals_ShootDecal(DECAL_TYPE_BLOOD_BIG, vPos, vNormal); + SAFE_CALL(GetDecalProvider(), shootDecal, XDT_BLOOD_BIG, vPos, vNormal); } bool CBaseAmmo::shouldRecochet(const float3 &vPos, const float3 &vNormal, const float3 &vDir, ID idMtl, float fSpeed, float3 *pvNewDir, float *pfNewSpeed) diff --git a/source/game/BaseEntity.h b/source/game/BaseEntity.h index 53ecd6de3f3eb3fbfd47e08a0069ab9b61f2c086..ffc3451770dbd181be8380fa816de6491a427714 100644 --- a/source/game/BaseEntity.h +++ b/source/game/BaseEntity.h @@ -36,11 +36,13 @@ See the license in LICENSE #include <light/IXLightSystem.h> +#include <xcommon/resource/IXDecalProvider.h> + #pragma pointers_to_members(full_generality, virtual_inheritance) IXRender* GetRender(); IXParticleSystem* GetParticleSystem(); - +IXDecalProvider* GetDecalProvider(); diff --git a/source/game/BaseTool.cpp b/source/game/BaseTool.cpp index 5d84a3a0fa5b3c5dfe5026fd5c4fe95d425a8bfb..ccc15248bd43e6211a5ace7862af189df0c4ef6a 100644 --- a/source/game/BaseTool.cpp +++ b/source/game/BaseTool.cpp @@ -7,7 +7,6 @@ See the license in LICENSE #include "BaseTool.h" #include <particles/sxparticles.h> -#include <decals/sxdecals.h> #include "Player.h" /*! \skydocent base_tool @@ -138,7 +137,8 @@ void CBaseTool::primaryAction(BOOL st) if(cb.hasHit()) { //shoot decal - SXDecals_ShootDecal(DECAL_TYPE_CONCRETE, BTVEC_F3(cb.m_hitPointWorld), BTVEC_F3(cb.m_hitNormalWorld)); + SAFE_CALL(GetDecalProvider(), shootDecal, XDT_CONCRETE, cb.m_result.vHitPoint, cb.m_result.vHitNormal); + //SXDecals_ShootDecal(DECAL_TYPE_CONCRETE, BTVEC_F3(cb.m_hitPointWorld), BTVEC_F3(cb.m_hitNormalWorld)); SPE_EffectPlayByName("fire", &BTVEC_F3(cb.m_hitPointWorld), &BTVEC_F3(cb.m_hitNormalWorld)); IXRigidBody *pRB = cb.m_result.pCollisionObject->asRigidBody(); diff --git a/source/game/GameData.cpp b/source/game/GameData.cpp index a759bab3943e33f7e5b6181e9951d5975667c0d7..ed4c747550e21407be8649c9c666cab2cfa48ada 100644 --- a/source/game/GameData.cpp +++ b/source/game/GameData.cpp @@ -81,6 +81,7 @@ static IXPhysics *g_pPhysics = NULL; static IXPhysicsWorld *g_pPhysWorld = NULL; static IXRender *g_pRender = NULL; static IXParticleSystem *g_pParticleSystem = NULL; +static IXDecalProvider *g_pDecalProvider = NULL; //########################################################################## @@ -114,6 +115,10 @@ IXParticleSystem* GetParticleSystem() { return(g_pParticleSystem); } +IXDecalProvider* GetDecalProvider() +{ + return(g_pDecalProvider); +} //########################################################################## @@ -489,6 +494,7 @@ GameData::GameData(HWND hWnd, bool isGame): g_pPhysics = (IXPhysics*)Core_GetIXCore()->getPluginManager()->getInterface(IXPHYSICS_GUID); g_pPhysWorld = g_pPhysics->getWorld(); g_pParticleSystem = (IXParticleSystem*)Core_GetIXCore()->getPluginManager()->getInterface(IXPARTICLESYSTEM_GUID); + g_pDecalProvider = (IXDecalProvider*)Core_GetIXCore()->getPluginManager()->getInterface(IXDECALPROVIDER_GUID); if(m_pLightSystem && false) { diff --git a/source/game/ZombieHands.cpp b/source/game/ZombieHands.cpp index 19b7df3658635fc40ebad62f6021db8467b8a3ec..4e8e4f1887143866e16efce5465d843585c807ef 100644 --- a/source/game/ZombieHands.cpp +++ b/source/game/ZombieHands.cpp @@ -1,7 +1,6 @@ #include "ZombieHands.h" #include "BaseCharacter.h" -#include <decals/sxdecals.h> #include "Tracer.h" /*! \skydocent wpn_zombie_hands @@ -52,7 +51,7 @@ void CZombieHands::actualShoot(float dt) if(cb.hasHit()) { - SXDecals_ShootDecal(DECAL_TYPE_BLOOD_BIG, BTVEC_F3(cb.m_hitPointWorld), BTVEC_F3(cb.m_hitNormalWorld)); + SAFE_CALL(GetDecalProvider(), shootDecal, XDT_BLOOD_BIG, cb.m_result.vHitPoint, cb.m_result.vHitNormal); if(cb.m_result.pCollisionObject->getUserPointer() && cb.m_result.pCollisionObject->getUserTypeId() == 1) { diff --git a/source/physics/PhyWorld.cpp b/source/physics/PhyWorld.cpp index 0dd249c57282fc0b1fd30fa45d528f3a1bc7590a..c4eb6d186be5e804983271a66b263e81adc9e567 100644 --- a/source/physics/PhyWorld.cpp +++ b/source/physics/PhyWorld.cpp @@ -56,7 +56,7 @@ struct XRayResultCallback: public btCollisionWorld::RayResultCallback m_hitPointWorld.setInterpolate3(m_rayFromWorld, m_rayToWorld, rayResult.m_hitFraction); m_result.vHitPoint = BTVEC_F3(m_hitPointWorld); - m_result.vHitNormal = BTVEC_F3(m_hitPointWorld); + m_result.vHitNormal = BTVEC_F3(m_hitNormalWorld); m_result.pCollisionObject = m_collisionObject->getUserIndex() == 2 ? (IXCollisionObject*)m_collisionObject->getUserPointer() : NULL; m_result.fHitFraction = rayResult.m_hitFraction; diff --git a/source/xcommon/resource/IXDecal.h b/source/xcommon/resource/IXDecal.h new file mode 100644 index 0000000000000000000000000000000000000000..f46c3df89d73af4c73069193cfd50984b57ccfd1 --- /dev/null +++ b/source/xcommon/resource/IXDecal.h @@ -0,0 +1,38 @@ +#ifndef __IXDECAL_H +#define __IXDECAL_H + +#include <gdefines.h> +#include "IXResourceModel.h" + +class IXDecal: public IXUnknown +{ +public: + virtual float3 XMETHODCALLTYPE getPosition() const = 0; + virtual void XMETHODCALLTYPE setPosition(const float3 &vPos) = 0; + + virtual SMQuaternion XMETHODCALLTYPE getOrientation() const = 0; + virtual void XMETHODCALLTYPE setOrientation(const SMQuaternion &qRot) = 0; + + virtual float3 XMETHODCALLTYPE getLocalBoundMin() const = 0; + virtual float3 XMETHODCALLTYPE getLocalBoundMax() const = 0; + virtual SMAABB XMETHODCALLTYPE getLocalBound() const = 0; + + virtual bool XMETHODCALLTYPE isEnabled() const = 0; + virtual void XMETHODCALLTYPE enable(bool yesNo) = 0; + + virtual bool XMETHODCALLTYPE rayTest(const float3 &vStart, const float3 &vEnd, float3 *pvOut = NULL, float3 *pvNormal = NULL, bool isRayInWorldSpace = true, bool bReturnNearestPoint = false) = 0; + + virtual void XMETHODCALLTYPE setLayer(UINT uLayer) = 0; + virtual UINT XMETHODCALLTYPE getLayer() = 0; + + virtual void XMETHODCALLTYPE setHeight(float fHeight) = 0; + virtual float XMETHODCALLTYPE getHeight() = 0; + + virtual void XMETHODCALLTYPE setTextureRangeU(const float2_t &vRange) = 0; + virtual float2_t XMETHODCALLTYPE getTextureRangeU() = 0; + + virtual void XMETHODCALLTYPE setTextureRangeV(const float2_t &vRange) = 0; + virtual float2_t XMETHODCALLTYPE getTextureRangeV() = 0; +}; + +#endif diff --git a/source/xcommon/resource/IXDecalProvider.h b/source/xcommon/resource/IXDecalProvider.h new file mode 100644 index 0000000000000000000000000000000000000000..c594d3028812fb392fe4f673e0e2276106a44018 --- /dev/null +++ b/source/xcommon/resource/IXDecalProvider.h @@ -0,0 +1,34 @@ +#ifndef __IXDECALPROVIDER_H +#define __IXDECALPROVIDER_H + +#include "IXDecal.h" + +// {878E7A1E-86D4-4967-9A6D-6054B4363119} +#define IXDECALPROVIDER_GUID DEFINE_XGUID(0x878e7a1e, 0x86d4, 0x4967, 0x9a, 0x6d, 0x60, 0x54, 0xb4, 0x36, 0x31, 0x19) + +enum XDECAL_TYPE +{ + XDT_CONCRETE = 0, + XDT_METAL, + XDT_GLASS, + XDT_PLASTIC, + XDT_WOOD, + XDT_FLESH, + XDT_EARTH, + XDT_BLOOD_BIG, + + XDT__COUNT +}; + +// Implemented in anim plugin +class IXDecalProvider: public IXUnknown +{ +public: + // create custom decal + //virtual bool XMETHODCALLTYPE newDecal(IXDecal **ppDecal) = 0; + + // create temporal standard decal at point/normal + virtual void XMETHODCALLTYPE shootDecal(XDECAL_TYPE type, const float3 &vWorldPos, const float3 &vNormal, float fScale = 1.0f, const float3 *pvSAxis = NULL) = 0; +}; + +#endif