You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/Cry3DEngine/DecalManager.cpp

1790 lines
63 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : draw, create decals on the world
#include "Cry3DEngine_precompiled.h"
#include "DecalManager.h"
#include "3dEngine.h"
#include <IStatObj.h>
#include "ObjMan.h"
#include "MatMan.h"
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include "RenderMeshMerger.h"
#include "RenderMeshUtils.h"
#include "VisAreas.h"
#include "DecalRenderNode.h"
#include "Environment/OceanEnvironmentBus.h"
#ifndef RENDER_MESH_TEST_DISTANCE
#define RENDER_MESH_TEST_DISTANCE (0.2f)
#endif
static const int MAX_ASSEMBLE_SIZE = 5;
CDecalManager::CDecalManager()
{
m_nCurDecal = 0;
memset(m_arrbActiveDecals, 0, sizeof(m_arrbActiveDecals));
}
CDecalManager::~CDecalManager()
{
CDecal::ResetStaticData();
}
bool CDecalManager::AdjustDecalPosition(CryEngineDecalInfo& DecalInfo, bool bMakeFatTest)
{
Matrix34A objMat, objMatInv;
Matrix33 objRot, objRotInv;
CStatObj* pEntObject = (CStatObj*)DecalInfo.ownerInfo.GetOwner(objMat);
if (!pEntObject || !pEntObject->GetRenderMesh() || !pEntObject->GetRenderTrisCount())
{
return false;
}
objRot = Matrix33(objMat);
objRot.NoScale(); // No scale.
objRotInv = objRot;
objRotInv.Invert();
float fWorldScale = objMat.GetColumn(0).GetLength(); // GetScale
float fWorldScaleInv = 1.0f / fWorldScale;
// transform decal into object space
objMatInv = objMat;
objMatInv.Invert();
// put into normal object space hit direction of projection
Vec3 vOS_HitDir = objRotInv.TransformVector(DecalInfo.vHitDirection).GetNormalized();
// put into position object space hit position
Vec3 vOS_HitPos = objMatInv.TransformPoint(DecalInfo.vPos);
vOS_HitPos -= vOS_HitDir * RENDER_MESH_TEST_DISTANCE * fWorldScaleInv;
_smart_ptr<IMaterial> pMat = DecalInfo.ownerInfo.pRenderNode ? DecalInfo.ownerInfo.pRenderNode->GetMaterial() : NULL;
Vec3 vOS_OutPos(0, 0, 0), vOS_OutNormal(0, 0, 0), vTmp;
IRenderMesh* pRM = pEntObject->GetRenderMesh();
AABB aabbRNode;
pRM->GetBBox(aabbRNode.min, aabbRNode.max);
Vec3 vOut(0, 0, 0);
if (!Intersect::Ray_AABB(Ray(vOS_HitPos, vOS_HitDir), aabbRNode, vOut))
{
return false;
}
if (!pRM || !pRM->GetVerticesCount())
{
return false;
}
if (RayRenderMeshIntersection(pRM, vOS_HitPos, vOS_HitDir, vOS_OutPos, vOS_OutNormal, false, 0, pMat))
{
// now check that none of decal sides run across edges
Vec3 srcp = vOS_OutPos + 0.01f * fWorldScaleInv * vOS_OutNormal; /// Rise hit point a little bit above hit plane.
Vec3 vDecalNormal = vOS_OutNormal;
float fMaxHitDistance = 0.02f * fWorldScaleInv;
// get decal directions
Vec3 vRi(0, 0, 0), vUp(0, 0, 0);
if (fabs(vOS_OutNormal.Dot(Vec3(0, 0, 1))) > 0.999f)
{ // horiz surface
vRi = Vec3(0, 1, 0);
vUp = Vec3(1, 0, 0);
}
else
{
vRi = vOS_OutNormal.Cross(Vec3(0, 0, 1));
vRi.Normalize();
vUp = vOS_OutNormal.Cross(vRi);
vUp.Normalize();
}
vRi *= DecalInfo.fSize * 0.65f;
vUp *= DecalInfo.fSize * 0.65f;
if (!bMakeFatTest || (
RayRenderMeshIntersection(pRM, srcp + vUp, -vDecalNormal, vTmp, vTmp, true, fMaxHitDistance, pMat) &&
RayRenderMeshIntersection(pRM, srcp - vUp, -vDecalNormal, vTmp, vTmp, true, fMaxHitDistance, pMat) &&
RayRenderMeshIntersection(pRM, srcp + vRi, -vDecalNormal, vTmp, vTmp, true, fMaxHitDistance, pMat) &&
RayRenderMeshIntersection(pRM, srcp - vRi, -vDecalNormal, vTmp, vTmp, true, fMaxHitDistance, pMat)))
{
DecalInfo.vPos = objMat.TransformPoint(vOS_OutPos + vOS_OutNormal * 0.001f * fWorldScaleInv);
DecalInfo.vNormal = objRot.TransformVector(vOS_OutNormal);
return true;
}
}
return false;
}
struct HitPosInfo
{
HitPosInfo() { memset(this, 0, sizeof(HitPosInfo)); }
Vec3 vPos, vNormal;
float fDistance;
};
int __cdecl CDecalManager__CmpHitPos(const void* v1, const void* v2)
{
HitPosInfo* p1 = (HitPosInfo*)v1;
HitPosInfo* p2 = (HitPosInfo*)v2;
if (p1->fDistance > p2->fDistance)
{
return 1;
}
else if (p1->fDistance < p2->fDistance)
{
return -1;
}
return 0;
}
bool CDecalManager::RayRenderMeshIntersection(IRenderMesh* pRenderMesh, const Vec3& vInPos, const Vec3& vInDir,
Vec3& vOutPos, Vec3& vOutNormal, bool bFastTest, float fMaxHitDistance, _smart_ptr<IMaterial> pMat)
{
SRayHitInfo hitInfo;
hitInfo.bUseCache = GetCVars()->e_DecalsHitCache != 0;
hitInfo.bInFirstHit = bFastTest;
hitInfo.inRay.origin = vInPos;
hitInfo.inRay.direction = vInDir.GetNormalized();
hitInfo.inReferencePoint = vInPos;
hitInfo.fMaxHitDistance = fMaxHitDistance;
bool bRes = CRenderMeshUtils::RayIntersection(pRenderMesh, hitInfo, pMat);
vOutPos = hitInfo.vHitPos;
vOutNormal = hitInfo.vHitNormal;
return bRes;
}
bool CDecalManager::SpawnHierarchical(const CryEngineDecalInfo& rootDecalInfo, CDecal* pCallerManagedDecal)
{
// decal on terrain or simple decal on always static object
if (!rootDecalInfo.ownerInfo.pRenderNode)
{
return Spawn(rootDecalInfo, pCallerManagedDecal);
}
bool bSuccess = false;
AABB decalBoxWS;
float fSize = rootDecalInfo.fSize;
decalBoxWS.max = rootDecalInfo.vPos + Vec3(fSize, fSize, fSize);
decalBoxWS.min = rootDecalInfo.vPos - Vec3(fSize, fSize, fSize);
for (int nEntitySlotId = 0; nEntitySlotId < 16; nEntitySlotId++)
{
CStatObj* _pStatObj = NULL;
Matrix34A entSlotMatrix;
entSlotMatrix.SetIdentity();
if (_pStatObj = (CStatObj*)rootDecalInfo.ownerInfo.pRenderNode->GetEntityStatObj(nEntitySlotId, ~0, &entSlotMatrix, true))
{
if (_pStatObj->m_nFlags & STATIC_OBJECT_COMPOUND)
{
if (int nSubCount = _pStatObj->GetSubObjectCount())
{ // spawn decals on stat obj sub objects
CryEngineDecalInfo decalInfo = rootDecalInfo;
decalInfo.ownerInfo.nRenderNodeSlotId = nEntitySlotId;
if (rootDecalInfo.ownerInfo.nRenderNodeSlotSubObjectId >= 0)
{
decalInfo.ownerInfo.nRenderNodeSlotSubObjectId = rootDecalInfo.ownerInfo.nRenderNodeSlotSubObjectId;
bSuccess |= Spawn(decalInfo, pCallerManagedDecal);
}
else
{
for (int nSubId = 0; nSubId < nSubCount; nSubId++)
{
IStatObj::SSubObject& subObj = _pStatObj->SubObject(nSubId);
if (subObj.pStatObj && !subObj.bHidden && subObj.nType == STATIC_SUB_OBJECT_MESH)
{
Matrix34 subObjMatrix = entSlotMatrix * subObj.tm;
AABB subObjAABB = AABB::CreateTransformedAABB(subObjMatrix, subObj.pStatObj->GetAABB());
if (Overlap::AABB_AABB(subObjAABB, decalBoxWS))
{
decalInfo.ownerInfo.nRenderNodeSlotSubObjectId = nSubId;
bSuccess |= Spawn(decalInfo, pCallerManagedDecal);
}
}
}
}
}
}
else
{
AABB subObjAABB = AABB::CreateTransformedAABB(entSlotMatrix, _pStatObj->GetAABB());
if (Overlap::AABB_AABB(subObjAABB, decalBoxWS))
{
CryEngineDecalInfo decalInfo = rootDecalInfo;
decalInfo.ownerInfo.nRenderNodeSlotId = nEntitySlotId;
decalInfo.ownerInfo.nRenderNodeSlotSubObjectId = -1; // no childs
bSuccess |= Spawn(decalInfo, pCallerManagedDecal);
}
}
}
}
return bSuccess;
}
bool CDecalManager::Spawn(CryEngineDecalInfo DecalInfo, CDecal* pCallerManagedDecal)
{
FUNCTION_PROFILER_3DENGINE;
Vec3 vCamPos = GetSystem()->GetViewCamera().GetPosition();
// do not spawn if too far
float fZoom = GetObjManager() ? Get3DEngine()->GetZoomFactor() : 1.f;
float fDecalDistance = DecalInfo.vPos.GetDistance(vCamPos);
if (!pCallerManagedDecal && (fDecalDistance > Get3DEngine()->GetMaxViewDistance() || fDecalDistance * fZoom > DecalInfo.fSize * ENTITY_DECAL_DIST_FACTOR * 3.f))
{
return false;
}
int overlapCount(0);
int targetSize(0);
int overlapIds[MAX_ASSEMBLE_SIZE];
// do not spawn new decals if they could overlap the existing and similar ones
if (!pCallerManagedDecal && !GetCVars()->e_DecalsOverlapping && DecalInfo.fSize && !DecalInfo.bSkipOverlappingTest)
{
for (int i = 0; i < DECAL_COUNT; i++)
{
if (m_arrbActiveDecals[i])
{
// skip overlapping check if decals are very different in size
if ((m_arrDecals[i].m_iAssembleSize > 0) == DecalInfo.bAssemble)
{
float fSizeRatio = m_arrDecals[i].m_fWSSize / DecalInfo.fSize;
if (((m_arrDecals[i].m_iAssembleSize > 0) || (fSizeRatio > 0.5f && fSizeRatio < 2.f)) && m_arrDecals[i].m_nGroupId != DecalInfo.nGroupId)
{
float fDist = m_arrDecals[i].m_vWSPos.GetSquaredDistance(DecalInfo.vPos);
if (fDist < sqr(m_arrDecals[i].m_fWSSize * 0.5f + DecalInfo.fSize * 0.5f) && (DecalInfo.vNormal.Dot(m_arrDecals[i].m_vFront) > 0.f))
{
if (DecalInfo.bAssemble && m_arrDecals[i].m_iAssembleSize < MAX_ASSEMBLE_SIZE)
{
if (overlapCount < MAX_ASSEMBLE_SIZE)
{
overlapIds[overlapCount] = i;
overlapCount++;
}
else
{
m_arrbActiveDecals[i] = false;
}
}
else
{
return true;
}
}
}
}
}
}
}
float fAssembleSizeModifier(1.0f);
if (DecalInfo.bAssemble)
{
Vec3 avgPos(0.0f, 0.0f, 0.0f);
int validAssembles(0);
for (int i = 0; i < overlapCount; i++)
{
int id = overlapIds[i];
float fDist = m_arrDecals[id].m_vWSPos.GetSquaredDistance(DecalInfo.vPos);
float minDist = sqr(m_arrDecals[id].m_fWSSize * 0.4f);
if (fDist > minDist)
{
avgPos += m_arrDecals[id].m_vWSPos;
targetSize += m_arrDecals[id].m_iAssembleSize;
validAssembles++;
}
}
if (overlapCount && !validAssembles)
{
return true;
}
for (int i = 0; i < overlapCount; i++)
{
int id = overlapIds[i];
m_arrbActiveDecals[id] = false;
}
++validAssembles;
++targetSize;
avgPos += DecalInfo.vPos;
if (targetSize > 1)
{
avgPos /= float(validAssembles);
DecalInfo.vPos = avgPos;
targetSize = min(targetSize, MAX_ASSEMBLE_SIZE);
const float sizetable[MAX_ASSEMBLE_SIZE] = {1.0f, 1.5f, 2.3f, 3.5f, 3.5f};
const char sValue[2] = { aznumeric_caster('0' + targetSize), 0 };
cry_strcat(DecalInfo.szMaterialName, sValue);
fAssembleSizeModifier = sizetable[targetSize - 1];
}
}
if (GetCVars()->e_Decals > 1)
{
DrawSphere(DecalInfo.vPos, DecalInfo.fSize);
}
// update lifetime for near decals under control by the decal manager
if (!pCallerManagedDecal)
{
if (DecalInfo.fSize > 1 && GetCVars()->e_DecalsNeighborMaxLifeTime)
{ // force near decals to fade faster
float fCurrTime = GetTimer()->GetCurrTime();
for (int i = 0; i < DECAL_COUNT; i++)
{
if (m_arrbActiveDecals[i] && m_arrDecals[i].m_nGroupId != DecalInfo.nGroupId)
{
if (m_arrDecals[i].m_vWSPos.GetSquaredDistance(DecalInfo.vPos) < sqr(m_arrDecals[i].m_fWSSize / 1.5f + DecalInfo.fSize / 2.0f))
{
if ((m_arrDecals[i]).m_fLifeBeginTime < fCurrTime - 0.1f)
{
if (m_arrDecals[i].m_fLifeTime > GetCVars()->e_DecalsNeighborMaxLifeTime)
{
if (m_arrDecals[i].m_fLifeTime < 10000) // decals spawn by cut scenes need to stay
{
m_arrDecals[i].m_fLifeTime = GetCVars()->e_DecalsNeighborMaxLifeTime;
}
}
}
}
}
}
}
// loop position in array
m_nCurDecal = (m_nCurDecal + 1) & (DECAL_COUNT - 1);
//if(m_nCurDecal>=DECAL_COUNT)
// m_nCurDecal=0;
}
// create reference to decal which is to be filled
CDecal& newDecal(pCallerManagedDecal ? *pCallerManagedDecal : m_arrDecals[ m_nCurDecal ]);
newDecal.m_bDeferred = DecalInfo.bDeferred;
newDecal.m_iAssembleSize = targetSize;
// free old pRM
newDecal.FreeRenderData();
newDecal.m_nGroupId = DecalInfo.nGroupId;
// get material if specified
newDecal.m_pMaterial = 0;
if (DecalInfo.szMaterialName[0] != '\0')
{
newDecal.m_pMaterial = GetMatMan()->LoadMaterial(DecalInfo.szMaterialName, false, true);
if (!newDecal.m_pMaterial)
{
newDecal.m_pMaterial = GetMatMan()->LoadMaterial("EngineAssets/Materials/Decals/Default", true, true);
newDecal.m_pMaterial->AddRef();
Warning("CDecalManager::Spawn: Specified decal material \"%s\" not found!\n", DecalInfo.szMaterialName);
}
}
newDecal.m_sortPrio = DecalInfo.sortPrio;
// set up user defined decal basis if provided
bool useDefinedUpRight(false);
Vec3 userDefinedUp;
Vec3 userDefinedRight;
if (DecalInfo.pExplicitRightUpFront)
{
userDefinedRight = DecalInfo.pExplicitRightUpFront->GetColumn(0);
userDefinedUp = DecalInfo.pExplicitRightUpFront->GetColumn(1);
DecalInfo.vNormal = DecalInfo.pExplicitRightUpFront->GetColumn(2);
useDefinedUpRight = true;
}
// just in case
DecalInfo.vNormal.NormalizeSafe();
// remember object we need to follow
newDecal.m_ownerInfo.nRenderNodeSlotId = DecalInfo.ownerInfo.nRenderNodeSlotId;
newDecal.m_ownerInfo.nRenderNodeSlotSubObjectId = DecalInfo.ownerInfo.nRenderNodeSlotSubObjectId;
newDecal.m_vWSPos = DecalInfo.vPos;
newDecal.m_fWSSize = DecalInfo.fSize * fAssembleSizeModifier;
// If owner entity and object is specified - make decal use entity geometry
float _fObjScale = 1.f;
Matrix34A _objMat;
Matrix33 worldRot;
IStatObj* pStatObj = DecalInfo.ownerInfo.GetOwner(_objMat);
if (pStatObj)
{
worldRot = Matrix33(_objMat);
_objMat.Invert();
}
float fWrapMinSize = GetFloatCVar(e_DecalsDefferedDynamicMinSize);
if (DecalInfo.ownerInfo.pRenderNode && DecalInfo.ownerInfo.nRenderNodeSlotId >= 0 && (DecalInfo.fSize > fWrapMinSize || pCallerManagedDecal) && !DecalInfo.bDeferred)
{
newDecal.m_eDecalType = eDecalType_OS_OwnersVerticesUsed;
IRenderMesh* pSourceRenderMesh = NULL;
if (pStatObj)
{
pSourceRenderMesh = pStatObj->GetRenderMesh();
}
if (!pSourceRenderMesh)
{
return false;
}
// transform decal into object space
Matrix33 objRotInv = Matrix33(_objMat);
objRotInv.NoScale();
if (useDefinedUpRight)
{
userDefinedRight = objRotInv.TransformVector(userDefinedRight).GetNormalized();
userDefinedUp = objRotInv.TransformVector(userDefinedUp).GetNormalized();
assert(fabsf(DecalInfo.vNormal.Dot(-DecalInfo.vHitDirection.GetNormalized()) - 1.0f) < 1e-4f);
}
// make decals smaller but longer if hit direction is near perpendicular to surface normal
float fSizeModificator = 0.25f + 0.75f * fabs(DecalInfo.vHitDirection.GetNormalized().Dot(DecalInfo.vNormal));
// put into normal object space hit direction of projection
DecalInfo.vNormal = -objRotInv.TransformVector((DecalInfo.vHitDirection - DecalInfo.vNormal * 0.25f).GetNormalized());
if (!DecalInfo.vNormal.IsZero())
{
DecalInfo.vNormal.Normalize();
}
// put into position object space hit position
DecalInfo.vPos = _objMat.TransformPoint(DecalInfo.vPos);
// find object scale
float fObjScale = 1.f;
Vec3 vTest(0, 0, 1.f);
vTest = _objMat.TransformVector(vTest);
fObjScale = 1.f / vTest.len();
if (fObjScale < 0.01f)
{
return false;
}
// transform size into object space
DecalInfo.fSize /= fObjScale;
DecalInfo.fSize *= (DecalInfo.bAssemble ? fAssembleSizeModifier : fSizeModificator);
if (DecalInfo.bForceEdge)
{
SRayHitInfo hitInfo;
hitInfo.bUseCache = GetCVars()->e_DecalsHitCache != 0;
hitInfo.bInFirstHit = false;
hitInfo.inRay.origin = DecalInfo.vPos + DecalInfo.vNormal;
hitInfo.inRay.direction = -DecalInfo.vNormal;
hitInfo.inReferencePoint = DecalInfo.vPos + DecalInfo.vNormal;
hitInfo.inRetTriangle = true;
CRenderMeshUtils::RayIntersection(pSourceRenderMesh, hitInfo, pStatObj ? pStatObj->GetMaterial() : NULL);
MoveToEdge(pSourceRenderMesh, DecalInfo.fSize, hitInfo.vHitPos, hitInfo.vHitNormal, hitInfo.vTri0, hitInfo.vTri1, hitInfo.vTri2);
DecalInfo.vPos = hitInfo.vHitPos;
DecalInfo.vNormal = hitInfo.vHitNormal;
}
// make decal geometry
if (!newDecal.m_pMaterial)
{
// I'm not sure what consequences will be if m_pMaterial is null, so we warn about it just in case. Feel free to remove this if you hit it and all is well.
Warning("CDecalManager::Spawn: Decal material is null while creating BigDecalRenderMesh");
}
newDecal.m_pRenderMesh = MakeBigDecalRenderMesh(pSourceRenderMesh, DecalInfo.vPos, DecalInfo.fSize, DecalInfo.vNormal, newDecal.m_pMaterial, pStatObj ? pStatObj->GetMaterial() : NULL);
if (!newDecal.m_pRenderMesh)
{
return false; // no geometry found
}
}
else if (!DecalInfo.ownerInfo.pRenderNode && DecalInfo.ownerInfo.pDecalReceivers && (DecalInfo.fSize > fWrapMinSize || pCallerManagedDecal) && !DecalInfo.bDeferred)
{
newDecal.m_eDecalType = eDecalType_WS_Merged;
assert(!newDecal.m_pRenderMesh);
// put into normal hit direction of projection
DecalInfo.vNormal = -DecalInfo.vHitDirection;
if (!DecalInfo.vNormal.IsZero())
{
DecalInfo.vNormal.Normalize();
}
Vec3 vSize(DecalInfo.fSize * 1.333f, DecalInfo.fSize * 1.333f, DecalInfo.fSize * 1.333f);
AABB decalAABB(DecalInfo.vPos - vSize, DecalInfo.vPos + vSize);
// build list of objects
PodArray<SRenderMeshInfoInput> lstRMI;
for (int nObj = 0; nObj < DecalInfo.ownerInfo.pDecalReceivers->Count(); nObj++)
{
IRenderNode* pDecalOwner = DecalInfo.ownerInfo.pDecalReceivers->Get(nObj)->pNode;
Matrix34A objMat;
if (IStatObj* pEntObject = pDecalOwner->GetEntityStatObj(DecalInfo.ownerInfo.nRenderNodeSlotId, 0, &objMat))
{
SRenderMeshInfoInput rmi;
rmi.pMesh = pEntObject->GetRenderMesh();
rmi.pMat = pEntObject->GetMaterial();
rmi.mat = objMat;
if (rmi.pMesh)
{
AABB transAABB = AABB::CreateTransformedAABB(rmi.mat, pEntObject->GetAABB());
if (Overlap::AABB_AABB(decalAABB, transAABB))
{
lstRMI.Add(rmi);
}
}
else
if (int nSubObjCount = pEntObject->GetSubObjectCount())
{ // multi sub objects
for (int nSubObj = 0; nSubObj < nSubObjCount; nSubObj++)
{
IStatObj::SSubObject* pSubObj = pEntObject->GetSubObject(nSubObj);
if (pSubObj->pStatObj)
{
rmi.pMesh = pSubObj->pStatObj->GetRenderMesh();
rmi.pMat = pSubObj->pStatObj->GetMaterial();
rmi.mat = objMat * pSubObj->tm;
if (rmi.pMesh)
{
AABB transAABB = AABB::CreateTransformedAABB(rmi.mat, pSubObj->pStatObj->GetAABB());
if (Overlap::AABB_AABB(decalAABB, transAABB))
{
lstRMI.Add(rmi);
}
}
}
}
}
}
}
if (!lstRMI.Count())
{
return false;
}
SDecalClipInfo DecalClipInfo;
DecalClipInfo.vPos = DecalInfo.vPos;
DecalClipInfo.fRadius = DecalInfo.fSize;
DecalClipInfo.vProjDir = DecalInfo.vNormal;
PodArray<SRenderMeshInfoOutput> outRenderMeshes;
CRenderMeshMerger Merger;
SMergeInfo info;
info.sMeshName = "MergedDecal";
info.sMeshType = "MergedDecal";
info.pDecalClipInfo = &DecalClipInfo;
info.vResultOffset = DecalInfo.vPos;
newDecal.m_pRenderMesh = Merger.MergeRenderMeshes(lstRMI.GetElements(), lstRMI.Count(), outRenderMeshes, info);
if (!newDecal.m_pRenderMesh)
{
return false; // no geometry found
}
assert(newDecal.m_pRenderMesh->GetChunks().size() == 1);
}
else
if (DecalInfo.ownerInfo.pRenderNode &&
(DecalInfo.ownerInfo.pRenderNode->GetRenderNodeType() == eERType_RenderComponent ||
DecalInfo.ownerInfo.pRenderNode->GetRenderNodeType() == eERType_StaticMeshRenderComponent ||
DecalInfo.ownerInfo.pRenderNode->GetRenderNodeType() == eERType_DynamicMeshRenderComponent ||
DecalInfo.ownerInfo.pRenderNode->GetRenderNodeType() == eERType_SkinnedMeshRenderComponent) &&
DecalInfo.ownerInfo.nRenderNodeSlotId >= 0)
{
newDecal.m_eDecalType = eDecalType_OS_SimpleQuad;
Matrix34A objMat;
// transform decal from world space into entity space
IStatObj* pEntObject = DecalInfo.ownerInfo.GetOwner(objMat);
if (!pEntObject)
{
return false;
}
assert(pEntObject);
objMat.Invert();
if (useDefinedUpRight)
{
userDefinedRight = objMat.TransformVector(userDefinedRight).GetNormalized();
userDefinedUp = objMat.TransformVector(userDefinedUp).GetNormalized();
assert(fabsf(DecalInfo.vNormal.Dot(-DecalInfo.vHitDirection.GetNormalized()) - 1.0f) < 1e-4f);
}
DecalInfo.vNormal = objMat.TransformVector(DecalInfo.vNormal).GetNormalized();
DecalInfo.vPos = objMat.TransformPoint(DecalInfo.vPos);
// find object scale
Vec3 vTest(0, 0, 1.f);
vTest = objMat.TransformVector(vTest);
_fObjScale = 1.f / vTest.len();
DecalInfo.fSize /= _fObjScale;
}
else
{
bool isHole = true;
auto enumerationCallback = [&](AzFramework::Terrain::TerrainDataRequests* terrain) -> bool
{
isHole = false;
if (!DecalInfo.preventDecalOnGround && DecalInfo.fSize > (fWrapMinSize * 2.f) && !DecalInfo.ownerInfo.pRenderNode &&
(DecalInfo.vPos.z - terrain->GetHeightFromFloats(DecalInfo.vPos.x, DecalInfo.vPos.y)) < DecalInfo.fSize && !DecalInfo.bDeferred)
{
newDecal.m_eDecalType = eDecalType_WS_OnTheGround;
const AZ::Vector2 terrainGridResolution = terrain->GetTerrainGridResolution();
const float unitSizeX = terrainGridResolution.GetX();
const float unitSizeY = terrainGridResolution.GetY();
const float x1 = (DecalInfo.vPos.x - DecalInfo.fSize) / unitSizeX * unitSizeX - unitSizeX;
const float x2 = (DecalInfo.vPos.x + DecalInfo.fSize) / unitSizeX * unitSizeX + unitSizeX;
const float y1 = (DecalInfo.vPos.y - DecalInfo.fSize) / unitSizeY * unitSizeY - unitSizeY;
const float y2 = (DecalInfo.vPos.y + DecalInfo.fSize) / unitSizeY * unitSizeY + unitSizeY;
for (float x = x1; x <= x2; x += unitSizeX)
{
for (float y = y1; y <= y2; y += unitSizeY)
{
if (terrain->GetIsHoleFromFloats(x, y))
{
isHole = true;
return false;
}
}
}
}
// Only one handler should exist.
return false;
};
AzFramework::Terrain::TerrainDataRequestBus::EnumerateHandlers(enumerationCallback);
if (isHole)
{
return false;
}
else
{
newDecal.m_eDecalType = eDecalType_WS_SimpleQuad;
}
DecalInfo.ownerInfo.pRenderNode = NULL;
}
// spawn
if (!useDefinedUpRight)
{
if (DecalInfo.vNormal.Dot(Vec3(0, 0, 1)) > 0.999f)
{ // floor
newDecal.m_vRight = Vec3(0, 1, 0);
newDecal.m_vUp = Vec3(-1, 0, 0);
}
else if (DecalInfo.vNormal.Dot(Vec3(0, 0, -1)) > 0.999f)
{ // ceil
newDecal.m_vRight = Vec3(1, 0, 0);
newDecal.m_vUp = Vec3(0, -1, 0);
}
else if (!DecalInfo.vNormal.IsZero())
{
newDecal.m_vRight = DecalInfo.vNormal.Cross(Vec3(0, 0, 1));
newDecal.m_vRight.Normalize();
newDecal.m_vUp = DecalInfo.vNormal.Cross(newDecal.m_vRight);
newDecal.m_vUp.Normalize();
}
// rotate vectors
if (!DecalInfo.vNormal.IsZero())
{
AngleAxis rotation(DecalInfo.fAngle, DecalInfo.vNormal);
newDecal.m_vRight = rotation * newDecal.m_vRight;
newDecal.m_vUp = rotation * newDecal.m_vUp;
}
}
else
{
newDecal.m_vRight = userDefinedRight;
newDecal.m_vUp = userDefinedUp;
}
newDecal.m_vFront = DecalInfo.vNormal;
newDecal.m_vPos = DecalInfo.vPos;
newDecal.m_vPos += DecalInfo.vNormal * 0.001f / _fObjScale;
newDecal.m_fSize = DecalInfo.fSize;
newDecal.m_fLifeTime = DecalInfo.fLifeTime * GetCVars()->e_DecalsLifeTimeScale;
assert(!DecalInfo.pIStatObj); // not used -> not supported
newDecal.m_ownerInfo.pRenderNode = DecalInfo.ownerInfo.pRenderNode;
if (DecalInfo.ownerInfo.pRenderNode)
{
DecalInfo.ownerInfo.pRenderNode->m_nInternalFlags |= IRenderNode::DECAL_OWNER;
}
newDecal.m_fGrowTime = DecalInfo.fGrowTime;
newDecal.m_fGrowTimeAlpha = DecalInfo.fGrowTimeAlpha;
newDecal.m_fLifeBeginTime = GetTimer()->GetCurrTime();
if (DecalInfo.pIStatObj && !pCallerManagedDecal)
{
//assert(!"Geometry decals neede to be re-debugged");
DecalInfo.pIStatObj->AddRef();
}
if (!pCallerManagedDecal)
{
m_arrbActiveDecals[ m_nCurDecal ] = true;
++m_nCurDecal;
}
#ifdef _DEBUG
if (newDecal.m_ownerInfo.pRenderNode)
{
cry_strcpy(newDecal.m_decalOwnerEntityClassName, newDecal.m_ownerInfo.pRenderNode->GetEntityClassName());
cry_strcpy(newDecal.m_decalOwnerName, newDecal.m_ownerInfo.pRenderNode->GetName());
newDecal.m_decalOwnerType = newDecal.m_ownerInfo.pRenderNode->GetRenderNodeType();
}
else
{
newDecal.m_decalOwnerEntityClassName[0] = '\0';
newDecal.m_decalOwnerName[0] = '\0';
newDecal.m_decalOwnerType = eERType_NotRenderNode;
}
#endif
return true;
}
void CDecalManager::Update(const float fFrameTime)
{
CryPrefetch(&m_arrbActiveDecals[0]);
CryPrefetch(&m_arrbActiveDecals[128]);
CryPrefetch(&m_arrbActiveDecals[256]);
CryPrefetch(&m_arrbActiveDecals[384]);
for (int i = 0; i < DECAL_COUNT; i++)
{
if (m_arrbActiveDecals[i])
{
IRenderNode* pRenderNode = m_arrDecals[i].m_ownerInfo.pRenderNode;
if (m_arrDecals[i].Update(m_arrbActiveDecals[i], fFrameTime))
{
if (pRenderNode && m_arrTempUpdatedOwners.Find(pRenderNode) < 0)
{
m_arrTempUpdatedOwners.Add(pRenderNode);
}
}
}
}
for (int i = 0; i < m_arrTempUpdatedOwners.Count(); i++)
{
m_arrTempUpdatedOwners[i]->m_nInternalFlags &= ~IRenderNode::UPDATE_DECALS;
}
m_arrTempUpdatedOwners.Clear();
}
void CDecalManager::Render(const SRenderingPassInfo& passInfo)
{
FUNCTION_PROFILER_3DENGINE;
if (!passInfo.RenderDecals() || !GetObjManager())
{
return;
}
float fCurrTime = GetTimer()->GetCurrTime();
float fZoom = passInfo.GetZoomFactor();
static int nLastUpdateStreamingPrioriryRoundId = 0;
bool bPrecacheMaterial = nLastUpdateStreamingPrioriryRoundId != GetObjManager()->GetUpdateStreamingPrioriryRoundId();
nLastUpdateStreamingPrioriryRoundId = GetObjManager()->GetUpdateStreamingPrioriryRoundId();
static int nLastUpdateStreamingPrioriryRoundIdFast = 0;
bool bPrecacheMaterialFast = nLastUpdateStreamingPrioriryRoundIdFast != GetObjManager()->GetUpdateStreamingPrioriryRoundIdFast();
nLastUpdateStreamingPrioriryRoundIdFast = GetObjManager()->GetUpdateStreamingPrioriryRoundIdFast();
const CCamera& rCamera = passInfo.GetCamera();
// draw
for (int i = 0; i < DECAL_COUNT; i++)
{
if (m_arrbActiveDecals[i])
{
CDecal* pDecal = &m_arrDecals[i];
pDecal->m_vWSPos = pDecal->GetWorldPosition();
float fDist = rCamera.GetPosition().GetDistance(pDecal->m_vWSPos) * fZoom;
float fMaxViewDist = pDecal->m_fWSSize * ENTITY_DECAL_DIST_FACTOR * 3.0f;
if (fDist < fMaxViewDist)
{
if (rCamera.IsSphereVisible_F(Sphere(pDecal->m_vWSPos, pDecal->m_fWSSize)))
{
bool bAfterWater = GetObjManager()->IsAfterWater(pDecal->m_vWSPos, passInfo);
if (pDecal->m_pMaterial)
{
if (passInfo.IsGeneralPass())
{
if (bPrecacheMaterialFast && (fDist < GetFloatCVar(e_StreamPredictionMinFarZoneDistance)))
{
if (CMatInfo* pMatInfo = (CMatInfo*)pDecal->m_pMaterial.get())
{
pMatInfo->PrecacheMaterial(fDist, NULL, true);
}
}
if (bPrecacheMaterial)
{
if (CMatInfo* pMatInfo = (CMatInfo*)pDecal->m_pMaterial.get())
{
pMatInfo->PrecacheMaterial(fDist, NULL, false);
}
}
}
// TODO: take entity orientation into account
Vec3 vSize(pDecal->m_fWSSize, pDecal->m_fWSSize, pDecal->m_fWSSize);
AABB aabb(pDecal->m_vWSPos - vSize, pDecal->m_vWSPos + vSize);
float fDistFading = SATURATE((1.f - fDist / fMaxViewDist) * DIST_FADING_FACTOR);
SRendItemSorter rendItemSorter = SRendItemSorter::CreateRendItemSorter(passInfo);
pDecal->Render(fCurrTime, bAfterWater, fDistFading, fDist, passInfo, rendItemSorter);
if (GetCVars()->e_Decals > 1)
{
Vec3 vCenter = pDecal->m_vWSPos;
AABB aabbCenter(vCenter - vSize * 0.05f, vCenter + vSize * 0.05f);
DrawBBox(aabb);
DrawBBox(aabbCenter, Col_Yellow);
Vec3 vNormal(Vec3(pDecal->m_vUp).Cross(-pDecal->m_vRight).GetNormalized());
Matrix34A objMat;
IStatObj* pEntObject = pDecal->m_ownerInfo.GetOwner(objMat);
if (pEntObject)
{
vNormal = objMat.TransformVector(vNormal).GetNormalized();
}
DrawLine(vCenter, vCenter + vNormal * pDecal->m_fWSSize);
if (pDecal->m_pRenderMesh)
{
pDecal->m_pRenderMesh->GetBBox(aabb.min, aabb.max);
DrawBBox(aabb, Col_Red);
}
}
}
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CDecalManager::OnEntityDeleted(IRenderNode* pRenderNode)
{
FUNCTION_PROFILER_3DENGINE;
// remove decals of this entity
for (int i = 0; i < DECAL_COUNT; i++)
{
if (m_arrbActiveDecals[i])
{
if (m_arrDecals[i].m_ownerInfo.pRenderNode == pRenderNode)
{
if (GetCVars()->e_Decals == 2)
{
CDecal& decal = m_arrDecals[i];
Vec3 vPos = decal.GetWorldPosition();
const char* szOwnerName = "none";
#ifdef _DEBUG
szOwnerName = decal.m_decalOwnerName;
#endif
PrintMessage("Debug: C3DEngine::OnDecalDeleted: Pos=(%.1f,%.1f,%.1f) Size=%.2f DecalMaterial=%s OwnerName=%s",
vPos.x, vPos.y, vPos.z, decal.m_fSize, decal.m_pMaterial ? decal.m_pMaterial->GetName() : "none", szOwnerName);
}
m_arrbActiveDecals[i] = false;
m_arrDecals[i].FreeRenderData();
}
}
}
// update decal render nodes
PodArray<IRenderNode*> lstObjects;
Get3DEngine()->GetObjectsByTypeGlobal(lstObjects, eERType_Decal, NULL);
if (Get3DEngine()->GetVisAreaManager())
{
Get3DEngine()->GetVisAreaManager()->GetObjectsByType(lstObjects, eERType_Decal, NULL);
}
for (int i = 0; i < lstObjects.Count(); i++)
{
((CDecalRenderNode*)lstObjects[i])->RequestUpdate();
}
}
//////////////////////////////////////////////////////////////////////////
void CDecalManager::OnRenderMeshDeleted(IRenderMesh* pRenderMesh)
{
// remove decals of this entity
for (int i = 0; i < DECAL_COUNT; i++)
{
if (m_arrbActiveDecals[i])
{
if (
(m_arrDecals[i].m_ownerInfo.pRenderNode && (
m_arrDecals[i].m_ownerInfo.pRenderNode->GetRenderMesh(0) == pRenderMesh ||
m_arrDecals[i].m_ownerInfo.pRenderNode->GetRenderMesh(1) == pRenderMesh ||
m_arrDecals[i].m_ownerInfo.pRenderNode->GetRenderMesh(2) == pRenderMesh))
||
(m_arrDecals[i].m_pRenderMesh && m_arrDecals[i].m_pRenderMesh->GetVertexContainer() == pRenderMesh)
)
{
m_arrbActiveDecals[i] = false;
m_arrDecals[i].FreeRenderData();
// PrintMessage("CDecalManager::OnRenderMeshDeleted succseed");
}
}
}
}
void CDecalManager::MoveToEdge(IRenderMesh* pRM, const float fRadius, Vec3& vOutPos, Vec3& vOutNormal, const Vec3& vTri0, const Vec3& vTri1, const Vec3& vTri2)
{
FUNCTION_PROFILER_3DENGINE;
AABB boxRM;
pRM->GetBBox(boxRM.min, boxRM.max);
Sphere sp(vOutPos, fRadius);
if (!Overlap::Sphere_AABB(sp, boxRM))
{
return;
}
// get position offset and stride
int nPosStride = 0;
byte* pPos = pRM->GetPosPtr(nPosStride, FSL_READ);
vtx_idx* pInds = pRM->GetIndexPtr(FSL_READ);
if (!pPos || !pInds)
{
return;
}
int nInds = pRM->GetIndicesCount();
// if(nInds>6000)
// return; // skip insane objects
assert(nInds % 3 == 0);
if (!vOutNormal.IsZero())
{
vOutNormal.Normalize();
}
else
{
return;
}
float bestDot = 2.0f;
Vec3 bestNormal(ZERO);
Vec3 bestPoint(ZERO);
// render tris
TRenderChunkArray& Chunks = pRM->GetChunks();
for (int nChunkId = 0; nChunkId < Chunks.size(); nChunkId++)
{
CRenderChunk* pChunk = &Chunks[nChunkId];
if (pChunk->m_nMatFlags & MTL_FLAG_NODRAW || !pChunk->pRE)
{
continue;
}
int nLastIndexId = pChunk->nFirstIndexId + pChunk->nNumIndices;
for (int i = pChunk->nFirstIndexId; i < nLastIndexId; i += 3)
{
assert(pInds[i + 0] < pChunk->nFirstVertId + pChunk->nNumVerts);
assert(pInds[i + 1] < pChunk->nFirstVertId + pChunk->nNumVerts);
assert(pInds[i + 2] < pChunk->nFirstVertId + pChunk->nNumVerts);
assert(pInds[i + 0] >= pChunk->nFirstVertId);
assert(pInds[i + 1] >= pChunk->nFirstVertId);
assert(pInds[i + 2] >= pChunk->nFirstVertId);
// get tri vertices
Vec3 v0 = (*(Vec3*)&pPos[nPosStride * pInds[i + 0]]);
Vec3 v1 = (*(Vec3*)&pPos[nPosStride * pInds[i + 1]]);
Vec3 v2 = (*(Vec3*)&pPos[nPosStride * pInds[i + 2]]);
bool first = false;
bool second = false;
bool third = false;
if (v0 == vTri0 || v0 == vTri1 || v0 == vTri2)
{
first = true;
}
else if (v1 == vTri0 || v1 == vTri1 || v1 == vTri2)
{
second = true;
}
else if (v2 == vTri0 || v2 == vTri1 || v2 == vTri2)
{
third = true;
}
if (first || second || third)
{
// get triangle normal
Vec3 vNormal = (v1 - v0).Cross(v2 - v0).GetNormalized();
float testDot = vNormal.Dot(vOutNormal);
if (testDot < bestDot)
{
bestDot = testDot;
bestNormal = vNormal;
if (first)
{
bestPoint = v0;
}
else if (second)
{
bestPoint = v1;
}
else if (third)
{
bestPoint = v2;
}
}
}
}
}
if (bestDot < 1.0f)
{
vOutNormal = (bestNormal + vOutNormal).GetNormalized();
vOutPos.x = bestPoint.x;
vOutPos.y = bestPoint.y;
}
}
void CDecalManager::FillBigDecalIndices(IRenderMesh* pRM, Vec3 vPos, float fRadius, Vec3 vProjDirIn, PodArray<vtx_idx>* plstIndices, _smart_ptr<IMaterial> pMat, AABB& meshBBox, float& texelAreaDensity)
{
FUNCTION_PROFILER_3DENGINE;
AABB boxRM;
pRM->GetBBox(boxRM.min, boxRM.max);
Sphere sp(vPos, fRadius);
if (!Overlap::Sphere_AABB(sp, boxRM))
{
return;
}
IRenderMesh::ThreadAccessLock lockrm(pRM);
HWVSphere hwSphere(sp);
// get position offset and stride
int nInds = pRM->GetIndicesCount();
if (nInds > GetCVars()->e_DecalsMaxTrisInObject * 3)
{
return; // skip insane objects
}
CDecalRenderNode::m_nFillBigDecalIndicesCounter++;
int nPosStride = 0;
byte* pPos = pRM->GetPosPtr(nPosStride, FSL_READ);
if (!pPos)
{
return;
}
vtx_idx* pInds = pRM->GetIndexPtr(FSL_READ);
if (!pInds)
{
return;
}
assert(nInds % 3 == 0);
plstIndices->Clear();
bool bPointProj(vProjDirIn.IsZeroFast());
if (!bPointProj)
{
vProjDirIn.Normalize();
}
if (!pMat)
{
return;
}
plstIndices->PreAllocate(16);
hwvec3 vProjDir = HWVLoadVecUnaligned(&vProjDirIn);
int usedTrianglesTotal = 0;
TRenderChunkArray& Chunks = pRM->GetChunks();
{
hwvec3 meshBBoxMin = HWVLoadVecUnaligned(&meshBBox.min);
hwvec3 meshBBoxMax = HWVLoadVecUnaligned(&meshBBox.max);
SIMDFConstant(fEpsilon, 0.001f);
const int kNumChunks = Chunks.size();
if (bPointProj)
{
hwvec3 hwvPos = HWVLoadVecUnaligned(&vPos);
for (int nChunkId = 0; nChunkId < kNumChunks; nChunkId++)
{
CRenderChunk* pChunk = &Chunks[nChunkId];
PrefetchLine(&Chunks[nChunkId + 1], 0);
PrefetchLine(&pInds[pChunk->nFirstIndexId], 0);
if (pChunk->m_nMatFlags & MTL_FLAG_NODRAW || !pChunk->pRE)
{
continue;
}
const SShaderItem& shaderItem = pMat->GetShaderItem(pChunk->m_nMatID);
if (!shaderItem.m_pShader || !shaderItem.m_pShaderResources)
{
continue;
}
if (shaderItem.m_pShader->GetFlags() & (EF_NODRAW | EF_DECAL))
{
continue;
}
PrefetchLine(plstIndices->GetElements(), 0);
int usedTriangles = 0;
int nLastIndexId = pChunk->nFirstIndexId + pChunk->nNumIndices;
int i = pChunk->nFirstIndexId;
int iPosIndex0 = nPosStride * pInds[i + 0];
int iPosIndex1 = nPosStride * pInds[i + 1];
int iPosIndex2 = nPosStride * pInds[i + 2];
for (; i < nLastIndexId; i += 3)
{
assert(pInds[i + 0] < pChunk->nFirstVertId + pChunk->nNumVerts);
assert(pInds[i + 1] < pChunk->nFirstVertId + pChunk->nNumVerts);
assert(pInds[i + 2] < pChunk->nFirstVertId + pChunk->nNumVerts);
assert(pInds[i + 0] >= pChunk->nFirstVertId);
assert(pInds[i + 1] >= pChunk->nFirstVertId);
assert(pInds[i + 2] >= pChunk->nFirstVertId);
PrefetchLine(&pInds[i], 128);
int iNextPosIndex0 = 0;
int iNextPosIndex1 = 0;
int iNextPosIndex2 = 0;
if (i + 5 < nLastIndexId)
{
iNextPosIndex0 = nPosStride * pInds[i + 3];
iNextPosIndex1 = nPosStride * pInds[i + 4];
iNextPosIndex2 = nPosStride * pInds[i + 5];
PrefetchLine(&pPos[iNextPosIndex0], 0);
PrefetchLine(&pPos[iNextPosIndex1], 0);
PrefetchLine(&pPos[iNextPosIndex2], 0);
}
// get tri vertices
const hwvec3 v0 = HWVLoadVecUnaligned(reinterpret_cast<Vec3*>(&pPos[iPosIndex0]));
const hwvec3 v1 = HWVLoadVecUnaligned(reinterpret_cast<Vec3*>(&pPos[iPosIndex1]));
const hwvec3 v2 = HWVLoadVecUnaligned(reinterpret_cast<Vec3*>(&pPos[iPosIndex2]));
// test the face
hwvec3 v0v1Diff = HWVSub(v0, v1);
hwvec3 v2v1Diff = HWVSub(v2, v1);
hwvec3 vPosv0Diff = HWVSub(hwvPos, v0);
hwvec3 vCrossResult = HWVCross(v0v1Diff, v2v1Diff);
simdf fDot = HWV3Dot(vPosv0Diff, vCrossResult);
if (SIMDFGreaterThan(fDot, fEpsilon))
{
if (Overlap::HWVSphere_TriangleFromPoints(hwSphere, v0, v1, v2))
{
plstIndices->AddList(&pInds[i], 3);
hwvec3 triBBoxMax1 = HWVMax(v1, v0);
hwvec3 triBBoxMax2 = HWVMax(meshBBoxMax, v2);
hwvec3 triBBoxMin1 = HWVMin(v1, v0);
hwvec3 triBBoxMin2 = HWVMin(meshBBoxMin, v2);
meshBBoxMax = HWVMax(triBBoxMax1, triBBoxMax2);
meshBBoxMin = HWVMin(triBBoxMin1, triBBoxMin2);
usedTriangles++;
}
}
iPosIndex0 = iNextPosIndex0;
iPosIndex1 = iNextPosIndex1;
iPosIndex2 = iNextPosIndex2;
}
if (pChunk->m_texelAreaDensity > 0.0f && pChunk->m_texelAreaDensity != (float)UINT_MAX)
{
texelAreaDensity += usedTriangles * pChunk->m_texelAreaDensity;
usedTrianglesTotal += usedTriangles;
}
}
}
else
{
for (int nChunkId = 0; nChunkId < kNumChunks; nChunkId++)
{
CRenderChunk* pChunk = &Chunks[nChunkId];
if (nChunkId + 1 < kNumChunks)
{
PrefetchLine(&Chunks[nChunkId + 1], 0);
}
PrefetchLine(&pInds[pChunk->nFirstIndexId], 0);
if (pChunk->m_nMatFlags & MTL_FLAG_NODRAW || !pChunk->pRE)
{
continue;
}
const SShaderItem& shaderItem = pMat->GetShaderItem(pChunk->m_nMatID);
if (!shaderItem.m_pShader || !shaderItem.m_pShaderResources)
{
continue;
}
if (shaderItem.m_pShader->GetFlags() & (EF_NODRAW | EF_DECAL))
{
continue;
}
PrefetchLine(plstIndices->GetElements(), 0);
int usedTriangles = 0;
const int nLastIndexId = pChunk->nFirstIndexId + pChunk->nNumIndices;
const int nLastValidIndexId = nLastIndexId - 1;
int i = pChunk->nFirstIndexId;
int iNextPosIndex0 = 0;
int iNextPosIndex1 = 0;
int iNextPosIndex2 = 0;
if (i + 5 < nLastIndexId)
{
iNextPosIndex0 = nPosStride * pInds[i + 3];
iNextPosIndex1 = nPosStride * pInds[i + 4];
iNextPosIndex2 = nPosStride * pInds[i + 5];
PrefetchLine(&pPos[iNextPosIndex0], 0);
PrefetchLine(&pPos[iNextPosIndex1], 0);
PrefetchLine(&pPos[iNextPosIndex2], 0);
}
hwvec3 v0Next = HWVLoadVecUnaligned(reinterpret_cast<Vec3*>(&pPos[nPosStride * pInds[i + 0]]));
hwvec3 v1Next = HWVLoadVecUnaligned(reinterpret_cast<Vec3*>(&pPos[nPosStride * pInds[i + 1]]));
hwvec3 v2Next = HWVLoadVecUnaligned(reinterpret_cast<Vec3*>(&pPos[nPosStride * pInds[i + 2]]));
const int nLastIndexToUse = nLastIndexId - 3;
for (; i < nLastIndexToUse; i += 3)
{
assert(pInds[i + 0] < pChunk->nFirstVertId + pChunk->nNumVerts);
assert(pInds[i + 1] < pChunk->nFirstVertId + pChunk->nNumVerts);
assert(pInds[i + 2] < pChunk->nFirstVertId + pChunk->nNumVerts);
assert(pInds[i + 0] >= pChunk->nFirstVertId);
assert(pInds[i + 1] >= pChunk->nFirstVertId);
assert(pInds[i + 2] >= pChunk->nFirstVertId);
const int iLookaheadIdx = min_branchless(i + 8, nLastValidIndexId);
const int iPrefetchIndex2 = nPosStride * pInds[iLookaheadIdx];
// get tri vertices
const hwvec3 v0 = v0Next;
const hwvec3 v1 = v1Next;
const hwvec3 v2 = v2Next;
//Need to prefetch further ahead
byte* pPrefetch = &pPos[iPrefetchIndex2];
PrefetchLine(pPrefetch, 0);
v0Next = HWVLoadVecUnaligned(reinterpret_cast<Vec3*>(&pPos[iNextPosIndex0]));
// get triangle normal
hwvec3 v1v0Diff = HWVSub(v1, v0);
hwvec3 v2v0Diff = HWVSub(v2, v0);
v1Next = HWVLoadVecUnaligned(reinterpret_cast<Vec3*>(&pPos[iNextPosIndex1]));
hwvec3 vNormal = HWVCross(v1v0Diff, v2v0Diff);
simdf fDot = HWV3Dot(vNormal, vProjDir);
v2Next = HWVLoadVecUnaligned(reinterpret_cast<Vec3*>(&pPos[iNextPosIndex2]));
// test the face
if (SIMDFGreaterThan(fDot, fEpsilon))
{
if (Overlap::HWVSphere_TriangleFromPoints(hwSphere, v0, v1, v2))
{
plstIndices->AddList(&pInds[i], 3);
hwvec3 triBBoxMax1 = HWVMax(v1, v0);
hwvec3 triBBoxMax2 = HWVMax(meshBBoxMax, v2);
hwvec3 triBBoxMin1 = HWVMin(v1, v0);
hwvec3 triBBoxMin2 = HWVMin(meshBBoxMin, v2);
meshBBoxMax = HWVMax(triBBoxMax1, triBBoxMax2);
meshBBoxMin = HWVMin(triBBoxMin1, triBBoxMin2);
usedTriangles++;
}
}
iNextPosIndex0 = nPosStride * pInds[iLookaheadIdx - 2];
iNextPosIndex1 = nPosStride * pInds[iLookaheadIdx - 1];
iNextPosIndex2 = iPrefetchIndex2;
}
const hwvec3 v0 = v0Next;
const hwvec3 v1 = v1Next;
const hwvec3 v2 = v2Next;
// get triangle normal
hwvec3 v1v0Diff = HWVSub(v1, v0);
hwvec3 v2v0Diff = HWVSub(v2, v0);
hwvec3 vNormal = HWVCross(v1v0Diff, v2v0Diff);
simdf fDot = HWV3Dot(vNormal, vProjDir);
// test the face
if (SIMDFGreaterThan(fDot, fEpsilon))
{
if (Overlap::HWVSphere_TriangleFromPoints(hwSphere, v0, v1, v2))
{
plstIndices->AddList(&pInds[i], 3);
hwvec3 triBBoxMax1 = HWVMax(v1, v0);
hwvec3 triBBoxMax2 = HWVMax(meshBBoxMax, v2);
hwvec3 triBBoxMin1 = HWVMin(v1, v0);
hwvec3 triBBoxMin2 = HWVMin(meshBBoxMin, v2);
meshBBoxMax = HWVMax(triBBoxMax1, triBBoxMax2);
meshBBoxMin = HWVMin(triBBoxMin1, triBBoxMin2);
// triBBoxMax = HWVMax(triBBoxMax, v2);
// triBBoxMin = HWVMin(triBBoxMin, v2);
//
// meshBBoxMax = HWVMax(triBBoxMax, meshBBoxMax);
// meshBBoxMin = HWVMin(triBBoxMin, meshBBoxMin);
usedTriangles++;
}
}
if (pChunk->m_texelAreaDensity > 0.0f && pChunk->m_texelAreaDensity != (float)UINT_MAX)
{
texelAreaDensity += usedTriangles * pChunk->m_texelAreaDensity;
usedTrianglesTotal += usedTriangles;
}
}
}
HWVSaveVecUnaligned(&meshBBox.max, meshBBoxMax);
HWVSaveVecUnaligned(&meshBBox.min, meshBBoxMin);
}
if (usedTrianglesTotal != 0)
{
texelAreaDensity = texelAreaDensity / usedTrianglesTotal;
}
}
_smart_ptr<IRenderMesh> CDecalManager::MakeBigDecalRenderMesh(IRenderMesh* pSourceRenderMesh, Vec3 vPos, float fRadius, Vec3 vProjDir, _smart_ptr<IMaterial> pDecalMat, _smart_ptr<IMaterial> pSrcMat)
{
if (!pSourceRenderMesh || pSourceRenderMesh->GetVerticesCount() == 0)
{
return 0;
}
// make indices of this decal
PodArray<vtx_idx> lstIndices;
AABB meshBBox(vPos, vPos);
float texelAreaDensity = 0.0f;
if (pSourceRenderMesh && pSourceRenderMesh->GetVerticesCount())
{
FillBigDecalIndices(pSourceRenderMesh, vPos, fRadius, vProjDir, &lstIndices, pSrcMat, meshBBox, texelAreaDensity);
}
if (!lstIndices.Count())
{
return 0;
}
// make fake vert buffer with one vertex // todo: remove this
PodArray<SVF_P3S_C4B_T2S> EmptyVertBuffer;
EmptyVertBuffer.Add(SVF_P3S_C4B_T2S());
_smart_ptr<IRenderMesh> pRenderMesh = 0;
pRenderMesh = GetRenderer()->CreateRenderMeshInitialized(EmptyVertBuffer.GetElements(), EmptyVertBuffer.Count(), eVF_P3S_C4B_T2S,
lstIndices.GetElements(), lstIndices.Count(), prtTriangleList, "BigDecalOnStatObj", "BigDecal", eRMT_Static, 1, 0, 0, 0, false, false, 0);
pRenderMesh->SetVertexContainer(pSourceRenderMesh);
pRenderMesh->SetChunk(pDecalMat, 0, pSourceRenderMesh->GetVerticesCount(), 0, lstIndices.Count(), texelAreaDensity, eVF_P3S_C4B_T2S);
pRenderMesh->SetBBox(meshBBox.min, meshBBox.max);
return pRenderMesh;
}
void CDecalManager::GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->Add (*this);
}
void CDecalManager::DeleteDecalsInRange(AABB* pAreaBox, IRenderNode* pEntity)
{
if (GetCVars()->e_Decals > 1 && pAreaBox)
{
DrawBBox(*pAreaBox);
}
for (int i = 0; i < DECAL_COUNT; i++)
{
if (!m_arrbActiveDecals[i])
{
continue;
}
if (pEntity && (pEntity != m_arrDecals[i].m_ownerInfo.pRenderNode))
{
continue;
}
if (pAreaBox)
{
Vec3 vPos = m_arrDecals[i].GetWorldPosition();
Vec3 vSize(m_arrDecals[i].m_fWSSize, m_arrDecals[i].m_fWSSize, m_arrDecals[i].m_fWSSize);
AABB decalBox(vPos - vSize, vPos + vSize);
if (!Overlap::AABB_AABB(*pAreaBox, decalBox))
{
continue;
}
}
if (m_arrDecals[i].m_eDecalType != eDecalType_WS_OnTheGround)
{
m_arrbActiveDecals[i] = false;
}
m_arrDecals[i].FreeRenderData();
if (GetCVars()->e_Decals == 2)
{
CDecal& decal = m_arrDecals[i];
PrintMessage("Debug: CDecalManager::DeleteDecalsInRange: Pos=(%.1f,%.1f,%.1f) Size=%.2f DecalMaterial=%s",
decal.m_vPos.x, decal.m_vPos.y, decal.m_vPos.z, decal.m_fSize, decal.m_pMaterial ? decal.m_pMaterial->GetName() : "none");
}
}
}
void CDecalManager::Serialize(TSerialize ser)
{
ser.BeginGroup("StaticDecals");
if (ser.IsReading())
{
Reset();
}
uint32 dwDecalCount = 0;
for (uint32 dwI = 0; dwI < DECAL_COUNT; dwI++)
{
if (m_arrbActiveDecals[dwI])
{
dwDecalCount++;
}
}
ser.Value("DecalCount", dwDecalCount);
if (ser.IsWriting())
{
for (uint32 _dwI = 0; _dwI < DECAL_COUNT; _dwI++)
{
if (m_arrbActiveDecals[_dwI])
{
CDecal& ref = m_arrDecals[_dwI];
ser.BeginGroup("Decal");
ser.Value("Pos", ref.m_vPos);
ser.Value("Right", ref.m_vRight);
ser.Value("Up", ref.m_vUp);
ser.Value("Front", ref.m_vFront);
ser.Value("Size", ref.m_fSize);
ser.Value("WSPos", ref.m_vWSPos);
ser.Value("WSSize", ref.m_fWSSize);
ser.Value("fLifeTime", ref.m_fLifeTime);
// serialize material, handle legacy decals with textureID converted to material created at runtime
string matName("");
if (ref.m_pMaterial && ref.m_pMaterial->GetName())
{
matName = ref.m_pMaterial->GetName();
}
ser.Value("MatName", matName);
// TODO: IStatObj * m_pStatObj; // only if real 3d object is used as decal ()
// TODO: IRenderNode * m_pDecalOwner; // transformation parent object (0 means parent is the world)
ser.Value("nRenderNodeSlotId", ref.m_ownerInfo.nRenderNodeSlotId);
ser.Value("nRenderNodeSlotSubObjectId", ref.m_ownerInfo.nRenderNodeSlotSubObjectId);
int nDecalType = (int)ref.m_eDecalType;
ser.Value("eDecalType", nDecalType);
ser.Value("fGrowTime", ref.m_fGrowTime);
ser.Value("fGrowTimeAlpha", ref.m_fGrowTimeAlpha);
ser.Value("fLifeBeginTime", ref.m_fLifeBeginTime);
bool bBigDecalUsed = ref.IsBigDecalUsed();
ser.Value("bBigDecal", bBigDecalUsed);
// m_arrBigDecalRMs[] will be created on the fly so no need load/save it
if (bBigDecalUsed)
{
for (uint32 dwI = 0; dwI < sizeof(ref.m_arrBigDecalRMCustomData) / sizeof(ref.m_arrBigDecalRMCustomData[0]); ++dwI)
{
char szName[16];
sprintf_s(szName, "BDCD%d", dwI);
ser.Value(szName, ref.m_arrBigDecalRMCustomData[dwI]);
}
}
ser.EndGroup();
}
}
}
else
if (ser.IsReading())
{
m_nCurDecal = 0;
for (uint32 _dwI = 0; _dwI < dwDecalCount; _dwI++)
{
if (m_nCurDecal < DECAL_COUNT)
{
CDecal& ref = m_arrDecals[m_nCurDecal];
ref.FreeRenderData();
ser.BeginGroup("Decal");
ser.Value("Pos", ref.m_vPos);
ser.Value("Right", ref.m_vRight);
ser.Value("Up", ref.m_vUp);
ser.Value("Front", ref.m_vFront);
ser.Value("Size", ref.m_fSize);
ser.Value("WSPos", ref.m_vWSPos);
ser.Value("WSSize", ref.m_fWSSize);
ser.Value("fLifeTime", ref.m_fLifeTime);
// serialize material, handle legacy decals with textureID converted to material created at runtime
string matName("");
ser.Value("MatName", matName);
bool isTempMat(false);
ser.Value("IsTempMat", isTempMat);
ref.m_pMaterial = 0;
if (!matName.empty())
{
ref.m_pMaterial = GetMatMan()->LoadMaterial(matName.c_str(), false, true);
if (!ref.m_pMaterial)
{
Warning("Decal material \"%s\" not found!\n", matName.c_str());
}
}
// TODO: IStatObj * m_pStatObj; // only if real 3d object is used as decal ()
// TODO: IRenderNode * m_pDecalOwner; // transformation parent object (0 means parent is the world)
ser.Value("nRenderNodeSlotId", ref.m_ownerInfo.nRenderNodeSlotId);
ser.Value("nRenderNodeSlotSubObjectId", ref.m_ownerInfo.nRenderNodeSlotSubObjectId);
int nDecalType = eDecalType_Undefined;
ser.Value("eDecalType", nDecalType);
ref.m_eDecalType = (EDecal_Type)nDecalType;
ser.Value("fGrowTime", ref.m_fGrowTime);
ser.Value("fGrowTimeAlpha", ref.m_fGrowTimeAlpha);
ser.Value("fLifeBeginTime", ref.m_fLifeBeginTime);
// no need to to store m_arrBigDecalRMs[] as it becomes recreated
bool bBigDecalsAreaUsed = false;
ser.Value("bBigDecals", bBigDecalsAreaUsed);
if (bBigDecalsAreaUsed)
{
for (uint32 dwI = 0; dwI < sizeof(ref.m_arrBigDecalRMCustomData) / sizeof(ref.m_arrBigDecalRMCustomData[0]); ++dwI)
{
char szName[16];
sprintf_s(szName, "BDCD%d", dwI);
ser.Value(szName, ref.m_arrBigDecalRMCustomData[dwI]);
}
}
// m_arrBigDecalRMs[] will be created on the fly so no need load/save it
m_arrbActiveDecals[m_nCurDecal] = bool(nDecalType != eDecalType_Undefined);
++m_nCurDecal;
ser.EndGroup();
}
}
}
ser.EndGroup();
}
_smart_ptr<IMaterial> CDecalManager::GetMaterialForDecalTexture(const char* pTextureName)
{
if (!pTextureName || *pTextureName == 0)
{
return 0;
}
IMaterialManager* pMatMan = GetMatMan();
_smart_ptr<IMaterial> pMat = pMatMan->FindMaterial(pTextureName);
if (pMat)
{
return pMat;
}
_smart_ptr<IMaterial> pMatSrc = pMatMan->LoadMaterial("EngineAssets/Materials/Decals/Default", false, true);
if (pMatSrc)
{
_smart_ptr<IMaterial> pMatDst = pMatMan->CreateMaterial(pTextureName, pMatSrc->GetFlags() | MTL_FLAG_NON_REMOVABLE);
if (pMatDst)
{
SShaderItem& si = pMatSrc->GetShaderItem();
SInputShaderResources isr = si.m_pShaderResources;
// This will create texture data insertion to the table for the diffuse slot
isr.m_TexturesResourcesMap[EFTT_DIFFUSE].m_Name = pTextureName;
SShaderItem siDst = GetRenderer()->EF_LoadShaderItem(si.m_pShader->GetName(), true, 0, &isr, si.m_pShader->GetGenerationMask());
pMatDst->AssignShaderItem(siDst);
return pMatDst;
}
}
return 0;
}
IStatObj* SDecalOwnerInfo::GetOwner(Matrix34A& objMat)
{
if (!pRenderNode)
{
return NULL;
}
IStatObj* pStatObj = NULL;
if (pStatObj = pRenderNode->GetEntityStatObj(nRenderNodeSlotId, nRenderNodeSlotSubObjectId, &objMat, true))
{
if (nRenderNodeSlotSubObjectId >= 0 && nRenderNodeSlotSubObjectId < pStatObj->GetSubObjectCount())
{
IStatObj::SSubObject* pSubObj = pStatObj->GetSubObject(nRenderNodeSlotSubObjectId);
pStatObj = pSubObj->pStatObj;
objMat = objMat * pSubObj->tm;
}
}
if (pStatObj && (pStatObj->GetFlags() & STATIC_OBJECT_HIDDEN))
{
return NULL;
}
if (pStatObj)
{
if (int nMinLod = ((CStatObj*)pStatObj)->GetMinUsableLod())
{
if (IStatObj* pLodObj = pStatObj->GetLodObject(nMinLod))
{
pStatObj = pLodObj;
}
}
}
return pStatObj;
}