Bullet Collision Detection & Physics Library
btHeightfieldTerrainShape.cpp
Go to the documentation of this file.
1/*
2Bullet Continuous Collision Detection and Physics Library
3Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.org
4
5This software is provided 'as-is', without any express or implied warranty.
6In no event will the authors be held liable for any damages arising from the use of this software.
7Permission is granted to anyone to use this software for any purpose,
8including commercial applications, and to alter it and redistribute it freely,
9subject to the following restrictions:
10
111. 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.
122. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
133. This notice may not be removed or altered from any source distribution.
14*/
15
17
19
21 int heightStickWidth, int heightStickLength,
22 const float* heightfieldData, btScalar minHeight, btScalar maxHeight,
23 int upAxis, bool flipQuadEdges)
24 : m_userValue3(0), m_triangleInfoMap(0)
25{
26 initialize(heightStickWidth, heightStickLength, heightfieldData,
27 /*heightScale=*/1, minHeight, maxHeight, upAxis, PHY_FLOAT,
28 flipQuadEdges);
29}
30
32 int heightStickWidth, int heightStickLength, const double* heightfieldData,
33 btScalar minHeight, btScalar maxHeight, int upAxis, bool flipQuadEdges)
34 : m_userValue3(0), m_triangleInfoMap(0)
35{
36 initialize(heightStickWidth, heightStickLength, heightfieldData,
37 /*heightScale=*/1, minHeight, maxHeight, upAxis, PHY_DOUBLE,
38 flipQuadEdges);
39}
40
42 int heightStickWidth, int heightStickLength, const short* heightfieldData, btScalar heightScale,
43 btScalar minHeight, btScalar maxHeight, int upAxis, bool flipQuadEdges)
44 : m_userValue3(0), m_triangleInfoMap(0)
45{
46 initialize(heightStickWidth, heightStickLength, heightfieldData,
47 heightScale, minHeight, maxHeight, upAxis, PHY_SHORT,
48 flipQuadEdges);
49}
50
52 int heightStickWidth, int heightStickLength, const unsigned char* heightfieldData, btScalar heightScale,
53 btScalar minHeight, btScalar maxHeight, int upAxis, bool flipQuadEdges)
54 : m_userValue3(0), m_triangleInfoMap(0)
55{
56 initialize(heightStickWidth, heightStickLength, heightfieldData,
57 heightScale, minHeight, maxHeight, upAxis, PHY_UCHAR,
58 flipQuadEdges);
59}
60
62 int heightStickWidth, int heightStickLength, const void* heightfieldData,
63 btScalar heightScale, btScalar minHeight, btScalar maxHeight, int upAxis,
64 PHY_ScalarType hdt, bool flipQuadEdges)
65 :m_userValue3(0),
66 m_triangleInfoMap(0)
67{
68 // legacy constructor: Assumes PHY_FLOAT means btScalar.
69#ifdef BT_USE_DOUBLE_PRECISION
70 if (hdt == PHY_FLOAT) hdt = PHY_DOUBLE;
71#endif
72 initialize(heightStickWidth, heightStickLength, heightfieldData,
73 heightScale, minHeight, maxHeight, upAxis, hdt,
74 flipQuadEdges);
75}
76
77btHeightfieldTerrainShape::btHeightfieldTerrainShape(int heightStickWidth, int heightStickLength, const void* heightfieldData, btScalar maxHeight, int upAxis, bool useFloatData, bool flipQuadEdges)
78 : m_userValue3(0),
79 m_triangleInfoMap(0)
80{
81 // legacy constructor: support only btScalar or unsigned char data,
82 // and min height is zero.
83 PHY_ScalarType hdt = (useFloatData) ? PHY_FLOAT : PHY_UCHAR;
84#ifdef BT_USE_DOUBLE_PRECISION
85 if (hdt == PHY_FLOAT) hdt = PHY_DOUBLE;
86#endif
87 btScalar minHeight = 0.0f;
88
89 // previously, height = uchar * maxHeight / 65535.
90 // So to preserve legacy behavior, heightScale = maxHeight / 65535
91 btScalar heightScale = maxHeight / 65535;
92
93 initialize(heightStickWidth, heightStickLength, heightfieldData,
94 heightScale, minHeight, maxHeight, upAxis, hdt,
95 flipQuadEdges);
96}
97
99 int heightStickWidth, int heightStickLength, const void* heightfieldData,
100 btScalar heightScale, btScalar minHeight, btScalar maxHeight, int upAxis,
101 PHY_ScalarType hdt, bool flipQuadEdges)
102{
103 // validation
104 btAssert(heightStickWidth > 1); // && "bad width");
105 btAssert(heightStickLength > 1); // && "bad length");
106 btAssert(heightfieldData); // && "null heightfield data");
107 // btAssert(heightScale) -- do we care? Trust caller here
108 btAssert(minHeight <= maxHeight); // && "bad min/max height");
109 btAssert(upAxis >= 0 && upAxis < 3); // && "bad upAxis--should be in range [0,2]");
110 btAssert(hdt != PHY_UCHAR || hdt != PHY_FLOAT || hdt != PHY_DOUBLE || hdt != PHY_SHORT); // && "Bad height data type enum");
111
112 // initialize member variables
114 m_heightStickWidth = heightStickWidth;
115 m_heightStickLength = heightStickLength;
116 m_minHeight = minHeight;
117 m_maxHeight = maxHeight;
118 m_width = (btScalar)(heightStickWidth - 1);
119 m_length = (btScalar)(heightStickLength - 1);
120 m_heightScale = heightScale;
121 m_heightfieldDataUnknown = heightfieldData;
122 m_heightDataType = hdt;
123 m_flipQuadEdges = flipQuadEdges;
126 m_flipTriangleWinding = false;
127 m_upAxis = upAxis;
129
133
134 // determine min/max axis-aligned bounding box (aabb) values
135 switch (m_upAxis)
136 {
137 case 0:
138 {
141 break;
142 }
143 case 1:
144 {
147 break;
148 };
149 case 2:
150 {
153 break;
154 }
155 default:
156 {
157 //need to get valid m_upAxis
158 btAssert(0); // && "Bad m_upAxis");
159 }
160 }
161
162 // remember origin (defined as exact middle of aabb)
164}
165
167{
169}
170
172{
174
175 btVector3 localOrigin(0, 0, 0);
176 localOrigin[m_upAxis] = (m_minHeight + m_maxHeight) * btScalar(0.5);
177 localOrigin *= m_localScaling;
178
179 btMatrix3x3 abs_b = t.getBasis().absolute();
180 btVector3 center = t.getOrigin();
181 btVector3 extent = halfExtents.dot3(abs_b[0], abs_b[1], abs_b[2]);
182 extent += btVector3(getMargin(), getMargin(), getMargin());
183
184 aabbMin = center - extent;
185 aabbMax = center + extent;
186}
187
193{
194 btScalar val = 0.f;
195 switch (m_heightDataType)
196 {
197 case PHY_FLOAT:
198 {
200 break;
201 }
202
203 case PHY_DOUBLE:
204 {
206 break;
207 }
208
209 case PHY_UCHAR:
210 {
211 unsigned char heightFieldValue = m_heightfieldDataUnsignedChar[(y * m_heightStickWidth) + x];
212 val = heightFieldValue * m_heightScale;
213 break;
214 }
215
216 case PHY_SHORT:
217 {
218 short hfValue = m_heightfieldDataShort[(y * m_heightStickWidth) + x];
219 val = hfValue * m_heightScale;
220 break;
221 }
222
223 default:
224 {
225 btAssert(!"Bad m_heightDataType");
226 }
227 }
228
229 return val;
230}
231
233void btHeightfieldTerrainShape::getVertex(int x, int y, btVector3& vertex) const
234{
235 btAssert(x >= 0);
236 btAssert(y >= 0);
239
240 btScalar height = getRawHeightFieldValue(x, y);
241
242 switch (m_upAxis)
243 {
244 case 0:
245 {
246 vertex.setValue(
247 height - m_localOrigin.getX(),
248 (-m_width / btScalar(2.0)) + x,
249 (-m_length / btScalar(2.0)) + y);
250 break;
251 }
252 case 1:
253 {
254 vertex.setValue(
255 (-m_width / btScalar(2.0)) + x,
256 height - m_localOrigin.getY(),
257 (-m_length / btScalar(2.0)) + y);
258 break;
259 };
260 case 2:
261 {
262 vertex.setValue(
263 (-m_width / btScalar(2.0)) + x,
264 (-m_length / btScalar(2.0)) + y,
265 height - m_localOrigin.getZ());
266 break;
267 }
268 default:
269 {
270 //need to get valid m_upAxis
271 btAssert(0);
272 }
273 }
274
275 vertex *= m_localScaling;
276}
277
278static inline int
280 btScalar x)
281{
282 if (x < 0.0)
283 {
284 return (int)(x - 0.5);
285 }
286 return (int)(x + 0.5);
287}
288
289// Equivalent to std::minmax({a, b, c}).
290// Performs at most 3 comparisons.
292{
293 if (a > b)
294 {
295 if (b > c)
297 else if (a > c)
299 else
301 }
302 else
303 {
304 if (a > c)
306 else if (b > c)
308 else
310 }
311}
312
314
322void btHeightfieldTerrainShape::quantizeWithClamp(int* out, const btVector3& point, int /*isMax*/) const
323{
324 btVector3 clampedPoint(point);
325 clampedPoint.setMax(m_localAabbMin);
326 clampedPoint.setMin(m_localAabbMax);
327
328 out[0] = getQuantized(clampedPoint.getX());
329 out[1] = getQuantized(clampedPoint.getY());
330 out[2] = getQuantized(clampedPoint.getZ());
331}
332
334
341{
342 // scale down the input aabb's so they are in local (non-scaled) coordinates
343 btVector3 localAabbMin = aabbMin * btVector3(1.f / m_localScaling[0], 1.f / m_localScaling[1], 1.f / m_localScaling[2]);
344 btVector3 localAabbMax = aabbMax * btVector3(1.f / m_localScaling[0], 1.f / m_localScaling[1], 1.f / m_localScaling[2]);
345
346 // account for local origin
347 localAabbMin += m_localOrigin;
348 localAabbMax += m_localOrigin;
349
350 //quantize the aabbMin and aabbMax, and adjust the start/end ranges
351 int quantizedAabbMin[3];
352 int quantizedAabbMax[3];
353 quantizeWithClamp(quantizedAabbMin, localAabbMin, 0);
354 quantizeWithClamp(quantizedAabbMax, localAabbMax, 1);
355
356 // expand the min/max quantized values
357 // this is to catch the case where the input aabb falls between grid points!
358 for (int i = 0; i < 3; ++i)
359 {
360 quantizedAabbMin[i]--;
361 quantizedAabbMax[i]++;
362 }
363
364 int startX = 0;
365 int endX = m_heightStickWidth - 1;
366 int startJ = 0;
367 int endJ = m_heightStickLength - 1;
368
369 switch (m_upAxis)
370 {
371 case 0:
372 {
373 if (quantizedAabbMin[1] > startX)
374 startX = quantizedAabbMin[1];
375 if (quantizedAabbMax[1] < endX)
376 endX = quantizedAabbMax[1];
377 if (quantizedAabbMin[2] > startJ)
378 startJ = quantizedAabbMin[2];
379 if (quantizedAabbMax[2] < endJ)
380 endJ = quantizedAabbMax[2];
381 break;
382 }
383 case 1:
384 {
385 if (quantizedAabbMin[0] > startX)
386 startX = quantizedAabbMin[0];
387 if (quantizedAabbMax[0] < endX)
388 endX = quantizedAabbMax[0];
389 if (quantizedAabbMin[2] > startJ)
390 startJ = quantizedAabbMin[2];
391 if (quantizedAabbMax[2] < endJ)
392 endJ = quantizedAabbMax[2];
393 break;
394 };
395 case 2:
396 {
397 if (quantizedAabbMin[0] > startX)
398 startX = quantizedAabbMin[0];
399 if (quantizedAabbMax[0] < endX)
400 endX = quantizedAabbMax[0];
401 if (quantizedAabbMin[1] > startJ)
402 startJ = quantizedAabbMin[1];
403 if (quantizedAabbMax[1] < endJ)
404 endJ = quantizedAabbMax[1];
405 break;
406 }
407 default:
408 {
409 //need to get valid m_upAxis
410 btAssert(0);
411 }
412 }
413
414 // TODO If m_vboundsGrid is available, use it to determine if we really need to process this area
415
416 const Range aabbUpRange(aabbMin[m_upAxis], aabbMax[m_upAxis]);
417 for (int j = startJ; j < endJ; j++)
418 {
419 for (int x = startX; x < endX; x++)
420 {
421 btVector3 vertices[3];
422 int indices[3] = { 0, 1, 2 };
424 {
425 indices[0] = 2;
426 indices[2] = 0;
427 }
428
429 if (m_flipQuadEdges || (m_useDiamondSubdivision && !((j + x) & 1)) || (m_useZigzagSubdivision && !(j & 1)))
430 {
431 getVertex(x, j, vertices[indices[0]]);
432 getVertex(x, j + 1, vertices[indices[1]]);
433 getVertex(x + 1, j + 1, vertices[indices[2]]);
434
435 // Skip triangle processing if the triangle is out-of-AABB.
436 Range upRange = minmaxRange(vertices[0][m_upAxis], vertices[1][m_upAxis], vertices[2][m_upAxis]);
437
438 if (upRange.overlaps(aabbUpRange))
439 callback->processTriangle(vertices, 2 * x, j);
440
441 // already set: getVertex(x, j, vertices[indices[0]])
442
443 // equivalent to: getVertex(x + 1, j + 1, vertices[indices[1]]);
444 vertices[indices[1]] = vertices[indices[2]];
445
446 getVertex(x + 1, j, vertices[indices[2]]);
447 upRange.min = btMin(upRange.min, vertices[indices[2]][m_upAxis]);
448 upRange.max = btMax(upRange.max, vertices[indices[2]][m_upAxis]);
449
450 if (upRange.overlaps(aabbUpRange))
451 callback->processTriangle(vertices, 2 * x + 1, j);
452 }
453 else
454 {
455 getVertex(x, j, vertices[indices[0]]);
456 getVertex(x, j + 1, vertices[indices[1]]);
457 getVertex(x + 1, j, vertices[indices[2]]);
458
459 // Skip triangle processing if the triangle is out-of-AABB.
460 Range upRange = minmaxRange(vertices[0][m_upAxis], vertices[1][m_upAxis], vertices[2][m_upAxis]);
461
462 if (upRange.overlaps(aabbUpRange))
463 callback->processTriangle(vertices, 2 * x, j);
464
465 // already set: getVertex(x, j + 1, vertices[indices[1]]);
466
467 // equivalent to: getVertex(x + 1, j, vertices[indices[0]]);
468 vertices[indices[0]] = vertices[indices[2]];
469
470 getVertex(x + 1, j + 1, vertices[indices[2]]);
471 upRange.min = btMin(upRange.min, vertices[indices[2]][m_upAxis]);
472 upRange.max = btMax(upRange.max, vertices[indices[2]][m_upAxis]);
473
474 if (upRange.overlaps(aabbUpRange))
475 callback->processTriangle(vertices, 2 * x + 1, j);
476 }
477 }
478 }
479}
480
482{
483 //moving concave objects not supported
484
485 inertia.setValue(btScalar(0.), btScalar(0.), btScalar(0.));
486}
487
489{
490 m_localScaling = scaling;
491}
493{
494 return m_localScaling;
495}
496
497namespace
498{
499 struct GridRaycastState
500 {
501 int x; // Next quad coords
502 int z;
503 int prev_x; // Previous quad coords
504 int prev_z;
505 btScalar param; // Exit param for previous quad
506 btScalar prevParam; // Enter param for previous quad
507 btScalar maxDistanceFlat;
508 btScalar maxDistance3d;
509 };
510}
511
512// TODO Does it really need to take 3D vectors?
516template <typename Action_T>
517void gridRaycast(Action_T& quadAction, const btVector3& beginPos, const btVector3& endPos, int indices[3])
518{
519 GridRaycastState rs;
520 rs.maxDistance3d = beginPos.distance(endPos);
521 if (rs.maxDistance3d < 0.0001)
522 {
523 // Consider the ray is too small to hit anything
524 return;
525 }
526
527
528 btScalar rayDirectionFlatX = endPos[indices[0]] - beginPos[indices[0]];
529 btScalar rayDirectionFlatZ = endPos[indices[2]] - beginPos[indices[2]];
530 rs.maxDistanceFlat = btSqrt(rayDirectionFlatX * rayDirectionFlatX + rayDirectionFlatZ * rayDirectionFlatZ);
531
532 if (rs.maxDistanceFlat < 0.0001)
533 {
534 // Consider the ray vertical
535 rayDirectionFlatX = 0;
536 rayDirectionFlatZ = 0;
537 }
538 else
539 {
540 rayDirectionFlatX /= rs.maxDistanceFlat;
541 rayDirectionFlatZ /= rs.maxDistanceFlat;
542 }
543
544 const int xiStep = rayDirectionFlatX > 0 ? 1 : rayDirectionFlatX < 0 ? -1 : 0;
545 const int ziStep = rayDirectionFlatZ > 0 ? 1 : rayDirectionFlatZ < 0 ? -1 : 0;
546
547 const float infinite = 9999999;
548 const btScalar paramDeltaX = xiStep != 0 ? 1.f / btFabs(rayDirectionFlatX) : infinite;
549 const btScalar paramDeltaZ = ziStep != 0 ? 1.f / btFabs(rayDirectionFlatZ) : infinite;
550
551 // pos = param * dir
552 btScalar paramCrossX; // At which value of `param` we will cross a x-axis lane?
553 btScalar paramCrossZ; // At which value of `param` we will cross a z-axis lane?
554
555 // paramCrossX and paramCrossZ are initialized as being the first cross
556 // X initialization
557 if (xiStep != 0)
558 {
559 if (xiStep == 1)
560 {
561 paramCrossX = (ceil(beginPos[indices[0]]) - beginPos[indices[0]]) * paramDeltaX;
562 }
563 else
564 {
565 paramCrossX = (beginPos[indices[0]] - floor(beginPos[indices[0]])) * paramDeltaX;
566 }
567 }
568 else
569 {
570 paramCrossX = infinite; // Will never cross on X
571 }
572
573 // Z initialization
574 if (ziStep != 0)
575 {
576 if (ziStep == 1)
577 {
578 paramCrossZ = (ceil(beginPos[indices[2]]) - beginPos[indices[2]]) * paramDeltaZ;
579 }
580 else
581 {
582 paramCrossZ = (beginPos[indices[2]] - floor(beginPos[indices[2]])) * paramDeltaZ;
583 }
584 }
585 else
586 {
587 paramCrossZ = infinite; // Will never cross on Z
588 }
589
590 rs.x = static_cast<int>(floor(beginPos[indices[0]]));
591 rs.z = static_cast<int>(floor(beginPos[indices[2]]));
592
593 // Workaround cases where the ray starts at an integer position
594 if (paramCrossX == 0.0)
595 {
596 paramCrossX += paramDeltaX;
597 // If going backwards, we should ignore the position we would get by the above flooring,
598 // because the ray is not heading in that direction
599 if (xiStep == -1)
600 {
601 rs.x -= 1;
602 }
603 }
604
605 if (paramCrossZ == 0.0)
606 {
607 paramCrossZ += paramDeltaZ;
608 if (ziStep == -1)
609 rs.z -= 1;
610 }
611
612 rs.prev_x = rs.x;
613 rs.prev_z = rs.z;
614 rs.param = 0;
615
616 while (true)
617 {
618 rs.prev_x = rs.x;
619 rs.prev_z = rs.z;
620 rs.prevParam = rs.param;
621
622 if (paramCrossX < paramCrossZ)
623 {
624 // X lane
625 rs.x += xiStep;
626 // Assign before advancing the param,
627 // to be in sync with the initialization step
628 rs.param = paramCrossX;
629 paramCrossX += paramDeltaX;
630 }
631 else
632 {
633 // Z lane
634 rs.z += ziStep;
635 rs.param = paramCrossZ;
636 paramCrossZ += paramDeltaZ;
637 }
638
639 if (rs.param > rs.maxDistanceFlat)
640 {
641 rs.param = rs.maxDistanceFlat;
642 quadAction(rs);
643 break;
644 }
645 else
646 {
647 quadAction(rs);
648 }
649 }
650}
651
653{
657 int width;
660
661 void exec(int x, int z) const
662 {
663 if (x < 0 || z < 0 || x >= width || z >= length)
664 {
665 return;
666 }
667
668 btVector3 vertices[3];
669
670 // TODO Since this is for raycasts, we could greatly benefit from an early exit on the first hit
671
672 // Check quad
673 if (flipQuadEdges || (useDiamondSubdivision && (((z + x) & 1) > 0)))
674 {
675 // First triangle
676 shape->getVertex(x, z, vertices[0]);
677 shape->getVertex(x + 1, z, vertices[1]);
678 shape->getVertex(x + 1, z + 1, vertices[2]);
679 callback->processTriangle(vertices, x, z);
680
681 // Second triangle
682 shape->getVertex(x, z, vertices[0]);
683 shape->getVertex(x + 1, z + 1, vertices[1]);
684 shape->getVertex(x, z + 1, vertices[2]);
685 callback->processTriangle(vertices, x, z);
686 }
687 else
688 {
689 // First triangle
690 shape->getVertex(x, z, vertices[0]);
691 shape->getVertex(x, z + 1, vertices[1]);
692 shape->getVertex(x + 1, z, vertices[2]);
693 callback->processTriangle(vertices, x, z);
694
695 // Second triangle
696 shape->getVertex(x + 1, z, vertices[0]);
697 shape->getVertex(x, z + 1, vertices[1]);
698 shape->getVertex(x + 1, z + 1, vertices[2]);
699 callback->processTriangle(vertices, x, z);
700 }
701 }
702
703 void operator()(const GridRaycastState& bs) const
704 {
705 exec(bs.prev_x, bs.prev_z);
706 }
707};
708
710{
712 int width;
715
719
722
724 : vbounds(bnd),
725 m_indices(indices)
726 {
727 }
728 void operator()(const GridRaycastState& rs) const
729 {
730 int x = rs.prev_x;
731 int z = rs.prev_z;
732
733 if (x < 0 || z < 0 || x >= width || z >= length)
734 {
735 return;
736 }
737
738 const btHeightfieldTerrainShape::Range chunk = vbounds[x + z * width];
739
740 btVector3 enterPos;
741 btVector3 exitPos;
742
743 if (rs.maxDistanceFlat > 0.0001)
744 {
745 btScalar flatTo3d = chunkSize * rs.maxDistance3d / rs.maxDistanceFlat;
746 btScalar enterParam3d = rs.prevParam * flatTo3d;
747 btScalar exitParam3d = rs.param * flatTo3d;
748 enterPos = rayBegin + rayDir * enterParam3d;
749 exitPos = rayBegin + rayDir * exitParam3d;
750
751 // We did enter the flat projection of the AABB,
752 // but we have to check if we intersect it on the vertical axis
753 if (enterPos[1] > chunk.max && exitPos[m_indices[1]] > chunk.max)
754 {
755 return;
756 }
757 if (enterPos[1] < chunk.min && exitPos[m_indices[1]] < chunk.min)
758 {
759 return;
760 }
761 }
762 else
763 {
764 // Consider the ray vertical
765 // (though we shouldn't reach this often because there is an early check up-front)
766 enterPos = rayBegin;
767 exitPos = rayEnd;
768 }
769
770 gridRaycast(processTriangles, enterPos, exitPos, m_indices);
771 // Note: it could be possible to have more than one grid at different levels,
772 // to do this there would be a branch using a pointer to another ProcessVBoundsAction
773 }
774};
775
776// TODO How do I interrupt the ray when there is a hit? `callback` does not return any result
779void btHeightfieldTerrainShape::performRaycast(btTriangleCallback* callback, const btVector3& raySource, const btVector3& rayTarget) const
780{
781 // Transform to cell-local
782 btVector3 beginPos = raySource / m_localScaling;
783 btVector3 endPos = rayTarget / m_localScaling;
784 beginPos += m_localOrigin;
785 endPos += m_localOrigin;
786
787 ProcessTrianglesAction processTriangles;
788 processTriangles.shape = this;
789 processTriangles.flipQuadEdges = m_flipQuadEdges;
791 processTriangles.callback = callback;
792 processTriangles.width = m_heightStickWidth - 1;
793 processTriangles.length = m_heightStickLength - 1;
794
795 // TODO Transform vectors to account for m_upAxis
796 int indices[3] = { 0, 1, 2 };
797 if (m_upAxis == 2)
798 {
799 indices[1] = 2;
800 indices[2] = 1;
801 }
802 int iBeginX = static_cast<int>(floor(beginPos[indices[0]]));
803 int iBeginZ = static_cast<int>(floor(beginPos[indices[2]]));
804 int iEndX = static_cast<int>(floor(endPos[indices[0]]));
805 int iEndZ = static_cast<int>(floor(endPos[indices[2]]));
806
807 if (iBeginX == iEndX && iBeginZ == iEndZ)
808 {
809 // The ray will never cross quads within the plane,
810 // so directly process triangles within one quad
811 // (typically, vertical rays should end up here)
812 processTriangles.exec(iBeginX, iEndZ);
813 return;
814 }
815
816
817
818 if (m_vboundsGrid.size()==0)
819 {
820 // Process all quads intersecting the flat projection of the ray
821 gridRaycast(processTriangles, beginPos, endPos, &indices[0]);
822 }
823 else
824 {
825 btVector3 rayDiff = endPos - beginPos;
826 btScalar flatDistance2 = rayDiff[indices[0]] * rayDiff[indices[0]] + rayDiff[indices[2]] * rayDiff[indices[2]];
827 if (flatDistance2 < m_vboundsChunkSize * m_vboundsChunkSize)
828 {
829 // Don't use chunks, the ray is too short in the plane
830 gridRaycast(processTriangles, beginPos, endPos, &indices[0]);
831 return;
832 }
833
834 ProcessVBoundsAction processVBounds(m_vboundsGrid, &indices[0]);
835 processVBounds.width = m_vboundsGridWidth;
836 processVBounds.length = m_vboundsGridLength;
837 processVBounds.rayBegin = beginPos;
838 processVBounds.rayEnd = endPos;
839 processVBounds.rayDir = rayDiff.normalized();
840 processVBounds.processTriangles = processTriangles;
841 processVBounds.chunkSize = m_vboundsChunkSize;
842 // The ray is long, run raycast on a higher-level grid
843 gridRaycast(processVBounds, beginPos / m_vboundsChunkSize, endPos / m_vboundsChunkSize, indices);
844 }
845}
846
851{
852 if (chunkSize <= 0)
853 {
855 return;
856 }
857
858 m_vboundsChunkSize = chunkSize;
859 int nChunksX = m_heightStickWidth / chunkSize;
860 int nChunksZ = m_heightStickLength / chunkSize;
861
862 if (m_heightStickWidth % chunkSize > 0)
863 {
864 ++nChunksX; // In case terrain size isn't dividable by chunk size
865 }
866 if (m_heightStickLength % chunkSize > 0)
867 {
868 ++nChunksZ;
869 }
870
871 if (m_vboundsGridWidth != nChunksX || m_vboundsGridLength != nChunksZ)
872 {
874 m_vboundsGridWidth = nChunksX;
875 m_vboundsGridLength = nChunksZ;
876 }
877
878 if (nChunksX == 0 || nChunksZ == 0)
879 {
880 return;
881 }
882
883 // This data structure is only reallocated if the required size changed
884 m_vboundsGrid.resize(nChunksX * nChunksZ);
885
886 // Compute min and max height for all chunks
887 for (int cz = 0; cz < nChunksZ; ++cz)
888 {
889 int z0 = cz * chunkSize;
890
891 for (int cx = 0; cx < nChunksX; ++cx)
892 {
893 int x0 = cx * chunkSize;
894
895 Range r;
896
897 r.min = getRawHeightFieldValue(x0, z0);
898 r.max = r.min;
899
900 // Compute min and max height for this chunk.
901 // We have to include one extra cell to account for neighbors.
902 // Here is why:
903 // Say we have a flat terrain, and a plateau that fits a chunk perfectly.
904 //
905 // Left Right
906 // 0---0---0---1---1---1
907 // | | | | | |
908 // 0---0---0---1---1---1
909 // | | | | | |
910 // 0---0---0---1---1---1
911 // x
912 //
913 // If the AABB for the Left chunk did not share vertices with the Right,
914 // then we would fail collision tests at x due to a gap.
915 //
916 for (int z = z0; z < z0 + chunkSize + 1; ++z)
917 {
918 if (z >= m_heightStickLength)
919 {
920 continue;
921 }
922
923 for (int x = x0; x < x0 + chunkSize + 1; ++x)
924 {
925 if (x >= m_heightStickWidth)
926 {
927 continue;
928 }
929
930 btScalar height = getRawHeightFieldValue(x, z);
931
932 if (height < r.min)
933 {
934 r.min = height;
935 }
936 else if (height > r.max)
937 {
938 r.max = height;
939 }
940 }
941 }
942
943 m_vboundsGrid[cx + cz * nChunksX] = r;
944 }
945 }
946}
947
949{
950 m_vboundsGrid.clear();
951}
@ TERRAIN_SHAPE_PROXYTYPE
PHY_ScalarType
PHY_ScalarType enumerates possible scalar types.
@ PHY_FLOAT
@ PHY_UCHAR
@ PHY_DOUBLE
@ PHY_SHORT
static btHeightfieldTerrainShape::Range minmaxRange(btScalar a, btScalar b, btScalar c)
static int getQuantized(btScalar x)
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...
const T & btMax(const T &a, const T &b)
Definition: btMinMax.h:27
const T & btMin(const T &a, const T &b)
Definition: btMinMax.h:21
float btScalar
The btScalar type abstracts floating point numbers, to easily switch between double and single floati...
Definition: btScalar.h:314
btScalar btSqrt(btScalar y)
Definition: btScalar.h:466
btScalar btFabs(btScalar x)
Definition: btScalar.h:497
#define btAssert(x)
Definition: btScalar.h:153
virtual btScalar getMargin() const
btHeightfieldTerrainShape simulates a 2D heightfield terrain
virtual btScalar getRawHeightFieldValue(int x, int y) const
This returns the "raw" (user's initial) height, not the actual height.
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.
void quantizeWithClamp(int *out, const btVector3 &point, int isMax) const
given input vector, return quantized version
void getVertex(int x, int y, btVector3 &vertex) const
this returns the vertex in bullet-local coordinates
virtual void calculateLocalInertia(btScalar mass, btVector3 &inertia) const
const unsigned char * m_heightfieldDataUnsignedChar
virtual void setLocalScaling(const btVector3 &scaling)
void performRaycast(btTriangleCallback *callback, const btVector3 &raySource, const btVector3 &rayTarget) const
Performs a raycast using a hierarchical Bresenham algorithm.
virtual void processAllTriangles(btTriangleCallback *callback, const btVector3 &aabbMin, const btVector3 &aabbMax) const
process all triangles within the provided axis-aligned bounding box
btHeightfieldTerrainShape(int heightStickWidth, int heightStickLength, const float *heightfieldData, btScalar minHeight, btScalar maxHeight, int upAxis, bool flipQuadEdges)
preferred constructors
btAlignedObjectArray< Range > m_vboundsGrid
virtual const btVector3 & getLocalScaling() const
void buildAccelerator(int chunkSize=16)
Builds a grid data structure storing the min and max heights of the terrain in chunks.
void initialize(int heightStickWidth, int heightStickLength, const void *heightfieldData, btScalar heightScale, btScalar minHeight, btScalar maxHeight, int upAxis, PHY_ScalarType heightDataType, bool flipQuadEdges)
protected initialization
The btMatrix3x3 class implements a 3x3 rotation matrix, to perform linear algebra in combination with...
Definition: btMatrix3x3.h:50
btMatrix3x3 absolute() const
Return the matrix with all values non negative.
Definition: btMatrix3x3.h:1028
The btTransform class supports rigid transforms with only translation and rotation and no scaling/she...
Definition: btTransform.h:30
btMatrix3x3 & getBasis()
Return the basis matrix for the rotation.
Definition: btTransform.h:109
btVector3 & getOrigin()
Return the origin vector translation.
Definition: btTransform.h:114
The btTriangleCallback provides a callback for each overlapping triangle when calling processAllTrian...
virtual void processTriangle(btVector3 *triangle, int partId, int triangleIndex)=0
btVector3 can be used to represent 3D points and vectors.
Definition: btVector3.h:82
const btScalar & getZ() const
Return the z value.
Definition: btVector3.h:565
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
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
btVector3 dot3(const btVector3 &v0, const btVector3 &v1, const btVector3 &v2) const
Definition: btVector3.h:720
void setValue(const btScalar &_x, const btScalar &_y, const btScalar &_z)
Definition: btVector3.h:640
btVector3 normalized() const
Return a normalized version of this vector.
Definition: btVector3.h:949
const btScalar & getY() const
Return the y value.
Definition: btVector3.h:563
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
const btScalar & getX() const
Return the x value.
Definition: btVector3.h:561
void operator()(const GridRaycastState &bs) const
void exec(int x, int z) const
const btHeightfieldTerrainShape * shape
void operator()(const GridRaycastState &rs) const
ProcessVBoundsAction(const btAlignedObjectArray< btHeightfieldTerrainShape::Range > &bnd, int *indices)
ProcessTrianglesAction processTriangles
const btAlignedObjectArray< btHeightfieldTerrainShape::Range > & vbounds
bool overlaps(const Range &other) const