📄 opc_raycollider.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 a ray collider.
* \file OPC_RayCollider.cpp
* \author Pierre Terdiman
* \date June, 2, 2001
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a ray-vs-tree collider.
* This class performs a stabbing query on an AABB tree, i.e. does a ray-mesh collision.
*
* HIGHER DISTANCE BOUND:
*
* If P0 and P1 are two 3D points, let's define:
* - d = distance between P0 and P1
* - Origin = P0
* - Direction = (P1 - P0) / d = normalized direction vector
* - A parameter t such as a point P on the line (P0,P1) is P = Origin + t * Direction
* - t = 0 --> P = P0
* - t = d --> P = P1
*
* Then we can define a general "ray" as:
*
* struct Ray
* {
* Point Origin;
* Point Direction;
* };
*
* But it actually maps three different things:
* - a segment, when 0 <= t <= d
* - a half-line, when 0 <= t < +infinity, or -infinity < t <= d
* - a line, when -infinity < t < +infinity
*
* In Opcode, we support segment queries, which yield half-line queries by setting d = +infinity.
* We don't support line-queries. If you need them, shift the origin along the ray by an appropriate margin.
*
* In short, the lower bound is always 0, and you can setup the higher bound "d" with RayCollider::SetMaxDist().
*
* Query |segment |half-line |line
* --------|-------------------|---------------|----------------
* Usages |-shadow feelers |-raytracing |-
* |-sweep tests |-in/out tests |
*
* FIRST CONTACT:
*
* - You can setup "first contact" mode or "all contacts" mode with RayCollider::SetFirstContact().
* - In "first contact" mode we return as soon as the ray hits one face. If can be useful e.g. for shadow feelers, where
* you want to know whether the path to the light is free or not (a boolean answer is enough).
* - In "all contacts" mode we return all faces hit by the ray.
*
* TEMPORAL COHERENCE:
*
* - You can enable or disable temporal coherence with RayCollider::SetTemporalCoherence().
* - It currently only works in "first contact" mode.
* - If temporal coherence is enabled, the previously hit triangle is cached during the first query. Then, next queries
* start by colliding the ray against the cached triangle. If they still collide, we return immediately.
*
* CLOSEST HIT:
*
* - You can enable or disable "closest hit" with RayCollider::SetClosestHit().
* - It currently only works in "all contacts" mode.
* - If closest hit is enabled, faces are sorted by distance on-the-fly and the closest one only is reported.
*
* BACKFACE CULLING:
*
* - You can enable or disable backface culling with RayCollider::SetCulling().
* - If culling is enabled, ray will not hit back faces (only front faces).
*
*
*
* \class RayCollider
* \author Pierre Terdiman
* \version 1.3
* \date June, 2, 2001
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* This class describes a face hit by a ray or segment.
* This is a particular class dedicated to stabbing queries.
*
* \class CollisionFace
* \author Pierre Terdiman
* \version 1.3
* \date March, 20, 2001
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* This class is a dedicated collection of CollisionFace.
*
* \class CollisionFaces
* \author Pierre Terdiman
* \version 1.3
* \date March, 20, 2001
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Opcode/Stdafx.h"
using namespace Opcode;
using namespace IceMaths;
// When this macro is set, the overlap tests considers an scaling on AABBs/tris.
// This means that the ray/line is not entirely in the model's local space when collision
// tests take places.
// #define OPC_RAYCOLLIDER_SCALE_BEFORE_OVERLAP
#include "Opcode/OPC_RayAABBOverlap.h"
#include "Opcode/OPC_RayTriOverlap.h"
#define SET_CONTACT(prim_index, flag) \
mNbIntersections++; \
/* Set contact status */ \
mFlags |= flag; \
/* In any case the contact has been found and recorded in mStabbedFace */ \
mStabbedFace.mFaceID = prim_index;
#ifdef OPC_RAYHIT_CALLBACK
#define HANDLE_CONTACT(prim_index, flag) \
SET_CONTACT(prim_index, flag) \
\
if(mHitCallback) (mHitCallback)(mStabbedFace, mUserData);
#define UPDATE_CACHE \
if(cache && GetContactStatus()) \
{ \
*cache = mStabbedFace.mFaceID; \
}
#else
#define HANDLE_CONTACT(prim_index, flag) \
SET_CONTACT(prim_index, flag) \
\
/* Now we can also record it in mStabbedFaces if available */ \
if(mStabbedFaces) \
{ \
/* If we want all faces or if that's the first one we hit */ \
if(!mClosestHit || !mStabbedFaces->GetNbFaces()) \
{ \
mStabbedFaces->AddFace(mStabbedFace); \
} \
else \
{ \
/* We only keep closest hit */ \
CollisionFace* Current = const_cast<CollisionFace*>(mStabbedFaces->GetFaces()); \
if(Current && mStabbedFace.mDistance<Current->mDistance) \
{ \
*Current = mStabbedFace; \
} \
} \
}
#define UPDATE_CACHE \
if(cache && GetContactStatus() && mStabbedFaces) \
{ \
const CollisionFace* Current = mStabbedFaces->GetFaces(); \
if(Current) *cache = Current->mFaceID; \
else *cache = INVALID_ID; \
}
#endif
#define SEGMENT_PRIM(prim_index, flag) \
/* Request vertices from the app */ \
VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
\
/* Perform ray-tri overlap test and return */ \
if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
{ \
/* Intersection point is valid if dist < segment's length */ \
/* We know dist>0 so we can use integers */ \
if(IR(mStabbedFace.mDistance)<IR(mMaxDist)) \
{ \
HANDLE_CONTACT(prim_index, flag) \
} \
}
#define RAY_PRIM(prim_index, flag) \
/* Request vertices from the app */ \
VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
\
/* Perform ray-tri overlap test and return */ \
if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
{ \
HANDLE_CONTACT(prim_index, flag) \
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RayCollider::RayCollider() :
mNbRayBVTests (0),
mNbRayPrimTests (0),
mNbIntersections (0),
mCulling (true),
#ifdef OPC_RAYHIT_CALLBACK
mHitCallback (null),
mUserData (0),
#else
mClosestHit (false),
mStabbedFaces (null),
#endif
mMaxDist (MAX_FLOAT)
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Destructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RayCollider::~RayCollider()
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Validates current settings. You should call this method after all the settings and callbacks have been defined.
* \return null if everything is ok, else a string describing the problem
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const char* RayCollider::ValidateSettings()
{
if(mMaxDist<0.0f) return "Higher distance bound must be positive!";
if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!";
#ifndef OPC_RAYHIT_CALLBACK
if(mClosestHit && FirstContactEnabled()) return "Closest hit doesn't work with ""First contact"" mode!";
if(TemporalCoherenceEnabled() && mClosestHit) return "Temporal coherence can't guarantee to report closest hit!";
#endif
if(SkipPrimitiveTests()) return "SkipPrimitiveTests not possible for RayCollider ! (not implemented)";
return null;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Generic stabbing query for generic OPCODE models. After the call, access the results:
* - with GetContactStatus()
* - in the user-provided destination array
*
* \param world_ray [in] stabbing ray in world space
* \param model [in] Opcode model to collide with
* \param world [in] model's world matrix, or null
* \param cache [in] a possibly cached face index, or null
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool RayCollider::Collide(const IceMaths::Ray& world_ray, const Model& model, const IceMaths::Matrix4x4* world, udword* cache)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -