📄 opc_lsscollider.cpp
字号:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* OPCODE - Optimized Collision Detection
* Copyright (C) 2001 Pierre Terdiman
* Homepage: http://www.codercorner.com/Opcode.htm
*
* OPCODE modifications for scaled model support (and other things)
* Copyright (C) 2004 Gilvan Maia (gilvan 'at' vdl.ufc.br)
* Check http://www.vdl.ufc.br/gilvan/coll/opcode/index.htm for updates.
*
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains code for an LSS collider.
* \file OPC_LSSCollider.cpp
* \author Pierre Terdiman
* \date December, 28, 2002
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a lss-vs-tree collider.
*
* \class LSSCollider
* \author Pierre Terdiman
* \version 1.3
* \date December, 28, 2002
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Opcode/Stdafx.h"
using namespace Opcode;
#include "Opcode/OPC_LSSAABBOverlap.h"
#include "Opcode/OPC_LSSTriOverlap.h"
#define SET_CONTACT(prim_index, flag) \
/* Set contact status */ \
mFlags |= flag; \
mTouchedPrimitives->Add(prim_index);
//! LSS-triangle overlap test
#define LSS_PRIM(prim_index, flag) \
/* Request vertices from the app */ \
VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
\
/* Perform LSS-tri overlap test */ \
if(LSSTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
{ \
SET_CONTACT(prim_index, flag) \
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
LSSCollider::LSSCollider()
{
// mCenter.Zero();
// mRadius2 = 0.0f;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Destructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
LSSCollider::~LSSCollider()
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Generic collision query for generic OPCODE models. After the call, access the results:
* - with GetContactStatus()
* - with GetNbTouchedPrimitives()
* - with GetTouchedPrimitives()
*
* \param cache [in/out] an lss cache
* \param lss [in] collision lss in local space
* \param model [in] Opcode model to collide with
* \param worldl [in] lss world matrix, or null
* \param worldm [in] model's world matrix, or null
* \return true if success
* \warning SCALE NOT SUPPORTED IN LSS WORLD MATRIX. The LSS matrix must contain rotation & translation parts only.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool LSSCollider::Collide(LSSCache& cache, const IceMaths::LSS& lss, const Model& model, const IceMaths::Matrix4x4* worldl, const IceMaths::Matrix4x4* worldm)
{
// Checkings
if(!Setup(&model)) return false;
// Init collision query
if(InitQuery(cache, lss, worldl, worldm)) 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 collision query
if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
else _Collide(Tree->GetNodes());
}
else
{
const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
// Perform collision query
if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
else _Collide(Tree->GetNodes());
}
}
else
{
if(model.IsQuantized())
{
const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
// Setup dequantization coeffs
mCenterCoeff = Tree->mCenterCoeff;
mExtentsCoeff = Tree->mExtentsCoeff;
// Perform collision query
if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
else _Collide(Tree->GetNodes());
}
else
{
const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
// Perform collision query
if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
else _Collide(Tree->GetNodes());
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Initializes a collision query :
* - reset stats & contact status
* - setup matrices
* - check temporal coherence
*
* \param cache [in/out] an lss cache
* \param lss [in] lss in local space
* \param worldl [in] lss world matrix, or null
* \param worldm [in] model's world matrix, or null
* \return TRUE if we can return immediately
* \warning SCALE NOT SUPPORTED IN LSS WORLD MATRIX. The matrix must contain rotation & translation parts only.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL LSSCollider::InitQuery(LSSCache& cache, const IceMaths::LSS& lss, const IceMaths::Matrix4x4* worldl, const IceMaths::Matrix4x4* worldm)
{
// 1) Call the base method
VolumeCollider::InitQuery();
// 2) Compute LSS in model space:
// - Precompute R^2
mRadius2 = lss.mRadius * lss.mRadius;
// - Compute segment
mSeg.mP0 = lss.mP0;
mSeg.mP1 = lss.mP1;
// -> to world space
if(worldl)
{
mSeg.mP0 *= *worldl;
mSeg.mP1 *= *worldl;
}
// -> to model space
if(worldm)
{
// Matrix normalization & scaling stripping
IceMaths::Matrix4x4 normWorldm;
IceMaths::NormalizePRSMatrix( normWorldm, mLocalScale, *worldm );
// Invert model matrix
IceMaths::Matrix4x4 InvWorldM;
IceMaths::InvertPRMatrix(InvWorldM, normWorldm);
mSeg.mP0 *= InvWorldM;
mSeg.mP1 *= InvWorldM;
}else
{
mLocalScale.Set(1.0,1.0,1.0);
}
// 3) Setup destination pointer
mTouchedPrimitives = &cache.TouchedPrimitives;
// 4) Special case: 1-triangle meshes [Opcode 1.3]
if(mCurrentModel && mCurrentModel->HasSingleNode())
{
if(!SkipPrimitiveTests())
{
// We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
mTouchedPrimitives->Reset();
// Perform overlap test between the unique triangle and the LSS (and set contact status if needed)
LSS_PRIM(udword(0), OPC_CONTACT)
// Return immediately regardless of status
return TRUE;
}
}
// 5) Check temporal coherence :
if(TemporalCoherenceEnabled())
{
// Here we use temporal coherence
// => check results from previous frame before performing the collision query
if(FirstContactEnabled())
{
// We're only interested in the first contact found => test the unique previously touched face
if(mTouchedPrimitives->GetNbEntries())
{
// Get index of previously touched face = the first entry in the array
udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
// Then reset the array:
// - if the overlap test below is successful, the index we'll get added back anyway
// - if it isn't, then the array should be reset anyway for the normal query
mTouchedPrimitives->Reset();
// Perform overlap test between the cached triangle and the LSS (and set contact status if needed)
LSS_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
// Return immediately if possible
if(GetContactStatus()) return TRUE;
}
// else no face has been touched during previous query
// => we'll have to perform a normal query
}
else
{
// We're interested in all contacts =>test the new real LSS N(ew) against the previous fat LSS P(revious):
// ### rewrite this
IceMaths::LSS Test(mSeg, lss.mRadius); // in model space
IceMaths::LSS Previous(cache.Previous, sqrtf(cache.Previous.mRadius));
// if(cache.Previous.Contains(Test))
if(IsCacheValid(cache) && Previous.Contains(Test))
{
// - if N is included in P, return previous list
// => we simply leave the list (mTouchedFaces) unchanged
// Set contact status if needed
if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT;
// In any case we don't need to do a query
return TRUE;
}
else
{
// - else do the query using a fat N
// Reset cache since we'll about to perform a real query
mTouchedPrimitives->Reset();
// Make a fat sphere so that coherence will work for subsequent frames
mRadius2 *= cache.FatCoeff;
// mRadius2 = (lss.mRadius * cache.FatCoeff)*(lss.mRadius * cache.FatCoeff);
// Update cache with query data (signature for cached faces)
cache.Previous.mP0 = mSeg.mP0;
cache.Previous.mP1 = mSeg.mP1;
cache.Previous.mRadius = mRadius2;
}
}
}
else
{
// Here we don't use temporal coherence => do a normal query
mTouchedPrimitives->Reset();
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Collision query for vanilla AABB trees.
* \param cache [in/out] an lss cache
* \param lss [in] collision lss in world space
* \param tree [in] AABB tree
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool LSSCollider::Collide(LSSCache& cache, const IceMaths::LSS& lss, const AABBTree* tree)
{
// 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
if(InitQuery(cache, lss)) return true;
// Perform collision query
_Collide(tree);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Checks the LSS completely contains the box. In which case we can end the query sooner.
* \param bc [in] box center
* \param be [in] box extents
* \return true if the LSS contains the whole box
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ BOOL LSSCollider::LSSContainsBox(const IceMaths::Point& bc, const IceMaths::Point& be)
{
// Not implemented
return FALSE;
}
#define TEST_BOX_IN_LSS(center, extents) \
if(LSSContainsBox(center, extents)) \
{ \
/* Set contact status */ \
mFlags |= OPC_CONTACT; \
_Dump(node); \
return; \
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Recursive collision query for normal AABB trees.
* \param node [in] current collision node
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void LSSCollider::_Collide(const AABBCollisionNode* node)
{
// Perform LSS-AABB overlap test
if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents)
if(node->IsLeaf())
{
LSS_PRIM(node->GetPrimitive(), OPC_CONTACT)
}
else
{
_Collide(node->GetPos());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -