KRAABB line cast culling algorithm is more intelligent now

Implemented ray casts and KRAABB ray intersections

--HG--
extra : convert_revision : svn%3A7752d6cf-9f14-4ad2-affc-04f1e67b81a5/trunk%40196
This commit is contained in:
kearwood
2012-12-20 19:24:02 +00:00
parent fd3a5a870c
commit 09736e6bb3
11 changed files with 65 additions and 54 deletions

View File

@@ -234,37 +234,48 @@ float KRAABB::longest_radius() const
bool KRAABB::intersectsLine(const KRVector3 &v1, const KRVector3 &v2) const
{
// http://www.3dkingdoms.com/weekly/bbox.h
KRVector3 dir = KRVector3::Normalize(v2 - v1);
float length = (v2 - v1).magnitude();
/*
// Put line in box space
KRMat4 MInv = m_M.InvertSimple();
KRVector3 LB1 = MInv * L1;
KRVector3 LB2 = MInv * L2;
// Get line midpoint and extent
KRVector3 LMid = (LB1 + LB2) * 0.5f;
KRVector3 L = (LB1 - LMid);
KRVector3 LExt = KRVector3( fabs(L.x), fabs(L.y), fabs(L.z) );
// Use Separating Axis Test
// Separation vector from box center to line center is LMid, since the line is in box space
if ( fabs( LMid.x ) > m_Extent.x + LExt.x ) return false;
if ( fabs( LMid.y ) > m_Extent.y + LExt.y ) return false;
if ( fabs( LMid.z ) > m_Extent.z + LExt.z ) return false;
// Crossproducts of line and each axis
if ( fabs( LMid.y * L.z - LMid.z * L.y) > (m_Extent.y * LExt.z + m_Extent.z * LExt.y) ) return false;
if ( fabs( LMid.x * L.z - LMid.z * L.x) > (m_Extent.x * LExt.z + m_Extent.z * LExt.x) ) return false;
if ( fabs( LMid.x * L.y - LMid.y * L.x) > (m_Extent.x * LExt.y + m_Extent.y * LExt.x) ) return false;
// No separating axis, the line intersects
// EZ cases: if the ray starts inside the box, or ends inside
// the box, then it definitely hits the box.
// I'm using this code for ray tracing with an octree,
// so I needed rays that start and end within an
// octree node to COUNT as hits.
// You could modify this test to (ray starts inside and ends outside)
// to qualify as a hit if you wanted to NOT count totally internal rays
if( contains( v1 ) || contains( v2 ) )
return true ;
*/
return false;
// the algorithm says, find 3 t's,
KRVector3 t ;
// LARGEST t is the only one we need to test if it's on the face.
for(int i = 0 ; i < 3 ; i++) {
if( dir[i] > 0 ) { // CULL BACK FACE
t[i] = ( min[i] - v1[i] ) / dir[i];
} else {
t[i] = ( max[i] - v1[i] ) / dir[i];
}
}
bool KRAABB::intersectsRay(const KRVector3 &v1, const KRVector3 &v2) const
int mi = 0;
if(t[1] > t[mi]) mi = 1;
if(t[2] > t[mi]) mi = 2;
if(t[mi] >= 0 && t[mi] <= length) {
KRVector3 pt = v1 + dir * t[mi];
// check it's in the box in other 2 dimensions
int o1 = ( mi + 1 ) % 3 ; // i=0: o1=1, o2=2, i=1: o1=2,o2=0 etc.
int o2 = ( mi + 2 ) % 3 ;
return pt[o1] >= min[o1] && pt[o1] <= max[o1] && pt[o2] >= min[o2] && pt[o2] <= max[o2];
}
return false ; // the ray did not hit the box.
}
bool KRAABB::intersectsRay(const KRVector3 &v1, const KRVector3 &dir) const
{
// TODO - Implementation needed
return true;
}

View File

@@ -33,7 +33,7 @@ public:
bool contains(const KRVector3 &v) const;
bool visible(const KRMat4 &matViewProjection) const;
bool intersectsLine(const KRVector3 &v1, const KRVector3 &v2) const;
bool intersectsRay(const KRVector3 &v1, const KRVector3 &v2) const;
bool intersectsRay(const KRVector3 &v1, const KRVector3 &dir) const;
KRAABB& operator =(const KRAABB& b);
bool operator ==(const KRAABB& b) const;

View File

@@ -89,14 +89,14 @@ bool KRCollider::lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &h
return false;
}
bool KRCollider::rayCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo)
bool KRCollider::rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitinfo)
{
if(m_models.size()) {
if(getBounds().intersectsRay(v0, v1)) {
if(getBounds().intersectsRay(v0, dir)) {
KRHitInfo hitinfo_model_space = KRHitInfo(KRMat4::Dot(getInverseModelMatrix(), hitinfo.getPosition()), KRMat4::DotNoTranslate(getInverseModelMatrix(), hitinfo.getNormal()), hitinfo.getNode());
KRVector3 v0_model_space = KRMat4::Dot(getModelMatrix(), v0);
KRVector3 v1_model_space = v0_model_space + KRMat4::DotNoTranslate(getModelMatrix(), KRVector3::Normalize(v1 - v0));
if(m_models[0]->lineCast(v0_model_space, v1_model_space, hitinfo_model_space)) {
KRVector3 dir_model_space = KRMat4::DotNoTranslate(getModelMatrix(), dir);
if(m_models[0]->rayCast(v0_model_space, dir, hitinfo_model_space)) {
hitinfo = KRHitInfo(KRMat4::Dot(getModelMatrix(), hitinfo_model_space.getPosition()), KRMat4::DotNoTranslate(getModelMatrix(), hitinfo_model_space.getNormal()), this);
return true;
}

View File

@@ -741,12 +741,12 @@ KRModel::model_format_t KRModel::getModelFormat() const
return (model_format_t)getHeader()->model_format;
}
bool KRModel::rayCast(const KRVector3 &line_v0, const KRVector3 &line_v1, const KRVector3 &tri_v0, const KRVector3 &tri_v1, const KRVector3 &tri_v2, const KRVector3 &tri_n0, const KRVector3 &tri_n1, const KRVector3 &tri_n2, KRHitInfo &hitinfo)
bool KRModel::rayCast(const KRVector3 &line_v0, const KRVector3 &dir, const KRVector3 &tri_v0, const KRVector3 &tri_v1, const KRVector3 &tri_v2, const KRVector3 &tri_n0, const KRVector3 &tri_n1, const KRVector3 &tri_n2, KRHitInfo &hitinfo)
{
// algorithm based on Dan Sunday's implementation at http://geomalgorithms.com/a06-_intersect-2.html
const float SMALL_NUM = 0.00000001; // anything that avoids division overflow
KRVector3 u, v, n; // triangle vectors
KRVector3 dir, w0, w; // ray vectors
KRVector3 w0, w; // ray vectors
float r, a, b; // params to calc ray-plane intersect
// get triangle edge vectors and plane normal
@@ -756,7 +756,6 @@ bool KRModel::rayCast(const KRVector3 &line_v0, const KRVector3 &line_v1, const
if (n == KRVector3::Zero()) // triangle is degenerate
return false; // do not deal with this case
dir = line_v1 - line_v0; // ray direction vector
w0 = line_v0 - tri_v0;
a = -KRVector3::Dot(n, w0);
b = KRVector3::Dot(n,dir);
@@ -817,12 +816,12 @@ bool KRModel::rayCast(const KRVector3 &line_v0, const KRVector3 &line_v1, const
return true; // hit_point is in triangle
}
bool KRModel::rayCast(const KRVector3 &line_v0, const KRVector3 &line_v1, int tri_index0, int tri_index1, int tri_index2, KRHitInfo &hitinfo) const
bool KRModel::rayCast(const KRVector3 &line_v0, const KRVector3 &dir, int tri_index0, int tri_index1, int tri_index2, KRHitInfo &hitinfo) const
{
return rayCast(line_v0, line_v1, getVertexPosition(tri_index0), getVertexPosition(tri_index1), getVertexPosition(tri_index2), getVertexNormal(tri_index0), getVertexNormal(tri_index1), getVertexNormal(tri_index2), hitinfo);
return rayCast(line_v0, dir, getVertexPosition(tri_index0), getVertexPosition(tri_index1), getVertexPosition(tri_index2), getVertexNormal(tri_index0), getVertexNormal(tri_index1), getVertexNormal(tri_index2), hitinfo);
}
bool KRModel::rayCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo) const
bool KRModel::rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitinfo) const
{
bool hit_found = false;
for(int submesh_index=0; submesh_index < getSubmeshCount(); submesh_index++) {
@@ -830,12 +829,12 @@ bool KRModel::rayCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitin
switch(getModelFormat()) {
case KRENGINE_MODEL_FORMAT_TRIANGLES:
for(int triangle_index=0; triangle_index < vertex_count / 3; triangle_index++) {
if(rayCast(v0, v1, getVertexPosition(triangle_index*3), getVertexPosition(triangle_index*3+1), getVertexPosition(triangle_index*3+2), getVertexNormal(triangle_index*3), getVertexNormal(triangle_index*3+1), getVertexNormal(triangle_index*3+2), hitinfo)) hit_found = true;
if(rayCast(v0, dir, getVertexPosition(triangle_index*3), getVertexPosition(triangle_index*3+1), getVertexPosition(triangle_index*3+2), getVertexNormal(triangle_index*3), getVertexNormal(triangle_index*3+1), getVertexNormal(triangle_index*3+2), hitinfo)) hit_found = true;
}
break;
case KRENGINE_MODEL_FORMAT_STRIP:
for(int triangle_index=0; triangle_index < vertex_count - 2; triangle_index++) {
if(rayCast(v0, v1, getVertexPosition(triangle_index), getVertexPosition(triangle_index+1), getVertexPosition(triangle_index+2), getVertexNormal(triangle_index), getVertexNormal(triangle_index+1), getVertexNormal(triangle_index+2), hitinfo)) hit_found = true;
if(rayCast(v0, dir, getVertexPosition(triangle_index), getVertexPosition(triangle_index+1), getVertexPosition(triangle_index+2), getVertexNormal(triangle_index), getVertexNormal(triangle_index+1), getVertexNormal(triangle_index+2), hitinfo)) hit_found = true;
}
break;
default:
@@ -848,7 +847,8 @@ bool KRModel::rayCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitin
bool KRModel::lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo) const
{
KRHitInfo new_hitinfo;
if(rayCast(v0, v1, new_hitinfo)) {
KRVector3 dir = KRVector3::Normalize(v1 - v0);
if(rayCast(v0, dir, new_hitinfo)) {
if((new_hitinfo.getPosition() - v0).sqrMagnitude() <= (v1 - v0).sqrMagnitude()) {
// The hit was between v1 and v2
hitinfo = new_hitinfo;

View File

@@ -186,12 +186,12 @@ public:
model_format_t getModelFormat() const;
bool lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo) const;
bool rayCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo) const;
bool rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitinfo) const;
static int GetLODCoverage(const std::string &name);
private:
bool rayCast(const KRVector3 &line_v0, const KRVector3 &line_v1, int tri_index0, int tri_index1, int tri_index2, KRHitInfo &hitinfo) const;
static bool rayCast(const KRVector3 &line_v0, const KRVector3 &line_v1, const KRVector3 &tri_v0, const KRVector3 &tri_v1, const KRVector3 &tri_v2, const KRVector3 &tri_n0, const KRVector3 &tri_n1, const KRVector3 &tri_n2, KRHitInfo &hitinfo);
bool rayCast(const KRVector3 &line_v0, const KRVector3 &dir, int tri_index0, int tri_index1, int tri_index2, KRHitInfo &hitinfo) const;
static bool rayCast(const KRVector3 &line_v0, const KRVector3 &dir, const KRVector3 &tri_v0, const KRVector3 &tri_v1, const KRVector3 &tri_v2, const KRVector3 &tri_n0, const KRVector3 &tri_n1, const KRVector3 &tri_n2, KRHitInfo &hitinfo);
int m_lodCoverage; // This LOD level is activated when the bounding box of the model will cover less than this percent of the screen (100 = highest detail model)
vector<KRMaterial *> m_materials;

View File

@@ -110,17 +110,17 @@ bool KROctree::lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hit
return hit_found;
}
bool KROctree::rayCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo)
bool KROctree::rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitinfo)
{
bool hit_found = false;
for(std::set<KRNode *>::iterator outer_nodes_itr=m_outerSceneNodes.begin(); outer_nodes_itr != m_outerSceneNodes.end(); outer_nodes_itr++) {
KRCollider *collider = dynamic_cast<KRCollider *>(*outer_nodes_itr);
if(collider) {
if(collider->rayCast(v0, v1, hitinfo)) hit_found = true;
if(collider->rayCast(v0, dir, hitinfo)) hit_found = true;
}
}
if(m_pRootNode) {
if(m_pRootNode->lineCast(v0, v1, hitinfo)) hit_found = true;
if(m_pRootNode->lineCast(v0, dir, hitinfo)) hit_found = true;
}
return hit_found;
}

View File

@@ -29,7 +29,7 @@ public:
std::set<KRNode *> &getOuterSceneNodes();
bool lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo);
bool rayCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo);
bool rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitinfo);
private:
KROctreeNode *m_pRootNode;

View File

@@ -205,24 +205,24 @@ bool KROctreeNode::lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo
return hit_found;
}
bool KROctreeNode::rayCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo)
bool KROctreeNode::rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitinfo)
{
bool hit_found = false;
if(hitinfo.didHit()) {
// Optimization: If we already have a hit, only search for hits that are closer
hit_found = lineCast(v0, hitinfo.getPosition(), hitinfo); // Note: This is purposefully lineCast as opposed to RayCast
} else {
if(getBounds().intersectsRay(v0, v1)) {
if(getBounds().intersectsRay(v0, dir)) {
for(std::set<KRNode *>::iterator nodes_itr=m_sceneNodes.begin(); nodes_itr != m_sceneNodes.end(); nodes_itr++) {
KRCollider *collider = dynamic_cast<KRCollider *>(*nodes_itr);
if(collider) {
if(collider->rayCast(v0, v1, hitinfo)) hit_found = true;
if(collider->rayCast(v0, dir, hitinfo)) hit_found = true;
}
}
for(int i=0; i<8; i++) {
if(m_children[i]) {
if(m_children[i]->rayCast(v0, v1, hitinfo)) {
if(m_children[i]->rayCast(v0, dir, hitinfo)) {
hit_found = true;
}
}

View File

@@ -48,7 +48,7 @@ public:
bool m_activeQuery;
bool lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo);
bool rayCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo);
bool rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitinfo);
private:
KRAABB m_bounds;

View File

@@ -447,7 +447,7 @@ bool KRScene::lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hiti
return m_nodeTree.lineCast(v0, v1, hitinfo);
}
bool KRScene::rayCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo)
bool KRScene::rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitinfo)
{
return m_nodeTree.rayCast(v0, v1, hitinfo);
return m_nodeTree.rayCast(v0, dir, hitinfo);
}

View File

@@ -61,7 +61,7 @@ public:
KRLight *getFirstLight();
bool lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo);
bool rayCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo);
bool rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitinfo);
#if TARGET_OS_IPHONE