bool MeshIntersects(const Ray& ray, float3* pos, uint32_t posCount, uint32_t* indices, uint32_t indicesCount, bool backfaceCull, float* out_tmin, float3* out_pos, float3* out_nor, float3* nearestVertex) { if(posCount == 0) return false; uint32_t LastTri = indicesCount -3; bool hit = false; bool tri_hit = false; float dist = FLT_MAX; float hit_dist = FLT_MAX; float3 hit_pos; float3 hit_nor; Triangle tri; for( uint32_t i = 0; i <= LastTri; i+=3 ) { assert(indices[i] < posCount); assert(indices[i+1] < posCount); assert(indices[i+2] < posCount); tri.A = pos[ indices[i]]; tri.B = pos[ indices[i+1]]; tri.C = pos[ indices[i+2]]; tri_hit = IntersectionRayTriangle(ray, tri, backfaceCull, &hit_dist, &hit_pos, &hit_nor); if(tri_hit) { hit = true; if(hit_dist < dist) { dist = hit_dist; *out_tmin = hit_dist; *out_pos = hit_pos; *out_nor = hit_nor; float distA = lengthsquared(hit_pos - tri.A); float distB = lengthsquared(hit_pos - tri.B); float distC = lengthsquared(hit_pos - tri.C); if(distA <= distB && distA <= distC) *nearestVertex = tri.A; else if( distB < distC) *nearestVertex = tri.B; else *nearestVertex = tri.C; } } } return hit; }
// find close point on line l that is closest to point p. // see arg l and p. float3 ClosestPointOnLineToPoint(const LineSeg &l, const float3 &p) { float3 ab = l.B - l.A; float t = dot((p-l.A), ab) / lengthsquared(ab); t = clamp(t,0.0f,1.0f); return l.A + t * ab; }
Matrix Matrix::CreateBillboard(const float3& objectPos, const float3& camPos, const float3& camUp, const float3& camLook) { Matrix matrix; float3 look = camPos - objectPos; float len = lengthsquared(look); if (len < 0.0001f) { look = -camLook; } else { look = normalize(look); } float3 right = normalize( cross(camUp, look) ); float3 up = cross(look,right); matrix.M11 = right.x; matrix.M12 = right.y; matrix.M13 = right.z; matrix.M14 = 0.0f; matrix.M21 = up.x; matrix.M22 = up.y; matrix.M23 = up.z; matrix.M24 = 0.0f; matrix.M31 = look.x; matrix.M32 = look.y; matrix.M33 = look.z; matrix.M34 = 0.0f; matrix.M41 = objectPos.x; matrix.M42 = objectPos.y; matrix.M43 = objectPos.z; matrix.M44 = 1.0f; return matrix; }
//----------------------------------------------------------------------------- // Frustum Triangle intersection using separating axis test. // note: the frustum and the triangle must be in the same space. // optimization needed bool FrustumTriangleIntersect(const Frustum& fr, const Triangle& tri) { bool allInside = true; for(int i = 0; i < 6; ++i) { const Plane& plane = fr[i]; float d1 = plane.Eval(tri.A); float d2 = plane.Eval(tri.B); float d3 = plane.Eval(tri.C); // if all outside a single plane, then there is // no intersection. if( d1 < 0 && d2 < 0 && d3 < 0) return false; allInside = allInside && ( d1 > 0 && d2 > 0 && d3 > 0); } // the tri is completely inside the frustum. if( allInside ) return true; // separating axis test. // Triangle: 3 edges 1 face normal. // Frustum: 6 edges, 5 face normal. // for total of 24 separating axis. // note this test can be optimized. // tri edges float3 triEdges[3]; triEdges[0] = tri.B - tri.A; triEdges[1] = tri.C - tri.A; triEdges[2] = tri.C - tri.B; // frustum edges float3 FrEdges[6]; FrEdges[0] = fr.Corner(Frustum::NearTopLeft) - fr.Corner(Frustum::NearTopRight); FrEdges[1] = fr.Corner(Frustum::NearBottomRight) - fr.Corner(Frustum::NearTopRight); FrEdges[2] = (fr.Corner(Frustum::FarBottonLeft) - fr.Corner(Frustum::NearBottonLeft)); FrEdges[3] = (fr.Corner(Frustum::FarBottomRight) - fr.Corner(Frustum::NearBottomRight)); FrEdges[4] = (fr.Corner(Frustum::FarTopRight) - fr.Corner(Frustum::NearTopRight)); FrEdges[5] = (fr.Corner(Frustum::FarTopLeft) - fr.Corner(Frustum::NearTopLeft)); int k = 0; float3 Axis[24]; Axis[k++] = fr.TopPlane().normal; Axis[k++] = fr.BottomPlane().normal; Axis[k++] = fr.LeftPlane().normal; Axis[k++] = fr.RightPlane().normal; Axis[k++] = fr.NearPlane().normal; Axis[k++] = normalize(cross(triEdges[0], triEdges[1])); for(int te = 0; te < 3; te++) { for(int fe = 0; fe < 6; fe++) { float3 axis = cross( triEdges[te], FrEdges[fe]); Axis[k++] = normalize(axis); } } for(int n = 0; n < 24; n++) { float3 axis = Axis[n]; if( lengthsquared(axis) < Epsilon) continue; float trid1 = dot(tri.A,axis); float trid2 = dot(tri.B,axis); float trid3 = dot(tri.C,axis); float trMin = std::min(trid1,trid2); trMin = std::min(trMin, trid3); float trMax = std::max(trid1,trid2); trMax = std::max(trMax,trid3); float frMin = dot(fr.Corner(0),axis); float frMax = frMin; for(int c = 1; c < 8; c++) { float fdist = dot(fr.Corner(c), axis); frMin = std::min(frMin,fdist); frMax = std::max(frMax,fdist); } if( (trMax < frMin) || (frMax < trMin)) { return false; } } // must be intersecting. return true; }