Implemented KRTriangle3 class.

Implemented swept sphere - mesh intersection testing / sphereCast functionality. - Not yet fully tested or optimized.

--HG--
branch : nfb
This commit is contained in:
2014-02-13 00:36:54 -08:00
parent d05b6c434c
commit 877b4b9bb7
17 changed files with 634 additions and 100 deletions

View File

@@ -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 = "<group>"; 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 = "<group>"; };
E4F89BB318A6DB1200015637 /* KRTriangle3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KRTriangle3.h; sourceTree = "<group>"; };
E4F975311536220900FD60B2 /* KRNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = KRNode.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
E4F975351536221C00FD60B2 /* KRNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = KRNode.cpp; sourceTree = "<group>"; 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 = "<group>";
@@ -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 */,

View File

@@ -279,6 +279,42 @@ bool KRAABB::intersectsRay(const KRVector3 &v1, const KRVector3 &dir) const
return true; /* ray hits box */
}
bool KRAABB::intersectsSphere(const KRVector3 &center, 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;

View File

@@ -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 &center, float radius) const;
void encapsulate(const KRAABB & b);
KRAABB& operator =(const KRAABB& b);

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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();

View File

@@ -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<KRMaterial *> m_materials;

View File

@@ -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<KRCollider *> outer_colliders;
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) {
outer_colliders.push_back(collider);
}
}
for(std::vector<KRCollider *>::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;
}

View File

@@ -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;

View File

@@ -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<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->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;
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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<KRAABB, int> &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass, bool new_frame);

View File

@@ -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;
}

View File

@@ -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