1 | // |
2 | // Redistribution and use in source and binary forms, with or without |
3 | // modification, are permitted provided that the following conditions |
4 | // are met: |
5 | // * Redistributions of source code must retain the above copyright |
6 | // notice, this list of conditions and the following disclaimer. |
7 | // * Redistributions in binary form must reproduce the above copyright |
8 | // notice, this list of conditions and the following disclaimer in the |
9 | // documentation and/or other materials provided with the distribution. |
10 | // * Neither the name of NVIDIA CORPORATION nor the names of its |
11 | // contributors may be used to endorse or promote products derived |
12 | // from this software without specific prior written permission. |
13 | // |
14 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY |
15 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
17 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
18 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
23 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
24 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | // |
26 | // Copyright (c) 2008-2021 NVIDIA Corporation. All rights reserved. |
27 | // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. |
28 | // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. |
29 | |
30 | |
31 | #include "GuConvexMesh.h" |
32 | #include "PsFoundation.h" |
33 | #include "PsMathUtils.h" |
34 | #include "Cooking.h" |
35 | |
36 | #include "GuHillClimbing.h" |
37 | #include "GuBigConvexData2.h" |
38 | #include "GuInternal.h" |
39 | #include "GuSerialize.h" |
40 | #include "VolumeIntegration.h" |
41 | |
42 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
43 | |
44 | #include "VolumeIntegration.h" |
45 | #include "ConvexHullBuilder.h" |
46 | #include "ConvexMeshBuilder.h" |
47 | #include "BigConvexDataBuilder.h" |
48 | |
49 | #include "CmUtils.h" |
50 | #include "PsVecMath.h" |
51 | |
52 | using namespace physx; |
53 | using namespace Gu; |
54 | using namespace Ps::aos; |
55 | |
56 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
57 | |
58 | ConvexMeshBuilder::ConvexMeshBuilder(const bool buildGRBData) : hullBuilder(&mHullData, buildGRBData), mBigConvexData(NULL), mMass(0.0f), mInertia(PxIdentity) |
59 | { |
60 | } |
61 | |
62 | ConvexMeshBuilder::~ConvexMeshBuilder() |
63 | { |
64 | PX_DELETE_AND_RESET(mBigConvexData); |
65 | } |
66 | |
67 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
68 | // load the mesh data from given polygons |
69 | bool ConvexMeshBuilder::build(const PxConvexMeshDesc& desc, PxU32 gaussMapVertexLimit, bool validateOnly, ConvexHullLib* hullLib) |
70 | { |
71 | if(!desc.isValid()) |
72 | { |
73 | Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, messageFmt: "Gu::ConvexMesh::loadFromDesc: desc.isValid() failed!" ); |
74 | return false; |
75 | } |
76 | |
77 | if(!loadConvexHull(desc, hullLib)) |
78 | return false; |
79 | |
80 | // Compute local bounds (*after* hull has been created) |
81 | PxBounds3 minMaxBounds; |
82 | computeBoundsAroundVertices(bounds&: minMaxBounds, nbVerts: mHullData.mNbHullVertices, verts: hullBuilder.mHullDataHullVertices); |
83 | mHullData.mAABB = CenterExtents(minMaxBounds); |
84 | |
85 | if(mHullData.mNbHullVertices > gaussMapVertexLimit) |
86 | { |
87 | if(!computeGaussMaps()) |
88 | { |
89 | return false; |
90 | } |
91 | } |
92 | |
93 | if(validateOnly) |
94 | return true; |
95 | |
96 | // TEST_INTERNAL_OBJECTS |
97 | computeInternalObjects(); |
98 | //~TEST_INTERNAL_OBJECTS |
99 | |
100 | return true; |
101 | } |
102 | |
103 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
104 | |
105 | PX_COMPILE_TIME_ASSERT(sizeof(PxMaterialTableIndex)==sizeof(PxU16)); |
106 | bool ConvexMeshBuilder::save(PxOutputStream& stream, bool platformMismatch) const |
107 | { |
108 | // Export header |
109 | if(!writeHeader(a: 'C', b: 'V', c: 'X', d: 'M', PX_CONVEX_VERSION, mismatch: platformMismatch, stream)) |
110 | return false; |
111 | |
112 | // Export serialization flags |
113 | PxU32 serialFlags = 0; |
114 | |
115 | writeDword(value: serialFlags, mismatch: platformMismatch, stream); |
116 | |
117 | if(!hullBuilder.save(stream, platformMismatch)) |
118 | return false; |
119 | |
120 | // Export local bounds |
121 | // writeFloat(geomEpsilon, platformMismatch, stream); |
122 | writeFloat(value: 0.0f, mismatch: platformMismatch, stream); |
123 | writeFloat(value: mHullData.mAABB.getMin(axis: 0), mismatch: platformMismatch, stream); |
124 | writeFloat(value: mHullData.mAABB.getMin(axis: 1), mismatch: platformMismatch, stream); |
125 | writeFloat(value: mHullData.mAABB.getMin(axis: 2), mismatch: platformMismatch, stream); |
126 | writeFloat(value: mHullData.mAABB.getMax(axis: 0), mismatch: platformMismatch, stream); |
127 | writeFloat(value: mHullData.mAABB.getMax(axis: 1), mismatch: platformMismatch, stream); |
128 | writeFloat(value: mHullData.mAABB.getMax(axis: 2), mismatch: platformMismatch, stream); |
129 | |
130 | // Export mass info |
131 | writeFloat(value: mMass, mismatch: platformMismatch, stream); |
132 | writeFloatBuffer(src: reinterpret_cast<const PxF32*>(&mInertia), nb: 9, mismatch: platformMismatch, stream); |
133 | writeFloatBuffer(src: &mHullData.mCenterOfMass.x, nb: 3, mismatch: platformMismatch, stream); |
134 | |
135 | // Export gaussmaps |
136 | if(mBigConvexData) |
137 | { |
138 | writeFloat(value: 1.0f, mismatch: platformMismatch, stream); //gauss map flag true |
139 | BigConvexDataBuilder SVMB(&mHullData, mBigConvexData, hullBuilder.mHullDataHullVertices); |
140 | SVMB.save(stream, platformMismatch); |
141 | } |
142 | else |
143 | writeFloat(value: -1.0f, mismatch: platformMismatch, stream); //gauss map flag false |
144 | |
145 | // TEST_INTERNAL_OBJECTS |
146 | writeFloat(value: mHullData.mInternal.mRadius, mismatch: platformMismatch, stream); |
147 | writeFloat(value: mHullData.mInternal.mExtents[0], mismatch: platformMismatch, stream); |
148 | writeFloat(value: mHullData.mInternal.mExtents[1], mismatch: platformMismatch, stream); |
149 | writeFloat(value: mHullData.mInternal.mExtents[2], mismatch: platformMismatch, stream); |
150 | //~TEST_INTERNAL_OBJECTS |
151 | return true; |
152 | } |
153 | |
154 | ////////////////////////////////////////////////////////////////////////// |
155 | // instead of saving the data into stream, we copy the mesh data |
156 | // into internal Gu::ConvexMesh. |
157 | bool ConvexMeshBuilder::copy(Gu::ConvexHullInitData& hullData) |
158 | { |
159 | // hull builder data copy |
160 | PxU32 nb = 0; |
161 | hullBuilder.copy(hullData&: hullData.mHullData, nb); |
162 | hullData.mNb = nb; |
163 | |
164 | hullData.mInertia = mInertia; |
165 | hullData.mMass = mMass; |
166 | |
167 | // mass props |
168 | hullData.mHullData.mAABB = mHullData.mAABB; |
169 | hullData.mHullData.mCenterOfMass = mHullData.mCenterOfMass; |
170 | |
171 | // big convex data |
172 | if(mBigConvexData) |
173 | { |
174 | hullData.mHullData.mBigConvexRawData = &mBigConvexData->mData; |
175 | hullData.mBigConvexData = mBigConvexData; |
176 | mBigConvexData = NULL; |
177 | } |
178 | else |
179 | { |
180 | hullData.mHullData.mBigConvexRawData = NULL; |
181 | hullData.mBigConvexData = NULL; |
182 | } |
183 | |
184 | // internal data |
185 | hullData.mHullData.mInternal.mRadius = mHullData.mInternal.mRadius; |
186 | hullData.mHullData.mInternal.mExtents[0] = mHullData.mInternal.mExtents[0]; |
187 | hullData.mHullData.mInternal.mExtents[1] = mHullData.mInternal.mExtents[1]; |
188 | hullData.mHullData.mInternal.mExtents[2] = mHullData.mInternal.mExtents[2]; |
189 | |
190 | return true; |
191 | } |
192 | |
193 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
194 | // compute mass and inertia of the convex mesh |
195 | void ConvexMeshBuilder::computeMassInfo(bool lowerPrecision) |
196 | { |
197 | if(mMass <= 0.0f) //not yet computed. |
198 | { |
199 | PxIntegrals integrals; |
200 | PxConvexMeshDesc meshDesc; |
201 | meshDesc.points.count = mHullData.mNbHullVertices; |
202 | meshDesc.points.data = hullBuilder.mHullDataHullVertices; |
203 | meshDesc.points.stride = sizeof(PxVec3); |
204 | |
205 | meshDesc.polygons.data = hullBuilder.mHullDataPolygons; |
206 | meshDesc.polygons.stride = sizeof(Gu::HullPolygonData); |
207 | meshDesc.polygons.count = hullBuilder.mHull->mNbPolygons; |
208 | |
209 | meshDesc.indices.data = hullBuilder.mHullDataVertexData8; |
210 | |
211 | // using the centroid of the convex for the volume integration solved accuracy issues in cases where the inertia tensor |
212 | // ended up close to not being positive definite and after a few further transforms the diagonalized inertia tensor ended |
213 | // up with negative values. |
214 | PxVec3 mean(0.0f); |
215 | for(PxU32 i=0; i < mHullData.mNbHullVertices; i++) |
216 | mean += hullBuilder.mHullDataHullVertices[i]; |
217 | mean *= (1.0f / mHullData.mNbHullVertices); |
218 | |
219 | bool status = lowerPrecision ? |
220 | computeVolumeIntegralsEberlySIMD(mesh: meshDesc, density: 1.0f, integrals, origin: mean) : computeVolumeIntegralsEberly(mesh: meshDesc, density: 1.0f, integrals, origin: mean); |
221 | if(status) |
222 | { |
223 | |
224 | integrals.getOriginInertia(inertia&: reinterpret_cast<PxMat33&>(mInertia)); |
225 | mHullData.mCenterOfMass = integrals.COM; |
226 | |
227 | //note: the mass will be negative for an inside-out mesh! |
228 | if(mInertia.column0.isFinite() && mInertia.column1.isFinite() && mInertia.column2.isFinite() |
229 | && mHullData.mCenterOfMass.isFinite() && PxIsFinite(f: PxReal(integrals.mass))) |
230 | { |
231 | if (integrals.mass < 0) |
232 | { |
233 | Ps::getFoundation().error(PX_WARN, messageFmt: "Gu::ConvexMesh: Mesh has a negative volume! Is it open or do (some) faces have reversed winding? (Taking absolute value.)" ); |
234 | integrals.mass = -integrals.mass; |
235 | mInertia = -mInertia; |
236 | } |
237 | |
238 | mMass = PxReal(integrals.mass); //set mass to valid value. |
239 | return; |
240 | } |
241 | } |
242 | Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, messageFmt: "Gu::ConvexMesh: Error computing mesh mass properties!\n" ); |
243 | } |
244 | } |
245 | |
246 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
247 | #if PX_VC |
248 | #pragma warning(push) |
249 | #pragma warning(disable:4996) // permitting use of gatherStrided until we have a replacement. |
250 | #endif |
251 | |
252 | bool ConvexMeshBuilder::loadConvexHull(const PxConvexMeshDesc& desc, ConvexHullLib* hullLib) |
253 | { |
254 | // gather points |
255 | PxVec3* geometry = reinterpret_cast<PxVec3*>(PxAlloca(sizeof(PxVec3)*desc.points.count)); |
256 | Cooking::gatherStrided(src: desc.points.data, dst: geometry, nbElem: desc.points.count, elemSize: sizeof(PxVec3), stride: desc.points.stride); |
257 | |
258 | PxU32* topology = NULL; |
259 | |
260 | // gather indices |
261 | // store the indices into topology if we have the polygon data |
262 | if(desc.indices.data) |
263 | { |
264 | topology = reinterpret_cast<PxU32*>(PxAlloca(sizeof(PxU32)*desc.indices.count)); |
265 | if (desc.flags & PxConvexFlag::e16_BIT_INDICES) |
266 | { |
267 | // conversion; 16 bit index -> 32 bit index & stride |
268 | PxU32* dest = topology; |
269 | const PxU32* pastLastDest = topology + desc.indices.count; |
270 | const PxU8* source = reinterpret_cast<const PxU8*>(desc.indices.data); |
271 | while (dest < pastLastDest) |
272 | { |
273 | const PxU16 * trig16 = reinterpret_cast<const PxU16*>(source); |
274 | *dest++ = *trig16; |
275 | source += desc.indices.stride; |
276 | } |
277 | } |
278 | else |
279 | { |
280 | Cooking::gatherStrided(src: desc.indices.data, dst: topology, nbElem: desc.indices.count, elemSize: sizeof(PxU32), stride: desc.indices.stride); |
281 | } |
282 | } |
283 | |
284 | // gather polygons |
285 | PxHullPolygon* hullPolygons = NULL; |
286 | if(desc.polygons.data) |
287 | { |
288 | hullPolygons = reinterpret_cast<PxHullPolygon*>(PxAlloca(sizeof(PxHullPolygon)*desc.polygons.count)); |
289 | Cooking::gatherStrided(src: desc.polygons.data,dst: hullPolygons,nbElem: desc.polygons.count,elemSize: sizeof(PxHullPolygon),stride: desc.polygons.stride); |
290 | |
291 | // if user polygons, make sure the largest one is the first one |
292 | if (!hullLib) |
293 | { |
294 | PxU32 largestPolygon = 0; |
295 | for (PxU32 i = 1; i < desc.polygons.count; i++) |
296 | { |
297 | if(hullPolygons[i].mNbVerts > hullPolygons[largestPolygon].mNbVerts) |
298 | largestPolygon = i; |
299 | } |
300 | if(largestPolygon != 0) |
301 | { |
302 | PxHullPolygon movedPolygon = hullPolygons[0]; |
303 | hullPolygons[0] = hullPolygons[largestPolygon]; |
304 | hullPolygons[largestPolygon] = movedPolygon; |
305 | } |
306 | } |
307 | } |
308 | |
309 | const bool doValidation = desc.flags & PxConvexFlag::eDISABLE_MESH_VALIDATION ? false : true; |
310 | if(!hullBuilder.init(nbVerts: desc.points.count, verts: geometry, indices: topology, nbIndices: desc.indices.count, nbPolygons: desc.polygons.count, hullPolygons, doValidation, hullLib)) |
311 | { |
312 | Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, messageFmt: "Gu::ConvexMesh::loadConvexHull: convex hull init failed!" ); |
313 | return false; |
314 | } |
315 | computeMassInfo(lowerPrecision: desc.flags & PxConvexFlag::eFAST_INERTIA_COMPUTATION); |
316 | |
317 | return true; |
318 | } |
319 | |
320 | #if PX_VC |
321 | #pragma warning(pop) |
322 | #endif |
323 | |
324 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
325 | // compute polygons from given triangles. This is support function used in extensions. We do not accept triangles as an input for convex mesh desc. |
326 | bool ConvexMeshBuilder::computeHullPolygons(const PxU32& nbVerts,const PxVec3* verts, const PxU32& nbTriangles, const PxU32* triangles, PxAllocatorCallback& inAllocator, |
327 | PxU32& outNbVerts, PxVec3*& outVertices , PxU32& nbIndices, PxU32*& indices, PxU32& nbPolygons, PxHullPolygon*& polygons) |
328 | { |
329 | if(!hullBuilder.computeHullPolygons(nbVerts,verts,nbTriangles,triangles)) |
330 | { |
331 | Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, messageFmt: "ConvexMeshBuilder::computeHullPolygons: compute convex hull polygons failed. Provided triangles dont form a convex hull." ); |
332 | return false; |
333 | } |
334 | |
335 | outNbVerts = hullBuilder.mHull->mNbHullVertices; |
336 | nbPolygons = hullBuilder.mHull->mNbPolygons; |
337 | |
338 | outVertices = reinterpret_cast<PxVec3*>(inAllocator.allocate(size: outNbVerts*sizeof(PxVec3),typeName: "PxVec3" ,__FILE__,__LINE__)); |
339 | PxMemCopy(dest: outVertices,src: hullBuilder.mHullDataHullVertices,count: outNbVerts*sizeof(PxVec3)); |
340 | |
341 | nbIndices = 0; |
342 | for (PxU32 i = 0; i < nbPolygons; i++) |
343 | { |
344 | nbIndices += hullBuilder.mHullDataPolygons[i].mNbVerts; |
345 | } |
346 | |
347 | indices = reinterpret_cast<PxU32*>(inAllocator.allocate(size: nbIndices*sizeof(PxU32),typeName: "PxU32" ,__FILE__,__LINE__)); |
348 | for (PxU32 i = 0; i < nbIndices; i++) |
349 | { |
350 | indices[i] = hullBuilder.mHullDataVertexData8[i]; |
351 | } |
352 | |
353 | polygons = reinterpret_cast<PxHullPolygon*>(inAllocator.allocate(size: nbPolygons*sizeof(PxHullPolygon),typeName: "PxHullPolygon" ,__FILE__,__LINE__)); |
354 | |
355 | for (PxU32 i = 0; i < nbPolygons; i++) |
356 | { |
357 | const Gu::HullPolygonData& polygonData = hullBuilder.mHullDataPolygons[i]; |
358 | PxHullPolygon& outPolygon = polygons[i]; |
359 | outPolygon.mPlane[0] = polygonData.mPlane.n.x; |
360 | outPolygon.mPlane[1] = polygonData.mPlane.n.y; |
361 | outPolygon.mPlane[2] = polygonData.mPlane.n.z; |
362 | outPolygon.mPlane[3] = polygonData.mPlane.d; |
363 | |
364 | outPolygon.mNbVerts = polygonData.mNbVerts; |
365 | outPolygon.mIndexBase = polygonData.mVRef8; |
366 | |
367 | for (PxU32 j = 0; j < polygonData.mNbVerts; j++) |
368 | { |
369 | PX_ASSERT(indices[outPolygon.mIndexBase + j] == hullBuilder.mHullDataVertexData8[polygonData.mVRef8+j]); |
370 | } |
371 | } |
372 | |
373 | return true; |
374 | } |
375 | |
376 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
377 | // compute big convex data |
378 | bool ConvexMeshBuilder::computeGaussMaps() |
379 | { |
380 | // The number of polygons is limited to 256 because the gaussmap encode 256 polys maximum |
381 | |
382 | PxU32 density = 16; |
383 | // density = 64; |
384 | // density = 8; |
385 | // density = 2; |
386 | |
387 | PX_DELETE(mBigConvexData); |
388 | PX_NEW_SERIALIZED(mBigConvexData,BigConvexData); |
389 | BigConvexDataBuilder SVMB(&mHullData, mBigConvexData, hullBuilder.mHullDataHullVertices); |
390 | // valencies we need to compute first, they are needed for min/max precompute |
391 | SVMB.computeValencies(meshBuilder: hullBuilder); |
392 | SVMB.precompute(subdiv: density); |
393 | |
394 | return true; |
395 | } |
396 | |
397 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
398 | // TEST_INTERNAL_OBJECTS |
399 | |
400 | static void ComputeInternalExtent(Gu::ConvexHullData& data, const Gu::HullPolygonData* hullPolys) |
401 | { |
402 | const PxVec3 e = data.mAABB.getMax() - data.mAABB.getMin(); |
403 | |
404 | // PT: For that formula, see %SDKRoot%\InternalDocumentation\Cooking\InternalExtents.png |
405 | const float r = data.mInternal.mRadius / sqrtf(x: 3.0f); |
406 | |
407 | const float epsilon = 1E-7f; |
408 | |
409 | const PxU32 largestExtent = Ps::largestAxis(v: e); |
410 | PxU32 e0 = Ps::getNextIndex3(i: largestExtent); |
411 | PxU32 e1 = Ps::getNextIndex3(i: e0); |
412 | if(e[e0] < e[e1]) |
413 | Ps::swap<PxU32>(x&: e0,y&: e1); |
414 | |
415 | data.mInternal.mExtents[0] = FLT_MAX; |
416 | data.mInternal.mExtents[1] = FLT_MAX; |
417 | data.mInternal.mExtents[2] = FLT_MAX; |
418 | |
419 | // PT: the following code does ray-vs-plane raycasts. |
420 | |
421 | // find the largest box along the largest extent, with given internal radius |
422 | for(PxU32 i = 0; i < data.mNbPolygons; i++) |
423 | { |
424 | // concurrent with search direction |
425 | const float d = hullPolys[i].mPlane.n[largestExtent]; |
426 | if((-epsilon < d && d < epsilon)) |
427 | continue; |
428 | |
429 | const float numBase = -hullPolys[i].mPlane.d - hullPolys[i].mPlane.n.dot(v: data.mCenterOfMass); |
430 | const float denBase = 1.0f/hullPolys[i].mPlane.n[largestExtent]; |
431 | const float numn0 = r * hullPolys[i].mPlane.n[e0]; |
432 | const float numn1 = r * hullPolys[i].mPlane.n[e1]; |
433 | |
434 | float num = numBase - numn0 - numn1; |
435 | float ext = PxMax(a: fabsf(x: num*denBase), b: r); |
436 | if(ext < data.mInternal.mExtents[largestExtent]) |
437 | data.mInternal.mExtents[largestExtent] = ext; |
438 | |
439 | num = numBase - numn0 + numn1; |
440 | ext = PxMax(a: fabsf(x: num *denBase), b: r); |
441 | if(ext < data.mInternal.mExtents[largestExtent]) |
442 | data.mInternal.mExtents[largestExtent] = ext; |
443 | |
444 | num = numBase + numn0 + numn1; |
445 | ext = PxMax(a: fabsf(x: num *denBase), b: r); |
446 | if(ext < data.mInternal.mExtents[largestExtent]) |
447 | data.mInternal.mExtents[largestExtent] = ext; |
448 | |
449 | num = numBase + numn0 - numn1; |
450 | ext = PxMax(a: fabsf(x: num *denBase), b: r); |
451 | if(ext < data.mInternal.mExtents[largestExtent]) |
452 | data.mInternal.mExtents[largestExtent] = ext; |
453 | } |
454 | |
455 | // Refine the box along e0,e1 |
456 | for(PxU32 i = 0; i < data.mNbPolygons; i++) |
457 | { |
458 | const float denumAdd = hullPolys[i].mPlane.n[e0] + hullPolys[i].mPlane.n[e1]; |
459 | const float denumSub = hullPolys[i].mPlane.n[e0] - hullPolys[i].mPlane.n[e1]; |
460 | |
461 | const float numBase = -hullPolys[i].mPlane.d - hullPolys[i].mPlane.n.dot(v: data.mCenterOfMass); |
462 | const float numn0 = data.mInternal.mExtents[largestExtent] * hullPolys[i].mPlane.n[largestExtent]; |
463 | |
464 | if(!(-epsilon < denumAdd && denumAdd < epsilon)) |
465 | { |
466 | float num = numBase - numn0; |
467 | float ext = PxMax(a: fabsf(x: num/ denumAdd), b: r); |
468 | if(ext < data.mInternal.mExtents[e0]) |
469 | data.mInternal.mExtents[e0] = ext; |
470 | |
471 | num = numBase + numn0; |
472 | ext = PxMax(a: fabsf(x: num / denumAdd), b: r); |
473 | if(ext < data.mInternal.mExtents[e0]) |
474 | data.mInternal.mExtents[e0] = ext; |
475 | } |
476 | |
477 | if(!(-epsilon < denumSub && denumSub < epsilon)) |
478 | { |
479 | float num = numBase - numn0; |
480 | float ext = PxMax(a: fabsf(x: num / denumSub), b: r); |
481 | if(ext < data.mInternal.mExtents[e0]) |
482 | data.mInternal.mExtents[e0] = ext; |
483 | |
484 | num = numBase + numn0; |
485 | ext = PxMax(a: fabsf(x: num / denumSub), b: r); |
486 | if(ext < data.mInternal.mExtents[e0]) |
487 | data.mInternal.mExtents[e0] = ext; |
488 | } |
489 | } |
490 | data.mInternal.mExtents[e1] = data.mInternal.mExtents[e0]; |
491 | } |
492 | |
493 | ////////////////////////////////////////////////////////////////////////// |
494 | // compute internal objects, get the internal extent and radius |
495 | void ConvexMeshBuilder::computeInternalObjects() |
496 | { |
497 | const Gu::HullPolygonData* hullPolys = hullBuilder.mHullDataPolygons; |
498 | Gu::ConvexHullData& data = mHullData; |
499 | |
500 | // compute the internal radius |
501 | data.mInternal.mRadius = FLT_MAX; |
502 | for(PxU32 i=0;i<data.mNbPolygons;i++) |
503 | { |
504 | const float dist = fabsf(x: hullPolys[i].mPlane.distance(p: data.mCenterOfMass)); |
505 | if(dist<data.mInternal.mRadius) |
506 | data.mInternal.mRadius = dist; |
507 | } |
508 | |
509 | ComputeInternalExtent(data, hullPolys); |
510 | } |
511 | |
512 | //~TEST_INTERNAL_OBJECTS |
513 | |