diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/GeomUtils/src/hf | |
| download | physx-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.h | 56 | ||||
| -rw-r--r-- | PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.cpp | 814 | ||||
| -rw-r--r-- | PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.h | 1520 | ||||
| -rw-r--r-- | PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldData.h | 88 | ||||
| -rw-r--r-- | PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.cpp | 1055 | ||||
| -rw-r--r-- | PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.h | 1532 | ||||
| -rw-r--r-- | PhysX_3.4/Source/GeomUtils/src/hf/GuOverlapTestsHF.cpp | 768 | ||||
| -rw-r--r-- | PhysX_3.4/Source/GeomUtils/src/hf/GuSweepsHF.cpp | 604 |
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); +} + +/////////////////////////////////////////////////////////////////////////////// |