#include "stdafx.h" #include "V8World/Poly.h" #include "V8World/Mesh.h" #include "Util/Math.h" #include "V8World/Tolerance.h" namespace RBX { bool Poly::hitTest(const Ray& rayInMe, Vector3& localHitPoint, Vector3& surfaceNormal) { return mesh->hitTest(rayInMe, localHitPoint, surfaceNormal); } bool Poly::collidesWithGroundPlane(const CoordinateFrame& c, float yHeight) const { for (size_t i = 0; i < mesh->numVertices(); ++i) { Vector3 worldLocation = c.pointToWorldSpace(mesh->getVertex(i)->getOffset()); if (worldLocation.y < yHeight) { return true; } } return false; } // See scanned calulations in V8 Technical Doc Matrix3 Poly::getMoment(float mass) const { Vector3 size = getSize(); float area = 2 * (size.x * size.y + size.y * size.z + size.z * size.x); Vector3 I; for (int i = 0; i < 3; i++) { int j = (i + 1) % 3; int k = (i + 2) % 3; float x = size[i]; // main axis; float y = size[j]; float z = size[k]; float Ix = (mass / (2.0f * area)) * ( (y*y*y*z/3.0f) + (y*z*z*z/3.0f) + (x*y*z*z) + (x*y*y*y/3.0f) + (x*y*y*z) + (x*z*z*z/3.0f) ); I[i] = Ix; } return Math::fromDiagonal(I); } Vector3 Poly::getCofmOffset( void ) const { return Vector3::zero(); } void Poly::setSize(const G3D::Vector3& _size) { Super::setSize(_size); RBXASSERT(_size == getSize()); centerToCornerDistance = 0.5f * _size.magnitude(); buildMesh(); } size_t Poly::closestSurfaceToPoint( const Vector3& pointInBody ) const { // Return the first face index where pointInBody is in and above the face. size_t faceId = (size_t)-1; float minFaceDist = 1e6f; for( size_t i = 0; i < mesh->numFaces(); i++ ) { bool inFace = mesh->getFace(i)->pointInExtrusion(pointInBody); if( inFace ) { float curFaceDist = fabs(mesh->getFace(i)->plane().distance(pointInBody)); if( curFaceDist < minFaceDist ) { minFaceDist = curFaceDist; faceId = i; } } } RBXASSERT( faceId != (size_t)-1 ); return faceId; } Plane Poly::getPlaneFromSurface( const size_t surfaceId ) const { RBXASSERT( surfaceId != (size_t)-1 ); return surfaceId != (size_t)-1 ? mesh->getFace(surfaceId)->plane() : Plane(); } Vector3 Poly::getSurfaceNormalInBody( const size_t surfaceId ) const { RBXASSERT( surfaceId != (size_t)-1 ); return surfaceId != (size_t)-1 ? mesh->getFace(surfaceId)->normal() : Vector3(0, 0, 0); } Vector3 Poly::getSurfaceVertInBody( const size_t surfaceId, const int vertId ) const { RBXASSERT( surfaceId != (size_t)-1 ); return surfaceId != (size_t)-1 ? mesh->getFace(surfaceId)->getVertex(vertId)->getOffset() : Vector3(0, 0, 0); } size_t Poly::getMostAlignedSurface( const Vector3& vecInWorld, const G3D::Matrix3& objectR ) const { size_t faceId = (size_t)-1; float maxDotProd = 0.0f; Vector3 vecInBody = objectR.transpose() * vecInWorld; for( size_t i = 0; i < mesh->numFaces(); i++ ) { float currentDotProd = mesh->getFace(i)->normal().dot(vecInBody); if( currentDotProd > maxDotProd ) { maxDotProd = currentDotProd; faceId = i; } } RBXASSERT( faceId != (size_t)-1 ); return faceId; } int Poly::getNumVertsInSurface( const size_t surfaceId ) const { RBXASSERT( surfaceId != (size_t)-1 ); return surfaceId != (size_t)-1 ? mesh->getFace(surfaceId)->numVertices() : 0; } bool Poly::vertOverlapsFace( const Vector3& pointInBody, const size_t surfaceId ) const { RBXASSERT( surfaceId != (size_t)-1 ); return surfaceId != (size_t)-1 ? mesh->getFace(surfaceId)->pointInExtrusion(pointInBody) : false; } CoordinateFrame Poly::getSurfaceCoordInBody( const size_t surfaceId ) const { RBXASSERT( surfaceId != (size_t)-1 ); CoordinateFrame aCS; if( surfaceId == (size_t)-1 ) return aCS; // the face reference coord origin is the midpoint b/t the 0 and 1 vertex aCS.translation = 0.5 * (mesh->getFace(surfaceId)->getVertex(0)->getOffset() + mesh->getFace(surfaceId)->getVertex(1)->getOffset()); aCS.rotation = Math::getWellFormedRotForZVector(mesh->getFace(surfaceId)->normal()); return aCS; } std::vector Poly::polygonIntersectionWithFace( const std::vector& otherPolygonInBody, const size_t surfaceId ) const { RBXASSERT( surfaceId != (size_t)-1 ); std::vector intersection3d; if( surfaceId == (size_t)-1 ) return intersection3d; std::vector myPolygon, otherPolygon; CoordinateFrame myC = getSurfaceCoordInBody(surfaceId); // represent the other polygon and this polyhrons face polygon in 2D coordinates. for( int i = 0; i < getNumVertsInSurface(surfaceId); i++ ) { Vector3 myVertInBody = getSurfaceVertInBody(surfaceId, i); Vector3 myVertInSurface = myC.rotation.transpose() * (myVertInBody - myC.translation); myPolygon.push_back(myVertInSurface.xy()); } for( unsigned int i = 0; i < otherPolygonInBody.size(); i++ ) { Vector3 otherVertInBody = otherPolygonInBody[i]; Vector3 otherVertInSurface = myC.rotation.transpose() * (otherVertInBody - myC.translation); otherPolygon.push_back(otherVertInSurface.xy()); } // find intersection std::vector intersection2d = Math::planarPolygonIntersection(myPolygon, otherPolygon); // convert result to 3d and transform from surface coord to body coord for( unsigned int i = 0; i < intersection2d.size(); i++ ) { Vector3 intersectionVertInSurface(intersection2d[i].x, intersection2d[i].y, 0.0f); Vector3 intersectionVertInBody = myC.translation + (myC.rotation * intersectionVertInSurface); intersection3d.push_back(intersectionVertInBody); } return intersection3d; } bool Poly::findTouchingSurfacesConvex( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId ) const { // find the touching surfaces // for each pair of surfaces, check passing of these required conditions // 1. parallel // 2. overlapping // 3. within distance tolerance for( myFaceId = 0; myFaceId < (size_t)getNumSurfaces(); myFaceId++ ) { for( otherFaceId = 0; otherFaceId < (size_t)otherGeom.getNumSurfaces(); otherFaceId++ ) { CoordinateFrame face0Coord = myCf * getSurfaceCoordInBody(myFaceId); CoordinateFrame face1Coord = otherCf * otherGeom.getSurfaceCoordInBody(otherFaceId); Vector3 p0ZInWorld = face0Coord.vectorToWorldSpace(Vector3::unitZ()); Vector3 p0ZInP1 = face1Coord.vectorToObjectSpace(p0ZInWorld); if( fabs(1.0f + p0ZInP1.dot(Vector3::unitZ())) > Tolerance::jointAngleMax() ) continue; if( !FacesOverlapped(myCf, myFaceId, otherGeom, otherCf, otherFaceId, 0.99f) ) continue; //if distance b/t faces is outside tolerance //Vector3 face0OriginWorld = face0Coord.translation; Vector3 face1OriginWorld = face1Coord.translation; Vector3 face1OriginInface0Coord = face0Coord.pointToObjectSpace(face1OriginWorld); // if the z distance (in face0 coord system) of the face1 origin is more than the distance tolerance, return false. // Scale tolerance up by factor of two for special shapes due to less precise face alignment. if( fabs(face1OriginInface0Coord.z) > 2.0 * Tolerance::jointPlanarMax() ) continue; // found a contacting pair return true; } } // Nothing found return false; } bool Poly::FacesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const { // The input "adjustPartTolerance" is used to more liberally or conservatively detect overlap. // It has the effect of slightly shrinking or expanding a part to more/less readily get overlap. // It defaults to 1.0. if( FaceVerticesOverlapped(myCf, myFaceId, otherGeom, otherCf, otherFaceId, tol) ) return true; if (FaceEdgesOverlapped(myCf, myFaceId, otherGeom, otherCf, otherFaceId, tol) ) return true; // no overlap found. return false; } bool Poly::FaceVerticesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const { // The input "adjustPartTolerance" is used to more liberally or conservatively detect overlap. // It has the effect of slightly shrinking or expanding a part to more/less readily get overlap. int numVertsInface0 = getNumVertsInSurface(myFaceId); for( int i = 0; i < numVertsInface0; ++i ) { Vector3 p0VertInp0 = getSurfaceVertInBody(myFaceId, i) * tol; Vector3 p0VertInWorld = myCf.pointToWorldSpace(p0VertInp0); Vector3 p0VertInp1 = otherCf.pointToObjectSpace(p0VertInWorld); if( otherGeom.vertOverlapsFace(p0VertInp1, otherFaceId) ) return true; } // Now test snap verts in drag face int numVertsInface1 = otherGeom.getNumVertsInSurface(otherFaceId); for( int i = 0; i < numVertsInface1; ++i ) { Vector3 p1VertInp1 = otherGeom.getSurfaceVertInBody(otherFaceId, i) * tol; Vector3 p1VertInWorld = otherCf.pointToWorldSpace(p1VertInp1); Vector3 p1VertInp0 = myCf.pointToObjectSpace(p1VertInWorld); if( vertOverlapsFace(p1VertInp0, myFaceId) ) return true; } return false; } bool Poly::FaceEdgesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const { // The input "adjustPartTolerance" is used to more liberally or conservatively detect overlap. // It has the effect of slightly shrinking or expanding a part to more/less readily get overlap. const float distanceTolerance = 1e-5; const float largeDistance = 1e6; int numVertsInface0 = getNumVertsInSurface(myFaceId); int numVertsInface1 = otherGeom.getNumVertsInSurface(otherFaceId); for( int i = 0; i < numVertsInface0-1; ++i ) { // line segment from A to B Vector3 face0VertInp0_A = getSurfaceVertInBody(myFaceId, i); Vector3 face0VertInWorld_A = myCf.pointToWorldSpace(face0VertInp0_A); Vector3 face0VertInp1_A = otherCf.pointToObjectSpace(face0VertInWorld_A); Vector3 face0VertInp0_B = getSurfaceVertInBody(myFaceId, i+1); Vector3 face0VertInWorld_B = myCf.pointToWorldSpace(face0VertInp0_B); Vector3 face0VertInp1_B = otherCf.pointToObjectSpace(face0VertInWorld_B); for( int j = 0; j < numVertsInface1-1; ++j ) { Vector3 face1VertInp1_A = otherGeom.getSurfaceVertInBody(otherFaceId, j); Vector3 face1VertInp1_B = otherGeom.getSurfaceVertInBody(otherFaceId, j+1); float distance = largeDistance; bool crossing = Math::lineSegmentDistanceIfCrossing(face0VertInp1_A, face0VertInp1_B, face1VertInp1_A, face1VertInp1_B, distance, tol); if( crossing && distance < distanceTolerance ) return true; } // Check last pair of p1-face1 verts Vector3 face1VertInp1_A = otherGeom.getSurfaceVertInBody(otherFaceId, numVertsInface1-1); Vector3 face1VertInp1_B = otherGeom.getSurfaceVertInBody(otherFaceId, 0); float distance = largeDistance; bool crossing = Math::lineSegmentDistanceIfCrossing(face0VertInp1_A, face0VertInp1_B, face1VertInp1_A, face1VertInp1_B, distance, tol); if( crossing && distance < distanceTolerance ) return true; } // Now check last pair of p0-face0 verts with all pairs of p1-face1 verts. Vector3 face0VertInp0_A = getSurfaceVertInBody(myFaceId, numVertsInface0-1); Vector3 face0VertInWorld_A = myCf.pointToWorldSpace(face0VertInp0_A); Vector3 face0VertInp1_A = otherCf.pointToObjectSpace(face0VertInWorld_A); Vector3 face0VertInp0_B = getSurfaceVertInBody(myFaceId, 0); Vector3 face0VertInWorld_B = myCf.pointToWorldSpace(face0VertInp0_B); Vector3 face0VertInp1_B = otherCf.pointToObjectSpace(face0VertInWorld_B); for( int j = 0; j < numVertsInface1-1; ++j ) { Vector3 face1VertInp1_A = otherGeom.getSurfaceVertInBody(otherFaceId, j); Vector3 face1VertInp1_B = otherGeom.getSurfaceVertInBody(otherFaceId, j+1); float distance = largeDistance; bool crossing = Math::lineSegmentDistanceIfCrossing(face0VertInp1_A, face0VertInp1_B, face1VertInp1_A, face1VertInp1_B, distance, tol); if( crossing && distance < distanceTolerance ) return true; } // Check last pair of p1-face1 verts Vector3 face1VertInp1_A = otherGeom.getSurfaceVertInBody(otherFaceId, numVertsInface1-1); Vector3 face1VertInp1_B = otherGeom.getSurfaceVertInBody(otherFaceId, 0); float distance = largeDistance; bool crossing = Math::lineSegmentDistanceIfCrossing(face0VertInp1_A, face0VertInp1_B, face1VertInp1_A, face1VertInp1_B, distance, tol); if( crossing && distance < distanceTolerance ) return true; // no overlap found. return false; } } // namespace RBX