Fixed bugs in RayCast routines, which resulted in incorrect hit test results for scaled or rotated collision meshes.

Added implementation to stub function , KRAABB::intersectsRay, to enable performance boost for RayCast routines
This commit is contained in:
2013-05-01 15:07:27 -07:00
parent b7b9bedf14
commit 09f6c3362d
4 changed files with 90 additions and 9 deletions

View File

@@ -208,8 +208,75 @@ bool KRAABB::intersectsLine(const KRVector3 &v1, const KRVector3 &v2) const
bool KRAABB::intersectsRay(const KRVector3 &v1, const KRVector3 &dir) const bool KRAABB::intersectsRay(const KRVector3 &v1, const KRVector3 &dir) const
{ {
// FINDME, TODO - Need to implement this /*
return true; Fast Ray-Box Intersection
by Andrew Woo
from "Graphics Gems", Academic Press, 1990
*/
// FINDME, TODO - Perhaps there is a more efficient algorithm, as we don't actually need the exact coordinate of the intersection
enum {
RIGHT = 0,
LEFT = 1,
MIDDLE = 2
} quadrant[3];
bool inside = true;
KRVector3 maxT;
KRVector3 coord;
double candidatePlane[3];
// Find candidate planes; this loop can be avoided if rays cast all from the eye(assume perpsective view)
for (int i=0; i<3; i++)
if(v1.c[i] < min.c[i]) {
quadrant[i] = LEFT;
candidatePlane[i] = min.c[i];
inside = FALSE;
}else if (v1.c[i] > max.c[i]) {
quadrant[i] = RIGHT;
candidatePlane[i] = max.c[i];
inside = FALSE;
}else {
quadrant[i] = MIDDLE;
}
/* Ray v1 inside bounding box */
if(inside) {
coord = v1;
return true;
}
/* Calculate T distances to candidate planes */
for (int i = 0; i < 3; i++) {
if (quadrant[i] != MIDDLE && dir[i] !=0.) {
maxT.c[i] = (candidatePlane[i]-v1.c[i]) / dir[i];
} else {
maxT.c[i] = -1.0f;
}
}
/* Get largest of the maxT's for final choice of intersection */
int whichPlane = 0;
for (int i = 1; i < 3; i++) {
if (maxT.c[whichPlane] < maxT.c[i]) {
whichPlane = i;
}
}
/* Check final candidate actually inside box */
if (maxT.c[whichPlane] < 0.0f) return false;
for (int i = 0; i < 3; i++) {
if (whichPlane != i) {
coord[i] = v1.c[i] + maxT.c[whichPlane] *dir[i];
if (coord[i] < min.c[i] || coord[i] > max.c[i])
return false;
} else {
coord[i] = candidatePlane[i];
}
}
return true; /* ray hits box */
} }
void KRAABB::encapsulate(const KRAABB & b) void KRAABB::encapsulate(const KRAABB & b)

View File

@@ -99,7 +99,10 @@ bool KRCollider::lineCast(const KRVector3 &v0, const KRVector3 &v1, KRHitInfo &h
loadModel(); loadModel();
if(m_models.size()) { if(m_models.size()) {
if(getBounds().intersectsLine(v0, v1)) { if(getBounds().intersectsLine(v0, v1)) {
KRHitInfo hitinfo_model_space = KRHitInfo(KRMat4::Dot(getInverseModelMatrix(), hitinfo.getPosition()), KRMat4::DotNoTranslate(getInverseModelMatrix(), hitinfo.getNormal()), hitinfo.getNode()); 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 v0_model_space = KRMat4::Dot(getInverseModelMatrix(), v0);
KRVector3 v1_model_space = KRMat4::Dot(getInverseModelMatrix(), v1); KRVector3 v1_model_space = KRMat4::Dot(getInverseModelMatrix(), v1);
if(m_models[0]->lineCast(v0_model_space, v1_model_space, hitinfo_model_space)) { if(m_models[0]->lineCast(v0_model_space, v1_model_space, hitinfo_model_space)) {
@@ -118,10 +121,13 @@ bool KRCollider::rayCast(const KRVector3 &v0, const KRVector3 &dir, KRHitInfo &h
loadModel(); loadModel();
if(m_models.size()) { if(m_models.size()) {
if(getBounds().intersectsRay(v0, dir)) { if(getBounds().intersectsRay(v0, dir)) {
KRHitInfo hitinfo_model_space = KRHitInfo(KRMat4::Dot(getInverseModelMatrix(), hitinfo.getPosition()), KRVector3::Normalize(KRMat4::DotNoTranslate(getInverseModelMatrix(), hitinfo.getNormal())), hitinfo.getNode()); 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 v0_model_space = KRMat4::Dot(getInverseModelMatrix(), v0);
KRVector3 dir_model_space = KRMat4::DotNoTranslate(getInverseModelMatrix(), dir); KRVector3 dir_model_space = KRVector3::Normalize(KRMat4::DotNoTranslate(getInverseModelMatrix(), dir));
if(m_models[0]->rayCast(v0_model_space, dir, hitinfo_model_space)) { 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); hitinfo = KRHitInfo(KRMat4::Dot(getModelMatrix(), hitinfo_model_space.getPosition()), KRVector3::Normalize(KRMat4::DotNoTranslate(getModelMatrix(), hitinfo_model_space.getNormal())), this);
return true; return true;
} }

View File

@@ -41,6 +41,9 @@ KRHitInfo::KRHitInfo()
KRHitInfo::KRHitInfo(const KRVector3 &position, const KRVector3 &normal, KRNode *node) KRHitInfo::KRHitInfo(const KRVector3 &position, const KRVector3 &normal, KRNode *node)
{ {
m_position = position; m_position = position;
if(m_position == KRVector3::Zero()) {
fprintf(stderr, "Zero position hitinfo\n");
}
m_normal = normal; m_normal = normal;
m_node = node; m_node = node;
} }

View File

@@ -836,7 +836,11 @@ bool KRMesh::rayCast(const KRVector3 &line_v0, const KRVector3 &dir, const KRVec
float new_hit_distance_sqr = (hit_point - line_v0).sqrMagnitude(); float new_hit_distance_sqr = (hit_point - line_v0).sqrMagnitude();
float prev_hit_distance_sqr = (hitinfo.getPosition() - line_v0).sqrMagnitude(); float prev_hit_distance_sqr = (hitinfo.getPosition() - line_v0).sqrMagnitude();
if(new_hit_distance_sqr < prev_hit_distance_sqr) {
// ---===--- 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 // 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 // Interpolate between the three vertex normals, performing a 3-way lerp of tri_n0, tri_n1, and tri_n2
@@ -850,9 +854,10 @@ bool KRMesh::rayCast(const KRVector3 &line_v0, const KRVector3 &dir, const KRVec
KRVector3 normal = KRVector3::Normalize(tri_n0 * (1.0 - distance_v0) + tri_n1 * (1.0 - distance_v1) + tri_n2 * (1.0 - distance_v2)); 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); hitinfo = KRHitInfo(hit_point, normal);
return true;
} else {
return false; // Either no hit, or the hit was farther than an existing hit
} }
return true; // hit_point is in triangle
} }
/* /*