// This code contains NVIDIA Confidential Information and is disclosed to you // under a form of NVIDIA software license agreement provided separately to you. // // Notice // NVIDIA Corporation and its licensors retain all intellectual property and // proprietary rights in and to this software and related documentation and // any modifications thereto. Any use, reproduction, disclosure, or // distribution of this software and related documentation without an express // license agreement from NVIDIA Corporation is strictly prohibited. // // ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES // NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO // THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. // // Information and code furnished is believed to be accurate and reliable. // However, NVIDIA Corporation assumes no responsibility for the consequences of use of such // information or for any infringement of patents or other rights of third parties that may // result from its use. No license is granted by implication or otherwise under any patent // or patent rights of NVIDIA Corporation. Details are subject to change without notice. // This code supersedes and replaces all information previously supplied. // NVIDIA Corporation products are not authorized for use as critical // components in life support devices or systems without express written approval of // NVIDIA Corporation. // // Copyright (c) 2008-2017 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 "Quantizer.h" #include "foundation/PxVec3.h" #include "foundation/PxBounds3.h" #include "PsUserAllocated.h" #include "PsAllocator.h" #include "PsArray.h" #include "CmPhysXCommon.h" using namespace physx; PxU32 kmeans_cluster3d(const PxVec3 *input, // an array of input 3d data points. PxU32 inputSize, // the number of input data points. PxU32 clumpCount, // the number of clumps you wish to product. PxVec3 *outputClusters, // The output array of clumps 3d vectors, should be at least 'clumpCount' in size. PxU32 *outputIndices, // A set of indices which remaps the input vertices to clumps; should be at least 'inputSize' float errorThreshold=0.01f, // The error threshold to converge towards before giving up. float collapseDistance=0.01f); // distance so small it is not worth bothering to create a new clump. template PxU32 kmeans_cluster(const Vec *input, PxU32 inputCount, PxU32 clumpCount, Vec *clusters, PxU32 *outputIndices, Type threshold, // controls how long it works to converge towards a least errors solution. Type collapseDistance) // distance between clumps to consider them to be essentially equal. { PxU32 convergeCount = 64; // maximum number of iterations attempting to converge to a solution.. PxU32* counts = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxU32)*clumpCount, "PxU32")); Type error=0; if ( inputCount <= clumpCount ) // if the number of input points is less than our clumping size, just return the input points. { clumpCount = inputCount; for (PxU32 i=0; i (PX_ALLOC_TEMP(sizeof(PxVec3)*clumpCount, "PxVec3")); // Take a sampling of the input points as initial centroid estimates. for (PxU32 i=0; i threshold ); // keep going until the error is reduced by this threshold amount. PX_FREE(centroids); } // ok..now we prune the clumps if necessary. // The rules are; first, if a clump has no 'counts' then we prune it as it's unused. // The second, is if the centroid of this clump is essentially the same (based on the distance tolerance) // as an existing clump, then it is pruned and all indices which used to point to it, now point to the one // it is closest too. PxU32 outCount = 0; // number of clumps output after pruning performed. Type d2 = collapseDistance*collapseDistance; // squared collapse distance. for (PxU32 i=0; i(input, inputSize, clumpCount, outputClusters, outputIndices, errorThreshold, collapseDistance); } class QuantizerImpl : public Quantizer, public Ps::UserAllocated { public: QuantizerImpl(void) { mScale = PxVec3(1.0f, 1.0f, 1.0f); mCenter = PxVec3(0.0f, 0.0f, 0.0f); } // Use the k-means quantizer, similar results, but much slower. virtual const PxVec3 * kmeansQuantize3D(PxU32 vcount, const PxVec3 *vertices, PxU32 stride, bool denormalizeResults, PxU32 maxVertices, PxU32 &outVertsCount) { const PxVec3 *ret = NULL; outVertsCount = 0; mNormalizedInput.clear(); mQuantizedOutput.clear(); if ( vcount > 0 ) { normalizeInput(vcount,vertices, stride); PxVec3* quantizedOutput = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxVec3)*vcount, "PxVec3")); PxU32* quantizedIndices = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxU32)*vcount, "PxU32")); outVertsCount = kmeans_cluster3d(&mNormalizedInput[0], vcount, maxVertices, quantizedOutput, quantizedIndices, 0.01f, 0.0001f ); if ( outVertsCount > 0 ) { if ( denormalizeResults ) { for (PxU32 i=0; i (vertices); mNormalizedInput.clear(); mQuantizedOutput.clear(); PxBounds3 bounds; bounds.setEmpty(); for (PxU32 i=0; i (vtx); vtx += stride; bounds.include(v); } mCenter = bounds.getCenter(); PxVec3 dim = bounds.getDimensions(); dim *= 1.001f; mScale = dim*0.5f; for (PxU32 i = 0; i < 3; i++) { if(dim[i] == 0) mScale[i] = 1.0f; } PxVec3 recip; recip.x = 1.0f / mScale.x; recip.y = 1.0f / mScale.y; recip.z = 1.0f / mScale.z; vtx = reinterpret_cast (vertices); for (PxU32 i=0; i (vtx); vtx += stride; v = (v - mCenter).multiply(recip); mNormalizedInput.pushBack(v); } } virtual ~QuantizerImpl(void) { } private: PxVec3 mScale; PxVec3 mCenter; Ps::Array mNormalizedInput; Ps::Array mQuantizedOutput; }; Quantizer * physx::createQuantizer(void) { QuantizerImpl *m = PX_NEW(QuantizerImpl); return static_cast< Quantizer *>(m); }