546 lines
18 KiB
C++
546 lines
18 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/PxErrorCallback.h"
|
|
#include "common/PxPhysicsInsertionCallback.h"
|
|
#include "geometry/PxSimpleTriangleMesh.h"
|
|
#include "geometry/PxHeightFieldDesc.h"
|
|
#include "cooking/PxTriangleMeshDesc.h"
|
|
#include "cooking/PxConvexMeshDesc.h"
|
|
#include "cooking/PxCooking.h"
|
|
#include "PxPhysXConfig.h"
|
|
|
|
#include "PsFoundation.h"
|
|
#include "PsUtilities.h"
|
|
#include "PsFPU.h"
|
|
#include "CmPhysXCommon.h"
|
|
#include "CmIO.h"
|
|
#include "CmUtils.h"
|
|
#include "Cooking.h"
|
|
#include "mesh/TriangleMeshBuilder.h"
|
|
#include "GuConvexMesh.h"
|
|
#include "GuHeightField.h"
|
|
#include "ConvexMeshBuilder.h"
|
|
#include "BVHStructureBuilder.h"
|
|
#include "QuickHullConvexHullLib.h"
|
|
#include "HeightFieldCooking.h"
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
using namespace physx;
|
|
using namespace Gu;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Cooking::setParams(const PxCookingParams& params)
|
|
{
|
|
mParams = params;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const PxCookingParams& Cooking::getParams() const
|
|
{
|
|
return mParams;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Cooking::platformMismatch() const
|
|
{
|
|
// Get current endianness (the one for the platform where cooking is performed)
|
|
const PxI8 currentEndian = Ps::littleEndian();
|
|
|
|
const bool mismatch = currentEndian!=1; // The files must be little endian - we don't have big endian platforms anymore.
|
|
return mismatch;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Cooking::release()
|
|
{
|
|
delete this;
|
|
|
|
Ps::Foundation::decRefCount();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool Cooking::validateTriangleMesh(const PxTriangleMeshDesc& desc) const
|
|
{
|
|
// cooking code does lots of float bitwise reinterpretation that generates exceptions
|
|
PX_FPU_GUARD;
|
|
|
|
if(!desc.isValid())
|
|
{
|
|
Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::validateTriangleMesh: user-provided triangle mesh descriptor is invalid!");
|
|
return false;
|
|
}
|
|
|
|
// PT: validation code doesn't look at midphase data, so ideally we wouldn't build the midphase structure at all here.
|
|
BV4TriangleMeshBuilder builder(mParams);
|
|
return builder.loadFromDesc(desc, NULL, true /*doValidate*/);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Cooking::cookTriangleMesh(TriangleMeshBuilder& builder, const PxTriangleMeshDesc& desc, PxOutputStream& stream, PxTriangleMeshCookingResult::Enum* condition) const
|
|
{
|
|
// cooking code does lots of float bitwise reinterpretation that generates exceptions
|
|
PX_FPU_GUARD;
|
|
|
|
if (condition)
|
|
*condition = PxTriangleMeshCookingResult::eSUCCESS;
|
|
if(!builder.loadFromDesc(desc, condition, false))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
builder.save(stream, platformMismatch(), mParams);
|
|
return true;
|
|
}
|
|
|
|
bool Cooking::cookTriangleMesh(const PxTriangleMeshDesc& desc, PxOutputStream& stream, PxTriangleMeshCookingResult::Enum* condition) const
|
|
{
|
|
if(mParams.midphaseDesc.getType() == PxMeshMidPhase::eBVH33)
|
|
{
|
|
RTreeTriangleMeshBuilder builder(mParams);
|
|
return cookTriangleMesh(builder, desc, stream, condition);
|
|
}
|
|
else
|
|
{
|
|
BV4TriangleMeshBuilder builder(mParams);
|
|
return cookTriangleMesh(builder, desc, stream, condition);
|
|
}
|
|
}
|
|
|
|
PxTriangleMesh* Cooking::createTriangleMesh(TriangleMeshBuilder& builder, const PxTriangleMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxTriangleMeshCookingResult::Enum* condition) const
|
|
{
|
|
// cooking code does lots of float bitwise reinterpretation that generates exceptions
|
|
PX_FPU_GUARD;
|
|
|
|
if (condition)
|
|
*condition = PxTriangleMeshCookingResult::eSUCCESS;
|
|
if(!builder.loadFromDesc(desc, condition, false))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// check if the indices can be moved from 32bits to 16bits
|
|
if(!(mParams.meshPreprocessParams & PxMeshPreprocessingFlag::eFORCE_32BIT_INDICES))
|
|
builder.checkMeshIndicesSize();
|
|
|
|
PxConcreteType::Enum type;
|
|
if(builder.getMidphaseID()==PxMeshMidPhase::eBVH33)
|
|
type = PxConcreteType::eTRIANGLE_MESH_BVH33;
|
|
else
|
|
type = PxConcreteType::eTRIANGLE_MESH_BVH34;
|
|
|
|
return static_cast<PxTriangleMesh*>(insertionCallback.buildObjectFromData(type, &builder.getMeshData()));
|
|
}
|
|
|
|
PxTriangleMesh* Cooking::createTriangleMesh(const PxTriangleMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxTriangleMeshCookingResult::Enum* condition) const
|
|
{
|
|
if(mParams.midphaseDesc.getType() == PxMeshMidPhase::eBVH33)
|
|
{
|
|
RTreeTriangleMeshBuilder builder(mParams);
|
|
return createTriangleMesh(builder, desc, insertionCallback, condition);
|
|
}
|
|
else
|
|
{
|
|
BV4TriangleMeshBuilder builder(mParams);
|
|
return createTriangleMesh(builder, desc, insertionCallback, condition);
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// cook convex mesh from given desc, internal function to be shared between create/cook convex mesh
|
|
bool Cooking::cookConvexMeshInternal(const PxConvexMeshDesc& desc_, ConvexMeshBuilder& meshBuilder, ConvexHullLib* hullLib,
|
|
PxConvexMeshCookingResult::Enum* condition) const
|
|
{
|
|
if (condition)
|
|
*condition = PxConvexMeshCookingResult::eFAILURE;
|
|
|
|
if (!desc_.isValid())
|
|
{
|
|
Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::cookConvexMesh: user-provided convex mesh descriptor is invalid!");
|
|
return false;
|
|
}
|
|
|
|
if (mParams.areaTestEpsilon <= 0.0f)
|
|
{
|
|
Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::cookConvexMesh: provided cooking parameter areaTestEpsilon is invalid!");
|
|
return false;
|
|
}
|
|
|
|
if(mParams.planeTolerance < 0.0f)
|
|
{
|
|
Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::cookConvexMesh: provided cooking parameter planeTolerance is invalid!");
|
|
return false;
|
|
}
|
|
|
|
PxConvexMeshDesc desc = desc_;
|
|
bool polygonsLimitReached = false;
|
|
|
|
// the convex will be cooked from provided points
|
|
if (desc_.flags & PxConvexFlag::eCOMPUTE_CONVEX)
|
|
{
|
|
PX_ASSERT(hullLib);
|
|
|
|
// clean up the indices information, it could have been set by accident
|
|
desc.flags &= ~PxConvexFlag::e16_BIT_INDICES;
|
|
desc.indices.count = 0;
|
|
desc.indices.data = NULL;
|
|
desc.indices.stride = 0;
|
|
desc.polygons.count = 0;
|
|
desc.polygons.data = NULL;
|
|
desc.polygons.stride = 0;
|
|
|
|
PxConvexMeshCookingResult::Enum res = hullLib->createConvexHull();
|
|
if (res == PxConvexMeshCookingResult::eSUCCESS || res == PxConvexMeshCookingResult::ePOLYGONS_LIMIT_REACHED)
|
|
{
|
|
if (res == PxConvexMeshCookingResult::ePOLYGONS_LIMIT_REACHED)
|
|
polygonsLimitReached = true;
|
|
|
|
hullLib->fillConvexMeshDesc(desc);
|
|
}
|
|
else
|
|
{
|
|
if (res == PxConvexMeshCookingResult::eZERO_AREA_TEST_FAILED)
|
|
{
|
|
*condition = PxConvexMeshCookingResult::eZERO_AREA_TEST_FAILED;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (desc.points.count >= 256)
|
|
{
|
|
Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Cooking::cookConvexMesh: user-provided hull must have less than 256 vertices!");
|
|
return false;
|
|
}
|
|
|
|
if (!meshBuilder.build(desc, mParams.gaussMapLimit, false, hullLib))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (condition)
|
|
{
|
|
*condition = polygonsLimitReached ? PxConvexMeshCookingResult::ePOLYGONS_LIMIT_REACHED : PxConvexMeshCookingResult::eSUCCESS;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// cook convex mesh from given desc, save the results into stream
|
|
bool Cooking::cookConvexMesh(const PxConvexMeshDesc& desc_, PxOutputStream& stream, PxConvexMeshCookingResult::Enum* condition) const
|
|
{
|
|
PX_FPU_GUARD;
|
|
// choose cooking library if needed
|
|
ConvexHullLib* hullLib = NULL;
|
|
PxConvexMeshDesc desc = desc_;
|
|
|
|
if(desc_.flags & PxConvexFlag::eCOMPUTE_CONVEX)
|
|
{
|
|
const PxU16 gpuMaxVertsLimit = 64;
|
|
|
|
// GRB supports 64 verts max
|
|
if(desc_.flags & PxConvexFlag::eGPU_COMPATIBLE)
|
|
{
|
|
desc.vertexLimit = PxMin(desc.vertexLimit, gpuMaxVertsLimit);
|
|
}
|
|
|
|
hullLib = PX_NEW(QuickHullConvexHullLib) (desc, mParams);
|
|
}
|
|
|
|
ConvexMeshBuilder meshBuilder(mParams.buildGPUData);
|
|
if(!cookConvexMeshInternal(desc,meshBuilder,hullLib , condition))
|
|
{
|
|
if(hullLib)
|
|
PX_DELETE(hullLib);
|
|
return false;
|
|
}
|
|
|
|
// save the cooked results into stream
|
|
if(!meshBuilder.save(stream, platformMismatch()))
|
|
{
|
|
if (condition)
|
|
{
|
|
*condition = PxConvexMeshCookingResult::eFAILURE;
|
|
}
|
|
if(hullLib)
|
|
PX_DELETE(hullLib);
|
|
return false;
|
|
}
|
|
|
|
if(hullLib)
|
|
PX_DELETE(hullLib);
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// cook convex mesh from given desc, copy the results into internal convex mesh
|
|
// and insert the mesh into PxPhysics
|
|
PxConvexMesh* Cooking::createConvexMesh(const PxConvexMeshDesc& desc_, PxPhysicsInsertionCallback& insertionCallback, PxConvexMeshCookingResult::Enum* condition) const
|
|
{
|
|
PX_FPU_GUARD;
|
|
// choose cooking library if needed
|
|
ConvexHullLib* hullLib = NULL;
|
|
PxConvexMeshDesc desc = desc_;
|
|
|
|
if(desc.flags & PxConvexFlag::eCOMPUTE_CONVEX)
|
|
{
|
|
const PxU16 gpuMaxVertsLimit = 64;
|
|
|
|
// GRB supports 64 verts max
|
|
if(desc_.flags & PxConvexFlag::eGPU_COMPATIBLE)
|
|
{
|
|
desc.vertexLimit = PxMin(desc.vertexLimit, gpuMaxVertsLimit);
|
|
}
|
|
|
|
hullLib = PX_NEW(QuickHullConvexHullLib) (desc, mParams);
|
|
}
|
|
|
|
// cook the mesh
|
|
ConvexMeshBuilder meshBuilder(mParams.buildGPUData);
|
|
if (!cookConvexMeshInternal(desc, meshBuilder, hullLib, condition))
|
|
{
|
|
if(hullLib)
|
|
PX_DELETE(hullLib);
|
|
return NULL;
|
|
}
|
|
|
|
// copy the constructed data into the new mesh
|
|
|
|
Gu::ConvexHullInitData meshData;
|
|
meshBuilder.copy(meshData);
|
|
|
|
// insert into physics
|
|
PxConvexMesh* convexMesh = static_cast<PxConvexMesh*>(insertionCallback.buildObjectFromData(PxConcreteType::eCONVEX_MESH, &meshData));
|
|
if (!convexMesh)
|
|
{
|
|
if(condition)
|
|
*condition = PxConvexMeshCookingResult::eFAILURE;
|
|
if (hullLib)
|
|
PX_DELETE(hullLib);
|
|
return NULL;
|
|
}
|
|
|
|
if(hullLib)
|
|
PX_DELETE(hullLib);
|
|
return convexMesh;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Cooking::validateConvexMesh(const PxConvexMeshDesc& desc) const
|
|
{
|
|
ConvexMeshBuilder mesh(mParams.buildGPUData);
|
|
return mesh.build(desc, mParams.gaussMapLimit, true);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Cooking::computeHullPolygons(const PxSimpleTriangleMesh& mesh, PxAllocatorCallback& inCallback,PxU32& nbVerts, PxVec3*& vertices,
|
|
PxU32& nbIndices, PxU32*& indices, PxU32& nbPolygons, PxHullPolygon*& hullPolygons) const
|
|
{
|
|
PxVec3* geometry = reinterpret_cast<PxVec3*>(PxAlloca(sizeof(PxVec3)*mesh.points.count));
|
|
Cooking::gatherStrided(mesh.points.data, geometry, mesh.points.count, sizeof(PxVec3), mesh.points.stride);
|
|
|
|
PxU32* topology = reinterpret_cast<PxU32*>(PxAlloca(sizeof(PxU32)*3*mesh.triangles.count));
|
|
if (mesh.flags & PxMeshFlag::e16_BIT_INDICES)
|
|
{
|
|
// conversion; 16 bit index -> 32 bit index & stride
|
|
PxU32* dest = topology;
|
|
const PxU32* pastLastDest = topology + 3*mesh.triangles.count;
|
|
const PxU8* source = reinterpret_cast<const PxU8*>(mesh.triangles.data);
|
|
while (dest < pastLastDest)
|
|
{
|
|
const PxU16 * trig16 = reinterpret_cast<const PxU16*>(source);
|
|
*dest++ = trig16[0];
|
|
*dest++ = trig16[1];
|
|
*dest++ = trig16[2];
|
|
source += mesh.triangles.stride;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Cooking::gatherStrided(mesh.triangles.data, topology, mesh.triangles.count, sizeof(PxU32) * 3, mesh.triangles.stride);
|
|
}
|
|
|
|
ConvexMeshBuilder meshBuilder(mParams.buildGPUData);
|
|
if(!meshBuilder.computeHullPolygons(mesh.points.count,geometry,mesh.triangles.count,topology,inCallback, nbVerts, vertices,nbIndices,indices,nbPolygons,hullPolygons))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Cooking::cookHeightField(const PxHeightFieldDesc& desc, PxOutputStream& stream) const
|
|
{
|
|
PX_FPU_GUARD;
|
|
|
|
if(!desc.isValid())
|
|
{
|
|
#if PX_CHECKED
|
|
Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::createHeightField: user-provided heightfield descriptor is invalid!");
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
Gu::HeightField hf(NULL);
|
|
|
|
if(!hf.loadFromDesc(desc))
|
|
{
|
|
hf.releaseMemory();
|
|
return false;
|
|
}
|
|
|
|
if (!saveHeightField(hf, stream, platformMismatch()))
|
|
{
|
|
hf.releaseMemory();
|
|
return false;
|
|
}
|
|
|
|
hf.releaseMemory();
|
|
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
PxHeightField* Cooking::createHeightField(const PxHeightFieldDesc& desc, PxPhysicsInsertionCallback& insertionCallback) const
|
|
{
|
|
PX_FPU_GUARD;
|
|
|
|
if(!desc.isValid())
|
|
{
|
|
#if PX_CHECKED
|
|
Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::createHeightField: user-provided heightfield descriptor is invalid!");
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
Gu::HeightField* hf;
|
|
PX_NEW_SERIALIZED(hf, Gu::HeightField)(NULL);
|
|
|
|
if(!hf->loadFromDesc(desc))
|
|
{
|
|
PX_DELETE(hf);
|
|
return NULL;
|
|
}
|
|
|
|
// create heightfield and set the HF data
|
|
Gu::HeightField* heightField = static_cast<Gu::HeightField*>(insertionCallback.buildObjectFromData(PxConcreteType::eHEIGHTFIELD, &hf->mData));
|
|
if(!heightField)
|
|
{
|
|
PX_DELETE(hf);
|
|
return NULL;
|
|
}
|
|
|
|
// copy the Gu::HeightField variables
|
|
heightField->mSampleStride = hf->mSampleStride;
|
|
heightField->mNbSamples = hf->mNbSamples;
|
|
heightField->mMinHeight = hf->mMinHeight;
|
|
heightField->mMaxHeight = hf->mMaxHeight;
|
|
heightField->mModifyCount = hf->mModifyCount;
|
|
|
|
PX_DELETE(hf);
|
|
return heightField;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Cooking::cookBVHStructure(const PxBVHStructureDesc& desc, PxOutputStream& stream) const
|
|
{
|
|
PX_FPU_GUARD;
|
|
|
|
if(!desc.isValid())
|
|
{
|
|
#if PX_CHECKED
|
|
Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::createHeightField: user-provided heightfield descriptor is invalid!");
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
BVHStructureBuilder builder;
|
|
if(!builder.loadFromDesc(desc))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
builder.save(stream, platformMismatch());
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
PxBVHStructure* Cooking::createBVHStructure(const PxBVHStructureDesc& desc, PxPhysicsInsertionCallback& insertionCallback) const
|
|
{
|
|
PX_FPU_GUARD;
|
|
|
|
if(!desc.isValid())
|
|
{
|
|
#if PX_CHECKED
|
|
Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::createHeightField: user-provided heightfield descriptor is invalid!");
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
BVHStructureBuilder builder;
|
|
if(!builder.loadFromDesc(desc))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Gu::BVHStructureData bvhData;
|
|
builder.moveData(bvhData);
|
|
Gu::BVHStructure* bvhStructure = static_cast<Gu::BVHStructure*>(insertionCallback.buildObjectFromData(PxConcreteType::eBVH_STRUCTURE, &bvhData));
|
|
|
|
return bvhStructure;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
PxCooking* PxCreateCooking(PxU32 /*version*/, PxFoundation& foundation, const PxCookingParams& params)
|
|
{
|
|
PX_ASSERT(static_cast<Ps::Foundation*>(&foundation) == &Ps::Foundation::getInstance());
|
|
PX_UNUSED(foundation);
|
|
|
|
Ps::Foundation::incRefCount();
|
|
|
|
return PX_NEW(Cooking)(params);
|
|
}
|
|
|