1 /********************************************************************************
2 * ReactPhysics3D physics library, http://www.reactphysics3d.com                 *
3 * Copyright (c) 2010-2020 Daniel Chappuis                                       *
4 *********************************************************************************
5 *                                                                               *
6 * This software is provided 'as-is', without any express or implied warranty.   *
7 * In no event will the authors be held liable for any damages arising from the  *
8 * use of this software.                                                         *
9 *                                                                               *
10 * Permission is granted to anyone to use this software for any purpose,         *
11 * including commercial applications, and to alter it and redistribute it        *
12 * freely, subject to the following restrictions:                                *
13 *                                                                               *
14 * 1. The origin of this software must not be misrepresented; you must not claim *
15 *    that you wrote the original software. If you use this software in a        *
16 *    product, an acknowledgment in the product documentation would be           *
17 *    appreciated but is not required.                                           *
18 *                                                                               *
19 * 2. Altered source versions must be plainly marked as such, and must not be    *
20 *    misrepresented as being the original software.                             *
21 *                                                                               *
22 * 3. This notice may not be removed or altered from any source distribution.    *
23 *                                                                               *
24 ********************************************************************************/
25 
26 // Libraries
27 #include <reactphysics3d/collision/shapes/TriangleShape.h>
28 #include <reactphysics3d/collision/Collider.h>
29 #include <reactphysics3d/mathematics/mathematics_functions.h>
30 #include <reactphysics3d/collision/RaycastInfo.h>
31 #include <reactphysics3d/utils/Profiler.h>
32 #include <reactphysics3d/configuration.h>
33 #include <cassert>
34 
35 using namespace reactphysics3d;
36 
37 
38 // Constructor
39 /**
40  * Do not use this constructor. It is supposed to be used internally only.
41  * Use a ConcaveMeshShape instead.
42  * @param point1 First point of the triangle
43  * @param point2 Second point of the triangle
44  * @param point3 Third point of the triangle
45  * @param verticesNormals The three vertices normals for smooth mesh collision
46  * @param margin The collision margin (in meters) around the collision shape
47  */
TriangleShape(const Vector3 * vertices,const Vector3 * verticesNormals,uint shapeId,MemoryAllocator & allocator)48 TriangleShape::TriangleShape(const Vector3* vertices, const Vector3* verticesNormals, uint shapeId,
49                              MemoryAllocator& allocator)
50     : ConvexPolyhedronShape(CollisionShapeName::TRIANGLE, allocator), mFaces{HalfEdgeStructure::Face(allocator), HalfEdgeStructure::Face(allocator)} {
51 
52     mPoints[0] = vertices[0];
53     mPoints[1] = vertices[1];
54     mPoints[2] = vertices[2];
55 
56     // Compute the triangle normal
57     mNormal = (vertices[1] - vertices[0]).cross(vertices[2] - vertices[0]);
58     mNormal.normalize();
59 
60     mVerticesNormals[0] = verticesNormals[0];
61     mVerticesNormals[1] = verticesNormals[1];
62     mVerticesNormals[2] = verticesNormals[2];
63 
64     // Faces
65     mFaces[0].faceVertices.reserve(3);
66     mFaces[0].faceVertices.add(0);
67     mFaces[0].faceVertices.add(1);
68     mFaces[0].faceVertices.add(2);
69     mFaces[0].edgeIndex = 0;
70 
71     mFaces[1].faceVertices.reserve(3);
72     mFaces[1].faceVertices.add(0);
73     mFaces[1].faceVertices.add(2);
74     mFaces[1].faceVertices.add(1);
75     mFaces[1].edgeIndex = 1;
76 
77     // Edges
78     for (uint i=0; i<6; i++) {
79         switch(i) {
80             case 0:
81                 mEdges[0].vertexIndex = 0;
82                 mEdges[0].twinEdgeIndex = 1;
83                 mEdges[0].faceIndex = 0;
84                 mEdges[0].nextEdgeIndex = 2;
85                 break;
86             case 1:
87                 mEdges[1].vertexIndex = 1;
88                 mEdges[1].twinEdgeIndex = 0;
89                 mEdges[1].faceIndex = 1;
90                 mEdges[1].nextEdgeIndex = 5;
91                 break;
92             case 2:
93                 mEdges[2].vertexIndex = 1;
94                 mEdges[2].twinEdgeIndex = 3;
95                 mEdges[2].faceIndex = 0;
96                 mEdges[2].nextEdgeIndex = 4;
97                 break;
98             case 3:
99                 mEdges[3].vertexIndex = 2;
100                 mEdges[3].twinEdgeIndex = 2;
101                 mEdges[3].faceIndex = 1;
102                 mEdges[3].nextEdgeIndex = 1;
103                 break;
104             case 4:
105                 mEdges[4].vertexIndex = 2;
106                 mEdges[4].twinEdgeIndex = 5;
107                 mEdges[4].faceIndex = 0;
108                 mEdges[4].nextEdgeIndex = 0;
109                 break;
110             case 5:
111                 mEdges[5].vertexIndex = 0;
112                 mEdges[5].twinEdgeIndex = 4;
113                 mEdges[5].faceIndex = 1;
114                 mEdges[5].nextEdgeIndex = 3;
115                 break;
116         }
117     }
118 
119 
120     mRaycastTestType = TriangleRaycastSide::FRONT;
121 
122     mId = shapeId;
123 }
124 
125 // This method compute the smooth mesh contact with a triangle in case one of the two collision
126 // shapes is a triangle. The idea in this case is to use a smooth vertex normal of the triangle mesh
127 // at the contact point instead of the triangle normal to avoid the internal edge collision issue.
128 // This method will return the new smooth world contact
129 // normal of the triangle and the the local contact point on the other shape.
computeSmoothTriangleMeshContact(const CollisionShape * shape1,const CollisionShape * shape2,Vector3 & localContactPointShape1,Vector3 & localContactPointShape2,const Transform & shape1ToWorld,const Transform & shape2ToWorld,decimal penetrationDepth,Vector3 & outSmoothVertexNormal)130 void TriangleShape::computeSmoothTriangleMeshContact(const CollisionShape* shape1, const CollisionShape* shape2,
131                                                      Vector3& localContactPointShape1, Vector3& localContactPointShape2,
132                                                      const Transform& shape1ToWorld, const Transform& shape2ToWorld,
133                                                      decimal penetrationDepth, Vector3& outSmoothVertexNormal) {
134 
135     assert(shape1->getName() != CollisionShapeName::TRIANGLE || shape2->getName() != CollisionShapeName::TRIANGLE);
136 
137     // If one the shape is a triangle
138     bool isShape1Triangle = shape1->getName() == CollisionShapeName::TRIANGLE;
139     if (isShape1Triangle || shape2->getName() == CollisionShapeName::TRIANGLE) {
140 
141         const TriangleShape* triangleShape = isShape1Triangle ? static_cast<const TriangleShape*>(shape1):
142                                                                 static_cast<const TriangleShape*>(shape2);
143 
144         // Compute the smooth triangle mesh contact normal and recompute the local contact point on the other shape
145         triangleShape->computeSmoothMeshContact(isShape1Triangle ? localContactPointShape1 : localContactPointShape2,
146                                                 isShape1Triangle ? shape1ToWorld : shape2ToWorld,
147                                                 isShape1Triangle ? shape2ToWorld.getInverse() : shape1ToWorld.getInverse(),
148                                                 penetrationDepth, isShape1Triangle,
149                                                 isShape1Triangle ? localContactPointShape2 : localContactPointShape1,
150                                                 outSmoothVertexNormal);
151     }
152 }
153 
154 
155 // This method implements the technique described in Game Physics Pearl book
156 // by Gino van der Bergen and Dirk Gregorius to get smooth triangle mesh collision. The idea is
157 // to replace the contact normal of the triangle shape with the precomputed normal of the triangle
158 // mesh at this point. Then, we need to recompute the contact point on the other shape in order to
159 // stay aligned with the new contact normal. This method will return the new smooth world contact
160 // normal of the triangle and the the local contact point on the other shape.
computeSmoothMeshContact(Vector3 localContactPointTriangle,const Transform & triangleShapeToWorldTransform,const Transform & worldToOtherShapeTransform,decimal penetrationDepth,bool isTriangleShape1,Vector3 & outNewLocalContactPointOtherShape,Vector3 & outSmoothWorldContactTriangleNormal) const161 void TriangleShape::computeSmoothMeshContact(Vector3 localContactPointTriangle, const Transform& triangleShapeToWorldTransform,
162                                              const Transform& worldToOtherShapeTransform, decimal penetrationDepth, bool isTriangleShape1,
163                                              Vector3& outNewLocalContactPointOtherShape, Vector3& outSmoothWorldContactTriangleNormal) const {
164 
165     // Get the smooth contact normal of the mesh at the contact point on the triangle
166     Vector3 triangleLocalNormal = computeSmoothLocalContactNormalForTriangle(localContactPointTriangle);
167 
168     // Convert the local contact normal into world-space
169     Vector3 triangleWorldNormal = triangleShapeToWorldTransform.getOrientation() * triangleLocalNormal;
170 
171     // Penetration axis with direction from triangle to other shape
172     Vector3 triangleToOtherShapePenAxis = isTriangleShape1 ? outSmoothWorldContactTriangleNormal : -outSmoothWorldContactTriangleNormal;
173 
174     // The triangle normal should be the one in the direction out of the current colliding face of the triangle
175     if (triangleWorldNormal.dot(triangleToOtherShapePenAxis) < decimal(0.0)) {
176         triangleWorldNormal = -triangleWorldNormal;
177         triangleLocalNormal = -triangleLocalNormal;
178     }
179 
180     // Compute the final contact normal from shape 1 to shape 2
181     outSmoothWorldContactTriangleNormal = isTriangleShape1 ? triangleWorldNormal : -triangleWorldNormal;
182 
183     // Re-align the local contact point on the other shape such that it is aligned along the new contact normal
184     Vector3 otherShapePointTriangleSpace = localContactPointTriangle - triangleLocalNormal * penetrationDepth;
185     Vector3 otherShapePoint = worldToOtherShapeTransform * triangleShapeToWorldTransform * otherShapePointTriangleSpace;
186     outNewLocalContactPointOtherShape.setAllValues(otherShapePoint.x, otherShapePoint.y, otherShapePoint.z);
187 }
188 
189 // Get a smooth contact normal for collision for a triangle of the mesh
190 /// This is used to avoid the internal edges issue that occurs when a shape is colliding with
191 /// several triangles of a concave mesh. If the shape collide with an edge of the triangle for instance,
192 /// the computed contact normal from this triangle edge is not necessarily in the direction of the surface
193 /// normal of the mesh at this point. The idea to solve this problem is to use the real (smooth) surface
194 /// normal of the mesh at this point as the contact normal. This technique is described in the chapter 5
195 /// of the Game Physics Pearl book by Gino van der Bergen and Dirk Gregorius. The vertices normals of the
196 /// mesh are either provided by the user or precomputed if the user did not provide them. Note that we only
197 /// use the interpolated normal if the contact point is on an edge of the triangle. If the contact is in the
198 /// middle of the triangle, we return the true triangle normal.
computeSmoothLocalContactNormalForTriangle(const Vector3 & localContactPoint) const199 Vector3 TriangleShape::computeSmoothLocalContactNormalForTriangle(const Vector3& localContactPoint) const {
200 
201     // Compute the barycentric coordinates of the point in the triangle
202     decimal u, v, w;
203     computeBarycentricCoordinatesInTriangle(mPoints[0], mPoints[1], mPoints[2], localContactPoint, u, v, w);
204 
205     // If the contact is in the middle of the triangle face (not on the edges)
206     if (u > MACHINE_EPSILON && v > MACHINE_EPSILON && w > MACHINE_EPSILON) {
207 
208         // We return the true triangle face normal (not the interpolated one)
209         return mNormal;
210     }
211 
212     // We compute the contact normal as the barycentric interpolation of the three vertices normals
213     Vector3 interpolatedNormal = u * mVerticesNormals[0] + v * mVerticesNormals[1] + w * mVerticesNormals[2];
214 
215     // If the interpolated normal is degenerated
216     if (interpolatedNormal.lengthSquare() < MACHINE_EPSILON) {
217 
218         // Return the original normal
219         return mNormal;
220     }
221 
222     return interpolatedNormal.getUnit();
223 }
224 
225 // Update the AABB of a body using its collision shape
226 /**
227  * @param[out] aabb The axis-aligned bounding box (AABB) of the collision shape
228  *                  computed in world-space coordinates
229  * @param transform Transform used to compute the AABB of the collision shape
230  */
computeAABB(AABB & aabb,const Transform & transform) const231 void TriangleShape::computeAABB(AABB& aabb, const Transform& transform) const {
232 
233     RP3D_PROFILE("TriangleShape::computeAABB()", mProfiler);
234 
235     const Vector3 worldPoint1 = transform * mPoints[0];
236     const Vector3 worldPoint2 = transform * mPoints[1];
237     const Vector3 worldPoint3 = transform * mPoints[2];
238 
239     const Vector3 xAxis(worldPoint1.x, worldPoint2.x, worldPoint3.x);
240     const Vector3 yAxis(worldPoint1.y, worldPoint2.y, worldPoint3.y);
241     const Vector3 zAxis(worldPoint1.z, worldPoint2.z, worldPoint3.z);
242     aabb.setMin(Vector3(xAxis.getMinValue(), yAxis.getMinValue(), zAxis.getMinValue()));
243     aabb.setMax(Vector3(xAxis.getMaxValue(), yAxis.getMaxValue(), zAxis.getMaxValue()));
244 }
245 
246 // Raycast method with feedback information
247 /// This method use the line vs triangle raycasting technique described in
248 /// Real-time Collision Detection by Christer Ericson.
raycast(const Ray & ray,RaycastInfo & raycastInfo,Collider * collider,MemoryAllocator & allocator) const249 bool TriangleShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, Collider* collider, MemoryAllocator& allocator) const {
250 
251     RP3D_PROFILE("TriangleShape::raycast()", mProfiler);
252 
253     const Vector3 pq = ray.point2 - ray.point1;
254     const Vector3 pa = mPoints[0] - ray.point1;
255     const Vector3 pb = mPoints[1] - ray.point1;
256     const Vector3 pc = mPoints[2] - ray.point1;
257 
258     // Test if the line PQ is inside the eges BC, CA and AB. We use the triple
259     // product for this test.
260     const Vector3 m = pq.cross(pc);
261     decimal u = pb.dot(m);
262     if (mRaycastTestType == TriangleRaycastSide::FRONT) {
263         if (u < decimal(0.0)) return false;
264     }
265     else if (mRaycastTestType == TriangleRaycastSide::BACK) {
266         if (u > decimal(0.0)) return false;
267     }
268 
269     decimal v = -pa.dot(m);
270     if (mRaycastTestType == TriangleRaycastSide::FRONT) {
271         if (v < decimal(0.0)) return false;
272     }
273     else if (mRaycastTestType == TriangleRaycastSide::BACK) {
274         if (v > decimal(0.0)) return false;
275     }
276     else if (mRaycastTestType == TriangleRaycastSide::FRONT_AND_BACK) {
277         if (!sameSign(u, v)) return false;
278     }
279 
280     decimal w = pa.dot(pq.cross(pb));
281     if (mRaycastTestType == TriangleRaycastSide::FRONT) {
282         if (w < decimal(0.0)) return false;
283     }
284     else if (mRaycastTestType == TriangleRaycastSide::BACK) {
285         if (w > decimal(0.0)) return false;
286     }
287     else if (mRaycastTestType == TriangleRaycastSide::FRONT_AND_BACK) {
288         if (!sameSign(u, w)) return false;
289     }
290 
291     // If the line PQ is in the triangle plane (case where u=v=w=0)
292     if (approxEqual(u, 0) && approxEqual(v, 0) && approxEqual(w, 0)) return false;
293 
294     // Compute the barycentric coordinates (u, v, w) to determine the
295     // intersection point R, R = u * a + v * b + w * c
296     decimal denom = decimal(1.0) / (u + v + w);
297     u *= denom;
298     v *= denom;
299     w *= denom;
300 
301     // Compute the local hit point using the barycentric coordinates
302     const Vector3 localHitPoint = u * mPoints[0] + v * mPoints[1] + w * mPoints[2];
303     const decimal hitFraction = (localHitPoint - ray.point1).length() / pq.length();
304 
305     if (hitFraction < decimal(0.0) || hitFraction > ray.maxFraction) return false;
306 
307     Vector3 localHitNormal = (mPoints[1] - mPoints[0]).cross(mPoints[2] - mPoints[0]);
308     if (localHitNormal.dot(pq) > decimal(0.0)) localHitNormal = -localHitNormal;
309 
310     raycastInfo.body = collider->getBody();
311     raycastInfo.collider = collider;
312     raycastInfo.worldPoint = localHitPoint;
313     raycastInfo.hitFraction = hitFraction;
314     raycastInfo.worldNormal = localHitNormal;
315 
316     return true;
317 }
318 
319