/* * Collision.cpp * * Copyright (C) 2009 Matthias Schiffer * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with this program. If not, see . */ #include "Collision.h" #include #include namespace Zoom { vmml::vec3f Collision::projectToEdge(const vmml::vec3f& p, const vmml::vec3f& v1, const vmml::vec3f& v2) { vmml::vec3f pVec = p - v1; vmml::vec3f edge = v2 - v1; float lengthSq = edge.squared_length(); float edgeProj = vmml::dot(edge, pVec); if(edgeProj < 0) return v1; if(edgeProj > lengthSq) return v2; return v1 + (edgeProj/lengthSq)*edge; } vmml::vec3f Collision::projectToNearestEdge(const vmml::vec3f& p, const Triangle &t) { vmml::vec3f p1 = projectToEdge(p, t.getVertex(0), t.getVertex(1)); vmml::vec3f p2 = projectToEdge(p, t.getVertex(1), t.getVertex(2)); vmml::vec3f p3 = projectToEdge(p, t.getVertex(2), t.getVertex(0)); if(p.squared_distance(p1) < p.squared_distance(p2) && p.squared_distance(p1) < p.squared_distance(p3)) return p1; else if(p.squared_distance(p2) < p.squared_distance(p3)) return p2; else return p3; } bool Collision::test(const Triangle &t, const MathUtil::Ray &ray, float *distance) { vmml::vec3f edge1 = t.getVertex(1) - t.getVertex(0); vmml::vec3f edge2 = t.getVertex(2) - t.getVertex(0); vmml::vec3f pvec = ray.getDirection().cross(edge2); float det = vmml::dot(edge1, pvec); if(det < MathUtil::EPSILON) return false; vmml::vec3f tvec = ray.getVertex() - t.getVertex(0); float u = vmml::dot(pvec, tvec); if(u < 0 || u > det) return false; vmml::vec3f qvec = tvec.cross(edge1); float v = vmml::dot(ray.getDirection(), qvec); if(v < 0 || u+v > det) return false; if(distance) *distance = vmml::dot(edge2, qvec) / det; return true; } bool Collision::testEdge(const vmml::vec3f &v1, const vmml::vec3f &v2, const vmml::vec3f &m, float r, const vmml::vec3f &move, float *distance) { vmml::vec3f edge = v2 - v1; vmml::vec3f avec = vmml::dot(edge, move)*edge / edge.squared_length() - move; vmml::vec3f cvec = v1 + vmml::dot(edge, m-v1)*edge / edge.squared_length() - m; float a = avec.squared_length(); float b = vmml::dot(avec, cvec); float c = cvec.squared_length() - r*r; float rootSq = b*b - a*c; if(rootSq < 0) return false; float root = std::sqrt(rootSq); *distance = -(b + root)/a; float edgeFact = edge.dot(m + move*(*distance) - v1); if(edgeFact < 0 || edgeFact > edge.squared_length()) return false; if(*distance > -MathUtil::EPSILON && *distance < 1) return true; else return false; } bool Collision::test(const Triangle &t, const vmml::vec3f &m, float r, const vmml::vec3f &move, float *distance) { float d; if(test(t, MathUtil::Ray(m - r*t.computeNormal(), move), &d) && d > -MathUtil::EPSILON) { if(distance) *distance = d; if(d < 1) { return true; } else { return false; } } float minEdgeDistance = std::numeric_limits::infinity(); for(int i = 0; i < 3; ++i) { if(testEdge(t.getVertex(i), t.getVertex((i+1)%3), m, r, move, &d)) { if(d < minEdgeDistance) { minEdgeDistance = d; } } } if(distance) *distance = minEdgeDistance; return (minEdgeDistance < 1); } }