diff --git a/KREngine/KREngine/Classes/KRAABB.cpp b/KREngine/KREngine/Classes/KRAABB.cpp index fdc39c6..319fe63 100644 --- a/KREngine/KREngine/Classes/KRAABB.cpp +++ b/KREngine/KREngine/Classes/KRAABB.cpp @@ -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; + // 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 ; - // 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) ); + // the algorithm says, find 3 t's, + KRVector3 t ; - // 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 - return true; + // 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]; + } + } - */ - return false; + 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 &v2) const +bool KRAABB::intersectsRay(const KRVector3 &v1, const KRVector3 &dir) const { - // TODO - Implementation needed return true; } diff --git a/KREngine/KREngine/Classes/KRAABB.h b/KREngine/KREngine/Classes/KRAABB.h index 11b740d..ca4e7b6 100644 --- a/KREngine/KREngine/Classes/KRAABB.h +++ b/KREngine/KREngine/Classes/KRAABB.h @@ -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; diff --git a/KREngine/KREngine/Classes/KRCollider.cpp b/KREngine/KREngine/Classes/KRCollider.cpp index 890a385..a9d245b 100644 --- a/KREngine/KREngine/Classes/KRCollider.cpp +++ b/KREngine/KREngine/Classes/KRCollider.cpp @@ -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; } diff --git a/KREngine/KREngine/Classes/KRModel.cpp b/KREngine/KREngine/Classes/KRModel.cpp index c21485a..fc90e91 100644 --- a/KREngine/KREngine/Classes/KRModel.cpp +++ b/KREngine/KREngine/Classes/KRModel.cpp @@ -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; diff --git a/KREngine/KREngine/Classes/KRModel.h b/KREngine/KREngine/Classes/KRModel.h index 077cde7..158fdb6 100644 --- a/KREngine/KREngine/Classes/KRModel.h +++ b/KREngine/KREngine/Classes/KRModel.h @@ -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 m_materials; diff --git a/KREngine/KREngine/Classes/KROctree.cpp b/KREngine/KREngine/Classes/KROctree.cpp index 13033c6..51ad29f 100644 --- a/KREngine/KREngine/Classes/KROctree.cpp +++ b/KREngine/KREngine/Classes/KROctree.cpp @@ -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::iterator outer_nodes_itr=m_outerSceneNodes.begin(); outer_nodes_itr != m_outerSceneNodes.end(); outer_nodes_itr++) { KRCollider *collider = dynamic_cast(*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; } diff --git a/KREngine/KREngine/Classes/KROctree.h b/KREngine/KREngine/Classes/KROctree.h index 7eeff49..edbb731 100644 --- a/KREngine/KREngine/Classes/KROctree.h +++ b/KREngine/KREngine/Classes/KROctree.h @@ -29,7 +29,7 @@ public: std::set &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; diff --git a/KREngine/KREngine/Classes/KROctreeNode.cpp b/KREngine/KREngine/Classes/KROctreeNode.cpp index 16e7930..856f330 100644 --- a/KREngine/KREngine/Classes/KROctreeNode.cpp +++ b/KREngine/KREngine/Classes/KROctreeNode.cpp @@ -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::iterator nodes_itr=m_sceneNodes.begin(); nodes_itr != m_sceneNodes.end(); nodes_itr++) { KRCollider *collider = dynamic_cast(*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; } } diff --git a/KREngine/KREngine/Classes/KROctreeNode.h b/KREngine/KREngine/Classes/KROctreeNode.h index a4b18d4..43dbb96 100644 --- a/KREngine/KREngine/Classes/KROctreeNode.h +++ b/KREngine/KREngine/Classes/KROctreeNode.h @@ -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; diff --git a/KREngine/KREngine/Classes/KRScene.cpp b/KREngine/KREngine/Classes/KRScene.cpp index af36774..84ece0c 100644 --- a/KREngine/KREngine/Classes/KRScene.cpp +++ b/KREngine/KREngine/Classes/KRScene.cpp @@ -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); } diff --git a/KREngine/KREngine/Classes/KRScene.h b/KREngine/KREngine/Classes/KRScene.h index bd4e6d2..c839296 100644 --- a/KREngine/KREngine/Classes/KRScene.h +++ b/KREngine/KREngine/Classes/KRScene.h @@ -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