Revision control
- // 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-2026 NVIDIA Corporation. All rights reserved.
+ // Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
- // Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
+ // SPDX-FileCopyrightText: Copyright (c) 2008-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ // SPDX-License-Identifier: Apache-2.0
#include "GuCooking.h"
#include "GuCookingConvexMeshBuilder.h"
#include "GuCookingQuickHullConvexHullLib.h"
#include "GuConvexMesh.h"
#include "foundation/PxAlloca.h"
#include "foundation/PxFPU.h"
#include "common/PxInsertionCallback.h"
using namespace physx;
using namespace Gu;
///////////////////////////////////////////////////////////////////////////////
PX_IMPLEMENT_OUTPUT_ERROR
///////////////////////////////////////////////////////////////////////////////
// cook convex mesh from given desc, internal function to be shared between create/cook convex mesh
static bool cookConvexMeshInternal(const PxCookingParams& params, const PxConvexMeshDesc& desc_, ConvexMeshBuilder& meshBuilder, ConvexHullLib* hullLib, PxConvexMeshCookingResult::Enum* condition)
{
if(condition)
*condition = PxConvexMeshCookingResult::eFAILURE;
if(!desc_.isValid())
return outputError<PxErrorCode::eINVALID_PARAMETER>(__LINE__, "Cooking::cookConvexMesh: user-provided convex mesh descriptor is invalid!");
if(params.areaTestEpsilon <= 0.0f)
return outputError<PxErrorCode::eINVALID_PARAMETER>(__LINE__, "Cooking::cookConvexMesh: provided cooking parameter areaTestEpsilon is invalid!");
if(params.planeTolerance < 0.0f)
return outputError<PxErrorCode::eINVALID_PARAMETER>(__LINE__, "Cooking::cookConvexMesh: provided cooking parameter planeTolerance is invalid!");
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)
{
*condition = PxConvexMeshCookingResult::eZERO_AREA_TEST_FAILED;
}
return false;
}
}
if(desc.points.count >= 256)
return outputError<PxErrorCode::eINTERNAL_ERROR>(__LINE__, "Cooking::cookConvexMesh: user-provided hull must have less than 256 vertices!");
if(desc.polygons.count >= 256)
return outputError<PxErrorCode::eINTERNAL_ERROR>(__LINE__, "Cooking::cookConvexMesh: user-provided hull must have less than 256 faces!");
if (params.buildGPUData)
{
if (desc.points.count > 64)
return outputError<PxErrorCode::eINTERNAL_ERROR>(__LINE__, "Cooking::cookConvexMesh: GPU-compatible user-provided hull must have less than 65 vertices!");
if (desc.polygons.count > 64)
return outputError<PxErrorCode::eINTERNAL_ERROR>(__LINE__, "Cooking::cookConvexMesh: GPU-compatible user-provided hull must have less than 65 faces!");
}
if(!meshBuilder.build(desc, params.gaussMapLimit, false, hullLib))
return false;
PxConvexMeshCookingResult::Enum result = PxConvexMeshCookingResult::eSUCCESS;
if (polygonsLimitReached)
result = PxConvexMeshCookingResult::ePOLYGONS_LIMIT_REACHED;
// AD: we check this outside of the actual convex cooking because we can still cook a valid convex hull
// but we won't be able to use it on GPU.
if (params.buildGPUData && !meshBuilder.checkExtentRadiusRatio())
{
result = PxConvexMeshCookingResult::eNON_GPU_COMPATIBLE;
outputError<PxErrorCode::eDEBUG_WARNING>(__LINE__, "Cooking::cookConvexMesh: GPU-compatible convex hull could not be built because of oblong shape. Will fall back to CPU collision, particles and deformables will not collide with this mesh!");
}
if(condition)
*condition = result;
return true;
}
static ConvexHullLib* createHullLib(PxConvexMeshDesc& desc, const PxCookingParams& params)
{
if(desc.flags & PxConvexFlag::eCOMPUTE_CONVEX)
{
const PxU16 gpuMaxVertsLimit = 64;
const PxU16 gpuMaxFacesLimit = 64;
// GRB supports 64 verts max
if(params.buildGPUData)
{
desc.vertexLimit = PxMin(desc.vertexLimit, gpuMaxVertsLimit);
desc.polygonLimit = PxMin(desc.polygonLimit, gpuMaxFacesLimit);
}
return PX_NEW(QuickHullConvexHullLib) (desc, params);
}
return NULL;
}
bool immediateCooking::cookConvexMesh(const PxCookingParams& params, const PxConvexMeshDesc& desc_, PxOutputStream& stream, PxConvexMeshCookingResult::Enum* condition)
{
PX_FPU_GUARD;
// choose cooking library if needed
PxConvexMeshDesc desc = desc_;
ConvexHullLib* hullLib = createHullLib(desc, params);
ConvexMeshBuilder meshBuilder(params.buildGPUData);
if(!cookConvexMeshInternal(params, desc, meshBuilder, hullLib, condition))
{
PX_DELETE(hullLib);
return false;
}
// save the cooked results into stream
if(!meshBuilder.save(stream, platformMismatch()))
{
if(condition)
*condition = PxConvexMeshCookingResult::eFAILURE;
PX_DELETE(hullLib);
return false;
}
PX_DELETE(hullLib);
return true;
}
PxConvexMesh* immediateCooking::createConvexMesh(const PxCookingParams& params, const PxConvexMeshDesc& desc_, PxInsertionCallback& insertionCallback, PxConvexMeshCookingResult::Enum* condition)
{
PX_FPU_GUARD;
// choose cooking library if needed
PxConvexMeshDesc desc = desc_;
ConvexHullLib* hullLib = createHullLib(desc, params);
// cook the mesh
ConvexMeshBuilder meshBuilder(params.buildGPUData);
if(!cookConvexMeshInternal(params, desc, meshBuilder, hullLib, condition))
{
PX_DELETE(hullLib);
return NULL;
}
// copy the constructed data into the new mesh
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;
PX_DELETE(hullLib);
return NULL;
}
PX_DELETE(hullLib);
return convexMesh;
}
bool immediateCooking::validateConvexMesh(const PxCookingParams& params, const PxConvexMeshDesc& desc)
{
ConvexMeshBuilder mesh(params.buildGPUData);
return mesh.build(desc, params.gaussMapLimit, true);
}
bool immediateCooking::computeHullPolygons(const PxCookingParams& params, const PxSimpleTriangleMesh& mesh, PxAllocatorCallback& inCallback, PxU32& nbVerts, PxVec3*& vertices,
PxU32& nbIndices, PxU32*& indices, PxU32& nbPolygons, PxHullPolygon*& hullPolygons)
{
PxVec3* geometry = reinterpret_cast<PxVec3*>(PxAlloca(sizeof(PxVec3)*mesh.points.count));
immediateCooking::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
{
immediateCooking::gatherStrided(mesh.triangles.data, topology, mesh.triangles.count, sizeof(PxU32) * 3, mesh.triangles.stride);
}
ConvexMeshBuilder meshBuilder(params.buildGPUData);
if(!meshBuilder.computeHullPolygons(mesh.points.count, geometry, mesh.triangles.count, topology, inCallback, nbVerts, vertices, nbIndices, indices, nbPolygons, hullPolygons))
return false;
return true;
}