feat(physics): wire physx sdk into build
This commit is contained in:
60
engine/third_party/physx/source/geomutils/src/hf/GuEntityReport.h
vendored
Normal file
60
engine/third_party/physx/source/geomutils/src/hf/GuEntityReport.h
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "PxQueryReport.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Gu
|
||||
{
|
||||
class EntityReport
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~EntityReport() {}
|
||||
|
||||
virtual bool onEvent(PxU32 nbEntities, const PxU32* entities) = 0;
|
||||
};
|
||||
|
||||
class OverlapReport
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~OverlapReport() {}
|
||||
|
||||
virtual bool reportTouchedTris(PxU32 nbEntities, const PxU32* entities) = 0;
|
||||
};
|
||||
|
||||
} // namespace Gu
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
438
engine/third_party/physx/source/geomutils/src/hf/GuHeightField.cpp
vendored
Normal file
438
engine/third_party/physx/source/geomutils/src/hf/GuHeightField.cpp
vendored
Normal file
@@ -0,0 +1,438 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "GuHeightField.h"
|
||||
#include "GuMeshFactory.h"
|
||||
#include "CmSerialize.h"
|
||||
#include "foundation/PxBitMap.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
using namespace Cm;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
HeightField::HeightField(MeshFactory* factory)
|
||||
: PxHeightField(PxConcreteType::eHEIGHTFIELD, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE)
|
||||
, mSampleStride (0)
|
||||
, mNbSamples (0)
|
||||
, mMinHeight (0.0f)
|
||||
, mMaxHeight (0.0f)
|
||||
, mModifyCount (0)
|
||||
, mMeshFactory (factory)
|
||||
{
|
||||
mData.format = PxHeightFieldFormat::eS16_TM;
|
||||
mData.rows = 0;
|
||||
mData.columns = 0;
|
||||
mData.convexEdgeThreshold = 0;
|
||||
mData.flags = PxHeightFieldFlags();
|
||||
mData.samples = NULL;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
HeightField::HeightField(MeshFactory* factory, 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
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
HeightField::~HeightField()
|
||||
{
|
||||
releaseMemory();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void HeightField::onRefCountZero()
|
||||
{
|
||||
::onRefCountZero(this, mMeshFactory, false, "PxHeightField::release: double deletion detected!");
|
||||
}
|
||||
|
||||
void 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 HeightField::importExtraData(PxDeserializationContext& context)
|
||||
{
|
||||
mData.samples = context.readExtraData<PxHeightFieldSample, PX_SERIAL_ALIGN>(mData.rows * mData.columns);
|
||||
}
|
||||
|
||||
HeightField* HeightField::createObject(PxU8*& address, PxDeserializationContext& context)
|
||||
{
|
||||
HeightField* obj = PX_PLACEMENT_NEW(address, HeightField(PxBaseFlag::eIS_RELEASABLE));
|
||||
address += sizeof(HeightField);
|
||||
obj->importExtraData(context);
|
||||
obj->resolveReferences(context);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void HeightField::release()
|
||||
{
|
||||
RefCountable_decRefCount(*this);
|
||||
}
|
||||
|
||||
void HeightField::acquireReference()
|
||||
{
|
||||
RefCountable_incRefCount(*this);
|
||||
}
|
||||
|
||||
PxU32 HeightField::getReferenceCount() const
|
||||
{
|
||||
return RefCountable_getRefCount(*this);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool 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;
|
||||
|
||||
// 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 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);
|
||||
if(version>=2)
|
||||
{
|
||||
mData.rowLimit = readDword(endian, stream);
|
||||
mData.colLimit = readDword(endian, stream);
|
||||
mData.nbColumns = readDword(endian, stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
mData.rowLimit = PxU32(readFloat(endian, stream));
|
||||
mData.colLimit = PxU32(readFloat(endian, stream));
|
||||
mData.nbColumns = PxU32(readFloat(endian, stream));
|
||||
}
|
||||
const float thickness = readFloat(endian, stream);
|
||||
PX_UNUSED(thickness);
|
||||
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 = PX_ALLOCATE(PxHeightFieldSample, nbVerts, "PxHeightFieldSample");
|
||||
if (mData.samples == NULL)
|
||||
return PxGetFoundation().error(PxErrorCode::eOUT_OF_MEMORY, PX_FL, "Gu::HeightField::load: PX_ALLOC failed!");
|
||||
|
||||
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 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.convexEdgeThreshold = desc.convexEdgeThreshold;
|
||||
mData.flags = desc.flags;
|
||||
mSampleStride = desc.samples.stride;
|
||||
|
||||
mData.rowLimit = mData.rows - 2;
|
||||
mData.colLimit = mData.columns - 2;
|
||||
mData.nbColumns = 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 = PX_ALLOCATE(PxHeightFieldSample, nbVerts, "PxHeightFieldSample");
|
||||
if(!mData.samples)
|
||||
return PxGetFoundation().error(PxErrorCode::eOUT_OF_MEMORY, PX_FL, "Gu::HeightField::load: PX_ALLOC failed!");
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
bool HeightField::save(PxOutputStream& stream, bool endian)
|
||||
{
|
||||
// write header
|
||||
if(!writeHeader('H', 'F', 'H', 'F', PX_HEIGHTFIELD_VERSION, endian, stream))
|
||||
return false;
|
||||
|
||||
const Gu::HeightFieldData& hfData = getData();
|
||||
|
||||
// write mData members
|
||||
writeDword(hfData.rows, endian, stream);
|
||||
writeDword(hfData.columns, endian, stream);
|
||||
writeDword(hfData.rowLimit, endian, stream);
|
||||
writeDword(hfData.colLimit, endian, stream);
|
||||
writeDword(hfData.nbColumns, endian, stream);
|
||||
writeFloat(0.0f, endian, stream); // thickness
|
||||
writeFloat(hfData.convexEdgeThreshold, endian, stream);
|
||||
writeWord(hfData.flags, endian, stream);
|
||||
writeDword(hfData.format, endian, stream);
|
||||
|
||||
writeFloat(hfData.mAABB.getMin(0), endian, stream);
|
||||
writeFloat(hfData.mAABB.getMin(1), endian, stream);
|
||||
writeFloat(hfData.mAABB.getMin(2), endian, stream);
|
||||
writeFloat(hfData.mAABB.getMax(0), endian, stream);
|
||||
writeFloat(hfData.mAABB.getMax(1), endian, stream);
|
||||
writeFloat(hfData.mAABB.getMax(2), endian, stream);
|
||||
|
||||
// write this-> members
|
||||
writeDword(mSampleStride, endian, stream);
|
||||
writeDword(mNbSamples, endian, stream);
|
||||
writeFloat(mMinHeight, endian, stream);
|
||||
writeFloat(mMaxHeight, endian, stream);
|
||||
|
||||
// write samples
|
||||
for(PxU32 i=0; i<mNbSamples; i++)
|
||||
{
|
||||
const PxHeightFieldSample& s = hfData.samples[i];
|
||||
writeWord(PxU16(s.height), endian, stream);
|
||||
stream.write(&s.materialIndex0, sizeof(s.materialIndex0));
|
||||
stream.write(&s.materialIndex1, sizeof(s.materialIndex1));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PxU32 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;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void HeightField::releaseMemory()
|
||||
{
|
||||
if(getBaseFlags() & PxBaseFlag::eOWNS_MEMORY)
|
||||
{
|
||||
PX_FREE(mData.samples);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace
|
||||
{
|
||||
struct EdgeData
|
||||
{
|
||||
PxU32 edgeIndex;
|
||||
PxU32 cell;
|
||||
PxU32 row;
|
||||
PxU32 column;
|
||||
};
|
||||
}
|
||||
|
||||
// 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 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
|
||||
const PxF32 epsx = 1.0f - PxAbs(x+1.0f) * 1e-6f; // epsilon needs to scale with values of x,z...
|
||||
const PxF32 epsz = 1.0f - PxAbs(z+1.0f) * 1e-6f;
|
||||
PxF32 x1 = i::selectMin(x, float(mData.rowLimit)+epsx);
|
||||
PxF32 z1 = i::selectMin(z, float(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 + PxU32(z);
|
||||
PX_ASSERT(vertexIndex < mData.rows*mData.columns);
|
||||
|
||||
return vertexIndex;
|
||||
}
|
||||
|
||||
1214
engine/third_party/physx/source/geomutils/src/hf/GuHeightField.h
vendored
Normal file
1214
engine/third_party/physx/source/geomutils/src/hf/GuHeightField.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
86
engine/third_party/physx/source/geomutils/src/hf/GuHeightFieldData.h
vendored
Normal file
86
engine/third_party/physx/source/geomutils/src/hf/GuHeightFieldData.h
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "geometry/PxHeightFieldFlag.h"
|
||||
#include "geometry/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)
|
||||
PxU32 rowLimit;
|
||||
PxU32 colLimit;
|
||||
PxU32 nbColumns;
|
||||
PxHeightFieldSample* samples; // PT: WARNING: don't change this member's name (used in ConvX)
|
||||
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
|
||||
229
engine/third_party/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp
vendored
Normal file
229
engine/third_party/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "geometry/PxMeshScale.h"
|
||||
|
||||
#include "GuHeightFieldUtil.h"
|
||||
#include "GuSweepSharedTests.h"
|
||||
#include "GuHeightField.h"
|
||||
#include "GuEntityReport.h"
|
||||
#include "foundation/PxIntrinsics.h"
|
||||
#include "CmScaling.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 = Cm::toMat33(scale);
|
||||
|
||||
bounds.minimum = mat33.transform(mHeightField->getData().mAABB.getMin());
|
||||
bounds.maximum = mat33.transform(mHeightField->getData().mAABB.getMax());
|
||||
|
||||
// PT: HFs will assert in Gu::intersectRayAABB2() if we don't deal with that
|
||||
const float deltaY = GU_MIN_AABB_EXTENT*0.5f - (bounds.maximum.y - bounds.minimum.y);
|
||||
if(deltaY>0.0f)
|
||||
{
|
||||
bounds.maximum.y += deltaY*0.6f;
|
||||
bounds.minimum.y -= deltaY*0.6f;
|
||||
}
|
||||
}
|
||||
|
||||
static PX_FORCE_INLINE bool reportTriangle(Gu::OverlapReport& callback, PxU32 material, PxU32* PX_RESTRICT indexBuffer, const PxU32 bufferSize, PxU32& indexBufferUsed, PxU32 triangleIndex)
|
||||
{
|
||||
if(material != PxHeightFieldMaterial::eHOLE)
|
||||
{
|
||||
indexBuffer[indexBufferUsed++] = triangleIndex;
|
||||
|
||||
if(indexBufferUsed >= bufferSize)
|
||||
{
|
||||
if(!callback.reportTouchedTris(indexBufferUsed, indexBuffer))
|
||||
return false;
|
||||
indexBufferUsed = 0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Gu::HeightFieldUtil::overlapAABBTriangles(const PxBounds3& bounds, OverlapReport& callback, PxU32 batchSize) const
|
||||
{
|
||||
PX_ASSERT(batchSize<=HF_OVERLAP_REPORT_BUFFER_SIZE);
|
||||
PX_ASSERT(!bounds.isEmpty());
|
||||
|
||||
PxBounds3 localBounds = bounds;
|
||||
|
||||
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.0f)
|
||||
PxSwap(localBounds.minimum.x, localBounds.maximum.x);
|
||||
|
||||
if(mHfGeom->columnScale < 0.0f)
|
||||
PxSwap(localBounds.minimum.z, localBounds.maximum.z);
|
||||
|
||||
// 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
|
||||
const PxU32 nbRows = mHeightField->getNbRowsFast();
|
||||
const PxU32 nbColumns = mHeightField->getNbColumnsFast();
|
||||
if(localBounds.minimum.x > float(nbRows - 1))
|
||||
return;
|
||||
if(localBounds.minimum.z > float(nbColumns - 1))
|
||||
return;
|
||||
if(localBounds.maximum.x < 0.0f)
|
||||
return;
|
||||
if(localBounds.maximum.z < 0.0f)
|
||||
return;
|
||||
|
||||
const PxU32 minRow = mHeightField->getMinRow(localBounds.minimum.x);
|
||||
const PxU32 maxRow = mHeightField->getMaxRow(localBounds.maximum.x);
|
||||
const PxU32 minColumn = mHeightField->getMinColumn(localBounds.minimum.z);
|
||||
const PxU32 maxColumn = mHeightField->getMaxColumn(localBounds.maximum.z);
|
||||
const PxU32 deltaColumn = maxColumn - minColumn;
|
||||
|
||||
const PxU32 maxNbTriangles = 2 * deltaColumn * (maxRow - minRow);
|
||||
if(!maxNbTriangles)
|
||||
return;
|
||||
|
||||
const PxU32 bufferSize = batchSize<=HF_OVERLAP_REPORT_BUFFER_SIZE ? batchSize : HF_OVERLAP_REPORT_BUFFER_SIZE;
|
||||
PxU32 indexBuffer[HF_OVERLAP_REPORT_BUFFER_SIZE];
|
||||
PxU32 indexBufferUsed = 0;
|
||||
|
||||
PxU32 offset = minRow * nbColumns + minColumn;
|
||||
|
||||
const PxReal miny = localBounds.minimum.y;
|
||||
const PxReal maxy = localBounds.maximum.y;
|
||||
const PxU32 columnStride = nbColumns - deltaColumn;
|
||||
|
||||
for(PxU32 row=minRow; row<maxRow; row++)
|
||||
{
|
||||
for(PxU32 column=minColumn; column<maxColumn; column++)
|
||||
{
|
||||
const PxReal h0 = mHeightField->getHeight(offset);
|
||||
const PxReal h1 = mHeightField->getHeight(offset + 1);
|
||||
const PxReal h2 = mHeightField->getHeight(offset + nbColumns);
|
||||
const PxReal h3 = mHeightField->getHeight(offset + nbColumns + 1);
|
||||
|
||||
const bool bmax = maxy < h0 && maxy < h1 && maxy < h2 && maxy < h3;
|
||||
const bool bmin = miny > h0 && miny > h1 && miny > h2 && miny > h3;
|
||||
|
||||
if(!(bmax || bmin))
|
||||
{
|
||||
if(!reportTriangle(callback, mHeightField->getMaterialIndex0(offset), indexBuffer, bufferSize, indexBufferUsed, offset << 1))
|
||||
return;
|
||||
|
||||
if(!reportTriangle(callback, mHeightField->getMaterialIndex1(offset), indexBuffer, bufferSize, indexBufferUsed, (offset << 1) + 1))
|
||||
return;
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
offset += columnStride;
|
||||
}
|
||||
|
||||
if(indexBufferUsed > 0)
|
||||
callback.reportTouchedTris(indexBufferUsed, indexBuffer);
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "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 (0) // ptchernev: Iterating over triangles becomes a pain.
|
||||
{
|
||||
if (mHeightField.getTriangleMaterial(triangleIndex) == mHfGeom.holeMaterialIndex)
|
||||
{
|
||||
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "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);
|
||||
}
|
||||
839
engine/third_party/physx/source/geomutils/src/hf/GuHeightFieldUtil.h
vendored
Normal file
839
engine/third_party/physx/source/geomutils/src/hf/GuHeightFieldUtil.h
vendored
Normal file
@@ -0,0 +1,839 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "geometry/PxHeightFieldGeometry.h"
|
||||
#include "geometry/PxTriangle.h"
|
||||
#include "foundation/PxBasicTemplates.h"
|
||||
#include "foundation/PxSIMDHelpers.h"
|
||||
|
||||
#include "GuHeightField.h"
|
||||
#include "../intersection/GuIntersectionRayTriangle.h"
|
||||
#include "../intersection/GuIntersectionRayBox.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
#define HF_SWEEP_REPORT_BUFFER_SIZE 64
|
||||
#define HF_OVERLAP_REPORT_BUFFER_SIZE 64
|
||||
|
||||
namespace Gu
|
||||
{
|
||||
class OverlapReport;
|
||||
|
||||
// PT: this is used in the context of sphere-vs-heightfield overlaps
|
||||
PX_FORCE_INLINE PxVec3 getLocalSphereData(PxBounds3& localBounds, const PxTransform& pose0, const PxTransform& pose1, float radius)
|
||||
{
|
||||
const PxVec3 localSphereCenter = pose1.transformInv(pose0.p);
|
||||
|
||||
const PxVec3 extents(radius);
|
||||
localBounds.minimum = localSphereCenter - extents;
|
||||
localBounds.maximum = localSphereCenter + extents;
|
||||
|
||||
return localSphereCenter;
|
||||
}
|
||||
|
||||
PX_FORCE_INLINE PxBounds3 getLocalCapsuleBounds(float radius, float halfHeight)
|
||||
{
|
||||
const PxVec3 extents(halfHeight + radius, radius, radius);
|
||||
return PxBounds3(-extents, extents);
|
||||
}
|
||||
|
||||
class PX_PHYSX_COMMON_API HeightFieldUtil
|
||||
{
|
||||
public:
|
||||
PxReal mOneOverRowScale;
|
||||
PxReal mOneOverHeightScale;
|
||||
PxReal mOneOverColumnScale;
|
||||
const Gu::HeightField* mHeightField;
|
||||
const PxHeightFieldGeometry* mHfGeom;
|
||||
|
||||
PX_FORCE_INLINE HeightFieldUtil(const PxHeightFieldGeometry& hfGeom) : mHeightField(static_cast<const Gu::HeightField*>(hfGeom.heightField)), mHfGeom(&hfGeom)
|
||||
{
|
||||
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_COMPILE_TIME_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;
|
||||
}
|
||||
|
||||
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 PxReal getHeightAtShapePoint(PxReal x, PxReal z) const
|
||||
{
|
||||
return mHfGeom->heightScale * mHeightField->getHeightInternal(x * mOneOverRowScale, z * mOneOverColumnScale);
|
||||
}
|
||||
|
||||
PX_FORCE_INLINE PxVec3 getNormalAtShapePoint(PxReal x, PxReal z) const
|
||||
{
|
||||
return mHeightField->getNormal_(x * mOneOverRowScale, z * mOneOverColumnScale, mOneOverRowScale, mOneOverHeightScale, mOneOverColumnScale);
|
||||
}
|
||||
|
||||
PxU32 getTriangle(const PxTransform&, PxTriangle& worldTri, PxU32* vertexIndices, PxU32* adjacencyIndices, PxTriangleID triangleIndex, bool worldSpaceTranslation=true, bool worldSpaceRotation=true) const;
|
||||
|
||||
void overlapAABBTriangles(const PxBounds3& localBounds, OverlapReport& callback, PxU32 batchSize=HF_OVERLAP_REPORT_BUFFER_SIZE) const;
|
||||
|
||||
PX_FORCE_INLINE void overlapAABBTriangles0to1(const PxTransform& pose0to1, const PxBounds3& bounds0, OverlapReport& callback, PxU32 batchSize=HF_OVERLAP_REPORT_BUFFER_SIZE) const
|
||||
{
|
||||
// PT: TODO: optimize PxBounds3::transformFast
|
||||
//overlapAABBTriangles(PxBounds3::transformFast(pose0to1, bounds0), callback, batchSize);
|
||||
{
|
||||
// PT: below is the equivalent, slightly faster code. Still not optimal but better.
|
||||
// PT: TODO: refactor with GuBounds.cpp
|
||||
|
||||
const PxMat33Padded basis(pose0to1.q);
|
||||
|
||||
// PT: TODO: pass c/e directly
|
||||
const PxBounds3 b = PxBounds3::basisExtent(pose0to1.transform(bounds0.getCenter()), basis, bounds0.getExtents());
|
||||
|
||||
overlapAABBTriangles(b, callback, batchSize);
|
||||
}
|
||||
}
|
||||
|
||||
PX_FORCE_INLINE void overlapAABBTriangles(const PxTransform& pose1, const PxBounds3& bounds0, OverlapReport& callback, PxU32 batchSize=HF_OVERLAP_REPORT_BUFFER_SIZE) const
|
||||
{
|
||||
overlapAABBTriangles0to1(pose1.getInverse(), bounds0, callback, batchSize);
|
||||
}
|
||||
|
||||
PX_FORCE_INLINE void overlapAABBTriangles(const PxTransform& pose0, const PxTransform& pose1, const PxBounds3& bounds0, OverlapReport& callback, PxU32 batchSize=HF_OVERLAP_REPORT_BUFFER_SIZE) const
|
||||
{
|
||||
overlapAABBTriangles0to1(pose1.transformInv(pose0), bounds0, callback, batchSize);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
class PX_PHYSX_COMMON_API HeightFieldTraceUtil : public HeightFieldUtil
|
||||
{
|
||||
public:
|
||||
PX_FORCE_INLINE HeightFieldTraceUtil(const PxHeightFieldGeometry& hfGeom) : HeightFieldUtil(hfGeom) {}
|
||||
|
||||
// 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 = OverlapLine();
|
||||
line.invalidate();
|
||||
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)
|
||||
{
|
||||
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(mHf.getMaterialIndex0(vertexIndex) != PxHeightFieldMaterial::eHOLE)
|
||||
{
|
||||
if(!addIndex(vertexIndex*2))
|
||||
return false;
|
||||
}
|
||||
if(mHf.getMaterialIndex1(vertexIndex) != PxHeightFieldMaterial::eHOLE)
|
||||
{
|
||||
if(!addIndex(vertexIndex*2 + 1))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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)
|
||||
// we need to use the unclamped values, otherwise we change the direction of the traversal
|
||||
const PxF32 uu0 = p0.x * mOneOverRowScale;
|
||||
PxF32 u0 = PxMin(PxMax(uu0, 1e-7f - expandu), nbUcells + expandu); // multiplication rescales the u,v grid steps to 1
|
||||
const PxF32 uv0 = p0.z * mOneOverColumnScale;
|
||||
PxF32 v0 = PxMin(PxMax(uv0, 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)
|
||||
// we need to use the unclamped values, otherwise we change the direction of the traversal
|
||||
const PxF32 uu1 = p1.x * mOneOverRowScale;
|
||||
const PxF32 uv1 = p1.z * mOneOverColumnScale;
|
||||
const PxReal h1 = p1.y; // we don't scale y
|
||||
|
||||
PxF32 du = uu1 - uu0, dv = uv1 - uv0; // 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 - uu0) / du;
|
||||
PxReal tv = (vhit0 - uv0) / dv;
|
||||
if(tu < 0.0f) // negative value may happen, as we may have started out of the AABB (since we did enlarge it)
|
||||
tu = PxAbs(clampEps / du);
|
||||
if(tv < 0.0f) // negative value may happen, as we may have started out of the AABB (since we did enlarge it)
|
||||
tv = PxAbs(clampEps / dv);
|
||||
|
||||
// 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)
|
||||
const PxF32 h00 = h[0+uflip+vflip];
|
||||
const PxF32 h01 = h[1+uflip-vflip];
|
||||
const PxF32 h10 = h[2-uflip+vflip];
|
||||
const PxF32 h11 = h[3-uflip-vflip];
|
||||
|
||||
const PxF32 minuif = PxMin(uif, uif+step_uif);
|
||||
const PxF32 maxuif = PxMax(uif, uif+step_uif);
|
||||
const PxF32 minvif = PxMin(vif, vif+step_vif);
|
||||
const PxF32 maxvif = PxMax(vif, vif+step_vif);
|
||||
const PxVec3 p00(minuif, h00, minvif);
|
||||
const PxVec3 p01(minuif, h01, maxvif);
|
||||
const PxVec3 p10(maxuif, h10, minvif);
|
||||
const 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)
|
||||
{
|
||||
PxReal triT0 = PX_MAX_REAL, triT1 = PX_MAX_REAL;
|
||||
bool hit0 = false, hit1 = false;
|
||||
PxF32 triU0, triV0, triU1, triV1;
|
||||
|
||||
// PT: TODO: consider testing hole first and skipping ray-tri test. Might be faster.
|
||||
if(Gu::intersectRayTriangle(auhP0, duhvNormalized, *p10a, *p00a, *p11a, triT0, triU0, triV0, backfaceCull, enlargeEpsilon) && triT0 >= 0.0f && triT0 <= duhvLength && (hf.getMaterialIndex0(vertIndex) != PxHeightFieldMaterial::eHOLE))
|
||||
{
|
||||
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 && (hf.getMaterialIndex1(vertIndex) != PxHeightFieldMaterial::eHOLE))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
const PxVec3 p00s(p00a->x * rowScale, p00a->y, p00a->z * columnScale);
|
||||
const PxVec3 p01s(p01a->x * rowScale, p01a->y, p01a->z * columnScale);
|
||||
const PxVec3 p10s(p10a->x * rowScale, p10a->y, p10a->z * columnScale);
|
||||
const 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)
|
||||
const PxF32 wnu = isZVS ? -1.0f : 1.0f, wnv = 1.0f; // uv-normal to triangle edge that splits the cell
|
||||
const 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
|
||||
const PxF32 tNext = PxMin(PxMin(tu, tv), 1.0f), tPrev = PxMax(last_tu, last_tv);
|
||||
|
||||
// compute uvs corresponding to tPrev, tNext
|
||||
const PxF32 unext = u0 + tNext*du, vnext = v0 + tNext*dv;
|
||||
const 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
|
||||
const PxU32 dotPrevGtz = PxU32(((uprev - wpu) * wnu + (vprev - wpv) * wnv) > 0);
|
||||
const PxU32 dotNextGtz = PxU32(((unext - wpu) * wnu + (vnext - wpv) * wnv) > 0);
|
||||
const PxU32 triIndex0 = cellIndex*2 + dotPrevGtz;
|
||||
const PxU32 triIndex1 = cellIndex*2 + dotNextGtz;
|
||||
PxU32 isHole0 = PxU32(hf.getMaterialIndex0(vertIndex) == PxHeightFieldMaterial::eHOLE);
|
||||
PxU32 isHole1 = PxU32(hf.getMaterialIndex1(vertIndex) == PxHeightFieldMaterial::eHOLE);
|
||||
if(triIndex0 > triIndex1)
|
||||
PxSwap<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
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Gu
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
361
engine/third_party/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp
vendored
Normal file
361
engine/third_party/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp
vendored
Normal file
@@ -0,0 +1,361 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "GuOverlapTests.h"
|
||||
#include "GuHeightFieldUtil.h"
|
||||
#include "GuBoxConversion.h"
|
||||
#include "GuInternal.h"
|
||||
#include "GuVecConvexHull.h"
|
||||
#include "GuEntityReport.h"
|
||||
#include "GuDistancePointTriangle.h"
|
||||
#include "GuIntersectionCapsuleTriangle.h"
|
||||
#include "GuDistanceSegmentTriangle.h"
|
||||
#include "GuBounds.h"
|
||||
#include "GuBV4_Common.h"
|
||||
#include "GuVecTriangle.h"
|
||||
#include "GuConvexMesh.h"
|
||||
#include "GuGJK.h"
|
||||
#include "geometry/PxSphereGeometry.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
using namespace aos;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace
|
||||
{
|
||||
struct HeightfieldOverlapReport : Gu::OverlapReport
|
||||
{
|
||||
PX_NOCOPY(HeightfieldOverlapReport)
|
||||
public:
|
||||
HeightfieldOverlapReport(const PxHeightFieldGeometry& hfGeom, const PxTransform& hfPose) : mHfUtil(hfGeom), mHFPose(hfPose), mOverlap(PxIntFalse) {}
|
||||
|
||||
const HeightFieldUtil mHfUtil;
|
||||
const PxTransform& mHFPose;
|
||||
PxIntBool mOverlap;
|
||||
};
|
||||
}
|
||||
|
||||
bool GeomOverlapCallback_SphereHeightfield(GU_OVERLAP_FUNC_PARAMS)
|
||||
{
|
||||
PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE);
|
||||
PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD);
|
||||
PX_UNUSED(cache);
|
||||
PX_UNUSED(threadContext);
|
||||
|
||||
const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0);
|
||||
const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom1);
|
||||
|
||||
struct SphereOverlapReport : HeightfieldOverlapReport
|
||||
{
|
||||
Sphere mLocalSphere;
|
||||
|
||||
SphereOverlapReport(const PxHeightFieldGeometry& hfGeom_, const PxTransform& hfPose, const PxVec3& localSphereCenter, float sphereRadius) : HeightfieldOverlapReport(hfGeom_, hfPose)
|
||||
{
|
||||
mLocalSphere.center = localSphereCenter;
|
||||
mLocalSphere.radius = sphereRadius * sphereRadius;
|
||||
}
|
||||
|
||||
virtual bool reportTouchedTris(PxU32 nb, const PxU32* indices)
|
||||
{
|
||||
while(nb--)
|
||||
{
|
||||
const PxU32 triangleIndex = *indices++;
|
||||
|
||||
PxTriangle currentTriangle;
|
||||
mHfUtil.getTriangle(mHFPose, currentTriangle, NULL, NULL, triangleIndex, false, false);
|
||||
|
||||
const PxVec3& p0 = currentTriangle.verts[0];
|
||||
const PxVec3& p1 = currentTriangle.verts[1];
|
||||
const PxVec3& p2 = currentTriangle.verts[2];
|
||||
|
||||
const PxVec3 edge10 = p1 - p0;
|
||||
const PxVec3 edge20 = p2 - p0;
|
||||
const PxVec3 cp = closestPtPointTriangle2(mLocalSphere.center, p0, p1, p2, edge10, edge20);
|
||||
const float sqrDist = (cp - mLocalSphere.center).magnitudeSquared();
|
||||
if(sqrDist <= mLocalSphere.radius) // mLocalSphere.radius has been pre-squared in the ctor
|
||||
{
|
||||
mOverlap = PxIntTrue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
PxBounds3 localBounds;
|
||||
const PxVec3 localSphereCenter = getLocalSphereData(localBounds, pose0, pose1, sphereGeom.radius);
|
||||
|
||||
SphereOverlapReport report(hfGeom, pose1, localSphereCenter, sphereGeom.radius);
|
||||
|
||||
report.mHfUtil.overlapAABBTriangles(localBounds, report, 4);
|
||||
return report.mOverlap!=PxIntFalse;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool GeomOverlapCallback_CapsuleHeightfield(GU_OVERLAP_FUNC_PARAMS)
|
||||
{
|
||||
PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE);
|
||||
PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD);
|
||||
PX_UNUSED(cache);
|
||||
PX_UNUSED(threadContext);
|
||||
|
||||
const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom0);
|
||||
const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom1);
|
||||
|
||||
struct CapsuleOverlapReport : HeightfieldOverlapReport
|
||||
{
|
||||
Capsule mLocalCapsule;
|
||||
CapsuleTriangleOverlapData mData;
|
||||
|
||||
CapsuleOverlapReport(const PxHeightFieldGeometry& hfGeom_, const PxTransform& hfPose) : HeightfieldOverlapReport(hfGeom_, hfPose) {}
|
||||
|
||||
virtual bool reportTouchedTris(PxU32 nb, const PxU32* indices)
|
||||
{
|
||||
while(nb--)
|
||||
{
|
||||
const PxU32 triangleIndex = *indices++;
|
||||
|
||||
PxTriangle currentTriangle;
|
||||
mHfUtil.getTriangle(mHFPose, currentTriangle, NULL, NULL, triangleIndex, false, false);
|
||||
|
||||
const PxVec3& p0 = currentTriangle.verts[0];
|
||||
const PxVec3& p1 = currentTriangle.verts[1];
|
||||
const PxVec3& p2 = currentTriangle.verts[2];
|
||||
|
||||
if(0)
|
||||
{
|
||||
PxReal t,u,v;
|
||||
const PxVec3 p1_p0 = p1 - p0;
|
||||
const PxVec3 p2_p0 = p2 - p0;
|
||||
const PxReal sqrDist = distanceSegmentTriangleSquared(mLocalCapsule, p0, p1_p0, p2_p0, &t, &u, &v);
|
||||
if(sqrDist <= mLocalCapsule.radius*mLocalCapsule.radius)
|
||||
{
|
||||
mOverlap = PxIntTrue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const PxVec3 normal = (p0 - p1).cross(p0 - p2);
|
||||
if(intersectCapsuleTriangle(normal, p0, p1, p2, mLocalCapsule, mData))
|
||||
{
|
||||
mOverlap = PxIntTrue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
CapsuleOverlapReport report(hfGeom, pose1);
|
||||
|
||||
// PT: TODO: move away from internal header
|
||||
const PxVec3 tmp = getCapsuleHalfHeightVector(pose0, capsuleGeom);
|
||||
|
||||
// PT: TODO: refactor - but might be difficult because we reuse relPose for two tasks here
|
||||
const PxTransform relPose = pose1.transformInv(pose0);
|
||||
const PxVec3 localDelta = pose1.rotateInv(tmp);
|
||||
|
||||
report.mLocalCapsule.p0 = relPose.p + localDelta;
|
||||
report.mLocalCapsule.p1 = relPose.p - localDelta;
|
||||
report.mLocalCapsule.radius = capsuleGeom.radius;
|
||||
report.mData.init(report.mLocalCapsule);
|
||||
|
||||
PxBounds3 localBounds;
|
||||
computeCapsuleBounds(localBounds, capsuleGeom, relPose);
|
||||
|
||||
report.mHfUtil.overlapAABBTriangles(localBounds, report, 4);
|
||||
//hfUtil.overlapAABBTriangles(pose0, pose1, getLocalCapsuleBounds(capsuleGeom.radius, capsuleGeom.halfHeight), report, 4);
|
||||
return report.mOverlap!=PxIntFalse;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PxIntBool intersectTriangleBoxBV4(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2,
|
||||
const PxMat33& rotModelToBox, const PxVec3& transModelToBox, const PxVec3& extents);
|
||||
|
||||
bool GeomOverlapCallback_BoxHeightfield(GU_OVERLAP_FUNC_PARAMS)
|
||||
{
|
||||
PX_ASSERT(geom0.getType()==PxGeometryType::eBOX);
|
||||
PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD);
|
||||
PX_UNUSED(cache);
|
||||
PX_UNUSED(threadContext);
|
||||
|
||||
const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom0);
|
||||
const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom1);
|
||||
|
||||
struct BoxOverlapReport : HeightfieldOverlapReport
|
||||
{
|
||||
PxMat33 mRModelToBox;
|
||||
PxVec3p mTModelToBox;
|
||||
PxVec3p mBoxExtents;
|
||||
|
||||
BoxOverlapReport(const PxHeightFieldGeometry& hfGeom_, const PxTransform& hfPose) : HeightfieldOverlapReport(hfGeom_, hfPose) {}
|
||||
|
||||
virtual bool reportTouchedTris(PxU32 nb, const PxU32* indices)
|
||||
{
|
||||
while(nb--)
|
||||
{
|
||||
const PxU32 triangleIndex = *indices++;
|
||||
|
||||
PxTrianglePadded currentTriangle;
|
||||
mHfUtil.getTriangle(mHFPose, currentTriangle, NULL, NULL, triangleIndex, false, false);
|
||||
|
||||
if(intersectTriangleBoxBV4(currentTriangle.verts[0], currentTriangle.verts[1], currentTriangle.verts[2], mRModelToBox, mTModelToBox, mBoxExtents))
|
||||
{
|
||||
mOverlap = PxIntTrue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
BoxOverlapReport report(hfGeom, pose1);
|
||||
|
||||
// PT: TODO: revisit / refactor all this code
|
||||
const PxTransform relPose = pose1.transformInv(pose0);
|
||||
Box localBox;
|
||||
buildFrom(localBox, relPose.p, boxGeom.halfExtents, relPose.q);
|
||||
|
||||
invertBoxMatrix(report.mRModelToBox, report.mTModelToBox, localBox);
|
||||
|
||||
report.mBoxExtents = localBox.extents;
|
||||
|
||||
PxBounds3 localBounds;
|
||||
{
|
||||
// PT: TODO: refactor with bounds code?
|
||||
const PxMat33& basis = localBox.rot;
|
||||
|
||||
// extended basis vectors
|
||||
const Vec4V c0V = V4Scale(V4LoadU(&basis.column0.x), FLoad(localBox.extents.x));
|
||||
const Vec4V c1V = V4Scale(V4LoadU(&basis.column1.x), FLoad(localBox.extents.y));
|
||||
const Vec4V c2V = V4Scale(V4LoadU(&basis.column2.x), FLoad(localBox.extents.z));
|
||||
|
||||
// find combination of base vectors that produces max. distance for each component = sum of abs()
|
||||
Vec4V extentsV = V4Add(V4Abs(c0V), V4Abs(c1V));
|
||||
extentsV = V4Add(extentsV, V4Abs(c2V));
|
||||
|
||||
const PxVec3p origin(localBox.center);
|
||||
|
||||
const Vec4V originV = V4LoadU(&origin.x);
|
||||
const Vec4V minV = V4Sub(originV, extentsV);
|
||||
const Vec4V maxV = V4Add(originV, extentsV);
|
||||
|
||||
StoreBounds(localBounds, minV, maxV);
|
||||
}
|
||||
report.mHfUtil.overlapAABBTriangles(localBounds, report, 4);
|
||||
return report.mOverlap!=PxIntFalse;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool GeomOverlapCallback_ConvexHeightfield(GU_OVERLAP_FUNC_PARAMS)
|
||||
{
|
||||
PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH);
|
||||
PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD);
|
||||
PX_UNUSED(cache);
|
||||
PX_UNUSED(threadContext);
|
||||
|
||||
const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom0);
|
||||
const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom1);
|
||||
|
||||
struct ConvexOverlapReport : HeightfieldOverlapReport
|
||||
{
|
||||
ConvexHullV mConvex;
|
||||
PxMatTransformV aToB;
|
||||
|
||||
ConvexOverlapReport(const PxHeightFieldGeometry& hfGeom_, const PxTransform& hfPose) : HeightfieldOverlapReport(hfGeom_, hfPose) {}
|
||||
|
||||
virtual bool reportTouchedTris(PxU32 nb, const PxU32* indices)
|
||||
{
|
||||
while(nb--)
|
||||
{
|
||||
const PxU32 triangleIndex = *indices++;
|
||||
|
||||
PxTrianglePadded currentTriangle;
|
||||
mHfUtil.getTriangle(mHFPose, currentTriangle, NULL, NULL, triangleIndex, false, false);
|
||||
|
||||
const PxVec3& p0 = currentTriangle.verts[0];
|
||||
const PxVec3& p1 = currentTriangle.verts[1];
|
||||
const PxVec3& p2 = currentTriangle.verts[2];
|
||||
|
||||
// PT: TODO: consider adding an extra triangle-vs-box culling test here
|
||||
|
||||
// PT: TODO: optimize
|
||||
const Vec3V v0 = V3LoadU(p0);
|
||||
const Vec3V v1 = V3LoadU(p1);
|
||||
const Vec3V v2 = V3LoadU(p2);
|
||||
|
||||
// PT: TODO: refactor with ConvexVsMeshOverlapCallback
|
||||
TriangleV triangle(v0, v1, v2);
|
||||
Vec3V contactA, contactB, normal;
|
||||
FloatV dist;
|
||||
const RelativeConvex<TriangleV> convexA(triangle, aToB);
|
||||
const LocalConvex<ConvexHullV> convexB(mConvex);
|
||||
const GjkStatus status = gjk(convexA, convexB, aToB.p, FZero(), contactA, contactB, normal, dist);
|
||||
if(status == GJK_CONTACT || status == GJK_CLOSE)// || FAllGrtrOrEq(mSqTolerance, sqDist))
|
||||
{
|
||||
mOverlap = PxIntTrue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
ConvexOverlapReport report(hfGeom, pose1);
|
||||
|
||||
const ConvexMesh* cm = static_cast<const ConvexMesh*>(convexGeom.convexMesh);
|
||||
|
||||
const bool idtScaleConvex = convexGeom.scale.isIdentity();
|
||||
|
||||
{
|
||||
const ConvexHullData* hullData = &cm->getHull();
|
||||
|
||||
const Vec3V vScale0 = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale
|
||||
const QuatV vQuat0 = QuatVLoadU(&convexGeom.scale.rotation.x);
|
||||
|
||||
report.mConvex = ConvexHullV(hullData, V3Zero(), vScale0, vQuat0, idtScaleConvex);
|
||||
// PT: TODO: is that transform correct? It looks like the opposite of what we do for other prims?
|
||||
report.aToB = PxMatTransformV(pose0.transformInv(pose1));
|
||||
//report.aToB = PxMatTransformV(pose1.transformInv(pose0));
|
||||
}
|
||||
|
||||
const PxTransform relPose = pose1.transformInv(pose0);
|
||||
|
||||
PxBounds3 localBounds;
|
||||
computeBounds(localBounds, convexGeom, relPose, 0.0f, 1.0f);
|
||||
|
||||
report.mHfUtil.overlapAABBTriangles(localBounds, report, 4);
|
||||
return report.mOverlap!=PxIntFalse;
|
||||
}
|
||||
604
engine/third_party/physx/source/geomutils/src/hf/GuSweepsHF.cpp
vendored
Normal file
604
engine/third_party/physx/source/geomutils/src/hf/GuSweepsHF.cpp
vendored
Normal file
@@ -0,0 +1,604 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "CmScaling.h"
|
||||
#include "GuSweepCapsuleTriangle.h"
|
||||
#include "GuInternal.h"
|
||||
#include "GuGJKRaycast.h"
|
||||
#include "CmMatrix34.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
using namespace Cm;
|
||||
using namespace aos;
|
||||
|
||||
#include "GuSweepConvexTri.h"
|
||||
|
||||
#define AbortTraversal false
|
||||
#define ContinueTraversal true
|
||||
|
||||
#if PX_VC
|
||||
#pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value.
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class HeightFieldTraceSegmentSweepHelper
|
||||
{
|
||||
PX_NOCOPY(HeightFieldTraceSegmentSweepHelper)
|
||||
public:
|
||||
HeightFieldTraceSegmentSweepHelper(const HeightFieldTraceUtil& 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 HeightFieldTraceUtil& mHfUtil;
|
||||
const PxVec3& mOverlapObjectExtent;
|
||||
PxBounds3 mLocalBounds;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class HeightFieldTraceSegmentReport : public EntityReport
|
||||
{
|
||||
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::eANY_HIT)
|
||||
{
|
||||
}
|
||||
|
||||
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, PxGeomSweepHit& sweepHit, const PxTransform& pose, PxReal distance) :
|
||||
HeightFieldTraceSegmentReport (hfUtil, hitFlags),
|
||||
mInflatedCapsule (inflatedCapsule),
|
||||
mUnitDir (unitDir),
|
||||
mSweepHit (sweepHit),
|
||||
mPose (pose),
|
||||
mDistance (distance)
|
||||
{
|
||||
mSweepHit.faceIndex = 0xFFFFffff;
|
||||
}
|
||||
|
||||
virtual bool onEvent(PxU32 nb, const 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);
|
||||
}
|
||||
|
||||
PxGeomSweepHit 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(PxGeomSweepHit& 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::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, 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::eNORMAL| PxHitFlag::ePOSITION | PxHitFlag::eFACE_INDEX;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const Capsule& mInflatedCapsule;
|
||||
const PxVec3& mUnitDir;
|
||||
PxGeomSweepHit& mSweepHit;
|
||||
const PxTransform& mPose;
|
||||
const PxReal mDistance;
|
||||
};
|
||||
|
||||
bool sweepCapsule_HeightFieldGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS)
|
||||
{
|
||||
PX_UNUSED(threadContext);
|
||||
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 HeightFieldTraceUtil 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 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 PxTransformV meshTransf(p0, q0);
|
||||
const PxTransformV 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 bool onEvent(PxU32 nbEntities, const 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(PxGeomSweepHit& 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, sweepHit);
|
||||
|
||||
sweepHit.faceIndex = mSweepHit.faceIndex;
|
||||
sweepHit.flags = 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:
|
||||
PxMatTransformV mMeshToConvex;
|
||||
PxTransformV mConvexPoseV;
|
||||
ConvexHullV mConvexHull;
|
||||
PxGeomSweepHit 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);
|
||||
PX_UNUSED(threadContext);
|
||||
const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom);
|
||||
|
||||
const Matrix34FromTransform convexTM(convexPose);
|
||||
const Matrix34FromTransform 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 HeightFieldTraceUtil 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);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class BoxTraceSegmentReport : public HeightFieldTraceSegmentReport
|
||||
{
|
||||
PX_NOCOPY(BoxTraceSegmentReport)
|
||||
public:
|
||||
BoxTraceSegmentReport( const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags,
|
||||
const PxTransformV& worldToBoxV, const PxTransform& pose, const BoxV& box, const PxVec3& localMotion,
|
||||
PxGeomSweepHit& sweepHit, PxReal inflation) :
|
||||
HeightFieldTraceSegmentReport (hfUtil, hitFlags),
|
||||
mWorldToBoxV (worldToBoxV),
|
||||
mPose (pose),
|
||||
mBox (box),
|
||||
mLocalMotion (localMotion),
|
||||
mSweepHit (sweepHit),
|
||||
mInflation (inflation)
|
||||
{
|
||||
mMinToi = FMax();
|
||||
mSweepHit.faceIndex = 0xFFFFffff;
|
||||
}
|
||||
|
||||
virtual bool onEvent(PxU32 nb, const 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(PxGeomSweepHit& 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::eNORMAL | PxHitFlag::eFACE_INDEX;
|
||||
|
||||
if(mHitFlags & PxHitFlag::eMTD)
|
||||
{
|
||||
const bool hasContacts = computeBox_HeightFieldMTD(hfGeom, pose, box, boxPose_, inflation, mIsDoubleSided, 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::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const PxTransformV& mWorldToBoxV;
|
||||
const PxTransform& mPose;
|
||||
const BoxV& mBox;
|
||||
FloatV mMinToi;
|
||||
const PxVec3 mLocalMotion;
|
||||
PxGeomSweepHit& mSweepHit;
|
||||
const PxReal mInflation;
|
||||
};
|
||||
|
||||
bool sweepBox_HeightFieldGeom(GU_BOX_SWEEP_FUNC_PARAMS)
|
||||
{
|
||||
PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD);
|
||||
PX_UNUSED(threadContext);
|
||||
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 PxTransformV 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 HeightFieldTraceUtil 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);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
Reference in New Issue
Block a user