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