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/HeightFieldShape.h>
28 #include <reactphysics3d/collision/RaycastInfo.h>
29 #include <reactphysics3d/utils/Profiler.h>
30 
31 using namespace reactphysics3d;
32 
33 // Constructor
34 /**
35  * @param nbGridColumns Number of columns in the grid of the height field
36  * @param nbGridRows Number of rows in the grid of the height field
37  * @param minHeight Minimum height value of the height field
38  * @param maxHeight Maximum height value of the height field
39  * @param heightFieldData Pointer to the first height value data (note that values are shared and not copied)
40  * @param dataType Data type for the height values (int, float, double)
41  * @param upAxis Integer representing the up axis direction (0 for x, 1 for y and 2 for z)
42  * @param integerHeightScale Scaling factor used to scale the height values (only when height values type is integer)
43  */
HeightFieldShape(int nbGridColumns,int nbGridRows,decimal minHeight,decimal maxHeight,const void * heightFieldData,HeightDataType dataType,MemoryAllocator & allocator,int upAxis,decimal integerHeightScale,const Vector3 & scaling)44 HeightFieldShape::HeightFieldShape(int nbGridColumns, int nbGridRows, decimal minHeight, decimal maxHeight,
45                                    const void* heightFieldData, HeightDataType dataType, MemoryAllocator& allocator, int upAxis,
46                                    decimal integerHeightScale, const Vector3& scaling)
47                  : ConcaveShape(CollisionShapeName::HEIGHTFIELD, allocator, scaling), mNbColumns(nbGridColumns), mNbRows(nbGridRows),
48                    mWidth(nbGridColumns - 1), mLength(nbGridRows - 1), mMinHeight(minHeight),
49                    mMaxHeight(maxHeight), mUpAxis(upAxis), mIntegerHeightScale(integerHeightScale),
50                    mHeightDataType(dataType) {
51 
52     assert(nbGridColumns >= 2);
53     assert(nbGridRows >= 2);
54     assert(mWidth >= 1);
55     assert(mLength >= 1);
56     assert(minHeight <= maxHeight);
57     assert(upAxis == 0 || upAxis == 1 || upAxis == 2);
58 
59     mHeightFieldData = heightFieldData;
60 
61     decimal halfHeight = (mMaxHeight - mMinHeight) * decimal(0.5);
62     assert(halfHeight >= 0);
63 
64     // Compute the local AABB of the height field
65     if (mUpAxis == 0) {
66         mAABB.setMin(Vector3(-halfHeight, -mWidth * decimal(0.5), -mLength * decimal(0.5)));
67         mAABB.setMax(Vector3(halfHeight, mWidth * decimal(0.5), mLength* decimal(0.5)));
68     }
69     else if (mUpAxis == 1) {
70         mAABB.setMin(Vector3(-mWidth * decimal(0.5), -halfHeight, -mLength * decimal(0.5)));
71         mAABB.setMax(Vector3(mWidth * decimal(0.5), halfHeight, mLength * decimal(0.5)));
72     }
73     else if (mUpAxis == 2) {
74         mAABB.setMin(Vector3(-mWidth * decimal(0.5), -mLength * decimal(0.5), -halfHeight));
75         mAABB.setMax(Vector3(mWidth * decimal(0.5), mLength * decimal(0.5), halfHeight));
76     }
77 }
78 
79 // Return the local bounds of the shape in x, y and z directions.
80 // This method is used to compute the AABB of the box
81 /**
82  * @param min The minimum bounds of the shape in local-space coordinates
83  * @param max The maximum bounds of the shape in local-space coordinates
84  */
getLocalBounds(Vector3 & min,Vector3 & max) const85 void HeightFieldShape::getLocalBounds(Vector3& min, Vector3& max) const {
86     min = mAABB.getMin() * mScale;
87     max = mAABB.getMax() * mScale;
88 }
89 
90 // Test collision with the triangles of the height field shape. The idea is to use the AABB
91 // of the body when need to test and see against which triangles of the height-field we need
92 // to test for collision. We compute the sub-grid points that are inside the other body's AABB
93 // and then for each rectangle in the sub-grid we generate two triangles that we use to test collision.
computeOverlappingTriangles(const AABB & localAABB,List<Vector3> & triangleVertices,List<Vector3> & triangleVerticesNormals,List<uint> & shapeIds,MemoryAllocator & allocator) const94 void HeightFieldShape::computeOverlappingTriangles(const AABB& localAABB, List<Vector3>& triangleVertices,
95                                                    List<Vector3>& triangleVerticesNormals, List<uint>& shapeIds,
96                                                    MemoryAllocator& allocator) const {
97 
98     RP3D_PROFILE("HeightFieldShape::computeOverlappingTriangles()", mProfiler);
99 
100    // Compute the non-scaled AABB
101    Vector3 inverseScale(decimal(1.0) / mScale.x, decimal(1.0) / mScale.y, decimal(1.0) / mScale.z);
102    AABB aabb(localAABB.getMin() * inverseScale, localAABB.getMax() * inverseScale);
103 
104    // Compute the integer grid coordinates inside the area we need to test for collision
105    int minGridCoords[3];
106    int maxGridCoords[3];
107    computeMinMaxGridCoordinates(minGridCoords, maxGridCoords, aabb);
108 
109    // Compute the starting and ending coords of the sub-grid according to the up axis
110    int iMin = 0;
111    int iMax = 0;
112    int jMin = 0;
113    int jMax = 0;
114    switch(mUpAxis) {
115         case 0 : iMin = clamp(minGridCoords[1], 0, mNbColumns - 1);
116                  iMax = clamp(maxGridCoords[1], 0, mNbColumns - 1);
117                  jMin = clamp(minGridCoords[2], 0, mNbRows - 1);
118                  jMax = clamp(maxGridCoords[2], 0, mNbRows - 1);
119                  break;
120         case 1 : iMin = clamp(minGridCoords[0], 0, mNbColumns - 1);
121                  iMax = clamp(maxGridCoords[0], 0, mNbColumns - 1);
122                  jMin = clamp(minGridCoords[2], 0, mNbRows - 1);
123                  jMax = clamp(maxGridCoords[2], 0, mNbRows - 1);
124                  break;
125         case 2 : iMin = clamp(minGridCoords[0], 0, mNbColumns - 1);
126                  iMax = clamp(maxGridCoords[0], 0, mNbColumns - 1);
127                  jMin = clamp(minGridCoords[1], 0, mNbRows - 1);
128                  jMax = clamp(maxGridCoords[1], 0, mNbRows - 1);
129                  break;
130    }
131 
132    assert(iMin >= 0 && iMin < mNbColumns);
133    assert(iMax >= 0 && iMax < mNbColumns);
134    assert(jMin >= 0 && jMin < mNbRows);
135    assert(jMax >= 0 && jMax < mNbRows);
136 
137    // For each sub-grid points (except the last ones one each dimension)
138    for (int i = iMin; i < iMax; i++) {
139        for (int j = jMin; j < jMax; j++) {
140 
141            // Compute the four point of the current quad
142            const Vector3 p1 = getVertexAt(i, j);
143            const Vector3 p2 = getVertexAt(i, j + 1);
144            const Vector3 p3 = getVertexAt(i + 1, j);
145            const Vector3 p4 = getVertexAt(i + 1, j + 1);
146 
147            // Generate the first triangle for the current grid rectangle
148            triangleVertices.add(p1);
149            triangleVertices.add(p2);
150            triangleVertices.add(p3);
151 
152            // Compute the triangle normal
153            Vector3 triangle1Normal = (p2 - p1).cross(p3 - p1).getUnit();
154 
155            // Use the triangle face normal as vertices normals (this is an aproximation. The correct
156            // solution would be to compute all the normals of the neighbor triangles and use their
157            // weighted average (with incident angle as weight) at the vertices. However, this solution
158            // seems too expensive (it requires to compute the normal of all neighbor triangles instead
159            // and compute the angle of incident edges with asin(). Maybe we could also precompute the
160            // vertices normal at the HeightFieldShape constructor but it will require extra memory to
161            // store them.
162            triangleVerticesNormals.add(triangle1Normal);
163            triangleVerticesNormals.add(triangle1Normal);
164            triangleVerticesNormals.add(triangle1Normal);
165 
166            // Compute the shape ID
167            shapeIds.add(computeTriangleShapeId(i, j, 0));
168 
169            // Generate the second triangle for the current grid rectangle
170            triangleVertices.add(p3);
171            triangleVertices.add(p2);
172            triangleVertices.add(p4);
173 
174            // Compute the triangle normal
175            Vector3 triangle2Normal = (p2 - p3).cross(p4 - p3).getUnit();
176 
177            // Use the triangle face normal as vertices normals (this is an aproximation. The correct
178            // solution would be to compute all the normals of the neighbor triangles and use their
179            // weighted average (with incident angle as weight) at the vertices. However, this solution
180            // seems too expensive (it requires to compute the normal of all neighbor triangles instead
181            // and compute the angle of incident edges with asin(). Maybe we could also precompute the
182            // vertices normal at the HeightFieldShape constructor but it will require extra memory to
183            // store them.
184            triangleVerticesNormals.add(triangle2Normal);
185            triangleVerticesNormals.add(triangle2Normal);
186            triangleVerticesNormals.add(triangle2Normal);
187 
188            // Compute the shape ID
189            shapeIds.add(computeTriangleShapeId(i, j, 1));
190        }
191    }
192 }
193 
194 // Compute the min/max grid coords corresponding to the intersection of the AABB of the height field and
195 // the AABB to collide
computeMinMaxGridCoordinates(int * minCoords,int * maxCoords,const AABB & aabbToCollide) const196 void HeightFieldShape::computeMinMaxGridCoordinates(int* minCoords, int* maxCoords, const AABB& aabbToCollide) const {
197 
198     // Clamp the min/max coords of the AABB to collide inside the height field AABB
199     Vector3 minPoint = Vector3::max(aabbToCollide.getMin(), mAABB.getMin());
200     minPoint = Vector3::min(minPoint, mAABB.getMax());
201 
202     Vector3 maxPoint = Vector3::min(aabbToCollide.getMax(), mAABB.getMax());
203     maxPoint = Vector3::max(maxPoint, mAABB.getMin());
204 
205     // Translate the min/max points such that the we compute grid points from [0 ... mNbWidthGridPoints]
206     // and from [0 ... mNbLengthGridPoints] because the AABB coordinates range are [-mWdith/2 ... mWidth/2]
207     // and [-mLength/2 ... mLength/2]
208     const Vector3 translateVec = mAABB.getExtent() * decimal(0.5);
209     minPoint += translateVec;
210     maxPoint += translateVec;
211 
212     // Convert the floating min/max coords of the AABB into closest integer
213     // grid values (note that we use the closest grid coordinate that is out
214     // of the AABB)
215     minCoords[0] = computeIntegerGridValue(minPoint.x) - 1;
216     minCoords[1] = computeIntegerGridValue(minPoint.y) - 1;
217     minCoords[2] = computeIntegerGridValue(minPoint.z) - 1;
218 
219     maxCoords[0] = computeIntegerGridValue(maxPoint.x) + 1;
220     maxCoords[1] = computeIntegerGridValue(maxPoint.y) + 1;
221     maxCoords[2] = computeIntegerGridValue(maxPoint.z) + 1;
222 }
223 
224 // Raycast method with feedback information
225 /// Note that only the first triangle hit by the ray in the mesh will be returned, even if
226 /// the ray hits many triangles.
raycast(const Ray & ray,RaycastInfo & raycastInfo,Collider * collider,MemoryAllocator & allocator) const227 bool HeightFieldShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, Collider* collider, MemoryAllocator& allocator) const {
228 
229     // TODO : Implement raycasting without using an AABB for the ray
230     //        but using a dynamic AABB tree or octree instead
231 
232     RP3D_PROFILE("HeightFieldShape::raycast()", mProfiler);
233 
234     // Compute the AABB for the ray
235     const Vector3 rayEnd = ray.point1 + ray.maxFraction * (ray.point2 - ray.point1);
236     const AABB rayAABB(Vector3::min(ray.point1, rayEnd), Vector3::max(ray.point1, rayEnd));
237 
238     // Compute the triangles overlapping with the ray AABB
239     List<Vector3> triangleVertices(allocator);
240     List<Vector3> triangleVerticesNormals(allocator);
241     List<uint> shapeIds(allocator);
242     computeOverlappingTriangles(rayAABB, triangleVertices, triangleVerticesNormals, shapeIds, allocator);
243 
244     assert(triangleVertices.size() == triangleVerticesNormals.size());
245     assert(shapeIds.size() == triangleVertices.size() / 3);
246     assert(triangleVertices.size() % 3 == 0);
247     assert(triangleVerticesNormals.size() % 3 == 0);
248 
249     bool isHit = false;
250     decimal smallestHitFraction = ray.maxFraction;
251 
252     // For each overlapping triangle
253     for (uint i=0; i < shapeIds.size(); i++)
254     {
255         // Create a triangle collision shape
256         TriangleShape triangleShape(&(triangleVertices[i * 3]), &(triangleVerticesNormals[i * 3]), shapeIds[i], allocator);
257         triangleShape.setRaycastTestType(getRaycastTestType());
258 
259     #ifdef IS_RP3D_PROFILING_ENABLED
260 
261 
262         // Set the profiler to the triangle shape
263         triangleShape.setProfiler(mProfiler);
264 
265     #endif
266 
267         // Ray casting test against the collision shape
268         RaycastInfo triangleRaycastInfo;
269         bool isTriangleHit = triangleShape.raycast(ray, triangleRaycastInfo, collider, allocator);
270 
271         // If the ray hit the collision shape
272         if (isTriangleHit && triangleRaycastInfo.hitFraction <= smallestHitFraction) {
273 
274             assert(triangleRaycastInfo.hitFraction >= decimal(0.0));
275 
276             raycastInfo.body = triangleRaycastInfo.body;
277             raycastInfo.collider = triangleRaycastInfo.collider;
278             raycastInfo.hitFraction = triangleRaycastInfo.hitFraction;
279             raycastInfo.worldPoint = triangleRaycastInfo.worldPoint;
280             raycastInfo.worldNormal = triangleRaycastInfo.worldNormal;
281             raycastInfo.meshSubpart = -1;
282             raycastInfo.triangleIndex = -1;
283 
284             smallestHitFraction = triangleRaycastInfo.hitFraction;
285             isHit = true;
286         }
287     }
288 
289     return isHit;
290 }
291 
292 // Return the vertex (local-coordinates) of the height field at a given (x,y) position
getVertexAt(int x,int y) const293 Vector3 HeightFieldShape::getVertexAt(int x, int y) const {
294 
295     // Get the height value
296     const decimal height = getHeightAt(x, y);
297 
298     // Height values origin
299     const decimal heightOrigin = -(mMaxHeight - mMinHeight) * decimal(0.5) - mMinHeight;
300 
301     Vector3 vertex;
302     switch (mUpAxis) {
303         case 0: vertex = Vector3(heightOrigin + height, -mWidth * decimal(0.5) + x, -mLength * decimal(0.5) + y);
304                 break;
305         case 1: vertex = Vector3(-mWidth * decimal(0.5) + x, heightOrigin + height, -mLength * decimal(0.5) + y);
306                 break;
307         case 2: vertex = Vector3(-mWidth * decimal(0.5) + x, -mLength * decimal(0.5) + y, heightOrigin + height);
308                 break;
309         default: assert(false);
310     }
311 
312     assert(mAABB.contains(vertex));
313 
314     return vertex * mScale;
315 }
316 
317 // Return the string representation of the shape
to_string() const318 std::string HeightFieldShape::to_string() const {
319 
320     std::stringstream ss;
321 
322     ss << "HeightFieldShape{" << std::endl;
323 
324     ss << "nbColumns=" << mNbColumns << std::endl;
325     ss << ", nbRows=" << mNbRows << std::endl;
326     ss << ", width=" << mWidth << std::endl;
327     ss << ", length=" << mLength << std::endl;
328     ss << ", minHeight=" << mMinHeight << std::endl;
329     ss << ", maxHeight=" << mMaxHeight << std::endl;
330     ss << ", upAxis=" << mUpAxis << std::endl;
331     ss << ", integerHeightScale=" << mIntegerHeightScale << std::endl;
332     ss << "}";
333 
334     return ss.str();
335 }
336