1 /*
2 Bullet Continuous Collision Detection and Physics Library
3 Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.org
4
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising from the use of this software.
7 Permission is granted to anyone to use this software for any purpose,
8 including commercial applications, and to alter it and redistribute it freely,
9 subject to the following restrictions:
10
11 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 3. This notice may not be removed or altered from any source distribution.
14 */
15
16 #include "btHeightfieldTerrainShape.h"
17
18 #include "LinearMath/btTransformUtil.h"
19
20
21
btHeightfieldTerrainShape(int heightStickWidth,int heightStickLength,const void * heightfieldData,btScalar heightScale,btScalar minHeight,btScalar maxHeight,int upAxis,PHY_ScalarType hdt,bool flipQuadEdges)22 btHeightfieldTerrainShape::btHeightfieldTerrainShape
23 (
24 int heightStickWidth, int heightStickLength, const void* heightfieldData,
25 btScalar heightScale, btScalar minHeight, btScalar maxHeight,int upAxis,
26 PHY_ScalarType hdt, bool flipQuadEdges
27 )
28 {
29 initialize(heightStickWidth, heightStickLength, heightfieldData,
30 heightScale, minHeight, maxHeight, upAxis, hdt,
31 flipQuadEdges);
32 }
33
34
35
btHeightfieldTerrainShape(int heightStickWidth,int heightStickLength,const void * heightfieldData,btScalar maxHeight,int upAxis,bool useFloatData,bool flipQuadEdges)36 btHeightfieldTerrainShape::btHeightfieldTerrainShape(int heightStickWidth, int heightStickLength,const void* heightfieldData,btScalar maxHeight,int upAxis,bool useFloatData,bool flipQuadEdges)
37 {
38 // legacy constructor: support only float or unsigned char,
39 // and min height is zero
40 PHY_ScalarType hdt = (useFloatData) ? PHY_FLOAT : PHY_UCHAR;
41 btScalar minHeight = 0.0f;
42
43 // previously, height = uchar * maxHeight / 65535.
44 // So to preserve legacy behavior, heightScale = maxHeight / 65535
45 btScalar heightScale = maxHeight / 65535;
46
47 initialize(heightStickWidth, heightStickLength, heightfieldData,
48 heightScale, minHeight, maxHeight, upAxis, hdt,
49 flipQuadEdges);
50 }
51
52
53
initialize(int heightStickWidth,int heightStickLength,const void * heightfieldData,btScalar heightScale,btScalar minHeight,btScalar maxHeight,int upAxis,PHY_ScalarType hdt,bool flipQuadEdges)54 void btHeightfieldTerrainShape::initialize
55 (
56 int heightStickWidth, int heightStickLength, const void* heightfieldData,
57 btScalar heightScale, btScalar minHeight, btScalar maxHeight, int upAxis,
58 PHY_ScalarType hdt, bool flipQuadEdges
59 )
60 {
61 // validation
62 btAssert(heightStickWidth > 1);// && "bad width");
63 btAssert(heightStickLength > 1);// && "bad length");
64 btAssert(heightfieldData);// && "null heightfield data");
65 // btAssert(heightScale) -- do we care? Trust caller here
66 btAssert(minHeight <= maxHeight);// && "bad min/max height");
67 btAssert(upAxis >= 0 && upAxis < 3);// && "bad upAxis--should be in range [0,2]");
68 btAssert(hdt != PHY_UCHAR || hdt != PHY_FLOAT || hdt != PHY_SHORT);// && "Bad height data type enum");
69
70 // initialize member variables
71 m_shapeType = TERRAIN_SHAPE_PROXYTYPE;
72 m_heightStickWidth = heightStickWidth;
73 m_heightStickLength = heightStickLength;
74 m_minHeight = minHeight;
75 m_maxHeight = maxHeight;
76 m_width = (btScalar) (heightStickWidth - 1);
77 m_length = (btScalar) (heightStickLength - 1);
78 m_heightScale = heightScale;
79 m_heightfieldDataUnknown = heightfieldData;
80 m_heightDataType = hdt;
81 m_flipQuadEdges = flipQuadEdges;
82 m_useDiamondSubdivision = false;
83 m_useZigzagSubdivision = false;
84 m_upAxis = upAxis;
85 m_localScaling.setValue(btScalar(1.), btScalar(1.), btScalar(1.));
86
87 // determine min/max axis-aligned bounding box (aabb) values
88 switch (m_upAxis)
89 {
90 case 0:
91 {
92 m_localAabbMin.setValue(m_minHeight, 0, 0);
93 m_localAabbMax.setValue(m_maxHeight, m_width, m_length);
94 break;
95 }
96 case 1:
97 {
98 m_localAabbMin.setValue(0, m_minHeight, 0);
99 m_localAabbMax.setValue(m_width, m_maxHeight, m_length);
100 break;
101 };
102 case 2:
103 {
104 m_localAabbMin.setValue(0, 0, m_minHeight);
105 m_localAabbMax.setValue(m_width, m_length, m_maxHeight);
106 break;
107 }
108 default:
109 {
110 //need to get valid m_upAxis
111 btAssert(0);// && "Bad m_upAxis");
112 }
113 }
114
115 // remember origin (defined as exact middle of aabb)
116 m_localOrigin = btScalar(0.5) * (m_localAabbMin + m_localAabbMax);
117 }
118
119
120
~btHeightfieldTerrainShape()121 btHeightfieldTerrainShape::~btHeightfieldTerrainShape()
122 {
123 }
124
125
126
getAabb(const btTransform & t,btVector3 & aabbMin,btVector3 & aabbMax) const127 void btHeightfieldTerrainShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const
128 {
129 btVector3 halfExtents = (m_localAabbMax-m_localAabbMin)* m_localScaling * btScalar(0.5);
130
131 btVector3 localOrigin(0, 0, 0);
132 localOrigin[m_upAxis] = (m_minHeight + m_maxHeight) * btScalar(0.5);
133 localOrigin *= m_localScaling;
134
135 btMatrix3x3 abs_b = t.getBasis().absolute();
136 btVector3 center = t.getOrigin();
137 btVector3 extent = halfExtents.dot3(abs_b[0], abs_b[1], abs_b[2]);
138 extent += btVector3(getMargin(),getMargin(),getMargin());
139
140 aabbMin = center - extent;
141 aabbMax = center + extent;
142 }
143
144
145 /// This returns the "raw" (user's initial) height, not the actual height.
146 /// The actual height needs to be adjusted to be relative to the center
147 /// of the heightfield's AABB.
148 btScalar
getRawHeightFieldValue(int x,int y) const149 btHeightfieldTerrainShape::getRawHeightFieldValue(int x,int y) const
150 {
151 btScalar val = 0.f;
152 switch (m_heightDataType)
153 {
154 case PHY_FLOAT:
155 {
156 val = m_heightfieldDataFloat[(y*m_heightStickWidth)+x];
157 break;
158 }
159
160 case PHY_UCHAR:
161 {
162 unsigned char heightFieldValue = m_heightfieldDataUnsignedChar[(y*m_heightStickWidth)+x];
163 val = heightFieldValue * m_heightScale;
164 break;
165 }
166
167 case PHY_SHORT:
168 {
169 short hfValue = m_heightfieldDataShort[(y * m_heightStickWidth) + x];
170 val = hfValue * m_heightScale;
171 break;
172 }
173
174 default:
175 {
176 btAssert(!"Bad m_heightDataType");
177 }
178 }
179
180 return val;
181 }
182
183
184
185
186 /// this returns the vertex in bullet-local coordinates
getVertex(int x,int y,btVector3 & vertex) const187 void btHeightfieldTerrainShape::getVertex(int x,int y,btVector3& vertex) const
188 {
189 btAssert(x>=0);
190 btAssert(y>=0);
191 btAssert(x<m_heightStickWidth);
192 btAssert(y<m_heightStickLength);
193
194 btScalar height = getRawHeightFieldValue(x,y);
195
196 switch (m_upAxis)
197 {
198 case 0:
199 {
200 vertex.setValue(
201 height - m_localOrigin.getX(),
202 (-m_width/btScalar(2.0)) + x,
203 (-m_length/btScalar(2.0) ) + y
204 );
205 break;
206 }
207 case 1:
208 {
209 vertex.setValue(
210 (-m_width/btScalar(2.0)) + x,
211 height - m_localOrigin.getY(),
212 (-m_length/btScalar(2.0)) + y
213 );
214 break;
215 };
216 case 2:
217 {
218 vertex.setValue(
219 (-m_width/btScalar(2.0)) + x,
220 (-m_length/btScalar(2.0)) + y,
221 height - m_localOrigin.getZ()
222 );
223 break;
224 }
225 default:
226 {
227 //need to get valid m_upAxis
228 btAssert(0);
229 }
230 }
231
232 vertex*=m_localScaling;
233 }
234
235
236
237 static inline int
getQuantized(btScalar x)238 getQuantized
239 (
240 btScalar x
241 )
242 {
243 if (x < 0.0) {
244 return (int) (x - 0.5);
245 }
246 return (int) (x + 0.5);
247 }
248
249
250
251 /// given input vector, return quantized version
252 /**
253 This routine is basically determining the gridpoint indices for a given
254 input vector, answering the question: "which gridpoint is closest to the
255 provided point?".
256
257 "with clamp" means that we restrict the point to be in the heightfield's
258 axis-aligned bounding box.
259 */
quantizeWithClamp(int * out,const btVector3 & point,int) const260 void btHeightfieldTerrainShape::quantizeWithClamp(int* out, const btVector3& point,int /*isMax*/) const
261 {
262 btVector3 clampedPoint(point);
263 clampedPoint.setMax(m_localAabbMin);
264 clampedPoint.setMin(m_localAabbMax);
265
266 out[0] = getQuantized(clampedPoint.getX());
267 out[1] = getQuantized(clampedPoint.getY());
268 out[2] = getQuantized(clampedPoint.getZ());
269
270 }
271
272
273
274 /// process all triangles within the provided axis-aligned bounding box
275 /**
276 basic algorithm:
277 - convert input aabb to local coordinates (scale down and shift for local origin)
278 - convert input aabb to a range of heightfield grid points (quantize)
279 - iterate over all triangles in that subset of the grid
280 */
processAllTriangles(btTriangleCallback * callback,const btVector3 & aabbMin,const btVector3 & aabbMax) const281 void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const
282 {
283 // scale down the input aabb's so they are in local (non-scaled) coordinates
284 btVector3 localAabbMin = aabbMin*btVector3(1.f/m_localScaling[0],1.f/m_localScaling[1],1.f/m_localScaling[2]);
285 btVector3 localAabbMax = aabbMax*btVector3(1.f/m_localScaling[0],1.f/m_localScaling[1],1.f/m_localScaling[2]);
286
287 // account for local origin
288 localAabbMin += m_localOrigin;
289 localAabbMax += m_localOrigin;
290
291 //quantize the aabbMin and aabbMax, and adjust the start/end ranges
292 int quantizedAabbMin[3];
293 int quantizedAabbMax[3];
294 quantizeWithClamp(quantizedAabbMin, localAabbMin,0);
295 quantizeWithClamp(quantizedAabbMax, localAabbMax,1);
296
297 // expand the min/max quantized values
298 // this is to catch the case where the input aabb falls between grid points!
299 for (int i = 0; i < 3; ++i) {
300 quantizedAabbMin[i]--;
301 quantizedAabbMax[i]++;
302 }
303
304 int startX=0;
305 int endX=m_heightStickWidth-1;
306 int startJ=0;
307 int endJ=m_heightStickLength-1;
308
309 switch (m_upAxis)
310 {
311 case 0:
312 {
313 if (quantizedAabbMin[1]>startX)
314 startX = quantizedAabbMin[1];
315 if (quantizedAabbMax[1]<endX)
316 endX = quantizedAabbMax[1];
317 if (quantizedAabbMin[2]>startJ)
318 startJ = quantizedAabbMin[2];
319 if (quantizedAabbMax[2]<endJ)
320 endJ = quantizedAabbMax[2];
321 break;
322 }
323 case 1:
324 {
325 if (quantizedAabbMin[0]>startX)
326 startX = quantizedAabbMin[0];
327 if (quantizedAabbMax[0]<endX)
328 endX = quantizedAabbMax[0];
329 if (quantizedAabbMin[2]>startJ)
330 startJ = quantizedAabbMin[2];
331 if (quantizedAabbMax[2]<endJ)
332 endJ = quantizedAabbMax[2];
333 break;
334 };
335 case 2:
336 {
337 if (quantizedAabbMin[0]>startX)
338 startX = quantizedAabbMin[0];
339 if (quantizedAabbMax[0]<endX)
340 endX = quantizedAabbMax[0];
341 if (quantizedAabbMin[1]>startJ)
342 startJ = quantizedAabbMin[1];
343 if (quantizedAabbMax[1]<endJ)
344 endJ = quantizedAabbMax[1];
345 break;
346 }
347 default:
348 {
349 //need to get valid m_upAxis
350 btAssert(0);
351 }
352 }
353
354
355
356
357 for(int j=startJ; j<endJ; j++)
358 {
359 for(int x=startX; x<endX; x++)
360 {
361 btVector3 vertices[3];
362 if (m_flipQuadEdges || (m_useDiamondSubdivision && !((j+x) & 1))|| (m_useZigzagSubdivision && !(j & 1)))
363 {
364 //first triangle
365 getVertex(x,j,vertices[0]);
366 getVertex(x, j + 1, vertices[1]);
367 getVertex(x + 1, j + 1, vertices[2]);
368 callback->processTriangle(vertices,x,j);
369 //second triangle
370 // getVertex(x,j,vertices[0]);//already got this vertex before, thanks to Danny Chapman
371 getVertex(x+1,j+1,vertices[1]);
372 getVertex(x + 1, j, vertices[2]);
373 callback->processTriangle(vertices, x, j);
374
375 } else
376 {
377 //first triangle
378 getVertex(x,j,vertices[0]);
379 getVertex(x,j+1,vertices[1]);
380 getVertex(x+1,j,vertices[2]);
381 callback->processTriangle(vertices,x,j);
382 //second triangle
383 getVertex(x+1,j,vertices[0]);
384 //getVertex(x,j+1,vertices[1]);
385 getVertex(x+1,j+1,vertices[2]);
386 callback->processTriangle(vertices,x,j);
387 }
388 }
389 }
390
391
392
393 }
394
calculateLocalInertia(btScalar,btVector3 & inertia) const395 void btHeightfieldTerrainShape::calculateLocalInertia(btScalar ,btVector3& inertia) const
396 {
397 //moving concave objects not supported
398
399 inertia.setValue(btScalar(0.),btScalar(0.),btScalar(0.));
400 }
401
setLocalScaling(const btVector3 & scaling)402 void btHeightfieldTerrainShape::setLocalScaling(const btVector3& scaling)
403 {
404 m_localScaling = scaling;
405 }
getLocalScaling() const406 const btVector3& btHeightfieldTerrainShape::getLocalScaling() const
407 {
408 return m_localScaling;
409 }
410