diff --git a/KREngine/Kraken.xcodeproj/project.pbxproj b/KREngine/Kraken.xcodeproj/project.pbxproj index 63028e0..5708595 100644 --- a/KREngine/Kraken.xcodeproj/project.pbxproj +++ b/KREngine/Kraken.xcodeproj/project.pbxproj @@ -388,6 +388,10 @@ E4F027E11697BFFF00D4427D /* KRAudioBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F027DD1697BFFF00D4427D /* KRAudioBuffer.h */; settings = {ATTRIBUTES = (Public, ); }; }; E4F027F71698115600D4427D /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4F027F61698115600D4427D /* AudioToolbox.framework */; }; E4F027FA1698116000D4427D /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4F027F91698116000D4427D /* AudioToolbox.framework */; }; + E4F89BB418A6DB1200015637 /* KRTriangle3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4F89BB218A6DB1200015637 /* KRTriangle3.cpp */; }; + E4F89BB518A6DB1200015637 /* KRTriangle3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4F89BB218A6DB1200015637 /* KRTriangle3.cpp */; }; + E4F89BB618A6DB1200015637 /* KRTriangle3.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F89BB318A6DB1200015637 /* KRTriangle3.h */; }; + E4F89BB718A6DB1200015637 /* KRTriangle3.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F89BB318A6DB1200015637 /* KRTriangle3.h */; }; E4F975321536220900FD60B2 /* KRNode.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F975311536220900FD60B2 /* KRNode.h */; settings = {ATTRIBUTES = (); }; }; E4F975331536220900FD60B2 /* KRNode.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F975311536220900FD60B2 /* KRNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; E4F975361536221C00FD60B2 /* KRNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4F975351536221C00FD60B2 /* KRNode.cpp */; }; @@ -685,6 +689,8 @@ E4F027DD1697BFFF00D4427D /* KRAudioBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = KRAudioBuffer.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; E4F027F61698115600D4427D /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/AudioToolbox.framework; sourceTree = DEVELOPER_DIR; }; E4F027F91698116000D4427D /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; + E4F89BB218A6DB1200015637 /* KRTriangle3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KRTriangle3.cpp; sourceTree = ""; }; + E4F89BB318A6DB1200015637 /* KRTriangle3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KRTriangle3.h; sourceTree = ""; }; E4F975311536220900FD60B2 /* KRNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = KRNode.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; E4F975351536221C00FD60B2 /* KRNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = KRNode.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; /* End PBXFileReference section */ @@ -948,6 +954,8 @@ E4EC73C01720B1FF0065299F /* KRVector4.h */, E48CF940173453990005EBBB /* KRFloat.cpp */, E48CF941173453990005EBBB /* KRFloat.h */, + E4F89BB218A6DB1200015637 /* KRTriangle3.cpp */, + E4F89BB318A6DB1200015637 /* KRTriangle3.h */, ); name = Math; sourceTree = ""; @@ -1369,6 +1377,7 @@ E48B68171697794F00D99917 /* KRAudioSource.h in Headers */, E4F027C916979CCD00D4427D /* KRAudioManager.h in Headers */, E4F027E01697BFFF00D4427D /* KRAudioBuffer.h in Headers */, + E4F89BB618A6DB1200015637 /* KRTriangle3.h in Headers */, E4943233169E08D200BCB891 /* KRAmbientZone.h in Headers */, E499BF1B16AE747C007FCDBE /* KRVector2.h in Headers */, E499BF1E16AE751E007FCDBE /* KRSceneManager.h in Headers */, @@ -1447,6 +1456,7 @@ E4F027CA16979CCD00D4427D /* KRAudioManager.h in Headers */, E4F027D116979CE200D4427D /* KRAudioSample.h in Headers */, E4F027E11697BFFF00D4427D /* KRAudioBuffer.h in Headers */, + E4F89BB718A6DB1200015637 /* KRTriangle3.h in Headers */, E4943234169E08D200BCB891 /* KRAmbientZone.h in Headers */, E414F9AF1694DA37000B3D58 /* KRUnknown.h in Headers */, E45E03BD18790DD1006DA23F /* PVRTTexture.h in Headers */, @@ -1717,6 +1727,7 @@ E4924C2B15EE96AB00B965C6 /* KROctreeNode.cpp in Sources */, E404702018695DD200F01F42 /* KRTextureKTX.cpp in Sources */, E40BA45415EFF79500D7C3DD /* KRAABB.cpp in Sources */, + E4F89BB418A6DB1200015637 /* KRTriangle3.cpp in Sources */, E488399415F928CA00BD66D5 /* KRBundle.cpp in Sources */, E488399C15F92BE000BD66D5 /* KRBundleManager.cpp in Sources */, E4B175AC161F5A1000B8FB80 /* KRTexture.cpp in Sources */, @@ -1808,6 +1819,7 @@ E40F9833184A7BAC00CFA4D8 /* KRSprite.cpp in Sources */, E4CA10EA1637BD2B005D9400 /* KRTexturePVR.cpp in Sources */, E4CA10F01637BD58005D9400 /* KRTextureTGA.cpp in Sources */, + E4F89BB518A6DB1200015637 /* KRTriangle3.cpp in Sources */, E4CA11791639CC90005D9400 /* KRViewport.cpp in Sources */, E41843921678704000DBD6CF /* KRCollider.cpp in Sources */, E4324BAF16444E120043185B /* KRParticleSystemNewtonian.cpp in Sources */, diff --git a/KREngine/kraken/KRAABB.cpp b/KREngine/kraken/KRAABB.cpp index 2532d26..9998775 100644 --- a/KREngine/kraken/KRAABB.cpp +++ b/KREngine/kraken/KRAABB.cpp @@ -279,6 +279,42 @@ bool KRAABB::intersectsRay(const KRVector3 &v1, const KRVector3 &dir) const return true; /* ray hits box */ } +bool KRAABB::intersectsSphere(const KRVector3 ¢er, float radius) const +{ + // Arvo's Algorithm + + float squaredDistance = 0; + + // process X + if (center.x < min.x) { + float diff = center.x - min.x; + squaredDistance += diff * diff; + } else if (center.x > max.x) { + float diff = center.x - max.x; + squaredDistance += diff * diff; + } + + // process Y + if (center.y < min.y) { + float diff = center.y - min.y; + squaredDistance += diff * diff; + } else if (center.y > max.y) { + float diff = center.y - max.y; + squaredDistance += diff * diff; + } + + // process Z + if (center.z < min.z) { + float diff = center.z - min.z; + squaredDistance += diff * diff; + } else if (center.z > max.z) { + float diff = center.z - max.z; + squaredDistance += diff * diff; + } + + return squaredDistance <= radius; +} + void KRAABB::encapsulate(const KRAABB & b) { if(b.min.x < min.x) min.x = b.min.x; diff --git a/KREngine/kraken/KRAABB.h b/KREngine/kraken/KRAABB.h index a0fbbfd..6a03c07 100644 --- a/KREngine/kraken/KRAABB.h +++ b/KREngine/kraken/KRAABB.h @@ -35,6 +35,7 @@ public: bool intersectsLine(const KRVector3 &v1, const KRVector3 &v2) const; bool intersectsRay(const KRVector3 &v1, const KRVector3 &dir) const; + bool intersectsSphere(const KRVector3 ¢er, float radius) const; void encapsulate(const KRAABB & b); KRAABB& operator =(const KRAABB& b); diff --git a/KREngine/kraken/KRCollider.cpp b/KREngine/kraken/KRCollider.cpp index ad7eba5..e6ee63f 100644 --- a/KREngine/kraken/KRCollider.cpp +++ b/KREngine/kraken/KRCollider.cpp @@ -99,14 +99,17 @@ bool KRCollider::lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &h loadModel(); if(m_models.size()) { if(getBounds().intersectsLine(v0, v1)) { - KRHitInfo hitinfo_model_space; - if(hitinfo.didHit()) { - hitinfo_model_space = KRHitInfo(KRMat4::Dot(getInverseModelMatrix(), hitinfo.getPosition()), KRMat4::DotNoTranslate(getInverseModelMatrix(), hitinfo.getNormal()), hitinfo.getNode()); - } KRVector3 v0_model_space = KRMat4::Dot(getInverseModelMatrix(), v0); KRVector3 v1_model_space = KRMat4::Dot(getInverseModelMatrix(), v1); + KRHitInfo hitinfo_model_space; + if(hitinfo.didHit()) { + KRVector3 hit_position_model_space = KRMat4::Dot(getInverseModelMatrix(), hitinfo.getPosition()); + hitinfo_model_space = KRHitInfo(hit_position_model_space, KRMat4::DotNoTranslate(getInverseModelMatrix(), hitinfo.getNormal()), (hit_position_model_space - v0_model_space).magnitude(), hitinfo.getNode()); + } + if(m_models[0]->lineCast(v0_model_space, v1_model_space, hitinfo_model_space)) { - hitinfo = KRHitInfo(KRMat4::Dot(getModelMatrix(), hitinfo_model_space.getPosition()), KRVector3::Normalize(KRMat4::DotNoTranslate(getModelMatrix(), hitinfo_model_space.getNormal())), this); + KRVector3 hit_position_world_space = KRMat4::Dot(getModelMatrix(), hitinfo_model_space.getPosition()); + hitinfo = KRHitInfo(hit_position_world_space, KRVector3::Normalize(KRMat4::DotNoTranslate(getModelMatrix(), hitinfo_model_space.getNormal())), (hit_position_world_space - v0).magnitude(), this); return true; } } @@ -121,14 +124,38 @@ bool KRCollider::rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &h loadModel(); if(m_models.size()) { if(getBounds().intersectsRay(v0, dir)) { - KRHitInfo hitinfo_model_space; - if(hitinfo.didHit()) { - hitinfo_model_space = KRHitInfo(KRMat4::Dot(getInverseModelMatrix(), hitinfo.getPosition()), KRVector3::Normalize(KRMat4::DotNoTranslate(getInverseModelMatrix(), hitinfo.getNormal())), hitinfo.getNode()); - } KRVector3 v0_model_space = KRMat4::Dot(getInverseModelMatrix(), v0); KRVector3 dir_model_space = KRVector3::Normalize(KRMat4::DotNoTranslate(getInverseModelMatrix(), dir)); + KRHitInfo hitinfo_model_space; + if(hitinfo.didHit()) { + KRVector3 hit_position_model_space = KRMat4::Dot(getInverseModelMatrix(), hitinfo.getPosition()); + hitinfo_model_space = KRHitInfo(KRMat4::Dot(getInverseModelMatrix(), hitinfo.getPosition()), KRVector3::Normalize(KRMat4::DotNoTranslate(getInverseModelMatrix(), hitinfo.getNormal())), (hit_position_model_space - v0_model_space).magnitude(), hitinfo.getNode()); + } + if(m_models[0]->rayCast(v0_model_space, dir_model_space, hitinfo_model_space)) { - hitinfo = KRHitInfo(KRMat4::Dot(getModelMatrix(), hitinfo_model_space.getPosition()), KRVector3::Normalize(KRMat4::DotNoTranslate(getModelMatrix(), hitinfo_model_space.getNormal())), this); + KRVector3 hit_position_world_space = KRMat4::Dot(getModelMatrix(), hitinfo_model_space.getPosition()); + hitinfo = KRHitInfo(hit_position_world_space, KRVector3::Normalize(KRMat4::DotNoTranslate(getModelMatrix(), hitinfo_model_space.getNormal())), (hit_position_world_space - v0).magnitude(), this); + return true; + } + } + } + } + return false; +} + +bool KRCollider::sphereCast(const KRVector3 &v0, const KRVector3 &v1, float radius, KRHitInfo &hitinfo, unsigned int layer_mask) +{ + if(layer_mask & m_layer_mask) { // Only test if layer masks have a common bit set + loadModel(); + if(m_models.size()) { + KRAABB sphereCastBounds = KRAABB( + KRVector3(KRMIN(v0.x, v1.x) - radius, KRMIN(v0.y, v1.y) - radius, KRMIN(v0.z, v1.z) - radius), + KRVector3(KRMAX(v0.x, v1.x) + radius, KRMAX(v0.y, v1.y) + radius, KRMAX(v0.z, v1.z) + radius) + ); + + if(getBounds().intersects(sphereCastBounds)) { + if(m_models[0]->sphereCast(getModelMatrix(), v0, v1, radius, hitinfo)) { + hitinfo = KRHitInfo(hitinfo.getPosition(), hitinfo.getNormal(), hitinfo.getDistance(), this); return true; } } diff --git a/KREngine/kraken/KRCollider.h b/KREngine/kraken/KRCollider.h index cabf368..cbf106b 100644 --- a/KREngine/kraken/KRCollider.h +++ b/KREngine/kraken/KRCollider.h @@ -61,6 +61,7 @@ public: bool lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo, unsigned int layer_mask); bool rayCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo, unsigned int layer_mask); + bool sphereCast(const KRVector3 &v0, const KRVector3 &v1, float radius, KRHitInfo &hitinfo, unsigned int layer_mask); unsigned int getLayerMask(); void setLayerMask(unsigned int layer_mask); diff --git a/KREngine/kraken/KRHitInfo.cpp b/KREngine/kraken/KRHitInfo.cpp index 724dfb0..b0b376e 100644 --- a/KREngine/kraken/KRHitInfo.cpp +++ b/KREngine/kraken/KRHitInfo.cpp @@ -36,23 +36,23 @@ KRHitInfo::KRHitInfo() { m_position = KRVector3::Zero(); m_normal = KRVector3::Zero(); + m_distance = 0.0f; m_node = NULL; } -KRHitInfo::KRHitInfo(const KRVector3 &position, const KRVector3 &normal, KRNode *node) +KRHitInfo::KRHitInfo(const KRVector3 &position, const KRVector3 &normal, const float distance, KRNode *node) { m_position = position; - if(m_position == KRVector3::Zero()) { - KRContext::Log(KRContext::LOG_LEVEL_ERROR, "Zero position hitinfo"); - } m_normal = normal; + m_distance = distance; m_node = node; } -KRHitInfo::KRHitInfo(const KRVector3 &position, const KRVector3 &normal) +KRHitInfo::KRHitInfo(const KRVector3 &position, const KRVector3 &normal, const float distance) { m_position = position; m_normal = normal; + m_distance = distance; m_node = NULL; } @@ -76,6 +76,11 @@ KRVector3 KRHitInfo::getNormal() const return m_normal; } +float KRHitInfo::getDistance() const +{ + return m_distance; +} + KRNode *KRHitInfo::getNode() const { return m_node; @@ -85,6 +90,7 @@ KRHitInfo& KRHitInfo::operator =(const KRHitInfo& b) { m_position = b.m_position; m_normal = b.m_normal; + m_distance = b.m_distance; m_node = b.m_node; return *this; } diff --git a/KREngine/kraken/KRHitInfo.h b/KREngine/kraken/KRHitInfo.h index ecadc52..e02feed 100644 --- a/KREngine/kraken/KRHitInfo.h +++ b/KREngine/kraken/KRHitInfo.h @@ -39,12 +39,13 @@ class KRNode; class KRHitInfo { public: KRHitInfo(); - KRHitInfo(const KRVector3 &position, const KRVector3 &normal); - KRHitInfo(const KRVector3 &position, const KRVector3 &normal, KRNode *node); + KRHitInfo(const KRVector3 &position, const KRVector3 &normal, const float distance); + KRHitInfo(const KRVector3 &position, const KRVector3 &normal, const float distance, KRNode *node); ~KRHitInfo(); KRVector3 getPosition() const; KRVector3 getNormal() const; + float getDistance() const; KRNode *getNode() const; bool didHit() const; @@ -55,6 +56,7 @@ private: KRNode *m_node; KRVector3 m_position; KRVector3 m_normal; + float m_distance; }; #endif diff --git a/KREngine/kraken/KRMesh.cpp b/KREngine/kraken/KRMesh.cpp index f379f27..99e4bcd 100644 --- a/KREngine/kraken/KRMesh.cpp +++ b/KREngine/kraken/KRMesh.cpp @@ -35,6 +35,7 @@ #include "KRMesh.h" #include "KRVector3.h" +#include "KRTriangle3.h" #include "KRShader.h" #include "KRShaderManager.h" #include "KRContext.h" @@ -981,94 +982,41 @@ KRMesh::model_format_t KRMesh::getModelFormat() const return f; } -bool KRMesh::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) +bool KRMesh::rayCast(const KRVector3 &start, const KRVector3 &dir, const KRTriangle3 &tri, 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 w0, w; // ray vectors - float r, a, b; // params to calc ray-plane intersect - - // get triangle edge vectors and plane normal - u = tri_v1 - tri_v0; - v = tri_v2 - tri_v0; - n = KRVector3::Cross(u, v); // cross product - if (n == KRVector3::Zero()) // triangle is degenerate - return false; // do not deal with this case - - w0 = line_v0 - tri_v0; - a = -KRVector3::Dot(n, w0); - b = KRVector3::Dot(n,dir); - if (fabs(b) < SMALL_NUM) { // ray is parallel to triangle plane - if (a == 0) - return false; // ray lies in triangle plane - else { - return false; // ray disjoint from plane + KRVector3 hit_point; + if(tri.rayCast(start, dir, hit_point)) { + // ---===--- hit_point is in triangle ---===--- + + float new_hit_distance = (hit_point - start).magnitude(); + if(new_hit_distance < hitinfo.getDistance() || !hitinfo.didHit()) { + // Update the hitinfo object if this hit is closer than the prior hit + + // Interpolate between the three vertex normals, performing a 3-way lerp of tri_n0, tri_n1, and tri_n2 + float distance_v0 = (tri[0] - hit_point).magnitude(); + float distance_v1 = (tri[1] - hit_point).magnitude(); + float distance_v2 = (tri[2] - hit_point).magnitude(); + float distance_total = distance_v0 + distance_v1 + distance_v2; + distance_v0 /= distance_total; + distance_v1 /= distance_total; + distance_v2 /= distance_total; + KRVector3 normal = KRVector3::Normalize(tri_n0 * (1.0 - distance_v0) + tri_n1 * (1.0 - distance_v1) + tri_n2 * (1.0 - distance_v2)); + + hitinfo = KRHitInfo(hit_point, normal, new_hit_distance); + return true; + } else { + return false; // The hit was farther than an existing hit } - } - - // get intersect point of ray with triangle plane - r = a / b; - if (r < 0.0) // ray goes away from triangle - return false; // => no intersect - // for a segment, also test if (r > 1.0) => no intersect - - - KRVector3 hit_point = line_v0 + dir * r; // intersect point of ray and plane - - // is hit_point inside triangle? - float uu, uv, vv, wu, wv, D; - uu = KRVector3::Dot(u,u); - uv = KRVector3::Dot(u,v); - vv = KRVector3::Dot(v,v); - w = hit_point - tri_v0; - wu = KRVector3::Dot(w,u); - wv = KRVector3::Dot(w,v); - D = uv * uv - uu * vv; - - // get and test parametric coords - float s, t; - s = (uv * wv - vv * wu) / D; - if (s < 0.0 || s > 1.0) // hit_point is outside triangle - return false; - t = (uv * wu - uu * wv) / D; - if (t < 0.0 || (s + t) > 1.0) // hit_point is outside triangle - return false; - - float new_hit_distance_sqr = (hit_point - line_v0).sqrMagnitude(); - float prev_hit_distance_sqr = (hitinfo.getPosition() - line_v0).sqrMagnitude(); - // ---===--- hit_point is in triangle ---===--- - - - if(new_hit_distance_sqr < prev_hit_distance_sqr || !hitinfo.didHit()) { - // Update the hitinfo object if this hit is closer than the prior hit - - // Interpolate between the three vertex normals, performing a 3-way lerp of tri_n0, tri_n1, and tri_n2 - float distance_v0 = (tri_v0 - hit_point).magnitude(); - float distance_v1 = (tri_v1 - hit_point).magnitude(); - float distance_v2 = (tri_v2 - hit_point).magnitude(); - float distance_total = distance_v0 + distance_v1 + distance_v2; - distance_v0 /= distance_total; - distance_v1 /= distance_total; - distance_v2 /= distance_total; - KRVector3 normal = KRVector3::Normalize(tri_n0 * (1.0 - distance_v0) + tri_n1 * (1.0 - distance_v1) + tri_n2 * (1.0 - distance_v2)); - - hitinfo = KRHitInfo(hit_point, normal); - return true; } else { - return false; // Either no hit, or the hit was farther than an existing hit + // Dit not hit the triangle + return false; } + } -/* -bool KRMesh::rayCast(const KRVector3 &line_v0, const KRVector3 &dir, int tri_index0, int tri_index1, int tri_index2, KRHitInfo &hitinfo) const -{ - 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 KRMesh::rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitinfo) const +bool KRMesh::rayCast(const KRVector3 &start, const KRVector3 &dir, KRHitInfo &hitinfo) const { m_pData->lock(); bool hit_found = false; @@ -1084,7 +1032,9 @@ bool KRMesh::rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitin tri_vert_index[1] = getTriangleVertexIndex(submesh_index, triangle_index*3 + 1); tri_vert_index[2] = getTriangleVertexIndex(submesh_index, triangle_index*3 + 2); - if(rayCast(v0, dir, getVertexPosition(tri_vert_index[0]), getVertexPosition(tri_vert_index[1]), getVertexPosition(tri_vert_index[2]), getVertexNormal(tri_vert_index[0]), getVertexNormal(tri_vert_index[1]), getVertexNormal(tri_vert_index[2]), hitinfo)) hit_found = true; + KRTriangle3 tri = KRTriangle3(getVertexPosition(tri_vert_index[0]), getVertexPosition(tri_vert_index[1]), getVertexPosition(tri_vert_index[2])); + + if(rayCast(start, dir, tri, getVertexNormal(tri_vert_index[0]), getVertexNormal(tri_vert_index[1]), getVertexNormal(tri_vert_index[2]), hitinfo)) hit_found = true; } break; /* @@ -1111,6 +1061,94 @@ bool KRMesh::rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitin return hit_found; } + +bool KRMesh::sphereCast(const KRMat4 &model_to_world, const KRVector3 &v0, const KRVector3 &v1, float radius, KRHitInfo &hitinfo) const +{ + m_pData->lock(); + KRHitInfo new_hitinfo; + KRVector3 dir = KRVector3::Normalize(v1 - v0); + + bool hit_found = false; + for(int submesh_index=0; submesh_index < getSubmeshCount(); submesh_index++) { + int vertex_count = getVertexCount(submesh_index); + switch(getModelFormat()) { + case KRENGINE_MODEL_FORMAT_TRIANGLES: + case KRENGINE_MODEL_FORMAT_INDEXED_TRIANGLES: + for(int triangle_index=0; triangle_index < vertex_count / 3; triangle_index++) { + int tri_vert_index[3]; // FINDME, HACK! This is not very efficient for indexed collider meshes... + tri_vert_index[0] = getTriangleVertexIndex(submesh_index, triangle_index*3); + tri_vert_index[1] = getTriangleVertexIndex(submesh_index, triangle_index*3 + 1); + tri_vert_index[2] = getTriangleVertexIndex(submesh_index, triangle_index*3 + 2); + + KRTriangle3 tri = KRTriangle3(getVertexPosition(tri_vert_index[0]), getVertexPosition(tri_vert_index[1]), getVertexPosition(tri_vert_index[2])); + + if(sphereCast(model_to_world, v0, dir, radius, tri, getVertexNormal(tri_vert_index[0]), getVertexNormal(tri_vert_index[1]), getVertexNormal(tri_vert_index[2]), new_hitinfo)) hit_found = true; + } + break; + /* + + NOTE: Not yet supported: + + case KRENGINE_MODEL_FORMAT_STRIP: + case KRENGINE_MODEL_FORMAT_INDEXED_STRIP: + for(int triangle_index=0; triangle_index < vertex_count - 2; triangle_index++) { + int tri_vert_index[3]; + tri_vert_index[0] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3); + tri_vert_index[1] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3 + 1); + tri_vert_index[2] = getTriangleVertexIndex(submesh_index, vertex_start + triangle_index*3 + 2); + + if(sphereCast(model_to_world, v0, v1, getVertexPosition(vertex_start + triangle_index), getVertexPosition(vertex_start + triangle_index+1), getVertexPosition(vertex_start + triangle_index+2), getVertexNormal(vertex_start + triangle_index), getVertexNormal(vertex_start + triangle_index+1), getVertexNormal(vertex_start + triangle_index+2), new_hitinfo)) hit_found = true; + } + break; + */ + default: + break; + } + } + m_pData->unlock(); + + + if(hit_found) { + if(new_hitinfo.getDistance() <= (v1 - v0).magnitude()) { + // The hit was between v1 and v2 + hitinfo = new_hitinfo; + return true; + } + } + return false; // Either no hit, or the hit was beyond v1 +} + +bool KRMesh::sphereCast(const KRMat4 &model_to_world, const KRVector3 &start, const KRVector3 &dir, float radius, const KRTriangle3 &tri, const KRVector3 &tri_n0, const KRVector3 &tri_n1, const KRVector3 &tri_n2, KRHitInfo &hitinfo) +{ + + // bool sphereCast(const KRVector3 &start, const KRVector3 &dir, float radius, KRVector3 &hit_point, float &hit_distance) const; + + KRVector3 new_hit_point; + float new_hit_distance; + + KRTriangle3 world_tri = KRTriangle3(KRMat4::Dot(model_to_world, tri[0]), KRMat4::Dot(model_to_world, tri[1]), KRMat4::Dot(model_to_world, tri[2])); + + if(world_tri.sphereCast(start, dir, radius, new_hit_point, new_hit_distance)) { + if(!hitinfo.didHit() || hitinfo.getDistance() > new_hit_distance) { + + // Interpolate between the three vertex normals, performing a 3-way lerp of tri_n0, tri_n1, and tri_n2 + float distance_v0 = (tri[0] - new_hit_point).magnitude(); + float distance_v1 = (tri[1] - new_hit_point).magnitude(); + float distance_v2 = (tri[2] - new_hit_point).magnitude(); + float distance_total = distance_v0 + distance_v1 + distance_v2; + distance_v0 /= distance_total; + distance_v1 /= distance_total; + distance_v2 /= distance_total; + KRVector3 normal = KRVector3::Normalize(KRMat4::DotNoTranslate(model_to_world, (tri_n0 * (1.0 - distance_v0) + tri_n1 * (1.0 - distance_v1) + tri_n2 * (1.0 - distance_v2)))); + + hitinfo = KRHitInfo(new_hit_point, normal, new_hit_distance); + return true; + } + } + + return false; +} + bool KRMesh::lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo) const { m_pData->lock(); diff --git a/KREngine/kraken/KRMesh.h b/KREngine/kraken/KRMesh.h index 92e2162..745ed3b 100644 --- a/KREngine/kraken/KRMesh.h +++ b/KREngine/kraken/KRMesh.h @@ -54,6 +54,7 @@ class KRMaterial; class KRNode; +class KRTriangle3; class KRMesh : public KRResource { @@ -208,6 +209,7 @@ public: bool lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo) const; bool rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitinfo) const; + bool sphereCast(const KRMat4 &model_to_world, const KRVector3 &v0, const KRVector3 &v1, float radius, KRHitInfo &hitinfo) const; static int GetLODCoverage(const std::string &name); private: @@ -217,8 +219,8 @@ private: void getSubmeshes(); -// 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); + static bool rayCast(const KRVector3 &start, const KRVector3 &dir, const KRTriangle3 &tri, const KRVector3 &tri_n0, const KRVector3 &tri_n1, const KRVector3 &tri_n2, KRHitInfo &hitinfo); + static bool sphereCast(const KRMat4 &model_to_world, const KRVector3 &start, const KRVector3 &dir, float radius, const KRTriangle3 &tri, 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/kraken/KROctree.cpp b/KREngine/kraken/KROctree.cpp index df006e5..641407f 100644 --- a/KREngine/kraken/KROctree.cpp +++ b/KREngine/kraken/KROctree.cpp @@ -132,3 +132,25 @@ bool KROctree::rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hit } return hit_found; } + +bool KROctree::sphereCast(const KRVector3 &v0, const KRVector3 &v1, float radius, KRHitInfo &hitinfo, unsigned int layer_mask) +{ + bool hit_found = false; + std::vector outer_colliders; + + 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) { + outer_colliders.push_back(collider); + } + } + for(std::vector::iterator itr=outer_colliders.begin(); itr != outer_colliders.end(); itr++) { + if((*itr)->sphereCast(v0, v1, radius, hitinfo, layer_mask)) hit_found = true; + } + + if(m_pRootNode) { + if(m_pRootNode->sphereCast(v0, v1, radius, hitinfo, layer_mask)) hit_found = true; + } + return hit_found; +} + diff --git a/KREngine/kraken/KROctree.h b/KREngine/kraken/KROctree.h index 7f0cd12..7e7edff 100644 --- a/KREngine/kraken/KROctree.h +++ b/KREngine/kraken/KROctree.h @@ -30,6 +30,7 @@ public: bool lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo, unsigned int layer_mask); bool rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitinfo, unsigned int layer_mask); + bool sphereCast(const KRVector3 &v0, const KRVector3 &v1, float radius, KRHitInfo &hitinfo, unsigned int layer_mask); private: KROctreeNode *m_pRootNode; diff --git a/KREngine/kraken/KROctreeNode.cpp b/KREngine/kraken/KROctreeNode.cpp index 7332275..929289f 100644 --- a/KREngine/kraken/KROctreeNode.cpp +++ b/KREngine/kraken/KROctreeNode.cpp @@ -251,3 +251,39 @@ bool KROctreeNode::rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo return hit_found; } + +bool KROctreeNode::sphereCast(const KRVector3 &v0, const KRVector3 &v1, float radius, KRHitInfo &hitinfo, unsigned int layer_mask) +{ + bool hit_found = false; + /* + // FINDME, TODO - Adapt this optimization to work with sphereCasts + + if(hitinfo.didHit() && v1 != hitinfo.getPosition()) { + // Optimization: If we already have a hit, only search for hits that are closer + hit_found = sphereCast(v0, hitinfo.getPosition(), radius, hitinfo, layer_mask); + } else { + */ + KRAABB swept_bounds = KRAABB(KRVector3(KRMIN(v0.x, v1.x) - radius, KRMIN(v0.y, v1.y) - radius, KRMIN(v0.z, v1.z) - radius), KRVector3(KRMAX(v0.x, v1.x) + radius, KRMAX(v0.y, v1.y) + radius, KRMAX(v0.z, v1.z) + radius)); + // FINDME, TODO - Investigate AABB - swept sphere intersections or OBB - AABB intersections: "if(getBounds().intersectsSweptSphere(v0, v1, radius)) {" + if(getBounds().intersects(swept_bounds)) { + + 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->sphereCast(v0, v1, radius, hitinfo, layer_mask)) hit_found = true; + } + } + + for(int i=0; i<8; i++) { + if(m_children[i]) { + if(m_children[i]->sphereCast(v0, v1, radius, hitinfo, layer_mask)) { + hit_found = true; + } + } + } + } + // } + + return hit_found; +} + diff --git a/KREngine/kraken/KROctreeNode.h b/KREngine/kraken/KROctreeNode.h index c1bb7cf..84eb549 100644 --- a/KREngine/kraken/KROctreeNode.h +++ b/KREngine/kraken/KROctreeNode.h @@ -51,6 +51,8 @@ public: bool lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo, unsigned int layer_mask); bool rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitinfo, unsigned int layer_mask); + bool sphereCast(const KRVector3 &v0, const KRVector3 &v1, float radius, KRHitInfo &hitinfo, unsigned int layer_mask); + private: KRAABB m_bounds; diff --git a/KREngine/kraken/KRScene.cpp b/KREngine/kraken/KRScene.cpp index b103167..1f07bc7 100644 --- a/KREngine/kraken/KRScene.cpp +++ b/KREngine/kraken/KRScene.cpp @@ -552,3 +552,9 @@ bool KRScene::rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hiti return m_nodeTree.rayCast(v0, dir, hitinfo, layer_mask); } +bool KRScene::sphereCast(const KRVector3 &v0, const KRVector3 &v1, float radius, KRHitInfo &hitinfo, unsigned int layer_mask) +{ + return m_nodeTree.sphereCast(v0, v1, radius, hitinfo, layer_mask); +} + + diff --git a/KREngine/kraken/KRScene.h b/KREngine/kraken/KRScene.h index 9242a03..90ef75c 100644 --- a/KREngine/kraken/KRScene.h +++ b/KREngine/kraken/KRScene.h @@ -66,6 +66,7 @@ public: bool lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &hitinfo, unsigned int layer_mask); bool rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &hitinfo, unsigned int layer_mask); + bool sphereCast(const KRVector3 &v0, const KRVector3 &v1, float radius, KRHitInfo &hitinfo, unsigned int layer_mask); void renderFrame(float deltaTime, int width, int height); void render(KRCamera *pCamera, unordered_map &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass, bool new_frame); diff --git a/KREngine/kraken/KRTriangle3.cpp b/KREngine/kraken/KRTriangle3.cpp new file mode 100644 index 0000000..d4d4419 --- /dev/null +++ b/KREngine/kraken/KRTriangle3.cpp @@ -0,0 +1,278 @@ +// +// KRTriangle.cpp +// Kraken +// +// Created by Kearwood Gilbert on 2/8/2014. +// Copyright (c) 2014 Kearwood Software. All rights reserved. +// + +#include "KRTriangle3.h" +#include "KRVector3.h" + +KRTriangle3::KRTriangle3(const KRVector3 &v1, const KRVector3 &v2, const KRVector3 &v3) +{ + m_c[0] = v1; + m_c[1] = v2; + m_c[2] = v3; +} + +KRTriangle3::KRTriangle3(const KRTriangle3 &tri) +{ + m_c[0] = tri[0]; + m_c[1] = tri[1]; + m_c[3] = tri[3]; +} + + +KRTriangle3::~KRTriangle3() +{ + +} + +bool KRTriangle3::operator ==(const KRTriangle3& b) const +{ + return m_c[0] == b[0] && m_c[1] == b[1] && m_c[2] == b[2]; +} + +bool KRTriangle3::operator !=(const KRTriangle3& b) const +{ + return m_c[0] != b[0] || m_c[1] != b[1] || m_c[2] != b[2]; +} + +KRTriangle3& KRTriangle3::operator =(const KRTriangle3& b) +{ + + m_c[0] = b[0]; + m_c[1] = b[1]; + m_c[3] = b[3]; + return *this; +} + +KRVector3& KRTriangle3::operator[](unsigned i) +{ + return m_c[i]; +} + +KRVector3 KRTriangle3::operator[](unsigned i) const +{ + return m_c[i]; +} + + +bool KRTriangle3::rayCast(const KRVector3 &start, const KRVector3 &dir, KRVector3 &hit_point) const +{ + // 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 w0, w; // ray vectors + float r, a, b; // params to calc ray-plane intersect + + // get triangle edge vectors and plane normal + u = m_c[1] - m_c[0]; + v = m_c[2] - m_c[0]; + n = KRVector3::Cross(u, v); // cross product + if (n == KRVector3::Zero()) // triangle is degenerate + return false; // do not deal with this case + + w0 = start - m_c[0]; + a = -KRVector3::Dot(n, w0); + b = KRVector3::Dot(n,dir); + if (fabs(b) < SMALL_NUM) { // ray is parallel to triangle plane + if (a == 0) + return false; // ray lies in triangle plane + else { + return false; // ray disjoint from plane + } + } + + // get intersect point of ray with triangle plane + r = a / b; + if (r < 0.0) // ray goes away from triangle + return false; // => no intersect + // for a segment, also test if (r > 1.0) => no intersect + + + KRVector3 plane_hit_point = start + dir * r; // intersect point of ray and plane + + // is plane_hit_point inside triangle? + float uu, uv, vv, wu, wv, D; + uu = KRVector3::Dot(u,u); + uv = KRVector3::Dot(u,v); + vv = KRVector3::Dot(v,v); + w = plane_hit_point - m_c[0]; + wu = KRVector3::Dot(w,u); + wv = KRVector3::Dot(w,v); + D = uv * uv - uu * vv; + + // get and test parametric coords + float s, t; + s = (uv * wv - vv * wu) / D; + if (s < 0.0 || s > 1.0) // plane_hit_point is outside triangle + return false; + t = (uv * wu - uu * wv) / D; + if (t < 0.0 || (s + t) > 1.0) // plane_hit_point is outside triangle + return false; + + // plane_hit_point is inside the triangle + hit_point = plane_hit_point; + return true; +} + +KRVector3 KRTriangle3::calculateNormal() const +{ + KRVector3 v1 = m_c[1] - m_c[0]; + KRVector3 v2 = m_c[2] - m_c[0]; + + return KRVector3::Normalize(KRVector3::Cross(v1, v2)); +} + +bool _intersectSphere(const KRVector3 &rO, const KRVector3 &rV, const KRVector3 &sO, float sR, float &distance) +{ + // From: http://archive.gamedev.net/archive/reference/articles/article1026.html + + // TODO - Move to another class? + KRVector3 Q = sO - rO; + float c = Q.magnitude(); + float v = KRVector3::Dot(Q, rV); + float d = sR * sR - (c * c - v * v); + + // If there was no intersection, return -1 + + if(d < 0.0) return false; + + // Return the distance to the [first] intersecting point + + distance = v - sqrt(d); + return true; +} + +bool _sameSide(const KRVector3 &p1, const KRVector3 &p2, const KRVector3 &a, const KRVector3 &b) +{ + // TODO - Move to KRVector3 class? + // From: http://stackoverflow.com/questions/995445/determine-if-a-3d-point-is-within-a-triangle + + KRVector3 cp1 = KRVector3::Cross(b - a, p1 - a); + KRVector3 cp2 = KRVector3::Cross(b - a, p2 - a); + if(KRVector3::Dot(cp1, cp2) >= 0) return true; + return false; +} + +KRVector3 _closestPointOnLine(const KRVector3 &a, const KRVector3 &b, const KRVector3 &p) +{ + // From: http://stackoverflow.com/questions/995445/determine-if-a-3d-point-is-within-a-triangle + + // Determine t (the length of the vector from ‘a’ to ‘p’) + + KRVector3 c = p - a; + KRVector3 V = KRVector3::Normalize(b - a); + float d = (a - b).magnitude(); + float t = KRVector3::Dot(V, c); + + // Check to see if ‘t’ is beyond the extents of the line segment + + if (t < 0) return a; + if (t > d) return b; + + // Return the point between ‘a’ and ‘b’ + + return a + V * t; +} + +KRVector3 KRTriangle3::closestPointOnTriangle(const KRVector3 &p) const +{ + KRVector3 a = m_c[0]; + KRVector3 b = m_c[1]; + KRVector3 c = m_c[2]; + + KRVector3 Rab = _closestPointOnLine(a, b, p); + KRVector3 Rbc = _closestPointOnLine(b, c, p); + KRVector3 Rca = _closestPointOnLine(c, a, p); + + // return closest [Rab, Rbc, Rca] to p; + float sd_Rab = (p - Rab).sqrMagnitude(); + float sd_Rbc = (p - Rbc).sqrMagnitude(); + float sd_Rca = (p - Rca).sqrMagnitude(); + + if(sd_Rab < sd_Rbc && sd_Rab < sd_Rca) { + return Rab; + } else if(sd_Rbc < sd_Rab && sd_Rbc < sd_Rca) { + return sd_Rbc; + } else { + return sd_Rca; + } +} + +bool KRTriangle3::sphereCast(const KRVector3 &start, const KRVector3 &dir, float radius, KRVector3 &hit_point, float &hit_distance) const +{ + // Dir must be normalized + const float SMALL_NUM = 0.00000001f; // anything that avoids division overflow + + KRVector3 tri_normal = calculateNormal(); + + float d = KRVector3::Dot(tri_normal, m_c[0]); + float e = KRVector3::Dot(tri_normal, start) - radius; + float cotangent_distance = e - d; + + KRVector3 plane_intersect; + float plane_intersect_distance; + + // Detect an embedded plane, caused by a sphere that is already intersecting the plane. + if(cotangent_distance <= 0 && cotangent_distance >= -radius * 2.0f) { + // Embedded plane - Sphere is already intersecting the plane. + // Use the point closest to the origin of the sphere as the intersection + plane_intersect = start - tri_normal * (cotangent_distance + radius); + plane_intersect_distance = 0.0f; + } else { + // Sphere is not intersecting the plane + // Determine the first point hit by the swept sphere on the triangle's plane + + float denom = KRVector3::Dot(tri_normal, dir); + + if(fabs(denom) < SMALL_NUM) { + return false; // dir is co-planar with the triangle; no intersection + } + + plane_intersect_distance = -(cotangent_distance / denom); + plane_intersect = start + dir * plane_intersect_distance; + } + + if(containsPoint(plane_intersect)) { + // Triangle contains point + hit_point = plane_intersect; + hit_distance = plane_intersect_distance; + return true; + } else { + // Triangle does not contain point, cast ray back to sphere from closest point on triangle edge or vertice + KRVector3 closest_point = closestPointOnTriangle(hit_point); + float reverse_hit_distance; + if(_intersectSphere(closest_point, -dir, start, radius, reverse_hit_distance)) { + // Reverse cast hit sphere + hit_distance = reverse_hit_distance; + hit_point = closest_point - dir * reverse_hit_distance; + return true; + } else { + // Reverse cast did not hit sphere + return false; + } + } +} + + +bool KRTriangle3::containsPoint(const KRVector3 &p) const +{ + // From: http://stackoverflow.com/questions/995445/determine-if-a-3d-point-is-within-a-triangle + + const float SMALL_NUM = 0.00000001f; // anything that avoids division overflow + // KRVector3 A = m_c[0], B = m_c[1], C = m_c[2]; + if (_sameSide(p, m_c[0], m_c[1], m_c[2]) && _sameSide(p, m_c[1], m_c[0], m_c[2]) && _sameSide(p, m_c[2], m_c[0], m_c[1])) { + KRVector3 vc1 = KRVector3::Cross(m_c[0] - m_c[1], m_c[0] - m_c[2]); + if(fabs(KRVector3::Dot(m_c[0] - p, vc1)) <= SMALL_NUM) { + return true; + } + } + + return false; +} + + + diff --git a/KREngine/kraken/KRTriangle3.h b/KREngine/kraken/KRTriangle3.h new file mode 100644 index 0000000..25ade3e --- /dev/null +++ b/KREngine/kraken/KRTriangle3.h @@ -0,0 +1,63 @@ +// +// KRTriangle.h +// KREngine +// +// Copyright 2012 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#ifndef KRTRIANGLE3_H +#define KRTRIANGLE3_H + +#include "KRVector3.h" + +class KRTriangle3 +{ +public: + KRTriangle3(const KRTriangle3 &tri); + KRTriangle3(const KRVector3 &v1, const KRVector3 &v2, const KRVector3 &v3); + ~KRTriangle3(); + + KRVector3 calculateNormal() const; + + bool operator ==(const KRTriangle3& b) const; + bool operator !=(const KRTriangle3& b) const; + KRTriangle3& operator =(const KRTriangle3& b); + KRVector3& operator[](unsigned i); + KRVector3 operator[](unsigned i) const; + + + bool rayCast(const KRVector3 &start, const KRVector3 &dir, KRVector3 &hit_point) const; + bool sphereCast(const KRVector3 &start, const KRVector3 &dir, float radius, KRVector3 &hit_point, float &hit_distance) const; + + bool containsPoint(const KRVector3 &p) const; + KRVector3 closestPointOnTriangle(const KRVector3 &p) const; +private: + + KRVector3 m_c[3]; +}; + +#endif // KRTRIANGLE3_H