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
23 int upAxis, bool flipQuadEdges)
24 : m_userValue3(0), m_triangleInfoMap(0)
25{
27 /*heightScale=*/1, minHeight, maxHeight, upAxis, PHY_FLOAT,
28 flipQuadEdges);
29}
30
33 btScalar minHeight, btScalar maxHeight, int upAxis, bool flipQuadEdges)
34 : m_userValue3(0), m_triangleInfoMap(0)
35{
37 /*heightScale=*/1, minHeight, maxHeight, upAxis, PHY_DOUBLE,
38 flipQuadEdges);
39}
40
43 btScalar minHeight, btScalar maxHeight, int upAxis, bool flipQuadEdges)
44 : m_userValue3(0), m_triangleInfoMap(0)
45{
48 flipQuadEdges);
49}
50
53 btScalar minHeight, btScalar maxHeight, int upAxis, bool flipQuadEdges)
54 : m_userValue3(0), m_triangleInfoMap(0)
55{
58 flipQuadEdges);
59}
60
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
74 flipQuadEdges);
75}
76
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.
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
92
95 flipQuadEdges);
96}
97
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
123 m_flipQuadEdges = flipQuadEdges;
126 m_flipTriangleWinding = false;
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);
178
179 btMatrix3x3 abs_b = t.getBasis().absolute();
180 btVector3 center = t.getOrigin();
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 {
213 break;
214 }
215
216 case PHY_SHORT:
217 {
220 break;
221 }
222
223 default:
224 {
225 btAssert(!"Bad m_heightDataType");
226 }
227 }
228
229 return val;
230}
231
234{
235 btAssert(x >= 0);
236 btAssert(y >= 0);
239
241
242 switch (m_upAxis)
243 {
244 case 0:
245 {
246 vertex.setValue(
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,
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,
266 break;
267 }
268 default:
269 {
270 //need to get valid m_upAxis
271 btAssert(0);
272 }
273 }
274
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);
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
345
346 // account for local origin
349
350 //quantize the aabbMin and aabbMax, and adjust the start/end ranges
351 int quantizedAabbMin[3];
352 int quantizedAabbMax[3];
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)
375 if (quantizedAabbMax[1] < endX)
377 if (quantizedAabbMin[2] > startJ)
379 if (quantizedAabbMax[2] < endJ)
381 break;
382 }
383 case 1:
384 {
385 if (quantizedAabbMin[0] > startX)
387 if (quantizedAabbMax[0] < endX)
389 if (quantizedAabbMin[2] > startJ)
391 if (quantizedAabbMax[2] < endJ)
393 break;
394 };
395 case 2:
396 {
397 if (quantizedAabbMin[0] > startX)
399 if (quantizedAabbMax[0] < endX)
401 if (quantizedAabbMin[1] > startJ)
403 if (quantizedAabbMax[1] < endJ)
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
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>
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
531
532 if (rs.maxDistanceFlat < 0.0001)
533 {
534 // Consider the ray vertical
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;
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 {
562 }
563 else
564 {
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 {
579 }
580 else
581 {
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 {
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 {
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
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;
630 }
631 else
632 {
633 // Z lane
634 rs.z += ziStep;
635 rs.param = paramCrossZ;
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),
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
739
742
743 if (rs.maxDistanceFlat > 0.0001)
744 {
745 btScalar flatTo3d = chunkSize * rs.maxDistance3d / rs.maxDistanceFlat;
746 btScalar enterParam3d = rs.prevParam * flatTo3d;
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)
767 exitPos = rayEnd;
768 }
769
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
780{
781 // Transform to cell-local
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 {
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
837 processVBounds.rayBegin = beginPos;
838 processVBounds.rayEnd = endPos;
839 processVBounds.rayDir = rayDiff.normalized();
840 processVBounds.processTriangles = processTriangles;
842 // The ray is long, run raycast on a higher-level grid
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
872 {
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
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
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
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
The btAlignedObjectArray template class uses a subset of the stl::vector interface for its methods It...
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.
The btTransform class supports rigid transforms with only translation and rotation and no scaling/she...
Definition btTransform.h:30
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
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
const btScalar & getY() const
Return the y value.
Definition btVector3.h:563
const btScalar & getX() const
Return the x value.
Definition btVector3.h:561
void operator()(const GridRaycastState &bs) 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