📄 opc_raycollider.cpp
字号:
// Checkings
if(!Setup(&model)) return false;
// Init collision query
float maxDistanceBkp = mMaxDist;
Point originBkp = mOrigin;
Point dirBkp = mDir;
if(InitQuery(world_ray, world, cache))
{
mMaxDist = maxDistanceBkp;
mDir = dirBkp;
mOrigin = originBkp;
return true;
}
if(!model.HasLeafNodes())
{
if(model.IsQuantized())
{
const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
// Setup dequantization coeffs
mCenterCoeff = Tree->mCenterCoeff;
mExtentsCoeff = Tree->mExtentsCoeff;
// Perform stabbing query
if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
else _RayStab(Tree->GetNodes());
}
else
{
const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
// Perform stabbing query
if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
else _RayStab(Tree->GetNodes());
}
}
else
{
if(model.IsQuantized())
{
const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
// Setup dequantization coeffs
mCenterCoeff = Tree->mCenterCoeff;
mExtentsCoeff = Tree->mExtentsCoeff;
// Perform stabbing query
if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
else _RayStab(Tree->GetNodes());
}
else
{
const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
// Perform stabbing query
if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
else _RayStab(Tree->GetNodes());
}
}
// reverts max distance, etc
mMaxDist = maxDistanceBkp;
mDir = dirBkp;
mOrigin = originBkp;
// Update cache if needed
UPDATE_CACHE
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Initializes a stabbing query :
* - reset stats & contact status
* - compute ray in local space
* - check temporal coherence
*
* \param world_ray [in] stabbing ray in world space
* \param world [in] object's world matrix, or null
* \param face_id [in] index of previously stabbed triangle
* \return TRUE if we can return immediately
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL RayCollider::InitQuery(const IceMaths::Ray& world_ray, const IceMaths::Matrix4x4* world, udword* face_id)
{
// Reset stats & contact status
Collider::InitQuery();
mNbRayBVTests = 0;
mNbRayPrimTests = 0;
mNbIntersections = 0;
#ifndef OPC_RAYHIT_CALLBACK
if(mStabbedFaces) mStabbedFaces->Reset();
#endif
// Compute ray in local space
// The (Origin/Dir) form is needed for the ray-triangle test anyway (even for segment tests)
if(world)
{
#ifdef OPC_RAYCOLLIDER_SCALE_BEFORE_OVERLAP
// Matrix normalization & scaling stripping
Matrix4x4 normWorldm;
NormalizePRSMatrix( normWorldm, mLocalScale, *world );
// Invert model matrix
Matrix3x3 InvWorld = normWorldm;
mDir = InvWorld * world_ray.mDir;
Matrix4x4 World;
InvertPRMatrix(World, normWorldm);
mOrigin = world_ray.mOrig * World;
#else
// Now we are a much better code to get the ray in local space.
// Some notes about this new code:
// - faster, because we don't need to compute square roots anymore;
// - faster yet, because the number of divisions is even smaller now;
// - the intersection tests are robust enough to handle rays with non-unit direction vectors;
// - matrices are less subject to FPU errors, because I don't like square root;
// (it seems to introduce errors, because it cuts number precision by a half when
// stripping the matrix scale off)
// - the code is shorter and easier to maintain; :P
#pragma message(" >> Using new code for ray collision")
// first, invert the world matrix and transform the ray's origin
Matrix4x4 World;
InvertPRSMatrix(World, *world);
mOrigin = world_ray.mOrig * World;
// second, transform the ray's direction
Matrix3x3 InvWorld = World;
mDir = world_ray.mDir * InvWorld;
#endif
}
else
{
mLocalScale.Set(1.0f,1.0f,1.0f);
mDir = world_ray.mDir;
mOrigin = world_ray.mOrig;
}
// 4) Special case: 1-triangle meshes [Opcode 1.3]
if(mCurrentModel && mCurrentModel->HasSingleNode())
{
// We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
if(!SkipPrimitiveTests())
{
// Perform overlap test between the unique triangle and the ray (and set contact status if needed)
SEGMENT_PRIM(udword(0), OPC_CONTACT)
// Return immediately regardless of status
return TRUE;
}
}
// Check temporal coherence :
// Test previously colliding primitives first
if(TemporalCoherenceEnabled() && FirstContactEnabled() && face_id && *face_id!=INVALID_ID)
{
#ifdef OLD_CODE
#ifndef OPC_RAYHIT_CALLBACK
if(!mClosestHit)
#endif
{
// Request vertices from the app
VertexPointers VP;
mIMesh->GetTriangle(VP, *face_id);
// Perform ray-cached tri overlap test
if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2]))
{
// Intersection point is valid if:
// - distance is positive (else it can just be a face behind the orig point)
// - distance is smaller than a given max distance (useful for shadow feelers)
// if(mStabbedFace.mDistance>0.0f && mStabbedFace.mDistance<mMaxDist)
if(IR(mStabbedFace.mDistance)<IR(mMaxDist)) // The other test is already performed in RayTriOverlap
{
// Set contact status
mFlags |= OPC_TEMPORAL_CONTACT;
mStabbedFace.mFaceID = *face_id;
#ifndef OPC_RAYHIT_CALLBACK
if(mStabbedFaces) mStabbedFaces->AddFace(mStabbedFace);
#endif
return TRUE;
}
}
}
#else
// New code
// We handle both Segment/ray queries with the same segment code, and a possible infinite limit
SEGMENT_PRIM(*face_id, OPC_TEMPORAL_CONTACT)
// Return immediately if possible
if(GetContactStatus()) return TRUE;
#endif
}
// Precompute data (moved after temporal coherence since only needed for ray-AABB)
if(IR(mMaxDist)!=IEEE_MAX_FLOAT)
{
// For Segment-AABB overlap
mData = 0.5f * mDir * mMaxDist;
mData2 = mOrigin + mData;
// Precompute mFDir;
mFDir.x = fabsf(mData.x);
mFDir.y = fabsf(mData.y);
mFDir.z = fabsf(mData.z);
}
else
{
// For Ray-AABB overlap
// udword x = SIR(mDir.x)-1;
// udword y = SIR(mDir.y)-1;
// udword z = SIR(mDir.z)-1;
// mData.x = FR(x);
// mData.y = FR(y);
// mData.z = FR(z);
// Precompute mFDir;
mFDir.x = fabsf(mDir.x);
mFDir.y = fabsf(mDir.y);
mFDir.z = fabsf(mDir.z);
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Stabbing query for vanilla AABB trees.
* \param world_ray [in] stabbing ray in world space
* \param tree [in] AABB tree
* \param box_indices [out] indices of stabbed boxes
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool RayCollider::Collide(const IceMaths::Ray& world_ray, const AABBTree* tree, Container& box_indices)
{
// ### bad design here
// This is typically called for a scene tree, full of -AABBs-, not full of triangles.
// So we don't really have "primitives" to deal with. Hence it doesn't work with
// "FirstContact" + "TemporalCoherence".
ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) );
// Checkings
if(!tree) return false;
// Init collision query
// Basically this is only called to initialize precomputed data
if(InitQuery(world_ray)) return true;
// Perform stabbing query
if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(tree, box_indices);
else _RayStab(tree, box_indices);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Recursive stabbing query for normal AABB trees.
* \param node [in] current collision node
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -