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.
905 lines
31 KiB
C++
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;
|
|
}
|