aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs')
-rwxr-xr-xOpenSim/Region/PhysicsModules/BulletS/BSShapes.cs1465
1 files changed, 1465 insertions, 0 deletions
diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs b/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs
new file mode 100755
index 0000000..79f1a89
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs
@@ -0,0 +1,1465 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Text;
31
32using OpenSim.Framework;
33using OpenSim.Region.PhysicsModules.SharedBase;
34using OpenSim.Region.PhysicsModules.Meshing;
35using OpenSim.Region.PhysicsModule.ConvexDecompositionDotNet;
36
37using OMV = OpenMetaverse;
38
39namespace OpenSim.Region.PhysicsModule.BulletS
40{
41// Information class that holds stats for the shape. Which values mean
42// something depends on the type of shape.
43// This information is used for debugging and stats and is not used
44// for operational things.
45public class ShapeInfoInfo
46{
47 public int Vertices { get; set; }
48 private int m_hullCount;
49 private int[] m_verticesPerHull;
50 public ShapeInfoInfo()
51 {
52 Vertices = 0;
53 m_hullCount = 0;
54 m_verticesPerHull = null;
55 }
56 public int HullCount
57 {
58 set
59 {
60 m_hullCount = value;
61 m_verticesPerHull = new int[m_hullCount];
62 Array.Clear(m_verticesPerHull, 0, m_hullCount);
63 }
64 get { return m_hullCount; }
65 }
66 public void SetVerticesPerHull(int hullNum, int vertices)
67 {
68 if (m_verticesPerHull != null && hullNum < m_verticesPerHull.Length)
69 {
70 m_verticesPerHull[hullNum] = vertices;
71 }
72 }
73 public int GetVerticesPerHull(int hullNum)
74 {
75 if (m_verticesPerHull != null && hullNum < m_verticesPerHull.Length)
76 {
77 return m_verticesPerHull[hullNum];
78 }
79 return 0;
80 }
81 public override string ToString()
82 {
83 StringBuilder buff = new StringBuilder();
84 // buff.Append("ShapeInfo=<");
85 buff.Append("<");
86 if (Vertices > 0)
87 {
88 buff.Append("verts=");
89 buff.Append(Vertices.ToString());
90 }
91
92 if (Vertices > 0 && HullCount > 0) buff.Append(",");
93
94 if (HullCount > 0)
95 {
96 buff.Append("nHulls=");
97 buff.Append(HullCount.ToString());
98 buff.Append(",");
99 buff.Append("hullVerts=");
100 for (int ii = 0; ii < HullCount; ii++)
101 {
102 if (ii != 0) buff.Append(",");
103 buff.Append(GetVerticesPerHull(ii).ToString());
104 }
105 }
106 buff.Append(">");
107 return buff.ToString();
108 }
109}
110
111public abstract class BSShape
112{
113 private static string LogHeader = "[BULLETSIM SHAPE]";
114
115 public int referenceCount { get; set; }
116 public DateTime lastReferenced { get; set; }
117 public BulletShape physShapeInfo { get; set; }
118 public ShapeInfoInfo shapeInfo { get; private set; }
119
120 public BSShape()
121 {
122 referenceCount = 1;
123 lastReferenced = DateTime.Now;
124 physShapeInfo = new BulletShape();
125 shapeInfo = new ShapeInfoInfo();
126 }
127 public BSShape(BulletShape pShape)
128 {
129 referenceCount = 1;
130 lastReferenced = DateTime.Now;
131 physShapeInfo = pShape;
132 shapeInfo = new ShapeInfoInfo();
133 }
134
135 // Get another reference to this shape.
136 public abstract BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim);
137
138 // Called when this shape is being used again.
139 // Used internally. External callers should call instance.GetReference() to properly copy/reference
140 // the shape.
141 protected virtual void IncrementReference()
142 {
143 referenceCount++;
144 lastReferenced = DateTime.Now;
145 }
146
147 // Called when this shape is done being used.
148 protected virtual void DecrementReference()
149 {
150 referenceCount--;
151 lastReferenced = DateTime.Now;
152 }
153
154 // Release the use of a physical shape.
155 public abstract void Dereference(BSScene physicsScene);
156
157 // Return 'true' if there is an allocated physics physical shape under this class instance.
158 public virtual bool HasPhysicalShape
159 {
160 get
161 {
162 if (physShapeInfo != null)
163 return physShapeInfo.HasPhysicalShape;
164 return false;
165 }
166 }
167 public virtual BSPhysicsShapeType ShapeType
168 {
169 get
170 {
171 BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN;
172 if (physShapeInfo != null && physShapeInfo.HasPhysicalShape)
173 ret = physShapeInfo.shapeType;
174 return ret;
175 }
176 }
177
178 // Returns a string for debugging that uniquily identifies the memory used by this instance
179 public virtual string AddrString
180 {
181 get
182 {
183 if (physShapeInfo != null)
184 return physShapeInfo.AddrString;
185 return "unknown";
186 }
187 }
188
189 public override string ToString()
190 {
191 StringBuilder buff = new StringBuilder();
192 if (physShapeInfo == null)
193 {
194 buff.Append("<noPhys");
195 }
196 else
197 {
198 buff.Append("<phy=");
199 buff.Append(physShapeInfo.ToString());
200 }
201 buff.Append(",c=");
202 buff.Append(referenceCount.ToString());
203 buff.Append(">");
204 return buff.ToString();
205 }
206
207 #region Common shape routines
208 // Create a hash of all the shape parameters to be used as a key for this particular shape.
209 public static System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod)
210 {
211 // level of detail based on size and type of the object
212 float lod = BSParam.MeshLOD;
213 if (pbs.SculptEntry)
214 lod = BSParam.SculptLOD;
215
216 // Mega prims usually get more detail because one can interact with shape approximations at this size.
217 float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z));
218 if (maxAxis > BSParam.MeshMegaPrimThreshold)
219 lod = BSParam.MeshMegaPrimLOD;
220
221 retLod = lod;
222 return pbs.GetMeshKey(size, lod);
223 }
224
225 // The creation of a mesh or hull can fail if an underlying asset is not available.
226 // There are two cases: 1) the asset is not in the cache and it needs to be fetched;
227 // and 2) the asset cannot be converted (like failed decompression of JPEG2000s).
228 // The first case causes the asset to be fetched. The second case requires
229 // us to not loop forever.
230 // Called after creating a physical mesh or hull. If the physical shape was created,
231 // just return.
232 public static BulletShape VerifyMeshCreated(BSScene physicsScene, BulletShape newShape, BSPhysObject prim)
233 {
234 // If the shape was successfully created, nothing more to do
235 if (newShape.HasPhysicalShape)
236 return newShape;
237
238 // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been
239 // fetched but we end up here again, the meshing of the asset must have failed.
240 // Prevent trying to keep fetching the mesh by declaring failure.
241 if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched)
242 {
243 prim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedMeshing;
244 physicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. prim={1}, texture={2}",
245 LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
246 physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,setFailed,prim={1},tex={2}",
247 prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
248 }
249 else
250 {
251 // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
252 if (prim.BaseShape.SculptEntry
253 && !prim.AssetFailed()
254 && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting
255 && prim.BaseShape.SculptTexture != OMV.UUID.Zero
256 )
257 {
258 physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAsset,objNam={1},tex={2}",
259 prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture);
260 // Multiple requestors will know we're waiting for this asset
261 prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting;
262
263 BSPhysObject xprim = prim;
264 RequestAssetDelegate assetProvider = physicsScene.RequestAssetMethod;
265 if (assetProvider != null)
266 {
267 BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
268 assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
269 {
270 // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,assetProviderCallback", xprim.LocalID);
271 bool assetFound = false;
272 string mismatchIDs = String.Empty; // DEBUG DEBUG
273 if (asset != null && yprim.BaseShape.SculptEntry)
274 {
275 if (yprim.BaseShape.SculptTexture.ToString() == asset.ID)
276 {
277 yprim.BaseShape.SculptData = asset.Data;
278 // This will cause the prim to see that the filler shape is not the right
279 // one and try again to build the object.
280 // No race condition with the normal shape setting since the rebuild is at taint time.
281 yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched;
282 yprim.ForceBodyShapeRebuild(false /* inTaintTime */);
283 assetFound = true;
284 }
285 else
286 {
287 mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID;
288 }
289 }
290 if (!assetFound)
291 {
292 yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedAssetFetch;
293 }
294 physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAssetCallback,found={1},isSculpt={2},ids={3}",
295 yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs );
296 });
297 }
298 else
299 {
300 xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedAssetFetch;
301 physicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}",
302 LogHeader, physicsScene.PhysicsSceneName);
303 }
304 }
305 else
306 {
307 if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedAssetFetch)
308 {
309 physicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. prim={1}, texture={2}",
310 LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
311 physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,wasFailed,prim={1},tex={2}",
312 prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
313 }
314 if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedMeshing)
315 {
316 physicsScene.Logger.WarnFormat("{0} Mesh asset would not mesh. prim={1}, texture={2}",
317 LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
318 physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,wasFailedMeshing,prim={1},tex={2}",
319 prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
320 }
321 }
322 }
323
324 // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object.
325 BSShape fillShape = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
326 physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,boxTempShape", prim.LocalID);
327
328 return fillShape.physShapeInfo;
329 }
330
331 public static String UsefulPrimInfo(BSScene pScene, BSPhysObject prim)
332 {
333 StringBuilder buff = new StringBuilder(prim.PhysObjectName);
334 buff.Append("/pos=");
335 buff.Append(prim.RawPosition.ToString());
336 if (pScene != null)
337 {
338 buff.Append("/rgn=");
339 buff.Append(pScene.PhysicsSceneName);
340 }
341 return buff.ToString();
342 }
343
344 #endregion // Common shape routines
345}
346
347// ============================================================================================================
348public class BSShapeNull : BSShape
349{
350 public BSShapeNull() : base()
351 {
352 }
353 public static BSShape GetReference() { return new BSShapeNull(); }
354 public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) { return new BSShapeNull(); }
355 public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ }
356}
357
358// ============================================================================================================
359// BSShapeNative is a wrapper for a Bullet 'native' shape -- cube and sphere.
360// They are odd in that they don't allocate meshes but are computated/procedural.
361// This means allocation and freeing is different than meshes.
362public class BSShapeNative : BSShape
363{
364 private static string LogHeader = "[BULLETSIM SHAPE NATIVE]";
365 public BSShapeNative(BulletShape pShape) : base(pShape)
366 {
367 }
368
369 public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim,
370 BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
371 {
372 // Native shapes are not shared and are always built anew.
373 return new BSShapeNative(CreatePhysicalNativeShape(physicsScene, prim, shapeType, shapeKey));
374 }
375
376 public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
377 {
378 // Native shapes are not shared so we return a new shape.
379 BSShape ret = null;
380 lock (physShapeInfo)
381 {
382 ret = new BSShapeNative(CreatePhysicalNativeShape(pPhysicsScene, pPrim,
383 physShapeInfo.shapeType, (FixedShapeKey)physShapeInfo.shapeKey));
384 }
385 return ret;
386 }
387
388 // Make this reference to the physical shape go away since native shapes are not shared.
389 public override void Dereference(BSScene physicsScene)
390 {
391 // Native shapes are not tracked and are released immediately
392 lock (physShapeInfo)
393 {
394 if (physShapeInfo.HasPhysicalShape)
395 {
396 physicsScene.DetailLog("{0},BSShapeNative.Dereference,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this);
397 physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo);
398 }
399 physShapeInfo.Clear();
400 // Garbage collection will free up this instance.
401 }
402 }
403
404 private static BulletShape CreatePhysicalNativeShape(BSScene physicsScene, BSPhysObject prim,
405 BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
406 {
407 BulletShape newShape;
408
409 ShapeData nativeShapeData = new ShapeData();
410 nativeShapeData.Type = shapeType;
411 nativeShapeData.ID = prim.LocalID;
412 nativeShapeData.Scale = prim.Scale;
413 nativeShapeData.Size = prim.Scale;
414 nativeShapeData.MeshKey = (ulong)shapeKey;
415 nativeShapeData.HullKey = (ulong)shapeKey;
416
417 if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
418 {
419 newShape = physicsScene.PE.BuildCapsuleShape(physicsScene.World, 1f, 1f, prim.Scale);
420 physicsScene.DetailLog("{0},BSShapeNative,capsule,scale={1}", prim.LocalID, prim.Scale);
421 }
422 else
423 {
424 newShape = physicsScene.PE.BuildNativeShape(physicsScene.World, nativeShapeData);
425 }
426 if (!newShape.HasPhysicalShape)
427 {
428 physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
429 LogHeader, prim.LocalID, shapeType);
430 }
431 newShape.shapeType = shapeType;
432 newShape.isNativeShape = true;
433 newShape.shapeKey = (UInt64)shapeKey;
434 return newShape;
435 }
436
437}
438
439// ============================================================================================================
440// BSShapeMesh is a simple mesh.
441public class BSShapeMesh : BSShape
442{
443 private static string LogHeader = "[BULLETSIM SHAPE MESH]";
444 public static Dictionary<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>();
445
446 public BSShapeMesh(BulletShape pShape) : base(pShape)
447 {
448 }
449 public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
450 {
451 float lod;
452 System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
453
454 BSShapeMesh retMesh = null;
455 lock (Meshes)
456 {
457 if (Meshes.TryGetValue(newMeshKey, out retMesh))
458 {
459 // The mesh has already been created. Return a new reference to same.
460 retMesh.IncrementReference();
461 }
462 else
463 {
464 retMesh = new BSShapeMesh(new BulletShape());
465 // An instance of this mesh has not been created. Build and remember same.
466 BulletShape newShape = retMesh.CreatePhysicalMesh(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod);
467
468 // Check to see if mesh was created (might require an asset).
469 newShape = VerifyMeshCreated(physicsScene, newShape, prim);
470 if (!newShape.isNativeShape || prim.AssetFailed() )
471 {
472 // If a mesh was what was created, remember the built shape for later sharing.
473 // Also note that if meshing failed we put it in the mesh list as there is nothing else to do about the mesh.
474 Meshes.Add(newMeshKey, retMesh);
475 }
476
477 retMesh.physShapeInfo = newShape;
478 }
479 }
480 physicsScene.DetailLog("{0},BSShapeMesh,getReference,mesh={1},size={2},lod={3}", prim.LocalID, retMesh, prim.Size, lod);
481 return retMesh;
482 }
483 public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
484 {
485 BSShape ret = null;
486 // If the underlying shape is native, the actual shape has not been build (waiting for asset)
487 // and we must create a copy of the native shape since they are never shared.
488 if (physShapeInfo.HasPhysicalShape && physShapeInfo.isNativeShape)
489 {
490 // TODO: decide when the native shapes should be freed. Check in Dereference?
491 ret = BSShapeNative.GetReference(pPhysicsScene, pPrim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
492 }
493 else
494 {
495 // Another reference to this shape is just counted.
496 IncrementReference();
497 ret = this;
498 }
499 return ret;
500 }
501 public override void Dereference(BSScene physicsScene)
502 {
503 lock (Meshes)
504 {
505 this.DecrementReference();
506 physicsScene.DetailLog("{0},BSShapeMesh.Dereference,shape={1}", BSScene.DetailLogZero, this);
507 // TODO: schedule aging and destruction of unused meshes.
508 }
509 }
510 // Loop through all the known meshes and return the description based on the physical address.
511 public static bool TryGetMeshByPtr(BulletShape pShape, out BSShapeMesh outMesh)
512 {
513 bool ret = false;
514 BSShapeMesh foundDesc = null;
515 lock (Meshes)
516 {
517 foreach (BSShapeMesh sm in Meshes.Values)
518 {
519 if (sm.physShapeInfo.ReferenceSame(pShape))
520 {
521 foundDesc = sm;
522 ret = true;
523 break;
524 }
525
526 }
527 }
528 outMesh = foundDesc;
529 return ret;
530 }
531
532 public delegate BulletShape CreateShapeCall(BulletWorld world, int indicesCount, int[] indices, int verticesCount, float[] vertices );
533 private BulletShape CreatePhysicalMesh(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey,
534 PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
535 {
536 return BSShapeMesh.CreatePhysicalMeshShape(physicsScene, prim, newMeshKey, pbs, size, lod,
537 (w, iC, i, vC, v) =>
538 {
539 shapeInfo.Vertices = vC;
540 return physicsScene.PE.CreateMeshShape(w, iC, i, vC, v);
541 });
542 }
543
544 // Code that uses the mesher to create the index/vertices info for a trimesh shape.
545 // This is used by the passed 'makeShape' call to create the Bullet mesh shape.
546 // The actual build call is passed so this logic can be used by several of the shapes that use a
547 // simple mesh as their base shape.
548 public static BulletShape CreatePhysicalMeshShape(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey,
549 PrimitiveBaseShape pbs, OMV.Vector3 size, float lod, CreateShapeCall makeShape)
550 {
551 BulletShape newShape = new BulletShape();
552
553 IMesh meshData = null;
554 lock (physicsScene.mesher)
555 {
556 meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod,
557 false, // say it is not physical so a bounding box is not built
558 false, // do not cache the mesh and do not use previously built versions
559 false,
560 false
561 );
562 }
563
564 if (meshData != null)
565 {
566 if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched)
567 {
568 // Release the fetched asset data once it has been used.
569 pbs.SculptData = new byte[0];
570 prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown;
571 }
572
573 int[] indices = meshData.getIndexListAsInt();
574 int realIndicesIndex = indices.Length;
575 float[] verticesAsFloats = meshData.getVertexListAsFloat();
576
577 if (BSParam.ShouldRemoveZeroWidthTriangles)
578 {
579 // Remove degenerate triangles. These are triangles with two of the vertices
580 // are the same. This is complicated by the problem that vertices are not
581 // made unique in sculpties so we have to compare the values in the vertex.
582 realIndicesIndex = 0;
583 for (int tri = 0; tri < indices.Length; tri += 3)
584 {
585 // Compute displacements into vertex array for each vertex of the triangle
586 int v1 = indices[tri + 0] * 3;
587 int v2 = indices[tri + 1] * 3;
588 int v3 = indices[tri + 2] * 3;
589 // Check to see if any two of the vertices are the same
590 if (!( ( verticesAsFloats[v1 + 0] == verticesAsFloats[v2 + 0]
591 && verticesAsFloats[v1 + 1] == verticesAsFloats[v2 + 1]
592 && verticesAsFloats[v1 + 2] == verticesAsFloats[v2 + 2])
593 || ( verticesAsFloats[v2 + 0] == verticesAsFloats[v3 + 0]
594 && verticesAsFloats[v2 + 1] == verticesAsFloats[v3 + 1]
595 && verticesAsFloats[v2 + 2] == verticesAsFloats[v3 + 2])
596 || ( verticesAsFloats[v1 + 0] == verticesAsFloats[v3 + 0]
597 && verticesAsFloats[v1 + 1] == verticesAsFloats[v3 + 1]
598 && verticesAsFloats[v1 + 2] == verticesAsFloats[v3 + 2]) )
599 )
600 {
601 // None of the vertices of the triangles are the same. This is a good triangle;
602 indices[realIndicesIndex + 0] = indices[tri + 0];
603 indices[realIndicesIndex + 1] = indices[tri + 1];
604 indices[realIndicesIndex + 2] = indices[tri + 2];
605 realIndicesIndex += 3;
606 }
607 }
608 }
609 physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,key={1},origTri={2},realTri={3},numVerts={4}",
610 BSScene.DetailLogZero, newMeshKey.ToString("X"), indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3);
611
612 if (realIndicesIndex != 0)
613 {
614 newShape = makeShape(physicsScene.World, realIndicesIndex, indices, verticesAsFloats.Length / 3, verticesAsFloats);
615 }
616 else
617 {
618 // Force the asset condition to 'failed' so we won't try to keep fetching and processing this mesh.
619 prim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedMeshing;
620 physicsScene.Logger.DebugFormat("{0} All mesh triangles degenerate. Prim={1}", LogHeader, UsefulPrimInfo(physicsScene, prim) );
621 physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,allDegenerate,key={1}", prim.LocalID, newMeshKey);
622 }
623 }
624 newShape.shapeKey = newMeshKey;
625
626 return newShape;
627 }
628}
629
630// ============================================================================================================
631// BSShapeHull is a physical shape representation htat is made up of many convex hulls.
632// The convex hulls are either supplied with the asset or are approximated by one of the
633// convex hull creation routines (in OpenSim or in Bullet).
634public class BSShapeHull : BSShape
635{
636#pragma warning disable 414
637 private static string LogHeader = "[BULLETSIM SHAPE HULL]";
638#pragma warning restore 414
639
640 public static Dictionary<System.UInt64, BSShapeHull> Hulls = new Dictionary<System.UInt64, BSShapeHull>();
641
642
643 public BSShapeHull(BulletShape pShape) : base(pShape)
644 {
645 }
646 public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
647 {
648 float lod;
649 System.UInt64 newHullKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
650
651 BSShapeHull retHull = null;
652 lock (Hulls)
653 {
654 if (Hulls.TryGetValue(newHullKey, out retHull))
655 {
656 // The mesh has already been created. Return a new reference to same.
657 retHull.IncrementReference();
658 }
659 else
660 {
661 retHull = new BSShapeHull(new BulletShape());
662 // An instance of this mesh has not been created. Build and remember same.
663 BulletShape newShape = retHull.CreatePhysicalHull(physicsScene, prim, newHullKey, prim.BaseShape, prim.Size, lod);
664
665 // Check to see if hull was created (might require an asset).
666 newShape = VerifyMeshCreated(physicsScene, newShape, prim);
667 if (!newShape.isNativeShape || prim.AssetFailed())
668 {
669 // If a mesh was what was created, remember the built shape for later sharing.
670 Hulls.Add(newHullKey, retHull);
671 }
672 retHull.physShapeInfo = newShape;
673 }
674 }
675 physicsScene.DetailLog("{0},BSShapeHull,getReference,hull={1},size={2},lod={3}", prim.LocalID, retHull, prim.Size, lod);
676 return retHull;
677 }
678 public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
679 {
680 BSShape ret = null;
681 // If the underlying shape is native, the actual shape has not been build (waiting for asset)
682 // and we must create a copy of the native shape since they are never shared.
683 if (physShapeInfo.HasPhysicalShape && physShapeInfo.isNativeShape)
684 {
685 // TODO: decide when the native shapes should be freed. Check in Dereference?
686 ret = BSShapeNative.GetReference(pPhysicsScene, pPrim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
687 }
688 else
689 {
690 // Another reference to this shape is just counted.
691 IncrementReference();
692 ret = this;
693 }
694 return ret;
695 }
696 public override void Dereference(BSScene physicsScene)
697 {
698 lock (Hulls)
699 {
700 this.DecrementReference();
701 physicsScene.DetailLog("{0},BSShapeHull.Dereference,shape={1}", BSScene.DetailLogZero, this);
702 // TODO: schedule aging and destruction of unused meshes.
703 }
704 }
705
706 List<ConvexResult> m_hulls;
707 private BulletShape CreatePhysicalHull(BSScene physicsScene, BSPhysObject prim, System.UInt64 newHullKey,
708 PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
709 {
710 BulletShape newShape = new BulletShape();
711
712 IMesh meshData = null;
713 List<List<OMV.Vector3>> allHulls = null;
714 lock (physicsScene.mesher)
715 {
716 // Pass true for physicalness as this prevents the creation of bounding box which is not needed
717 meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, true /* isPhysical */, false /* shouldCache */, false, false);
718
719 // If we should use the asset's hull info, fetch it out of the locked mesher
720 if (meshData != null && BSParam.ShouldUseAssetHulls)
721 {
722 Meshmerizer realMesher = physicsScene.mesher as Meshmerizer;
723 if (realMesher != null)
724 {
725 allHulls = realMesher.GetConvexHulls(size);
726 }
727 if (allHulls == null)
728 {
729 physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,assetHulls,noAssetHull", prim.LocalID);
730 }
731 }
732 }
733
734 // If there is hull data in the mesh asset, build the hull from that
735 if (allHulls != null && BSParam.ShouldUseAssetHulls)
736 {
737 int hullCount = allHulls.Count;
738 shapeInfo.HullCount = hullCount;
739 int totalVertices = 1; // include one for the count of the hulls
740 // Using the structure described for HACD hulls, create the memory sturcture
741 // to pass the hull data to the creater.
742 foreach (List<OMV.Vector3> hullVerts in allHulls)
743 {
744 totalVertices += 4; // add four for the vertex count and centroid
745 totalVertices += hullVerts.Count * 3; // one vertex is three dimensions
746 }
747 float[] convHulls = new float[totalVertices];
748
749 convHulls[0] = (float)hullCount;
750 int jj = 1;
751 int hullIndex = 0;
752 foreach (List<OMV.Vector3> hullVerts in allHulls)
753 {
754 convHulls[jj + 0] = hullVerts.Count;
755 convHulls[jj + 1] = 0f; // centroid x,y,z
756 convHulls[jj + 2] = 0f;
757 convHulls[jj + 3] = 0f;
758 jj += 4;
759 foreach (OMV.Vector3 oneVert in hullVerts)
760 {
761 convHulls[jj + 0] = oneVert.X;
762 convHulls[jj + 1] = oneVert.Y;
763 convHulls[jj + 2] = oneVert.Z;
764 jj += 3;
765 }
766 shapeInfo.SetVerticesPerHull(hullIndex, hullVerts.Count);
767 hullIndex++;
768 }
769
770 // create the hull data structure in Bullet
771 newShape = physicsScene.PE.CreateHullShape(physicsScene.World, hullCount, convHulls);
772
773 physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,assetHulls,hulls={1},totVert={2},shape={3}",
774 prim.LocalID, hullCount, totalVertices, newShape);
775 }
776
777 // If no hull specified in the asset and we should use Bullet's HACD approximation...
778 if (!newShape.HasPhysicalShape && BSParam.ShouldUseBulletHACD)
779 {
780 // Build the hull shape from an existing mesh shape.
781 // The mesh should have already been created in Bullet.
782 physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,bulletHACD,entry", prim.LocalID);
783 BSShape meshShape = BSShapeMesh.GetReference(physicsScene, true, prim);
784
785 if (meshShape.physShapeInfo.HasPhysicalShape)
786 {
787 HACDParams parms = new HACDParams();
788 parms.maxVerticesPerHull = BSParam.BHullMaxVerticesPerHull;
789 parms.minClusters = BSParam.BHullMinClusters;
790 parms.compacityWeight = BSParam.BHullCompacityWeight;
791 parms.volumeWeight = BSParam.BHullVolumeWeight;
792 parms.concavity = BSParam.BHullConcavity;
793 parms.addExtraDistPoints = BSParam.NumericBool(BSParam.BHullAddExtraDistPoints);
794 parms.addNeighboursDistPoints = BSParam.NumericBool(BSParam.BHullAddNeighboursDistPoints);
795 parms.addFacesPoints = BSParam.NumericBool(BSParam.BHullAddFacesPoints);
796 parms.shouldAdjustCollisionMargin = BSParam.NumericBool(BSParam.BHullShouldAdjustCollisionMargin);
797 parms.whichHACD = 0; // Use the HACD routine that comes with Bullet
798
799 physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,beforeCall", prim.LocalID, newShape.HasPhysicalShape);
800 newShape = physicsScene.PE.BuildHullShapeFromMesh(physicsScene.World, meshShape.physShapeInfo, parms);
801 physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,shape={1}", prim.LocalID, newShape);
802
803 // Now done with the mesh shape.
804 shapeInfo.HullCount = 1;
805 BSShapeMesh maybeMesh = meshShape as BSShapeMesh;
806 if (maybeMesh != null)
807 shapeInfo.SetVerticesPerHull(0, maybeMesh.shapeInfo.Vertices);
808 meshShape.Dereference(physicsScene);
809 }
810 physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,bulletHACD,exit,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape);
811 }
812
813 // If no other hull specifications, use our HACD hull approximation.
814 if (!newShape.HasPhysicalShape && meshData != null)
815 {
816 if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched)
817 {
818 // Release the fetched asset data once it has been used.
819 pbs.SculptData = new byte[0];
820 prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown;
821 }
822
823 int[] indices = meshData.getIndexListAsInt();
824 List<OMV.Vector3> vertices = meshData.getVertexList();
825
826 //format conversion from IMesh format to DecompDesc format
827 List<int> convIndices = new List<int>();
828 List<float3> convVertices = new List<float3>();
829 for (int ii = 0; ii < indices.GetLength(0); ii++)
830 {
831 convIndices.Add(indices[ii]);
832 }
833 foreach (OMV.Vector3 vv in vertices)
834 {
835 convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
836 }
837
838 uint maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplit;
839 if (BSParam.CSHullMaxDepthSplit != BSParam.CSHullMaxDepthSplitForSimpleShapes)
840 {
841 // Simple primitive shapes we know are convex so they are better implemented with
842 // fewer hulls.
843 // Check for simple shape (prim without cuts) and reduce split parameter if so.
844 if (BSShapeCollection.PrimHasNoCuts(pbs))
845 {
846 maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplitForSimpleShapes;
847 }
848 }
849
850 // setup and do convex hull conversion
851 m_hulls = new List<ConvexResult>();
852 DecompDesc dcomp = new DecompDesc();
853 dcomp.mIndices = convIndices;
854 dcomp.mVertices = convVertices;
855 dcomp.mDepth = maxDepthSplit;
856 dcomp.mCpercent = BSParam.CSHullConcavityThresholdPercent;
857 dcomp.mPpercent = BSParam.CSHullVolumeConservationThresholdPercent;
858 dcomp.mMaxVertices = (uint)BSParam.CSHullMaxVertices;
859 dcomp.mSkinWidth = BSParam.CSHullMaxSkinWidth;
860 ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
861 // create the hull into the _hulls variable
862 convexBuilder.process(dcomp);
863
864 physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalHull,key={1},inVert={2},inInd={3},split={4},hulls={5}",
865 BSScene.DetailLogZero, newHullKey, indices.GetLength(0), vertices.Count, maxDepthSplit, m_hulls.Count);
866
867 // Convert the vertices and indices for passing to unmanaged.
868 // The hull information is passed as a large floating point array.
869 // The format is:
870 // convHulls[0] = number of hulls
871 // convHulls[1] = number of vertices in first hull
872 // convHulls[2] = hull centroid X coordinate
873 // convHulls[3] = hull centroid Y coordinate
874 // convHulls[4] = hull centroid Z coordinate
875 // convHulls[5] = first hull vertex X
876 // convHulls[6] = first hull vertex Y
877 // convHulls[7] = first hull vertex Z
878 // convHulls[8] = second hull vertex X
879 // ...
880 // convHulls[n] = number of vertices in second hull
881 // convHulls[n+1] = second hull centroid X coordinate
882 // ...
883 //
884 // TODO: is is very inefficient. Someday change the convex hull generator to return
885 // data structures that do not need to be converted in order to pass to Bullet.
886 // And maybe put the values directly into pinned memory rather than marshaling.
887 int hullCount = m_hulls.Count;
888 int totalVertices = 1; // include one for the count of the hulls
889 foreach (ConvexResult cr in m_hulls)
890 {
891 totalVertices += 4; // add four for the vertex count and centroid
892 totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
893 }
894 float[] convHulls = new float[totalVertices];
895
896 convHulls[0] = (float)hullCount;
897 int jj = 1;
898 foreach (ConvexResult cr in m_hulls)
899 {
900 // copy vertices for index access
901 float3[] verts = new float3[cr.HullVertices.Count];
902 int kk = 0;
903 foreach (float3 ff in cr.HullVertices)
904 {
905 verts[kk++] = ff;
906 }
907
908 // add to the array one hull's worth of data
909 convHulls[jj++] = cr.HullIndices.Count;
910 convHulls[jj++] = 0f; // centroid x,y,z
911 convHulls[jj++] = 0f;
912 convHulls[jj++] = 0f;
913 foreach (int ind in cr.HullIndices)
914 {
915 convHulls[jj++] = verts[ind].x;
916 convHulls[jj++] = verts[ind].y;
917 convHulls[jj++] = verts[ind].z;
918 }
919 }
920 // create the hull data structure in Bullet
921 newShape = physicsScene.PE.CreateHullShape(physicsScene.World, hullCount, convHulls);
922 }
923 newShape.shapeKey = newHullKey;
924 return newShape;
925 }
926 // Callback from convex hull creater with a newly created hull.
927 // Just add it to our collection of hulls for this shape.
928 private void HullReturn(ConvexResult result)
929 {
930 m_hulls.Add(result);
931 return;
932 }
933 // Loop through all the known hulls and return the description based on the physical address.
934 public static bool TryGetHullByPtr(BulletShape pShape, out BSShapeHull outHull)
935 {
936 bool ret = false;
937 BSShapeHull foundDesc = null;
938 lock (Hulls)
939 {
940 foreach (BSShapeHull sh in Hulls.Values)
941 {
942 if (sh.physShapeInfo.ReferenceSame(pShape))
943 {
944 foundDesc = sh;
945 ret = true;
946 break;
947 }
948
949 }
950 }
951 outHull = foundDesc;
952 return ret;
953 }
954}
955
956// ============================================================================================================
957// BSShapeCompound is a wrapper for the Bullet compound shape which is built from multiple, separate
958// meshes. Used by BulletSim for complex shapes like linksets.
959public class BSShapeCompound : BSShape
960{
961 private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]";
962 public static Dictionary<string, BSShapeCompound> CompoundShapes = new Dictionary<string, BSShapeCompound>();
963
964 public BSShapeCompound(BulletShape pShape) : base(pShape)
965 {
966 }
967 public static BSShape GetReference(BSScene physicsScene)
968 {
969 // Base compound shapes are not shared so this returns a raw shape.
970 // A built compound shape can be reused in linksets.
971 BSShapeCompound ret = new BSShapeCompound(CreatePhysicalCompoundShape(physicsScene));
972 CompoundShapes.Add(ret.AddrString, ret);
973 return ret;
974 }
975 public override BSShape GetReference(BSScene physicsScene, BSPhysObject prim)
976 {
977 // Calling this reference means we want another handle to an existing compound shape
978 // (usually linksets) so return this copy.
979 IncrementReference();
980 return this;
981 }
982 // Dereferencing a compound shape releases the hold on all the child shapes.
983 public override void Dereference(BSScene physicsScene)
984 {
985 lock (physShapeInfo)
986 {
987 this.DecrementReference();
988 physicsScene.DetailLog("{0},BSShapeCompound.Dereference,shape={1}", BSScene.DetailLogZero, this);
989 if (referenceCount <= 0)
990 {
991 if (!physicsScene.PE.IsCompound(physShapeInfo))
992 {
993 // Failed the sanity check!!
994 physicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}",
995 LogHeader, physShapeInfo.shapeType, physShapeInfo.AddrString);
996 physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}",
997 BSScene.DetailLogZero, physShapeInfo.shapeType, physShapeInfo.AddrString);
998 return;
999 }
1000
1001 int numChildren = physicsScene.PE.GetNumberOfCompoundChildren(physShapeInfo);
1002 physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}",
1003 BSScene.DetailLogZero, physShapeInfo, numChildren);
1004
1005 // Loop through all the children dereferencing each.
1006 for (int ii = numChildren - 1; ii >= 0; ii--)
1007 {
1008 BulletShape childShape = physicsScene.PE.RemoveChildShapeFromCompoundShapeIndex(physShapeInfo, ii);
1009 DereferenceAnonCollisionShape(physicsScene, childShape);
1010 }
1011
1012 lock (CompoundShapes)
1013 CompoundShapes.Remove(physShapeInfo.AddrString);
1014 physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo);
1015 }
1016 }
1017 }
1018 public static bool TryGetCompoundByPtr(BulletShape pShape, out BSShapeCompound outCompound)
1019 {
1020 lock (CompoundShapes)
1021 {
1022 string addr = pShape.AddrString;
1023 return CompoundShapes.TryGetValue(addr, out outCompound);
1024 }
1025 }
1026 private static BulletShape CreatePhysicalCompoundShape(BSScene physicsScene)
1027 {
1028 BulletShape cShape = physicsScene.PE.CreateCompoundShape(physicsScene.World, false);
1029 return cShape;
1030 }
1031 // Sometimes we have a pointer to a collision shape but don't know what type it is.
1032 // Figure out type and call the correct dereference routine.
1033 // Called at taint-time.
1034 private void DereferenceAnonCollisionShape(BSScene physicsScene, BulletShape pShape)
1035 {
1036 // TODO: figure a better way to go through all the shape types and find a possible instance.
1037 physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,shape={1}",
1038 BSScene.DetailLogZero, pShape);
1039 BSShapeMesh meshDesc;
1040 if (BSShapeMesh.TryGetMeshByPtr(pShape, out meshDesc))
1041 {
1042 meshDesc.Dereference(physicsScene);
1043 // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundMesh,shape={1}", BSScene.DetailLogZero, pShape);
1044 }
1045 else
1046 {
1047 BSShapeHull hullDesc;
1048 if (BSShapeHull.TryGetHullByPtr(pShape, out hullDesc))
1049 {
1050 hullDesc.Dereference(physicsScene);
1051 // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundHull,shape={1}", BSScene.DetailLogZero, pShape);
1052 }
1053 else
1054 {
1055 BSShapeConvexHull chullDesc;
1056 if (BSShapeConvexHull.TryGetConvexHullByPtr(pShape, out chullDesc))
1057 {
1058 chullDesc.Dereference(physicsScene);
1059 // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundConvexHull,shape={1}", BSScene.DetailLogZero, pShape);
1060 }
1061 else
1062 {
1063 BSShapeGImpact gImpactDesc;
1064 if (BSShapeGImpact.TryGetGImpactByPtr(pShape, out gImpactDesc))
1065 {
1066 gImpactDesc.Dereference(physicsScene);
1067 // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundgImpact,shape={1}", BSScene.DetailLogZero, pShape);
1068 }
1069 else
1070 {
1071 // Didn't find it in the lists of specific types. It could be compound.
1072 BSShapeCompound compoundDesc;
1073 if (BSShapeCompound.TryGetCompoundByPtr(pShape, out compoundDesc))
1074 {
1075 compoundDesc.Dereference(physicsScene);
1076 // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,recursiveCompoundShape,shape={1}", BSScene.DetailLogZero, pShape);
1077 }
1078 else
1079 {
1080 // If none of the above, maybe it is a simple native shape.
1081 if (physicsScene.PE.IsNativeShape(pShape))
1082 {
1083 // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,assumingNative,shape={1}", BSScene.DetailLogZero, pShape);
1084 BSShapeNative nativeShape = new BSShapeNative(pShape);
1085 nativeShape.Dereference(physicsScene);
1086 }
1087 else
1088 {
1089 physicsScene.Logger.WarnFormat("{0} DereferenceAnonCollisionShape. Did not find shape. {1}",
1090 LogHeader, pShape);
1091 }
1092 }
1093 }
1094 }
1095 }
1096 }
1097 }
1098}
1099
1100// ============================================================================================================
1101// BSShapeConvexHull is a wrapper for a Bullet single convex hull. A BSShapeHull contains multiple convex
1102// hull shapes. This is used for simple prims that are convex and thus can be made into a simple
1103// collision shape (a single hull). More complex physical shapes will be BSShapeHull's.
1104public class BSShapeConvexHull : BSShape
1105{
1106#pragma warning disable 414
1107 private static string LogHeader = "[BULLETSIM SHAPE CONVEX HULL]";
1108#pragma warning restore 414
1109
1110 public static Dictionary<System.UInt64, BSShapeConvexHull> ConvexHulls = new Dictionary<System.UInt64, BSShapeConvexHull>();
1111
1112 public BSShapeConvexHull(BulletShape pShape) : base(pShape)
1113 {
1114 }
1115 public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
1116 {
1117 float lod;
1118 System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
1119
1120 physicsScene.DetailLog("{0},BSShapeConvexHull,getReference,newKey={1},size={2},lod={3}",
1121 prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod);
1122
1123 BSShapeConvexHull retConvexHull = null;
1124 lock (ConvexHulls)
1125 {
1126 if (ConvexHulls.TryGetValue(newMeshKey, out retConvexHull))
1127 {
1128 // The mesh has already been created. Return a new reference to same.
1129 retConvexHull.IncrementReference();
1130 }
1131 else
1132 {
1133 retConvexHull = new BSShapeConvexHull(new BulletShape());
1134 BulletShape convexShape = null;
1135
1136 // Get a handle to a mesh to build the hull from
1137 BSShape baseMesh = BSShapeMesh.GetReference(physicsScene, false /* forceRebuild */, prim);
1138 if (baseMesh.physShapeInfo.isNativeShape)
1139 {
1140 // We get here if the mesh was not creatable. Could be waiting for an asset from the disk.
1141 // In the short term, we return the native shape and a later ForceBodyShapeRebuild should
1142 // get back to this code with a buildable mesh.
1143 // TODO: not sure the temp native shape is freed when the mesh is rebuilt. When does this get freed?
1144 convexShape = baseMesh.physShapeInfo;
1145 }
1146 else
1147 {
1148 convexShape = physicsScene.PE.BuildConvexHullShapeFromMesh(physicsScene.World, baseMesh.physShapeInfo);
1149 convexShape.shapeKey = newMeshKey;
1150 ConvexHulls.Add(convexShape.shapeKey, retConvexHull);
1151 physicsScene.DetailLog("{0},BSShapeConvexHull.GetReference,addingNewlyCreatedShape,shape={1}",
1152 BSScene.DetailLogZero, convexShape);
1153 }
1154
1155 // Done with the base mesh
1156 baseMesh.Dereference(physicsScene);
1157
1158 retConvexHull.physShapeInfo = convexShape;
1159 }
1160 }
1161 return retConvexHull;
1162 }
1163 public override BSShape GetReference(BSScene physicsScene, BSPhysObject prim)
1164 {
1165 // Calling this reference means we want another handle to an existing shape
1166 // (usually linksets) so return this copy.
1167 IncrementReference();
1168 return this;
1169 }
1170 // Dereferencing a compound shape releases the hold on all the child shapes.
1171 public override void Dereference(BSScene physicsScene)
1172 {
1173 lock (ConvexHulls)
1174 {
1175 this.DecrementReference();
1176 physicsScene.DetailLog("{0},BSShapeConvexHull.Dereference,shape={1}", BSScene.DetailLogZero, this);
1177 // TODO: schedule aging and destruction of unused meshes.
1178 }
1179 }
1180 // Loop through all the known hulls and return the description based on the physical address.
1181 public static bool TryGetConvexHullByPtr(BulletShape pShape, out BSShapeConvexHull outHull)
1182 {
1183 bool ret = false;
1184 BSShapeConvexHull foundDesc = null;
1185 lock (ConvexHulls)
1186 {
1187 foreach (BSShapeConvexHull sh in ConvexHulls.Values)
1188 {
1189 if (sh.physShapeInfo.ReferenceSame(pShape))
1190 {
1191 foundDesc = sh;
1192 ret = true;
1193 break;
1194 }
1195
1196 }
1197 }
1198 outHull = foundDesc;
1199 return ret;
1200 }
1201}
1202// ============================================================================================================
1203// BSShapeGImpact is a wrapper for the Bullet GImpact shape which is a collision mesh shape that
1204// can handle concave as well as convex shapes. Much slower computationally but creates smoother
1205// shapes than multiple convex hull approximations.
1206public class BSShapeGImpact : BSShape
1207{
1208#pragma warning disable 414
1209 private static string LogHeader = "[BULLETSIM SHAPE GIMPACT]";
1210#pragma warning restore 414
1211
1212 public static Dictionary<System.UInt64, BSShapeGImpact> GImpacts = new Dictionary<System.UInt64, BSShapeGImpact>();
1213
1214 public BSShapeGImpact(BulletShape pShape) : base(pShape)
1215 {
1216 }
1217 public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
1218 {
1219 float lod;
1220 System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
1221
1222 physicsScene.DetailLog("{0},BSShapeGImpact,getReference,newKey={1},size={2},lod={3}",
1223 prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod);
1224
1225 BSShapeGImpact retGImpact = null;
1226 lock (GImpacts)
1227 {
1228 if (GImpacts.TryGetValue(newMeshKey, out retGImpact))
1229 {
1230 // The mesh has already been created. Return a new reference to same.
1231 retGImpact.IncrementReference();
1232 }
1233 else
1234 {
1235 retGImpact = new BSShapeGImpact(new BulletShape());
1236 BulletShape newShape = retGImpact.CreatePhysicalGImpact(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod);
1237
1238 // Check to see if mesh was created (might require an asset).
1239 newShape = VerifyMeshCreated(physicsScene, newShape, prim);
1240 newShape.shapeKey = newMeshKey;
1241 if (!newShape.isNativeShape || prim.AssetFailed())
1242 {
1243 // If a mesh was what was created, remember the built shape for later sharing.
1244 // Also note that if meshing failed we put it in the mesh list as there is nothing
1245 // else to do about the mesh.
1246 GImpacts.Add(newMeshKey, retGImpact);
1247 }
1248
1249 retGImpact.physShapeInfo = newShape;
1250 }
1251 }
1252 return retGImpact;
1253 }
1254
1255 private BulletShape CreatePhysicalGImpact(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey,
1256 PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
1257 {
1258 return BSShapeMesh.CreatePhysicalMeshShape(physicsScene, prim, newMeshKey, pbs, size, lod,
1259 (w, iC, i, vC, v) =>
1260 {
1261 shapeInfo.Vertices = vC;
1262 return physicsScene.PE.CreateGImpactShape(w, iC, i, vC, v);
1263 });
1264 }
1265
1266 public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
1267 {
1268 BSShape ret = null;
1269 // If the underlying shape is native, the actual shape has not been build (waiting for asset)
1270 // and we must create a copy of the native shape since they are never shared.
1271 if (physShapeInfo.HasPhysicalShape && physShapeInfo.isNativeShape)
1272 {
1273 // TODO: decide when the native shapes should be freed. Check in Dereference?
1274 ret = BSShapeNative.GetReference(pPhysicsScene, pPrim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
1275 }
1276 else
1277 {
1278 // Another reference to this shape is just counted.
1279 IncrementReference();
1280 ret = this;
1281 }
1282 return ret;
1283 }
1284 // Dereferencing a compound shape releases the hold on all the child shapes.
1285 public override void Dereference(BSScene physicsScene)
1286 {
1287 lock (GImpacts)
1288 {
1289 this.DecrementReference();
1290 physicsScene.DetailLog("{0},BSShapeGImpact.Dereference,shape={1}", BSScene.DetailLogZero, this);
1291 // TODO: schedule aging and destruction of unused meshes.
1292 }
1293 }
1294 // Loop through all the known hulls and return the description based on the physical address.
1295 public static bool TryGetGImpactByPtr(BulletShape pShape, out BSShapeGImpact outHull)
1296 {
1297 bool ret = false;
1298 BSShapeGImpact foundDesc = null;
1299 lock (GImpacts)
1300 {
1301 foreach (BSShapeGImpact sh in GImpacts.Values)
1302 {
1303 if (sh.physShapeInfo.ReferenceSame(pShape))
1304 {
1305 foundDesc = sh;
1306 ret = true;
1307 break;
1308 }
1309
1310 }
1311 }
1312 outHull = foundDesc;
1313 return ret;
1314 }
1315}
1316
1317// ============================================================================================================
1318// BSShapeAvatar is a specialized mesh shape for avatars.
1319public class BSShapeAvatar : BSShape
1320{
1321#pragma warning disable 414
1322 private static string LogHeader = "[BULLETSIM SHAPE AVATAR]";
1323#pragma warning restore 414
1324
1325 public BSShapeAvatar()
1326 : base()
1327 {
1328 }
1329 public static BSShape GetReference(BSPhysObject prim)
1330 {
1331 return new BSShapeNull();
1332 }
1333 public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
1334 {
1335 return new BSShapeNull();
1336 }
1337 public override void Dereference(BSScene physicsScene) { }
1338
1339 // From the front:
1340 // A---A
1341 // / \
1342 // B-------B
1343 // / \ +Z
1344 // C-----------C |
1345 // \ / -Y --+-- +Y
1346 // \ / |
1347 // \ / -Z
1348 // D-----D
1349 // \ /
1350 // E-E
1351
1352 // From the top A and E are just lines.
1353 // B, C and D are hexagons:
1354 //
1355 // C1--C2 +X
1356 // / \ |
1357 // C0 C3 -Y --+-- +Y
1358 // \ / |
1359 // C5--C4 -X
1360
1361 // Zero goes directly through the middle so the offsets are from that middle axis
1362 // and up and down from a middle horizon (A and E are the same distance from the zero).
1363 // The height, width and depth is one. All scaling is done by the simulator.
1364
1365 // Z component -- how far the level is from the middle zero
1366 private const float Aup = 0.5f;
1367 private const float Bup = 0.4f;
1368 private const float Cup = 0.3f;
1369 private const float Dup = -0.4f;
1370 private const float Eup = -0.5f;
1371
1372 // Y component -- distance from center to x0 and x3
1373 private const float Awid = 0.25f;
1374 private const float Bwid = 0.3f;
1375 private const float Cwid = 0.5f;
1376 private const float Dwid = 0.3f;
1377 private const float Ewid = 0.2f;
1378
1379 // Y component -- distance from center to x1, x2, x4 and x5
1380 private const float Afwid = 0.0f;
1381 private const float Bfwid = 0.2f;
1382 private const float Cfwid = 0.4f;
1383 private const float Dfwid = 0.2f;
1384 private const float Efwid = 0.0f;
1385
1386 // X component -- distance from zero to the front or back of a level
1387 private const float Adep = 0f;
1388 private const float Bdep = 0.3f;
1389 private const float Cdep = 0.5f;
1390 private const float Ddep = 0.2f;
1391 private const float Edep = 0f;
1392
1393 private OMV.Vector3[] avatarVertices = {
1394 new OMV.Vector3( 0.0f, -Awid, Aup), // A0
1395 new OMV.Vector3( 0.0f, +Awid, Aup), // A3
1396
1397 new OMV.Vector3( 0.0f, -Bwid, Bup), // B0
1398 new OMV.Vector3(+Bdep, -Bfwid, Bup), // B1
1399 new OMV.Vector3(+Bdep, +Bfwid, Bup), // B2
1400 new OMV.Vector3( 0.0f, +Bwid, Bup), // B3
1401 new OMV.Vector3(-Bdep, +Bfwid, Bup), // B4
1402 new OMV.Vector3(-Bdep, -Bfwid, Bup), // B5
1403
1404 new OMV.Vector3( 0.0f, -Cwid, Cup), // C0
1405 new OMV.Vector3(+Cdep, -Cfwid, Cup), // C1
1406 new OMV.Vector3(+Cdep, +Cfwid, Cup), // C2
1407 new OMV.Vector3( 0.0f, +Cwid, Cup), // C3
1408 new OMV.Vector3(-Cdep, +Cfwid, Cup), // C4
1409 new OMV.Vector3(-Cdep, -Cfwid, Cup), // C5
1410
1411 new OMV.Vector3( 0.0f, -Dwid, Dup), // D0
1412 new OMV.Vector3(+Ddep, -Dfwid, Dup), // D1
1413 new OMV.Vector3(+Ddep, +Dfwid, Dup), // D2
1414 new OMV.Vector3( 0.0f, +Dwid, Dup), // D3
1415 new OMV.Vector3(-Ddep, +Dfwid, Dup), // D4
1416 new OMV.Vector3(-Ddep, -Dfwid, Dup), // D5
1417
1418 new OMV.Vector3( 0.0f, -Ewid, Eup), // E0
1419 new OMV.Vector3( 0.0f, +Ewid, Eup), // E3
1420 };
1421
1422 // Offsets of the vertices in the vertices array
1423 private enum Ind : int
1424 {
1425 A0, A3,
1426 B0, B1, B2, B3, B4, B5,
1427 C0, C1, C2, C3, C4, C5,
1428 D0, D1, D2, D3, D4, D5,
1429 E0, E3
1430 }
1431
1432 // Comments specify trianges and quads in clockwise direction
1433 private Ind[] avatarIndices = {
1434 Ind.A0, Ind.B0, Ind.B1, // A0,B0,B1
1435 Ind.A0, Ind.B1, Ind.B2, Ind.B2, Ind.A3, Ind.A0, // A0,B1,B2,A3
1436 Ind.A3, Ind.B2, Ind.B3, // A3,B2,B3
1437 Ind.A3, Ind.B3, Ind.B4, // A3,B3,B4
1438 Ind.A3, Ind.B4, Ind.B5, Ind.B5, Ind.A0, Ind.A3, // A3,B4,B5,A0
1439 Ind.A0, Ind.B5, Ind.B0, // A0,B5,B0
1440
1441 Ind.B0, Ind.C0, Ind.C1, Ind.C1, Ind.B1, Ind.B0, // B0,C0,C1,B1
1442 Ind.B1, Ind.C1, Ind.C2, Ind.C2, Ind.B2, Ind.B1, // B1,C1,C2,B2
1443 Ind.B2, Ind.C2, Ind.C3, Ind.C3, Ind.B3, Ind.B2, // B2,C2,C3,B3
1444 Ind.B3, Ind.C3, Ind.C4, Ind.C4, Ind.B4, Ind.B3, // B3,C3,C4,B4
1445 Ind.B4, Ind.C4, Ind.C5, Ind.C5, Ind.B5, Ind.B4, // B4,C4,C5,B5
1446 Ind.B5, Ind.C5, Ind.C0, Ind.C0, Ind.B0, Ind.B5, // B5,C5,C0,B0
1447
1448 Ind.C0, Ind.D0, Ind.D1, Ind.D1, Ind.C1, Ind.C0, // C0,D0,D1,C1
1449 Ind.C1, Ind.D1, Ind.D2, Ind.D2, Ind.C2, Ind.C1, // C1,D1,D2,C2
1450 Ind.C2, Ind.D2, Ind.D3, Ind.D3, Ind.C3, Ind.C2, // C2,D2,D3,C3
1451 Ind.C3, Ind.D3, Ind.D4, Ind.D4, Ind.C4, Ind.C3, // C3,D3,D4,C4
1452 Ind.C4, Ind.D4, Ind.D5, Ind.D5, Ind.C5, Ind.C4, // C4,D4,D5,C5
1453 Ind.C5, Ind.D5, Ind.D0, Ind.D0, Ind.C0, Ind.C5, // C5,D5,D0,C0
1454
1455 Ind.E0, Ind.D0, Ind.D1, // E0,D0,D1
1456 Ind.E0, Ind.D1, Ind.D2, Ind.D2, Ind.E3, Ind.E0, // E0,D1,D2,E3
1457 Ind.E3, Ind.D2, Ind.D3, // E3,D2,D3
1458 Ind.E3, Ind.D3, Ind.D4, // E3,D3,D4
1459 Ind.E3, Ind.D4, Ind.D5, Ind.D5, Ind.E0, Ind.E3, // E3,D4,D5,E0
1460 Ind.E0, Ind.D5, Ind.D0, // E0,D5,D0
1461
1462 };
1463
1464}
1465}