RavEngine
Loading...
Searching...
No Matches
GuHeightFieldUtil.h
1// Redistribution and use in source and binary forms, with or without
2// modification, are permitted provided that the following conditions
3// are met:
4// * Redistributions of source code must retain the above copyright
5// notice, this list of conditions and the following disclaimer.
6// * Redistributions in binary form must reproduce the above copyright
7// notice, this list of conditions and the following disclaimer in the
8// documentation and/or other materials provided with the distribution.
9// * Neither the name of NVIDIA CORPORATION nor the names of its
10// contributors may be used to endorse or promote products derived
11// from this software without specific prior written permission.
12//
13// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
14// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24//
25// Copyright (c) 2008-2022 NVIDIA Corporation. All rights reserved.
26// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
27// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
28
29#ifndef GU_HEIGHTFIELD_UTIL_H
30#define GU_HEIGHTFIELD_UTIL_H
31
32#include "geometry/PxHeightFieldGeometry.h"
33#include "geometry/PxTriangle.h"
34#include "foundation/PxBasicTemplates.h"
35#include "foundation/PxSIMDHelpers.h"
36
37#include "GuHeightField.h"
38#include "../intersection/GuIntersectionRayTriangle.h"
39#include "../intersection/GuIntersectionRayBox.h"
40
41namespace physx
42{
43#define HF_SWEEP_REPORT_BUFFER_SIZE 64
44#define HF_OVERLAP_REPORT_BUFFER_SIZE 64
45
46namespace Gu
47{
48 class OverlapReport;
49
50 // PT: this is used in the context of sphere-vs-heightfield overlaps
51 PX_FORCE_INLINE PxVec3 getLocalSphereData(PxBounds3& localBounds, const PxTransform& pose0, const PxTransform& pose1, float radius)
52 {
53 const PxVec3 localSphereCenter = pose1.transformInv(pose0.p);
54
55 const PxVec3 extents(radius);
56 localBounds.minimum = localSphereCenter - extents;
57 localBounds.maximum = localSphereCenter + extents;
58
59 return localSphereCenter;
60 }
61
62 PX_FORCE_INLINE PxBounds3 getLocalCapsuleBounds(float radius, float halfHeight)
63 {
64 const PxVec3 extents(halfHeight + radius, radius, radius);
65 return PxBounds3(-extents, extents);
66 }
67
68 class PX_PHYSX_COMMON_API HeightFieldUtil
69 {
70 public:
71 PxReal mOneOverRowScale;
72 PxReal mOneOverHeightScale;
73 PxReal mOneOverColumnScale;
74 const Gu::HeightField* mHeightField;
75 const PxHeightFieldGeometry* mHfGeom;
76
77 PX_FORCE_INLINE HeightFieldUtil(const PxHeightFieldGeometry& hfGeom) : mHeightField(static_cast<const Gu::HeightField*>(hfGeom.heightField)), mHfGeom(&hfGeom)
78 {
79 const PxReal absRowScale = PxAbs(mHfGeom->rowScale);
80 const PxReal absColScale = PxAbs(mHfGeom->columnScale);
81 //warning #1931-D on WIIU: sizeof is not a type, variable, or dereferenced pointer expression
82 PX_COMPILE_TIME_ASSERT(sizeof(reinterpret_cast<PxHeightFieldSample*>(0)->height) == 2);
83 //PxReal minHeightPerSample = PX_MIN_HEIGHTFIELD_Y_SCALE;
84 PX_ASSERT(mHfGeom->heightScale >= PX_MIN_HEIGHTFIELD_Y_SCALE);
85 PX_ASSERT(absRowScale >= PX_MIN_HEIGHTFIELD_XZ_SCALE);
86 PX_ASSERT(absColScale >= PX_MIN_HEIGHTFIELD_XZ_SCALE);
87 PX_UNUSED(absRowScale);
88 PX_UNUSED(absColScale);
89 //using physx::intrinsics::fsel;
90 //mOneOverHeightScale = fsel(mHfGeom->heightScale - minHeightPerSample, 1.0f / mHfGeom->heightScale, 1.0f / minHeightPerSample);
91 mOneOverHeightScale = 1.0f / mHfGeom->heightScale;
92 mOneOverRowScale = 1.0f / mHfGeom->rowScale;
93 mOneOverColumnScale = 1.0f / mHfGeom->columnScale;
94 }
95
96 PX_CUDA_CALLABLE PX_FORCE_INLINE const Gu::HeightField& getHeightField() const { return *mHeightField; }
97 PX_CUDA_CALLABLE PX_FORCE_INLINE const PxHeightFieldGeometry& getHeightFieldGeometry() const { return *mHfGeom; }
98
99 PX_FORCE_INLINE PxReal getOneOverRowScale() const { return mOneOverRowScale; }
100 PX_FORCE_INLINE PxReal getOneOverHeightScale() const { return mOneOverHeightScale; }
101 PX_FORCE_INLINE PxReal getOneOverColumnScale() const { return mOneOverColumnScale; }
102
103 void computeLocalBounds(PxBounds3& bounds) const;
104
105 PX_FORCE_INLINE PxReal getHeightAtShapePoint(PxReal x, PxReal z) const
106 {
107 return mHfGeom->heightScale * mHeightField->getHeightInternal(x * mOneOverRowScale, z * mOneOverColumnScale);
108 }
109
110 PX_FORCE_INLINE PxVec3 getNormalAtShapePoint(PxReal x, PxReal z) const
111 {
112 return mHeightField->getNormal_(x * mOneOverRowScale, z * mOneOverColumnScale, mOneOverRowScale, mOneOverHeightScale, mOneOverColumnScale);
113 }
114
115 PxU32 getTriangle(const PxTransform&, PxTriangle& worldTri, PxU32* vertexIndices, PxU32* adjacencyIndices, PxTriangleID triangleIndex, bool worldSpaceTranslation=true, bool worldSpaceRotation=true) const;
116
117 void overlapAABBTriangles(const PxBounds3& localBounds, OverlapReport& callback, PxU32 batchSize=HF_OVERLAP_REPORT_BUFFER_SIZE) const;
118
119 PX_FORCE_INLINE void overlapAABBTriangles0to1(const PxTransform& pose0to1, const PxBounds3& bounds0, OverlapReport& callback, PxU32 batchSize=HF_OVERLAP_REPORT_BUFFER_SIZE) const
120 {
121 // PT: TODO: optimize PxBounds3::transformFast
122 //overlapAABBTriangles(PxBounds3::transformFast(pose0to1, bounds0), callback, batchSize);
123 {
124 // PT: below is the equivalent, slightly faster code. Still not optimal but better.
125 // PT: TODO: refactor with GuBounds.cpp
126
127 const PxMat33Padded basis(pose0to1.q);
128
129 // PT: TODO: pass c/e directly
130 const PxBounds3 b = PxBounds3::basisExtent(pose0to1.transform(bounds0.getCenter()), basis, bounds0.getExtents());
131
132 overlapAABBTriangles(b, callback, batchSize);
133 }
134 }
135
136 PX_FORCE_INLINE void overlapAABBTriangles(const PxTransform& pose1, const PxBounds3& bounds0, OverlapReport& callback, PxU32 batchSize=HF_OVERLAP_REPORT_BUFFER_SIZE) const
137 {
138 overlapAABBTriangles0to1(pose1.getInverse(), bounds0, callback, batchSize);
139 }
140
141 PX_FORCE_INLINE void overlapAABBTriangles(const PxTransform& pose0, const PxTransform& pose1, const PxBounds3& bounds0, OverlapReport& callback, PxU32 batchSize=HF_OVERLAP_REPORT_BUFFER_SIZE) const
142 {
143 overlapAABBTriangles0to1(pose1.transformInv(pose0), bounds0, callback, batchSize);
144 }
145
146 PX_FORCE_INLINE PxVec3 hf2shapen(const PxVec3& v) const
147 {
148 return PxVec3(v.x * mOneOverRowScale, v.y * mOneOverHeightScale, v.z * mOneOverColumnScale);
149 }
150
151 PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 shape2hfp(const PxVec3& v) const
152 {
153 return PxVec3(v.x * mOneOverRowScale, v.y * mOneOverHeightScale, v.z * mOneOverColumnScale);
154 }
155
156 PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 hf2shapep(const PxVec3& v) const
157 {
158 return PxVec3(v.x * mHfGeom->rowScale, v.y * mHfGeom->heightScale, v.z * mHfGeom->columnScale);
159 }
160
161 PX_INLINE PxVec3 hf2worldp(const PxTransform& pose, const PxVec3& v) const
162 {
163 const PxVec3 s = hf2shapep(v);
164 return pose.transform(s);
165 }
166
167 PX_INLINE PxVec3 hf2worldn(const PxTransform& pose, const PxVec3& v) const
168 {
169 const PxVec3 s = hf2shapen(v);
170 return pose.q.rotate(s);
171 }
172 };
173
174 class PX_PHYSX_COMMON_API HeightFieldTraceUtil : public HeightFieldUtil
175 {
176 public:
178
179 // floor and ceil don't clamp down exact integers but we want that
180 static PX_FORCE_INLINE PxF32 floorDown(PxF32 x) { PxF32 f = PxFloor(x); return (f == x) ? f-1 : f; }
181 static PX_FORCE_INLINE PxF32 ceilUp (PxF32 x) { PxF32 f = PxCeil (x); return (f == x) ? f+1 : f; }
182
183 // helper class for testing triangle height and reporting the overlapped triangles
184 template<class T>
186 {
187 public:
188 // helper rectangle struct
190 {
191 PxI32 mMinu;
192 PxI32 mMaxu;
193 PxI32 mMinv;
194 PxI32 mMaxv;
195
196 void invalidate()
197 {
198 mMinu = 1;
199 mMaxu = -1;
200 mMinv = 1;
201 mMaxv = -1;
202 }
203 };
204
205 // helper line struct
207 {
208 bool mColumn;
209 PxI32 mLine;
210 PxI32 mMin;
211 PxI32 mMax;
212
213 void invalidate()
214 {
215 mMin = 1;
216 mMax = -1;
217 }
218 };
219
220 public:
221 void operator = (OverlapTraceSegment&) {}
222
224 : mInitialized(false), mHfUtil(hfUtil), mHf(hf), mNbIndices(0) {}
225
226 PX_FORCE_INLINE bool initialized() const { return mInitialized; }
227
228 // prepare for iterations, set the expand u|v
229 PX_INLINE void prepare(const PxVec3& aP0, const PxVec3& aP1, const PxVec3& overlapObjectExtent, PxF32& expandu, PxF32& expandv)
230 {
231 // height test bounds
232 mMinY = (PxMin(aP1.y,aP0.y) - overlapObjectExtent.y) * mHfUtil.getOneOverHeightScale();
233 mMaxY = (PxMax(aP1.y,aP0.y) + overlapObjectExtent.y) * mHfUtil.getOneOverHeightScale();
234
235 // sets the clipping variables
236 mMinRow = PxI32(mHf.getMinRow((PxMin(aP1.x,aP0.x) - overlapObjectExtent.x)* mHfUtil.getOneOverRowScale()));
237 mMaxRow = PxI32(mHf.getMaxRow((PxMax(aP1.x,aP0.x) + overlapObjectExtent.x)* mHfUtil.getOneOverRowScale()));
238 mMinColumn = PxI32(mHf.getMinColumn((PxMin(aP1.z,aP0.z) - overlapObjectExtent.z)* mHfUtil.getOneOverColumnScale()));
239 mMaxColumn = PxI32(mHf.getMaxColumn((PxMax(aP1.z,aP0.z) + overlapObjectExtent.z)* mHfUtil.getOneOverColumnScale()));
240
241 // sets the expanded u|v coordinates
242 expandu = PxCeil(overlapObjectExtent.x*mHfUtil.getOneOverRowScale());
243 expandv = PxCeil(overlapObjectExtent.z*mHfUtil.getOneOverColumnScale());
244
245 // sets the offset that will be overlapped in each axis
246 mOffsetU = PxI32(expandu) + 1;
247 mOffsetV = PxI32(expandv) + 1;
248 }
249
250 // sets all necessary variables and makes initial rectangle setup and overlap
251 PX_INLINE bool init(const PxI32 ui, const PxI32 vi, const PxI32 nbVi, const PxI32 step_ui, const PxI32 step_vi, T* aCallback)
252 {
253 mInitialized = true;
254 mCallback = aCallback;
255 mNumColumns = nbVi;
256 mStep_ui = step_ui > 0 ? 0 : -1;
257 mStep_vi = step_vi > 0 ? 0 : -1;
258
259 // sets the rectangles
260 mCurrentRectangle.invalidate();
261 mPreviousRectangle.mMinu = ui - mOffsetU;
262 mPreviousRectangle.mMaxu = ui + mOffsetU;
263 mPreviousRectangle.mMinv = vi - mOffsetV;
264 mPreviousRectangle.mMaxv = vi + mOffsetV;
265
266 // visits all cells in given initial rectangle
267 if(!visitCells(mPreviousRectangle))
268 return false;
269
270 // reports all overlaps
271 if(!reportOverlaps())
272 return false;
273
274 return true;
275 }
276
277 // u|v changed, check for new rectangle - compare with previous one and parse
278 // the added line, which is a result from the rectangle compare
279 PX_INLINE bool step(const PxI32 ui, const PxI32 vi)
280 {
281 mCurrentRectangle.mMinu = ui - mOffsetU;
282 mCurrentRectangle.mMaxu = ui + mOffsetU;
283 mCurrentRectangle.mMinv = vi - mOffsetV;
284 mCurrentRectangle.mMaxv = vi + mOffsetV;
285 OverlapLine line;
286 computeRectangleDifference(mCurrentRectangle,mPreviousRectangle,line);
287
288 if(!visitCells(line))
289 return false;
290 if(!reportOverlaps())
291 return false;
292
293 mPreviousRectangle = mCurrentRectangle;
294 return true;
295 }
296
297 PX_INLINE void computeRectangleDifference(const OverlapRectangle& currentRectangle, const OverlapRectangle& previousRectangle, OverlapLine& line)
298 {
299 // check if u changes - add the row for visit
300 if(currentRectangle.mMinu != previousRectangle.mMinu)
301 {
302 line.mColumn = false;
303 line.mLine = currentRectangle.mMinu < previousRectangle.mMinu ? currentRectangle.mMinu : currentRectangle.mMaxu;
304 line.mMin = currentRectangle.mMinv;
305 line.mMax = currentRectangle.mMaxv;
306 return;
307 }
308
309 // check if v changes - add the column for visit
310 if(currentRectangle.mMinv != previousRectangle.mMinv)
311 {
312 line.mColumn = true;
313 line.mLine = currentRectangle.mMinv < previousRectangle.mMinv ? currentRectangle.mMinv : currentRectangle.mMaxv;
314 line.mMin = currentRectangle.mMinu;
315 line.mMax = currentRectangle.mMaxu;
316 }
317 }
318
319 // visits all cells in given rectangle
320 PX_INLINE bool visitCells(const OverlapRectangle& rectangle)
321 {
322 for(PxI32 ui = rectangle.mMinu + mStep_ui; ui <= rectangle.mMaxu + mStep_ui; ui++)
323 {
324 if(ui < mMinRow)
325 continue;
326 if(ui >= mMaxRow)
327 break;
328 for(PxI32 vi = rectangle.mMinv + mStep_vi; vi <= rectangle.mMaxv + mStep_vi; vi++)
329 {
330 if(vi < mMinColumn)
331 continue;
332 if(vi >= mMaxColumn)
333 break;
334 const PxI32 vertexIndex = ui*mNumColumns + vi;
335 if(!testVertexIndex(PxU32(vertexIndex)))
336 return false;
337 }
338 }
339 return true;
340 }
341
342 // visits all cells in given line - can be row or column
343 PX_INLINE bool visitCells(const OverlapLine& line)
344 {
345 if(line.mMin > line.mMax)
346 return true;
347
348 if(line.mColumn)
349 {
350 const PxI32 vi = line.mLine + mStep_vi;
351 // early exit if column is out of hf clip area
352 if(vi < mMinColumn)
353 return true;
354 if(vi >= mMaxColumn)
355 return true;
356
357 for(PxI32 ui = line.mMin + mStep_ui; ui <= line.mMax + mStep_ui; ui++)
358 {
359 // early exit or continue if row is out of hf clip area
360 if(ui >= mMaxRow)
361 break;
362 // continue if we did not reach the valid area, we can still get there
363 if(ui < mMinRow)
364 continue;
365 // if the cell has not been tested test and report
366 if(!testVertexIndex(PxU32(mNumColumns * ui + vi)))
367 return false;
368 }
369 }
370 else
371 {
372 const PxI32 ui = line.mLine + mStep_ui;
373 // early exit if row is out of hf clip area
374 if(ui < mMinRow)
375 return true;
376 if(ui >= mMaxRow)
377 return true;
378
379 for(PxI32 vi = line.mMin + mStep_vi; vi <= line.mMax + mStep_vi; vi++)
380 {
381 // early exit or continue if column is out of hf clip area
382 if(vi >= mMaxColumn)
383 break;
384 // continue if we did not reach the valid area, we can still get there
385 if(vi < mMinColumn)
386 continue;
387 // if the cell has not been tested test and report
388 if(!testVertexIndex(PxU32(mNumColumns * ui + vi)))
389 return false;
390 }
391 }
392 return true;
393 }
394
395 // does height check and if succeeded adds to report
396 PX_INLINE bool testVertexIndex(const PxU32 vertexIndex)
397 {
398 const PxReal h0 = mHf.getHeight(vertexIndex);
399 const PxReal h1 = mHf.getHeight(vertexIndex + 1);
400 const PxReal h2 = mHf.getHeight(vertexIndex + mNumColumns);
401 const PxReal h3 = mHf.getHeight(vertexIndex + mNumColumns + 1);
402 // actual height test, if some height pass we accept the cell
403 if(!((mMaxY < h0 && mMaxY < h1 && mMaxY < h2 && mMaxY < h3) || (mMinY > h0 && mMinY > h1 && mMinY > h2 && mMinY > h3)))
404 {
405 // check if the triangle is not a hole
406 if(mHf.getMaterialIndex0(vertexIndex) != PxHeightFieldMaterial::eHOLE)
407 {
408 if(!addIndex(vertexIndex*2))
409 return false;
410 }
411 if(mHf.getMaterialIndex1(vertexIndex) != PxHeightFieldMaterial::eHOLE)
412 {
413 if(!addIndex(vertexIndex*2 + 1))
414 return false;
415 }
416 }
417 return true;
418 }
419
420 // add triangle index, if we get out of buffer size, report them
421 bool addIndex(PxU32 triangleIndex)
422 {
423 if(mNbIndices == HF_SWEEP_REPORT_BUFFER_SIZE)
424 {
425 if(!reportOverlaps())
426 return false;
427 }
428
429 mIndexBuffer[mNbIndices++] = triangleIndex;
430 return true;
431 }
432
433 PX_FORCE_INLINE bool reportOverlaps()
434 {
435 if(mNbIndices)
436 {
437 if(!mCallback->onEvent(mNbIndices, mIndexBuffer))
438 return false;
439 mNbIndices = 0;
440 }
441 return true;
442 }
443
444 private:
445 bool mInitialized;
446 const HeightFieldUtil& mHfUtil;
447 const Gu::HeightField& mHf;
448 T* mCallback;
449 PxI32 mOffsetU;
450 PxI32 mOffsetV;
451 float mMinY;
452 float mMaxY;
453 PxI32 mMinRow;
454 PxI32 mMaxRow;
455 PxI32 mMinColumn;
456 PxI32 mMaxColumn;
457 PxI32 mNumColumns;
458 PxI32 mStep_ui;
459 PxI32 mStep_vi;
460 OverlapRectangle mPreviousRectangle;
461 OverlapRectangle mCurrentRectangle;
462 PxU32 mIndexBuffer[HF_SWEEP_REPORT_BUFFER_SIZE];
463 PxU32 mNbIndices;
464 };
465
466 // If useUnderFaceCalblack is false, traceSegment will report segment/triangle hits via
467 // faceHit(const Gu::HeightFieldUtil& hf, const PxVec3& point, PxU32 triangleIndex)
468 // Otherwise traceSegment will report all triangles the segment passes under via
469 // underFaceHit(const Gu::HeightFieldUtil& hf, const PxVec3& triNormal, const PxVec3& crossedEdge,
470 // PxF32 x, PxF32 z, PxF32 rayHeight, PxU32 triangleIndex)
471 // where x,z is the point of previous intercept in hf coords, rayHeight is at that same point
472 // crossedEdge is the edge vector crossed from last call to underFaceHit, undefined for first call
473 // Note that underFaceHit can be called when a line is above a triangle if it's within AABB for that hf cell
474 // Note that backfaceCull is ignored if useUnderFaceCallback is true
475 // overlapObjectExtent (localSpace) and overlap are used for triangle collecting using an inflated tracesegment
476 // Note that hfLocalBounds are passed as a parameter instead of being computed inside the traceSegment.
477 // The localBounds can be obtained: PxBounds3 hfLocalBounds; hfUtil.computeLocalBounds(hfLocalBounds); and passed as
478 // a parameter.
479 template<class T, bool useUnderFaceCallback, bool overlap>
480 PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& rayDir, const float rayLength , T* aCallback, const PxBounds3& hfLocalBounds, bool backfaceCull,
481 const PxVec3* overlapObjectExtent = NULL) const
482 {
483 PxF32 tnear, tfar;
484 if(!Gu::intersectRayAABB2(hfLocalBounds.minimum, hfLocalBounds.maximum, aP0, rayDir, rayLength, tnear, tfar))
485 return;
486
487 const PxVec3 p0 = aP0 + rayDir * tnear;
488 const PxVec3 p1 = aP0 + rayDir * tfar;
489
490 // helper class used for overlap tests
491 OverlapTraceSegment<T> overlapTraceSegment(*this, *mHeightField);
492
493 // values which expand the HF area
494 PxF32 expandu = 0.0f, expandv = 0.0f;
495
496 if (overlap)
497 {
498 // setup overlap variables
499 overlapTraceSegment.prepare(aP0,aP0 + rayDir*rayLength,*overlapObjectExtent,expandu,expandv);
500 }
501
502 // row = x|u, column = z|v
503 const PxF32 rowScale = mHfGeom->rowScale, columnScale = mHfGeom->columnScale, heightScale = mHfGeom->heightScale;
504 const PxI32 nbVi = PxI32(mHeightField->getNbColumnsFast()), nbUi = PxI32(mHeightField->getNbRowsFast());
505 PX_ASSERT(nbVi > 0 && nbUi > 0);
506
507 // clampEps is chosen so that we get a reasonable clamp value for 65536*0.9999999f = 65535.992187500000
508 const PxF32 clampEps = 1e-7f; // shrink u,v to within 1e-7 away from the world bounds
509
510 // we now clamp uvs to [1e-7, rowLimit-1e-7] to avoid out of range uvs and eliminate related checks in the loop
511 const PxF32 nbUcells = PxF32(nbUi-1)*(1.0f-clampEps), nbVcells = PxF32(nbVi-1)*(1.0f-clampEps);
512
513 // if u0,v0 is near an integer, shift up or down in direction opposite to du,dv by PxMax(|u,v|*1e-7, 1e-7)
514 // (same direction as du,dv for u1,v1)
515 // we do this to ensure that we get at least one intersection with u or v when near the cell edge to eliminate special cases in the loop
516 // we need to extend the field for the inflated radius, we will now operate even with negative u|v
517
518 // map p0 from (x, z, y) to (u0, v0, h0)
519 // we need to use the unclamped values, otherwise we change the direction of the traversal
520 const PxF32 uu0 = p0.x * mOneOverRowScale;
521 PxF32 u0 = PxMin(PxMax(uu0, 1e-7f - expandu), nbUcells + expandu); // multiplication rescales the u,v grid steps to 1
522 const PxF32 uv0 = p0.z * mOneOverColumnScale;
523 PxF32 v0 = PxMin(PxMax(uv0, 1e-7f - expandv), nbVcells + expandv);
524 const PxReal h0 = p0.y; // we don't scale y
525
526 // map p1 from (x, z, y) to (u1, v1, h1)
527 // we need to use the unclamped values, otherwise we change the direction of the traversal
528 const PxF32 uu1 = p1.x * mOneOverRowScale;
529 const PxF32 uv1 = p1.z * mOneOverColumnScale;
530 const PxReal h1 = p1.y; // we don't scale y
531
532 PxF32 du = uu1 - uu0, dv = uv1 - uv0; // recompute du, dv from adjusted uvs
533 const PxReal dh = h1 - h0;
534
535 // grid u&v step is always either 1 or -1, we precompute as both integers and floats to avoid conversions
536 // so step_uif is +/-1.0f, step_ui is +/-1
537 const PxF32 step_uif = PxSign(du), step_vif = PxSign(dv);
538 const PxI32 step_ui = PxI32(step_uif), step_vi = PxI32(step_vif);
539
540 // clamp magnitude of du, dv to at least clampEpsilon to avoid special cases when dividing
541 const PxF32 divEpsilon = 1e-10f;
542 if(PxAbs(du) < divEpsilon)
543 du = step_uif * divEpsilon;
544 if(PxAbs(dv) < divEpsilon)
545 dv = step_vif * divEpsilon;
546
547 const PxVec3 auhP0(aP0.x*mOneOverRowScale, aP0.y, aP0.z*mOneOverColumnScale);
548 const PxVec3 duhv(rayDir.x*rayLength*mOneOverRowScale, rayDir.y*rayLength, rayDir.z*rayLength*mOneOverColumnScale);
549 const PxReal duhvLength = duhv.magnitude();
550 PxVec3 duhvNormalized = duhv;
551 if(duhvLength > PX_NORMALIZATION_EPSILON)
552 duhvNormalized *= 1.0f/duhvLength;
553
554 // Math derivation:
555 // points on 2d segment are parametrized as: [u0,v0] + t [du, dv]. We solve for t_u[n], t for nth u-intercept
556 // u0 + t_un du = un
557 // t_un = (un-u0) / du
558 // t_un1 = (un+1-u0) / du ; we use +1 since we rescaled the grid step to 1
559 // therefore step_tu = t_un - t_un1 = 1/du
560
561 // seed the initial integer cell coordinates with u0, v0 rounded up or down with standard PxFloor/Ceil behavior
562 // to ensure we have the correct first cell between (ui,vi) and (ui+step_ui,vi+step_vi)
563 PxI32 ui = (du > 0.0f) ? PxI32(PxFloor(u0)) : PxI32(PxCeil(u0));
564 PxI32 vi = (dv > 0.0f) ? PxI32(PxFloor(v0)) : PxI32(PxCeil(v0));
565
566 // find the nearest integer u, v in ray traversal direction and corresponding tu and tv
567 const PxReal uhit0 = du > 0.0f ? ceilUp(u0) : floorDown(u0);
568 const PxReal vhit0 = dv > 0.0f ? ceilUp(v0) : floorDown(v0);
569
570 // tu, tv can be > 1 but since the loop is structured as do {} while(tMin < tEnd) we still visit the first cell
571 PxF32 last_tu = 0.0f, last_tv = 0.0f;
572 PxReal tu = (uhit0 - uu0) / du;
573 PxReal tv = (vhit0 - uv0) / dv;
574 if(tu < 0.0f) // negative value may happen, as we may have started out of the AABB (since we did enlarge it)
575 tu = PxAbs(clampEps / du);
576 if(tv < 0.0f) // negative value may happen, as we may have started out of the AABB (since we did enlarge it)
577 tv = PxAbs(clampEps / dv);
578
579 // compute step_tu and step_tv; t steps per grid cell in u and v direction
580 const PxReal step_tu = 1.0f / PxAbs(du), step_tv = 1.0f / PxAbs(dv);
581
582 // t advances at the same rate for u, v and h therefore we can compute h at u,v grid intercepts
583 #define COMPUTE_H_FROM_T(t) (h0 + (t) * dh)
584
585 const PxF32 hEpsilon = 1e-4f;
586 PxF32 uif = PxF32(ui), vif = PxF32(vi);
587
588 // these are used to remap h values to correspond to u,v increasing order
589 PxI32 uflip = 1-step_ui; /*0 or 2*/
590 PxI32 vflip = (1-step_vi)/2; /*0 or 1*/
591
592 // this epsilon is needed to ensure that we include the last [t, t+1] range in the do {} while(t<tEnd) loop
593 // A.B. in case of overlap we do miss actually a line with this epsilon, should it not be +?
594 PxF32 tEnd = 1.0f - 1e-4f;
595 if(overlap)
596 tEnd = 1.0f + 1e-4f;
597 PxF32 tMinUV;
598
599 const Gu::HeightField& hf = *mHeightField;
600
601 // seed hLinePrev as h(0)
602 PxReal hLinePrev = COMPUTE_H_FROM_T(0);
603
604 do
605 {
606 tMinUV = PxMin(tu, tv); // determine where next closest u or v-intercept point is
607 PxF32 hLineNext = COMPUTE_H_FROM_T(tMinUV); // compute the corresponding h
608
609 // the operating u|v space has been extended by expandu|expandv if inflation is used
610 PX_ASSERT(ui >= 0 - expandu && ui < nbUi + expandu && vi >= 0 - expandv && vi < nbVi + expandv);
611 PX_ASSERT(ui+step_ui >= 0 - expandu && ui+step_ui < nbUi + expandu && vi+step_vi >= 0 - expandv && vi+step_vi < nbVi + expandv);
612
613 // handle overlap in overlapCallback
614 if(overlap)
615 {
616 if(!overlapTraceSegment.initialized())
617 {
618 // initial overlap and setup
619 if(!overlapTraceSegment.init(ui,vi,nbVi,step_ui,step_vi,aCallback))
620 return;
621 }
622 else
623 {
624 // overlap step
625 if(!overlapTraceSegment.step(ui,vi))
626 return;
627 }
628 }
629 else
630 {
631 const PxU32 colIndex0 = PxU32(nbVi * ui + vi);
632 const PxU32 colIndex1 = PxU32(nbVi * (ui + step_ui) + vi);
633 const PxReal h[4] = { // h[0]=h00, h[1]=h01, h[2]=h10, h[3]=h11 - oriented relative to step_uv
634 hf.getHeight(colIndex0) * heightScale, hf.getHeight(colIndex0 + step_vi) * heightScale,
635 hf.getHeight(colIndex1) * heightScale, hf.getHeight(colIndex1 + step_vi) * heightScale };
636
637 PxF32 minH = PxMin(PxMin(h[0], h[1]), PxMin(h[2], h[3]));
638 PxF32 maxH = PxMax(PxMax(h[0], h[1]), PxMax(h[2], h[3]));
639
640 // how much space in h have we covered from previous to current u or v intercept
641 PxF32 hLineCellRangeMin = PxMin(hLinePrev, hLineNext);
642 PxF32 hLineCellRangeMax = PxMax(hLinePrev, hLineNext);
643
644 // do a quick overlap test in h, this should be rejecting the vast majority of tests
645 if(!(hLineCellRangeMin-hEpsilon > maxH || hLineCellRangeMax+hEpsilon < minH) ||
646 (useUnderFaceCallback && hLineCellRangeMax < maxH))
647 {
648 // arrange h so that h00 corresponds to min(uif, uif+step_uif) h10 to max et c.
649 // this is only needed for backface culling to work so we know the proper winding order without branches
650 // uflip is 0 or 2, vflip is 0 or 1 (corresponding to positive and negative ui_step and vi_step)
651 const PxF32 h00 = h[0+uflip+vflip];
652 const PxF32 h01 = h[1+uflip-vflip];
653 const PxF32 h10 = h[2-uflip+vflip];
654 const PxF32 h11 = h[3-uflip-vflip];
655
656 const PxF32 minuif = PxMin(uif, uif+step_uif);
657 const PxF32 maxuif = PxMax(uif, uif+step_uif);
658 const PxF32 minvif = PxMin(vif, vif+step_vif);
659 const PxF32 maxvif = PxMax(vif, vif+step_vif);
660 const PxVec3 p00(minuif, h00, minvif);
661 const PxVec3 p01(minuif, h01, maxvif);
662 const PxVec3 p10(maxuif, h10, minvif);
663 const PxVec3 p11(maxuif, h11, maxvif);
664
665 const PxF32 enlargeEpsilon = 0.0001f;
666 const PxVec3* p00a = &p00, *p01a = &p01, *p10a = &p10, *p11a = &p11;
667 PxU32 minui = PxU32(PxMin(ui+step_ui, ui)), minvi = PxU32(PxMin(vi+step_vi, vi));
668
669 // row = x|u, column = z|v
670 const PxU32 vertIndex = nbVi * minui + minvi;
671 const PxU32 cellIndex = vertIndex; // this adds a dummy unused cell in the end of each row; was -minui
672 bool isZVS = hf.isZerothVertexShared(vertIndex);
673 if(!isZVS)
674 {
675 // rotate the pointers for flipped edge cells
676 p10a = &p00;
677 p00a = &p01;
678 p01a = &p11;
679 p11a = &p10;
680 }
681
682 // For triangle index computation, see illustration in Gu::HeightField::getTriangleNormal()
683 // Since row = u, column = v
684 // for zeroth vert shared the 10 index is the corner of the 0-index triangle, and 01 is 1-index
685 // if zeroth vertex is not shared, the 00 index is the corner of 0-index triangle
686 if(!useUnderFaceCallback)
687 {
688 PxReal triT0 = PX_MAX_REAL, triT1 = PX_MAX_REAL;
689 bool hit0 = false, hit1 = false;
690 PxF32 triU0, triV0, triU1, triV1;
691
692 // PT: TODO: consider testing hole first and skipping ray-tri test. Might be faster.
693 if(Gu::intersectRayTriangle(auhP0, duhvNormalized, *p10a, *p00a, *p11a, triT0, triU0, triV0, backfaceCull, enlargeEpsilon) && triT0 >= 0.0f && triT0 <= duhvLength && (hf.getMaterialIndex0(vertIndex) != PxHeightFieldMaterial::eHOLE))
694 {
695 hit0 = true;
696 }
697 else
698 triT0 = PX_MAX_REAL;
699
700 if(Gu::intersectRayTriangle(auhP0, duhvNormalized, *p01a, *p11a, *p00a, triT1, triU1, triV1, backfaceCull, enlargeEpsilon) && triT1 >= 0.0f && triT1 <= duhvLength && (hf.getMaterialIndex1(vertIndex) != PxHeightFieldMaterial::eHOLE))
701 {
702 hit1 = true;
703 }
704 else
705 triT1 = PX_MAX_REAL;
706
707 if(hit0 && triT0 <= triT1)
708 {
709 const PxVec3 hitPoint((auhP0.x + duhvNormalized.x*triT0) * rowScale, auhP0.y + duhvNormalized.y * triT0, (auhP0.z + duhvNormalized.z*triT0) * columnScale);
710 if(!aCallback->faceHit(*this, hitPoint, cellIndex*2, triU0, triV0))
711 return;
712 if(hit1) // possible to hit both triangles in a cell with eMESH_MULTIPLE
713 {
714 PxVec3 hitPoint1((auhP0.x + duhvNormalized.x*triT1) * rowScale, auhP0.y + duhvNormalized.y * triT1, (auhP0.z + duhvNormalized.z*triT1) * columnScale);
715 if(!aCallback->faceHit(*this, hitPoint1, cellIndex*2 + 1, triU1, triV1))
716 return;
717 }
718 }
719 else if(hit1 && triT1 <= triT0)
720 {
721 PxVec3 hitPoint((auhP0.x + duhvNormalized.x*triT1) * rowScale, auhP0.y + duhvNormalized.y * triT1, (auhP0.z + duhvNormalized.z*triT1) * columnScale);
722 if(!aCallback->faceHit(*this, hitPoint, cellIndex*2 + 1, triU1, triV1))
723 return;
724 if(hit0) // possible to hit both triangles in a cell with eMESH_MULTIPLE
725 {
726 PxVec3 hitPoint1((auhP0.x + duhvNormalized.x*triT0) * rowScale, auhP0.y + duhvNormalized.y * triT0, (auhP0.z + duhvNormalized.z*triT0) * columnScale);
727 if(!aCallback->faceHit(*this, hitPoint1, cellIndex*2, triU0, triV0))
728 return;
729 }
730 }
731 }
732 else
733 {
734 // TODO: quite a few optimizations are possible here. edges can be shared, intersectRayTriangle inlined etc
735 // Go to shape space. Height is already in shape space so we only scale x and z
736 const PxVec3 p00s(p00a->x * rowScale, p00a->y, p00a->z * columnScale);
737 const PxVec3 p01s(p01a->x * rowScale, p01a->y, p01a->z * columnScale);
738 const PxVec3 p10s(p10a->x * rowScale, p10a->y, p10a->z * columnScale);
739 const PxVec3 p11s(p11a->x * rowScale, p11a->y, p11a->z * columnScale);
740
741 PxVec3 triNormals[2] = { (p00s - p10s).cross(p11s - p10s), (p11s - p01s).cross(p00s-p01s) };
742 triNormals[0] *= PxRecipSqrt(triNormals[0].magnitudeSquared());
743 triNormals[1] *= PxRecipSqrt(triNormals[1].magnitudeSquared());
744 // since the heightfield can be mirrored with negative rowScale or columnScale, this assert doesn't hold
745 //PX_ASSERT(triNormals[0].y >= 0.0f && triNormals[1].y >= 0.0f);
746
747 // at this point we need to compute the edge direction that we crossed
748 // also since we don't DDA the w we need to find u,v for w-intercept (w refers to diagonal adjusted with isZVS)
749 const PxF32 wnu = isZVS ? -1.0f : 1.0f, wnv = 1.0f; // uv-normal to triangle edge that splits the cell
750 const PxF32 wpu = uif + 0.5f * step_uif, wpv = vif + 0.5f * step_vif; // a point on triangle edge that splits the cell
751 // note that (wpu, wpv) is on both edges (for isZVS and non-ZVS cases) which is nice
752
753 // we clamp tNext to 1 because we still want to issue callbacks even if we stay in one cell
754 // note that tNext can potentially be arbitrarily large for a segment contained within a cell
755 const PxF32 tNext = PxMin(PxMin(tu, tv), 1.0f), tPrev = PxMax(last_tu, last_tv);
756
757 // compute uvs corresponding to tPrev, tNext
758 const PxF32 unext = u0 + tNext*du, vnext = v0 + tNext*dv;
759 const PxF32 uprev = u0 + tPrev*du, vprev = v0 + tPrev*dv;
760
761 const PxReal& h00_ = h[0], &h01_ = h[1], &h10_ = h[2]/*, h11_ = h[3]*/; // aliases for step-oriented h
762
763 // (wpu, wpv) is a point on the diagonal
764 // we compute a dot of ((unext, vnext) - (wpu, wpv), wn) to see on which side of triangle edge we are
765 // if the dot is positive we need to add 1 to triangle index
766 const PxU32 dotPrevGtz = PxU32(((uprev - wpu) * wnu + (vprev - wpv) * wnv) > 0);
767 const PxU32 dotNextGtz = PxU32(((unext - wpu) * wnu + (vnext - wpv) * wnv) > 0);
768 const PxU32 triIndex0 = cellIndex*2 + dotPrevGtz;
769 const PxU32 triIndex1 = cellIndex*2 + dotNextGtz;
770 PxU32 isHole0 = PxU32(hf.getMaterialIndex0(vertIndex) == PxHeightFieldMaterial::eHOLE);
771 PxU32 isHole1 = PxU32(hf.getMaterialIndex1(vertIndex) == PxHeightFieldMaterial::eHOLE);
772 if(triIndex0 > triIndex1)
773 PxSwap<PxU32>(isHole0, isHole1);
774
775 // TODO: compute height at u,v inside here, change callback param to PxVec3
776 PxVec3 crossedEdge;
777 if(last_tu > last_tv) // previous intercept was at u, so we use u=const edge
778 crossedEdge = PxVec3(0.0f, h01_-h00_, step_vif * columnScale);
779 else // previous intercept at v, use v=const edge
780 crossedEdge = PxVec3(step_uif * rowScale, h10_-h00_, 0.0f);
781
782 if(!isHole0 && !aCallback->underFaceHit(*this, triNormals[dotPrevGtz], crossedEdge,
783 uprev * rowScale, vprev * columnScale, COMPUTE_H_FROM_T(tPrev), triIndex0))
784 return;
785
786 if(triIndex1 != triIndex0 && !isHole1) // if triIndex0 != triIndex1 that means we cross the triangle edge
787 {
788 // Need to compute tw, the t for ray intersecting the diagonal within the current cell
789 // dot((wnu, wnv), (u0+tw*du, v0+tw*dv)-(wpu, wpv)) = 0
790 // wnu*(u0+tw*du-wpu) + wnv*(v0+tw*dv-wpv) = 0
791 // wnu*u0+wnv*v0-wnu*wpu-wnv*wpv + tw*(du*wnu + dv*wnv) = 0
792 const PxF32 denom = du*wnu + dv*wnv;
793 if(PxAbs(denom) > 1e-6f)
794 {
795 const PxF32 tw = (wnu*(wpu-u0)+wnv*(wpv-v0)) / denom;
796 if(!aCallback->underFaceHit(*this, triNormals[dotNextGtz], p10s-p01s,
797 (u0+tw*du) * rowScale, (v0+tw*dv) * columnScale, COMPUTE_H_FROM_T(tw), triIndex1))
798 return;
799 }
800 }
801 }
802 }
803 }
804
805 if(tu < tv)
806 {
807 last_tu = tu;
808 ui += step_ui;
809 // AP: very rare condition, wasn't able to repro but we need this if anyway (DE6565)
810 if(ui+step_ui< (0 - expandu) || ui+step_ui>=(nbUi + expandu)) // should hold true for ui without step from previous iteration
811 break;
812 uif += step_uif;
813 tu += step_tu;
814 }
815 else
816 {
817 last_tv = tv;
818 vi += step_vi;
819 // AP: very rare condition, wasn't able to repro but we need this if anyway (DE6565)
820 if(vi+step_vi< (0 - expandv) || vi+step_vi>=(nbVi + expandv)) // should hold true for vi without step from previous iteration
821 break;
822 vif += step_vif;
823 tv += step_tv;
824 }
825 hLinePrev = hLineNext;
826 }
827 // since min(tu,tv) is the END of the active interval we need to check if PREVIOUS min(tu,tv) was past interval end
828 // since we update tMinUV in the beginning of the loop, at this point it stores the min(last tu,last tv)
829 while (tMinUV < tEnd);
830 #undef COMPUTE_H_FROM_T
831 }
832 };
833
834} // namespace Gu
835
836}
837
838#endif
Definition GuHeightFieldUtil.h:175
Definition GuHeightFieldUtil.h:69
Definition GuHeightField.h:54
Definition GuEntityReport.h:48
Class representing 3D range or axis aligned bounding box.
Definition PxBounds3.h:58
PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getCenter() const
returns the center of this axis aligned box.
Definition PxBounds3.h:396
PX_CUDA_CALLABLE PX_FORCE_INLINE float getExtents(uint32_t axis) const
get component of the box's extents along a given axis
Definition PxBounds3.h:408
Height field geometry class.
Definition PxHeightFieldGeometry.h:55
PxReal heightScale
The scaling factor for the height field in vertical direction (y direction in local space).
Definition PxHeightFieldGeometry.h:123
PxHeightField * heightField
The height field data.
Definition PxHeightFieldGeometry.h:118
PxReal columnScale
The scaling factor for the height field in the column direction (z direction in local space).
Definition PxHeightFieldGeometry.h:133
PxReal rowScale
The scaling factor for the height field in the row direction (x direction in local space).
Definition PxHeightFieldGeometry.h:128
A padded version of PxMat33, to safely load its data using SIMD.
Definition PxSIMDHelpers.h:41
PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3 rotate(const PxVec3 &v) const
Definition PxQuat.h:287
class representing a rigid euclidean transform as a quaternion and a vector
Definition PxTransform.h:49
Triangle class.
Definition PxTriangle.h:47
3 Element vector class.
Definition PxVec3.h:50
GLM_FUNC_QUALIFIER vec< 3, T, Q > cross(vec< 3, T, Q > const &x, vec< 3, T, Q > const &y)
Definition func_geometric.inl:175
#define PX_FORCE_INLINE
Definition PxPreprocessor.h:335
#define PX_COMPILE_TIME_ASSERT(exp)
Definition PxPreprocessor.h:428
#define PX_INLINE
Definition PxPreprocessor.h:320
Sorts an array of objects in ascending order, assuming that the predicate implements the < operator:
Definition PxBoxController.h:39
PX_CUDA_CALLABLE PX_FORCE_INLINE float PxAbs(float a)
abs returns the absolute value of its argument.
Definition PxMath.h:109
PX_CUDA_CALLABLE PX_FORCE_INLINE float PxRecipSqrt(float a)
reciprocal square root.
Definition PxMath.h:158
PX_CUDA_CALLABLE PX_FORCE_INLINE T PxMax(T a, T b)
The return value is the greater of the two specified values.
Definition PxMath.h:72
PX_CUDA_CALLABLE PX_FORCE_INLINE T PxMin(T a, T b)
The return value is the lesser of the two specified values.
Definition PxMath.h:88
Heightfield sample format.
Definition PxHeightFieldSample.h:67