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, const void* heightfieldData,
22 btScalar heightScale, btScalar minHeight, btScalar maxHeight, int upAxis,
23 PHY_ScalarType hdt, bool flipQuadEdges)
24 :m_userValue3(0),
26{
27 initialize(heightStickWidth, heightStickLength, heightfieldData,
28 heightScale, minHeight, maxHeight, upAxis, hdt,
29 flipQuadEdges);
30}
31
32btHeightfieldTerrainShape::btHeightfieldTerrainShape(int heightStickWidth, int heightStickLength, const void* heightfieldData, btScalar maxHeight, int upAxis, bool useFloatData, bool flipQuadEdges)
33 : m_userValue3(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;
79 m_upAxis = upAxis;
80 m_localScaling.setValue(btScalar(1.), btScalar(1.), btScalar(1.));
81
85
86 // determine min/max axis-aligned bounding box (aabb) values
87 switch (m_upAxis)
88 {
89 case 0:
90 {
91 m_localAabbMin.setValue(m_minHeight, 0, 0);
93 break;
94 }
95 case 1:
96 {
97 m_localAabbMin.setValue(0, m_minHeight, 0);
99 break;
100 };
101 case 2:
102 {
103 m_localAabbMin.setValue(0, 0, m_minHeight);
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
122
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
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
179void 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
224static 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
244void 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
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}
395
396namespace
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?
415template <typename Action_T>
416void 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;
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;
614
618
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
678void 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
@ TERRAIN_SHAPE_PROXYTYPE
PHY_ScalarType
PHY_ScalarType enumerates possible scalar types.
@ PHY_FLOAT
@ PHY_UCHAR
@ PHY_SHORT
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...
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
btHeightfieldTerrainShape(int heightStickWidth, int heightStickLength, const void *heightfieldData, btScalar heightScale, btScalar minHeight, btScalar maxHeight, int upAxis, PHY_ScalarType heightDataType, bool flipQuadEdges)
preferred constructor
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
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
struct btTriangleInfoMap * m_triangleInfoMap
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
btMatrix3x3 & getBasis()
Return the basis matrix for the rotation.
btVector3 & getOrigin()
Return the origin vector translation.
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
const btHeightfieldTerrainShape * shape
void operator()(const GridRaycastState &rs) const
ProcessVBoundsAction(const btAlignedObjectArray< btHeightfieldTerrainShape::Range > &bnd, int *indices)
ProcessTrianglesAction processTriangles
const btAlignedObjectArray< btHeightfieldTerrainShape::Range > & vbounds