diff options
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs')
-rwxr-xr-x | OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs | 756 |
1 files changed, 756 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs new file mode 100755 index 0000000..d189f1d --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs | |||
@@ -0,0 +1,756 @@ | |||
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 | using System; | ||
28 | using System.Collections.Generic; | ||
29 | using System.Text; | ||
30 | using OMV = OpenMetaverse; | ||
31 | using OpenSim.Framework; | ||
32 | using OpenSim.Region.Physics.Manager; | ||
33 | using OpenSim.Region.Physics.ConvexDecompositionDotNet; | ||
34 | |||
35 | namespace OpenSim.Region.Physics.BulletSPlugin | ||
36 | { | ||
37 | public class BSShapeCollection : IDisposable | ||
38 | { | ||
39 | // private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]"; | ||
40 | |||
41 | protected BSScene PhysicsScene { get; set; } | ||
42 | |||
43 | private Object m_collectionActivityLock = new Object(); | ||
44 | |||
45 | // Description of a Mesh | ||
46 | private struct MeshDesc | ||
47 | { | ||
48 | public IntPtr ptr; | ||
49 | public int referenceCount; | ||
50 | public DateTime lastReferenced; | ||
51 | } | ||
52 | |||
53 | // Description of a hull. | ||
54 | // Meshes and hulls have the same shape hash key but we only need hulls for efficient physical objects | ||
55 | private struct HullDesc | ||
56 | { | ||
57 | public IntPtr ptr; | ||
58 | public int referenceCount; | ||
59 | public DateTime lastReferenced; | ||
60 | } | ||
61 | |||
62 | private struct BodyDesc | ||
63 | { | ||
64 | public IntPtr ptr; | ||
65 | // Bodies are only used once so reference count is always either one or zero | ||
66 | public int referenceCount; | ||
67 | public DateTime lastReferenced; | ||
68 | } | ||
69 | |||
70 | private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>(); | ||
71 | private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>(); | ||
72 | private Dictionary<uint, BodyDesc> Bodies = new Dictionary<uint, BodyDesc>(); | ||
73 | |||
74 | public BSShapeCollection(BSScene physScene) | ||
75 | { | ||
76 | PhysicsScene = physScene; | ||
77 | } | ||
78 | |||
79 | public void Dispose() | ||
80 | { | ||
81 | // TODO!!!!!!!!! | ||
82 | } | ||
83 | |||
84 | // Callbacks called just before either the body or shape is destroyed. | ||
85 | // Mostly used for changing bodies out from under Linksets. | ||
86 | // Useful for other cases where parameters need saving. | ||
87 | // Passing 'null' says no callback. | ||
88 | public delegate void ShapeDestructionCallback(BulletShape shape); | ||
89 | public delegate void BodyDestructionCallback(BulletBody body); | ||
90 | |||
91 | // Called to update/change the body and shape for an object. | ||
92 | // First checks the shape and updates that if necessary then makes | ||
93 | // sure the body is of the right type. | ||
94 | // Return 'true' if either the body or the shape changed. | ||
95 | // Called at taint-time!! | ||
96 | public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim, | ||
97 | ShapeData shapeData, PrimitiveBaseShape pbs, | ||
98 | ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback) | ||
99 | { | ||
100 | bool ret = false; | ||
101 | |||
102 | // This lock could probably be pushed down lower but building shouldn't take long | ||
103 | lock (m_collectionActivityLock) | ||
104 | { | ||
105 | // Do we have the correct geometry for this type of object? | ||
106 | // Updates prim.BSShape with information/pointers to requested shape | ||
107 | bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, shapeCallback); | ||
108 | // If we had to select a new shape geometry for the object, | ||
109 | // rebuild the body around it. | ||
110 | // Updates prim.BSBody with information/pointers to requested body | ||
111 | bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, | ||
112 | prim.BSShape, shapeData, bodyCallback); | ||
113 | ret = newGeom || newBody; | ||
114 | } | ||
115 | DetailLog("{0},BSShapeCollection.GetBodyAndShape,force={1},ret={2},body={3},shape={4}", | ||
116 | prim.LocalID, forceRebuild, ret, prim.BSBody, prim.BSShape); | ||
117 | |||
118 | return ret; | ||
119 | } | ||
120 | |||
121 | // Track another user of a body | ||
122 | // We presume the caller has allocated the body. | ||
123 | // Bodies only have one user so the reference count is either 1 or 0. | ||
124 | public void ReferenceBody(BulletBody body, bool inTaintTime) | ||
125 | { | ||
126 | lock (m_collectionActivityLock) | ||
127 | { | ||
128 | BodyDesc bodyDesc; | ||
129 | if (Bodies.TryGetValue(body.ID, out bodyDesc)) | ||
130 | { | ||
131 | bodyDesc.referenceCount++; | ||
132 | DetailLog("{0},BSShapeCollection.ReferenceBody,existingBody,body={1},ref={2}", body.ID, body, bodyDesc.referenceCount); | ||
133 | } | ||
134 | else | ||
135 | { | ||
136 | // New entry | ||
137 | bodyDesc.ptr = body.ptr; | ||
138 | bodyDesc.referenceCount = 1; | ||
139 | DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,ref={2}", | ||
140 | body.ID, body, bodyDesc.referenceCount); | ||
141 | BSScene.TaintCallback createOperation = delegate() | ||
142 | { | ||
143 | if (!BulletSimAPI.IsInWorld2(body.ptr)) | ||
144 | { | ||
145 | BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr); | ||
146 | DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", | ||
147 | body.ID, body); | ||
148 | } | ||
149 | }; | ||
150 | if (inTaintTime) | ||
151 | createOperation(); | ||
152 | else | ||
153 | PhysicsScene.TaintedObject("BSShapeCollection.ReferenceBody", createOperation); | ||
154 | } | ||
155 | bodyDesc.lastReferenced = System.DateTime.Now; | ||
156 | Bodies[body.ID] = bodyDesc; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | // Release the usage of a body. | ||
161 | // Called when releasing use of a BSBody. BSShape is handled separately. | ||
162 | public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback ) | ||
163 | { | ||
164 | if (body.ptr == IntPtr.Zero) | ||
165 | return; | ||
166 | |||
167 | lock (m_collectionActivityLock) | ||
168 | { | ||
169 | BodyDesc bodyDesc; | ||
170 | if (Bodies.TryGetValue(body.ID, out bodyDesc)) | ||
171 | { | ||
172 | bodyDesc.referenceCount--; | ||
173 | bodyDesc.lastReferenced = System.DateTime.Now; | ||
174 | Bodies[body.ID] = bodyDesc; | ||
175 | DetailLog("{0},BSShapeCollection.DereferenceBody,ref={1}", body.ID, bodyDesc.referenceCount); | ||
176 | |||
177 | // If body is no longer being used, free it -- bodies can never be shared. | ||
178 | if (bodyDesc.referenceCount == 0) | ||
179 | { | ||
180 | Bodies.Remove(body.ID); | ||
181 | BSScene.TaintCallback removeOperation = delegate() | ||
182 | { | ||
183 | DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}, inTaintTime={2}", | ||
184 | body.ID, body.ptr.ToString("X"), inTaintTime); | ||
185 | // If the caller needs to know the old body is going away, pass the event up. | ||
186 | if (bodyCallback != null) bodyCallback(body); | ||
187 | |||
188 | // It may have already been removed from the world in which case the next is a NOOP. | ||
189 | BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr); | ||
190 | |||
191 | // Zero any reference to the shape so it is not freed when the body is deleted. | ||
192 | BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero); | ||
193 | BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr); | ||
194 | }; | ||
195 | // If already in taint-time, do the operations now. Otherwise queue for later. | ||
196 | if (inTaintTime) | ||
197 | removeOperation(); | ||
198 | else | ||
199 | PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation); | ||
200 | } | ||
201 | } | ||
202 | else | ||
203 | { | ||
204 | DetailLog("{0},BSShapeCollection.DereferenceBody,DID NOT FIND BODY", body.ID, bodyDesc.referenceCount); | ||
205 | } | ||
206 | } | ||
207 | } | ||
208 | |||
209 | // Track the datastructures and use count for a shape. | ||
210 | // When creating a hull, this is called first to reference the mesh | ||
211 | // and then again to reference the hull. | ||
212 | // Meshes and hulls for the same shape have the same hash key. | ||
213 | // NOTE that native shapes are not added to the mesh list or removed. | ||
214 | // Returns 'true' if this is the initial reference to the shape. Otherwise reused. | ||
215 | private bool ReferenceShape(BulletShape shape) | ||
216 | { | ||
217 | bool ret = false; | ||
218 | switch (shape.type) | ||
219 | { | ||
220 | case ShapeData.PhysicsShapeType.SHAPE_MESH: | ||
221 | MeshDesc meshDesc; | ||
222 | if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) | ||
223 | { | ||
224 | // There is an existing instance of this mesh. | ||
225 | meshDesc.referenceCount++; | ||
226 | DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}", | ||
227 | BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); | ||
228 | } | ||
229 | else | ||
230 | { | ||
231 | // This is a new reference to a mesh | ||
232 | meshDesc.ptr = shape.ptr; | ||
233 | // We keep a reference to the underlying IMesh data so a hull can be built | ||
234 | meshDesc.referenceCount = 1; | ||
235 | DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}", | ||
236 | BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); | ||
237 | ret = true; | ||
238 | } | ||
239 | meshDesc.lastReferenced = System.DateTime.Now; | ||
240 | Meshes[shape.shapeKey] = meshDesc; | ||
241 | break; | ||
242 | case ShapeData.PhysicsShapeType.SHAPE_HULL: | ||
243 | HullDesc hullDesc; | ||
244 | if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) | ||
245 | { | ||
246 | // There is an existing instance of this hull. | ||
247 | hullDesc.referenceCount++; | ||
248 | DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}", | ||
249 | BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); | ||
250 | } | ||
251 | else | ||
252 | { | ||
253 | // This is a new reference to a hull | ||
254 | hullDesc.ptr = shape.ptr; | ||
255 | hullDesc.referenceCount = 1; | ||
256 | DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}", | ||
257 | BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); | ||
258 | ret = true; | ||
259 | |||
260 | } | ||
261 | hullDesc.lastReferenced = System.DateTime.Now; | ||
262 | Hulls[shape.shapeKey] = hullDesc; | ||
263 | break; | ||
264 | case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: | ||
265 | break; | ||
266 | default: | ||
267 | // Native shapes are not tracked and they don't go into any list | ||
268 | break; | ||
269 | } | ||
270 | return ret; | ||
271 | } | ||
272 | |||
273 | // Release the usage of a shape. | ||
274 | // The collisionObject is released since it is a copy of the real collision shape. | ||
275 | public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback) | ||
276 | { | ||
277 | if (shape.ptr == IntPtr.Zero) | ||
278 | return; | ||
279 | |||
280 | BSScene.TaintCallback dereferenceOperation = delegate() | ||
281 | { | ||
282 | switch (shape.type) | ||
283 | { | ||
284 | case ShapeData.PhysicsShapeType.SHAPE_HULL: | ||
285 | DereferenceHull(shape, shapeCallback); | ||
286 | break; | ||
287 | case ShapeData.PhysicsShapeType.SHAPE_MESH: | ||
288 | DereferenceMesh(shape, shapeCallback); | ||
289 | break; | ||
290 | case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: | ||
291 | break; | ||
292 | default: | ||
293 | // Native shapes are not tracked and are released immediately | ||
294 | if (shape.ptr != IntPtr.Zero & shape.isNativeShape) | ||
295 | { | ||
296 | DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}", | ||
297 | BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime); | ||
298 | if (shapeCallback != null) shapeCallback(shape); | ||
299 | BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); | ||
300 | } | ||
301 | break; | ||
302 | } | ||
303 | }; | ||
304 | if (inTaintTime) | ||
305 | { | ||
306 | lock (m_collectionActivityLock) | ||
307 | { | ||
308 | dereferenceOperation(); | ||
309 | } | ||
310 | } | ||
311 | else | ||
312 | { | ||
313 | PhysicsScene.TaintedObject("BSShapeCollection.DereferenceShape", dereferenceOperation); | ||
314 | } | ||
315 | } | ||
316 | |||
317 | // Count down the reference count for a mesh shape | ||
318 | // Called at taint-time. | ||
319 | private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback) | ||
320 | { | ||
321 | MeshDesc meshDesc; | ||
322 | if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) | ||
323 | { | ||
324 | meshDesc.referenceCount--; | ||
325 | // TODO: release the Bullet storage | ||
326 | if (shapeCallback != null) shapeCallback(shape); | ||
327 | meshDesc.lastReferenced = System.DateTime.Now; | ||
328 | Meshes[shape.shapeKey] = meshDesc; | ||
329 | DetailLog("{0},BSShapeCollection.DereferenceMesh,key={1},refCnt={2}", | ||
330 | BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); | ||
331 | |||
332 | } | ||
333 | } | ||
334 | |||
335 | // Count down the reference count for a hull shape | ||
336 | // Called at taint-time. | ||
337 | private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback) | ||
338 | { | ||
339 | HullDesc hullDesc; | ||
340 | if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) | ||
341 | { | ||
342 | hullDesc.referenceCount--; | ||
343 | // TODO: release the Bullet storage (aging old entries?) | ||
344 | if (shapeCallback != null) shapeCallback(shape); | ||
345 | hullDesc.lastReferenced = System.DateTime.Now; | ||
346 | Hulls[shape.shapeKey] = hullDesc; | ||
347 | DetailLog("{0},BSShapeCollection.DereferenceHull,key={1},refCnt={2}", | ||
348 | BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); | ||
349 | } | ||
350 | } | ||
351 | |||
352 | // Create the geometry information in Bullet for later use. | ||
353 | // The objects needs a hull if it's physical otherwise a mesh is enough. | ||
354 | // No locking here because this is done when we know physics is not simulating. | ||
355 | // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used. | ||
356 | // Returns 'true' if the geometry was rebuilt. | ||
357 | // Called at taint-time! | ||
358 | private bool CreateGeom(bool forceRebuild, BSPrim prim, ShapeData shapeData, | ||
359 | PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback) | ||
360 | { | ||
361 | bool ret = false; | ||
362 | bool haveShape = false; | ||
363 | bool nativeShapePossible = true; | ||
364 | |||
365 | // If the prim attributes are simple, this could be a simple Bullet native shape | ||
366 | if (nativeShapePossible | ||
367 | && ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim) | ||
368 | || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 | ||
369 | && pbs.ProfileHollow == 0 | ||
370 | && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0 | ||
371 | && pbs.PathBegin == 0 && pbs.PathEnd == 0 | ||
372 | && pbs.PathTaperX == 0 && pbs.PathTaperY == 0 | ||
373 | && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 | ||
374 | && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) ) | ||
375 | { | ||
376 | if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) | ||
377 | && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z) | ||
378 | { | ||
379 | haveShape = true; | ||
380 | if (forceRebuild | ||
381 | || prim.Scale != shapeData.Size | ||
382 | || prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE | ||
383 | ) | ||
384 | { | ||
385 | ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_SPHERE, | ||
386 | ShapeData.FixedShapeKey.KEY_SPHERE, shapeCallback); | ||
387 | DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}", | ||
388 | prim.LocalID, forceRebuild, prim.BSShape); | ||
389 | } | ||
390 | } | ||
391 | if (pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) | ||
392 | { | ||
393 | haveShape = true; | ||
394 | if (forceRebuild | ||
395 | || prim.Scale != shapeData.Size | ||
396 | || prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX | ||
397 | ) | ||
398 | { | ||
399 | ret = GetReferenceToNativeShape( prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_BOX, | ||
400 | ShapeData.FixedShapeKey.KEY_BOX, shapeCallback); | ||
401 | DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}", | ||
402 | prim.LocalID, forceRebuild, prim.BSShape); | ||
403 | } | ||
404 | } | ||
405 | } | ||
406 | // If a simple shape is not happening, create a mesh and possibly a hull. | ||
407 | // Note that if it's a native shape, the check for physical/non-physical is not | ||
408 | // made. Native shapes are best used in either case. | ||
409 | if (!haveShape) | ||
410 | { | ||
411 | if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects) | ||
412 | { | ||
413 | // Update prim.BSShape to reference a hull of this shape. | ||
414 | ret = GetReferenceToHull(prim, shapeData, pbs, shapeCallback); | ||
415 | DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}", | ||
416 | shapeData.ID, prim.BSShape, prim.BSShape.shapeKey.ToString("X")); | ||
417 | } | ||
418 | else | ||
419 | { | ||
420 | ret = GetReferenceToMesh(prim, shapeData, pbs, shapeCallback); | ||
421 | DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}", | ||
422 | shapeData.ID, prim.BSShape, prim.BSShape.shapeKey.ToString("X")); | ||
423 | } | ||
424 | } | ||
425 | return ret; | ||
426 | } | ||
427 | |||
428 | // Creates a native shape and assignes it to prim.BSShape | ||
429 | private bool GetReferenceToNativeShape( BSPrim prim, ShapeData shapeData, | ||
430 | ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey, | ||
431 | ShapeDestructionCallback shapeCallback) | ||
432 | { | ||
433 | BulletShape newShape; | ||
434 | |||
435 | shapeData.Type = shapeType; | ||
436 | // Bullet native objects are scaled by the Bullet engine so pass the size in | ||
437 | prim.Scale = shapeData.Size; | ||
438 | shapeData.Scale = shapeData.Size; | ||
439 | |||
440 | // release any previous shape | ||
441 | DereferenceShape(prim.BSShape, true, shapeCallback); | ||
442 | |||
443 | // Native shapes are always built independently. | ||
444 | newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType); | ||
445 | newShape.shapeKey = (System.UInt64)shapeKey; | ||
446 | newShape.isNativeShape = true; | ||
447 | |||
448 | // Don't need to do a 'ReferenceShape()' here because native shapes are not tracked. | ||
449 | // DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1}", shapeData.ID, newShape); | ||
450 | |||
451 | prim.BSShape = newShape; | ||
452 | return true; | ||
453 | } | ||
454 | |||
455 | // Builds a mesh shape in the physical world and updates prim.BSShape. | ||
456 | // Dereferences previous shape in BSShape and adds a reference for this new shape. | ||
457 | // Returns 'true' of a mesh was actually built. Otherwise . | ||
458 | // Called at taint-time! | ||
459 | private bool GetReferenceToMesh(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs, | ||
460 | ShapeDestructionCallback shapeCallback) | ||
461 | { | ||
462 | BulletShape newShape = new BulletShape(IntPtr.Zero); | ||
463 | |||
464 | float lod; | ||
465 | System.UInt64 newMeshKey = ComputeShapeKey(shapeData, pbs, out lod); | ||
466 | |||
467 | // if this new shape is the same as last time, don't recreate the mesh | ||
468 | if (newMeshKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH) | ||
469 | return false; | ||
470 | |||
471 | DetailLog("{0},BSShapeCollection.CreateGeomMesh,create,oldKey={1},newKey={2}", | ||
472 | prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newMeshKey.ToString("X")); | ||
473 | |||
474 | // Since we're recreating new, get rid of the reference to the previous shape | ||
475 | DereferenceShape(prim.BSShape, true, shapeCallback); | ||
476 | |||
477 | newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod); | ||
478 | |||
479 | ReferenceShape(newShape); | ||
480 | |||
481 | // meshes are already scaled by the meshmerizer | ||
482 | prim.Scale = new OMV.Vector3(1f, 1f, 1f); | ||
483 | prim.BSShape = newShape; | ||
484 | |||
485 | return true; // 'true' means a new shape has been added to this prim | ||
486 | } | ||
487 | |||
488 | private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) | ||
489 | { | ||
490 | IMesh meshData = null; | ||
491 | IntPtr meshPtr; | ||
492 | MeshDesc meshDesc; | ||
493 | if (Meshes.TryGetValue(newMeshKey, out meshDesc)) | ||
494 | { | ||
495 | // If the mesh has already been built just use it. | ||
496 | meshPtr = meshDesc.ptr; | ||
497 | } | ||
498 | else | ||
499 | { | ||
500 | // Pass false for physicalness as this creates some sort of bounding box which we don't need | ||
501 | meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); | ||
502 | |||
503 | int[] indices = meshData.getIndexListAsInt(); | ||
504 | List<OMV.Vector3> vertices = meshData.getVertexList(); | ||
505 | |||
506 | float[] verticesAsFloats = new float[vertices.Count * 3]; | ||
507 | int vi = 0; | ||
508 | foreach (OMV.Vector3 vv in vertices) | ||
509 | { | ||
510 | verticesAsFloats[vi++] = vv.X; | ||
511 | verticesAsFloats[vi++] = vv.Y; | ||
512 | verticesAsFloats[vi++] = vv.Z; | ||
513 | } | ||
514 | |||
515 | // m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}", | ||
516 | // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count); | ||
517 | |||
518 | meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr, | ||
519 | indices.GetLength(0), indices, vertices.Count, verticesAsFloats); | ||
520 | } | ||
521 | BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH); | ||
522 | newShape.shapeKey = newMeshKey; | ||
523 | |||
524 | return newShape; | ||
525 | } | ||
526 | |||
527 | // See that hull shape exists in the physical world and update prim.BSShape. | ||
528 | // We could be creating the hull because scale changed or whatever. | ||
529 | private bool GetReferenceToHull(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs, | ||
530 | ShapeDestructionCallback shapeCallback) | ||
531 | { | ||
532 | BulletShape newShape; | ||
533 | |||
534 | float lod; | ||
535 | System.UInt64 newHullKey = ComputeShapeKey(shapeData, pbs, out lod); | ||
536 | |||
537 | // if the hull hasn't changed, don't rebuild it | ||
538 | if (newHullKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL) | ||
539 | return false; | ||
540 | |||
541 | DetailLog("{0},BSShapeCollection.CreateGeomHull,create,oldKey={1},newKey={2}", | ||
542 | prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newHullKey.ToString("X")); | ||
543 | |||
544 | // Remove usage of the previous shape. | ||
545 | DereferenceShape(prim.BSShape, true, shapeCallback); | ||
546 | |||
547 | newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod); | ||
548 | |||
549 | ReferenceShape(newShape); | ||
550 | |||
551 | // hulls are already scaled by the meshmerizer | ||
552 | prim.Scale = new OMV.Vector3(1f, 1f, 1f); | ||
553 | prim.BSShape = newShape; | ||
554 | return true; // 'true' means a new shape has been added to this prim | ||
555 | } | ||
556 | |||
557 | List<ConvexResult> m_hulls; | ||
558 | private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) | ||
559 | { | ||
560 | |||
561 | IntPtr hullPtr; | ||
562 | HullDesc hullDesc; | ||
563 | if (Hulls.TryGetValue(newHullKey, out hullDesc)) | ||
564 | { | ||
565 | // If the hull shape already is created, just use it. | ||
566 | hullPtr = hullDesc.ptr; | ||
567 | } | ||
568 | else | ||
569 | { | ||
570 | // Build a new hull in the physical world | ||
571 | // Pass false for physicalness as this creates some sort of bounding box which we don't need | ||
572 | IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); | ||
573 | |||
574 | int[] indices = meshData.getIndexListAsInt(); | ||
575 | List<OMV.Vector3> vertices = meshData.getVertexList(); | ||
576 | |||
577 | //format conversion from IMesh format to DecompDesc format | ||
578 | List<int> convIndices = new List<int>(); | ||
579 | List<float3> convVertices = new List<float3>(); | ||
580 | for (int ii = 0; ii < indices.GetLength(0); ii++) | ||
581 | { | ||
582 | convIndices.Add(indices[ii]); | ||
583 | } | ||
584 | foreach (OMV.Vector3 vv in vertices) | ||
585 | { | ||
586 | convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); | ||
587 | } | ||
588 | |||
589 | // setup and do convex hull conversion | ||
590 | m_hulls = new List<ConvexResult>(); | ||
591 | DecompDesc dcomp = new DecompDesc(); | ||
592 | dcomp.mIndices = convIndices; | ||
593 | dcomp.mVertices = convVertices; | ||
594 | ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); | ||
595 | // create the hull into the _hulls variable | ||
596 | convexBuilder.process(dcomp); | ||
597 | |||
598 | // Convert the vertices and indices for passing to unmanaged. | ||
599 | // The hull information is passed as a large floating point array. | ||
600 | // The format is: | ||
601 | // convHulls[0] = number of hulls | ||
602 | // convHulls[1] = number of vertices in first hull | ||
603 | // convHulls[2] = hull centroid X coordinate | ||
604 | // convHulls[3] = hull centroid Y coordinate | ||
605 | // convHulls[4] = hull centroid Z coordinate | ||
606 | // convHulls[5] = first hull vertex X | ||
607 | // convHulls[6] = first hull vertex Y | ||
608 | // convHulls[7] = first hull vertex Z | ||
609 | // convHulls[8] = second hull vertex X | ||
610 | // ... | ||
611 | // convHulls[n] = number of vertices in second hull | ||
612 | // convHulls[n+1] = second hull centroid X coordinate | ||
613 | // ... | ||
614 | // | ||
615 | // TODO: is is very inefficient. Someday change the convex hull generator to return | ||
616 | // data structures that do not need to be converted in order to pass to Bullet. | ||
617 | // And maybe put the values directly into pinned memory rather than marshaling. | ||
618 | int hullCount = m_hulls.Count; | ||
619 | int totalVertices = 1; // include one for the count of the hulls | ||
620 | foreach (ConvexResult cr in m_hulls) | ||
621 | { | ||
622 | totalVertices += 4; // add four for the vertex count and centroid | ||
623 | totalVertices += cr.HullIndices.Count * 3; // we pass just triangles | ||
624 | } | ||
625 | float[] convHulls = new float[totalVertices]; | ||
626 | |||
627 | convHulls[0] = (float)hullCount; | ||
628 | int jj = 1; | ||
629 | foreach (ConvexResult cr in m_hulls) | ||
630 | { | ||
631 | // copy vertices for index access | ||
632 | float3[] verts = new float3[cr.HullVertices.Count]; | ||
633 | int kk = 0; | ||
634 | foreach (float3 ff in cr.HullVertices) | ||
635 | { | ||
636 | verts[kk++] = ff; | ||
637 | } | ||
638 | |||
639 | // add to the array one hull's worth of data | ||
640 | convHulls[jj++] = cr.HullIndices.Count; | ||
641 | convHulls[jj++] = 0f; // centroid x,y,z | ||
642 | convHulls[jj++] = 0f; | ||
643 | convHulls[jj++] = 0f; | ||
644 | foreach (int ind in cr.HullIndices) | ||
645 | { | ||
646 | convHulls[jj++] = verts[ind].x; | ||
647 | convHulls[jj++] = verts[ind].y; | ||
648 | convHulls[jj++] = verts[ind].z; | ||
649 | } | ||
650 | } | ||
651 | // create the hull data structure in Bullet | ||
652 | hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls); | ||
653 | } | ||
654 | |||
655 | BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL); | ||
656 | newShape.shapeKey = newHullKey; | ||
657 | |||
658 | return newShape; // 'true' means a new shape has been added to this prim | ||
659 | } | ||
660 | |||
661 | // Callback from convex hull creater with a newly created hull. | ||
662 | // Just add it to our collection of hulls for this shape. | ||
663 | private void HullReturn(ConvexResult result) | ||
664 | { | ||
665 | m_hulls.Add(result); | ||
666 | return; | ||
667 | } | ||
668 | |||
669 | // Create a hash of all the shape parameters to be used as a key | ||
670 | // for this particular shape. | ||
671 | private System.UInt64 ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs, out float retLod) | ||
672 | { | ||
673 | // level of detail based on size and type of the object | ||
674 | float lod = PhysicsScene.MeshLOD; | ||
675 | if (pbs.SculptEntry) | ||
676 | lod = PhysicsScene.SculptLOD; | ||
677 | |||
678 | // Mega prims usually get more detail because one can interact with shape approximations at this size. | ||
679 | float maxAxis = Math.Max(shapeData.Size.X, Math.Max(shapeData.Size.Y, shapeData.Size.Z)); | ||
680 | if (maxAxis > PhysicsScene.MeshMegaPrimThreshold) | ||
681 | lod = PhysicsScene.MeshMegaPrimLOD; | ||
682 | |||
683 | retLod = lod; | ||
684 | return pbs.GetMeshKey(shapeData.Size, lod); | ||
685 | } | ||
686 | // For those who don't want the LOD | ||
687 | private System.UInt64 ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs) | ||
688 | { | ||
689 | float lod; | ||
690 | return ComputeShapeKey(shapeData, pbs, out lod); | ||
691 | } | ||
692 | |||
693 | // Create a body object in Bullet. | ||
694 | // Updates prim.BSBody with the information about the new body if one is created. | ||
695 | // Returns 'true' if an object was actually created. | ||
696 | // Called at taint-time. | ||
697 | private bool CreateBody(bool forceRebuild, BSPrim prim, BulletSim sim, BulletShape shape, | ||
698 | ShapeData shapeData, BodyDestructionCallback bodyCallback) | ||
699 | { | ||
700 | bool ret = false; | ||
701 | |||
702 | // the mesh, hull or native shape must have already been created in Bullet | ||
703 | bool mustRebuild = (prim.BSBody.ptr == IntPtr.Zero); | ||
704 | |||
705 | // If there is an existing body, verify it's of an acceptable type. | ||
706 | // If not a solid object, body is a GhostObject. Otherwise a RigidBody. | ||
707 | if (!mustRebuild) | ||
708 | { | ||
709 | CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.BSBody.ptr); | ||
710 | if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY | ||
711 | || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT) | ||
712 | { | ||
713 | // If the collisionObject is not the correct type for solidness, rebuild what's there | ||
714 | mustRebuild = true; | ||
715 | } | ||
716 | |||
717 | } | ||
718 | |||
719 | if (mustRebuild || forceRebuild) | ||
720 | { | ||
721 | // Free any old body | ||
722 | DereferenceBody(prim.BSBody, true, bodyCallback); | ||
723 | |||
724 | BulletBody aBody; | ||
725 | IntPtr bodyPtr = IntPtr.Zero; | ||
726 | if (prim.IsSolid) | ||
727 | { | ||
728 | bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr, | ||
729 | shapeData.ID, shapeData.Position, shapeData.Rotation); | ||
730 | DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); | ||
731 | } | ||
732 | else | ||
733 | { | ||
734 | bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr, | ||
735 | shapeData.ID, shapeData.Position, shapeData.Rotation); | ||
736 | DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); | ||
737 | } | ||
738 | aBody = new BulletBody(shapeData.ID, bodyPtr); | ||
739 | |||
740 | ReferenceBody(aBody, true); | ||
741 | |||
742 | prim.BSBody = aBody; | ||
743 | |||
744 | ret = true; | ||
745 | } | ||
746 | |||
747 | return ret; | ||
748 | } | ||
749 | |||
750 | private void DetailLog(string msg, params Object[] args) | ||
751 | { | ||
752 | if (PhysicsScene.PhysicsLogging.Enabled) | ||
753 | PhysicsScene.DetailLog(msg, args); | ||
754 | } | ||
755 | } | ||
756 | } | ||