// // 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. // **************************************************************************** // This snippet demonstrates the possibilities of triangle mesh creation. // // The snippet creates triangle mesh with a different cooking settings // and shows how these settings affect the triangle mesh creation speed. // **************************************************************************** #include #include "PxPhysicsAPI.h" #include "../snippetutils/SnippetUtils.h" using namespace physx; PxDefaultAllocator gAllocator; PxDefaultErrorCallback gErrorCallback; PxFoundation* gFoundation = NULL; PxPhysics* gPhysics = NULL; PxCooking* gCooking = NULL; float rand(float loVal, float hiVal) { return loVal + float(rand()/float(RAND_MAX))*(hiVal - loVal); } PxU32 rand(PxU32 loVal, PxU32 hiVal) { return loVal + PxU32(rand()%(hiVal - loVal)); } void indexToCoord(PxU32& x, PxU32& z, PxU32 index, PxU32 w) { x = index % w; z = index / w; } // Creates a random terrain data. void createRandomTerrain(const PxVec3& origin, PxU32 numRows, PxU32 numColumns, PxReal cellSizeRow, PxReal cellSizeCol, PxReal heightScale, PxVec3*& vertices, PxU32*& indices) { PxU32 numX = (numColumns + 1); PxU32 numZ = (numRows + 1); PxU32 numVertices = numX*numZ; PxU32 numTriangles = numRows*numColumns * 2; if (vertices == NULL) vertices = new PxVec3[numVertices]; if (indices == NULL) indices = new PxU32[numTriangles * 3]; PxU32 currentIdx = 0; for (PxU32 i = 0; i <= numRows; i++) { for (PxU32 j = 0; j <= numColumns; j++) { PxVec3 v(origin.x + PxReal(j)*cellSizeRow, origin.y, origin.z + PxReal(i)*cellSizeCol); vertices[currentIdx++] = v; } } currentIdx = 0; for (PxU32 i = 0; i < numRows; i++) { for (PxU32 j = 0; j < numColumns; j++) { PxU32 base = (numColumns + 1)*i + j; indices[currentIdx++] = base + 1; indices[currentIdx++] = base; indices[currentIdx++] = base + numColumns + 1; indices[currentIdx++] = base + numColumns + 2; indices[currentIdx++] = base + 1; indices[currentIdx++] = base + numColumns + 1; } } for (PxU32 i = 0; i < numVertices; i++) { PxVec3& v = vertices[i]; v.y += heightScale * rand(-1.0f, 1.0f); } } // Setup common cooking params void setupCommonCookingParams(PxCookingParams& params, bool skipMeshCleanup, bool skipEdgeData) { // we suppress the triangle mesh remap table computation to gain some speed, as we will not need it // in this snippet params.suppressTriangleMeshRemapTable = true; // If DISABLE_CLEAN_MESH is set, the mesh is not cleaned during the cooking. The input mesh must be valid. // The following conditions are true for a valid triangle mesh : // 1. There are no duplicate vertices(within specified vertexWeldTolerance.See PxCookingParams::meshWeldTolerance) // 2. There are no large triangles(within specified PxTolerancesScale.) // It is recommended to run a separate validation check in debug/checked builds, see below. if (!skipMeshCleanup) params.meshPreprocessParams &= ~static_cast(PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH); else params.meshPreprocessParams |= PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH; // If DISABLE_ACTIVE_EDGES_PREDOCOMPUTE is set, the cooking does not compute the active (convex) edges, and instead // marks all edges as active. This makes cooking faster but can slow down contact generation. This flag may change // the collision behavior, as all edges of the triangle mesh will now be considered active. if (!skipEdgeData) params.meshPreprocessParams &= ~static_cast(PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE); else params.meshPreprocessParams |= PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE; } // Creates a triangle mesh using BVH33 midphase with different settings. void createBV33TriangleMesh(PxU32 numVertices, const PxVec3* vertices, PxU32 numTriangles, const PxU32* indices, bool skipMeshCleanup, bool skipEdgeData, bool inserted, bool cookingPerformance, bool meshSizePerfTradeoff) { PxU64 startTime = SnippetUtils::getCurrentTimeCounterValue(); PxTriangleMeshDesc meshDesc; meshDesc.points.count = numVertices; meshDesc.points.data = vertices; meshDesc.points.stride = sizeof(PxVec3); meshDesc.triangles.count = numTriangles; meshDesc.triangles.data = indices; meshDesc.triangles.stride = 3 * sizeof(PxU32); PxCookingParams params = gCooking->getParams(); // Create BVH33 midphase params.midphaseDesc = PxMeshMidPhase::eBVH33; // setup common cooking params setupCommonCookingParams(params, skipMeshCleanup, skipEdgeData); // The COOKING_PERFORMANCE flag for BVH33 midphase enables a fast cooking path at the expense of somewhat lower quality BVH construction. if (cookingPerformance) params.midphaseDesc.mBVH33Desc.meshCookingHint = PxMeshCookingHint::eCOOKING_PERFORMANCE; else params.midphaseDesc.mBVH33Desc.meshCookingHint = PxMeshCookingHint::eSIM_PERFORMANCE; // If meshSizePerfTradeoff is set to true, smaller mesh cooked mesh is produced. The mesh size/performance trade-off // is controlled by setting the meshSizePerformanceTradeOff from 0.0f (smaller mesh) to 1.0f (larger mesh). if(meshSizePerfTradeoff) { params.midphaseDesc.mBVH33Desc.meshSizePerformanceTradeOff = 0.0f; } else { // using the default value params.midphaseDesc.mBVH33Desc.meshSizePerformanceTradeOff = 0.55f; } gCooking->setParams(params); #if defined(PX_CHECKED) || defined(PX_DEBUG) // If DISABLE_CLEAN_MESH is set, the mesh is not cleaned during the cooking. // We should check the validity of provided triangles in debug/checked builds though. if (skipMeshCleanup) { PX_ASSERT(gCooking->validateTriangleMesh(meshDesc)); } #endif // DEBUG PxTriangleMesh* triMesh = NULL; PxU32 meshSize = 0; // The cooked mesh may either be saved to a stream for later loading, or inserted directly into PxPhysics. if (inserted) { triMesh = gCooking->createTriangleMesh(meshDesc, gPhysics->getPhysicsInsertionCallback()); } else { PxDefaultMemoryOutputStream outBuffer; gCooking->cookTriangleMesh(meshDesc, outBuffer); PxDefaultMemoryInputData stream(outBuffer.getData(), outBuffer.getSize()); triMesh = gPhysics->createTriangleMesh(stream); meshSize = outBuffer.getSize(); } // Print the elapsed time for comparison PxU64 stopTime = SnippetUtils::getCurrentTimeCounterValue(); float elapsedTime = SnippetUtils::getElapsedTimeInMilliseconds(stopTime - startTime); printf("\t -----------------------------------------------\n"); printf("\t Create triangle mesh with %d triangles: \n",numTriangles); cookingPerformance ? printf("\t\t Cooking performance on\n") : printf("\t\t Cooking performance off\n"); inserted ? printf("\t\t Mesh inserted on\n") : printf("\t\t Mesh inserted off\n"); !skipEdgeData ? printf("\t\t Precompute edge data on\n") : printf("\t\t Precompute edge data off\n"); !skipMeshCleanup ? printf("\t\t Mesh cleanup on\n") : printf("\t\t Mesh cleanup off\n"); printf("\t\t Mesh size/performance trade-off: %f \n", double(params.midphaseDesc.mBVH33Desc.meshSizePerformanceTradeOff)); printf("\t Elapsed time in ms: %f \n", double(elapsedTime)); if(!inserted) { printf("\t Mesh size: %d \n", meshSize); } triMesh->release(); } // Creates a triangle mesh using BVH34 midphase with different settings. void createBV34TriangleMesh(PxU32 numVertices, const PxVec3* vertices, PxU32 numTriangles, const PxU32* indices, bool skipMeshCleanup, bool skipEdgeData, bool inserted, const PxU32 numTrisPerLeaf) { PxU64 startTime = SnippetUtils::getCurrentTimeCounterValue(); PxTriangleMeshDesc meshDesc; meshDesc.points.count = numVertices; meshDesc.points.data = vertices; meshDesc.points.stride = sizeof(PxVec3); meshDesc.triangles.count = numTriangles; meshDesc.triangles.data = indices; meshDesc.triangles.stride = 3 * sizeof(PxU32); PxCookingParams params = gCooking->getParams(); // Create BVH34 midphase params.midphaseDesc = PxMeshMidPhase::eBVH34; // setup common cooking params setupCommonCookingParams(params, skipMeshCleanup, skipEdgeData); // Cooking mesh with less triangles per leaf produces larger meshes with better runtime performance // and worse cooking performance. Cooking time is better when more triangles per leaf are used. params.midphaseDesc.mBVH34Desc.numPrimsPerLeaf = numTrisPerLeaf; gCooking->setParams(params); #if defined(PX_CHECKED) || defined(PX_DEBUG) // If DISABLE_CLEAN_MESH is set, the mesh is not cleaned during the cooking. // We should check the validity of provided triangles in debug/checked builds though. if (skipMeshCleanup) { PX_ASSERT(gCooking->validateTriangleMesh(meshDesc)); } #endif // DEBUG PxTriangleMesh* triMesh = NULL; PxU32 meshSize = 0; // The cooked mesh may either be saved to a stream for later loading, or inserted directly into PxPhysics. if (inserted) { triMesh = gCooking->createTriangleMesh(meshDesc, gPhysics->getPhysicsInsertionCallback()); } else { PxDefaultMemoryOutputStream outBuffer; gCooking->cookTriangleMesh(meshDesc, outBuffer); PxDefaultMemoryInputData stream(outBuffer.getData(), outBuffer.getSize()); triMesh = gPhysics->createTriangleMesh(stream); meshSize = outBuffer.getSize(); } // Print the elapsed time for comparison PxU64 stopTime = SnippetUtils::getCurrentTimeCounterValue(); float elapsedTime = SnippetUtils::getElapsedTimeInMilliseconds(stopTime - startTime); printf("\t -----------------------------------------------\n"); printf("\t Create triangle mesh with %d triangles: \n", numTriangles); inserted ? printf("\t\t Mesh inserted on\n") : printf("\t\t Mesh inserted off\n"); !skipEdgeData ? printf("\t\t Precompute edge data on\n") : printf("\t\t Precompute edge data off\n"); !skipMeshCleanup ? printf("\t\t Mesh cleanup on\n") : printf("\t\t Mesh cleanup off\n"); printf("\t\t Num triangles per leaf: %d \n", numTrisPerLeaf); printf("\t Elapsed time in ms: %f \n", double(elapsedTime)); if (!inserted) { printf("\t Mesh size: %d \n", meshSize); } triMesh->release(); } void createTriangleMeshes() { const PxU32 numColumns = 128; const PxU32 numRows = 128; const PxU32 numVertices = (numColumns + 1)*(numRows + 1); const PxU32 numTriangles = numColumns*numRows * 2; PxVec3* vertices = new PxVec3[numVertices]; PxU32* indices = new PxU32[numTriangles * 3]; srand(50); createRandomTerrain(PxVec3(0.0f, 0.0f, 0.0f), numRows, numColumns, 1.0f, 1.0f, 1.f, vertices, indices); // Create triangle mesh using BVH33 midphase with different settings printf("-----------------------------------------------\n"); printf("Create triangles mesh using BVH33 midphase: \n\n"); // Favor runtime speed, cleaning the mesh and precomputing active edges. Store the mesh in a stream. // These are the default settings, suitable for offline cooking. createBV33TriangleMesh(numVertices,vertices,numTriangles,indices, false, false, false, false, false); // Favor mesh size, cleaning the mesh and precomputing active edges. Store the mesh in a stream. createBV33TriangleMesh(numVertices, vertices, numTriangles, indices, false, false, false, false, true); // Favor cooking speed, skip mesh cleanup, but precompute active edges. Insert into PxPhysics. // These settings are suitable for runtime cooking, although selecting fast cooking may reduce // runtime performance of simulation and queries. We still need to ensure the triangles // are valid, so we perform a validation check in debug/checked builds. createBV33TriangleMesh(numVertices,vertices,numTriangles,indices, true, false, true, true, false); // Favor cooking speed, skip mesh cleanup, and don't precompute the active edges. Insert into PxPhysics. // This is the fastest possible solution for runtime cooking, but all edges are marked as active, which can // further reduce runtime performance, and also affect behavior. createBV33TriangleMesh(numVertices,vertices,numTriangles,indices, false, true, true, true, false); // Create triangle mesh using BVH34 midphase with different settings printf("-----------------------------------------------\n"); printf("Create triangles mesh using BVH34 midphase: \n\n"); // Favor runtime speed, cleaning the mesh and precomputing active edges. Store the mesh in a stream. // These are the default settings, suitable for offline cooking. createBV34TriangleMesh(numVertices, vertices, numTriangles, indices, false, false, false, 4); // Favor mesh size, cleaning the mesh and precomputing active edges. Store the mesh in a stream. createBV34TriangleMesh(numVertices, vertices, numTriangles, indices, false, false, false, 15); // Favor cooking speed, skip mesh cleanup, but precompute active edges. Insert into PxPhysics. // These settings are suitable for runtime cooking, although selecting more triangles per leaf may reduce // runtime performance of simulation and queries. We still need to ensure the triangles // are valid, so we perform a validation check in debug/checked builds. createBV34TriangleMesh(numVertices, vertices, numTriangles, indices, true, false, true, 15); // Favor cooking speed, skip mesh cleanup, and don't precompute the active edges. Insert into PxPhysics. // This is the fastest possible solution for runtime cooking, but all edges are marked as active, which can // further reduce runtime performance, and also affect behavior. createBV34TriangleMesh(numVertices, vertices, numTriangles, indices, false, true, true, 15); delete [] vertices; delete [] indices; } void initPhysics() { gFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gAllocator, gErrorCallback); gPhysics = PxCreatePhysics(PX_PHYSICS_VERSION, *gFoundation, PxTolerancesScale(),true); gCooking = PxCreateCooking(PX_PHYSICS_VERSION, *gFoundation, PxCookingParams(PxTolerancesScale())); createTriangleMeshes(); } void cleanupPhysics() { gPhysics->release(); gCooking->release(); gFoundation->release(); printf("SnippetTriangleMeshCreate done.\n"); } int snippetMain(int, const char*const*) { initPhysics(); cleanupPhysics(); return 0; }