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/RenderMeshUtils.cpp

905 lines
31 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.
#include "Cry3DEngine_precompiled.h"
#include "RenderMeshUtils.h"
namespace
{
enum
{
MAX_CACHED_HITS = 8
};
struct SCachedHit
{
IRenderMesh* pRenderMesh;
SRayHitInfo hitInfo;
Vec3 tri[3];
};
static SCachedHit last_hits[MAX_CACHED_HITS];
}
bool SIntersectionData::Init(IRenderMesh* param_pRenderMesh, SRayHitInfo* param_pHitInfo, _smart_ptr<IMaterial> param_pMtl, bool param_bRequestDecalPlacementTest)
{
pRenderMesh = param_pRenderMesh;
pHitInfo = param_pHitInfo;
pMtl = param_pMtl;
bDecalPlacementTestRequested = param_bRequestDecalPlacementTest;
bool bAllDMeshData = pHitInfo->bGetVertColorAndTC;
nVerts = pRenderMesh->GetVerticesCount();
nInds = pRenderMesh->GetIndicesCount();
if (nInds == 0 || nVerts == 0)
{
return false;
}
pPos = (uint8*)pRenderMesh->GetPosPtr(nPosStride, FSL_READ);
pInds = pRenderMesh->GetIndexPtr(FSL_READ);
#if defined (_DEBUG)
// Perform a quick validation of the vectors from this render mesh
for (int index = 0;index < nVerts; index++)
{
Vec3& check = *((Vec3*)&pPos[nPosStride * index]);
if (isnan(check.x) || isnan(check.y) || isnan(check.z))
{
CryLog("Warning: Invalid vector at index (%d) detected in render mesh for %s.", nPosStride * index, pRenderMesh->GetSourceName());
break;
}
}
#endif
if (!pPos || !pInds)
{
return false;
}
if (bAllDMeshData)
{
pUV = (uint8*)pRenderMesh->GetUVPtr(nUVStride, FSL_READ);
pCol = (uint8*)pRenderMesh->GetColorPtr(nColStride, FSL_READ);
pTangs = pRenderMesh->GetTangentPtr(nTangsStride, FSL_READ);
}
return true;
}
template <class T>
bool GetBarycentricCoordinates(const T& a, const T& b, const T& c, const T& p, float& u, float& v, float& w, float fBorder /* = 0.f */)
{
// Compute vectors
T v0 = b - a;
T v1 = c - a;
T v2 = p - a;
// Compute dot products
float dot00 = v0.Dot(v0);
float dot01 = v0.Dot(v1);
float dot02 = v0.Dot(v2);
float dot11 = v1.Dot(v1);
float dot12 = v1.Dot(v2);
// Compute barycentric coordinates
float invDenom = 1.f / (dot00 * dot11 - dot01 * dot01);
v = (dot11 * dot02 - dot01 * dot12) * invDenom;
w = (dot00 * dot12 - dot01 * dot02) * invDenom;
u = 1.f - v - w;
// Check if point is in triangle
return (u >= -fBorder) && (v >= -fBorder) && (w >= -fBorder);
}
void CRenderMeshUtils::ClearHitCache()
{
// do not allow items to stay too long in the cache, it allows to minimize wrong hit detections
memmove(&last_hits[1], &last_hits[0], sizeof(last_hits) - sizeof(last_hits[0])); // Move hits to the end of array, throwing out the last one.
memset(&last_hits[0], 0, sizeof(last_hits[0]));
}
//////////////////////////////////////////////////////////////////////////
bool CRenderMeshUtils::RayIntersection(IRenderMesh* pRenderMesh, SRayHitInfo& hitInfo, _smart_ptr<IMaterial> pMtl)
{
SIntersectionData data;
pRenderMesh->LockForThreadAccess();
if (!data.Init(pRenderMesh, &hitInfo, pMtl))
{
return false;
}
// forward call to implementation
bool result = CRenderMeshUtils::RayIntersectionImpl(&data, &hitInfo, pMtl, false);
pRenderMesh->UnlockStream(VSF_GENERAL);
pRenderMesh->UnlockIndexStream();
pRenderMesh->UnLockForThreadAccess();
return result;
}
void CRenderMeshUtils::RayIntersectionAsync(SIntersectionData* pIntersectionRMData)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::ThreeDEngine);
// forward call to implementation
SRayHitInfo& rHitInfo = *pIntersectionRMData->pHitInfo;
SIntersectionData& rIntersectionData = *pIntersectionRMData;
if (CRenderMeshUtils::RayIntersectionImpl(&rIntersectionData, &rHitInfo, rIntersectionData.pMtl, true))
{
const float testAreaSize = GetFloatCVar(e_DecalsPlacementTestAreaSize);
const float minTestDepth = GetFloatCVar(e_DecalsPlacementTestMinDepth);
if (rIntersectionData.bDecalPlacementTestRequested && testAreaSize)
{
rIntersectionData.fDecalPlacementTestMaxSize = 0.f;
float fRange = testAreaSize * 0.5f;
for (uint32 i = 0; i < 2; ++i, fRange *= 2.f)
{
Vec3 vDir = -rHitInfo.vHitNormal;
vDir.Normalize();
Vec3 vRight;
Vec3 vUp;
if (fabs(vDir.Dot(Vec3(0, 0, 1))) > 0.995f)
{
vRight = Vec3(1, 0, 0);
vUp = Vec3(0, 1, 0);
}
else
{
vRight = vDir.Cross(Vec3(0, 0, 1));
vUp = vRight.Cross(vDir);
}
Vec3 arrOffset[4] = { vRight* fRange, -vRight * fRange, vUp * fRange, -vUp * fRange };
SRayHitInfo hInfo;
SIntersectionData intersectionData;
float fDepth = max(minTestDepth, fRange * 0.2f);
int nPoint;
for (nPoint = 0; nPoint < 4; nPoint++)
{
intersectionData = rIntersectionData;
hInfo = rHitInfo;
hInfo.inReferencePoint = hInfo.vHitPos + arrOffset[nPoint];//*fRange;
hInfo.inRay.origin = hInfo.inReferencePoint + hInfo.vHitNormal * fDepth;
hInfo.inRay.direction = -hInfo.vHitNormal * fDepth * 2.f;
hInfo.fMaxHitDistance = fDepth;
if (!CRenderMeshUtils::RayIntersectionImpl(&intersectionData, &hInfo, rIntersectionData.pMtl, true))
{
break;
}
}
if (nPoint == 4)
{
rIntersectionData.fDecalPlacementTestMaxSize = fRange;
}
else
{
break;
}
}
}
}
}
bool GetVertColorAndTC(SIntersectionData& rIntersectionRMData, SRayHitInfo& hitInfo)
{
int nPosStride = rIntersectionRMData.nPosStride;
uint8* pPos = rIntersectionRMData.pPos;
int nUVStride = rIntersectionRMData.nUVStride;
uint8* pUV = rIntersectionRMData.pUV;
int nColStride = rIntersectionRMData.nColStride;
uint8* pCol = rIntersectionRMData.pCol;
vtx_idx* pInds = rIntersectionRMData.pInds;
int I0 = pInds[hitInfo.nHitTriID + 0];
int I1 = pInds[hitInfo.nHitTriID + 1];
int I2 = pInds[hitInfo.nHitTriID + 2];
// get tri vertices
Vec3& tv0 = *((Vec3*)&pPos[nPosStride * I0]);
Vec3& tv1 = *((Vec3*)&pPos[nPosStride * I1]);
Vec3& tv2 = *((Vec3*)&pPos[nPosStride * I2]);
float u = 0.f, v = 0.f, w = 0.f;
if (GetBarycentricCoordinates(tv0, tv1, tv2, hitInfo.vHitPos, u, v, w, 16.0f))
{
float arrVertWeight[3] = { max(0.f, u), max(0.f, v), max(0.f, w) };
float fDiv = 1.f / (arrVertWeight[0] + arrVertWeight[1] + arrVertWeight[2]);
arrVertWeight[0] *= fDiv;
arrVertWeight[1] *= fDiv;
arrVertWeight[2] *= fDiv;
Vec2 tc0 = *((Vec2*)&pUV[nUVStride * I0]);
Vec2 tc1 = *((Vec2*)&pUV[nUVStride * I1]);
Vec2 tc2 = *((Vec2*)&pUV[nUVStride * I2]);
hitInfo.vHitTC = tc0 * arrVertWeight[0] + tc1 * arrVertWeight[1] + tc2 * arrVertWeight[2];
Vec4 c0 = (*(ColorB*)&pCol[nColStride * I0]).toVec4();
Vec4 c1 = (*(ColorB*)&pCol[nColStride * I1]).toVec4();
Vec4 c2 = (*(ColorB*)&pCol[nColStride * I2]).toVec4();
// get tangent basis
int nTangsStride = rIntersectionRMData.nTangsStride;
byte* pTangs = rIntersectionRMData.pTangs;
Vec4 tangent[3];
Vec4 bitangent[3];
int arrId[3] = { I0, I1, I2 };
for (int ii = 0; ii < 3; ii++)
{
SPipTangents tb = *(SPipTangents*)&pTangs[nTangsStride * arrId[ii]];
tb.GetTB(tangent[ii], bitangent[ii]);
}
hitInfo.vHitTangent = (tangent[0] * arrVertWeight[0] + tangent[1] * arrVertWeight[1] + tangent[2] * arrVertWeight[2]);
hitInfo.vHitBitangent = (bitangent[0] * arrVertWeight[0] + bitangent[1] * arrVertWeight[1] + bitangent[2] * arrVertWeight[2]);
hitInfo.vHitColor = (c0 * arrVertWeight[0] + c1 * arrVertWeight[1] + c2 * arrVertWeight[2]) / 255.f;
return true;
}
return false;
}
bool CRenderMeshUtils::RayIntersectionImpl(SIntersectionData* pIntersectionRMData, SRayHitInfo* phitInfo, _smart_ptr<IMaterial> pMtl, bool bAsync)
{
#ifdef RENDER_MESH_TRIANGLE_HASH_MAP_SUPPORT
IF (phitInfo->bGetVertColorAndTC, 0)
#else
IF (phitInfo->bGetVertColorAndTC && phitInfo->inRay.direction.IsZero(), 0)
#endif
{
return RayIntersectionFastImpl(*pIntersectionRMData, *phitInfo, pMtl, bAsync);
}
SIntersectionData& rIntersectionRMData = *pIntersectionRMData;
SRayHitInfo& hitInfo = *phitInfo;
FUNCTION_PROFILER_3DENGINE;
//CTimeValue t0 = gEnv->pTimer->GetAsyncTime();
float fMaxDist2 = hitInfo.fMaxHitDistance * hitInfo.fMaxHitDistance;
Vec3 vHitPos(0, 0, 0);
Vec3 vHitNormal(0, 0, 0);
static bool bClearHitCache = true;
if (bClearHitCache && !bAsync)
{
memset(last_hits, 0, sizeof(last_hits));
bClearHitCache = false;
}
if (hitInfo.bUseCache && !bAsync)
{
Vec3 vOut;
// Check for cached hits.
for (int i = 0; i < MAX_CACHED_HITS; i++)
{
if (last_hits[i].pRenderMesh == rIntersectionRMData.pRenderMesh)
{
// If testing same render mesh, check if we hit the same triangle again.
if (Intersect::Ray_Triangle(hitInfo.inRay, last_hits[i].tri[0], last_hits[i].tri[2], last_hits[i].tri[1], vOut))
{
if (fMaxDist2)
{
float fDistance2 = hitInfo.inReferencePoint.GetSquaredDistance(vOut);
if (fDistance2 > fMaxDist2)
{
continue; // Ignore hits that are too far.
}
}
// Cached hit.
hitInfo.vHitPos = vOut;
hitInfo.vHitNormal = last_hits[i].hitInfo.vHitNormal;
hitInfo.nHitMatID = last_hits[i].hitInfo.nHitMatID;
hitInfo.nHitSurfaceID = last_hits[i].hitInfo.nHitSurfaceID;
if (hitInfo.inRetTriangle)
{
hitInfo.vTri0 = last_hits[i].tri[0];
hitInfo.vTri1 = last_hits[i].tri[1];
hitInfo.vTri2 = last_hits[i].tri[2];
}
//CTimeValue t1 = gEnv->pTimer->GetAsyncTime();
//CryLogAlways( "TestTime :%.2f", (t1-t0).GetMilliSeconds() );
//static int nCount = 0; CryLogAlways( "Cached Hit %d",++nCount );
hitInfo.pRenderMesh = rIntersectionRMData.pRenderMesh;
rIntersectionRMData.bResult = true;
return true;
}
}
}
}
uint nVerts = rIntersectionRMData.nVerts;
int nInds = rIntersectionRMData.nInds;
assert(nInds != 0 && nVerts != 0);
// get position offset and stride
const int nPosStride = rIntersectionRMData.nPosStride;
uint8* const pPos = rIntersectionRMData.pPos;
// get indices
vtx_idx* const pInds = rIntersectionRMData.pInds;
assert(pInds != NULL && pPos != NULL);
assert(nInds % 3 == 0);
float fMinDistance2 = FLT_MAX;
Ray inRay = hitInfo.inRay;
bool bAnyHit = false;
Vec3 vOut;
Vec3 tri[3];
// test tris
TRenderChunkArray& RESTRICT_REFERENCE Chunks = rIntersectionRMData.pRenderMesh->GetChunks();
int nChunkCount = Chunks.size();
for (int nChunkId = 0; nChunkId < nChunkCount; nChunkId++)
{
CRenderChunk* pChunk = &Chunks[nChunkId];
IF (pChunk->m_nMatFlags & MTL_FLAG_NODRAW || !pChunk->pRE, 0)
{
continue;
}
const int16 nChunkMatID = pChunk->m_nMatID;
bool b2Sided = false;
if (pMtl)
{
const SShaderItem& shaderItem = pMtl->GetShaderItem(nChunkMatID);
if (hitInfo.bOnlyZWrite && !shaderItem.IsZWrite())
{
continue;
}
if (!shaderItem.m_pShader || shaderItem.m_pShader->GetFlags() & EF_NODRAW || shaderItem.m_pShader->GetFlags() & EF_DECAL)
{
continue;
}
if (shaderItem.m_pShader->GetCull() & eCULL_None)
{
b2Sided = true;
}
if (shaderItem.m_pShaderResources && shaderItem.m_pShaderResources->GetResFlags() & MTL_FLAG_2SIDED)
{
b2Sided = true;
}
}
int nLastIndexId = pChunk->nFirstIndexId + pChunk->nNumIndices;
const vtx_idx* __restrict pIndices = rIntersectionRMData.pInds;
IF (nLastIndexId - 1 >= nInds, 0)
{
Error("%s (%s): invalid mesh chunk", __FUNCTION__, rIntersectionRMData.pRenderMesh->GetSourceName());
rIntersectionRMData.bResult = false;
return 0;
}
// make line triangle intersection
int i = pChunk->nFirstIndexId;
while (i < nLastIndexId)
{
int p = nLastIndexId;
for (; i < p; i += 3)//process all prefetched vertices
{
IF (pInds[i + 2] >= nVerts, 0)
{
Error("%s (%s): invalid mesh indices", __FUNCTION__, rIntersectionRMData.pRenderMesh->GetSourceName());
rIntersectionRMData.bResult = false;
return 0;
}
// get tri vertices
const Vec3& tv0 = *(const Vec3*)&pPos[ nPosStride * pIndices[i + 0] ];
const Vec3& tv1 = *(const Vec3*)&pPos[ nPosStride * pIndices[i + 1] ];
const Vec3& tv2 = *(const Vec3*)&pPos[ nPosStride * pIndices[i + 2] ];
if (Intersect::Ray_Triangle(inRay, tv0, tv2, tv1, vOut))
{
float fDistance2 = hitInfo.inReferencePoint.GetSquaredDistance(vOut);
if (fMaxDist2)
{
if (fDistance2 > fMaxDist2)
{
continue; // Ignore hits that are too far.
}
}
bAnyHit = true;
// Test front.
if (hitInfo.bInFirstHit)
{
vHitPos = vOut;
hitInfo.nHitMatID = nChunkMatID;
hitInfo.nHitTriID = i;
tri[0] = tv0;
tri[1] = tv1;
tri[2] = tv2;
goto AnyHit;//need to break chunk loops, vertex loop and prefetched loop
}
if (fDistance2 < fMinDistance2)
{
fMinDistance2 = fDistance2;
vHitPos = vOut;
hitInfo.nHitMatID = nChunkMatID;
hitInfo.nHitTriID = i;
tri[0] = tv0;
tri[1] = tv1;
tri[2] = tv2;
}
}
else
if (b2Sided)
{
if (Intersect::Ray_Triangle(inRay, tv0, tv1, tv2, vOut))
{
float fDistance2 = hitInfo.inReferencePoint.GetSquaredDistance(vOut);
if (fMaxDist2)
{
if (fDistance2 > fMaxDist2)
{
continue; // Ignore hits that are too far.
}
}
bAnyHit = true;
// Test back.
if (hitInfo.bInFirstHit)
{
vHitPos = vOut;
hitInfo.nHitMatID = nChunkMatID;
hitInfo.nHitTriID = i;
tri[0] = tv0;
tri[1] = tv2;
tri[2] = tv1;
goto AnyHit;//need to break chunk loops, vertex loop and prefetched loop
}
if (fDistance2 < fMinDistance2)
{
fMinDistance2 = fDistance2;
vHitPos = vOut;
hitInfo.nHitMatID = nChunkMatID;
hitInfo.nHitTriID = i;
tri[0] = tv0;
tri[1] = tv2;
tri[2] = tv1;
}
}
}
}
}
}
AnyHit:
if (bAnyHit)
{
hitInfo.pRenderMesh = rIntersectionRMData.pRenderMesh;
// return closest to the shooter
hitInfo.fDistance = (float)sqrt_tpl(fMinDistance2);
hitInfo.vHitNormal = (tri[1] - tri[0]).Cross(tri[2] - tri[0]).GetNormalized();
hitInfo.vHitPos = vHitPos;
hitInfo.nHitSurfaceID = 0;
if (hitInfo.inRetTriangle)
{
hitInfo.vTri0 = tri[0];
hitInfo.vTri1 = tri[1];
hitInfo.vTri2 = tri[2];
}
#ifndef RENDER_MESH_TRIANGLE_HASH_MAP_SUPPORT
if (hitInfo.bGetVertColorAndTC && hitInfo.nHitTriID >= 0 && !inRay.direction.IsZero())
{
GetVertColorAndTC(rIntersectionRMData, hitInfo);
}
#endif
if (pMtl)
{
pMtl = pMtl->GetSafeSubMtl(hitInfo.nHitMatID);
if (pMtl)
{
hitInfo.nHitSurfaceID = pMtl->GetSurfaceTypeId();
}
}
if (!bAsync)
{
//////////////////////////////////////////////////////////////////////////
// Add to cached results.
memmove(&last_hits[1], &last_hits[0], sizeof(last_hits) - sizeof(last_hits[0])); // Move hits to the end of array, throwing out the last one.
last_hits[0].pRenderMesh = rIntersectionRMData.pRenderMesh;
last_hits[0].hitInfo = hitInfo;
memcpy(last_hits[0].tri, tri, sizeof(tri));
//////////////////////////////////////////////////////////////////////////
}
}
//CTimeValue t1 = gEnv->pTimer->GetAsyncTime();
//CryLogAlways( "TestTime :%.2f", (t1-t0).GetMilliSeconds() );
rIntersectionRMData.bResult = bAnyHit;
return bAnyHit;
}
bool CRenderMeshUtils::RayIntersectionFast(IRenderMesh* pRenderMesh, SRayHitInfo& hitInfo, _smart_ptr<IMaterial> pMtl)
{
SIntersectionData data;
if (!data.Init(pRenderMesh, &hitInfo, pMtl))
{
return false;
}
// forward call to implementation
return CRenderMeshUtils::RayIntersectionFastImpl(data, hitInfo, pMtl, false);
}
//////////////////////////////////////////////////////////////////////////
bool CRenderMeshUtils::RayIntersectionFastImpl(SIntersectionData& rIntersectionRMData, SRayHitInfo& hitInfo, _smart_ptr<IMaterial> pMtl, [[maybe_unused]] bool bAsync)
{
float fBestDist = hitInfo.fMaxHitDistance; // squared distance works different for values less and more than 1.f
Vec3 vHitPos(0, 0, 0);
Vec3 vHitNormal(0, 0, 0);
int nVerts = rIntersectionRMData.nVerts;
int nInds = rIntersectionRMData.nInds;
assert(nInds != 0 && nVerts != 0);
// get position offset and stride
int nPosStride = rIntersectionRMData.nPosStride;
uint8* pPos = rIntersectionRMData.pPos;
int nUVStride = rIntersectionRMData.nUVStride;
uint8* pUV = rIntersectionRMData.pUV;
int nColStride = rIntersectionRMData.nColStride;
uint8* pCol = rIntersectionRMData.pCol;
// get indices
vtx_idx* pInds = rIntersectionRMData.pInds;
assert(pInds != NULL && pPos != NULL);
assert(nInds % 3 == 0);
Ray inRay = hitInfo.inRay;
bool bAnyHit = false;
Vec3 vOut;
Vec3 tri[3];
// test tris
Line inLine(inRay.origin, inRay.direction);
if (!inRay.direction.IsZero() && hitInfo.nHitTriID >= 0)
{
if (hitInfo.nHitTriID + 2 >= nInds)
{
return false;
}
int I0 = pInds[hitInfo.nHitTriID + 0];
int I1 = pInds[hitInfo.nHitTriID + 1];
int I2 = pInds[hitInfo.nHitTriID + 2];
if (I0 < nVerts && I1 < nVerts && I2 < nVerts)
{
// get tri vertices
Vec3& tv0 = *((Vec3*)&pPos[nPosStride * I0]);
Vec3& tv1 = *((Vec3*)&pPos[nPosStride * I1]);
Vec3& tv2 = *((Vec3*)&pPos[nPosStride * I2]);
if (Intersect::Line_Triangle(inLine, tv0, tv2, tv1, vOut))// || Intersect::Line_Triangle( inLine, tv0, tv1, tv2, vOut ))
{
float fDistance = (hitInfo.inReferencePoint - vOut).GetLengthFast();
if (fBestDist == 0.f || fDistance < fBestDist)
{
bAnyHit = true;
fBestDist = fDistance;
vHitPos = vOut;
tri[0] = tv0;
tri[1] = tv1;
tri[2] = tv2;
}
}
}
}
if (hitInfo.nHitTriID == HIT_UNKNOWN)
{
if (inRay.direction.IsZero())
{
ProcessBoxIntersection(inRay, hitInfo, rIntersectionRMData, pMtl, pInds, nVerts, pPos, nPosStride, pUV, nUVStride, pCol, nColStride, nInds, bAnyHit, fBestDist, vHitPos, tri);
}
else
{
if (const PodArray<std::pair<int, int> >* pTris = rIntersectionRMData.pRenderMesh->GetTrisForPosition(inRay.origin + inRay.direction * 0.5f, pMtl))
{
for (int nId = 0; nId < pTris->Count(); ++nId)
{
const std::pair<int, int>& t = pTris->GetAt(nId);
if (t.first + 2 >= nInds)
{
return false;
}
int I0 = pInds[t.first + 0];
int I1 = pInds[t.first + 1];
int I2 = pInds[t.first + 2];
if (I0 >= nVerts || I1 >= nVerts || I2 >= nVerts)
{
return false;
}
// get tri vertices
Vec3& tv0 = *((Vec3*)&pPos[nPosStride * I0]);
Vec3& tv1 = *((Vec3*)&pPos[nPosStride * I1]);
Vec3& tv2 = *((Vec3*)&pPos[nPosStride * I2]);
if (Intersect::Line_Triangle(inLine, tv0, tv2, tv1, vOut)) // || Intersect::Line_Triangle( inLine, tv0, tv1, tv2, vOut ))
{
float fDistance = (hitInfo.inReferencePoint - vOut).GetLengthFast();
if (fDistance < fBestDist)
{
bAnyHit = true;
fBestDist = fDistance;
vHitPos = vOut;
tri[0] = tv0;
tri[1] = tv1;
tri[2] = tv2;
hitInfo.nHitMatID = t.second;
hitInfo.nHitTriID = t.first;
}
}
}
}
}
}
if (bAnyHit)
{
hitInfo.pRenderMesh = rIntersectionRMData.pRenderMesh;
// return closest to the shooter
hitInfo.fDistance = fBestDist;
hitInfo.vHitNormal = (tri[1] - tri[0]).Cross(tri[2] - tri[0]).GetNormalized();
hitInfo.vHitPos = vHitPos;
hitInfo.nHitSurfaceID = 0;
if (pMtl)
{
pMtl = pMtl->GetSafeSubMtl(hitInfo.nHitMatID);
if (pMtl)
{
hitInfo.nHitSurfaceID = pMtl->GetSurfaceTypeId();
}
}
if (hitInfo.bGetVertColorAndTC && hitInfo.nHitTriID >= 0 && !inRay.direction.IsZero())
{
GetVertColorAndTC(rIntersectionRMData, hitInfo);
}
}
//CTimeValue t1 = gEnv->pTimer->GetAsyncTime();
//CryLogAlways( "TestTime :%.2f", (t1-t0).GetMilliSeconds() );
return bAnyHit;
}
// used for CPU voxelization
bool CRenderMeshUtils::ProcessBoxIntersection(Ray& inRay, SRayHitInfo& hitInfo, SIntersectionData& rIntersectionRMData, _smart_ptr<IMaterial> pMtl, vtx_idx* pInds, int nVerts, uint8* pPos, int nPosStride, uint8* pUV, int nUVStride, uint8* pCol, int nColStride, int nInds, bool& bAnyHit, float& fBestDist, Vec3& vHitPos, Vec3* tri)
{
AABB voxBox;
voxBox.min = inRay.origin - Vec3(hitInfo.fMaxHitDistance);
voxBox.max = inRay.origin + Vec3(hitInfo.fMaxHitDistance);
if (hitInfo.pHitTris)
{ // just collect tris
TRenderChunkArray& Chunks = rIntersectionRMData.pRenderMesh->GetChunks();
int nChunkCount = Chunks.size();
for (int nChunkId = 0; nChunkId < nChunkCount; nChunkId++)
{
CRenderChunk* pChunk(&Chunks[nChunkId]);
IF (pChunk->m_nMatFlags & MTL_FLAG_NODRAW || !pChunk->pRE, 0)
{
continue;
}
const int16 nChunkMatID = pChunk->m_nMatID;
bool b2Sided = false;
const SShaderItem& shaderItem = pMtl->GetShaderItem(nChunkMatID);
// if (!shaderItem.IsZWrite())
// continue;
IShader* pShader = shaderItem.m_pShader;
if (!pShader || pShader->GetFlags() & EF_NODRAW || pShader->GetFlags() & EF_DECAL || (pShader->GetShaderType() != eST_General))
{
continue;
}
if (pShader->GetCull() & eCULL_None)
{
b2Sided = true;
}
if (shaderItem.m_pShaderResources && shaderItem.m_pShaderResources->GetResFlags() & MTL_FLAG_2SIDED)
{
b2Sided = true;
}
float fOpacity = shaderItem.m_pShaderResources->GetStrengthValue(EFTT_OPACITY) * shaderItem.m_pShaderResources->GetVoxelCoverage();
if (fOpacity < hitInfo.fMinHitOpacity)
{
continue;
}
// ColorB colEm = shaderItem.m_pShaderResources->GetEmissiveColor();
// if(!colEm.r && !colEm.g && !colEm.b)
// colEm = Col_DarkGray;
// make line triangle intersection
for (uint ii = pChunk->nFirstIndexId; ii < pChunk->nFirstIndexId + pChunk->nNumIndices; ii += 3)
{
int I0 = pInds[ii + 0];
int I1 = pInds[ii + 1];
int I2 = pInds[ii + 2];
if (I0 >= nVerts || I1 >= nVerts || I2 >= nVerts)
{
return false;
}
// get tri vertices
Vec3& tv0 = *((Vec3*)&pPos[nPosStride * I0]);
Vec3& tv1 = *((Vec3*)&pPos[nPosStride * I1]);
Vec3& tv2 = *((Vec3*)&pPos[nPosStride * I2]);
#if defined(_DEBUG)
// Additional validation checks against the vectors that will be used in the triangle overlap
// validation
if (isnan(tv0.x) || isnan(tv0.y) || isnan(tv0.z) ||
isnan(tv1.x) || isnan(tv1.y) || isnan(tv1.z) ||
isnan(tv2.x) || isnan(tv2.y) || isnan(tv2.z))
{
return false;
}
#endif // defined(_DEBUG)
if (Overlap::AABB_Triangle(voxBox, tv0, tv2, tv1))
{
AABB triBox(tv0, tv0);
triBox.Add(tv2);
triBox.Add(tv1);
if (triBox.GetRadiusSqr() > 0.00001f)
{
SRayHitTriangle ht;
ht.v[0] = tv0;
ht.v[1] = tv1;
ht.v[2] = tv2;
ht.t[0] = *((Vec2*)&pUV[nUVStride * I0]);
ht.t[1] = *((Vec2*)&pUV[nUVStride * I1]);
ht.t[2] = *((Vec2*)&pUV[nUVStride * I2]);
ht.pMat = pMtl->GetSafeSubMtl(nChunkMatID);
ht.c[0] = (*(ColorB*)&pCol[nColStride * I0]);
ht.c[1] = (*(ColorB*)&pCol[nColStride * I1]);
ht.c[2] = (*(ColorB*)&pCol[nColStride * I2]);
ht.nOpacity = SATURATEB(int(fOpacity * 255.f));
hitInfo.pHitTris->Add(ht);
}
}
}
}
}
else if (const PodArray<std::pair<int, int> >* pTris = rIntersectionRMData.pRenderMesh->GetTrisForPosition(inRay.origin, pMtl))
{
for (int nId = 0; nId < pTris->Count(); ++nId)
{
const std::pair<int, int>& t = pTris->GetAt(nId);
if (t.first + 2 >= nInds)
{
return false;
}
int I0 = pInds[t.first + 0];
int I1 = pInds[t.first + 1];
int I2 = pInds[t.first + 2];
if (I0 >= nVerts || I1 >= nVerts || I2 >= nVerts)
{
return false;
}
// get tri vertices
Vec3& tv0 = *((Vec3*)&pPos[nPosStride * I0]);
Vec3& tv1 = *((Vec3*)&pPos[nPosStride * I1]);
Vec3& tv2 = *((Vec3*)&pPos[nPosStride * I2]);
if (Overlap::AABB_Triangle(voxBox, tv0, tv2, tv1))
{
{
_smart_ptr<IMaterial> pSubMtl = pMtl->GetSafeSubMtl(t.second);
if (pSubMtl)
{
if (!pSubMtl->GetShaderItem().IsZWrite())
{
continue;
}
if (!pSubMtl->GetShaderItem().m_pShader)
{
continue;
}
if (pSubMtl->GetShaderItem().m_pShader->GetShaderType() != eST_Metal && pSubMtl->GetShaderItem().m_pShader->GetShaderType() != eST_General)
{
continue;
}
}
}
bAnyHit = true;
fBestDist = 0;
vHitPos = voxBox.GetCenter();
tri[0] = tv0;
tri[1] = tv1;
tri[2] = tv2;
hitInfo.nHitMatID = t.second;
hitInfo.nHitTriID = t.first;
break;
}
}
}
return bAnyHit;
}