feat(physics): wire physx sdk into build

This commit is contained in:
2026-04-15 12:22:15 +08:00
parent 5bf258df6d
commit 31f40e2cbb
2044 changed files with 752623 additions and 1 deletions

View File

@@ -0,0 +1,411 @@
// 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 "foundation/PxMemory.h"
#include "GuBV32.h"
#include "CmSerialize.h"
#include "CmUtils.h"
#include "foundation/PxUtilities.h"
#include "foundation/PxVecMath.h"
using namespace physx;
using namespace Gu;
using namespace Cm;
BV32Tree::BV32Tree(SourceMesh* meshInterface, const PxBounds3& localBounds)
{
reset();
init(meshInterface, localBounds);
}
BV32Tree::BV32Tree()
{
reset();
}
void BV32Tree::release()
{
if (!mUserAllocated)
{
PX_DELETE_ARRAY(mNodes);
PX_FREE(mPackedNodes);
PX_FREE(mTreeDepthInfo);
PX_FREE(mRemapPackedNodeIndexWithDepth);
}
mNodes = NULL;
mNbNodes = 0;
mMaxTreeDepth = 0;
}
BV32Tree::~BV32Tree()
{
release();
}
void BV32Tree::reset()
{
mMeshInterface = NULL;
mNbNodes = 0;
mNodes = NULL;
mNbPackedNodes = 0;
mPackedNodes = NULL;
mMaxTreeDepth = 0;
mTreeDepthInfo = NULL;
mRemapPackedNodeIndexWithDepth = NULL;
mInitData = 0;
mUserAllocated = false;
}
void BV32Tree::operator=(BV32Tree& v)
{
mMeshInterface = v.mMeshInterface;
mLocalBounds = v.mLocalBounds;
mNbNodes = v.mNbNodes;
mNodes = v.mNodes;
mInitData = v.mInitData;
mUserAllocated = v.mUserAllocated;
v.reset();
}
bool BV32Tree::init(SourceMeshBase* meshInterface, const PxBounds3& localBounds)
{
mMeshInterface = meshInterface;
mLocalBounds.init(localBounds);
return true;
}
// PX_SERIALIZATION
BV32Tree::BV32Tree(const PxEMPTY)
{
mUserAllocated = true;
}
void BV32Tree::exportExtraData(PxSerializationContext& stream)
{
stream.alignData(16);
stream.writeData(mPackedNodes, mNbPackedNodes*sizeof(BV32DataPacked));
}
void BV32Tree::importExtraData(PxDeserializationContext& context)
{
context.alignExtraData(16);
mPackedNodes = context.readExtraData<BV32DataPacked>(mNbPackedNodes);
}
//~PX_SERIALIZATION
bool BV32Tree::load(PxInputStream& stream, bool mismatch_)
{
PX_ASSERT(!mUserAllocated);
release();
PxI8 a, b, c, d;
readChunk(a, b, c, d, stream);
if(a != 'B' || b != 'V' || c != '3' || d != '2')
return false;
bool mismatch;
PxU32 fileVersion;
if(!readBigEndianVersionNumber(stream, mismatch_, fileVersion, mismatch))
return false;
mLocalBounds.mCenter.x = readFloat(mismatch, stream);
mLocalBounds.mCenter.y = readFloat(mismatch, stream);
mLocalBounds.mCenter.z = readFloat(mismatch, stream);
mLocalBounds.mExtentsMagnitude = readFloat(mismatch, stream);
mInitData = readDword(mismatch, stream);
/*const PxU32 nbNodes = readDword(mismatch, stream);
mNbNodes = nbNodes;
if (nbNodes)
{
BV32Data* nodes = PX_NEW(BV32Data)[nbNodes];
mNodes = nodes;
PxMarkSerializedMemory(nodes, sizeof(BV32Data)*nbNodes);
for (PxU32 i = 0; i<nbNodes; i++)
{
BV32Data& node = nodes[i];
readFloatBuffer(&node.mCenter.x, 3, mismatch, stream);
node.mData = readDword(mismatch, stream);
readFloatBuffer(&node.mExtents.x, 3, mismatch, stream);
}
}*/
//read SOA format node data
const PxU32 nbPackedNodes = readDword(mismatch, stream);
mNbPackedNodes = nbPackedNodes;
if (nbPackedNodes)
{
mPackedNodes = reinterpret_cast<BV32DataPacked*>(PX_ALLOC(sizeof(BV32DataPacked)*nbPackedNodes, "BV32DataPacked"));
PxMarkSerializedMemory(mPackedNodes, sizeof(BV32DataPacked)*nbPackedNodes);
for (PxU32 i = 0; i < nbPackedNodes; ++i)
{
BV32DataPacked& node = mPackedNodes[i];
node.mNbNodes = readDword(mismatch, stream);
PX_ASSERT(node.mNbNodes > 0);
node.mDepth = readDword(mismatch, stream);
ReadDwordBuffer(node.mData, node.mNbNodes, mismatch, stream);
const PxU32 nbElements = 4 * node.mNbNodes;
readFloatBuffer(&node.mMin[0].x, nbElements, mismatch, stream);
readFloatBuffer(&node.mMax[0].x, nbElements, mismatch, stream);
}
}
const PxU32 maxTreeDepth = readDword(mismatch, stream);
mMaxTreeDepth = maxTreeDepth;
if (maxTreeDepth > 0)
{
mTreeDepthInfo = reinterpret_cast<BV32DataDepthInfo*>(PX_ALLOC(sizeof(BV32DataDepthInfo)*maxTreeDepth, "BV32DataDepthInfo"));
for (PxU32 i = 0; i < maxTreeDepth; ++i)
{
BV32DataDepthInfo& info = mTreeDepthInfo[i];
info.offset = readDword(mismatch, stream);
info.count = readDword(mismatch, stream);
}
mRemapPackedNodeIndexWithDepth = reinterpret_cast<PxU32*>(PX_ALLOC(sizeof(PxU32)*nbPackedNodes, "PxU32"));
ReadDwordBuffer(mRemapPackedNodeIndexWithDepth, nbPackedNodes, mismatch, stream);
}
return true;
}
void BV32Tree::createSOAformatNode(BV32DataPacked& packedData,
const BV32Data& node, const PxU32 childOffset, PxU32& currentIndex, PxU32& nbPackedNodes)
{
//found the next 32 nodes and fill it in SOA format
const PxU32 nbChildren = node.getNbChildren();
const PxU32 offset = node.getChildOffset();
packedData.mDepth = node.mDepth;
for (PxU32 i = 0; i < nbChildren; ++i)
{
BV32Data& child = mNodes[offset + i];
packedData.mMin[i] = PxVec4(child.mMin, 0.f);
packedData.mMax[i] = PxVec4(child.mMax, 0.f);
packedData.mData[i] = PxU32(child.mData);
}
packedData.mNbNodes = nbChildren;
PxU32 NbToGo = 0;
PxU32 NextIDs[32];
PxMemSet(NextIDs, PX_INVALID_U32, sizeof(PxU32) * 32);
const BV32Data* ChildNodes[32];
PxMemSet(ChildNodes, 0, sizeof(BV32Data*) * 32);
for (PxU32 i = 0; i< nbChildren; i++)
{
BV32Data& child = mNodes[offset + i];
if (!child.isLeaf())
{
const PxU32 NextID = currentIndex;
const PxU32 ChildSize = child.getNbChildren() - child.mNbLeafNodes;
currentIndex += ChildSize;
//packedData.mData[i] = (packedData.mData[i] & ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1)) | (NextID << GU_BV4_CHILD_OFFSET_SHIFT_COUNT);
packedData.mData[i] = (packedData.mData[i] & ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1)) | ((childOffset + NbToGo) << GU_BV4_CHILD_OFFSET_SHIFT_COUNT);
NextIDs[NbToGo] = NextID;
ChildNodes[NbToGo] = &child;
NbToGo++;
}
}
nbPackedNodes += NbToGo;
for (PxU32 i = 0; i < NbToGo; ++i)
{
const BV32Data& child = *ChildNodes[i];
BV32DataPacked& childData = mPackedNodes[childOffset+i];
createSOAformatNode(childData, child, NextIDs[i], currentIndex, nbPackedNodes);
}
}
bool BV32Tree::refit(float epsilon)
{
using namespace aos;
if (!mPackedNodes)
{
PxBounds3 bounds;
bounds.setEmpty();
if (mMeshInterface)
{
PxU32 nbVerts = mMeshInterface->getNbVertices();
const PxVec3* verts = mMeshInterface->getVerts();
while (nbVerts--)
bounds.include(*verts++);
mLocalBounds.init(bounds);
}
return true;
}
class PxBounds3Padded : public PxBounds3
{
public:
PX_FORCE_INLINE PxBounds3Padded() {}
PX_FORCE_INLINE ~PxBounds3Padded() {}
PxU32 padding;
};
PxU32 nb = mNbPackedNodes;
while (nb--)
{
BV32DataPacked* PX_RESTRICT current = mPackedNodes + nb;
const PxU32 nbChildren = current->mNbNodes;
for (PxU32 j = 0; j< nbChildren; j++)
{
if (current->isLeaf(j))
{
PxU32 nbTets = current->getNbReferencedPrimitives(j);
PxU32 primIndex = current->getPrimitiveStartIndex(j);
Vec4V minV = V4Load(FLT_MAX);
Vec4V maxV = V4Load(-FLT_MAX);
//TetrahedronPointers
do
{
PX_ASSERT(primIndex< mMeshInterface->getNbPrimitives());
//meshInterface->getTriangle(VP, primIndex);
Vec4V tMin, tMax;
mMeshInterface->getPrimitiveBox(primIndex, tMin, tMax);
minV = V4Min(minV, tMin);
maxV = V4Max(maxV, tMax);
primIndex++;
} while (--nbTets);
const Vec4V epsilonV = V4Load(epsilon);
minV = V4Sub(minV, epsilonV);
maxV = V4Add(maxV, epsilonV);
PxBounds3Padded refitBox;
V4StoreU_Safe(minV, &refitBox.minimum.x);
V4StoreU_Safe(maxV, &refitBox.maximum.x);
current->mMin[j].x = refitBox.minimum.x;
current->mMin[j].y = refitBox.minimum.y;
current->mMin[j].z = refitBox.minimum.z;
current->mMax[j].x = refitBox.maximum.x;
current->mMax[j].y = refitBox.maximum.y;
current->mMax[j].z = refitBox.maximum.z;
}
else
{
PxU32 childOffset = current->getChildOffset(j);
PX_ASSERT(childOffset < mNbPackedNodes);
BV32DataPacked* next = mPackedNodes + childOffset;
const PxU32 nextNbChilds = next->mNbNodes;
Vec4V minV = V4Load(FLT_MAX);
Vec4V maxV = V4Load(-FLT_MAX);
for (PxU32 a = 0; a < nextNbChilds; ++a)
{
const Vec4V tMin = V4LoadU(&next->mMin[a].x);
const Vec4V tMax = V4LoadU(&next->mMax[a].x);
minV = V4Min(minV, tMin);
maxV = V4Max(maxV, tMax);
}
PxBounds3Padded refitBox;
V4StoreU_Safe(minV, &refitBox.minimum.x);
V4StoreU_Safe(maxV, &refitBox.maximum.x);
current->mMin[j].x = refitBox.minimum.x;
current->mMin[j].y = refitBox.minimum.y;
current->mMin[j].z = refitBox.minimum.z;
current->mMax[j].x = refitBox.maximum.x;
current->mMax[j].y = refitBox.maximum.y;
current->mMax[j].z = refitBox.maximum.z;
}
}
}
BV32DataPacked* root = mPackedNodes;
{
PxBounds3 globalBounds;
globalBounds.setEmpty();
const PxU32 nbChildren = root->mNbNodes;
Vec4V minV = V4Load(FLT_MAX);
Vec4V maxV = V4Load(-FLT_MAX);
for (PxU32 a = 0; a < nbChildren; ++a)
{
const Vec4V tMin = V4LoadU(&root->mMin[a].x);
const Vec4V tMax = V4LoadU(&root->mMax[a].x);
minV = V4Min(minV, tMin);
maxV = V4Max(maxV, tMax);
}
PxBounds3Padded refitBox;
V4StoreU_Safe(minV, &refitBox.minimum.x);
V4StoreU_Safe(maxV, &refitBox.maximum.x);
globalBounds.minimum.x = refitBox.minimum.x;
globalBounds.minimum.y = refitBox.minimum.y;
globalBounds.minimum.z = refitBox.minimum.z;
globalBounds.maximum.x = refitBox.maximum.x;
globalBounds.maximum.y = refitBox.maximum.y;
globalBounds.maximum.z = refitBox.maximum.z;
mLocalBounds.init(globalBounds);
}
return true;
}

View File

@@ -0,0 +1,164 @@
// 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_BV32_H
#define GU_BV32_H
#include "foundation/PxSimpleTypes.h"
#include "foundation/PxBounds3.h"
#include "foundation/PxVec4.h"
#include "common/PxSerialFramework.h"
#include "foundation/PxUserAllocated.h"
#include "foundation/PxArray.h"
#include "GuBV4.h"
namespace physx
{
namespace Gu
{
struct BV32Data : public physx::PxUserAllocated
{
PxVec3 mMin;
PxVec3 mMax;
PxU32 mNbLeafNodes;
PxU32 mDepth;
size_t mData;
PX_FORCE_INLINE BV32Data() : mNbLeafNodes(0), mDepth(0), mData(PX_INVALID_U32)
{
setEmpty();
}
PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 isLeaf() const { return mData & 1; }
//if the node is leaf,
PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbReferencedPrimitives() const { PX_ASSERT(isLeaf()); return PxU32((mData >>1)&63); }
PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getPrimitiveStartIndex() const { PX_ASSERT(isLeaf()); return PxU32(mData >> 7); }
//PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getPrimitive() const { return mData >> 1; }
//if the node isn't leaf, we will get the childOffset
PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getChildOffset() const { PX_ASSERT(!isLeaf()); return PxU32(mData >> GU_BV4_CHILD_OFFSET_SHIFT_COUNT); }
PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbChildren() const { PX_ASSERT(!isLeaf()); return ((mData) & ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1))>>1; }
PX_CUDA_CALLABLE PX_FORCE_INLINE void getMinMax(PxVec3& min, PxVec3& max) const
{
//min = mCenter - mExtents;
//max = mCenter + mExtents;
min = mMin;
max = mMax;
}
PX_FORCE_INLINE void setEmpty()
{
//mCenter = PxVec3(0.0f, 0.0f, 0.0f);
//mExtents = PxVec3(-1.0f, -1.0f, -1.0f);
mMin = PxVec3(PX_MAX_F32);
mMax = PxVec3(-PX_MAX_F32);
}
};
PX_ALIGN_PREFIX(16)
struct BV32DataPacked
{
/*PxVec4 mCenter[32];
PxVec4 mExtents[32];*/
PxVec4 mMin[32];
PxVec4 mMax[32];
PxU32 mData[32];
PxU32 mNbNodes;
PxU32 mDepth;
PxU32 padding[2];
PX_CUDA_CALLABLE PX_FORCE_INLINE BV32DataPacked() : mNbNodes(0), mDepth(0)
{
}
PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 isLeaf(const PxU32 index) const { return mData[index] & 1; }
//if the node is leaf
PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbReferencedPrimitives(const PxU32 index) const { PX_ASSERT(isLeaf(index)); return (mData[index] >> 1) & 63; }
PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getPrimitiveStartIndex(const PxU32 index) const { PX_ASSERT(isLeaf(index)); return (mData[index] >> 7); }
//if the node isn't leaf, we will get the childOffset
PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getChildOffset(const PxU32 index) const { PX_ASSERT(!isLeaf(index)); return mData[index] >> GU_BV4_CHILD_OFFSET_SHIFT_COUNT; }
PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbChildren(const PxU32 index) const { PX_ASSERT(!isLeaf(index)); return ((mData[index])& ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1)) >> 1; }
}
PX_ALIGN_SUFFIX(16);
//This struct store the start and end index of the packed node at the same depth level in the tree
struct BV32DataDepthInfo
{
public:
PxU32 offset;
PxU32 count;
};
class BV32Tree : public physx::PxUserAllocated
{
public:
// PX_SERIALIZATION
BV32Tree(const PxEMPTY);
void exportExtraData(PxSerializationContext&);
void importExtraData(PxDeserializationContext& context);
//~PX_SERIALIZATION
BV32Tree();
BV32Tree(SourceMesh* meshInterface, const PxBounds3& localBounds);
~BV32Tree();
bool refit(const float epsilon);
bool load(PxInputStream& stream, bool mismatch);
void createSOAformatNode(BV32DataPacked& packedData, const BV32Data& node, const PxU32 childOffset, PxU32& currentIndex, PxU32& nbPackedNodes);
void reset();
void operator = (BV32Tree& v);
bool init(SourceMeshBase* meshInterface, const PxBounds3& localBounds);
void release();
SourceMeshBase* mMeshInterface;
LocalBounds mLocalBounds;
PxU32 mNbNodes;
BV32Data* mNodes;
BV32DataPacked* mPackedNodes;
PxU32 mNbPackedNodes;
PxU32* mRemapPackedNodeIndexWithDepth;
BV32DataDepthInfo* mTreeDepthInfo;
PxU32 mMaxTreeDepth;
PxU32 mInitData;
bool mUserAllocated; // PT: please keep these 4 bytes right after mCenterOrMinCoeff/mExtentsOrMaxCoeff for safe V4 loading
bool mPadding[2];
};
} // namespace Gu
}
#endif // GU_BV32_H

View File

@@ -0,0 +1,694 @@
// 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 "foundation/PxVec4.h"
#include "foundation/PxBasicTemplates.h"
#include "foundation/PxAllocator.h"
#include "foundation/PxMemory.h"
#include "geometry/PxTriangle.h"
#include "GuBV32Build.h"
#include "GuBV32.h"
#include "GuCenterExtents.h"
#include "GuBV4Build.h"
using namespace physx;
using namespace Gu;
#include "foundation/PxVecMath.h"
using namespace aos;
struct BV32Node : public PxUserAllocated
{
BV32Node() : mNbChildBVNodes(0)
{}
BV32Data mBVData[32];
PxU32 mNbChildBVNodes;
PX_FORCE_INLINE size_t isLeaf(PxU32 i) const { return mBVData[i].mData & 1; }
PX_FORCE_INLINE PxU32 getPrimitive(PxU32 i) const { return PxU32(mBVData[i].mData >> 1); }
PX_FORCE_INLINE const BV32Node* getChild(PxU32 i) const { return reinterpret_cast<BV32Node*>(mBVData[i].mData); }
PxU32 getSize() const
{
return sizeof(BV32Data)*mNbChildBVNodes;
}
};
static void fillInNodes(const AABBTreeNode* current_node, const PxU32 startIndex, const PxU32 endIndex, const AABBTreeNode** NODES, PxU32& stat)
{
if (startIndex + 1 == endIndex)
{
//fill in nodes
const AABBTreeNode* P = current_node->getPos();
const AABBTreeNode* N = current_node->getNeg();
NODES[startIndex] = P;
NODES[endIndex] = N;
stat += 2;
}
else
{
const AABBTreeNode* P = current_node->getPos();
const AABBTreeNode* N = current_node->getNeg();
const PxU32 midIndex = startIndex + ((endIndex - startIndex) / 2);
if (!P->isLeaf())
fillInNodes(P, startIndex, midIndex, NODES, stat);
else
{
NODES[startIndex] = P;
stat++;
}
if (!N->isLeaf())
fillInNodes(N, midIndex + 1, endIndex, NODES, stat);
else
{
NODES[midIndex + 1] = N;
stat++;
}
}
}
static void setPrimitive(const BV4_AABBTree& source, BV32Node* node32, PxU32 i, const AABBTreeNode* node, float epsilon)
{
const PxU32 nbPrims = node->getNbPrimitives();
PX_ASSERT(nbPrims<=32);
const PxU32* indexBase = source.getIndices();
const PxU32* prims = node->getPrimitives();
const PxU32 offset = PxU32(prims - indexBase);
#if BV32_VALIDATE
for (PxU32 j = 0; j<nbPrims; j++)
{
PX_ASSERT(prims[j] == offset + j);
}
#endif
const PxU32 primitiveIndex = (offset << 6) | (nbPrims & 63);
node32->mBVData[i].mMin = node->getAABB().minimum;
node32->mBVData[i].mMax = node->getAABB().maximum;
if (epsilon != 0.0f)
{
node32->mBVData[i].mMin -= PxVec3(epsilon);
node32->mBVData[i].mMax += PxVec3(epsilon);
}
node32->mBVData[i].mData = (primitiveIndex << 1) | 1;
}
static BV32Node* setNode(const BV4_AABBTree& source, BV32Node* node32, PxU32 i, const AABBTreeNode* node, float epsilon)
{
BV32Node* child = NULL;
if (node)
{
if (node->isLeaf())
{
setPrimitive(source, node32, i, node, epsilon);
}
else
{
node32->mBVData[i].mMin = node->getAABB().minimum;
node32->mBVData[i].mMax = node->getAABB().maximum;
if (epsilon != 0.0f)
{
node32->mBVData[i].mMin -= PxVec3(epsilon);
node32->mBVData[i].mMax += PxVec3(epsilon);
}
child = PX_NEW(BV32Node);
node32->mBVData[i].mData = size_t(child);
}
}
return child;
}
static void buildBV32(const BV4_AABBTree& source, BV32Node* tmp, const AABBTreeNode* current_node, float epsilon, PxU32& nbNodes)
{
PX_ASSERT(!current_node->isLeaf());
const AABBTreeNode* NODES[32];
PxMemSet(NODES, 0, sizeof(AABBTreeNode*) * 32);
fillInNodes(current_node, 0, 31, NODES, tmp->mNbChildBVNodes);
PxU32 left = 0;
PxU32 right = 31;
while (left < right)
{
//sweep from the front
while (left<right)
{
//found a hole
if (NODES[left] == NULL)
break;
left++;
}
//sweep from the back
while (left < right)
{
//found a node
if (NODES[right])
break;
right--;
}
if (left != right)
{
//swap left and right
const AABBTreeNode* node = NODES[right];
NODES[right] = NODES[left];
NODES[left] = node;
}
}
nbNodes += tmp->mNbChildBVNodes;
for (PxU32 i = 0; i < tmp->mNbChildBVNodes; ++i)
{
const AABBTreeNode* tempNode = NODES[i];
BV32Node* Child = setNode(source, tmp, i, tempNode, epsilon);
if (Child)
{
buildBV32(source, Child, tempNode, epsilon, nbNodes);
}
}
}
//
//static void validateTree(const AABBTree& Source, const AABBTreeNode* currentNode)
//{
// if (currentNode->isLeaf())
// {
// const PxU32* indexBase = Source.getIndices();
// const PxU32* prims = currentNode->getPrimitives();
// const PxU32 offset = PxU32(prims - indexBase);
// const PxU32 nbPrims = currentNode->getNbPrimitives();
// for (PxU32 j = 0; j<nbPrims; j++)
// {
// PX_ASSERT(prims[j] == offset + j);
// }
// }
// else
// {
// const AABBTreeNode* pos = currentNode->getPos();
// validateTree(Source, pos);
// const AABBTreeNode* neg = currentNode->getNeg();
// validateTree(Source, neg);
// }
//}
#if BV32_VALIDATE
static void validateNodeBound(const BV32Node* currentNode, SourceMeshBase* mesh, float epsilon)
{
const PxU32 nbPrimitivesFromMesh = mesh->getNbPrimitives();
const PxReal eps = 1e-5f;
const PxU32 nbNodes = currentNode->mNbChildBVNodes;
for (PxU32 i = 0; i < nbNodes; ++i)
{
const BV32Node* node = currentNode->getChild(i);
if (currentNode->isLeaf(i))
{
BV32Data data = currentNode->mBVData[i];
PxU32 nbPrimitives = data.getNbReferencedPrimitives();
PxU32 startIndex = data.getPrimitiveStartIndex();
PX_ASSERT(startIndex< nbPrimitivesFromMesh);
PxVec3 min(PX_MAX_F32, PX_MAX_F32, PX_MAX_F32);
PxVec3 max(-PX_MAX_F32, -PX_MAX_F32, -PX_MAX_F32);
const PxVec3* verts = mesh->getVerts();
if (mesh->getMeshType() == SourceMeshBase::MeshType::TRI_MESH)
{
const IndTri32* triIndices = static_cast<SourceMesh*>(mesh)->getTris32();
for (PxU32 j = 0; j < nbPrimitives; ++j)
{
IndTri32 index = triIndices[startIndex + j];
for (PxU32 k = 0; k < 3; ++k)
{
const PxVec3& v = verts[index.mRef[k]];
min.x = (min.x > v.x) ? v.x : min.x;
min.y = (min.y > v.y) ? v.y : min.y;
min.z = (min.z > v.z) ? v.z : min.z;
max.x = (max.x < v.x) ? v.x : max.x;
max.y = (max.y < v.y) ? v.y : max.y;
max.z = (max.z < v.z) ? v.z : max.z;
}
}
}
else
{
const IndTetrahedron32* tetIndices = static_cast<TetrahedronSourceMesh*>(mesh)->getTetrahedrons32();
for (PxU32 j = 0; j < nbPrimitives; ++j)
{
IndTetrahedron32 index = tetIndices[startIndex + j];
for (PxU32 k = 0; k < 4; ++k)
{
const PxVec3& v = verts[index.mRef[k]];
min.x = (min.x > v.x) ? v.x : min.x;
min.y = (min.y > v.y) ? v.y : min.y;
min.z = (min.z > v.z) ? v.z : min.z;
max.x = (max.x < v.x) ? v.x : max.x;
max.y = (max.y < v.y) ? v.y : max.y;
max.z = (max.z < v.z) ? v.z : max.z;
}
}
}
PxVec3 dMin, dMax;
data.getMinMax(dMin, dMax);
const PxVec3 difMin = min - dMin;
const PxVec3 difMax = dMax - max;
PX_ASSERT(PxAbs(difMin.x - epsilon) < eps && PxAbs(difMin.y - epsilon) < eps && PxAbs(difMin.z - epsilon) < eps);
PX_ASSERT(PxAbs(difMax.x - epsilon) < eps && PxAbs(difMax.y - epsilon) < eps && PxAbs(difMax.z - epsilon) < eps);
}
else
{
validateNodeBound(node, mesh, epsilon);
}
}
}
#endif
static bool BuildBV32Internal(BV32Tree& bv32Tree, const BV4_AABBTree& Source, SourceMeshBase* mesh, float epsilon)
{
GU_PROFILE_ZONE("..BuildBV32Internal")
const PxU32 nbPrimitives = mesh->getNbPrimitives();
if (nbPrimitives <= 32)
{
bv32Tree.mNbPackedNodes = 1;
bv32Tree.mPackedNodes = reinterpret_cast<BV32DataPacked*>(PX_ALLOC(sizeof(BV32DataPacked), "BV32DataPacked"));
BV32DataPacked& packedData = bv32Tree.mPackedNodes[0];
packedData.mNbNodes = 1;
packedData.mMin[0] = PxVec4(Source.getBV().minimum, 0.f);
packedData.mMax[0] = PxVec4(Source.getBV().maximum, 0.f);
packedData.mData[0] = (nbPrimitives << 1) | 1;
bv32Tree.mMaxTreeDepth = 1;
bv32Tree.mTreeDepthInfo = reinterpret_cast<BV32DataDepthInfo*>(PX_ALLOC(sizeof(BV32DataDepthInfo), "BV32DataDepthInfo"));
bv32Tree.mRemapPackedNodeIndexWithDepth = reinterpret_cast<PxU32*>(PX_ALLOC(sizeof(PxU32), "PxU32"));
bv32Tree.mTreeDepthInfo[0].offset = 0;
bv32Tree.mTreeDepthInfo[0].count = 1;
bv32Tree.mRemapPackedNodeIndexWithDepth[0] = 0;
return bv32Tree.init(mesh, Source.getBV());
}
{
GU_PROFILE_ZONE("...._checkMD")
struct Local
{
static void _checkMD(const AABBTreeNode* current_node, PxU32& md, PxU32& cd)
{
cd++;
md = PxMax(md, cd);
if (current_node->getPos()) { _checkMD(current_node->getPos(), md, cd); cd--; }
if (current_node->getNeg()) { _checkMD(current_node->getNeg(), md, cd); cd--; }
}
static void _check(AABBTreeNode* current_node)
{
if (current_node->isLeaf())
return;
AABBTreeNode* P = const_cast<AABBTreeNode*>(current_node->getPos());
AABBTreeNode* N = const_cast<AABBTreeNode*>(current_node->getNeg());
{
PxU32 MDP = 0; PxU32 CDP = 0; _checkMD(P, MDP, CDP);
PxU32 MDN = 0; PxU32 CDN = 0; _checkMD(N, MDN, CDN);
if (MDP>MDN)
// if(MDP<MDN)
{
PxSwap(*P, *N);
PxSwap(P, N);
}
}
_check(P);
_check(N);
}
};
Local::_check(const_cast<AABBTreeNode*>(Source.getNodes()));
}
PxU32 nbNodes = 1;
BV32Node* Root32 = PX_NEW(BV32Node);
{
GU_PROFILE_ZONE("....buildBV32")
buildBV32(Source, Root32, Source.getNodes(), epsilon, nbNodes);
}
#if BV32_VALIDATE
validateNodeBound(Root32, mesh, epsilon);
#endif
if (!bv32Tree.init(mesh, Source.getBV()))
return false;
BV32Tree* T = &bv32Tree;
PxU32 MaxDepth = 0;
// Version with variable-sized nodes in single stream
{
GU_PROFILE_ZONE("...._flatten")
struct Local
{
static void _flatten(BV32Data* const dest, const PxU32 box_id, PxU32& current_id, const BV32Node* current, PxU32& max_depth, PxU32& current_depth, const PxU32 nb_nodes)
{
// Entering a new node => increase depth
current_depth++;
// Keep track of max depth
if (current_depth>max_depth)
max_depth = current_depth;
for (PxU32 i = 0; i<current->mNbChildBVNodes; i++)
{
dest[box_id + i].mMin = current->mBVData[i].mMin;
dest[box_id + i].mMax = current->mBVData[i].mMax;
dest[box_id + i].mData = PxU32(current->mBVData[i].mData);
dest[box_id + i].mDepth = current_depth;
PX_ASSERT(box_id + i < nb_nodes);
}
PxU32 NbToGo = 0;
PxU32 NextIDs[32];
PxMemSet(NextIDs, PX_INVALID_U32, sizeof(PxU32)*32);
const BV32Node* ChildNodes[32];
PxMemSet(ChildNodes, 0, sizeof(BV32Node*)*32);
BV32Data* data = dest + box_id;
for (PxU32 i = 0; i<current->mNbChildBVNodes; i++)
{
PX_ASSERT(current->mBVData[i].mData != PX_INVALID_U32);
if (!current->isLeaf(i))
{
const BV32Node* ChildNode = current->getChild(i);
const PxU32 NextID = current_id;
const PxU32 ChildSize = ChildNode->mNbChildBVNodes;
current_id += ChildSize;
const PxU32 ChildType = ChildNode->mNbChildBVNodes << 1;
data[i].mData = size_t(ChildType + (NextID << GU_BV4_CHILD_OFFSET_SHIFT_COUNT));
//PX_ASSERT(data[i].mData == size_t(ChildType+(NextID<<3)));
PX_ASSERT(box_id + i < nb_nodes);
NextIDs[NbToGo] = NextID;
ChildNodes[NbToGo] = ChildNode;
NbToGo++;
}
}
for (PxU32 i = 0; i<NbToGo; i++)
{
_flatten(dest, NextIDs[i], current_id, ChildNodes[i], max_depth, current_depth, nb_nodes);
current_depth--;
}
PX_DELETE(current);
}
};
PxU32 CurID = Root32->mNbChildBVNodes+1;
BV32Data* Nodes = PX_NEW(BV32Data)[nbNodes];
Nodes[0].mMin = Source.getBV().minimum;
Nodes[0].mMax = Source.getBV().maximum;
const PxU32 ChildType = Root32->mNbChildBVNodes << 1;
Nodes[0].mData = size_t(ChildType + (1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT));
const PxU32 nbChilden = Nodes[0].getNbChildren();
PX_UNUSED(nbChilden);
T->mInitData = CurID;
PxU32 CurrentDepth = 0;
Local::_flatten(Nodes, 1, CurID, Root32, MaxDepth, CurrentDepth, nbNodes);
PX_ASSERT(CurID == nbNodes);
T->mNbNodes = nbNodes;
T->mNodes = Nodes;
}
{
GU_PROFILE_ZONE("....calculateLeafNode")
BV32Data* nodes = bv32Tree.mNodes;
for(PxU32 i=0; i<nbNodes; i++)
{
BV32Data& node = nodes[i];
if(!node.isLeaf())
{
PxU32 nbChildren = node.getNbChildren();
PxU32 offset = node.getChildOffset();
//calculate how many children nodes are leaf nodes
PxU32 nbLeafNodes = 0;
while(nbChildren--)
{
BV32Data& child = nodes[offset++];
if(child.isLeaf())
nbLeafNodes++;
}
node.mNbLeafNodes = nbLeafNodes;
}
}
}
bv32Tree.mPackedNodes = PX_ALLOCATE(BV32DataPacked, nbNodes, "BV32DataPacked");
bv32Tree.mNbPackedNodes = nbNodes;
bv32Tree.mMaxTreeDepth = MaxDepth;
PxU32 nbPackedNodes = 1;
PxU32 currentIndex = bv32Tree.mNodes[0].getNbChildren() - bv32Tree.mNodes[0].mNbLeafNodes + 1;
BV32DataPacked& packedData = bv32Tree.mPackedNodes[0];
//BV32DataDepth& depthData = bv32Tree.mMaxDepthForPackedNodes[0];
{
GU_PROFILE_ZONE("....createSOAformatNode")
bv32Tree.createSOAformatNode(packedData, bv32Tree.mNodes[0], 1, currentIndex, nbPackedNodes);
}
PX_ASSERT(nbPackedNodes == currentIndex);
PX_ASSERT(nbPackedNodes > 0);
bv32Tree.mNbPackedNodes = nbPackedNodes;
#if BV32_VALIDATE
/*for (PxU32 i = 0; i < nbNodes; ++i)
{
BV32Data& iNode = bv32Tree.mNodes[i];
for (PxU32 j = i+1; j < nbNodes; ++j)
{
BV32Data& jNode = bv32Tree.mNodes[j];
PX_ASSERT(iNode.mDepth <= jNode.mDepth);
}
}*/
#endif
{
GU_PROFILE_ZONE("....depth stuff")
//bv32Tree.mMaxDepthForPackedNodes = reinterpret_cast<BV32DataDepth*>(PX_ALLOC(sizeof(BV32DataDepth)*MaxDepth, "BV32DataDepth"));
bv32Tree.mTreeDepthInfo = PX_ALLOCATE(BV32DataDepthInfo, MaxDepth, "BV32DataDepthInfo");
PxU32 totalCount = 0;
for (PxU32 i = 0; i < MaxDepth; ++i)
{
PxU32 count = 0;
for (PxU32 j = 0; j < nbPackedNodes; ++j)
{
BV32DataPacked& jPackedData = bv32Tree.mPackedNodes[j];
if (jPackedData.mDepth == i)
{
count++;
}
}
bv32Tree.mTreeDepthInfo[i].offset = totalCount;
bv32Tree.mTreeDepthInfo[i].count = count;
totalCount += count;
}
PX_ASSERT(totalCount == nbPackedNodes);
bv32Tree.mRemapPackedNodeIndexWithDepth = PX_ALLOCATE(PxU32, nbPackedNodes, "PxU32");
for (PxU32 i = 0; i < MaxDepth; ++i)
{
PxU32 count = 0;
const PxU32 offset = bv32Tree.mTreeDepthInfo[i].offset;
PxU32* treeDepth = &bv32Tree.mRemapPackedNodeIndexWithDepth[offset];
for (PxU32 j = 0; j < nbPackedNodes; ++j)
{
BV32DataPacked& jPackedData = bv32Tree.mPackedNodes[j];
if (jPackedData.mDepth == i)
{
treeDepth[count++] = j;
}
}
}
#if BV32_VALIDATE
for (PxU32 i = MaxDepth; i > 0; i--)
{
const PxU32 iOffset = bv32Tree.mTreeDepthInfo[i - 1].offset;
const PxU32 iCount = bv32Tree.mTreeDepthInfo[i - 1].count;
PxU32* iRempapNodeIndex = &bv32Tree.mRemapPackedNodeIndexWithDepth[iOffset];
for (PxU32 j = 0; j < iCount; ++j)
{
const PxU32 nodeIndex = iRempapNodeIndex[j];
BV32DataPacked& currentNode = bv32Tree.mPackedNodes[nodeIndex];
PX_ASSERT(currentNode.mDepth == i - 1);
}
}
#endif
}
return true;
}
/////
struct ReorderData32
{
//const SourceMesh* mMesh;
SourceMeshBase* mMesh;
PxU32* mOrder;
PxU32 mNbPrimitivesPerLeaf;
PxU32 mIndex;
PxU32 mNbPrimitives;
PxU32 mStats[32];
};
static bool gReorderCallback(const AABBTreeNode* current, PxU32 /*depth*/, void* userData)
{
ReorderData32* Data = reinterpret_cast<ReorderData32*>(userData);
if (current->isLeaf())
{
const PxU32 n = current->getNbPrimitives();
PX_ASSERT(n > 0);
PX_ASSERT(n <= Data->mNbPrimitivesPerLeaf);
Data->mStats[n-1]++;
PxU32* Prims = const_cast<PxU32*>(current->getPrimitives());
for (PxU32 i = 0; i<n; i++)
{
PX_ASSERT(Prims[i]<Data->mNbPrimitives);
Data->mOrder[Data->mIndex] = Prims[i];
PX_ASSERT(Data->mIndex<Data->mNbPrimitives);
Prims[i] = Data->mIndex;
Data->mIndex++;
}
}
return true;
}
bool Gu::BuildBV32Ex(BV32Tree& tree, SourceMeshBase& mesh, float epsilon, PxU32 nbPrimitivesPerLeaf)
{
const PxU32 nbPrimitives = mesh.getNbPrimitives();
BV4_AABBTree Source;
{
GU_PROFILE_ZONE("..BuildBV32Ex_buildFromMesh")
// if (!Source.buildFromMesh(mesh, nbPrimitivesPerLeaf, BV4_SPLATTER_POINTS_SPLIT_GEOM_CENTER))
if (!Source.buildFromMesh(mesh, nbPrimitivesPerLeaf, BV4_SAH))
return false;
}
{
GU_PROFILE_ZONE("..BuildBV32Ex_remap")
PxU32* order = PX_ALLOCATE(PxU32, nbPrimitives, "BV32");
ReorderData32 RD;
RD.mMesh = &mesh;
RD.mOrder = order;
RD.mNbPrimitivesPerLeaf = nbPrimitivesPerLeaf;
RD.mIndex = 0;
RD.mNbPrimitives = nbPrimitives;
for (PxU32 i = 0; i<32; i++)
RD.mStats[i] = 0;
Source.walk(gReorderCallback, &RD);
PX_ASSERT(RD.mIndex == nbPrimitives);
mesh.remapTopology(order);
PX_FREE(order);
// for(PxU32 i=0;i<16;i++)
// printf("%d: %d\n", i, RD.mStats[i]);
}
/*if (mesh.getNbPrimitives() <= nbPrimitivesPerLeaf)
return tree.init(&mesh, Source.getBV());*/
return BuildBV32Internal(tree, Source, &mesh, epsilon);
}

View File

@@ -0,0 +1,49 @@
// 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_BV32_BUILD_H
#define GU_BV32_BUILD_H
#include "foundation/PxSimpleTypes.h"
#include "common/PxPhysXCommonConfig.h"
#define BV32_VALIDATE 0
namespace physx
{
namespace Gu
{
class BV32Tree;
class SourceMeshBase;
bool BuildBV32Ex(BV32Tree& tree, SourceMeshBase& mesh, float epsilon, PxU32 nbPrimitivesPerLeaf);
} // namespace Gu
}
#endif // GU_BV32_BUILD_H

View File

@@ -0,0 +1,737 @@
// 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 "foundation/PxMemory.h"
#include "GuBV4.h"
#include "GuBV4_Common.h"
#include "CmSerialize.h"
#include "foundation/PxVecMath.h"
#include "common/PxSerialFramework.h"
using namespace physx;
using namespace Gu;
using namespace Cm;
using namespace aos;
SourceMeshBase::SourceMeshBase(MeshType meshType) : mNbVerts(0), mVerts(NULL), mType(meshType), mRemap(NULL)
{
}
SourceMeshBase::~SourceMeshBase()
{
PX_FREE(mRemap);
}
///////////////////////////////////////////////////////////////////////////////
TetrahedronSourceMesh::TetrahedronSourceMesh() : SourceMeshBase(MeshType::TET_MESH)
{
reset();
}
TetrahedronSourceMesh::~TetrahedronSourceMesh()
{
}
void TetrahedronSourceMesh::reset()
{
mNbVerts = 0;
mVerts = NULL;
mNbTetrahedrons = 0;
mTetrahedrons32 = NULL;
mTetrahedrons16 = NULL;
mRemap = NULL;
}
void TetrahedronSourceMesh::operator=(TetrahedronSourceMesh& v)
{
mNbVerts = v.mNbVerts;
mVerts = v.mVerts;
mNbTetrahedrons = v.mNbTetrahedrons;
mTetrahedrons32 = v.mTetrahedrons32;
mTetrahedrons16 = v.mTetrahedrons16;
v.reset();
}
void TetrahedronSourceMesh::remapTopology(const PxU32* order)
{
if(!mNbTetrahedrons)
return;
if(mTetrahedrons32)
{
IndTetrahedron32* newTopo = PX_NEW(IndTetrahedron32)[mNbTetrahedrons];
for(PxU32 i = 0; i<mNbTetrahedrons; i++)
newTopo[i] = mTetrahedrons32[order[i]];
PxMemCopy(mTetrahedrons32, newTopo, sizeof(IndTetrahedron32)*mNbTetrahedrons);
PX_DELETE_ARRAY(newTopo);
}
else
{
PX_ASSERT(mTetrahedrons16);
IndTetrahedron16* newTopo = PX_NEW(IndTetrahedron16)[mNbTetrahedrons];
for(PxU32 i = 0; i<mNbTetrahedrons; i++)
newTopo[i] = mTetrahedrons16[order[i]];
PxMemCopy(mTetrahedrons16, newTopo, sizeof(IndTetrahedron16)*mNbTetrahedrons);
PX_DELETE_ARRAY(newTopo);
}
{
PxU32* newMap = PX_ALLOCATE(PxU32, mNbTetrahedrons, "newMap");
for(PxU32 i = 0; i<mNbTetrahedrons; i++)
newMap[i] = mRemap ? mRemap[order[i]] : order[i];
PX_FREE(mRemap);
mRemap = newMap;
}
}
void TetrahedronSourceMesh::getPrimitiveBox(const PxU32 primitiveInd, Vec4V& minV, Vec4V& maxV)
{
TetrahedronPointers VP;
getTetrahedron(VP, primitiveInd);
const Vec4V v0V = V4LoadU(&VP.Vertex[0]->x);
const Vec4V v1V = V4LoadU(&VP.Vertex[1]->x);
const Vec4V v2V = V4LoadU(&VP.Vertex[2]->x);
const Vec4V v3V = V4LoadU(&VP.Vertex[3]->x);
minV = V4Min(v0V, v1V);
minV = V4Min(minV, v2V);
minV = V4Min(minV, v3V);
maxV = V4Max(v0V, v1V);
maxV = V4Max(maxV, v2V);
maxV = V4Max(maxV, v3V);
}
void TetrahedronSourceMesh::refit(const PxU32 primitiveInd, PxBounds3& refitBox)
{
TetrahedronPointers VP;
getTetrahedron(VP, primitiveInd);
refitBox.include(*VP.Vertex[0]);
refitBox.include(*VP.Vertex[1]);
refitBox.include(*VP.Vertex[2]);
refitBox.include(*VP.Vertex[3]);
}
///////////////////////////////////////////////////////////////////////////////
SourceMesh::SourceMesh() : SourceMeshBase(MeshType::TRI_MESH)
{
reset();
}
SourceMesh::~SourceMesh()
{
}
void SourceMesh::reset()
{
mNbVerts = 0;
mVerts = NULL;
mNbTris = 0;
mTriangles32 = NULL;
mTriangles16 = NULL;
mRemap = NULL;
}
void SourceMesh::operator=(SourceMesh& v)
{
mNbVerts = v.mNbVerts;
mVerts = v.mVerts;
mNbTris = v.mNbTris;
mTriangles32 = v.mTriangles32;
mTriangles16 = v.mTriangles16;
mRemap = v.mRemap;
v.reset();
}
void SourceMesh::remapTopology(const PxU32* order)
{
if(!mNbTris)
return;
if(mTriangles32)
{
IndTri32* newTopo = PX_NEW(IndTri32)[mNbTris];
for(PxU32 i=0;i<mNbTris;i++)
newTopo[i] = mTriangles32[order[i]];
PxMemCopy(mTriangles32, newTopo, sizeof(IndTri32)*mNbTris);
PX_DELETE_ARRAY(newTopo);
}
else
{
PX_ASSERT(mTriangles16);
IndTri16* newTopo = PX_NEW(IndTri16)[mNbTris];
for(PxU32 i=0;i<mNbTris;i++)
newTopo[i] = mTriangles16[order[i]];
PxMemCopy(mTriangles16, newTopo, sizeof(IndTri16)*mNbTris);
PX_DELETE_ARRAY(newTopo);
}
{
PxU32* newMap = PX_ALLOCATE(PxU32, mNbTris, "newMap");
for(PxU32 i=0;i<mNbTris;i++)
newMap[i] = mRemap ? mRemap[order[i]] : order[i];
PX_FREE(mRemap);
mRemap = newMap;
}
}
void SourceMesh::getPrimitiveBox(const PxU32 primitiveInd, Vec4V& minV, Vec4V& maxV)
{
VertexPointers VP;
getTriangle(VP, primitiveInd);
const Vec4V v0V = V4LoadU(&VP.Vertex[0]->x);
const Vec4V v1V = V4LoadU(&VP.Vertex[1]->x);
const Vec4V v2V = V4LoadU(&VP.Vertex[2]->x);
minV = V4Min(v0V, v1V);
minV = V4Min(minV, v2V);
maxV = V4Max(v0V, v1V);
maxV = V4Max(maxV, v2V);
}
void SourceMesh::refit(const PxU32 primitiveInd, PxBounds3& refitBox)
{
VertexPointers VP;
getTriangle(VP, primitiveInd);
refitBox.include(*VP.Vertex[0]);
refitBox.include(*VP.Vertex[1]);
refitBox.include(*VP.Vertex[2]);
}
bool SourceMesh::isValid() const
{
if(!mNbTris || !mNbVerts) return false;
if(!mVerts) return false;
if(!mTriangles32 && !mTriangles16) return false;
return true;
}
/////
BV4Tree::BV4Tree(SourceMesh* meshInterface, const PxBounds3& localBounds)
{
reset();
init(meshInterface, localBounds);
}
BV4Tree::BV4Tree()
{
reset();
}
void BV4Tree::release()
{
if(!mUserAllocated)
{
#ifdef GU_BV4_USE_SLABS
PX_FREE(mNodes);
// PX_DELETE(mNodes);
#else
PX_DELETE_ARRAY(mNodes);
#endif
}
mNodes = NULL;
mNbNodes = 0;
reset();
}
BV4Tree::~BV4Tree()
{
release();
}
void BV4Tree::reset()
{
mMeshInterface = NULL;
//mTetrahedronMeshInterface = NULL;
mNbNodes = 0;
mNodes = NULL;
mInitData = 0;
mCenterOrMinCoeff = PxVec3(0.0f);
mExtentsOrMaxCoeff = PxVec3(0.0f);
mUserAllocated = false;
mQuantized = false;
mIsEdgeSet = false;
}
void BV4Tree::operator=(BV4Tree& v)
{
mMeshInterface = v.mMeshInterface;
//mTetrahedronMeshInterface = v.mTetrahedronMeshInterface;
mLocalBounds = v.mLocalBounds;
mNbNodes = v.mNbNodes;
mNodes = v.mNodes;
mInitData = v.mInitData;
mCenterOrMinCoeff = v.mCenterOrMinCoeff;
mExtentsOrMaxCoeff = v.mExtentsOrMaxCoeff;
mUserAllocated = v.mUserAllocated;
mQuantized = v.mQuantized;
mIsEdgeSet = false;
v.reset();
}
bool BV4Tree::init(SourceMeshBase* meshInterface, const PxBounds3& localBounds)
{
mMeshInterface = meshInterface;
mLocalBounds.init(localBounds);
return true;
}
//bool BV4Tree::init(TetrahedronSourceMesh* meshInterface, const PxBounds3& localBounds)
//{
// mTetrahedronMeshInterface = meshInterface;
// mLocalBounds.init(localBounds);
// return true;
//}
// PX_SERIALIZATION
BV4Tree::BV4Tree(const PxEMPTY) : mLocalBounds(PxEmpty)
{
mUserAllocated = true;
mIsEdgeSet = false;
}
void BV4Tree::exportExtraData(PxSerializationContext& stream)
{
if(mNbNodes)
{
stream.alignData(16);
const PxU32 nodeSize = mQuantized ? sizeof(BVDataPackedQ) : sizeof(BVDataPackedNQ);
stream.writeData(mNodes, mNbNodes*nodeSize);
}
}
void BV4Tree::importExtraData(PxDeserializationContext& context)
{
if(mNbNodes)
{
context.alignExtraData(16);
if(mQuantized)
mNodes = context.readExtraData<BVDataPackedQ>(mNbNodes);
else
mNodes = context.readExtraData<BVDataPackedNQ>(mNbNodes);
}
}
//~PX_SERIALIZATION
bool BV4Tree::load(PxInputStream& stream, bool mismatch_)
{
PX_ASSERT(!mUserAllocated);
release();
PxI8 a, b, c, d;
readChunk(a, b, c, d, stream);
if(a!='B' || b!='V' || c!='4' || d!=' ')
return false;
bool mismatch;
PxU32 fileVersion;
if(!readBigEndianVersionNumber(stream, mismatch_, fileVersion, mismatch))
return false;
readFloatBuffer(&mLocalBounds.mCenter.x, 3, mismatch, stream);
mLocalBounds.mExtentsMagnitude = readFloat(mismatch, stream);
mInitData = readDword(mismatch, stream);
readFloatBuffer(&mCenterOrMinCoeff.x, 3, mismatch, stream);
readFloatBuffer(&mExtentsOrMaxCoeff.x, 3, mismatch, stream);
// PT: version 3
if(fileVersion>=3)
{
const PxU32 Quantized = readDword(mismatch, stream);
mQuantized = Quantized!=0;
}
else
mQuantized = true;
const PxU32 nbNodes = readDword(mismatch, stream);
mNbNodes = nbNodes;
if(nbNodes)
{
PxU32 dataSize = 0;
#ifdef GU_BV4_USE_SLABS
const PxU32 nodeSize = mQuantized ? sizeof(BVDataPackedQ) : sizeof(BVDataPackedNQ);
dataSize = nodeSize*nbNodes;
void* nodes = PX_ALLOC(dataSize, "BV4 nodes"); // PT: PX_NEW breaks alignment here
// BVDataPacked* nodes = reinterpret_cast<BVDataPacked*>(PX_ALLOC(sizeof(BVDataPacked)*nbNodes, "BV4 nodes")); // PT: PX_NEW breaks alignment here
mNodes = nodes;
#else
BVDataPacked* nodes = PX_NEW(BVDataPacked)[nbNodes];
mNodes = nodes;
#endif
// PxMarkSerializedMemory(nodes, dataSize);
stream.read(nodes, dataSize);
PX_ASSERT(!mismatch);
}
else mNodes = NULL;
mIsEdgeSet = false;
return true;
}
#define VERSION2
#ifdef VERSION1
bool BV4Tree::refit(PxBounds3& globalBounds, float epsilon)
{
if(mQuantized)
if(!mNodes)
{
PxBounds3 bounds;
bounds.setEmpty();
if(mMeshInterface)
{
PxU32 nbVerts = mMeshInterface->getNbVertices();
const PxVec3* verts = mMeshInterface->getVerts();
while(nbVerts--)
bounds.include(*verts++);
mLocalBounds.init(bounds);
}
if(mTetrahedronMeshInterface)
{
PX_ASSERT(0);
}
return true;
}
class PxBounds3Padded : public PxBounds3
{
public:
PX_FORCE_INLINE PxBounds3Padded() {}
PX_FORCE_INLINE ~PxBounds3Padded() {}
PxU32 padding;
};
PX_ASSERT(!(mNbNodes&3));
PxU32 nb = mNbNodes/4;
BVDataSwizzledNQ* data = reinterpret_cast<BVDataSwizzledNQ*>(mNodes);
while(nb--)
{
BVDataSwizzledNQ* PX_RESTRICT current = data + nb;
for(PxU32 j=0;j<4;j++)
{
if(current->getChildData(j)==PX_INVALID_U32)
continue;
Vec4V minV = V4Load(FLT_MAX);
Vec4V maxV = V4Load(-FLT_MAX);
if(current->isLeaf(j))
{
PxU32 primIndex = current->getPrimitive(j);
PxU32 nbToGo = getNbPrimitives(primIndex);
VertexPointers VP;
do
{
PX_ASSERT(primIndex<mMeshInterface->getNbTriangles());
mMeshInterface->getTriangle(VP, primIndex);
const Vec4V v0V = V4LoadU(&VP.Vertex[0]->x);
const Vec4V v1V = V4LoadU(&VP.Vertex[1]->x);
const Vec4V v2V = V4LoadU(&VP.Vertex[2]->x);
minV = V4Min(minV, v0V);
minV = V4Min(minV, v1V);
minV = V4Min(minV, v2V);
maxV = V4Max(maxV, v0V);
maxV = V4Max(maxV, v1V);
maxV = V4Max(maxV, v2V);
primIndex++;
}while(nbToGo--);
const Vec4V epsilonV = V4Load(epsilon);
minV = V4Sub(minV, epsilonV);
maxV = V4Add(maxV, epsilonV);
}
else
{
PxU32 childOffset = current->getChildOffset(j);
PX_ASSERT(!(childOffset&3));
childOffset>>=2;
PX_ASSERT(childOffset>nb);
const PxU32 childType = current->getChildType(j);
// PT: TODO: revisit SIMD here, not great
const BVDataSwizzledNQ* PX_RESTRICT next = data + childOffset;
{
{
const Vec4V childMinV = V4LoadXYZW(next->mMinX[0], next->mMinY[0], next->mMinZ[0], 0.0f);
const Vec4V childMaxV = V4LoadXYZW(next->mMaxX[0], next->mMaxY[0], next->mMaxZ[0], 0.0f);
// minV = V4Min(minV, childMinV);
// maxV = V4Max(maxV, childMaxV);
minV = childMinV;
maxV = childMaxV;
}
{
const Vec4V childMinV = V4LoadXYZW(next->mMinX[1], next->mMinY[1], next->mMinZ[1], 0.0f);
const Vec4V childMaxV = V4LoadXYZW(next->mMaxX[1], next->mMaxY[1], next->mMaxZ[1], 0.0f);
minV = V4Min(minV, childMinV);
maxV = V4Max(maxV, childMaxV);
}
/* {
const Vec4V childMinV0 = V4LoadXYZW(next->mMinX[0], next->mMinY[0], next->mMinZ[0], 0.0f);
const Vec4V childMaxV0 = V4LoadXYZW(next->mMaxX[0], next->mMaxY[0], next->mMaxZ[0], 0.0f);
const Vec4V childMinV1 = V4LoadXYZW(next->mMinX[1], next->mMinY[1], next->mMinZ[1], 0.0f);
const Vec4V childMaxV1 = V4LoadXYZW(next->mMaxX[1], next->mMaxY[1], next->mMaxZ[1], 0.0f);
minV = V4Min(childMinV0, childMinV1);
maxV = V4Max(childMaxV0, childMaxV1);
}*/
if(childType>0)
{
const Vec4V childMinV = V4LoadXYZW(next->mMinX[2], next->mMinY[2], next->mMinZ[2], 0.0f);
const Vec4V childMaxV = V4LoadXYZW(next->mMaxX[2], next->mMaxY[2], next->mMaxZ[2], 0.0f);
minV = V4Min(minV, childMinV);
maxV = V4Max(maxV, childMaxV);
}
if(childType>1)
{
const Vec4V childMinV = V4LoadXYZW(next->mMinX[3], next->mMinY[3], next->mMinZ[3], 0.0f);
const Vec4V childMaxV = V4LoadXYZW(next->mMaxX[3], next->mMaxY[3], next->mMaxZ[3], 0.0f);
minV = V4Min(minV, childMinV);
maxV = V4Max(maxV, childMaxV);
}
}
}
PxBounds3Padded refitBox;
V4StoreU_Safe(minV, &refitBox.minimum.x);
V4StoreU_Safe(maxV, &refitBox.maximum.x);
current->mMinX[j] = refitBox.minimum.x;
current->mMinY[j] = refitBox.minimum.y;
current->mMinZ[j] = refitBox.minimum.z;
current->mMaxX[j] = refitBox.maximum.x;
current->mMaxY[j] = refitBox.maximum.y;
current->mMaxZ[j] = refitBox.maximum.z;
}
}
BVDataSwizzledNQ* root = reinterpret_cast<BVDataSwizzledNQ*>(mNodes);
{
globalBounds.setEmpty();
for(PxU32 j=0;j<4;j++)
{
if(root->getChildData(j)==PX_INVALID_U32)
continue;
PxBounds3 refitBox;
refitBox.minimum.x = root->mMinX[j];
refitBox.minimum.y = root->mMinY[j];
refitBox.minimum.z = root->mMinZ[j];
refitBox.maximum.x = root->mMaxX[j];
refitBox.maximum.y = root->mMaxY[j];
refitBox.maximum.z = root->mMaxZ[j];
globalBounds.include(refitBox);
}
mLocalBounds.init(globalBounds);
}
return true;
}
#endif
#ifdef VERSION2
bool BV4Tree::refit(PxBounds3& globalBounds, float epsilon)
{
if(mQuantized)
return false;
if(!mNodes)
{
globalBounds.setEmpty();
if(mMeshInterface)
{
PxU32 nbVerts = mMeshInterface->getNbVertices();
const PxVec3* verts = mMeshInterface->getVerts();
while(nbVerts--)
globalBounds.include(*verts++);
mLocalBounds.init(globalBounds);
}
return true;
}
class PxBounds3Padded : public PxBounds3
{
public:
PX_FORCE_INLINE PxBounds3Padded() {}
PX_FORCE_INLINE ~PxBounds3Padded() {}
PxU32 padding;
};
PX_ASSERT(!(mNbNodes&3));
PxU32 nb = mNbNodes/4;
BVDataSwizzledNQ* data = reinterpret_cast<BVDataSwizzledNQ*>(mNodes);
while(nb--)
{
BVDataSwizzledNQ* PX_RESTRICT current = data + nb;
for(PxU32 j=0;j<4;j++)
{
if(current->getChildData(j)==PX_INVALID_U32)
continue;
if(current->isLeaf(j))
{
PxU32 primIndex = current->getPrimitive(j);
Vec4V minV = V4Load(FLT_MAX);
Vec4V maxV = V4Load(-FLT_MAX);
PxU32 nbToGo = getNbPrimitives(primIndex);
//VertexPointers VP;
do
{
PX_ASSERT(primIndex< mMeshInterface->getNbPrimitives());
//meshInterface->getTriangle(VP, primIndex);
Vec4V tMin, tMax;
mMeshInterface->getPrimitiveBox(primIndex, tMin, tMax);
minV = V4Min(minV, tMin);
maxV = V4Max(maxV, tMax);
/* const Vec4V v0V = V4LoadU(&VP.Vertex[0]->x);
const Vec4V v1V = V4LoadU(&VP.Vertex[1]->x);
const Vec4V v2V = V4LoadU(&VP.Vertex[2]->x);
minV = V4Min(minV, v0V);
minV = V4Min(minV, v1V);
minV = V4Min(minV, v2V);
maxV = V4Max(maxV, v0V);
maxV = V4Max(maxV, v1V);
maxV = V4Max(maxV, v2V);*/
primIndex++;
}while(nbToGo--);
const Vec4V epsilonV = V4Load(epsilon);
minV = V4Sub(minV, epsilonV);
maxV = V4Add(maxV, epsilonV);
PxBounds3Padded refitBox;
V4StoreU_Safe(minV, &refitBox.minimum.x);
V4StoreU_Safe(maxV, &refitBox.maximum.x);
current->mMinX[j] = refitBox.minimum.x;
current->mMinY[j] = refitBox.minimum.y;
current->mMinZ[j] = refitBox.minimum.z;
current->mMaxX[j] = refitBox.maximum.x;
current->mMaxY[j] = refitBox.maximum.y;
current->mMaxZ[j] = refitBox.maximum.z;
}
else
{
PxU32 childOffset = current->getChildOffset(j);
PX_ASSERT(!(childOffset&3));
childOffset>>=2;
PX_ASSERT(childOffset>nb);
const PxU32 childType = current->getChildType(j);
const BVDataSwizzledNQ* PX_RESTRICT next = data + childOffset;
{
current->mMinX[j] = PxMin(next->mMinX[0], next->mMinX[1]);
current->mMinY[j] = PxMin(next->mMinY[0], next->mMinY[1]);
current->mMinZ[j] = PxMin(next->mMinZ[0], next->mMinZ[1]);
current->mMaxX[j] = PxMax(next->mMaxX[0], next->mMaxX[1]);
current->mMaxY[j] = PxMax(next->mMaxY[0], next->mMaxY[1]);
current->mMaxZ[j] = PxMax(next->mMaxZ[0], next->mMaxZ[1]);
if(childType>0)
{
current->mMinX[j] = PxMin(current->mMinX[j], next->mMinX[2]);
current->mMinY[j] = PxMin(current->mMinY[j], next->mMinY[2]);
current->mMinZ[j] = PxMin(current->mMinZ[j], next->mMinZ[2]);
current->mMaxX[j] = PxMax(current->mMaxX[j], next->mMaxX[2]);
current->mMaxY[j] = PxMax(current->mMaxY[j], next->mMaxY[2]);
current->mMaxZ[j] = PxMax(current->mMaxZ[j], next->mMaxZ[2]);
}
if(childType>1)
{
current->mMinX[j] = PxMin(current->mMinX[j], next->mMinX[3]);
current->mMinY[j] = PxMin(current->mMinY[j], next->mMinY[3]);
current->mMinZ[j] = PxMin(current->mMinZ[j], next->mMinZ[3]);
current->mMaxX[j] = PxMax(current->mMaxX[j], next->mMaxX[3]);
current->mMaxY[j] = PxMax(current->mMaxY[j], next->mMaxY[3]);
current->mMaxZ[j] = PxMax(current->mMaxZ[j], next->mMaxZ[3]);
}
}
}
}
}
BVDataSwizzledNQ* root = reinterpret_cast<BVDataSwizzledNQ*>(mNodes);
{
globalBounds.setEmpty();
for(PxU32 j=0;j<4;j++)
{
if(root->getChildData(j)==PX_INVALID_U32)
continue;
PxBounds3 refitBox;
refitBox.minimum.x = root->mMinX[j];
refitBox.minimum.y = root->mMinY[j];
refitBox.minimum.z = root->mMinZ[j];
refitBox.maximum.x = root->mMaxX[j];
refitBox.maximum.y = root->mMaxY[j];
refitBox.maximum.z = root->mMaxZ[j];
globalBounds.include(refitBox);
}
mLocalBounds.init(globalBounds);
}
return true;
}
#endif

View File

@@ -0,0 +1,380 @@
// 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_BV4_H
#define GU_BV4_H
#include "foundation/PxBounds3.h"
#include "GuBV4Settings.h"
#include "GuCenterExtents.h"
#include "GuTriangle.h"
#include "foundation/PxVecMath.h"
#include "common/PxPhysXCommonConfig.h"
#define V4LoadU_Safe physx::aos::V4LoadU // PT: prefix needed on Linux. Sigh.
#define V4LoadA_Safe V4LoadA
#define V4StoreA_Safe V4StoreA
#define V4StoreU_Safe V4StoreU
namespace physx
{
class PxSerializationContext;
class PxDeserializationContext;
class PxOutputStream;
class PxInputStream;
namespace Gu
{
struct VertexPointers
{
const PxVec3* Vertex[3];
};
struct TetrahedronPointers
{
const PxVec3* Vertex[4];
};
// PT: TODO: make this more generic, rename to IndQuad32, refactor with GRB's int4
class IndTetrahedron32 : public physx::PxUserAllocated
{
public:
public:
PX_FORCE_INLINE IndTetrahedron32() {}
PX_FORCE_INLINE IndTetrahedron32(PxU32 r0, PxU32 r1, PxU32 r2, PxU32 r3) { mRef[0] = r0; mRef[1] = r1; mRef[2] = r2; mRef[3] = r3; }
PX_FORCE_INLINE IndTetrahedron32(const IndTetrahedron32& tetrahedron)
{
mRef[0] = tetrahedron.mRef[0];
mRef[1] = tetrahedron.mRef[1];
mRef[2] = tetrahedron.mRef[2];
mRef[3] = tetrahedron.mRef[3];
}
PX_FORCE_INLINE ~IndTetrahedron32() {}
PxU32 mRef[4];
};
PX_COMPILE_TIME_ASSERT(sizeof(IndTetrahedron32) == 16);
// PT: TODO: make this more generic, rename to IndQuad16
class IndTetrahedron16 : public physx::PxUserAllocated
{
public:
public:
PX_FORCE_INLINE IndTetrahedron16() {}
PX_FORCE_INLINE IndTetrahedron16(PxU16 r0, PxU16 r1, PxU16 r2, PxU16 r3) { mRef[0] = r0; mRef[1] = r1; mRef[2] = r2; mRef[3] = r3; }
PX_FORCE_INLINE IndTetrahedron16(const IndTetrahedron16& tetrahedron)
{
mRef[0] = tetrahedron.mRef[0];
mRef[1] = tetrahedron.mRef[1];
mRef[2] = tetrahedron.mRef[2];
mRef[3] = tetrahedron.mRef[3];
}
PX_FORCE_INLINE ~IndTetrahedron16() {}
PxU16 mRef[4];
};
PX_COMPILE_TIME_ASSERT(sizeof(IndTetrahedron16) == 8);
typedef IndexedTriangle32 IndTri32;
typedef IndexedTriangle16 IndTri16;
PX_FORCE_INLINE void getVertexReferences(PxU32& vref0, PxU32& vref1, PxU32& vref2, PxU32 index, const IndTri32* T32, const IndTri16* T16)
{
if(T32)
{
const IndTri32* PX_RESTRICT tri = T32 + index;
vref0 = tri->mRef[0];
vref1 = tri->mRef[1];
vref2 = tri->mRef[2];
}
else
{
const IndTri16* PX_RESTRICT tri = T16 + index;
vref0 = tri->mRef[0];
vref1 = tri->mRef[1];
vref2 = tri->mRef[2];
}
}
PX_FORCE_INLINE void getVertexReferences(PxU32& vref0, PxU32& vref1, PxU32& vref2, PxU32& vref3, PxU32 index, const IndTetrahedron32* T32, const IndTetrahedron16* T16)
{
if(T32)
{
const IndTetrahedron32* PX_RESTRICT tet = T32 + index;
vref0 = tet->mRef[0];
vref1 = tet->mRef[1];
vref2 = tet->mRef[2];
vref3 = tet->mRef[3];
}
else
{
const IndTetrahedron16* PX_RESTRICT tet = T16 + index;
vref0 = tet->mRef[0];
vref1 = tet->mRef[1];
vref2 = tet->mRef[2];
vref3 = tet->mRef[3];
}
}
class SourceMeshBase : public physx::PxUserAllocated
{
public:
enum MeshType
{
TRI_MESH,
TET_MESH,
FORCE_DWORD = 0x7fffffff
};
SourceMeshBase(MeshType meshType);
virtual ~SourceMeshBase();
SourceMeshBase(const PxEMPTY) {}
PxU32 mNbVerts;
const PxVec3* mVerts;
PX_FORCE_INLINE PxU32 getNbVertices() const { return mNbVerts; }
PX_FORCE_INLINE const PxVec3* getVerts() const { return mVerts; }
PX_FORCE_INLINE void setNbVertices(PxU32 nb) { mNbVerts = nb; }
PX_FORCE_INLINE void initRemap() { mRemap = NULL; }
PX_FORCE_INLINE const PxU32* getRemap() const { return mRemap; }
PX_FORCE_INLINE void releaseRemap() { PX_FREE(mRemap); }
PX_FORCE_INLINE MeshType getMeshType() const { return mType; }
// PT: TODO: check whether adding these vcalls affected build & runtime performance
virtual PxU32 getNbPrimitives() const = 0;
virtual void remapTopology(const PxU32* order) = 0;
virtual void getPrimitiveBox(const PxU32 primitiveInd, physx::aos::Vec4V& minV, physx::aos::Vec4V& maxV) = 0;
virtual void refit(const PxU32 primitiveInd, PxBounds3& refitBox) = 0;
protected:
MeshType mType;
PxU32* mRemap;
};
class SourceMesh : public SourceMeshBase
{
public:
SourceMesh();
virtual ~SourceMesh();
// PX_SERIALIZATION
SourceMesh(const PxEMPTY) : SourceMeshBase(PxEmpty) {}
//~PX_SERIALIZATION
void reset();
void operator = (SourceMesh& v);
PxU32 mNbTris;
IndTri32* mTriangles32;
IndTri16* mTriangles16;
PX_FORCE_INLINE PxU32 getNbTriangles() const { return mNbTris; }
PX_FORCE_INLINE const IndTri32* getTris32() const { return mTriangles32; }
PX_FORCE_INLINE const IndTri16* getTris16() const { return mTriangles16; }
PX_FORCE_INLINE void setNbTriangles(PxU32 nb) { mNbTris = nb; }
// SourceMeshBase
virtual PxU32 getNbPrimitives() const { return getNbTriangles(); }
virtual void remapTopology(const PxU32* order);
virtual void getPrimitiveBox(const PxU32 primitiveInd, physx::aos::Vec4V& minV, physx::aos::Vec4V& maxV);
virtual void refit(const PxU32 primitiveInd, PxBounds3& refitBox);
//~SourceMeshBase
PX_FORCE_INLINE void setPointers(IndTri32* tris32, IndTri16* tris16, const PxVec3* verts)
{
mTriangles32 = tris32;
mTriangles16 = tris16;
mVerts = verts;
}
bool isValid() const;
PX_FORCE_INLINE void getTriangle(VertexPointers& vp, PxU32 index) const
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, index, mTriangles32, mTriangles16);
vp.Vertex[0] = mVerts + VRef0;
vp.Vertex[1] = mVerts + VRef1;
vp.Vertex[2] = mVerts + VRef2;
}
};
class TetrahedronSourceMesh : public SourceMeshBase
{
public:
TetrahedronSourceMesh();
virtual ~TetrahedronSourceMesh();
// PX_SERIALIZATION
TetrahedronSourceMesh(const PxEMPTY) : SourceMeshBase(TET_MESH) {}
//~PX_SERIALIZATION
void reset();
void operator = (TetrahedronSourceMesh& v);
PxU32 mNbTetrahedrons;
IndTetrahedron32* mTetrahedrons32;
IndTetrahedron16* mTetrahedrons16;
PX_FORCE_INLINE PxU32 getNbTetrahedrons() const { return mNbTetrahedrons; }
PX_FORCE_INLINE const IndTetrahedron32* getTetrahedrons32() const { return mTetrahedrons32; }
PX_FORCE_INLINE const IndTetrahedron16* getTetrahedrons16() const { return mTetrahedrons16; }
PX_FORCE_INLINE void setNbTetrahedrons(PxU32 nb) { mNbTetrahedrons = nb; }
// SourceMeshBase
virtual PxU32 getNbPrimitives() const { return getNbTetrahedrons(); }
virtual void remapTopology(const PxU32* order);
virtual void getPrimitiveBox(const PxU32 primitiveInd, physx::aos::Vec4V& minV, physx::aos::Vec4V& maxV);
virtual void refit(const PxU32 primitiveInd, PxBounds3& refitBox);
//~SourceMeshBase
PX_FORCE_INLINE void setPointers(IndTetrahedron32* tets32, IndTetrahedron16* tets16, const PxVec3* verts)
{
mTetrahedrons32 = tets32;
mTetrahedrons16 = tets16;
mVerts = verts;
}
bool isValid() const;
PX_FORCE_INLINE void getTetrahedron(TetrahedronPointers& vp, PxU32 index) const
{
PxU32 VRef0, VRef1, VRef2, VRef3;
getVertexReferences(VRef0, VRef1, VRef2, VRef3, index, mTetrahedrons32, mTetrahedrons16);
vp.Vertex[0] = mVerts + VRef0;
vp.Vertex[1] = mVerts + VRef1;
vp.Vertex[2] = mVerts + VRef2;
vp.Vertex[3] = mVerts + VRef3;
}
};
struct LocalBounds
{
// PX_SERIALIZATION
LocalBounds(const PxEMPTY) {}
//~PX_SERIALIZATION
LocalBounds() : mCenter(PxVec3(0.0f)), mExtentsMagnitude(0.0f) {}
PxVec3 mCenter;
float mExtentsMagnitude;
PX_FORCE_INLINE void init(const PxBounds3& bounds)
{
mCenter = bounds.getCenter();
// PT: TODO: compute mag first, then multiplies by 0.5f (TA34704)
mExtentsMagnitude = bounds.getExtents().magnitude();
}
};
class QuantizedAABB
{
public:
struct Data
{
PxU16 mExtents; //!< Quantized extents
PxI16 mCenter; //!< Quantized center
};
Data mData[3];
};
PX_COMPILE_TIME_ASSERT(sizeof(QuantizedAABB)==12);
/////
#define GU_BV4_CHILD_OFFSET_SHIFT_COUNT 11
static PX_FORCE_INLINE PxU32 getChildOffset(PxU32 data) { return data>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT; }
static PX_FORCE_INLINE PxU32 getChildType(PxU32 data) { return (data>>1)&3; }
template<class BoxType>
struct BVDataPackedT
{
BoxType mAABB;
PxU32 mData;
PX_FORCE_INLINE PxU32 isLeaf() const { return mData&1; }
PX_FORCE_INLINE PxU32 getPrimitive() const { return mData>>1; }
PX_FORCE_INLINE PxU32 getChildOffset() const { return mData>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT;}
PX_FORCE_INLINE PxU32 getChildType() const { return (mData>>1)&3; }
PX_FORCE_INLINE PxU32 getChildData() const { return mData; }
PX_FORCE_INLINE void encodePNS(PxU32 code)
{
PX_ASSERT(code<256);
mData |= code<<3;
}
PX_FORCE_INLINE PxU32 decodePNSNoShift() const { return mData; }
};
typedef BVDataPackedT<QuantizedAABB> BVDataPackedQ;
typedef BVDataPackedT<CenterExtents> BVDataPackedNQ;
// PT: TODO: align class to 16? (TA34704)
class BV4Tree : public physx::PxUserAllocated
{
public:
// PX_SERIALIZATION
BV4Tree(const PxEMPTY);
void exportExtraData(PxSerializationContext&);
void importExtraData(PxDeserializationContext& context);
//~PX_SERIALIZATION
BV4Tree();
BV4Tree(SourceMesh* meshInterface, const PxBounds3& localBounds);
~BV4Tree();
bool refit(PxBounds3& globalBounds, float epsilon);
bool load(PxInputStream& stream, bool mismatch);
void reset();
void operator = (BV4Tree& v);
bool init(SourceMeshBase* meshInterface, const PxBounds3& localBounds);
void release();
SourceMeshBase* mMeshInterface;
LocalBounds mLocalBounds;
PxU32 mNbNodes;
void* mNodes; // PT: BVDataPacked / BVDataSwizzled
PxU32 mInitData;
// PT: the dequantization coeffs are only used for quantized trees
PxVec3 mCenterOrMinCoeff; // PT: dequantization coeff, either for Center or Min (depending on AABB format)
PxVec3 mExtentsOrMaxCoeff; // PT: dequantization coeff, either for Extents or Max (depending on AABB format)
bool mUserAllocated; // PT: please keep these 4 bytes right after mCenterOrMinCoeff/mExtentsOrMaxCoeff for safe V4 loading
bool mQuantized; // PT: true for quantized trees
bool mIsEdgeSet; // PT: equivalent to RTree::IS_EDGE_SET
bool mPadding;
};
} // namespace Gu
}
#endif // GU_BV4_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,123 @@
// 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_BV4_BUILD_H
#define GU_BV4_BUILD_H
#include "foundation/PxSimpleTypes.h"
#include "foundation/PxUserAllocated.h"
#include "foundation/PxBounds3.h"
#include "GuBV4Settings.h"
namespace physx
{
namespace Gu
{
class BV4Tree;
class SourceMeshBase;
// PT: TODO: refactor with SQ version (TA34704)
class AABBTreeNode : public physx::PxUserAllocated
{
public:
PX_FORCE_INLINE AABBTreeNode() : mPos(0), mNodePrimitives(NULL), mNbPrimitives(0)
#ifdef GU_BV4_FILL_GAPS
, mNextSplit(0)
#endif
{
}
PX_FORCE_INLINE ~AABBTreeNode()
{
mPos = 0;
mNodePrimitives = NULL; // This was just a shortcut to the global list => no release
mNbPrimitives = 0;
}
// Data access
PX_FORCE_INLINE const PxBounds3& getAABB() const { return mBV; }
PX_FORCE_INLINE const AABBTreeNode* getPos() const { return reinterpret_cast<const AABBTreeNode*>(mPos); }
PX_FORCE_INLINE const AABBTreeNode* getNeg() const { const AABBTreeNode* P = getPos(); return P ? P+1 : NULL; }
PX_FORCE_INLINE bool isLeaf() const { return !getPos(); }
PxBounds3 mBV; // Global bounding-volume enclosing all the node-related primitives
size_t mPos; // "Positive" & "Negative" children
// Data access
PX_FORCE_INLINE const PxU32* getPrimitives() const { return mNodePrimitives; }
PX_FORCE_INLINE PxU32 getNbPrimitives() const { return mNbPrimitives; }
PxU32* mNodePrimitives; //!< Node-related primitives (shortcut to a position in mIndices below)
PxU32 mNbPrimitives; //!< Number of primitives for this node
#ifdef GU_BV4_FILL_GAPS
PxU32 mNextSplit;
#endif
};
typedef bool (*WalkingCallback) (const AABBTreeNode* current, PxU32 depth, void* userData);
typedef bool (*WalkingDistanceCallback) (const AABBTreeNode* current, void* userData);
enum BV4_BuildStrategy
{
BV4_SPLATTER_POINTS,
BV4_SPLATTER_POINTS_SPLIT_GEOM_CENTER,
BV4_SAH
};
// PT: TODO: refactor with SQ version (TA34704)
class BV4_AABBTree : public physx::PxUserAllocated
{
public:
BV4_AABBTree();
~BV4_AABBTree();
bool buildFromMesh(SourceMeshBase& mesh, PxU32 limit, BV4_BuildStrategy strategy=BV4_SPLATTER_POINTS);
void release();
PX_FORCE_INLINE const PxU32* getIndices() const { return mIndices; } //!< Catch the indices
PX_FORCE_INLINE PxU32 getNbNodes() const { return mTotalNbNodes; } //!< Catch the number of nodes
PX_FORCE_INLINE const PxU32* getPrimitives() const { return mPool->mNodePrimitives; }
PX_FORCE_INLINE PxU32 getNbPrimitives() const { return mPool->mNbPrimitives; }
PX_FORCE_INLINE const AABBTreeNode* getNodes() const { return mPool; }
PX_FORCE_INLINE const PxBounds3& getBV() const { return mPool->mBV; }
PxU32 walk(WalkingCallback callback, void* userData) const;
PxU32 walkDistance(WalkingCallback callback, WalkingDistanceCallback distancCallback, void* userData) const;
private:
PxU32* mIndices; //!< Indices in the app list. Indices are reorganized during build (permutation).
AABBTreeNode* mPool; //!< Linear pool of nodes for complete trees. Null otherwise. [Opcode 1.3]
PxU32 mTotalNbNodes; //!< Number of nodes in the tree.
};
bool BuildBV4Ex(BV4Tree& tree, SourceMeshBase& mesh, float epsilon, PxU32 nbPrimitivePerLeaf, bool quantized, BV4_BuildStrategy strategy=BV4_SPLATTER_POINTS);
} // namespace Gu
}
#endif // GU_BV4_BUILD_H

View File

@@ -0,0 +1,66 @@
// 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_BV4_SETTINGS_H
#define GU_BV4_SETTINGS_H
// PT: "BV4" ported from "Opcode 2.0". Available compile-time options are:
#define GU_BV4_STACK_SIZE 256 // Default size of local stacks for non-recursive traversals.
#define GU_BV4_PRECOMPUTED_NODE_SORT // Use node sorting or not. This should probably always be enabled.
// #define GU_BV4_QUANTIZED_TREE // Use AABB quantization/compression or not.
#define GU_BV4_USE_SLABS // Use swizzled data format or not. Swizzled = faster raycasts, but slower overlaps & larger trees.
// #define GU_BV4_COMPILE_NON_QUANTIZED_TREE //
#define GU_BV4_FILL_GAPS
//#define PROFILE_MESH_COOKING
#ifdef PROFILE_MESH_COOKING
#include <intrin.h>
#include <stdio.h>
struct LocalProfileZone
{
LocalProfileZone(const char* name)
{
mName = name;
mTime = __rdtsc();
}
~LocalProfileZone()
{
mTime = __rdtsc() - mTime;
printf("%s: %d\n", mName, unsigned int(mTime/1024));
}
const char* mName;
unsigned long long mTime;
};
#define GU_PROFILE_ZONE(name) LocalProfileZone zone(name);
#else
#define GU_PROFILE_ZONE(name)
#endif
#endif // GU_BV4_SETTINGS_H

View File

@@ -0,0 +1,110 @@
// 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_BV4_AABB_AABB_SWEEP_TEST_H
#define GU_BV4_AABB_AABB_SWEEP_TEST_H
#ifndef GU_BV4_USE_SLABS
PX_FORCE_INLINE PxIntBool BV4_SegmentAABBOverlap(const PxVec3& center, const PxVec3& extents, const PxVec3& extents2, const RayParams* PX_RESTRICT params)
{
const Vec4V fdirV = V4LoadA_Safe(&params->mFDir_PaddedAligned.x);
const Vec4V extentsV = V4Add(V4LoadU(&extents.x), V4LoadU(&extents2.x));
const Vec4V DV = V4Sub(V4LoadA_Safe(&params->mData2_PaddedAligned.x), V4LoadU(&center.x));
const Vec4V absDV = V4Abs(DV);
const BoolV resDV = V4IsGrtr(absDV, V4Add(extentsV, fdirV));
const PxU32 test = BGetBitMask(resDV);
if(test&7)
return 0;
if(1)
{
const Vec4V dataZYX_V = V4LoadA_Safe(&params->mData_PaddedAligned.x);
const Vec4V dataXZY_V = V4Perm<1, 2, 0, 3>(dataZYX_V);
const Vec4V DXZY_V = V4Perm<1, 2, 0, 3>(DV);
const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV));
const Vec4V fdirZYX_V = V4LoadA_Safe(&params->mFDir_PaddedAligned.x);
const Vec4V fdirXZY_V = V4Perm<1, 2, 0, 3>(fdirZYX_V);
const Vec4V extentsXZY_V = V4Perm<1, 2, 0, 3>(extentsV);
// PT: TODO: use V4MulAdd here (TA34704)
const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V));
const Vec4V absfV = V4Abs(fV);
const BoolV resAbsfV = V4IsGrtr(absfV, fg);
const PxU32 test2 = BGetBitMask(resAbsfV);
if(test2&7)
return 0;
return 1;
}
}
#ifdef GU_BV4_QUANTIZED_TREE
template<class T>
PX_FORCE_INLINE PxIntBool BV4_SegmentAABBOverlap(const T* PX_RESTRICT node, const PxVec3& extents2, const RayParams* PX_RESTRICT params)
{
const VecI32V testV = I4LoadA((PxI32*)node->mAABB.mData);
const VecI32V qextentsV = VecI32V_And(testV, I4Load(0x0000ffff));
const VecI32V qcenterV = VecI32V_RightShift(testV, 16);
const Vec4V centerV0 = V4Mul(Vec4V_From_VecI32V(qcenterV), V4LoadA_Safe(&params->mCenterOrMinCoeff_PaddedAligned.x));
const Vec4V extentsV0 = V4Mul(Vec4V_From_VecI32V(qextentsV), V4LoadA_Safe(&params->mExtentsOrMaxCoeff_PaddedAligned.x));
const Vec4V fdirV = V4LoadA_Safe(&params->mFDir_PaddedAligned.x);
const Vec4V extentsV = V4Add(extentsV0, V4LoadU(&extents2.x));
const Vec4V DV = V4Sub(V4LoadA_Safe(&params->mData2_PaddedAligned.x), centerV0);
const Vec4V absDV = V4Abs(DV);
const BoolV res = V4IsGrtr(absDV, V4Add(extentsV, fdirV));
const PxU32 test = BGetBitMask(res);
if(test&7)
return 0;
if(1)
{
const Vec4V dataZYX_V = V4LoadA_Safe(&params->mData_PaddedAligned.x);
const Vec4V dataXZY_V = V4Perm<1, 2, 0, 3>(dataZYX_V);
const Vec4V DXZY_V = V4Perm<1, 2, 0, 3>(DV);
const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV));
const Vec4V fdirZYX_V = V4LoadA_Safe(&params->mFDir_PaddedAligned.x);
const Vec4V fdirXZY_V = V4Perm<1, 2, 0, 3>(fdirZYX_V);
const Vec4V extentsXZY_V = V4Perm<1, 2, 0, 3>(extentsV);
// PT: TODO: use V4MulAdd here (TA34704)
const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V));
const Vec4V absfV = V4Abs(fV);
const BoolV res2 = V4IsGrtr(absfV, fg);
const PxU32 test2 = BGetBitMask(res2);
if(test2&7)
return 0;
return 1;
}
}
#endif
#endif
#endif // GU_BV4_AABB_AABB_SWEEP_TEST_H

View File

@@ -0,0 +1,36 @@
// 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 "GuBV4.h"
using namespace physx;
using namespace Gu;
#define SWEEP_AABB_IMPL
#include "foundation/PxVecMath.h"
using namespace aos;
#include "GuBV4_BoxSweep_Internal.h"

View File

@@ -0,0 +1,198 @@
// 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_BV4_BOX_BOX_OVERLAP_TEST_H
#define GU_BV4_BOX_BOX_OVERLAP_TEST_H
#ifndef GU_BV4_USE_SLABS
PX_FORCE_INLINE PxIntBool BV4_BoxBoxOverlap(const PxVec3& extents, const PxVec3& center, const OBBTestParams* PX_RESTRICT params)
{
const Vec4V extentsV = V4LoadU(&extents.x);
const Vec4V TV = V4Sub(V4LoadA_Safe(&params->mTBoxToModel_PaddedAligned.x), V4LoadU(&center.x));
{
const Vec4V absTV = V4Abs(TV);
const BoolV resTV = V4IsGrtr(absTV, V4Add(extentsV, V4LoadA_Safe(&params->mBB_PaddedAligned.x)));
const PxU32 test = BGetBitMask(resTV);
if(test&7)
return 0;
}
Vec4V tV;
{
const Vec4V T_YZX_V = V4Perm<1, 2, 0, 3>(TV);
const Vec4V T_ZXY_V = V4Perm<2, 0, 1, 3>(TV);
tV = V4Mul(TV, V4LoadA_Safe(&params->mPreca0_PaddedAligned.x));
tV = V4Add(tV, V4Mul(T_YZX_V, V4LoadA_Safe(&params->mPreca1_PaddedAligned.x)));
tV = V4Add(tV, V4Mul(T_ZXY_V, V4LoadA_Safe(&params->mPreca2_PaddedAligned.x)));
}
Vec4V t2V;
{
const Vec4V extents_YZX_V = V4Perm<1, 2, 0, 3>(extentsV);
const Vec4V extents_ZXY_V = V4Perm<2, 0, 1, 3>(extentsV);
t2V = V4Mul(extentsV, V4LoadA_Safe(&params->mPreca0b_PaddedAligned.x));
t2V = V4Add(t2V, V4Mul(extents_YZX_V, V4LoadA_Safe(&params->mPreca1b_PaddedAligned.x)));
t2V = V4Add(t2V, V4Mul(extents_ZXY_V, V4LoadA_Safe(&params->mPreca2b_PaddedAligned.x)));
t2V = V4Add(t2V, V4LoadA_Safe(&params->mBoxExtents_PaddedAligned.x));
}
{
const Vec4V abstV = V4Abs(tV);
const BoolV resB = V4IsGrtr(abstV, t2V);
const PxU32 test = BGetBitMask(resB);
if(test&7)
return 0;
}
return 1;
}
#ifdef GU_BV4_QUANTIZED_TREE
template<class T>
PX_FORCE_INLINE PxIntBool BV4_BoxBoxOverlap(const T* PX_RESTRICT node, const OBBTestParams* PX_RESTRICT params)
{
// A.B. enable new version only for intel non simd path
#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED)
// #define NEW_VERSION
#endif
#ifdef NEW_VERSION
SSE_CONST4(maskV, 0x7fffffff);
SSE_CONST4(maskQV, 0x0000ffff);
#endif
#ifdef NEW_VERSION
Vec4V centerV = V4LoadA((float*)node->mAABB.mData);
__m128 extentsV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(centerV), SSE_CONST(maskQV)));
extentsV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(extentsV)), V4LoadA_Safe(&params->mExtentsOrMaxCoeff_PaddedAligned.x));
centerV = _mm_castsi128_ps(_mm_srai_epi32(_mm_castps_si128(centerV), 16));
centerV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(centerV)), V4LoadA_Safe(&params->mCenterOrMinCoeff_PaddedAligned.x));
#else
const VecI32V centerVI = I4LoadA((PxI32*)node->mAABB.mData);
const VecI32V extentsVI = VecI32V_And(centerVI, I4Load(0x0000ffff));
const Vec4V extentsV = V4Mul(Vec4V_From_VecI32V(extentsVI), V4LoadA_Safe(&params->mExtentsOrMaxCoeff_PaddedAligned.x));
const VecI32V centerVShift = VecI32V_RightShift(centerVI, 16);
const Vec4V centerV = V4Mul(Vec4V_From_VecI32V(centerVShift), V4LoadA_Safe(&params->mCenterOrMinCoeff_PaddedAligned.x));
#endif
const Vec4V TV = V4Sub(V4LoadA_Safe(&params->mTBoxToModel_PaddedAligned.x), centerV);
{
#ifdef NEW_VERSION
const __m128 absTV = _mm_and_ps(TV, SSE_CONSTF(maskV));
#else
const Vec4V absTV = V4Abs(TV);
#endif
const BoolV resTV = V4IsGrtr(absTV, V4Add(extentsV, V4LoadA_Safe(&params->mBB_PaddedAligned.x)));
const PxU32 test = BGetBitMask(resTV);
if(test&7)
return 0;
}
Vec4V tV;
{
const Vec4V T_YZX_V = V4Perm<1, 2, 0, 3>(TV);
const Vec4V T_ZXY_V = V4Perm<2, 0, 1, 3>(TV);
tV = V4Mul(TV, V4LoadA_Safe(&params->mPreca0_PaddedAligned.x));
tV = V4Add(tV, V4Mul(T_YZX_V, V4LoadA_Safe(&params->mPreca1_PaddedAligned.x)));
tV = V4Add(tV, V4Mul(T_ZXY_V, V4LoadA_Safe(&params->mPreca2_PaddedAligned.x)));
}
Vec4V t2V;
{
const Vec4V extents_YZX_V = V4Perm<1, 2, 0, 3>(extentsV);
const Vec4V extents_ZXY_V = V4Perm<2, 0, 1, 3>(extentsV);
t2V = V4Mul(extentsV, V4LoadA_Safe(&params->mPreca0b_PaddedAligned.x));
t2V = V4Add(t2V, V4Mul(extents_YZX_V, V4LoadA_Safe(&params->mPreca1b_PaddedAligned.x)));
t2V = V4Add(t2V, V4Mul(extents_ZXY_V, V4LoadA_Safe(&params->mPreca2b_PaddedAligned.x)));
t2V = V4Add(t2V, V4LoadA_Safe(&params->mBoxExtents_PaddedAligned.x));
}
{
#ifdef NEW_VERSION
const __m128 abstV = _mm_and_ps(tV, SSE_CONSTF(maskV));
#else
const Vec4V abstV = V4Abs(tV);
#endif
const BoolV resB = V4IsGrtr(abstV, t2V);
const PxU32 test = BGetBitMask(resB);
if(test&7)
return 0;
}
return 1;
}
#endif // GU_BV4_QUANTIZED_TREE
#endif // GU_BV4_USE_SLABS
#ifdef GU_BV4_USE_SLABS
PX_FORCE_INLINE PxIntBool BV4_BoxBoxOverlap(const Vec4V boxCenter, const Vec4V extentsV, const OBBTestParams* PX_RESTRICT params)
{
const Vec4V TV = V4Sub(V4LoadA_Safe(&params->mTBoxToModel_PaddedAligned.x), boxCenter);
{
const Vec4V absTV = V4Abs(TV);
const BoolV res = V4IsGrtr(absTV, V4Add(extentsV, V4LoadA_Safe(&params->mBB_PaddedAligned.x)));
const PxU32 test = BGetBitMask(res);
if(test&7)
return 0;
}
Vec4V tV;
{
const Vec4V T_YZX_V = V4Perm<1, 2, 0, 3>(TV);
const Vec4V T_ZXY_V = V4Perm<2, 0, 1, 3>(TV);
tV = V4Mul(TV, V4LoadA_Safe(&params->mPreca0_PaddedAligned.x));
tV = V4Add(tV, V4Mul(T_YZX_V, V4LoadA_Safe(&params->mPreca1_PaddedAligned.x)));
tV = V4Add(tV, V4Mul(T_ZXY_V, V4LoadA_Safe(&params->mPreca2_PaddedAligned.x)));
}
Vec4V t2V;
{
const Vec4V extents_YZX_V = V4Perm<1, 2, 0, 3>(extentsV);
const Vec4V extents_ZXY_V = V4Perm<2, 0, 1, 3>(extentsV);
t2V = V4Mul(extentsV, V4LoadA_Safe(&params->mPreca0b_PaddedAligned.x));
t2V = V4Add(t2V, V4Mul(extents_YZX_V, V4LoadA_Safe(&params->mPreca1b_PaddedAligned.x)));
t2V = V4Add(t2V, V4Mul(extents_ZXY_V, V4LoadA_Safe(&params->mPreca2b_PaddedAligned.x)));
t2V = V4Add(t2V, V4LoadA_Safe(&params->mBoxExtents_PaddedAligned.x));
}
{
const Vec4V abstV = V4Abs(tV);
const BoolV resB = V4IsGrtr(abstV, t2V);
const PxU32 test = BGetBitMask(resB);
if(test&7)
return 0;
}
return 1;
}
#endif // GU_BV4_USE_SLABS
#endif // GU_BV4_BOX_BOX_OVERLAP_TEST_H

View File

@@ -0,0 +1,532 @@
// 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 "GuBV4.h"
using namespace physx;
using namespace Gu;
using namespace aos;
#include "GuInternal.h"
#include "GuDistancePointSegment.h"
#include "GuIntersectionCapsuleTriangle.h"
#include "GuBV4_BoxOverlap_Internal.h"
#include "GuBV4_BoxBoxOverlapTest.h"
// Box overlap any
struct OBBParams : OBBTestParams
{
const IndTri32* PX_RESTRICT mTris32;
const IndTri16* PX_RESTRICT mTris16;
const PxVec3* PX_RESTRICT mVerts;
PxMat33 mRModelToBox_Padded; //!< Rotation from model space to obb space
PxVec3p mTModelToBox_Padded; //!< Translation from model space to obb space
};
struct OBBTetParams : OBBTestParams
{
const IndTetrahedron32* PX_RESTRICT mTets32;
const IndTetrahedron16* PX_RESTRICT mTets16;
const PxVec3* PX_RESTRICT mVerts;
PxMat33 mRModelToBox_Padded; //!< Rotation from model space to obb space
PxVec3p mTModelToBox_Padded; //!< Translation from model space to obb space
};
// PT: TODO: this used to be inlined so we lost some perf by moving to PhysX's version. Revisit. (TA34704)
PxIntBool intersectTriangleBoxBV4(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2,
const PxMat33& rotModelToBox, const PxVec3& transModelToBox, const PxVec3& extents);
namespace
{
class LeafFunction_BoxOverlapAny
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(const OBBParams* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
if(intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned))
return 1;
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
template<class ParamsT, class SourceMeshT>
static PX_FORCE_INLINE void setupBoxParams(ParamsT* PX_RESTRICT params, const Box& localBox, const BV4Tree* PX_RESTRICT tree, const SourceMeshT* PX_RESTRICT mesh)
{
invertBoxMatrix(params->mRModelToBox_Padded, params->mTModelToBox_Padded, localBox);
setupMeshPointersAndQuantizedCoeffs(params, mesh, tree);
params->setupFullBoxBoxData(localBox.center, localBox.extents, &localBox.rot);
}
///////////////////////////////////////////////////////////////////////////////
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs.h"
#endif
#include "GuBV4_ProcessStreamNoOrder_OBBOBB.h"
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs_SwizzledNoOrder.h"
#endif
#define GU_BV4_PROCESS_STREAM_NO_ORDER
#include "GuBV4_Internal.h"
PxIntBool BV4_OverlapBoxAny(const Box& box, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
Box localBox;
computeLocalBox(localBox, box, worldm_Aligned);
OBBParams Params;
setupBoxParams(&Params, localBox, &tree, mesh);
if(tree.mNodes)
return processStreamNoOrder<LeafFunction_BoxOverlapAny>(tree, &Params);
else
{
const PxU32 nbTris = mesh->getNbPrimitives();
PX_ASSERT(nbTris<16);
return LeafFunction_BoxOverlapAny::doLeafTest(&Params, nbTris);
}
}
// Box overlap all
struct OBBParamsAll : OBBParams
{
PxU32 mNbHits;
PxU32 mMaxNbHits;
PxU32* mHits;
};
namespace
{
class LeafFunction_BoxOverlapAll
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(OBBParams* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
if(intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned))
{
OBBParamsAll* ParamsAll = static_cast<OBBParamsAll*>(params);
if(ParamsAll->mNbHits==ParamsAll->mMaxNbHits)
return 1;
ParamsAll->mHits[ParamsAll->mNbHits] = primIndex;
ParamsAll->mNbHits++;
}
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
PxU32 BV4_OverlapBoxAll(const Box& box, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
Box localBox;
computeLocalBox(localBox, box, worldm_Aligned);
OBBParamsAll Params;
Params.mNbHits = 0;
Params.mMaxNbHits = size;
Params.mHits = results;
setupBoxParams(&Params, localBox, &tree, mesh);
if(tree.mNodes)
overflow = processStreamNoOrder<LeafFunction_BoxOverlapAll>(tree, &Params)!=0;
else
{
const PxU32 nbTris = mesh->getNbPrimitives();
PX_ASSERT(nbTris<16);
overflow = LeafFunction_BoxOverlapAll::doLeafTest(&Params, nbTris)!=0;
}
return Params.mNbHits;
}
// Box overlap - callback version
struct OBBParamsCB : OBBParams
{
MeshOverlapCallback mCallback;
void* mUserData;
};
struct OBBTetParamsCB : OBBTetParams
{
TetMeshOverlapCallback mCallback;
void* mUserData;
};
namespace
{
class LeafFunction_BoxOverlapCB
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(const OBBParamsCB* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
if (intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned))
{
const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 };
if ((params->mCallback)(params->mUserData, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], primIndex, vrefs))
return 1;
}
primIndex++;
}while(nbToGo--);
return 0;
}
static PX_FORCE_INLINE PxIntBool doLeafTest(const OBBTetParamsCB* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
PxU32 VRef0, VRef1, VRef2, VRef3;
getVertexReferences(VRef0, VRef1, VRef2, VRef3, primIndex, params->mTets32, params->mTets16);
if (intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned))
{
const PxU32 vrefs[4] = { VRef0, VRef1, VRef2, VRef3};
if ((params->mCallback)(params->mUserData, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mVerts[VRef3], primIndex, vrefs))
return 1;
}
if (intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef3], params->mVerts[VRef1], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned))
{
const PxU32 vrefs[4] = { VRef0, VRef1, VRef2, VRef3 };
if ((params->mCallback)(params->mUserData, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mVerts[VRef3], primIndex, vrefs))
return 1;
}
if (intersectTriangleBoxBV4(params->mVerts[VRef1], params->mVerts[VRef3], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned))
{
const PxU32 vrefs[4] = { VRef0, VRef1, VRef2, VRef3 };
if ((params->mCallback)(params->mUserData, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mVerts[VRef3], primIndex, vrefs))
return 1;
}
if (intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef3], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned))
{
const PxU32 vrefs[4] = { VRef0, VRef1, VRef2, VRef3 };
if ((params->mCallback)(params->mUserData, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mVerts[VRef3], primIndex, vrefs))
return 1;
}
primIndex++;
} while (nbToGo--);
return 0;
}
};
}
void BV4_OverlapBoxCB(const Box& localBox, const BV4Tree& tree, MeshOverlapCallback callback, void* userData)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
OBBParamsCB Params;
Params.mCallback = callback;
Params.mUserData = userData;
setupBoxParams(&Params, localBox, &tree, mesh);
if(tree.mNodes)
processStreamNoOrder<LeafFunction_BoxOverlapCB>(tree, &Params);
else
{
const PxU32 nbTris = mesh->getNbPrimitives();
PX_ASSERT(nbTris<16);
LeafFunction_BoxOverlapCB::doLeafTest(&Params, nbTris);
}
}
void BV4_OverlapBoxCB(const Box& localBox, const BV4Tree& tree, TetMeshOverlapCallback callback, void* userData)
{
const TetrahedronSourceMesh* PX_RESTRICT mesh = static_cast<TetrahedronSourceMesh*>(tree.mMeshInterface);
OBBTetParamsCB Params;
Params.mCallback = callback;
Params.mUserData = userData;
setupBoxParams(&Params, localBox, &tree, mesh);
if (tree.mNodes)
processStreamNoOrder<LeafFunction_BoxOverlapCB>(tree, &Params);
else
{
const PxU32 nbTetrahedrons = mesh->getNbTetrahedrons();
PX_ASSERT(nbTetrahedrons<16);
LeafFunction_BoxOverlapCB::doLeafTest(&Params, nbTetrahedrons);
}
}
// Capsule overlap any
struct CapsuleParamsAny : OBBParams
{
Capsule mLocalCapsule; // Capsule in mesh space
CapsuleTriangleOverlapData mData;
};
// PT: TODO: try to refactor this one with the PhysX version (TA34704)
static bool CapsuleVsTriangle_SAT(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const CapsuleParamsAny* PX_RESTRICT params)
{
// PX_ASSERT(capsule.p0!=capsule.p1);
{
const PxReal d2 = distancePointSegmentSquaredInternal(params->mLocalCapsule.p0, params->mData.mCapsuleDir, p0);
if(d2<=params->mLocalCapsule.radius*params->mLocalCapsule.radius)
return 1;
}
const PxVec3 N = (p0 - p1).cross(p0 - p2);
if(!testAxis(p0, p1, p2, params->mLocalCapsule, N))
return 0;
const float BDotB = params->mData.mBDotB;
const float oneOverBDotB = params->mData.mOneOverBDotB;
const PxVec3& capP0 = params->mLocalCapsule.p0;
const PxVec3& capDir = params->mData.mCapsuleDir;
if(!testAxis(p0, p1, p2, params->mLocalCapsule, computeEdgeAxis(p0, p1 - p0, capP0, capDir, BDotB, oneOverBDotB)))
return 0;
if(!testAxis(p0, p1, p2, params->mLocalCapsule, computeEdgeAxis(p1, p2 - p1, capP0, capDir, BDotB, oneOverBDotB)))
return 0;
if(!testAxis(p0, p1, p2, params->mLocalCapsule, computeEdgeAxis(p2, p0 - p2, capP0, capDir, BDotB, oneOverBDotB)))
return 0;
return 1;
}
static PxIntBool PX_FORCE_INLINE capsuleTriangle(const CapsuleParamsAny* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
return CapsuleVsTriangle_SAT(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params);
}
namespace
{
class LeafFunction_CapsuleOverlapAny
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(const OBBParams* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
if(capsuleTriangle(static_cast<const CapsuleParamsAny*>(params), primIndex))
return 1;
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
template<class ParamsT>
static PX_FORCE_INLINE void setupCapsuleParams(ParamsT* PX_RESTRICT params, const Capsule& capsule, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT worldm_Aligned, const SourceMesh* PX_RESTRICT mesh)
{
computeLocalCapsule(params->mLocalCapsule, capsule, worldm_Aligned);
params->mData.init(params->mLocalCapsule);
Box localBox;
computeBoxAroundCapsule(params->mLocalCapsule, localBox);
setupBoxParams(params, localBox, tree, mesh);
}
PxIntBool BV4_OverlapCapsuleAny(const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<SourceMesh*>(tree.mMeshInterface);
CapsuleParamsAny Params;
setupCapsuleParams(&Params, capsule, &tree, worldm_Aligned, mesh);
if(tree.mNodes)
return processStreamNoOrder<LeafFunction_CapsuleOverlapAny>(tree, &Params);
else
{
const PxU32 nbTris = mesh->getNbTriangles();
PX_ASSERT(nbTris<16);
return LeafFunction_CapsuleOverlapAny::doLeafTest(&Params, nbTris);
}
}
// Capsule overlap all
struct CapsuleParamsAll : CapsuleParamsAny
{
PxU32 mNbHits;
PxU32 mMaxNbHits;
PxU32* mHits;
};
namespace
{
class LeafFunction_CapsuleOverlapAll
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(OBBParams* PX_RESTRICT params, PxU32 primIndex)
{
CapsuleParamsAll* ParamsAll = static_cast<CapsuleParamsAll*>(params);
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
if(capsuleTriangle(ParamsAll, primIndex))
{
if(ParamsAll->mNbHits==ParamsAll->mMaxNbHits)
return 1;
ParamsAll->mHits[ParamsAll->mNbHits] = primIndex;
ParamsAll->mNbHits++;
}
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
PxU32 BV4_OverlapCapsuleAll(const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<SourceMesh*>(tree.mMeshInterface);
CapsuleParamsAll Params;
Params.mNbHits = 0;
Params.mMaxNbHits = size;
Params.mHits = results;
setupCapsuleParams(&Params, capsule, &tree, worldm_Aligned, mesh);
if(tree.mNodes)
overflow = processStreamNoOrder<LeafFunction_CapsuleOverlapAll>(tree, &Params)!=0;
else
{
const PxU32 nbTris = mesh->getNbTriangles();
PX_ASSERT(nbTris<16);
overflow = LeafFunction_CapsuleOverlapAll::doLeafTest(&Params, nbTris)!=0;
}
return Params.mNbHits;
}
// Capsule overlap - callback version
struct CapsuleParamsCB : CapsuleParamsAny
{
MeshOverlapCallback mCallback;
void* mUserData;
};
namespace
{
class LeafFunction_CapsuleOverlapCB
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(const CapsuleParamsCB* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
const PxVec3& p0 = params->mVerts[VRef0];
const PxVec3& p1 = params->mVerts[VRef1];
const PxVec3& p2 = params->mVerts[VRef2];
if(CapsuleVsTriangle_SAT(p0, p1, p2, params))
{
const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 };
if((params->mCallback)(params->mUserData, p0, p1, p2, primIndex, vrefs))
return 1;
}
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
// PT: this one is currently not used
void BV4_OverlapCapsuleCB(const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshOverlapCallback callback, void* userData)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
CapsuleParamsCB Params;
Params.mCallback = callback;
Params.mUserData = userData;
setupCapsuleParams(&Params, capsule, &tree, worldm_Aligned, mesh);
if(tree.mNodes)
processStreamNoOrder<LeafFunction_CapsuleOverlapCB>(tree, &Params);
else
{
const PxU32 nbTris = mesh->getNbTriangles();
PX_ASSERT(nbTris<16);
LeafFunction_CapsuleOverlapCB::doLeafTest(&Params, nbTris);
}
}

View File

@@ -0,0 +1,131 @@
// 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_BV4_BOX_OVERLAP_INTERNAL_H
#define GU_BV4_BOX_OVERLAP_INTERNAL_H
#include "GuBV4_Common.h"
// PT: precompute the constant rotation data used in the OBB-OBB overlap test.
template<class ParamsT>
PX_FORCE_INLINE void setupBoxBoxRotationData(ParamsT* PX_RESTRICT dst, PxMat33* PX_RESTRICT absRot, const PxMat33* PX_RESTRICT boxToModelR)
{
// Precompute absolute box-to-model rotation matrix
dst->mPreca0_PaddedAligned.x = boxToModelR->column0.x;
dst->mPreca0_PaddedAligned.y = boxToModelR->column1.y;
dst->mPreca0_PaddedAligned.z = boxToModelR->column2.z;
dst->mPreca1_PaddedAligned.x = boxToModelR->column0.y;
dst->mPreca1_PaddedAligned.y = boxToModelR->column1.z;
dst->mPreca1_PaddedAligned.z = boxToModelR->column2.x;
dst->mPreca2_PaddedAligned.x = boxToModelR->column0.z;
dst->mPreca2_PaddedAligned.y = boxToModelR->column1.x;
dst->mPreca2_PaddedAligned.z = boxToModelR->column2.y;
// Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID)
const PxReal epsilon = 1e-6f;
// PT: TODO: it shouldn't be necessary to output both absRot and mPrecaXX_PaddedAligned
absRot->column0.x = dst->mPreca0b_PaddedAligned.x = epsilon + fabsf(boxToModelR->column0.x);
absRot->column0.y = dst->mPreca1b_PaddedAligned.x = epsilon + fabsf(boxToModelR->column0.y);
absRot->column0.z = dst->mPreca2b_PaddedAligned.x = epsilon + fabsf(boxToModelR->column0.z);
absRot->column1.x = dst->mPreca2b_PaddedAligned.y = epsilon + fabsf(boxToModelR->column1.x);
absRot->column1.y = dst->mPreca0b_PaddedAligned.y = epsilon + fabsf(boxToModelR->column1.y);
absRot->column1.z = dst->mPreca1b_PaddedAligned.y = epsilon + fabsf(boxToModelR->column1.z);
absRot->column2.x = dst->mPreca1b_PaddedAligned.z = epsilon + fabsf(boxToModelR->column2.x);
absRot->column2.y = dst->mPreca2b_PaddedAligned.z = epsilon + fabsf(boxToModelR->column2.y);
absRot->column2.z = dst->mPreca0b_PaddedAligned.z = epsilon + fabsf(boxToModelR->column2.z);
}
// PT: precompute the extent data used in the OBB-OBB overlap test. This is separate from setupBoxBoxRotationData(),
// because the extents can shrink during sweep traversals (while the rotation is constant).
template<class ParamsT>
PX_FORCE_INLINE void setupBoxBoxExtentData(ParamsT* PX_RESTRICT dst, const PxVec3& extents, const PxMat33* PX_RESTRICT absRot)
{
dst->mBoxExtents_PaddedAligned = extents;
const float Ex = extents.x;
const float Ey = extents.y;
const float Ez = extents.z;
//dst->mBB_PaddedAligned.x = Ex*absRot->column0.x + Ey*absRot->column1.x + Ez*absRot->column2.x;
//dst->mBB_PaddedAligned.y = Ex*absRot->column0.y + Ey*absRot->column1.y + Ez*absRot->column2.y;
//dst->mBB_PaddedAligned.z = Ex*absRot->column0.z + Ey*absRot->column1.z + Ez*absRot->column2.z;
// PT: the above code with absRot should be equivalent to:
PX_ASSERT(absRot->column0.x==dst->mPreca0b_PaddedAligned.x);
PX_ASSERT(absRot->column0.y==dst->mPreca1b_PaddedAligned.x);
PX_ASSERT(absRot->column0.z==dst->mPreca2b_PaddedAligned.x);
PX_ASSERT(absRot->column1.x==dst->mPreca2b_PaddedAligned.y);
PX_ASSERT(absRot->column1.y==dst->mPreca0b_PaddedAligned.y);
PX_ASSERT(absRot->column1.z==dst->mPreca1b_PaddedAligned.y);
PX_ASSERT(absRot->column2.x==dst->mPreca1b_PaddedAligned.z);
PX_ASSERT(absRot->column2.y==dst->mPreca2b_PaddedAligned.z);
PX_ASSERT(absRot->column2.z==dst->mPreca0b_PaddedAligned.z);
PX_UNUSED(absRot);
dst->mBB_PaddedAligned.x = Ex*dst->mPreca0b_PaddedAligned.x + Ey*dst->mPreca2b_PaddedAligned.y + Ez*dst->mPreca1b_PaddedAligned.z;
dst->mBB_PaddedAligned.y = Ex*dst->mPreca1b_PaddedAligned.x + Ey*dst->mPreca0b_PaddedAligned.y + Ez*dst->mPreca2b_PaddedAligned.z;
dst->mBB_PaddedAligned.z = Ex*dst->mPreca2b_PaddedAligned.x + Ey*dst->mPreca1b_PaddedAligned.y + Ez*dst->mPreca0b_PaddedAligned.z;
}
struct OBBTestParams // Data needed to perform the OBB-OBB overlap test
{
BV4_ALIGN16(PxVec3p mCenterOrMinCoeff_PaddedAligned);
BV4_ALIGN16(PxVec3p mExtentsOrMaxCoeff_PaddedAligned);
BV4_ALIGN16(PxVec3p mTBoxToModel_PaddedAligned); // (1) Translation from obb space to model space
BV4_ALIGN16(PxVec3p mBB_PaddedAligned); // (2)
BV4_ALIGN16(PxVec3p mBoxExtents_PaddedAligned); // (3)
BV4_ALIGN16(PxVec3p mPreca0_PaddedAligned); // (4)
BV4_ALIGN16(PxVec3p mPreca1_PaddedAligned); //
BV4_ALIGN16(PxVec3p mPreca2_PaddedAligned); //
BV4_ALIGN16(PxVec3p mPreca0b_PaddedAligned); // (5)
BV4_ALIGN16(PxVec3p mPreca1b_PaddedAligned); //
BV4_ALIGN16(PxVec3p mPreca2b_PaddedAligned); //
// PT: precompute the full data used in the OBB-OBB overlap test.
PX_FORCE_INLINE void setupFullBoxBoxData(const PxVec3& center, const PxVec3& extents, const PxMat33* PX_RESTRICT box_to_model)
{
// PT: setup (1)
mTBoxToModel_PaddedAligned = center;
// PT: setup (4) and (5)
PxMat33 absRot; //!< Absolute rotation matrix
setupBoxBoxRotationData(this, &absRot, box_to_model);
// PT: setup (2) and (3)
setupBoxBoxExtentData(this, extents, &absRot);
}
};
#endif // GU_BV4_BOX_OVERLAP_INTERNAL_H

View File

@@ -0,0 +1,519 @@
// 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 "GuSweepTriangleUtils.h"
#include "GuSweepBoxTriangle_FeatureBased.h"
#include "GuSweepBoxTriangle_SAT.h"
#include "GuBV4_BoxOverlap_Internal.h"
// PT: for box-sweeps please refer to %SDKRoot%\InternalDocumentation\GU\Sweep strategies.ppt.
// We use:
// - method 3 if the box is an AABB (SWEEP_AABB_IMPL is defined)
// - method 2 if the box is an OBB (SWEEP_AABB_IMPL is undefined)
#ifdef SWEEP_AABB_IMPL
// PT: TODO: refactor structure (TA34704)
namespace
{
struct RayParams
{
BV4_ALIGN16(PxVec3p mCenterOrMinCoeff_PaddedAligned);
BV4_ALIGN16(PxVec3p mExtentsOrMaxCoeff_PaddedAligned);
#ifndef GU_BV4_USE_SLABS
BV4_ALIGN16(PxVec3p mData2_PaddedAligned);
BV4_ALIGN16(PxVec3p mFDir_PaddedAligned);
BV4_ALIGN16(PxVec3p mData_PaddedAligned);
BV4_ALIGN16(PxVec3p mLocalDir_PaddedAligned);
#endif
BV4_ALIGN16(PxVec3p mOrigin_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704)
};
}
#include "GuBV4_AABBAABBSweepTest.h"
#else
#include "GuBV4_BoxBoxOverlapTest.h"
#endif
#include "GuBV4_BoxSweep_Params.h"
static PX_FORCE_INLINE Vec4V multiply3x3V(const Vec4V p, const PxMat33& mat_Padded)
{
const FloatV xxxV = V4GetX(p);
const FloatV yyyV = V4GetY(p);
const FloatV zzzV = V4GetZ(p);
Vec4V ResV = V4Scale(V4LoadU_Safe(&mat_Padded.column0.x), xxxV);
ResV = V4Add(ResV, V4Scale(V4LoadU_Safe(&mat_Padded.column1.x), yyyV));
ResV = V4Add(ResV, V4Scale(V4LoadU_Safe(&mat_Padded.column2.x), zzzV));
return ResV;
}
// PT: TODO: __fastcall removed to make it compile everywhere. Revisit.
static bool /*__fastcall*/ triBoxSweep(BoxSweepParams* PX_RESTRICT params, PxU32 primIndex, bool nodeSorting=true)
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
const PxVec3& p0 = params->mVerts[VRef0];
const PxVec3& p1 = params->mVerts[VRef1];
const PxVec3& p2 = params->mVerts[VRef2];
// Don't bother doing the actual sweep test if the triangle is too far away
if(1)
{
const float dp0 = p0.dot(params->mLocalDir_Padded);
const float dp1 = p1.dot(params->mLocalDir_Padded);
const float dp2 = p2.dot(params->mLocalDir_Padded);
float TriMin = PxMin(dp0, dp1);
TriMin = PxMin(TriMin, dp2);
if(TriMin >= params->mOffset + params->mStabbedFace.mDistance)
return false;
}
PxTrianglePadded triBoxSpace;
const Vec4V transModelToBoxV = V4LoadU_Safe(&params->mTModelToBox_Padded.x);
const Vec4V v0V = V4Add(multiply3x3V(V4LoadU_Safe(&p0.x), params->mRModelToBox_Padded), transModelToBoxV);
V4StoreU_Safe(v0V, &triBoxSpace.verts[0].x);
const Vec4V v1V = V4Add(multiply3x3V(V4LoadU_Safe(&p1.x), params->mRModelToBox_Padded), transModelToBoxV);
V4StoreU_Safe(v1V, &triBoxSpace.verts[1].x);
const Vec4V v2V = V4Add(multiply3x3V(V4LoadU_Safe(&p2.x), params->mRModelToBox_Padded), transModelToBoxV);
V4StoreU_Safe(v2V, &triBoxSpace.verts[2].x);
float Dist;
if(triBoxSweepTestBoxSpace_inlined(triBoxSpace, params->mOriginalExtents_Padded, params->mOriginalDir_Padded*params->mStabbedFace.mDistance, params->mOneOverDir_Padded, 1.0f, Dist, params->mBackfaceCulling))
{
// PT: TODO: these muls & divs may not be needed at all - we just pass the unit dir/inverse dir to the sweep code. Revisit. (TA34704)
Dist *= params->mStabbedFace.mDistance;
params->mOneOverDir_Padded = params->mOneOverOriginalDir / Dist;
params->mStabbedFace.mDistance = Dist;
params->mStabbedFace.mTriangleID = primIndex;
// PT: TODO: revisit this (TA34704)
params->mP0 = triBoxSpace.verts[0];
params->mP1 = triBoxSpace.verts[1];
params->mP2 = triBoxSpace.verts[2];
// V4StoreU_Safe(v0V, &params->mP0.x);
// V4StoreU_Safe(v1V, &params->mP1.x);
// V4StoreU_Safe(v2V, &params->mP2.x);
if(nodeSorting)
{
#ifdef SWEEP_AABB_IMPL
#ifndef GU_BV4_USE_SLABS
setupRayData(params, Dist, params->mOrigin_Padded, params->mLocalDir_PaddedAligned);
#endif
#else
params->shrinkOBB(Dist);
#endif
}
return true;
}
return false;
}
namespace
{
class LeafFunction_BoxSweepClosest
{
public:
static PX_FORCE_INLINE void doLeafTest(BoxSweepParams* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
triBoxSweep(params, primIndex);
primIndex++;
}while(nbToGo--);
}
};
class LeafFunction_BoxSweepAny
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(BoxSweepParams* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
if(triBoxSweep(params, primIndex))
return 1;
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
// PT: TODO: refactor with sphere/capsule versions (TA34704)
static PX_FORCE_INLINE bool computeImpactData(const Box& box, const PxVec3& dir, SweepHit* PX_RESTRICT hit, const BoxSweepParams* PX_RESTRICT params, bool isDoubleSided, bool meshBothSides)
{
if(params->mStabbedFace.mTriangleID==PX_INVALID_U32)
return false; // We didn't touch any triangle
if(hit)
{
const float t = params->mStabbedFace.mDistance;
hit->mTriangleID = params->mStabbedFace.mTriangleID;
hit->mDistance = t;
if(t==0.0f)
{
hit->mPos = PxVec3(0.0f);
hit->mNormal = -dir;
}
else
{
// PT: TODO: revisit/optimize/use this (TA34704)
const PxTriangle triInBoxSpace(params->mP0, params->mP1, params->mP2);
PxHitFlags outFlags = PxHitFlag::Enum(0);
computeBoxLocalImpact(hit->mPos, hit->mNormal, outFlags, box, params->mOriginalDir_Padded, triInBoxSpace, PxHitFlag::ePOSITION|PxHitFlag::eNORMAL, isDoubleSided, meshBothSides, t);
}
}
return true;
}
template<class ParamsT>
static PX_FORCE_INLINE void setupBoxSweepParams(ParamsT* PX_RESTRICT params, const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree* PX_RESTRICT tree, const SourceMesh* PX_RESTRICT mesh, PxU32 flags)
{
params->mStabbedFace.mTriangleID = PX_INVALID_U32;
setupParamsFlags(params, flags);
setupMeshPointersAndQuantizedCoeffs(params, mesh, tree);
prepareSweepData(localBox, localDir, maxDist, params);
#ifdef SWEEP_AABB_IMPL
params->mOrigin_Padded = localBox.center;
#ifndef GU_BV4_USE_SLABS
params->mLocalDir_PaddedAligned = localDir;
setupRayData(params, maxDist, localBox.center, localDir);
#endif
#endif
}
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs.h"
#endif
#ifdef SWEEP_AABB_IMPL
#include "GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h"
#include "GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h"
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs_KajiyaNoOrder.h"
#include "GuBV4_Slabs_KajiyaOrdered.h"
#endif
#else
#include "GuBV4_ProcessStreamOrdered_OBBOBB.h"
#include "GuBV4_ProcessStreamNoOrder_OBBOBB.h"
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs_SwizzledNoOrder.h"
#include "GuBV4_Slabs_SwizzledOrdered.h"
#endif
#endif
#ifdef SWEEP_AABB_IMPL
#define GU_BV4_PROCESS_STREAM_RAY_NO_ORDER
#define GU_BV4_PROCESS_STREAM_RAY_ORDERED
#else
#define GU_BV4_PROCESS_STREAM_NO_ORDER
#define GU_BV4_PROCESS_STREAM_ORDERED
#endif
#include "GuBV4_Internal.h"
#ifdef SWEEP_AABB_IMPL
PxIntBool Sweep_AABB_BV4(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags)
#else
PxIntBool Sweep_OBB_BV4(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags)
#endif
{
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
BoxSweepParams Params;
setupBoxSweepParams(&Params, localBox, localDir, maxDist, &tree, mesh, flags);
if(tree.mNodes)
{
#ifdef SWEEP_AABB_IMPL
if(Params.mEarlyExit)
processStreamRayNoOrder<1, LeafFunction_BoxSweepAny>(tree, &Params);
else
processStreamRayOrdered<1, LeafFunction_BoxSweepClosest>(tree, &Params);
#else
if(Params.mEarlyExit)
processStreamNoOrder<LeafFunction_BoxSweepAny>(tree, &Params);
else
processStreamOrdered<LeafFunction_BoxSweepClosest>(tree, &Params);
#endif
}
else
doBruteForceTests<LeafFunction_BoxSweepAny, LeafFunction_BoxSweepClosest>(mesh->getNbTriangles(), &Params);
return computeImpactData(localBox, localDir, hit, &Params, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0);
}
// PT: box sweep callback version - currently not used
namespace
{
struct BoxSweepParamsCB : BoxSweepParams
{
// PT: these new members are only here to call computeImpactData during traversal :(
// PT: TODO: most of them may not be needed
Box mBoxCB; // Box in original space (maybe not local/mesh space)
PxVec3 mDirCB; // Dir in original space (maybe not local/mesh space)
const PxMat44* mWorldm_Aligned;
PxU32 mFlags;
SweepUnlimitedCallback mCallback;
void* mUserData;
float mMaxDist;
bool mNodeSorting;
};
class LeafFunction_BoxSweepCB
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(BoxSweepParamsCB* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
if(triBoxSweep(params, primIndex, params->mNodeSorting))
{
// PT: TODO: in this version we must compute the impact data immediately,
// which is a bad idea in general, but I'm not sure what else I can do.
SweepHit hit;
const bool b = computeImpactData(params->mBoxCB, params->mDirCB, &hit, params, (params->mFlags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (params->mFlags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0);
PX_ASSERT(b);
// PT: then replicate part from BV4_BoxSweepSingle:
if(b && params->mWorldm_Aligned)
{
// Move to world space
// PT: TODO: optimize (TA34704)
hit.mPos = params->mWorldm_Aligned->transform(hit.mPos);
hit.mNormal = params->mWorldm_Aligned->rotate(hit.mNormal);
}
reportUnlimitedCallbackHit(params, hit);
}
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB().
// PT: 'worldm_Aligned' is only here to move back results to world space, but input is already in local space.
#ifdef SWEEP_AABB_IMPL
void Sweep_AABB_BV4_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting)
#else
void Sweep_OBB_BV4_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting)
#endif
{
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
BoxSweepParamsCB Params;
Params.mBoxCB = localBox;
Params.mDirCB = localDir;
Params.mWorldm_Aligned = worldm_Aligned;
Params.mFlags = flags;
Params.mCallback = callback;
Params.mUserData = userData;
Params.mMaxDist = maxDist;
Params.mNodeSorting = nodeSorting;
setupBoxSweepParams(&Params, localBox, localDir, maxDist, &tree, mesh, flags);
PX_ASSERT(!Params.mEarlyExit);
if(tree.mNodes)
{
if(nodeSorting)
{
#ifdef SWEEP_AABB_IMPL
processStreamRayOrdered<1, LeafFunction_BoxSweepCB>(tree, &Params);
#else
processStreamOrdered<LeafFunction_BoxSweepCB>(tree, &Params);
#endif
}
else
{
#ifdef SWEEP_AABB_IMPL
processStreamRayNoOrder<1, LeafFunction_BoxSweepCB>(tree, &Params);
#else
processStreamNoOrder<LeafFunction_BoxSweepCB>(tree, &Params);
#endif
}
}
else
doBruteForceTests<LeafFunction_BoxSweepCB, LeafFunction_BoxSweepCB>(mesh->getNbTriangles(), &Params);
}
// New callback-based box sweeps. Reuses code above, allow early exits. Some init code may be done in vain
// since the leaf tests are not performed (we don't do box-sweeps-vs-tri since the box is only a BV around
// the actual shape, say a convex)
namespace
{
struct GenericSweepParamsCB : BoxSweepParams
{
MeshSweepCallback mCallback;
void* mUserData;
};
class LeafFunction_BoxSweepClosestCB
{
public:
static PX_FORCE_INLINE void doLeafTest(GenericSweepParamsCB* PX_RESTRICT params, PxU32 prim_index)
{
PxU32 nbToGo = getNbPrimitives(prim_index);
do
{
// PT: in the regular version we'd do a box-vs-triangle sweep test here
// Instead we just grab the triangle and send it to the callback
//
// This can be used for regular "closest hit" sweeps, when the scale is not identity or
// when the box is just around a more complex shape (e.g. convex). In this case we want
// the calling code to compute a convex-triangle distance, and then we want to shrink
// the ray/box while doing an ordered traversal.
//
// For "sweep all" or "sweep any" purposes we want to either report all hits or early exit
// as soon as we find one. There is no need for shrinking or ordered traversals here.
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, prim_index, params->mTris32, params->mTris16);
const PxVec3& p0 = params->mVerts[VRef0];
const PxVec3& p1 = params->mVerts[VRef1];
const PxVec3& p2 = params->mVerts[VRef2];
// Don't bother doing the actual sweep test if the triangle is too far away
const float dp0 = p0.dot(params->mLocalDir_Padded);
const float dp1 = p1.dot(params->mLocalDir_Padded);
const float dp2 = p2.dot(params->mLocalDir_Padded);
float TriMin = PxMin(dp0, dp1);
TriMin = PxMin(TriMin, dp2);
if(TriMin < params->mOffset + params->mStabbedFace.mDistance)
{
// const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 };
float Dist = params->mStabbedFace.mDistance;
if((params->mCallback)(params->mUserData, p0, p1, p2, prim_index, /*vrefs,*/ Dist))
return; // PT: TODO: we return here but the ordered path doesn't really support early exits (TA34704)
if(Dist<params->mStabbedFace.mDistance)
{
params->mStabbedFace.mDistance = Dist;
params->mStabbedFace.mTriangleID = prim_index;
#ifdef SWEEP_AABB_IMPL
#ifndef GU_BV4_USE_SLABS
setupRayData(params, Dist, params->mOrigin_Padded, params->mLocalDir_PaddedAligned);
#endif
#else
params->shrinkOBB(Dist);
#endif
}
}
prim_index++;
}while(nbToGo--);
}
};
class LeafFunction_BoxSweepAnyCB
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(GenericSweepParamsCB* PX_RESTRICT params, PxU32 prim_index)
{
PxU32 nbToGo = getNbPrimitives(prim_index);
do
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, prim_index, params->mTris32, params->mTris16);
const PxVec3& p0 = params->mVerts[VRef0];
const PxVec3& p1 = params->mVerts[VRef1];
const PxVec3& p2 = params->mVerts[VRef2];
{
// const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 };
float Dist = params->mStabbedFace.mDistance;
if((params->mCallback)(params->mUserData, p0, p1, p2, prim_index, /*vrefs,*/ Dist))
return 1;
}
prim_index++;
}while(nbToGo--);
return 0;
}
};
}
#ifdef SWEEP_AABB_IMPL
void GenericSweep_AABB_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, PxU32 flags)
#else
void GenericSweep_OBB_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, PxU32 flags)
#endif
{
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
GenericSweepParamsCB Params;
Params.mCallback = callback;
Params.mUserData = userData;
setupBoxSweepParams(&Params, localBox, localDir, maxDist, &tree, mesh, flags);
if(tree.mNodes)
{
#ifdef SWEEP_AABB_IMPL
if(Params.mEarlyExit)
processStreamRayNoOrder<1, LeafFunction_BoxSweepAnyCB>(tree, &Params);
else
processStreamRayOrdered<1, LeafFunction_BoxSweepClosestCB>(tree, &Params);
#else
if(Params.mEarlyExit)
processStreamNoOrder<LeafFunction_BoxSweepAnyCB>(tree, &Params);
else
processStreamOrdered<LeafFunction_BoxSweepClosestCB>(tree, &Params);
#endif
}
else
doBruteForceTests<LeafFunction_BoxSweepAnyCB, LeafFunction_BoxSweepClosestCB>(mesh->getNbTriangles(), &Params);
}

View File

@@ -0,0 +1,210 @@
// 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.
// This is used by the box-sweep & capsule-sweep code
#if PX_VC
#pragma warning(disable: 4505) // unreferenced local function has been removed
#endif
#include "foundation/PxBasicTemplates.h"
namespace
{
#ifdef SWEEP_AABB_IMPL
struct BoxSweepParams : RayParams
#else
struct BoxSweepParams : OBBTestParams
#endif
{
const IndTri32* PX_RESTRICT mTris32;
const IndTri16* PX_RESTRICT mTris16;
const PxVec3* PX_RESTRICT mVerts;
#ifndef SWEEP_AABB_IMPL
Box mLocalBox;
#endif
PxVec3 mLocalDir_Padded;
RaycastHitInternal mStabbedFace;
PxU32 mBackfaceCulling;
PxU32 mEarlyExit;
PxVec3 mP0, mP1, mP2;
PxVec3 mBestTriNormal;
float mOffset;
PxVec3 mProj;
PxVec3 mDP;
#ifndef SWEEP_AABB_IMPL
PxMat33 mAbsRot; //!< Absolute rotation matrix
#endif
PxMat33 mRModelToBox_Padded; //!< Rotation from model space to obb space
PxVec3 mTModelToBox_Padded; //!< Translation from model space to obb space
PxVec3 mOriginalExtents_Padded;
PxVec3 mOriginalDir_Padded;
PxVec3 mOneOverDir_Padded;
PxVec3 mOneOverOriginalDir;
#ifndef SWEEP_AABB_IMPL
PX_FORCE_INLINE void shrinkOBB(float d)
{
const PxVec3 BoxExtents = mDP + d * mProj;
mTBoxToModel_PaddedAligned = mLocalBox.center + mLocalDir_Padded*d*0.5f;
setupBoxBoxExtentData(this, BoxExtents, &mAbsRot);
}
#endif
};
}
// PT: TODO: check asm again in PhysX version, compare to original (TA34704)
static void prepareSweepData(const Box& box, const PxVec3& dir, float maxDist, BoxSweepParams* PX_RESTRICT params)
{
invertBoxMatrix(params->mRModelToBox_Padded, params->mTModelToBox_Padded, box);
params->mOriginalExtents_Padded = box.extents;
const PxVec3 OriginalDir = params->mRModelToBox_Padded.transform(dir);
params->mOriginalDir_Padded = OriginalDir;
const PxVec3 OneOverOriginalDir(OriginalDir.x!=0.0f ? 1.0f/OriginalDir.x : 0.0f,
OriginalDir.y!=0.0f ? 1.0f/OriginalDir.y : 0.0f,
OriginalDir.z!=0.0f ? 1.0f/OriginalDir.z : 0.0f);
params->mOneOverOriginalDir = OneOverOriginalDir;
params->mOneOverDir_Padded = OneOverOriginalDir / maxDist;
{
const Box& LocalBox = box;
const PxVec3& LocalDir = dir;
params->mLocalDir_Padded = LocalDir;
params->mStabbedFace.mDistance = maxDist;
#ifndef SWEEP_AABB_IMPL
params->mLocalBox = LocalBox; // PT: TODO: check asm for operator=
#endif
PxMat33 boxToModelR;
// Original code:
// OBB::CreateOBB(LocalBox, LocalDir, 0.5f)
{
PxVec3 R1, R2;
{
float dd[3];
dd[0] = fabsf(LocalBox.rot.column0.dot(LocalDir));
dd[1] = fabsf(LocalBox.rot.column1.dot(LocalDir));
dd[2] = fabsf(LocalBox.rot.column2.dot(LocalDir));
float dmax = dd[0];
PxU32 ax0=1;
PxU32 ax1=2;
if(dd[1]>dmax)
{
dmax=dd[1];
ax0=0;
ax1=2;
}
if(dd[2]>dmax)
{
dmax=dd[2];
ax0=0;
ax1=1;
}
if(dd[ax1]<dd[ax0])
PxSwap(ax0, ax1);
R1 = LocalBox.rot[ax0];
R1 -= R1.dot(LocalDir)*LocalDir; // Project to plane whose normal is dir
R1.normalize();
R2 = LocalDir.cross(R1);
}
// Original code:
// mRot = params->mRBoxToModel
boxToModelR.column0 = LocalDir;
boxToModelR.column1 = R1;
boxToModelR.column2 = R2;
// Original code:
// float Offset[3];
// 0.5f comes from the Offset[r]*0.5f, doesn't mean 'd' is 0.5f
params->mProj.x = 0.5f;
params->mProj.y = LocalDir.dot(R1)*0.5f;
params->mProj.z = LocalDir.dot(R2)*0.5f;
// Original code:
//mExtents[r] = Offset[r]*0.5f + fabsf(box.mRot[0]|R)*box.mExtents.x + fabsf(box.mRot[1]|R)*box.mExtents.y + fabsf(box.mRot[2]|R)*box.mExtents.z;
// => we store the first part of the computation, minus 'Offset[r]*0.5f'
for(PxU32 r=0;r<3;r++)
{
const PxVec3& R = boxToModelR[r];
params->mDP[r] = fabsf(LocalBox.rot.column0.dot(R)*LocalBox.extents.x)
+ fabsf(LocalBox.rot.column1.dot(R)*LocalBox.extents.y)
+ fabsf(LocalBox.rot.column2.dot(R)*LocalBox.extents.z);
}
// In the original code, both mCenter & mExtents depend on 'd', and thus we will need to recompute these two members.
//
// For mExtents we have:
//
// float Offset[3];
// Offset[0] = d;
// Offset[1] = d*(dir|R1);
// Offset[2] = d*(dir|R2);
//
// mExtents[r] = Offset[r]*0.5f + fabsf(box.mRot[0]|R)*box.mExtents.x + fabsf(box.mRot[1]|R)*box.mExtents.y + fabsf(box.mRot[2]|R)*box.mExtents.z;
// <=> mExtents[r] = Offset[r]*0.5f + Params.mDP[r]; We precompute the second part that doesn't depend on d, stored in mDP
// <=> mExtents[r] = Params.mProj[r]*d + Params.mDP[r]; We extract d from the first part, store what is left in mProj
//
// Thus in shrinkOBB the code needed to update the extents is just:
// mBoxExtents = mDP + d * mProj;
//
// For mCenter we have:
//
// mCenter = box.mCenter + dir*d*0.5f;
//
// So we simply use this formula directly, with the new d. Result is stored in 'mTBoxToModel'
/*
PX_FORCE_INLINE void shrinkOBB(float d)
{
mBoxExtents = mDP + d * mProj;
mTBoxToModel = mLocalBox.mCenter + mLocalDir*d*0.5f;
*/
}
// This one is for culling tris, unrelated to CreateOBB
params->mOffset = params->mDP.x + LocalBox.center.dot(LocalDir);
#ifndef SWEEP_AABB_IMPL
setupBoxBoxRotationData(params, &params->mAbsRot, &boxToModelR);
params->shrinkOBB(maxDist);
#endif
}
}

View File

@@ -0,0 +1,171 @@
// 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 "GuBV4.h"
#include "GuSweepSphereTriangle.h"
using namespace physx;
using namespace Gu;
#include "foundation/PxVecMath.h"
using namespace aos;
#include "GuInternal.h"
#include "GuBV4_BoxOverlap_Internal.h"
#include "GuBV4_BoxSweep_Params.h"
namespace
{
struct CapsuleSweepParams : BoxSweepParams
{
Capsule mLocalCapsule;
PxVec3 mCapsuleCenter;
PxVec3 mExtrusionDir;
float mBestAlignmentValue;
float mBestDistance;
float mMaxDist;
// PX_FORCE_INLINE float getReportDistance() const { return mStabbedFace.mDistance; }
PX_FORCE_INLINE float getReportDistance() const { return mBestDistance; }
};
}
#include "GuBV4_CapsuleSweep_Internal.h"
#include "GuBV4_BoxBoxOverlapTest.h"
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs.h"
#endif
#include "GuBV4_ProcessStreamOrdered_OBBOBB.h"
#include "GuBV4_ProcessStreamNoOrder_OBBOBB.h"
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs_SwizzledNoOrder.h"
#include "GuBV4_Slabs_SwizzledOrdered.h"
#endif
#define GU_BV4_PROCESS_STREAM_NO_ORDER
#define GU_BV4_PROCESS_STREAM_ORDERED
#include "GuBV4_Internal.h"
PxIntBool BV4_CapsuleSweepSingle(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
CapsuleSweepParams Params;
setupCapsuleParams(&Params, capsule, dir, maxDist, &tree, mesh, flags);
if(tree.mNodes)
{
if(Params.mEarlyExit)
processStreamNoOrder<LeafFunction_CapsuleSweepAny>(tree, &Params);
else
processStreamOrdered<LeafFunction_CapsuleSweepClosest>(tree, &Params);
}
else
doBruteForceTests<LeafFunction_CapsuleSweepAny, LeafFunction_CapsuleSweepClosest>(mesh->getNbTriangles(), &Params);
return computeImpactDataT<ImpactFunctionCapsule>(capsule, dir, hit, &Params, NULL, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0);
}
// PT: capsule sweep callback version - currently not used
namespace
{
struct CapsuleSweepParamsCB : CapsuleSweepParams
{
// PT: these new members are only here to call computeImpactDataT during traversal :(
// PT: TODO: most of them may not be needed
// PT: TODO: for example mCapsuleCB probably dup of mLocalCapsule
Capsule mCapsuleCB; // Capsule in original space (maybe not local/mesh space)
PxVec3 mDirCB; // Dir in original space (maybe not local/mesh space)
const PxMat44* mWorldm_Aligned;
PxU32 mFlags;
SweepUnlimitedCallback mCallback;
void* mUserData;
bool mNodeSorting;
};
class LeafFunction_CapsuleSweepCB
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(CapsuleSweepParamsCB* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
if(triCapsuleSweep(params, primIndex, params->mNodeSorting))
{
// PT: TODO: in this version we must compute the impact data immediately,
// which is a bad idea in general, but I'm not sure what else I can do.
SweepHit hit;
const bool b = computeImpactDataT<ImpactFunctionCapsule>(params->mCapsuleCB, params->mDirCB, &hit, params, params->mWorldm_Aligned, (params->mFlags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (params->mFlags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0);
PX_ASSERT(b);
PX_UNUSED(b);
reportUnlimitedCallbackHit(params, hit);
}
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB().
void BV4_CapsuleSweepCB(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
CapsuleSweepParamsCB Params;
Params.mCapsuleCB = capsule;
Params.mDirCB = dir;
Params.mWorldm_Aligned = worldm_Aligned;
Params.mFlags = flags;
Params.mCallback = callback;
Params.mUserData = userData;
Params.mMaxDist = maxDist;
Params.mNodeSorting = nodeSorting;
setupCapsuleParams(&Params, capsule, dir, maxDist, &tree, mesh, flags);
PX_ASSERT(!Params.mEarlyExit);
if(tree.mNodes)
{
if(nodeSorting)
processStreamOrdered<LeafFunction_CapsuleSweepCB>(tree, &Params);
else
processStreamNoOrder<LeafFunction_CapsuleSweepCB>(tree, &Params);
}
else
doBruteForceTests<LeafFunction_CapsuleSweepCB, LeafFunction_CapsuleSweepCB>(mesh->getNbTriangles(), &Params);
}

View File

@@ -0,0 +1,111 @@
// 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 "GuBV4.h"
#include "GuSweepSphereTriangle.h"
using namespace physx;
using namespace Gu;
#include "foundation/PxVecMath.h"
using namespace aos;
#include "GuBV4_Common.h"
#include "GuInternal.h"
#define SWEEP_AABB_IMPL
// PT: TODO: refactor structure (TA34704)
namespace
{
struct RayParams
{
BV4_ALIGN16(PxVec3p mCenterOrMinCoeff_PaddedAligned);
BV4_ALIGN16(PxVec3p mExtentsOrMaxCoeff_PaddedAligned);
#ifndef GU_BV4_USE_SLABS
BV4_ALIGN16(PxVec3p mData2_PaddedAligned);
BV4_ALIGN16(PxVec3p mFDir_PaddedAligned);
BV4_ALIGN16(PxVec3p mData_PaddedAligned);
BV4_ALIGN16(PxVec3p mLocalDir_PaddedAligned);
#endif
BV4_ALIGN16(PxVec3p mOrigin_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704)
};
}
#include "GuBV4_BoxSweep_Params.h"
namespace
{
struct CapsuleSweepParams : BoxSweepParams
{
Capsule mLocalCapsule;
PxVec3 mCapsuleCenter;
PxVec3 mExtrusionDir;
float mBestAlignmentValue;
float mBestDistance;
float mMaxDist;
// PX_FORCE_INLINE float getReportDistance() const { return mStabbedFace.mDistance; }
PX_FORCE_INLINE float getReportDistance() const { return mBestDistance; }
};
}
#include "GuBV4_CapsuleSweep_Internal.h"
#include "GuBV4_AABBAABBSweepTest.h"
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs.h"
#endif
#include "GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h"
#include "GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h"
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs_KajiyaNoOrder.h"
#include "GuBV4_Slabs_KajiyaOrdered.h"
#endif
#define GU_BV4_PROCESS_STREAM_RAY_NO_ORDER
#define GU_BV4_PROCESS_STREAM_RAY_ORDERED
#include "GuBV4_Internal.h"
PxIntBool BV4_CapsuleSweepSingleAA(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
CapsuleSweepParams Params;
setupCapsuleParams(&Params, capsule, dir, maxDist, &tree, mesh, flags);
if(tree.mNodes)
{
if(Params.mEarlyExit)
processStreamRayNoOrder<1, LeafFunction_CapsuleSweepAny>(tree, &Params);
else
processStreamRayOrdered<1, LeafFunction_CapsuleSweepClosest>(tree, &Params);
}
else
doBruteForceTests<LeafFunction_CapsuleSweepAny, LeafFunction_CapsuleSweepClosest>(mesh->getNbPrimitives(), &Params);
return computeImpactDataT<ImpactFunctionCapsule>(capsule, dir, hit, &Params, NULL, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0);
}

View File

@@ -0,0 +1,442 @@
// 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_BV4_CAPSULE_SWEEP_INTERNAL_H
#define GU_BV4_CAPSULE_SWEEP_INTERNAL_H
// PT: for capsule-sweeps please refer to %SDKRoot%\InternalDocumentation\GU\Sweep strategies.ppt.
// We use:
// - method 3 if the capsule is axis-aligned (SWEEP_AABB_IMPL is defined)
// - method 2 otherwise (SWEEP_AABB_IMPL is undefined)
// PT: TODO: get rid of that one
static PX_FORCE_INLINE bool sweepSphereVSTriangle( const PxVec3& center, const float radius,
const PxVec3* PX_RESTRICT triVerts, const PxVec3& triUnitNormal,
const PxVec3& unitDir,
float& curT, bool& directHit)
{
float currentDistance;
if(!sweepSphereVSTri(triVerts, triUnitNormal, center, radius, unitDir, currentDistance, directHit, true))
return false;
// PT: using ">" or ">=" is enough to block the CCT or not in the DE5967 visual test. Change to ">=" if a repro is needed.
if(currentDistance > curT + GU_EPSILON_SAME_DISTANCE * PxMax(1.0f, curT))
return false;
curT = currentDistance;
return true;
}
static PX_FORCE_INLINE bool sweepSphereVSQuad( const PxVec3& center, const float radius,
const PxVec3* PX_RESTRICT quadVerts, const PxVec3& quadUnitNormal,
const PxVec3& unitDir,
float& curT)
{
float currentDistance;
if(!sweepSphereVSQuad(quadVerts, quadUnitNormal, center, radius, unitDir, currentDistance))
return false;
// PT: using ">" or ">=" is enough to block the CCT or not in the DE5967 visual test. Change to ">=" if a repro is needed.
if(currentDistance > curT + GU_EPSILON_SAME_DISTANCE * PxMax(1.0f, curT))
return false;
curT = currentDistance;
return true;
}
///////////////////////////////////////////////////////////////////////////////
// PT: TODO: __fastcall removed to make it compile everywhere. Revisit.
static bool /*__fastcall*/ testTri( const CapsuleSweepParams* PX_RESTRICT params, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxVec3& N,
const PxVec3& unitDir, const float capsuleRadius, const float dpc0, float& curT, bool& status)
{
// PT: TODO: check the assembly here (TA34704)
PxVec3 currentTri[3];
// PT: TODO: optimize this copy (TA34704)
currentTri[0] = p0;
currentTri[1] = p1;
currentTri[2] = p2;
// PT: beware, culling is only ok on the sphere I think
if(rejectTriangle(params->mCapsuleCenter, unitDir, curT, capsuleRadius, currentTri, dpc0))
return false;
float magnitude = N.magnitude();
if(magnitude==0.0f)
return false;
PxVec3 triNormal = N / magnitude;
bool DirectHit;
if(sweepSphereVSTriangle(params->mCapsuleCenter, capsuleRadius, currentTri, triNormal, unitDir, curT, DirectHit))
{
status = true;
}
return DirectHit;
}
// PT: TODO: __fastcall removed to make it compile everywhere. Revisit.
static void /*__fastcall*/ testQuad(const CapsuleSweepParams* PX_RESTRICT params, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxVec3& p3, const PxVec3& N,
const PxVec3& unitDir, const float capsuleRadius, const float dpc0, float& curT, bool& status)
{
// PT: TODO: optimize this copy (TA34704)
PxVec3 currentQuad[4];
currentQuad[0] = p0;
currentQuad[1] = p1;
currentQuad[2] = p2;
currentQuad[3] = p3;
// PT: beware, culling is only ok on the sphere I think
if(rejectQuad(params->mCapsuleCenter, unitDir, curT, capsuleRadius, currentQuad, dpc0))
return;
float magnitude = N.magnitude();
if(magnitude==0.0f)
return;
PxVec3 triNormal = N / magnitude;
if(sweepSphereVSQuad(params->mCapsuleCenter, capsuleRadius, currentQuad, triNormal, unitDir, curT))
{
status = true;
}
}
static PX_FORCE_INLINE float Set2(const PxVec3& p0, const PxVec3& n, const PxVec3& p)
{
return (p-p0).dot(n);
}
static PX_FORCE_INLINE bool sweepCapsuleVsTriangle(const CapsuleSweepParams* PX_RESTRICT params, const PxTriangle& triangle, float& t, bool isDoubleSided, PxVec3& normal)
{
const PxVec3& unitDir = params->mLocalDir_Padded;
// Create triangle normal
PxVec3 denormalizedNormal = (triangle.verts[0] - triangle.verts[1]).cross(triangle.verts[0] - triangle.verts[2]);
normal = denormalizedNormal;
// Backface culling
const bool culled = denormalizedNormal.dot(unitDir) > 0.0f;
if(culled)
{
if(!isDoubleSided)
return false;
denormalizedNormal = -denormalizedNormal;
}
const float capsuleRadius = params->mLocalCapsule.radius;
float curT = params->mStabbedFace.mDistance;// + GU_EPSILON_SAME_DISTANCE*20.0f;
const float dpc0 = params->mCapsuleCenter.dot(unitDir);
bool status = false;
// Extrude mesh on the fly
const PxVec3 p0 = triangle.verts[0] - params->mExtrusionDir;
const PxVec3 p1 = triangle.verts[1+culled] - params->mExtrusionDir;
const PxVec3 p2 = triangle.verts[2-culled] - params->mExtrusionDir;
const PxVec3 p0b = triangle.verts[0] + params->mExtrusionDir;
const PxVec3 p1b = triangle.verts[1+culled] + params->mExtrusionDir;
const PxVec3 p2b = triangle.verts[2-culled] + params->mExtrusionDir;
const float extrusionSign = denormalizedNormal.dot(params->mExtrusionDir);
const PxVec3 p2b_p1b = p2b - p1b;
const PxVec3 p0b_p1b = p0b - p1b;
const PxVec3 p2b_p2 = 2.0f * params->mExtrusionDir;
const PxVec3 p1_p1b = -p2b_p2;
const PxVec3 N1 = p2b_p1b.cross(p0b_p1b);
const float dp0 = Set2(p0b, N1, params->mCapsuleCenter);
const PxVec3 N2 = (p2 - p1).cross(p0 - p1);
const float dp1 = -Set2(p0, N2, params->mCapsuleCenter);
bool directHit;
if(extrusionSign >= 0.0f)
directHit = testTri(params, p0b, p1b, p2b, N1, unitDir, capsuleRadius, dpc0, curT, status);
else
directHit = testTri(params, p0, p1, p2, N2, unitDir, capsuleRadius, dpc0, curT, status);
const PxVec3 N3 = p2b_p1b.cross(p1_p1b);
const float dp2 = -Set2(p1, N3, params->mCapsuleCenter);
if(!directHit)
{
const float dp = N3.dot(unitDir);
if(dp*extrusionSign>=0.0f)
testQuad(params, p1, p1b, p2, p2b, N3, unitDir, capsuleRadius, dpc0, curT, status);
}
const PxVec3 N5 = p2b_p2.cross(p0 - p2);
const float dp3 = -Set2(p0, N5, params->mCapsuleCenter);
if(!directHit)
{
const float dp = N5.dot(unitDir);
if(dp*extrusionSign>=0.0f)
testQuad(params, p2, p2b, p0, p0b, N5, unitDir, capsuleRadius, dpc0, curT, status);
}
const PxVec3 N7 = p1_p1b.cross(p0b_p1b);
const float dp4 = -Set2(p0b, N7, params->mCapsuleCenter);
if(!directHit)
{
const float dp = N7.dot(unitDir);
if(dp*extrusionSign>=0.0f)
testQuad(params, p0, p0b, p1, p1b, N7, unitDir, capsuleRadius, dpc0, curT, status);
}
if(1)
{
bool originInside = true;
if(extrusionSign<0.0f)
{
if(dp0<0.0f || dp1<0.0f || dp2<0.0f || dp3<0.0f || dp4<0.0f)
originInside = false;
}
else
{
if(dp0>0.0f || dp1>0.0f || dp2>0.0f || dp3>0.0f || dp4>0.0f)
originInside = false;
}
if(originInside)
{
t = 0.0f;
return true;
}
}
if(!status)
return false; // We didn't touch any triangle
t = curT;
return true;
}
// PT: TODO: __fastcall removed to make it compile everywhere. Revisit.
static bool /*__fastcall*/ triCapsuleSweep(CapsuleSweepParams* PX_RESTRICT params, PxU32 primIndex, bool nodeSorting=true)
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
const PxVec3& p0 = params->mVerts[VRef0];
const PxVec3& p1 = params->mVerts[VRef1];
const PxVec3& p2 = params->mVerts[VRef2];
const PxTriangle Tri(p0, p1, p2); // PT: TODO: check calls to empty ctor/dtor here (TA34704)
const bool isDoubleSided = params->mBackfaceCulling==0;
float dist;
PxVec3 denormalizedNormal;
if(sweepCapsuleVsTriangle(params, Tri, dist, isDoubleSided, denormalizedNormal))
{
denormalizedNormal.normalize();
const PxReal alignmentValue = computeAlignmentValue(denormalizedNormal, params->mLocalDir_Padded);
if(keepTriangle(dist, alignmentValue, params->mBestDistance, params->mBestAlignmentValue, params->mMaxDist))
{
params->mStabbedFace.mDistance = dist;
params->mStabbedFace.mTriangleID = primIndex;
params->mP0 = p0;
params->mP1 = p1;
params->mP2 = p2;
params->mBestDistance = PxMin(params->mBestDistance, dist); // exact lower bound
params->mBestAlignmentValue = alignmentValue;
params->mBestTriNormal = denormalizedNormal;
if(nodeSorting)
{
#ifdef SWEEP_AABB_IMPL
#ifndef GU_BV4_USE_SLABS
//setupRayData(params, dist, params->mOrigin_Padded, params->mLocalDir_PaddedAligned);
setupRayData(params, params->mBestDistance, params->mOrigin_Padded, params->mLocalDir_PaddedAligned);
#endif
#else
//params->shrinkOBB(dist);
params->shrinkOBB(params->mBestDistance);
#endif
}
return true;
}
///
else if(keepTriangleBasic(dist, params->mBestDistance, params->mMaxDist))
{
params->mStabbedFace.mDistance = dist;
params->mBestDistance = PxMin(params->mBestDistance, dist); // exact lower bound
}
///
}
return false;
}
#include "GuDistanceSegmentTriangle.h"
namespace
{
class LeafFunction_CapsuleSweepClosest
{
public:
static PX_FORCE_INLINE void doLeafTest(CapsuleSweepParams* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
triCapsuleSweep(params, primIndex);
primIndex++;
}while(nbToGo--);
}
};
class LeafFunction_CapsuleSweepAny
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(CapsuleSweepParams* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
if(triCapsuleSweep(params, primIndex))
return 1;
primIndex++;
}while(nbToGo--);
return 0;
}
};
class ImpactFunctionCapsule
{
public:
static PX_FORCE_INLINE void computeImpact(PxVec3& impactPos, PxVec3& impactNormal, const Capsule& capsule, const PxVec3& dir, const PxReal t, const PxTrianglePadded& triangle)
{
const PxVec3 delta = dir * t;
const PxVec3p P0 = capsule.p0 + delta;
const PxVec3p P1 = capsule.p1 + delta;
Vec3V pointOnSeg, pointOnTri;
distanceSegmentTriangleSquared(
// PT: we use PxVec3p so it is safe to V4LoadU P0 and P1
V3LoadU_SafeReadW(P0), V3LoadU_SafeReadW(P1),
// PT: we use PxTrianglePadded so it is safe to V4LoadU the triangle vertices
V3LoadU_SafeReadW(triangle.verts[0]), V3LoadU_SafeReadW(triangle.verts[1]), V3LoadU_SafeReadW(triangle.verts[2]),
pointOnSeg, pointOnTri);
PxVec3 localImpactPos, tmp;
V3StoreU(pointOnTri, localImpactPos);
V3StoreU(pointOnSeg, tmp);
// PT: TODO: refactor with computeSphereTriImpactData (TA34704)
PxVec3 localImpactNormal = tmp - localImpactPos;
const float M = localImpactNormal.magnitude();
if(M<1e-3f)
{
localImpactNormal = (triangle.verts[0] - triangle.verts[1]).cross(triangle.verts[0] - triangle.verts[2]);
localImpactNormal.normalize();
}
else
localImpactNormal /= M;
impactPos = localImpactPos;
impactNormal = localImpactNormal;
}
};
}
static void computeBoxAroundCapsule(const Capsule& capsule, Box& box, PxVec3& extrusionDir)
{
// Box center = center of the two capsule's endpoints
box.center = capsule.computeCenter();
extrusionDir = (capsule.p0 - capsule.p1)*0.5f;
const PxF32 d = extrusionDir.magnitude();
// Box extents
box.extents.x = capsule.radius + d;
box.extents.y = capsule.radius;
box.extents.z = capsule.radius;
// Box orientation
if(d==0.0f)
{
box.rot = PxMat33(PxIdentity);
}
else
{
PxVec3 dir, right, up;
PxComputeBasisVectors(capsule.p0, capsule.p1, dir, right, up);
box.setAxes(dir, right, up);
}
}
template<class ParamsT>
static PX_FORCE_INLINE void setupCapsuleParams(ParamsT* PX_RESTRICT params, const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree* PX_RESTRICT tree, const SourceMesh* PX_RESTRICT mesh, PxU32 flags)
{
params->mStabbedFace.mTriangleID = PX_INVALID_U32;
params->mBestAlignmentValue = 2.0f;
params->mBestDistance = maxDist + GU_EPSILON_SAME_DISTANCE;
params->mMaxDist = maxDist;
setupParamsFlags(params, flags);
setupMeshPointersAndQuantizedCoeffs(params, mesh, tree);
params->mLocalCapsule = capsule;
Box localBox;
computeBoxAroundCapsule(capsule, localBox, params->mExtrusionDir);
params->mCapsuleCenter = localBox.center;
const PxVec3& localDir = dir;
#ifdef SWEEP_AABB_IMPL
const PxVec3& localP0 = params->mLocalCapsule.p0;
const PxVec3& localP1 = params->mLocalCapsule.p1;
const PxVec3 sweepOrigin = (localP0+localP1)*0.5f;
const PxVec3 sweepExtents = PxVec3(params->mLocalCapsule.radius) + (localP0-localP1).abs()*0.5f;
#ifndef GU_BV4_USE_SLABS
params->mLocalDir_PaddedAligned = localDir;
#endif
params->mOrigin_Padded = sweepOrigin;
const Box aabb(sweepOrigin, sweepExtents, PxMat33(PxIdentity));
prepareSweepData(aabb, localDir, maxDist, params); // PT: TODO: optimize this call for idt rotation (TA34704)
#ifndef GU_BV4_USE_SLABS
setupRayData(params, maxDist, sweepOrigin, localDir);
#endif
#else
prepareSweepData(localBox, localDir, maxDist, params);
#endif
}
#endif // GU_BV4_CAPSULE_SWEEP_INTERNAL_H

View File

@@ -0,0 +1,457 @@
// 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_BV4_COMMON_H
#define GU_BV4_COMMON_H
#include "foundation/PxMat44.h"
#include "geometry/PxTriangle.h"
#include "GuBox.h"
#include "GuSphere.h"
#include "GuCapsule.h"
#include "GuBV4.h"
#define BV4_ALIGN16(x) PX_ALIGN_PREFIX(16) x PX_ALIGN_SUFFIX(16)
namespace physx
{
namespace Gu
{
enum QueryModifierFlag
{
QUERY_MODIFIER_ANY_HIT = (1<<0),
QUERY_MODIFIER_DOUBLE_SIDED = (1<<1),
QUERY_MODIFIER_MESH_BOTH_SIDES = (1<<2)
};
template<class ParamsT>
PX_FORCE_INLINE void setupParamsFlags(ParamsT* PX_RESTRICT params, PxU32 flags)
{
params->mBackfaceCulling = (flags & (QUERY_MODIFIER_DOUBLE_SIDED|QUERY_MODIFIER_MESH_BOTH_SIDES)) ? 0 : 1u;
params->mEarlyExit = flags & QUERY_MODIFIER_ANY_HIT;
}
enum HitCode
{
HIT_NONE = 0, //!< No hit
HIT_CONTINUE = 1, //!< Hit found, but keep looking for closer one
HIT_EXIT = 2 //!< Hit found, you can early-exit (raycast any)
};
class RaycastHitInternal : public physx::PxUserAllocated
{
public:
PX_FORCE_INLINE RaycastHitInternal() {}
PX_FORCE_INLINE ~RaycastHitInternal() {}
float mDistance;
PxU32 mTriangleID;
};
class SweepHit : public physx::PxUserAllocated
{
public:
PX_FORCE_INLINE SweepHit() {}
PX_FORCE_INLINE ~SweepHit() {}
PxU32 mTriangleID; //!< Index of touched face
float mDistance; //!< Impact distance
PxVec3 mPos;
PxVec3 mNormal;
};
typedef HitCode (*MeshRayCallback) (void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, float dist, float u, float v);
typedef bool (*MeshOverlapCallback) (void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* vertexIndices);
typedef bool (*TetMeshOverlapCallback) (void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxVec3& p3, PxU32 tetIndex, const PxU32* vertexIndices);
typedef bool (*MeshSweepCallback) (void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, /*const PxU32* vertexIndices,*/ float& dist);
typedef bool (*SweepUnlimitedCallback) (void* userData, const SweepHit& hit);
template<class ParamsT>
PX_FORCE_INLINE void reportUnlimitedCallbackHit(ParamsT* PX_RESTRICT params, const SweepHit& hit)
{
// PT: we can't reuse the MeshSweepCallback here since it's designed for doing the sweep test inside the callback
// (in the user's code) rather than inside the traversal code. So we use the SweepUnlimitedCallback instead to
// report the already fully computed hit to users.
// PT: TODO: this may not be very efficient, since computing the full hit is expensive. If we use this codepath
// to implement the Epic Tweak, the resulting code will not be optimal.
(params->mCallback)(params->mUserData, hit);
// PT: the existing traversal code already shrunk the ray. For real "sweep all" calls we must undo that by reseting the max dist.
// (params->mStabbedFace.mDistance is used in computeImpactDataX code, so we need it before that point - we can't simply avoid
// modifying this value before this point).
if(!params->mNodeSorting)
params->mStabbedFace.mDistance = params->mMaxDist;
}
PX_FORCE_INLINE void invertPRMatrix(PxMat44* PX_RESTRICT dest, const PxMat44* PX_RESTRICT src)
{
const float m30 = src->column3.x;
const float m31 = src->column3.y;
const float m32 = src->column3.z;
const float m00 = src->column0.x;
const float m01 = src->column0.y;
const float m02 = src->column0.z;
dest->column0.x = m00;
dest->column1.x = m01;
dest->column2.x = m02;
dest->column3.x = -(m30*m00 + m31*m01 + m32*m02);
const float m10 = src->column1.x;
const float m11 = src->column1.y;
const float m12 = src->column1.z;
dest->column0.y = m10;
dest->column1.y = m11;
dest->column2.y = m12;
dest->column3.y = -(m30*m10 + m31*m11 + m32*m12);
const float m20 = src->column2.x;
const float m21 = src->column2.y;
const float m22 = src->column2.z;
dest->column0.z = m20;
dest->column1.z = m21;
dest->column2.z = m22;
dest->column3.z = -(m30*m20 + m31*m21 + m32*m22);
dest->column0.w = 0.0f;
dest->column1.w = 0.0f;
dest->column2.w = 0.0f;
dest->column3.w = 1.0f;
}
PX_FORCE_INLINE void invertBoxMatrix(PxMat33& m, PxVec3& t, const Gu::Box& box)
{
const float m30 = box.center.x;
const float m31 = box.center.y;
const float m32 = box.center.z;
const float m00 = box.rot.column0.x;
const float m01 = box.rot.column0.y;
const float m02 = box.rot.column0.z;
m.column0.x = m00;
m.column1.x = m01;
m.column2.x = m02;
t.x = -(m30*m00 + m31*m01 + m32*m02);
const float m10 = box.rot.column1.x;
const float m11 = box.rot.column1.y;
const float m12 = box.rot.column1.z;
m.column0.y = m10;
m.column1.y = m11;
m.column2.y = m12;
t.y = -(m30*m10 + m31*m11 + m32*m12);
const float m20 = box.rot.column2.x;
const float m21 = box.rot.column2.y;
const float m22 = box.rot.column2.z;
m.column0.z = m20;
m.column1.z = m21;
m.column2.z = m22;
t.z = -(m30*m20 + m31*m21 + m32*m22);
}
#ifdef GU_BV4_USE_SLABS
// PT: this class moved here to make things compile with pedantic compilers.
// PT: now duplicated because not easy to do otherwise
struct BVDataSwizzledQ : public physx::PxUserAllocated
{
struct Data
{
PxI16 mMin; //!< Quantized min
PxI16 mMax; //!< Quantized max
};
Data mX[4];
Data mY[4];
Data mZ[4];
PxU32 mData[4];
PX_FORCE_INLINE PxU32 isLeaf(PxU32 i) const { return mData[i]&1; }
PX_FORCE_INLINE PxU32 getPrimitive(PxU32 i) const { return mData[i]>>1; }
PX_FORCE_INLINE PxU32 getChildOffset(PxU32 i) const { return mData[i]>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT; }
PX_FORCE_INLINE PxU32 getChildType(PxU32 i) const { return (mData[i]>>1)&3; }
PX_FORCE_INLINE PxU32 getChildData(PxU32 i) const { return mData[i]; }
PX_FORCE_INLINE PxU32 decodePNSNoShift(PxU32 i) const { return mData[i]; }
};
struct BVDataSwizzledNQ : public physx::PxUserAllocated
{
float mMinX[4];
float mMinY[4];
float mMinZ[4];
float mMaxX[4];
float mMaxY[4];
float mMaxZ[4];
PxU32 mData[4];
PX_FORCE_INLINE PxU32 isLeaf(PxU32 i) const { return mData[i]&1; }
PX_FORCE_INLINE PxU32 getPrimitive(PxU32 i) const { return mData[i]>>1; }
PX_FORCE_INLINE PxU32 getChildOffset(PxU32 i) const { return mData[i]>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT; }
PX_FORCE_INLINE PxU32 getChildType(PxU32 i) const { return (mData[i]>>1)&3; }
PX_FORCE_INLINE PxU32 getChildData(PxU32 i) const { return mData[i]; }
PX_FORCE_INLINE PxU32 decodePNSNoShift(PxU32 i) const { return mData[i]; }
};
#else
#define SSE_CONST4(name, val) static const __declspec(align(16)) PxU32 name[4] = { (val), (val), (val), (val) }
#define SSE_CONST(name) *(const __m128i *)&name
#define SSE_CONSTF(name) *(const __m128 *)&name
#endif
PX_FORCE_INLINE PxU32 getNbPrimitives(PxU32& primIndex)
{
PxU32 NbToGo = (primIndex & 15)-1;
primIndex>>=4;
return NbToGo;
}
template<class ParamsT>
PX_FORCE_INLINE void setupMeshPointersAndQuantizedCoeffs(ParamsT* PX_RESTRICT params, const SourceMesh* PX_RESTRICT mesh, const BV4Tree* PX_RESTRICT tree)
{
using namespace physx::aos;
params->mTris32 = mesh->getTris32();
params->mTris16 = mesh->getTris16();
params->mVerts = mesh->getVerts();
V4StoreA_Safe(V4LoadU_Safe(&tree->mCenterOrMinCoeff.x), &params->mCenterOrMinCoeff_PaddedAligned.x);
V4StoreA_Safe(V4LoadU_Safe(&tree->mExtentsOrMaxCoeff.x), &params->mExtentsOrMaxCoeff_PaddedAligned.x);
}
template<class ParamsT>
PX_FORCE_INLINE void setupMeshPointersAndQuantizedCoeffs(ParamsT* PX_RESTRICT params, const TetrahedronSourceMesh* PX_RESTRICT mesh, const BV4Tree* PX_RESTRICT tree)
{
params->mTets32 = mesh->getTetrahedrons32();
params->mTets16 = mesh->getTetrahedrons16();
params->mVerts = mesh->getVerts();
V4StoreA_Safe(V4LoadU_Safe(&tree->mCenterOrMinCoeff.x), &params->mCenterOrMinCoeff_PaddedAligned.x);
V4StoreA_Safe(V4LoadU_Safe(&tree->mExtentsOrMaxCoeff.x), &params->mExtentsOrMaxCoeff_PaddedAligned.x);
}
PX_FORCE_INLINE void rotateBox(Gu::Box& dst, const PxMat44& m, const Gu::Box& src)
{
// The extents remain constant
dst.extents = src.extents;
// The center gets x-formed
dst.center = m.transform(src.center);
// Combine rotations
// PT: TODO: revisit.. this is awkward... grab 3x3 part of 4x4 matrix (TA34704)
const PxMat33 tmp( PxVec3(m.column0.x, m.column0.y, m.column0.z),
PxVec3(m.column1.x, m.column1.y, m.column1.z),
PxVec3(m.column2.x, m.column2.y, m.column2.z));
dst.rot = tmp * src.rot;
}
PX_FORCE_INLINE PxVec3 inverseRotate(const PxMat44* PX_RESTRICT src, const PxVec3& p)
{
const float m00 = src->column0.x;
const float m01 = src->column0.y;
const float m02 = src->column0.z;
const float m10 = src->column1.x;
const float m11 = src->column1.y;
const float m12 = src->column1.z;
const float m20 = src->column2.x;
const float m21 = src->column2.y;
const float m22 = src->column2.z;
return PxVec3( m00*p.x + m01*p.y + m02*p.z,
m10*p.x + m11*p.y + m12*p.z,
m20*p.x + m21*p.y + m22*p.z);
}
PX_FORCE_INLINE PxVec3 inverseTransform(const PxMat44* PX_RESTRICT src, const PxVec3& p)
{
const float m30 = src->column3.x;
const float m31 = src->column3.y;
const float m32 = src->column3.z;
const float m00 = src->column0.x;
const float m01 = src->column0.y;
const float m02 = src->column0.z;
const float m10 = src->column1.x;
const float m11 = src->column1.y;
const float m12 = src->column1.z;
const float m20 = src->column2.x;
const float m21 = src->column2.y;
const float m22 = src->column2.z;
return PxVec3( m00*p.x + m01*p.y + m02*p.z -(m30*m00 + m31*m01 + m32*m02),
m10*p.x + m11*p.y + m12*p.z -(m30*m10 + m31*m11 + m32*m12),
m20*p.x + m21*p.y + m22*p.z -(m30*m20 + m31*m21 + m32*m22));
}
PX_FORCE_INLINE void computeLocalRay(PxVec3& localDir, PxVec3& localOrigin, const PxVec3& dir, const PxVec3& origin, const PxMat44* PX_RESTRICT worldm_Aligned)
{
if(worldm_Aligned)
{
localDir = inverseRotate(worldm_Aligned, dir);
localOrigin = inverseTransform(worldm_Aligned, origin);
}
else
{
localDir = dir;
localOrigin = origin;
}
}
PX_FORCE_INLINE void computeLocalSphere(float& radius2, PxVec3& local_center, const Sphere& sphere, const PxMat44* PX_RESTRICT worldm_Aligned)
{
radius2 = sphere.radius * sphere.radius;
if(worldm_Aligned)
{
local_center = inverseTransform(worldm_Aligned, sphere.center);
}
else
{
local_center = sphere.center;
}
}
PX_FORCE_INLINE void computeLocalCapsule(Capsule& localCapsule, const Capsule& capsule, const PxMat44* PX_RESTRICT worldm_Aligned)
{
localCapsule.radius = capsule.radius;
if(worldm_Aligned)
{
localCapsule.p0 = inverseTransform(worldm_Aligned, capsule.p0);
localCapsule.p1 = inverseTransform(worldm_Aligned, capsule.p1);
}
else
{
localCapsule.p0 = capsule.p0;
localCapsule.p1 = capsule.p1;
}
}
PX_FORCE_INLINE void computeLocalBox(Gu::Box& dst, const Gu::Box& src, const PxMat44* PX_RESTRICT worldm_Aligned)
{
if(worldm_Aligned)
{
PxMat44 invWorldM;
invertPRMatrix(&invWorldM, worldm_Aligned);
rotateBox(dst, invWorldM, src);
}
else
{
dst = src; // PT: TODO: check asm for operator= (TA34704)
}
}
template<class ImpactFunctionT, class ShapeT, class ParamsT>
static PX_FORCE_INLINE bool computeImpactDataT(const ShapeT& shape, const PxVec3& dir, SweepHit* PX_RESTRICT hit, const ParamsT* PX_RESTRICT params, const PxMat44* PX_RESTRICT worldm, bool isDoubleSided, bool meshBothSides)
{
if(params->mStabbedFace.mTriangleID==PX_INVALID_U32)
return false; // We didn't touch any triangle
if(hit)
{
const float t = params->getReportDistance();
hit->mTriangleID = params->mStabbedFace.mTriangleID;
hit->mDistance = t;
if(t==0.0f)
{
hit->mPos = PxVec3(0.0f);
hit->mNormal = -dir;
}
else
{
// PT: TODO: we shouldn't compute impact in world space, and in fact moving this to local space is necessary if we want to reuse this for box-sweeps (TA34704)
PxTrianglePadded WP;
if(worldm)
{
WP.verts[0] = worldm->transform(params->mP0);
WP.verts[1] = worldm->transform(params->mP1);
WP.verts[2] = worldm->transform(params->mP2);
}
else
{
WP.verts[0] = params->mP0;
WP.verts[1] = params->mP1;
WP.verts[2] = params->mP2;
}
PxVec3 impactNormal;
ImpactFunctionT::computeImpact(hit->mPos, impactNormal, shape, dir, t, WP);
// PT: by design, returned normal is opposed to the sweep direction.
if(shouldFlipNormal(impactNormal, meshBothSides, isDoubleSided, params->mBestTriNormal, dir))
impactNormal = -impactNormal;
hit->mNormal = impactNormal;
}
}
return true;
}
// PT: we don't create a structure for small meshes with just a few triangles. We use brute-force tests on these.
template<class LeafFunction_AnyT, class LeafFunction_ClosestT, class ParamsT>
void doBruteForceTests(PxU32 nbTris, ParamsT* PX_RESTRICT params)
{
PX_ASSERT(nbTris<16);
if(params->mEarlyExit)
LeafFunction_AnyT::doLeafTest(params, nbTris);
else
LeafFunction_ClosestT::doLeafTest(params, nbTris);
}
#ifndef GU_BV4_USE_SLABS
template<class ParamsT>
PX_FORCE_INLINE void setupRayData(ParamsT* PX_RESTRICT params, float max_dist, const PxVec3& origin, const PxVec3& dir)
{
const float Half = 0.5f*max_dist;
const FloatV HalfV = FLoad(Half);
const Vec4V DataV = V4Scale(V4LoadU(&dir.x), HalfV);
const Vec4V Data2V = V4Add(V4LoadU(&origin.x), DataV);
const Vec4V FDirV = V4Abs(DataV);
V4StoreA_Safe(DataV, &params->mData_PaddedAligned.x);
V4StoreA_Safe(Data2V, &params->mData2_PaddedAligned.x);
V4StoreA_Safe(FDirV, &params->mFDir_PaddedAligned.x);
}
#endif
}
}
#endif // GU_BV4_COMMON_H

View File

@@ -0,0 +1,298 @@
// 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_BV4_INTERNAL_H
#define GU_BV4_INTERNAL_H
#include "foundation/PxFPU.h"
// PT: the general structure is that there is a root "process stream" function which is the entry point for the query.
// It then calls "process node" functions for each traversed node, except for the Slabs-based raycast versions that deal
// with 4 nodes at a time within the "process stream" function itself. When a leaf is found, "doLeafTest" functors
// passed to the "process stream" entry point are called.
#ifdef GU_BV4_USE_SLABS
// PT: Linux tries to compile templates even when they're not used so I had to wrap them all with defines to avoid build errors. Blame the only platform that does this.
#ifdef GU_BV4_PROCESS_STREAM_NO_ORDER
template<class LeafTestT, class ParamsT>
PX_FORCE_INLINE PxIntBool processStreamNoOrder(const BV4Tree& tree, ParamsT* PX_RESTRICT params)
{
if(tree.mQuantized)
return BV4_ProcessStreamSwizzledNoOrderQ<LeafTestT, ParamsT>(reinterpret_cast<const BVDataPackedQ*>(tree.mNodes), tree.mInitData, params);
else
return BV4_ProcessStreamSwizzledNoOrderNQ<LeafTestT, ParamsT>(reinterpret_cast<const BVDataPackedNQ*>(tree.mNodes), tree.mInitData, params);
}
#endif
#ifdef GU_BV4_PROCESS_STREAM_ORDERED
template<class LeafTestT, class ParamsT>
PX_FORCE_INLINE void processStreamOrdered(const BV4Tree& tree, ParamsT* PX_RESTRICT params)
{
if(tree.mQuantized)
BV4_ProcessStreamSwizzledOrderedQ<LeafTestT, ParamsT>(reinterpret_cast<const BVDataPackedQ*>(tree.mNodes), tree.mInitData, params);
else
BV4_ProcessStreamSwizzledOrderedNQ<LeafTestT, ParamsT>(reinterpret_cast<const BVDataPackedNQ*>(tree.mNodes), tree.mInitData, params);
}
#endif
#ifdef GU_BV4_PROCESS_STREAM_RAY_NO_ORDER
template<int inflateT, class LeafTestT, class ParamsT>
PX_FORCE_INLINE PxIntBool processStreamRayNoOrder(const BV4Tree& tree, ParamsT* PX_RESTRICT params)
{
if(tree.mQuantized)
return BV4_ProcessStreamKajiyaNoOrderQ<inflateT, LeafTestT, ParamsT>(reinterpret_cast<const BVDataPackedQ*>(tree.mNodes), tree.mInitData, params);
else
return BV4_ProcessStreamKajiyaNoOrderNQ<inflateT, LeafTestT, ParamsT>(reinterpret_cast<const BVDataPackedNQ*>(tree.mNodes), tree.mInitData, params);
}
#endif
#ifdef GU_BV4_PROCESS_STREAM_RAY_ORDERED
template<int inflateT, class LeafTestT, class ParamsT>
PX_FORCE_INLINE void processStreamRayOrdered(const BV4Tree& tree, ParamsT* PX_RESTRICT params)
{
if(tree.mQuantized)
BV4_ProcessStreamKajiyaOrderedQ<inflateT, LeafTestT, ParamsT>(reinterpret_cast<const BVDataPackedQ*>(tree.mNodes), tree.mInitData, params);
else
BV4_ProcessStreamKajiyaOrderedNQ<inflateT, LeafTestT, ParamsT>(reinterpret_cast<const BVDataPackedNQ*>(tree.mNodes), tree.mInitData, params);
}
#endif
#else
#define processStreamNoOrder BV4_ProcessStreamNoOrder
#define processStreamOrdered BV4_ProcessStreamOrdered2
#define processStreamRayNoOrder(a, b) BV4_ProcessStreamNoOrder<b>
#define processStreamRayOrdered(a, b) BV4_ProcessStreamOrdered2<b>
#endif
#ifndef GU_BV4_USE_SLABS
#ifdef GU_BV4_PRECOMPUTED_NODE_SORT
// PT: see http://www.codercorner.com/blog/?p=734
// PT: TODO: refactor with dup in bucket pruner (TA34704)
PX_FORCE_INLINE PxU32 computeDirMask(const PxVec3& dir)
{
// XYZ
// ---
// --+
// -+-
// -++
// +--
// +-+
// ++-
// +++
const PxU32 X = PX_IR(dir.x)>>31;
const PxU32 Y = PX_IR(dir.y)>>31;
const PxU32 Z = PX_IR(dir.z)>>31;
const PxU32 bitIndex = Z|(Y<<1)|(X<<2);
return 1u<<bitIndex;
}
// 0 0 0 PP PN NP NN 0 1 2 3
// 0 0 1 PP PN NN NP 0 1 3 2
// 0 1 0 PN PP NP NN 1 0 2 3
// 0 1 1 PN PP NN NP 1 0 3 2
// 1 0 0 NP NN PP PN 2 3 0 1
// 1 0 1 NN NP PP PN 3 2 0 1
// 1 1 0 NP NN PN PP 2 3 1 0
// 1 1 1 NN NP PN PP 3 2 1 0
static const PxU8 order[] = {
0,1,2,3,
0,1,3,2,
1,0,2,3,
1,0,3,2,
2,3,0,1,
3,2,0,1,
2,3,1,0,
3,2,1,0,
};
PX_FORCE_INLINE PxU32 decodePNS(const BVDataPacked* PX_RESTRICT node, const PxU32 dirMask)
{
const PxU32 bit0 = (node[0].decodePNSNoShift() & dirMask) ? 1u : 0;
const PxU32 bit1 = (node[1].decodePNSNoShift() & dirMask) ? 1u : 0;
const PxU32 bit2 = (node[2].decodePNSNoShift() & dirMask) ? 1u : 0; //### potentially reads past the end of the stream here!
return bit2|(bit1<<1)|(bit0<<2);
}
#endif // GU_BV4_PRECOMPUTED_NODE_SORT
#define PNS_BLOCK(i, a, b, c, d) \
case i: \
{ \
if(code & (1<<a)) { stack[nb++] = node[a].getChildData(); } \
if(code & (1<<b)) { stack[nb++] = node[b].getChildData(); } \
if(code & (1<<c)) { stack[nb++] = node[c].getChildData(); } \
if(code & (1<<d)) { stack[nb++] = node[d].getChildData(); } \
}break;
#define PNS_BLOCK1(i, a, b, c, d) \
case i: \
{ \
stack[nb] = node[a].getChildData(); nb += (code & (1<<a))?1:0; \
stack[nb] = node[b].getChildData(); nb += (code & (1<<b))?1:0; \
stack[nb] = node[c].getChildData(); nb += (code & (1<<c))?1:0; \
stack[nb] = node[d].getChildData(); nb += (code & (1<<d))?1:0; \
}break;
#define PNS_BLOCK2(a, b, c, d) { \
if(code & (1<<a)) { stack[nb++] = node[a].getChildData(); } \
if(code & (1<<b)) { stack[nb++] = node[b].getChildData(); } \
if(code & (1<<c)) { stack[nb++] = node[c].getChildData(); } \
if(code & (1<<d)) { stack[nb++] = node[d].getChildData(); } } \
template<class LeafTestT, class ParamsT>
static PxIntBool BV4_ProcessStreamNoOrder(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPacked* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const PxU32 nodeType = getChildType(childData);
if(nodeType>1 && BV4_ProcessNodeNoOrder<LeafTestT, 3>(stack, nb, node, params))
return 1;
if(nodeType>0 && BV4_ProcessNodeNoOrder<LeafTestT, 2>(stack, nb, node, params))
return 1;
if(BV4_ProcessNodeNoOrder<LeafTestT, 1>(stack, nb, node, params))
return 1;
if(BV4_ProcessNodeNoOrder<LeafTestT, 0>(stack, nb, node, params))
return 1;
}while(nb);
return 0;
}
template<class LeafTestT, class ParamsT>
static void BV4_ProcessStreamOrdered(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPacked* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
const PxU32 dirMask = computeDirMask(params->mLocalDir)<<3;
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const PxU8* PX_RESTRICT ord = order + decodePNS(node, dirMask)*4;
const PxU32 limit = 2 + getChildType(childData);
BV4_ProcessNodeOrdered<LeafTestT>(stack, nb, node, params, ord[0], limit);
BV4_ProcessNodeOrdered<LeafTestT>(stack, nb, node, params, ord[1], limit);
BV4_ProcessNodeOrdered<LeafTestT>(stack, nb, node, params, ord[2], limit);
BV4_ProcessNodeOrdered<LeafTestT>(stack, nb, node, params, ord[3], limit);
}while(Nb);
}
// Alternative, experimental version using PNS
template<class LeafTestT, class ParamsT>
static void BV4_ProcessStreamOrdered2(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPacked* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31;
const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31;
const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31;
const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2));
const PxU32 dirMask = 1u<<bitIndex;
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const PxU32 nodeType = getChildType(childData);
PxU32 code = 0;
BV4_ProcessNodeOrdered2<LeafTestT, 0>(code, node, params);
BV4_ProcessNodeOrdered2<LeafTestT, 1>(code, node, params);
if(nodeType>0)
BV4_ProcessNodeOrdered2<LeafTestT, 2>(code, node, params);
if(nodeType>1)
BV4_ProcessNodeOrdered2<LeafTestT, 3>(code, node, params);
if(code)
{
// PT: TODO: check which implementation is best on each platform (TA34704)
#define FOURTH_TEST // Version avoids computing the PNS index, and also avoids all non-constant shifts. Full of branches though. Fastest on Win32.
#ifdef FOURTH_TEST
{
if(node[0].decodePNSNoShift() & dirMask) // Bit2
{
if(node[1].decodePNSNoShift() & dirMask) // Bit1
{
if(node[2].decodePNSNoShift() & dirMask) // Bit0
PNS_BLOCK2(3,2,1,0) // 7
else
PNS_BLOCK2(2,3,1,0) // 6
}
else
{
if(node[2].decodePNSNoShift() & dirMask) // Bit0
PNS_BLOCK2(3,2,0,1) // 5
else
PNS_BLOCK2(2,3,0,1) // 4
}
}
else
{
if(node[1].decodePNSNoShift() & dirMask) // Bit1
{
if(node[2].decodePNSNoShift() & dirMask) // Bit0
PNS_BLOCK2(1,0,3,2) // 3
else
PNS_BLOCK2(1,0,2,3) // 2
}
else
{
if(node[2].decodePNSNoShift() & dirMask) // Bit0
PNS_BLOCK2(0,1,3,2) // 1
else
PNS_BLOCK2(0,1,2,3) // 0
}
}
}
#endif
}
}while(nb);
}
#endif // GU_BV4_USE_SLABS
#endif // GU_BV4_INTERNAL_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,167 @@
// 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 "GuBV4.h"
using namespace physx;
using namespace Gu;
#include "foundation/PxVecMath.h"
using namespace aos;
#include "GuBV4_BoxSweep_Internal.h"
PxIntBool Sweep_AABB_BV4(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags);
void GenericSweep_AABB_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, PxU32 flags);
void Sweep_AABB_BV4_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting);
// PT: TODO: optimize this (TA34704)
static PX_FORCE_INLINE void computeLocalData(Box& localBox, PxVec3& localDir, const Box& box, const PxVec3& dir, const PxMat44* PX_RESTRICT worldm_Aligned)
{
if(worldm_Aligned)
{
PxMat44 IWM;
invertPRMatrix(&IWM, worldm_Aligned);
localDir = IWM.rotate(dir);
rotateBox(localBox, IWM, box);
}
else
{
localDir = dir;
localBox = box; // PT: TODO: check asm for operator= (TA34704)
}
}
static PX_FORCE_INLINE bool isAxisAligned(const PxVec3& axis)
{
const PxReal minLimit = 1e-3f;
const PxReal maxLimit = 1.0f - 1e-3f;
const PxReal absX = PxAbs(axis.x);
if(absX>minLimit && absX<maxLimit)
return false;
const PxReal absY = PxAbs(axis.y);
if(absY>minLimit && absY<maxLimit)
return false;
const PxReal absZ = PxAbs(axis.z);
if(absZ>minLimit && absZ<maxLimit)
return false;
return true;
}
static PX_FORCE_INLINE bool isAABB(const Box& box)
{
if(!isAxisAligned(box.rot.column0))
return false;
if(!isAxisAligned(box.rot.column1))
return false;
if(!isAxisAligned(box.rot.column2))
return false;
return true;
}
PxIntBool BV4_BoxSweepSingle(const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepHit* PX_RESTRICT hit, PxU32 flags)
{
Box localBox;
PxVec3 localDir;
computeLocalData(localBox, localDir, box, dir, worldm_Aligned);
PxIntBool Status;
if(isAABB(localBox))
Status = Sweep_AABB_BV4(localBox, localDir, maxDist, tree, hit, flags);
else
Status = Sweep_OBB_BV4(localBox, localDir, maxDist, tree, hit, flags);
if(Status && worldm_Aligned)
{
// Move to world space
// PT: TODO: optimize (TA34704)
hit->mPos = worldm_Aligned->transform(hit->mPos);
hit->mNormal = worldm_Aligned->rotate(hit->mNormal);
}
return Status;
}
// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB().
void BV4_BoxSweepCB(const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting)
{
Box localBox;
PxVec3 localDir;
computeLocalData(localBox, localDir, box, dir, worldm_Aligned);
if(isAABB(localBox))
Sweep_AABB_BV4_CB(localBox, localDir, maxDist, tree, worldm_Aligned, callback, userData, flags, nodeSorting);
else
Sweep_OBB_BV4_CB(localBox, localDir, maxDist, tree, worldm_Aligned, callback, userData, flags, nodeSorting);
}
// PT: this generic sweep uses an OBB because this is the most versatile volume, but it does not mean this function is
// a "box sweep function" per-se. In fact it could be used all alone to implement all sweeps in the SDK (but that would
// have an impact on performance).
//
// So the idea here is simply to provide and use a generic function for everything that the BV4 code does not support directly.
// In particular this should be used:
// - for convex sweeps (where the OBB is the box around the swept convex)
// - for non-trivial sphere/capsule/box sweeps where mesh scaling or inflation
//
// By design we don't do leaf tests inside the BV4 traversal code here (because we don't support them, e.g. convex
// sweeps. If we could do them inside the BV4 traversal code, like we do for regular sweeps, then this would not be a generic
// sweep function, but instead a built-in, natively supported query). So the leaf tests are performed outside of BV4, in the
// client code, through MeshSweepCallback. This has a direct impact on the design & parameters of MeshSweepCallback.
//
// On the other hand this is used for "regular sweeps with shapes we don't natively support", i.e. SweepSingle kind of queries.
// This means that we need to support an early-exit codepath (without node-sorting) and a regular sweep single codepath (with
// node sorting) for this generic function. The leaf tests are external, but everything traversal-related should be exactly the
// same as the regular box-sweep function otherwise.
//
// As a consequence, this function is not well-suited to implement "unlimited results" kind of queries, a.k.a. "sweep all":
//
// - for regular sphere/capsule/box "sweep all" queries, the leaf tests should be internal (same as sweep single queries). This
// means the existing MeshSweepCallback can't be reused.
//
// - there is no need to support "sweep any" (it is already supported by the other sweep functions).
//
// - there may be no need for ordered traversal/node sorting/ray shrinking, since we want to return all results anyway. But this
// may not be true if the "sweep all" function is used to emulate the Epic Tweak. In that case we still want to shrink the ray
// and use node sorting. Since both versions are useful, we should probably have a bool param to enable/disable node sorting.
//
// - we are interested in all hits so we can't delay the computation of impact data (computing it only once in the end, for the
// closest hit). We actually need to compute the data for all hits, possibly within the traversal code.
void BV4_GenericSweepCB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, bool anyHit)
{
const PxU32 flags = anyHit ? PxU32(QUERY_MODIFIER_ANY_HIT) : 0;
if(isAABB(localBox))
GenericSweep_AABB_CB(localBox, localDir, maxDist, tree, callback, userData, flags);
else
GenericSweep_OBB_CB(localBox, localDir, maxDist, tree, callback, userData, flags);
}

View File

@@ -0,0 +1,109 @@
// 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_BV4_PROCESS_STREAM_NOORDER_OBB_OBB_H
#define GU_BV4_PROCESS_STREAM_NOORDER_OBB_OBB_H
#ifdef GU_BV4_USE_SLABS
/* template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE PxIntBool BV4_ProcessNodeNoOrder_Swizzled(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzled* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
OPC_SLABS_GET_CE(i)
if(BV4_BoxBoxOverlap(centerV, extentsV, params))
{
if(node->isLeaf(i))
{
if(LeafTestT::doLeafTest(params, node->getPrimitive(i)))
return 1;
}
else
Stack[Nb++] = node->getChildData(i);
}
return 0;
}*/
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE PxIntBool BV4_ProcessNodeNoOrder_SwizzledQ(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzledQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
OPC_SLABS_GET_CEQ(i)
if(BV4_BoxBoxOverlap(centerV, extentsV, params))
{
if(node->isLeaf(i))
{
if(LeafTestT::doLeafTest(params, node->getPrimitive(i)))
return 1;
}
else
Stack[Nb++] = node->getChildData(i);
}
return 0;
}
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE PxIntBool BV4_ProcessNodeNoOrder_SwizzledNQ(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzledNQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
OPC_SLABS_GET_CENQ(i)
if(BV4_BoxBoxOverlap(centerV, extentsV, params))
{
if(node->isLeaf(i))
{
if(LeafTestT::doLeafTest(params, node->getPrimitive(i)))
return 1;
}
else
Stack[Nb++] = node->getChildData(i);
}
return 0;
}
#else
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE PxIntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
#ifdef GU_BV4_QUANTIZED_TREE
if(BV4_BoxBoxOverlap(node+i, params))
#else
if(BV4_BoxBoxOverlap(node[i].mAABB.mExtents, node[i].mAABB.mCenter, params))
#endif
{
if(node[i].isLeaf())
{
if(LeafTestT::doLeafTest(params, node[i].getPrimitive()))
return 1;
}
else
Stack[Nb++] = node[i].getChildData();
}
return 0;
}
#endif
#endif // GU_BV4_PROCESS_STREAM_NOORDER_OBB_OBB_H

View File

@@ -0,0 +1,54 @@
// 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_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_H
#define GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_H
#ifndef GU_BV4_USE_SLABS
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE PxIntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
#ifdef GU_BV4_QUANTIZED_TREE
if(BV4_SegmentAABBOverlap(node+i, params))
#else
if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params))
#endif
{
if(node[i].isLeaf())
{
if(LeafTestT::doLeafTest(params, node[i].getPrimitive()))
return 1;
}
else
Stack[Nb++] = node[i].getChildData();
}
return 0;
}
#endif
#endif // GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_H

View File

@@ -0,0 +1,54 @@
// 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_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_INFLATED_H
#define GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_INFLATED_H
#ifndef GU_BV4_USE_SLABS
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE PxIntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
#ifdef GU_BV4_QUANTIZED_TREE
if(BV4_SegmentAABBOverlap(node+i, params->mOriginalExtents_Padded, params))
#else
if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params->mOriginalExtents_Padded, params))
#endif
{
if(node[i].isLeaf())
{
if(LeafTestT::doLeafTest(params, node[i].getPrimitive()))
return 1;
}
else
Stack[Nb++] = node[i].getChildData();
}
return 0;
}
#endif
#endif // GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_INFLATED_H

View File

@@ -0,0 +1,110 @@
// 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_BV4_PROCESS_STREAM_NOORDER_SPHERE_AABB_H
#define GU_BV4_PROCESS_STREAM_NOORDER_SPHERE_AABB_H
#ifdef GU_BV4_USE_SLABS
/* template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE PxIntBool BV4_ProcessNodeNoOrder_Swizzled(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzled* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
// OPC_SLABS_GET_CE(i)
OPC_SLABS_GET_CE2(i)
if(BV4_SphereAABBOverlap(centerV, extentsV, params))
{
if(node->isLeaf(i))
{
if(LeafTestT::doLeafTest(params, node->getPrimitive(i)))
return 1;
}
else
Stack[Nb++] = node->getChildData(i);
}
return 0;
}*/
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE PxIntBool BV4_ProcessNodeNoOrder_SwizzledQ(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzledQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
OPC_SLABS_GET_CE2Q(i)
if(BV4_SphereAABBOverlap(centerV, extentsV, params))
{
if(node->isLeaf(i))
{
if(LeafTestT::doLeafTest(params, node->getPrimitive(i)))
return 1;
}
else
Stack[Nb++] = node->getChildData(i);
}
return 0;
}
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE PxIntBool BV4_ProcessNodeNoOrder_SwizzledNQ(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzledNQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
OPC_SLABS_GET_CE2NQ(i)
if(BV4_SphereAABBOverlap(centerV, extentsV, params))
{
if(node->isLeaf(i))
{
if(LeafTestT::doLeafTest(params, node->getPrimitive(i)))
return 1;
}
else
Stack[Nb++] = node->getChildData(i);
}
return 0;
}
#else
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE PxIntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
#ifdef GU_BV4_QUANTIZED_TREE
if(BV4_SphereAABBOverlap(node+i, params))
#else
if(BV4_SphereAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params))
#endif
{
if(node[i].isLeaf())
{
if(LeafTestT::doLeafTest(params, node[i].getPrimitive()))
return 1;
}
else
Stack[Nb++] = node[i].getChildData();
}
return 0;
}
#endif
#endif // GU_BV4_PROCESS_STREAM_NOORDER_SPHERE_AABB_H

View File

@@ -0,0 +1,109 @@
// 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_BV4_PROCESS_STREAM_ORDERED_OBB_OBB_H
#define GU_BV4_PROCESS_STREAM_ORDERED_OBB_OBB_H
#ifdef GU_BV4_USE_SLABS
/* template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE void BV4_ProcessNodeOrdered2_Swizzled(PxU32& code, const BVDataSwizzled* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
OPC_SLABS_GET_CE(i)
if(BV4_BoxBoxOverlap(centerV, extentsV, params))
{
if(node->isLeaf(i))
LeafTestT::doLeafTest(params, node->getPrimitive(i));
else
code |= 1<<i;
}
}*/
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE void BV4_ProcessNodeOrdered2_SwizzledQ(PxU32& code, const BVDataSwizzledQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
OPC_SLABS_GET_CEQ(i)
if(BV4_BoxBoxOverlap(centerV, extentsV, params))
{
if(node->isLeaf(i))
LeafTestT::doLeafTest(params, node->getPrimitive(i));
else
code |= 1<<i;
}
}
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE void BV4_ProcessNodeOrdered2_SwizzledNQ(PxU32& code, const BVDataSwizzledNQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
OPC_SLABS_GET_CENQ(i)
if(BV4_BoxBoxOverlap(centerV, extentsV, params))
{
if(node->isLeaf(i))
LeafTestT::doLeafTest(params, node->getPrimitive(i));
else
code |= 1<<i;
}
}
#else
template<class LeafTestT, class ParamsT>
PX_FORCE_INLINE void BV4_ProcessNodeOrdered(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params, PxU32 i, PxU32 limit)
{
#ifdef GU_BV4_QUANTIZED_TREE
if(i<limit && BV4_BoxBoxOverlap(node+i, params))
#else
if(i<limit && BV4_BoxBoxOverlap(node[i].mAABB.mExtents, node[i].mAABB.mCenter, params))
#endif
{
if(node[i].isLeaf())
LeafTestT::doLeafTest(params, node[i].getPrimitive());
else
Stack[Nb++] = node[i].getChildData();
}
}
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE void BV4_ProcessNodeOrdered2(PxU32& code, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
#ifdef GU_BV4_QUANTIZED_TREE
if(BV4_BoxBoxOverlap(node+i, params))
#else
if(BV4_BoxBoxOverlap(node[i].mAABB.mExtents, node[i].mAABB.mCenter, params))
#endif
{
if(node[i].isLeaf())
LeafTestT::doLeafTest(params, node[i].getPrimitive());
else
code |= 1<<i;
}
}
#endif
#endif // GU_BV4_PROCESS_STREAM_ORDERED_OBB_OBB_H

View File

@@ -0,0 +1,66 @@
// 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_BV4_PROCESS_STREAM_ORDERED_SEGMENT_AABB_H
#define GU_BV4_PROCESS_STREAM_ORDERED_SEGMENT_AABB_H
#ifndef GU_BV4_USE_SLABS
template<class LeafTestT, class ParamsT>
PX_FORCE_INLINE void BV4_ProcessNodeOrdered(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params, PxU32 i, PxU32 limit)
{
#ifdef GU_BV4_QUANTIZED_TREE
if(i<limit && BV4_SegmentAABBOverlap(node+i, params))
#else
if(i<limit && BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params))
#endif
{
if(node[i].isLeaf())
LeafTestT::doLeafTest(params, node[i].getPrimitive());
else
Stack[Nb++] = node[i].getChildData();
}
}
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE void BV4_ProcessNodeOrdered2(PxU32& code, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
#ifdef GU_BV4_QUANTIZED_TREE
if(BV4_SegmentAABBOverlap(node+i, params))
#else
if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params))
#endif
{
if(node[i].isLeaf())
LeafTestT::doLeafTest(params, node[i].getPrimitive());
else
code |= 1<<i;
}
}
#endif
#endif // GU_BV4_PROCESS_STREAM_ORDERED_SEGMENT_AABB_H

View File

@@ -0,0 +1,66 @@
// 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_BV4_PROCESS_STREAM_ORDERED_SEGMENT_AABB_INFLATED_H
#define GU_BV4_PROCESS_STREAM_ORDERED_SEGMENT_AABB_INFLATED_H
#ifndef GU_BV4_USE_SLABS
template<class LeafTestT, class ParamsT>
PX_FORCE_INLINE void BV4_ProcessNodeOrdered(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params, PxU32 i, PxU32 limit)
{
#ifdef GU_BV4_QUANTIZED_TREE
if(i<limit && BV4_SegmentAABBOverlap(node+i, params->mOriginalExtents, params))
#else
if(i<limit && BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params->mOriginalExtents, params))
#endif
{
if(node[i].isLeaf())
LeafTestT::doLeafTest(params, node[i].getPrimitive());
else
Stack[Nb++] = node[i].getChildData();
}
}
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE void BV4_ProcessNodeOrdered2(PxU32& code, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params)
{
#ifdef GU_BV4_QUANTIZED_TREE
if(BV4_SegmentAABBOverlap(node+i, params->mOriginalExtents_Padded, params))
#else
if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params->mOriginalExtents_Padded, params))
#endif
{
if(node[i].isLeaf())
LeafTestT::doLeafTest(params, node[i].getPrimitive());
else
code |= 1<<i;
}
}
#endif
#endif // GU_BV4_PROCESS_STREAM_ORDERED_SEGMENT_AABB_INFLATED_H

View File

@@ -0,0 +1,906 @@
// 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 "GuBV4.h"
using namespace physx;
using namespace Gu;
// PT: this one avoids a divide but doesn't seem to make a difference
//#define TEST_DISTANCE_INSIDE_RAY_TRI
// PT: works but unclear performance benefits. Needs more testing.
//#define USE_SIMD_RAY_VS_TRI
#include "PxQueryReport.h"
#include "GuInternal.h"
#include "GuIntersectionRayTriangle.h"
#include "foundation/PxVecMath.h"
using namespace aos;
#include "GuBV4_Common.h"
class RaycastHitInternalUV : public RaycastHitInternal
{
public:
PX_FORCE_INLINE RaycastHitInternalUV() {}
PX_FORCE_INLINE ~RaycastHitInternalUV() {}
float mU, mV;
};
#ifdef USE_SIMD_RAY_VS_TRI
//#define DotV V4Dot
#define DotV V4Dot3
//PX_FORCE_INLINE FloatV V4Dot3_SSE42(const Vec4V a, const Vec4V b) { return _mm_dp_ps(a, b, 0x7f); }
//#define DotV V4Dot3_SSE42
template<class T>
static PX_FORCE_INLINE PxIntBool SimdRayTriOverlapT(PxGeomRaycastHit& mStabbedFace, const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, const T* PX_RESTRICT params)
{
// Find vectors for two edges sharing vert0
const Vec4V vert0V = V4LoadU(&vert0.x);
const Vec4V edge1V = V4Sub(V4LoadU(&vert1.x), vert0V); // const PxVec3 edge1 = vert1 - vert0;
const Vec4V edge2V = V4Sub(V4LoadU(&vert2.x), vert0V); // const PxVec3 edge2 = vert2 - vert0;
// Begin calculating determinant - also used to calculate U parameter
const Vec4V localDirV = V4LoadU(&params->mLocalDir_Padded.x);
/*const*/ Vec4V pvecV = V4Cross(localDirV, edge2V); // const PxVec3 pvec = params->mLocalDir_Padded.cross(edge2);
//pvecV = V4ClearW(pvecV);
// If determinant is near zero, ray lies in plane of triangle
const FloatV detV = DotV(edge1V, pvecV); // const float det = edge1.dot(pvec);
// PT: TODO: if we zero W in pvecV and qvecV we can switch to V4Dot *************
// PT: because the SIMD version delays the branches we can share more computations between culling & non-culling codepaths
// Calculate distance from vert0 to ray origin
const Vec4V tvecV = V4Sub(V4LoadU(&params->mOrigin_Padded.x), vert0V); // const PxVec3 tvec = params->mOrigin_Padded - vert0;
// Prepare to test V parameter
/*const*/ Vec4V qvecV = V4Cross(tvecV, edge1V); // const PxVec3 qvec = tvec.cross(edge1);
//qvecV = V4ClearW(qvecV);
const FloatV localEpsilonV = FLoad(GU_CULLING_EPSILON_RAY_TRIANGLE);
const FloatV geomEpsilonV = FLoad(params->mGeomEpsilon);
if(params->mBackfaceCulling)
{
// Calculate U parameter and test bounds
const FloatV uV = DotV(tvecV, pvecV); // const float u = tvec.dot(pvec);
// Calculate V parameter and test bounds
const FloatV vV = DotV(localDirV, qvecV); // const float v = params->mLocalDir_Padded.dot(qvec);
// Calculate t, scale parameters, ray intersects triangle
const FloatV dV = DotV(edge2V, qvecV); // const float d = edge2.dot(qvec);
/////
// if(det<GU_CULLING_EPSILON_RAY_TRIANGLE)
// return 0;
BoolV res = FIsGrtr(localEpsilonV, detV);
/////
// const PxReal enlargeCoeff = params->mGeomEpsilon*det;
const FloatV enlargeCoeffV = FMul(detV, geomEpsilonV);
/*
const PxReal uvlimit = -enlargeCoeff;
const PxReal uvlimit2 = det + enlargeCoeff;
if(u < uvlimit || u > uvlimit2)
return 0;
if(v < uvlimit || (u + v) > uvlimit2)
return 0;
// Det > 0 so we can early exit here
// Intersection point is valid if distance is positive (else it can just be a face behind the orig point)
if(d<0.0f)
return 0;
*/
res = BOr(res, FIsGrtr(FZero(), FMin(dV, FMin(FAdd(uV, enlargeCoeffV), FAdd(vV, enlargeCoeffV)))));
res = BOr(res, FIsGrtr(FMax(uV, FAdd(uV, vV)), FAdd(detV, enlargeCoeffV)));
// if(!BAllEqFFFF(res))
// return 0;
if(BGetBitMask(res))
return 0;
// Else go on
// const float OneOverDet = 1.0f / det;
const FloatV oneOverDetV = FDiv(FLoad(1.0f), detV);
FStore(FMul(dV, oneOverDetV), &mStabbedFace.distance); // mStabbedFace.distance = d * OneOverDet;
FStore(FMul(uV, oneOverDetV), &mStabbedFace.u); // mStabbedFace.u = u * OneOverDet;
FStore(FMul(vV, oneOverDetV), &mStabbedFace.v); // mStabbedFace.v = v * OneOverDet;
return 1;
}
else
{
//if(PxAbs(det)<GU_CULLING_EPSILON_RAY_TRIANGLE)
// return 0;
BoolV res = FIsGrtr(localEpsilonV, FAbs(detV));
const FloatV oneV = FLoad(1.0f);
const FloatV oneOverDetV = FDiv(oneV, detV); // const float OneOverDet = 1.0f / det;
const FloatV uV = FMul(DotV(tvecV, pvecV), oneOverDetV); // const float u = tvec.dot(pvec) * OneOverDet;
// Calculate V parameter and test bounds
const FloatV vV = FMul(DotV(localDirV, qvecV), oneOverDetV); // const float v = params->mLocalDir_Padded.dot(qvec) * OneOverDet;
// Calculate t, ray intersects triangle
const FloatV dV = FMul(DotV(edge2V, qvecV), oneOverDetV); // const float d = edge2.dot(qvec) * OneOverDet;
/*
if(u<-params->mGeomEpsilon || u>1.0f+params->mGeomEpsilon)
return 0;
if(v < -params->mGeomEpsilon || (u + v) > 1.0f + params->mGeomEpsilon)
return 0;
// Intersection point is valid if distance is positive (else it can just be a face behind the orig point)
if(d<0.0f)
return 0;
*/
res = BOr(res, FIsGrtr(FZero(), FMin(dV, FMin(FAdd(uV, geomEpsilonV), FAdd(vV, geomEpsilonV)))));
res = BOr(res, FIsGrtr(FMax(uV, FAdd(uV, vV)), FAdd(oneV, geomEpsilonV)));
//if(!BAllEqFFFF(res))
// return 0;
if(BGetBitMask(res))
return 0;
FStore(dV, &mStabbedFace.distance); // mStabbedFace.distance = d;
FStore(uV, &mStabbedFace.u); // mStabbedFace.u = u;
FStore(vV, &mStabbedFace.v); // mStabbedFace.v = v;
return 1;
}
}
template<class T>
static PX_FORCE_INLINE PxIntBool SimdRayTriOverlapT2(PxGeomRaycastHit& mStabbedFace, const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, const T* PX_RESTRICT params)
{
// Find vectors for two edges sharing vert0
const Vec4V vert0V = V4LoadU(&vert0.x);
const Vec4V edge1V = V4Sub(V4LoadU(&vert1.x), vert0V); // const PxVec3 edge1 = vert1 - vert0;
const Vec4V edge2V = V4Sub(V4LoadU(&vert2.x), vert0V); // const PxVec3 edge2 = vert2 - vert0;
// Begin calculating determinant - also used to calculate U parameter
const Vec4V localDirV = V4LoadU(&params->mLocalDir_Padded.x);
const Vec4V pvecV = V4Cross(localDirV, edge2V); // const PxVec3 pvec = params->mLocalDir_Padded.cross(edge2);
// If determinant is near zero, ray lies in plane of triangle
const FloatV detV = DotV(edge1V, pvecV); // const float det = edge1.dot(pvec);
const FloatV localEpsilonV = FLoad(GU_CULLING_EPSILON_RAY_TRIANGLE);
if(params->mBackfaceCulling)
{
// if(det<GU_CULLING_EPSILON_RAY_TRIANGLE)
// return 0;
BoolV res = FIsGrtr(localEpsilonV, detV);
if(BGetBitMask(res))
return 0;
// Calculate distance from vert0 to ray origin
const Vec4V tvecV = V4Sub(V4LoadU(&params->mOrigin_Padded.x), vert0V); // const PxVec3 tvec = params->mOrigin_Padded - vert0;
// Prepare to test V parameter
const Vec4V qvecV = V4Cross(tvecV, edge1V); // const PxVec3 qvec = tvec.cross(edge1);
const FloatV geomEpsilonV = FLoad(params->mGeomEpsilon);
// Calculate U parameter and test bounds
const FloatV uV = DotV(tvecV, pvecV); // const float u = tvec.dot(pvec);
// Calculate V parameter and test bounds
const FloatV vV = DotV(localDirV, qvecV); // const float v = params->mLocalDir_Padded.dot(qvec);
// Calculate t, scale parameters, ray intersects triangle
const FloatV dV = DotV(edge2V, qvecV); // const float d = edge2.dot(qvec);
/////
// const PxReal enlargeCoeff = params->mGeomEpsilon*det;
const FloatV enlargeCoeffV = FMul(detV, geomEpsilonV);
res = BOr(res, FIsGrtr(FZero(), FMin(dV, FMin(FAdd(uV, enlargeCoeffV), FAdd(vV, enlargeCoeffV)))));
res = BOr(res, FIsGrtr(FMax(uV, FAdd(uV, vV)), FAdd(detV, enlargeCoeffV)));
// if(!BAllEqFFFF(res))
// return 0;
if(BGetBitMask(res))
return 0;
// Else go on
// const float OneOverDet = 1.0f / det;
const FloatV oneOverDetV = FDiv(FLoad(1.0f), detV);
FStore(FMul(dV, oneOverDetV), &mStabbedFace.distance); // mStabbedFace.distance = d * OneOverDet;
FStore(FMul(uV, oneOverDetV), &mStabbedFace.u); // mStabbedFace.u = u * OneOverDet;
FStore(FMul(vV, oneOverDetV), &mStabbedFace.v); // mStabbedFace.v = v * OneOverDet;
return 1;
}
else
{
//if(PxAbs(det)<GU_CULLING_EPSILON_RAY_TRIANGLE)
// return 0;
BoolV res = FIsGrtr(localEpsilonV, FAbs(detV));
if(BGetBitMask(res))
return 0;
// Calculate distance from vert0 to ray origin
const Vec4V tvecV = V4Sub(V4LoadU(&params->mOrigin_Padded.x), vert0V); // const PxVec3 tvec = params->mOrigin_Padded - vert0;
// Prepare to test V parameter
const Vec4V qvecV = V4Cross(tvecV, edge1V); // const PxVec3 qvec = tvec.cross(edge1);
const FloatV geomEpsilonV = FLoad(params->mGeomEpsilon);
const FloatV oneV = FLoad(1.0f);
const FloatV oneOverDetV = FDiv(oneV, detV); // const float OneOverDet = 1.0f / det;
const FloatV uV = FMul(DotV(tvecV, pvecV), oneOverDetV); // const float u = tvec.dot(pvec) * OneOverDet;
// Calculate V parameter and test bounds
const FloatV vV = FMul(DotV(localDirV, qvecV), oneOverDetV); // const float v = params->mLocalDir_Padded.dot(qvec) * OneOverDet;
// Calculate t, ray intersects triangle
const FloatV dV = FMul(DotV(edge2V, qvecV), oneOverDetV); // const float d = edge2.dot(qvec) * OneOverDet;
res = BOr(res, FIsGrtr(FZero(), FMin(dV, FMin(FAdd(uV, geomEpsilonV), FAdd(vV, geomEpsilonV)))));
res = BOr(res, FIsGrtr(FMax(uV, FAdd(uV, vV)), FAdd(oneV, geomEpsilonV)));
if(BGetBitMask(res))
return 0;
FStore(dV, &mStabbedFace.distance); // mStabbedFace.distance = d;
FStore(uV, &mStabbedFace.u); // mStabbedFace.u = u;
FStore(vV, &mStabbedFace.v); // mStabbedFace.v = v;
return 1;
}
}
#endif
template<class T>
static PX_FORCE_INLINE PxIntBool RayTriOverlapT(PxGeomRaycastHit& mStabbedFace, const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, const T* PX_RESTRICT params)
{
#ifdef USE_SIMD_RAY_VS_TRI
if(0)
return SimdRayTriOverlapT(mStabbedFace, vert0, vert1, vert2, params);
if(0)
return SimdRayTriOverlapT2(mStabbedFace, vert0, vert1, vert2, params);
#endif
// Find vectors for two edges sharing vert0
const PxVec3 edge1 = vert1 - vert0;
const PxVec3 edge2 = vert2 - vert0;
// Begin calculating determinant - also used to calculate U parameter
const PxVec3 pvec = params->mLocalDir_Padded.cross(edge2);
// If determinant is near zero, ray lies in plane of triangle
const float det = edge1.dot(pvec);
if(params->mBackfaceCulling)
{
if(det<GU_CULLING_EPSILON_RAY_TRIANGLE)
return 0;
// Calculate distance from vert0 to ray origin
const PxVec3 tvec = params->mOrigin_Padded - vert0;
// Calculate U parameter and test bounds
const float u = tvec.dot(pvec);
const PxReal enlargeCoeff = params->mGeomEpsilon*det;
const PxReal uvlimit = -enlargeCoeff;
const PxReal uvlimit2 = det + enlargeCoeff;
if(u < uvlimit || u > uvlimit2)
return 0;
// Prepare to test V parameter
const PxVec3 qvec = tvec.cross(edge1);
// Calculate V parameter and test bounds
const float v = params->mLocalDir_Padded.dot(qvec);
if(v < uvlimit || (u + v) > uvlimit2)
return 0;
// Calculate t, scale parameters, ray intersects triangle
const float d = edge2.dot(qvec);
// Det > 0 so we can early exit here
// Intersection point is valid if distance is positive (else it can just be a face behind the orig point)
if(d<0.0f)
return 0;
#ifdef TEST_DISTANCE_INSIDE_RAY_TRI
if(d>=det*params->mStabbedFace.mDistance) //### just for a corner case UT in PhysX :(
return 0;
#endif
// Else go on
const float OneOverDet = 1.0f / det;
mStabbedFace.distance = d * OneOverDet;
mStabbedFace.u = u * OneOverDet;
mStabbedFace.v = v * OneOverDet;
}
else
{
if(PxAbs(det)<GU_CULLING_EPSILON_RAY_TRIANGLE)
return 0;
const float OneOverDet = 1.0f / det;
const PxVec3 tvec = params->mOrigin_Padded - vert0;
const float u = tvec.dot(pvec) * OneOverDet;
if(u<-params->mGeomEpsilon || u>1.0f+params->mGeomEpsilon)
return 0;
// prepare to test V parameter
const PxVec3 qvec = tvec.cross(edge1);
// Calculate V parameter and test bounds
const float v = params->mLocalDir_Padded.dot(qvec) * OneOverDet;
if(v < -params->mGeomEpsilon || (u + v) > 1.0f + params->mGeomEpsilon)
return 0;
// Calculate t, ray intersects triangle
const float d = edge2.dot(qvec) * OneOverDet;
// Intersection point is valid if distance is positive (else it can just be a face behind the orig point)
if(d<0.0f)
return 0;
#ifdef TEST_DISTANCE_INSIDE_RAY_TRI
if(d>=params->mStabbedFace.mDistance) //### just for a corner case UT in PhysX :(
return 0;
#endif
mStabbedFace.distance = d;
mStabbedFace.u = u;
mStabbedFace.v = v;
}
return 1;
}
#if PX_VC
#pragma warning ( disable : 4324 )
#endif
namespace
{
struct RayParams_Raycast // PT: compiler gets confused otherwise
{
BV4_ALIGN16(PxVec3p mCenterOrMinCoeff_PaddedAligned);
BV4_ALIGN16(PxVec3p mExtentsOrMaxCoeff_PaddedAligned);
// Organized in the order they are accessed
#ifndef GU_BV4_USE_SLABS
BV4_ALIGN16(PxVec3p mData2_PaddedAligned);
BV4_ALIGN16(PxVec3p mFDir_PaddedAligned);
BV4_ALIGN16(PxVec3p mData_PaddedAligned);
#endif
const IndTri32* PX_RESTRICT mTris32;
const IndTri16* PX_RESTRICT mTris16;
const PxVec3* PX_RESTRICT mVerts;
PxVec3 mLocalDir_Padded;
PxVec3 mOrigin_Padded;
float mGeomEpsilon;
PxU32 mBackfaceCulling;
RaycastHitInternalUV mStabbedFace;
PxU32 mEarlyExit;
PxVec3 mOriginalExtents_Padded; // Added to please the slabs code
BV4_ALIGN16(PxVec3p mP0_PaddedAligned);
BV4_ALIGN16(PxVec3p mP1_PaddedAligned);
BV4_ALIGN16(PxVec3p mP2_PaddedAligned);
};
}
///////////////////////////////////////////////////////////////////////////////
static PX_FORCE_INLINE void updateParamsAfterImpact(RayParams_Raycast* PX_RESTRICT params, PxU32 primIndex, PxU32 VRef0, PxU32 VRef1, PxU32 VRef2, const PxGeomRaycastHit& StabbedFace)
{
V4StoreA_Safe(V4LoadU_Safe(&params->mVerts[VRef0].x), &params->mP0_PaddedAligned.x);
V4StoreA_Safe(V4LoadU_Safe(&params->mVerts[VRef1].x), &params->mP1_PaddedAligned.x);
V4StoreA_Safe(V4LoadU_Safe(&params->mVerts[VRef2].x), &params->mP2_PaddedAligned.x);
params->mStabbedFace.mTriangleID = primIndex;
params->mStabbedFace.mDistance = StabbedFace.distance;
params->mStabbedFace.mU = StabbedFace.u;
params->mStabbedFace.mV = StabbedFace.v;
}
namespace
{
class LeafFunction_RaycastClosest
{
public:
static /*PX_FORCE_INLINE*/ PxIntBool doLeafTest(RayParams_Raycast* PX_RESTRICT params, PxU32 primIndex)
{
PX_ALIGN_PREFIX(16) char buffer[sizeof(PxGeomRaycastHit)] PX_ALIGN_SUFFIX(16);
PxGeomRaycastHit& StabbedFace = reinterpret_cast<PxGeomRaycastHit&>(buffer);
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
if(RayTriOverlapT<RayParams_Raycast>(StabbedFace, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params))
{
#ifndef TEST_DISTANCE_INSIDE_RAY_TRI
if(StabbedFace.distance<params->mStabbedFace.mDistance) //### just for a corner case UT in PhysX :(
#endif
{
updateParamsAfterImpact(params, primIndex, VRef0, VRef1, VRef2, StabbedFace);
#ifndef GU_BV4_USE_SLABS
setupRayData(params, StabbedFace.distance, params->mOrigin_Padded, params->mLocalDir_Padded);
#endif
}
}
primIndex++;
}while(nbToGo--);
return 0;
}
};
class LeafFunction_RaycastAny
{
public:
static /*PX_FORCE_INLINE*/ PxIntBool doLeafTest(RayParams_Raycast* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
PX_ALIGN_PREFIX(16) char buffer[sizeof(PxGeomRaycastHit)] PX_ALIGN_SUFFIX(16);
PxGeomRaycastHit& StabbedFace = reinterpret_cast<PxGeomRaycastHit&>(buffer);
if(RayTriOverlapT<RayParams_Raycast>(StabbedFace, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params))
{
#ifndef TEST_DISTANCE_INSIDE_RAY_TRI
if(StabbedFace.distance<params->mStabbedFace.mDistance) //### just for a corner case UT in PhysX :(
#endif
{
updateParamsAfterImpact(params, primIndex, VRef0, VRef1, VRef2, StabbedFace);
return 1;
}
}
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
static PX_FORCE_INLINE Vec4V multiply3x3V_Aligned(const Vec4V p, const PxMat44* PX_RESTRICT mat)
{
const FloatV xxxV = V4GetX(p);
const FloatV yyyV = V4GetY(p);
const FloatV zzzV = V4GetZ(p);
Vec4V ResV = V4Scale(V4LoadA(&mat->column0.x), xxxV);
ResV = V4Add(ResV, V4Scale(V4LoadA(&mat->column1.x), yyyV));
ResV = V4Add(ResV, V4Scale(V4LoadA(&mat->column2.x), zzzV));
return ResV;
}
static PX_FORCE_INLINE PxIntBool computeImpactData(PxGeomRaycastHit* PX_RESTRICT hit, const RayParams_Raycast* PX_RESTRICT params, const PxMat44* PX_RESTRICT worldm_Aligned, PxHitFlags /*hitFlags*/)
{
if(params->mStabbedFace.mTriangleID!=PX_INVALID_U32 /*&& !params->mEarlyExit*/) //### PhysX needs the raycast data even for "any hit" :(
{
const float u = params->mStabbedFace.mU;
const float v = params->mStabbedFace.mV;
const float d = params->mStabbedFace.mDistance;
const PxU32 id = params->mStabbedFace.mTriangleID;
hit->u = u;
hit->v = v;
hit->distance = d;
hit->faceIndex = id;
{
const Vec4V P0V = V4LoadA_Safe(&params->mP0_PaddedAligned.x);
const Vec4V P1V = V4LoadA_Safe(&params->mP1_PaddedAligned.x);
const Vec4V P2V = V4LoadA_Safe(&params->mP2_PaddedAligned.x);
const FloatV uV = FLoad(params->mStabbedFace.mU);
const FloatV vV = FLoad(params->mStabbedFace.mV);
const float w = 1.0f - params->mStabbedFace.mU - params->mStabbedFace.mV;
const FloatV wV = FLoad(w);
//pt = (1.0f - u - v)*p0 + u*p1 + v*p2;
Vec4V LocalPtV = V4Scale(P1V, uV);
LocalPtV = V4Add(LocalPtV, V4Scale(P2V, vV));
LocalPtV = V4Add(LocalPtV, V4Scale(P0V, wV));
const Vec4V LocalNormalV = V4Cross(V4Sub(P0V, P1V), V4Sub(P0V, P2V));
BV4_ALIGN16(PxVec3p tmp_PaddedAligned);
if(worldm_Aligned)
{
const Vec4V TransV = V4LoadA(&worldm_Aligned->column3.x);
V4StoreU_Safe(V4Add(multiply3x3V_Aligned(LocalPtV, worldm_Aligned), TransV), &hit->position.x);
V4StoreA_Safe(multiply3x3V_Aligned(LocalNormalV, worldm_Aligned), &tmp_PaddedAligned.x);
}
else
{
V4StoreU_Safe(LocalPtV, &hit->position.x);
V4StoreA_Safe(LocalNormalV, &tmp_PaddedAligned.x);
}
tmp_PaddedAligned.normalize();
hit->normal = tmp_PaddedAligned; // PT: TODO: check asm here (TA34704)
}
}
return params->mStabbedFace.mTriangleID!=PX_INVALID_U32;
}
static PX_FORCE_INLINE float clipRay(const PxVec3& ray_orig, const PxVec3& ray_dir, const LocalBounds& local_bounds)
{
const float dpc = local_bounds.mCenter.dot(ray_dir);
const float dpMin = dpc - local_bounds.mExtentsMagnitude;
const float dpMax = dpc + local_bounds.mExtentsMagnitude;
const float dpO = ray_orig.dot(ray_dir);
const float boxLength = local_bounds.mExtentsMagnitude * 2.0f;
const float distToBox = PxMin(fabsf(dpMin - dpO), fabsf(dpMax - dpO));
return distToBox + boxLength * 2.0f;
}
template<class ParamsT>
static PX_FORCE_INLINE void setupRayParams(ParamsT* PX_RESTRICT params, const PxVec3& origin, const PxVec3& dir, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT world, const SourceMesh* PX_RESTRICT mesh, float maxDist, float geomEpsilon, PxU32 flags)
{
params->mGeomEpsilon = geomEpsilon;
setupParamsFlags(params, flags);
computeLocalRay(params->mLocalDir_Padded, params->mOrigin_Padded, dir, origin, world);
// PT: TODO: clipRay may not be needed with GU_BV4_USE_SLABS (TA34704)
const float MaxDist = clipRay(params->mOrigin_Padded, params->mLocalDir_Padded, tree->mLocalBounds);
maxDist = PxMin(maxDist, MaxDist);
params->mStabbedFace.mDistance = maxDist;
params->mStabbedFace.mTriangleID = PX_INVALID_U32;
setupMeshPointersAndQuantizedCoeffs(params, mesh, tree);
#ifndef GU_BV4_USE_SLABS
setupRayData(params, maxDist, params->mOrigin_Padded, params->mLocalDir_Padded);
#endif
}
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs.h"
#endif
#include "GuBV4_ProcessStreamOrdered_SegmentAABB.h"
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs_KajiyaNoOrder.h"
#include "GuBV4_Slabs_KajiyaOrdered.h"
#endif
#define GU_BV4_PROCESS_STREAM_RAY_NO_ORDER
#define GU_BV4_PROCESS_STREAM_RAY_ORDERED
#include "GuBV4_Internal.h"
#ifndef GU_BV4_USE_SLABS
#ifdef GU_BV4_QUANTIZED_TREE
// A.B. enable new version only for intel non simd path
#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED)
// #define NEW_VERSION
#endif // PX_INTEL_FAMILY && !PX_SIMD_DISABLED
static PX_FORCE_INLINE /*PX_NOINLINE*/ PxIntBool BV4_SegmentAABBOverlap(const BVDataPacked* PX_RESTRICT node, const RayParams* PX_RESTRICT params)
{
#ifdef NEW_VERSION
SSE_CONST4(maskV, 0x7fffffff);
SSE_CONST4(maskQV, 0x0000ffff);
#endif
#ifdef NEW_VERSION
Vec4V centerV = V4LoadA((float*)node->mAABB.mData);
__m128 extentsV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(centerV), SSE_CONST(maskQV)));
extentsV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(extentsV)), V4LoadA_Safe(&params->mExtentsOrMaxCoeff_PaddedAligned.x));
centerV = _mm_castsi128_ps(_mm_srai_epi32(_mm_castps_si128(centerV), 16));
centerV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(centerV)), V4LoadA_Safe(&params->mCenterOrMinCoeff_PaddedAligned.x));
#else
const VecI32V centerVI = I4LoadA((PxI32*)node->mAABB.mData);
const VecI32V extentsVI = VecI32V_And(centerVI, I4Load(0x0000ffff));
const Vec4V extentsV = V4Mul(Vec4V_From_VecI32V(extentsVI), V4LoadA_Safe(&params->mExtentsOrMaxCoeff_PaddedAligned.x));
const VecI32V centerVShift = VecI32V_RightShift(centerVI, 16);
const Vec4V centerV = V4Mul(Vec4V_From_VecI32V(centerVShift), V4LoadA_Safe(&params->mCenterOrMinCoeff_PaddedAligned.x));
#endif
const Vec4V fdirV = V4LoadA_Safe(&params->mFDir_PaddedAligned.x);
const Vec4V DV = V4Sub(V4LoadA_Safe(&params->mData2_PaddedAligned.x), centerV);
#ifdef NEW_VERSION
__m128 absDV = _mm_and_ps(DV, SSE_CONSTF(maskV));
#else
Vec4V absDV = V4Abs(DV);
#endif
const BoolV resDV = V4IsGrtr(absDV, V4Add(extentsV, fdirV));
const PxU32 test = BGetBitMask(resDV);
if(test&7)
return 0;
if(1)
{
const Vec4V dataZYX_V = V4LoadA_Safe(&params->mData_PaddedAligned.x);
const Vec4V dataXZY_V = V4Perm<1, 2, 0, 3>(dataZYX_V);
const Vec4V DXZY_V = V4Perm<1, 2, 0, 3>(DV);
const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV));
const Vec4V fdirZYX_V = V4LoadA_Safe(&params->mFDir_PaddedAligned.x);
const Vec4V fdirXZY_V = V4Perm<1, 2, 0, 3>(fdirZYX_V);
const Vec4V extentsXZY_V = V4Perm<1, 2, 0, 3>(extentsV);
// PT: TODO: use V4MulAdd here (TA34704)
const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V));
#ifdef NEW_VERSION
__m128 absfV = _mm_and_ps(fV, SSE_CONSTF(maskV));
#else
Vec4V absfV = V4Abs(fV);
#endif
const BoolV resfV = V4IsGrtr(absfV, fg);
const PxU32 test2 = BGetBitMask(resfV);
if(test2&7)
return 0;
return 1;
}
}
#else
static PX_FORCE_INLINE /*PX_NOINLINE*/ PxIntBool BV4_SegmentAABBOverlap(const PxVec3& center, const PxVec3& extents, const RayParams* PX_RESTRICT params)
{
const PxU32 maskI = 0x7fffffff;
const Vec4V fdirV = V4LoadA_Safe(&params->mFDir_PaddedAligned.x);
const Vec4V extentsV = V4LoadU(&extents.x);
const Vec4V DV = V4Sub(V4LoadA_Safe(&params->mData2_PaddedAligned.x), V4LoadU(&center.x)); //###center should be aligned
__m128 absDV = _mm_and_ps(DV, _mm_load1_ps((float*)&maskI));
absDV = _mm_cmpgt_ps(absDV, V4Add(extentsV, fdirV));
const PxU32 test = (PxU32)_mm_movemask_ps(absDV);
if(test&7)
return 0;
if(1)
{
const Vec4V dataZYX_V = V4LoadA_Safe(&params->mData_PaddedAligned.x);
const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1)));
const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1)));
const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV));
const Vec4V fdirZYX_V = V4LoadA_Safe(&params->mFDir_PaddedAligned.x);
const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1)));
const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1)));
// PT: TODO: use V4MulAdd here (TA34704)
const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V));
__m128 absfV = _mm_and_ps(fV, _mm_load1_ps((float*)&maskI));
absfV = _mm_cmpgt_ps(absfV, fg);
const PxU32 test2 = (PxU32)_mm_movemask_ps(absfV);
if(test2&7)
return 0;
return 1;
}
}
#endif
#endif
PxIntBool BV4_RaycastSingle(const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxGeomRaycastHit* PX_RESTRICT hit, float maxDist, float geomEpsilon, PxU32 flags, PxHitFlags hitFlags)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<SourceMesh*>(tree.mMeshInterface);
RayParams_Raycast Params;
setupRayParams(&Params, origin, dir, &tree, worldm_Aligned, mesh, maxDist, geomEpsilon, flags);
if(tree.mNodes)
{
if(Params.mEarlyExit)
processStreamRayNoOrder<0, LeafFunction_RaycastAny>(tree, &Params);
else
processStreamRayOrdered<0, LeafFunction_RaycastClosest>(tree, &Params);
}
else
doBruteForceTests<LeafFunction_RaycastAny, LeafFunction_RaycastClosest>(mesh->getNbTriangles(), &Params);
return computeImpactData(hit, &Params, worldm_Aligned, hitFlags);
}
// Callback-based version
namespace
{
struct RayParamsCB : RayParams_Raycast
{
MeshRayCallback mCallback;
void* mUserData;
};
class LeafFunction_RaycastCB
{
public:
static PxIntBool doLeafTest(RayParamsCB* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
const PxVec3& p0 = params->mVerts[VRef0];
const PxVec3& p1 = params->mVerts[VRef1];
const PxVec3& p2 = params->mVerts[VRef2];
PX_ALIGN_PREFIX(16) char buffer[sizeof(PxGeomRaycastHit)] PX_ALIGN_SUFFIX(16);
PxGeomRaycastHit& StabbedFace = reinterpret_cast<PxGeomRaycastHit&>(buffer);
if(RayTriOverlapT<RayParams_Raycast>(StabbedFace, p0, p1, p2, params))
{
#ifndef TEST_DISTANCE_INSIDE_RAY_TRI
if(StabbedFace.distance<params->mStabbedFace.mDistance)
#endif
{
HitCode Code = (params->mCallback)(params->mUserData, p0, p1, p2, primIndex, StabbedFace.distance, StabbedFace.u, StabbedFace.v);
if(Code==HIT_EXIT)
return 1;
}
// PT: TODO: no shrinking here? (TA34704)
}
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
#include "GuBV4_ProcessStreamNoOrder_SegmentAABB.h"
void BV4_RaycastCB(const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, float maxDist, float geomEpsilon, PxU32 flags, MeshRayCallback callback, void* userData)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<SourceMesh*>(tree.mMeshInterface);
//### beware, some parameters in the struct aren't used
RayParamsCB Params;
Params.mCallback = callback;
Params.mUserData = userData;
setupRayParams(&Params, origin, dir, &tree, worldm_Aligned, mesh, maxDist, geomEpsilon, flags);
if(tree.mNodes)
processStreamRayNoOrder<0, LeafFunction_RaycastCB>(tree, &Params);
else
{
const PxU32 nbTris = mesh->getNbTriangles();
PX_ASSERT(nbTris<16);
// if(Params.mEarlyExit)
// LeafFunction_BoxSweepAnyCB::doLeafTest(&Params, nbTris);
// else
LeafFunction_RaycastCB::doLeafTest(&Params, nbTris);
}
}
// Raycast all
namespace
{
struct RayParamsAll : RayParams_Raycast
{
PX_FORCE_INLINE RayParamsAll(PxGeomRaycastHit* hits, PxU32 maxNbHits, PxU32 stride, const PxMat44* mat, PxHitFlags hitFlags) :
mHits (reinterpret_cast<PxU8*>(hits)),
mNbHits (0),
mMaxNbHits (maxNbHits),
mStride (stride),
mWorld_Aligned (mat),
mHitFlags (hitFlags)
{
}
PxU8* mHits;
PxU32 mNbHits;
const PxU32 mMaxNbHits;
const PxU32 mStride;
const PxMat44* mWorld_Aligned;
const PxHitFlags mHitFlags;
PX_NOCOPY(RayParamsAll)
};
class LeafFunction_RaycastAll
{
public:
static /*PX_FORCE_INLINE*/ PxIntBool doLeafTest(RayParams_Raycast* PX_RESTRICT p, PxU32 primIndex)
{
RayParamsAll* params = static_cast<RayParamsAll*>(p);
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
PxGeomRaycastHit& StabbedFace = *reinterpret_cast<PxGeomRaycastHit*>(params->mHits);
if(RayTriOverlapT<RayParams_Raycast>(StabbedFace, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params))
{
#ifndef TEST_DISTANCE_INSIDE_RAY_TRI
if(StabbedFace.distance<params->mStabbedFace.mDistance)
#endif
{
updateParamsAfterImpact(params, primIndex, VRef0, VRef1, VRef2, StabbedFace);
computeImpactData(&StabbedFace, params, params->mWorld_Aligned, params->mHitFlags);
params->mNbHits++;
params->mHits += params->mStride;
if(params->mNbHits==params->mMaxNbHits)
return 1;
}
}
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
// PT: this function is not used yet, but eventually it should be
PxU32 BV4_RaycastAll(const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxGeomRaycastHit* PX_RESTRICT hits, PxU32 maxNbHits, PxU32 stride, float maxDist, float geomEpsilon, PxU32 flags, PxHitFlags hitFlags)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<SourceMesh*>(tree.mMeshInterface);
RayParamsAll Params(hits, maxNbHits, stride, worldm_Aligned, hitFlags);
setupRayParams(&Params, origin, dir, &tree, worldm_Aligned, mesh, maxDist, geomEpsilon, flags);
if(tree.mNodes)
processStreamRayNoOrder<0, LeafFunction_RaycastAll>(tree, &Params);
else
{
PX_ASSERT(0);
}
return Params.mNbHits;
}

View File

@@ -0,0 +1,176 @@
// 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_BV4_SLABS_H
#define GU_BV4_SLABS_H
#include "foundation/PxFPU.h"
#include "GuBV4_Common.h"
#ifdef GU_BV4_USE_SLABS
// PT: contains code for tree-traversal using the swizzled format.
// PT: ray traversal based on Kay & Kajiya's slab intersection code, but using SIMD to do 4 ray-vs-AABB tests at a time.
// PT: other (ordered or unordered) traversals just process one node at a time, similar to the non-swizzled format.
#define BV4_SLABS_FIX
#define BV4_SLABS_SORT
#define PNS_BLOCK3(a, b, c, d) { \
if(code2 & (1<<a)) { stack[nb++] = tn->getChildData(a); } \
if(code2 & (1<<b)) { stack[nb++] = tn->getChildData(b); } \
if(code2 & (1<<c)) { stack[nb++] = tn->getChildData(c); } \
if(code2 & (1<<d)) { stack[nb++] = tn->getChildData(d); } } \
#define OPC_SLABS_GET_MIN_MAX(i) \
const VecI32V minVi = I4LoadXYZW(node->mX[i].mMin, node->mY[i].mMin, node->mZ[i].mMin, 0); \
const Vec4V minCoeffV = V4LoadA_Safe(&params->mCenterOrMinCoeff_PaddedAligned.x); \
Vec4V minV = V4Mul(Vec4V_From_VecI32V(minVi), minCoeffV); \
const VecI32V maxVi = I4LoadXYZW(node->mX[i].mMax, node->mY[i].mMax, node->mZ[i].mMax, 0); \
const Vec4V maxCoeffV = V4LoadA_Safe(&params->mExtentsOrMaxCoeff_PaddedAligned.x); \
Vec4V maxV = V4Mul(Vec4V_From_VecI32V(maxVi), maxCoeffV); \
#define OPC_SLABS_GET_CEQ(i) \
OPC_SLABS_GET_MIN_MAX(i) \
const FloatV HalfV = FLoad(0.5f); \
const Vec4V centerV = V4Scale(V4Add(maxV, minV), HalfV); \
const Vec4V extentsV = V4Scale(V4Sub(maxV, minV), HalfV);
#define OPC_SLABS_GET_CE2Q(i) \
OPC_SLABS_GET_MIN_MAX(i) \
const Vec4V centerV = V4Add(maxV, minV); \
const Vec4V extentsV = V4Sub(maxV, minV);
#define OPC_SLABS_GET_CENQ(i) \
const FloatV HalfV = FLoad(0.5f); \
const Vec4V minV = V4LoadXYZW(node->mMinX[i], node->mMinY[i], node->mMinZ[i], 0.0f); \
const Vec4V maxV = V4LoadXYZW(node->mMaxX[i], node->mMaxY[i], node->mMaxZ[i], 0.0f); \
const Vec4V centerV = V4Scale(V4Add(maxV, minV), HalfV); \
const Vec4V extentsV = V4Scale(V4Sub(maxV, minV), HalfV);
#define OPC_SLABS_GET_CE2NQ(i) \
const Vec4V minV = V4LoadXYZW(node->mMinX[i], node->mMinY[i], node->mMinZ[i], 0.0f); \
const Vec4V maxV = V4LoadXYZW(node->mMaxX[i], node->mMaxY[i], node->mMaxZ[i], 0.0f); \
const Vec4V centerV = V4Add(maxV, minV); \
const Vec4V extentsV = V4Sub(maxV, minV);
#define OPC_DEQ4(part2xV, part1xV, mMember, minCoeff, maxCoeff) \
{ \
part2xV = V4LoadA(reinterpret_cast<const float*>(tn->mMember)); \
part1xV = Vec4V_ReinterpretFrom_VecI32V(VecI32V_And(VecI32V_ReinterpretFrom_Vec4V(part2xV), I4Load(0x0000ffff))); \
part1xV = Vec4V_ReinterpretFrom_VecI32V(VecI32V_RightShift(VecI32V_LeftShift(VecI32V_ReinterpretFrom_Vec4V(part1xV),16), 16)); \
part1xV = V4Mul(Vec4V_From_VecI32V(VecI32V_ReinterpretFrom_Vec4V(part1xV)), minCoeff); \
part2xV = Vec4V_ReinterpretFrom_VecI32V(VecI32V_RightShift(VecI32V_ReinterpretFrom_Vec4V(part2xV), 16)); \
part2xV = V4Mul(Vec4V_From_VecI32V(VecI32V_ReinterpretFrom_Vec4V(part2xV)), maxCoeff); \
}
#define SLABS_INIT\
Vec4V maxT4 = V4Load(params->mStabbedFace.mDistance);\
const Vec4V rayP = V4LoadU_Safe(&params->mOrigin_Padded.x);\
Vec4V rayD = V4LoadU_Safe(&params->mLocalDir_Padded.x);\
const VecU32V raySign = V4U32and(VecU32V_ReinterpretFrom_Vec4V(rayD), signMask);\
const Vec4V rayDAbs = V4Abs(rayD);\
Vec4V rayInvD = Vec4V_ReinterpretFrom_VecU32V(V4U32or(raySign, VecU32V_ReinterpretFrom_Vec4V(V4Max(rayDAbs, epsFloat4))));\
rayD = rayInvD;\
rayInvD = V4RecipFast(rayInvD);\
rayInvD = V4Mul(rayInvD, V4NegMulSub(rayD, rayInvD, twos));\
const Vec4V rayPinvD = V4NegMulSub(rayInvD, rayP, zeroes);\
const Vec4V rayInvDsplatX = V4SplatElement<0>(rayInvD);\
const Vec4V rayInvDsplatY = V4SplatElement<1>(rayInvD);\
const Vec4V rayInvDsplatZ = V4SplatElement<2>(rayInvD);\
const Vec4V rayPinvDsplatX = V4SplatElement<0>(rayPinvD);\
const Vec4V rayPinvDsplatY = V4SplatElement<1>(rayPinvD);\
const Vec4V rayPinvDsplatZ = V4SplatElement<2>(rayPinvD);
#define SLABS_TEST\
const Vec4V tminxa0 = V4MulAdd(minx4a, rayInvDsplatX, rayPinvDsplatX);\
const Vec4V tminya0 = V4MulAdd(miny4a, rayInvDsplatY, rayPinvDsplatY);\
const Vec4V tminza0 = V4MulAdd(minz4a, rayInvDsplatZ, rayPinvDsplatZ);\
const Vec4V tmaxxa0 = V4MulAdd(maxx4a, rayInvDsplatX, rayPinvDsplatX);\
const Vec4V tmaxya0 = V4MulAdd(maxy4a, rayInvDsplatY, rayPinvDsplatY);\
const Vec4V tmaxza0 = V4MulAdd(maxz4a, rayInvDsplatZ, rayPinvDsplatZ);\
const Vec4V tminxa = V4Min(tminxa0, tmaxxa0);\
const Vec4V tmaxxa = V4Max(tminxa0, tmaxxa0);\
const Vec4V tminya = V4Min(tminya0, tmaxya0);\
const Vec4V tmaxya = V4Max(tminya0, tmaxya0);\
const Vec4V tminza = V4Min(tminza0, tmaxza0);\
const Vec4V tmaxza = V4Max(tminza0, tmaxza0);\
const Vec4V maxOfNeasa = V4Max(V4Max(tminxa, tminya), tminza);\
const Vec4V minOfFarsa = V4Min(V4Min(tmaxxa, tmaxya), tmaxza);\
#define SLABS_TEST2\
BoolV ignore4a = V4IsGrtr(epsFloat4, minOfFarsa); /* if tfar is negative, ignore since its a ray, not a line */\
ignore4a = BOr(ignore4a, V4IsGrtr(maxOfNeasa, maxT4)); /* if tnear is over maxT, ignore this result */\
BoolV resa4 = V4IsGrtr(maxOfNeasa, minOfFarsa); /* if 1 => fail */\
resa4 = BOr(resa4, ignore4a);\
const PxU32 code = BGetBitMask(resa4);\
if(code==15)\
continue;
#define SLABS_PNS \
if(code2) \
{ \
if(tn->decodePNSNoShift(0) & dirMask) \
{ \
if(tn->decodePNSNoShift(1) & dirMask) \
{ \
if(tn->decodePNSNoShift(2) & dirMask) \
PNS_BLOCK3(3,2,1,0) \
else \
PNS_BLOCK3(2,3,1,0) \
} \
else \
{ \
if(tn->decodePNSNoShift(2) & dirMask) \
PNS_BLOCK3(3,2,0,1) \
else \
PNS_BLOCK3(2,3,0,1) \
} \
} \
else \
{ \
if(tn->decodePNSNoShift(1) & dirMask) \
{ \
if(tn->decodePNSNoShift(2) & dirMask) \
PNS_BLOCK3(1,0,3,2) \
else \
PNS_BLOCK3(1,0,2,3) \
} \
else \
{ \
if(tn->decodePNSNoShift(2) & dirMask) \
PNS_BLOCK3(0,1,3,2) \
else \
PNS_BLOCK3(0,1,2,3) \
} \
} \
}
#endif // GU_BV4_USE_SLABS
#endif // GU_BV4_SLABS_H

View File

@@ -0,0 +1,305 @@
// 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_BV4_SLABS_KAJIYA_NO_ORDER_H
#define GU_BV4_SLABS_KAJIYA_NO_ORDER_H
#include "GuBVConstants.h"
#ifdef REMOVED
// Kajiya, no sort
template<int inflateT, class LeafTestT, class ParamsT>
static PxIntBool BV4_ProcessStreamKajiyaNoOrder(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPacked* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
///
Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ;
if(inflateT)
{
Vec4V fattenAABBs4 = V4LoadU_Safe(&params->mOriginalExtents_Padded.x);
fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap
fattenAABBsX = V4SplatElement<0>(fattenAABBs4);
fattenAABBsY = V4SplatElement<1>(fattenAABBs4);
fattenAABBsZ = V4SplatElement<2>(fattenAABBs4);
}
///
SLABS_INIT
#ifdef GU_BV4_QUANTIZED_TREE
const Vec4V minCoeffV = V4LoadA_Safe(&params->mCenterOrMinCoeff_PaddedAligned.x);
const Vec4V maxCoeffV = V4LoadA_Safe(&params->mExtentsOrMaxCoeff_PaddedAligned.x);
const Vec4V minCoeffxV = V4SplatElement<0>(minCoeffV);
const Vec4V minCoeffyV = V4SplatElement<1>(minCoeffV);
const Vec4V minCoeffzV = V4SplatElement<2>(minCoeffV);
const Vec4V maxCoeffxV = V4SplatElement<0>(maxCoeffV);
const Vec4V maxCoeffyV = V4SplatElement<1>(maxCoeffV);
const Vec4V maxCoeffzV = V4SplatElement<2>(maxCoeffV);
#endif
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const BVDataSwizzled* tn = reinterpret_cast<const BVDataSwizzled*>(node);
#ifdef GU_BV4_QUANTIZED_TREE
Vec4V minx4a;
Vec4V maxx4a;
OPC_DEQ4(maxx4a, minx4a, mX, minCoeffxV, maxCoeffxV)
Vec4V miny4a;
Vec4V maxy4a;
OPC_DEQ4(maxy4a, miny4a, mY, minCoeffyV, maxCoeffyV)
Vec4V minz4a;
Vec4V maxz4a;
OPC_DEQ4(maxz4a, minz4a, mZ, minCoeffzV, maxCoeffzV)
#else
Vec4V minx4a = V4LoadA(tn->mMinX);
Vec4V miny4a = V4LoadA(tn->mMinY);
Vec4V minz4a = V4LoadA(tn->mMinZ);
Vec4V maxx4a = V4LoadA(tn->mMaxX);
Vec4V maxy4a = V4LoadA(tn->mMaxY);
Vec4V maxz4a = V4LoadA(tn->mMaxZ);
#endif
if(inflateT)
{
maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ);
minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ);
}
SLABS_TEST
SLABS_TEST2
#define DO_LEAF_TEST(x) \
{if(tn->isLeaf(x)) \
{ \
if(LeafTestT::doLeafTest(params, tn->getPrimitive(x))) \
return 1; \
} \
else \
stack[nb++] = tn->getChildData(x);}
const PxU32 nodeType = getChildType(childData);
if(!(code&8) && nodeType>1)
DO_LEAF_TEST(3)
if(!(code&4) && nodeType>0)
DO_LEAF_TEST(2)
if(!(code&2))
DO_LEAF_TEST(1)
if(!(code&1))
DO_LEAF_TEST(0)
}while(nb);
return 0;
}
#undef DO_LEAF_TEST
#endif
#define DO_LEAF_TEST(x) \
{if(tn->isLeaf(x)) \
{ \
if(LeafTestT::doLeafTest(params, tn->getPrimitive(x))) \
return 1; \
} \
else \
stack[nb++] = tn->getChildData(x);}
// Kajiya, no sort
template<int inflateT, class LeafTestT, class ParamsT>
static PxIntBool BV4_ProcessStreamKajiyaNoOrderQ(const BVDataPackedQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPackedQ* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
///
Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ;
if(inflateT)
{
Vec4V fattenAABBs4 = V4LoadU_Safe(&params->mOriginalExtents_Padded.x);
fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap
fattenAABBsX = V4SplatElement<0>(fattenAABBs4);
fattenAABBsY = V4SplatElement<1>(fattenAABBs4);
fattenAABBsZ = V4SplatElement<2>(fattenAABBs4);
}
///
SLABS_INIT
const Vec4V minCoeffV = V4LoadA_Safe(&params->mCenterOrMinCoeff_PaddedAligned.x);
const Vec4V maxCoeffV = V4LoadA_Safe(&params->mExtentsOrMaxCoeff_PaddedAligned.x);
const Vec4V minCoeffxV = V4SplatElement<0>(minCoeffV);
const Vec4V minCoeffyV = V4SplatElement<1>(minCoeffV);
const Vec4V minCoeffzV = V4SplatElement<2>(minCoeffV);
const Vec4V maxCoeffxV = V4SplatElement<0>(maxCoeffV);
const Vec4V maxCoeffyV = V4SplatElement<1>(maxCoeffV);
const Vec4V maxCoeffzV = V4SplatElement<2>(maxCoeffV);
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const BVDataSwizzledQ* tn = reinterpret_cast<const BVDataSwizzledQ*>(node);
Vec4V minx4a;
Vec4V maxx4a;
OPC_DEQ4(maxx4a, minx4a, mX, minCoeffxV, maxCoeffxV)
Vec4V miny4a;
Vec4V maxy4a;
OPC_DEQ4(maxy4a, miny4a, mY, minCoeffyV, maxCoeffyV)
Vec4V minz4a;
Vec4V maxz4a;
OPC_DEQ4(maxz4a, minz4a, mZ, minCoeffzV, maxCoeffzV)
if(inflateT)
{
maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ);
minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ);
}
SLABS_TEST
SLABS_TEST2
const PxU32 nodeType = getChildType(childData);
if(!(code&8) && nodeType>1)
DO_LEAF_TEST(3)
if(!(code&4) && nodeType>0)
DO_LEAF_TEST(2)
if(!(code&2))
DO_LEAF_TEST(1)
if(!(code&1))
DO_LEAF_TEST(0)
}while(nb);
return 0;
}
// Kajiya, no sort
template<int inflateT, class LeafTestT, class ParamsT>
static PxIntBool BV4_ProcessStreamKajiyaNoOrderNQ(const BVDataPackedNQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPackedNQ* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
///
Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ;
if(inflateT)
{
Vec4V fattenAABBs4 = V4LoadU_Safe(&params->mOriginalExtents_Padded.x);
fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap
fattenAABBsX = V4SplatElement<0>(fattenAABBs4);
fattenAABBsY = V4SplatElement<1>(fattenAABBs4);
fattenAABBsZ = V4SplatElement<2>(fattenAABBs4);
}
///
SLABS_INIT
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const BVDataSwizzledNQ* tn = reinterpret_cast<const BVDataSwizzledNQ*>(node);
Vec4V minx4a = V4LoadA(tn->mMinX);
Vec4V miny4a = V4LoadA(tn->mMinY);
Vec4V minz4a = V4LoadA(tn->mMinZ);
Vec4V maxx4a = V4LoadA(tn->mMaxX);
Vec4V maxy4a = V4LoadA(tn->mMaxY);
Vec4V maxz4a = V4LoadA(tn->mMaxZ);
if(inflateT)
{
maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ);
minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ);
}
SLABS_TEST
SLABS_TEST2
const PxU32 nodeType = getChildType(childData);
if(!(code&8) && nodeType>1)
DO_LEAF_TEST(3)
if(!(code&4) && nodeType>0)
DO_LEAF_TEST(2)
if(!(code&2))
DO_LEAF_TEST(1)
if(!(code&1))
DO_LEAF_TEST(0)
}while(nb);
return 0;
}
#undef DO_LEAF_TEST
#endif // GU_BV4_SLABS_KAJIYA_NO_ORDER_H

View File

@@ -0,0 +1,570 @@
// 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_BV4_SLABS_KAJIYA_ORDERED_H
#define GU_BV4_SLABS_KAJIYA_ORDERED_H
#include "GuBVConstants.h"
#ifdef REMOVED
// Kajiya + PNS
template<const int inflateT, class LeafTestT, class ParamsT>
static void BV4_ProcessStreamKajiyaOrdered(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPacked* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
#ifdef BV4_SLABS_SORT
const PxU32* tmp = reinterpret_cast<const PxU32*>(&params->mLocalDir_Padded);
const PxU32 X = tmp[0]>>31;
const PxU32 Y = tmp[1]>>31;
const PxU32 Z = tmp[2]>>31;
// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31;
// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31;
// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31;
const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2));
const PxU32 dirMask = 1u<<bitIndex;
#endif
#ifdef BV4_SLABS_FIX
BV4_ALIGN16(float distances4[4]);
#endif
///
Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ;
if(inflateT)
{
Vec4V fattenAABBs4 = V4LoadU_Safe(&params->mOriginalExtents_Padded.x);
fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap
fattenAABBsX = V4SplatElement<0>(fattenAABBs4);
fattenAABBsY = V4SplatElement<1>(fattenAABBs4);
fattenAABBsZ = V4SplatElement<2>(fattenAABBs4);
}
///
SLABS_INIT
#ifdef GU_BV4_QUANTIZED_TREE
const Vec4V minCoeffV = V4LoadA_Safe(&params->mCenterOrMinCoeff_PaddedAligned.x);
const Vec4V maxCoeffV = V4LoadA_Safe(&params->mExtentsOrMaxCoeff_PaddedAligned.x);
const Vec4V minCoeffxV = V4SplatElement<0>(minCoeffV);
const Vec4V minCoeffyV = V4SplatElement<1>(minCoeffV);
const Vec4V minCoeffzV = V4SplatElement<2>(minCoeffV);
const Vec4V maxCoeffxV = V4SplatElement<0>(maxCoeffV);
const Vec4V maxCoeffyV = V4SplatElement<1>(maxCoeffV);
const Vec4V maxCoeffzV = V4SplatElement<2>(maxCoeffV);
#endif
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const BVDataSwizzled* tn = reinterpret_cast<const BVDataSwizzled*>(node);
#ifdef GU_BV4_QUANTIZED_TREE
Vec4V minx4a;
Vec4V maxx4a;
OPC_DEQ4(maxx4a, minx4a, mX, minCoeffxV, maxCoeffxV)
Vec4V miny4a;
Vec4V maxy4a;
OPC_DEQ4(maxy4a, miny4a, mY, minCoeffyV, maxCoeffyV)
Vec4V minz4a;
Vec4V maxz4a;
OPC_DEQ4(maxz4a, minz4a, mZ, minCoeffzV, maxCoeffzV)
#else
Vec4V minx4a = V4LoadA(tn->mMinX);
Vec4V miny4a = V4LoadA(tn->mMinY);
Vec4V minz4a = V4LoadA(tn->mMinZ);
Vec4V maxx4a = V4LoadA(tn->mMaxX);
Vec4V maxy4a = V4LoadA(tn->mMaxY);
Vec4V maxz4a = V4LoadA(tn->mMaxZ);
#endif
if(inflateT)
{
maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ);
minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ);
}
SLABS_TEST
#ifdef BV4_SLABS_FIX
if(inflateT)
V4StoreA(maxOfNeasa, &distances4[0]);
#endif
SLABS_TEST2
#ifdef BV4_SLABS_SORT
#ifdef BV4_SLABS_FIX
// PT: for some unknown reason the Linux/OSX compilers fail to understand this version
/* #define DO_LEAF_TEST(x) \
{ \
if(!inflateT) \
{ \
if(tn->isLeaf(x)) \
{ \
LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \
maxT4 = V4Load(params->mStabbedFace.mDistance); \
} \
else \
{ \
code2 |= 1<<x; \
} \
} \
else \
{ \
if(distances4[x]<params->mStabbedFace.mDistance) \
{ \
if(tn->isLeaf(x)) \
{ \
LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \
maxT4 = V4Load(params->mStabbedFace.mDistance); \
} \
else \
{ \
code2 |= 1<<x; \
} \
} \
} \
}*/
// PT: TODO: check that this version compiles to the same code as above. Redo benchmarks.
#define DO_LEAF_TEST(x) \
{ \
if(!inflateT || distances4[x]<params->mStabbedFace.mDistance + GU_EPSILON_SAME_DISTANCE) \
{ \
if(tn->isLeaf(x)) \
{ \
LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \
maxT4 = V4Load(params->mStabbedFace.mDistance); \
} \
else \
{ \
code2 |= 1<<x; \
} \
} \
}
#else
#define DO_LEAF_TEST(x) \
{ \
if(tn->isLeaf(x)) \
{ \
LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \
maxT4 = V4Load(params->mStabbedFace.mDistance); \
} \
else \
{ \
code2 |= 1<<x; \
} \
}
#endif
PxU32 code2 = 0;
const PxU32 nodeType = getChildType(childData);
if(!(code&8) && nodeType>1)
DO_LEAF_TEST(3)
if(!(code&4) && nodeType>0)
DO_LEAF_TEST(2)
if(!(code&2))
DO_LEAF_TEST(1)
if(!(code&1))
DO_LEAF_TEST(0)
SLABS_PNS
#else
#define DO_LEAF_TEST(x) \
{if(tn->isLeaf(x)) \
{ \
LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \
maxT4 = V4Load(params->mStabbedFace.mDistance); \
} \
else \
{ \
stack[nb++] = tn->getChildData(x); \
}}
const PxU32 nodeType = getChildType(childData);
if(!(code&8) && nodeType>1)
DO_LEAF_TEST(3)
if(!(code&4) && nodeType>0)
DO_LEAF_TEST(2)
if(!(code&2))
DO_LEAF_TEST(1)
if(!(code&1))
DO_LEAF_TEST(0)
#endif
}while(nb);
}
#undef DO_LEAF_TEST
#endif
#ifdef BV4_SLABS_SORT
#ifdef BV4_SLABS_FIX
// PT: for some unknown reason the Linux/OSX compilers fail to understand this version
/* #define DO_LEAF_TEST(x) \
{ \
if(!inflateT) \
{ \
if(tn->isLeaf(x)) \
{ \
LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \
maxT4 = V4Load(params->mStabbedFace.mDistance); \
} \
else \
{ \
code2 |= 1<<x; \
} \
} \
else \
{ \
if(distances4[x]<params->mStabbedFace.mDistance) \
{ \
if(tn->isLeaf(x)) \
{ \
LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \
maxT4 = V4Load(params->mStabbedFace.mDistance); \
} \
else \
{ \
code2 |= 1<<x; \
} \
} \
} \
}*/
// PT: TODO: check that this version compiles to the same code as above. Redo benchmarks.
#define DO_LEAF_TEST(x) \
{ \
if(!inflateT || distances4[x]<params->mStabbedFace.mDistance + GU_EPSILON_SAME_DISTANCE) \
{ \
if(tn->isLeaf(x)) \
{ \
LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \
maxT4 = V4Load(params->mStabbedFace.mDistance); \
} \
else \
{ \
code2 |= 1<<x; nbHits++; \
} \
} \
}
#else
#define DO_LEAF_TEST(x) \
{ \
if(tn->isLeaf(x)) \
{ \
LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \
maxT4 = V4Load(params->mStabbedFace.mDistance); \
} \
else \
{ \
code2 |= 1<<x; nbHits++; \
} \
}
#endif
#else
#define DO_LEAF_TEST(x) \
{if(tn->isLeaf(x)) \
{ \
LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \
maxT4 = V4Load(params->mStabbedFace.mDistance); \
} \
else \
{ \
stack[nb++] = tn->getChildData(x); \
}}
#endif
// Kajiya + PNS
template<const int inflateT, class LeafTestT, class ParamsT>
static void BV4_ProcessStreamKajiyaOrderedQ(const BVDataPackedQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPackedQ* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
#ifdef BV4_SLABS_SORT
const PxU32* tmp = reinterpret_cast<const PxU32*>(&params->mLocalDir_Padded);
const PxU32 X = tmp[0]>>31;
const PxU32 Y = tmp[1]>>31;
const PxU32 Z = tmp[2]>>31;
// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31;
// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31;
// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31;
const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2));
const PxU32 dirMask = 1u<<bitIndex;
#endif
#ifdef BV4_SLABS_FIX
BV4_ALIGN16(float distances4[4]);
#endif
///
Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ;
if(inflateT)
{
Vec4V fattenAABBs4 = V4LoadU_Safe(&params->mOriginalExtents_Padded.x);
fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap
fattenAABBsX = V4SplatElement<0>(fattenAABBs4);
fattenAABBsY = V4SplatElement<1>(fattenAABBs4);
fattenAABBsZ = V4SplatElement<2>(fattenAABBs4);
}
///
SLABS_INIT
const Vec4V minCoeffV = V4LoadA_Safe(&params->mCenterOrMinCoeff_PaddedAligned.x);
const Vec4V maxCoeffV = V4LoadA_Safe(&params->mExtentsOrMaxCoeff_PaddedAligned.x);
const Vec4V minCoeffxV = V4SplatElement<0>(minCoeffV);
const Vec4V minCoeffyV = V4SplatElement<1>(minCoeffV);
const Vec4V minCoeffzV = V4SplatElement<2>(minCoeffV);
const Vec4V maxCoeffxV = V4SplatElement<0>(maxCoeffV);
const Vec4V maxCoeffyV = V4SplatElement<1>(maxCoeffV);
const Vec4V maxCoeffzV = V4SplatElement<2>(maxCoeffV);
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const BVDataSwizzledQ* tn = reinterpret_cast<const BVDataSwizzledQ*>(node);
Vec4V minx4a;
Vec4V maxx4a;
OPC_DEQ4(maxx4a, minx4a, mX, minCoeffxV, maxCoeffxV)
Vec4V miny4a;
Vec4V maxy4a;
OPC_DEQ4(maxy4a, miny4a, mY, minCoeffyV, maxCoeffyV)
Vec4V minz4a;
Vec4V maxz4a;
OPC_DEQ4(maxz4a, minz4a, mZ, minCoeffzV, maxCoeffzV)
if(inflateT)
{
maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ);
minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ);
}
SLABS_TEST
#ifdef BV4_SLABS_FIX
if(inflateT)
V4StoreA(maxOfNeasa, &distances4[0]);
#endif
SLABS_TEST2
#ifdef BV4_SLABS_SORT
PxU32 code2 = 0;
PxU32 nbHits = 0;
const PxU32 nodeType = getChildType(childData);
if(!(code&8) && nodeType>1)
DO_LEAF_TEST(3)
if(!(code&4) && nodeType>0)
DO_LEAF_TEST(2)
if(!(code&2))
DO_LEAF_TEST(1)
if(!(code&1))
DO_LEAF_TEST(0)
//SLABS_PNS
if(nbHits==1)
{
PNS_BLOCK3(0,1,2,3)
}
else
{
SLABS_PNS
}
#else
const PxU32 nodeType = getChildType(childData);
if(!(code&8) && nodeType>1)
DO_LEAF_TEST(3)
if(!(code&4) && nodeType>0)
DO_LEAF_TEST(2)
if(!(code&2))
DO_LEAF_TEST(1)
if(!(code&1))
DO_LEAF_TEST(0)
#endif
}while(nb);
}
// Kajiya + PNS
template<const int inflateT, class LeafTestT, class ParamsT>
static void BV4_ProcessStreamKajiyaOrderedNQ(const BVDataPackedNQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPackedNQ* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
#ifdef BV4_SLABS_SORT
const PxU32* tmp = reinterpret_cast<const PxU32*>(&params->mLocalDir_Padded);
const PxU32 X = tmp[0]>>31;
const PxU32 Y = tmp[1]>>31;
const PxU32 Z = tmp[2]>>31;
// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31;
// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31;
// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31;
const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2));
const PxU32 dirMask = 1u<<bitIndex;
#endif
#ifdef BV4_SLABS_FIX
BV4_ALIGN16(float distances4[4]);
#endif
///
Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ;
if(inflateT)
{
Vec4V fattenAABBs4 = V4LoadU_Safe(&params->mOriginalExtents_Padded.x);
fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap
fattenAABBsX = V4SplatElement<0>(fattenAABBs4);
fattenAABBsY = V4SplatElement<1>(fattenAABBs4);
fattenAABBsZ = V4SplatElement<2>(fattenAABBs4);
}
///
SLABS_INIT
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const BVDataSwizzledNQ* tn = reinterpret_cast<const BVDataSwizzledNQ*>(node);
Vec4V minx4a = V4LoadA(tn->mMinX);
Vec4V miny4a = V4LoadA(tn->mMinY);
Vec4V minz4a = V4LoadA(tn->mMinZ);
Vec4V maxx4a = V4LoadA(tn->mMaxX);
Vec4V maxy4a = V4LoadA(tn->mMaxY);
Vec4V maxz4a = V4LoadA(tn->mMaxZ);
if(inflateT)
{
maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ);
minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ);
}
SLABS_TEST
#ifdef BV4_SLABS_FIX
if(inflateT)
V4StoreA(maxOfNeasa, &distances4[0]);
#endif
SLABS_TEST2
#ifdef BV4_SLABS_SORT
PxU32 code2 = 0;
PxU32 nbHits = 0;
const PxU32 nodeType = getChildType(childData);
if(!(code&8) && nodeType>1)
DO_LEAF_TEST(3)
if(!(code&4) && nodeType>0)
DO_LEAF_TEST(2)
if(!(code&2))
DO_LEAF_TEST(1)
if(!(code&1))
DO_LEAF_TEST(0)
//SLABS_PNS
if(nbHits==1)
{
PNS_BLOCK3(0,1,2,3)
}
else
{
SLABS_PNS
}
#else
const PxU32 nodeType = getChildType(childData);
if(!(code&8) && nodeType>1)
DO_LEAF_TEST(3)
if(!(code&4) && nodeType>0)
DO_LEAF_TEST(2)
if(!(code&2))
DO_LEAF_TEST(1)
if(!(code&1))
DO_LEAF_TEST(0)
#endif
}while(nb);
}
#undef DO_LEAF_TEST
#endif // GU_BV4_SLABS_KAJIYA_ORDERED_H

View File

@@ -0,0 +1,129 @@
// 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_BV4_SLABS_SWIZZLED_NO_ORDER_H
#define GU_BV4_SLABS_SWIZZLED_NO_ORDER_H
// Generic, no sort
/* template<class LeafTestT, class ParamsT>
static PxIntBool BV4_ProcessStreamSwizzledNoOrder(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPacked* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const BVDataSwizzled* tn = reinterpret_cast<const BVDataSwizzled*>(node);
const PxU32 nodeType = getChildType(childData);
if(nodeType>1 && BV4_ProcessNodeNoOrder_Swizzled<LeafTestT, 3>(stack, nb, tn, params))
return 1;
if(nodeType>0 && BV4_ProcessNodeNoOrder_Swizzled<LeafTestT, 2>(stack, nb, tn, params))
return 1;
if(BV4_ProcessNodeNoOrder_Swizzled<LeafTestT, 1>(stack, nb, tn, params))
return 1;
if(BV4_ProcessNodeNoOrder_Swizzled<LeafTestT, 0>(stack, nb, tn, params))
return 1;
}while(nb);
return 0;
}*/
template<class LeafTestT, class ParamsT>
static PxIntBool BV4_ProcessStreamSwizzledNoOrderQ(const BVDataPackedQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPackedQ* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const BVDataSwizzledQ* tn = reinterpret_cast<const BVDataSwizzledQ*>(node);
const PxU32 nodeType = getChildType(childData);
if(nodeType>1 && BV4_ProcessNodeNoOrder_SwizzledQ<LeafTestT, 3>(stack, nb, tn, params))
return 1;
if(nodeType>0 && BV4_ProcessNodeNoOrder_SwizzledQ<LeafTestT, 2>(stack, nb, tn, params))
return 1;
if(BV4_ProcessNodeNoOrder_SwizzledQ<LeafTestT, 1>(stack, nb, tn, params))
return 1;
if(BV4_ProcessNodeNoOrder_SwizzledQ<LeafTestT, 0>(stack, nb, tn, params))
return 1;
}while(nb);
return 0;
}
template<class LeafTestT, class ParamsT>
static PxIntBool BV4_ProcessStreamSwizzledNoOrderNQ(const BVDataPackedNQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPackedNQ* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const BVDataSwizzledNQ* tn = reinterpret_cast<const BVDataSwizzledNQ*>(node);
const PxU32 nodeType = getChildType(childData);
if(nodeType>1 && BV4_ProcessNodeNoOrder_SwizzledNQ<LeafTestT, 3>(stack, nb, tn, params))
return 1;
if(nodeType>0 && BV4_ProcessNodeNoOrder_SwizzledNQ<LeafTestT, 2>(stack, nb, tn, params))
return 1;
if(BV4_ProcessNodeNoOrder_SwizzledNQ<LeafTestT, 1>(stack, nb, tn, params))
return 1;
if(BV4_ProcessNodeNoOrder_SwizzledNQ<LeafTestT, 0>(stack, nb, tn, params))
return 1;
}while(nb);
return 0;
}
#endif // GU_BV4_SLABS_SWIZZLED_NO_ORDER_H

View File

@@ -0,0 +1,156 @@
// 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_BV4_SLABS_SWIZZLED_ORDERED_H
#define GU_BV4_SLABS_SWIZZLED_ORDERED_H
// Generic + PNS
/* template<class LeafTestT, class ParamsT>
static void BV4_ProcessStreamSwizzledOrdered(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPacked* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
const PxU32* tmp = reinterpret_cast<const PxU32*>(&params->mLocalDir_Padded);
const PxU32 X = tmp[0]>>31;
const PxU32 Y = tmp[1]>>31;
const PxU32 Z = tmp[2]>>31;
// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31;
// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31;
// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31;
const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2));
const PxU32 dirMask = 1u<<bitIndex;
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const PxU32 nodeType = getChildType(childData);
const BVDataSwizzled* tn = reinterpret_cast<const BVDataSwizzled*>(node);
PxU32 code2 = 0;
BV4_ProcessNodeOrdered2_Swizzled<LeafTestT, 0>(code2, tn, params);
BV4_ProcessNodeOrdered2_Swizzled<LeafTestT, 1>(code2, tn, params);
if(nodeType>0)
BV4_ProcessNodeOrdered2_Swizzled<LeafTestT, 2>(code2, tn, params);
if(nodeType>1)
BV4_ProcessNodeOrdered2_Swizzled<LeafTestT, 3>(code2, tn, params);
SLABS_PNS
}while(nb);
}*/
// Generic + PNS
template<class LeafTestT, class ParamsT>
static void BV4_ProcessStreamSwizzledOrderedQ(const BVDataPackedQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPackedQ* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
const PxU32* tmp = reinterpret_cast<const PxU32*>(&params->mLocalDir_Padded);
const PxU32 X = tmp[0]>>31;
const PxU32 Y = tmp[1]>>31;
const PxU32 Z = tmp[2]>>31;
// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31;
// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31;
// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31;
const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2));
const PxU32 dirMask = 1u<<bitIndex;
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const PxU32 nodeType = getChildType(childData);
const BVDataSwizzledQ* tn = reinterpret_cast<const BVDataSwizzledQ*>(node);
PxU32 code2 = 0;
BV4_ProcessNodeOrdered2_SwizzledQ<LeafTestT, 0>(code2, tn, params);
BV4_ProcessNodeOrdered2_SwizzledQ<LeafTestT, 1>(code2, tn, params);
if(nodeType>0)
BV4_ProcessNodeOrdered2_SwizzledQ<LeafTestT, 2>(code2, tn, params);
if(nodeType>1)
BV4_ProcessNodeOrdered2_SwizzledQ<LeafTestT, 3>(code2, tn, params);
SLABS_PNS
}while(nb);
}
// Generic + PNS
template<class LeafTestT, class ParamsT>
static void BV4_ProcessStreamSwizzledOrderedNQ(const BVDataPackedNQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params)
{
const BVDataPackedNQ* root = node;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
const PxU32* tmp = reinterpret_cast<const PxU32*>(&params->mLocalDir_Padded);
const PxU32 X = tmp[0]>>31;
const PxU32 Y = tmp[1]>>31;
const PxU32 Z = tmp[2]>>31;
// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31;
// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31;
// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31;
const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2));
const PxU32 dirMask = 1u<<bitIndex;
do
{
const PxU32 childData = stack[--nb];
node = root + getChildOffset(childData);
const PxU32 nodeType = getChildType(childData);
const BVDataSwizzledNQ* tn = reinterpret_cast<const BVDataSwizzledNQ*>(node);
PxU32 code2 = 0;
BV4_ProcessNodeOrdered2_SwizzledNQ<LeafTestT, 0>(code2, tn, params);
BV4_ProcessNodeOrdered2_SwizzledNQ<LeafTestT, 1>(code2, tn, params);
if(nodeType>0)
BV4_ProcessNodeOrdered2_SwizzledNQ<LeafTestT, 2>(code2, tn, params);
if(nodeType>1)
BV4_ProcessNodeOrdered2_SwizzledNQ<LeafTestT, 3>(code2, tn, params);
SLABS_PNS
}while(nb);
}
#endif // GU_BV4_SLABS_SWIZZLED_ORDERED_H

View File

@@ -0,0 +1,617 @@
// 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 "GuBV4.h"
using namespace physx;
using namespace Gu;
#include "foundation/PxBasicTemplates.h"
#include "foundation/PxVecMath.h"
using namespace aos;
#include "GuBV4_Common.h"
#include "GuSphere.h"
#include "GuDistancePointTriangle.h"
#if PX_VC
#pragma warning ( disable : 4324 )
#endif
// Sphere overlap any
struct SphereParams
{
const IndTri32* PX_RESTRICT mTris32;
const IndTri16* PX_RESTRICT mTris16;
const PxVec3* PX_RESTRICT mVerts;
BV4_ALIGN16(PxVec3p mCenterOrMinCoeff_PaddedAligned);
BV4_ALIGN16(PxVec3p mExtentsOrMaxCoeff_PaddedAligned);
BV4_ALIGN16(PxVec3 mCenter_PaddedAligned); float mRadius2;
#ifdef GU_BV4_USE_SLABS
BV4_ALIGN16(PxVec3 mCenter_PaddedAligned2); float mRadius22;
#endif
};
#ifndef GU_BV4_USE_SLABS
// PT: TODO: refactor with bucket pruner code (TA34704)
static PX_FORCE_INLINE PxIntBool BV4_SphereAABBOverlap(const PxVec3& center, const PxVec3& extents, const SphereParams* PX_RESTRICT params)
{
const Vec4V mCenter = V4LoadA_Safe(&params->mCenter_PaddedAligned.x);
const FloatV mRadius2 = FLoad(params->mRadius2);
const Vec4V boxCenter = V4LoadU(&center.x);
const Vec4V boxExtents = V4LoadU(&extents.x);
const Vec4V offset = V4Sub(mCenter, boxCenter);
const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents);
const Vec4V d = V4Sub(offset, closest);
const PxU32 test = (PxU32)_mm_movemask_ps(FIsGrtrOrEq(mRadius2, V4Dot3(d, d)));
return (test & 0x7) == 0x7;
}
#endif
static PX_FORCE_INLINE PxIntBool SphereTriangle(const SphereParams* PX_RESTRICT params, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2)
{
{
const float sqrDist = (p0 - params->mCenter_PaddedAligned).magnitudeSquared();
if(sqrDist <= params->mRadius2)
return 1;
}
const PxVec3 edge10 = p1 - p0;
const PxVec3 edge20 = p2 - p0;
const PxVec3 cp = closestPtPointTriangle2(params->mCenter_PaddedAligned, p0, p1, p2, edge10, edge20);
const float sqrDist = (cp - params->mCenter_PaddedAligned).magnitudeSquared();
return sqrDist <= params->mRadius2;
}
// PT: TODO: evaluate if SIMD distance function would be faster here (TA34704)
// PT: TODO: __fastcall removed to make it compile everywhere. Revisit.
static /*PX_FORCE_INLINE*/ PxIntBool /*__fastcall*/ SphereTriangle(const SphereParams* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
return SphereTriangle(params, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2]);
}
namespace
{
class LeafFunction_SphereOverlapAny
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(const SphereParams* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
if(SphereTriangle(params, primIndex))
return 1;
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
template<class ParamsT>
static PX_FORCE_INLINE void setupSphereParams(ParamsT* PX_RESTRICT params, const Sphere& sphere, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT worldm_Aligned, const SourceMesh* PX_RESTRICT mesh)
{
computeLocalSphere(params->mRadius2, params->mCenter_PaddedAligned, sphere, worldm_Aligned);
#ifdef GU_BV4_USE_SLABS
params->mCenter_PaddedAligned2 = params->mCenter_PaddedAligned*2.0f;
params->mRadius22 = params->mRadius2*4.0f;
#endif
setupMeshPointersAndQuantizedCoeffs(params, mesh, tree);
}
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs.h"
static PX_FORCE_INLINE PxIntBool BV4_SphereAABBOverlap(const Vec4V boxCenter, const Vec4V boxExtents, const SphereParams* PX_RESTRICT params)
{
const Vec4V mCenter = V4LoadA_Safe(&params->mCenter_PaddedAligned2.x);
const FloatV mRadius2 = FLoad(params->mRadius22);
const Vec4V offset = V4Sub(mCenter, boxCenter);
const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents);
const Vec4V d = V4Sub(offset, closest);
const PxU32 test = BGetBitMask(FIsGrtrOrEq(mRadius2, V4Dot3(d, d)));
return (test & 0x7) == 0x7;
}
#else
#ifdef GU_BV4_QUANTIZED_TREE
static PX_FORCE_INLINE PxIntBool BV4_SphereAABBOverlap(const BVDataPacked* PX_RESTRICT node, const SphereParams* PX_RESTRICT params)
{
const VecI32V testV = I4LoadA((const PxI32*)&node->mAABB.mData[0]);
const VecI32V qextentsV = VecI32V_And(testV, I4LoadXYZW(0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff));
const VecI32V qcenterV = VecI32V_RightShift(testV, 16);
const Vec4V boxCenter = V4Mul(Vec4V_From_VecI32V(qcenterV), V4LoadA_Safe(&params->mCenterOrMinCoeff_PaddedAligned.x));
const Vec4V boxExtents = V4Mul(Vec4V_From_VecI32V(qextentsV), V4LoadA_Safe(&params->mExtentsOrMaxCoeff_PaddedAligned.x));
const Vec4V mCenter = V4LoadA_Safe(&params->mCenter_PaddedAligned.x);
const FloatV mRadius2 = FLoad(params->mRadius2);
const Vec4V offset = V4Sub(mCenter, boxCenter);
const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents);
const Vec4V d = V4Sub(offset, closest);
const PxU32 test = BGetBitMask(FIsGrtrOrEq(mRadius2, V4Dot3(d, d)));
return (test & 0x7) == 0x7;
}
#endif
#endif
#include "GuBV4_ProcessStreamNoOrder_SphereAABB.h"
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs_SwizzledNoOrder.h"
#endif
#define GU_BV4_PROCESS_STREAM_NO_ORDER
#include "GuBV4_Internal.h"
PxIntBool BV4_OverlapSphereAny(const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
SphereParams Params;
setupSphereParams(&Params, sphere, &tree, worldm_Aligned, mesh);
if(tree.mNodes)
return processStreamNoOrder<LeafFunction_SphereOverlapAny>(tree, &Params);
else
{
const PxU32 nbTris = mesh->getNbPrimitives();
PX_ASSERT(nbTris<16);
return LeafFunction_SphereOverlapAny::doLeafTest(&Params, nbTris);
}
}
// Sphere overlap all
struct SphereParamsAll : SphereParams
{
PxU32 mNbHits;
PxU32 mMaxNbHits;
PxU32* mHits;
};
namespace
{
class LeafFunction_SphereOverlapAll
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(SphereParams* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
if(SphereTriangle(params, primIndex))
{
SphereParamsAll* ParamsAll = static_cast<SphereParamsAll*>(params);
if(ParamsAll->mNbHits==ParamsAll->mMaxNbHits)
return 1;
ParamsAll->mHits[ParamsAll->mNbHits] = primIndex;
ParamsAll->mNbHits++;
}
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
PxU32 BV4_OverlapSphereAll(const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
SphereParamsAll Params;
Params.mNbHits = 0;
Params.mMaxNbHits = size;
Params.mHits = results;
setupSphereParams(&Params, sphere, &tree, worldm_Aligned, mesh);
if(tree.mNodes)
overflow = processStreamNoOrder<LeafFunction_SphereOverlapAll>(tree, &Params)!=0;
else
{
const PxU32 nbTris = mesh->getNbPrimitives();
PX_ASSERT(nbTris<16);
overflow = LeafFunction_SphereOverlapAll::doLeafTest(&Params, nbTris)!=0;
}
return Params.mNbHits;
}
// Sphere overlap - callback version
struct SphereParamsCB : SphereParams
{
MeshOverlapCallback mCallback;
void* mUserData;
};
namespace
{
class LeafFunction_SphereOverlapCB
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(const SphereParamsCB* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
const PxVec3& p0 = params->mVerts[VRef0];
const PxVec3& p1 = params->mVerts[VRef1];
const PxVec3& p2 = params->mVerts[VRef2];
if(SphereTriangle(params, p0, p1, p2))
{
const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 };
if((params->mCallback)(params->mUserData, p0, p1, p2, primIndex, vrefs))
return 1;
}
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
// PT: this one is currently not used
void BV4_OverlapSphereCB(const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshOverlapCallback callback, void* userData)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<SourceMesh*>(tree.mMeshInterface);
SphereParamsCB Params;
Params.mCallback = callback;
Params.mUserData = userData;
setupSphereParams(&Params, sphere, &tree, worldm_Aligned, mesh);
if(tree.mNodes)
processStreamNoOrder<LeafFunction_SphereOverlapCB>(tree, &Params);
else
{
const PxU32 nbTris = mesh->getNbTriangles();
PX_ASSERT(nbTris<16);
LeafFunction_SphereOverlapCB::doLeafTest(&Params, nbTris);
}
}
// Point distance query
// Implemented here as a variation on the sphere-overlap codepath
// TODO:
// * visit closest nodes first
// - revisit inlining
// - lazy-compute final results
// - SIMD
// - return uvs
// - maxDist input param
struct PointDistanceParams : SphereParams
{
PxVec3 mClosestPt;
PxU32 mIndex;
};
namespace
{
class LeafFunction_PointDistance
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(SphereParams* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
const PxVec3& p0 = params->mVerts[VRef0];
const PxVec3& p1 = params->mVerts[VRef1];
const PxVec3& p2 = params->mVerts[VRef2];
const PxVec3 edge10 = p1 - p0;
const PxVec3 edge20 = p2 - p0;
const PxVec3 cp = closestPtPointTriangle2(params->mCenter_PaddedAligned, p0, p1, p2, edge10, edge20);
const float sqrDist = (cp - params->mCenter_PaddedAligned).magnitudeSquared();
if(sqrDist <= params->mRadius2)
{
PointDistanceParams* Params = static_cast<PointDistanceParams*>(params);
Params->mClosestPt = cp;
Params->mIndex = primIndex;
Params->mRadius2 = sqrDist;
#ifdef GU_BV4_USE_SLABS
Params->mRadius22 = sqrDist*4.0f;
#endif
}
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
namespace
{
static PX_FORCE_INLINE PxIntBool BV4_SphereAABBOverlap2(const Vec4V boxCenter, const Vec4V boxExtents, const SphereParams* PX_RESTRICT params, float* PX_RESTRICT _d)
{
const Vec4V mCenter = V4LoadA_Safe(&params->mCenter_PaddedAligned2.x);
const FloatV mRadius2 = FLoad(params->mRadius22);
const Vec4V offset = V4Sub(mCenter, boxCenter);
const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents);
const Vec4V d = V4Sub(offset, closest);
const FloatV dot = V4Dot3(d, d);
FStore(dot, _d);
const PxU32 test = BGetBitMask(FIsGrtrOrEq(mRadius2, dot));
return (test & 0x7) == 0x7;
}
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE void BV4_ProcessNodeNoOrder_SwizzledQ2(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzledQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params, float* PX_RESTRICT _d)
{
OPC_SLABS_GET_CE2Q(i)
if(BV4_SphereAABBOverlap2(centerV, extentsV, params, _d))
{
if(node->isLeaf(i))
LeafTestT::doLeafTest(params, node->getPrimitive(i));
else
Stack[Nb++] = node->getChildData(i);
}
}
template<class LeafTestT, int i, class ParamsT>
PX_FORCE_INLINE void BV4_ProcessNodeNoOrder_SwizzledNQ2(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzledNQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params, float* PX_RESTRICT _d)
{
OPC_SLABS_GET_CE2NQ(i)
if(BV4_SphereAABBOverlap2(centerV, extentsV, params, _d))
{
if(node->isLeaf(i))
{
LeafTestT::doLeafTest(params, node->getPrimitive(i));
}
else
Stack[Nb++] = node->getChildData(i);
}
}
static PX_FORCE_INLINE void sort(PxU32* next, float* di, PxU32 i, PxU32 j)
{
if(di[i]<di[j]) // PT: "wrong" side on purpose, it's a LIFO stack
{
PxSwap(next[i], next[j]);
PxSwap(di[i], di[j]);
}
}
static PX_FORCE_INLINE void sortCandidates(PxU32 nbMore, PxU32* next, float* di, PxU32* stack, PxU32& nb)
{
// PT: this is not elegant and it looks slow, but for distance queries the time spent here is well worth it.
if(0)
{
while(nbMore)
{
float minDist = di[0];
PxU32 minIndex = 0;
for(PxU32 i=1;i<nbMore;i++)
{
if(di[i]>minDist) // PT: "wrong" side on purpose, it's a LIFO stack
{
minDist = di[i];
minIndex = i;
}
}
stack[nb++] = next[minIndex];
nbMore--;
PxSwap(next[minIndex], next[nbMore]);
PxSwap(di[minIndex], di[nbMore]);
}
}
else
{
switch(nbMore)
{
case 1:
{
stack[nb++] = next[0];
break;
}
case 2:
{
sort(next, di, 0, 1);
PX_ASSERT(di[0]>=di[1]);
stack[nb++] = next[0];
stack[nb++] = next[1];
break;
}
case 3:
{
sort(next, di, 0, 1);
sort(next, di, 1, 2);
sort(next, di, 0, 1);
PX_ASSERT(di[0]>=di[1]);
PX_ASSERT(di[1]>=di[2]);
stack[nb++] = next[0];
stack[nb++] = next[1];
stack[nb++] = next[2];
break;
}
case 4:
{
sort(next, di, 0, 1);
sort(next, di, 2, 3);
sort(next, di, 0, 2);
sort(next, di, 1, 3);
sort(next, di, 1, 2);
PX_ASSERT(di[0]>=di[1]);
PX_ASSERT(di[1]>=di[2]);
PX_ASSERT(di[2]>=di[3]);
stack[nb++] = next[0];
stack[nb++] = next[1];
stack[nb++] = next[2];
stack[nb++] = next[3];
break;
}
}
}
}
}
void BV4_PointDistance(const PxVec3& point, const BV4Tree& tree, float maxDist, PxU32& index, float& dist, PxVec3& cp/*, const PxMat44* PX_RESTRICT worldm_Aligned*/)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<SourceMesh*>(tree.mMeshInterface);
const float limit = sqrtf(sqrtf(PX_MAX_F32));
if(maxDist>limit)
maxDist = limit;
PointDistanceParams Params;
setupSphereParams(&Params, Sphere(point, maxDist), &tree, NULL/*worldm_Aligned*/, mesh);
if(tree.mNodes)
{
if(0)
{
// PT: unfortunately distance queries don't map nicely to the current BV4 framework
// This works but it doesn't visit closest nodes first so it's suboptimal. We also
// cannot use the ordered traversal since it's based on PNS, i.e. a fixed ray direction.
processStreamNoOrder<LeafFunction_PointDistance>(tree, &Params);
}
PxU32 initData = tree.mInitData;
PointDistanceParams* PX_RESTRICT params = &Params;
if(tree.mQuantized)
{
const BVDataPackedQ* PX_RESTRICT nodes = reinterpret_cast<const BVDataPackedQ*>(tree.mNodes);
const BVDataPackedQ* root = nodes;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
do
{
const PxU32 childData = stack[--nb];
nodes = root + getChildOffset(childData);
const BVDataSwizzledQ* tn = reinterpret_cast<const BVDataSwizzledQ*>(nodes);
const PxU32 nodeType = getChildType(childData);
PxU32 nbMore=0;
PxU32 next[4];
float di[4];
if(nodeType>1)
BV4_ProcessNodeNoOrder_SwizzledQ2<LeafFunction_PointDistance, 3>(next, nbMore, tn, params, &di[nbMore]);
if(nodeType>0)
BV4_ProcessNodeNoOrder_SwizzledQ2<LeafFunction_PointDistance, 2>(next, nbMore, tn, params, &di[nbMore]);
BV4_ProcessNodeNoOrder_SwizzledQ2<LeafFunction_PointDistance, 1>(next, nbMore, tn, params, &di[nbMore]);
BV4_ProcessNodeNoOrder_SwizzledQ2<LeafFunction_PointDistance, 0>(next, nbMore, tn, params, &di[nbMore]);
sortCandidates(nbMore, next, di, stack, nb);
}while(nb);
}
else
{
const BVDataPackedNQ* PX_RESTRICT nodes = reinterpret_cast<const BVDataPackedNQ*>(tree.mNodes);
const BVDataPackedNQ* root = nodes;
PxU32 nb=1;
PxU32 stack[GU_BV4_STACK_SIZE];
stack[0] = initData;
do
{
const PxU32 childData = stack[--nb];
nodes = root + getChildOffset(childData);
const BVDataSwizzledNQ* tn = reinterpret_cast<const BVDataSwizzledNQ*>(nodes);
const PxU32 nodeType = getChildType(childData);
PxU32 nbMore=0;
PxU32 next[4];
float di[4];
if(nodeType>1)
BV4_ProcessNodeNoOrder_SwizzledNQ2<LeafFunction_PointDistance, 3>(next, nbMore, tn, params, &di[nbMore]);
if(nodeType>0)
BV4_ProcessNodeNoOrder_SwizzledNQ2<LeafFunction_PointDistance, 2>(next, nbMore, tn, params, &di[nbMore]);
BV4_ProcessNodeNoOrder_SwizzledNQ2<LeafFunction_PointDistance, 1>(next, nbMore, tn, params, &di[nbMore]);
BV4_ProcessNodeNoOrder_SwizzledNQ2<LeafFunction_PointDistance, 0>(next, nbMore, tn, params, &di[nbMore]);
sortCandidates(nbMore, next, di, stack, nb);
}while(nb);
}
}
else
{
const PxU32 nbTris = mesh->getNbTriangles();
PX_ASSERT(nbTris<16);
LeafFunction_PointDistance::doLeafTest(&Params, nbTris);
}
index = Params.mIndex;
dist = sqrtf(Params.mRadius2);
cp = Params.mClosestPt;
}

View File

@@ -0,0 +1,391 @@
// 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 "foundation/PxSimpleTypes.h"
#include "foundation/PxMat44.h"
#include "GuBV4.h"
#include "GuBox.h"
#include "GuSphere.h"
#include "GuSweepSphereTriangle.h"
using namespace physx;
using namespace Gu;
#include "foundation/PxVecMath.h"
using namespace aos;
#include "GuBV4_Common.h"
// PT: for sphere-sweeps we use method 3 in %SDKRoot%\InternalDocumentation\GU\Sweep strategies.ppt
namespace
{
// PT: TODO: refactor structure (TA34704)
struct RayParams
{
BV4_ALIGN16(PxVec3p mCenterOrMinCoeff_PaddedAligned);
BV4_ALIGN16(PxVec3p mExtentsOrMaxCoeff_PaddedAligned);
#ifndef GU_BV4_USE_SLABS
BV4_ALIGN16(PxVec3p mData2_PaddedAligned);
BV4_ALIGN16(PxVec3p mFDir_PaddedAligned);
BV4_ALIGN16(PxVec3p mData_PaddedAligned);
#endif
BV4_ALIGN16(PxVec3p mLocalDir_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704)
BV4_ALIGN16(PxVec3p mOrigin_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704)
};
struct SphereSweepParams : RayParams
{
const IndTri32* PX_RESTRICT mTris32;
const IndTri16* PX_RESTRICT mTris16;
const PxVec3* PX_RESTRICT mVerts;
PxVec3 mOriginalExtents_Padded;
RaycastHitInternal mStabbedFace;
PxU32 mBackfaceCulling;
PxU32 mEarlyExit;
PxVec3 mP0, mP1, mP2;
PxVec3 mBestTriNormal;
float mBestAlignmentValue;
float mBestDistance;
float mMaxDist;
// PX_FORCE_INLINE float getReportDistance() const { return mStabbedFace.mDistance; }
PX_FORCE_INLINE float getReportDistance() const { return mBestDistance; }
};
}
#include "GuBV4_AABBAABBSweepTest.h"
// PT: TODO: __fastcall removed to make it compile everywhere. Revisit.
static bool /*__fastcall*/ triSphereSweep(SphereSweepParams* PX_RESTRICT params, PxU32 primIndex, bool nodeSorting=true)
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
const PxVec3& p0 = params->mVerts[VRef0];
const PxVec3& p1 = params->mVerts[VRef1];
const PxVec3& p2 = params->mVerts[VRef2];
PxVec3 normal = (p1 - p0).cross(p2 - p0);
// Backface culling
const bool culled = params->mBackfaceCulling && normal.dot(params->mLocalDir_Padded) > 0.0f;
if(culled)
return false;
const PxTriangle T(p0, p1, p2); // PT: TODO: check potential bad ctor/dtor here (TA34704) <= or avoid creating the tri, not needed anymore
normal.normalize();
// PT: TODO: we lost some perf when switching to PhysX version. Revisit/investigate. (TA34704)
float dist;
bool directHit;
if(!sweepSphereVSTri(T.verts, normal, params->mOrigin_Padded, params->mOriginalExtents_Padded.x, params->mLocalDir_Padded, dist, directHit, true))
return false;
const PxReal alignmentValue = computeAlignmentValue(normal, params->mLocalDir_Padded);
if(keepTriangle(dist, alignmentValue, params->mBestDistance, params->mBestAlignmentValue, params->mMaxDist))
{
params->mStabbedFace.mDistance = dist;
params->mStabbedFace.mTriangleID = primIndex;
params->mP0 = p0;
params->mP1 = p1;
params->mP2 = p2;
params->mBestDistance = PxMin(params->mBestDistance, dist); // exact lower bound
params->mBestAlignmentValue = alignmentValue;
params->mBestTriNormal = normal;
if(nodeSorting)
{
#ifndef GU_BV4_USE_SLABS
setupRayData(params, params->mBestDistance, params->mOrigin_Padded, params->mLocalDir_Padded);
//setupRayData(params, dist, params->mOrigin_Padded, params->mLocalDir_Padded);
#endif
}
return true;
}
//
else if(keepTriangleBasic(dist, params->mBestDistance, params->mMaxDist))
{
params->mStabbedFace.mDistance = dist;
params->mBestDistance = PxMin(params->mBestDistance, dist); // exact lower bound
}
//
return false;
}
namespace
{
class LeafFunction_SphereSweepClosest
{
public:
static PX_FORCE_INLINE void doLeafTest(SphereSweepParams* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
triSphereSweep(params, primIndex);
primIndex++;
}while(nbToGo--);
}
};
class LeafFunction_SphereSweepAny
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(SphereSweepParams* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
if(triSphereSweep(params, primIndex))
return 1;
primIndex++;
}while(nbToGo--);
return 0;
}
};
class ImpactFunctionSphere
{
public:
static PX_FORCE_INLINE void computeImpact(PxVec3& impactPos, PxVec3& impactNormal, const Sphere& sphere, const PxVec3& dir, const PxReal t, const PxTrianglePadded& triangle)
{
computeSphereTriImpactData(impactPos, impactNormal, sphere.center, dir, t, triangle);
}
};
}
template<class ParamsT>
static PX_FORCE_INLINE void setupSphereParams(ParamsT* PX_RESTRICT params, const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT worldm_Aligned, const SourceMesh* PX_RESTRICT mesh, PxU32 flags)
{
params->mOriginalExtents_Padded = PxVec3(sphere.radius);
params->mStabbedFace.mTriangleID = PX_INVALID_U32;
params->mStabbedFace.mDistance = maxDist;
params->mBestDistance = PX_MAX_REAL;
params->mBestAlignmentValue = 2.0f;
params->mMaxDist = maxDist;
setupParamsFlags(params, flags);
setupMeshPointersAndQuantizedCoeffs(params, mesh, tree);
computeLocalRay(params->mLocalDir_Padded, params->mOrigin_Padded, dir, sphere.center, worldm_Aligned);
#ifndef GU_BV4_USE_SLABS
setupRayData(params, maxDist, params->mOrigin_Padded, params->mLocalDir_Padded);
#endif
}
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs.h"
#endif
#include "GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h"
#include "GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h"
#ifdef GU_BV4_USE_SLABS
#include "GuBV4_Slabs_KajiyaNoOrder.h"
#include "GuBV4_Slabs_KajiyaOrdered.h"
#endif
#define GU_BV4_PROCESS_STREAM_RAY_NO_ORDER
#define GU_BV4_PROCESS_STREAM_RAY_ORDERED
#include "GuBV4_Internal.h"
PxIntBool BV4_SphereSweepSingle(const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepHit* PX_RESTRICT hit, PxU32 flags)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
SphereSweepParams Params;
setupSphereParams(&Params, sphere, dir, maxDist, &tree, worldm_Aligned, mesh, flags);
if(tree.mNodes)
{
if(Params.mEarlyExit)
processStreamRayNoOrder<1, LeafFunction_SphereSweepAny>(tree, &Params);
else
processStreamRayOrdered<1, LeafFunction_SphereSweepClosest>(tree, &Params);
}
else
doBruteForceTests<LeafFunction_SphereSweepAny, LeafFunction_SphereSweepClosest>(mesh->getNbTriangles(), &Params);
return computeImpactDataT<ImpactFunctionSphere>(sphere, dir, hit, &Params, worldm_Aligned, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0);
}
// PT: sphere sweep callback version - currently not used
namespace
{
struct SphereSweepParamsCB : SphereSweepParams
{
// PT: these new members are only here to call computeImpactDataT during traversal :(
// PT: TODO: most of them may not be needed if we just move sphere to local space before traversal
Sphere mSphere; // Sphere in original space (maybe not local/mesh space)
PxVec3 mDir; // Dir in original space (maybe not local/mesh space)
const PxMat44* mWorldm_Aligned;
PxU32 mFlags;
SweepUnlimitedCallback mCallback;
void* mUserData;
bool mNodeSorting;
};
class LeafFunction_SphereSweepCB
{
public:
static PX_FORCE_INLINE PxIntBool doLeafTest(SphereSweepParamsCB* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
if(triSphereSweep(params, primIndex, params->mNodeSorting))
{
// PT: TODO: in this version we must compute the impact data immediately,
// which is a bad idea in general, but I'm not sure what else I can do.
SweepHit hit;
const bool b = computeImpactDataT<ImpactFunctionSphere>(params->mSphere, params->mDir, &hit, params, params->mWorldm_Aligned, (params->mFlags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (params->mFlags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0);
PX_ASSERT(b);
PX_UNUSED(b);
reportUnlimitedCallbackHit(params, hit);
}
primIndex++;
}while(nbToGo--);
return 0;
}
};
}
// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB().
void BV4_SphereSweepCB(const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting)
{
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
SphereSweepParamsCB Params;
Params.mSphere = sphere;
Params.mDir = dir;
Params.mWorldm_Aligned = worldm_Aligned;
Params.mFlags = flags;
Params.mCallback = callback;
Params.mUserData = userData;
Params.mMaxDist = maxDist;
Params.mNodeSorting = nodeSorting;
setupSphereParams(&Params, sphere, dir, maxDist, &tree, worldm_Aligned, mesh, flags);
PX_ASSERT(!Params.mEarlyExit);
if(tree.mNodes)
{
if(nodeSorting)
processStreamRayOrdered<1, LeafFunction_SphereSweepCB>(tree, &Params);
else
processStreamRayNoOrder<1, LeafFunction_SphereSweepCB>(tree, &Params);
}
else
doBruteForceTests<LeafFunction_SphereSweepCB, LeafFunction_SphereSweepCB>(mesh->getNbTriangles(), &Params);
}
// Old box sweep callback version, using sphere code
namespace
{
struct BoxSweepParamsCB : SphereSweepParams
{
MeshSweepCallback mCallback;
void* mUserData;
};
class ExLeafTestSweepCB
{
public:
static PX_FORCE_INLINE void doLeafTest(BoxSweepParamsCB* PX_RESTRICT params, PxU32 primIndex)
{
PxU32 nbToGo = getNbPrimitives(primIndex);
do
{
PxU32 VRef0, VRef1, VRef2;
getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16);
{
// const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 };
float dist = params->mStabbedFace.mDistance;
if((params->mCallback)(params->mUserData, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], primIndex, /*vrefs,*/ dist))
return;
if(dist<params->mStabbedFace.mDistance)
{
params->mStabbedFace.mDistance = dist;
#ifndef GU_BV4_USE_SLABS
setupRayData(params, dist, params->mOrigin_Padded, params->mLocalDir_Padded);
#endif
}
}
primIndex++;
}while(nbToGo--);
}
};
}
void BV4_GenericSweepCB_Old(const PxVec3& origin, const PxVec3& extents, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshSweepCallback callback, void* userData)
{
BoxSweepParamsCB Params;
Params.mCallback = callback;
Params.mUserData = userData;
Params.mOriginalExtents_Padded = extents;
Params.mStabbedFace.mTriangleID = PX_INVALID_U32;
Params.mStabbedFace.mDistance = maxDist;
computeLocalRay(Params.mLocalDir_Padded, Params.mOrigin_Padded, dir, origin, worldm_Aligned);
#ifndef GU_BV4_USE_SLABS
setupRayData(&Params, maxDist, Params.mOrigin_Padded, Params.mLocalDir_Padded);
#endif
const SourceMesh* PX_RESTRICT mesh = static_cast<const SourceMesh*>(tree.mMeshInterface);
setupMeshPointersAndQuantizedCoeffs(&Params, mesh, &tree);
if(tree.mNodes)
processStreamRayOrdered<1, ExLeafTestSweepCB>(tree, &Params);
else
{
const PxU32 nbTris = mesh->getNbTriangles();
PX_ASSERT(nbTris<16);
ExLeafTestSweepCB::doLeafTest(&Params, nbTris);
}
}

View File

@@ -0,0 +1,43 @@
// 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_BV_CONSTANTS_H
#define GU_BV_CONSTANTS_H
#include "foundation/PxVecMath.h"
namespace
{
const physx::aos::VecU32V signMask = physx::aos::U4LoadXYZW((physx::PxU32(1)<<31), (physx::PxU32(1)<<31), (physx::PxU32(1)<<31), (physx::PxU32(1)<<31));
const physx::aos::Vec4V epsFloat4 = physx::aos::V4Load(1e-9f);
const physx::aos::Vec4V zeroes = physx::aos::V4Zero();
const physx::aos::Vec4V twos = physx::aos::V4Load(2.0f);
const physx::aos::Vec4V epsInflateFloat4 = physx::aos::V4Load(1e-7f);
}
#endif // GU_BV_CONSTANTS_H

View File

@@ -0,0 +1,649 @@
// 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_MESH_DATA_H
#define GU_MESH_DATA_H
#include "foundation/PxSimpleTypes.h"
#include "foundation/PxVec4.h"
#include "foundation/PxBounds3.h"
#include "geometry/PxTriangleMesh.h"
#include "geometry/PxTetrahedronMesh.h"
#include "foundation/PxUserAllocated.h"
#include "foundation/PxAllocator.h"
#include "GuRTree.h"
#include "GuBV4.h"
#include "GuBV32.h"
#include "GuSDF.h"
namespace physx
{
namespace Gu {
// 1: support stackless collision trees for non-recursive collision queries
// 2: height field functionality not supported anymore
// 3: mass struct removed
// 4: bounding sphere removed
// 5: RTree added, opcode tree still in the binary image, physx 3.0
// 6: opcode tree removed from binary image
// 7: convex decomposition is out
// 8: adjacency information added
// 9: removed leaf triangles and most of opcode data, changed rtree layout
// 10: float rtrees
// 11: new build, isLeaf added to page
// 12: isLeaf is now the lowest bit in ptrs
// 13: TA30159 removed deprecated convexEdgeThreshold and bumped version
// 14: added midphase ID
// 15: GPU data simplification
// 16: vertex2Face mapping enabled by default if using GPU
#define PX_MESH_VERSION 16
#define PX_TET_MESH_VERSION 1
#define PX_DEFORMABLE_VOLUME_MESH_VERSION 3 // 3: parallel GS + new linear corotated model.
// these flags are used to indicate/validate the contents of a cooked mesh file
enum InternalMeshSerialFlag
{
IMSF_MATERIALS = (1<<0), //!< if set, the cooked mesh file contains per-triangle material indices
IMSF_FACE_REMAP = (1<<1), //!< if set, the cooked mesh file contains a remap table
IMSF_8BIT_INDICES = (1<<2), //!< if set, the cooked mesh file contains 8bit indices (topology)
IMSF_16BIT_INDICES = (1<<3), //!< if set, the cooked mesh file contains 16bit indices (topology)
IMSF_ADJACENCIES = (1<<4), //!< if set, the cooked mesh file contains adjacency structures
IMSF_GRB_DATA = (1<<5), //!< if set, the cooked mesh file contains GRB data structures
IMSF_SDF = (1<<6), //!< if set, the cooked mesh file contains SDF data structures
IMSF_VERT_MAPPING = (1<<7), //!< if set, the cooked mesh file contains vertex mapping information
IMSF_GRB_INV_REMAP = (1<<8), //!< if set, the cooked mesh file contains vertex inv mapping information. Required for deformable surfaces
IMSF_INERTIA = (1<<9) //!< if set, the cooked mesh file contains inertia tensor for the mesh
};
#if PX_VC
#pragma warning(push)
#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value.
#endif
class MeshDataBase : public PxUserAllocated
{
public:
PxMeshMidPhase::Enum mType;
PxU8 mFlags;
PxU32 mNbVertices;
PxVec3* mVertices;
PxReal mMass; //this is mass assuming a unit density that can be scaled by instances!
PxMat33 mInertia; //in local space of mesh!
PxVec3 mLocalCenterOfMass; //local space com
PxBounds3 mAABB;
PxReal mGeomEpsilon;
PxU32* mFaceRemap;
// GRB data -------------------------
void* mGRB_primIndices; //!< GRB: GPU-friendly primitive indices(either triangle or tetrahedron)
PxU32* mGRB_faceRemap; //!< GRB: this remap the GPU triangle indices to CPU triangle indices
PxU32* mGRB_faceRemapInverse; //
// End of GRB data ------------------
// SDF data
SDF mSdfData;
// Deformable surface data : each vert has a list of associated triangles in the mesh, this is for attachement constraints to enable default filtering
PxU32* mAccumulatedTrianglesRef;//runsum
PxU32* mTrianglesReferences;
PxU32 mNbTrianglesReferences;
MeshDataBase() :
mFlags (0),
mNbVertices (0),
mVertices (NULL),
mMass (0.f),
mInertia (PxZero),
mLocalCenterOfMass (0.f),
mAABB (PxBounds3::empty()),
mGeomEpsilon (0.0f),
mFaceRemap (NULL),
mGRB_primIndices (NULL),
mGRB_faceRemap (NULL),
mGRB_faceRemapInverse (NULL),
mSdfData (PxZero),
mAccumulatedTrianglesRef(NULL),
mTrianglesReferences (NULL),
mNbTrianglesReferences (0)
{
}
virtual ~MeshDataBase()
{
PX_FREE(mVertices);
PX_FREE(mFaceRemap);
PX_FREE(mGRB_primIndices);
PX_FREE(mGRB_faceRemap);
PX_FREE(mGRB_faceRemapInverse);
PX_FREE(mAccumulatedTrianglesRef);
PX_FREE(mTrianglesReferences);
}
PX_NOINLINE PxVec3* allocateVertices(PxU32 nbVertices)
{
PX_ASSERT(!mVertices);
// PT: we allocate one more vertex to make sure it's safe to V4Load the last one
const PxU32 nbAllocatedVerts = nbVertices + 1;
mVertices = PX_ALLOCATE(PxVec3, nbAllocatedVerts, "PxVec3");
mNbVertices = nbVertices;
return mVertices;
}
PX_FORCE_INLINE bool has16BitIndices() const
{
return (mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) ? true : false;
}
};
class TriangleMeshData : public MeshDataBase
{
public:
PxU32 mNbTriangles;
void* mTriangles;
PxU32* mAdjacencies;
PxU8* mExtraTrigData;
PxU16* mMaterialIndices;
// GRB data -------------------------
void* mGRB_primAdjacencies; //!< GRB: adjacency data, with BOUNDARY and NONCONVEX flags (flags replace adj indices where applicable) [uin4]
Gu::BV32Tree* mGRB_BV32Tree;
// End of GRB data ------------------
TriangleMeshData() :
mNbTriangles (0),
mTriangles (NULL),
mAdjacencies (NULL),
mExtraTrigData (NULL),
mMaterialIndices (NULL),
mGRB_primAdjacencies (NULL),
mGRB_BV32Tree (NULL)
{
}
virtual ~TriangleMeshData()
{
PX_FREE(mTriangles);
PX_FREE(mAdjacencies);
PX_FREE(mMaterialIndices);
PX_FREE(mExtraTrigData);
PX_FREE(mGRB_primAdjacencies);
PX_DELETE(mGRB_BV32Tree);
}
PX_NOINLINE PxU32* allocateAdjacencies()
{
PX_ASSERT(mNbTriangles);
PX_ASSERT(!mAdjacencies);
mAdjacencies = PX_ALLOCATE(PxU32, mNbTriangles * 3, "mAdjacencies");
mFlags |= PxTriangleMeshFlag::eADJACENCY_INFO;
return mAdjacencies;
}
PX_NOINLINE PxU32* allocateFaceRemap()
{
PX_ASSERT(mNbTriangles);
PX_ASSERT(!mFaceRemap);
mFaceRemap = PX_ALLOCATE(PxU32, mNbTriangles, "mFaceRemap");
return mFaceRemap;
}
PX_NOINLINE void* allocateTriangles(PxU32 nbTriangles, bool force32Bit, PxU32 allocateGPUData = 0)
{
PX_ASSERT(mNbVertices);
PX_ASSERT(!mTriangles);
bool index16 = mNbVertices <= 0xffff && !force32Bit;
if(index16)
mFlags |= PxTriangleMeshFlag::e16_BIT_INDICES;
mTriangles = PX_ALLOC(nbTriangles * (index16 ? sizeof(PxU16) : sizeof(PxU32)) * 3, "mTriangles");
if (allocateGPUData)
mGRB_primIndices = PX_ALLOC(nbTriangles * (index16 ? sizeof(PxU16) : sizeof(PxU32)) * 3, "mGRB_triIndices");
mNbTriangles = nbTriangles;
return mTriangles;
}
PX_NOINLINE PxU16* allocateMaterials()
{
PX_ASSERT(mNbTriangles);
PX_ASSERT(!mMaterialIndices);
mMaterialIndices = PX_ALLOCATE(PxU16, mNbTriangles, "mMaterialIndices");
return mMaterialIndices;
}
PX_NOINLINE PxU8* allocateExtraTrigData()
{
PX_ASSERT(mNbTriangles);
PX_ASSERT(!mExtraTrigData);
mExtraTrigData = PX_ALLOCATE(PxU8, mNbTriangles, "mExtraTrigData");
return mExtraTrigData;
}
PX_FORCE_INLINE void setTriangleAdjacency(PxU32 triangleIndex, PxU32 adjacency, PxU32 offset)
{
PX_ASSERT(mAdjacencies);
mAdjacencies[triangleIndex*3 + offset] = adjacency;
}
};
class RTreeTriangleData : public TriangleMeshData
{
public:
RTreeTriangleData() { mType = PxMeshMidPhase::eBVH33; }
virtual ~RTreeTriangleData() {}
Gu::RTree mRTree;
};
class BV4TriangleData : public TriangleMeshData
{
public:
BV4TriangleData() { mType = PxMeshMidPhase::eBVH34; }
virtual ~BV4TriangleData() {}
Gu::SourceMesh mMeshInterface;
Gu::BV4Tree mBV4Tree;
};
// PT: TODO: the following classes should probably be in their own specific files (e.g. GuTetrahedronMeshData.h, GuDeformableVolumeMeshData.h)
class TetrahedronMeshData : public PxTetrahedronMeshData
{
public:
PxU32 mNbVertices;
PxVec3* mVertices;
PxU16* mMaterialIndices; //each tetrahedron should have a material index
PxU32 mNbTetrahedrons;
void* mTetrahedrons; //IndTetrahedron32
PxU8 mFlags;
PxReal mGeomEpsilon;
PxBounds3 mAABB;
TetrahedronMeshData() :
mNbVertices(0),
mVertices(NULL),
mMaterialIndices(NULL),
mNbTetrahedrons(0),
mTetrahedrons(NULL),
mFlags(0),
mGeomEpsilon(0.0f),
mAABB(PxBounds3::empty())
{}
TetrahedronMeshData(PxVec3* vertices, PxU32 nbVertices, void* tetrahedrons, PxU32 nbTetrahedrons, PxU8 flags, PxReal geomEpsilon, PxBounds3 aabb) :
mNbVertices(nbVertices),
mVertices(vertices),
mNbTetrahedrons(nbTetrahedrons),
mTetrahedrons(tetrahedrons),
mFlags(flags),
mGeomEpsilon(geomEpsilon),
mAABB(aabb)
{}
void allocateTetrahedrons(const PxU32 nbGridTetrahedrons, const PxU32 allocateGPUData = 0)
{
if (allocateGPUData)
{
mTetrahedrons = PX_ALLOC(nbGridTetrahedrons * sizeof(PxU32) * 4, "mGridModelTetrahedrons");
}
mNbTetrahedrons = nbGridTetrahedrons;
}
PxVec3* allocateVertices(PxU32 nbVertices, const PxU32 allocateGPUData = 1)
{
PX_ASSERT(!mVertices);
// PT: we allocate one more vertex to make sure it's safe to V4Load the last one
if (allocateGPUData)
{
const PxU32 nbAllocatedVerts = nbVertices + 1;
mVertices = PX_ALLOCATE(PxVec3, nbAllocatedVerts, "PxVec3");
}
mNbVertices = nbVertices;
return mVertices;
}
PxU16* allocateMaterials()
{
PX_ASSERT(mNbTetrahedrons);
PX_ASSERT(!mMaterialIndices);
mMaterialIndices = PX_ALLOCATE(PxU16, mNbTetrahedrons, "mMaterialIndices");
return mMaterialIndices;
}
PX_FORCE_INLINE bool has16BitIndices() const
{
return (mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) ? true : false;
}
~TetrahedronMeshData()
{
PX_FREE(mTetrahedrons);
PX_FREE(mVertices);
PX_FREE(mMaterialIndices)
}
};
class DeformableVolumeCollisionData : public PxDeformableVolumeCollisionData
{
public:
PxU32* mFaceRemap;
// GRB data -------------------------
void * mGRB_primIndices; //!< GRB: GPU-friendly primitive indices(either triangle or tetrahedron)
PxU32* mGRB_faceRemap; //!< GRB: this remap the GPU triangle indices to CPU triangle indices
PxU32* mGRB_faceRemapInverse;
Gu::BV32Tree* mGRB_BV32Tree;
PxU8* mGRB_tetraSurfaceHint;
// End of GRB data ------------------
Gu::TetrahedronSourceMesh mMeshInterface;
Gu::BV4Tree mBV4Tree;
PxMat33* mTetraRestPoses;
DeformableVolumeCollisionData() :
mFaceRemap(NULL),
mGRB_primIndices(NULL),
mGRB_faceRemap(NULL),
mGRB_faceRemapInverse(NULL),
mGRB_BV32Tree(NULL),
mGRB_tetraSurfaceHint(NULL),
mTetraRestPoses(NULL)
{}
virtual ~DeformableVolumeCollisionData()
{
PX_FREE(mGRB_tetraSurfaceHint);
PX_DELETE(mGRB_BV32Tree);
PX_FREE(mFaceRemap);
PX_FREE(mGRB_primIndices);
PX_FREE(mGRB_faceRemap);
PX_FREE(mGRB_faceRemapInverse);
PX_FREE(mTetraRestPoses);
}
PxU32* allocateFaceRemap(PxU32 nbTetrahedrons)
{
PX_ASSERT(nbTetrahedrons);
PX_ASSERT(!mFaceRemap);
mFaceRemap = PX_ALLOCATE(PxU32, nbTetrahedrons, "mFaceRemap");
return mFaceRemap;
}
void allocateCollisionData(PxU32 nbTetrahedrons)
{
mGRB_primIndices = PX_ALLOC(nbTetrahedrons * 4 * sizeof(PxU32), "mGRB_primIndices");
mGRB_tetraSurfaceHint = PX_ALLOCATE(PxU8, nbTetrahedrons, "mGRB_tetraSurfaceHint");
mTetraRestPoses = PX_ALLOCATE(PxMat33, nbTetrahedrons, "mTetraRestPoses");
}
};
class CollisionMeshMappingData : public PxCollisionMeshMappingData
{
public:
PxReal* mVertsBarycentricInGridModel;
PxU32* mVertsRemapInGridModel;
PxU32* mTetsRemapColToSim;
PxU32 mTetsRemapSize;
PxU32* mTetsAccumulatedRemapColToSim; //runsum, size of number of tetrahedrons in collision mesh
//in the collision model, each vert has a list of associated simulation tetrahedrons, this is for attachement constraints to enable default filtering
PxU32* mCollisionAccumulatedTetrahedronsRef;//runsum
PxU32* mCollisionTetrahedronsReferences;
PxU32 mCollisionNbTetrahedronsReferences;
PxU32* mCollisionSurfaceVertToTetRemap;
PxU8* mCollisionSurfaceVertsHint;
CollisionMeshMappingData() :
mVertsBarycentricInGridModel(NULL),
mVertsRemapInGridModel(NULL),
mTetsRemapColToSim(NULL),
mTetsRemapSize(0),
mTetsAccumulatedRemapColToSim(NULL),
mCollisionAccumulatedTetrahedronsRef(NULL),
mCollisionTetrahedronsReferences(NULL),
mCollisionNbTetrahedronsReferences(0),
mCollisionSurfaceVertToTetRemap(NULL),
mCollisionSurfaceVertsHint(NULL)
{
}
virtual ~CollisionMeshMappingData()
{
PX_FREE(mVertsBarycentricInGridModel);
PX_FREE(mVertsRemapInGridModel);
PX_FREE(mTetsRemapColToSim);
PX_FREE(mTetsAccumulatedRemapColToSim);
PX_FREE(mCollisionAccumulatedTetrahedronsRef);
PX_FREE(mCollisionTetrahedronsReferences);
PX_FREE(mCollisionSurfaceVertsHint);
PX_FREE(mCollisionSurfaceVertToTetRemap);
}
void allocatemappingData(const PxU32 nbVerts, const PxU32 tetRemapSize, const PxU32 nbColTetrahedrons, const PxU32 allocateGPUData = 0)
{
if (allocateGPUData)
{
mVertsBarycentricInGridModel = reinterpret_cast<PxReal*>(PX_ALLOC(nbVerts * sizeof(PxReal) * 4, "mVertsBarycentricInGridModel"));
mVertsRemapInGridModel = reinterpret_cast<PxU32*>(PX_ALLOC(nbVerts * sizeof(PxU32), "mVertsRemapInGridModel"));
mTetsRemapColToSim = reinterpret_cast<PxU32*>(PX_ALLOC(tetRemapSize * sizeof(PxU32), "mTetsRemapInSimModel"));
mTetsAccumulatedRemapColToSim = reinterpret_cast<PxU32*>(PX_ALLOC(nbColTetrahedrons * sizeof(PxU32), "mTetsAccumulatedRemapInSimModel"));
mCollisionSurfaceVertsHint = reinterpret_cast<PxU8*>(PX_ALLOC(nbVerts * sizeof(PxU8), "mCollisionSurfaceVertsHint"));
mCollisionSurfaceVertToTetRemap = reinterpret_cast<PxU32*>(PX_ALLOC(nbVerts * sizeof(PxU32), "mCollisionSurfaceVertToTetRemap"));
}
mTetsRemapSize = tetRemapSize;
}
void allocateTetRefData(const PxU32 totalTetReference, const PxU32 nbCollisionVerts, const PxU32 allocateGPUData /*= 0*/)
{
if (allocateGPUData)
{
mCollisionAccumulatedTetrahedronsRef = reinterpret_cast<PxU32*>(PX_ALLOC(nbCollisionVerts * sizeof(PxU32), "mGMAccumulatedTetrahedronsRef"));
mCollisionTetrahedronsReferences = reinterpret_cast<PxU32*>(PX_ALLOC(totalTetReference * sizeof(PxU32), "mGMTetrahedronsReferences"));
}
mCollisionNbTetrahedronsReferences = totalTetReference;
}
virtual void release()
{
PX_DELETE_THIS;
}
};
class DeformableVolumeSimulationData : public PxDeformableVolumeSimulationData
{
public:
PxReal* mGridModelInvMass;
PxMat33* mGridModelTetraRestPoses;
PxU32 mGridModelNbPartitions;
PxU32 mGridModelMaxTetsPerPartitions;
PxU32* mGridModelOrderedTetrahedrons; // the corresponding tetrahedron index for the runsum
PxU32* mGMRemapOutputCP;
PxU32* mGMAccumulatedPartitionsCP; //runsum for the combined partition
PxU32* mGMAccumulatedCopiesCP; //runsum for the vert copies in combined partitions
PxU32 mGMRemapOutputSize;
PxU32* mGMPullIndices;
PxU32 mNumTetsPerElement;
DeformableVolumeSimulationData() :
mGridModelInvMass(NULL),
mGridModelTetraRestPoses(NULL),
mGridModelNbPartitions(0),
mGridModelOrderedTetrahedrons(NULL),
mGMRemapOutputCP(NULL),
mGMAccumulatedPartitionsCP(NULL),
mGMAccumulatedCopiesCP(NULL),
mGMRemapOutputSize(0),
mGMPullIndices(NULL)
{}
virtual ~DeformableVolumeSimulationData()
{
PX_FREE(mGridModelInvMass);
PX_FREE(mGridModelTetraRestPoses);
PX_FREE(mGridModelOrderedTetrahedrons);
PX_FREE(mGMRemapOutputCP);
PX_FREE(mGMAccumulatedPartitionsCP);
PX_FREE(mGMAccumulatedCopiesCP);
PX_FREE(mGMPullIndices);
}
void allocateGridModelData(const PxU32 nbGridTetrahedrons, const PxU32 nbGridVerts,
const PxU32 nbVerts, const PxU32 nbPartitions, const PxU32 remapOutputSize, const PxU32 numTetsPerElement, const PxU32 allocateGPUData = 0)
{
PX_UNUSED(nbVerts);
if (allocateGPUData)
{
const PxU32 numElements = nbGridTetrahedrons / numTetsPerElement;
const PxU32 numVertsPerElement = (numTetsPerElement == 6 || numTetsPerElement == 5) ? 8 : 4;
mGridModelInvMass = reinterpret_cast<float*>(PX_ALLOC(nbGridVerts * sizeof(float), "mGridModelInvMass"));
mGridModelTetraRestPoses = reinterpret_cast<PxMat33*>(PX_ALLOC(nbGridTetrahedrons * sizeof(PxMat33), "mGridModelTetraRestPoses"));
mGridModelOrderedTetrahedrons = reinterpret_cast<PxU32*>(PX_ALLOC(numElements * sizeof(PxU32), "mGridModelOrderedTetrahedrons"));
if (remapOutputSize) // tet mesh only (or old hex mesh)
{
mGMRemapOutputCP = reinterpret_cast<PxU32*>(PX_ALLOC(numElements * numVertsPerElement * sizeof(PxU32), "mGMRemapOutputCP"));
mGMAccumulatedCopiesCP = reinterpret_cast<PxU32*>(PX_ALLOC(nbGridVerts * sizeof(PxU32), "mGMAccumulatedCopiesCP"));
}
mGMAccumulatedPartitionsCP = reinterpret_cast<PxU32*>(PX_ALLOC(nbPartitions * sizeof(PxU32), "mGMAccumulatedPartitionsCP"));
mGMPullIndices = reinterpret_cast<PxU32*>(PX_ALLOC(numElements * numVertsPerElement * sizeof(PxU32) , "mGMPullIndices"));
}
mGridModelNbPartitions = nbPartitions;
mGMRemapOutputSize = remapOutputSize;
}
};
class CollisionTetrahedronMeshData : public PxCollisionTetrahedronMeshData
{
public:
TetrahedronMeshData* mMesh;
DeformableVolumeCollisionData* mCollisionData;
virtual PxTetrahedronMeshData* getMesh() { return mMesh; }
virtual const PxTetrahedronMeshData* getMesh() const { return mMesh; }
virtual PxDeformableVolumeCollisionData* getData() { return mCollisionData; }
virtual const PxDeformableVolumeCollisionData* getData() const { return mCollisionData; }
virtual ~CollisionTetrahedronMeshData()
{
PX_FREE(mMesh);
PX_FREE(mCollisionData);
}
virtual void release()
{
PX_DELETE_THIS;
}
};
class SimulationTetrahedronMeshData : public PxSimulationTetrahedronMeshData
{
public:
TetrahedronMeshData* mMesh;
DeformableVolumeSimulationData* mSimulationData;
virtual PxTetrahedronMeshData* getMesh() { return mMesh; }
virtual PxDeformableVolumeSimulationData* getData() { return mSimulationData; }
virtual ~SimulationTetrahedronMeshData()
{
PX_FREE(mMesh);
PX_FREE(mSimulationData);
}
virtual void release()
{
PX_DELETE_THIS;
}
};
class DeformableVolumeMeshData : public PxUserAllocated
{
PX_NOCOPY(DeformableVolumeMeshData)
public:
TetrahedronMeshData& mSimulationMesh;
DeformableVolumeSimulationData& mSimulationData;
TetrahedronMeshData& mCollisionMesh;
DeformableVolumeCollisionData& mCollisionData;
CollisionMeshMappingData& mMappingData;
DeformableVolumeMeshData(TetrahedronMeshData& simulationMesh, DeformableVolumeSimulationData& simulationData,
TetrahedronMeshData& collisionMesh, DeformableVolumeCollisionData& collisionData, CollisionMeshMappingData& mappingData) :
mSimulationMesh(simulationMesh),
mSimulationData(simulationData),
mCollisionMesh(collisionMesh),
mCollisionData(collisionData),
mMappingData(mappingData)
{ }
};
#if PX_VC
#pragma warning(pop)
#endif
} // namespace Gu
}
#endif // #ifdef GU_MESH_DATA_H

View File

@@ -0,0 +1,331 @@
// 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 "common/PxProfileZone.h"
#include "geometry/PxMeshQuery.h"
#include "geometry/PxSphereGeometry.h"
#include "geometry/PxGeometryQuery.h"
#include "GuInternal.h"
#include "GuEntityReport.h"
#include "GuHeightFieldUtil.h"
#include "GuBoxConversion.h"
#include "GuIntersectionTriangleBox.h"
#include "CmScaling.h"
#include "GuSweepTests.h"
#include "GuMidphaseInterface.h"
#include "foundation/PxFPU.h"
using namespace physx;
using namespace Gu;
namespace {
class HfTrianglesEntityReport2 : public OverlapReport, public LimitedResults
{
public:
HfTrianglesEntityReport2(
PxU32* results, PxU32 maxResults, PxU32 startIndex,
HeightFieldUtil& hfUtil,
const PxVec3& boxCenter, const PxVec3& boxExtents, const PxQuat& boxRot,
bool aabbOverlap) :
LimitedResults (results, maxResults, startIndex),
mHfUtil (hfUtil),
mAABBOverlap (aabbOverlap)
{
buildFrom(mBox2Hf, boxCenter, boxExtents, boxRot);
}
virtual bool reportTouchedTris(PxU32 nbEntities, const PxU32* entities)
{
if(mAABBOverlap)
{
while(nbEntities--)
if(!add(*entities++))
return false;
}
else
{
const PxTransform idt(PxIdentity);
for(PxU32 i=0; i<nbEntities; i++)
{
PxTrianglePadded tri;
mHfUtil.getTriangle(idt, tri, NULL, NULL, entities[i], false, false); // First parameter not needed if local space triangle is enough
// PT: this one is safe because triangle class is padded
if(intersectTriangleBox(mBox2Hf, tri.verts[0], tri.verts[1], tri.verts[2]))
{
if(!add(entities[i]))
return false;
}
}
}
return true;
}
HeightFieldUtil& mHfUtil;
BoxPadded mBox2Hf;
bool mAABBOverlap;
private:
HfTrianglesEntityReport2& operator=(const HfTrianglesEntityReport2&);
};
} // namespace
void physx::PxMeshQuery::getTriangle(const PxTriangleMeshGeometry& triGeom, const PxTransform& globalPose, PxTriangleID triangleIndex, PxTriangle& triangle, PxU32* vertexIndices, PxU32* adjacencyIndices)
{
const TriangleMesh* tm = static_cast<const TriangleMesh*>(triGeom.triangleMesh);
PX_CHECK_AND_RETURN(triangleIndex<tm->getNbTriangles(), "PxMeshQuery::getTriangle: triangle index is out of bounds");
if(adjacencyIndices && !tm->getAdjacencies())
PxGetFoundation().error(PxErrorCode::eINVALID_OPERATION, PX_FL, "Adjacency information not created. Set buildTriangleAdjacencies on Cooking params.");
const PxMat34 vertex2worldSkew = globalPose * triGeom.scale;
tm->computeWorldTriangle(triangle, triangleIndex, vertex2worldSkew, triGeom.scale.hasNegativeDeterminant(), vertexIndices, adjacencyIndices);
}
///////////////////////////////////////////////////////////////////////////////
void physx::PxMeshQuery::getTriangle(const PxHeightFieldGeometry& hfGeom, const PxTransform& globalPose, PxTriangleID triangleIndex, PxTriangle& triangle, PxU32* vertexIndices, PxU32* adjacencyIndices)
{
HeightFieldUtil hfUtil(hfGeom);
hfUtil.getTriangle(globalPose, triangle, vertexIndices, adjacencyIndices, triangleIndex, true, true);
}
///////////////////////////////////////////////////////////////////////////////
PxU32 physx::PxMeshQuery::findOverlapTriangleMesh(
const PxGeometry& geom, const PxTransform& geomPose,
const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose,
PxU32* results, PxU32 maxResults, PxU32 startIndex, bool& overflow, PxGeometryQueryFlags queryFlags)
{
PX_SIMD_GUARD_CNDT(queryFlags & PxGeometryQueryFlag::eSIMD_GUARD)
LimitedResults limitedResults(results, maxResults, startIndex);
const TriangleMesh* tm = static_cast<const TriangleMesh*>(meshGeom.triangleMesh);
switch(geom.getType())
{
case PxGeometryType::eBOX:
{
const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom);
Box box;
buildFrom(box, geomPose.p, boxGeom.halfExtents, geomPose.q);
Midphase::intersectBoxVsMesh(box, *tm, meshPose, meshGeom.scale, &limitedResults);
break;
}
case PxGeometryType::eCAPSULE:
{
const PxCapsuleGeometry& capsGeom = static_cast<const PxCapsuleGeometry&>(geom);
Capsule capsule;
getCapsule(capsule, capsGeom, geomPose);
Midphase::intersectCapsuleVsMesh(capsule, *tm, meshPose, meshGeom.scale, &limitedResults);
break;
}
case PxGeometryType::eSPHERE:
{
const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom);
Midphase::intersectSphereVsMesh(Sphere(geomPose.p, sphereGeom.radius), *tm, meshPose, meshGeom.scale, &limitedResults);
break;
}
default:
{
PX_CHECK_MSG(false, "findOverlapTriangleMesh: Only box, capsule and sphere geometries are supported.");
}
}
overflow = limitedResults.mOverflow;
return limitedResults.mNbResults;
}
///////////////////////////////////////////////////////////////////////////////
bool physx::PxMeshQuery::findOverlapTriangleMesh( PxReportCallback<PxGeomIndexPair>& callback,
const PxTriangleMeshGeometry& meshGeom0, const PxTransform& meshPose0,
const PxTriangleMeshGeometry& meshGeom1, const PxTransform& meshPose1,
PxGeometryQueryFlags queryFlags, PxMeshMeshQueryFlags meshMeshFlags, float tolerance)
{
PX_SIMD_GUARD_CNDT(queryFlags & PxGeometryQueryFlag::eSIMD_GUARD)
const TriangleMesh* tm0 = static_cast<const TriangleMesh*>(meshGeom0.triangleMesh);
const TriangleMesh* tm1 = static_cast<const TriangleMesh*>(meshGeom1.triangleMesh);
// PT: only implemented for BV4
if(!tm0 || !tm1 || tm0->getConcreteType()!=PxConcreteType::eTRIANGLE_MESH_BVH34 || tm1->getConcreteType()!=PxConcreteType::eTRIANGLE_MESH_BVH34)
return PxGetFoundation().error(PxErrorCode::eINVALID_OPERATION, PX_FL, "PxMeshQuery::findOverlapTriangleMesh(): only available between two BVH34 triangles meshes.");
// PT: ...so we don't need a table like for the other ops, just go straight to BV4
return intersectMeshVsMesh_BV4(callback, *tm0, meshPose0, meshGeom0.scale, *tm1, meshPose1, meshGeom1.scale, meshMeshFlags, tolerance);
}
bool physx::PxMeshQuery::findOverlapTriangleMesh( PxReportCallback<PxGeomIndexClosePair>& callback,
const PxTriangleMeshGeometry& meshGeom0, const PxTransform& meshPose0,
const PxTriangleMeshGeometry& meshGeom1, const PxTransform& meshPose1,
PxGeometryQueryFlags queryFlags, PxMeshMeshQueryFlags meshMeshFlags, float tolerance)
{
PX_SIMD_GUARD_CNDT(queryFlags & PxGeometryQueryFlag::eSIMD_GUARD)
const TriangleMesh* tm0 = static_cast<const TriangleMesh*>(meshGeom0.triangleMesh);
const TriangleMesh* tm1 = static_cast<const TriangleMesh*>(meshGeom1.triangleMesh);
// PT: only implemented for BV4
if(!tm0 || !tm1 || tm0->getConcreteType()!=PxConcreteType::eTRIANGLE_MESH_BVH34 || tm1->getConcreteType()!=PxConcreteType::eTRIANGLE_MESH_BVH34)
return PxGetFoundation().error(PxErrorCode::eINVALID_OPERATION, PX_FL, "PxMeshQuery::findOverlapTriangleMesh(): only available between two BVH34 triangles meshes.");
// PT: ...so we don't need a table like for the other ops, just go straight to BV4
return distanceMeshVsMesh_BV4(callback, *tm0, meshPose0, meshGeom0.scale, *tm1, meshPose1, meshGeom1.scale, meshMeshFlags, tolerance);
}
///////////////////////////////////////////////////////////////////////////////
PxU32 physx::PxMeshQuery::findOverlapHeightField( const PxGeometry& geom, const PxTransform& geomPose,
const PxHeightFieldGeometry& hfGeom, const PxTransform& hfPose,
PxU32* results, PxU32 maxResults, PxU32 startIndex, bool& overflow, PxGeometryQueryFlags queryFlags)
{
PX_SIMD_GUARD_CNDT(queryFlags & PxGeometryQueryFlag::eSIMD_GUARD)
const PxTransform localPose0 = hfPose.transformInv(geomPose);
PxBoxGeometry boxGeom;
switch(geom.getType())
{
case PxGeometryType::eCAPSULE:
{
const PxCapsuleGeometry& cap = static_cast<const PxCapsuleGeometry&>(geom);
boxGeom.halfExtents = PxVec3(cap.halfHeight+cap.radius, cap.radius, cap.radius);
// PT: TODO: improve these bounds - see computeCapsuleBounds
}
break;
case PxGeometryType::eSPHERE:
{
const PxSphereGeometry& sph = static_cast<const PxSphereGeometry&>(geom);
boxGeom.halfExtents = PxVec3(sph.radius);
// PT: TODO: could this codepath be improved using the following?
//PxBounds3 localBounds;
//const PxVec3 localSphereCenter = getLocalSphereData(localBounds, pose0, pose1, sphereGeom.radius);
}
break;
case PxGeometryType::eBOX:
boxGeom = static_cast<const PxBoxGeometry&>(geom);
break;
default:
{
overflow = false;
PX_CHECK_AND_RETURN_VAL(false, "findOverlapHeightField: Only box, sphere and capsule queries are supported.", false);
}
}
const bool isAABB = ((localPose0.q.x == 0.0f) && (localPose0.q.y == 0.0f) && (localPose0.q.z == 0.0f));
PxBounds3 bounds;
if (isAABB)
bounds = PxBounds3::centerExtents(localPose0.p, boxGeom.halfExtents);
else
bounds = PxBounds3::poseExtent(localPose0, boxGeom.halfExtents); // box.halfExtents is really extent
HeightFieldUtil hfUtil(hfGeom);
HfTrianglesEntityReport2 entityReport(results, maxResults, startIndex, hfUtil, localPose0.p, boxGeom.halfExtents, localPose0.q, isAABB);
hfUtil.overlapAABBTriangles(bounds, entityReport);
overflow = entityReport.mOverflow;
return entityReport.mNbResults;
}
///////////////////////////////////////////////////////////////////////////////
bool physx::PxMeshQuery::sweep( const PxVec3& unitDir, const PxReal maxDistance,
const PxGeometry& geom, const PxTransform& pose,
PxU32 triangleCount, const PxTriangle* triangles,
PxGeomSweepHit& sweepHit, PxHitFlags hitFlags,
const PxU32* cachedIndex, const PxReal inflation, bool doubleSided, PxGeometryQueryFlags queryFlags)
{
PX_SIMD_GUARD_CNDT(queryFlags & PxGeometryQueryFlag::eSIMD_GUARD)
PX_CHECK_AND_RETURN_VAL(pose.isValid(), "PxMeshQuery::sweep(): pose is not valid.", false);
PX_CHECK_AND_RETURN_VAL(unitDir.isFinite(), "PxMeshQuery::sweep(): unitDir is not valid.", false);
PX_CHECK_AND_RETURN_VAL(PxIsFinite(maxDistance), "PxMeshQuery::sweep(): distance is not valid.", false);
PX_CHECK_AND_RETURN_VAL(maxDistance > 0, "PxMeshQuery::sweep(): sweep distance must be greater than 0.", false);
PX_PROFILE_ZONE("MeshQuery.sweep", 0);
const PxReal distance = PxMin(maxDistance, PX_MAX_SWEEP_DISTANCE);
switch(geom.getType())
{
case PxGeometryType::eSPHERE:
{
const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom);
const PxCapsuleGeometry capsuleGeom(sphereGeom.radius, 0.0f);
return sweepCapsuleTriangles( triangleCount, triangles, doubleSided, capsuleGeom, pose, unitDir, distance,
sweepHit, cachedIndex, inflation, hitFlags);
}
case PxGeometryType::eCAPSULE:
{
const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom);
return sweepCapsuleTriangles( triangleCount, triangles, doubleSided, capsuleGeom, pose, unitDir, distance,
sweepHit, cachedIndex, inflation, hitFlags);
}
case PxGeometryType::eBOX:
{
const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom);
if(hitFlags & PxHitFlag::ePRECISE_SWEEP)
{
return sweepBoxTriangles_Precise( triangleCount, triangles, doubleSided, boxGeom, pose, unitDir, distance, sweepHit, cachedIndex,
inflation, hitFlags);
}
else
{
return sweepBoxTriangles( triangleCount, triangles, doubleSided, boxGeom, pose, unitDir, distance, sweepHit, cachedIndex,
inflation, hitFlags);
}
}
default:
PX_CHECK_MSG(false, "PxMeshQuery::sweep(): geometry object parameter must be sphere, capsule or box geometry.");
}
return false;
}
///////////////////////////////////////////////////////////////////////////////

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,384 @@
// 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_MIDPHASE_INTERFACE_H
#define GU_MIDPHASE_INTERFACE_H
#include "GuOverlapTests.h"
#include "GuRaycastTests.h"
#include "GuTriangleMesh.h"
#include "GuTetrahedronMesh.h"
#include "foundation/PxVecMath.h"
#include "PxQueryReport.h"
#include "geometry/PxMeshQuery.h" // PT: TODO: revisit this include
// PT: this file contains the common interface for all midphase implementations. Specifically the Midphase namespace contains the
// midphase-related entry points, dispatching calls to the proper implementations depending on the triangle mesh's type. The rest of it
// is simply classes & structs shared by all implementations.
namespace physx
{
class PxMeshScale;
class PxTriangleMeshGeometry;
namespace Cm
{
class FastVertex2ShapeScaling;
}
namespace Gu
{
struct ConvexHullData;
struct CallbackMode { enum Enum { eANY, eCLOSEST, eMULTIPLE }; };
template<typename HitType>
struct MeshHitCallback
{
CallbackMode::Enum mode;
MeshHitCallback(CallbackMode::Enum aMode) : mode(aMode) {}
PX_FORCE_INLINE bool inAnyMode() const { return mode == CallbackMode::eANY; }
PX_FORCE_INLINE bool inClosestMode() const { return mode == CallbackMode::eCLOSEST; }
PX_FORCE_INLINE bool inMultipleMode() const { return mode == CallbackMode::eMULTIPLE; }
virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position
const HitType& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal& shrunkMaxT, const PxU32* vIndices) = 0;
virtual ~MeshHitCallback() {}
};
template<typename HitType>
struct TetMeshHitCallback
{
CallbackMode::Enum mode;
TetMeshHitCallback(CallbackMode::Enum aMode) : mode(aMode) {}
PX_FORCE_INLINE bool inAnyMode() const { return mode == CallbackMode::eANY; }
PX_FORCE_INLINE bool inClosestMode() const { return mode == CallbackMode::eCLOSEST; }
PX_FORCE_INLINE bool inMultipleMode() const { return mode == CallbackMode::eMULTIPLE; }
virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position
const HitType& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, const PxVec3& v3, PxReal& shrunkMaxT, const PxU32* vIndices) = 0;
virtual ~TetMeshHitCallback() {}
};
struct SweepConvexMeshHitCallback;
struct LimitedResults
{
PxU32* mResults;
PxU32 mNbResults;
PxU32 mMaxResults;
PxU32 mStartIndex;
PxU32 mNbSkipped;
bool mOverflow;
PX_FORCE_INLINE LimitedResults(PxU32* results, PxU32 maxResults, PxU32 startIndex)
: mResults(results), mMaxResults(maxResults), mStartIndex(startIndex)
{
reset();
}
PX_FORCE_INLINE void reset()
{
mNbResults = 0;
mNbSkipped = 0;
mOverflow = false;
}
PX_FORCE_INLINE bool add(PxU32 index)
{
if(mNbResults>=mMaxResults)
{
mOverflow = true;
return false;
}
if(mNbSkipped>=mStartIndex)
mResults[mNbResults++] = index;
else
mNbSkipped++;
return true;
}
};
// RTree forward declarations
PX_PHYSX_COMMON_API PxU32 raycast_triangleMesh_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose,
const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist,
PxHitFlags hitFlags, PxU32 maxHits, PxGeomRaycastHit* PX_RESTRICT hits, PxU32 stride);
PX_PHYSX_COMMON_API bool intersectSphereVsMesh_RTREE(const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results);
PX_PHYSX_COMMON_API bool intersectBoxVsMesh_RTREE (const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results);
PX_PHYSX_COMMON_API bool intersectCapsuleVsMesh_RTREE(const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results);
PX_PHYSX_COMMON_API void intersectOBB_RTREE(const TriangleMesh* mesh, const Box& obb, MeshHitCallback<PxGeomRaycastHit>& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned);
PX_PHYSX_COMMON_API bool sweepCapsule_MeshGeom_RTREE( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose,
const Gu::Capsule& lss, const PxVec3& unitDir, PxReal distance,
PxGeomSweepHit& sweepHit, PxHitFlags hitFlags, PxReal inflation);
PX_PHYSX_COMMON_API bool sweepBox_MeshGeom_RTREE( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose,
const Gu::Box& box, const PxVec3& unitDir, PxReal distance,
PxGeomSweepHit& sweepHit, PxHitFlags hitFlags, PxReal inflation);
PX_PHYSX_COMMON_API void sweepConvex_MeshGeom_RTREE(const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit);
PX_PHYSX_COMMON_API void pointMeshDistance_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, const PxVec3& point, float maxDist, PxU32& index, float& dist, PxVec3& closestPt);
// BV4 forward declarations
PX_PHYSX_COMMON_API PxU32 raycast_triangleMesh_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose,
const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist,
PxHitFlags hitFlags, PxU32 maxHits, PxGeomRaycastHit* PX_RESTRICT hits, PxU32 stride);
PX_PHYSX_COMMON_API bool intersectSphereVsMesh_BV4 (const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results);
PX_PHYSX_COMMON_API bool intersectBoxVsMesh_BV4 (const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results);
PX_PHYSX_COMMON_API bool intersectCapsuleVsMesh_BV4 (const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results);
PX_PHYSX_COMMON_API void intersectOBB_BV4(const TriangleMesh* mesh, const Box& obb, MeshHitCallback<PxGeomRaycastHit>& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned);
PX_PHYSX_COMMON_API void intersectOBB_BV4(const TetrahedronMesh* mesh, const Box& obb, TetMeshHitCallback<PxGeomRaycastHit>& callback);
PX_PHYSX_COMMON_API bool sweepCapsule_MeshGeom_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose,
const Gu::Capsule& lss, const PxVec3& unitDir, PxReal distance,
PxGeomSweepHit& sweepHit, PxHitFlags hitFlags, PxReal inflation);
PX_PHYSX_COMMON_API bool sweepBox_MeshGeom_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose,
const Gu::Box& box, const PxVec3& unitDir, PxReal distance,
PxGeomSweepHit& sweepHit, PxHitFlags hitFlags, PxReal inflation);
PX_PHYSX_COMMON_API void sweepConvex_MeshGeom_BV4(const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit);
PX_PHYSX_COMMON_API void pointMeshDistance_BV4(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, const PxVec3& point, float maxDist, PxU32& index, float& dist, PxVec3& closestPt);
PX_PHYSX_COMMON_API bool intersectMeshVsMesh_BV4( PxReportCallback<PxGeomIndexPair>& callback,
const TriangleMesh& triMesh0, const PxTransform& meshPose0, const PxMeshScale& meshScale0,
const TriangleMesh& triMesh1, const PxTransform& meshPose1, const PxMeshScale& meshScale1,
PxMeshMeshQueryFlags meshMeshFlags, float tolerance);
PX_PHYSX_COMMON_API bool distanceMeshVsMesh_BV4( PxReportCallback<PxGeomIndexClosePair>& callback,
const TriangleMesh& triMesh0, const PxTransform& meshPose0, const PxMeshScale& meshScale0,
const TriangleMesh& triMesh1, const PxTransform& meshPose1, const PxMeshScale& meshScale1,
PxMeshMeshQueryFlags meshMeshFlags, float tolerance);
typedef PxU32 (*MidphaseRaycastFunction)( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose,
const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist,
PxHitFlags hitFlags, PxU32 maxHits, PxGeomRaycastHit* PX_RESTRICT hits, PxU32 stride);
typedef bool (*MidphaseSphereOverlapFunction) (const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results);
typedef bool (*MidphaseBoxOverlapFunction) (const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results);
typedef bool (*MidphaseCapsuleOverlapFunction) (const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results);
typedef void (*MidphaseBoxCBOverlapFunction) (const TriangleMesh* mesh, const Box& obb, MeshHitCallback<PxGeomRaycastHit>& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned);
typedef bool (*MidphaseCapsuleSweepFunction)( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose,
const Gu::Capsule& lss, const PxVec3& unitDir, PxReal distance,
PxGeomSweepHit& sweepHit, PxHitFlags hitFlags, PxReal inflation);
typedef bool (*MidphaseBoxSweepFunction)( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose,
const Gu::Box& box, const PxVec3& unitDir, PxReal distance,
PxGeomSweepHit& sweepHit, PxHitFlags hitFlags, PxReal inflation);
typedef void (*MidphaseConvexSweepFunction)( const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit);
typedef void (*MidphasePointMeshFunction)(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, const PxVec3& point, float maxDist, PxU32& index, float& dist, PxVec3& closestPt);
static const MidphaseRaycastFunction gMidphaseRaycastTable[PxMeshMidPhase::eLAST] =
{
raycast_triangleMesh_RTREE,
raycast_triangleMesh_BV4,
};
static const MidphaseSphereOverlapFunction gMidphaseSphereOverlapTable[PxMeshMidPhase::eLAST] =
{
intersectSphereVsMesh_RTREE,
intersectSphereVsMesh_BV4,
};
static const MidphaseBoxOverlapFunction gMidphaseBoxOverlapTable[PxMeshMidPhase::eLAST] =
{
intersectBoxVsMesh_RTREE,
intersectBoxVsMesh_BV4,
};
static const MidphaseCapsuleOverlapFunction gMidphaseCapsuleOverlapTable[PxMeshMidPhase::eLAST] =
{
intersectCapsuleVsMesh_RTREE,
intersectCapsuleVsMesh_BV4,
};
static const MidphaseBoxCBOverlapFunction gMidphaseBoxCBOverlapTable[PxMeshMidPhase::eLAST] =
{
intersectOBB_RTREE,
intersectOBB_BV4,
};
static const MidphaseBoxSweepFunction gMidphaseBoxSweepTable[PxMeshMidPhase::eLAST] =
{
sweepBox_MeshGeom_RTREE,
sweepBox_MeshGeom_BV4,
};
static const MidphaseCapsuleSweepFunction gMidphaseCapsuleSweepTable[PxMeshMidPhase::eLAST] =
{
sweepCapsule_MeshGeom_RTREE,
sweepCapsule_MeshGeom_BV4,
};
static const MidphaseConvexSweepFunction gMidphaseConvexSweepTable[PxMeshMidPhase::eLAST] =
{
sweepConvex_MeshGeom_RTREE,
sweepConvex_MeshGeom_BV4,
};
static const MidphasePointMeshFunction gMidphasePointMeshTable[PxMeshMidPhase::eLAST] =
{
pointMeshDistance_RTREE,
pointMeshDistance_BV4,
};
namespace Midphase
{
// \param[in] mesh triangle mesh to raycast against
// \param[in] meshGeom geometry object associated with the mesh
// \param[in] meshTransform pose/transform of geometry object
// \param[in] rayOrigin ray's origin
// \param[in] rayDir ray's unit dir
// \param[in] maxDist ray's length/max distance
// \param[in] hitFlags query behavior flags
// \param[in] maxHits max number of hits = size of 'hits' buffer
// \param[out] hits result buffer where to write raycast hits
// \return number of hits written to 'hits' result buffer
// \note there's no mechanism to report overflow. Returned number of hits is just clamped to maxHits.
PX_FORCE_INLINE PxU32 raycastTriangleMesh( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshTransform,
const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist,
PxHitFlags hitFlags, PxU32 maxHits, PxGeomRaycastHit* PX_RESTRICT hits, PxU32 stride)
{
const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33);
return gMidphaseRaycastTable[index](mesh, meshGeom, meshTransform, rayOrigin, rayDir, maxDist, hitFlags, maxHits, hits, stride);
}
// \param[in] sphere sphere
// \param[in] mesh triangle mesh
// \param[in] meshTransform pose/transform of triangle mesh
// \param[in] meshScale mesh scale
// \param[out] results results object if multiple hits are needed, NULL if a simple boolean answer is enough
// \return true if at least one overlap has been found
PX_FORCE_INLINE bool intersectSphereVsMesh(const Sphere& sphere, const TriangleMesh& mesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results)
{
const PxU32 index = PxU32(mesh.getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33);
return gMidphaseSphereOverlapTable[index](sphere, mesh, meshTransform, meshScale, results);
}
// \param[in] box box
// \param[in] mesh triangle mesh
// \param[in] meshTransform pose/transform of triangle mesh
// \param[in] meshScale mesh scale
// \param[out] results results object if multiple hits are needed, NULL if a simple boolean answer is enough
// \return true if at least one overlap has been found
PX_FORCE_INLINE bool intersectBoxVsMesh(const Box& box, const TriangleMesh& mesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results)
{
const PxU32 index = PxU32(mesh.getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33);
return gMidphaseBoxOverlapTable[index](box, mesh, meshTransform, meshScale, results);
}
// \param[in] capsule capsule
// \param[in] mesh triangle mesh
// \param[in] meshTransform pose/transform of triangle mesh
// \param[in] meshScale mesh scale
// \param[out] results results object if multiple hits are needed, NULL if a simple boolean answer is enough
// \return true if at least one overlap has been found
PX_FORCE_INLINE bool intersectCapsuleVsMesh(const Capsule& capsule, const TriangleMesh& mesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results)
{
const PxU32 index = PxU32(mesh.getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33);
return gMidphaseCapsuleOverlapTable[index](capsule, mesh, meshTransform, meshScale, results);
}
// \param[in] mesh triangle mesh
// \param[in] box box
// \param[in] callback callback object, called each time a hit is found
// \param[in] bothTriangleSidesCollide true for double-sided meshes
// \param[in] checkObbIsAligned true to use a dedicated codepath for axis-aligned boxes
PX_FORCE_INLINE void intersectOBB(const TriangleMesh* mesh, const Box& obb, MeshHitCallback<PxGeomRaycastHit>& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned = true)
{
const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33);
gMidphaseBoxCBOverlapTable[index](mesh, obb, callback, bothTriangleSidesCollide, checkObbIsAligned);
}
// \param[in] mesh tetrahedron mesh
// \param[in] box box
// \param[in] callback callback object, called each time a hit is found
PX_FORCE_INLINE void intersectOBB(const TetrahedronMesh* mesh, const Box& obb, TetMeshHitCallback<PxGeomRaycastHit>& callback)
{
intersectOBB_BV4(mesh, obb, callback);
}
// \param[in] mesh triangle mesh
// \param[in] meshGeom geometry object associated with the mesh
// \param[in] meshTransform pose/transform of geometry object
// \param[in] capsule swept capsule
// \param[in] unitDir sweep's unit dir
// \param[in] distance sweep's length/max distance
// \param[out] sweepHit hit result
// \param[in] hitFlags query behavior flags
// \param[in] inflation optional inflation value for swept shape
// \return true if a hit was found, false otherwise
PX_FORCE_INLINE bool sweepCapsuleVsMesh(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshTransform,
const Gu::Capsule& capsule, const PxVec3& unitDir, PxReal distance,
PxGeomSweepHit& sweepHit, PxHitFlags hitFlags, PxReal inflation)
{
const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33);
return gMidphaseCapsuleSweepTable[index](mesh, meshGeom, meshTransform, capsule, unitDir, distance, sweepHit, hitFlags, inflation);
}
// \param[in] mesh triangle mesh
// \param[in] meshGeom geometry object associated with the mesh
// \param[in] meshTransform pose/transform of geometry object
// \param[in] box swept box
// \param[in] unitDir sweep's unit dir
// \param[in] distance sweep's length/max distance
// \param[out] sweepHit hit result
// \param[in] hitFlags query behavior flags
// \param[in] inflation optional inflation value for swept shape
// \return true if a hit was found, false otherwise
PX_FORCE_INLINE bool sweepBoxVsMesh(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshTransform,
const Gu::Box& box, const PxVec3& unitDir, PxReal distance,
PxGeomSweepHit& sweepHit, PxHitFlags hitFlags, PxReal inflation)
{
const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33);
return gMidphaseBoxSweepTable[index](mesh, meshGeom, meshTransform, box, unitDir, distance, sweepHit, hitFlags, inflation);
}
// \param[in] mesh triangle mesh
// \param[in] hullBox hull's bounding box
// \param[in] localDir sweep's unit dir, in local/mesh space
// \param[in] distance sweep's length/max distance
// \param[in] callback callback object, called each time a hit is found
// \param[in] anyHit true for PxHitFlag::eANY_HIT queries
PX_FORCE_INLINE void sweepConvexVsMesh(const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit)
{
const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33);
gMidphaseConvexSweepTable[index](mesh, hullBox, localDir, distance, callback, anyHit);
}
PX_FORCE_INLINE void pointMeshDistance(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, const PxVec3& point, float maxDist
, PxU32& closestIndex, float& dist, PxVec3& closestPt)
{
const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33);
gMidphasePointMeshTable[index](mesh, meshGeom, pose, point, maxDist, closestIndex, dist, closestPt);
}
}
}
}
#endif // GU_MIDPHASE_INTERFACE_H

View File

@@ -0,0 +1,920 @@
// 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 "GuSweepMesh.h"
#include "GuIntersectionRayTriangle.h"
#include "GuIntersectionCapsuleTriangle.h"
#include "GuIntersectionRayBox.h"
#include "GuSphere.h"
#include "GuBoxConversion.h"
#include "GuConvexUtilsInternal.h"
#include "GuVecTriangle.h"
#include "GuIntersectionTriangleBox.h"
#include "GuRTree.h"
#include "GuTriangleMeshRTree.h"
#include "GuInternal.h"
#include "CmMatrix34.h"
// This file contains code specific to the RTree midphase.
using namespace physx;
using namespace Cm;
using namespace Gu;
using namespace aos;
struct MeshRayCollider
{
template <int tInflate, int tRayTest>
PX_PHYSX_COMMON_API static void collide(
const PxVec3& orig, const PxVec3& dir, // dir is not normalized (full length), both in mesh space (unless meshWorld is non-zero)
PxReal maxT, // maxT is from [0,1], if maxT is 0.0f, AABB traversal will be used
bool bothTriangleSidesCollide, const RTreeTriangleMesh* mesh, MeshHitCallback<PxGeomRaycastHit>& callback,
const PxVec3* inflate = NULL);
PX_PHYSX_COMMON_API static void collideOBB(
const Box& obb, bool bothTriangleSidesCollide, const RTreeTriangleMesh* mesh, MeshHitCallback<PxGeomRaycastHit>& callback,
bool checkObbIsAligned = true); // perf hint, pass false if obb is rarely axis aligned
};
class SimpleRayTriOverlap
{
public:
PX_FORCE_INLINE SimpleRayTriOverlap(const PxVec3& origin, const PxVec3& dir, bool bothSides, PxReal geomEpsilon)
: mOrigin(origin), mDir(dir), mBothSides(bothSides), mGeomEpsilon(geomEpsilon)
{
}
PX_FORCE_INLINE PxIntBool overlap(const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, PxGeomRaycastHit& hit) const
{
if(!intersectRayTriangle(mOrigin, mDir, vert0, vert1, vert2, hit.distance, hit.u, hit.v, !mBothSides, mGeomEpsilon))
return false;
if(hit.distance< 0.0f) // test if the ray intersection t is negative
return false;
return true;
}
PxVec3 mOrigin;
PxVec3 mDir;
bool mBothSides;
PxReal mGeomEpsilon;
};
using Gu::RTree;
// This callback comes from RTree and decodes LeafTriangle indices stored in rtree into actual triangles
// This callback is needed because RTree doesn't know that it stores triangles since it's a general purpose spatial index
#if PX_VC
#pragma warning(push)
#pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value.
#endif
template <int tInflate, bool tRayTest>
struct RayRTreeCallback : RTree::CallbackRaycast, RTree::Callback
{
MeshHitCallback<PxGeomRaycastHit>& outerCallback;
PxI32 has16BitIndices;
const void* mTris;
const PxVec3* mVerts;
const PxVec3* mInflate;
const SimpleRayTriOverlap rayCollider;
PxReal maxT;
PxGeomRaycastHit closestHit; // recorded closest hit over the whole traversal (only for callback mode eCLOSEST)
PxVec3 cv0, cv1, cv2; // PT: make sure these aren't last in the class, to safely V4Load them
PxU32 cis[3];
bool hadClosestHit;
const bool closestMode;
Vec3V inflateV, rayOriginV, rayDirV;
RayRTreeCallback(
PxReal geomEpsilon, MeshHitCallback<PxGeomRaycastHit>& callback,
PxI32 has16BitIndices_, const void* tris, const PxVec3* verts,
const PxVec3& origin, const PxVec3& dir, PxReal maxT_, bool bothSides, const PxVec3* inflate)
: outerCallback(callback), has16BitIndices(has16BitIndices_),
mTris(tris), mVerts(verts), mInflate(inflate), rayCollider(origin, dir, bothSides, geomEpsilon),
maxT(maxT_), closestMode(callback.inClosestMode())
{
PX_ASSERT(closestHit.distance == PX_MAX_REAL);
hadClosestHit = false;
if (tInflate)
inflateV = V3LoadU(*mInflate);
rayOriginV = V3LoadU(rayCollider.mOrigin);
rayDirV = V3LoadU(rayCollider.mDir);
}
PX_FORCE_INLINE void getVertIndices(PxU32 triIndex, PxU32& i0, PxU32 &i1, PxU32 &i2)
{
if(has16BitIndices)
{
const PxU16* p = reinterpret_cast<const PxU16*>(mTris) + triIndex*3;
i0 = p[0]; i1 = p[1]; i2 = p[2];
}
else
{
const PxU32* p = reinterpret_cast<const PxU32*>(mTris) + triIndex*3;
i0 = p[0]; i1 = p[1]; i2 = p[2];
}
}
virtual PX_FORCE_INLINE bool processResults(PxU32 NumTouched, PxU32* Touched, PxF32& newMaxT)
{
PX_ASSERT(NumTouched > 0);
// Loop through touched leaves
PxGeomRaycastHit tempHit;
for(PxU32 leaf = 0; leaf<NumTouched; leaf++)
{
// Each leaf box has a set of triangles
LeafTriangles currentLeaf;
currentLeaf.Data = Touched[leaf];
PxU32 nbLeafTris = currentLeaf.GetNbTriangles();
PxU32 baseLeafTriIndex = currentLeaf.GetTriangleIndex();
for(PxU32 i = 0; i < nbLeafTris; i++)
{
PxU32 i0, i1, i2;
const PxU32 triangleIndex = baseLeafTriIndex+i;
getVertIndices(triangleIndex, i0, i1, i2);
const PxVec3& v0 = mVerts[i0], &v1 = mVerts[i1], &v2 = mVerts[i2];
const PxU32 vinds[3] = { i0, i1, i2 };
if (tRayTest)
{
PxIntBool overlap;
if (tInflate)
{
// AP: mesh skew is already included here (ray is pre-transformed)
Vec3V v0v = V3LoadU(v0), v1v = V3LoadU(v1), v2v = V3LoadU(v2);
Vec3V minB = V3Min(V3Min(v0v, v1v), v2v), maxB = V3Max(V3Max(v0v, v1v), v2v);
// PT: we add an epsilon to max distance, to make sure we don't reject triangles that are just at the same
// distance as best triangle so far. We need to keep all of these to make sure we return the one with the
// best normal.
const float relativeEpsilon = GU_EPSILON_SAME_DISTANCE * PxMax(1.0f, maxT);
FloatV tNear, tFar;
overlap = intersectRayAABB2(
V3Sub(minB, inflateV), V3Add(maxB, inflateV), rayOriginV, rayDirV, FLoad(maxT+relativeEpsilon), tNear, tFar);
if (overlap)
{
// can't clip to tFar here because hitting the AABB doesn't guarantee that we can clip
// (since we can still miss the actual tri)
tempHit.distance = maxT;
tempHit.faceIndex = triangleIndex;
tempHit.u = tempHit.v = 0.0f;
}
} else
overlap = rayCollider.overlap(v0, v1, v2, tempHit) && tempHit.distance <= maxT;
if(!overlap)
continue;
}
tempHit.faceIndex = triangleIndex;
tempHit.flags = PxHitFlag::ePOSITION;
// Intersection point is valid if dist < segment's length
// We know dist>0 so we can use integers
if (closestMode)
{
if(tempHit.distance < closestHit.distance)
{
closestHit = tempHit;
newMaxT = PxMin(tempHit.distance, newMaxT);
cv0 = v0; cv1 = v1; cv2 = v2;
cis[0] = vinds[0]; cis[1] = vinds[1]; cis[2] = vinds[2];
hadClosestHit = true;
}
} else
{
PxReal shrunkMaxT = newMaxT;
PxAgain again = outerCallback.processHit(tempHit, v0, v1, v2, shrunkMaxT, vinds);
if (!again)
return false;
if (shrunkMaxT < newMaxT)
{
newMaxT = shrunkMaxT;
maxT = shrunkMaxT;
}
}
if (outerCallback.inAnyMode()) // early out if in ANY mode
return false;
}
} // for(PxU32 leaf = 0; leaf<NumTouched; leaf++)
return true;
}
virtual bool processResults(PxU32 numTouched, PxU32* touched)
{
PxF32 dummy;
return RayRTreeCallback::processResults(numTouched, touched, dummy);
}
virtual ~RayRTreeCallback()
{
if (hadClosestHit)
{
PX_ASSERT(outerCallback.inClosestMode());
outerCallback.processHit(closestHit, cv0, cv1, cv2, maxT, cis);
}
}
private:
RayRTreeCallback& operator=(const RayRTreeCallback&);
};
#if PX_VC
#pragma warning(pop)
#endif
void MeshRayCollider::collideOBB(
const Box& obb, bool bothTriangleSidesCollide, const RTreeTriangleMesh* mi, MeshHitCallback<PxGeomRaycastHit>& callback,
bool checkObbIsAligned)
{
const PxU32 maxResults = RTREE_N; // maxResults=rtree page size for more efficient early out
PxU32 buf[maxResults];
RayRTreeCallback<false, false> rTreeCallback(
mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(),
PxVec3(0), PxVec3(0), 0.0f, bothTriangleSidesCollide, NULL);
if (checkObbIsAligned && PxAbs(PxQuat(obb.rot).w) > 0.9999f)
{
PxVec3 aabbExtents = obb.computeAABBExtent();
mi->getRTree().traverseAABB(obb.center - aabbExtents, obb.center + aabbExtents, maxResults, buf, &rTreeCallback);
} else
mi->getRTree().traverseOBB(obb, maxResults, buf, &rTreeCallback);
}
template <int tInflate, int tRayTest>
void MeshRayCollider::collide(
const PxVec3& orig, const PxVec3& dir, PxReal maxT, bool bothSides,
const RTreeTriangleMesh* mi, MeshHitCallback<PxGeomRaycastHit>& callback,
const PxVec3* inflate)
{
const PxU32 maxResults = RTREE_N; // maxResults=rtree page size for more efficient early out
PxU32 buf[maxResults];
if (maxT == 0.0f) // AABB traversal path
{
RayRTreeCallback<tInflate, false> rTreeCallback(
mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(),
orig, dir, maxT, bothSides, inflate);
PxVec3 inflate1 = tInflate ? *inflate : PxVec3(0); // both maxT and inflate can be zero, so need to check tInflate
mi->getRTree().traverseAABB(orig-inflate1, orig+inflate1, maxResults, buf, &rTreeCallback);
}
else // ray traversal path
{
RayRTreeCallback<tInflate, tRayTest> rTreeCallback(
mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(),
orig, dir, maxT, bothSides, inflate);
mi->getRTree().traverseRay<tInflate>(orig, dir, maxResults, buf, &rTreeCallback, inflate, maxT);
}
}
#define TINST(a,b) \
template void MeshRayCollider::collide<a,b>( \
const PxVec3& orig, const PxVec3& dir, PxReal maxT, bool bothSides, const RTreeTriangleMesh* mesh, \
MeshHitCallback<PxGeomRaycastHit>& callback, const PxVec3* inflate);
TINST(0,0)
TINST(1,0)
TINST(0,1)
TINST(1,1)
#undef TINST
#include "GuRaycastTests.h"
#include "geometry/PxTriangleMeshGeometry.h"
#include "GuTriangleMesh.h"
#include "CmScaling.h"
struct RayMeshColliderCallback : public MeshHitCallback<PxGeomRaycastHit>
{
PxU8* mDstBase;
PxU32 mHitNum;
const PxU32 mMaxHits;
const PxU32 mStride;
const PxMeshScale* mScale;
const PxTransform* mPose;
const PxMat34* mWorld2vertexSkew;
PxU32 mHitFlags;
const PxVec3& mRayDir;
bool mIsDoubleSided;
float mDistCoeff;
RayMeshColliderCallback(
CallbackMode::Enum mode_, PxGeomRaycastHit* hits, PxU32 maxHits, PxU32 stride, const PxMeshScale* scale, const PxTransform* pose,
const PxMat34* world2vertexSkew, PxU32 hitFlags, const PxVec3& rayDir, bool isDoubleSided, float distCoeff) :
MeshHitCallback<PxGeomRaycastHit> (mode_),
mDstBase (reinterpret_cast<PxU8*>(hits)),
mHitNum (0),
mMaxHits (maxHits),
mStride (stride),
mScale (scale),
mPose (pose),
mWorld2vertexSkew (world2vertexSkew),
mHitFlags (hitFlags),
mRayDir (rayDir),
mIsDoubleSided (isDoubleSided),
mDistCoeff (distCoeff)
{
}
// return false for early out
virtual bool processHit(
const PxGeomRaycastHit& lHit, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxReal&, const PxU32*)
{
if(mHitNum == mMaxHits)
return false;
const PxReal u = lHit.u, v = lHit.v;
const PxVec3 localImpact = (1.0f - u - v)*lp0 + u*lp1 + v*lp2;
//not worth concatenating to do 1 transform: PxMat34Legacy vertex2worldSkew = scaling.getVertex2WorldSkew(absPose);
// PT: TODO: revisit this for N hits
PxGeomRaycastHit& hit = *reinterpret_cast<PxGeomRaycastHit*>(mDstBase);
hit = lHit;
hit.position = mPose->transform(mScale->transform(localImpact));
hit.flags = PxHitFlag::ePOSITION|PxHitFlag::eUV|PxHitFlag::eFACE_INDEX;
hit.normal = PxVec3(0.0f);
hit.distance *= mDistCoeff;
// Compute additional information if needed
if(mHitFlags & PxHitFlag::eNORMAL)
{
// User requested impact normal
const PxVec3 localNormal = (lp1 - lp0).cross(lp2 - lp0);
if(mWorld2vertexSkew)
{
hit.normal = mWorld2vertexSkew->rotateTranspose(localNormal);
if (mScale->hasNegativeDeterminant())
PxSwap<PxReal>(hit.u, hit.v); // have to swap the UVs though since they were computed in mesh local space
}
else
hit.normal = mPose->rotate(localNormal);
hit.normal.normalize();
// PT: figure out correct normal orientation (DE7458)
// - if the mesh is single-sided the normal should be the regular triangle normal N, regardless of eMESH_BOTH_SIDES.
// - if the mesh is double-sided the correct normal can be either N or -N. We take the one opposed to ray direction.
if(mIsDoubleSided && hit.normal.dot(mRayDir) > 0.0f)
hit.normal = -hit.normal;
hit.flags |= PxHitFlag::eNORMAL;
}
mHitNum++;
mDstBase += mStride;
return true;
}
private:
RayMeshColliderCallback& operator=(const RayMeshColliderCallback&);
};
PxU32 physx::Gu::raycast_triangleMesh_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose,
const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist,
PxHitFlags hitFlags, PxU32 maxHits, PxGeomRaycastHit* PX_RESTRICT hits, PxU32 stride)
{
PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33);
const RTreeTriangleMesh* meshData = static_cast<const RTreeTriangleMesh*>(mesh);
//scaling: transform the ray to vertex space
PxVec3 orig, dir;
PxMat34 world2vertexSkew;
PxMat34* world2vertexSkewP = NULL;
PxReal distCoeff = 1.0f;
if(meshGeom.scale.isIdentity())
{
orig = pose.transformInv(rayOrigin);
dir = pose.rotateInv(rayDir);
}
else
{
world2vertexSkew = meshGeom.scale.getInverse() * pose.getInverse();
world2vertexSkewP = &world2vertexSkew;
orig = world2vertexSkew.transform(rayOrigin);
dir = world2vertexSkew.rotate(rayDir);
{
distCoeff = dir.normalize();
maxDist *= distCoeff;
maxDist += 1e-3f;
distCoeff = 1.0f / distCoeff;
}
}
const bool isDoubleSided = meshGeom.meshFlags.isSet(PxMeshGeometryFlag::eDOUBLE_SIDED);
const bool bothSides = isDoubleSided || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES);
const bool multipleHits = hitFlags & PxHitFlag::eMESH_MULTIPLE;
RayMeshColliderCallback callback(
multipleHits ? CallbackMode::eMULTIPLE : (hitFlags & PxHitFlag::eANY_HIT ? CallbackMode::eANY : CallbackMode::eCLOSEST),
hits, maxHits, stride, &meshGeom.scale, &pose, world2vertexSkewP, hitFlags, rayDir, isDoubleSided, distCoeff);
MeshRayCollider::collide<0, 1>(orig, dir, maxDist, bothSides, static_cast<const RTreeTriangleMesh*>(meshData), callback, NULL);
return callback.mHitNum;
}
/**
\brief returns indices for the largest axis and 2 other axii
*/
PX_FORCE_INLINE PxU32 largestAxis(const PxVec3& v, PxU32& other1, PxU32& other2)
{
if (v.x >= PxMax(v.y, v.z))
{
other1 = 1;
other2 = 2;
return 0;
}
else if (v.y >= v.z)
{
other1 = 0;
other2 = 2;
return 1;
}
else
{
other1 = 0;
other2 = 1;
return 2;
}
}
static PX_INLINE void computeSweptAABBAroundOBB(
const Box& obb, PxVec3& sweepOrigin, PxVec3& sweepExtents, PxVec3& sweepDir, PxReal& sweepLen)
{
PxU32 other1, other2;
// largest axis of the OBB is the sweep direction, sum of abs of two other is the swept AABB extents
PxU32 lai = largestAxis(obb.extents, other1, other2);
PxVec3 longestAxis = obb.rot[lai]*obb.extents[lai];
PxVec3 absOther1 = obb.rot[other1].abs()*obb.extents[other1];
PxVec3 absOther2 = obb.rot[other2].abs()*obb.extents[other2];
sweepOrigin = obb.center - longestAxis;
sweepExtents = absOther1 + absOther2 + PxVec3(GU_MIN_AABB_EXTENT); // see comments for GU_MIN_AABB_EXTENT
sweepLen = 2.0f; // length is already included in longestAxis
sweepDir = longestAxis;
}
enum { eSPHERE, eCAPSULE, eBOX }; // values for tSCB
#if PX_VC
#pragma warning(push)
#pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value.
#pragma warning( disable : 4512 ) // assignment operator could not be generated
#endif
namespace
{
struct IntersectShapeVsMeshCallback : MeshHitCallback<PxGeomRaycastHit>
{
PX_NOCOPY(IntersectShapeVsMeshCallback)
public:
IntersectShapeVsMeshCallback(const PxMat33& vertexToShapeSkew, LimitedResults* results, bool flipNormal)
: MeshHitCallback<PxGeomRaycastHit>(CallbackMode::eMULTIPLE),
mVertexToShapeSkew (vertexToShapeSkew),
mResults (results),
mAnyHits (false),
mFlipNormal (flipNormal)
{
}
virtual ~IntersectShapeVsMeshCallback(){}
const PxMat33& mVertexToShapeSkew; // vertex to box without translation for boxes
LimitedResults* mResults;
bool mAnyHits;
bool mFlipNormal;
PX_FORCE_INLINE bool recordHit(const PxGeomRaycastHit& aHit, PxIntBool hit)
{
if(hit)
{
mAnyHits = true;
if(mResults)
mResults->add(aHit.faceIndex);
else
return false; // abort traversal if we are only interested in firstContact (mResults is NULL)
}
return true; // if we are here, either no triangles were hit or multiple results are expected => continue traversal
}
};
template<bool tScaleIsIdentity>
struct IntersectSphereVsMeshCallback : IntersectShapeVsMeshCallback
{
IntersectSphereVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {}
virtual ~IntersectSphereVsMeshCallback(){}
PxF32 mMinDist2;
PxVec3 mLocalCenter; // PT: sphere center in local/mesh space
virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position
const PxGeomRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*)
{
const Vec3V v0 = V3LoadU(tScaleIsIdentity ? av0 : mVertexToShapeSkew * av0);
const Vec3V v1 = V3LoadU(tScaleIsIdentity ? av1 : mVertexToShapeSkew * (mFlipNormal ? av2 : av1));
const Vec3V v2 = V3LoadU(tScaleIsIdentity ? av2 : mVertexToShapeSkew * (mFlipNormal ? av1 : av2));
FloatV dummy1, dummy2;
Vec3V closestP;
PxReal dist2;
FStore(distancePointTriangleSquared(V3LoadU(mLocalCenter), v0, v1, v2, dummy1, dummy2, closestP), &dist2);
return recordHit(aHit, dist2 <= mMinDist2);
}
};
template<bool tScaleIsIdentity>
struct IntersectCapsuleVsMeshCallback : IntersectShapeVsMeshCallback
{
IntersectCapsuleVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {}
virtual ~IntersectCapsuleVsMeshCallback(){}
Capsule mLocalCapsule; // PT: capsule in mesh/local space
CapsuleTriangleOverlapData mParams;
virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position
const PxGeomRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*)
{
bool hit;
if(tScaleIsIdentity)
{
const PxVec3 normal = (av0 - av1).cross(av0 - av2);
hit = intersectCapsuleTriangle(normal, av0, av1, av2, mLocalCapsule, mParams);
}
else
{
const PxVec3 v0 = mVertexToShapeSkew * av0;
const PxVec3 v1 = mVertexToShapeSkew * (mFlipNormal ? av2 : av1);
const PxVec3 v2 = mVertexToShapeSkew * (mFlipNormal ? av1 : av2);
const PxVec3 normal = (v0 - v1).cross(v0 - v2);
hit = intersectCapsuleTriangle(normal, v0, v1, v2, mLocalCapsule, mParams);
}
return recordHit(aHit, hit);
}
};
template<bool tScaleIsIdentity>
struct IntersectBoxVsMeshCallback : IntersectShapeVsMeshCallback
{
IntersectBoxVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {}
virtual ~IntersectBoxVsMeshCallback(){}
PxMat34 mVertexToBox;
PxVec3p mBoxExtents, mBoxCenter;
virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position
const PxGeomRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*)
{
PxVec3p v0, v1, v2;
if(tScaleIsIdentity)
{
v0 = mVertexToShapeSkew * av0; // transform from skewed mesh vertex to box space,
v1 = mVertexToShapeSkew * av1; // this includes inverse skew, inverse mesh shape transform and inverse box basis
v2 = mVertexToShapeSkew * av2;
}
else
{
v0 = mVertexToBox.transform(av0);
v1 = mVertexToBox.transform(mFlipNormal ? av2 : av1);
v2 = mVertexToBox.transform(mFlipNormal ? av1 : av2);
}
// PT: this one is safe because we're using PxVec3p for all parameters
const PxIntBool hit = intersectTriangleBox_Unsafe(mBoxCenter, mBoxExtents, v0, v1, v2);
return recordHit(aHit, hit);
}
};
}
#if PX_VC
#pragma warning(pop)
#endif
template<int tSCB, bool idtMeshScale>
static bool intersectAnyVsMeshT(
const Sphere* worldSphere, const Capsule* worldCapsule, const Box* worldOBB,
const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale,
LimitedResults* results)
{
const bool flipNormal = meshScale.hasNegativeDeterminant();
PxMat33 shapeToVertexSkew, vertexToShapeSkew;
if (!idtMeshScale && tSCB != eBOX)
{
vertexToShapeSkew = toMat33(meshScale);
shapeToVertexSkew = vertexToShapeSkew.getInverse();
}
if (tSCB == eSPHERE)
{
IntersectSphereVsMeshCallback<idtMeshScale> callback(vertexToShapeSkew, results, flipNormal);
// transform sphere center from world to mesh shape space
const PxVec3 center = meshTransform.transformInv(worldSphere->center);
// callback will transform verts
callback.mLocalCenter = center;
callback.mMinDist2 = worldSphere->radius*worldSphere->radius;
PxVec3 sweepOrigin, sweepDir, sweepExtents;
PxReal sweepLen;
if (!idtMeshScale)
{
// AP: compute a swept AABB around an OBB around a skewed sphere
// TODO: we could do better than an AABB around OBB actually because we can slice off the corners..
const Box worldOBB_(worldSphere->center, PxVec3(worldSphere->radius), PxMat33(PxIdentity));
Box vertexOBB;
computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale);
computeSweptAABBAroundOBB(vertexOBB, sweepOrigin, sweepExtents, sweepDir, sweepLen);
} else
{
sweepOrigin = center;
sweepDir = PxVec3(1.0f,0,0);
sweepLen = 0.0f;
sweepExtents = PxVec3(PxMax(worldSphere->radius, GU_MIN_AABB_EXTENT));
}
MeshRayCollider::collide<1, 1>(sweepOrigin, sweepDir, sweepLen, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback, &sweepExtents);
return callback.mAnyHits;
}
else if (tSCB == eCAPSULE)
{
IntersectCapsuleVsMeshCallback<idtMeshScale> callback(vertexToShapeSkew, results, flipNormal);
const PxF32 radius = worldCapsule->radius;
// transform world capsule to mesh shape space
callback.mLocalCapsule.p0 = meshTransform.transformInv(worldCapsule->p0);
callback.mLocalCapsule.p1 = meshTransform.transformInv(worldCapsule->p1);
callback.mLocalCapsule.radius = radius;
callback.mParams.init(callback.mLocalCapsule);
if (idtMeshScale)
{
// traverse a sweptAABB around the capsule
const PxVec3 radius3(radius);
MeshRayCollider::collide<1, 0>(callback.mLocalCapsule.p0, callback.mLocalCapsule.p1-callback.mLocalCapsule.p0, 1.0f, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback, &radius3);
}
else
{
// make vertex space OBB
Box vertexOBB;
Box worldOBB_;
worldOBB_.create(*worldCapsule); // AP: potential optimization (meshTransform.inverse is already in callback.mCapsule)
computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale);
MeshRayCollider::collideOBB(vertexOBB, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback);
}
return callback.mAnyHits;
}
else if (tSCB == eBOX)
{
Box vertexOBB; // query box in vertex space
if (idtMeshScale)
{
// mesh scale is identity - just inverse transform the box without optimization
vertexOBB = transformBoxOrthonormal(*worldOBB, meshTransform.getInverse());
// mesh vertices will be transformed from skewed vertex space directly to box AABB space
// box inverse rotation is baked into the vertexToShapeSkew transform
// if meshScale is not identity, vertexOBB already effectively includes meshScale transform
PxVec3 boxCenter;
getInverse(vertexToShapeSkew, boxCenter, vertexOBB.rot, vertexOBB.center);
IntersectBoxVsMeshCallback<idtMeshScale> callback(vertexToShapeSkew, results, flipNormal);
callback.mBoxCenter = -boxCenter;
callback.mBoxExtents = worldOBB->extents; // extents do not change
MeshRayCollider::collideOBB(vertexOBB, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback);
return callback.mAnyHits;
} else
{
computeVertexSpaceOBB(vertexOBB, *worldOBB, meshTransform, meshScale);
// mesh scale needs to be included - inverse transform and optimize the box
const PxMat33 vertexToWorldSkew_Rot = PxMat33Padded(meshTransform.q) * toMat33(meshScale);
const PxVec3& vertexToWorldSkew_Trans = meshTransform.p;
PxMat34 tmp;
buildMatrixFromBox(tmp, *worldOBB);
const PxMat34 inv = tmp.getInverseRT();
const PxMat34 _vertexToWorldSkew(vertexToWorldSkew_Rot, vertexToWorldSkew_Trans);
IntersectBoxVsMeshCallback<idtMeshScale> callback(vertexToShapeSkew, results, flipNormal);
callback.mVertexToBox = inv * _vertexToWorldSkew;
callback.mBoxCenter = PxVec3(0.0f);
callback.mBoxExtents = worldOBB->extents; // extents do not change
MeshRayCollider::collideOBB(vertexOBB, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback);
return callback.mAnyHits;
}
}
else
{
PX_ASSERT(0);
return false;
}
}
template<int tSCB>
static bool intersectAnyVsMesh(
const Sphere* worldSphere, const Capsule* worldCapsule, const Box* worldOBB,
const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale,
LimitedResults* results)
{
PX_ASSERT(triMesh.getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33);
if (meshScale.isIdentity())
return intersectAnyVsMeshT<tSCB, true>(worldSphere, worldCapsule, worldOBB, triMesh, meshTransform, meshScale, results);
else
return intersectAnyVsMeshT<tSCB, false>(worldSphere, worldCapsule, worldOBB, triMesh, meshTransform, meshScale, results);
}
bool physx::Gu::intersectSphereVsMesh_RTREE(const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results)
{
return intersectAnyVsMesh<eSPHERE>(&sphere, NULL, NULL, triMesh, meshTransform, meshScale, results);
}
bool physx::Gu::intersectBoxVsMesh_RTREE(const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results)
{
return intersectAnyVsMesh<eBOX>(NULL, NULL, &box, triMesh, meshTransform, meshScale, results);
}
bool physx::Gu::intersectCapsuleVsMesh_RTREE(const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results)
{
return intersectAnyVsMesh<eCAPSULE>(NULL, &capsule, NULL, triMesh, meshTransform, meshScale, results);
}
void physx::Gu::intersectOBB_RTREE(const TriangleMesh* mesh, const Box& obb, MeshHitCallback<PxGeomRaycastHit>& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned)
{
MeshRayCollider::collideOBB(obb, bothTriangleSidesCollide, static_cast<const RTreeTriangleMesh*>(mesh), callback, checkObbIsAligned);
}
// PT: TODO: refactor/share bits of this
bool physx::Gu::sweepCapsule_MeshGeom_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose,
const Capsule& lss, const PxVec3& unitDir, PxReal distance,
PxGeomSweepHit& sweepHit, PxHitFlags hitFlags, PxReal inflation)
{
PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33);
const RTreeTriangleMesh* meshData = static_cast<const RTreeTriangleMesh*>(mesh);
const Capsule inflatedCapsule(lss.p0, lss.p1, lss.radius + inflation);
const bool isIdentity = triMeshGeom.scale.isIdentity();
bool isDoubleSided = (triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED);
const PxU32 meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES;
// compute sweptAABB
const PxVec3 localP0 = pose.transformInv(inflatedCapsule.p0);
const PxVec3 localP1 = pose.transformInv(inflatedCapsule.p1);
PxVec3 sweepOrigin = (localP0+localP1)*0.5f;
PxVec3 sweepDir = pose.rotateInv(unitDir);
PxVec3 sweepExtents = PxVec3(inflatedCapsule.radius) + (localP0-localP1).abs()*0.5f;
PxReal distance1 = distance;
PxReal distCoeff = 1.0f;
PxMat34 poseWithScale;
if(!isIdentity)
{
poseWithScale = pose * triMeshGeom.scale;
distance1 = computeSweepData(triMeshGeom, sweepOrigin, sweepExtents, sweepDir, distance);
distCoeff = distance1 / distance;
} else
poseWithScale = Matrix34FromTransform(pose);
SweepCapsuleMeshHitCallback callback(sweepHit, poseWithScale, distance, isDoubleSided, inflatedCapsule, unitDir, hitFlags, triMeshGeom.scale.hasNegativeDeterminant(), distCoeff);
MeshRayCollider::collide<1, 1>(sweepOrigin, sweepDir, distance1, true, meshData, callback, &sweepExtents);
if(meshBothSides)
isDoubleSided = true;
return callback.finalizeHit(sweepHit, inflatedCapsule, triMeshGeom, pose, isDoubleSided);
}
#include "GuSweepSharedTests.h"
// PT: TODO: refactor/share bits of this
bool physx::Gu::sweepBox_MeshGeom_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose,
const Box& box, const PxVec3& unitDir, PxReal distance,
PxGeomSweepHit& sweepHit, PxHitFlags hitFlags, PxReal inflation)
{
PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33);
const RTreeTriangleMesh* meshData = static_cast<const RTreeTriangleMesh*>(mesh);
const bool isIdentity = triMeshGeom.scale.isIdentity();
const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES;
const bool isDoubleSided = triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED;
PxMat34 meshToWorldSkew;
PxVec3 sweptAABBMeshSpaceExtents, meshSpaceOrigin, meshSpaceDir;
// Input sweep params: geom, pose, box, unitDir, distance
// We convert the origin from world space to mesh local space
// and convert the box+pose to mesh space AABB
if(isIdentity)
{
meshToWorldSkew = Matrix34FromTransform(pose);
const PxMat33Padded worldToMeshRot(pose.q.getConjugate()); // extract rotation matrix from pose.q
meshSpaceOrigin = worldToMeshRot.transform(box.center - pose.p);
meshSpaceDir = worldToMeshRot.transform(unitDir) * distance;
const PxMat33 boxToMeshRot = worldToMeshRot * box.rot;
sweptAABBMeshSpaceExtents = boxToMeshRot.column0.abs() * box.extents.x +
boxToMeshRot.column1.abs() * box.extents.y +
boxToMeshRot.column2.abs() * box.extents.z;
}
else
{
meshToWorldSkew = pose * triMeshGeom.scale;
const PxMat33 meshToWorldSkew_Rot = PxMat33Padded(pose.q) * toMat33(triMeshGeom.scale);
const PxVec3& meshToWorldSkew_Trans = pose.p;
PxMat33 worldToVertexSkew_Rot;
PxVec3 worldToVertexSkew_Trans;
getInverse(worldToVertexSkew_Rot, worldToVertexSkew_Trans, meshToWorldSkew_Rot, meshToWorldSkew_Trans);
//make vertex space OBB
Box vertexSpaceBox1;
const PxMat34 worldToVertexSkew(worldToVertexSkew_Rot, worldToVertexSkew_Trans);
vertexSpaceBox1 = transform(worldToVertexSkew, box);
// compute swept aabb
sweptAABBMeshSpaceExtents = vertexSpaceBox1.computeAABBExtent();
meshSpaceOrigin = worldToVertexSkew.transform(box.center);
meshSpaceDir = worldToVertexSkew.rotate(unitDir*distance); // also applies scale to direction/length
}
sweptAABBMeshSpaceExtents += PxVec3(inflation); // inflate the bounds with additive inflation
sweptAABBMeshSpaceExtents *= 1.01f; // fatten the bounds to account for numerical discrepancies
PxReal dirLen = PxMax(meshSpaceDir.magnitude(), 1e-5f);
PxReal distCoeff = 1.0f;
if (!isIdentity)
distCoeff = dirLen / distance;
// Move to AABB space
PxMat34 worldToBox;
computeWorldToBoxMatrix(worldToBox, box);
const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides;
const PxMat34Padded meshToBox = worldToBox*meshToWorldSkew;
const PxTransform boxTransform = box.getTransform();
const PxVec3 localDir = worldToBox.rotate(unitDir);
const PxVec3 localDirDist = localDir*distance;
SweepBoxMeshHitCallback callback( // using eMULTIPLE with shrinkMaxT
CallbackMode::eMULTIPLE, meshToBox, distance, bothTriangleSidesCollide, box, localDirDist, localDir, unitDir, hitFlags, inflation, triMeshGeom.scale.hasNegativeDeterminant(), distCoeff);
MeshRayCollider::collide<1, 1>(meshSpaceOrigin, meshSpaceDir/dirLen, dirLen, bothTriangleSidesCollide, meshData, callback, &sweptAABBMeshSpaceExtents);
return callback.finalizeHit(sweepHit, triMeshGeom, pose, boxTransform, localDir, meshBothSides, isDoubleSided);
}
#include "GuInternal.h"
void physx::Gu::sweepConvex_MeshGeom_RTREE(const TriangleMesh* mesh, const Box& hullBox, const PxVec3& localDir, PxReal distance, SweepConvexMeshHitCallback& callback, bool)
{
PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33);
const RTreeTriangleMesh* meshData = static_cast<const RTreeTriangleMesh*>(mesh);
// create temporal bounds
Box querySweptBox;
computeSweptBox(querySweptBox, hullBox.extents, hullBox.center, hullBox.rot, localDir, distance);
MeshRayCollider::collideOBB(querySweptBox, true, meshData, callback);
}
void physx::Gu::pointMeshDistance_RTREE(const TriangleMesh*, const PxTriangleMeshGeometry&, const PxTransform&, const PxVec3&, float, PxU32&, float&, PxVec3&)
{
PxGetFoundation().error(PxErrorCode::eINVALID_OPERATION, PX_FL, "Point-mesh distance query not supported for BVH33. Please use a BVH34 mesh.\n");
}

View File

@@ -0,0 +1,354 @@
// 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/PxSphereGeometry.h"
#include "geometry/PxConvexCoreGeometry.h"
#include "GuMidphaseInterface.h"
#include "CmScaling.h"
#include "GuSphere.h"
#include "GuInternal.h"
#include "GuConvexUtilsInternal.h"
#include "GuVecTriangle.h"
#include "GuVecConvexHull.h"
#include "GuConvexMesh.h"
#include "GuGJK.h"
#include "GuSweepSharedTests.h"
#include "CmMatrix34.h"
#include "GuBounds.h"
#include "GuConvexSupport.h"
#include "GuConvexGeometry.h"
using namespace physx;
using namespace Cm;
using namespace Gu;
using namespace aos;
bool GeomOverlapCallback_SphereMesh(GU_OVERLAP_FUNC_PARAMS)
{
PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE);
PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH);
PX_UNUSED(cache);
PX_UNUSED(threadContext);
const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0);
const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom1);
const Sphere worldSphere(pose0.p, sphereGeom.radius);
TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh);
return Midphase::intersectSphereVsMesh(worldSphere, *meshData, pose1, meshGeom.scale, NULL);
}
bool GeomOverlapCallback_CapsuleMesh(GU_OVERLAP_FUNC_PARAMS)
{
PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE);
PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH);
PX_UNUSED(cache);
PX_UNUSED(threadContext);
const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom0);
const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom1);
TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh);
Capsule capsule;
getCapsule(capsule, capsuleGeom, pose0);
return Midphase::intersectCapsuleVsMesh(capsule, *meshData, pose1, meshGeom.scale, NULL);
}
bool GeomOverlapCallback_BoxMesh(GU_OVERLAP_FUNC_PARAMS)
{
PX_ASSERT(geom0.getType()==PxGeometryType::eBOX);
PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH);
PX_UNUSED(cache);
PX_UNUSED(threadContext);
const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom0);
const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom1);
TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh);
Box box;
buildFrom(box, pose0.p, boxGeom.halfExtents, pose0.q);
return Midphase::intersectBoxVsMesh(box, *meshData, pose1, meshGeom.scale, NULL);
}
///////////////////////////////////////////////////////////////////////////////
namespace
{
struct ConvexVsMeshOverlapCallback : MeshHitCallback<PxGeomRaycastHit>
{
PxMatTransformV MeshToBoxV;
Vec3V boxExtents;
ConvexVsMeshOverlapCallback(
const ConvexMesh& cm, const PxMeshScale& convexScale, const FastVertex2ShapeScaling& meshScale,
const PxTransform& tr0, const PxTransform& tr1, bool identityScale, const Box& meshSpaceOBB)
:
MeshHitCallback<PxGeomRaycastHit>(CallbackMode::eMULTIPLE),
mAnyHit (false),
mIdentityScale (identityScale)
{
if (!identityScale) // not done in initializer list for performance
mMeshScale = aos::Mat33V(
V3LoadU(meshScale.getVertex2ShapeSkew().column0),
V3LoadU(meshScale.getVertex2ShapeSkew().column1),
V3LoadU(meshScale.getVertex2ShapeSkew().column2) );
using namespace aos;
const ConvexHullData* hullData = &cm.getHull();
const Vec3V vScale0 = V3LoadU_SafeReadW(convexScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale
const QuatV vQuat0 = QuatVLoadU(&convexScale.rotation.x);
mConvex = ConvexHullV(hullData, V3Zero(), vScale0, vQuat0, convexScale.isIdentity());
aToB = PxMatTransformV(tr0.transformInv(tr1));
{
// Move to AABB space
PxMat34 MeshToBox;
computeWorldToBoxMatrix(MeshToBox, meshSpaceOBB);
const Vec3V base0 = V3LoadU(MeshToBox.m.column0);
const Vec3V base1 = V3LoadU(MeshToBox.m.column1);
const Vec3V base2 = V3LoadU(MeshToBox.m.column2);
const Mat33V matV(base0, base1, base2);
const Vec3V p = V3LoadU(MeshToBox.p);
MeshToBoxV = PxMatTransformV(p, matV);
boxExtents = V3LoadU(meshSpaceOBB.extents+PxVec3(0.001f));
}
}
virtual ~ConvexVsMeshOverlapCallback() {}
virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position
const PxGeomRaycastHit&, const PxVec3& v0a, const PxVec3& v1a, const PxVec3& v2a, PxReal&, const PxU32*)
{
using namespace aos;
Vec3V v0 = V3LoadU(v0a);
Vec3V v1 = V3LoadU(v1a);
Vec3V v2 = V3LoadU(v2a);
// test triangle AABB in box space vs box AABB in box local space
{
const Vec3V triV0 = MeshToBoxV.transform(v0); // AP: MeshToBoxV already includes mesh scale so we have to use unscaled verts here
const Vec3V triV1 = MeshToBoxV.transform(v1);
const Vec3V triV2 = MeshToBoxV.transform(v2);
const Vec3V triMn = V3Min(V3Min(triV0, triV1), triV2);
const Vec3V triMx = V3Max(V3Max(triV0, triV1), triV2);
const Vec3V negExtents = V3Neg(boxExtents);
const BoolV minSeparated = V3IsGrtr(triMn, boxExtents), maxSeparated = V3IsGrtr(negExtents, triMx);
const BoolV bSeparated = BAnyTrue3(BOr(minSeparated, maxSeparated));
if(BAllEqTTTT(bSeparated))
return true; // continue traversal
}
if(!mIdentityScale)
{
v0 = M33MulV3(mMeshScale, v0);
v1 = M33MulV3(mMeshScale, v1);
v2 = M33MulV3(mMeshScale, v2);
}
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))
{
mAnyHit = true;
return false; // abort traversal
}
return true; // continue traversal
}
ConvexHullV mConvex;
PxMatTransformV aToB;
aos::Mat33V mMeshScale;
bool mAnyHit;
const bool mIdentityScale;
private:
ConvexVsMeshOverlapCallback& operator=(const ConvexVsMeshOverlapCallback&);
};
}
// PT: TODO: refactor bits of this with convex-vs-mesh code
bool GeomOverlapCallback_ConvexMesh(GU_OVERLAP_FUNC_PARAMS)
{
PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH);
PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH);
PX_UNUSED(cache);
PX_UNUSED(threadContext);
const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom0);
const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom1);
ConvexMesh* cm = static_cast<ConvexMesh*>(convexGeom.convexMesh);
TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh);
const bool idtScaleConvex = convexGeom.scale.isIdentity();
const bool idtScaleMesh = meshGeom.scale.isIdentity();
FastVertex2ShapeScaling convexScaling;
if (!idtScaleConvex)
convexScaling.init(convexGeom.scale);
FastVertex2ShapeScaling meshScaling;
if (!idtScaleMesh)
meshScaling.init(meshGeom.scale);
PX_ASSERT(!cm->getLocalBoundsFast().isEmpty());
const PxBounds3 hullAABB = cm->getLocalBoundsFast().transformFast(convexScaling.getVertex2ShapeSkew());
Box hullOBB;
{
const Matrix34FromTransform world0(pose0);
const Matrix34FromTransform world1(pose1);
computeHullOBB(hullOBB, hullAABB, 0.0f, world0, world1, meshScaling, idtScaleMesh);
}
ConvexVsMeshOverlapCallback cb(*cm, convexGeom.scale, meshScaling, pose0, pose1, idtScaleMesh, hullOBB);
Midphase::intersectOBB(meshData, hullOBB, cb, true, false);
return cb.mAnyHit;
}
///////////////////////////////////////////////////////////////////////////////
bool GeomOverlapCallback_MeshMesh(GU_OVERLAP_FUNC_PARAMS)
{
PX_ASSERT(geom0.getType()==PxGeometryType::eTRIANGLEMESH);
PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH);
PX_UNUSED(cache);
PX_UNUSED(threadContext);
const PxTriangleMeshGeometry& meshGeom0 = static_cast<const PxTriangleMeshGeometry&>(geom0);
const PxTriangleMeshGeometry& meshGeom1 = static_cast<const PxTriangleMeshGeometry&>(geom1);
const TriangleMesh* tm0 = static_cast<const TriangleMesh*>(meshGeom0.triangleMesh);
const TriangleMesh* tm1 = static_cast<const TriangleMesh*>(meshGeom1.triangleMesh);
// PT: only implemented for BV4
if(!tm0 || !tm1 || tm0->getConcreteType()!=PxConcreteType::eTRIANGLE_MESH_BVH34 || tm1->getConcreteType()!=PxConcreteType::eTRIANGLE_MESH_BVH34)
return PxGetFoundation().error(PxErrorCode::eINVALID_OPERATION, PX_FL, "PxGeometryQuery::overlap(): only available between two BVH34 triangles meshes.");
class AnyHitReportCallback : public PxReportCallback<PxGeomIndexPair>
{
public:
AnyHitReportCallback()
{
mCapacity = 1;
}
virtual bool flushResults(PxU32, const PxGeomIndexPair*)
{
return false;
}
};
AnyHitReportCallback callback;
// PT: ...so we don't need a table like for the other ops, just go straight to BV4
return intersectMeshVsMesh_BV4(callback, *tm0, pose0, meshGeom0.scale, *tm1, pose1, meshGeom1.scale, PxMeshMeshQueryFlag::eDEFAULT, 0.0f);
}
///////////////////////////////////////////////////////////////////////////////
bool GeomOverlapCallback_ConvexCoreMesh(GU_OVERLAP_FUNC_PARAMS)
{
PX_UNUSED(geom0);
PX_UNUSED(pose0);
PX_UNUSED(geom1);
PX_UNUSED(pose1);
PX_UNUSED(cache);
PX_UNUSED(threadContext);
struct Callback : MeshHitCallback<PxGeomRaycastHit>
{
Gu::ConvexShape convex;
PxMeshScale scale;
bool hit;
Callback(const PxConvexCoreGeometry& geom, const PxTransform& pose, const PxMeshScale& s)
:
MeshHitCallback<PxGeomRaycastHit>(CallbackMode::eMULTIPLE),
scale(s), hit(false)
{
Gu::makeConvexShape(geom, pose, convex);
}
virtual PxAgain processHit(const PxGeomRaycastHit& /*hit*/, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32*)
{
const PxVec3 verts[] = { v0, v1, v2 };
Gu::ConvexShape tri;
tri.coreType = Gu::ConvexCore::Type::ePOINTS;
tri.pose = PxTransform(PxIdentity);
Gu::ConvexCore::PointsCore& core = *reinterpret_cast<Gu::ConvexCore::PointsCore*>(tri.coreData);
core.points = verts;
core.numPoints = 3;
core.stride = sizeof(PxVec3);
core.S = scale.scale;
core.R = scale.rotation;
tri.margin = 0;
PxVec3 point0, point1, axis;
PxReal dist = Gu::RefGjkEpa::computeGjkDistance(convex, tri, convex.pose, tri.pose, convex.margin + tri.margin, point0, point1, axis);
hit = (dist <= convex.margin + tri.margin);
return !hit;
}
};
PX_ASSERT(geom0.getType() == PxGeometryType::eCONVEXCORE);
PX_ASSERT(geom1.getType() == PxGeometryType::eTRIANGLEMESH);
const PxConvexCoreGeometry& shapeConvex = static_cast<const PxConvexCoreGeometry&>(geom0);
const PxTriangleMeshGeometry& shapeMesh = static_cast<const PxTriangleMeshGeometry&>(geom1);
const TriangleMesh* meshData = _getMeshData(shapeMesh);
const PxTransform pose0in1 = pose1.transformInv(pose0);
const PxBounds3 bounds = Gu::computeBounds(geom0, PxTransform(PxIdentity));
Box queryBox;
queryBox.extents = bounds.getExtents();
queryBox.center = pose0in1.transform(bounds.getCenter());
queryBox.rot = PxMat33(pose0in1.q);
const FastVertex2ShapeScaling meshScaling(shapeMesh.scale);
meshScaling.transformQueryBounds(queryBox.center, queryBox.extents, queryBox.rot);
Callback callback(shapeConvex, pose0in1, shapeMesh.scale);
Midphase::intersectOBB(meshData, queryBox, callback, false);
return callback.hit;
}

View File

@@ -0,0 +1,404 @@
// 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 "foundation/PxPreprocessor.h"
#define RTREE_TEXT_DUMP_ENABLE 0
#if PX_P64_FAMILY
#define RTREE_PAGES_PER_POOL_SLAB 16384 // preallocate all pages in first batch to make sure we stay within 32 bits for relative pointers.. this is 2 megs
#else
#define RTREE_PAGES_PER_POOL_SLAB 128
#endif
#define INSERT_SCAN_LOOKAHEAD 1 // enable one level lookahead scan for determining which child page is best to insert a node into
#define RTREE_INFLATION_EPSILON 5e-4f
#include "GuRTree.h"
#include "foundation/PxSort.h"
#include "CmSerialize.h"
#include "CmUtils.h"
#include "foundation/PxUtilities.h"
using namespace physx;
using namespace aos;
using namespace Gu;
using namespace Cm;
namespace physx
{
namespace Gu {
bool RTree::load(PxInputStream& stream, PxU32 meshVersion, bool mismatch_) // PT: 'meshVersion' is the PX_MESH_VERSION from cooked file
{
PX_UNUSED(meshVersion);
release();
PxI8 a, b, c, d;
readChunk(a, b, c, d, stream);
if(a!='R' || b!='T' || c!='R' || d!='E')
return false;
bool mismatch;
PxU32 fileVersion;
if(!readBigEndianVersionNumber(stream, mismatch_, fileVersion, mismatch))
return false;
readFloatBuffer(&mBoundsMin.x, 4, mismatch, stream);
readFloatBuffer(&mBoundsMax.x, 4, mismatch, stream);
readFloatBuffer(&mInvDiagonal.x, 4, mismatch, stream);
readFloatBuffer(&mDiagonalScaler.x, 4, mismatch, stream);
mPageSize = readDword(mismatch, stream);
mNumRootPages = readDword(mismatch, stream);
mNumLevels = readDword(mismatch, stream);
mTotalNodes = readDword(mismatch, stream);
mTotalPages = readDword(mismatch, stream);
PxU32 unused = readDword(mismatch, stream); PX_UNUSED(unused); // backwards compatibility
mPages = static_cast<RTreePage*>(PxAlignedAllocator<128>().allocate(sizeof(RTreePage)*mTotalPages, PX_FL));
PxMarkSerializedMemory(mPages, sizeof(RTreePage)*mTotalPages);
for(PxU32 j=0; j<mTotalPages; j++)
{
readFloatBuffer(mPages[j].minx, RTREE_N, mismatch, stream);
readFloatBuffer(mPages[j].miny, RTREE_N, mismatch, stream);
readFloatBuffer(mPages[j].minz, RTREE_N, mismatch, stream);
readFloatBuffer(mPages[j].maxx, RTREE_N, mismatch, stream);
readFloatBuffer(mPages[j].maxy, RTREE_N, mismatch, stream);
readFloatBuffer(mPages[j].maxz, RTREE_N, mismatch, stream);
ReadDwordBuffer(mPages[j].ptrs, RTREE_N, mismatch, stream);
}
return true;
}
/////////////////////////////////////////////////////////////////////////
PxU32 RTree::computeBottomLevelCount(PxU32 multiplier) const
{
PxU32 topCount = 0, curCount = mNumRootPages;
const RTreePage* rightMostPage = &mPages[mNumRootPages-1];
PX_ASSERT(rightMostPage);
for (PxU32 level = 0; level < mNumLevels-1; level++)
{
topCount += curCount;
PxU32 nc = rightMostPage->nodeCount();
PX_ASSERT(nc > 0 && nc <= RTREE_N);
// old version pointer, up to PX_MESH_VERSION 8
PxU32 ptr = (rightMostPage->ptrs[nc-1]) * multiplier;
PX_ASSERT(ptr % sizeof(RTreePage) == 0);
const RTreePage* rightMostPageNext = mPages + (ptr / sizeof(RTreePage));
curCount = PxU32(rightMostPageNext - rightMostPage);
rightMostPage = rightMostPageNext;
}
return mTotalPages - topCount;
}
/////////////////////////////////////////////////////////////////////////
RTree::RTree(const PxEMPTY)
{
mFlags |= USER_ALLOCATED;
}
// PX_SERIALIZATION
/////////////////////////////////////////////////////////////////////////
void RTree::exportExtraData(PxSerializationContext& stream)
{
stream.alignData(128);
stream.writeData(mPages, mTotalPages*sizeof(RTreePage));
}
/////////////////////////////////////////////////////////////////////////
void RTree::importExtraData(PxDeserializationContext& context)
{
context.alignExtraData(128);
mPages = context.readExtraData<RTreePage>(mTotalPages);
}
/////////////////////////////////////////////////////////////////////////
PX_FORCE_INLINE PxU32 RTreePage::nodeCount() const
{
for (int j = 0; j < RTREE_N; j ++)
if (minx[j] == MX)
return PxU32(j);
return RTREE_N;
}
/////////////////////////////////////////////////////////////////////////
PX_FORCE_INLINE void RTreePage::clearNode(PxU32 nodeIndex)
{
PX_ASSERT(nodeIndex < RTREE_N);
minx[nodeIndex] = miny[nodeIndex] = minz[nodeIndex] = MX; // initialize empty node with sentinels
maxx[nodeIndex] = maxy[nodeIndex] = maxz[nodeIndex] = MN;
ptrs[nodeIndex] = 0;
}
/////////////////////////////////////////////////////////////////////////
PX_FORCE_INLINE void RTreePage::getNode(const PxU32 nodeIndex, RTreeNodeQ& r) const
{
PX_ASSERT(nodeIndex < RTREE_N);
r.minx = minx[nodeIndex];
r.miny = miny[nodeIndex];
r.minz = minz[nodeIndex];
r.maxx = maxx[nodeIndex];
r.maxy = maxy[nodeIndex];
r.maxz = maxz[nodeIndex];
r.ptr = ptrs[nodeIndex];
}
/////////////////////////////////////////////////////////////////////////
PX_FORCE_INLINE void RTreePage::setEmpty(PxU32 startIndex)
{
PX_ASSERT(startIndex < RTREE_N);
for (PxU32 j = startIndex; j < RTREE_N; j ++)
clearNode(j);
}
/////////////////////////////////////////////////////////////////////////
PX_FORCE_INLINE void RTreePage::computeBounds(RTreeNodeQ& newBounds)
{
RTreeValue _minx = MX, _miny = MX, _minz = MX, _maxx = MN, _maxy = MN, _maxz = MN;
for (PxU32 j = 0; j < RTREE_N; j++)
{
if (isEmpty(j))
continue;
_minx = PxMin(_minx, minx[j]);
_miny = PxMin(_miny, miny[j]);
_minz = PxMin(_minz, minz[j]);
_maxx = PxMax(_maxx, maxx[j]);
_maxy = PxMax(_maxy, maxy[j]);
_maxz = PxMax(_maxz, maxz[j]);
}
newBounds.minx = _minx;
newBounds.miny = _miny;
newBounds.minz = _minz;
newBounds.maxx = _maxx;
newBounds.maxy = _maxy;
newBounds.maxz = _maxz;
}
/////////////////////////////////////////////////////////////////////////
PX_FORCE_INLINE void RTreePage::adjustChildBounds(PxU32 index, const RTreeNodeQ& adjChild)
{
PX_ASSERT(index < RTREE_N);
minx[index] = adjChild.minx;
miny[index] = adjChild.miny;
minz[index] = adjChild.minz;
maxx[index] = adjChild.maxx;
maxy[index] = adjChild.maxy;
maxz[index] = adjChild.maxz;
}
/////////////////////////////////////////////////////////////////////////
PX_FORCE_INLINE void RTreePage::growChildBounds(PxU32 index, const RTreeNodeQ& child)
{
PX_ASSERT(index < RTREE_N);
minx[index] = PxMin(minx[index], child.minx);
miny[index] = PxMin(miny[index], child.miny);
minz[index] = PxMin(minz[index], child.minz);
maxx[index] = PxMax(maxx[index], child.maxx);
maxy[index] = PxMax(maxy[index], child.maxy);
maxz[index] = PxMax(maxz[index], child.maxz);
}
/////////////////////////////////////////////////////////////////////////
PX_FORCE_INLINE void RTreePage::copyNode(PxU32 targetIndex, const RTreePage& sourcePage, PxU32 sourceIndex)
{
PX_ASSERT(targetIndex < RTREE_N);
PX_ASSERT(sourceIndex < RTREE_N);
minx[targetIndex] = sourcePage.minx[sourceIndex];
miny[targetIndex] = sourcePage.miny[sourceIndex];
minz[targetIndex] = sourcePage.minz[sourceIndex];
maxx[targetIndex] = sourcePage.maxx[sourceIndex];
maxy[targetIndex] = sourcePage.maxy[sourceIndex];
maxz[targetIndex] = sourcePage.maxz[sourceIndex];
ptrs[targetIndex] = sourcePage.ptrs[sourceIndex];
}
/////////////////////////////////////////////////////////////////////////
PX_FORCE_INLINE void RTreePage::setNode(PxU32 targetIndex, const RTreeNodeQ& sourceNode)
{
PX_ASSERT(targetIndex < RTREE_N);
minx[targetIndex] = sourceNode.minx;
miny[targetIndex] = sourceNode.miny;
minz[targetIndex] = sourceNode.minz;
maxx[targetIndex] = sourceNode.maxx;
maxy[targetIndex] = sourceNode.maxy;
maxz[targetIndex] = sourceNode.maxz;
ptrs[targetIndex] = sourceNode.ptr;
}
/////////////////////////////////////////////////////////////////////////
PX_FORCE_INLINE void RTreeNodeQ::grow(const RTreePage& page, int nodeIndex)
{
PX_ASSERT(nodeIndex < RTREE_N);
minx = PxMin(minx, page.minx[nodeIndex]);
miny = PxMin(miny, page.miny[nodeIndex]);
minz = PxMin(minz, page.minz[nodeIndex]);
maxx = PxMax(maxx, page.maxx[nodeIndex]);
maxy = PxMax(maxy, page.maxy[nodeIndex]);
maxz = PxMax(maxz, page.maxz[nodeIndex]);
}
/////////////////////////////////////////////////////////////////////////
PX_FORCE_INLINE void RTreeNodeQ::grow(const RTreeNodeQ& node)
{
minx = PxMin(minx, node.minx); miny = PxMin(miny, node.miny); minz = PxMin(minz, node.minz);
maxx = PxMax(maxx, node.maxx); maxy = PxMax(maxy, node.maxy); maxz = PxMax(maxz, node.maxz);
}
/////////////////////////////////////////////////////////////////////////
void RTree::validateRecursive(PxU32 level, RTreeNodeQ parentBounds, RTreePage* page, CallbackRefit* cbLeaf)
{
PX_UNUSED(parentBounds);
RTreeNodeQ n;
PxU32 pageNodeCount = page->nodeCount();
for (PxU32 j = 0; j < pageNodeCount; j++)
{
page->getNode(j, n);
if (page->isEmpty(j))
continue;
PX_ASSERT(n.minx >= parentBounds.minx); PX_ASSERT(n.miny >= parentBounds.miny); PX_ASSERT(n.minz >= parentBounds.minz);
PX_ASSERT(n.maxx <= parentBounds.maxx); PX_ASSERT(n.maxy <= parentBounds.maxy); PX_ASSERT(n.maxz <= parentBounds.maxz);
if (!n.isLeaf())
{
PX_ASSERT((n.ptr&1) == 0);
RTreePage* childPage = reinterpret_cast<RTreePage*>(size_t(mPages) + n.ptr);
validateRecursive(level+1, n, childPage, cbLeaf);
} else if (cbLeaf)
{
Vec3V mnv, mxv;
cbLeaf->recomputeBounds(page->ptrs[j] & ~1, mnv, mxv);
PxVec3 mn3, mx3; V3StoreU(mnv, mn3); V3StoreU(mxv, mx3);
const PxBounds3 lb(mn3, mx3);
const PxVec3& mn = lb.minimum; const PxVec3& mx = lb.maximum; PX_UNUSED(mn); PX_UNUSED(mx);
PX_ASSERT(mn.x >= n.minx); PX_ASSERT(mn.y >= n.miny); PX_ASSERT(mn.z >= n.minz);
PX_ASSERT(mx.x <= n.maxx); PX_ASSERT(mx.y <= n.maxy); PX_ASSERT(mx.z <= n.maxz);
}
}
RTreeNodeQ recomputedBounds;
page->computeBounds(recomputedBounds);
PX_ASSERT((recomputedBounds.minx - parentBounds.minx)<=RTREE_INFLATION_EPSILON);
PX_ASSERT((recomputedBounds.miny - parentBounds.miny)<=RTREE_INFLATION_EPSILON);
PX_ASSERT((recomputedBounds.minz - parentBounds.minz)<=RTREE_INFLATION_EPSILON);
PX_ASSERT((recomputedBounds.maxx - parentBounds.maxx)<=RTREE_INFLATION_EPSILON);
PX_ASSERT((recomputedBounds.maxy - parentBounds.maxy)<=RTREE_INFLATION_EPSILON);
PX_ASSERT((recomputedBounds.maxz - parentBounds.maxz)<=RTREE_INFLATION_EPSILON);
}
/////////////////////////////////////////////////////////////////////////
void RTree::validate(CallbackRefit* cbLeaf)
{
for (PxU32 j = 0; j < mNumRootPages; j++)
{
RTreeNodeQ rootBounds;
mPages[j].computeBounds(rootBounds);
validateRecursive(0, rootBounds, mPages+j, cbLeaf);
}
}
void RTree::refitAllStaticTree(CallbackRefit& cb, PxBounds3* retBounds)
{
PxU8* treeNodes8 = reinterpret_cast<PxU8*>(mPages);
// since pages are ordered we can scan back to front and the hierarchy will be updated
for (PxI32 iPage = PxI32(mTotalPages)-1; iPage>=0; iPage--)
{
RTreePage& page = mPages[iPage];
for (PxU32 j = 0; j < RTREE_N; j++)
{
if (page.isEmpty(j))
continue;
if (page.isLeaf(j))
{
Vec3V childMn, childMx;
cb.recomputeBounds(page.ptrs[j]-1, childMn, childMx); // compute the bound around triangles
PxVec3 mn3, mx3;
V3StoreU(childMn, mn3);
V3StoreU(childMx, mx3);
page.minx[j] = mn3.x; page.miny[j] = mn3.y; page.minz[j] = mn3.z;
page.maxx[j] = mx3.x; page.maxy[j] = mx3.y; page.maxz[j] = mx3.z;
} else
{
const RTreePage* child = reinterpret_cast<const RTreePage*>(treeNodes8 + page.ptrs[j]);
PX_COMPILE_TIME_ASSERT(RTREE_N == 4);
bool first = true;
for (PxU32 k = 0; k < RTREE_N; k++)
{
if (child->isEmpty(k))
continue;
if (first)
{
page.minx[j] = child->minx[k]; page.miny[j] = child->miny[k]; page.minz[j] = child->minz[k];
page.maxx[j] = child->maxx[k]; page.maxy[j] = child->maxy[k]; page.maxz[j] = child->maxz[k];
first = false;
} else
{
page.minx[j] = PxMin(page.minx[j], child->minx[k]);
page.miny[j] = PxMin(page.miny[j], child->miny[k]);
page.minz[j] = PxMin(page.minz[j], child->minz[k]);
page.maxx[j] = PxMax(page.maxx[j], child->maxx[k]);
page.maxy[j] = PxMax(page.maxy[j], child->maxy[k]);
page.maxz[j] = PxMax(page.maxz[j], child->maxz[k]);
}
}
}
}
}
if (retBounds)
{
RTreeNodeQ bound1;
for (PxU32 ii = 0; ii<mNumRootPages; ii++)
{
mPages[ii].computeBounds(bound1);
if (ii == 0)
{
retBounds->minimum = PxVec3(bound1.minx, bound1.miny, bound1.minz);
retBounds->maximum = PxVec3(bound1.maxx, bound1.maxy, bound1.maxz);
} else
{
retBounds->minimum = retBounds->minimum.minimum(PxVec3(bound1.minx, bound1.miny, bound1.minz));
retBounds->maximum = retBounds->maximum.maximum(PxVec3(bound1.maxx, bound1.maxy, bound1.maxz));
}
}
}
#if PX_CHECKED
validate(&cb);
#endif
}
//~PX_SERIALIZATION
const RTreeValue RTreePage::MN = -PX_MAX_F32;
const RTreeValue RTreePage::MX = PX_MAX_F32;
} // namespace Gu
}

View File

@@ -0,0 +1,272 @@
// 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_RTREE_H
#define GU_RTREE_H
#include "foundation/PxSimpleTypes.h"
#include "foundation/PxVec4.h"
#include "foundation/PxBounds3.h"
#include "foundation/PxAssert.h"
#include "foundation/PxIO.h"
#include "common/PxSerialFramework.h"
#include "geometry/PxTriangleMesh.h"
#include "foundation/PxUserAllocated.h" // for PxSerializationContext
#include "foundation/PxAlignedMalloc.h"
#include "foundation/PxVecMath.h"
#define RTREE_N 4 // changing this number will affect the mesh format
PX_COMPILE_TIME_ASSERT(RTREE_N == 4 || RTREE_N == 8); // using the low 5 bits for storage of index(childPtr) for dynamic rtree
namespace physx
{
#if PX_VC
#pragma warning(push)
#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value.
#endif
namespace Gu {
class Box;
struct RTreePage;
typedef PxF32 RTreeValue;
/////////////////////////////////////////////////////////////////////////
// quantized untransposed RTree node - used for offline build and dynamic insertion
struct RTreeNodeQ
{
RTreeValue minx, miny, minz, maxx, maxy, maxz;
PxU32 ptr; // lowest bit is leaf flag
PX_FORCE_INLINE void setLeaf(bool set) { if (set) ptr |= 1; else ptr &= ~1; }
PX_FORCE_INLINE PxU32 isLeaf() const { return ptr & 1; }
PX_FORCE_INLINE void setEmpty();
PX_FORCE_INLINE void grow(const RTreePage& page, int nodeIndex);
PX_FORCE_INLINE void grow(const RTreeNodeQ& node);
};
/////////////////////////////////////////////////////////////////////////
// RTreePage data structure, holds RTREE_N transposed nodes
// RTreePage data structure, holds 8 transposed nodes
PX_ALIGN_PREFIX(16)
struct RTreePage
{
static const RTreeValue MN, MX;
RTreeValue minx[RTREE_N]; // [min=MX, max=MN] is used as a sentinel range for empty bounds
RTreeValue miny[RTREE_N];
RTreeValue minz[RTREE_N];
RTreeValue maxx[RTREE_N];
RTreeValue maxy[RTREE_N];
RTreeValue maxz[RTREE_N];
PxU32 ptrs[RTREE_N]; // for static rtree this is an offset relative to the first page divided by 16, for dynamics it's an absolute pointer divided by 16
PX_FORCE_INLINE PxU32 nodeCount() const; // returns the number of occupied nodes in this page
PX_FORCE_INLINE void setEmpty(PxU32 startIndex = 0);
PX_FORCE_INLINE bool isEmpty(PxU32 index) const { return minx[index] > maxx[index]; }
PX_FORCE_INLINE void copyNode(PxU32 targetIndex, const RTreePage& sourcePage, PxU32 sourceIndex);
PX_FORCE_INLINE void setNode(PxU32 targetIndex, const RTreeNodeQ& node);
PX_FORCE_INLINE void clearNode(PxU32 nodeIndex);
PX_FORCE_INLINE void getNode(PxU32 nodeIndex, RTreeNodeQ& result) const;
PX_FORCE_INLINE void computeBounds(RTreeNodeQ& bounds);
PX_FORCE_INLINE void adjustChildBounds(PxU32 index, const RTreeNodeQ& adjustedChildBounds);
PX_FORCE_INLINE void growChildBounds(PxU32 index, const RTreeNodeQ& adjustedChildBounds);
PX_FORCE_INLINE PxU32 getNodeHandle(PxU32 index) const;
PX_FORCE_INLINE PxU32 isLeaf(PxU32 index) const { return ptrs[index] & 1; }
} PX_ALIGN_SUFFIX(16);
/////////////////////////////////////////////////////////////////////////
// RTree root data structure
PX_ALIGN_PREFIX(16)
struct RTree
{
// PX_SERIALIZATION
RTree(const PxEMPTY);
void exportExtraData(PxSerializationContext&);
void importExtraData(PxDeserializationContext& context);
//~PX_SERIALIZATION
PX_INLINE RTree(); // offline static rtree constructor used with cooking
~RTree() { release(); }
PX_INLINE void release();
bool load(PxInputStream& stream, PxU32 meshVersion, bool mismatch);
////////////////////////////////////////////////////////////////////////////
// QUERIES
struct Callback
{
// result buffer should have room for at least RTREE_N items
// should return true to continue traversal. If false is returned, traversal is aborted
virtual bool processResults(PxU32 count, PxU32* buf) = 0;
virtual void profile() {}
virtual ~Callback() {}
};
struct CallbackRaycast
{
// result buffer should have room for at least RTREE_N items
// should return true to continue traversal. If false is returned, traversal is aborted
// newMaxT serves as both input and output, as input it's the maxT so far
// set it to a new value (which should be smaller) and it will become the new far clip t
virtual bool processResults(PxU32 count, PxU32* buf, PxF32& newMaxT) = 0;
virtual ~CallbackRaycast() {}
};
// callback will be issued as soon as the buffer overflows maxResultsPerBlock-RTreePage:SIZE entries
// use maxResults = RTreePage:SIZE and return false from callback for "first hit" early out
void traverseAABB(
const PxVec3& boxMin, const PxVec3& boxMax,
const PxU32 maxResultsPerBlock, PxU32* resultsBlockBuf, Callback* processResultsBlockCallback) const;
void traverseOBB(
const Gu::Box& obb,
const PxU32 maxResultsPerBlock, PxU32* resultsBlockBuf, Callback* processResultsBlockCallback) const;
template <int inflate>
void traverseRay(
const PxVec3& rayOrigin, const PxVec3& rayDir, // dir doesn't have to be normalized and is B-A for raySegment
const PxU32 maxResults, PxU32* resultsPtr,
Gu::RTree::CallbackRaycast* callback,
const PxVec3* inflateAABBs, // inflate tree's AABBs by this amount. This function turns into AABB sweep.
PxF32 maxT = PX_MAX_F32 // maximum ray t parameter, p(t)=origin+t*dir; use 1.0f for ray segment
) const;
struct CallbackRefit
{
// In this callback index is the number stored in the RTree, which is a LeafTriangles object for current PhysX mesh
virtual void recomputeBounds(PxU32 index, aos::Vec3V& mn, aos::Vec3V& mx) = 0;
virtual ~CallbackRefit() {}
};
void refitAllStaticTree(CallbackRefit& cb, PxBounds3* resultMeshBounds); // faster version of refit for static RTree only
////////////////////////////////////////////////////////////////////////////
// DEBUG HELPER FUNCTIONS
PX_PHYSX_COMMON_API void validate(CallbackRefit* cb = NULL); // verify that all children are indeed included in parent bounds
void openTextDump();
void closeTextDump();
void textDump(const char* prefix);
void maxscriptExport();
PxU32 computeBottomLevelCount(PxU32 storedToMemMultiplier) const;
////////////////////////////////////////////////////////////////////////////
// DATA
// remember to update save() and load() when adding or removing data
PxVec4 mBoundsMin, mBoundsMax, mInvDiagonal, mDiagonalScaler; // 16
PxU32 mPageSize;
PxU32 mNumRootPages;
PxU32 mNumLevels;
PxU32 mTotalNodes; // 16
PxU32 mTotalPages;
PxU32 mFlags; enum { USER_ALLOCATED = 0x1, IS_EDGE_SET = 0x2 };
RTreePage* mPages;
protected:
typedef PxU32 NodeHandle;
void validateRecursive(PxU32 level, RTreeNodeQ parentBounds, RTreePage* page, CallbackRefit* cb = NULL);
friend struct RTreePage;
} PX_ALIGN_SUFFIX(16);
#if PX_SUPPORT_EXTERN_TEMPLATE
//explicit template instantiation declaration
extern template
void RTree::traverseRay<0>(const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32) const;
extern template
void RTree::traverseRay<1>(const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32) const;
#endif
#if PX_VC
#pragma warning(pop)
#endif
/////////////////////////////////////////////////////////////////////////
PX_INLINE RTree::RTree()
{
mFlags = 0;
mPages = NULL;
mTotalNodes = 0;
mNumLevels = 0;
mPageSize = RTREE_N;
}
/////////////////////////////////////////////////////////////////////////
PX_INLINE void RTree::release()
{
if ((mFlags & USER_ALLOCATED) == 0 && mPages)
{
physx::PxAlignedAllocator<128>().deallocate(mPages);
mPages = NULL;
}
}
/////////////////////////////////////////////////////////////////////////
PX_FORCE_INLINE void RTreeNodeQ::setEmpty()
{
minx = miny = minz = RTreePage::MX;
maxx = maxy = maxz = RTreePage::MN;
}
// bit 1 is always expected to be set to differentiate between leaf and non-leaf node
PX_FORCE_INLINE PxU32 LeafGetNbTriangles(PxU32 Data) { return ((Data>>1) & 15)+1; }
PX_FORCE_INLINE PxU32 LeafGetTriangleIndex(PxU32 Data) { return Data>>5; }
PX_FORCE_INLINE PxU32 LeafSetData(PxU32 nb, PxU32 index)
{
PX_ASSERT(nb>0 && nb<=16); PX_ASSERT(index < (1<<27));
return (index<<5)|(((nb-1)&15)<<1) | 1;
}
struct LeafTriangles
{
PxU32 Data;
// Gets number of triangles in the leaf, returns the number of triangles N, with 0 < N <= 16
PX_FORCE_INLINE PxU32 GetNbTriangles() const { return LeafGetNbTriangles(Data); }
// Gets triangle index for this leaf. Indexed model's array of indices retrieved with RTreeMidphase::GetIndices()
PX_FORCE_INLINE PxU32 GetTriangleIndex() const { return LeafGetTriangleIndex(Data); }
PX_FORCE_INLINE void SetData(PxU32 nb, PxU32 index) { Data = LeafSetData(nb, index); }
};
PX_COMPILE_TIME_ASSERT(sizeof(LeafTriangles)==4); // RTree has space for 4 bytes
} // namespace Gu
}
#endif // #ifdef PX_COLLISION_RTREE

View File

@@ -0,0 +1,571 @@
// 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.
/*
General notes:
rtree depth-first traversal looks like this:
push top level page onto stack
pop page from stack
for each node in page
if node overlaps with testrect
push node's subpage
we want to efficiently keep track of current stack level to know if the current page is a leaf or not
(since we don't store a flag with the page due to no space, we can't determine it just by looking at current page)
since we traverse depth first, the levels for nodes on the stack look like this:
l0 l0 l1 l2 l2 l3 l3 l3 l4
we can encode this as an array of 4 bits per level count into a 32-bit integer
to simplify the code->level computation we also keep track of current level by incrementing the level whenever any subpages
from current test page are pushed onto the stack
when we pop a page off the stack we use this encoding to determine if we should decrement the stack level
*/
#include "foundation/PxBounds3.h"
#include "foundation/PxIntrinsics.h"
#include "foundation/PxBitUtils.h"
#include "GuRTree.h"
#include "GuBox.h"
#include "foundation/PxVecMath.h"
#include "PxQueryReport.h" // for PxAgain
#include "GuBVConstants.h"
//#define VERIFY_RTREE
#ifdef VERIFY_RTREE
#include "GuIntersectionRayBox.h"
#include "GuIntersectionBoxBox.h"
#include "stdio.h"
#endif
using namespace physx;
using namespace aos;
namespace physx
{
namespace Gu {
using namespace aos;
#define v_absm(a) V4Andc(a, signMask)
#define V4FromF32A(x) V4LoadA(x)
#define PxF32FV(x) FStore(x)
#define CAST_U8(a) reinterpret_cast<PxU8*>(a)
/////////////////////////////////////////////////////////////////////////
void RTree::traverseAABB(const PxVec3& boxMin, const PxVec3& boxMax, const PxU32 maxResults, PxU32* resultsPtr, Callback* callback) const
{
PX_UNUSED(resultsPtr);
PX_ASSERT(callback);
PX_ASSERT(maxResults >= mPageSize);
PX_UNUSED(maxResults);
const PxU32 maxStack = 128;
PxU32 stack1[maxStack];
PxU32* stack = stack1+1;
PX_ASSERT(mPages);
PX_ASSERT((uintptr_t(mPages) & 127) == 0);
PX_ASSERT((uintptr_t(this) & 15) == 0);
// conservatively quantize the input box
Vec4V nqMin = Vec4V_From_PxVec3_WUndefined(boxMin);
Vec4V nqMax = Vec4V_From_PxVec3_WUndefined(boxMax);
Vec4V nqMinx4 = V4SplatElement<0>(nqMin);
Vec4V nqMiny4 = V4SplatElement<1>(nqMin);
Vec4V nqMinz4 = V4SplatElement<2>(nqMin);
Vec4V nqMaxx4 = V4SplatElement<0>(nqMax);
Vec4V nqMaxy4 = V4SplatElement<1>(nqMax);
Vec4V nqMaxz4 = V4SplatElement<2>(nqMax);
// on 64-bit platforms the dynamic rtree pointer is also relative to mPages
PxU8* treeNodes8 = CAST_U8(mPages);
PxU32* stackPtr = stack;
// AP potential perf optimization - fetch the top level right away
PX_ASSERT(RTREE_N == 4 || RTREE_N == 8);
PX_ASSERT(PxIsPowerOfTwo(mPageSize));
for (PxI32 j = PxI32(mNumRootPages-1); j >= 0; j --)
*stackPtr++ = j*sizeof(RTreePage);
PxU32 cacheTopValid = true;
PxU32 cacheTop = 0;
do {
stackPtr--;
PxU32 top;
if (cacheTopValid) // branch is faster than lhs
top = cacheTop;
else
top = stackPtr[0];
PX_ASSERT(!cacheTopValid || stackPtr[0] == cacheTop);
RTreePage* PX_RESTRICT tn = reinterpret_cast<RTreePage*>(treeNodes8 + top);
const PxU32* ptrs = (reinterpret_cast<RTreePage*>(tn))->ptrs;
Vec4V minx4 = V4LoadA(tn->minx);
Vec4V miny4 = V4LoadA(tn->miny);
Vec4V minz4 = V4LoadA(tn->minz);
Vec4V maxx4 = V4LoadA(tn->maxx);
Vec4V maxy4 = V4LoadA(tn->maxy);
Vec4V maxz4 = V4LoadA(tn->maxz);
// AABB/AABB overlap test
BoolV res0 = V4IsGrtr(nqMinx4, maxx4); BoolV res1 = V4IsGrtr(nqMiny4, maxy4); BoolV res2 = V4IsGrtr(nqMinz4, maxz4);
BoolV res3 = V4IsGrtr(minx4, nqMaxx4); BoolV res4 = V4IsGrtr(miny4, nqMaxy4); BoolV res5 = V4IsGrtr(minz4, nqMaxz4);
BoolV resx = BOr(BOr(BOr(res0, res1), BOr(res2, res3)), BOr(res4, res5));
PX_ALIGN_PREFIX(16) PxU32 resa[RTREE_N] PX_ALIGN_SUFFIX(16);
VecU32V res4x = VecU32V_From_BoolV(resx);
U4StoreA(res4x, resa);
cacheTopValid = false;
for (PxU32 i = 0; i < RTREE_N; i++)
{
PxU32 ptr = ptrs[i] & ~1; // clear the isLeaf bit
if (resa[i])
continue;
if (tn->isLeaf(i))
{
if (!callback->processResults(1, &ptr))
return;
}
else
{
*(stackPtr++) = ptr;
cacheTop = ptr;
cacheTopValid = true;
}
}
} while (stackPtr > stack);
}
/////////////////////////////////////////////////////////////////////////
template <int inflate>
void RTree::traverseRay(
const PxVec3& rayOrigin, const PxVec3& rayDir,
const PxU32 maxResults, PxU32* resultsPtr, Gu::RTree::CallbackRaycast* callback,
const PxVec3* fattenAABBs, PxF32 maxT) const
{
// implements Kay-Kajiya 4-way SIMD test
PX_UNUSED(resultsPtr);
PX_UNUSED(maxResults);
const PxU32 maxStack = 128;
PxU32 stack1[maxStack];
PxU32* stack = stack1+1;
PX_ASSERT(mPages);
PX_ASSERT((uintptr_t(mPages) & 127) == 0);
PX_ASSERT((uintptr_t(this) & 15) == 0);
PxU8* treeNodes8 = CAST_U8(mPages);
Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ;
PX_UNUSED(fattenAABBsX); PX_UNUSED(fattenAABBsY); PX_UNUSED(fattenAABBsZ);
if (inflate)
{
Vec4V fattenAABBs4 = Vec4V_From_PxVec3_WUndefined(*fattenAABBs);
fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap
fattenAABBsX = V4SplatElement<0>(fattenAABBs4);
fattenAABBsY = V4SplatElement<1>(fattenAABBs4);
fattenAABBsZ = V4SplatElement<2>(fattenAABBs4);
}
Vec4V maxT4;
maxT4 = V4Load(maxT);
Vec4V rayP = Vec4V_From_PxVec3_WUndefined(rayOrigin);
Vec4V rayD = Vec4V_From_PxVec3_WUndefined(rayDir);
VecU32V raySign = V4U32and(VecU32V_ReinterpretFrom_Vec4V(rayD), signMask);
Vec4V rayDAbs = V4Abs(rayD); // abs value of rayD
Vec4V rayInvD = Vec4V_ReinterpretFrom_VecU32V(V4U32or(raySign, VecU32V_ReinterpretFrom_Vec4V(V4Max(rayDAbs, epsFloat4)))); // clamp near-zero components up to epsilon
rayD = rayInvD;
//rayInvD = V4Recip(rayInvD);
// Newton-Raphson iteration for reciprocal (see wikipedia):
// X[n+1] = X[n]*(2-original*X[n]), X[0] = V4RecipFast estimate
//rayInvD = rayInvD*(twos-rayD*rayInvD);
rayInvD = V4RecipFast(rayInvD); // initial estimate, not accurate enough
rayInvD = V4Mul(rayInvD, V4NegMulSub(rayD, rayInvD, twos));
// P+tD=a; t=(a-P)/D
// t=(a - p.x)*1/d.x = a/d.x +(- p.x/d.x)
Vec4V rayPinvD = V4NegMulSub(rayInvD, rayP, zeroes);
Vec4V rayInvDsplatX = V4SplatElement<0>(rayInvD);
Vec4V rayInvDsplatY = V4SplatElement<1>(rayInvD);
Vec4V rayInvDsplatZ = V4SplatElement<2>(rayInvD);
Vec4V rayPinvDsplatX = V4SplatElement<0>(rayPinvD);
Vec4V rayPinvDsplatY = V4SplatElement<1>(rayPinvD);
Vec4V rayPinvDsplatZ = V4SplatElement<2>(rayPinvD);
PX_ASSERT(RTREE_N == 4 || RTREE_N == 8);
PX_ASSERT(mNumRootPages > 0);
PxU32 stackPtr = 0;
for (PxI32 j = PxI32(mNumRootPages-1); j >= 0; j --)
stack[stackPtr++] = j*sizeof(RTreePage);
PX_ALIGN_PREFIX(16) PxU32 resa[4] PX_ALIGN_SUFFIX(16);
while (stackPtr)
{
PxU32 top = stack[--stackPtr];
if (top&1) // isLeaf test
{
top--;
PxF32 newMaxT = maxT;
if (!callback->processResults(1, &top, newMaxT))
return;
/* shrink the ray if newMaxT is reduced compared to the original maxT */
if (maxT != newMaxT)
{
PX_ASSERT(newMaxT < maxT);
maxT = newMaxT;
maxT4 = V4Load(newMaxT);
}
continue;
}
RTreePage* PX_RESTRICT tn = reinterpret_cast<RTreePage*>(treeNodes8 + top);
// 6i load
Vec4V minx4a = V4LoadA(tn->minx), miny4a = V4LoadA(tn->miny), minz4a = V4LoadA(tn->minz);
Vec4V maxx4a = V4LoadA(tn->maxx), maxy4a = V4LoadA(tn->maxy), maxz4a = V4LoadA(tn->maxz);
// 1i disabled test
// AP scaffold - optimization opportunity - can save 2 instructions here
VecU32V ignore4a = V4IsGrtrV32u(minx4a, maxx4a); // 1 if degenerate box (empty slot in the page)
if (inflate)
{
// 6i
maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ);
minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ);
}
// P+tD=a; t=(a-P)/D
// t=(a - p.x)*1/d.x = a/d.x +(- p.x/d.x)
// 6i
Vec4V tminxa0 = V4MulAdd(minx4a, rayInvDsplatX, rayPinvDsplatX);
Vec4V tminya0 = V4MulAdd(miny4a, rayInvDsplatY, rayPinvDsplatY);
Vec4V tminza0 = V4MulAdd(minz4a, rayInvDsplatZ, rayPinvDsplatZ);
Vec4V tmaxxa0 = V4MulAdd(maxx4a, rayInvDsplatX, rayPinvDsplatX);
Vec4V tmaxya0 = V4MulAdd(maxy4a, rayInvDsplatY, rayPinvDsplatY);
Vec4V tmaxza0 = V4MulAdd(maxz4a, rayInvDsplatZ, rayPinvDsplatZ);
// test half-spaces
// P+tD=dN
// t = (d(N,D)-(P,D))/(D,D) , (D,D)=1
// compute 4x dot products (N,D) and (P,N) for each AABB in the page
// 6i
// now compute tnear and tfar for each pair of planes for each box
Vec4V tminxa = V4Min(tminxa0, tmaxxa0); Vec4V tmaxxa = V4Max(tminxa0, tmaxxa0);
Vec4V tminya = V4Min(tminya0, tmaxya0); Vec4V tmaxya = V4Max(tminya0, tmaxya0);
Vec4V tminza = V4Min(tminza0, tmaxza0); Vec4V tmaxza = V4Max(tminza0, tmaxza0);
// 8i
Vec4V maxOfNeasa = V4Max(V4Max(tminxa, tminya), tminza);
Vec4V minOfFarsa = V4Min(V4Min(tmaxxa, tmaxya), tmaxza);
ignore4a = V4U32or(ignore4a, V4IsGrtrV32u(epsFloat4, minOfFarsa)); // if tfar is negative, ignore since its a ray, not a line
// AP scaffold: update the build to eliminate 3 more instructions for ignore4a above
//VecU32V ignore4a = V4IsGrtrV32u(epsFloat4, minOfFarsa); // if tfar is negative, ignore since its a ray, not a line
ignore4a = V4U32or(ignore4a, V4IsGrtrV32u(maxOfNeasa, maxT4)); // if tnear is over maxT, ignore this result
// 2i
VecU32V resa4 = V4IsGrtrV32u(maxOfNeasa, minOfFarsa); // if 1 => fail
resa4 = V4U32or(resa4, ignore4a);
// 1i
V4U32StoreAligned(resa4, reinterpret_cast<VecU32V*>(resa));
PxU32* ptrs = (reinterpret_cast<RTreePage*>(tn))->ptrs;
stack[stackPtr] = ptrs[0]; stackPtr += (1+resa[0]); // AP scaffold TODO: use VecU32add
stack[stackPtr] = ptrs[1]; stackPtr += (1+resa[1]);
stack[stackPtr] = ptrs[2]; stackPtr += (1+resa[2]);
stack[stackPtr] = ptrs[3]; stackPtr += (1+resa[3]);
}
}
//explicit template instantiation
template void RTree::traverseRay<0>(const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32) const;
template void RTree::traverseRay<1>(const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32) const;
/////////////////////////////////////////////////////////////////////////
void RTree::traverseOBB(
const Gu::Box& obb, const PxU32 maxResults, PxU32* resultsPtr, Gu::RTree::Callback* callback) const
{
PX_UNUSED(resultsPtr);
PX_UNUSED(maxResults);
const PxU32 maxStack = 128;
PxU32 stack[maxStack];
PX_ASSERT(mPages);
PX_ASSERT((uintptr_t(mPages) & 127) == 0);
PX_ASSERT((uintptr_t(this) & 15) == 0);
PxU8* treeNodes8 = CAST_U8(mPages);
PxU32* stackPtr = stack;
Vec4V ones, halves, eps;
ones = V4Load(1.0f);
halves = V4Load(0.5f);
eps = V4Load(1e-6f);
PX_UNUSED(ones);
Vec4V obbO = Vec4V_From_PxVec3_WUndefined(obb.center);
Vec4V obbE = Vec4V_From_PxVec3_WUndefined(obb.extents);
// Gu::Box::rot matrix columns are the OBB axes
Vec4V obbX = Vec4V_From_PxVec3_WUndefined(obb.rot.column0);
Vec4V obbY = Vec4V_From_PxVec3_WUndefined(obb.rot.column1);
Vec4V obbZ = Vec4V_From_PxVec3_WUndefined(obb.rot.column2);
#if PX_WINDOWS
// Visual Studio compiler hangs with #defines
// On VMX platforms we use #defines in the other branch of this #ifdef to avoid register spills (LHS)
Vec4V obbESplatX = V4SplatElement<0>(obbE);
Vec4V obbESplatY = V4SplatElement<1>(obbE);
Vec4V obbESplatZ = V4SplatElement<2>(obbE);
Vec4V obbESplatNegX = V4Sub(zeroes, obbESplatX);
Vec4V obbESplatNegY = V4Sub(zeroes, obbESplatY);
Vec4V obbESplatNegZ = V4Sub(zeroes, obbESplatZ);
Vec4V obbXE = V4MulAdd(obbX, obbESplatX, zeroes); // scale axii by E
Vec4V obbYE = V4MulAdd(obbY, obbESplatY, zeroes); // scale axii by E
Vec4V obbZE = V4MulAdd(obbZ, obbESplatZ, zeroes); // scale axii by E
Vec4V obbOSplatX = V4SplatElement<0>(obbO);
Vec4V obbOSplatY = V4SplatElement<1>(obbO);
Vec4V obbOSplatZ = V4SplatElement<2>(obbO);
Vec4V obbXSplatX = V4SplatElement<0>(obbX);
Vec4V obbXSplatY = V4SplatElement<1>(obbX);
Vec4V obbXSplatZ = V4SplatElement<2>(obbX);
Vec4V obbYSplatX = V4SplatElement<0>(obbY);
Vec4V obbYSplatY = V4SplatElement<1>(obbY);
Vec4V obbYSplatZ = V4SplatElement<2>(obbY);
Vec4V obbZSplatX = V4SplatElement<0>(obbZ);
Vec4V obbZSplatY = V4SplatElement<1>(obbZ);
Vec4V obbZSplatZ = V4SplatElement<2>(obbZ);
Vec4V obbXESplatX = V4SplatElement<0>(obbXE);
Vec4V obbXESplatY = V4SplatElement<1>(obbXE);
Vec4V obbXESplatZ = V4SplatElement<2>(obbXE);
Vec4V obbYESplatX = V4SplatElement<0>(obbYE);
Vec4V obbYESplatY = V4SplatElement<1>(obbYE);
Vec4V obbYESplatZ = V4SplatElement<2>(obbYE);
Vec4V obbZESplatX = V4SplatElement<0>(obbZE);
Vec4V obbZESplatY = V4SplatElement<1>(obbZE);
Vec4V obbZESplatZ = V4SplatElement<2>(obbZE);
#else
#define obbESplatX V4SplatElement<0>(obbE)
#define obbESplatY V4SplatElement<1>(obbE)
#define obbESplatZ V4SplatElement<2>(obbE)
#define obbESplatNegX V4Sub(zeroes, obbESplatX)
#define obbESplatNegY V4Sub(zeroes, obbESplatY)
#define obbESplatNegZ V4Sub(zeroes, obbESplatZ)
#define obbXE V4MulAdd(obbX, obbESplatX, zeroes)
#define obbYE V4MulAdd(obbY, obbESplatY, zeroes)
#define obbZE V4MulAdd(obbZ, obbESplatZ, zeroes)
#define obbOSplatX V4SplatElement<0>(obbO)
#define obbOSplatY V4SplatElement<1>(obbO)
#define obbOSplatZ V4SplatElement<2>(obbO)
#define obbXSplatX V4SplatElement<0>(obbX)
#define obbXSplatY V4SplatElement<1>(obbX)
#define obbXSplatZ V4SplatElement<2>(obbX)
#define obbYSplatX V4SplatElement<0>(obbY)
#define obbYSplatY V4SplatElement<1>(obbY)
#define obbYSplatZ V4SplatElement<2>(obbY)
#define obbZSplatX V4SplatElement<0>(obbZ)
#define obbZSplatY V4SplatElement<1>(obbZ)
#define obbZSplatZ V4SplatElement<2>(obbZ)
#define obbXESplatX V4SplatElement<0>(obbXE)
#define obbXESplatY V4SplatElement<1>(obbXE)
#define obbXESplatZ V4SplatElement<2>(obbXE)
#define obbYESplatX V4SplatElement<0>(obbYE)
#define obbYESplatY V4SplatElement<1>(obbYE)
#define obbYESplatZ V4SplatElement<2>(obbYE)
#define obbZESplatX V4SplatElement<0>(obbZE)
#define obbZESplatY V4SplatElement<1>(obbZE)
#define obbZESplatZ V4SplatElement<2>(obbZE)
#endif
PX_ASSERT(mPageSize == 4 || mPageSize == 8);
PX_ASSERT(mNumRootPages > 0);
for (PxI32 j = PxI32(mNumRootPages-1); j >= 0; j --)
*stackPtr++ = j*sizeof(RTreePage);
PxU32 cacheTopValid = true;
PxU32 cacheTop = 0;
PX_ALIGN_PREFIX(16) PxU32 resa_[4] PX_ALIGN_SUFFIX(16);
do {
stackPtr--;
PxU32 top;
if (cacheTopValid) // branch is faster than lhs
top = cacheTop;
else
top = stackPtr[0];
PX_ASSERT(!cacheTopValid || top == cacheTop);
RTreePage* PX_RESTRICT tn = reinterpret_cast<RTreePage*>(treeNodes8 + top);
const PxU32 offs = 0;
PxU32* ptrs = (reinterpret_cast<RTreePage*>(tn))->ptrs;
// 6i
Vec4V minx4a = V4LoadA(tn->minx+offs);
Vec4V miny4a = V4LoadA(tn->miny+offs);
Vec4V minz4a = V4LoadA(tn->minz+offs);
Vec4V maxx4a = V4LoadA(tn->maxx+offs);
Vec4V maxy4a = V4LoadA(tn->maxy+offs);
Vec4V maxz4a = V4LoadA(tn->maxz+offs);
VecU32V noOverlapa;
VecU32V resa4u;
{
// PRECOMPUTE FOR A BLOCK
// 109 instr per 4 OBB/AABB
// ABB iteration 1, start with OBB origin as other point -- 6
Vec4V p1ABBxa = V4Max(minx4a, V4Min(maxx4a, obbOSplatX));
Vec4V p1ABBya = V4Max(miny4a, V4Min(maxy4a, obbOSplatY));
Vec4V p1ABBza = V4Max(minz4a, V4Min(maxz4a, obbOSplatZ));
// OBB iteration 1, move to OBB space first -- 12
Vec4V p1ABBOxa = V4Sub(p1ABBxa, obbOSplatX);
Vec4V p1ABBOya = V4Sub(p1ABBya, obbOSplatY);
Vec4V p1ABBOza = V4Sub(p1ABBza, obbOSplatZ);
Vec4V obbPrjXa = V4MulAdd(p1ABBOxa, obbXSplatX, V4MulAdd(p1ABBOya, obbXSplatY, V4MulAdd(p1ABBOza, obbXSplatZ, zeroes)));
Vec4V obbPrjYa = V4MulAdd(p1ABBOxa, obbYSplatX, V4MulAdd(p1ABBOya, obbYSplatY, V4MulAdd(p1ABBOza, obbYSplatZ, zeroes)));
Vec4V obbPrjZa = V4MulAdd(p1ABBOxa, obbZSplatX, V4MulAdd(p1ABBOya, obbZSplatY, V4MulAdd(p1ABBOza, obbZSplatZ, zeroes)));
// clamp AABB point in OBB space to OBB extents. Since we scaled the axii, the extents are [-1,1] -- 6
Vec4V pOBBxa = V4Max(obbESplatNegX, V4Min(obbPrjXa, obbESplatX));
Vec4V pOBBya = V4Max(obbESplatNegY, V4Min(obbPrjYa, obbESplatY));
Vec4V pOBBza = V4Max(obbESplatNegZ, V4Min(obbPrjZa, obbESplatZ));
// go back to AABB space. we have x,y,z in obb space, need to multiply by axii -- 9
Vec4V p1OBBxa = V4MulAdd(pOBBxa, obbXSplatX, V4MulAdd(pOBBya, obbYSplatX, V4MulAdd(pOBBza, obbZSplatX, obbOSplatX)));
Vec4V p1OBBya = V4MulAdd(pOBBxa, obbXSplatY, V4MulAdd(pOBBya, obbYSplatY, V4MulAdd(pOBBza, obbZSplatY, obbOSplatY)));
Vec4V p1OBBza = V4MulAdd(pOBBxa, obbXSplatZ, V4MulAdd(pOBBya, obbYSplatZ, V4MulAdd(pOBBza, obbZSplatZ, obbOSplatZ)));
// ABB iteration 2 -- 6 instructions
Vec4V p2ABBxa = V4Max(minx4a, V4Min(maxx4a, p1OBBxa));
Vec4V p2ABBya = V4Max(miny4a, V4Min(maxy4a, p1OBBya));
Vec4V p2ABBza = V4Max(minz4a, V4Min(maxz4a, p1OBBza));
// above blocks add up to 12+12+15=39 instr
// END PRECOMPUTE FOR A BLOCK
// for AABBs precompute extents and center -- 9i
Vec4V abbCxa = V4MulAdd(V4Add(maxx4a, minx4a), halves, zeroes);
Vec4V abbCya = V4MulAdd(V4Add(maxy4a, miny4a), halves, zeroes);
Vec4V abbCza = V4MulAdd(V4Add(maxz4a, minz4a), halves, zeroes);
Vec4V abbExa = V4Sub(maxx4a, abbCxa);
Vec4V abbEya = V4Sub(maxy4a, abbCya);
Vec4V abbEza = V4Sub(maxz4a, abbCza);
// now test separating axes D1 = p1OBB-p1ABB and D2 = p1OBB-p2ABB -- 37 instructions per axis
// D1 first -- 3 instructions
Vec4V d1xa = V4Sub(p1OBBxa, p1ABBxa), d1ya = V4Sub(p1OBBya, p1ABBya), d1za = V4Sub(p1OBBza, p1ABBza);
// for AABB compute projections of extents and center -- 6
Vec4V abbExd1Prja = V4MulAdd(d1xa, abbExa, zeroes);
Vec4V abbEyd1Prja = V4MulAdd(d1ya, abbEya, zeroes);
Vec4V abbEzd1Prja = V4MulAdd(d1za, abbEza, zeroes);
Vec4V abbCd1Prja = V4MulAdd(d1xa, abbCxa, V4MulAdd(d1ya, abbCya, V4MulAdd(d1za, abbCza, zeroes)));
// for obb project each halfaxis and origin and add abs values of half-axis projections -- 12 instructions
Vec4V obbXEd1Prja = V4MulAdd(d1xa, obbXESplatX, V4MulAdd(d1ya, obbXESplatY, V4MulAdd(d1za, obbXESplatZ, zeroes)));
Vec4V obbYEd1Prja = V4MulAdd(d1xa, obbYESplatX, V4MulAdd(d1ya, obbYESplatY, V4MulAdd(d1za, obbYESplatZ, zeroes)));
Vec4V obbZEd1Prja = V4MulAdd(d1xa, obbZESplatX, V4MulAdd(d1ya, obbZESplatY, V4MulAdd(d1za, obbZESplatZ, zeroes)));
Vec4V obbOd1Prja = V4MulAdd(d1xa, obbOSplatX, V4MulAdd(d1ya, obbOSplatY, V4MulAdd(d1za, obbOSplatZ, zeroes)));
// compare lengths between projected centers with sum of projected radii -- 16i
Vec4V originDiffd1a = v_absm(V4Sub(abbCd1Prja, obbOd1Prja));
Vec4V absABBRd1a = V4Add(V4Add(v_absm(abbExd1Prja), v_absm(abbEyd1Prja)), v_absm(abbEzd1Prja));
Vec4V absOBBRd1a = V4Add(V4Add(v_absm(obbXEd1Prja), v_absm(obbYEd1Prja)), v_absm(obbZEd1Prja));
VecU32V noOverlapd1a = V4IsGrtrV32u(V4Sub(originDiffd1a, eps), V4Add(absABBRd1a, absOBBRd1a));
VecU32V epsNoOverlapd1a = V4IsGrtrV32u(originDiffd1a, eps);
// D2 next (35 instr)
// 3i
Vec4V d2xa = V4Sub(p1OBBxa, p2ABBxa), d2ya = V4Sub(p1OBBya, p2ABBya), d2za = V4Sub(p1OBBza, p2ABBza);
// for AABB compute projections of extents and center -- 6
Vec4V abbExd2Prja = V4MulAdd(d2xa, abbExa, zeroes);
Vec4V abbEyd2Prja = V4MulAdd(d2ya, abbEya, zeroes);
Vec4V abbEzd2Prja = V4MulAdd(d2za, abbEza, zeroes);
Vec4V abbCd2Prja = V4MulAdd(d2xa, abbCxa, V4MulAdd(d2ya, abbCya, V4MulAdd(d2za, abbCza, zeroes)));
// for obb project each halfaxis and origin and add abs values of half-axis projections -- 12i
Vec4V obbXEd2Prja = V4MulAdd(d2xa, obbXESplatX, V4MulAdd(d2ya, obbXESplatY, V4MulAdd(d2za, obbXESplatZ, zeroes)));
Vec4V obbYEd2Prja = V4MulAdd(d2xa, obbYESplatX, V4MulAdd(d2ya, obbYESplatY, V4MulAdd(d2za, obbYESplatZ, zeroes)));
Vec4V obbZEd2Prja = V4MulAdd(d2xa, obbZESplatX, V4MulAdd(d2ya, obbZESplatY, V4MulAdd(d2za, obbZESplatZ, zeroes)));
Vec4V obbOd2Prja = V4MulAdd(d2xa, obbOSplatX, V4MulAdd(d2ya, obbOSplatY, V4MulAdd(d2za, obbOSplatZ, zeroes)));
// compare lengths between projected centers with sum of projected radii -- 16i
Vec4V originDiffd2a = v_absm(V4Sub(abbCd2Prja, obbOd2Prja));
Vec4V absABBRd2a = V4Add(V4Add(v_absm(abbExd2Prja), v_absm(abbEyd2Prja)), v_absm(abbEzd2Prja));
Vec4V absOBBRd2a = V4Add(V4Add(v_absm(obbXEd2Prja), v_absm(obbYEd2Prja)), v_absm(obbZEd2Prja));
VecU32V noOverlapd2a = V4IsGrtrV32u(V4Sub(originDiffd2a, eps), V4Add(absABBRd2a, absOBBRd2a));
VecU32V epsNoOverlapd2a = V4IsGrtrV32u(originDiffd2a, eps);
// 8i
noOverlapa = V4U32or(V4U32and(noOverlapd1a, epsNoOverlapd1a), V4U32and(noOverlapd2a, epsNoOverlapd2a));
VecU32V ignore4a = V4IsGrtrV32u(minx4a, maxx4a); // 1 if degenerate box (empty slot)
noOverlapa = V4U32or(noOverlapa, ignore4a);
resa4u = V4U32Andc(U4Load(1), noOverlapa); // 1 & ~noOverlap
V4U32StoreAligned(resa4u, reinterpret_cast<VecU32V*>(resa_));
///// 8+16+12+6+3+16+12+6+3+9+6+9+6+12+6+6=136i from load to result
}
cacheTopValid = false;
for (PxU32 i = 0; i < 4; i++)
{
PxU32 ptr = ptrs[i+offs] & ~1; // clear the isLeaf bit
if (resa_[i])
{
if (tn->isLeaf(i))
{
if (!callback->processResults(1, &ptr))
return;
}
else
{
*(stackPtr++) = ptr;
cacheTop = ptr;
cacheTopValid = true;
}
}
}
} while (stackPtr > stack);
}
} // namespace Gu
}

View File

@@ -0,0 +1,104 @@
// 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_SWEEP_CONVEX_TRI
#define GU_SWEEP_CONVEX_TRI
#include "geometry/PxConvexMeshGeometry.h"
#include "GuVecTriangle.h"
#include "GuVecConvexHull.h"
#include "GuConvexMesh.h"
#include "GuGJKRaycast.h"
// return true if hit, false if no hit
static PX_FORCE_INLINE bool sweepConvexVsTriangle(
const PxVec3& v0, const PxVec3& v1, const PxVec3& v2,
ConvexHullV& convexHull, const aos::PxMatTransformV& meshToConvex, const aos::PxTransformV& convexTransfV,
const aos::Vec3VArg convexSpaceDir, const PxVec3& unitDir, const PxVec3& meshSpaceUnitDir,
const aos::FloatVArg fullDistance, PxReal shrunkDistance,
PxGeomSweepHit& hit, bool isDoubleSided, PxReal inflation, bool& initialOverlap, PxU32 faceIndex)
{
using namespace aos;
if(!isDoubleSided)
{
// Create triangle normal
const PxVec3 denormalizedNormal = (v1 - v0).cross(v2 - v1);
// Backface culling
// PT: WARNING, the test is reversed compared to usual because we pass -unitDir to this function
const bool culled = denormalizedNormal.dot(meshSpaceUnitDir) <= 0.0f;
if(culled)
return false;
}
const Vec3V zeroV = V3Zero();
const FloatV zero = FZero();
const Vec3V p0 = V3LoadU(v0); // in mesh local space
const Vec3V p1 = V3LoadU(v1);
const Vec3V p2 = V3LoadU(v2);
// transform triangle verts from mesh local to convex local space
TriangleV triangleV(meshToConvex.transform(p0), meshToConvex.transform(p1), meshToConvex.transform(p2));
FloatV toi;
Vec3V closestA,normal;
const LocalConvex<TriangleV> convexA(triangleV);
const LocalConvex<ConvexHullV> convexB(convexHull);
const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), convexHull.getCenter());
// run GJK raycast
// sweep triangle in convex local space vs convex, closestA will be the impact point in convex local space
const bool gjkHit = gjkRaycastPenetration<LocalConvex<TriangleV>, LocalConvex<ConvexHullV> >(
convexA, convexB, initialSearchDir, zero, zeroV, convexSpaceDir, toi, normal, closestA, inflation, false);
if(!gjkHit)
return false;
if(FAllGrtrOrEq(zero, toi))
{
initialOverlap = true; // PT: TODO: redundant with hit distance, consider removing
return setInitialOverlapResults(hit, unitDir, faceIndex);
}
const FloatV minDist = FLoad(shrunkDistance);
const FloatV dist = FMul(toi, fullDistance); // scale the toi to original full sweep distance
if(FAllGrtr(minDist, dist)) // is current dist < minDist?
{
hit.faceIndex = faceIndex;
hit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX;
const Vec3V destWorldPointA = convexTransfV.transform(closestA);
const Vec3V destNormal = V3Normalize(convexTransfV.rotate(normal));
V3StoreU(destWorldPointA, hit.position);
V3StoreU(destNormal, hit.normal);
FStore(dist, &hit.distance);
return true; // report a hit
}
return false; // report no hit
}
#endif

View File

@@ -0,0 +1,157 @@
// 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_SWEEP_MESH_H
#define GU_SWEEP_MESH_H
#include "GuMidphaseInterface.h"
#include "GuVecConvexHull.h"
namespace physx
{
namespace Gu
{
// PT: intermediate class containing shared bits of code & members
struct SweepShapeMeshHitCallback : MeshHitCallback<PxGeomRaycastHit>
{
SweepShapeMeshHitCallback(CallbackMode::Enum mode, const PxHitFlags& hitFlags, bool flipNormal, float distCoef);
const PxHitFlags mHitFlags;
bool mStatus; // Default is false, set to true if a valid hit is found. Stays true once true.
bool mInitialOverlap; // Default is false, set to true if an initial overlap hit is found. Reset for each hit.
bool mFlipNormal; // If negative scale is used we need to flip normal
PxReal mDistCoeff; // dist coeff from unscaled to scaled distance
void operator=(const SweepShapeMeshHitCallback&) {}
};
struct SweepCapsuleMeshHitCallback : SweepShapeMeshHitCallback
{
PxGeomSweepHit& mSweepHit;
const PxMat34& mVertexToWorldSkew;
const PxReal mTrueSweepDistance; // max sweep distance that can be used
PxReal mBestAlignmentValue; // best alignment value for triangle normal
PxReal mBestDist; // best distance, not the same as sweepHit.distance, can be shorter by epsilon
const Capsule& mCapsule;
const PxVec3& mUnitDir;
const bool mMeshDoubleSided; // PT: true if PxMeshGeometryFlag::eDOUBLE_SIDED
const bool mIsSphere;
SweepCapsuleMeshHitCallback(PxGeomSweepHit& sweepHit, const PxMat34& worldMatrix, PxReal distance, bool meshDoubleSided,
const Capsule& capsule, const PxVec3& unitDir, const PxHitFlags& hitFlags, bool flipNormal, float distCoef);
virtual PxAgain processHit(const PxGeomRaycastHit& aHit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal& shrunkMaxT, const PxU32*);
// PT: TODO: unify these operators
void operator=(const SweepCapsuleMeshHitCallback&) {}
bool finalizeHit( PxGeomSweepHit& sweepHit, const Capsule& lss, const PxTriangleMeshGeometry& triMeshGeom,
const PxTransform& pose, bool isDoubleSided) const;
};
#if PX_VC
#pragma warning(push)
#pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value.
#endif
struct SweepBoxMeshHitCallback : SweepShapeMeshHitCallback
{
const PxMat34Padded& mMeshToBox;
PxReal mDist, mDist0;
physx::aos::FloatV mDistV;
const Box& mBox;
const PxVec3& mLocalDir;
const PxVec3& mWorldUnitDir;
PxReal mInflation;
PxTriangle mHitTriangle;
physx::aos::Vec3V mMinClosestA;
physx::aos::Vec3V mMinNormal;
physx::aos::Vec3V mLocalMotionV;
PxU32 mMinTriangleIndex;
PxVec3 mOneOverDir;
const bool mBothTriangleSidesCollide; // PT: true if PxMeshGeometryFlag::eDOUBLE_SIDED || PxHitFlag::eMESH_BOTH_SIDES
SweepBoxMeshHitCallback(CallbackMode::Enum mode_, const PxMat34Padded& meshToBox, PxReal distance, bool bothTriangleSidesCollide,
const Box& box, const PxVec3& localMotion, const PxVec3& localDir, const PxVec3& unitDir,
const PxHitFlags& hitFlags, PxReal inflation, bool flipNormal, float distCoef);
virtual ~SweepBoxMeshHitCallback() {}
virtual PxAgain processHit(const PxGeomRaycastHit& meshHit, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxReal& shrinkMaxT, const PxU32*);
bool finalizeHit( PxGeomSweepHit& sweepHit, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose,
const PxTransform& boxTransform, const PxVec3& localDir,
bool meshBothSides, bool isDoubleSided) const;
private:
SweepBoxMeshHitCallback& operator=(const SweepBoxMeshHitCallback&);
};
struct SweepConvexMeshHitCallback : SweepShapeMeshHitCallback
{
PxTriangle mHitTriangle;
ConvexHullV mConvexHull;
physx::aos::PxMatTransformV mMeshToConvex;
physx::aos::PxTransformV mConvexPoseV;
const Cm::FastVertex2ShapeScaling& mMeshScale;
PxGeomSweepHit mSweepHit; // stores either the closest or any hit depending on value of mAnyHit
physx::aos::FloatV mInitialDistance;
physx::aos::Vec3V mConvexSpaceDir; // convexPose.rotateInv(-unit*distance)
PxVec3 mUnitDir;
PxVec3 mMeshSpaceUnitDir;
PxReal mInflation;
const bool mAnyHit;
const bool mBothTriangleSidesCollide; // PT: true if PxMeshGeometryFlag::eDOUBLE_SIDED || PxHitFlag::eMESH_BOTH_SIDES
SweepConvexMeshHitCallback( const ConvexHullData& hull, const PxMeshScale& convexScale, const Cm::FastVertex2ShapeScaling& meshScale,
const PxTransform& convexPose, const PxTransform& meshPose,
const PxVec3& unitDir, PxReal distance, PxHitFlags hitFlags, bool bothTriangleSidesCollide, PxReal inflation,
bool anyHit, float distCoef);
virtual ~SweepConvexMeshHitCallback() {}
virtual PxAgain processHit(const PxGeomRaycastHit& hit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal& shrunkMaxT, const PxU32*);
bool finalizeHit(PxGeomSweepHit& sweepHit, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose,
const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose,
const PxVec3& unitDir, PxReal inflation,
bool isMtd, bool meshBothSides, bool isDoubleSided, bool bothTriangleSidesCollide);
private:
SweepConvexMeshHitCallback& operator=(const SweepConvexMeshHitCallback&);
};
#if PX_VC
#pragma warning(pop)
#endif
}
}
#endif

View File

@@ -0,0 +1,613 @@
// 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 "GuSweepMesh.h"
#include "GuInternal.h"
#include "GuConvexUtilsInternal.h"
#include "CmScaling.h"
#include "GuSweepMTD.h"
#include "GuVecBox.h"
#include "GuVecCapsule.h"
#include "GuSweepBoxTriangle_SAT.h"
#include "GuSweepCapsuleTriangle.h"
#include "GuSweepSphereTriangle.h"
#include "GuDistancePointTriangle.h"
#include "GuCapsule.h"
#include "CmMatrix34.h"
using namespace physx;
using namespace Gu;
using namespace Cm;
using namespace aos;
#include "GuSweepConvexTri.h"
///////////////////////////////////////////////////////////////////////////////
static bool sweepSphereTriangle(const PxTriangle& tri,
const PxVec3& center, PxReal radius,
const PxVec3& unitDir, PxReal distance,
PxGeomSweepHit& hit, PxVec3& triNormalOut,
PxHitFlags hitFlags, bool isDoubleSided)
{
const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES;
if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP))
{
const bool doBackfaceCulling = !isDoubleSided && !meshBothSides;
// PT: test if shapes initially overlap
// PT: add culling here for now, but could be made more efficiently...
// Create triangle normal
PxVec3 denormalizedNormal;
tri.denormalizedNormal(denormalizedNormal);
// Backface culling
if(doBackfaceCulling && (denormalizedNormal.dot(unitDir) > 0.0f))
return false;
float s_unused, t_unused;
const PxVec3 cp = closestPtPointTriangle(center, tri.verts[0], tri.verts[1], tri.verts[2], s_unused, t_unused);
const PxReal dist2 = (cp - center).magnitudeSquared();
if(dist2<=radius*radius)
{
triNormalOut = denormalizedNormal.getNormalized();
return setInitialOverlapResults(hit, unitDir, 0);
}
}
return sweepSphereTriangles(1, &tri,
center, radius,
unitDir, distance,
NULL,
hit, triNormalOut,
isDoubleSided, meshBothSides, false, false);
}
///////////////////////////////////////////////////////////////////////////////
SweepShapeMeshHitCallback::SweepShapeMeshHitCallback(CallbackMode::Enum inMode, const PxHitFlags& hitFlags, bool flipNormal, float distCoef) :
MeshHitCallback<PxGeomRaycastHit> (inMode),
mHitFlags (hitFlags),
mStatus (false),
mInitialOverlap (false),
mFlipNormal (flipNormal),
mDistCoeff (distCoef)
{
}
///////////////////////////////////////////////////////////////////////////////
SweepCapsuleMeshHitCallback::SweepCapsuleMeshHitCallback(
PxGeomSweepHit& sweepHit, const PxMat34& worldMatrix, PxReal distance, bool meshDoubleSided,
const Capsule& capsule, const PxVec3& unitDir, const PxHitFlags& hitFlags, bool flipNormal, float distCoef) :
SweepShapeMeshHitCallback (CallbackMode::eMULTIPLE, hitFlags, flipNormal, distCoef),
mSweepHit (sweepHit),
mVertexToWorldSkew (worldMatrix),
mTrueSweepDistance (distance),
mBestAlignmentValue (2.0f),
mBestDist (distance + GU_EPSILON_SAME_DISTANCE),
mCapsule (capsule),
mUnitDir (unitDir),
mMeshDoubleSided (meshDoubleSided),
mIsSphere (capsule.p0 == capsule.p1)
{
mSweepHit.distance = mTrueSweepDistance;
}
PxAgain SweepCapsuleMeshHitCallback::processHit( // all reported coords are in mesh local space including hit.position
const PxGeomRaycastHit& aHit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal& shrunkMaxT, const PxU32*)
{
const PxTriangle tmpt( mVertexToWorldSkew.transform(v0),
mVertexToWorldSkew.transform(mFlipNormal ? v2 : v1),
mVertexToWorldSkew.transform(mFlipNormal ? v1 : v2));
PxGeomSweepHit localHit; // PT: TODO: ctor!
PxVec3 triNormal;
// pick a farther hit within distEpsilon that is more opposing than the previous closest hit
// make it a relative epsilon to make sure it still works with large distances
const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE * PxMax(1.0f, mSweepHit.distance);
const float minD = mSweepHit.distance + distEpsilon;
if(mIsSphere)
{
if(!::sweepSphereTriangle( tmpt,
mCapsule.p0, mCapsule.radius,
mUnitDir, minD,
localHit, triNormal,
mHitFlags, mMeshDoubleSided))
return true;
}
else
{
// PT: this one is safe because cullbox is NULL (no need to allocate one more triangle)
if(!sweepCapsuleTriangles_Precise( 1, &tmpt,
mCapsule,
mUnitDir, minD,
NULL,
localHit, triNormal,
mHitFlags, mMeshDoubleSided,
NULL))
return true;
}
const PxReal alignmentValue = computeAlignmentValue(triNormal, mUnitDir);
if(keepTriangle(localHit.distance, alignmentValue, mBestDist, mBestAlignmentValue, mTrueSweepDistance))
{
mBestAlignmentValue = alignmentValue;
// AP: need to shrink the sweep distance passed into sweepCapsuleTriangles for correctness so that next sweep is closer
shrunkMaxT = localHit.distance * mDistCoeff; // shrunkMaxT is scaled
mBestDist = PxMin(mBestDist, localHit.distance); // exact lower bound
mSweepHit.flags = localHit.flags;
mSweepHit.distance = localHit.distance;
mSweepHit.normal = localHit.normal;
mSweepHit.position = localHit.position;
mSweepHit.faceIndex = aHit.faceIndex;
mStatus = true;
//ML:this is the initial overlap condition
if(localHit.distance == 0.0f)
{
mInitialOverlap = true;
return false;
}
if(mHitFlags & PxHitFlag::eANY_HIT)
return false; // abort traversal
}
///
else if(keepTriangleBasic(localHit.distance, mBestDist, mTrueSweepDistance))
{
mSweepHit.distance = localHit.distance;
mBestDist = PxMin(mBestDist, localHit.distance); // exact lower bound
}
///
return true;
}
bool SweepCapsuleMeshHitCallback::finalizeHit( PxGeomSweepHit& sweepHit, const Capsule& lss, const PxTriangleMeshGeometry& triMeshGeom,
const PxTransform& pose, bool isDoubleSided) const
{
if(!mStatus)
return false;
if(mInitialOverlap)
{
// PT: TODO: consider using 'setInitialOverlapResults' here
bool hasContacts = false;
if(mHitFlags & PxHitFlag::eMTD)
{
const Vec3V p0 = V3LoadU(mCapsule.p0);
const Vec3V p1 = V3LoadU(mCapsule.p1);
const FloatV radius = FLoad(lss.radius);
CapsuleV capsuleV;
capsuleV.initialize(p0, p1, radius);
//we need to calculate the MTD
hasContacts = computeCapsule_TriangleMeshMTD(triMeshGeom, pose, capsuleV, mCapsule.radius, isDoubleSided, sweepHit);
}
setupSweepHitForMTD(sweepHit, hasContacts, mUnitDir);
}
else
{
mSweepHit.distance = mBestDist;
sweepHit.flags = PxHitFlag::eNORMAL | PxHitFlag::ePOSITION | PxHitFlag::eFACE_INDEX;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
bool sweepCapsule_MeshGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS)
{
PX_UNUSED(threadContext);
PX_UNUSED(capsuleGeom_);
PX_UNUSED(capsulePose_);
PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH);
const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom);
TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh);
return Midphase::sweepCapsuleVsMesh(meshData, meshGeom, pose, lss, unitDir, distance, sweepHit, hitFlags, inflation);
}
///////////////////////////////////////////////////////////////////////////////
// same as 'mat.transform(p)' but using SIMD
static PX_FORCE_INLINE Vec4V transformV(const Vec4V p, const PxMat34Padded& mat)
{
Vec4V ResV = V4Scale(V4LoadU(&mat.m.column0.x), V4GetX(p));
ResV = V4ScaleAdd(V4LoadU(&mat.m.column1.x), V4GetY(p), ResV);
ResV = V4ScaleAdd(V4LoadU(&mat.m.column2.x), V4GetZ(p), ResV);
ResV = V4Add(ResV, V4LoadU(&mat.p.x)); // PT: this load is safe thanks to padding
return ResV;
}
///////////////////////////////////////////////////////////////////////////////
SweepBoxMeshHitCallback::SweepBoxMeshHitCallback( CallbackMode::Enum mode_, const PxMat34Padded& meshToBox, PxReal distance, bool bothTriangleSidesCollide,
const Box& box, const PxVec3& localMotion, const PxVec3& localDir, const PxVec3& unitDir,
const PxHitFlags& hitFlags, PxReal inflation, bool flipNormal, float distCoef) :
SweepShapeMeshHitCallback (mode_, hitFlags, flipNormal,distCoef),
mMeshToBox (meshToBox),
mDist (distance),
mBox (box),
mLocalDir (localDir),
mWorldUnitDir (unitDir),
mInflation (inflation),
mBothTriangleSidesCollide (bothTriangleSidesCollide)
{
mLocalMotionV = V3LoadU(localMotion);
mDistV = FLoad(distance);
mDist0 = distance;
mOneOverDir = PxVec3(
mLocalDir.x!=0.0f ? 1.0f/mLocalDir.x : 0.0f,
mLocalDir.y!=0.0f ? 1.0f/mLocalDir.y : 0.0f,
mLocalDir.z!=0.0f ? 1.0f/mLocalDir.z : 0.0f);
}
PxAgain SweepBoxMeshHitCallback::processHit( // all reported coords are in mesh local space including hit.position
const PxGeomRaycastHit& meshHit, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxReal& shrinkMaxT, const PxU32*)
{
if(mHitFlags & PxHitFlag::ePRECISE_SWEEP)
{
const PxTriangle currentTriangle(
mMeshToBox.transform(lp0),
mMeshToBox.transform(mFlipNormal ? lp2 : lp1),
mMeshToBox.transform(mFlipNormal ? lp1 : lp2));
PxF32 t = PX_MAX_REAL; // PT: could be better!
if(!triBoxSweepTestBoxSpace(currentTriangle, mBox.extents, mLocalDir, mOneOverDir, mDist, t, !mBothTriangleSidesCollide))
return true;
if(t <= mDist)
{
// PT: test if shapes initially overlap
mDist = t;
shrinkMaxT = t * mDistCoeff; // shrunkMaxT is scaled
mMinClosestA = V3LoadU(currentTriangle.verts[0]); // PT: this is arbitrary
mMinNormal = V3LoadU(-mWorldUnitDir);
mStatus = true;
mMinTriangleIndex = meshHit.faceIndex;
mHitTriangle = currentTriangle;
if(t == 0.0f)
{
mInitialOverlap = true;
return false; // abort traversal
}
}
}
else
{
const FloatV zero = FZero();
// PT: SIMD code similar to:
// const Vec3V triV0 = V3LoadU(mMeshToBox.transform(lp0));
// const Vec3V triV1 = V3LoadU(mMeshToBox.transform(lp1));
// const Vec3V triV2 = V3LoadU(mMeshToBox.transform(lp2));
//
// SIMD version works but we need to ensure all loads are safe.
// For incoming vertices they should either come from the vertex array or from a binary deserialized file.
// For the vertex array we can just allocate one more vertex. For the binary file it should be ok as soon
// as vertices aren't the last thing serialized in the file.
// For the matrix only the last column is a problem, and we can easily solve that with some padding in the local class.
const Vec3V triV0 = Vec3V_From_Vec4V(transformV(V4LoadU(&lp0.x), mMeshToBox));
const Vec3V triV1 = Vec3V_From_Vec4V(transformV(V4LoadU(mFlipNormal ? &lp2.x : &lp1.x), mMeshToBox));
const Vec3V triV2 = Vec3V_From_Vec4V(transformV(V4LoadU(mFlipNormal ? &lp1.x : &lp2.x), mMeshToBox));
if(!mBothTriangleSidesCollide)
{
const Vec3V triNormal = V3Cross(V3Sub(triV2, triV1),V3Sub(triV0, triV1));
if(FAllGrtrOrEq(V3Dot(triNormal, mLocalMotionV), zero))
return true;
}
const Vec3V zeroV = V3Zero();
const Vec3V boxExtents = V3LoadU(mBox.extents);
const BoxV boxV(zeroV, boxExtents);
const TriangleV triangleV(triV0, triV1, triV2);
FloatV lambda;
Vec3V closestA, normal;//closestA and normal is in the local space of convex hull
const LocalConvex<TriangleV> convexA(triangleV);
const LocalConvex<BoxV> convexB(boxV);
const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), boxV.getCenter());
if(!gjkRaycastPenetration<LocalConvex<TriangleV>, LocalConvex<BoxV> >(convexA, convexB, initialSearchDir, zero, zeroV, mLocalMotionV, lambda, normal, closestA, mInflation, false))
return true;
mStatus = true;
mMinClosestA = closestA;
mMinTriangleIndex = meshHit.faceIndex;
if(FAllGrtrOrEq(zero, lambda)) // lambda < 0? => initial overlap
{
mInitialOverlap = true;
shrinkMaxT = 0.0f;
mDistV = zero;
mDist = 0.0f;
mMinNormal = V3LoadU(-mWorldUnitDir);
return false;
}
PxF32 f;
FStore(lambda, &f);
mDist = f*mDist; // shrink dist
mLocalMotionV = V3Scale(mLocalMotionV, lambda); // shrink localMotion
mDistV = FMul(mDistV, lambda); // shrink distV
mMinNormal = normal;
if(mDist * mDistCoeff < shrinkMaxT) // shrink shrinkMaxT
shrinkMaxT = mDist * mDistCoeff; // shrunkMaxT is scaled
//mHitTriangle = currentTriangle;
V3StoreU(triV0, mHitTriangle.verts[0]);
V3StoreU(triV1, mHitTriangle.verts[1]);
V3StoreU(triV2, mHitTriangle.verts[2]);
}
return true;
}
bool SweepBoxMeshHitCallback::finalizeHit( PxGeomSweepHit& sweepHit, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose,
const PxTransform& boxTransform, const PxVec3& localDir,
bool meshBothSides, bool isDoubleSided) const
{
if(!mStatus)
return false;
Vec3V minClosestA = mMinClosestA;
Vec3V minNormal = mMinNormal;
sweepHit.faceIndex = mMinTriangleIndex;
if(mInitialOverlap)
{
bool hasContacts = false;
if(mHitFlags & PxHitFlag::eMTD)
hasContacts = computeBox_TriangleMeshMTD(triMeshGeom, pose, mBox, boxTransform, mInflation, mBothTriangleSidesCollide, sweepHit);
setupSweepHitForMTD(sweepHit, hasContacts, mWorldUnitDir);
}
else
{
sweepHit.distance = mDist;
sweepHit.flags = PxHitFlag::eFACE_INDEX;
// PT: we need the "best triangle" normal in order to call 'shouldFlipNormal'. We stored the best
// triangle in both GJK & precise codepaths (in box space). We use a dedicated 'shouldFlipNormal'
// function that delays computing the triangle normal.
// TODO: would still be more efficient to store the best normal directly, it's already computed at least
// in the GJK codepath.
const Vec3V p0 = V3LoadU(&boxTransform.p.x);
const QuatV q0 = QuatVLoadU(&boxTransform.q.x);
const PxTransformV boxPos(p0, q0);
if(mHitFlags & PxHitFlag::ePRECISE_SWEEP)
{
computeBoxLocalImpact(sweepHit.position, sweepHit.normal, sweepHit.flags, mBox, localDir, mHitTriangle, mHitFlags, isDoubleSided, meshBothSides, mDist);
}
else
{
sweepHit.flags |= PxHitFlag::eNORMAL|PxHitFlag::ePOSITION;
// PT: now for the GJK path, we must first always negate the returned normal. Similar to what happens in the precise path,
// we can't delay this anymore: our normal must be properly oriented in order to call 'shouldFlipNormal'.
minNormal = V3Neg(minNormal);
// PT: this one is to ensure the normal respects the mesh-both-sides/double-sided convention
PxVec3 tmp;
V3StoreU(minNormal, tmp);
if(shouldFlipNormal(tmp, meshBothSides, isDoubleSided, mHitTriangle, localDir, NULL))
minNormal = V3Neg(minNormal);
// PT: finally, this moves everything back to world space
V3StoreU(boxPos.rotate(minNormal), sweepHit.normal);
V3StoreU(boxPos.transform(minClosestA), sweepHit.position);
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
bool sweepBox_MeshGeom(GU_BOX_SWEEP_FUNC_PARAMS)
{
PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH);
PX_UNUSED(threadContext);
PX_UNUSED(boxPose_);
PX_UNUSED(boxGeom_);
const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom);
TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh);
return Midphase::sweepBoxVsMesh(meshData, meshGeom, pose, box, unitDir, distance, sweepHit, hitFlags, inflation);
}
///////////////////////////////////////////////////////////////////////////////
SweepConvexMeshHitCallback::SweepConvexMeshHitCallback( const ConvexHullData& hull, const PxMeshScale& convexScale, const FastVertex2ShapeScaling& meshScale,
const PxTransform& convexPose, const PxTransform& meshPose,
const PxVec3& unitDir, PxReal distance, PxHitFlags hitFlags, bool bothTriangleSidesCollide, PxReal inflation,
bool anyHit, float distCoef) :
SweepShapeMeshHitCallback (CallbackMode::eMULTIPLE, hitFlags, meshScale.flipsNormal(), distCoef),
mMeshScale (meshScale),
mUnitDir (unitDir),
mInflation (inflation),
mAnyHit (anyHit),
mBothTriangleSidesCollide (bothTriangleSidesCollide)
{
mSweepHit.distance = distance; // this will be shrinking progressively as we sweep and clip the sweep length
mSweepHit.faceIndex = 0xFFFFFFFF;
mMeshSpaceUnitDir = meshPose.rotateInv(unitDir);
const Vec3V worldDir = V3LoadU(unitDir);
const FloatV dist = FLoad(distance);
const QuatV q0 = QuatVLoadU(&meshPose.q.x);
const Vec3V p0 = V3LoadU(&meshPose.p.x);
const QuatV q1 = QuatVLoadU(&convexPose.q.x);
const Vec3V p1 = V3LoadU(&convexPose.p.x);
const PxTransformV meshPoseV(p0, q0);
const PxTransformV convexPoseV(p1, q1);
mMeshToConvex = convexPoseV.transformInv(meshPoseV);
mConvexPoseV = convexPoseV;
mConvexSpaceDir = convexPoseV.rotateInv(V3Neg(V3Scale(worldDir, dist)));
mInitialDistance = dist;
const Vec3V vScale = V3LoadU_SafeReadW(convexScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale
const QuatV vQuat = QuatVLoadU(&convexScale.rotation.x);
mConvexHull.initialize(&hull, V3Zero(), vScale, vQuat, convexScale.isIdentity());
}
PxAgain SweepConvexMeshHitCallback::processHit( // all reported coords are in mesh local space including hit.position
const PxGeomRaycastHit& hit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal& shrunkMaxT, const PxU32*)
{
const PxVec3 v0 = mMeshScale * av0;
const PxVec3 v1 = mMeshScale * (mFlipNormal ? av2 : av1);
const PxVec3 v2 = mMeshScale * (mFlipNormal ? av1 : av2);
// mSweepHit will be updated if sweep distance is < input mSweepHit.distance
const PxReal oldDist = mSweepHit.distance;
if(sweepConvexVsTriangle(
v0, v1, v2, mConvexHull, mMeshToConvex, mConvexPoseV, mConvexSpaceDir,
mUnitDir, mMeshSpaceUnitDir, mInitialDistance, oldDist, mSweepHit, mBothTriangleSidesCollide,
mInflation, mInitialOverlap, hit.faceIndex))
{
mStatus = true;
shrunkMaxT = mSweepHit.distance * mDistCoeff; // shrunkMaxT is scaled
// PT: added for 'shouldFlipNormal'
mHitTriangle.verts[0] = v0;
mHitTriangle.verts[1] = v1;
mHitTriangle.verts[2] = v2;
if(mAnyHit)
return false; // abort traversal
if(mSweepHit.distance == 0.0f)
return false;
}
return true; // continue traversal
}
bool SweepConvexMeshHitCallback::finalizeHit( PxGeomSweepHit& sweepHit, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose,
const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose,
const PxVec3& unitDir, PxReal inflation,
bool isMtd, bool meshBothSides, bool isDoubleSided, bool bothTriangleSidesCollide)
{
if(!mStatus)
return false;
if(mInitialOverlap)
{
bool hasContacts = false;
if(isMtd)
hasContacts = computeConvex_TriangleMeshMTD(meshGeom, pose, convexGeom, convexPose, inflation, bothTriangleSidesCollide, sweepHit);
setupSweepHitForMTD(sweepHit, hasContacts, unitDir);
sweepHit.faceIndex = mSweepHit.faceIndex;
}
else
{
sweepHit = mSweepHit;
//sweepHit.position += unitDir * sweepHit.distance;
sweepHit.normal = -sweepHit.normal;
sweepHit.normal.normalize();
// PT: this one is to ensure the normal respects the mesh-both-sides/double-sided convention
// PT: beware, the best triangle is in mesh-space, but the impact data is in world-space already
if(shouldFlipNormal(sweepHit.normal, meshBothSides, isDoubleSided, mHitTriangle, unitDir, &pose))
sweepHit.normal = -sweepHit.normal;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
bool sweepConvex_MeshGeom(GU_CONVEX_SWEEP_FUNC_PARAMS)
{
PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH);
PX_UNUSED(threadContext);
const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom);
ConvexMesh* convexMesh = static_cast<ConvexMesh*>(convexGeom.convexMesh);
TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh);
const bool idtScaleConvex = convexGeom.scale.isIdentity();
const bool idtScaleMesh = meshGeom.scale.isIdentity();
FastVertex2ShapeScaling convexScaling;
if(!idtScaleConvex)
convexScaling.init(convexGeom.scale);
FastVertex2ShapeScaling meshScaling;
if(!idtScaleMesh)
meshScaling.init(meshGeom.scale);
PX_ASSERT(!convexMesh->getLocalBoundsFast().isEmpty());
const PxBounds3 hullAABB = convexMesh->getLocalBoundsFast().transformFast(convexScaling.getVertex2ShapeSkew());
Box hullOBB;
computeHullOBB(hullOBB, hullAABB, 0.0f, Matrix34FromTransform(convexPose), Matrix34FromTransform(pose), meshScaling, idtScaleMesh);
hullOBB.extents.x += inflation;
hullOBB.extents.y += inflation;
hullOBB.extents.z += inflation;
const PxVec3 localDir = pose.rotateInv(unitDir);
// inverse transform the sweep direction and distance to mesh space
PxVec3 meshSpaceSweepVector = meshScaling.getShape2VertexSkew().transform(localDir*distance);
const PxReal meshSpaceSweepDist = meshSpaceSweepVector.normalize();
PxReal distCoeff = 1.0f;
if (!idtScaleMesh)
distCoeff = meshSpaceSweepDist / distance;
const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES;
const bool isDoubleSided = meshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED;
const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides;
const bool anyHit = hitFlags & PxHitFlag::eANY_HIT;
SweepConvexMeshHitCallback callback(
convexMesh->getHull(), convexGeom.scale, meshScaling, convexPose, pose, -unitDir, distance, hitFlags,
bothTriangleSidesCollide, inflation, anyHit, distCoeff);
Midphase::sweepConvexVsMesh(meshData, hullOBB, meshSpaceSweepVector, meshSpaceSweepDist, callback, anyHit);
const bool isMtd = hitFlags & PxHitFlag::eMTD;
return callback.finalizeHit(sweepHit, meshGeom, pose, convexGeom, convexPose, unitDir, inflation, isMtd, meshBothSides, isDoubleSided, bothTriangleSidesCollide);
}
///////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,106 @@
// 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_TETRAHEDRON_H
#define GU_TETRAHEDRON_H
#include "foundation/PxVec3.h"
#include "foundation/PxUtilities.h"
#include "GuTriangle.h"
namespace physx
{
namespace Gu
{
/**
\brief Structure used to store indices for a triangles points. T is either PxU32 or PxU16
*/
template <class T>
struct TetrahedronT
{
PX_INLINE TetrahedronT() {}
PX_INLINE TetrahedronT(T a, T b, T c, T d) { v[0] = a; v[1] = b; v[2] = c; v[3] = d; }
template <class TX>
PX_INLINE TetrahedronT(const TetrahedronT<TX>& other) { v[0] = other[0]; v[1] = other[1]; v[2] = other[2]; }
PX_INLINE T& operator[](T i) { return v[i]; }
template<class TX>//any type of TriangleT<>, possibly with different T
PX_INLINE TetrahedronT<T>& operator=(const TetrahedronT<TX>& i) { v[0] = i[0]; v[1] = i[1]; v[2] = i[2]; v[3] = i[3]; return *this; }
PX_INLINE const T& operator[](T i) const { return v[i]; }
PX_INLINE PxI32 indexOf(T i) const
{
if (v[0] == i) return 0;
if (v[1] == i) return 1;
if (v[2] == i) return 2;
if (v[3] == i) return 3;
return -1;
}
PX_INLINE bool contains(T id) const
{
return v[0] == id || v[1] == id || v[2] == id || v[3] == id;
}
PX_INLINE void replace(T oldId, T newId)
{
if (v[0] == oldId) v[0] = newId;
if (v[1] == oldId) v[1] = newId;
if (v[2] == oldId) v[2] = newId;
if (v[3] == oldId) v[3] = newId;
}
PX_INLINE void sort()
{
if (v[0] > v[1]) PxSwap(v[0], v[1]);
if (v[2] > v[3]) PxSwap(v[2], v[3]);
if (v[0] > v[2]) PxSwap(v[0], v[2]);
if (v[1] > v[3]) PxSwap(v[1], v[3]);
if (v[1] > v[2]) PxSwap(v[1], v[2]);
}
PX_INLINE bool containsFace(const Gu::IndexedTriangleT<T>& triangle) const
{
return contains(triangle[0]) && contains(triangle[1]) && contains(triangle[2]);
}
PX_INLINE static bool identical(Gu::TetrahedronT<T> x, Gu::TetrahedronT<T> y)
{
x.sort();
y.sort();
return x.v[0] == y.v[0] && x.v[1] == y.v[1] && x.v[2] == y.v[2] && x.v[3] == y.v[3];
}
T v[4]; //vertex indices
};
}
}
#endif

View File

@@ -0,0 +1,324 @@
// 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 "GuMidphaseInterface.h"
#include "GuTetrahedronMesh.h"
#include "GuBox.h"
using namespace physx;
using namespace Gu;
void TetrahedronMesh::onRefCountZero()
{
if (mMeshFactory)
{
::onRefCountZero(this, mMeshFactory, false, "PxTetrahedronMesh::release: double deletion detected!");
}
}
void DeformableVolumeMesh::onRefCountZero()
{
if (mMeshFactory)
{
::onRefCountZero(this, mMeshFactory, false, "PxDeformableVolumeMesh::release: double deletion detected!");
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
DeformableVolumeAuxData::DeformableVolumeAuxData(DeformableVolumeSimulationData& d, DeformableVolumeCollisionData& c, CollisionMeshMappingData& e)
: PxDeformableVolumeAuxData(PxType(PxConcreteType::eDEFORMABLE_VOLUME_STATE), PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE)
, mGridModelInvMass(d.mGridModelInvMass)
, mGridModelTetraRestPoses(d.mGridModelTetraRestPoses)
, mGridModelOrderedTetrahedrons(d.mGridModelOrderedTetrahedrons)
, mGMNbPartitions(d.mGridModelNbPartitions)
, mGMMaxMaxTetsPerPartitions(d.mGridModelMaxTetsPerPartitions)
, mGMRemapOutputSize(d.mGMRemapOutputSize)
, mGMRemapOutputCP(d.mGMRemapOutputCP)
, mGMAccumulatedPartitionsCP(d.mGMAccumulatedPartitionsCP)
, mGMAccumulatedCopiesCP(d.mGMAccumulatedCopiesCP)
, mCollisionAccumulatedTetrahedronsRef(e.mCollisionAccumulatedTetrahedronsRef)
, mCollisionTetrahedronsReferences(e.mCollisionTetrahedronsReferences)
, mCollisionNbTetrahedronsReferences(e.mCollisionNbTetrahedronsReferences)
, mCollisionSurfaceVertsHint(e.mCollisionSurfaceVertsHint)
, mCollisionSurfaceVertToTetRemap(e.mCollisionSurfaceVertToTetRemap)
, mVertsBarycentricInGridModel(e.mVertsBarycentricInGridModel)
, mVertsRemapInGridModel(e.mVertsRemapInGridModel)
, mTetsRemapColToSim(e.mTetsRemapColToSim)
, mTetsRemapSize(e.mTetsRemapSize)
, mTetsAccumulatedRemapColToSim(e.mTetsAccumulatedRemapColToSim)
, mGMPullIndices(d.mGMPullIndices)
, mTetraRestPoses(c.mTetraRestPoses)
, mNumTetsPerElement(d.mNumTetsPerElement)
{
// this constructor takes ownership of memory from the data object
d.mGridModelInvMass = 0;
d.mGridModelTetraRestPoses = 0;
d.mGridModelOrderedTetrahedrons = 0;
d.mGMRemapOutputCP = 0;
d.mGMAccumulatedPartitionsCP = 0;
d.mGMAccumulatedCopiesCP = 0;
e.mCollisionAccumulatedTetrahedronsRef = 0;
e.mCollisionTetrahedronsReferences = 0;
e.mCollisionSurfaceVertsHint = 0;
e.mCollisionSurfaceVertToTetRemap = 0;
d.mGMPullIndices = 0;
e.mVertsBarycentricInGridModel = 0;
e.mVertsRemapInGridModel = 0;
e.mTetsRemapColToSim = 0;
e.mTetsAccumulatedRemapColToSim = 0;
c.mTetraRestPoses = 0;
}
DeformableVolumeAuxData::~DeformableVolumeAuxData()
{
PX_FREE(mGridModelInvMass);
PX_FREE(mGridModelTetraRestPoses);
PX_FREE(mGridModelOrderedTetrahedrons);
PX_FREE(mGMRemapOutputCP);
PX_FREE(mGMAccumulatedPartitionsCP);
PX_FREE(mGMAccumulatedCopiesCP);
PX_FREE(mCollisionAccumulatedTetrahedronsRef);
PX_FREE(mCollisionTetrahedronsReferences);
PX_FREE(mCollisionSurfaceVertsHint);
PX_FREE(mCollisionSurfaceVertToTetRemap);
PX_FREE(mVertsBarycentricInGridModel);
PX_FREE(mVertsRemapInGridModel);
PX_FREE(mTetsRemapColToSim);
PX_FREE(mTetsAccumulatedRemapColToSim);
PX_FREE(mGMPullIndices);
PX_FREE(mTetraRestPoses);
}
TetrahedronMesh::TetrahedronMesh(PxU32 nbVertices, PxVec3* vertices, PxU32 nbTetrahedrons, void* tetrahedrons, PxU8 flags, PxBounds3 aabb, PxReal geomEpsilon)
: PxTetrahedronMesh(PxType(PxConcreteType::eTETRAHEDRON_MESH), PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE)
, mNbVertices(nbVertices)
, mVertices(vertices)
, mNbTetrahedrons(nbTetrahedrons)
, mTetrahedrons(tetrahedrons)
, mFlags(flags)
, mMaterialIndices(NULL)
, mAABB(aabb)
, mGeomEpsilon(geomEpsilon)
{
}
TetrahedronMesh::TetrahedronMesh(TetrahedronMeshData& mesh)
: PxTetrahedronMesh(PxType(PxConcreteType::eTETRAHEDRON_MESH), PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE)
, mNbVertices(mesh.mNbVertices)
, mVertices(mesh.mVertices)
, mNbTetrahedrons(mesh.mNbTetrahedrons)
, mTetrahedrons(mesh.mTetrahedrons)
, mFlags(mesh.mFlags)
, mMaterialIndices(mesh.mMaterialIndices)
, mAABB(mesh.mAABB)
, mGeomEpsilon(mesh.mGeomEpsilon)
{
// this constructor takes ownership of memory from the data object
mesh.mVertices = 0;
mesh.mTetrahedrons = 0;
mesh.mMaterialIndices = 0;
}
TetrahedronMesh::TetrahedronMesh(MeshFactory* meshFactory, TetrahedronMeshData& mesh)
: PxTetrahedronMesh(PxType(PxConcreteType::eTETRAHEDRON_MESH), PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE)
, mNbVertices(mesh.mNbVertices)
, mVertices(mesh.mVertices)
, mNbTetrahedrons(mesh.mNbTetrahedrons)
, mTetrahedrons(mesh.mTetrahedrons)
, mFlags(mesh.mFlags)
, mMaterialIndices(mesh.mMaterialIndices)
, mAABB(mesh.mAABB)
, mGeomEpsilon(mesh.mGeomEpsilon)
, mMeshFactory(meshFactory)
{
// this constructor takes ownership of memory from the data object
mesh.mVertices = 0;
mesh.mTetrahedrons = 0;
mesh.mMaterialIndices = 0;
}
TetrahedronMesh::~TetrahedronMesh()
{
PX_FREE(mTetrahedrons);
PX_FREE(mVertices);
PX_FREE(mMaterialIndices);
}
BVTetrahedronMesh::BVTetrahedronMesh(TetrahedronMeshData& mesh, DeformableVolumeCollisionData& d, MeshFactory* factory) : TetrahedronMesh(mesh)
, mFaceRemap(d.mFaceRemap)
, mGRB_tetraIndices(d.mGRB_primIndices)
, mGRB_tetraSurfaceHint(d.mGRB_tetraSurfaceHint)
, mGRB_faceRemap(d.mGRB_faceRemap)
, mGRB_faceRemapInverse(d.mGRB_faceRemapInverse)
, mGRB_BV32Tree(d.mGRB_BV32Tree)
{
mMeshFactory = factory;
bool has16BitIndices = (mesh.mFlags & PxMeshFlag::e16_BIT_INDICES) ? true : false;
mMeshInterface4.mVerts = mVertices; // mesh.mVertices;
mMeshInterface4.mNbVerts = mesh.mNbVertices;
mMeshInterface4.mNbTetrahedrons = mesh.mNbTetrahedrons;
mMeshInterface4.mTetrahedrons16 = has16BitIndices ? reinterpret_cast<IndTetrahedron16*>(mTetrahedrons/*mesh.mTetrahedrons*/) : NULL;
mMeshInterface4.mTetrahedrons32 = has16BitIndices ? NULL : reinterpret_cast<IndTetrahedron32*>(mTetrahedrons/*mesh.mTetrahedrons*/);
mBV4Tree = d.mBV4Tree;
mBV4Tree.mMeshInterface = &mMeshInterface4;
if (mGRB_BV32Tree)
{
mMeshInterface32.mVerts = mVertices; // mesh.mVertices;
mMeshInterface32.mNbVerts = mesh.mNbVertices;
mMeshInterface32.mNbTetrahedrons = mesh.mNbTetrahedrons;
mMeshInterface32.mTetrahedrons16 = has16BitIndices ? reinterpret_cast<IndTetrahedron16*>(d.mGRB_primIndices) : NULL;
mMeshInterface32.mTetrahedrons32 = has16BitIndices ? NULL : reinterpret_cast<IndTetrahedron32*>(d.mGRB_primIndices);
mGRB_BV32Tree->mMeshInterface = &mMeshInterface32;
}
// this constructor takes ownership of memory from the data object
d.mGRB_tetraSurfaceHint = NULL;
d.mFaceRemap = NULL;
d.mGRB_primIndices = NULL;
d.mGRB_faceRemap = NULL;
d.mGRB_faceRemapInverse = NULL;
d.mGRB_BV32Tree = NULL;
mesh.mVertices = NULL;
mesh.mTetrahedrons = NULL;
}
DeformableVolumeMesh::DeformableVolumeMesh(MeshFactory* factory, DeformableVolumeMeshData& d)
: PxDeformableVolumeMesh(PxType(PxConcreteType::eDEFORMABLE_VOLUME_MESH), PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE)
, mMeshFactory(factory)
{
mDeformableVolumeAuxData = PX_NEW(DeformableVolumeAuxData)(d.mSimulationData, d.mCollisionData, d.mMappingData);
mCollisionMesh = PX_NEW(BVTetrahedronMesh)(d.mCollisionMesh, d.mCollisionData, factory);
mSimulationMesh = PX_NEW(TetrahedronMesh)(factory, d.mSimulationMesh);
}
DeformableVolumeMesh::~DeformableVolumeMesh()
{
if (getBaseFlags() & PxBaseFlag::eOWNS_MEMORY)
{
PX_DELETE(mDeformableVolumeAuxData);
PX_DELETE(mCollisionMesh);
PX_DELETE(mSimulationMesh);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PT: used to be automatic but making it manual saves bytes in the internal mesh
void DeformableVolumeMesh::exportExtraData(PxSerializationContext& stream)
{
//PX_DEFINE_DYNAMIC_ARRAY(TriangleMesh, mVertices, PxField::eVEC3, mNbVertices, Ps::PxFieldFlag::eSERIALIZE),
if (getCollisionMeshFast()->mVertices)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(getCollisionMeshFast()->mVertices, getCollisionMeshFast()->mNbVertices * sizeof(PxVec3));
}
/*if (mSurfaceTriangles)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(mSurfaceTriangles, mNbTriangles * 3 * sizeof(PxU32));
}*/
if (getCollisionMeshFast()->mTetrahedrons)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(getCollisionMeshFast()->mTetrahedrons, getCollisionMeshFast()->mNbTetrahedrons * 4 * sizeof(PxU32));
}
/*if (mTetSurfaceHint)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(mTetSurfaceHint, mNbTetrahedrons * sizeof(PxU8));
}*/
if (getCollisionMeshFast()->mMaterialIndices)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(getCollisionMeshFast()->mMaterialIndices, getCollisionMeshFast()->mNbTetrahedrons * sizeof(PxU16));
}
if (getCollisionMeshFast()->mFaceRemap)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(getCollisionMeshFast()->mFaceRemap, getCollisionMeshFast()->mNbTetrahedrons * sizeof(PxU32));
}
}
void DeformableVolumeMesh::importExtraData(PxDeserializationContext& context)
{
// PT: vertices are followed by indices, so it will be safe to V4Load vertices from a deserialized binary file
if (getCollisionMeshFast()->mVertices)
getCollisionMeshFast()->mVertices = context.readExtraData<PxVec3, PX_SERIAL_ALIGN>(getCollisionMeshFast()->mNbVertices);
if (getCollisionMeshFast()->mTetrahedrons)
getCollisionMeshFast()->mTetrahedrons = context.readExtraData<PxU32, PX_SERIAL_ALIGN>(4 * getCollisionMeshFast()->mNbTetrahedrons);
if (getCollisionMeshFast()->mFaceRemap)
getCollisionMeshFast()->mFaceRemap = context.readExtraData<PxU32, PX_SERIAL_ALIGN>(getCollisionMeshFast()->mNbTetrahedrons);
getCollisionMeshFast()->mGRB_tetraIndices = NULL;
getCollisionMeshFast()->mGRB_tetraSurfaceHint = NULL;
getCollisionMeshFast()->mGRB_faceRemap = NULL;
getCollisionMeshFast()->mGRB_faceRemapInverse = NULL;
getCollisionMeshFast()->mGRB_BV32Tree = NULL;
}
void DeformableVolumeMesh::release()
{
Cm::RefCountable_decRefCount(*this);
}
//PxVec3* TetrahedronMesh::getVerticesForModification()
//{
// PxGetFoundation().error(PxErrorCode::eINVALID_OPERATION, PX_FL, "PxDeformableVolumeMesh::getVerticesForModification() is not currently supported.");
//
// return NULL;
//}
//PxBounds3 BVTetrahedronMesh::refitBVH()
//{
// PxGetFoundation().error(PxErrorCode::eINVALID_OPERATION, PX_FL, "PxDeformableVolumeMesh::refitBVH() is not currently supported.");
//
// return PxBounds3(mAABB.getMin(), mAABB.getMax());
//}

View File

@@ -0,0 +1,293 @@
// 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_TETRAHEDRONMESH_H
#define GU_TETRAHEDRONMESH_H
#include "foundation/PxIO.h"
#include "geometry/PxTetrahedronMeshGeometry.h"
#include "geometry/PxTetrahedronMesh.h"
#include "geometry/PxTetrahedron.h"
#include "geometry/PxSimpleTriangleMesh.h"
#include "CmRefCountable.h"
#include "common/PxRenderOutput.h"
#include "GuMeshData.h"
#include "GuCenterExtents.h"
#include "GuMeshFactory.h"
namespace physx
{
namespace Gu
{
class MeshFactory;
#if PX_VC
#pragma warning(push)
#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value.
#endif
class DeformableVolumeAuxData : public PxDeformableVolumeAuxData, public PxUserAllocated
{
public:
DeformableVolumeAuxData(DeformableVolumeSimulationData& d, DeformableVolumeCollisionData& c, CollisionMeshMappingData& e);
virtual ~DeformableVolumeAuxData();
virtual const char* getConcreteTypeName() const { return "PxDeformableVolumeAuxData"; }
virtual void acquireReference() { Cm::RefCountable_incRefCount(*this); }
virtual PxU32 getReferenceCount() const { return Cm::RefCountable_getRefCount(*this); }
virtual void release() { Cm::RefCountable_decRefCount(*this); }
virtual void onRefCountZero() { PX_DELETE_THIS; }
virtual PxReal* getGridModelInvMass() { return mGridModelInvMass; }
PX_FORCE_INLINE PxU32 getNbTetRemapSizeFast() const { return mTetsRemapSize; }
PX_FORCE_INLINE PxReal* getGridModelInvMassFast() { return mGridModelInvMass; }
PX_FORCE_INLINE PxU32 getNbGMPartitionFast() const { return mGMNbPartitions; }
PX_FORCE_INLINE PxU32 getGMRemapOutputSizeFast() const { return mGMRemapOutputSize; }
PX_FORCE_INLINE PxU32 getGMMaxTetsPerPartitionsFast() const { return mGMMaxMaxTetsPerPartitions; }
PX_FORCE_INLINE PxU32* getCollisionAccumulatedTetrahedronRefs() const { return mCollisionAccumulatedTetrahedronsRef; }
PX_FORCE_INLINE PxU32* getCollisionTetrahedronRefs() const { return mCollisionTetrahedronsReferences; }
PX_FORCE_INLINE PxU32 getCollisionNbTetrahedronRefs() const { return mCollisionNbTetrahedronsReferences; }
PX_FORCE_INLINE PxU32* getCollisionSurfaceVertToTetRemap() const { return mCollisionSurfaceVertToTetRemap; }
PX_FORCE_INLINE PxMat33* getGridModelRestPosesFast() { return mGridModelTetraRestPoses; }
PX_FORCE_INLINE PxMat33* getRestPosesFast() { return mTetraRestPoses; }
float* mGridModelInvMass;
PxMat33* mGridModelTetraRestPoses;
PxU32* mGridModelOrderedTetrahedrons;
PxU32 mGMNbPartitions;
PxU32 mGMMaxMaxTetsPerPartitions;
PxU32 mGMRemapOutputSize;
PxU32* mGMRemapOutputCP;
PxU32* mGMAccumulatedPartitionsCP;
PxU32* mGMAccumulatedCopiesCP;
PxU32* mCollisionAccumulatedTetrahedronsRef;
PxU32* mCollisionTetrahedronsReferences;
PxU32 mCollisionNbTetrahedronsReferences;
PxU8* mCollisionSurfaceVertsHint;
PxU32* mCollisionSurfaceVertToTetRemap;
PxReal* mVertsBarycentricInGridModel;
PxU32* mVertsRemapInGridModel;
PxU32* mTetsRemapColToSim;
PxU32 mTetsRemapSize;
PxU32* mTetsAccumulatedRemapColToSim;
PxU32* mGMPullIndices;
PxMat33* mTetraRestPoses;
PxU32 mNumTetsPerElement;
};
class TetrahedronMesh : public PxTetrahedronMesh, public PxUserAllocated
{
public:
TetrahedronMesh(PxU32 nbVertices, PxVec3* vertices, PxU32 nbTetrahedrons, void* tetrahedrons, PxU8 flags, PxBounds3 aabb, PxReal geomEpsilon);
TetrahedronMesh(TetrahedronMeshData& mesh);
TetrahedronMesh(MeshFactory* meshFactory, TetrahedronMeshData& mesh);
virtual ~TetrahedronMesh();
virtual const char* getConcreteTypeName() const { return "PxTetrahedronMesh"; }
virtual void acquireReference() { Cm::RefCountable_incRefCount(*this); }
virtual PxU32 getReferenceCount() const { return Cm::RefCountable_getRefCount(*this); }
virtual void release() { Cm::RefCountable_decRefCount(*this); }
virtual void onRefCountZero();
virtual PxU32 getNbVertices() const { return mNbVertices; }
virtual const PxVec3* getVertices() const { return mVertices; }
virtual PxU32 getNbTetrahedrons() const { return mNbTetrahedrons; }
virtual const void* getTetrahedrons() const { return mTetrahedrons; }
virtual PxTetrahedronMeshFlags getTetrahedronMeshFlags() const { return PxTetrahedronMeshFlags(mFlags); }
virtual const PxU32* getTetrahedraRemap() const { return NULL; }
PX_FORCE_INLINE PxU32 getNbVerticesFast() const { return mNbVertices; }
PX_FORCE_INLINE PxVec3* getVerticesFast() const { return mVertices; }
PX_FORCE_INLINE PxU32 getNbTetrahedronsFast() const { return mNbTetrahedrons; }
PX_FORCE_INLINE const void* getTetrahedronsFast() const { return mTetrahedrons; }
PX_FORCE_INLINE bool has16BitIndices() const { return (mFlags & PxMeshFlag::e16_BIT_INDICES) ? true : false; }
PX_FORCE_INLINE bool hasPerTriangleMaterials() const { return mMaterialIndices != NULL; }
PX_FORCE_INLINE const PxU16* getMaterials() const { return mMaterialIndices; }
PX_FORCE_INLINE const CenterExtents& getLocalBoundsFast() const { return mAABB; }
PX_FORCE_INLINE const CenterExtentsPadded& getPaddedBounds() const
{
// PT: see compile-time assert in cpp
return static_cast<const CenterExtentsPadded&>(mAABB);
}
virtual PxBounds3 getLocalBounds() const
{
PX_ASSERT(mAABB.isValid());
return PxBounds3::centerExtents(mAABB.mCenter, mAABB.mExtents);
}
PxU32 mNbVertices;
PxVec3* mVertices;
PxU32 mNbTetrahedrons;
void* mTetrahedrons;
PxU8 mFlags; //!< Flag whether indices are 16 or 32 bits wide
PxU16* mMaterialIndices; //!< the size of the array is mNbTetrahedrons.
// PT: WARNING: bounds must be followed by at least 32bits of data for safe SIMD loading
CenterExtents mAABB;
PxReal mGeomEpsilon;
MeshFactory* mMeshFactory; // PT: changed to pointer for serialization
};
PX_FORCE_INLINE const Gu::TetrahedronMesh* _getTetraMeshData(const PxTetrahedronMeshGeometry& meshGeom)
{
return static_cast<const Gu::TetrahedronMesh*>(meshGeom.tetrahedronMesh);
}
class BVTetrahedronMesh : public TetrahedronMesh
{
public:
BVTetrahedronMesh(TetrahedronMeshData& mesh, DeformableVolumeCollisionData& d, MeshFactory* factory = NULL);
virtual ~BVTetrahedronMesh()
{
PX_FREE(mGRB_tetraIndices);
PX_FREE(mGRB_tetraSurfaceHint);
PX_FREE(mGRB_faceRemap);
PX_FREE(mGRB_faceRemapInverse);
PX_DELETE(mGRB_BV32Tree);
PX_FREE(mFaceRemap);
}
//virtual PxBounds3 refitBVH();
PX_FORCE_INLINE const Gu::BV4Tree& getBV4Tree() const { return mBV4Tree; }
PX_FORCE_INLINE Gu::BV4Tree& getBV4Tree() { return mBV4Tree; }
PX_FORCE_INLINE void* getGRBTetraFaceRemap() { return mGRB_faceRemap; }
PX_FORCE_INLINE void* getGRBTetraFaceRemapInverse() { return mGRB_faceRemapInverse; }
virtual const PxU32* getTetrahedraRemap() const { return mFaceRemap; }
PX_FORCE_INLINE bool isTetMeshGPUCompatible() const
{
return mGRB_BV32Tree != NULL;
}
PxU32* mFaceRemap; //!< new faces to old faces mapping (after cleaning, etc). Usage: old = faceRemap[new]
// GRB data -------------------------
void* mGRB_tetraIndices; //!< GRB: GPU-friendly tri indices [uint4]
PxU8* mGRB_tetraSurfaceHint;
PxU32* mGRB_faceRemap;
PxU32* mGRB_faceRemapInverse;
Gu::BV32Tree* mGRB_BV32Tree; //!< GRB: BV32 tree
private:
Gu::TetrahedronSourceMesh mMeshInterface4;
Gu::BV4Tree mBV4Tree;
Gu::TetrahedronSourceMesh mMeshInterface32;
};
// Possible optimization: align the whole struct to cache line
class DeformableVolumeMesh : public PxDeformableVolumeMesh, public PxUserAllocated
{
public:
virtual const char* getConcreteTypeName() const { return "PxDeformableVolumeMesh"; }
// PX_SERIALIZATION
virtual void exportExtraData(PxSerializationContext& ctx);
void importExtraData(PxDeserializationContext&);
virtual void release();
void resolveReferences(PxDeserializationContext&) {}
virtual void requiresObjects(PxProcessPxBaseCallback&) {}
//~PX_SERIALIZATION
virtual void acquireReference() { Cm::RefCountable_incRefCount(*this); }
virtual PxU32 getReferenceCount() const { return Cm::RefCountable_getRefCount(*this); }
virtual void onRefCountZero();
//virtual PxMeshMidPhase::Enum getMidphaseID() const { return PxMeshMidPhase::eBVH34; }
DeformableVolumeMesh(MeshFactory* factory, DeformableVolumeMeshData& data);
virtual ~DeformableVolumeMesh();
void setMeshFactory(MeshFactory* factory) { mMeshFactory = factory; }
virtual const PxTetrahedronMesh* getCollisionMesh() const { return mCollisionMesh; }
virtual PxTetrahedronMesh* getCollisionMesh() { return mCollisionMesh; }
PX_FORCE_INLINE const BVTetrahedronMesh* getCollisionMeshFast() const { return mCollisionMesh; }
PX_FORCE_INLINE BVTetrahedronMesh* getCollisionMeshFast() { return mCollisionMesh; }
virtual const PxTetrahedronMesh* getSimulationMesh() const { return mSimulationMesh; }
virtual PxTetrahedronMesh* getSimulationMesh() { return mSimulationMesh; }
PX_FORCE_INLINE const TetrahedronMesh* getSimulationMeshFast() const { return mSimulationMesh; }
PX_FORCE_INLINE TetrahedronMesh* getSimulationMeshFast() { return mSimulationMesh; }
virtual const PxDeformableVolumeAuxData* getDeformableVolumeAuxData() const { return mDeformableVolumeAuxData; }
virtual PxDeformableVolumeAuxData* getDeformableVolumeAuxData() { return mDeformableVolumeAuxData; }
PX_FORCE_INLINE const DeformableVolumeAuxData* getDeformableVolumeAuxDataFast() const { return mDeformableVolumeAuxData; }
PX_FORCE_INLINE DeformableVolumeAuxData* getDeformableVolumeAuxDataFast() { return mDeformableVolumeAuxData; }
protected:
TetrahedronMesh* mSimulationMesh;
BVTetrahedronMesh* mCollisionMesh;
DeformableVolumeAuxData* mDeformableVolumeAuxData;
MeshFactory* mMeshFactory; // PT: changed to pointer for serialization
};
#if PX_VC
#pragma warning(pop)
#endif
} // namespace Gu
}
#endif

View File

@@ -0,0 +1,134 @@
// 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.
#include "GuTetrahedronMeshUtils.h"
#include "GuDistancePointTetrahedron.h"
namespace physx
{
namespace Gu
{
void convertDeformableVolumeCollisionToSimMeshTets(const PxTetrahedronMesh& simMesh, const DeformableVolumeAuxData& simState, const BVTetrahedronMesh& collisionMesh,
PxU32 inTetId, const PxVec4& inTetBarycentric, PxU32& outTetId, PxVec4& outTetBarycentric, bool bClampToClosestPoint)
{
if (inTetId == 0xFFFFFFFF)
{
outTetId = 0xFFFFFFFF;
outTetBarycentric = PxVec4(0.0f);
return;
}
// Map from CPU tet ID (corresponds to the ID in the BV4 mesh) to the GPU tet ID (corresponds to the ID in
// the BV32 mesh)
inTetId = collisionMesh.mGRB_faceRemapInverse[inTetId];
const PxU32 endIdx = simState.mTetsAccumulatedRemapColToSim[inTetId];
const PxU32 startIdx = inTetId != 0 ? simState.mTetsAccumulatedRemapColToSim[inTetId - 1] : 0;
const PxU32* const tetRemapColToSim = simState.mTetsRemapColToSim;
typedef PxVec4T<unsigned int> uint4;
const uint4* const collInds = reinterpret_cast<const uint4*>(collisionMesh.mGRB_tetraIndices /*collisionMesh->mTetrahedrons*/);
const uint4* const simInds = reinterpret_cast<const uint4*>(simMesh.getTetrahedrons());
const PxVec3* const collVerts = collisionMesh.mVertices;
const PxVec3* const simVerts = simMesh.getVertices();
const uint4 ind = collInds[inTetId];
const PxVec3 point = collVerts[ind.x] * inTetBarycentric.x + collVerts[ind.y] * inTetBarycentric.y +
collVerts[ind.z] * inTetBarycentric.z + collVerts[ind.w] * inTetBarycentric.w;
PxReal currDist = PX_MAX_F32;
for (PxU32 i = startIdx; i < endIdx; ++i)
{
const PxU32 simTet = tetRemapColToSim[i];
const uint4 simInd = simInds[simTet];
const PxVec3 a = simVerts[simInd.x];
const PxVec3 b = simVerts[simInd.y];
const PxVec3 c = simVerts[simInd.z];
const PxVec3 d = simVerts[simInd.w];
const PxVec3 tmpClosest = closestPtPointTetrahedronWithInsideCheck(point, a, b, c, d);
const PxVec3 v = point - tmpClosest;
const PxReal tmpDist = v.dot(v);
if (tmpDist < currDist)
{
PxVec4 tmpBarycentric;
if (bClampToClosestPoint)
PxComputeBarycentric(a, b, c, d, tmpClosest, tmpBarycentric);
else
PxComputeBarycentric(a, b, c, d, point, tmpBarycentric);
currDist = tmpDist;
outTetId = simTet;
outTetBarycentric = tmpBarycentric;
if (tmpDist < 1e-6f)
break;
}
}
PX_ASSERT(outTetId != 0xFFFFFFFF);
}
PxVec4 addAxisToSimMeshBarycentric(const PxTetrahedronMesh& simMesh, const PxU32 simTetId, const PxVec4& simBary, const PxVec3& axis)
{
const PxVec3* simMeshVerts = simMesh.getVertices();
PxVec3 tetVerts[4];
if(simMesh.getTetrahedronMeshFlags() & PxTetrahedronMeshFlag::e16_BIT_INDICES)
{
const PxU16* indices = reinterpret_cast<const PxU16*>(simMesh.getTetrahedrons());
tetVerts[0] = simMeshVerts[indices[simTetId*4 + 0]];
tetVerts[1] = simMeshVerts[indices[simTetId*4 + 1]];
tetVerts[2] = simMeshVerts[indices[simTetId*4 + 2]];
tetVerts[3] = simMeshVerts[indices[simTetId*4 + 3]];
}
else
{
const PxU32* indices = reinterpret_cast<const PxU32*>(simMesh.getTetrahedrons());
tetVerts[0] = simMeshVerts[indices[simTetId*4 + 0]];
tetVerts[1] = simMeshVerts[indices[simTetId*4 + 1]];
tetVerts[2] = simMeshVerts[indices[simTetId*4 + 2]];
tetVerts[3] = simMeshVerts[indices[simTetId*4 + 3]];
}
const PxVec3 simPoint = tetVerts[0]*simBary.x + tetVerts[1]*simBary.y + tetVerts[2]*simBary.z + tetVerts[3]*simBary.w;
const PxVec3 offsetPoint = simPoint + axis;
PxVec4 offsetBary;
PxComputeBarycentric(tetVerts[0], tetVerts[1], tetVerts[2], tetVerts[3], offsetPoint, offsetBary);
return offsetBary;
}
}
}

View File

@@ -0,0 +1,48 @@
// 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.
#ifndef GU_TETRAHEDRONMESHUTILS_H
#define GU_TETRAHEDRONMESHUTILS_H
#include <GuTetrahedronMesh.h>
namespace physx
{
namespace Gu
{
PX_PHYSX_COMMON_API
void convertDeformableVolumeCollisionToSimMeshTets(const PxTetrahedronMesh& simMesh, const DeformableVolumeAuxData& simState, const BVTetrahedronMesh& collisionMesh,
PxU32 inTetId, const PxVec4& inTetBarycentric, PxU32& outTetId, PxVec4& outTetBarycentric, bool bClampToClosestPoint = true);
PX_PHYSX_COMMON_API
PxVec4 addAxisToSimMeshBarycentric(const PxTetrahedronMesh& simMesh, const PxU32 simTetId,
const PxVec4& simBarycentric, const PxVec3& axis);
}
}
#endif

View File

@@ -0,0 +1,144 @@
// 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_TRIANGLE_H
#define GU_TRIANGLE_H
#include "foundation/PxVec3.h"
#include "foundation/PxUtilities.h"
#include "foundation/PxUserAllocated.h"
namespace physx
{
namespace Gu
{
// PT: I'm taking back control of these files and re-introducing the "ICE" naming conventions:
// - "Triangle" is for actual triangles (like the PxTriangle class)
// - If it contains vertex indices, it's "IndexedTriangle".
// - "v" is too ambiguous (it could be either an actual vertex or a vertex reference) so use "ref" instead.
// Plus we sometimes reference edges, not vertices, so "v" is too restrictive.
template <class T>
struct IndexedTriangleT : public PxUserAllocated
{
PX_INLINE IndexedTriangleT () {}
PX_INLINE IndexedTriangleT (T a, T b, T c) { mRef[0] = a; mRef[1] = b; mRef[2] = c; }
template <class TX>
PX_INLINE IndexedTriangleT (const IndexedTriangleT <TX>& other) { mRef[0] = other[0]; mRef[1] = other[1]; mRef[2] = other[2]; }
PX_INLINE T& operator[](T i) { return mRef[i]; }
PX_INLINE const T& operator[](T i) const { return mRef[i]; }
template<class TX>//any type of IndexedTriangleT <>, possibly with different T
PX_INLINE IndexedTriangleT <T>& operator=(const IndexedTriangleT <TX>& i) { mRef[0]=i[0]; mRef[1]=i[1]; mRef[2]=i[2]; return *this; }
void flip()
{
PxSwap(mRef[1], mRef[2]);
}
PX_INLINE bool contains(T id) const
{
return mRef[0] == id || mRef[1] == id || mRef[2] == id;
}
PX_INLINE void center(const PxVec3* verts, PxVec3& center) const
{
const PxVec3& p0 = verts[mRef[0]];
const PxVec3& p1 = verts[mRef[1]];
const PxVec3& p2 = verts[mRef[2]];
center = (p0+p1+p2)*0.33333333333333333333f;
}
float area(const PxVec3* verts) const
{
const PxVec3& p0 = verts[mRef[0]];
const PxVec3& p1 = verts[mRef[1]];
const PxVec3& p2 = verts[mRef[2]];
return ((p0-p1).cross(p0-p2)).magnitude() * 0.5f;
}
PxU8 findEdge(T vref0, T vref1) const
{
if(mRef[0]==vref0 && mRef[1]==vref1) return 0;
else if(mRef[0]==vref1 && mRef[1]==vref0) return 0;
else if(mRef[0]==vref0 && mRef[2]==vref1) return 1;
else if(mRef[0]==vref1 && mRef[2]==vref0) return 1;
else if(mRef[1]==vref0 && mRef[2]==vref1) return 2;
else if(mRef[1]==vref1 && mRef[2]==vref0) return 2;
return 0xff;
}
// counter clock wise order
PxU8 findEdgeCCW(T vref0, T vref1) const
{
if(mRef[0]==vref0 && mRef[1]==vref1) return 0;
else if(mRef[0]==vref1 && mRef[1]==vref0) return 0;
else if(mRef[0]==vref0 && mRef[2]==vref1) return 2;
else if(mRef[0]==vref1 && mRef[2]==vref0) return 2;
else if(mRef[1]==vref0 && mRef[2]==vref1) return 1;
else if(mRef[1]==vref1 && mRef[2]==vref0) return 1;
return 0xff;
}
bool replaceVertex(T oldref, T newref)
{
if(mRef[0]==oldref) { mRef[0] = newref; return true; }
else if(mRef[1]==oldref) { mRef[1] = newref; return true; }
else if(mRef[2]==oldref) { mRef[2] = newref; return true; }
return false;
}
bool isDegenerate() const
{
if(mRef[0]==mRef[1]) return true;
if(mRef[1]==mRef[2]) return true;
if(mRef[2]==mRef[0]) return true;
return false;
}
PX_INLINE void denormalizedNormal(const PxVec3* verts, PxVec3& normal) const
{
const PxVec3& p0 = verts[mRef[0]];
const PxVec3& p1 = verts[mRef[1]];
const PxVec3& p2 = verts[mRef[2]];
normal = ((p2 - p1).cross(p0 - p1));
}
T mRef[3]; //vertex indices
};
typedef IndexedTriangleT<PxU32> IndexedTriangle32;
typedef IndexedTriangleT<PxU16> IndexedTriangle16;
PX_COMPILE_TIME_ASSERT(sizeof(IndexedTriangle32)==12);
PX_COMPILE_TIME_ASSERT(sizeof(IndexedTriangle16)==6);
}
}
#endif

View File

@@ -0,0 +1,239 @@
// 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_TRIANGLE_CACHE_H
#define GU_TRIANGLE_CACHE_H
#include "foundation/PxHash.h"
#include "foundation/PxUtilities.h"
namespace physx
{
namespace Gu
{
struct CachedEdge
{
protected:
PxU32 mId0, mId1;
public:
CachedEdge(PxU32 i0, PxU32 i1)
{
mId0 = PxMin(i0, i1);
mId1 = PxMax(i0, i1);
}
CachedEdge()
{
}
PxU32 getId0() const { return mId0; }
PxU32 getId1() const { return mId1; }
bool operator == (const CachedEdge& other) const
{
return mId0 == other.mId0 && mId1 == other.mId1;
}
PxU32 getHashCode() const
{
return PxComputeHash(mId0 << 16 | mId1);
}
};
struct CachedVertex
{
private:
PxU32 mId;
public:
CachedVertex(PxU32 id)
{
mId = id;
}
CachedVertex()
{
}
PxU32 getId() const { return mId; }
PxU32 getHashCode() const
{
return mId;
}
bool operator == (const CachedVertex& other) const
{
return mId == other.mId;
}
};
template <typename Elem, PxU32 MaxCount>
struct CacheMap
{
PX_COMPILE_TIME_ASSERT(MaxCount < 0xFF);
Elem mCache[MaxCount];
PxU8 mNextInd[MaxCount];
PxU8 mIndex[MaxCount];
PxU32 mSize;
CacheMap() : mSize(0)
{
for(PxU32 a = 0; a < MaxCount; ++a)
{
mIndex[a] = 0xFF;
}
}
bool addData(const Elem& data)
{
if(mSize == MaxCount)
return false;
const PxU8 hash = PxU8(data.getHashCode() % MaxCount);
PxU8 index = hash;
PxU8 nextInd = mIndex[hash];
while(nextInd != 0xFF)
{
index = nextInd;
if(mCache[index] == data)
return false;
nextInd = mNextInd[nextInd];
}
if(mIndex[hash] == 0xFF)
{
mIndex[hash] = PxTo8(mSize);
}
else
{
mNextInd[index] = PxTo8(mSize);
}
mNextInd[mSize] = 0xFF;
mCache[mSize++] = data;
return true;
}
bool contains(const Elem& data) const
{
PxU32 hash = (data.getHashCode() % MaxCount);
PxU8 index = mIndex[hash];
while(index != 0xFF)
{
if(mCache[index] == data)
return true;
index = mNextInd[index];
}
return false;
}
const Elem* get(const Elem& data) const
{
PxU32 hash = (data.getHashCode() % MaxCount);
PxU8 index = mIndex[hash];
while(index != 0xFF)
{
if(mCache[index] == data)
return &mCache[index];
index = mNextInd[index];
}
return NULL;
}
};
template <PxU32 MaxTriangles>
struct TriangleCache
{
PxVec3 mVertices[3*MaxTriangles];
PxU32 mIndices[3*MaxTriangles];
PxU32 mTriangleIndex[MaxTriangles];
PxU8 mEdgeFlags[MaxTriangles];
PxU32 mNumTriangles;
TriangleCache() : mNumTriangles(0)
{
}
PX_FORCE_INLINE bool isEmpty() const { return mNumTriangles == 0; }
PX_FORCE_INLINE bool isFull() const { return mNumTriangles == MaxTriangles; }
PX_FORCE_INLINE void reset() { mNumTriangles = 0; }
void addTriangle(const PxVec3* verts, const PxU32* indices, PxU32 triangleIndex, PxU8 edgeFlag)
{
PX_ASSERT(mNumTriangles < MaxTriangles);
PxU32 triInd = mNumTriangles++;
PxU32 triIndMul3 = triInd*3;
mVertices[triIndMul3] = verts[0];
mVertices[triIndMul3+1] = verts[1];
mVertices[triIndMul3+2] = verts[2];
mIndices[triIndMul3] = indices[0];
mIndices[triIndMul3+1] = indices[1];
mIndices[triIndMul3+2] = indices[2];
mTriangleIndex[triInd] = triangleIndex;
mEdgeFlags[triInd] = edgeFlag;
}
};
template <PxU32 MaxTetrahedrons>
struct TetrahedronCache
{
PxVec3 mVertices[4 * MaxTetrahedrons];
PxU32 mTetVertIndices[4 * MaxTetrahedrons];
PxU32 mTetrahedronIndices[MaxTetrahedrons];
PxU32 mNumTetrahedrons;
TetrahedronCache() : mNumTetrahedrons(0)
{
}
PX_FORCE_INLINE bool isEmpty() const { return mNumTetrahedrons == 0; }
PX_FORCE_INLINE bool isFull() const { return mNumTetrahedrons == MaxTetrahedrons; }
PX_FORCE_INLINE void reset() { mNumTetrahedrons = 0; }
void addTetrahedrons(const PxVec3* verts, const PxU32* indices, PxU32 tetIndex)
{
PX_ASSERT(mNumTetrahedrons < MaxTetrahedrons);
PxU32 tetInd = mNumTetrahedrons++;
PxU32 tetIndMul4 = tetInd * 4;
mVertices[tetIndMul4] = verts[0];
mVertices[tetIndMul4 + 1] = verts[1];
mVertices[tetIndMul4 + 2] = verts[2];
mVertices[tetIndMul4 + 3] = verts[3];
mTetVertIndices[tetIndMul4] = indices[0];
mTetVertIndices[tetIndMul4 + 1] = indices[1];
mTetVertIndices[tetIndMul4 + 2] = indices[2];
mTetVertIndices[tetIndMul4 + 3] = indices[3];
mTetrahedronIndices[tetInd] = tetIndex;
}
};
}
}
#endif

View File

@@ -0,0 +1,341 @@
// 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 "GuMidphaseInterface.h"
#include "GuMeshFactory.h"
#include "GuConvexEdgeFlags.h"
#include "GuEdgeList.h"
#include "geometry/PxGeometryInternal.h"
using namespace physx;
using namespace Gu;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static PxConcreteType::Enum gTable[] = { PxConcreteType::eTRIANGLE_MESH_BVH33,
PxConcreteType::eTRIANGLE_MESH_BVH34
};
TriangleMesh::TriangleMesh(MeshFactory* factory, TriangleMeshData& d) :
PxTriangleMesh (PxType(gTable[d.mType]), PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE),
mNbVertices (d.mNbVertices),
mNbTriangles (d.mNbTriangles),
mVertices (d.mVertices),
mTriangles (d.mTriangles),
mAABB (d.mAABB),
mExtraTrigData (d.mExtraTrigData),
mGeomEpsilon (d.mGeomEpsilon),
mFlags (d.mFlags),
mMaterialIndices (d.mMaterialIndices),
mFaceRemap (d.mFaceRemap),
mAdjacencies (d.mAdjacencies),
mMeshFactory (factory),
mEdgeList (NULL),
mMass (d.mMass),
mInertia (d.mInertia),
mLocalCenterOfMass (d.mLocalCenterOfMass),
mGRB_triIndices (d.mGRB_primIndices),
mGRB_triAdjacencies (d.mGRB_primAdjacencies),
mGRB_faceRemap (d.mGRB_faceRemap),
mGRB_faceRemapInverse (d.mGRB_faceRemapInverse),
mGRB_BV32Tree (d.mGRB_BV32Tree),
mSdfData (d.mSdfData),
mAccumulatedTrianglesRef (d.mAccumulatedTrianglesRef),
mTrianglesReferences (d.mTrianglesReferences),
mNbTrianglesReferences (d.mNbTrianglesReferences)
{
// this constructor takes ownership of memory from the data object
d.mVertices = NULL;
d.mTriangles = NULL;
d.mExtraTrigData = NULL;
d.mFaceRemap = NULL;
d.mAdjacencies = NULL;
d.mMaterialIndices = NULL;
d.mGRB_primIndices = NULL;
d.mGRB_primAdjacencies = NULL;
d.mGRB_faceRemap = NULL;
d.mGRB_faceRemapInverse = NULL;
d.mGRB_BV32Tree = NULL;
d.mSdfData.mSdf = NULL;
d.mSdfData.mSubgridStartSlots = NULL;
d.mSdfData.mSubgridSdf = NULL;
d.mAccumulatedTrianglesRef = NULL;
d.mTrianglesReferences = NULL;
// 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(TriangleMesh, mExtraTrigData)>=PX_OFFSET_OF(TriangleMesh, mAABB)+4);
}
// PT: temporary for Kit
TriangleMesh::TriangleMesh(const PxTriangleMeshInternalData& data) :
PxTriangleMesh (PxConcreteType::eTRIANGLE_MESH_BVH34, PxBaseFlags(0)),
mNbVertices (data.mNbVertices),
mNbTriangles (data.mNbTriangles),
mVertices (data.mVertices),
mTriangles (data.mTriangles),
mExtraTrigData (NULL),
mGeomEpsilon (data.mGeomEpsilon),
mFlags (data.mFlags),
mMaterialIndices (NULL),
mFaceRemap (data.mFaceRemap),
mAdjacencies (NULL),
mMeshFactory (NULL),
mEdgeList (NULL),
mGRB_triIndices (NULL),
mGRB_triAdjacencies (NULL),
mGRB_faceRemap (NULL),
mGRB_faceRemapInverse (NULL),
mGRB_BV32Tree (NULL),
mAccumulatedTrianglesRef(NULL),
mTrianglesReferences (NULL),
mNbTrianglesReferences (0)
{
mAABB.mCenter = data.mAABB_Center;
mAABB.mExtents = data.mAABB_Extents;
}
//~ PT: temporary for Kit
TriangleMesh::~TriangleMesh()
{
if(getBaseFlags() & PxBaseFlag::eOWNS_MEMORY)
{
PX_FREE(mExtraTrigData);
PX_FREE(mFaceRemap);
PX_FREE(mAdjacencies);
PX_FREE(mMaterialIndices);
PX_FREE(mTriangles);
PX_FREE(mVertices);
PX_FREE(mGRB_triIndices);
PX_FREE(mGRB_triAdjacencies);
PX_FREE(mGRB_faceRemap);
PX_FREE(mGRB_faceRemapInverse);
PX_DELETE(mGRB_BV32Tree);
PX_FREE(mAccumulatedTrianglesRef);
PX_FREE(mTrianglesReferences);
}
PX_DELETE(mEdgeList);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PT: used to be automatic but making it manual saves bytes in the internal mesh
void TriangleMesh::exportExtraData(PxSerializationContext& stream)
{
//PX_DEFINE_DYNAMIC_ARRAY(TriangleMesh, mVertices, PxField::eVEC3, mNbVertices, Ps::PxFieldFlag::eSERIALIZE),
if(mVertices)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(mVertices, mNbVertices * sizeof(PxVec3));
}
if(mTriangles)
{
const PxU32 triangleSize = mFlags & PxTriangleMeshFlag::e16_BIT_INDICES ? sizeof(PxU16) : sizeof(PxU32);
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(mTriangles, mNbTriangles * 3 * triangleSize);
}
//PX_DEFINE_DYNAMIC_ARRAY(TriangleMesh, mExtraTrigData, PxField::eBYTE, mNbTriangles, Ps::PxFieldFlag::eSERIALIZE),
if(mExtraTrigData)
{
// PT: it might not be needed to 16-byte align this array of PxU8....
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(mExtraTrigData, mNbTriangles * sizeof(PxU8));
}
if(mMaterialIndices)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(mMaterialIndices, mNbTriangles * sizeof(PxU16));
}
if(mFaceRemap)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(mFaceRemap, mNbTriangles * sizeof(PxU32));
}
if(mAdjacencies)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(mAdjacencies, mNbTriangles * sizeof(PxU32) * 3);
}
if(mGRB_triIndices)
{
const PxU32 triangleSize = mFlags & PxTriangleMeshFlag::e16_BIT_INDICES ? sizeof(PxU16) : sizeof(PxU32);
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(mGRB_triIndices, mNbTriangles * 3 * triangleSize);
}
if(mGRB_triAdjacencies)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(mGRB_triAdjacencies, mNbTriangles * sizeof(PxU32) * 4);
}
if(mGRB_faceRemap)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(mGRB_faceRemap, mNbTriangles * sizeof(PxU32));
}
if(mGRB_faceRemapInverse)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(mGRB_faceRemapInverse, mNbTriangles * sizeof(PxU32));
}
if(mGRB_BV32Tree)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(mGRB_BV32Tree, sizeof(BV32Tree));
mGRB_BV32Tree->exportExtraData(stream);
}
mSdfData.exportExtraData(stream);
}
void TriangleMesh::importExtraData(PxDeserializationContext& context)
{
// PT: vertices are followed by indices, so it will be safe to V4Load vertices from a deserialized binary file
if(mVertices)
mVertices = context.readExtraData<PxVec3, PX_SERIAL_ALIGN>(mNbVertices);
if(mTriangles)
{
if(mFlags & PxTriangleMeshFlag::e16_BIT_INDICES)
mTriangles = context.readExtraData<PxU16, PX_SERIAL_ALIGN>(3*mNbTriangles);
else
mTriangles = context.readExtraData<PxU32, PX_SERIAL_ALIGN>(3*mNbTriangles);
}
if(mExtraTrigData)
mExtraTrigData = context.readExtraData<PxU8, PX_SERIAL_ALIGN>(mNbTriangles);
if(mMaterialIndices)
mMaterialIndices = context.readExtraData<PxU16, PX_SERIAL_ALIGN>(mNbTriangles);
if(mFaceRemap)
mFaceRemap = context.readExtraData<PxU32, PX_SERIAL_ALIGN>(mNbTriangles);
if(mAdjacencies)
mAdjacencies = context.readExtraData<PxU32, PX_SERIAL_ALIGN>(3*mNbTriangles);
if(mGRB_triIndices)
{
if(mFlags & PxTriangleMeshFlag::e16_BIT_INDICES)
mGRB_triIndices = context.readExtraData<PxU16, PX_SERIAL_ALIGN>(3 * mNbTriangles);
else
mGRB_triIndices = context.readExtraData<PxU32, PX_SERIAL_ALIGN>(3 * mNbTriangles);
}
if(mGRB_triAdjacencies)
{
mGRB_triAdjacencies = context.readExtraData<PxU32, PX_SERIAL_ALIGN>(4 * mNbTriangles);
}
if(mGRB_faceRemap)
{
mGRB_faceRemap = context.readExtraData<PxU32, PX_SERIAL_ALIGN>(mNbTriangles);
}
if(mGRB_faceRemapInverse)
{
mGRB_faceRemapInverse = context.readExtraData<PxU32, PX_SERIAL_ALIGN>(mNbTriangles);
}
if(mGRB_BV32Tree)
{
mGRB_BV32Tree = context.readExtraData<BV32Tree, PX_SERIAL_ALIGN>();
PX_PLACEMENT_NEW(mGRB_BV32Tree, BV32Tree(PxEmpty));
mGRB_BV32Tree->importExtraData(context);
}
mSdfData.importExtraData(context);
}
void TriangleMesh::onRefCountZero()
{
::onRefCountZero(this, mMeshFactory, false, "PxTriangleMesh::release: double deletion detected!");
}
void TriangleMesh::release()
{
Cm::RefCountable_decRefCount(*this);
}
PxVec3* TriangleMesh::getVerticesForModification()
{
PxGetFoundation().error(PxErrorCode::eINVALID_OPERATION, PX_FL, "PxTriangleMesh::getVerticesForModification() is not supported for this type of meshes.");
return NULL;
}
PxBounds3 TriangleMesh::refitBVH()
{
PxGetFoundation().error(PxErrorCode::eINVALID_OPERATION, PX_FL, "PxTriangleMesh::refitBVH() is not supported for this type of meshes.");
return PxBounds3(mAABB.getMin(), mAABB.getMax());
}
void TriangleMesh::setAllEdgesActive()
{
if(mExtraTrigData)
{
const PxU32 nbTris = mNbTriangles;
for(PxU32 i=0; i<nbTris; i++)
mExtraTrigData[i] |= ETD_CONVEX_EDGE_ALL;
}
}
const EdgeList* TriangleMesh::requestEdgeList() const
{
if(!mEdgeList)
{
EDGELISTCREATE create;
create.NbFaces = mNbTriangles;
create.Verts = mVertices;
if(has16BitIndices())
create.WFaces = reinterpret_cast<const PxU16*>(mTriangles);
else
create.DFaces = reinterpret_cast<const PxU32*>(mTriangles);
mEdgeList = PX_NEW(EdgeList);
mEdgeList->init(create);
}
return mEdgeList;
}

View File

@@ -0,0 +1,346 @@
// 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_TRIANGLEMESH_H
#define GU_TRIANGLEMESH_H
#include "foundation/PxIO.h"
#include "geometry/PxTriangle.h"
#include "geometry/PxTriangleMeshGeometry.h"
#include "geometry/PxSimpleTriangleMesh.h"
#include "geometry/PxTriangleMesh.h"
#include "GuMeshData.h"
#include "GuCenterExtents.h"
#include "CmScaling.h"
#include "CmRefCountable.h"
#include "common/PxRenderOutput.h"
namespace physx
{
class PxMeshScale;
struct PxTriangleMeshInternalData;
namespace Gu
{
PX_FORCE_INLINE void getVertexRefs(PxU32 triangleIndex, PxU32& vref0, PxU32& vref1, PxU32& vref2, const void* indices, bool has16BitIndices)
{
if(has16BitIndices)
{
const PxU16* inds = reinterpret_cast<const PxU16*>(indices) + triangleIndex*3;
vref0 = inds[0];
vref1 = inds[1];
vref2 = inds[2];
}
else
{
const PxU32* inds = reinterpret_cast<const PxU32*>(indices) + triangleIndex*3;
vref0 = inds[0];
vref1 = inds[1];
vref2 = inds[2];
}
}
class MeshFactory;
#if PX_VC
#pragma warning(push)
#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value.
#endif
class EdgeList;
class TriangleMesh : public PxTriangleMesh, public PxUserAllocated
{
public:
// PX_SERIALIZATION
TriangleMesh(PxBaseFlags baseFlags) : PxTriangleMesh(baseFlags), mSdfData(PxEmpty) {}
void preExportDataReset() { Cm::RefCountable_preExportDataReset(*this); }
virtual void exportExtraData(PxSerializationContext& context);
void importExtraData(PxDeserializationContext& context);
virtual void release();
virtual void requiresObjects(PxProcessPxBaseCallback&){}
//~PX_SERIALIZATION
TriangleMesh(MeshFactory* factory, TriangleMeshData& data);
TriangleMesh(const PxTriangleMeshInternalData& data);
virtual ~TriangleMesh();
// PxBase
virtual void onRefCountZero();
//~PxBase
// PxRefCounted
virtual void acquireReference() { Cm::RefCountable_incRefCount(*this); }
virtual PxU32 getReferenceCount() const { return Cm::RefCountable_getRefCount(*this); }
//~PxRefCounted
// PxTriangleMesh
virtual PxU32 getNbVertices() const { return mNbVertices;}
virtual const PxVec3* getVertices() const { return mVertices; }
virtual PxVec3* getVerticesForModification();
virtual PxBounds3 refitBVH();
virtual PxU32 getNbTriangles() const { return mNbTriangles; }
virtual const void* getTriangles() const { return mTriangles; }
virtual PxTriangleMeshFlags getTriangleMeshFlags() const { return PxTriangleMeshFlags(mFlags); }
virtual const PxU32* getTrianglesRemap() const { return mFaceRemap; }
virtual void setPreferSDFProjection(bool preferProjection)
{
if (preferProjection)
mFlags &= PxU8(~PxTriangleMeshFlag::ePREFER_NO_SDF_PROJ);
else
mFlags |= PxTriangleMeshFlag::ePREFER_NO_SDF_PROJ;
}
virtual bool getPreferSDFProjection() const { return !(mFlags & PxTriangleMeshFlag::ePREFER_NO_SDF_PROJ); }
virtual PxMaterialTableIndex getTriangleMaterialIndex(PxTriangleID triangleIndex) const
{
return hasPerTriangleMaterials() ? getMaterials()[triangleIndex] : PxMaterialTableIndex(0xffff);
}
virtual PxBounds3 getLocalBounds() const
{
PX_ASSERT(mAABB.isValid());
return PxBounds3::centerExtents(mAABB.mCenter, mAABB.mExtents);
}
virtual const PxReal* getSDF() const
{
return mSdfData.mSdf;
}
virtual void getSDFDimensions(PxU32& numX, PxU32& numY, PxU32& numZ) const
{
if(mSdfData.mSdf)
{
numX = mSdfData.mDims.x;
numY = mSdfData.mDims.y;
numZ = mSdfData.mDims.z;
}
else
numX = numY = numZ = 0;
}
virtual void getMassInformation(PxReal& mass, PxMat33& localInertia, PxVec3& localCenterOfMass) const
{
mass = mMass; localInertia = mInertia; localCenterOfMass = mLocalCenterOfMass;
}
//~PxTriangleMesh
virtual bool getInternalData(PxTriangleMeshInternalData&, bool) const { return false; }
// PT: this one is just to prevent instancing Gu::TriangleMesh.
// But you should use PxBase::getConcreteType() instead to avoid the virtual call.
virtual PxMeshMidPhase::Enum getMidphaseID() const = 0;
PX_FORCE_INLINE const PxU32* getFaceRemap() const { return mFaceRemap; }
PX_FORCE_INLINE bool has16BitIndices() const { return (mFlags & PxMeshFlag::e16_BIT_INDICES) ? true : false; }
PX_FORCE_INLINE bool hasPerTriangleMaterials() const { return mMaterialIndices != NULL; }
PX_FORCE_INLINE PxU32 getNbVerticesFast() const { return mNbVertices; }
PX_FORCE_INLINE PxU32 getNbTrianglesFast() const { return mNbTriangles; }
PX_FORCE_INLINE const void* getTrianglesFast() const { return mTriangles; }
PX_FORCE_INLINE const PxVec3* getVerticesFast() const { return mVertices; }
PX_FORCE_INLINE const PxU32* getAdjacencies() const { return mAdjacencies; }
PX_FORCE_INLINE PxReal getGeomEpsilon() const { return mGeomEpsilon; }
PX_FORCE_INLINE const CenterExtents& getLocalBoundsFast() const { return mAABB; }
PX_FORCE_INLINE const PxU16* getMaterials() const { return mMaterialIndices; }
PX_FORCE_INLINE const PxU8* getExtraTrigData() const { return mExtraTrigData; }
PX_FORCE_INLINE const PxU32* getAccumulatedTriangleRef() const { return mAccumulatedTrianglesRef; }
PX_FORCE_INLINE const PxU32* getTriangleReferences() const { return mTrianglesReferences; }
PX_FORCE_INLINE PxU32 getNbTriangleReferences() const { return mNbTrianglesReferences; }
PX_FORCE_INLINE const CenterExtentsPadded& getPaddedBounds() const
{
// PT: see compile-time assert in cpp
return static_cast<const CenterExtentsPadded&>(mAABB);
}
PX_FORCE_INLINE void computeWorldTriangle(
PxTriangle& worldTri, PxTriangleID triangleIndex, const PxMat34& worldMatrix, bool flipNormal = false,
PxU32* PX_RESTRICT vertexIndices=NULL, PxU32* PX_RESTRICT adjacencyIndices=NULL) const;
PX_FORCE_INLINE void getLocalTriangle(PxTriangle& localTri, PxTriangleID triangleIndex, bool flipNormal = false) const;
void setMeshFactory(MeshFactory* factory) { mMeshFactory = factory; }
// SDF methods
PX_FORCE_INLINE const SDF& getSdfDataFast() const { return mSdfData; }
//~SDF methods
PX_FORCE_INLINE PxReal getMass() const { return mMass; }
// PT: for debug viz
PX_PHYSX_COMMON_API const Gu::EdgeList* requestEdgeList() const;
protected:
PxU32 mNbVertices;
PxU32 mNbTriangles;
PxVec3* mVertices;
void* mTriangles; //!< 16 (<= 0xffff #vertices) or 32 bit trig indices (mNbTriangles * 3)
// 16 bytes block
// PT: WARNING: bounds must be followed by at least 32bits of data for safe SIMD loading
CenterExtents mAABB;
PxU8* mExtraTrigData; //one per trig
PxReal mGeomEpsilon; //!< see comments in cooking code referencing this variable
// 16 bytes block
/*
low 3 bits (mask: 7) are the edge flags:
b001 = 1 = ignore edge 0 = edge v0-->v1
b010 = 2 = ignore edge 1 = edge v0-->v2
b100 = 4 = ignore edge 2 = edge v1-->v2
*/
PxU8 mFlags; //!< Flag whether indices are 16 or 32 bits wide
//!< Flag whether triangle adajacencies are build
PxU16* mMaterialIndices; //!< the size of the array is numTriangles.
PxU32* mFaceRemap; //!< new faces to old faces mapping (after cleaning, etc). Usage: old = faceRemap[new]
PxU32* mAdjacencies; //!< Adjacency information for each face - 3 adjacent faces
//!< Set to 0xFFFFffff if no adjacent face
MeshFactory* mMeshFactory; // PT: changed to pointer for serialization
mutable Gu::EdgeList* mEdgeList; // PT: for debug viz
PxReal mMass; //this is mass assuming a unit density that can be scaled by instances!
PxMat33 mInertia; //in local space of mesh!
PxVec3 mLocalCenterOfMass; //local space com
public:
// GRB data -------------------------
void* mGRB_triIndices; //!< GRB: GPU-friendly tri indices
// TODO avoroshilov: cooking - adjacency info - duplicated, remove it and use 'mAdjacencies' and 'mExtraTrigData' see GuTriangleMesh.cpp:325
void* mGRB_triAdjacencies; //!< GRB: adjacency data, with BOUNDARY and NONCONVEX flags (flags replace adj indices where applicable)
PxU32* mGRB_faceRemap; //!< GRB : gpu to cpu triangle indice remap
PxU32* mGRB_faceRemapInverse;
Gu::BV32Tree* mGRB_BV32Tree; //!< GRB: BV32 tree
// End of GRB data ------------------
// SDF data -------------------------
SDF mSdfData;
// End of SDF data ------------------
void setAllEdgesActive();
//Vertex mapping data
PxU32* mAccumulatedTrianglesRef;//runsum
PxU32* mTrianglesReferences;
PxU32 mNbTrianglesReferences;
//End of vertex mapping data
};
#if PX_VC
#pragma warning(pop)
#endif
} // namespace Gu
PX_FORCE_INLINE void Gu::TriangleMesh::computeWorldTriangle(PxTriangle& worldTri, PxTriangleID triangleIndex, const PxMat34& worldMatrix, bool flipNormal,
PxU32* PX_RESTRICT vertexIndices, PxU32* PX_RESTRICT adjacencyIndices) const
{
PxU32 vref0, vref1, vref2;
getVertexRefs(triangleIndex, vref0, vref1, vref2, mTriangles, has16BitIndices());
if(flipNormal)
PxSwap<PxU32>(vref1, vref2);
const PxVec3* PX_RESTRICT vertices = getVerticesFast();
worldTri.verts[0] = worldMatrix.transform(vertices[vref0]);
worldTri.verts[1] = worldMatrix.transform(vertices[vref1]);
worldTri.verts[2] = worldMatrix.transform(vertices[vref2]);
if(vertexIndices)
{
vertexIndices[0] = vref0;
vertexIndices[1] = vref1;
vertexIndices[2] = vref2;
}
if(adjacencyIndices)
{
if(mAdjacencies)
{
// PT: TODO: is this correct?
adjacencyIndices[0] = flipNormal ? mAdjacencies[triangleIndex*3 + 2] : mAdjacencies[triangleIndex*3 + 0];
adjacencyIndices[1] = mAdjacencies[triangleIndex*3 + 1];
adjacencyIndices[2] = flipNormal ? mAdjacencies[triangleIndex*3 + 0] : mAdjacencies[triangleIndex*3 + 2];
}
else
{
adjacencyIndices[0] = 0xffffffff;
adjacencyIndices[1] = 0xffffffff;
adjacencyIndices[2] = 0xffffffff;
}
}
}
PX_FORCE_INLINE void Gu::TriangleMesh::getLocalTriangle(PxTriangle& localTri, PxTriangleID triangleIndex, bool flipNormal) const
{
PxU32 vref0, vref1, vref2;
getVertexRefs(triangleIndex, vref0, vref1, vref2, mTriangles, has16BitIndices());
if(flipNormal)
PxSwap<PxU32>(vref1, vref2);
const PxVec3* PX_RESTRICT vertices = getVerticesFast();
localTri.verts[0] = vertices[vref0];
localTri.verts[1] = vertices[vref1];
localTri.verts[2] = vertices[vref2];
}
PX_INLINE float computeSweepData(const PxTriangleMeshGeometry& triMeshGeom, /*const Cm::FastVertex2ShapeScaling& scaling,*/ PxVec3& sweepOrigin, PxVec3& sweepExtents, PxVec3& sweepDir, float distance)
{
PX_ASSERT(!Cm::isEmpty(sweepOrigin, sweepExtents));
const PxVec3 endPt = sweepOrigin + sweepDir*distance;
PX_ASSERT(!Cm::isEmpty(endPt, sweepExtents));
const Cm::FastVertex2ShapeScaling meshScaling(triMeshGeom.scale.getInverse()); // shape to vertex transform
const PxMat33& vertex2ShapeSkew = meshScaling.getVertex2ShapeSkew();
const PxVec3 originBoundsCenter = vertex2ShapeSkew * sweepOrigin;
const PxVec3 originBoundsExtents = Cm::basisExtent(vertex2ShapeSkew.column0, vertex2ShapeSkew.column1, vertex2ShapeSkew.column2, sweepExtents);
sweepOrigin = originBoundsCenter;
sweepExtents = originBoundsExtents;
sweepDir = (vertex2ShapeSkew * endPt) - originBoundsCenter;
return sweepDir.normalizeSafe();
}
PX_FORCE_INLINE const Gu::TriangleMesh* _getMeshData(const PxTriangleMeshGeometry& meshGeom)
{
return static_cast<const Gu::TriangleMesh*>(meshGeom.triangleMesh);
}
}
#endif

View File

@@ -0,0 +1,166 @@
// 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 "GuTriangleMesh.h"
#include "GuTriangleMeshBV4.h"
#include "geometry/PxGeometryInternal.h"
using namespace physx;
using namespace Gu;
namespace physx
{
// PT: temporary for Kit
BV4TriangleMesh::BV4TriangleMesh(const PxTriangleMeshInternalData& data) : TriangleMesh(data)
{
mMeshInterface.setNbTriangles(getNbTrianglesFast());
if(has16BitIndices())
mMeshInterface.setPointers(NULL, const_cast<IndTri16*>(reinterpret_cast<const IndTri16*>(getTrianglesFast())), getVerticesFast());
else
mMeshInterface.setPointers(const_cast<IndTri32*>(reinterpret_cast<const IndTri32*>(getTrianglesFast())), NULL, getVerticesFast());
mBV4Tree.mMeshInterface = &mMeshInterface;
mBV4Tree.mLocalBounds.mCenter = data.mAABB_Center;
mBV4Tree.mLocalBounds.mExtentsMagnitude = data.mAABB_Extents.magnitude();
mBV4Tree.mNbNodes = data.mNbNodes;
mBV4Tree.mNodes = data.mNodes;
mBV4Tree.mInitData = data.mInitData;
mBV4Tree.mCenterOrMinCoeff = data.mCenterOrMinCoeff;
mBV4Tree.mExtentsOrMaxCoeff = data.mExtentsOrMaxCoeff;
mBV4Tree.mQuantized = data.mQuantized;
mBV4Tree.mUserAllocated = true;
}
bool BV4TriangleMesh::getInternalData(PxTriangleMeshInternalData& data, bool takeOwnership) const
{
data.mNbVertices = mNbVertices;
data.mNbTriangles = mNbTriangles;
data.mVertices = mVertices;
data.mTriangles = mTriangles;
data.mFaceRemap = mFaceRemap;
data.mAABB_Center = mAABB.mCenter;
data.mAABB_Extents = mAABB.mExtents;
data.mGeomEpsilon = mGeomEpsilon;
data.mFlags = mFlags;
data.mNbNodes = mBV4Tree.mNbNodes;
data.mNodeSize = mBV4Tree.mQuantized ? sizeof(BVDataPackedQ) : sizeof(BVDataPackedNQ);
data.mNodes = mBV4Tree.mNodes;
data.mInitData = mBV4Tree.mInitData;
data.mCenterOrMinCoeff = mBV4Tree.mCenterOrMinCoeff;
data.mExtentsOrMaxCoeff = mBV4Tree.mExtentsOrMaxCoeff;
data.mQuantized = mBV4Tree.mQuantized;
if(takeOwnership)
{
const_cast<BV4TriangleMesh*>(this)->setBaseFlag(PxBaseFlag::eOWNS_MEMORY, false);
const_cast<BV4TriangleMesh*>(this)->mBV4Tree.mUserAllocated = true;
}
return true;
}
bool PxGetTriangleMeshInternalData(PxTriangleMeshInternalData& data, const PxTriangleMesh& mesh, bool takeOwnership)
{
return static_cast<const TriangleMesh&>(mesh).getInternalData(data, takeOwnership);
}
//~ PT: temporary for Kit
BV4TriangleMesh::BV4TriangleMesh(MeshFactory* factory, TriangleMeshData& d) : TriangleMesh(factory, d)
{
PX_ASSERT(d.mType==PxMeshMidPhase::eBVH34);
BV4TriangleData& bv4Data = static_cast<BV4TriangleData&>(d);
mMeshInterface = bv4Data.mMeshInterface;
mBV4Tree = bv4Data.mBV4Tree;
mBV4Tree.mMeshInterface = &mMeshInterface;
}
TriangleMesh* BV4TriangleMesh::createObject(PxU8*& address, PxDeserializationContext& context)
{
BV4TriangleMesh* obj = PX_PLACEMENT_NEW(address, BV4TriangleMesh(PxBaseFlag::eIS_RELEASABLE));
address += sizeof(BV4TriangleMesh);
obj->importExtraData(context);
return obj;
}
void BV4TriangleMesh::exportExtraData(PxSerializationContext& stream)
{
mBV4Tree.exportExtraData(stream);
TriangleMesh::exportExtraData(stream);
}
void BV4TriangleMesh::importExtraData(PxDeserializationContext& context)
{
mBV4Tree.importExtraData(context);
TriangleMesh::importExtraData(context);
if(has16BitIndices())
mMeshInterface.setPointers(NULL, const_cast<IndTri16*>(reinterpret_cast<const IndTri16*>(getTrianglesFast())), getVerticesFast());
else
mMeshInterface.setPointers(const_cast<IndTri32*>(reinterpret_cast<const IndTri32*>(getTrianglesFast())), NULL, getVerticesFast());
mBV4Tree.mMeshInterface = &mMeshInterface;
}
PxVec3 * BV4TriangleMesh::getVerticesForModification()
{
return const_cast<PxVec3*>(getVertices());
}
PxBounds3 BV4TriangleMesh::refitBVH()
{
PxBounds3 newBounds;
const float gBoxEpsilon = 2e-4f;
if(mBV4Tree.refit(newBounds, gBoxEpsilon))
{
mAABB.setMinMax(newBounds.minimum, newBounds.maximum);
}
else
{
newBounds = PxBounds3::centerExtents(mAABB.mCenter, mAABB.mExtents);
PxGetFoundation().error(PxErrorCode::eINVALID_OPERATION, PX_FL, "BVH34 trees: refit operation only available on non-quantized trees.\n");
}
// PT: copied from RTreeTriangleMesh::refitBVH()
// reset edge flags and remember we did that using a mesh flag (optimization)
if(!mBV4Tree.mIsEdgeSet)
{
mBV4Tree.mIsEdgeSet = true;
setAllEdgesActive();
}
return newBounds;
}
} // namespace physx

View File

@@ -0,0 +1,80 @@
// 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_TRIANGLEMESH_BV4_H
#define GU_TRIANGLEMESH_BV4_H
#include "GuTriangleMesh.h"
namespace physx
{
namespace Gu
{
class MeshFactory;
#if PX_VC
#pragma warning(push)
#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value.
#endif
class BV4TriangleMesh : public TriangleMesh
{
public:
virtual const char* getConcreteTypeName() const { return "PxBVH34TriangleMesh"; }
// PX_SERIALIZATION
BV4TriangleMesh(PxBaseFlags baseFlags) : TriangleMesh(baseFlags), mMeshInterface(PxEmpty), mBV4Tree(PxEmpty) {}
PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext& ctx);
void importExtraData(PxDeserializationContext&);
PX_PHYSX_COMMON_API static TriangleMesh* createObject(PxU8*& address, PxDeserializationContext& context);
//~PX_SERIALIZATION
BV4TriangleMesh(MeshFactory* factory, TriangleMeshData& data);
virtual ~BV4TriangleMesh(){}
virtual PxMeshMidPhase::Enum getMidphaseID() const { return PxMeshMidPhase::eBVH34; }
virtual PxVec3* getVerticesForModification();
virtual PxBounds3 refitBVH();
PX_PHYSX_COMMON_API BV4TriangleMesh(const PxTriangleMeshInternalData& data);
virtual bool getInternalData(PxTriangleMeshInternalData&, bool) const;
PX_FORCE_INLINE const Gu::BV4Tree& getBV4Tree() const { return mBV4Tree; }
private:
Gu::SourceMesh mMeshInterface;
Gu::BV4Tree mBV4Tree;
};
#if PX_VC
#pragma warning(pop)
#endif
} // namespace Gu
}
#endif

View File

@@ -0,0 +1,138 @@
// 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 "GuTriangleMesh.h"
#include "GuTriangleMeshRTree.h"
using namespace physx;
namespace physx
{
Gu::RTreeTriangleMesh::RTreeTriangleMesh(MeshFactory* factory, TriangleMeshData& d) : TriangleMesh(factory, d)
{
PX_ASSERT(d.mType==PxMeshMidPhase::eBVH33);
RTreeTriangleData& rtreeData = static_cast<RTreeTriangleData&>(d);
mRTree = rtreeData.mRTree;
rtreeData.mRTree.mPages = NULL;
}
Gu::TriangleMesh* Gu::RTreeTriangleMesh::createObject(PxU8*& address, PxDeserializationContext& context)
{
RTreeTriangleMesh* obj = PX_PLACEMENT_NEW(address, RTreeTriangleMesh(PxBaseFlag::eIS_RELEASABLE));
address += sizeof(RTreeTriangleMesh);
obj->importExtraData(context);
return obj;
}
void Gu::RTreeTriangleMesh::exportExtraData(PxSerializationContext& stream)
{
mRTree.exportExtraData(stream);
TriangleMesh::exportExtraData(stream);
}
void Gu::RTreeTriangleMesh::importExtraData(PxDeserializationContext& context)
{
mRTree.importExtraData(context);
TriangleMesh::importExtraData(context);
}
PxVec3 * Gu::RTreeTriangleMesh::getVerticesForModification()
{
return const_cast<PxVec3*>(getVertices());
}
template<typename IndexType>
struct RefitCallback : Gu::RTree::CallbackRefit
{
const PxVec3* newPositions;
const IndexType* indices;
RefitCallback(const PxVec3* aNewPositions, const IndexType* aIndices) : newPositions(aNewPositions), indices(aIndices) {}
PX_FORCE_INLINE ~RefitCallback() {}
virtual void recomputeBounds(PxU32 index, aos::Vec3V& aMn, aos::Vec3V& aMx)
{
using namespace aos;
// Each leaf box has a set of triangles
Gu::LeafTriangles currentLeaf; currentLeaf.Data = index;
PxU32 nbTris = currentLeaf.GetNbTriangles();
PxU32 baseTri = currentLeaf.GetTriangleIndex();
PX_ASSERT(nbTris > 0);
const IndexType* vInds = indices + 3 * baseTri;
Vec3V vPos = V3LoadU(newPositions[vInds[0]]);
Vec3V mn = vPos, mx = vPos;
//PxBounds3 result(newPositions[vInds[0]], newPositions[vInds[0]]);
vPos = V3LoadU(newPositions[vInds[1]]);
mn = V3Min(mn, vPos); mx = V3Max(mx, vPos);
vPos = V3LoadU(newPositions[vInds[2]]);
mn = V3Min(mn, vPos); mx = V3Max(mx, vPos);
for (PxU32 i = 1; i < nbTris; i++)
{
const IndexType* vInds1 = indices + 3 * (baseTri + i);
vPos = V3LoadU(newPositions[vInds1[0]]);
mn = V3Min(mn, vPos); mx = V3Max(mx, vPos);
vPos = V3LoadU(newPositions[vInds1[1]]);
mn = V3Min(mn, vPos); mx = V3Max(mx, vPos);
vPos = V3LoadU(newPositions[vInds1[2]]);
mn = V3Min(mn, vPos); mx = V3Max(mx, vPos);
}
aMn = mn;
aMx = mx;
}
};
PxBounds3 Gu::RTreeTriangleMesh::refitBVH()
{
PxBounds3 meshBounds;
if (has16BitIndices())
{
RefitCallback<PxU16> cb(mVertices, static_cast<const PxU16*>(mTriangles));
mRTree.refitAllStaticTree(cb, &meshBounds);
}
else
{
RefitCallback<PxU32> cb(mVertices, static_cast<const PxU32*>(mTriangles));
mRTree.refitAllStaticTree(cb, &meshBounds);
}
// reset edge flags and remember we did that using a mesh flag (optimization)
if ((mRTree.mFlags & RTree::IS_EDGE_SET) == 0)
{
mRTree.mFlags |= RTree::IS_EDGE_SET;
setAllEdgesActive();
}
mAABB = meshBounds;
return meshBounds;
}
} // namespace physx

View File

@@ -0,0 +1,76 @@
// 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_TRIANGLEMESH_RTREE_H
#define GU_TRIANGLEMESH_RTREE_H
#include "GuTriangleMesh.h"
namespace physx
{
namespace Gu
{
class MeshFactory;
#if PX_VC
#pragma warning(push)
#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value.
#endif
class RTreeTriangleMesh : public TriangleMesh
{
public:
virtual const char* getConcreteTypeName() const { return "PxBVH33TriangleMesh"; }
// PX_SERIALIZATION
RTreeTriangleMesh(PxBaseFlags baseFlags) : TriangleMesh(baseFlags), mRTree(PxEmpty) {}
PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext& ctx);
void importExtraData(PxDeserializationContext&);
PX_PHYSX_COMMON_API static TriangleMesh* createObject(PxU8*& address, PxDeserializationContext& context);
//~PX_SERIALIZATION
RTreeTriangleMesh(MeshFactory* factory, TriangleMeshData& data);
virtual ~RTreeTriangleMesh(){}
virtual PxMeshMidPhase::Enum getMidphaseID() const { return PxMeshMidPhase::eBVH33; }
virtual PxVec3* getVerticesForModification();
virtual PxBounds3 refitBVH();
PX_FORCE_INLINE const Gu::RTree& getRTree() const { return mRTree; }
private:
Gu::RTree mRTree;
};
#if PX_VC
#pragma warning(pop)
#endif
} // namespace Gu
}
#endif

View File

@@ -0,0 +1,121 @@
// 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_TRIANGLE_REFINEMENT_H
#define GU_TRIANGLE_REFINEMENT_H
// Indexing scheme for sub-triangles in recursive triangle subdivision scheme
// Each subdivision step creates 4 triangles out of the previous one:
// - First triangle is the "a corner"
// - Second triangle is the "b corner"
// - Third triangle is the "c corner"
// - Fourth triangle is the center
#include "foundation/PxVec3.h"
namespace physx {
namespace Gu {
PX_FORCE_INLINE PX_CUDA_CALLABLE void getSubTriangle4(int id, PxVec3& baryA, PxVec3& baryB, PxVec3& baryC)
{
PxVec3 ab = 0.5f * (baryA + baryB);
PxVec3 bc = 0.5f * (baryB + baryC);
PxVec3 ca = 0.5f * (baryC + baryA);
switch (id)
{
case 0:
baryB = ab;
baryC = ca;
break;
case 1:
baryA = ab;
baryC = bc;
break;
case 2:
baryA = ca;
baryB = bc;
break;
case 3:
baryA = ab;
baryB = bc;
baryC = ca;
break;
default:
PX_ASSERT(false);
}
}
PX_FORCE_INLINE PX_CUDA_CALLABLE void getSubTriangle(PxVec3& a, PxVec3& b, PxVec3& c, PxU32 id, PxU32 numSubdivisionSteps)
{
for (PxU32 i = 0; i < numSubdivisionSteps; ++i)
{
PxU32 j = numSubdivisionSteps - i - 1;
PxU32 local = id >> (2 * j);
local = local & 3;
getSubTriangle4(local, a, b, c);
}
}
PX_FORCE_INLINE PX_CUDA_CALLABLE PxU32 encodeSubdivisionId(PxU32 subdivisionLevel, PxU32 subTriangleIndex)
{
//4 bits for subdivision level, the rest for the index
const PxU32 shift = 30 - 4;
return (subdivisionLevel << shift) | subTriangleIndex;
}
PX_FORCE_INLINE PX_CUDA_CALLABLE void decodeSubdivisionId(PxU32 encodedId, PxU32& subdivisionLevel, PxU32& subTriangleIndex)
{
const PxU32 shift = 30 - 4;
subdivisionLevel = encodedId >> shift;
subTriangleIndex = encodedId & ((1 << shift) - 1);
}
//Increases the subdivision level by one and sets the index to the specified sub triangle that got created out of the previous triangle due to one additional level of subdivision
PX_FORCE_INLINE PX_CUDA_CALLABLE PxU32 elevateSubdivisionId(PxU32 encodedId, PxU32 subTriangle = 0)
{
PX_ASSERT(subTriangle >= 0 && subTriangle < 4);
PxU32 subdivisionLevel;
PxU32 subTriangleIndex;
decodeSubdivisionId(encodedId, subdivisionLevel, subTriangleIndex);
return encodeSubdivisionId(subdivisionLevel + 1, 4 * subTriangleIndex + subTriangle);
}
PX_FORCE_INLINE PX_CUDA_CALLABLE void getSubTriangleEncoded(PxVec3& a, PxVec3& b, PxVec3& c, PxU32 encodedSubIndex)
{
PxU32 subdivisionLevel;
PxU32 subTriangleIndex;
decodeSubdivisionId(encodedSubIndex, subdivisionLevel, subTriangleIndex);
getSubTriangle(a, b, c, subTriangleIndex, subdivisionLevel);
}
} // namespace Gu
} // namespace physx
#endif // GU_TRIANGLE_REFINEMENT_H