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