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