Files
XCEngine/engine/third_party/physx/source/geomutils/src/mesh/GuBV32.cpp

412 lines
11 KiB
C++

// 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;
}