Projekt_Grafika/dependencies/physx-4.1/source/physxcooking/src/BVHStructureBuilder.cpp

216 lines
6.6 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-2019 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/PxIO.h"
#include "BVHStructureBuilder.h"
#include "GuAABBTreeBuild.h"
#include "GuSerialize.h"
#include "GuBVHStructure.h"
using namespace physx;
using namespace Gu;
// A.B. move this to some common place?
#define NB_OBJECTS_PER_NODE 4
// PT: TODO: - check that this is compatible with Gu::computeBounds(..., SQ_PRUNER_INFLATION, ...)
// PT: TODO: - refactor with "inflateBounds" in GuBounds.cpp if possible
// PT: TODO: - use SQ_PRUNER_INFLATION instead of hardcoding "0.01f"
// A.B. move to common place
PX_FORCE_INLINE void inflateBounds(PxBounds3& dst, const PxBounds3& src)
{
using namespace physx::shdfnd::aos;
const Vec4V minV = V4LoadU(&src.minimum.x);
const Vec4V maxV = V4LoadU(&src.maximum.x);
const Vec4V eV = V4Scale(V4Sub(maxV, minV), FLoad(0.5f * 0.01f));
V4StoreU(V4Sub(minV, eV), &dst.minimum.x);
PX_ALIGN(16, PxVec4) max4;
V4StoreA(V4Add(maxV, eV), &max4.x);
dst.maximum = PxVec3(max4.x, max4.y, max4.z);
}
void flatten(const NodeAllocator& nodeAllocator, BVHNode* dest)
{
// PT: gathers all build nodes allocated so far and flatten them to a linear destination array of smaller runtime nodes
PxU32 offset = 0;
const PxU32 nbSlabs = nodeAllocator.mSlabs.size();
for(PxU32 s=0;s<nbSlabs;s++)
{
const NodeAllocator::Slab& currentSlab = nodeAllocator.mSlabs[s];
AABBTreeBuildNode* pool = currentSlab.mPool;
for(PxU32 i=0;i<currentSlab.mNbUsedNodes;i++)
{
dest[offset].mBV = pool[i].mBV;
if(pool[i].isLeaf())
{
const PxU32 index = pool[i].mNodeIndex;
const PxU32 nbPrims = pool[i].getNbPrimitives();
PX_ASSERT(nbPrims<=16);
dest[offset].mData = (index<<5)|((nbPrims&15)<<1)|1;
}
else
{
PX_ASSERT(pool[i].mPos);
PxU32 localNodeIndex = 0xffffffff;
PxU32 nodeBase = 0;
for(PxU32 j=0;j<nbSlabs;j++)
{
if(pool[i].mPos>= nodeAllocator.mSlabs[j].mPool && pool[i].mPos < nodeAllocator.mSlabs[j].mPool + nodeAllocator.mSlabs[j].mNbUsedNodes)
{
localNodeIndex = PxU32(pool[i].mPos - nodeAllocator.mSlabs[j].mPool);
break;
}
nodeBase += nodeAllocator.mSlabs[j].mNbUsedNodes;
}
const PxU32 nodeIndex = nodeBase + localNodeIndex;
dest[offset].mData = nodeIndex<<1;
}
offset++;
}
}
}
BVHStructureBuilder::BVHStructureBuilder():
mBounds(NULL),
mNumVolumes(0),
mNumNodes(0),
mNodes(NULL),
mIndices(NULL)
{
}
BVHStructureBuilder::~BVHStructureBuilder()
{
PX_FREE_AND_RESET(mBounds);
PX_FREE_AND_RESET(mNodes);
PX_FREE_AND_RESET(mIndices);
}
bool BVHStructureBuilder::loadFromDesc(const PxBVHStructureDesc& desc)
{
PX_ASSERT(desc.isValid());
const PxU32 numPrimitives = desc.bounds.count;
// allocate one more for safe SIMD vec4 load
mBounds = reinterpret_cast<PxBounds3*>(PX_ALLOC((numPrimitives + 1) * sizeof(PxBounds3), "PxBounds3"));
const PxU8* sB = reinterpret_cast<const PxU8*>(desc.bounds.data);
for(PxU32 i = 0; i < numPrimitives; i++)
{
inflateBounds(mBounds[i], *reinterpret_cast<const PxBounds3*>(sB));
sB += desc.bounds.stride;
}
mNumVolumes = numPrimitives;
// build the BVH
AABBTreeBuildParams params;
params.mNbPrimitives = desc.bounds.count;
params.mAABBArray = mBounds;
params.mLimit = NB_OBJECTS_PER_NODE;
BuildStats stats;
NodeAllocator nodeAllocator;
const bool buildStatus = buildAABBTree(params, nodeAllocator, stats, mIndices);
PX_UNUSED(buildStatus);
PX_ASSERT(buildStatus);
// store the computed hierarchy
mNumNodes = stats.getCount();
mNodes = reinterpret_cast<BVHNode*>(PX_ALLOC(sizeof(BVHNode)*mNumNodes, "AABB tree nodes"));
PX_ASSERT(mNumNodes==nodeAllocator.mTotalNbNodes);
// store the results into BVHNode list
flatten(nodeAllocator, mNodes);
nodeAllocator.release();
return true;
}
// A.B. move to load code
#define PX_BVH_STRUCTURE_VERSION 1
bool BVHStructureBuilder::save(PxOutputStream& stream, bool endian) const
{
// write header
if(!writeHeader('B', 'V', 'H', 'S', PX_BVH_STRUCTURE_VERSION, endian, stream))
return false;
// write mData members
writeDword(mNumVolumes, endian, stream);
writeDword(mNumNodes, endian, stream);
// write indices and bounds
for(PxU32 i = 0; i < mNumVolumes; i++)
{
writeDword(mIndices[i], endian, stream);
}
for(PxU32 i = 0; i < mNumVolumes; i++)
{
writeFloatBuffer(&mBounds[i].minimum.x, 3, endian, stream);
writeFloatBuffer(&mBounds[i].maximum.x, 3, endian, stream);
}
// write nodes
for(PxU32 i = 0; i < mNumNodes; i++)
{
writeDword(mNodes[i].mData, endian, stream);
writeFloatBuffer(&mNodes[i].mBV.minimum.x, 3, endian, stream);
writeFloatBuffer(&mNodes[i].mBV.maximum.x, 3, endian, stream);
}
return true;
}
void BVHStructureBuilder::moveData(Gu::BVHStructureData& bvhData)
{
bvhData.mBounds = mBounds;
bvhData.mIndices = mIndices;
bvhData.mNodes = mNodes;
bvhData.mNumNodes = mNumNodes;
bvhData.mNumVolumes = mNumVolumes;
// set pointers to NULL so we do not release the memory that has been passed to physics create
mBounds = NULL;
mIndices = NULL;
mNodes = NULL;
mNumNodes = 0;
mNumVolumes = 0;
}