aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Source/GeomUtils/src/hf
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/GeomUtils/src/hf
downloadphysx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz
physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip
Initial commit:
PhysX 3.4.0 Update @ 21294896 APEX 1.4.0 Update @ 21275617 [CL 21300167]
Diffstat (limited to 'PhysX_3.4/Source/GeomUtils/src/hf')
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/hf/GuEntityReport.h56
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.cpp814
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.h1520
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldData.h88
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.cpp1055
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.h1532
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/hf/GuOverlapTestsHF.cpp768
-rw-r--r--PhysX_3.4/Source/GeomUtils/src/hf/GuSweepsHF.cpp604
8 files changed, 6437 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuEntityReport.h b/PhysX_3.4/Source/GeomUtils/src/hf/GuEntityReport.h
new file mode 100644
index 00000000..5a423f3b
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuEntityReport.h
@@ -0,0 +1,56 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved.
+// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
+// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
+
+#ifndef GU_ENTITY_REPORT_H
+#define GU_ENTITY_REPORT_H
+
+#include "Ps.h"
+#include "PxQueryReport.h"
+
+namespace physx
+{
+namespace Gu
+{
+
+template<class T>
+class EntityReport
+{
+ public:
+
+ virtual ~EntityReport() {}
+
+ virtual PxAgain onEvent(PxU32 nbEntities, T* entities) = 0;
+};
+
+
+} // namespace Gu
+
+}
+
+#endif
diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.cpp b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.cpp
new file mode 100644
index 00000000..d376715c
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.cpp
@@ -0,0 +1,814 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved.
+// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
+// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
+
+
+#include "foundation/PxMemory.h"
+#include "PsIntrinsics.h"
+#include "GuHeightField.h"
+#include "PsAllocator.h"
+#include "PsUtilities.h"
+#include "GuMeshFactory.h"
+#include "GuSerialize.h"
+#include "CmUtils.h"
+#include "CmBitMap.h"
+#include "PsFoundation.h"
+
+using namespace physx;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+Gu::HeightField::HeightField(GuMeshFactory* meshFactory)
+: PxHeightField(PxConcreteType::eHEIGHTFIELD, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE)
+, mSampleStride (0)
+, mNbSamples (0)
+, mMinHeight (0.0f)
+, mMaxHeight (0.0f)
+, mModifyCount (0)
+, mMeshFactory (meshFactory)
+{
+ mData.format = PxHeightFieldFormat::eS16_TM;
+ mData.rows = 0;
+ mData.columns = 0;
+ mData.convexEdgeThreshold = 0;
+ mData.flags = PxHeightFieldFlags();
+ mData.samples = NULL;
+ mData.thickness = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+Gu::HeightField::HeightField(GuMeshFactory& factory, Gu::HeightFieldData& data)
+: PxHeightField(PxConcreteType::eHEIGHTFIELD, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE)
+, mSampleStride (0)
+, mNbSamples (0)
+, mMinHeight (0.0f)
+, mMaxHeight (0.0f)
+, mModifyCount (0)
+, mMeshFactory (&factory)
+{
+ mData = data;
+ data.samples = NULL; // set to null so that we don't release the memory
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+Gu::HeightField::~HeightField()
+{
+ releaseMemory();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// PX_SERIALIZATION
+void Gu::HeightField::onRefCountZero()
+{
+ PX_ASSERT(mMeshFactory);
+ if(mMeshFactory->removeHeightField(*this))
+ {
+ GuMeshFactory* mf = mMeshFactory;
+ Cm::deletePxBase(this);
+ mf->notifyFactoryListener(this, PxConcreteType::eHEIGHTFIELD);
+ return;
+ }
+
+ // PT: if we reach this point, we didn't find the mesh in the Physics object => don't delete!
+ // This prevents deleting the object twice.
+ Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Gu::HeightField::onRefCountZero: double deletion detected!");
+}
+
+void Gu::HeightField::exportExtraData(PxSerializationContext& stream)
+{
+ // PT: warning, order matters for the converter. Needs to export the base stuff first
+ const PxU32 size = mData.rows * mData.columns * sizeof(PxHeightFieldSample);
+ stream.alignData(PX_SERIAL_ALIGN); // PT: generic align within the generic allocator
+ stream.writeData(mData.samples, size);
+}
+
+void Gu::HeightField::importExtraData(PxDeserializationContext& context)
+{
+ mData.samples = context.readExtraData<PxHeightFieldSample, PX_SERIAL_ALIGN>(mData.rows * mData.columns);
+}
+
+Gu::HeightField* Gu::HeightField::createObject(PxU8*& address, PxDeserializationContext& context)
+{
+ HeightField* obj = new (address) HeightField(PxBaseFlag::eIS_RELEASABLE);
+ address += sizeof(HeightField);
+ obj->importExtraData(context);
+ obj->resolveReferences(context);
+ return obj;
+}
+
+//~PX_SERIALIZATION
+
+void Gu::HeightField::release()
+{
+ decRefCount();
+}
+
+void Gu::HeightField::acquireReference()
+{
+ incRefCount();
+}
+
+PxU32 Gu::HeightField::getReferenceCount() const
+{
+ return getRefCount();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool Gu::HeightField::modifySamples(PxI32 startCol, PxI32 startRow, const PxHeightFieldDesc& desc, bool shrinkBounds)
+{
+ const PxU32 nbCols = getNbColumns();
+ const PxU32 nbRows = getNbRows();
+ PX_CHECK_AND_RETURN_NULL(desc.format == mData.format, "Gu::HeightField::modifySamples: desc.format mismatch");
+ //PX_CHECK_AND_RETURN_NULL(startCol + desc.nbColumns <= nbCols,
+ // "Gu::HeightField::modifySamples: startCol + nbColumns out of range");
+ //PX_CHECK_AND_RETURN_NULL(startRow + desc.nbRows <= nbRows,
+ // "Gu::HeightField::modifySamples: startRow + nbRows out of range");
+ //PX_CHECK_AND_RETURN_NULL(desc.samples.stride == mSampleStride, "Gu::HeightField::modifySamples: desc.samples.stride mismatch");
+
+ // by default bounds don't shrink since the whole point of this function is to avoid modifying the whole HF
+ // unless shrinkBounds is specified. then the bounds will be fully recomputed later
+ PxReal minHeight = mMinHeight;
+ PxReal maxHeight = mMaxHeight;
+ PxU32 hiRow = PxMin(PxU32(PxMax(0, startRow + PxI32(desc.nbRows))), nbRows);
+ PxU32 hiCol = PxMin(PxU32(PxMax(0, startCol + PxI32(desc.nbColumns))), nbCols);
+ for (PxU32 row = PxU32(PxMax(startRow, 0)); row < hiRow; row++)
+ {
+ for (PxU32 col = PxU32(PxMax(startCol, 0)); col < hiCol; col++)
+ {
+ const PxU32 vertexIndex = col + row*nbCols;
+ PxHeightFieldSample* targetSample = &mData.samples[vertexIndex];
+
+ // update target sample from source sample
+ const PxHeightFieldSample& sourceSample =
+ (reinterpret_cast<const PxHeightFieldSample*>(desc.samples.data))[col - startCol + (row - startRow) * desc.nbColumns];
+ *targetSample = sourceSample;
+
+ if(isCollisionVertexPreca(vertexIndex, row, col, PxHeightFieldMaterial::eHOLE))
+ targetSample->materialIndex1.setBit();
+ else
+ targetSample->materialIndex1.clearBit();
+
+ // grow (but not shrink) the height extents
+ const PxReal h = getHeight(vertexIndex);
+ minHeight = physx::intrinsics::selectMin(h, minHeight);
+ maxHeight = physx::intrinsics::selectMax(h, maxHeight);
+ }
+ }
+
+ if (shrinkBounds)
+ {
+ // do a full recompute on vertical bounds to allow shrinking
+ minHeight = PX_MAX_REAL;
+ maxHeight = -PX_MAX_REAL;
+ // have to recompute the min&max from scratch...
+ for (PxU32 vertexIndex = 0; vertexIndex < nbRows * nbCols; vertexIndex ++)
+ {
+ // update height extents
+ const PxReal h = getHeight(vertexIndex);
+ minHeight = physx::intrinsics::selectMin(h, minHeight);
+ maxHeight = physx::intrinsics::selectMax(h, maxHeight);
+ }
+ }
+ mMinHeight = minHeight;
+ mMaxHeight = maxHeight;
+
+ // update local space aabb
+ CenterExtents& bounds = mData.mAABB;
+ bounds.mCenter.y = (maxHeight + minHeight)*0.5f;
+ bounds.mExtents.y = (maxHeight - minHeight)*0.5f;
+
+ mModifyCount++;
+
+ return true;
+}
+
+bool Gu::HeightField::load(PxInputStream& stream)
+{
+ // release old memory
+ releaseMemory();
+
+ // Import header
+ PxU32 version;
+ bool endian;
+ if(!readHeader('H', 'F', 'H', 'F', version, endian, stream))
+ return false;
+
+ // load mData
+ mData.rows = readDword(endian, stream);
+ mData.columns = readDword(endian, stream);
+ mData.rowLimit = readFloat(endian, stream);
+ mData.colLimit = readFloat(endian, stream);
+ mData.nbColumns = readFloat(endian, stream);
+ mData.thickness = readFloat(endian, stream);
+ mData.convexEdgeThreshold = readFloat(endian, stream);
+
+ PxU16 flags = readWord(endian, stream);
+ mData.flags = PxHeightFieldFlags(flags);
+
+ PxU32 format = readDword(endian, stream);
+ mData.format = PxHeightFieldFormat::Enum(format);
+
+ PxBounds3 minMaxBounds;
+ minMaxBounds.minimum.x = readFloat(endian, stream);
+ minMaxBounds.minimum.y = readFloat(endian, stream);
+ minMaxBounds.minimum.z = readFloat(endian, stream);
+ minMaxBounds.maximum.x = readFloat(endian, stream);
+ minMaxBounds.maximum.y = readFloat(endian, stream);
+ minMaxBounds.maximum.z = readFloat(endian, stream);
+ mData.mAABB = CenterExtents(minMaxBounds);
+
+ mSampleStride = readDword(endian, stream);
+ mNbSamples = readDword(endian, stream);
+ mMinHeight = readFloat(endian, stream);
+ mMaxHeight = readFloat(endian, stream);
+
+ // allocate height samples
+ mData.samples = NULL;
+ const PxU32 nbVerts = mData.rows * mData.columns;
+ if (nbVerts > 0)
+ {
+ mData.samples = reinterpret_cast<PxHeightFieldSample*>(PX_ALLOC(nbVerts*sizeof(PxHeightFieldSample), "PxHeightFieldSample"));
+ if (mData.samples == NULL)
+ {
+ Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "Gu::HeightField::load: PX_ALLOC failed!");
+ return false;
+ }
+ stream.read(mData.samples, mNbSamples*sizeof(PxHeightFieldSample));
+ if (endian)
+ for(PxU32 i = 0; i < mNbSamples; i++)
+ {
+ PxHeightFieldSample& s = mData.samples[i];
+ PX_ASSERT(sizeof(PxU16) == sizeof(s.height));
+ flip(s.height);
+ }
+ }
+
+ return true;
+}
+
+bool Gu::HeightField::loadFromDesc(const PxHeightFieldDesc& desc)
+{
+ // verify descriptor
+ PX_CHECK_AND_RETURN_NULL(desc.isValid(), "Gu::HeightField::loadFromDesc: desc.isValid() failed!");
+
+ // release old memory
+ releaseMemory();
+
+ // copy trivial data
+ mData.format = desc.format;
+ mData.rows = desc.nbRows;
+ mData.columns = desc.nbColumns;
+ mData.thickness = desc.thickness;
+ mData.convexEdgeThreshold = desc.convexEdgeThreshold;
+ mData.flags = desc.flags;
+ mSampleStride = desc.samples.stride;
+
+ // PT: precompute some data - mainly for Xbox
+ mData.rowLimit = float(mData.rows - 2);
+ mData.colLimit = float(mData.columns - 2);
+ mData.nbColumns = float(desc.nbColumns);
+
+ // allocate and copy height samples
+ // compute extents too
+ mData.samples = NULL;
+ const PxU32 nbVerts = desc.nbRows * desc.nbColumns;
+ mMinHeight = PX_MAX_REAL;
+ mMaxHeight = -PX_MAX_REAL;
+
+ if (nbVerts > 0)
+ {
+ mData.samples = reinterpret_cast<PxHeightFieldSample*>(PX_ALLOC(nbVerts*sizeof(PxHeightFieldSample), "PxHeightFieldSample"));
+ if (mData.samples == NULL)
+ {
+ Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "Gu::HeightField::load: PX_ALLOC failed!");
+ return false;
+ }
+ const PxU8* PX_RESTRICT src = reinterpret_cast<const PxU8*>(desc.samples.data);
+ PxHeightFieldSample* PX_RESTRICT dst = mData.samples;
+ PxI16 minHeight = PX_MAX_I16;
+ PxI16 maxHeight = PX_MIN_I16;
+ for(PxU32 i=0;i<nbVerts;i++)
+ {
+ const PxHeightFieldSample& sample = *reinterpret_cast<const PxHeightFieldSample*>(src);
+ *dst++ = sample;
+ const PxI16 height = sample.height;
+ minHeight = height < minHeight ? height : minHeight;
+ maxHeight = height > maxHeight ? height : maxHeight;
+ src += desc.samples.stride;
+ }
+ mMinHeight = PxReal(minHeight);
+ mMaxHeight = PxReal(maxHeight);
+ }
+
+ PX_ASSERT(mMaxHeight >= mMinHeight);
+
+ parseTrianglesForCollisionVertices(PxHeightFieldMaterial::eHOLE);
+
+// PT: "mNbSamples" only used by binary converter
+ mNbSamples = mData.rows * mData.columns;
+
+ //Compute local space aabb.
+ PxBounds3 bounds;
+ bounds.minimum.y = getMinHeight();
+ bounds.maximum.y = getMaxHeight();
+
+ bounds.minimum.x = 0;
+ bounds.maximum.x = PxReal(getNbRowsFast() - 1);
+ bounds.minimum.z = 0;
+ bounds.maximum.z = PxReal(getNbColumnsFast() - 1);
+ mData.mAABB=bounds;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+PxU32 Gu::HeightField::saveCells(void* destBuffer, PxU32 destBufferSize) const
+{
+ PxU32 n = mData.columns * mData.rows * sizeof(PxHeightFieldSample);
+ if (n > destBufferSize) n = destBufferSize;
+ PxMemCopy(destBuffer, mData.samples, n);
+
+ return n;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+PX_PHYSX_COMMON_API void Gu::HeightField::releaseMemory()
+{
+// PX_SERIALIZATION
+ if(getBaseFlags() & PxBaseFlag::eOWNS_MEMORY)
+//~PX_SERIALIZATION
+ {
+ PX_FREE(mData.samples);
+ mData.samples = NULL;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// PT: TODO: use those faster functions everywhere
+namespace physx
+{
+
+PX_PHYSX_COMMON_API PxU32 getVertexEdgeIndices(const Gu::HeightField& heightfield, PxU32 vertexIndex, PxU32 row, PxU32 column, EdgeData edgeIndices[8])
+{
+ const PxU32 nbColumns = heightfield.getData().columns;
+ const PxU32 nbRows = heightfield.getData().rows;
+ PX_ASSERT((vertexIndex / nbColumns)==row);
+ PX_ASSERT((vertexIndex % nbColumns)==column);
+
+ PxU32 count = 0;
+
+ if (row > 0)
+ {
+// edgeIndices[count++] = 3 * (vertexIndex - nbColumns) + 2;
+ const PxU32 cell = vertexIndex - nbColumns;
+ edgeIndices[count].edgeIndex = 3 * cell + 2;
+ edgeIndices[count].cell = cell;
+ edgeIndices[count].row = row-1;
+ edgeIndices[count].column = column;
+ count++;
+ }
+
+ if (column < nbColumns-1)
+ {
+ if (row > 0)
+ {
+ if (!heightfield.isZerothVertexShared(vertexIndex - nbColumns))
+ {
+// edgeIndices[count++] = 3 * (vertexIndex - nbColumns) + 1;
+ const PxU32 cell = vertexIndex - nbColumns;
+ edgeIndices[count].edgeIndex = 3 * cell + 1;
+ edgeIndices[count].cell = cell;
+ edgeIndices[count].row = row-1;
+ edgeIndices[count].column = column;
+ count++;
+ }
+ }
+// edgeIndices[count++] = 3 * vertexIndex;
+ edgeIndices[count].edgeIndex = 3 * vertexIndex;
+ edgeIndices[count].cell = vertexIndex;
+ edgeIndices[count].row = row;
+ edgeIndices[count].column = column;
+ count++;
+
+ if (row < nbRows - 1)
+ {
+ if (heightfield.isZerothVertexShared(vertexIndex))
+ {
+// edgeIndices[count++] = 3 * vertexIndex + 1;
+ edgeIndices[count].edgeIndex = 3 * vertexIndex + 1;
+ edgeIndices[count].cell = vertexIndex;
+ edgeIndices[count].row = row;
+ edgeIndices[count].column = column;
+ count++;
+ }
+ }
+ }
+
+ if (row < nbRows - 1)
+ {
+// edgeIndices[count++] = 3 * vertexIndex + 2;
+ edgeIndices[count].edgeIndex = 3 * vertexIndex + 2;
+ edgeIndices[count].cell = vertexIndex;
+ edgeIndices[count].row = row;
+ edgeIndices[count].column = column;
+ count++;
+ }
+
+ if (column > 0)
+ {
+ if (row < nbRows - 1)
+ {
+ if (!heightfield.isZerothVertexShared(vertexIndex - 1))
+ {
+// edgeIndices[count++] = 3 * (vertexIndex - 1) + 1;
+ const PxU32 cell = vertexIndex - 1;
+ edgeIndices[count].edgeIndex = 3 * cell + 1;
+ edgeIndices[count].cell = cell;
+ edgeIndices[count].row = row;
+ edgeIndices[count].column = column-1;
+ count++;
+ }
+ }
+// edgeIndices[count++] = 3 * (vertexIndex - 1);
+ const PxU32 cell = vertexIndex - 1;
+ edgeIndices[count].edgeIndex = 3 * cell;
+ edgeIndices[count].cell = cell;
+ edgeIndices[count].row = row;
+ edgeIndices[count].column = column-1;
+ count++;
+ if (row > 0)
+ {
+ if (heightfield.isZerothVertexShared(vertexIndex - nbColumns - 1))
+ {
+// edgeIndices[count++] = 3 * (vertexIndex - nbColumns - 1) + 1;
+ const PxU32 cell1 = vertexIndex - nbColumns - 1;
+ edgeIndices[count].edgeIndex = 3 * cell1 + 1;
+ edgeIndices[count].cell = cell1;
+ edgeIndices[count].row = row-1;
+ edgeIndices[count].column = column-1;
+ count++;
+ }
+ }
+ }
+ return count;
+}
+
+PX_PHYSX_COMMON_API PxU32 getEdgeTriangleIndices(const Gu::HeightField& heightfield, const EdgeData& edgeData, PxU32* PX_RESTRICT triangleIndices)
+{
+ const PxU32 nbColumns = heightfield.getData().columns;
+ const PxU32 nbRows = heightfield.getData().rows;
+
+ const PxU32 edgeIndex = edgeData.edgeIndex;
+ const PxU32 cell = edgeData.cell;
+ const PxU32 row = edgeData.row;
+ const PxU32 column = edgeData.column;
+ PX_ASSERT(cell==edgeIndex / 3);
+ PX_ASSERT(row==cell / nbColumns);
+ PX_ASSERT(column==cell % nbColumns);
+ PxU32 count = 0;
+ switch (edgeIndex - cell*3)
+ {
+ case 0:
+ if (column < nbColumns - 1)
+ {
+ if (row > 0)
+ {
+ if (heightfield.isZerothVertexShared(cell - nbColumns))
+ triangleIndices[count++] = ((cell - nbColumns) << 1);
+ else
+ triangleIndices[count++] = ((cell - nbColumns) << 1) + 1;
+ }
+ if (row < nbRows - 1)
+ {
+ if (heightfield.isZerothVertexShared(cell))
+ triangleIndices[count++] = (cell << 1) + 1;
+ else
+ triangleIndices[count++] = cell << 1;
+ }
+ }
+ break;
+ case 1:
+ if ((row < nbRows - 1) && (column < nbColumns - 1))
+ {
+ triangleIndices[count++] = cell << 1;
+ triangleIndices[count++] = (cell << 1) + 1;
+ }
+ break;
+ case 2:
+ if (row < nbRows - 1)
+ {
+ if (column > 0)
+ {
+ triangleIndices[count++] = ((cell - 1) << 1) + 1;
+ }
+ if (column < nbColumns - 1)
+ {
+ triangleIndices[count++] = cell << 1;
+ }
+ }
+ break;
+ }
+
+ return count;
+}
+
+}
+
+PX_FORCE_INLINE PxU32 anyHole(PxU32 doubleMatIndex, PxU16 holeMaterialIndex)
+{
+ return PxU32((doubleMatIndex & 0xFFFF) == holeMaterialIndex) | (PxU32(doubleMatIndex >> 16) == holeMaterialIndex);
+}
+
+void Gu::HeightField::parseTrianglesForCollisionVertices(PxU16 holeMaterialIndex)
+{
+ const PxU32 nbColumns = getNbColumnsFast();
+ const PxU32 nbRows = getNbRowsFast();
+
+ Cm::BitMap rowHoles[2];
+ rowHoles[0].resizeAndClear(nbColumns + 1);
+ rowHoles[1].resizeAndClear(nbColumns + 1);
+
+ for (PxU32 iCol = 0; iCol < nbColumns; iCol++)
+ {
+ if (anyHole(getMaterialIndex01(iCol), holeMaterialIndex))
+ {
+ rowHoles[0].set(iCol);
+ rowHoles[0].set(iCol + 1);
+ }
+ PxU32 vertIndex = iCol;
+ if(isCollisionVertexPreca(vertIndex, 0, iCol, holeMaterialIndex))
+ mData.samples[vertIndex].materialIndex1.setBit();
+ else
+ mData.samples[vertIndex].materialIndex1.clearBit();
+ }
+
+ PxU32 nextRow = 1, currentRow = 0;
+ for (PxU32 iRow = 1; iRow < nbRows; iRow++)
+ {
+ PxU32 rowOffset = iRow*nbColumns;
+ for (PxU32 iCol = 0; iCol < nbColumns; iCol++)
+ {
+ const PxU32 vertIndex = rowOffset + iCol; // column index plus current row offset (vertex/cell index)
+ if(anyHole(getMaterialIndex01(vertIndex), holeMaterialIndex))
+ {
+ rowHoles[currentRow].set(iCol);
+ rowHoles[currentRow].set(iCol + 1);
+ rowHoles[nextRow].set(iCol);
+ rowHoles[nextRow].set(iCol + 1);
+ }
+
+ if ((iCol == 0) || (iCol == nbColumns - 1) || (iRow == nbRows - 1) || rowHoles[currentRow].test(iCol))
+ {
+ if(isCollisionVertexPreca(vertIndex, iRow, iCol, holeMaterialIndex))
+ mData.samples[vertIndex].materialIndex1.setBit();
+ else
+ mData.samples[vertIndex].materialIndex1.clearBit();
+ } else
+ {
+ if (isConvexVertex(vertIndex, iRow, iCol))
+ mData.samples[vertIndex].materialIndex1.setBit();
+ }
+ }
+
+ rowHoles[currentRow].clear();
+
+ // swap prevRow and prevPrevRow
+ nextRow ^= 1; currentRow ^= 1;
+ }
+}
+
+bool Gu::HeightField::isSolidVertex(PxU32 vertexIndex, PxU32 row, PxU32 column, PxU16 holeMaterialIndex, bool& nbSolid) const
+{
+ // check if solid and boundary
+ // retrieve edge indices for current vertexIndex
+ EdgeData edgeIndices[8];
+ const PxU32 edgeCount = ::getVertexEdgeIndices(*this, vertexIndex, row, column, edgeIndices);
+
+ PxU32 faceCounts[8];
+ PxU32 faceIndices[2 * 8];
+ PxU32* dst = faceIndices;
+ for (PxU32 i = 0; i < edgeCount; i++)
+ {
+ faceCounts[i] = ::getEdgeTriangleIndices(*this, edgeIndices[i], dst);
+ dst += 2;
+ }
+
+ nbSolid = false;
+ const PxU32* currentfaceIndices = faceIndices; // parallel array of pairs of face indices per edge index
+ for (PxU32 i = 0; i < edgeCount; i++)
+ {
+ if (faceCounts[i] > 1)
+ {
+ const PxU16& material0 = getTriangleMaterial(currentfaceIndices[0]);
+ const PxU16& material1 = getTriangleMaterial(currentfaceIndices[1]);
+ // ptchernev TODO: this is a bit arbitrary
+ if (material0 != holeMaterialIndex)
+ {
+ nbSolid = true;
+ if (material1 == holeMaterialIndex)
+ return true; // edge between solid and hole => return true
+ }
+ if (material1 != holeMaterialIndex)
+ {
+ nbSolid = true;
+ if (material0 == holeMaterialIndex)
+ return true; // edge between hole and solid => return true
+ }
+ }
+ else
+ {
+ if (getTriangleMaterial(currentfaceIndices[0]) != holeMaterialIndex)
+ return true;
+ }
+ currentfaceIndices += 2; // 2 face indices per edge
+ }
+ return false;
+}
+
+bool Gu::HeightField::isCollisionVertexPreca(PxU32 vertexIndex, PxU32 row, PxU32 column, PxU16 holeMaterialIndex) const
+{
+#ifdef PX_HEIGHTFIELD_DEBUG
+ PX_ASSERT(isValidVertex(vertexIndex));
+#endif
+ PX_ASSERT((vertexIndex / getNbColumnsFast()) == row);
+ PX_ASSERT((vertexIndex % getNbColumnsFast()) == column);
+
+ // check boundary conditions - boundary edges shouldn't produce collision with eNO_BOUNDARY_EDGES flag
+ if(mData.flags & PxHeightFieldFlag::eNO_BOUNDARY_EDGES)
+ if ((row == 0) || (column == 0) || (row >= mData.rows-1) || (column >= mData.columns-1))
+ return false;
+
+ bool nbSolid;
+ if(isSolidVertex(vertexIndex, row, column, holeMaterialIndex, nbSolid))
+ return true;
+
+ // return true if it is boundary or solid and convex
+ return (nbSolid && isConvexVertex(vertexIndex, row, column));
+}
+
+
+/*struct int64
+{
+ int a,b;
+};*/
+
+#ifdef REMOVED
+// PT: special version computing vertex index directly
+PxU32 Gu::HeightField::computeCellCoordinates(PxReal x, PxReal z, PxU32 nbColumns, PxReal& fracX, PxReal& fracZ) const
+{
+ x = physx::intrinsics::selectMax(x, 0.0f);
+ z = physx::intrinsics::selectMax(z, 0.0f);
+
+ PxU32 row = (PxU32)x;
+ PxU32 column = (PxU32)z;
+
+/*int64 tmp_x, tmp_z;
+_asm lwz r11, x
+_asm lfs fr0, 0(r11)
+_asm fctiwz fr13, fr0
+_asm stfd fr13, tmp_x
+
+_asm lwz r11, z
+_asm lfs fr0, 0(r11)
+_asm fctiwz fr13, fr0
+_asm stfd fr13, tmp_z
+
+PxU32 row = tmp_x.b;
+PX_ASSERT(row==PxU32(x));*/
+ if (row > mData.rows - 2)
+ {
+ row = mData.rows - 2;
+ fracX = PxReal(1);
+ }
+ else
+ {
+ fracX = x - PxReal(row);
+ }
+
+//PxU32 column = tmp_z.b;
+//PX_ASSERT(column==PxU32(z));
+
+ if (column > mData.columns - 2)
+ {
+ column = mData.columns - 2;
+ fracZ = PxReal(1);
+ }
+ else
+ {
+ fracZ = z - PxReal(column);
+ }
+ const PxU32 vertexIndex = row * nbColumns + column;
+
+ return vertexIndex;
+}
+#endif
+
+// AP: this naming is confusing and inconsistent with return value. the function appears to compute vertex coord rather than cell coords
+// it would most likely be better to stay in cell coords instead, since fractional vertex coords just do not make any sense
+PxU32 Gu::HeightField::computeCellCoordinates(PxReal x, PxReal z,
+ PxReal& fracX, PxReal& fracZ) const
+{
+ namespace i = physx::intrinsics;
+
+ x = i::selectMax(x, 0.0f);
+ z = i::selectMax(z, 0.0f);
+#if 0 // validation code for scaled clamping epsilon computation
+ for (PxReal ii = 1.0f; ii < 100000.0f; ii+=1.0f)
+ {
+ PX_UNUSED(ii);
+ PX_ASSERT(PxFloor(ii+(1-1e-7f*ii)) == ii);
+ }
+#endif
+ PxF32 epsx = 1.0f - PxAbs(x+1.0f) * 1e-6f; // epsilon needs to scale with values of x,z...
+ PxF32 epsz = 1.0f - PxAbs(z+1.0f) * 1e-6f;
+ PxF32 x1 = i::selectMin(x, mData.rowLimit+epsx);
+ PxF32 z1 = i::selectMin(z, mData.colLimit+epsz);
+ x = PxFloor(x1);
+ fracX = x1 - x;
+ z = PxFloor(z1);
+ fracZ = z1 - z;
+ PX_ASSERT(x >= 0.0f && x < PxF32(mData.rows));
+ PX_ASSERT(z >= 0.0f && z < PxF32(mData.columns));
+
+ const PxU32 vertexIndex = PxU32(x * (mData.nbColumns) + z);
+ PX_ASSERT(vertexIndex < (mData.rows)*(mData.columns));
+
+ return vertexIndex;
+}
+
+PxReal Gu::HeightField::computeExtreme(PxU32 minRow, PxU32 maxRow, PxU32 minColumn, PxU32 maxColumn) const
+{
+ const bool thicknessNegOrNull = (getThicknessFast() <= 0.0f);
+
+// PxReal hfExtreme = thicknessNegOrNull ? -PX_MAX_REAL : PX_MAX_REAL;
+ PxI32 hfExtreme = thicknessNegOrNull ? PX_MIN_I32 : PX_MAX_I32;
+
+/* for(PxU32 row = minRow; row <= maxRow; row++)
+ {
+ for(PxU32 column = minColumn; column <= maxColumn; column++)
+ {
+ const PxReal h = getHeight(row * getNbColumnsFast() + column);
+ hfExtreme = thicknessNegOrNull ? PxMax(hfExtreme, h) : PxMin(hfExtreme, h);
+ }
+ }*/
+
+ if(thicknessNegOrNull)
+ {
+ for(PxU32 row = minRow; row <= maxRow; row++)
+ {
+ for(PxU32 column = minColumn; column <= maxColumn; column++)
+ {
+// const PxReal h = getHeight(row * getNbColumnsFast() + column);
+ const PxI32 h = getSample(row * getNbColumnsFast() + column).height;
+ hfExtreme = PxMax(hfExtreme, h);
+ }
+ }
+ }
+ else
+ {
+ for(PxU32 row = minRow; row <= maxRow; row++)
+ {
+ for(PxU32 column = minColumn; column <= maxColumn; column++)
+ {
+// const PxReal h = getHeight(row * getNbColumnsFast() + column);
+ const PxI32 h = getSample(row * getNbColumnsFast() + column).height;
+ hfExtreme = PxMin(hfExtreme, h);
+ }
+ }
+ }
+
+// return hfExtreme;
+ return PxReal(hfExtreme);
+}
diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.h b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.h
new file mode 100644
index 00000000..4cc9c6ba
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.h
@@ -0,0 +1,1520 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved.
+// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
+// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
+
+#ifndef GU_HEIGHTFIELD_H
+#define GU_HEIGHTFIELD_H
+
+#include "PsUserAllocated.h"
+#include "CmRefCountable.h"
+#include "PsMathUtils.h"
+#include "GuSphere.h"
+#include "PxHeightFieldSample.h"
+#include "PxHeightFieldDesc.h"
+#include "GuHeightFieldData.h"
+#include "PxHeightField.h"
+
+//#define PX_HEIGHTFIELD_VERSION 0
+#define PX_HEIGHTFIELD_VERSION 1 // tiled version that was needed for PS3 only has been removed
+
+namespace physx
+{
+
+class GuMeshFactory;
+class PxHeightFieldDesc;
+
+}
+
+namespace physx
+{
+namespace Gu
+{
+class HeightField : public PxHeightField, public Ps::UserAllocated, public Cm::RefCountable
+{
+//= ATTENTION! =====================================================================================
+// Changing the data layout of this class breaks the binary serialization format. See comments for
+// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData
+// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION
+// accordingly.
+//==================================================================================================
+public:
+// PX_SERIALIZATION
+ HeightField(PxBaseFlags baseFlags) : PxHeightField(baseFlags), Cm::RefCountable(PxEmpty), mData(PxEmpty), mModifyCount(0) {}
+
+ PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext&);
+ PX_PHYSX_COMMON_API void importExtraData(PxDeserializationContext& context);
+ PX_FORCE_INLINE void setMeshFactory(GuMeshFactory* f) { mMeshFactory = f; }
+ PX_PHYSX_COMMON_API static HeightField* createObject(PxU8*& address, PxDeserializationContext& context);
+ PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream);
+ void resolveReferences(PxDeserializationContext&) {}
+ virtual void requires(PxProcessPxBaseCallback&){}
+//~PX_SERIALIZATION
+ PX_PHYSX_COMMON_API HeightField(GuMeshFactory* meshFactory);
+ PX_PHYSX_COMMON_API HeightField(GuMeshFactory& factory, Gu::HeightFieldData& data);
+
+ // PxHeightField
+ PX_PHYSX_COMMON_API virtual void release();
+ PX_PHYSX_COMMON_API virtual PxU32 saveCells(void* destBuffer, PxU32 destBufferSize) const;
+ PX_PHYSX_COMMON_API virtual bool modifySamples(PxI32 startCol, PxI32 startRow, const PxHeightFieldDesc& subfieldDesc, bool shrinkBounds);
+ PX_PHYSX_COMMON_API virtual PxU32 getNbRows() const { return mData.rows; }
+ PX_PHYSX_COMMON_API virtual PxU32 getNbColumns() const { return mData.columns; }
+ PX_PHYSX_COMMON_API virtual PxHeightFieldFormat::Enum getFormat() const { return mData.format; }
+ PX_PHYSX_COMMON_API virtual PxU32 getSampleStride() const { return sizeof(PxHeightFieldSample); }
+ PX_PHYSX_COMMON_API virtual PxReal getThickness() const { return mData.thickness; }
+ PX_PHYSX_COMMON_API virtual PxReal getConvexEdgeThreshold() const { return mData.convexEdgeThreshold; }
+ PX_PHYSX_COMMON_API virtual PxHeightFieldFlags getFlags() const { return mData.flags; }
+ PX_PHYSX_COMMON_API virtual PxReal getHeight(PxReal x, PxReal z) const { return getHeightInternal(x, z); }
+
+ PX_PHYSX_COMMON_API virtual void acquireReference();
+ PX_PHYSX_COMMON_API virtual PxU32 getReferenceCount() const;
+ //~PxHeightField
+
+ // RefCountable
+ PX_PHYSX_COMMON_API virtual void onRefCountZero();
+ //~RefCountable
+ PX_PHYSX_COMMON_API virtual PxMaterialTableIndex getTriangleMaterialIndex(PxTriangleID triangleIndex) const
+ {
+ return getTriangleMaterial(triangleIndex);
+ }
+ PX_PHYSX_COMMON_API virtual PxVec3 getTriangleNormal(PxTriangleID triangleIndex) const
+ {
+ return getTriangleNormalInternal(triangleIndex);
+ }
+
+ /**
+ \brief Returns the number of times the heightfield data has been modified
+
+ Each time the heightfield is changed via 'modifySamples' this increments a counter. This method will return
+ the number of times the heightfield has been modified so that rendering code can know whether or not it needs to
+ rebuild the graphics representation of the mesh.
+
+ \return the number of times the heightfield sample data has been modified.
+ */
+ PX_PHYSX_COMMON_API virtual PxU32 getTimestamp() const
+ {
+ return mModifyCount;
+ }
+
+ PX_PHYSX_COMMON_API bool loadFromDesc(const PxHeightFieldDesc&);
+ PX_PHYSX_COMMON_API bool load(PxInputStream&);
+
+ PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbRowsFast() const { return mData.rows; }
+ PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbColumnsFast() const { return mData.columns; }
+ PX_FORCE_INLINE PxHeightFieldFormat::Enum getFormatFast() const { return mData.format; }
+ PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getThicknessFast() const { return mData.thickness; }
+ PX_FORCE_INLINE PxU32 getFlagsFast() const { return mData.flags; }
+
+ PX_FORCE_INLINE bool isDeltaHeightInsideExtent(PxReal dy, PxReal eps = 0.0f) const
+ {
+ return (mData.thickness <= 0.0f && dy <= eps && dy >= mData.thickness) ||
+ (mData.thickness > 0.0f && dy > -eps && dy < mData.thickness);
+ }
+
+ PX_FORCE_INLINE bool isDeltaHeightOppositeExtent(PxReal dy) const
+ {
+ return (mData.thickness <= 0.0f && dy > 0.0f) || (mData.thickness > 0.0f && dy < 0.0f);
+ }
+
+ PX_CUDA_CALLABLE PX_FORCE_INLINE bool isZerothVertexShared(PxU32 vertexIndex) const
+ {
+// return (getSample(vertexIndex).tessFlag & PxHeightFieldTessFlag::e0TH_VERTEX_SHARED);
+ return getSample(vertexIndex).tessFlag() != 0;
+ }
+
+ PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getMaterialIndex0(PxU32 vertexIndex) const { return getSample(vertexIndex).materialIndex0; }
+ PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getMaterialIndex1(PxU32 vertexIndex) const { return getSample(vertexIndex).materialIndex1; }
+ PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMaterialIndex01(PxU32 vertexIndex) const
+ {
+ const PxHeightFieldSample& sample = getSample(vertexIndex);
+ return PxU32(sample.materialIndex0 | (sample.materialIndex1 << 16));
+ }
+
+ PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getHeight(PxU32 vertexIndex) const
+ {
+ return PxReal(getSample(vertexIndex).height);
+ }
+
+ PX_INLINE PxReal getHeightInternal2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ) const;
+ PX_FORCE_INLINE PxReal getHeightInternal(PxReal x, PxReal z) const
+ {
+ PxReal fracX, fracZ;
+ const PxU32 vertexIndex = computeCellCoordinates(x, z, fracX, fracZ);
+
+ return getHeightInternal2(vertexIndex, fracX, fracZ);
+ }
+
+ PX_FORCE_INLINE bool isValidVertex(PxU32 vertexIndex) const { return vertexIndex < mData.rows*mData.columns; }
+
+ PX_INLINE PxVec3 getVertex(PxU32 vertexIndex) const;
+ PX_INLINE bool isConvexVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const;
+
+ PX_INLINE bool isValidEdge(PxU32 edgeIndex) const;
+ PX_INLINE PxU32 getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2]) const;
+ PX_INLINE PxU32 getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2], PxU32 cell, PxU32 row, PxU32 column) const;
+ PX_INLINE void getEdgeVertexIndices(PxU32 edgeIndex, PxU32& vertexIndex0, PxU32& vertexIndex1) const;
+// PX_INLINE bool isConvexEdge(PxU32 edgeIndex) const;
+ PX_INLINE bool isConvexEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const;
+ PX_FORCE_INLINE bool isConvexEdge(PxU32 edgeIndex) const
+ {
+ const PxU32 cell = edgeIndex / 3;
+ const PxU32 row = cell / mData.columns;
+ const PxU32 column = cell % mData.columns;
+ return isConvexEdge(edgeIndex, cell, row, column);
+ }
+
+// PX_INLINE void computeCellCoordinates(PxReal x, PxReal z, PxU32& row, PxU32& column, PxReal& fracX, PxReal& fracZ) const;
+// PX_INLINE PxU32 computeCellCoordinates(PxReal x, PxReal z, PxU32 nbColumns, PxReal& fracX, PxReal& fracZ) const;
+ PX_PHYSX_COMMON_API PxU32 computeCellCoordinates(PxReal x, PxReal z, PxReal& fracX, PxReal& fracZ) const;
+
+ PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMinRow(PxReal x) const { return PxU32(PxClamp(PxI32(Ps::floor(x)), PxI32(0), PxI32(mData.rows-2))); }
+ PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMaxRow(PxReal x) const { return PxU32(PxClamp(PxI32(Ps::ceil(x)), PxI32(0), PxI32(mData.rows-1))); }
+ PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMinColumn(PxReal z) const { return PxU32(PxClamp(PxI32(Ps::floor(z)), PxI32(0), PxI32(mData.columns-2))); }
+ PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMaxColumn(PxReal z) const { return PxU32(PxClamp(PxI32(Ps::ceil(z)), PxI32(0), PxI32(mData.columns-1))); }
+
+ PX_CUDA_CALLABLE PX_INLINE bool isValidTriangle(PxU32 triangleIndex) const;
+ PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFirstTriangle(PxU32 triangleIndex) const { return ((triangleIndex & 0x1) == 0); }
+
+ PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getTriangleMaterial(PxU32 triangleIndex) const
+ {
+ return isFirstTriangle(triangleIndex) ? getMaterialIndex0(triangleIndex >> 1) : getMaterialIndex1(triangleIndex >> 1);
+ }
+
+ PX_CUDA_CALLABLE PX_INLINE void getTriangleVertexIndices(PxU32 triangleIndex, PxU32& vertexIndex0, PxU32& vertexIndex1, PxU32& vertexIndex2) const;
+ PX_CUDA_CALLABLE PX_INLINE PxVec3 getTriangleNormalInternal(PxU32 triangleIndex) const;
+ PX_INLINE void getTriangleAdjacencyIndices(PxU32 triangleIndex,PxU32 vertexIndex0, PxU32 vertexIndex1, PxU32 vertexIndex2, PxU32& adjacencyIndex0, PxU32& adjacencyIndex1, PxU32& adjacencyIndex2) const;
+
+ PX_INLINE PxVec3 getNormal_2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ, PxReal xcoeff, PxReal ycoeff, PxReal zcoeff) const;
+ PX_FORCE_INLINE PxVec3 getNormal_(PxReal x, PxReal z, PxReal xcoeff, PxReal ycoeff, PxReal zcoeff) const
+ {
+ PxReal fracX, fracZ;
+ const PxU32 vertexIndex = computeCellCoordinates(x, z, fracX, fracZ);
+
+ return getNormal_2(vertexIndex, fracX, fracZ, xcoeff, ycoeff, zcoeff);
+ }
+
+ PX_INLINE PxU32 getTriangleIndex(PxReal x, PxReal z) const;
+ PX_INLINE PxU32 getTriangleIndex2(PxU32 cell, PxReal fracX, PxReal fracZ) const;
+ PX_FORCE_INLINE PxU16 getMaterial(PxReal x, PxReal z) const
+ {
+ return getTriangleMaterial(getTriangleIndex(x, z));
+ }
+
+ PX_FORCE_INLINE PxReal getMinHeight() const { return mMinHeight; }
+ PX_FORCE_INLINE PxReal getMaxHeight() const { return mMaxHeight; }
+
+ PX_FORCE_INLINE const Gu::HeightFieldData& getData() const { return mData; }
+
+ PX_CUDA_CALLABLE PX_FORCE_INLINE void getTriangleVertices(PxU32 triangleIndex, PxU32 row, PxU32 column, PxVec3& v0, PxVec3& v1, PxVec3& v2) const;
+
+ // checks if current vertex is solid or not
+ bool isSolidVertex(PxU32 vertexIndex, PxU32 row, PxU32 coloumn, PxU16 holeMaterialIndex,
+ bool& nbSolid) const;
+
+ // if precomputed bitmap define is used, the collision vertex information
+ // is precomputed during create height field and stored as a bit in materialIndex1
+ PX_PHYSX_COMMON_API bool isCollisionVertexPreca(PxU32 vertexIndex, PxU32 row, PxU32 column, PxU16 holeMaterialIndex) const;
+ PX_FORCE_INLINE bool isCollisionVertex(PxU32 vertexIndex, PxU32, PxU32, PxU16) const
+ {
+ return getSample(vertexIndex).materialIndex1.isBitSet()!=0;
+ }
+ void parseTrianglesForCollisionVertices(PxU16 holeMaterialIndex);
+
+ PX_PHYSX_COMMON_API PxReal computeExtreme(PxU32 minRow, PxU32 maxRow, PxU32 minColumn, PxU32 maxColumn) const;
+
+ PX_FORCE_INLINE
+ PX_CUDA_CALLABLE const PxHeightFieldSample& getSample(PxU32 vertexIndex) const
+ {
+ PX_ASSERT(isValidVertex(vertexIndex));
+ return mData.samples[vertexIndex];
+ }
+
+#ifdef __CUDACC__
+ PX_CUDA_CALLABLE void setSamplePtr(PxHeightFieldSample* s) { mData.samples = s; }
+#endif
+
+ Gu::HeightFieldData mData;
+ PxU32 mSampleStride;
+ PxU32 mNbSamples; // PT: added for platform conversion. Try to remove later.
+ PxReal mMinHeight;
+ PxReal mMaxHeight;
+ PxU32 mModifyCount;
+ // methods
+ PX_PHYSX_COMMON_API void releaseMemory();
+
+ PX_PHYSX_COMMON_API virtual ~HeightField();
+
+private:
+ GuMeshFactory* mMeshFactory; // PT: changed to pointer for serialization
+};
+
+} // namespace Gu
+
+
+
+PX_INLINE PxVec3 Gu::HeightField::getVertex(PxU32 vertexIndex) const
+{
+ const PxU32 row = vertexIndex / mData.columns;
+ const PxU32 column = vertexIndex % mData.columns;
+// return PxVec3(PxReal(row), getHeight(row * mData.columns + column), PxReal(column));
+ return PxVec3(PxReal(row), getHeight(vertexIndex), PxReal(column));
+}
+
+// PT: only called from "isCollisionVertex", should move
+PX_INLINE bool Gu::HeightField::isConvexVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const
+{
+#ifdef PX_HEIGHTFIELD_DEBUG
+ PX_ASSERT(isValidVertex(vertexIndex));
+#endif
+ PX_ASSERT((vertexIndex / mData.columns)==row);
+ PX_ASSERT((vertexIndex % mData.columns)==column);
+
+// PxReal h0 = PxReal(2) * getHeight(vertexIndex);
+ PxI32 h0 = getSample(vertexIndex).height;
+ h0 += h0;
+
+ bool definedInX, definedInZ;
+ PxI32 convexityX, convexityZ;
+
+ if ((row > 0) && (row < mData.rows - 1))
+ {
+// convexityX = h0 - getHeight(vertexIndex + mData.columns) - getHeight(vertexIndex - mData.columns);
+ convexityX = h0 - getSample(vertexIndex + mData.columns).height - getSample(vertexIndex - mData.columns).height;
+ definedInX = true;
+ }
+ else
+ {
+ convexityX = 0;
+ definedInX = false;
+ }
+
+ if ((column > 0) && (column < mData.columns - 1))
+ {
+// convexityZ = h0 - getHeight(vertexIndex + 1) - getHeight(vertexIndex - 1);
+ convexityZ = h0 - getSample(vertexIndex + 1).height - getSample(vertexIndex - 1).height;
+ definedInZ = true;
+ }
+ else
+ {
+ convexityZ = 0;
+ definedInZ = false;
+ }
+
+ if(definedInX || definedInZ)
+ {
+ // PT: use XOR here
+ // saddle points
+/* if ((convexityX > 0) && (convexityZ < 0))
+ return false;
+ if ((convexityX < 0) && (convexityZ > 0))
+ return false;*/
+ if(((convexityX ^ convexityZ) & 0x80000000)==0)
+ return false;
+
+ // inequality depends on thickness and offset by threshold.
+ const PxReal value = PxReal(convexityX + convexityZ);
+ // PT: thickness is always the same for a given heightfield so the comparison shouldn't be here
+ if (mData.thickness <= 0)
+ return value > mData.convexEdgeThreshold;
+ else
+ return value < -mData.convexEdgeThreshold;
+ }
+
+ // this has to be one of the two corner vertices
+ return true;
+}
+
+
+PX_INLINE bool Gu::HeightField::isValidEdge(PxU32 edgeIndex) const
+{
+ const PxU32 cell = (edgeIndex / 3);
+ const PxU32 row = cell / mData.columns;
+ const PxU32 column = cell % mData.columns;
+// switch (edgeIndex % 3)
+ switch (edgeIndex - cell*3)
+ {
+ case 0:
+ if (row > mData.rows - 1) return false;
+ if (column >= mData.columns - 1) return false;
+ break;
+ case 1:
+ if (row >= mData.rows - 1) return false;
+ if (column >= mData.columns - 1) return false;
+ break;
+ case 2:
+ if (row >= mData.rows - 1) return false;
+ if (column > mData.columns - 1) return false;
+ break;
+ }
+ return true;
+}
+
+
+PX_INLINE PxU32 Gu::HeightField::getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2]) const
+{
+ const PxU32 cell = edgeIndex / 3;
+ const PxU32 row = cell / mData.columns;
+ const PxU32 column = cell % mData.columns;
+ PxU32 count = 0;
+// switch (edgeIndex % 3)
+ switch (edgeIndex - cell*3)
+ {
+ case 0:
+ if (column < mData.columns - 1)
+ {
+ if (row > 0)
+ {
+/* if (isZerothVertexShared(cell - mData.columns))
+ triangleIndices[count++] = ((cell - mData.columns) << 1);
+ else
+ triangleIndices[count++] = ((cell - mData.columns) << 1) + 1;*/
+ triangleIndices[count++] = ((cell - mData.columns) << 1) + 1 - isZerothVertexShared(cell - mData.columns);
+ }
+ if (row < mData.rows - 1)
+ {
+/* if (isZerothVertexShared(cell))
+ triangleIndices[count++] = (cell << 1) + 1;
+ else
+ triangleIndices[count++] = cell << 1;*/
+ triangleIndices[count++] = (cell << 1) + isZerothVertexShared(cell);
+ }
+ }
+ break;
+ case 1:
+ if ((row < mData.rows - 1) && (column < mData.columns - 1))
+ {
+ triangleIndices[count++] = cell << 1;
+ triangleIndices[count++] = (cell << 1) + 1;
+ }
+ break;
+ case 2:
+ if (row < mData.rows - 1)
+ {
+ if (column > 0)
+ triangleIndices[count++] = ((cell - 1) << 1) + 1;
+ if (column < mData.columns - 1)
+ triangleIndices[count++] = cell << 1;
+ }
+ break;
+ }
+ return count;
+}
+
+PX_INLINE PxU32 Gu::HeightField::getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2], PxU32 cell, PxU32 row, PxU32 column) const
+{
+// const PxU32 cell = edgeIndex / 3;
+// const PxU32 row = cell / mData.columns;
+// const PxU32 column = cell % mData.columns;
+ PxU32 count = 0;
+// switch (edgeIndex % 3)
+ switch (edgeIndex - cell*3)
+ {
+ case 0:
+ if (column < mData.columns - 1)
+ {
+ if (row > 0)
+ {
+/* if (isZerothVertexShared(cell - mData.columns))
+ triangleIndices[count++] = ((cell - mData.columns) << 1);
+ else
+ triangleIndices[count++] = ((cell - mData.columns) << 1) + 1;*/
+ triangleIndices[count++] = ((cell - mData.columns) << 1) + 1 - isZerothVertexShared(cell - mData.columns);
+ }
+ if (row < mData.rows - 1)
+ {
+/* if (isZerothVertexShared(cell))
+ triangleIndices[count++] = (cell << 1) + 1;
+ else
+ triangleIndices[count++] = cell << 1;*/
+ triangleIndices[count++] = (cell << 1) + isZerothVertexShared(cell);
+ }
+ }
+ break;
+ case 1:
+ if ((row < mData.rows - 1) && (column < mData.columns - 1))
+ {
+ triangleIndices[count++] = cell << 1;
+ triangleIndices[count++] = (cell << 1) + 1;
+ }
+ break;
+ case 2:
+ if (row < mData.rows - 1)
+ {
+ if (column > 0)
+ triangleIndices[count++] = ((cell - 1) << 1) + 1;
+ if (column < mData.columns - 1)
+ triangleIndices[count++] = cell << 1;
+ }
+ break;
+ }
+ return count;
+}
+
+
+PX_INLINE void Gu::HeightField::getEdgeVertexIndices(PxU32 edgeIndex, PxU32& vertexIndex0, PxU32& vertexIndex1) const
+{
+ const PxU32 cell = edgeIndex / 3;
+// switch (edgeIndex % 3)
+ switch (edgeIndex - cell*3)
+ {
+ case 0:
+ vertexIndex0 = cell;
+ vertexIndex1 = cell + 1;
+ break;
+ case 1:
+ {
+/* if (isZerothVertexShared(cell))
+ {
+ vertexIndex0 = cell;
+ vertexIndex1 = cell + mData.columns + 1;
+ }
+ else
+ {
+ vertexIndex0 = cell + 1;
+ vertexIndex1 = cell + mData.columns;
+ }*/
+ const bool b = isZerothVertexShared(cell);
+ vertexIndex0 = cell + 1 - b;
+ vertexIndex1 = cell + mData.columns + b;
+ }
+ break;
+ case 2:
+ vertexIndex0 = cell;
+ vertexIndex1 = cell + mData.columns;
+ break;
+ }
+}
+
+#ifdef REMOVED
+PX_INLINE bool Gu::HeightField::isConvexEdge(PxU32 edgeIndex) const
+{
+ const PxU32 cell = edgeIndex / 3;
+
+ const PxU32 row = cell / mData.columns;
+ if (row > mData.rows-2) return false;
+
+ const PxU32 column = cell % mData.columns;
+ if (column > mData.columns-2) return false;
+
+// PxReal h0 = 0, h1 = 0, h2 = 0, h3 = 0;
+// PxReal convexity = 0;
+ PxI32 h0 = 0, h1 = 0, h2 = 0, h3 = 0;
+ PxI32 convexity = 0;
+
+// switch (edgeIndex % 3)
+ switch (edgeIndex - cell*3)
+ {
+ case 0:
+ if (row < 1) return false;
+/* if(isZerothVertexShared(cell - mData.columns))
+ {
+ // <------ COL
+ // +----+ 0 R
+ // | / /# O
+ // | / / # W
+ // | / / # |
+ // |/ / # |
+ // + +====1 |
+ // |
+ // |
+ // |
+ // |
+ // |
+ // |
+ // V
+ //
+// h0 = getHeight(cell - mData.columns);
+// h1 = getHeight(cell);
+ h0 = getSample(cell - mData.columns).height;
+ h1 = getSample(cell).height;
+ }
+ else
+ {
+ // <------ COL
+ // 0 +----+ R
+ // #\ \ | O
+ // # \ \ | W
+ // # \ \ | |
+ // # \ \| |
+ // 1====+ + |
+ // |
+ // |
+ // |
+ // |
+ // |
+ // |
+ // V
+ //
+// h0 = getHeight(cell - mData.columns + 1);
+// h1 = getHeight(cell + 1);
+ h0 = getSample(cell - mData.columns + 1).height;
+ h1 = getSample(cell + 1).height;
+ }*/
+ const bool b0 = !isZerothVertexShared(cell - mData.columns);
+ h0 = getSample(cell - mData.columns + b0).height;
+ h1 = getSample(cell + b0).height;
+
+/* if(isZerothVertexShared(cell))
+ {
+ // <------ COL
+ // R
+ // O
+ // W
+ // |
+ // |
+ // |
+ // 2====+ 0 |
+ // # / /| |
+ // # / / | |
+ // # / / | |
+ // #/ / | |
+ // 3 +----+ |
+ // V
+ //
+// h2 = getHeight(cell + 1);
+// h3 = getHeight(cell + mData.columns + 1);
+ h2 = getSample(cell + 1).height;
+ h3 = getSample(cell + mData.columns + 1).height;
+ }
+ else
+ {
+ // <------ COL
+ // R
+ // O
+ // W
+ // |
+ // |
+ // |
+ // + +====2 |
+ // |\ \ # |
+ // | \ \ # |
+ // | \ \ # |
+ // | \ \# |
+ // +----+ 3 |
+ // V
+ //
+// h2 = getHeight(cell);
+// h3 = getHeight(cell + mData.columns);
+ h2 = getSample(cell).height;
+ h3 = getSample(cell + mData.columns).height;
+ }*/
+ const bool b1 = isZerothVertexShared(cell);
+ h2 = getSample(cell + b1).height;
+ h3 = getSample(cell + mData.columns + b1).height;
+
+ //convex = (h3-h2) < (h1-h0);
+ convexity = (h1-h0) - (h3-h2);
+ break;
+ case 1:
+// h0 = getHeight(cell);
+// h1 = getHeight(cell + 1);
+// h2 = getHeight(cell + mData.columns);
+// h3 = getHeight(cell + mData.columns + 1);
+ h0 = getSample(cell).height;
+ h1 = getSample(cell + 1).height;
+ h2 = getSample(cell + mData.columns).height;
+ h3 = getSample(cell + mData.columns + 1).height;
+ if (isZerothVertexShared(cell))
+ //convex = (h0 + h3) > (h1 + h2);
+ convexity = (h0 + h3) - (h1 + h2);
+ else
+ //convex = (h2 + h1) > (h0 + h3);
+ convexity = (h2 + h1) - (h0 + h3);
+ break;
+ case 2:
+ if (column < 1) return false;
+/* if(isZerothVertexShared(cell-1))
+ {
+ // <-------------- COL
+ // 1====0 + R
+ // + / /| O
+ // + / / | W
+ // + / / | |
+ // +/ / | |
+ // + +----+ V
+ //
+// h0 = getHeight(cell - 1);
+// h1 = getHeight(cell);
+ h0 = getSample(cell - 1).height;
+ h1 = getSample(cell).height;
+ }
+ else
+ {
+ // <-------------- COL
+ // + +----+ R
+ // +\ \ | O
+ // + \ \ | W
+ // + \ \ | |
+ // + \ \| |
+ // 1====0 + V
+ //
+// h0 = getHeight(cell - 1 + mData.columns);
+// h1 = getHeight(cell + mData.columns);
+ h0 = getSample(cell - 1 + mData.columns).height;
+ h1 = getSample(cell + mData.columns).height;
+ }*/
+ const PxU32 offset0 = isZerothVertexShared(cell-1) ? 0 : mData.columns;
+ h0 = getSample(cell - 1 + offset0).height;
+ h1 = getSample(cell + offset0).height;
+
+/* if(isZerothVertexShared(cell))
+ {
+ // <-------------- COL
+ // +----+ + R
+ // | / /+ O
+ // | / / + W
+ // | / / + |
+ // |/ / + |
+ // + 3====2 V
+ //
+// h2 = getHeight(cell + mData.columns);
+// h3 = getHeight(cell + mData.columns + 1);
+ h2 = getSample(cell + mData.columns).height;
+ h3 = getSample(cell + mData.columns + 1).height;
+ }
+ else
+ {
+ // <-------------- COL
+ // + 3====2 R
+ // |\ \ + O
+ // | \ \ + W
+ // | \ \ + |
+ // | \ \+ |
+ // +----+ + V
+ //
+// h2 = getHeight(cell);
+// h3 = getHeight(cell + 1);
+ h2 = getSample(cell).height;
+ h3 = getSample(cell + 1).height;
+ }*/
+ const PxU32 offset1 = isZerothVertexShared(cell) ? mData.columns : 0;
+ h2 = getSample(cell + offset1).height;
+ h3 = getSample(cell + offset1 + 1).height;
+
+ //convex = (h3-h2) < (h1-h0);
+ convexity = (h1-h0) - (h3-h2);
+ break;
+ }
+
+ const PxI32 threshold = PxI32(mData.convexEdgeThreshold);
+ if (mData.thickness <= 0)
+ {
+// return convexity > mData.convexEdgeThreshold;
+ return convexity > threshold;
+ }
+ else
+ {
+// return convexity < -mData.convexEdgeThreshold;
+ return convexity < -threshold;
+ }
+}
+#endif
+
+PX_INLINE bool Gu::HeightField::isConvexEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const
+{
+// const PxU32 cell = edgeIndex / 3;
+ PX_ASSERT(cell == edgeIndex / 3);
+
+// const PxU32 row = cell / mData.columns;
+ PX_ASSERT(row == cell / mData.columns);
+ if (row > mData.rows-2) return false;
+
+// const PxU32 column = cell % mData.columns;
+ PX_ASSERT(column == cell % mData.columns);
+ if (column > mData.columns-2) return false;
+
+// PxReal h0 = 0, h1 = 0, h2 = 0, h3 = 0;
+// PxReal convexity = 0;
+ PxI32 h0 = 0, h1 = 0, h2 = 0, h3 = 0;
+ PxI32 convexity = 0;
+
+// switch (edgeIndex % 3)
+ switch (edgeIndex - cell*3)
+ {
+ case 0:
+ {
+ if (row < 1) return false;
+/* if(isZerothVertexShared(cell - mData.columns))
+ {
+ // <------ COL
+ // +----+ 0 R
+ // | / /# O
+ // | / / # W
+ // | / / # |
+ // |/ / # |
+ // + +====1 |
+ // |
+ // |
+ // |
+ // |
+ // |
+ // |
+ // V
+ //
+// h0 = getHeight(cell - mData.columns);
+// h1 = getHeight(cell);
+ h0 = getSample(cell - mData.columns).height;
+ h1 = getSample(cell).height;
+ }
+ else
+ {
+ // <------ COL
+ // 0 +----+ R
+ // #\ \ | O
+ // # \ \ | W
+ // # \ \ | |
+ // # \ \| |
+ // 1====+ + |
+ // |
+ // |
+ // |
+ // |
+ // |
+ // |
+ // V
+ //
+// h0 = getHeight(cell - mData.columns + 1);
+// h1 = getHeight(cell + 1);
+ h0 = getSample(cell - mData.columns + 1).height;
+ h1 = getSample(cell + 1).height;
+ }*/
+ const bool b0 = !isZerothVertexShared(cell - mData.columns);
+ h0 = getSample(cell - mData.columns + b0).height;
+ h1 = getSample(cell + b0).height;
+
+/* if(isZerothVertexShared(cell))
+ {
+ // <------ COL
+ // R
+ // O
+ // W
+ // |
+ // |
+ // |
+ // 2====+ 0 |
+ // # / /| |
+ // # / / | |
+ // # / / | |
+ // #/ / | |
+ // 3 +----+ |
+ // V
+ //
+// h2 = getHeight(cell + 1);
+// h3 = getHeight(cell + mData.columns + 1);
+ h2 = getSample(cell + 1).height;
+ h3 = getSample(cell + mData.columns + 1).height;
+ }
+ else
+ {
+ // <------ COL
+ // R
+ // O
+ // W
+ // |
+ // |
+ // |
+ // + +====2 |
+ // |\ \ # |
+ // | \ \ # |
+ // | \ \ # |
+ // | \ \# |
+ // +----+ 3 |
+ // V
+ //
+// h2 = getHeight(cell);
+// h3 = getHeight(cell + mData.columns);
+ h2 = getSample(cell).height;
+ h3 = getSample(cell + mData.columns).height;
+ }*/
+ const bool b1 = isZerothVertexShared(cell);
+ h2 = getSample(cell + b1).height;
+ h3 = getSample(cell + mData.columns + b1).height;
+
+ //convex = (h3-h2) < (h1-h0);
+ convexity = (h1-h0) - (h3-h2);
+ }
+ break;
+ case 1:
+// h0 = getHeight(cell);
+// h1 = getHeight(cell + 1);
+// h2 = getHeight(cell + mData.columns);
+// h3 = getHeight(cell + mData.columns + 1);
+ h0 = getSample(cell).height;
+ h1 = getSample(cell + 1).height;
+ h2 = getSample(cell + mData.columns).height;
+ h3 = getSample(cell + mData.columns + 1).height;
+ if (isZerothVertexShared(cell))
+ //convex = (h0 + h3) > (h1 + h2);
+ convexity = (h0 + h3) - (h1 + h2);
+ else
+ //convex = (h2 + h1) > (h0 + h3);
+ convexity = (h2 + h1) - (h0 + h3);
+ break;
+ case 2:
+ {
+ if (column < 1) return false;
+/* if(isZerothVertexShared(cell-1))
+ {
+ // <-------------- COL
+ // 1====0 + R
+ // + / /| O
+ // + / / | W
+ // + / / | |
+ // +/ / | |
+ // + +----+ V
+ //
+// h0 = getHeight(cell - 1);
+// h1 = getHeight(cell);
+ h0 = getSample(cell - 1).height;
+ h1 = getSample(cell).height;
+ }
+ else
+ {
+ // <-------------- COL
+ // + +----+ R
+ // +\ \ | O
+ // + \ \ | W
+ // + \ \ | |
+ // + \ \| |
+ // 1====0 + V
+ //
+// h0 = getHeight(cell - 1 + mData.columns);
+// h1 = getHeight(cell + mData.columns);
+ h0 = getSample(cell - 1 + mData.columns).height;
+ h1 = getSample(cell + mData.columns).height;
+ }*/
+ const PxU32 offset0 = isZerothVertexShared(cell-1) ? 0 : mData.columns;
+ h0 = getSample(cell - 1 + offset0).height;
+ h1 = getSample(cell + offset0).height;
+
+/* if(isZerothVertexShared(cell))
+ {
+ // <-------------- COL
+ // +----+ + R
+ // | / /+ O
+ // | / / + W
+ // | / / + |
+ // |/ / + |
+ // + 3====2 V
+ //
+// h2 = getHeight(cell + mData.columns);
+// h3 = getHeight(cell + mData.columns + 1);
+ h2 = getSample(cell + mData.columns).height;
+ h3 = getSample(cell + mData.columns + 1).height;
+ }
+ else
+ {
+ // <-------------- COL
+ // + 3====2 R
+ // |\ \ + O
+ // | \ \ + W
+ // | \ \ + |
+ // | \ \+ |
+ // +----+ + V
+ //
+// h2 = getHeight(cell);
+// h3 = getHeight(cell + 1);
+ h2 = getSample(cell).height;
+ h3 = getSample(cell + 1).height;
+ }*/
+ const PxU32 offset1 = isZerothVertexShared(cell) ? mData.columns : 0;
+ h2 = getSample(cell + offset1).height;
+ h3 = getSample(cell + offset1 + 1).height;
+
+ //convex = (h3-h2) < (h1-h0);
+ convexity = (h1-h0) - (h3-h2);
+ }
+ break;
+ }
+
+ const PxI32 threshold = PxI32(mData.convexEdgeThreshold);
+ if (mData.thickness <= 0)
+ {
+// return convexity > mData.convexEdgeThreshold;
+ return convexity > threshold;
+ }
+ else
+ {
+// return convexity < -mData.convexEdgeThreshold;
+ return convexity < -threshold;
+ }
+}
+#ifdef REMOVED
+PX_INLINE void Gu::HeightField::computeCellCoordinates(PxReal x, PxReal z, PxU32& _row, PxU32& _column, PxReal& _fracX, PxReal& _fracZ) const
+{
+ x = physx::intrinsics::selectMax(x, 0.0f);
+ z = physx::intrinsics::selectMax(z, 0.0f);
+
+ PxU32 row = (PxU32)x;
+ PxReal fracX = x - PxReal(row);
+ if (row > mData.rows - 2)
+ {
+ row = mData.rows - 2;
+ fracX = PxReal(1);
+ }
+
+ PxU32 column = (PxU32)z;
+ PxReal fracZ = z - PxReal(column);
+ if (column > mData.columns - 2)
+ {
+ column = mData.columns - 2;
+ fracZ = PxReal(1);
+ }
+
+ _row = row;
+ _column = column;
+ _fracX = fracX;
+ _fracZ = fracZ;
+}
+#endif
+
+PX_INLINE bool Gu::HeightField::isValidTriangle(PxU32 triangleIndex) const
+{
+ const PxU32 cell = triangleIndex >> 1;
+ const PxU32 row = cell / mData.columns;
+ if (row >= (mData.rows - 1)) return false;
+ const PxU32 column = cell % mData.columns;
+ if (column >= (mData.columns - 1)) return false;
+ return true;
+}
+
+
+
+
+PX_INLINE void Gu::HeightField::getTriangleVertexIndices(PxU32 triangleIndex, PxU32& vertexIndex0, PxU32& vertexIndex1, PxU32& vertexIndex2) const
+{
+ const PxU32 cell = triangleIndex >> 1;
+ if (isZerothVertexShared(cell))
+ {
+ // <---- COL
+ // 0----2 1 R
+ // | 1 / /| O
+ // | / / | W
+ // | / / | |
+ // |/ / 0 | |
+ // 1 2----0 V
+ //
+ if (isFirstTriangle(triangleIndex))
+ {
+ vertexIndex0 = cell + mData.columns;
+ vertexIndex1 = cell;
+ vertexIndex2 = cell + mData.columns + 1;
+ }
+ else
+ {
+ vertexIndex0 = cell + 1;
+ vertexIndex1 = cell + mData.columns + 1;
+ vertexIndex2 = cell;
+ }
+ }
+ else
+ {
+ // <---- COL
+ // 2 1----0 R
+ // |\ \ 0 | O
+ // | \ \ | W
+ // | \ \ | |
+ // | 1 \ \| |
+ // 0----1 2 V
+ //
+ if (isFirstTriangle(triangleIndex))
+ {
+ vertexIndex0 = cell;
+ vertexIndex1 = cell + 1;
+ vertexIndex2 = cell + mData.columns;
+ }
+ else
+ {
+ vertexIndex0 = cell + mData.columns + 1;
+ vertexIndex1 = cell + mData.columns;
+ vertexIndex2 = cell + 1;
+ }
+ }
+}
+
+PX_INLINE void Gu::HeightField::getTriangleAdjacencyIndices(PxU32 triangleIndex, PxU32 vertexIndex0, PxU32 vertexIndex1, PxU32 vertexIndex2, PxU32& adjacencyIndex0, PxU32& adjacencyIndex1, PxU32& adjacencyIndex2) const
+{
+ PX_UNUSED(vertexIndex0);
+ PX_UNUSED(vertexIndex1);
+ PX_UNUSED(vertexIndex2);
+
+ const PxU32 cell = triangleIndex >> 1;
+ if (isZerothVertexShared(cell))
+ {
+ // <---- COL
+ // 0----2 1 R
+ // | 1 / /| O
+ // | / / | W
+ // | / / | |
+ // |/ / 0 | |
+ // 1 2----0 V
+ //
+ if (isFirstTriangle(triangleIndex))
+ {
+ adjacencyIndex0 = 0xFFFFFFFF;
+ adjacencyIndex1 = triangleIndex + 1;
+ adjacencyIndex2 = 0xFFFFFFFF;
+
+ if((cell % (mData.columns) != 0))
+ {
+ adjacencyIndex0 = triangleIndex - 1;
+ }
+
+ if((cell / mData.columns != mData.rows - 2))
+ {
+ adjacencyIndex2 = ((cell + mData.columns) * 2) + 1;
+ }
+ }
+ else
+ {
+ adjacencyIndex0 = 0xFFFFFFFF;
+ adjacencyIndex1 = triangleIndex - 1;
+ adjacencyIndex2 = 0xFFFFFFFF;
+
+ if(cell % (mData.columns) < (mData.columns - 2))
+ {
+ adjacencyIndex0 = triangleIndex + 1;
+ }
+
+ if(cell >= mData.columns - 1)
+ {
+ adjacencyIndex2 = (cell - mData.columns) * 2;
+ }
+ }
+ }
+ else
+ {
+ // <---- COL
+ // 2 1----0 R
+ // |\ \ 0 | O
+ // | \ \ | W
+ // | \ \ | |
+ // | 1 \ \| |
+ // 0----1 2 V
+ //
+ if (isFirstTriangle(triangleIndex))
+ {
+ adjacencyIndex0 = 0xFFFFFFFF;
+ adjacencyIndex1 = triangleIndex + 1;
+ adjacencyIndex2 = 0xFFFFFFFF;
+
+ if(cell >= mData.columns - 1)
+ {
+ adjacencyIndex0 = ((cell - (mData.columns)) * 2) + 1;
+ }
+
+ if((cell % (mData.columns) != 0))
+ {
+ adjacencyIndex2 = triangleIndex - 1;
+ }
+ }
+ else
+ {
+ adjacencyIndex0 = 0xFFFFFFFF;
+ adjacencyIndex1 = triangleIndex - 1;
+ adjacencyIndex2 = 0xFFFFFFFF;
+
+ if((cell / mData.columns != mData.rows - 2))
+ {
+ adjacencyIndex0 = (cell + (mData.columns)) * 2;
+ }
+
+ if(cell % (mData.columns) < (mData.columns - 2))
+ {
+ adjacencyIndex2 = triangleIndex + 1;
+ }
+ }
+ }
+}
+
+PX_INLINE PxVec3 Gu::HeightField::getTriangleNormalInternal(PxU32 triangleIndex) const
+{
+ PxU32 v0, v1, v2;
+ getTriangleVertexIndices(triangleIndex, v0, v1, v2);
+
+// const PxReal h0 = getHeight(v0);
+// const PxReal h1 = getHeight(v1);
+// const PxReal h2 = getHeight(v2);
+ const PxI32 h0 = getSample(v0).height;
+ const PxI32 h1 = getSample(v1).height;
+ const PxI32 h2 = getSample(v2).height;
+
+ // Fix for NvBug 685420
+// if(mThickness>0.0f)
+// n = -n;
+ const PxReal coeff = physx::intrinsics::fsel(mData.thickness, -1.0f, 1.0f);
+
+// PxVec3 n(0,1,0);
+ const PxU32 cell = triangleIndex >> 1;
+ if (isZerothVertexShared(cell))
+ {
+ // <---- COL
+ // 0----2 1 R
+ // | 1 / /| O
+ // | / / | W
+ // | / / | |
+ // |/ / 0 | |
+ // 1 2----0 V
+ //
+ if (isFirstTriangle(triangleIndex))
+ {
+// n.x = -(h0-h1);
+// n.z = -(h2-h0);
+ return PxVec3(coeff*PxReal(h1-h0), coeff, coeff*PxReal(h0-h2));
+ }
+ else
+ {
+// n.x = -(h1-h0);
+// n.z = -(h0-h2);
+ return PxVec3(coeff*PxReal(h0-h1), coeff, coeff*PxReal(h2-h0));
+ }
+ }
+ else
+ {
+ // <---- COL
+ // 2 1----0 R
+ // |\ \ 0 | O
+ // | \ \ | W
+ // | \ \ | |
+ // | 1 \ \| |
+ // 0----1 2 V
+ //
+ if (isFirstTriangle(triangleIndex))
+ {
+// n.x = -(h2-h0);
+// n.z = -(h1-h0);
+ return PxVec3(coeff*PxReal(h0-h2), coeff, coeff*PxReal(h0-h1));
+ }
+ else
+ {
+// n.x = -(h0-h2);
+// n.z = -(h0-h1);
+ return PxVec3(coeff*PxReal(h2-h0), coeff, coeff*PxReal(h1-h0));
+ }
+ }
+// return n;
+}
+
+PX_INLINE PxReal Gu::HeightField::getHeightInternal2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ) const
+{
+ if (isZerothVertexShared(vertexIndex))
+ {
+ // <----Z---+
+ // +----+ |
+ // | /| |
+ // | / | X
+ // | / | |
+ // |/ | |
+ // +----+ |
+ // V
+ const PxReal h0 = getHeight(vertexIndex);
+ const PxReal h2 = getHeight(vertexIndex + mData.columns + 1);
+ if (fracZ > fracX)
+ {
+ // <----Z---+
+ // 1----0 |
+ // | / |
+ // | / X
+ // | / |
+ // |/ |
+ // 2 |
+ // V
+ const PxReal h1 = getHeight(vertexIndex + 1);
+ return h0 + fracZ*(h1-h0) + fracX*(h2-h1);
+ }
+ else
+ {
+ // <----Z---+
+ // 0 |
+ // /| |
+ // / | X
+ // / | |
+ // / | |
+ // 2----1 |
+ // V
+ const PxReal h1 = getHeight(vertexIndex + mData.columns);
+ return h0 + fracX*(h1-h0) + fracZ*(h2-h1);
+ }
+ }
+ else
+ {
+ // <----Z---+
+ // +----+ |
+ // |\ | |
+ // | \ | X
+ // | \ | |
+ // | \| |
+ // +----+ |
+ // V
+ const PxReal h2 = getHeight(vertexIndex + mData.columns);
+ const PxReal h1 = getHeight(vertexIndex + 1);
+ if (fracX + fracZ < 1.0f)
+ {
+ // <----Z---+
+ // 1----0 |
+ // \ | |
+ // \ | X
+ // \ | |
+ // \| |
+ // 2 |
+ // V
+ const PxReal h0 = getHeight(vertexIndex);
+ return h0 + fracZ*(h1-h0) + fracX*(h2-h0);
+ }
+ else
+ {
+ // <----Z---+
+ // 1 |
+ // |\ |
+ // | \ X
+ // | \ |
+ // | \ |
+ // 0----2 |
+ // V
+ //
+ // Note that we need to flip fracX and fracZ since we are moving the origin
+ const PxReal h0 = getHeight(vertexIndex + mData.columns + 1);
+ return h0 + (1.0f - fracZ)*(h2-h0) + (1.0f - fracX)*(h1-h0);
+ }
+ }
+}
+
+PX_INLINE PxVec3 Gu::HeightField::getNormal_2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ, PxReal xcoeff, PxReal ycoeff, PxReal zcoeff) const
+{
+ PxVec3 normal;
+ if (isZerothVertexShared(vertexIndex))
+ {
+ // <----Z---+
+ // +----+ |
+ // | /| |
+ // | / | X
+ // | / | |
+ // |/ | |
+ // +----+ |
+ // V
+// const PxReal h0 = getHeight(vertexIndex);
+// const PxReal h2 = getHeight(vertexIndex + mData.columns + 1);
+ const PxI32 ih0 = getSample(vertexIndex).height;
+ const PxI32 ih2 = getSample(vertexIndex + mData.columns + 1).height;
+ if (fracZ >= fracX)
+ {
+ // <----Z---+
+ // 1----0 |
+ // | / |
+ // | / X
+ // | / |
+ // |/ |
+ // 2 |
+ // V
+// const PxReal h0 = getHeight(vertexIndex);
+// const PxReal h1 = getHeight(vertexIndex + 1);
+// const PxReal h2 = getHeight(vertexIndex + mData.columns + 1);
+// normal.set(-(h2-h1), 1.0f, -(h1-h0));
+ const PxI32 ih1 = getSample(vertexIndex + 1).height;
+ normal = PxVec3(PxReal(ih1 - ih2)*xcoeff, ycoeff, PxReal(ih0 - ih1)*zcoeff);
+ }
+ else
+ {
+ // <----Z---+
+ // 0 |
+ // /| |
+ // / | X
+ // / | |
+ // / | |
+ // 2----1 |
+ // V
+// const PxReal h0 = getHeight(vertexIndex);
+// const PxReal h1 = getHeight(vertexIndex + mData.columns);
+// const PxReal h2 = getHeight(vertexIndex + mData.columns + 1);
+// normal.set(-(h1-h0), 1.0f, -(h2-h1));
+ const PxI32 ih1 = getSample(vertexIndex + mData.columns).height;
+ normal = PxVec3(PxReal(ih0 - ih1)*xcoeff, ycoeff, PxReal(ih1 - ih2)*zcoeff);
+ }
+ }
+ else
+ {
+ // <----Z---+
+ // +----+ |
+ // |\ | |
+ // | \ | X
+ // | \ | |
+ // | \| |
+ // +----+ |
+ // V
+ const PxI32 ih1 = getSample(vertexIndex + 1).height;
+ const PxI32 ih2 = getSample(vertexIndex + mData.columns).height;
+ if (fracX + fracZ <= PxReal(1))
+ {
+ // <----Z---+
+ // 1----0 |
+ // \ | |
+ // \ | X
+ // \ | |
+ // \| |
+ // 2 |
+ // V
+// const PxReal h0 = getHeight(vertexIndex);
+// const PxReal h1 = getHeight(vertexIndex + 1);
+// const PxReal h2 = getHeight(vertexIndex + mData.columns);
+// normal.set(-(h2-h0), 1.0f, -(h1-h0));
+ const PxI32 ih0 = getSample(vertexIndex).height;
+// const PxI32 ih1 = getSample(vertexIndex + 1).height;
+// const PxI32 ih2 = getSample(vertexIndex + mData.columns).height;
+ normal = PxVec3(PxReal(ih0 - ih2)*xcoeff, ycoeff, PxReal(ih0 - ih1)*zcoeff);
+ }
+ else
+ {
+ // <----Z---+
+ // 2 |
+ // |\ |
+ // | \ X
+ // | \ |
+ // | \ |
+ // 0----1 |
+ // V
+ //
+ // Note that we need to flip fracX and fracZ since we are moving the origin
+// const PxReal h2 = getHeight(vertexIndex + 1);
+// const PxReal h1 = getHeight(vertexIndex + mData.columns);
+// const PxReal h0 = getHeight(vertexIndex + mData.columns + 1);
+// normal.set(-(h0-h2), 1.0f, -(h0-h1));
+// const PxI32 ih2 = getSample(vertexIndex + 1).height;
+// const PxI32 ih1 = getSample(vertexIndex + mData.columns).height;
+ const PxI32 ih0 = getSample(vertexIndex + mData.columns + 1).height;
+// normal.set(PxReal(ih2 - ih0), 1.0f, PxReal(ih1b - ih0));
+ normal = PxVec3(PxReal(ih1 - ih0)*xcoeff, ycoeff, PxReal(ih2 - ih0)*zcoeff);
+ }
+ }
+ return (mData.thickness <= 0.0f) ? normal : -normal;
+}
+
+PX_INLINE PxU32 Gu::HeightField::getTriangleIndex2(PxU32 cell, PxReal fracX, PxReal fracZ) const
+{
+ if (isZerothVertexShared(cell))
+ return (fracZ > fracX) ? (cell << 1) + 1 : (cell << 1);
+ else
+ return (fracX + fracZ > 1) ? (cell << 1) + 1 : (cell << 1);
+}
+
+PX_INLINE PxU32 Gu::HeightField::getTriangleIndex(PxReal x, PxReal z) const
+{
+ PxReal fracX, fracZ;
+ const PxU32 cell = computeCellCoordinates(x, z, fracX, fracZ);
+
+ return getTriangleIndex2(cell, fracX, fracZ);
+}
+
+/**
+Although inefficient, this is used for particles (PxcHeightFieldAabbTest.h).
+*/
+PX_FORCE_INLINE void Gu::HeightField::getTriangleVertices(PxU32 triangleIndex, PxU32 row, PxU32 column, PxVec3& v0, PxVec3& v1, PxVec3& v2) const
+{
+ PxU32 cell = triangleIndex >> 1;
+ PX_ASSERT(row * getNbColumnsFast() + column == cell);
+
+ PxReal h0 = getHeight(cell);
+ PxReal h1 = getHeight(cell + 1);
+ PxReal h2 = getHeight(cell + getNbColumnsFast());
+ PxReal h3 = getHeight(cell + getNbColumnsFast() + 1);
+
+ if (isFirstTriangle(triangleIndex))
+ {
+ if (isZerothVertexShared(cell))
+ {
+ // <---- COL
+ // 1 R
+ // /| O
+ // / | W
+ // / | |
+ // / 0 | |
+ // 2----0 V
+ //
+ v0 = PxVec3(PxReal(row + 1), h2, PxReal(column ));
+ v1 = PxVec3(PxReal(row ), h0, PxReal(column ));
+ v2 = PxVec3(PxReal(row + 1), h3, PxReal(column + 1));
+ }
+ else
+ {
+ // <---- COL
+ // 1----0 R
+ // \ 0 | O
+ // \ | W
+ // \ | |
+ // \| |
+ // 2 V
+ //
+ v0 = PxVec3(PxReal(row ), h0, PxReal(column ));
+ v1 = PxVec3(PxReal(row ), h1, PxReal(column + 1));
+ v2 = PxVec3(PxReal(row + 1), h2, PxReal(column ));
+ }
+ }
+ else
+ {
+ if (isZerothVertexShared(cell))
+ {
+ // <---- COL
+ // 0----2 R
+ // | 1 / O
+ // | / W
+ // | / |
+ // |/ |
+ // 1 V
+ //
+ v0 = PxVec3(PxReal(row ), h1, PxReal(column + 1));
+ v1 = PxVec3(PxReal(row + 1), h3, PxReal(column + 1));
+ v2 = PxVec3(PxReal(row ), h0, PxReal(column ));
+ }
+ else
+ {
+ // <---- COL
+ // 2 R
+ // |\ O
+ // | \ W
+ // | \ |
+ // | 1 \ |
+ // 0----1 V
+ //
+ v0 = PxVec3(PxReal(row + 1), h3, PxReal(column + 1));
+ v1 = PxVec3(PxReal(row + 1), h2, PxReal(column ));
+ v2 = PxVec3(PxReal(row ), h1, PxReal(column + 1));
+ }
+ }
+}
+
+struct EdgeData
+{
+ PxU32 edgeIndex;
+ PxU32 cell;
+ PxU32 row;
+ PxU32 column;
+};
+PX_PHYSX_COMMON_API PxU32 getVertexEdgeIndices(const Gu::HeightField& heightfield, PxU32 vertexIndex, PxU32 row, PxU32 column, EdgeData edgeIndices[8]);
+PX_PHYSX_COMMON_API PxU32 getEdgeTriangleIndices(const Gu::HeightField& heightfield, const EdgeData& edgeData, PxU32* PX_RESTRICT triangleIndices);
+
+}
+
+#endif
diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldData.h b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldData.h
new file mode 100644
index 00000000..74c746dc
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldData.h
@@ -0,0 +1,88 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved.
+// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
+// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
+
+#ifndef GU_HEIGHTFIELD_DATA_H
+#define GU_HEIGHTFIELD_DATA_H
+
+#include "foundation/PxSimpleTypes.h"
+#include "PxHeightFieldFlag.h"
+#include "PxHeightFieldSample.h"
+#include "GuCenterExtents.h"
+
+namespace physx
+{
+
+namespace Gu
+{
+
+#if PX_VC
+ #pragma warning(push)
+ #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class
+#endif
+struct PX_PHYSX_COMMON_API HeightFieldData
+{
+// PX_SERIALIZATION
+ PX_FORCE_INLINE HeightFieldData() {}
+ PX_FORCE_INLINE HeightFieldData(const PxEMPTY) : flags(PxEmpty) {}
+//~PX_SERIALIZATION
+
+ //properties
+ // PT: WARNING: bounds must be followed by at least 32bits of data for safe SIMD loading
+ CenterExtents mAABB;
+ PxU32 rows; // PT: WARNING: don't change this member's name (used in ConvX)
+ PxU32 columns; // PT: WARNING: don't change this member's name (used in ConvX)
+ PxReal rowLimit; // PT: to avoid runtime int-to-float conversions on Xbox
+ PxReal colLimit; // PT: to avoid runtime int-to-float conversions on Xbox
+ PxReal nbColumns; // PT: to avoid runtime int-to-float conversions on Xbox
+ PxHeightFieldSample* samples; // PT: WARNING: don't change this member's name (used in ConvX)
+ PxReal thickness;
+ PxReal convexEdgeThreshold;
+
+ PxHeightFieldFlags flags;
+
+ PxHeightFieldFormat::Enum format;
+
+ PX_FORCE_INLINE const CenterExtentsPadded& getPaddedBounds() const
+ {
+ // PT: see compile-time assert below
+ return static_cast<const CenterExtentsPadded&>(mAABB);
+ }
+};
+#if PX_VC
+ #pragma warning(pop)
+#endif
+
+ // PT: 'getPaddedBounds()' is only safe if we make sure the bounds member is followed by at least 32bits of data
+ PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(Gu::HeightFieldData, rows)>=PX_OFFSET_OF(Gu::HeightFieldData, mAABB)+4);
+
+} // namespace Gu
+
+}
+
+#endif
diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.cpp b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.cpp
new file mode 100644
index 00000000..a5ffaccf
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.cpp
@@ -0,0 +1,1055 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved.
+// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
+// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
+
+
+#include "PsIntrinsics.h"
+#include "GuHeightFieldUtil.h"
+#include "GuSweepSharedTests.h"
+
+#include "PsFoundation.h"
+#include "GuHeightField.h"
+#include "GuEntityReport.h"
+#include "PxMeshScale.h"
+
+using namespace physx;
+
+void Gu::HeightFieldUtil::computeLocalBounds(PxBounds3& bounds) const
+{
+ const PxMeshScale scale(PxVec3(mHfGeom->rowScale, mHfGeom->heightScale, mHfGeom->columnScale), PxQuat(PxIdentity));
+ const PxMat33 mat33 = scale.toMat33();
+
+ bounds.minimum = mat33.transform(mHeightField->getData().mAABB.getMin());
+ bounds.maximum = mat33.transform(mHeightField->getData().mAABB.getMax());
+
+ if(mHeightField->getData().thickness < 0)
+ bounds.minimum.y += mHeightField->getData().thickness;
+ else
+ bounds.maximum.y += mHeightField->getData().thickness;
+}
+
+PxU32 Gu::HeightFieldUtil::getFaceIndexAtShapePoint(PxReal x, PxReal z) const
+{
+ if (isShapePointOnHeightField(x, z))
+ {
+ const PxU32 triangleIndex = mHeightField->getTriangleIndex(x * mOneOverRowScale, z * mOneOverColumnScale);
+ return (mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE) ? triangleIndex : 0xffffffff;
+ }
+ return 0xffffffff;
+}
+
+PxU32 Gu::HeightFieldUtil::getFaceIndexAtTriangleIndex(PxU32 triangleIndex) const
+{
+ return (mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE) ? triangleIndex : 0xffffffff;
+}
+
+PxU32 Gu::HeightFieldUtil::getFaceIndexAtShapePointNoTest(PxReal x, PxReal z) const
+{
+ PX_ASSERT(isShapePointOnHeightField(x, z));
+
+ const PxU32 triangleIndex = mHeightField->getTriangleIndex(x * mOneOverRowScale, z * mOneOverColumnScale);
+ return (mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE) ? triangleIndex : 0xffffffff;
+}
+
+PxU32 Gu::HeightFieldUtil::getFaceIndexAtShapePointNoTest2(PxU32 cell, PxReal fracX, PxReal fracZ) const
+{
+ const PxU32 triangleIndex = mHeightField->getTriangleIndex2(cell, fracX, fracZ);
+ return (mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE) ? triangleIndex : 0xffffffff;
+}
+
+
+PxVec3 Gu::HeightFieldUtil::getSmoothNormalAtShapePoint(PxReal x, PxReal z) const
+{
+#ifdef PX_HEIGHTFIELD_DEBUG
+ PX_ASSERT(getFaceIndexAtShapePoint(x, z) != 0xffffffff);
+#endif
+ x *= mOneOverRowScale;
+ z *= mOneOverColumnScale;
+
+ PxReal fracX, fracZ;
+ const PxU32 cell = mHeightField->computeCellCoordinates(x, z, fracX, fracZ);
+
+ if (mHeightField->isZerothVertexShared(cell))
+ {
+ // <----Z---+
+ // +----+ |
+ // | /| |
+ // | / | X
+ // | / | |
+ // |/ | |
+ // +----+ |
+ // V
+ if (fracZ > fracX)
+ {
+ // <----Z---+
+ // 1----0 |
+ // | / |
+ // | / X
+ // | / |
+ // |/ |
+ // 2 |
+ // V
+ const PxVec3 n0 = getVertexNormal(cell);
+ const PxVec3 n1 = getVertexNormal(cell + 1);
+ const PxVec3 n2 = getVertexNormal(cell + mHeightField->getNbColumnsFast() + 1);
+ return n0 + fracZ*(n1-n0) + fracX*(n2-n1);
+ }
+ else
+ {
+ // <----Z---+
+ // 0 |
+ // /| |
+ // / | X
+ // / | |
+ // / | |
+ // 2----1 |
+ // V
+ const PxVec3 n0 = getVertexNormal(cell);
+ const PxVec3 n1 = getVertexNormal(cell + mHeightField->getNbColumnsFast());
+ const PxVec3 n2 = getVertexNormal(cell + mHeightField->getNbColumnsFast() + 1);
+ return n0 + fracX*(n1-n0) + fracZ*(n2-n1);
+ }
+ }
+ else
+ {
+ // <----Z---+
+ // +----+ |
+ // |\ | |
+ // | \ | X
+ // | \ | |
+ // | \| |
+ // +----+ |
+ // V
+ if (fracX + fracZ < 1)
+ {
+ // <----Z---+
+ // 1----0 |
+ // \ | |
+ // \ | X
+ // \ | |
+ // \| |
+ // 2 |
+ // V
+ const PxVec3 n0 = getVertexNormal(cell);
+ const PxVec3 n1 = getVertexNormal(cell + 1);
+ const PxVec3 n2 = getVertexNormal(cell + mHeightField->getNbColumnsFast());
+ return n0 + fracZ*(n1-n0) + fracX*(n2-n0);
+ }
+ else
+ {
+ // <----Z---+
+ // 2 |
+ // |\ |
+ // | \ X
+ // | \ |
+ // | \ |
+ // 0----1 |
+ // V
+ //
+ // Note that we need to flip fracX and fracZ since we are moving the origin
+ const PxVec3 n0 = getVertexNormal(cell + mHeightField->getNbColumnsFast() + 1);
+ const PxVec3 n1 = getVertexNormal(cell + mHeightField->getNbColumnsFast());
+ const PxVec3 n2 = getVertexNormal(cell + 1);
+ return n0 + (1-fracZ)*(n1-n0) + (1-fracX)*(n2 - n0);
+ }
+ }
+}
+
+PxVec3 Gu::HeightFieldUtil::getVertexNormal(PxU32 vertexIndex, PxU32 row, PxU32 column) const
+{
+#ifdef PX_HEIGHTFIELD_DEBUG
+ PX_ASSERT(mHeightField->isValidVertex(vertexIndex));
+#endif
+ // PxU32 edges[8];
+ // const PxU32 edgeCount = mHeightField.getVertexEdgeIndices(vertexIndex, edges);
+
+ //const PxU32 nbColumns = mHeightField.getData().columns;
+ //const PxU32 row = vertexIndex / nbColumns;
+ //const PxU32 column = vertexIndex % nbColumns;
+ PX_ASSERT(row == vertexIndex / mHeightField->getData().columns);
+ PX_ASSERT(column == vertexIndex % mHeightField->getData().columns);
+ EdgeData edgeIndices[8];
+ const PxU32 edgeCount = ::getVertexEdgeIndices(*mHeightField, vertexIndex, row, column, edgeIndices);
+
+
+ PxVec3 n(0.0f);
+ PxVec3 tn;
+ for (PxU32 i=0; i<edgeCount; i++)
+ {
+ PxU32 faces[2];
+ // const PxU32 faceCount = mHeightField.getEdgeTriangleIndices(edges[i], faces);
+ const PxU32 faceCount = ::getEdgeTriangleIndices(*mHeightField, edgeIndices[i], faces);
+
+ for(PxU32 j=0; j<faceCount; j++)
+ {
+ if (mHeightField->getTriangleMaterial(faces[j]) != PxHeightFieldMaterial::eHOLE)
+ {
+ tn = hf2shapen(mHeightField->getTriangleNormalInternal(faces[j])).getNormalized();
+ n+=tn;
+ }
+ }
+ }
+
+ return n.getNormalized();
+}
+
+PxU32 Gu::HeightFieldUtil::findClosestPointsOnCell(
+ PxU32 row, PxU32 column, PxVec3 point,
+ PxVec3* PX_RESTRICT closestPoints, PxU32* PX_RESTRICT featureCodes,
+ bool testFaces, bool testEdges, bool skipEdgesIfFaceHits) const
+{
+ PxU32 count = 0;
+
+ const PxU32 offset = row * mHeightField->getNbColumnsFast() + column;
+ const PxU32 firstEdgeIndex = 3 * offset;
+
+ // ptchernev TODO:
+ // move the material assignments to an else in the ifs on triangle material
+ // instead of doing it all the time
+
+ PX_ASSERT(row < (mHeightField->getNbRowsFast() - 1));
+ PX_ASSERT(column < (mHeightField->getNbColumnsFast() - 1));
+ const bool lastRow = (row == (mHeightField->getNbRowsFast() - 2));
+ const bool lastColumn = (column == (mHeightField->getNbColumnsFast() - 2));
+
+ bool testVertex0 = testEdges;
+ bool testColumnEdge0 = testEdges;
+ bool testRowEdge0 = testEdges;
+ bool testDiagonal = testEdges;
+ bool testVertex1 = lastColumn && testEdges;
+ bool testVertex2 = lastRow && testEdges;
+
+ bool testRowEdge1 = lastColumn && testEdges;
+ bool testColumnEdge1 = lastRow && testEdges;
+ bool testVertex3 = lastRow && lastColumn && testEdges;
+
+ const PxU32 triangleIndex0 = offset << 1;
+ const PxMaterialTableIndex materialIndex0 = mHeightField->getTriangleMaterial(triangleIndex0);
+ const PxU32 triangleIndex1 = triangleIndex0 + 1;
+ const PxMaterialTableIndex materialIndex1 = mHeightField->getTriangleMaterial(triangleIndex1);
+
+ if (testFaces)
+ {
+ if (materialIndex0 != PxHeightFieldMaterial::eHOLE)
+ {
+ // face 0
+ PxVec3 closestPoint;
+ if (findProjectionOnTriangle(triangleIndex0, row, column, point, closestPoint))
+ {
+ closestPoints[count] = closestPoint;
+ if (featureCodes) featureCodes[count] = makeFeatureCode(triangleIndex0, eFACE);
+ count++;
+ testRowEdge0 = false;
+ testVertex0 = false;
+ testVertex2 = false;
+ testDiagonal = false;
+ }
+ }
+
+ if (materialIndex1 != PxHeightFieldMaterial::eHOLE)
+ {
+ // face 1
+ PxVec3 closestPoint;
+ if (findProjectionOnTriangle(triangleIndex1, row, column, point, closestPoint))
+ {
+ closestPoints[count] = closestPoint;
+ if (featureCodes) featureCodes[count] = makeFeatureCode(triangleIndex1, eFACE);
+ count++;
+ testRowEdge1 = false;
+ testVertex1 = false;
+ testVertex3 = false;
+ testDiagonal = false;
+ }
+ }
+ if (!testEdges)
+ return count;
+ }
+
+ // if there were any face contacts and we asked to skip edges if face contacts, return current count here
+ if (count && skipEdgesIfFaceHits)
+ return count;
+
+ const PxU32 nbColumns = mHeightField->getNbColumnsFast();
+ if (testVertex0 || testColumnEdge0 || testVertex1)
+ {
+ PxVec3 closestPoint;
+ PxReal t = findClosestPointOnEdge(firstEdgeIndex, offset, row, column, point, closestPoint);
+ if (t <= 0)
+ {
+ if (testVertex0 && 0xffffffff != (getVertexFaceIndex(offset, row, column)))
+ {
+ closestPoints[count] = closestPoint;
+ if (featureCodes) featureCodes[count] = makeFeatureCode(row*nbColumns+column, eVERTEX);
+ count++;
+ }
+ testVertex0 = false;
+ }
+ else if (t < 1)
+ {
+ if (testColumnEdge0 && 0xffffffff != (getEdgeFaceIndex(firstEdgeIndex)))
+ {
+ closestPoints[count] = closestPoint;
+ if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex, eEDGE);
+ count++;
+ }
+ }
+ else
+ {
+ if (testVertex1 && 0xffffffff != (getVertexFaceIndex(offset + 1, row, column + 1)))
+ {
+ closestPoints[count] = closestPoint;
+ if (featureCodes) featureCodes[count] = makeFeatureCode(row*nbColumns+column+1, eVERTEX);
+ count++;
+ }
+ }
+ }
+
+ if (testVertex0 || testRowEdge0 || testVertex2)
+ {
+ PxVec3 closestPoint;
+ PxReal t = findClosestPointOnEdge(firstEdgeIndex + 2, offset, row, column, point, closestPoint);
+ if (t <= 0)
+ {
+ if (testVertex0 && 0xffffffff != (getVertexFaceIndex(offset, row, column)))
+ {
+ closestPoints[count] = closestPoint;
+ if (featureCodes) featureCodes[count] = makeFeatureCode(row*nbColumns+column, eVERTEX);
+ count++;
+ }
+ }
+ else if(t < 1)
+ {
+ if (testRowEdge0 && 0xffffffff != (getEdgeFaceIndex(firstEdgeIndex + 2)))
+ {
+ closestPoints[count] = closestPoint;
+ if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex+2, eEDGE);
+ count++;
+ }
+ }
+ else
+ {
+ if (testVertex2 && 0xffffffff != (getVertexFaceIndex(offset + nbColumns, row + 1, column)))
+ {
+ closestPoints[count] = closestPoint;
+ if (featureCodes) featureCodes[count] = makeFeatureCode((row+1)*nbColumns+column, eVERTEX);
+ count++;
+ }
+ }
+ }
+
+ if (testColumnEdge1)
+ {
+ PxVec3 closestPoint;
+ PxReal t = findClosestPointOnEdge(firstEdgeIndex + 3 * nbColumns, offset + nbColumns, row + 1, column, point, closestPoint);
+ if (t <= 0)
+ ; // do nothing
+ else if (t < 1)
+ {
+ const PxU32 edgeIndex3 = firstEdgeIndex + 3 * nbColumns;
+ if (0xffffffff != (getEdgeFaceIndex(edgeIndex3)))
+ {
+ closestPoints[count] = closestPoint;
+ if (featureCodes) featureCodes[count] = makeFeatureCode(edgeIndex3, eEDGE);
+ count++;
+ }
+ }
+ }
+
+ if (testRowEdge1)
+ {
+ PxVec3 closestPoint;
+ PxReal t = findClosestPointOnEdge(firstEdgeIndex + 5, offset + 1, row, column + 1, point, closestPoint);
+ if (t <= 0)
+ ; // do nothing
+ else if (t < 1)
+ {
+ if (0xffffffff != (getEdgeFaceIndex(firstEdgeIndex + 5)))
+ {
+ closestPoints[count] = closestPoint;
+ if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex+5, eEDGE);
+ count++;
+ }
+ }
+ }
+
+ if (testVertex3 && 0xffffffff != (getVertexFaceIndex(offset + nbColumns + 1, row + 1, column + 1)))
+ {
+ closestPoints[count] = PxVec3((row + 1) * mHfGeom->rowScale, mHfGeom->heightScale * mHeightField->getHeight(offset + mHeightField->getNbColumnsFast() + 1), (column + 1) * mHfGeom->columnScale);
+ if (featureCodes) featureCodes[count] = makeFeatureCode((row+1)*nbColumns+column+1, eVERTEX);
+ count++;
+ }
+
+ if (testDiagonal && 0xffffffff != (getEdgeFaceIndex(firstEdgeIndex + 1)))
+ {
+ PxVec3 closestPoint;
+ PxReal t = findClosestPointOnEdge(firstEdgeIndex + 1, offset, row, column, point, closestPoint);
+ if (t <= 0)
+ ; // do nothing
+ else if (t < 1)
+ {
+ closestPoints[count] = closestPoint;
+ if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex+1, eEDGE);
+ count++;
+ }
+ }
+
+ return count;
+}
+
+//PxReal Gu::HeightFieldUtil::findClosestPointOnEdge(PxU32 edgeIndex, const PxVec3& point, PxVec3& closestPoint) const
+PxReal Gu::HeightFieldUtil::findClosestPointOnEdge(
+ PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& closestPoint) const
+{
+// const PxU32 cell = edgeIndex / 3;
+ PX_ASSERT(cell == edgeIndex / 3);
+// const PxU32 row = cell / mHeightField->getNbColumnsFast();
+ PX_ASSERT(row == cell / mHeightField->getNbColumnsFast());
+// const PxU32 column = cell % mHeightField->getNbColumnsFast();
+ PX_ASSERT(column == cell % mHeightField->getNbColumnsFast());
+
+ PxVec3 origin, direction;
+ PxReal lengthSquared;
+// switch (edgeIndex % 3)
+ switch (edgeIndex - cell*3)
+ {
+ case 0:
+ {
+ const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell);
+ const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1);
+ origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale);
+ const PxReal dy = y1 - y0;
+ direction = PxVec3(0, dy, mHfGeom->columnScale);
+ lengthSquared = mHfGeom->columnScale * mHfGeom->columnScale + dy * dy;
+ }
+ break;
+ case 1:
+ if (mHeightField->isZerothVertexShared(cell))
+ {
+ const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell);
+ const PxReal y3 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast() + 1);
+ origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale);
+ const PxReal dy = y3 - y0;
+ direction = PxVec3(mHfGeom->rowScale, dy, mHfGeom->columnScale);
+ lengthSquared = mHfGeom->rowScale * mHfGeom->rowScale + mHfGeom->columnScale * mHfGeom->columnScale + dy * dy;
+ }
+ else
+ {
+ const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1);
+ const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast());
+ origin = PxVec3(row * mHfGeom->rowScale, y1, (column + 1) * mHfGeom->columnScale);
+ const PxReal dy = y2 - y1;
+ direction = PxVec3(mHfGeom->rowScale, dy, -mHfGeom->columnScale);
+ lengthSquared = mHfGeom->rowScale * mHfGeom->rowScale + mHfGeom->columnScale * mHfGeom->columnScale + dy * dy;
+ }
+ break;
+ case 2:
+ {
+ const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell);
+ const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast());
+ origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale);
+ const PxReal dy = y2 - y0;
+ direction = PxVec3(mHfGeom->rowScale, dy, 0);
+ lengthSquared = mHfGeom->rowScale * mHfGeom->rowScale + dy * dy;
+ }
+ break;
+ default:
+ origin = direction = PxVec3(PxReal(0));
+ lengthSquared = 0.0f;
+ PX_ALWAYS_ASSERT_MESSAGE("Invalid edge index in findClosestPointOnEdge");
+ } // switch (edgeIndex % 3)
+
+ const PxVec3 relative = point - origin;
+ const PxReal t = relative.dot(direction) / lengthSquared;
+ if (t < 0)
+ closestPoint = origin;
+ else if (t > 1)
+ closestPoint = origin + direction;
+ else
+ closestPoint = origin + direction * t;
+
+ return t;
+}
+
+PxU32 Gu::HeightFieldUtil::getVertexFaceIndex(PxU32 vertexIndex, PxU32 row, PxU32 column) const
+{
+#ifdef PX_HEIGHTFIELD_DEBUG
+ PX_ASSERT(mHeightField->isValidVertex(vertexIndex));
+#endif
+
+// PxU32 edgeIndices[8];
+// const PxU32 count = mHeightField->getVertexEdgeIndices(vertexIndex, edgeIndices);
+
+//const PxU32 nbColumns = mHeightField->getData().columns;
+//const PxU32 row = vertexIndex / nbColumns;
+//const PxU32 column = vertexIndex % nbColumns;
+PX_ASSERT(row == vertexIndex / mHeightField->getData().columns);
+PX_ASSERT(column == vertexIndex % mHeightField->getData().columns);
+EdgeData edgeIndices[8];
+const PxU32 count = ::getVertexEdgeIndices(*mHeightField, vertexIndex, row, column, edgeIndices);
+
+ for (PxU32 i = 0; i<count; i+= 2)
+ {
+ const PxU32 index = getEdgeFaceIndex(edgeIndices[i].edgeIndex, edgeIndices[i].cell, edgeIndices[i].row, edgeIndices[i].column);
+ if (index != 0xffffffff) return index;
+ }
+ return 0xffffffff;
+}
+
+PxU32 Gu::HeightFieldUtil::getEdgeFaceIndex(PxU32 edgeIndex) const
+{
+#ifdef PX_HEIGHTFIELD_DEBUG
+ PX_ASSERT(mHeightField->isValidEdge(edgeIndex));
+#endif
+ PxU32 faceIndices[2];
+ const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices);
+ if (count > 1)
+ {
+ // ptchernev TODO: this is a bit arbitrary
+ if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0];
+ if (mHeightField->getTriangleMaterial(faceIndices[1]) != PxHeightFieldMaterial::eHOLE) return faceIndices[1];
+ }
+ else
+ {
+ if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0];
+ }
+ return 0xffffffff;
+}
+
+PxU32 Gu::HeightFieldUtil::getEdgeFaceIndex(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const
+{
+#ifdef PX_HEIGHTFIELD_DEBUG
+ PX_ASSERT(mHeightField->isValidEdge(edgeIndex));
+#endif
+ PxU32 faceIndices[2];
+ const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices, cell, row, column);
+ if (count > 1)
+ {
+ // ptchernev TODO: this is a bit arbitrary
+ if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0];
+ if (mHeightField->getTriangleMaterial(faceIndices[1]) != PxHeightFieldMaterial::eHOLE) return faceIndices[1];
+ }
+ else
+ {
+ if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0];
+ }
+ return 0xffffffff;
+}
+
+PxU32 Gu::HeightFieldUtil::getEdgeFaceIndex(PxU32 edgeIndex, PxU32 count, const PxU32* PX_RESTRICT faceIndices) const
+{
+ PX_UNUSED(edgeIndex);
+
+#ifdef PX_HEIGHTFIELD_DEBUG
+ PX_ASSERT(mHeightField->isValidEdge(edgeIndex));
+#endif
+// PxU32 faceIndices[2];
+// const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices);
+ if (count > 1)
+ {
+ // ptchernev TODO: this is a bit arbitrary
+ if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0];
+ if (mHeightField->getTriangleMaterial(faceIndices[1]) != PxHeightFieldMaterial::eHOLE) return faceIndices[1];
+ }
+ else
+ {
+ if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0];
+ }
+ return 0xffffffff;
+}
+
+bool Gu::HeightFieldUtil::findProjectionOnTriangle(PxU32 triangleIndex, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& projection) const
+{
+ const PxU32 cell = (triangleIndex >> 1);
+ PX_ASSERT(row == cell / mHeightField->getNbColumnsFast());
+ PX_ASSERT(column == cell % mHeightField->getNbColumnsFast());
+ const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell);
+ const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1);
+ const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast());
+ const PxReal y3 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast() + 1);
+ PxVec3 origin;
+ PxReal h0, h1, h2, uInvScale, vInvScale;
+
+ // specify a triangle according to current triangle index as origin, 3 heights and (uInvScale, vInvScale) vector for (z,x)
+ // set uInvScale in h1-h0; vScale in h2-h0 direction
+ if (mHeightField->isZerothVertexShared(cell))
+ {
+ // COLUMN -->
+ //
+ // R 0---1
+ // O |\ 1|
+ // W | \ |
+ // | |0 \|
+ // | 2---3
+ // V
+ if ((triangleIndex & 1) == 0)
+ {
+ // case 0
+ // face 0
+ origin = PxVec3((row + 1) * mHfGeom->rowScale, y2, column * mHfGeom->columnScale);
+ // origin -> 2
+ // verts -> 2,3,0
+ h0 = y2;
+ h1 = y3;
+ h2 = y0;
+ uInvScale = mOneOverColumnScale;
+ vInvScale = -mOneOverRowScale;
+ }
+ else // if (testFace1)
+ {
+ // case 1
+ // face 1
+ origin = PxVec3(row * mHfGeom->rowScale, y1, (column + 1) * mHfGeom->columnScale);
+ // origin -> 1
+ // verts -> 1,0,3
+ h0 = y1;
+ h1 = y0;
+ h2 = y3;
+ uInvScale = -mOneOverColumnScale;
+ vInvScale = mOneOverRowScale;
+ }
+ }
+ else
+ {
+ // COLUMN -->
+ //
+ // R 0---1
+ // O |0 /|
+ // W | / |
+ // | |/ 1|
+ // | 2---3
+ // V
+ if ((triangleIndex & 1) == 0)
+ {
+ // case 2
+ // face 0
+ origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale);
+ // origin -> 0
+ // verts -> 0,1,2
+ h0 = y0;
+ h1 = y1;
+ h2 = y2;
+ uInvScale = mOneOverColumnScale;
+ vInvScale = mOneOverRowScale;
+ }
+ else
+ {
+ // case 3
+ // face 1
+ origin = PxVec3((row + 1) * mHfGeom->rowScale, y3, (column + 1) * mHfGeom->columnScale);
+ // origin -> 3
+ // verts -> 3,2,1
+ h0 = y3;
+ h1 = y2;
+ h2 = y1;
+ uInvScale = -mOneOverColumnScale;
+ vInvScale = -mOneOverRowScale;
+ }
+ }
+
+ // vector from triangle origin to point we want to project
+ const PxVec3 relative = point - origin;
+
+ // Looking at the triangle diagram for case 2
+ // The normal computation should be
+ // n = (p1-p0) x (p2-p0)
+ // For a right handed cross product that's pointing into the screen (negative h), so -n is in the direction of increasing h
+ // cs = column scale, rs = row scale, h10 = h1-h0; u=column, v=row
+ // (i j k);
+ // p1-p0 = (cs, h10, 0); this is column, u and z
+ // p2-p0 = (0, h20, rs); this is row, v and x
+ // n = (h10*rs, -cs*rs, +cs*h20)
+ // n/(cs*rs) = (h10/cs, -1, h20/rs)
+ // -n = (-h10/cs, 1, -h20/rs)
+ PxReal h10 = h1-h0, h20 = h2-h0;
+ PxReal nu = -h10 * uInvScale;
+ PxReal nv = -h20 * vInvScale;
+
+ PxVec3 n(nv, 1.0f, nu); // for whatever reason.. x is v, z is u.
+ //n *= 1.0f / PxSqrt(nu*nu + nv*nv + 1.0f); // technically we need to do this but since later
+
+ // project relative onto the n plane, it gives us unclipped projection onto the triangle
+ // the computation without sqrt shortcut is relPrj = relative - n.dot(relative)*n, but because we divide by sqrt^2 we skip sqrt
+ PxVec3 relPrj = relative - n.dot(relative)* (1.0f/(nu*nu+nv*nv+1.0f)) * n;
+
+ // project relPrj onto 2d UV plane with h = 0 oriented according to vInvScale, uInvScale (for x and z)
+ // to convert to HF cell coords we'd multiply by inv scale, after that the coords should be >0 and the sum within 1 to be
+ // inside of a 2d triangle
+ PxReal scaledX = relPrj.x * vInvScale, scaledZ = relPrj.z * uInvScale;
+ //PxVec3 testProjection = relPrj + origin;
+ //PxVec3 testN = (point - testProjection).getNormalized();
+ if (scaledX > 0.0f && scaledZ > 0.0f && scaledX + scaledZ < 1.0f)
+ {
+ projection = relPrj + origin;
+ return true;
+ }
+
+ return false;
+}
+
+bool Gu::HeightFieldUtil::isCollisionEdge(PxU32 edgeIndex) const
+{
+#ifdef PX_HEIGHTFIELD_DEBUG
+ PX_ASSERT(mHeightField->isValidEdge(edgeIndex));
+#endif
+
+ // This code was simple, readable but slow
+ // return isBoundaryEdge(edgeIndex) || (mHeightField->isConvexEdge(edgeIndex) && isSolidEdge(edgeIndex));
+
+ PxU32 faceIndices[2];
+ const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices);
+ if (count > 1)
+ {
+ PxMaterialTableIndex mat0 = mHeightField->getTriangleMaterial(faceIndices[0]);
+ PxMaterialTableIndex mat1 = mHeightField->getTriangleMaterial(faceIndices[1]);
+ if (mat0 == PxHeightFieldMaterial::eHOLE) return (mat1 != PxHeightFieldMaterial::eHOLE);
+ if (mat1 == PxHeightFieldMaterial::eHOLE) return (mat0 != PxHeightFieldMaterial::eHOLE);
+ }
+ else
+ {
+ if (mHeightField->getFlagsFast() & PxHeightFieldFlag::eNO_BOUNDARY_EDGES) return false;
+ PxMaterialTableIndex mat0 = mHeightField->getTriangleMaterial(faceIndices[0]);
+ return (mat0 != PxHeightFieldMaterial::eHOLE);
+ }
+
+ return mHeightField->isConvexEdge(edgeIndex);
+}
+
+bool Gu::HeightFieldUtil::isCollisionEdge(PxU32 edgeIndex, PxU32 count, const PxU32* PX_RESTRICT faceIndices, PxU32 cell, PxU32 row, PxU32 column) const
+{
+#ifdef PX_HEIGHTFIELD_DEBUG
+ PX_ASSERT(mHeightField->isValidEdge(edgeIndex));
+#endif
+
+ // This code was simple, readable but slow
+ // return isBoundaryEdge(edgeIndex) || (mHeightField->isConvexEdge(edgeIndex) && isSolidEdge(edgeIndex));
+
+// PxU32 faceIndices[2];
+// const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices);
+ if (count > 1)
+ {
+ PxMaterialTableIndex mat0 = mHeightField->getTriangleMaterial(faceIndices[0]);
+ PxMaterialTableIndex mat1 = mHeightField->getTriangleMaterial(faceIndices[1]);
+ if (mat0 == PxHeightFieldMaterial::eHOLE) return (mat1 != PxHeightFieldMaterial::eHOLE);
+ if (mat1 == PxHeightFieldMaterial::eHOLE) return (mat0 != PxHeightFieldMaterial::eHOLE);
+ }
+ else
+ {
+ if (mHeightField->getFlagsFast() & PxHeightFieldFlag::eNO_BOUNDARY_EDGES) return false;
+ PxMaterialTableIndex mat0 = mHeightField->getTriangleMaterial(faceIndices[0]);
+ return (mat0 != PxHeightFieldMaterial::eHOLE);
+ }
+
+// return mHeightField->isConvexEdge(edgeIndex);
+ return mHeightField->isConvexEdge(edgeIndex, cell, row, column);
+}
+
+void Gu::HeightFieldUtil::getEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, PxVec3& origin, PxVec3& extent) const
+{
+#ifdef PX_HEIGHTFIELD_DEBUG
+ PX_ASSERT(mHeightField->isValidEdge(edgeIndex));
+#endif
+// const PxU32 cell = edgeIndex / 3;
+ PX_ASSERT(cell == edgeIndex / 3);
+// const PxU32 row = cell / mHeightField->getNbColumnsFast();
+ PX_ASSERT(row == cell / mHeightField->getNbColumnsFast());
+// const PxU32 column = cell % mHeightField->getNbColumnsFast();
+ PX_ASSERT(column == cell % mHeightField->getNbColumnsFast());
+
+// switch (edgeIndex % 3)
+ switch (edgeIndex - cell*3)
+ {
+ case 0:
+ {
+ const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell);
+ const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1);
+ origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale);
+ extent = PxVec3(0, y1 - y0, mHfGeom->columnScale);
+ }
+ break;
+ case 1:
+ if (mHeightField->isZerothVertexShared(cell))
+ {
+ const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell);
+ const PxReal y3 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast() + 1);
+ origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale);
+ extent = PxVec3(mHfGeom->rowScale, y3 - y0, mHfGeom->columnScale);
+ }
+ else
+ {
+ const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1);
+ const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast());
+ origin = PxVec3(row * mHfGeom->rowScale, y1, (column + 1) * mHfGeom->columnScale);
+ extent = PxVec3(mHfGeom->rowScale, y2 - y1, -mHfGeom->columnScale);
+ }
+ break;
+ case 2:
+ {
+ const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell);
+ const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast());
+ origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale);
+ extent = PxVec3(mHfGeom->rowScale, y2 - y0, 0);
+ }
+ break;
+ }
+}
+
+bool Gu::HeightFieldUtil::overlapAABBTriangles(
+ const PxTransform& pose, const PxBounds3& bounds, PxU32 flags, EntityReport<PxU32>* callback) const
+{
+ PxBounds3 localBounds = bounds;
+
+ if(flags & GuHfQueryFlags::eWORLD_SPACE)
+ {
+ PX_ASSERT(!localBounds.isEmpty());
+ localBounds = PxBounds3::transformFast(pose.getInverse(), localBounds);
+ }
+
+ localBounds.minimum.x *= mOneOverRowScale;
+ localBounds.minimum.y *= mOneOverHeightScale;
+ localBounds.minimum.z *= mOneOverColumnScale;
+
+ localBounds.maximum.x *= mOneOverRowScale;
+ localBounds.maximum.y *= mOneOverHeightScale;
+ localBounds.maximum.z *= mOneOverColumnScale;
+
+ if (mHfGeom->rowScale < 0)
+ {
+ PxReal swap = localBounds.minimum.x;
+ localBounds.minimum.x = localBounds.maximum.x;
+ localBounds.maximum.x = swap;
+ }
+
+ if (mHfGeom->columnScale < 0)
+ {
+ PxReal swap = localBounds.minimum.z;
+ localBounds.minimum.z = localBounds.maximum.z;
+ localBounds.maximum.z = swap;
+ }
+
+ // early exit for aabb does not overlap in XZ plane
+ // DO NOT MOVE: since rowScale / columnScale may be negative this has to be done after scaling localBounds
+ if (localBounds.minimum.x > mHeightField->getNbRowsFast() - 1)
+ return false;
+ if (localBounds.minimum.z > mHeightField->getNbColumnsFast() - 1)
+ return false;
+ if (localBounds.maximum.x < 0)
+ return false;
+ if (localBounds.maximum.z < 0)
+ return false;
+
+ PxU32 minRow = mHeightField->getMinRow(localBounds.minimum.x);
+ PxU32 maxRow = mHeightField->getMaxRow(localBounds.maximum.x);
+ PxU32 minColumn = mHeightField->getMinColumn(localBounds.minimum.z);
+ PxU32 maxColumn = mHeightField->getMaxColumn(localBounds.maximum.z);
+
+ PxU32 maxNbTriangles = 2 * (maxColumn - minColumn) * (maxRow - minRow);
+
+ if (maxNbTriangles == 0)
+ return false;
+
+ if (flags & GuHfQueryFlags::eFIRST_CONTACT) maxNbTriangles = 1;
+
+ static const PxU32 bufferSize = HF_SWEEP_REPORT_BUFFER_SIZE;
+ PxU32 indexBuffer[bufferSize];
+ PxU32 indexBufferUsed = 0;
+ PxU32 nb = 0;
+
+ PxU32 offset = minRow * mHeightField->getNbColumnsFast() + minColumn;
+
+ const PxReal& miny = localBounds.minimum.y;
+ const PxReal& maxy = localBounds.maximum.y;
+
+ for (PxU32 row = minRow; row < maxRow; row++)
+ {
+ for (PxU32 column = minColumn; column < maxColumn; column++)
+ {
+ PxReal h0 = mHeightField->getHeight(offset);
+ PxReal h1 = mHeightField->getHeight(offset + 1);
+ PxReal h2 = mHeightField->getHeight(offset + mHeightField->getNbColumnsFast());
+ PxReal h3 = mHeightField->getHeight(offset + mHeightField->getNbColumnsFast() + 1);
+ if (!((maxy < h0 && maxy < h1 && maxy < h2 && maxy < h3) || (miny > h0 && miny > h1 && miny > h2 && miny > h3)))
+ {
+ PxU32 material0 = mHeightField->getMaterialIndex0(offset);
+ if (material0 != PxHeightFieldMaterial::eHOLE)
+ {
+ if(indexBufferUsed >= bufferSize)
+ {
+ callback->onEvent(indexBufferUsed, indexBuffer);
+ indexBufferUsed = 0;
+ }
+
+ indexBuffer[indexBufferUsed++] = offset << 1;
+ nb++;
+
+ if (flags & GuHfQueryFlags::eFIRST_CONTACT) goto search_done;
+ }
+
+ PxU32 material1 = mHeightField->getMaterialIndex1(offset);
+ if (material1 != PxHeightFieldMaterial::eHOLE)
+ {
+ if(indexBufferUsed >= bufferSize)
+ {
+ callback->onEvent(indexBufferUsed, indexBuffer);
+ indexBufferUsed = 0;
+ }
+
+ indexBuffer[indexBufferUsed++] = (offset << 1) + 1;
+ nb++;
+
+ if (flags & GuHfQueryFlags::eFIRST_CONTACT) goto search_done;
+ }
+ }
+ offset++;
+ }
+ offset += (mHeightField->getNbColumnsFast() - (maxColumn - minColumn));
+ }
+
+search_done:
+
+ if(indexBufferUsed > 0)
+ callback->onEvent(indexBufferUsed, indexBuffer);
+
+ return nb > 0;
+}
+
+PxU32 Gu::HeightFieldUtil::getTriangle(const PxTransform& pose, PxTriangle& worldTri,
+ PxU32* _vertexIndices, PxU32* adjacencyIndices, PxTriangleID triangleIndex, bool worldSpaceTranslation, bool worldSpaceRotation) const
+{
+#if PX_CHECKED
+ if (!mHeightField->isValidTriangle(triangleIndex))
+ {
+ Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "HeightFieldShape::getTriangle: Invalid triangle index!");
+ return 0;
+ }
+#endif
+
+ PxVec3 handedness(1.0f); // Vector to invert normal coordinates according to the heightfield scales
+ bool wrongHanded = false;
+ if (mHfGeom->columnScale < 0)
+ {
+ wrongHanded = !wrongHanded;
+ handedness.z = -1.0f;
+ }
+ if (mHfGeom->rowScale < 0)
+ {
+ wrongHanded = !wrongHanded;
+ handedness.x = -1.0f;
+ }
+ if (mHeightField->getThicknessFast() > 0)
+ {
+ wrongHanded = !wrongHanded;
+ handedness.y = -1.0f;
+ }
+
+/* if (0) // ptchernev: Iterating over triangles becomes a pain.
+ {
+ if (mHeightField.getTriangleMaterial(triangleIndex) == mHfGeom.holeMaterialIndex)
+ {
+ Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "HeightFieldShape::getTriangle: Non-existing triangle (triangle has hole material)!");
+ return 0;
+ }
+ }*/
+
+ PxU32 vertexIndices[3];
+ mHeightField->getTriangleVertexIndices(triangleIndex, vertexIndices[0], vertexIndices[1+wrongHanded], vertexIndices[2-wrongHanded]);
+
+ if(adjacencyIndices)
+ {
+ mHeightField->getTriangleAdjacencyIndices( triangleIndex, vertexIndices[0], vertexIndices[1+wrongHanded], vertexIndices[2-wrongHanded],
+ adjacencyIndices[wrongHanded ? 2 : 0], adjacencyIndices[1], adjacencyIndices[wrongHanded ? 0 : 2]);
+ }
+
+ if(_vertexIndices)
+ {
+ _vertexIndices[0] = vertexIndices[0];
+ _vertexIndices[1] = vertexIndices[1];
+ _vertexIndices[2] = vertexIndices[2];
+ }
+
+ if (worldSpaceRotation)
+ {
+ if (worldSpaceTranslation)
+ {
+ for (PxU32 vi = 0; vi < 3; vi++)
+ worldTri.verts[vi] = hf2worldp(pose, mHeightField->getVertex(vertexIndices[vi]));
+ }
+ else
+ {
+ for (PxU32 vi = 0; vi < 3; vi++)
+ {
+ // TTP 2390
+ // local space here is rotated (but not translated) world space
+ worldTri.verts[vi] = pose.q.rotate(hf2shapep(mHeightField->getVertex(vertexIndices[vi])));
+ }
+ }
+ }
+ else
+ {
+ const PxVec3 offset = worldSpaceTranslation ? pose.p : PxVec3(0.0f);
+ for (PxU32 vi = 0; vi < 3; vi++)
+ worldTri.verts[vi] = hf2shapep(mHeightField->getVertex(vertexIndices[vi])) + offset;
+ }
+ return PxU32(mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE);
+}
+
+
+bool Gu::HeightFieldUtil::isBoundaryEdge(PxU32 edgeIndex) const
+{
+#ifdef PX_HEIGHTFIELD_DEBUG
+ PX_ASSERT(mHeightField.isValidEdge(edgeIndex));
+#endif
+ PxU32 faceIndices[2];
+ const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices);
+ if (count > 1)
+ {
+ const PxMaterialTableIndex mat0 = mHeightField->getTriangleMaterial(faceIndices[0]);
+ const PxMaterialTableIndex mat1 = mHeightField->getTriangleMaterial(faceIndices[1]);
+ if (mat0 == PxHeightFieldMaterial::eHOLE) return (mat1 != PxHeightFieldMaterial::eHOLE);
+ if (mat1 == PxHeightFieldMaterial::eHOLE) return (mat0 != PxHeightFieldMaterial::eHOLE);
+ }
+ else
+ {
+ const PxMaterialTableIndex mat0 = mHeightField->getTriangleMaterial(faceIndices[0]);
+ return (mat0 != PxHeightFieldMaterial::eHOLE);
+ }
+ return false;
+}
+
+
+/*PxReal Gu::HeightFieldUtil::getHeightAtShapePoint(PxReal x, PxReal z) const
+{
+ return mHfGeom.heightScale * mHeightField->getHeightInternal(x * mOneOverRowScale, z * mOneOverColumnScale);
+}*/
+
+
+/*
+PxVec3 Gu::HeightFieldUtil::getNormalAtShapePoint(PxReal x, PxReal z) const
+{
+ return hf2shapen(mHeightField->getNormal_(x * mOneOverRowScale, z * mOneOverColumnScale));
+}
+*/
diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.h b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.h
new file mode 100644
index 00000000..08db5883
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.h
@@ -0,0 +1,1532 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved.
+// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
+// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
+
+#ifndef GU_HEIGHTFIELD_UTIL_H
+#define GU_HEIGHTFIELD_UTIL_H
+
+#include "PxHeightFieldGeometry.h"
+#include "GuHeightField.h"
+#include "PxTriangle.h"
+#include "../intersection/GuIntersectionRayTriangle.h"
+#include "../intersection/GuIntersectionRayBox.h"
+#include "PsBasicTemplates.h"
+
+namespace physx
+{
+#define HF_SWEEP_REPORT_BUFFER_SIZE 64
+
+/**
+\brief Used to control contact queries.
+*/
+struct GuHfQueryFlags
+{
+ enum Enum
+ {
+ eWORLD_SPACE = (1<<0), //!< world-space parameter, else object space
+ eFIRST_CONTACT = (1<<1) //!< returns first contact only, else returns all contacts
+ };
+};
+
+namespace Gu
+{
+ template<class T> class EntityReport;
+
+ class PX_PHYSX_COMMON_API HeightFieldUtil
+ {
+ private:
+
+ PX_CUDA_CALLABLE PX_FORCE_INLINE void initialize()
+ {
+ const PxReal absRowScale = PxAbs(mHfGeom->rowScale);
+ const PxReal absColScale = PxAbs(mHfGeom->columnScale);
+ //warning #1931-D on WIIU: sizeof is not a type, variable, or dereferenced pointer expression
+ PX_ASSERT(sizeof(reinterpret_cast<PxHeightFieldSample*>(0)->height) == 2);
+ //PxReal minHeightPerSample = PX_MIN_HEIGHTFIELD_Y_SCALE;
+ PX_ASSERT(mHfGeom->heightScale >= PX_MIN_HEIGHTFIELD_Y_SCALE);
+ PX_ASSERT(absRowScale >= PX_MIN_HEIGHTFIELD_XZ_SCALE);
+ PX_ASSERT(absColScale >= PX_MIN_HEIGHTFIELD_XZ_SCALE);
+ PX_UNUSED(absRowScale);
+ PX_UNUSED(absColScale);
+ //using physx::intrinsics::fsel;
+ //mOneOverHeightScale = fsel(mHfGeom->heightScale - minHeightPerSample, 1.0f / mHfGeom->heightScale, 1.0f / minHeightPerSample);
+ mOneOverHeightScale = 1.0f / mHfGeom->heightScale;
+ mOneOverRowScale = 1.0f / mHfGeom->rowScale;
+ mOneOverColumnScale = 1.0f / mHfGeom->columnScale;
+ }
+
+ PxReal mOneOverRowScale;
+ PxReal mOneOverHeightScale;
+ PxReal mOneOverColumnScale;
+ const Gu::HeightField* mHeightField;
+ const PxHeightFieldGeometry* mHfGeom;
+
+ public:
+
+ //sschirm: added empty ctor for gpu shared mem allocation
+ PX_FORCE_INLINE HeightFieldUtil() {}
+
+ PX_FORCE_INLINE HeightFieldUtil(const PxHeightFieldGeometry& hfGeom, const Gu::HeightField& hf) : mHeightField(&hf), mHfGeom(&hfGeom)
+ {
+ initialize();
+ }
+
+ PX_FORCE_INLINE HeightFieldUtil(const PxHeightFieldGeometry& hfGeom) : mHeightField(static_cast<const Gu::HeightField*>(hfGeom.heightField)), mHfGeom(&hfGeom)
+ {
+ initialize();
+ }
+
+ //sschirm: initialize with PxHeightFieldGeometry and Gu::HeightField for gpu shared mem allocation
+ PX_CUDA_CALLABLE PX_FORCE_INLINE void initialize(const PxHeightFieldGeometry& hfGeom, const Gu::HeightField& hf)
+ {
+ mHeightField = &hf;
+ mHfGeom = &hfGeom;
+ initialize();
+ }
+
+ PX_CUDA_CALLABLE PX_FORCE_INLINE const Gu::HeightField& getHeightField() const { return *mHeightField; }
+ PX_CUDA_CALLABLE PX_FORCE_INLINE const PxHeightFieldGeometry& getHeightFieldGeometry() const { return *mHfGeom; }
+
+ PX_FORCE_INLINE PxReal getOneOverRowScale() const { return mOneOverRowScale; }
+ PX_FORCE_INLINE PxReal getOneOverHeightScale() const { return mOneOverHeightScale; }
+ PX_FORCE_INLINE PxReal getOneOverColumnScale() const { return mOneOverColumnScale; }
+
+ void computeLocalBounds(PxBounds3& bounds) const;
+ PX_FORCE_INLINE bool isCollisionVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const
+ {
+ return mHeightField->isCollisionVertex(vertexIndex, row, column, PxHeightFieldMaterial::eHOLE);
+ }
+ PX_CUDA_CALLABLE bool isCollisionEdge(PxU32 edgeIndex) const;
+ bool isCollisionEdge(PxU32 edgeIndex, PxU32 count, const PxU32* PX_RESTRICT faceIndices, PxU32 cell, PxU32 row, PxU32 column) const;
+ bool isBoundaryEdge(PxU32 edgeIndex) const;
+// PxReal getHeightAtShapePoint(PxReal x, PxReal z) const;
+ PX_FORCE_INLINE PxReal getHeightAtShapePoint(PxReal x, PxReal z) const
+ {
+ return mHfGeom->heightScale * mHeightField->getHeightInternal(x * mOneOverRowScale, z * mOneOverColumnScale);
+ }
+ PX_FORCE_INLINE PxReal getHeightAtShapePoint2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ) const
+ {
+ return mHfGeom->heightScale * mHeightField->getHeightInternal2(vertexIndex, fracX, fracZ);
+ }
+
+// PxVec3 getNormalAtShapePoint(PxReal x, PxReal z) const;
+ PX_FORCE_INLINE PxVec3 getNormalAtShapePoint(PxReal x, PxReal z) const
+ {
+ return mHeightField->getNormal_(x * mOneOverRowScale, z * mOneOverColumnScale, mOneOverRowScale, mOneOverHeightScale, mOneOverColumnScale);
+ }
+ PX_FORCE_INLINE PxVec3 getNormalAtShapePoint2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ) const
+ {
+ return mHeightField->getNormal_2(vertexIndex, fracX, fracZ, mOneOverRowScale, mOneOverHeightScale, mOneOverColumnScale);
+ }
+
+ PxU32 getFaceIndexAtShapePoint(PxReal x, PxReal z) const;
+ PxU32 getFaceIndexAtShapePointNoTest(PxReal x, PxReal z) const;
+ PxU32 getFaceIndexAtShapePointNoTest2(PxU32 cell, PxReal fracX, PxReal fracZ) const;
+ PxU32 getFaceIndexAtTriangleIndex(PxU32 triangleIndex) const;
+
+ PxVec3 getSmoothNormalAtShapePoint(PxReal x, PxReal z) const;
+
+ PxVec3 getVertexNormal(PxU32 vertexIndex, PxU32 row, PxU32 column) const;
+// PxVec3 getVertexNormal(PxU32 vertexIndex) const;
+ PX_FORCE_INLINE PxVec3 getVertexNormal(PxU32 vertexIndex) const
+ {
+ const PxU32 nbColumns = mHeightField->getData().columns;
+ const PxU32 row = vertexIndex / nbColumns;
+ const PxU32 column = vertexIndex % nbColumns;
+ return getVertexNormal(vertexIndex, row, column);
+ }
+
+ PxU32 getVertexFaceIndex(PxU32 vertexIndex, PxU32 row, PxU32 column) const;
+ void getEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, PxVec3& origin, PxVec3& extent) const;
+
+ PxU32 getEdgeFaceIndex(PxU32 edgeIndex) const;
+ PxU32 getEdgeFaceIndex(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const;
+ PxU32 getEdgeFaceIndex(PxU32 edgeIndex, PxU32 count, const PxU32* PX_RESTRICT faceIndices) const;
+
+ enum Feature { eFACE, eEDGE, eVERTEX };
+ static PX_INLINE Feature getFeatureType(PxU32 featureCode) { return Feature(featureCode >> 30); }
+ static PX_INLINE PxU32 getFeatureIndex(PxU32 featureCode) { return (featureCode & ~0xC0000000); }
+ static PX_INLINE PxU32 makeFeatureCode(PxU32 index, Feature type) { return (index | (PxU32(type) << 30)); }
+
+ // possible improvement: face index and edge index can be folded into one incremental feature index (along with vert index)
+ // such as, 0: vert index for cell, 1,2: edge indices, 3,4: face indices, then wrap around in multiples of 5 or 8
+ // all return arrays must have a size of 11 to accomodate the possible 11 contacts
+ // the feature index in featureIndices array is encoded as follows:
+ // the high 2 bits are 00 - face, 01 - edge, 10 - vertex, 11 - unused/hole
+ PxU32 findClosestPointsOnCell(
+ PxU32 row, PxU32 column, PxVec3 point,
+ PxVec3* PX_RESTRICT closestPoints, PxU32* PX_RESTRICT featureCodes,
+ bool testFaces, bool testEdges, bool skipEdgesIfFaceHits) const;
+
+ bool findProjectionOnTriangle(PxU32 triangleIndex, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& projection) const;
+
+// PxReal findClosestPointOnEdge(PxU32 edgeIndex, const PxVec3& point, PxVec3& closestPoint) const;
+ PxReal findClosestPointOnEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& closestPoint) const;
+
+ PxU32 getTriangle(const PxTransform&, PxTriangle& worldTri,
+ PxU32* vertexIndices, PxU32* adjacencyIndices, PxTriangleID triangleIndex, bool worldSpaceTranslation=true, bool worldSpaceRotation=true) const;
+ bool overlapAABBTriangles(const PxTransform&, const PxBounds3& bounds, PxU32 flags, EntityReport<PxU32>* callback) const;
+
+ // check's if vertex can be used for scene query - note it is not the same as collision vertex
+ // the difference here is because of the eNO_BOUNDARY_EDGES flag, which should not ignore boundary edges.
+ // We don t have precomputed data for this case, we need to manually check these vertices if they are inside
+ // a hole or not - check if the vertex isSolid
+ PX_FORCE_INLINE bool isQueryVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const
+ {
+ // if noBoundaryEdges flag is on, we need to check the solid vertices manually for
+ // vertices which are on the boundaries, as those data are not precomputed
+ if((mHeightField->getFlags() & PxHeightFieldFlag::eNO_BOUNDARY_EDGES) &&
+ (row == 0 || column == 0 || (row >= mHeightField->getNbRowsFast() - 1) || (column >= mHeightField->getNbColumnsFast() - 1)))
+ {
+ // early exit if the material0 for the vertex is not a hole
+ if(mHeightField->getMaterialIndex0(vertexIndex) != PxHeightFieldMaterial::eHOLE)
+ return true;
+ bool nbSolid;
+ return mHeightField->isSolidVertex(vertexIndex, row, column, PxHeightFieldMaterial::eHOLE, nbSolid);
+ }
+ else
+ {
+ return mHeightField->isCollisionVertex(vertexIndex, row, column, PxHeightFieldMaterial::eHOLE);
+ }
+ }
+
+ PX_FORCE_INLINE PxVec3 computePointNormal(PxU32 meshFlags, const PxVec3& d, const PxTransform& transform, PxReal l_sqr, PxReal x, PxReal z, PxReal epsilon, PxReal& l) const
+ {
+ PX_UNUSED(meshFlags);
+
+ PxVec3 n;
+ if(l_sqr > epsilon)
+ {
+ n = transform.rotate(d);
+ l = n.normalize();
+ }
+ else // l == 0
+ {
+ n = transform.rotate(getNormalAtShapePoint(x, z));
+ n = n.getNormalized();
+ l = PxSqrt(l_sqr);
+ }
+ return n;
+ }
+
+ PX_INLINE bool isShapePointOnHeightField(PxReal x, PxReal z) const
+ {
+ x *= mOneOverRowScale;
+ z *= mOneOverColumnScale;
+/* return ((!(x < 0))
+ && (!(z < 0))
+ && (x < (mHeightField.getNbRowsFast()-1))
+ && (z < (mHeightField.getNbColumnsFast()-1)));*/
+ return ((x >= 0.0f)
+ && (z >= 0.0f)
+ && (x < (mHeightField->getData().rowLimit+1.0f))
+ && (z < (mHeightField->getData().colLimit+1.0f)));
+ }
+
+ // floor and ceil don't clamp down exact integers but we want that
+ static PX_FORCE_INLINE PxF32 floorDown(PxF32 x) { PxF32 f = PxFloor(x); return (f == x) ? f-1 : f; }
+ static PX_FORCE_INLINE PxF32 ceilUp (PxF32 x) { PxF32 f = PxCeil (x); return (f == x) ? f+1 : f; }
+
+ // helper class for testing triangle height and reporting the overlapped triangles
+ template<class T>
+ class OverlapTraceSegment
+ {
+ public:
+ // helper rectangle struct
+ struct OverlapRectangle
+ {
+ PxI32 mMinu;
+ PxI32 mMaxu;
+ PxI32 mMinv;
+ PxI32 mMaxv;
+
+ void invalidate()
+ {
+ mMinu = 1;
+ mMaxu = -1;
+ mMinv = 1;
+ mMaxv = -1;
+ }
+ };
+
+ // helper line struct
+ struct OverlapLine
+ {
+ bool mColumn;
+ PxI32 mLine;
+ PxI32 mMin;
+ PxI32 mMax;
+
+ void invalidate()
+ {
+ mMin = 1;
+ mMax = -1;
+ }
+ };
+
+ public:
+ void operator = (OverlapTraceSegment&) {}
+
+ OverlapTraceSegment(const HeightFieldUtil& hfUtil,const Gu::HeightField& hf)
+ : mInitialized(false), mHfUtil(hfUtil), mHf(hf), mNbIndices(0) {}
+
+ PX_FORCE_INLINE bool initialized() const { return mInitialized; }
+
+ // prepare for iterations, set the expand u|v
+ PX_INLINE void prepare(const PxVec3& aP0, const PxVec3& aP1, const PxVec3& overlapObjectExtent, PxF32& expandu, PxF32& expandv)
+ {
+ // height test bounds
+ mMinY = (PxMin(aP1.y,aP0.y) - overlapObjectExtent.y) * mHfUtil.getOneOverHeightScale();
+ mMaxY = (PxMax(aP1.y,aP0.y) + overlapObjectExtent.y) * mHfUtil.getOneOverHeightScale();
+
+ // sets the clipping variables
+ mMinRow = PxI32(mHf.getMinRow((PxMin(aP1.x,aP0.x) - overlapObjectExtent.x)* mHfUtil.getOneOverRowScale()));
+ mMaxRow = PxI32(mHf.getMaxRow((PxMax(aP1.x,aP0.x) + overlapObjectExtent.x)* mHfUtil.getOneOverRowScale()));
+ mMinColumn = PxI32(mHf.getMinColumn((PxMin(aP1.z,aP0.z) - overlapObjectExtent.z)* mHfUtil.getOneOverColumnScale()));
+ mMaxColumn = PxI32(mHf.getMaxColumn((PxMax(aP1.z,aP0.z) + overlapObjectExtent.z)* mHfUtil.getOneOverColumnScale()));
+
+ // sets the expanded u|v coordinates
+ expandu = PxCeil(overlapObjectExtent.x*mHfUtil.getOneOverRowScale());
+ expandv = PxCeil(overlapObjectExtent.z*mHfUtil.getOneOverColumnScale());
+
+ // sets the offset that will be overlapped in each axis
+ mOffsetU = PxI32(expandu) + 1;
+ mOffsetV = PxI32(expandv) + 1;
+ }
+
+ // sets all necessary variables and makes initial rectangle setup and overlap
+ PX_INLINE bool init(const PxI32 ui, const PxI32 vi, const PxI32 nbVi, const PxI32 step_ui, const PxI32 step_vi, T* aCallback)
+ {
+ mInitialized = true;
+ mCallback = aCallback;
+ mNumColumns = nbVi;
+ mStep_ui = step_ui > 0 ? 0 : -1;
+ mStep_vi = step_vi > 0 ? 0 : -1;
+
+ // sets the rectangles
+ mCurrentRectangle.invalidate();
+ mPreviousRectangle.mMinu = ui - mOffsetU;
+ mPreviousRectangle.mMaxu = ui + mOffsetU;
+ mPreviousRectangle.mMinv = vi - mOffsetV;
+ mPreviousRectangle.mMaxv = vi + mOffsetV;
+
+ // visits all cells in given initial rectangle
+ if(!visitCells(mPreviousRectangle))
+ return false;
+
+ // reports all overlaps
+ if(!reportOverlaps())
+ return false;
+
+ return true;
+ }
+
+ // u|v changed, check for new rectangle - compare with previous one and parse
+ // the added line, which is a result from the rectangle compare
+ PX_INLINE bool step(const PxI32 ui, const PxI32 vi)
+ {
+ mCurrentRectangle.mMinu = ui - mOffsetU;
+ mCurrentRectangle.mMaxu = ui + mOffsetU;
+ mCurrentRectangle.mMinv = vi - mOffsetV;
+ mCurrentRectangle.mMaxv = vi + mOffsetV;
+ OverlapLine line;
+ computeRectangleDifference(mCurrentRectangle,mPreviousRectangle,line);
+
+ if(!visitCells(line))
+ return false;
+ if(!reportOverlaps())
+ return false;
+
+ mPreviousRectangle = mCurrentRectangle;
+ return true;
+ }
+
+ PX_INLINE void computeRectangleDifference(const OverlapRectangle& currentRectangle, const OverlapRectangle& previousRectangle, OverlapLine& line)
+ {
+ // check if u changes - add the row for visit
+ if(currentRectangle.mMinu != previousRectangle.mMinu)
+ {
+ line.mColumn = false;
+ line.mLine = currentRectangle.mMinu < previousRectangle.mMinu ? currentRectangle.mMinu : currentRectangle.mMaxu;
+ line.mMin = currentRectangle.mMinv;
+ line.mMax = currentRectangle.mMaxv;
+ return;
+ }
+
+ // check if v changes - add the column for visit
+ if(currentRectangle.mMinv != previousRectangle.mMinv)
+ {
+ line.mColumn = true;
+ line.mLine = currentRectangle.mMinv < previousRectangle.mMinv ? currentRectangle.mMinv : currentRectangle.mMaxv;
+ line.mMin = currentRectangle.mMinu;
+ line.mMax = currentRectangle.mMaxu;
+ }
+ }
+
+ // visits all cells in given rectangle
+ PX_INLINE bool visitCells(const OverlapRectangle& rectangle)
+ {
+ for(PxI32 ui = rectangle.mMinu + mStep_ui; ui <= rectangle.mMaxu + mStep_ui; ui++)
+ {
+ if(ui < mMinRow)
+ continue;
+ if(ui >= mMaxRow)
+ break;
+ for(PxI32 vi = rectangle.mMinv + mStep_vi; vi <= rectangle.mMaxv + mStep_vi; vi++)
+ {
+ if(vi < mMinColumn)
+ continue;
+ if(vi >= mMaxColumn)
+ break;
+ const PxI32 vertexIndex = ui*mNumColumns + vi;
+ if(!testVertexIndex(PxU32(vertexIndex)))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // visits all cells in given line - can be row or column
+ PX_INLINE bool visitCells(const OverlapLine& line)
+ {
+ if(line.mMin > line.mMax)
+ return true;
+
+ if(line.mColumn)
+ {
+ const PxI32 vi = line.mLine + mStep_vi;
+ // early exit if column is out of hf clip area
+ if(vi < mMinColumn)
+ return true;
+ if(vi >= mMaxColumn)
+ return true;
+
+ for(PxI32 ui = line.mMin + mStep_ui; ui <= line.mMax + mStep_ui; ui++)
+ {
+ // early exit or continue if row is out of hf clip area
+ if(ui >= mMaxRow)
+ break;
+ // continue if we did not reach the valid area, we can still get there
+ if(ui < mMinRow)
+ continue;
+ // if the cell has not been tested test and report
+ if(!testVertexIndex(PxU32(mNumColumns * ui + vi)))
+ return false;
+ }
+ }
+ else
+ {
+ const PxI32 ui = line.mLine + mStep_ui;
+ // early exit if row is out of hf clip area
+ if(ui < mMinRow)
+ return true;
+ if(ui >= mMaxRow)
+ return true;
+
+ for(PxI32 vi = line.mMin + mStep_vi; vi <= line.mMax + mStep_vi; vi++)
+ {
+ // early exit or continue if column is out of hf clip area
+ if(vi >= mMaxColumn)
+ break;
+ // continue if we did not reach the valid area, we can still get there
+ if(vi < mMinColumn)
+ continue;
+ // if the cell has not been tested test and report
+ if(!testVertexIndex(PxU32(mNumColumns * ui + vi)))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // does height check and if succeeded adds to report
+ PX_INLINE bool testVertexIndex(const PxU32 vertexIndex)
+ {
+#define ISHOLE0 (mHf.getMaterialIndex0(vertexIndex) == PxHeightFieldMaterial::eHOLE)
+#define ISHOLE1 (mHf.getMaterialIndex1(vertexIndex) == PxHeightFieldMaterial::eHOLE)
+ const PxReal h0 = mHf.getHeight(vertexIndex);
+ const PxReal h1 = mHf.getHeight(vertexIndex + 1);
+ const PxReal h2 = mHf.getHeight(vertexIndex + mNumColumns);
+ const PxReal h3 = mHf.getHeight(vertexIndex + mNumColumns + 1);
+ // actual height test, if some height pass we accept the cell
+ if(!((mMaxY < h0 && mMaxY < h1 && mMaxY < h2 && mMaxY < h3) || (mMinY > h0 && mMinY > h1 && mMinY > h2 && mMinY > h3)))
+ {
+ // check if the triangle is not a hole
+ if(!ISHOLE0)
+ {
+ if(!addIndex(vertexIndex*2))
+ return false;
+ }
+ if(!ISHOLE1)
+ {
+ if(!addIndex(vertexIndex*2 + 1))
+ return false;
+ }
+ }
+#undef ISHOLE0
+#undef ISHOLE1
+ return true;
+ }
+
+ // add triangle index, if we get out of buffer size, report them
+ bool addIndex(PxU32 triangleIndex)
+ {
+ if(mNbIndices == HF_SWEEP_REPORT_BUFFER_SIZE)
+ {
+ if(!reportOverlaps())
+ return false;
+ }
+
+ mIndexBuffer[mNbIndices++] = triangleIndex;
+ return true;
+ }
+
+ PX_FORCE_INLINE bool reportOverlaps()
+ {
+ if(mNbIndices)
+ {
+ if(!mCallback->onEvent(mNbIndices, mIndexBuffer))
+ return false;
+ mNbIndices = 0;
+ }
+ return true;
+ }
+
+ private:
+ bool mInitialized;
+ const HeightFieldUtil& mHfUtil;
+ const Gu::HeightField& mHf;
+ T* mCallback;
+ PxI32 mOffsetU;
+ PxI32 mOffsetV;
+ float mMinY;
+ float mMaxY;
+ PxI32 mMinRow;
+ PxI32 mMaxRow;
+ PxI32 mMinColumn;
+ PxI32 mMaxColumn;
+ PxI32 mNumColumns;
+ PxI32 mStep_ui;
+ PxI32 mStep_vi;
+ OverlapRectangle mPreviousRectangle;
+ OverlapRectangle mCurrentRectangle;
+ PxU32 mIndexBuffer[HF_SWEEP_REPORT_BUFFER_SIZE];
+ PxU32 mNbIndices;
+ };
+
+ // If useUnderFaceCalblack is false, traceSegment will report segment/triangle hits via
+ // faceHit(const Gu::HeightFieldUtil& hf, const PxVec3& point, PxU32 triangleIndex)
+ // Otherwise traceSegment will report all triangles the segment passes under via
+ // underFaceHit(const Gu::HeightFieldUtil& hf, const PxVec3& triNormal, const PxVec3& crossedEdge,
+ // PxF32 x, PxF32 z, PxF32 rayHeight, PxU32 triangleIndex)
+ // where x,z is the point of previous intercept in hf coords, rayHeight is at that same point
+ // crossedEdge is the edge vector crossed from last call to underFaceHit, undefined for first call
+ // Note that underFaceHit can be called when a line is above a triangle if it's within AABB for that hf cell
+ // Note that backfaceCull is ignored if useUnderFaceCallback is true
+ // overlapObjectExtent (localSpace) and overlap are used for triangle collecting using an inflated tracesegment
+ // Note that hfLocalBounds are passed as a parameter instead of being computed inside the traceSegment.
+ // The localBounds can be obtained: PxBounds3 hfLocalBounds; hfUtil.computeLocalBounds(hfLocalBounds); and passed as
+ // a parameter.
+ template<class T, bool useUnderFaceCallback, bool overlap>
+ PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& rayDir, const float rayLength , T* aCallback, const PxBounds3& hfLocalBounds, bool backfaceCull,
+ const PxVec3* overlapObjectExtent = NULL) const
+ {
+ PxF32 tnear, tfar;
+ if(!Gu::intersectRayAABB2(hfLocalBounds.minimum, hfLocalBounds.maximum, aP0, rayDir, rayLength, tnear, tfar))
+ return;
+
+ const PxVec3 p0 = aP0 + rayDir * tnear;
+ const PxVec3 p1 = aP0 + rayDir * tfar;
+
+ // helper class used for overlap tests
+ OverlapTraceSegment<T> overlapTraceSegment(*this, *mHeightField);
+
+ // values which expand the HF area
+ PxF32 expandu = 0.0f, expandv = 0.0f;
+
+ if (overlap)
+ {
+ // setup overlap variables
+ overlapTraceSegment.prepare(aP0,aP0 + rayDir*rayLength,*overlapObjectExtent,expandu,expandv);
+ }
+
+ // row = x|u, column = z|v
+ const PxF32 rowScale = mHfGeom->rowScale, columnScale = mHfGeom->columnScale, heightScale = mHfGeom->heightScale;
+ const PxI32 nbVi = PxI32(mHeightField->getNbColumnsFast()), nbUi = PxI32(mHeightField->getNbRowsFast());
+ PX_ASSERT(nbVi > 0 && nbUi > 0);
+
+ // clampEps is chosen so that we get a reasonable clamp value for 65536*0.9999999f = 65535.992187500000
+ const PxF32 clampEps = 1e-7f; // shrink u,v to within 1e-7 away from the world bounds
+
+ // we now clamp uvs to [1e-7, rowLimit-1e-7] to avoid out of range uvs and eliminate related checks in the loop
+ const PxF32 nbUcells = PxF32(nbUi-1)*(1.0f-clampEps), nbVcells = PxF32(nbVi-1)*(1.0f-clampEps);
+
+ // 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)
+ // (same direction as du,dv for u1,v1)
+ // 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
+ // we need to extend the field for the inflated radius, we will now operate even with negative u|v
+
+ // map p0 from (x, z, y) to (u0, v0, h0)
+ PxF32 u0 = PxMin(PxMax(p0.x * mOneOverRowScale, 1e-7f - expandu), nbUcells + expandu); // multiplication rescales the u,v grid steps to 1
+ PxF32 v0 = PxMin(PxMax(p0.z * mOneOverColumnScale, 1e-7f - expandv), nbVcells + expandv);
+ const PxReal h0 = p0.y; // we don't scale y
+
+ // map p1 from (x, z, y) to (u1, v1, h1)
+ PxF32 u1 = PxMin(PxMax(p1.x * mOneOverRowScale, 1e-7f - expandu), nbUcells + expandu);
+ PxF32 v1 = PxMin(PxMax(p1.z * mOneOverColumnScale, 1e-7f - expandv), nbVcells + expandv);
+ const PxReal h1 = p1.y; // we don't scale y
+
+ PxF32 du = u1 - u0, dv = v1 - v0; // recompute du, dv from adjusted uvs
+ const PxReal dh = h1 - h0;
+
+ // grid u&v step is always either 1 or -1, we precompute as both integers and floats to avoid conversions
+ // so step_uif is +/-1.0f, step_ui is +/-1
+ const PxF32 step_uif = PxSign(du), step_vif = PxSign(dv);
+ const PxI32 step_ui = PxI32(step_uif), step_vi = PxI32(step_vif);
+
+ // clamp magnitude of du, dv to at least clampEpsilon to avoid special cases when dividing
+ const PxF32 divEpsilon = 1e-10f;
+ if(PxAbs(du) < divEpsilon)
+ du = step_uif * divEpsilon;
+ if(PxAbs(dv) < divEpsilon)
+ dv = step_vif * divEpsilon;
+
+ const PxVec3 auhP0(aP0.x*mOneOverRowScale, aP0.y, aP0.z*mOneOverColumnScale);
+ const PxVec3 duhv(rayDir.x*rayLength*mOneOverRowScale, rayDir.y*rayLength, rayDir.z*rayLength*mOneOverColumnScale);
+ const PxReal duhvLength = duhv.magnitude();
+ PxVec3 duhvNormalized = duhv;
+ if(duhvLength > PX_NORMALIZATION_EPSILON)
+ duhvNormalized *= 1.0f/duhvLength;
+
+ // Math derivation:
+ // points on 2d segment are parametrized as: [u0,v0] + t [du, dv]. We solve for t_u[n], t for nth u-intercept
+ // u0 + t_un du = un
+ // t_un = (un-u0) / du
+ // t_un1 = (un+1-u0) / du ; we use +1 since we rescaled the grid step to 1
+ // therefore step_tu = t_un - t_un1 = 1/du
+
+ // seed the initial integer cell coordinates with u0, v0 rounded up or down with standard PxFloor/Ceil behavior
+ // to ensure we have the correct first cell between (ui,vi) and (ui+step_ui,vi+step_vi)
+ PxI32 ui = (du > 0.0f) ? PxI32(PxFloor(u0)) : PxI32(PxCeil(u0));
+ PxI32 vi = (dv > 0.0f) ? PxI32(PxFloor(v0)) : PxI32(PxCeil(v0));
+
+ // find the nearest integer u, v in ray traversal direction and corresponding tu and tv
+ const PxReal uhit0 = du > 0.0f ? ceilUp(u0) : floorDown(u0);
+ const PxReal vhit0 = dv > 0.0f ? ceilUp(v0) : floorDown(v0);
+
+ // tu, tv can be > 1 but since the loop is structured as do {} while(tMin < tEnd) we still visit the first cell
+ PxF32 last_tu = 0.0f, last_tv = 0.0f;
+ PxReal tu = (uhit0-u0) / du;
+ PxReal tv = (vhit0-v0) / dv;
+ PX_ASSERT(tu >= 0.0f && tv >= 0.0f);
+
+ // compute step_tu and step_tv; t steps per grid cell in u and v direction
+ const PxReal step_tu = 1.0f / PxAbs(du), step_tv = 1.0f / PxAbs(dv);
+
+ // t advances at the same rate for u, v and h therefore we can compute h at u,v grid intercepts
+ #define COMPUTE_H_FROM_T(t) (h0 + (t) * dh)
+
+ const PxF32 hEpsilon = 1e-4f;
+ PxF32 uif = PxF32(ui), vif = PxF32(vi);
+
+ // these are used to remap h values to correspond to u,v increasing order
+ PxI32 uflip = 1-step_ui; /*0 or 2*/
+ PxI32 vflip = (1-step_vi)/2; /*0 or 1*/
+
+ // this epsilon is needed to ensure that we include the last [t, t+1] range in the do {} while(t<tEnd) loop
+ // A.B. in case of overlap we do miss actually a line with this epsilon, should it not be +?
+ PxF32 tEnd = 1.0f - 1e-4f;
+ if(overlap)
+ tEnd = 1.0f + 1e-4f;
+ PxF32 tMinUV;
+
+ const Gu::HeightField& hf = *mHeightField;
+
+ // seed hLinePrev as h(0)
+ PxReal hLinePrev = COMPUTE_H_FROM_T(0);
+
+ do
+ {
+ tMinUV = PxMin(tu, tv); // determine where next closest u or v-intercept point is
+ PxF32 hLineNext = COMPUTE_H_FROM_T(tMinUV); // compute the corresponding h
+
+ // the operating u|v space has been extended by expandu|expandv if inflation is used
+ PX_ASSERT(ui >= 0 - expandu && ui < nbUi + expandu && vi >= 0 - expandv && vi < nbVi + expandv);
+ PX_ASSERT(ui+step_ui >= 0 - expandu && ui+step_ui < nbUi + expandu && vi+step_vi >= 0 - expandv && vi+step_vi < nbVi + expandv);
+
+ // handle overlap in overlapCallback
+ if(overlap)
+ {
+ if(!overlapTraceSegment.initialized())
+ {
+ // initial overlap and setup
+ if(!overlapTraceSegment.init(ui,vi,nbVi,step_ui,step_vi,aCallback))
+ return;
+ }
+ else
+ {
+ // overlap step
+ if(!overlapTraceSegment.step(ui,vi))
+ return;
+ }
+ }
+ else
+ {
+ const PxU32 colIndex0 = PxU32(nbVi * ui + vi);
+ const PxU32 colIndex1 = PxU32(nbVi * (ui + step_ui) + vi);
+ const PxReal h[4] = { // h[0]=h00, h[1]=h01, h[2]=h10, h[3]=h11 - oriented relative to step_uv
+ hf.getHeight(colIndex0) * heightScale, hf.getHeight(colIndex0 + step_vi) * heightScale,
+ hf.getHeight(colIndex1) * heightScale, hf.getHeight(colIndex1 + step_vi) * heightScale };
+
+ PxF32 minH = PxMin(PxMin(h[0], h[1]), PxMin(h[2], h[3]));
+ PxF32 maxH = PxMax(PxMax(h[0], h[1]), PxMax(h[2], h[3]));
+
+ // how much space in h have we covered from previous to current u or v intercept
+ PxF32 hLineCellRangeMin = PxMin(hLinePrev, hLineNext);
+ PxF32 hLineCellRangeMax = PxMax(hLinePrev, hLineNext);
+
+ // do a quick overlap test in h, this should be rejecting the vast majority of tests
+ if(!(hLineCellRangeMin-hEpsilon > maxH || hLineCellRangeMax+hEpsilon < minH) ||
+ (useUnderFaceCallback && hLineCellRangeMax < maxH))
+ {
+ // arrange h so that h00 corresponds to min(uif, uif+step_uif) h10 to max et c.
+ // this is only needed for backface culling to work so we know the proper winding order without branches
+ // uflip is 0 or 2, vflip is 0 or 1 (corresponding to positive and negative ui_step and vi_step)
+ PxF32 h00 = h[0+uflip+vflip];
+ PxF32 h01 = h[1+uflip-vflip];
+ PxF32 h10 = h[2-uflip+vflip];
+ PxF32 h11 = h[3-uflip-vflip];
+
+ PxF32 minuif = PxMin(uif, uif+step_uif);
+ PxF32 maxuif = PxMax(uif, uif+step_uif);
+ PxF32 minvif = PxMin(vif, vif+step_vif);
+ PxF32 maxvif = PxMax(vif, vif+step_vif);
+ PxVec3 p00(minuif, h00, minvif);
+ PxVec3 p01(minuif, h01, maxvif);
+ PxVec3 p10(maxuif, h10, minvif);
+ PxVec3 p11(maxuif, h11, maxvif);
+
+ const PxF32 enlargeEpsilon = 0.0001f;
+ const PxVec3* p00a = &p00, *p01a = &p01, *p10a = &p10, *p11a = &p11;
+ PxU32 minui = PxU32(PxMin(ui+step_ui, ui)), minvi = PxU32(PxMin(vi+step_vi, vi));
+
+ // row = x|u, column = z|v
+ const PxU32 vertIndex = nbVi * minui + minvi;
+ const PxU32 cellIndex = vertIndex; // this adds a dummy unused cell in the end of each row; was -minui
+ bool isZVS = hf.isZerothVertexShared(vertIndex);
+ if(!isZVS)
+ {
+ // rotate the pointers for flipped edge cells
+ p10a = &p00;
+ p00a = &p01;
+ p01a = &p11;
+ p11a = &p10;
+ }
+
+ // For triangle index computation, see illustration in Gu::HeightField::getTriangleNormal()
+ // Since row = u, column = v
+ // for zeroth vert shared the 10 index is the corner of the 0-index triangle, and 01 is 1-index
+ // if zeroth vertex is not shared, the 00 index is the corner of 0-index triangle
+ if(!useUnderFaceCallback)
+ {
+ if(mHeightField->getThicknessFast() > 0.0f) // new in 3.4: flip triangle winding if thickness is positive
+ Ps::swap<const PxVec3*>(p00a, p11a);
+
+ #define ISHOLE0 (hf.getMaterialIndex0(vertIndex) == PxHeightFieldMaterial::eHOLE)
+ #define ISHOLE1 (hf.getMaterialIndex1(vertIndex) == PxHeightFieldMaterial::eHOLE)
+ PxReal triT0 = PX_MAX_REAL, triT1 = PX_MAX_REAL;
+ bool hit0 = false, hit1 = false;
+ PxF32 triU0, triV0, triU1, triV1;
+ if(Gu::intersectRayTriangle(auhP0, duhvNormalized, *p10a, *p00a, *p11a, triT0, triU0, triV0, backfaceCull, enlargeEpsilon) &&
+ triT0 >= 0.0f && triT0 <= duhvLength && !ISHOLE0)
+ {
+ hit0 = true;
+ } else
+ triT0 = PX_MAX_REAL;
+ if(Gu::intersectRayTriangle(auhP0, duhvNormalized, *p01a, *p11a, *p00a, triT1, triU1, triV1, backfaceCull, enlargeEpsilon)
+ && triT1 >= 0.0f && triT1 <= duhvLength && !ISHOLE1)
+ {
+ hit1 = true;
+ } else
+ triT1 = PX_MAX_REAL;
+
+ if(hit0 && triT0 <= triT1)
+ {
+ const PxVec3 hitPoint((auhP0.x + duhvNormalized.x*triT0) * rowScale, auhP0.y + duhvNormalized.y * triT0, (auhP0.z + duhvNormalized.z*triT0) * columnScale);
+ if(!aCallback->faceHit(*this, hitPoint, cellIndex*2, triU0, triV0))
+ return;
+ if(hit1) // possible to hit both triangles in a cell with eMESH_MULTIPLE
+ {
+ PxVec3 hitPoint1((auhP0.x + duhvNormalized.x*triT1) * rowScale, auhP0.y + duhvNormalized.y * triT1, (auhP0.z + duhvNormalized.z*triT1) * columnScale);
+ if(!aCallback->faceHit(*this, hitPoint1, cellIndex*2 + 1, triU1, triV1))
+ return;
+ }
+ }
+ else if(hit1 && triT1 <= triT0)
+ {
+ PxVec3 hitPoint((auhP0.x + duhvNormalized.x*triT1) * rowScale, auhP0.y + duhvNormalized.y * triT1, (auhP0.z + duhvNormalized.z*triT1) * columnScale);
+ if(!aCallback->faceHit(*this, hitPoint, cellIndex*2 + 1, triU1, triV1))
+ return;
+ if(hit0) // possible to hit both triangles in a cell with eMESH_MULTIPLE
+ {
+ PxVec3 hitPoint1((auhP0.x + duhvNormalized.x*triT0) * rowScale, auhP0.y + duhvNormalized.y * triT0, (auhP0.z + duhvNormalized.z*triT0) * columnScale);
+ if(!aCallback->faceHit(*this, hitPoint1, cellIndex*2, triU0, triV0))
+ return;
+ }
+ }
+ #undef ISHOLE0
+ #undef ISHOLE1
+ }
+ else
+ {
+ // TODO: quite a few optimizations are possible here. edges can be shared, intersectRayTriangle inlined etc
+ // Go to shape space. Height is already in shape space so we only scale x and z
+ PxVec3 p00s(p00a->x * rowScale, p00a->y, p00a->z * columnScale);
+ PxVec3 p01s(p01a->x * rowScale, p01a->y, p01a->z * columnScale);
+ PxVec3 p10s(p10a->x * rowScale, p10a->y, p10a->z * columnScale);
+ PxVec3 p11s(p11a->x * rowScale, p11a->y, p11a->z * columnScale);
+
+ PxVec3 triNormals[2] = { (p00s - p10s).cross(p11s - p10s), (p11s - p01s).cross(p00s-p01s) };
+ triNormals[0] *= PxRecipSqrt(triNormals[0].magnitudeSquared());
+ triNormals[1] *= PxRecipSqrt(triNormals[1].magnitudeSquared());
+ // since the heightfield can be mirrored with negative rowScale or columnScale, this assert doesn't hold
+ //PX_ASSERT(triNormals[0].y >= 0.0f && triNormals[1].y >= 0.0f);
+
+ // at this point we need to compute the edge direction that we crossed
+ // also since we don't DDA the w we need to find u,v for w-intercept (w refers to diagonal adjusted with isZVS)
+ PxF32 wnu = isZVS ? -1.0f : 1.0f, wnv = 1.0f; // uv-normal to triangle edge that splits the cell
+ PxF32 wpu = uif + 0.5f * step_uif, wpv = vif + 0.5f * step_vif; // a point on triangle edge that splits the cell
+ // note that (wpu, wpv) is on both edges (for isZVS and non-ZVS cases) which is nice
+
+ // we clamp tNext to 1 because we still want to issue callbacks even if we stay in one cell
+ // note that tNext can potentially be arbitrarily large for a segment contained within a cell
+ PxF32 tNext = PxMin(PxMin(tu, tv), 1.0f), tPrev = PxMax(last_tu, last_tv);
+
+ // compute uvs corresponding to tPrev, tNext
+ PxF32 unext = u0 + tNext*du, vnext = v0 + tNext*dv;
+ PxF32 uprev = u0 + tPrev*du, vprev = v0 + tPrev*dv;
+
+ const PxReal& h00_ = h[0], &h01_ = h[1], &h10_ = h[2]/*, h11_ = h[3]*/; // aliases for step-oriented h
+
+ // (wpu, wpv) is a point on the diagonal
+ // we compute a dot of ((unext, vnext) - (wpu, wpv), wn) to see on which side of triangle edge we are
+ // if the dot is positive we need to add 1 to triangle index
+ PxU32 dotPrevGtz = PxU32(((uprev - wpu) * wnu + (vprev - wpv) * wnv) > 0);
+ PxU32 dotNextGtz = PxU32(((unext - wpu) * wnu + (vnext - wpv) * wnv) > 0);
+ PxU32 triIndex0 = cellIndex*2 + dotPrevGtz;
+ PxU32 triIndex1 = cellIndex*2 + dotNextGtz;
+ PxU32 isHole0 = PxU32(hf.getMaterialIndex0(vertIndex) == PxHeightFieldMaterial::eHOLE);
+ PxU32 isHole1 = PxU32(hf.getMaterialIndex1(vertIndex) == PxHeightFieldMaterial::eHOLE);
+ if(triIndex0 > triIndex1)
+ shdfnd::swap<PxU32>(isHole0, isHole1);
+
+ // TODO: compute height at u,v inside here, change callback param to PxVec3
+ PxVec3 crossedEdge;
+ if(last_tu > last_tv) // previous intercept was at u, so we use u=const edge
+ crossedEdge = PxVec3(0.0f, h01_-h00_, step_vif * columnScale);
+ else // previous intercept at v, use v=const edge
+ crossedEdge = PxVec3(step_uif * rowScale, h10_-h00_, 0.0f);
+
+ if(!isHole0 && !aCallback->underFaceHit(*this, triNormals[dotPrevGtz], crossedEdge,
+ uprev * rowScale, vprev * columnScale, COMPUTE_H_FROM_T(tPrev), triIndex0))
+ return;
+
+ if(triIndex1 != triIndex0 && !isHole1) // if triIndex0 != triIndex1 that means we cross the triangle edge
+ {
+ // Need to compute tw, the t for ray intersecting the diagonal within the current cell
+ // dot((wnu, wnv), (u0+tw*du, v0+tw*dv)-(wpu, wpv)) = 0
+ // wnu*(u0+tw*du-wpu) + wnv*(v0+tw*dv-wpv) = 0
+ // wnu*u0+wnv*v0-wnu*wpu-wnv*wpv + tw*(du*wnu + dv*wnv) = 0
+ const PxF32 denom = du*wnu + dv*wnv;
+ if(PxAbs(denom) > 1e-6f)
+ {
+ const PxF32 tw = (wnu*(wpu-u0)+wnv*(wpv-v0)) / denom;
+ if(!aCallback->underFaceHit(*this, triNormals[dotNextGtz], p10s-p01s,
+ (u0+tw*du) * rowScale, (v0+tw*dv) * columnScale, COMPUTE_H_FROM_T(tw), triIndex1))
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ if(tu < tv)
+ {
+ last_tu = tu;
+ ui += step_ui;
+ // AP: very rare condition, wasn't able to repro but we need this if anyway (DE6565)
+ if(ui+step_ui< (0 - expandu) || ui+step_ui>=(nbUi + expandu)) // should hold true for ui without step from previous iteration
+ break;
+ uif += step_uif;
+ tu += step_tu;
+ }
+ else
+ {
+ last_tv = tv;
+ vi += step_vi;
+ // AP: very rare condition, wasn't able to repro but we need this if anyway (DE6565)
+ if(vi+step_vi< (0 - expandv) || vi+step_vi>=(nbVi + expandv)) // should hold true for vi without step from previous iteration
+ break;
+ vif += step_vif;
+ tv += step_tv;
+ }
+ hLinePrev = hLineNext;
+ }
+ // since min(tu,tv) is the END of the active interval we need to check if PREVIOUS min(tu,tv) was past interval end
+ // since we update tMinUV in the beginning of the loop, at this point it stores the min(last tu,last tv)
+ while (tMinUV < tEnd);
+ #undef COMPUTE_H_FROM_T
+ }
+
+ PX_FORCE_INLINE PxVec3 hf2shapen(const PxVec3& v) const
+ {
+ return PxVec3(v.x * mOneOverRowScale, v.y * mOneOverHeightScale, v.z * mOneOverColumnScale);
+ }
+
+ PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 shape2hfp(const PxVec3& v) const
+ {
+ return PxVec3(v.x * mOneOverRowScale, v.y * mOneOverHeightScale, v.z * mOneOverColumnScale);
+ }
+
+ PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 hf2shapep(const PxVec3& v) const
+ {
+ return PxVec3(v.x * mHfGeom->rowScale, v.y * mHfGeom->heightScale, v.z * mHfGeom->columnScale);
+ }
+
+ PX_INLINE PxVec3 hf2worldp(const PxTransform& pose, const PxVec3& v) const
+ {
+ const PxVec3 s = hf2shapep(v);
+ return pose.transform(s);
+ }
+
+ PX_INLINE PxVec3 hf2worldn(const PxTransform& pose, const PxVec3& v) const
+ {
+ const PxVec3 s = hf2shapen(v);
+ return pose.q.rotate(s);
+ }
+
+#ifdef REMOVED
+bool clipShapeNormalToEdgeVoronoi(PxVec3& normal, PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const
+{
+// const PxU32 cell = edgeIndex / 3;
+ PX_ASSERT(cell == edgeIndex / 3);
+// const PxU32 row = cell / mHeightField.getNbColumnsFast();
+// const PxU32 column = cell % mHeightField.getNbColumnsFast();
+ PX_ASSERT(row == cell / mHeightField.getNbColumnsFast());
+ PX_ASSERT(column == cell % mHeightField.getNbColumnsFast());
+
+ //PxcHeightFieldFormat format = getFormatFast();
+// PxHeightFieldFormat::Enum format = mHeightField.getFormatFast();
+
+ bool result = false;
+
+// switch (edgeIndex % 3)
+ switch (edgeIndex - cell*3)
+ {
+ case 0:
+ if (row > 0)
+ {
+ //const PxcHeightFieldSample& sample = getSample(cell - getNbColumnsFast());
+ //if(isZerothVertexShared(cell - getNbColumnsFast()))
+ if(mHeightField.isZerothVertexShared(cell - mHeightField.getNbColumnsFast()))
+ {
+ //if (getMaterialIndex0(cell - getNbColumnsFast()) != getHoleMaterial())
+ if (mHeightField.getMaterialIndex0(cell - mHeightField.getNbColumnsFast()) != PxHeightFieldMaterial::eHOLE)
+ {
+ // <------ COL
+ // +----+ 0 R
+ // |1 / /^ O
+ // | / / # W
+ // | / / # |
+ // |/ / 0 # |
+ // + 2<===1 |
+ // |
+ // |
+ // |
+ // |
+ // |
+ // |
+ // V
+ //
+ //PxReal h0 = getHeightScale() * getHeight(cell - getNbColumnsFast());
+ //PxReal h1 = getHeightScale() * getHeight(cell);
+ //PxReal h2 = getHeightScale() * getHeight(cell + 1);
+ const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell - mHeightField.getNbColumnsFast());
+ const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell);
+ const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell + 1);
+ //PxVec3 eC;
+ //eC.set(0, h2-h1, getColumnScale());
+ const PxVec3 eC(0, h2-h1, mHfGeom.columnScale);
+ //PxVec3 eR;
+ //eR.set(-getRowScale(), h0-h1, 0);
+ const PxVec3 eR(-mHfGeom.rowScale, h0-h1, 0);
+ const PxVec3 e = eR - eC * eC.dot(eR) / eC.magnitudeSquared();
+ const PxReal s = normal.dot(e);
+ if (s > 0)
+ {
+ normal -= e * s / e.magnitudeSquared();
+ result = true;
+ }
+ }
+ }
+ else
+ {
+ //if (getMaterialIndex1(cell - getNbColumnsFast()) != getHoleMaterial())
+ if (mHeightField.getMaterialIndex1(cell - mHeightField.getNbColumnsFast()) != PxHeightFieldMaterial::eHOLE)
+ {
+ // <------ COL
+ // 0 +----+ R
+ // ^\ \ 0 | O
+ // # \ \ | W
+ // # \ \ | |
+ // # 1 \ \| |
+ // 1===>2 + |
+ // |
+ // |
+ // |
+ // |
+ // |
+ // |
+ // V
+ //
+ //PxReal h0 = getHeightScale() * getHeight(cell - getNbColumnsFast() + 1);
+ //PxReal h1 = getHeightScale() * getHeight(cell + 1);
+ //PxReal h2 = getHeightScale() * getHeight(cell);
+ const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell - mHeightField.getNbColumnsFast() + 1);
+ const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell + 1);
+ const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell);
+ //PxVec3 eC;
+ //eC.set(0, h2-h1, -getColumnScale());
+ const PxVec3 eC(0, h2-h1, -mHfGeom.columnScale);
+ //PxVec3 eR;
+ //eR.set(-getRowScale(), h0-h1, 0);
+ const PxVec3 eR(-mHfGeom.rowScale, h0-h1, 0);
+ const PxVec3 e = eR - eC * eC.dot(eR) / eC.magnitudeSquared();
+ const PxReal s = normal.dot(e);
+ if (s > 0)
+ {
+ normal -= e * s / e.magnitudeSquared();
+ result = true;
+ }
+ }
+ }
+ }
+ //if (row < getNbRowsFast() - 1)
+ if (row < mHeightField.getNbRowsFast() - 1)
+ {
+ //const PxcHeightFieldSample& sample = getSample(cell);
+ //if(isZerothVertexShared(cell))
+ if(mHeightField.isZerothVertexShared(cell))
+ {
+ //if (getMaterialIndex1(cell) != getHoleMaterial())
+ if (mHeightField.getMaterialIndex1(cell) != PxHeightFieldMaterial::eHOLE)
+ {
+ // <------ COL
+ // R
+ // O
+ // W
+ // |
+ // |
+ // |
+ // 0===>2 0 |
+ // # 1 / /| |
+ // # / / | |
+ // # / / | |
+ // V/ / 0 | |
+ // 1 +----+ |
+ // V
+ //
+ //PxReal h0 = getHeightScale() * getHeight(cell + 1);
+ //PxReal h1 = getHeightScale() * getHeight(cell + getNbColumnsFast() + 1);
+ //PxReal h2 = getHeightScale() * getHeight(cell + getNbColumnsFast());
+ const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell + 1);
+ const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast() + 1);
+ const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast());
+ //PxVec3 eC;
+ //eC.set(0, h2-h0, -getColumnScale());
+ const PxVec3 eC(0, h2-h0, -mHfGeom.columnScale);
+ //PxVec3 eR;
+ //eR.set(getRowScale(), h1-h0, 0);
+ const PxVec3 eR(mHfGeom.rowScale, h1-h0, 0);
+ const PxVec3 e = eR - eC * eC.dot(eR) / eC.magnitudeSquared();
+ const PxReal s = normal.dot(e);
+ if (s > 0)
+ {
+ normal -= e * s / e.magnitudeSquared();
+ result = true;
+ }
+ }
+ }
+ else
+ {
+ //if (getMaterialIndex0(cell) != getHoleMaterial())
+ if (mHeightField.getMaterialIndex0(cell) != PxHeightFieldMaterial::eHOLE)
+ {
+ // <------ COL
+ // R
+ // O
+ // W
+ // |
+ // |
+ // |
+ // + 2<===0 |
+ // |\ \ 0 # |
+ // | \ \ # |
+ // | \ \ # |
+ // |1 \ \V |
+ // +----+ 1 |
+ // V
+ //
+ //PxReal h0 = getHeightScale() * getHeight(cell);
+ //PxReal h1 = getHeightScale() * getHeight(cell + getNbColumnsFast());
+ //PxReal h2 = getHeightScale() * getHeight(cell + 1);
+ const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell);
+ const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast());
+ const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell + 1);
+ //PxVec3 eC;
+ //eC.set(0, h2-h0, getColumnScale());
+ const PxVec3 eC(0, h2-h0, mHfGeom.columnScale);
+ //PxVec3 eR;
+ //eR.set(getRowScale(), h1-h0, 0);
+ const PxVec3 eR(mHfGeom.rowScale, h1-h0, 0);
+ const PxVec3 e = eR - eC * eC.dot(eR) / eC.magnitudeSquared();
+ const PxReal s = normal.dot(e);
+ if (s > 0)
+ {
+ normal -= e * s / e.magnitudeSquared();
+ result = true;
+ } }
+ }
+ }
+ break;
+ case 1:
+ //if ((row < getNbRowsFast() - 1) && (column < getNbColumnsFast() - 1))
+ if ((row < mHeightField.getNbRowsFast() - 1) && (column < mHeightField.getNbColumnsFast() - 1))
+ {
+ //const PxcHeightFieldSample& sample = getSample(cell);
+
+ //PxReal h0 = getHeightScale() * getHeight(cell);
+ //PxReal h1 = getHeightScale() * getHeight(cell + 1);
+ //PxReal h2 = getHeightScale() * getHeight(cell + getNbColumnsFast());
+ //PxReal h3 = getHeightScale() * getHeight(cell + getNbColumnsFast() + 1);
+ const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell);
+ const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell + 1);
+ const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast());
+ const PxReal h3 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast() + 1);
+
+ //if (isZerothVertexShared(cell))
+ if (mHeightField.isZerothVertexShared(cell))
+ {
+ // <------ COL
+ // 1<---0 R
+ // |1 /| O
+ // | / | W
+ // | / | |
+ // |V 0 V |
+ // 3----2 |
+ // V
+ //
+ //PxVec3 eD;
+ //eD.set(getRowScale(), h3-h0, getColumnScale());
+ const PxVec3 eD(mHfGeom.rowScale, h3-h0, mHfGeom.columnScale);
+ const PxReal DD = eD.magnitudeSquared();
+
+ //if (getMaterialIndex0(cell) != getHoleMaterial())
+ if (mHeightField.getMaterialIndex0(cell) != PxHeightFieldMaterial::eHOLE)
+ {
+ //PxVec3 eR;
+ //eR.set(getRowScale(), h2-h0, 0);
+ const PxVec3 eR(mHfGeom.rowScale, h2-h0, 0);
+ const PxVec3 e = eR - eD * eD.dot(eR) / DD;
+ const PxReal proj = e.dot(normal);
+ if (proj > 0) {
+ normal -= e * proj / e.magnitudeSquared();
+ result = true;
+ }
+ }
+
+ //if (getMaterialIndex1(cell) != getHoleMaterial())
+ if (mHeightField.getMaterialIndex1(cell) != PxHeightFieldMaterial::eHOLE)
+ {
+ //PxVec3 eC;
+ //eC.set(0, h1-h0, getColumnScale());
+ const PxVec3 eC(0, h1-h0, mHfGeom.columnScale);
+ const PxVec3 e = eC - eD * eD.dot(eC) / DD;
+ const PxReal proj = e.dot(normal);
+ if (proj > 0)
+ {
+ normal -= e * proj / e.magnitudeSquared();
+ result = true;
+ }
+ }
+ }
+ else
+ {
+ // <------ COL
+ // 1--->0 R
+ // |\ 0 | O
+ // | \ | W
+ // | \ | |
+ // V 1 V| |
+ // 3----2 |
+ // V
+ //
+ //PxVec3 eD;
+ //eD.set(getRowScale(), h2-h1, -getColumnScale());
+ const PxVec3 eD(mHfGeom.rowScale, h2-h1, -mHfGeom.columnScale);
+ const PxReal DD = eD.magnitudeSquared();
+
+ //if (getMaterialIndex0(cell) != getHoleMaterial())
+ if (mHeightField.getMaterialIndex0(cell) != PxHeightFieldMaterial::eHOLE)
+ {
+ //PxVec3 eC;
+ //eC.set(0, h0-h1, -getColumnScale());
+ const PxVec3 eC(0, h0-h1, -mHfGeom.columnScale);
+ const PxVec3 e = eC - eD * eD.dot(eC) / DD;
+ const PxReal proj = e.dot(normal);
+ if (proj > 0)
+ {
+ normal -= e * proj / e.magnitudeSquared();
+ result = true;
+ }
+ }
+
+ //if (getMaterialIndex1(cell) != getHoleMaterial())
+ if (mHeightField.getMaterialIndex1(cell) != PxHeightFieldMaterial::eHOLE)
+ {
+ //PxVec3 eR;
+ //eR.set(getRowScale(), h3-h1, 0);
+ const PxVec3 eR(mHfGeom.rowScale, h3-h1, 0);
+ const PxVec3 e = eR - eD * eD.dot(eR) / DD;
+ const PxReal proj = e.dot(normal);
+ if (proj > 0)
+ {
+ normal -= e * proj / e.magnitudeSquared();
+ result = true;
+ }
+ }
+ }
+ }
+ break;
+ case 2:
+ if (column > 0)
+ {
+ //const PxcHeightFieldSample& sample = getSample(cell - 1);
+
+ //if(isZerothVertexShared(cell - 1))
+ if(mHeightField.isZerothVertexShared(cell - 1))
+ {
+ //if (getMaterialIndex1(cell - 1) != getHoleMaterial())
+ if (mHeightField.getMaterialIndex1(cell - 1) != PxHeightFieldMaterial::eHOLE)
+ {
+ // <-------------- COL
+ // 1===>0 + R
+ // + 1 / /| O
+ // + / / | W
+ // + / / | |
+ // V/ / 0 | |
+ // 2 +----+ V
+ //
+ //PxReal h0 = getHeightScale() * getHeight(cell - 1);
+ //PxReal h1 = getHeightScale() * getHeight(cell);
+ //PxReal h2 = getHeightScale() * getHeight(cell + getNbColumnsFast());
+ const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell - 1);
+ const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell);
+ const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast());
+ //PxVec3 eC;
+ //eC.set(0,h0-h1,-getColumnScale());
+ const PxVec3 eC(0,h0-h1,-mHfGeom.columnScale);
+ //PxVec3 eR;
+ //eR.set(getRowScale(),h2-h1,0);
+ const PxVec3 eR(mHfGeom.rowScale,h2-h1,0);
+ const PxVec3 e = eC - eR * eR.dot(eC) / eR.magnitudeSquared();
+ const PxReal s = normal.dot(e);
+ if (s > 0)
+ {
+ normal -= e * s / e.magnitudeSquared();
+ result = true;
+ }
+ }
+ }
+ else
+ {
+ //if (getMaterialIndex1(cell - 1) != getHoleMaterial())
+ if (mHeightField.getMaterialIndex1(cell - 1) != PxHeightFieldMaterial::eHOLE)
+ {
+ // <-------------- COL
+ // 2 +----+ R
+ // ^\ \ 0 | O
+ // + \ \ | W
+ // + \ \ | |
+ // + 1 \ \| |
+ // 1===>0 + V
+ //
+ //PxReal h0 = getHeightScale() * getHeight(cell - 1 + getNbColumnsFast());
+ //PxReal h1 = getHeightScale() * getHeight(cell + getNbColumnsFast());
+ //PxReal h2 = getHeightScale() * getHeight(cell);
+ const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell - 1 + mHeightField.getNbColumnsFast());
+ const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast());
+ const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell);
+ //PxVec3 eC;
+ //eC.set(0,h0-h1,-getColumnScale());
+ const PxVec3 eC(0,h0-h1,-mHfGeom.columnScale);
+ //PxVec3 eR;
+ //eC.set(-getRowScale(),h2-h1,0);
+ //eC.set(-mHfGeom.rowScale,h2-h1,0);
+ const PxVec3 eR(-mHfGeom.rowScale,h2-h1,0); // PT: I assume this was eR, not eC !!!!!
+ const PxVec3 e = eC - eR * eR.dot(eC) / eR.magnitudeSquared();
+ const PxReal s = normal.dot(e);
+ if (s > 0)
+ {
+ normal -= e * s / e.magnitudeSquared();
+ result = true;
+ }
+ }
+ }
+ }
+ //if (column < getNbColumnsFast() - 1)
+ if (column < mHeightField.getNbColumnsFast() - 1)
+ {
+ //const PxcHeightFieldSample& sample = getSample(cell);
+
+ //if (isZerothVertexShared(cell))
+ if (mHeightField.isZerothVertexShared(cell))
+ {
+ //if (getMaterialIndex0(cell) != getHoleMaterial())
+ if (mHeightField.getMaterialIndex0(cell) != PxHeightFieldMaterial::eHOLE)
+ {
+ // <-------------- COL
+ // +----+ 2 R
+ // | 1 / /^ O
+ // | / / + W
+ // | / / + |
+ // |/ / 0 + |
+ // + 1<===0 V
+ //
+ //PxReal h0 = getHeightScale() * getHeight(cell + getNbColumnsFast());
+ //PxReal h1 = getHeightScale() * getHeight(cell + getNbColumnsFast() + 1);
+ //PxReal h2 = getHeightScale() * getHeight(cell);
+ const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast());
+ const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast() + 1);
+ const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell);
+ //PxVec3 eC;
+ //eC.set(0,h1-h0,getColumnScale());
+ const PxVec3 eC(0,h1-h0,mHfGeom.columnScale);
+ //PxVec3 eR;
+ //eR.set(-getRowScale(),h2-h0,0);
+ const PxVec3 eR(-mHfGeom.rowScale,h2-h0,0);
+ const PxVec3 e = eC - eR * eR.dot(eC) / eR.magnitudeSquared();
+ const PxReal s = normal.dot(e);
+ if (s > 0)
+ {
+ normal -= e * s / e.magnitudeSquared();
+ result = true;
+ }
+ }
+ }
+ else
+ {
+ //if (getMaterialIndex0(cell) != getHoleMaterial())
+ if (mHeightField.getMaterialIndex0(cell) != PxHeightFieldMaterial::eHOLE)
+ {
+ // <-------------- COL
+ // + 1<===0 R
+ // |\ \ 0 + O
+ // | \ \ + W
+ // | \ \ + |
+ // | 1 \ \V |
+ // +----+ 2 V
+ //
+ //PxReal h0 = getHeightScale() * getHeight(cell);
+ //PxReal h1 = getHeightScale() * getHeight(cell + 1);
+ //PxReal h2 = getHeightScale() * getHeight(cell + getNbColumnsFast());
+ const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell);
+ const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell + 1);
+ const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast());
+ //PxVec3 eC;
+ //eC.set(0,h1-h0,getColumnScale());
+ const PxVec3 eC(0,h1-h0,mHfGeom.columnScale);
+ //PxVec3 eR;
+ //eR.set(getRowScale(),h2-h0,0);
+ const PxVec3 eR(mHfGeom.rowScale,h2-h0,0);
+ const PxVec3 e = eC - eR * eR.dot(eC) / eR.magnitudeSquared();
+ const PxReal s = normal.dot(e);
+ if (s > 0) {
+ normal -= e * s / e.magnitudeSquared();
+ result = true;
+ }
+ }
+ }
+ }
+ break;
+ }
+ return result;
+}
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // ptchernev TODO: this is wrong it only clips in x and z
+ bool clipShapeNormalToVertexVoronoi(PxVec3& normal, PxU32 vertexIndex, PxU32 row, PxU32 column) const
+ {
+ //PxU32 row = vertexIndex / getNbColumnsFast();
+ //PxU32 column = vertexIndex % getNbColumnsFast();
+// const PxU32 row = vertexIndex / mHeightField.getNbColumnsFast();
+// const PxU32 column = vertexIndex % mHeightField.getNbColumnsFast();
+ PX_ASSERT(row == vertexIndex / mHeightField->getNbColumnsFast());
+ PX_ASSERT(column == vertexIndex % mHeightField->getNbColumnsFast());
+
+ //PxReal h0 = getHeight(vertexIndex);
+ const PxReal h0 = mHeightField->getHeight(vertexIndex);
+
+ bool result = false;
+
+ if(row > 0)
+ {
+ // - row
+ //PxVec3 e;
+ //e.set(-getRowScale(), getHeightScale() * (getHeight(vertexIndex - getNbColumnsFast()) - h0), 0);
+ const PxVec3 e(-mHfGeom->rowScale, mHfGeom->heightScale * (mHeightField->getHeight(vertexIndex - mHeightField->getNbColumnsFast()) - h0), 0);
+ const PxReal proj = e.dot(normal);
+ if(proj > 0)
+ {
+ normal -= e * proj / e.magnitudeSquared();
+ result = true;
+ }
+ }
+
+ //if(row < getNbRowsFast() - 1)
+ if(row < mHeightField->getNbRowsFast() - 1)
+ {
+ // + row
+ //PxVec3 e;
+ //e.set(getRowScale(), getHeightScale() * (getHeight(vertexIndex + getNbColumnsFast()) - h0), 0);
+ const PxVec3 e(mHfGeom->rowScale, mHfGeom->heightScale * (mHeightField->getHeight(vertexIndex + mHeightField->getNbColumnsFast()) - h0), 0);
+ const PxReal proj = e.dot(normal);
+ if(proj > 0)
+ {
+ normal -= e * proj / e.magnitudeSquared();
+ result = true;
+ }
+ }
+
+ if(column > 0)
+ {
+ // - column
+ //PxVec3 e;
+ //e.set(0, getHeightScale() * (getHeight(vertexIndex - 1) - h0), -getColumnScale());
+ const PxVec3 e(0, mHfGeom->heightScale * (mHeightField->getHeight(vertexIndex - 1) - h0), -mHfGeom->columnScale);
+ const PxReal proj = e.dot(normal);
+ if(proj > 0)
+ {
+ normal -= e * proj / e.magnitudeSquared();
+ result = true;
+ }
+ }
+
+ //if(column < getNbColumnsFast() - 1)
+ if(column < mHeightField->getNbColumnsFast() - 1)
+ {
+ // + column
+ //PxVec3 e;
+ //e.set(0, getHeightScale() * (getHeight(vertexIndex + 1) - h0), getColumnScale());
+ const PxVec3 e(0, mHfGeom->heightScale * (mHeightField->getHeight(vertexIndex + 1) - h0), mHfGeom->columnScale);
+ const PxReal proj = e.dot(normal);
+ if(proj > 0)
+ {
+ normal -= e * proj / e.magnitudeSquared();
+ result = true;
+ }
+ }
+
+ return result;
+ }
+
+PxVec3 getEdgeDirection(PxU32 edgeIndex, PxU32 cell) const
+{
+// const PxU32 cell = edgeIndex / 3;
+ PX_ASSERT(cell == edgeIndex / 3);
+// switch (edgeIndex % 3)
+ switch (edgeIndex - cell*3)
+ {
+ case 0:
+ {
+// const PxReal y0 = mHeightField.getHeight(cell);
+// const PxReal y1 = mHeightField.getHeight(cell + 1);
+// return PxVec3(0.0f, mHfGeom.heightScale * (y1 - y0), mHfGeom.columnScale);
+ const PxI32 y0 = mHeightField->getSample(cell).height;
+ const PxI32 y1 = mHeightField->getSample(cell + 1).height;
+ return PxVec3(0.0f, mHfGeom->heightScale * PxReal(y1 - y0), mHfGeom->columnScale);
+ }
+ case 1:
+ if(mHeightField->isZerothVertexShared(cell))
+ {
+// const PxReal y0 = mHeightField.getHeight(cell);
+// const PxReal y3 = mHeightField.getHeight(cell + mHeightField.getNbColumnsFast() + 1);
+// return PxVec3(mHfGeom.rowScale, mHfGeom.heightScale * (y3 - y0), mHfGeom.columnScale);
+ const PxI32 y0 = mHeightField->getSample(cell).height;
+ const PxI32 y3 = mHeightField->getSample(cell + mHeightField->getNbColumnsFast() + 1).height;
+ return PxVec3(mHfGeom->rowScale, mHfGeom->heightScale * PxReal(y3 - y0), mHfGeom->columnScale);
+ }
+ else
+ {
+// const PxReal y1 = mHeightField.getHeight(cell + 1);
+// const PxReal y2 = mHeightField.getHeight(cell + mHeightField.getNbColumnsFast());
+// return PxVec3(mHfGeom.rowScale, mHfGeom.heightScale * (y2 - y1), -mHfGeom.columnScale);
+ const PxI32 y1 = mHeightField->getSample(cell + 1).height;
+ const PxI32 y2 = mHeightField->getSample(cell + mHeightField->getNbColumnsFast()).height;
+ return PxVec3(mHfGeom->rowScale, mHfGeom->heightScale * PxReal(y2 - y1), -mHfGeom->columnScale);
+ }
+ case 2:
+ {
+// const PxReal y0 = mHeightField.getHeight(cell);
+// const PxReal y2 = mHeightField.getHeight(cell + mHeightField.getNbColumnsFast());
+// return PxVec3(mHfGeom.rowScale, mHfGeom.heightScale * (y2 - y0), 0.0f);
+ const PxI32 y0 = mHeightField->getSample(cell).height;
+ const PxI32 y2 = mHeightField->getSample(cell + mHeightField->getNbColumnsFast()).height;
+ return PxVec3(mHfGeom->rowScale, mHfGeom->heightScale * PxReal(y2 - y0), 0.0f);
+ }
+ }
+ return PxVec3(0);
+}
+
+/*PX_FORCE_INLINE PxVec3 getEdgeDirection(PxU32 edgeIndex) const
+{
+ const PxU32 cell = edgeIndex / 3;
+ return getEdgeDirection(edgeIndex, cell);
+}*/
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ };
+
+
+} // namespace Gu
+
+}
+
+#endif
diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuOverlapTestsHF.cpp b/PhysX_3.4/Source/GeomUtils/src/hf/GuOverlapTestsHF.cpp
new file mode 100644
index 00000000..0fe55f18
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuOverlapTestsHF.cpp
@@ -0,0 +1,768 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved.
+// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
+// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
+
+#include "PsIntrinsics.h"
+#include "PsAllocator.h"
+#include "GuOverlapTests.h"
+
+#include "PsUtilities.h"
+#include "PsVecMath.h"
+
+#include "GuHeightFieldUtil.h"
+#include "GuIntersectionBoxBox.h"
+#include "GuIntersectionTriangleBox.h"
+#include "GuDistancePointSegment.h"
+#include "GuDistanceSegmentBox.h"
+#include "GuDistanceSegmentSegment.h"
+#include "GuDistanceSegmentSegmentSIMD.h"
+
+#include "PxSphereGeometry.h"
+#include "PxBoxGeometry.h"
+#include "PxCapsuleGeometry.h"
+#include "PxPlaneGeometry.h"
+#include "PxConvexMeshGeometry.h"
+
+#include "GuCapsule.h"
+#include "GuEdgeCache.h"
+#include "GuBoxConversion.h"
+
+#include "GuInternal.h"
+#include "GuConvexUtilsInternal.h"
+
+#include "GuVecTriangle.h"
+#include "GuVecSphere.h"
+#include "GuVecCapsule.h"
+#include "GuVecConvexHull.h"
+#include "GuConvexMesh.h"
+
+using namespace physx;
+using namespace Cm;
+using namespace Gu;
+using namespace Ps::aos;
+
+static bool intersectHeightFieldSphere(const HeightFieldUtil& hfUtil, const Sphere& sphereInHfShape)
+{
+ const HeightField& hf = hfUtil.getHeightField();
+
+ // sample the sphere center in the heightfield to find out
+ // if we have penetration with more than the sphere radius
+ if(hfUtil.isShapePointOnHeightField(sphereInHfShape.center.x, sphereInHfShape.center.z))
+ {
+ // The sphere origin projects within the bounds of the heightfield in the X-Z plane
+ PxReal sampleHeight = hfUtil.getHeightAtShapePoint(sphereInHfShape.center.x, sphereInHfShape.center.z);
+ PxReal deltaHeight = sphereInHfShape.center.y - sampleHeight;
+ if(hf.isDeltaHeightInsideExtent(deltaHeight))
+ {
+ // The sphere origin is 'below' the heightfield surface
+ PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(sphereInHfShape.center.x, sphereInHfShape.center.z);
+ if(faceIndex != 0xffffffff)
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ const PxReal radiusSquared = sphereInHfShape.radius * sphereInHfShape.radius;
+
+ const PxVec3 sphereInHF = hfUtil.shape2hfp(sphereInHfShape.center);
+
+ const PxReal radiusOverRowScale = sphereInHfShape.radius * PxAbs(hfUtil.getOneOverRowScale());
+ const PxReal radiusOverColumnScale = sphereInHfShape.radius * PxAbs(hfUtil.getOneOverColumnScale());
+
+ const PxU32 minRow = hf.getMinRow(sphereInHF.x - radiusOverRowScale);
+ const PxU32 maxRow = hf.getMaxRow(sphereInHF.x + radiusOverRowScale);
+ const PxU32 minColumn = hf.getMinColumn(sphereInHF.z - radiusOverColumnScale);
+ const PxU32 maxColumn = hf.getMaxColumn(sphereInHF.z + radiusOverColumnScale);
+
+ for(PxU32 r = minRow; r < maxRow; r++)
+ {
+ for(PxU32 c = minColumn; c < maxColumn; c++)
+ {
+
+ // x--x--x
+ // | x |
+ // x x x
+ // | x |
+ // x--x--x
+ PxVec3 pcp[11];
+ PxU32 npcp = 0;
+ npcp = hfUtil.findClosestPointsOnCell(r, c, sphereInHfShape.center, pcp, NULL, true, true, true);
+
+ for(PxU32 pi = 0; pi < npcp; pi++)
+ {
+ PxVec3 d = sphereInHfShape.center - pcp[pi];
+
+ PxReal ll = d.magnitudeSquared();
+
+ if(ll > radiusSquared)
+ // Too far
+ continue;
+
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static bool intersectHeightFieldCapsule(const HeightFieldUtil& hfUtil, const PxCapsuleGeometry& capsuleGeom, const PxTransform& capsulePose)
+{
+ const HeightField& hf = hfUtil.getHeightField();
+
+ PxVec3 verticesInHfShape[2];
+ PxVec3 capsuleOrigin, capsuleExtent;
+ {
+ const PxVec3 capsuleHalfHeightVector = getCapsuleHalfHeightVector(capsulePose, capsuleGeom);
+
+ capsuleOrigin = capsulePose.p + capsuleHalfHeightVector;
+ capsuleExtent = -capsuleHalfHeightVector*2.0f;
+
+ verticesInHfShape[0] = capsuleOrigin;
+ verticesInHfShape[1] = capsulePose.p - capsuleHalfHeightVector;
+ }
+
+ const PxReal radius = capsuleGeom.radius;
+ const PxReal radiusOverRowScale = radius * PxAbs(hfUtil.getOneOverRowScale());
+ const PxReal radiusOverColumnScale = radius * PxAbs(hfUtil.getOneOverColumnScale());
+
+ PxU32 absMinRow = 0xffffffff;
+ PxU32 absMaxRow = 0;
+ PxU32 absMinColumn = 0xffffffff;
+ PxU32 absMaxColumn = 0;
+
+ PxReal radiusSquared = radius * radius;
+
+ // test both of capsule's corner vertices+radius for HF overlap
+ for(PxU32 i = 0; i<2; i++)
+ {
+ const PxVec3& sphereInHfShape = verticesInHfShape[i];
+
+ // we have to do this first to update the absMin / absMax correctly even if
+ // we decide to continue from inside the deep penetration code.
+
+ const PxVec3 sphereInHF = hfUtil.shape2hfp(sphereInHfShape);
+
+ const PxU32 minRow = hf.getMinRow(sphereInHF.x - radiusOverRowScale);
+ const PxU32 maxRow = hf.getMaxRow(sphereInHF.x + radiusOverRowScale);
+ const PxU32 minColumn = hf.getMinColumn(sphereInHF.z - radiusOverColumnScale);
+ const PxU32 maxColumn = hf.getMaxColumn(sphereInHF.z + radiusOverColumnScale);
+
+ if(minRow < absMinRow) absMinRow = minRow;
+ if(minColumn < absMinColumn) absMinColumn = minColumn;
+ if(maxRow > absMaxRow) absMaxRow = maxRow;
+ if(maxColumn > absMaxColumn) absMaxColumn = maxColumn;
+
+ if(hfUtil.isShapePointOnHeightField(sphereInHfShape.x, sphereInHfShape.z))
+ {
+ // The sphere origin projects within the bounds of the heightfield in the X-Z plane
+ const PxReal sampleHeight = hfUtil.getHeightAtShapePoint(sphereInHfShape.x, sphereInHfShape.z);
+ const PxReal deltaHeight = sphereInHfShape.y - sampleHeight;
+ if(hf.isDeltaHeightInsideExtent(deltaHeight))
+ {
+ // The sphere origin is 'below' the heightfield surface
+ const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(sphereInHfShape.x, sphereInHfShape.z);
+ if(faceIndex != 0xffffffff)
+ {
+ return true;
+ }
+ continue;
+ }
+ }
+
+ for(PxU32 r = minRow; r < maxRow; r++)
+ {
+ for(PxU32 c = minColumn; c < maxColumn; c++)
+ {
+
+ // x--x--x
+ // | x |
+ // x x x
+ // | x |
+ // x--x--x
+ PxVec3 pcp[11];
+ PxU32 npcp = 0;
+ npcp = hfUtil.findClosestPointsOnCell(r, c, sphereInHfShape, pcp, NULL, true, true, true);
+
+ for(PxU32 pi = 0; pi < npcp; pi++)
+ {
+ const PxVec3 d = sphereInHfShape - pcp[pi];
+
+ if(hf.isDeltaHeightOppositeExtent(d.y))
+ {
+ // We are 'above' the heightfield
+
+ const PxReal ll = d.magnitudeSquared();
+ if(ll > radiusSquared)
+ // Too far above
+ continue;
+
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ const Vec3V p1 = V3LoadU(capsuleOrigin);
+ const Vec3V d1 = V3LoadU(capsuleExtent);
+
+ // now test capsule's inflated segment for overlap with HF edges
+ PxU32 row, column;
+ for(row = absMinRow; row <= absMaxRow; row++)
+ {
+ for(column = absMinColumn; column <= absMaxColumn; column++)
+ {
+ const PxU32 vertexIndex = row * hf.getNbColumnsFast() + column;
+ const PxU32 firstEdge = 3 * vertexIndex;
+ // omg I am sorry about this code but I can't find a simpler way:
+ // last column will only test edge 2
+ // last row will only test edge 0
+ // and most importantly last row and column will not go inside the for
+ const PxU32 minEi = (column == absMaxColumn) ? 2u : 0;
+ const PxU32 maxEi = (row == absMaxRow ) ? 1u : 3u;
+ for(PxU32 ei = minEi; ei < maxEi; ei++)
+ {
+ const PxU32 edgeIndex = firstEdge + ei;
+
+ const PxU32 cell = vertexIndex;
+ PX_ASSERT(cell == edgeIndex / 3);
+ const PxU32 row_ = row;
+ PX_ASSERT(row_ == cell / hf.getNbColumnsFast());
+ const PxU32 column_ = column;
+ PX_ASSERT(column_ == cell % hf.getNbColumnsFast());
+
+ const PxU32 faceIndex = hfUtil.getEdgeFaceIndex(edgeIndex, cell, row_, column_);
+ if(faceIndex != 0xffffffff)
+ {
+ PxVec3 origin;
+ PxVec3 direction;
+ hfUtil.getEdge(edgeIndex, cell, row_, column_, origin, direction);
+
+ const Vec3V p2 = V3LoadU(origin);
+ const Vec3V d2 = V3LoadU(direction);
+ FloatV s, t;
+ const FloatV llV = Gu::distanceSegmentSegmentSquared(p1, d1, p2, d2, s, t);
+
+ PxReal ll;
+ FStore(llV, &ll);
+
+ if(ll < radiusSquared)
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+namespace physx
+{
+namespace Gu
+{
+ const PxReal signs[24] =
+ {
+ -1,-1,-1,
+ -1,-1, 1,
+ -1, 1,-1,
+ -1, 1, 1,
+ 1,-1,-1,
+ 1,-1, 1,
+ 1, 1,-1,
+ 1, 1, 1,
+ };
+
+ const char edges[24] =
+ {
+ 0,1,
+ 1,3,
+ 3,2,
+ 2,0,
+ 4,5,
+ 5,7,
+ 7,6,
+ 6,4,
+ 0,4,
+ 1,5,
+ 2,6,
+ 3,7,
+ };
+
+ struct TriggerTraceSegmentCallback
+ {
+ bool intersection;
+
+ PX_INLINE TriggerTraceSegmentCallback() : intersection(false)
+ {
+ }
+
+ PX_INLINE bool underFaceHit(
+ const HeightFieldUtil&, const PxVec3&,
+ const PxVec3&, PxF32, PxF32, PxF32, PxU32)
+ {
+ return true;
+ }
+
+ PX_INLINE bool faceHit(const HeightFieldUtil&, const PxVec3&, PxU32, PxReal, PxReal)
+ {
+ intersection = true;
+ return false;
+ }
+ bool onEvent(PxU32 , PxU32* )
+ {
+ return true;
+ }
+ };
+
+
+ class OverlapHeightfieldTraceSegmentHelper
+ {
+ PX_NOCOPY(OverlapHeightfieldTraceSegmentHelper)
+ public:
+ OverlapHeightfieldTraceSegmentHelper(const HeightFieldUtil& hfUtil)
+ : mHfUtil(hfUtil)
+ {
+ mHfUtil.computeLocalBounds(mLocalBounds);
+ }
+
+ PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& aP1, TriggerTraceSegmentCallback* aCallback) const
+ {
+ mHfUtil.traceSegment<TriggerTraceSegmentCallback, false, false>(aP0, aP1 - aP0, 1.0f, aCallback, mLocalBounds, false, NULL);
+ }
+
+ private:
+ const HeightFieldUtil& mHfUtil;
+ PxBounds3 mLocalBounds;
+ };
+
+} // namespace
+}
+
+static bool intersectHeightFieldBox(const HeightFieldUtil& hfUtil, const Box& boxInHfShape)
+{
+ const HeightField& hf = hfUtil.getHeightField();
+
+ // Get box vertices
+ PxVec3 boxVertices[8];
+ for(PxU32 i=0; i<8; i++)
+ boxVertices[i] = PxVec3(boxInHfShape.extents.x*signs[3*i], boxInHfShape.extents.y*signs[3*i+1], boxInHfShape.extents.z*signs[3*i+2]);
+
+ // Transform box vertices to HeightFieldShape space
+ PxVec3 boxVerticesInHfShape[8];
+ for(PxU32 i=0; i<8; i++)
+ boxVerticesInHfShape[i] = boxInHfShape.transform(boxVertices[i]);
+
+ // Test box vertices.
+ {
+ for(PxU32 i=0; i<8; i++)
+ {
+ const PxVec3& boxVertexInHfShape = boxVerticesInHfShape[i];
+ if(hfUtil.isShapePointOnHeightField(boxVertexInHfShape.x, boxVertexInHfShape.z))
+ {
+ const PxReal y = hfUtil.getHeightAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z);
+ const PxReal dy = boxVertexInHfShape.y - y;
+ if(hf.isDeltaHeightInsideExtent(dy))
+ {
+ PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z);
+ if(faceIndex != 0xffffffff)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ // Test box edges.
+ {
+ OverlapHeightfieldTraceSegmentHelper traceSegmentHelper(hfUtil);
+
+ for(PxU32 i=0; i<12; i++)
+ {
+ const PxVec3 v0 = boxVerticesInHfShape[PxU8(edges[2*i])];
+ const PxVec3 v1 = boxVerticesInHfShape[PxU8(edges[2*i+1])];
+ TriggerTraceSegmentCallback cb;
+ traceSegmentHelper.traceSegment(v0, v1, &cb);
+ if(cb.intersection)
+ return true;
+ }
+ }
+
+ // Test HeightField vertices.
+ {
+ PsTransformV _hfShape2BoxShape;
+ const PxQuat bq(boxInHfShape.rot);
+ const QuatV q1 = QuatVLoadU(&bq.x);
+ const Vec3V p1 = V3LoadU(&boxInHfShape.center.x);
+ const PsTransformV _boxPose(p1, q1);
+ _hfShape2BoxShape = _boxPose.getInverse();
+
+ PxReal minx(PX_MAX_REAL);
+ PxReal minz(PX_MAX_REAL);
+ PxReal maxx(-PX_MAX_REAL);
+ PxReal maxz(-PX_MAX_REAL);
+
+ for(PxU32 i=0; i<8; i++)
+ {
+ const PxVec3& boxVertexInHfShape = boxVerticesInHfShape[i];
+
+/* if(boxVertexInHfShape.x < minx) minx = boxVertexInHfShape.x;
+ if(boxVertexInHfShape.z < minz) minz = boxVertexInHfShape.z;
+ if(boxVertexInHfShape.x > maxx) maxx = boxVertexInHfShape.x;
+ if(boxVertexInHfShape.z > maxz) maxz = boxVertexInHfShape.z;*/
+ minx = physx::intrinsics::selectMin(boxVertexInHfShape.x, minx);
+ minz = physx::intrinsics::selectMin(boxVertexInHfShape.z, minz);
+ maxx = physx::intrinsics::selectMax(boxVertexInHfShape.x, maxx);
+ maxz = physx::intrinsics::selectMax(boxVertexInHfShape.z, maxz);
+ }
+
+ const PxReal oneOverRowScale = hfUtil.getOneOverRowScale();
+ const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale();
+ const PxU32 minRow = hf.getMinRow(minx * oneOverRowScale);
+ const PxU32 maxRow = hf.getMaxRow(maxx * oneOverRowScale);
+ const PxU32 minColumn = hf.getMinColumn(minz * oneOverColumnScale);
+ const PxU32 maxColumn = hf.getMaxColumn(maxz * oneOverColumnScale);
+
+ const Vec4V extentV = V4LoadXYZW(boxInHfShape.extents.x, boxInHfShape.extents.y, boxInHfShape.extents.z, PX_MAX_REAL);
+ const PxHeightFieldGeometry& geom = hfUtil.getHeightFieldGeometry();
+
+ for(PxU32 row = minRow; row <= maxRow; row++)
+ {
+ for(PxU32 column = minColumn; column <= maxColumn; column++)
+ {
+ PxU32 vertexIndex = row * hf.getNbColumnsFast() + column;
+ if(hfUtil.isQueryVertex(vertexIndex, row, column))
+ {
+ // check if hf vertex is inside the box
+ const Vec4V hfVertex = V4LoadXYZW(geom.rowScale * row, geom.heightScale * hf.getHeight(vertexIndex), geom.columnScale * column, 0.0f);
+ const Vec4V hfVertexInBoxShape = Vec4V_From_Vec3V(_hfShape2BoxShape.transform(Vec3V_From_Vec4V(hfVertex)));
+ const Vec4V hfVertexInBoxShapeAbs = V4Abs(hfVertexInBoxShape);
+
+ if(V4AllGrtr(extentV, hfVertexInBoxShapeAbs))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static Matrix34 multiplyInverseRTLeft(const Matrix34& left, const Matrix34& right)
+{
+// t = left.M % (right.t - left.t);
+ PxVec3 t = left.rotateTranspose(right.p - left.p);
+
+// M.setMultiplyTransposeLeft(left.M, right.M);
+ const PxMat33& left33 = left.m;
+ const PxMat33& right33 = right.m;
+ PxMat33 multiplyTransposeLeft33 = (left33.getTranspose()) * right33;
+
+ return Matrix34(multiplyTransposeLeft33, t);
+}
+
+static bool intersectHeightFieldConvex(
+ const HeightFieldUtil& hfUtil, const PxTransform& _hfAbsPose, const ConvexMesh& convexMesh,
+ const PxTransform& _convexAbsPose, const PxMeshScale& convexMeshScaling)
+{
+ const Matrix34 hfAbsPose34(_hfAbsPose);
+ const Matrix34 convexAbsPose34(_convexAbsPose);
+ const Matrix34 vertexToShapeSkew34(convexMeshScaling.toMat33());
+ const Matrix34 temp34 = convexAbsPose34 * vertexToShapeSkew34;
+ const Matrix34 convexShape2HfShapeSkew34 = multiplyInverseRTLeft(hfAbsPose34, temp34);
+
+ const ConvexHullData* hull = &convexMesh.getHull();
+
+ // Allocate space for transformed vertices.
+ PxVec3* convexVerticesInHfShape = reinterpret_cast<PxVec3*>(PxAlloca(hull->mNbHullVertices*sizeof(PxVec3)));
+
+ // Transform vertices to height field shape
+ const PxVec3* hullVerts = hull->getHullVertices();
+ for(PxU32 i=0; i<hull->mNbHullVertices; i++)
+ convexVerticesInHfShape[i] = convexShape2HfShapeSkew34.transform(hullVerts[i]);
+
+ // Compute bounds of convex in hf space
+ PxBounds3 convexBoundsInHfShape;
+ computeBoundsAroundVertices(convexBoundsInHfShape, hull->mNbHullVertices, convexVerticesInHfShape);
+
+ // Compute the height field extreme over the bounds area.
+ const HeightField& hf = hfUtil.getHeightField();
+ PxReal hfExtreme = (hf.getThicknessFast() <= 0) ? -PX_MAX_REAL : PX_MAX_REAL;
+ const PxReal oneOverRowScale = hfUtil.getOneOverRowScale();
+ const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale();
+ const PxReal rowScale = (1.0f / hfUtil.getOneOverRowScale());
+ const PxReal columnScale = (1.0f / hfUtil.getOneOverColumnScale());
+ const PxReal heightScale = (1.0f / hfUtil.getOneOverHeightScale());
+
+ // negative scale support
+ PxU32 minRow;
+ PxU32 maxRow;
+ if(oneOverRowScale > 0.0f)
+ {
+ minRow = hf.getMinRow(convexBoundsInHfShape.minimum.x * oneOverRowScale);
+ maxRow = hf.getMaxRow(convexBoundsInHfShape.maximum.x * oneOverRowScale);
+ }
+ else
+ {
+ minRow = hf.getMinRow(convexBoundsInHfShape.maximum.x * oneOverRowScale);
+ maxRow = hf.getMaxRow(convexBoundsInHfShape.minimum.x * oneOverRowScale);
+ }
+
+ PxU32 minColumn;
+ PxU32 maxColumn;
+ if(oneOverColumnScale > 0.0f)
+ {
+ minColumn = hf.getMinColumn(convexBoundsInHfShape.minimum.z * oneOverColumnScale);
+ maxColumn = hf.getMaxColumn(convexBoundsInHfShape.maximum.z * oneOverColumnScale);
+ }
+ else
+ {
+ minColumn = hf.getMinColumn(convexBoundsInHfShape.maximum.z * oneOverColumnScale);
+ maxColumn = hf.getMaxColumn(convexBoundsInHfShape.minimum.z * oneOverColumnScale);
+ }
+
+ for(PxU32 row = minRow; row <= maxRow; row++)
+ {
+ for(PxU32 column = minColumn; column <= maxColumn; column++)
+ {
+ const PxReal h = hf.getHeight(row * hf.getNbColumnsFast() + column);
+ hfExtreme = (hf.getThicknessFast() <= 0) ? PxMax(hfExtreme, h) : PxMin(hfExtreme, h);
+ }
+ }
+ hfExtreme *= heightScale;
+
+
+ // Return if convex is on the wrong side of the extreme.
+ if(hf.getThicknessFast() <= 0)
+ {
+ if(convexBoundsInHfShape.minimum.y > hfExtreme)
+ return false;
+ }
+ else
+ {
+ if(convexBoundsInHfShape.maximum.y < hfExtreme)
+ return false;
+ }
+
+
+ // Test convex vertices
+ {
+ for(PxU32 i=0; i<hull->mNbHullVertices; i++)
+ {
+ const PxVec3& convexVertexInHfShape = convexVerticesInHfShape[i];
+ bool insideExtreme = (hf.getThicknessFast() <= 0) ? (convexVertexInHfShape.y < hfExtreme) : (convexVertexInHfShape.y > hfExtreme);
+ if(insideExtreme && hfUtil.isShapePointOnHeightField(convexVertexInHfShape.x, convexVertexInHfShape.z))
+ {
+ const PxReal y = hfUtil.getHeightAtShapePoint(convexVertexInHfShape.x, convexVertexInHfShape.z);
+ const PxReal dy = convexVertexInHfShape.y - y;
+ if(hf.isDeltaHeightInsideExtent(dy))
+ {
+ const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(convexVertexInHfShape.x, convexVertexInHfShape.z);
+ if(faceIndex != 0xffffffff)
+ return true;
+ }
+ }
+ }
+ }
+
+ // Test convex edges.
+ {
+ EdgeCache edgeCache;
+ PxU32 numPolygons = hull->mNbPolygons;
+ const HullPolygonData* polygons = hull->mPolygons;
+ const PxU8* const vertexData = hull->getVertexData8();
+
+ OverlapHeightfieldTraceSegmentHelper traceSegmentHelper(hfUtil);
+
+ while(numPolygons--)
+ {
+ const HullPolygonData& polygon = *polygons++;
+
+ const PxU8* verts = vertexData + polygon.mVRef8;
+
+ PxU32 numEdges = polygon.mNbVerts;
+
+ PxU32 a = numEdges - 1;
+ PxU32 b = 0;
+ while(numEdges--)
+ {
+ PxU8 vi0 = verts[a];
+ PxU8 vi1 = verts[b];
+
+ if(vi1 < vi0)
+ {
+ PxU8 tmp = vi0;
+ vi0 = vi1;
+ vi1 = tmp;
+ }
+
+ if(edgeCache.isInCache(vi0, vi1)) //avoid processing edges 2x if possible (this will typically have cache misses about 5% of the time leading to 5% redundant work.
+ continue;
+
+ const PxVec3& sv0 = convexVerticesInHfShape[vi0];
+ const PxVec3& sv1 = convexVerticesInHfShape[vi1];
+ a = b;
+ b++;
+
+
+ if(hf.getThicknessFast() <= 0)
+ {
+ if((sv0.y > hfExtreme) && (sv1.y > hfExtreme))
+ continue;
+ }
+ else
+ {
+ if((sv0.y < hfExtreme) && (sv1.y < hfExtreme))
+ continue;
+ }
+ const PxVec3 v0 = sv0;
+ const PxVec3 v1 = sv1;
+ TriggerTraceSegmentCallback cb;
+ traceSegmentHelper.traceSegment(v0, v1, &cb);
+ if(cb.intersection)
+ return true;
+ }
+ }
+ }
+
+ // Test HeightField vertices
+ {
+ const Matrix34 tmp34 = multiplyInverseRTLeft(convexAbsPose34, hfAbsPose34);
+ const Matrix34 hfShape2ConvexShapeSkew34 = vertexToShapeSkew34 * tmp34;
+
+ for(PxU32 row = minRow; row <= maxRow; row++)
+ {
+ for(PxU32 column = minColumn; column <= maxColumn; column++)
+ {
+ const PxU32 hfVertexIndex = row * hf.getNbColumnsFast() + column;
+ if(hfUtil.isQueryVertex(hfVertexIndex, row, column))
+ {
+ // Check if hf vertex is inside the convex
+ const PxVec3 hfVertex(rowScale * row, heightScale * hf.getHeight(hfVertexIndex), columnScale * column);
+ const PxVec3 hfVertexInConvexShape = hfShape2ConvexShapeSkew34.transform(hfVertex);
+
+ bool inside = true;
+ for(PxU32 poly = 0; poly < hull->mNbPolygons; poly++)
+ {
+ PxReal d = hull->mPolygons[poly].mPlane.distance(hfVertexInConvexShape);
+ if(d >= 0)
+ {
+ inside = false;
+ break;
+ }
+ }
+ if(inside)
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool Gu::checkOverlapAABB_heightFieldGeom(const PxGeometry& geom, const PxTransform& pose, const PxBounds3& box)
+{
+ PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD);
+ const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom);
+
+ const Matrix34 invAbsPose(pose.getInverse());
+
+ const Box boxInHfShape(
+ invAbsPose.transform(box.getCenter()),
+ box.getExtents(),
+ invAbsPose.m);
+
+ HeightFieldUtil hfUtil(hfGeom);
+ return intersectHeightFieldBox(hfUtil, boxInHfShape);
+}
+
+bool GeomOverlapCallback_SphereHeightfield(GU_OVERLAP_FUNC_PARAMS)
+{
+ PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE);
+ PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD);
+ PX_UNUSED(cache);
+
+ const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0);
+ const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom1);
+
+ const Sphere sphereInHf(pose1.transformInv(pose0.p), sphereGeom.radius);
+
+ HeightFieldUtil hfUtil(hfGeom);
+ return intersectHeightFieldSphere(hfUtil, sphereInHf);
+}
+
+bool GeomOverlapCallback_CapsuleHeightfield(GU_OVERLAP_FUNC_PARAMS)
+{
+ PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE);
+ PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD);
+ PX_UNUSED(cache);
+
+ const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom0);
+ const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom1);
+
+ const PxTransform capsuleShapeToHfShape = pose1.transformInv(pose0);
+
+ const HeightFieldUtil hfUtil(hfGeom);
+ return intersectHeightFieldCapsule(hfUtil, capsuleGeom, capsuleShapeToHfShape);
+}
+
+bool GeomOverlapCallback_BoxHeightfield(GU_OVERLAP_FUNC_PARAMS)
+{
+ PX_ASSERT(geom0.getType()==PxGeometryType::eBOX);
+ PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD);
+ PX_UNUSED(cache);
+
+ const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom0);
+ const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom1);
+
+ const PxTransform boxShape2HfShape = pose1.transformInv(pose0);
+
+ Box box;
+ buildFrom(box, boxShape2HfShape.p, boxGeom.halfExtents, boxShape2HfShape.q);
+
+ HeightFieldUtil hfUtil(hfGeom);
+ return intersectHeightFieldBox(hfUtil, box);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool GeomOverlapCallback_ConvexHeightfield(GU_OVERLAP_FUNC_PARAMS)
+{
+ PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH);
+ PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD);
+ PX_UNUSED(cache);
+
+ const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom0);
+ const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom1);
+
+ ConvexMesh* cm = static_cast<ConvexMesh*>(convexGeom.convexMesh);
+
+ HeightFieldUtil hfUtil(hfGeom);
+ return intersectHeightFieldConvex(hfUtil, pose1, *cm, pose0, convexGeom.scale);
+}
diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuSweepsHF.cpp b/PhysX_3.4/Source/GeomUtils/src/hf/GuSweepsHF.cpp
new file mode 100644
index 00000000..323a7cb6
--- /dev/null
+++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuSweepsHF.cpp
@@ -0,0 +1,604 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved.
+// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
+// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
+
+#include "GuSweepTests.h"
+#include "GuHeightFieldUtil.h"
+#include "GuEntityReport.h"
+#include "GuVecCapsule.h"
+#include "GuSweepMTD.h"
+#include "GuSweepTriangleUtils.h"
+#include "GuVecBox.h"
+#include "GuVecShrunkBox.h"
+#include "CmScaling.h"
+#include "GuSweepCapsuleTriangle.h"
+#include "GuInternal.h"
+#include "GuGJKRaycast.h"
+
+using namespace physx;
+using namespace Gu;
+using namespace Cm;
+using namespace physx::shdfnd::aos;
+
+#include "GuSweepConvexTri.h"
+
+#define AbortTraversal false
+#define ContinueTraversal true
+
+///////////////////////////////////////////////////////////////////////////////
+
+class HeightFieldTraceSegmentSweepHelper
+{
+ PX_NOCOPY(HeightFieldTraceSegmentSweepHelper)
+public:
+ HeightFieldTraceSegmentSweepHelper(const HeightFieldUtil& hfUtil, const PxVec3& aabbExtentHfLocalSpace)
+ : mHfUtil(hfUtil), mOverlapObjectExtent(aabbExtentHfLocalSpace)
+ {
+ mHfUtil.computeLocalBounds(mLocalBounds);
+ // extend the bounds
+ mLocalBounds.minimum = mLocalBounds.minimum - aabbExtentHfLocalSpace;
+ mLocalBounds.maximum = mLocalBounds.maximum + aabbExtentHfLocalSpace;
+ }
+
+ template<class T>
+ PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& rayDirNorm, const float rayLength, T* aCallback) const
+ {
+ mHfUtil.traceSegment<T, false, true>(aP0, rayDirNorm, rayLength, aCallback, mLocalBounds, false, &mOverlapObjectExtent);
+ }
+
+private:
+ const HeightFieldUtil& mHfUtil;
+ const PxVec3& mOverlapObjectExtent;
+ PxBounds3 mLocalBounds;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class HeightFieldTraceSegmentReport : public EntityReport<PxU32>
+{
+ PX_NOCOPY(HeightFieldTraceSegmentReport)
+public:
+
+ HeightFieldTraceSegmentReport(const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags) :
+ mHfUtil (hfUtil),
+ mHitFlags (hitFlags),
+ mStatus (false),
+ mInitialOverlap (false),
+ mIsDoubleSided ((hfUtil.getHeightFieldGeometry().heightFieldFlags & PxMeshGeometryFlag::eDOUBLE_SIDED) || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES)),
+ mIsAnyHit (hitFlags & PxHitFlag::eMESH_ANY)
+ {
+ }
+
+ bool underFaceHit(const Gu::HeightFieldUtil&, const PxVec3&, const PxVec3&, PxF32, PxF32, PxF32, PxU32)
+ {
+ return true;
+ }
+
+ bool faceHit(const Gu::HeightFieldUtil&, const PxVec3&, PxU32, PxReal, PxReal)
+ {
+ return true;
+ }
+
+ protected:
+ const HeightFieldUtil& mHfUtil;
+ const PxHitFlags mHitFlags;
+ bool mStatus;
+ bool mInitialOverlap;
+ const bool mIsDoubleSided;
+ const bool mIsAnyHit;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class CapsuleTraceSegmentReport : public HeightFieldTraceSegmentReport
+{
+ PX_NOCOPY(CapsuleTraceSegmentReport)
+public:
+ CapsuleTraceSegmentReport( const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags,
+ const Capsule& inflatedCapsule,
+ const PxVec3& unitDir, PxSweepHit& sweepHit, const PxTransform& pose, PxReal distance) :
+ HeightFieldTraceSegmentReport (hfUtil, hitFlags),
+ mInflatedCapsule (inflatedCapsule),
+ mUnitDir (unitDir),
+ mSweepHit (sweepHit),
+ mPose (pose),
+ mDistance (distance)
+ {
+ }
+
+ virtual PxAgain onEvent(PxU32 nb, PxU32* indices)
+ {
+ PX_ALIGN_PREFIX(16) PxU8 tribuf[HF_SWEEP_REPORT_BUFFER_SIZE*sizeof(PxTriangle)] PX_ALIGN_SUFFIX(16);
+ PxTriangle* tmpT = reinterpret_cast<PxTriangle*>(tribuf);
+ PX_ASSERT(nb <= HF_SWEEP_REPORT_BUFFER_SIZE);
+ for(PxU32 i=0; i<nb; i++)
+ {
+ const PxU32 triangleIndex = indices[i];
+ mHfUtil.getTriangle(mPose, tmpT[i], NULL, NULL, triangleIndex, true);
+ }
+
+ PxSweepHit h; // PT: TODO: ctor!
+ // PT: this one is safe because cullbox is NULL (no need to allocate one more triangle)
+ // PT: TODO: is it ok to pass the initial distance here?
+ PxVec3 bestNormal;
+ const bool status = sweepCapsuleTriangles_Precise(nb, tmpT, mInflatedCapsule, mUnitDir, mDistance, NULL, h, bestNormal, mHitFlags, mIsDoubleSided);
+ if(status && (h.distance <= mSweepHit.distance))
+ {
+ mSweepHit.faceIndex = indices[h.faceIndex];
+ mSweepHit.normal = h.normal;
+ mSweepHit.position = h.position;
+ mSweepHit.distance = h.distance;
+
+ mStatus = true;
+ if(h.distance == 0.0f)
+ {
+ mInitialOverlap = true;
+ return AbortTraversal;
+ }
+
+ if(mIsAnyHit)
+ return AbortTraversal;
+ }
+ return ContinueTraversal;
+ }
+
+ bool finalizeHit(PxSweepHit& sweepHit, const PxHeightFieldGeometry& hfGeom, const PxTransform& pose, const Capsule& lss, const Capsule& inflatedCapsule, const PxVec3& unitDir)
+ {
+ if(!mStatus)
+ return false;
+
+ if(mInitialOverlap)
+ {
+ // PT: TODO: consider using 'setInitialOverlapResults' here
+ sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX;
+
+ if(mHitFlags & PxHitFlag::eMTD)
+ {
+ const Vec3V p0 = V3LoadU(lss.p0);
+ const Vec3V p1 = V3LoadU(lss.p1);
+ const FloatV radius = FLoad(lss.radius);
+ CapsuleV capsuleV;
+ capsuleV.initialize(p0, p1, radius);
+
+ //calculate MTD
+ const bool hasContacts = computeCapsule_HeightFieldMTD(hfGeom, pose, capsuleV, inflatedCapsule.radius, mIsDoubleSided, GuHfQueryFlags::eWORLD_SPACE, sweepHit);
+
+ //ML: the center of mass is below the surface, we won't have MTD contact generate
+ if(!hasContacts)
+ {
+ sweepHit.distance = 0.0f;
+ sweepHit.normal = -unitDir;
+ }
+ else
+ {
+ sweepHit.flags |= PxHitFlag::ePOSITION;
+ }
+ }
+ else
+ {
+ sweepHit.distance = 0.0f;
+ sweepHit.normal = -unitDir;
+ }
+ }
+ else
+ {
+ sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL| PxHitFlag::ePOSITION | PxHitFlag::eFACE_INDEX;
+ }
+ return true;
+ }
+
+ private:
+ const Capsule& mInflatedCapsule;
+ const PxVec3& mUnitDir;
+ PxSweepHit& mSweepHit;
+ const PxTransform& mPose;
+ const PxReal mDistance;
+};
+
+bool sweepCapsule_HeightFieldGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS)
+{
+ PX_UNUSED(capsuleGeom_);
+ PX_UNUSED(capsulePose_);
+
+ PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD);
+ const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom);
+
+ const Capsule inflatedCapsule(lss.p0, lss.p1, lss.radius + inflation);
+
+ // Compute swept box
+ Box capsuleBox;
+ computeBoxAroundCapsule(inflatedCapsule, capsuleBox);
+
+ const PxVec3 capsuleAABBExtents = capsuleBox.computeAABBExtent();
+
+ const HeightFieldUtil hfUtil(hfGeom);
+ CapsuleTraceSegmentReport myReport(hfUtil, hitFlags, inflatedCapsule, unitDir, sweepHit, pose, distance);
+
+ sweepHit.distance = PX_MAX_F32;
+
+ // need hf local space stuff
+ const PxTransform inversePose = pose.getInverse();
+ const PxVec3 centerLocalSpace = inversePose.transform(capsuleBox.center);
+ const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir);
+ const PxVec3 capsuleAABBBExtentHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), capsuleAABBExtents).getExtents();
+
+ HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, capsuleAABBBExtentHfLocalSpace);
+ traceSegmentHelper.traceSegment<CapsuleTraceSegmentReport>(centerLocalSpace, sweepDirLocalSpace, distance, &myReport);
+
+ return myReport.finalizeHit(sweepHit, hfGeom, pose, lss, inflatedCapsule, unitDir);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ConvexTraceSegmentReport : public HeightFieldTraceSegmentReport
+{
+ PX_NOCOPY(ConvexTraceSegmentReport)
+public:
+ ConvexTraceSegmentReport( const HeightFieldUtil& hfUtil, const ConvexHullData& hull, const PxMeshScale& convexScale,
+ const PxTransform& convexPose, const PxTransform& heightFieldPose,
+ const PxVec3& unitDir, PxReal distance, PxHitFlags hitFlags, PxReal inflation) :
+ HeightFieldTraceSegmentReport (hfUtil, hitFlags),
+ mUnitDir (unitDir),
+ mInflation (inflation)
+ {
+ using namespace Ps::aos;
+ mSweepHit.faceIndex = 0xFFFFffff;
+ mSweepHit.distance = distance;
+ const Vec3V worldDir = V3LoadU(unitDir);
+ const FloatV dist = FLoad(distance);
+ const QuatV q0 = QuatVLoadU(&heightFieldPose.q.x);
+ const Vec3V p0 = V3LoadU(&heightFieldPose.p.x);
+
+ const QuatV q1 = QuatVLoadU(&convexPose.q.x);
+ const Vec3V p1 = V3LoadU(&convexPose.p.x);
+
+ const PsTransformV meshTransf(p0, q0);
+ const PsTransformV convexTransf(p1, q1);
+
+ mMeshToConvex = convexTransf.transformInv(meshTransf);
+ mConvexPoseV = convexTransf;
+ mConvexSpaceDir = convexTransf.rotateInv(V3Neg(V3Scale(worldDir, dist)));
+ mDistance = dist;
+
+ const Vec3V vScale = V3LoadU_SafeReadW(convexScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale
+ const QuatV vQuat = QuatVLoadU(&convexScale.rotation.x);
+
+ mMeshSpaceUnitDir = heightFieldPose.rotateInv(unitDir);
+ mConvexHull.initialize(&hull, V3Zero(), vScale, vQuat, convexScale.isIdentity());
+ }
+
+ virtual PxAgain onEvent(PxU32 nbEntities, PxU32* entities)
+ {
+ const PxTransform idt(PxIdentity);
+ for(PxU32 i=0; i<nbEntities; i++)
+ {
+ PxTriangle tri;
+ mHfUtil.getTriangle(idt, tri, NULL, NULL, entities[i], false, false); // First parameter not needed if local space triangle is enough
+
+ // use mSweepHit.distance as max sweep distance so far, mSweepHit.distance will be clipped by this function
+ if(sweepConvexVsTriangle( tri.verts[0], tri.verts[1], tri.verts[2], mConvexHull, mMeshToConvex, mConvexPoseV,
+ mConvexSpaceDir, mUnitDir, mMeshSpaceUnitDir, mDistance, mSweepHit.distance, mSweepHit, mIsDoubleSided,
+ mInflation, mInitialOverlap, entities[i]))
+ {
+ mStatus = true;
+ if(mIsAnyHit || mSweepHit.distance == 0.0f)
+ return AbortTraversal;
+ }
+ }
+ return ContinueTraversal;
+ }
+
+ bool finalizeHit(PxSweepHit& sweepHit,
+ const PxHeightFieldGeometry& hfGeom, const PxTransform& pose,
+ const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose,
+ const PxVec3& unitDir, PxReal inflation)
+ {
+ if(!mStatus)
+ return false;
+
+ if(mInitialOverlap)
+ {
+ if(mHitFlags & PxHitFlag::eMTD)
+ {
+ const bool hasContacts = computeConvex_HeightFieldMTD(hfGeom, pose, convexGeom, convexPose, inflation, mIsDoubleSided, GuHfQueryFlags::eWORLD_SPACE, sweepHit);
+
+ sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX;
+ if(!hasContacts)
+ {
+ sweepHit.distance = 0.0f;
+ sweepHit.normal = -unitDir;
+ }
+ else
+ {
+ sweepHit.flags |= PxHitFlag::ePOSITION;
+ }
+ }
+ else
+ {
+ setInitialOverlapResults(sweepHit, unitDir, mSweepHit.faceIndex); // hit index must be set to closest for IO
+ }
+ }
+ else
+ {
+ sweepHit = mSweepHit;
+ sweepHit.normal = -sweepHit.normal;
+ sweepHit.normal.normalize();
+ }
+ return true;
+ }
+
+ private:
+ PsMatTransformV mMeshToConvex;
+ PsTransformV mConvexPoseV;
+ ConvexHullV mConvexHull;
+ PxSweepHit mSweepHit;
+ Vec3V mConvexSpaceDir;
+ FloatV mDistance;
+ const PxVec3 mUnitDir;
+ PxVec3 mMeshSpaceUnitDir;
+ const PxReal mInflation;
+};
+
+bool sweepConvex_HeightFieldGeom(GU_CONVEX_SWEEP_FUNC_PARAMS)
+{
+ PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD);
+ const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom);
+
+ const Matrix34 convexTM(convexPose);
+ const Matrix34 meshTM(pose);
+
+ ConvexMesh* convexMesh = static_cast<ConvexMesh*>(convexGeom.convexMesh);
+
+ const bool idtScaleConvex = convexGeom.scale.isIdentity();
+
+ FastVertex2ShapeScaling convexScaling;
+ if(!idtScaleConvex)
+ convexScaling.init(convexGeom.scale);
+
+ PX_ASSERT(!convexMesh->getLocalBoundsFast().isEmpty());
+ const PxBounds3 hullAABBLocalSpace = convexMesh->getLocalBoundsFast().transformFast(convexScaling.getVertex2ShapeSkew());
+
+ const HeightFieldUtil hfUtil(hfGeom);
+ ConvexTraceSegmentReport entityReport(
+ hfUtil, convexMesh->getHull(), convexGeom.scale, convexPose, pose, -unitDir, distance, hitFlags, inflation);
+
+ // need hf local space stuff
+ const PxBounds3 hullAABB = PxBounds3::transformFast(convexPose, hullAABBLocalSpace);
+ const PxVec3 aabbExtents = hullAABB.getExtents() + PxVec3(inflation);
+ const PxTransform inversePose = pose.getInverse();
+ const PxVec3 centerLocalSpace = inversePose.transform(hullAABB.getCenter());
+ const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir);
+ const PxVec3 convexAABBExtentHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), aabbExtents).getExtents();
+
+ HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, convexAABBExtentHfLocalSpace);
+ traceSegmentHelper.traceSegment<ConvexTraceSegmentReport>(centerLocalSpace, sweepDirLocalSpace, distance, &entityReport);
+
+ return entityReport.finalizeHit(sweepHit, hfGeom, pose, convexGeom, convexPose, unitDir, inflation);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if PX_VC
+ #pragma warning(push)
+ #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value.
+#endif
+
+class BoxTraceSegmentReport : public HeightFieldTraceSegmentReport
+{
+ PX_NOCOPY(BoxTraceSegmentReport)
+public:
+ BoxTraceSegmentReport( const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags,
+ const PsTransformV& worldToBoxV, const PxTransform& pose, const BoxV& box, const PxVec3& localMotion,
+ PxSweepHit& sweepHit, PxReal inflation) :
+ HeightFieldTraceSegmentReport (hfUtil, hitFlags),
+ mWorldToBoxV (worldToBoxV),
+ mPose (pose),
+ mBox (box),
+ mLocalMotion (localMotion),
+ mSweepHit (sweepHit),
+ mInflation (inflation)
+ {
+ mMinToi = FMax();
+ }
+
+ virtual PxAgain onEvent(PxU32 nb, PxU32* indices)
+ {
+ const FloatV zero = FZero();
+ const Vec3V zeroV = V3Zero();
+ const Vec3V dir = V3LoadU(mLocalMotion);
+ //FloatV minToi = FMax();
+ FloatV toi;
+ Vec3V closestA, normal;//closestA and normal is in the local space of box
+
+ for(PxU32 i=0; i<nb; i++)
+ {
+ const PxU32 triangleIndex = indices[i];
+
+ PxTriangle currentTriangle; // in world space
+ mHfUtil.getTriangle(mPose, currentTriangle, NULL, NULL, triangleIndex, true, true);
+
+ const Vec3V localV0 = V3LoadU(currentTriangle.verts[0]);
+ const Vec3V localV1 = V3LoadU(currentTriangle.verts[1]);
+ const Vec3V localV2 = V3LoadU(currentTriangle.verts[2]);
+
+ const Vec3V triV0 = mWorldToBoxV.transform(localV0);
+ const Vec3V triV1 = mWorldToBoxV.transform(localV1);
+ const Vec3V triV2 = mWorldToBoxV.transform(localV2);
+
+ if(!mIsDoubleSided)
+ {
+ const Vec3V triNormal = V3Cross(V3Sub(triV2, triV1),V3Sub(triV0, triV1));
+ if(FAllGrtrOrEq(V3Dot(triNormal, dir), zero))
+ continue;
+ }
+
+ const TriangleV triangle(triV0, triV1, triV2);
+
+ ////move triangle to box space
+ //const Vec3V localV0 = Vec3V_From_PxVec3(WorldToBox.transform(currentTriangle.verts[0]));
+ //const Vec3V localV1 = Vec3V_From_PxVec3(WorldToBox.transform(currentTriangle.verts[1]));
+ //const Vec3V localV2 = Vec3V_From_PxVec3(WorldToBox.transform(currentTriangle.verts[2]));
+
+ //TriangleV triangle(localV0, localV1, localV2);
+ const LocalConvex<TriangleV> convexA(triangle);
+ const LocalConvex<BoxV> convexB(mBox);
+ const Vec3V initialSearchDir = V3Sub(triangle.getCenter(), mBox.getCenter());
+
+ if(gjkRaycastPenetration< LocalConvex<TriangleV>, LocalConvex<BoxV> >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, mInflation, false))
+ {
+ mStatus = true;
+ if(FAllGrtr(toi, zero))
+ {
+ if(FAllGrtr(mMinToi, toi))
+ {
+ mMinToi = toi;
+ FStore(toi, &mSweepHit.distance);
+ V3StoreU(normal, mSweepHit.normal);
+ V3StoreU(closestA, mSweepHit.position);
+ mSweepHit.faceIndex = triangleIndex;
+
+ if(mIsAnyHit)
+ return AbortTraversal;
+ }
+ }
+ else
+ {
+ mSweepHit.distance = 0.0f;
+ mSweepHit.faceIndex = triangleIndex;
+ mInitialOverlap = true;
+ return AbortTraversal;
+ }
+ }
+ }
+ return ContinueTraversal;
+ }
+
+ bool finalizeHit(PxSweepHit& sweepHit,
+ const PxHeightFieldGeometry& hfGeom, const PxTransform& pose,
+ const PxTransform& boxPose_, const Box& box,
+ const PxVec3& unitDir, PxReal distance, PxReal inflation)
+ {
+ if(!mStatus)
+ return false;
+
+ if(mInitialOverlap)
+ {
+ // PT: TODO: consider using 'setInitialOverlapResults' here
+
+ sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX;
+
+ if(mHitFlags & PxHitFlag::eMTD)
+ {
+ const bool hasContacts = computeBox_HeightFieldMTD(hfGeom, pose, box, boxPose_, inflation, mIsDoubleSided, GuHfQueryFlags::eWORLD_SPACE, sweepHit);
+
+ //ML: the center of mass is below the surface, we won't have MTD contact generate
+ if(!hasContacts)
+ {
+ sweepHit.distance = 0.0f;
+ sweepHit.normal = -unitDir;
+ }
+ else
+ {
+ sweepHit.flags |= PxHitFlag::ePOSITION;
+ }
+ }
+ else
+ {
+ sweepHit.distance = 0.0f;
+ sweepHit.normal = -unitDir;
+ }
+ }
+ else
+ {
+ PxVec3 n = sweepHit.normal.getNormalized();
+ if((n.dot(mLocalMotion))>0.0f)
+ n = -n;
+
+ sweepHit.distance *= distance; // stored as toi [0,1] during computation -> scale
+ sweepHit.normal = boxPose_.rotate(n);
+ sweepHit.position = boxPose_.transform(sweepHit.position);
+ sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX;
+ }
+ return true;
+ }
+
+ private:
+ const PsTransformV& mWorldToBoxV;
+ const PxTransform& mPose;
+ const BoxV& mBox;
+ FloatV mMinToi;
+ const PxVec3 mLocalMotion;
+ PxSweepHit& mSweepHit;
+ const PxReal mInflation;
+};
+
+#if PX_VC
+ #pragma warning(pop)
+#endif
+
+bool sweepBox_HeightFieldGeom(GU_BOX_SWEEP_FUNC_PARAMS)
+{
+ PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD);
+ PX_UNUSED(boxGeom_);
+ PX_UNUSED(hitFlags);
+
+ const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom);
+
+ const PxVec3 boxAABBExtent = box.computeAABBExtent() + PxVec3(inflation);
+
+ // Move to AABB space
+ PX_ALIGN_PREFIX(16) PxTransform WorldToBox PX_ALIGN_SUFFIX(16);
+ WorldToBox = boxPose_.getInverse();
+
+ const QuatV q1 = QuatVLoadA(&WorldToBox.q.x);
+ const Vec3V p1 = V3LoadA(&WorldToBox.p.x);
+ const PsTransformV WorldToBoxV(p1, q1);
+
+ const PxVec3 motion = unitDir * distance;
+ const PxVec3 localMotion = WorldToBox.rotate(motion);
+
+ const BoxV boxV(V3Zero(), V3LoadU(box.extents));
+
+ sweepHit.distance = PX_MAX_F32;
+
+ const HeightFieldUtil hfUtil(hfGeom);
+ BoxTraceSegmentReport myReport(hfUtil, hitFlags, WorldToBoxV, pose, boxV, localMotion, sweepHit, inflation);
+
+ // need hf local space stuff
+ const PxTransform inversePose = pose.getInverse();
+ const PxVec3 centerLocalSpace = inversePose.transform(box.center);
+ const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir);
+ const PxVec3 boxAABBExtentInHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), boxAABBExtent).getExtents();
+
+ HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, boxAABBExtentInHfLocalSpace);
+ traceSegmentHelper.traceSegment<BoxTraceSegmentReport>(centerLocalSpace, sweepDirLocalSpace, distance, &myReport);
+
+ return myReport.finalizeHit(sweepHit, hfGeom, pose, boxPose_, box, unitDir, distance, inflation);
+}
+
+///////////////////////////////////////////////////////////////////////////////