Bullet Collision Detection & Physics Library
btHeightfieldTerrainShape.cpp
Go to the documentation of this file.
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 
17 
19 
21  int heightStickWidth, int heightStickLength, const void* heightfieldData,
22  btScalar heightScale, btScalar minHeight, btScalar maxHeight, int upAxis,
23  PHY_ScalarType hdt, bool flipQuadEdges)
24  :m_userValue3(0),
25  m_triangleInfoMap(0)
26 {
27  initialize(heightStickWidth, heightStickLength, heightfieldData,
28  heightScale, minHeight, maxHeight, upAxis, hdt,
29  flipQuadEdges);
30 }
31 
32 btHeightfieldTerrainShape::btHeightfieldTerrainShape(int heightStickWidth, int heightStickLength, const void* heightfieldData, btScalar maxHeight, int upAxis, bool useFloatData, bool flipQuadEdges)
33  : m_userValue3(0),
34  m_triangleInfoMap(0)
35 {
36  // legacy constructor: support only float or unsigned char,
37  // and min height is zero
38  PHY_ScalarType hdt = (useFloatData) ? PHY_FLOAT : PHY_UCHAR;
39  btScalar minHeight = 0.0f;
40 
41  // previously, height = uchar * maxHeight / 65535.
42  // So to preserve legacy behavior, heightScale = maxHeight / 65535
43  btScalar heightScale = maxHeight / 65535;
44 
45  initialize(heightStickWidth, heightStickLength, heightfieldData,
46  heightScale, minHeight, maxHeight, upAxis, hdt,
47  flipQuadEdges);
48 }
49 
51  int heightStickWidth, int heightStickLength, const void* heightfieldData,
52  btScalar heightScale, btScalar minHeight, btScalar maxHeight, int upAxis,
53  PHY_ScalarType hdt, bool flipQuadEdges)
54 {
55  // validation
56  btAssert(heightStickWidth > 1); // && "bad width");
57  btAssert(heightStickLength > 1); // && "bad length");
58  btAssert(heightfieldData); // && "null heightfield data");
59  // btAssert(heightScale) -- do we care? Trust caller here
60  btAssert(minHeight <= maxHeight); // && "bad min/max height");
61  btAssert(upAxis >= 0 && upAxis < 3); // && "bad upAxis--should be in range [0,2]");
62  btAssert(hdt != PHY_UCHAR || hdt != PHY_FLOAT || hdt != PHY_SHORT); // && "Bad height data type enum");
63 
64  // initialize member variables
66  m_heightStickWidth = heightStickWidth;
67  m_heightStickLength = heightStickLength;
68  m_minHeight = minHeight;
69  m_maxHeight = maxHeight;
70  m_width = (btScalar)(heightStickWidth - 1);
71  m_length = (btScalar)(heightStickLength - 1);
72  m_heightScale = heightScale;
73  m_heightfieldDataUnknown = heightfieldData;
74  m_heightDataType = hdt;
75  m_flipQuadEdges = flipQuadEdges;
77  m_useZigzagSubdivision = false;
78  m_flipTriangleWinding = false;
79  m_upAxis = upAxis;
81 
85 
86  // determine min/max axis-aligned bounding box (aabb) values
87  switch (m_upAxis)
88  {
89  case 0:
90  {
93  break;
94  }
95  case 1:
96  {
99  break;
100  };
101  case 2:
102  {
105  break;
106  }
107  default:
108  {
109  //need to get valid m_upAxis
110  btAssert(0); // && "Bad m_upAxis");
111  }
112  }
113 
114  // remember origin (defined as exact middle of aabb)
116 }
117 
119 {
121 }
122 
123 void btHeightfieldTerrainShape::getAabb(const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const
124 {
126 
127  btVector3 localOrigin(0, 0, 0);
128  localOrigin[m_upAxis] = (m_minHeight + m_maxHeight) * btScalar(0.5);
129  localOrigin *= m_localScaling;
130 
131  btMatrix3x3 abs_b = t.getBasis().absolute();
132  btVector3 center = t.getOrigin();
133  btVector3 extent = halfExtents.dot3(abs_b[0], abs_b[1], abs_b[2]);
134  extent += btVector3(getMargin(), getMargin(), getMargin());
135 
136  aabbMin = center - extent;
137  aabbMax = center + extent;
138 }
139 
143 btScalar
145 {
146  btScalar val = 0.f;
147  switch (m_heightDataType)
148  {
149  case PHY_FLOAT:
150  {
152  break;
153  }
154 
155  case PHY_UCHAR:
156  {
157  unsigned char heightFieldValue = m_heightfieldDataUnsignedChar[(y * m_heightStickWidth) + x];
158  val = heightFieldValue * m_heightScale;
159  break;
160  }
161 
162  case PHY_SHORT:
163  {
164  short hfValue = m_heightfieldDataShort[(y * m_heightStickWidth) + x];
165  val = hfValue * m_heightScale;
166  break;
167  }
168 
169  default:
170  {
171  btAssert(!"Bad m_heightDataType");
172  }
173  }
174 
175  return val;
176 }
177 
179 void btHeightfieldTerrainShape::getVertex(int x, int y, btVector3& vertex) const
180 {
181  btAssert(x >= 0);
182  btAssert(y >= 0);
185 
186  btScalar height = getRawHeightFieldValue(x, y);
187 
188  switch (m_upAxis)
189  {
190  case 0:
191  {
192  vertex.setValue(
193  height - m_localOrigin.getX(),
194  (-m_width / btScalar(2.0)) + x,
195  (-m_length / btScalar(2.0)) + y);
196  break;
197  }
198  case 1:
199  {
200  vertex.setValue(
201  (-m_width / btScalar(2.0)) + x,
202  height - m_localOrigin.getY(),
203  (-m_length / btScalar(2.0)) + y);
204  break;
205  };
206  case 2:
207  {
208  vertex.setValue(
209  (-m_width / btScalar(2.0)) + x,
210  (-m_length / btScalar(2.0)) + y,
211  height - m_localOrigin.getZ());
212  break;
213  }
214  default:
215  {
216  //need to get valid m_upAxis
217  btAssert(0);
218  }
219  }
220 
221  vertex *= m_localScaling;
222 }
223 
224 static inline int
226  btScalar x)
227 {
228  if (x < 0.0)
229  {
230  return (int)(x - 0.5);
231  }
232  return (int)(x + 0.5);
233 }
234 
236 
244 void btHeightfieldTerrainShape::quantizeWithClamp(int* out, const btVector3& point, int /*isMax*/) const
245 {
246  btVector3 clampedPoint(point);
247  clampedPoint.setMax(m_localAabbMin);
248  clampedPoint.setMin(m_localAabbMax);
249 
250  out[0] = getQuantized(clampedPoint.getX());
251  out[1] = getQuantized(clampedPoint.getY());
252  out[2] = getQuantized(clampedPoint.getZ());
253 }
254 
256 
262 void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback, const btVector3& aabbMin, const btVector3& aabbMax) const
263 {
264  // scale down the input aabb's so they are in local (non-scaled) coordinates
265  btVector3 localAabbMin = aabbMin * btVector3(1.f / m_localScaling[0], 1.f / m_localScaling[1], 1.f / m_localScaling[2]);
266  btVector3 localAabbMax = aabbMax * btVector3(1.f / m_localScaling[0], 1.f / m_localScaling[1], 1.f / m_localScaling[2]);
267 
268  // account for local origin
269  localAabbMin += m_localOrigin;
270  localAabbMax += m_localOrigin;
271 
272  //quantize the aabbMin and aabbMax, and adjust the start/end ranges
273  int quantizedAabbMin[3];
274  int quantizedAabbMax[3];
275  quantizeWithClamp(quantizedAabbMin, localAabbMin, 0);
276  quantizeWithClamp(quantizedAabbMax, localAabbMax, 1);
277 
278  // expand the min/max quantized values
279  // this is to catch the case where the input aabb falls between grid points!
280  for (int i = 0; i < 3; ++i)
281  {
282  quantizedAabbMin[i]--;
283  quantizedAabbMax[i]++;
284  }
285 
286  int startX = 0;
287  int endX = m_heightStickWidth - 1;
288  int startJ = 0;
289  int endJ = m_heightStickLength - 1;
290 
291  switch (m_upAxis)
292  {
293  case 0:
294  {
295  if (quantizedAabbMin[1] > startX)
296  startX = quantizedAabbMin[1];
297  if (quantizedAabbMax[1] < endX)
298  endX = quantizedAabbMax[1];
299  if (quantizedAabbMin[2] > startJ)
300  startJ = quantizedAabbMin[2];
301  if (quantizedAabbMax[2] < endJ)
302  endJ = quantizedAabbMax[2];
303  break;
304  }
305  case 1:
306  {
307  if (quantizedAabbMin[0] > startX)
308  startX = quantizedAabbMin[0];
309  if (quantizedAabbMax[0] < endX)
310  endX = quantizedAabbMax[0];
311  if (quantizedAabbMin[2] > startJ)
312  startJ = quantizedAabbMin[2];
313  if (quantizedAabbMax[2] < endJ)
314  endJ = quantizedAabbMax[2];
315  break;
316  };
317  case 2:
318  {
319  if (quantizedAabbMin[0] > startX)
320  startX = quantizedAabbMin[0];
321  if (quantizedAabbMax[0] < endX)
322  endX = quantizedAabbMax[0];
323  if (quantizedAabbMin[1] > startJ)
324  startJ = quantizedAabbMin[1];
325  if (quantizedAabbMax[1] < endJ)
326  endJ = quantizedAabbMax[1];
327  break;
328  }
329  default:
330  {
331  //need to get valid m_upAxis
332  btAssert(0);
333  }
334  }
335 
336  // TODO If m_vboundsGrid is available, use it to determine if we really need to process this area
337 
338  for (int j = startJ; j < endJ; j++)
339  {
340  for (int x = startX; x < endX; x++)
341  {
342  btVector3 vertices[3];
343  int indices[3] = { 0, 1, 2 };
345  {
346  indices[0] = 2;
347  indices[2] = 0;
348  }
349 
350  if (m_flipQuadEdges || (m_useDiamondSubdivision && !((j + x) & 1)) || (m_useZigzagSubdivision && !(j & 1)))
351  {
352  //first triangle
353  getVertex(x, j, vertices[indices[0]]);
354  getVertex(x, j + 1, vertices[indices[1]]);
355  getVertex(x + 1, j + 1, vertices[indices[2]]);
356  callback->processTriangle(vertices, 2 * x, j);
357  //second triangle
358  // getVertex(x,j,vertices[0]);//already got this vertex before, thanks to Danny Chapman
359  getVertex(x + 1, j + 1, vertices[indices[1]]);
360  getVertex(x + 1, j, vertices[indices[2]]);
361  callback->processTriangle(vertices, 2 * x+1, j);
362  }
363  else
364  {
365  //first triangle
366  getVertex(x, j, vertices[indices[0]]);
367  getVertex(x, j + 1, vertices[indices[1]]);
368  getVertex(x + 1, j, vertices[indices[2]]);
369  callback->processTriangle(vertices, 2 * x, j);
370  //second triangle
371  getVertex(x + 1, j, vertices[indices[0]]);
372  //getVertex(x,j+1,vertices[1]);
373  getVertex(x + 1, j + 1, vertices[indices[2]]);
374  callback->processTriangle(vertices, 2 * x+1, j);
375  }
376  }
377  }
378 }
379 
381 {
382  //moving concave objects not supported
383 
384  inertia.setValue(btScalar(0.), btScalar(0.), btScalar(0.));
385 }
386 
388 {
389  m_localScaling = scaling;
390 }
392 {
393  return m_localScaling;
394 }
395 
396 namespace
397 {
398  struct GridRaycastState
399  {
400  int x; // Next quad coords
401  int z;
402  int prev_x; // Previous quad coords
403  int prev_z;
404  btScalar param; // Exit param for previous quad
405  btScalar prevParam; // Enter param for previous quad
406  btScalar maxDistanceFlat;
407  btScalar maxDistance3d;
408  };
409 }
410 
411 // TODO Does it really need to take 3D vectors?
415 template <typename Action_T>
416 void gridRaycast(Action_T& quadAction, const btVector3& beginPos, const btVector3& endPos, int indices[3])
417 {
418  GridRaycastState rs;
419  rs.maxDistance3d = beginPos.distance(endPos);
420  if (rs.maxDistance3d < 0.0001)
421  {
422  // Consider the ray is too small to hit anything
423  return;
424  }
425 
426 
427  btScalar rayDirectionFlatX = endPos[indices[0]] - beginPos[indices[0]];
428  btScalar rayDirectionFlatZ = endPos[indices[2]] - beginPos[indices[2]];
429  rs.maxDistanceFlat = btSqrt(rayDirectionFlatX * rayDirectionFlatX + rayDirectionFlatZ * rayDirectionFlatZ);
430 
431  if (rs.maxDistanceFlat < 0.0001)
432  {
433  // Consider the ray vertical
434  rayDirectionFlatX = 0;
435  rayDirectionFlatZ = 0;
436  }
437  else
438  {
439  rayDirectionFlatX /= rs.maxDistanceFlat;
440  rayDirectionFlatZ /= rs.maxDistanceFlat;
441  }
442 
443  const int xiStep = rayDirectionFlatX > 0 ? 1 : rayDirectionFlatX < 0 ? -1 : 0;
444  const int ziStep = rayDirectionFlatZ > 0 ? 1 : rayDirectionFlatZ < 0 ? -1 : 0;
445 
446  const float infinite = 9999999;
447  const btScalar paramDeltaX = xiStep != 0 ? 1.f / btFabs(rayDirectionFlatX) : infinite;
448  const btScalar paramDeltaZ = ziStep != 0 ? 1.f / btFabs(rayDirectionFlatZ) : infinite;
449 
450  // pos = param * dir
451  btScalar paramCrossX; // At which value of `param` we will cross a x-axis lane?
452  btScalar paramCrossZ; // At which value of `param` we will cross a z-axis lane?
453 
454  // paramCrossX and paramCrossZ are initialized as being the first cross
455  // X initialization
456  if (xiStep != 0)
457  {
458  if (xiStep == 1)
459  {
460  paramCrossX = (ceil(beginPos[indices[0]]) - beginPos[indices[0]]) * paramDeltaX;
461  }
462  else
463  {
464  paramCrossX = (beginPos[indices[0]] - floor(beginPos[indices[0]])) * paramDeltaX;
465  }
466  }
467  else
468  {
469  paramCrossX = infinite; // Will never cross on X
470  }
471 
472  // Z initialization
473  if (ziStep != 0)
474  {
475  if (ziStep == 1)
476  {
477  paramCrossZ = (ceil(beginPos[indices[2]]) - beginPos[indices[2]]) * paramDeltaZ;
478  }
479  else
480  {
481  paramCrossZ = (beginPos[indices[2]] - floor(beginPos[indices[2]])) * paramDeltaZ;
482  }
483  }
484  else
485  {
486  paramCrossZ = infinite; // Will never cross on Z
487  }
488 
489  rs.x = static_cast<int>(floor(beginPos[indices[0]]));
490  rs.z = static_cast<int>(floor(beginPos[indices[2]]));
491 
492  // Workaround cases where the ray starts at an integer position
493  if (paramCrossX == 0.0)
494  {
495  paramCrossX += paramDeltaX;
496  // If going backwards, we should ignore the position we would get by the above flooring,
497  // because the ray is not heading in that direction
498  if (xiStep == -1)
499  {
500  rs.x -= 1;
501  }
502  }
503 
504  if (paramCrossZ == 0.0)
505  {
506  paramCrossZ += paramDeltaZ;
507  if (ziStep == -1)
508  rs.z -= 1;
509  }
510 
511  rs.prev_x = rs.x;
512  rs.prev_z = rs.z;
513  rs.param = 0;
514 
515  while (true)
516  {
517  rs.prev_x = rs.x;
518  rs.prev_z = rs.z;
519  rs.prevParam = rs.param;
520 
521  if (paramCrossX < paramCrossZ)
522  {
523  // X lane
524  rs.x += xiStep;
525  // Assign before advancing the param,
526  // to be in sync with the initialization step
527  rs.param = paramCrossX;
528  paramCrossX += paramDeltaX;
529  }
530  else
531  {
532  // Z lane
533  rs.z += ziStep;
534  rs.param = paramCrossZ;
535  paramCrossZ += paramDeltaZ;
536  }
537 
538  if (rs.param > rs.maxDistanceFlat)
539  {
540  rs.param = rs.maxDistanceFlat;
541  quadAction(rs);
542  break;
543  }
544  else
545  {
546  quadAction(rs);
547  }
548  }
549 }
550 
552 {
556  int width;
557  int length;
559 
560  void exec(int x, int z) const
561  {
562  if (x < 0 || z < 0 || x >= width || z >= length)
563  {
564  return;
565  }
566 
567  btVector3 vertices[3];
568 
569  // TODO Since this is for raycasts, we could greatly benefit from an early exit on the first hit
570 
571  // Check quad
572  if (flipQuadEdges || (useDiamondSubdivision && (((z + x) & 1) > 0)))
573  {
574  // First triangle
575  shape->getVertex(x, z, vertices[0]);
576  shape->getVertex(x + 1, z, vertices[1]);
577  shape->getVertex(x + 1, z + 1, vertices[2]);
578  callback->processTriangle(vertices, x, z);
579 
580  // Second triangle
581  shape->getVertex(x, z, vertices[0]);
582  shape->getVertex(x + 1, z + 1, vertices[1]);
583  shape->getVertex(x, z + 1, vertices[2]);
584  callback->processTriangle(vertices, x, z);
585  }
586  else
587  {
588  // First triangle
589  shape->getVertex(x, z, vertices[0]);
590  shape->getVertex(x, z + 1, vertices[1]);
591  shape->getVertex(x + 1, z, vertices[2]);
592  callback->processTriangle(vertices, x, z);
593 
594  // Second triangle
595  shape->getVertex(x + 1, z, vertices[0]);
596  shape->getVertex(x, z + 1, vertices[1]);
597  shape->getVertex(x + 1, z + 1, vertices[2]);
598  callback->processTriangle(vertices, x, z);
599  }
600  }
601 
602  void operator()(const GridRaycastState& bs) const
603  {
604  exec(bs.prev_x, bs.prev_z);
605  }
606 };
607 
609 {
611  int width;
612  int length;
614 
618 
619  int* m_indices;
621 
623  : vbounds(bnd),
624  m_indices(indices)
625  {
626  }
627  void operator()(const GridRaycastState& rs) const
628  {
629  int x = rs.prev_x;
630  int z = rs.prev_z;
631 
632  if (x < 0 || z < 0 || x >= width || z >= length)
633  {
634  return;
635  }
636 
637  const btHeightfieldTerrainShape::Range chunk = vbounds[x + z * width];
638 
639  btVector3 enterPos;
640  btVector3 exitPos;
641 
642  if (rs.maxDistanceFlat > 0.0001)
643  {
644  btScalar flatTo3d = chunkSize * rs.maxDistance3d / rs.maxDistanceFlat;
645  btScalar enterParam3d = rs.prevParam * flatTo3d;
646  btScalar exitParam3d = rs.param * flatTo3d;
647  enterPos = rayBegin + rayDir * enterParam3d;
648  exitPos = rayBegin + rayDir * exitParam3d;
649 
650  // We did enter the flat projection of the AABB,
651  // but we have to check if we intersect it on the vertical axis
652  if (enterPos[1] > chunk.max && exitPos[m_indices[1]] > chunk.max)
653  {
654  return;
655  }
656  if (enterPos[1] < chunk.min && exitPos[m_indices[1]] < chunk.min)
657  {
658  return;
659  }
660  }
661  else
662  {
663  // Consider the ray vertical
664  // (though we shouldn't reach this often because there is an early check up-front)
665  enterPos = rayBegin;
666  exitPos = rayEnd;
667  }
668 
669  gridRaycast(processTriangles, enterPos, exitPos, m_indices);
670  // Note: it could be possible to have more than one grid at different levels,
671  // to do this there would be a branch using a pointer to another ProcessVBoundsAction
672  }
673 };
674 
675 // TODO How do I interrupt the ray when there is a hit? `callback` does not return any result
678 void btHeightfieldTerrainShape::performRaycast(btTriangleCallback* callback, const btVector3& raySource, const btVector3& rayTarget) const
679 {
680  // Transform to cell-local
681  btVector3 beginPos = raySource / m_localScaling;
682  btVector3 endPos = rayTarget / m_localScaling;
683  beginPos += m_localOrigin;
684  endPos += m_localOrigin;
685 
686  ProcessTrianglesAction processTriangles;
687  processTriangles.shape = this;
688  processTriangles.flipQuadEdges = m_flipQuadEdges;
690  processTriangles.callback = callback;
691  processTriangles.width = m_heightStickWidth - 1;
692  processTriangles.length = m_heightStickLength - 1;
693 
694  // TODO Transform vectors to account for m_upAxis
695  int indices[3] = { 0, 1, 2 };
696  if (m_upAxis == 2)
697  {
698  indices[1] = 2;
699  indices[2] = 1;
700  }
701  int iBeginX = static_cast<int>(floor(beginPos[indices[0]]));
702  int iBeginZ = static_cast<int>(floor(beginPos[indices[2]]));
703  int iEndX = static_cast<int>(floor(endPos[indices[0]]));
704  int iEndZ = static_cast<int>(floor(endPos[indices[2]]));
705 
706  if (iBeginX == iEndX && iBeginZ == iEndZ)
707  {
708  // The ray will never cross quads within the plane,
709  // so directly process triangles within one quad
710  // (typically, vertical rays should end up here)
711  processTriangles.exec(iBeginX, iEndZ);
712  return;
713  }
714 
715 
716 
717  if (m_vboundsGrid.size()==0)
718  {
719  // Process all quads intersecting the flat projection of the ray
720  gridRaycast(processTriangles, beginPos, endPos, &indices[0]);
721  }
722  else
723  {
724  btVector3 rayDiff = endPos - beginPos;
725  btScalar flatDistance2 = rayDiff[indices[0]] * rayDiff[indices[0]] + rayDiff[indices[2]] * rayDiff[indices[2]];
726  if (flatDistance2 < m_vboundsChunkSize * m_vboundsChunkSize)
727  {
728  // Don't use chunks, the ray is too short in the plane
729  gridRaycast(processTriangles, beginPos, endPos, &indices[0]);
730  }
731 
732  ProcessVBoundsAction processVBounds(m_vboundsGrid, &indices[0]);
733  processVBounds.width = m_vboundsGridWidth;
734  processVBounds.length = m_vboundsGridLength;
735  processVBounds.rayBegin = beginPos;
736  processVBounds.rayEnd = endPos;
737  processVBounds.rayDir = rayDiff.normalized();
738  processVBounds.processTriangles = processTriangles;
739  processVBounds.chunkSize = m_vboundsChunkSize;
740  // The ray is long, run raycast on a higher-level grid
741  gridRaycast(processVBounds, beginPos / m_vboundsChunkSize, endPos / m_vboundsChunkSize, indices);
742  }
743 }
744 
749 {
750  if (chunkSize <= 0)
751  {
753  return;
754  }
755 
756  m_vboundsChunkSize = chunkSize;
757  int nChunksX = m_heightStickWidth / chunkSize;
758  int nChunksZ = m_heightStickLength / chunkSize;
759 
760  if (m_heightStickWidth % chunkSize > 0)
761  {
762  ++nChunksX; // In case terrain size isn't dividable by chunk size
763  }
764  if (m_heightStickLength % chunkSize > 0)
765  {
766  ++nChunksZ;
767  }
768 
769  if (m_vboundsGridWidth != nChunksX || m_vboundsGridLength != nChunksZ)
770  {
772  m_vboundsGridWidth = nChunksX;
773  m_vboundsGridLength = nChunksZ;
774  }
775 
776  if (nChunksX == 0 || nChunksZ == 0)
777  {
778  return;
779  }
780 
781  // This data structure is only reallocated if the required size changed
782  m_vboundsGrid.resize(nChunksX * nChunksZ);
783 
784  // Compute min and max height for all chunks
785  for (int cz = 0; cz < nChunksZ; ++cz)
786  {
787  int z0 = cz * chunkSize;
788 
789  for (int cx = 0; cx < nChunksX; ++cx)
790  {
791  int x0 = cx * chunkSize;
792 
793  Range r;
794 
795  r.min = getRawHeightFieldValue(x0, z0);
796  r.max = r.min;
797 
798  // Compute min and max height for this chunk.
799  // We have to include one extra cell to account for neighbors.
800  // Here is why:
801  // Say we have a flat terrain, and a plateau that fits a chunk perfectly.
802  //
803  // Left Right
804  // 0---0---0---1---1---1
805  // | | | | | |
806  // 0---0---0---1---1---1
807  // | | | | | |
808  // 0---0---0---1---1---1
809  // x
810  //
811  // If the AABB for the Left chunk did not share vertices with the Right,
812  // then we would fail collision tests at x due to a gap.
813  //
814  for (int z = z0; z < z0 + chunkSize + 1; ++z)
815  {
816  if (z >= m_heightStickLength)
817  {
818  continue;
819  }
820 
821  for (int x = x0; x < x0 + chunkSize + 1; ++x)
822  {
823  if (x >= m_heightStickWidth)
824  {
825  continue;
826  }
827 
828  btScalar height = getRawHeightFieldValue(x, z);
829 
830  if (height < r.min)
831  {
832  r.min = height;
833  }
834  else if (height > r.max)
835  {
836  r.max = height;
837  }
838  }
839  }
840 
841  m_vboundsGrid[cx + cz * nChunksX] = r;
842  }
843  }
844 }
845 
847 {
848  m_vboundsGrid.clear();
849 }
btHeightfieldTerrainShape::m_localAabbMin
btVector3 m_localAabbMin
Definition: btHeightfieldTerrainShape.h:83
btHeightfieldTerrainShape::m_heightStickLength
int m_heightStickLength
Definition: btHeightfieldTerrainShape.h:89
ProcessTrianglesAction::useDiamondSubdivision
bool useDiamondSubdivision
Definition: btHeightfieldTerrainShape.cpp:555
btHeightfieldTerrainShape::initialize
void initialize(int heightStickWidth, int heightStickLength, const void *heightfieldData, btScalar heightScale, btScalar minHeight, btScalar maxHeight, int upAxis, PHY_ScalarType heightDataType, bool flipQuadEdges)
protected initialization
Definition: btHeightfieldTerrainShape.cpp:50
ProcessVBoundsAction::rayEnd
btVector3 rayEnd
Definition: btHeightfieldTerrainShape.cpp:616
TERRAIN_SHAPE_PROXYTYPE
@ TERRAIN_SHAPE_PROXYTYPE
Definition: btBroadphaseProxy.h:59
btHeightfieldTerrainShape::m_heightDataType
PHY_ScalarType m_heightDataType
Definition: btHeightfieldTerrainShape.h:102
btHeightfieldTerrainShape::getVertex
void getVertex(int x, int y, btVector3 &vertex) const
this returns the vertex in bullet-local coordinates
Definition: btHeightfieldTerrainShape.cpp:179
btVector3::dot3
btVector3 dot3(const btVector3 &v0, const btVector3 &v1, const btVector3 &v2) const
Definition: btVector3.h:720
btHeightfieldTerrainShape::quantizeWithClamp
void quantizeWithClamp(int *out, const btVector3 &point, int isMax) const
given input vector, return quantized version
Definition: btHeightfieldTerrainShape.cpp:244
btVector3::setValue
void setValue(const btScalar &_x, const btScalar &_y, const btScalar &_z)
Definition: btVector3.h:640
btScalar
float btScalar
The btScalar type abstracts floating point numbers, to easily switch between double and single floati...
Definition: btScalar.h:314
ProcessTrianglesAction::callback
btTriangleCallback * callback
Definition: btHeightfieldTerrainShape.cpp:558
btHeightfieldTerrainShape::m_vboundsGridLength
int m_vboundsGridLength
Definition: btHeightfieldTerrainShape.h:114
btHeightfieldTerrainShape::setLocalScaling
virtual void setLocalScaling(const btVector3 &scaling)
Definition: btHeightfieldTerrainShape.cpp:387
getQuantized
static int getQuantized(btScalar x)
Definition: btHeightfieldTerrainShape.cpp:225
ProcessVBoundsAction::ProcessVBoundsAction
ProcessVBoundsAction(const btAlignedObjectArray< btHeightfieldTerrainShape::Range > &bnd, int *indices)
Definition: btHeightfieldTerrainShape.cpp:622
ProcessVBoundsAction::chunkSize
int chunkSize
Definition: btHeightfieldTerrainShape.cpp:613
btHeightfieldTerrainShape
btHeightfieldTerrainShape simulates a 2D heightfield terrain
Definition: btHeightfieldTerrainShape.h:74
PHY_FLOAT
@ PHY_FLOAT
Definition: btConcaveShape.h:27
ProcessTrianglesAction::width
int width
Definition: btHeightfieldTerrainShape.cpp:556
btTransformUtil.h
btHeightfieldTerrainShape::Range
Definition: btHeightfieldTerrainShape.h:77
ProcessVBoundsAction::rayDir
btVector3 rayDir
Definition: btHeightfieldTerrainShape.cpp:617
ProcessVBoundsAction::processTriangles
ProcessTrianglesAction processTriangles
Definition: btHeightfieldTerrainShape.cpp:620
btHeightfieldTerrainShape::calculateLocalInertia
virtual void calculateLocalInertia(btScalar mass, btVector3 &inertia) const
Definition: btHeightfieldTerrainShape.cpp:380
btHeightfieldTerrainShape::m_localAabbMax
btVector3 m_localAabbMax
Definition: btHeightfieldTerrainShape.h:84
ProcessTrianglesAction
Definition: btHeightfieldTerrainShape.cpp:552
btVector3::getX
const btScalar & getX() const
Return the x value.
Definition: btVector3.h:561
btCollisionShape::m_shapeType
int m_shapeType
Definition: btCollisionShape.h:30
btHeightfieldTerrainShape::m_useDiamondSubdivision
bool m_useDiamondSubdivision
Definition: btHeightfieldTerrainShape.h:104
btHeightfieldTerrainShape::m_width
btScalar m_width
Definition: btHeightfieldTerrainShape.h:92
btHeightfieldTerrainShape::Range::min
btScalar min
Definition: btHeightfieldTerrainShape.h:78
btAssert
#define btAssert(x)
Definition: btScalar.h:153
btHeightfieldTerrainShape::processAllTriangles
virtual void processAllTriangles(btTriangleCallback *callback, const btVector3 &aabbMin, const btVector3 &aabbMax) const
process all triangles within the provided axis-aligned bounding box
Definition: btHeightfieldTerrainShape.cpp:262
btHeightfieldTerrainShape::m_heightfieldDataFloat
const btScalar * m_heightfieldDataFloat
Definition: btHeightfieldTerrainShape.h:98
btFabs
btScalar btFabs(btScalar x)
Definition: btScalar.h:497
btHeightfieldTerrainShape::m_upAxis
int m_upAxis
Definition: btHeightfieldTerrainShape.h:107
ProcessVBoundsAction::rayBegin
btVector3 rayBegin
Definition: btHeightfieldTerrainShape.cpp:615
btHeightfieldTerrainShape::getLocalScaling
virtual const btVector3 & getLocalScaling() const
Definition: btHeightfieldTerrainShape.cpp:391
btHeightfieldTerrainShape::m_heightfieldDataUnknown
const void * m_heightfieldDataUnknown
Definition: btHeightfieldTerrainShape.h:99
ProcessVBoundsAction::length
int length
Definition: btHeightfieldTerrainShape.cpp:612
btTransform::getBasis
btMatrix3x3 & getBasis()
Return the basis matrix for the rotation.
Definition: btTransform.h:108
ProcessTrianglesAction::length
int length
Definition: btHeightfieldTerrainShape.cpp:557
btVector3::setMax
void setMax(const btVector3 &other)
Set each element to the max of the current values and the values of another btVector3.
Definition: btVector3.h:609
btHeightfieldTerrainShape::getRawHeightFieldValue
virtual btScalar getRawHeightFieldValue(int x, int y) const
This returns the "raw" (user's initial) height, not the actual height.
Definition: btHeightfieldTerrainShape.cpp:144
PHY_ScalarType
PHY_ScalarType
PHY_ScalarType enumerates possible scalar types.
Definition: btConcaveShape.h:26
btHeightfieldTerrainShape::m_flipQuadEdges
bool m_flipQuadEdges
Definition: btHeightfieldTerrainShape.h:103
ProcessTrianglesAction::flipQuadEdges
bool flipQuadEdges
Definition: btHeightfieldTerrainShape.cpp:554
btMatrix3x3
The btMatrix3x3 class implements a 3x3 rotation matrix, to perform linear algebra in combination with...
Definition: btMatrix3x3.h:50
ProcessVBoundsAction::operator()
void operator()(const GridRaycastState &rs) const
Definition: btHeightfieldTerrainShape.cpp:627
btTriangleCallback
The btTriangleCallback provides a callback for each overlapping triangle when calling processAllTrian...
Definition: btTriangleCallback.h:24
btTransform
The btTransform class supports rigid transforms with only translation and rotation and no scaling/she...
Definition: btTransform.h:30
ProcessTrianglesAction::shape
const btHeightfieldTerrainShape * shape
Definition: btHeightfieldTerrainShape.cpp:553
btHeightfieldTerrainShape::getAabb
virtual void getAabb(const btTransform &t, btVector3 &aabbMin, btVector3 &aabbMax) const
getAabb returns the axis aligned bounding box in the coordinate frame of the given transform t.
Definition: btHeightfieldTerrainShape.cpp:123
btVector3
btVector3 can be used to represent 3D points and vectors.
Definition: btVector3.h:82
btHeightfieldTerrainShape::m_length
btScalar m_length
Definition: btHeightfieldTerrainShape.h:93
btHeightfieldTerrainShape::Range::max
btScalar max
Definition: btHeightfieldTerrainShape.h:79
btHeightfieldTerrainShape::m_minHeight
btScalar m_minHeight
Definition: btHeightfieldTerrainShape.h:90
btHeightfieldTerrainShape::m_useZigzagSubdivision
bool m_useZigzagSubdivision
Definition: btHeightfieldTerrainShape.h:105
btHeightfieldTerrainShape::m_heightfieldDataUnsignedChar
const unsigned char * m_heightfieldDataUnsignedChar
Definition: btHeightfieldTerrainShape.h:96
btTransform::getOrigin
btVector3 & getOrigin()
Return the origin vector translation.
Definition: btTransform.h:113
btHeightfieldTerrainShape::btHeightfieldTerrainShape
btHeightfieldTerrainShape(int heightStickWidth, int heightStickLength, const void *heightfieldData, btScalar heightScale, btScalar minHeight, btScalar maxHeight, int upAxis, PHY_ScalarType heightDataType, bool flipQuadEdges)
preferred constructor
Definition: btHeightfieldTerrainShape.cpp:20
btHeightfieldTerrainShape::~btHeightfieldTerrainShape
virtual ~btHeightfieldTerrainShape()
Definition: btHeightfieldTerrainShape.cpp:118
btTriangleCallback::processTriangle
virtual void processTriangle(btVector3 *triangle, int partId, int triangleIndex)=0
btVector3::getZ
const btScalar & getZ() const
Return the z value.
Definition: btVector3.h:565
btHeightfieldTerrainShape.h
btHeightfieldTerrainShape::m_heightStickWidth
int m_heightStickWidth
terrain data
Definition: btHeightfieldTerrainShape.h:88
btAlignedObjectArray< btHeightfieldTerrainShape::Range >
btVector3::getY
const btScalar & getY() const
Return the y value.
Definition: btVector3.h:563
btVector3::setMin
void setMin(const btVector3 &other)
Set each element to the min of the current values and the values of another btVector3.
Definition: btVector3.h:626
ProcessVBoundsAction::width
int width
Definition: btHeightfieldTerrainShape.cpp:611
btHeightfieldTerrainShape::m_vboundsChunkSize
int m_vboundsChunkSize
Definition: btHeightfieldTerrainShape.h:115
btHeightfieldTerrainShape::m_vboundsGrid
btAlignedObjectArray< Range > m_vboundsGrid
Definition: btHeightfieldTerrainShape.h:112
btHeightfieldTerrainShape::m_heightfieldDataShort
const short * m_heightfieldDataShort
Definition: btHeightfieldTerrainShape.h:97
ProcessVBoundsAction::m_indices
int * m_indices
Definition: btHeightfieldTerrainShape.cpp:619
ProcessVBoundsAction
Definition: btHeightfieldTerrainShape.cpp:609
ProcessTrianglesAction::exec
void exec(int x, int z) const
Definition: btHeightfieldTerrainShape.cpp:560
btHeightfieldTerrainShape::m_vboundsGridWidth
int m_vboundsGridWidth
Definition: btHeightfieldTerrainShape.h:113
btMatrix3x3::absolute
btMatrix3x3 absolute() const
Return the matrix with all values non negative.
Definition: btMatrix3x3.h:1028
btConcaveShape::getMargin
virtual btScalar getMargin() const
Definition: btConcaveShape.h:52
PHY_UCHAR
@ PHY_UCHAR
Definition: btConcaveShape.h:32
btHeightfieldTerrainShape::m_flipTriangleWinding
bool m_flipTriangleWinding
Definition: btHeightfieldTerrainShape.h:106
gridRaycast
void gridRaycast(Action_T &quadAction, const btVector3 &beginPos, const btVector3 &endPos, int indices[3])
Iterates through a virtual 2D grid of unit-sized square cells, and executes an action on each cell in...
Definition: btHeightfieldTerrainShape.cpp:416
btHeightfieldTerrainShape::m_localOrigin
btVector3 m_localOrigin
Definition: btHeightfieldTerrainShape.h:85
btHeightfieldTerrainShape::m_localScaling
btVector3 m_localScaling
Definition: btHeightfieldTerrainShape.h:109
btHeightfieldTerrainShape::clearAccelerator
void clearAccelerator()
Definition: btHeightfieldTerrainShape.cpp:846
btSqrt
btScalar btSqrt(btScalar y)
Definition: btScalar.h:466
ProcessTrianglesAction::operator()
void operator()(const GridRaycastState &bs) const
Definition: btHeightfieldTerrainShape.cpp:602
btHeightfieldTerrainShape::performRaycast
void performRaycast(btTriangleCallback *callback, const btVector3 &raySource, const btVector3 &rayTarget) const
Performs a raycast using a hierarchical Bresenham algorithm.
Definition: btHeightfieldTerrainShape.cpp:678
btHeightfieldTerrainShape::m_maxHeight
btScalar m_maxHeight
Definition: btHeightfieldTerrainShape.h:91
btVector3::normalized
btVector3 normalized() const
Return a normalized version of this vector.
Definition: btVector3.h:949
btVector3::distance
btScalar distance(const btVector3 &v) const
Return the distance between the ends of this and another vector This is symantically treating the vec...
Definition: btVector3.h:944
btHeightfieldTerrainShape::m_heightScale
btScalar m_heightScale
Definition: btHeightfieldTerrainShape.h:94
PHY_SHORT
@ PHY_SHORT
Definition: btConcaveShape.h:30
ProcessVBoundsAction::vbounds
const btAlignedObjectArray< btHeightfieldTerrainShape::Range > & vbounds
Definition: btHeightfieldTerrainShape.cpp:610
btHeightfieldTerrainShape::buildAccelerator
void buildAccelerator(int chunkSize=16)
Builds a grid data structure storing the min and max heights of the terrain in chunks.
Definition: btHeightfieldTerrainShape.cpp:748