/* * 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" 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::test(const Triangle &t, const vmml::vec3f &m, float r, const vmml::vec3f &move) { float distance; if(test(t, MathUtil::Ray(m - r*t.computeNormal(), move), &distance) && distance > -MathUtil::EPSILON) { if(distance < 1) return true; else return false; } vmml::vec3f collisionPoint = projectToNearestEdge(m, t); vmml::vec3f movedPoint = projectToEdge(m, collisionPoint, collisionPoint - move); if(m.squared_distance(movedPoint) < r*r) return true; else return false; } }