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