diff options
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs')
-rwxr-xr-x | OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs | 1000 |
1 files changed, 1000 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..29a23c0 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs | |||
@@ -0,0 +1,1000 @@ | |||
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 sealed class BSShapeCollection : IDisposable | ||
38 | { | ||
39 | private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]"; | ||
40 | |||
41 | private 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 | public UInt64 shapeKey; | ||
52 | } | ||
53 | |||
54 | // Description of a hull. | ||
55 | // Meshes and hulls have the same shape hash key but we only need hulls for efficient collision calculations. | ||
56 | private struct HullDesc | ||
57 | { | ||
58 | public IntPtr ptr; | ||
59 | public int referenceCount; | ||
60 | public DateTime lastReferenced; | ||
61 | public UInt64 shapeKey; | ||
62 | } | ||
63 | |||
64 | // The sharable set of meshes and hulls. Indexed by their shape hash. | ||
65 | private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>(); | ||
66 | private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>(); | ||
67 | |||
68 | public BSShapeCollection(BSScene physScene) | ||
69 | { | ||
70 | PhysicsScene = physScene; | ||
71 | } | ||
72 | |||
73 | public void Dispose() | ||
74 | { | ||
75 | // TODO!!!!!!!!! | ||
76 | } | ||
77 | |||
78 | // Callbacks called just before either the body or shape is destroyed. | ||
79 | // Mostly used for changing bodies out from under Linksets. | ||
80 | // Useful for other cases where parameters need saving. | ||
81 | // Passing 'null' says no callback. | ||
82 | public delegate void ShapeDestructionCallback(BulletShape shape); | ||
83 | public delegate void BodyDestructionCallback(BulletBody body); | ||
84 | |||
85 | // Called to update/change the body and shape for an object. | ||
86 | // First checks the shape and updates that if necessary then makes | ||
87 | // sure the body is of the right type. | ||
88 | // Return 'true' if either the body or the shape changed. | ||
89 | // 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before | ||
90 | // the current shape or body is destroyed. This allows the caller to remove any | ||
91 | // higher level dependencies on the shape or body. Mostly used for LinkSets to | ||
92 | // remove the physical constraints before the body is destroyed. | ||
93 | // Called at taint-time!! | ||
94 | public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim, | ||
95 | ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback) | ||
96 | { | ||
97 | PhysicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape"); | ||
98 | |||
99 | bool ret = false; | ||
100 | |||
101 | // This lock could probably be pushed down lower but building shouldn't take long | ||
102 | lock (m_collectionActivityLock) | ||
103 | { | ||
104 | // Do we have the correct geometry for this type of object? | ||
105 | // Updates prim.BSShape with information/pointers to shape. | ||
106 | // Returns 'true' of BSShape is changed to a new shape. | ||
107 | bool newGeom = CreateGeom(forceRebuild, prim, 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 | // Returns 'true' if BSBody was changed. | ||
112 | bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, | ||
113 | prim.PhysShape, bodyCallback); | ||
114 | ret = newGeom || newBody; | ||
115 | } | ||
116 | DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}", | ||
117 | prim.LocalID, forceRebuild, ret, prim.PhysBody, prim.PhysShape); | ||
118 | |||
119 | return ret; | ||
120 | } | ||
121 | |||
122 | // Track another user of a body. | ||
123 | // We presume the caller has allocated the body. | ||
124 | // Bodies only have one user so the body is just put into the world if not already there. | ||
125 | public void ReferenceBody(BulletBody body, bool inTaintTime) | ||
126 | { | ||
127 | lock (m_collectionActivityLock) | ||
128 | { | ||
129 | DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body); | ||
130 | PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.ReferenceBody", delegate() | ||
131 | { | ||
132 | if (!BulletSimAPI.IsInWorld2(body.ptr)) | ||
133 | { | ||
134 | BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr); | ||
135 | DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body); | ||
136 | } | ||
137 | }); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | // Release the usage of a body. | ||
142 | // Called when releasing use of a BSBody. BSShape is handled separately. | ||
143 | public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback ) | ||
144 | { | ||
145 | if (body.ptr == IntPtr.Zero) | ||
146 | return; | ||
147 | |||
148 | lock (m_collectionActivityLock) | ||
149 | { | ||
150 | PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceBody", delegate() | ||
151 | { | ||
152 | DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}", | ||
153 | body.ID, body, inTaintTime); | ||
154 | // If the caller needs to know the old body is going away, pass the event up. | ||
155 | if (bodyCallback != null) bodyCallback(body); | ||
156 | |||
157 | if (BulletSimAPI.IsInWorld2(body.ptr)) | ||
158 | { | ||
159 | BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr); | ||
160 | DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body); | ||
161 | } | ||
162 | |||
163 | // Zero any reference to the shape so it is not freed when the body is deleted. | ||
164 | BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero); | ||
165 | BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr); | ||
166 | }); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | // Track the datastructures and use count for a shape. | ||
171 | // When creating a hull, this is called first to reference the mesh | ||
172 | // and then again to reference the hull. | ||
173 | // Meshes and hulls for the same shape have the same hash key. | ||
174 | // NOTE that native shapes are not added to the mesh list or removed. | ||
175 | // Returns 'true' if this is the initial reference to the shape. Otherwise reused. | ||
176 | public bool ReferenceShape(BulletShape shape) | ||
177 | { | ||
178 | bool ret = false; | ||
179 | switch (shape.type) | ||
180 | { | ||
181 | case ShapeData.PhysicsShapeType.SHAPE_MESH: | ||
182 | MeshDesc meshDesc; | ||
183 | if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) | ||
184 | { | ||
185 | // There is an existing instance of this mesh. | ||
186 | meshDesc.referenceCount++; | ||
187 | DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}", | ||
188 | BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); | ||
189 | } | ||
190 | else | ||
191 | { | ||
192 | // This is a new reference to a mesh | ||
193 | meshDesc.ptr = shape.ptr; | ||
194 | meshDesc.shapeKey = shape.shapeKey; | ||
195 | // We keep a reference to the underlying IMesh data so a hull can be built | ||
196 | meshDesc.referenceCount = 1; | ||
197 | DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}", | ||
198 | BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); | ||
199 | ret = true; | ||
200 | } | ||
201 | meshDesc.lastReferenced = System.DateTime.Now; | ||
202 | Meshes[shape.shapeKey] = meshDesc; | ||
203 | break; | ||
204 | case ShapeData.PhysicsShapeType.SHAPE_HULL: | ||
205 | HullDesc hullDesc; | ||
206 | if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) | ||
207 | { | ||
208 | // There is an existing instance of this hull. | ||
209 | hullDesc.referenceCount++; | ||
210 | DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}", | ||
211 | BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); | ||
212 | } | ||
213 | else | ||
214 | { | ||
215 | // This is a new reference to a hull | ||
216 | hullDesc.ptr = shape.ptr; | ||
217 | hullDesc.shapeKey = shape.shapeKey; | ||
218 | hullDesc.referenceCount = 1; | ||
219 | DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}", | ||
220 | BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); | ||
221 | ret = true; | ||
222 | |||
223 | } | ||
224 | hullDesc.lastReferenced = System.DateTime.Now; | ||
225 | Hulls[shape.shapeKey] = hullDesc; | ||
226 | break; | ||
227 | case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: | ||
228 | break; | ||
229 | default: | ||
230 | // Native shapes are not tracked and they don't go into any list | ||
231 | break; | ||
232 | } | ||
233 | return ret; | ||
234 | } | ||
235 | |||
236 | // Release the usage of a shape. | ||
237 | public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback) | ||
238 | { | ||
239 | if (shape.ptr == IntPtr.Zero) | ||
240 | return; | ||
241 | |||
242 | PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceShape", delegate() | ||
243 | { | ||
244 | if (shape.ptr != IntPtr.Zero) | ||
245 | { | ||
246 | if (shape.isNativeShape) | ||
247 | { | ||
248 | // Native shapes are not tracked and are released immediately | ||
249 | DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}", | ||
250 | BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime); | ||
251 | if (shapeCallback != null) shapeCallback(shape); | ||
252 | BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); | ||
253 | } | ||
254 | else | ||
255 | { | ||
256 | switch (shape.type) | ||
257 | { | ||
258 | case ShapeData.PhysicsShapeType.SHAPE_HULL: | ||
259 | DereferenceHull(shape, shapeCallback); | ||
260 | break; | ||
261 | case ShapeData.PhysicsShapeType.SHAPE_MESH: | ||
262 | DereferenceMesh(shape, shapeCallback); | ||
263 | break; | ||
264 | case ShapeData.PhysicsShapeType.SHAPE_COMPOUND: | ||
265 | DereferenceCompound(shape, shapeCallback); | ||
266 | break; | ||
267 | case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: | ||
268 | break; | ||
269 | default: | ||
270 | break; | ||
271 | } | ||
272 | } | ||
273 | } | ||
274 | }); | ||
275 | } | ||
276 | |||
277 | // Count down the reference count for a mesh shape | ||
278 | // Called at taint-time. | ||
279 | private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback) | ||
280 | { | ||
281 | MeshDesc meshDesc; | ||
282 | if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) | ||
283 | { | ||
284 | meshDesc.referenceCount--; | ||
285 | // TODO: release the Bullet storage | ||
286 | if (shapeCallback != null) shapeCallback(shape); | ||
287 | meshDesc.lastReferenced = System.DateTime.Now; | ||
288 | Meshes[shape.shapeKey] = meshDesc; | ||
289 | DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}", | ||
290 | BSScene.DetailLogZero, shape, meshDesc.referenceCount); | ||
291 | |||
292 | } | ||
293 | } | ||
294 | |||
295 | // Count down the reference count for a hull shape | ||
296 | // Called at taint-time. | ||
297 | private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback) | ||
298 | { | ||
299 | HullDesc hullDesc; | ||
300 | if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) | ||
301 | { | ||
302 | hullDesc.referenceCount--; | ||
303 | // TODO: release the Bullet storage (aging old entries?) | ||
304 | |||
305 | // Tell upper layers that, if they have dependencies on this shape, this link is going away | ||
306 | if (shapeCallback != null) shapeCallback(shape); | ||
307 | |||
308 | hullDesc.lastReferenced = System.DateTime.Now; | ||
309 | Hulls[shape.shapeKey] = hullDesc; | ||
310 | DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}", | ||
311 | BSScene.DetailLogZero, shape, hullDesc.referenceCount); | ||
312 | } | ||
313 | } | ||
314 | |||
315 | // Remove a reference to a compound shape. | ||
316 | // Taking a compound shape apart is a little tricky because if you just delete the | ||
317 | // physical shape, it will free all the underlying children. We can't do that because | ||
318 | // they could be shared. So, this removes each of the children from the compound and | ||
319 | // dereferences them separately before destroying the compound collision object itself. | ||
320 | // Called at taint-time. | ||
321 | private void DereferenceCompound(BulletShape shape, ShapeDestructionCallback shapeCallback) | ||
322 | { | ||
323 | if (!BulletSimAPI.IsCompound2(shape.ptr)) | ||
324 | { | ||
325 | // Failed the sanity check!! | ||
326 | PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", | ||
327 | LogHeader, shape.type, shape.ptr.ToString("X")); | ||
328 | DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", | ||
329 | BSScene.DetailLogZero, shape.type, shape.ptr.ToString("X")); | ||
330 | return; | ||
331 | } | ||
332 | |||
333 | int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr); | ||
334 | DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren); | ||
335 | |||
336 | for (int ii = numChildren - 1; ii >= 0; ii--) | ||
337 | { | ||
338 | IntPtr childShape = BulletSimAPI.RemoveChildShapeFromCompoundShapeIndex2(shape.ptr, ii); | ||
339 | DereferenceAnonCollisionShape(childShape); | ||
340 | } | ||
341 | BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); | ||
342 | } | ||
343 | |||
344 | // Sometimes we have a pointer to a collision shape but don't know what type it is. | ||
345 | // Figure out type and call the correct dereference routine. | ||
346 | // Called at taint-time. | ||
347 | private void DereferenceAnonCollisionShape(IntPtr cShape) | ||
348 | { | ||
349 | MeshDesc meshDesc; | ||
350 | HullDesc hullDesc; | ||
351 | |||
352 | BulletShape shapeInfo = new BulletShape(cShape); | ||
353 | if (TryGetMeshByPtr(cShape, out meshDesc)) | ||
354 | { | ||
355 | shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_MESH; | ||
356 | shapeInfo.shapeKey = meshDesc.shapeKey; | ||
357 | } | ||
358 | else | ||
359 | { | ||
360 | if (TryGetHullByPtr(cShape, out hullDesc)) | ||
361 | { | ||
362 | shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_HULL; | ||
363 | shapeInfo.shapeKey = hullDesc.shapeKey; | ||
364 | } | ||
365 | else | ||
366 | { | ||
367 | if (BulletSimAPI.IsCompound2(cShape)) | ||
368 | { | ||
369 | shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_COMPOUND; | ||
370 | } | ||
371 | else | ||
372 | { | ||
373 | if (BulletSimAPI.IsNativeShape2(cShape)) | ||
374 | { | ||
375 | shapeInfo.isNativeShape = true; | ||
376 | shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter) | ||
377 | } | ||
378 | } | ||
379 | } | ||
380 | } | ||
381 | |||
382 | DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo); | ||
383 | |||
384 | if (shapeInfo.type != ShapeData.PhysicsShapeType.SHAPE_UNKNOWN) | ||
385 | { | ||
386 | DereferenceShape(shapeInfo, true, null); | ||
387 | } | ||
388 | else | ||
389 | { | ||
390 | PhysicsScene.Logger.ErrorFormat("{0} Could not decypher shape type. Region={1}, addr={2}", | ||
391 | LogHeader, PhysicsScene.RegionName, cShape.ToString("X")); | ||
392 | } | ||
393 | } | ||
394 | |||
395 | // Create the geometry information in Bullet for later use. | ||
396 | // The objects needs a hull if it's physical otherwise a mesh is enough. | ||
397 | // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls, | ||
398 | // shared geometries will be used. If the parameters of the existing shape are the same | ||
399 | // as this request, the shape is not rebuilt. | ||
400 | // Info in prim.BSShape is updated to the new shape. | ||
401 | // Returns 'true' if the geometry was rebuilt. | ||
402 | // Called at taint-time! | ||
403 | private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) | ||
404 | { | ||
405 | bool ret = false; | ||
406 | bool haveShape = false; | ||
407 | |||
408 | if (!haveShape && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_AVATAR) | ||
409 | { | ||
410 | // an avatar capsule is close to a native shape (it is not shared) | ||
411 | ret = GetReferenceToNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_AVATAR, | ||
412 | ShapeData.FixedShapeKey.KEY_CAPSULE, shapeCallback); | ||
413 | DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape); | ||
414 | ret = true; | ||
415 | haveShape = true; | ||
416 | } | ||
417 | |||
418 | // Compound shapes are handled special as they are rebuilt from scratch. | ||
419 | // This isn't too great a hardship since most of the child shapes will already been created. | ||
420 | if (!haveShape && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_COMPOUND) | ||
421 | { | ||
422 | ret = GetReferenceToCompoundShape(prim, shapeCallback); | ||
423 | DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape); | ||
424 | haveShape = true; | ||
425 | } | ||
426 | |||
427 | if (!haveShape) | ||
428 | { | ||
429 | ret = CreateGeomNonSpecial(forceRebuild, prim, shapeCallback); | ||
430 | } | ||
431 | |||
432 | return ret; | ||
433 | } | ||
434 | |||
435 | // Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'. | ||
436 | private bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) | ||
437 | { | ||
438 | bool ret = false; | ||
439 | bool haveShape = false; | ||
440 | bool nativeShapePossible = true; | ||
441 | PrimitiveBaseShape pbs = prim.BaseShape; | ||
442 | |||
443 | // If the prim attributes are simple, this could be a simple Bullet native shape | ||
444 | if (!haveShape | ||
445 | && pbs != null | ||
446 | && nativeShapePossible | ||
447 | && ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim) | ||
448 | || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 | ||
449 | && pbs.ProfileHollow == 0 | ||
450 | && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0 | ||
451 | && pbs.PathBegin == 0 && pbs.PathEnd == 0 | ||
452 | && pbs.PathTaperX == 0 && pbs.PathTaperY == 0 | ||
453 | && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 | ||
454 | && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) ) | ||
455 | { | ||
456 | // It doesn't look like Bullet scales spheres so make sure the scales are all equal | ||
457 | if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) | ||
458 | && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z) | ||
459 | { | ||
460 | haveShape = true; | ||
461 | if (forceRebuild | ||
462 | || prim.Scale != prim.Size | ||
463 | || prim.PhysShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE | ||
464 | ) | ||
465 | { | ||
466 | ret = GetReferenceToNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_SPHERE, | ||
467 | ShapeData.FixedShapeKey.KEY_SPHERE, shapeCallback); | ||
468 | DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}", | ||
469 | prim.LocalID, forceRebuild, prim.PhysShape); | ||
470 | } | ||
471 | } | ||
472 | if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) | ||
473 | { | ||
474 | haveShape = true; | ||
475 | if (forceRebuild | ||
476 | || prim.Scale != prim.Size | ||
477 | || prim.PhysShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX | ||
478 | ) | ||
479 | { | ||
480 | ret = GetReferenceToNativeShape( prim, ShapeData.PhysicsShapeType.SHAPE_BOX, | ||
481 | ShapeData.FixedShapeKey.KEY_BOX, shapeCallback); | ||
482 | DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}", | ||
483 | prim.LocalID, forceRebuild, prim.PhysShape); | ||
484 | } | ||
485 | } | ||
486 | } | ||
487 | |||
488 | // If a simple shape is not happening, create a mesh and possibly a hull. | ||
489 | if (!haveShape && pbs != null) | ||
490 | { | ||
491 | ret = CreateGeomMeshOrHull(prim, shapeCallback); | ||
492 | } | ||
493 | |||
494 | return ret; | ||
495 | } | ||
496 | |||
497 | public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback) | ||
498 | { | ||
499 | |||
500 | bool ret = false; | ||
501 | // Note that if it's a native shape, the check for physical/non-physical is not | ||
502 | // made. Native shapes work in either case. | ||
503 | if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects) | ||
504 | { | ||
505 | // Update prim.BSShape to reference a hull of this shape. | ||
506 | ret = GetReferenceToHull(prim,shapeCallback); | ||
507 | DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}", | ||
508 | prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); | ||
509 | } | ||
510 | else | ||
511 | { | ||
512 | ret = GetReferenceToMesh(prim, shapeCallback); | ||
513 | DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}", | ||
514 | prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); | ||
515 | } | ||
516 | return ret; | ||
517 | } | ||
518 | |||
519 | // Creates a native shape and assignes it to prim.BSShape. | ||
520 | // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape(). | ||
521 | private bool GetReferenceToNativeShape(BSPhysObject prim, | ||
522 | ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey, | ||
523 | ShapeDestructionCallback shapeCallback) | ||
524 | { | ||
525 | // release any previous shape | ||
526 | DereferenceShape(prim.PhysShape, true, shapeCallback); | ||
527 | |||
528 | // Bullet native objects are scaled by the Bullet engine so pass the size in | ||
529 | prim.Scale = prim.Size; | ||
530 | |||
531 | BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey); | ||
532 | |||
533 | // Don't need to do a 'ReferenceShape()' here because native shapes are not shared. | ||
534 | DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}", | ||
535 | prim.LocalID, newShape, prim.Scale); | ||
536 | |||
537 | prim.PhysShape = newShape; | ||
538 | return true; | ||
539 | } | ||
540 | |||
541 | private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, ShapeData.PhysicsShapeType shapeType, | ||
542 | ShapeData.FixedShapeKey shapeKey) | ||
543 | { | ||
544 | BulletShape newShape; | ||
545 | // Need to make sure the passed shape information is for the native type. | ||
546 | ShapeData nativeShapeData = new ShapeData(); | ||
547 | nativeShapeData.Type = shapeType; | ||
548 | nativeShapeData.ID = prim.LocalID; | ||
549 | nativeShapeData.Scale = prim.Scale; | ||
550 | nativeShapeData.Size = prim.Scale; | ||
551 | nativeShapeData.MeshKey = (ulong)shapeKey; | ||
552 | nativeShapeData.HullKey = (ulong)shapeKey; | ||
553 | |||
554 | if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR) | ||
555 | { | ||
556 | newShape = new BulletShape( | ||
557 | BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale) | ||
558 | , shapeType); | ||
559 | DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale); | ||
560 | } | ||
561 | else | ||
562 | { | ||
563 | newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType); | ||
564 | } | ||
565 | if (newShape.ptr == IntPtr.Zero) | ||
566 | { | ||
567 | PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", | ||
568 | LogHeader, prim.LocalID, shapeType); | ||
569 | } | ||
570 | newShape.shapeKey = (System.UInt64)shapeKey; | ||
571 | newShape.isNativeShape = true; | ||
572 | |||
573 | return newShape; | ||
574 | } | ||
575 | |||
576 | // Builds a mesh shape in the physical world and updates prim.BSShape. | ||
577 | // Dereferences previous shape in BSShape and adds a reference for this new shape. | ||
578 | // Returns 'true' of a mesh was actually built. Otherwise . | ||
579 | // Called at taint-time! | ||
580 | private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback) | ||
581 | { | ||
582 | BulletShape newShape = new BulletShape(IntPtr.Zero); | ||
583 | |||
584 | float lod; | ||
585 | System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); | ||
586 | |||
587 | // if this new shape is the same as last time, don't recreate the mesh | ||
588 | if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH) | ||
589 | return false; | ||
590 | |||
591 | DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}", | ||
592 | prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X")); | ||
593 | |||
594 | // Since we're recreating new, get rid of the reference to the previous shape | ||
595 | DereferenceShape(prim.PhysShape, true, shapeCallback); | ||
596 | |||
597 | newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, prim.BaseShape, prim.Size, lod); | ||
598 | // Take evasive action if the mesh was not constructed. | ||
599 | newShape = VerifyMeshCreated(newShape, prim); | ||
600 | |||
601 | ReferenceShape(newShape); | ||
602 | |||
603 | // meshes are already scaled by the meshmerizer | ||
604 | prim.Scale = new OMV.Vector3(1f, 1f, 1f); | ||
605 | prim.PhysShape = newShape; | ||
606 | |||
607 | return true; // 'true' means a new shape has been added to this prim | ||
608 | } | ||
609 | |||
610 | private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) | ||
611 | { | ||
612 | IMesh meshData = null; | ||
613 | IntPtr meshPtr = IntPtr.Zero; | ||
614 | MeshDesc meshDesc; | ||
615 | if (Meshes.TryGetValue(newMeshKey, out meshDesc)) | ||
616 | { | ||
617 | // If the mesh has already been built just use it. | ||
618 | meshPtr = meshDesc.ptr; | ||
619 | } | ||
620 | else | ||
621 | { | ||
622 | // Pass false for physicalness as this creates some sort of bounding box which we don't need | ||
623 | meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); | ||
624 | |||
625 | if (meshData != null) | ||
626 | { | ||
627 | int[] indices = meshData.getIndexListAsInt(); | ||
628 | List<OMV.Vector3> vertices = meshData.getVertexList(); | ||
629 | |||
630 | float[] verticesAsFloats = new float[vertices.Count * 3]; | ||
631 | int vi = 0; | ||
632 | foreach (OMV.Vector3 vv in vertices) | ||
633 | { | ||
634 | verticesAsFloats[vi++] = vv.X; | ||
635 | verticesAsFloats[vi++] = vv.Y; | ||
636 | verticesAsFloats[vi++] = vv.Z; | ||
637 | } | ||
638 | |||
639 | // m_log.DebugFormat("{0}: BSShapeCollection.CreatePhysicalMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}", | ||
640 | // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count); | ||
641 | |||
642 | meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr, | ||
643 | indices.GetLength(0), indices, vertices.Count, verticesAsFloats); | ||
644 | } | ||
645 | } | ||
646 | BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH); | ||
647 | newShape.shapeKey = newMeshKey; | ||
648 | |||
649 | return newShape; | ||
650 | } | ||
651 | |||
652 | // See that hull shape exists in the physical world and update prim.BSShape. | ||
653 | // We could be creating the hull because scale changed or whatever. | ||
654 | private bool GetReferenceToHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback) | ||
655 | { | ||
656 | BulletShape newShape; | ||
657 | |||
658 | float lod; | ||
659 | System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); | ||
660 | |||
661 | // if the hull hasn't changed, don't rebuild it | ||
662 | if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL) | ||
663 | return false; | ||
664 | |||
665 | DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}", | ||
666 | prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X")); | ||
667 | |||
668 | // Remove usage of the previous shape. | ||
669 | DereferenceShape(prim.PhysShape, true, shapeCallback); | ||
670 | |||
671 | newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, prim.BaseShape, prim.Size, lod); | ||
672 | newShape = VerifyMeshCreated(newShape, prim); | ||
673 | |||
674 | ReferenceShape(newShape); | ||
675 | |||
676 | // hulls are already scaled by the meshmerizer | ||
677 | prim.Scale = new OMV.Vector3(1f, 1f, 1f); | ||
678 | prim.PhysShape = newShape; | ||
679 | return true; // 'true' means a new shape has been added to this prim | ||
680 | } | ||
681 | |||
682 | List<ConvexResult> m_hulls; | ||
683 | private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) | ||
684 | { | ||
685 | |||
686 | IntPtr hullPtr = IntPtr.Zero; | ||
687 | HullDesc hullDesc; | ||
688 | if (Hulls.TryGetValue(newHullKey, out hullDesc)) | ||
689 | { | ||
690 | // If the hull shape already is created, just use it. | ||
691 | hullPtr = hullDesc.ptr; | ||
692 | } | ||
693 | else | ||
694 | { | ||
695 | // Build a new hull in the physical world | ||
696 | // Pass false for physicalness as this creates some sort of bounding box which we don't need | ||
697 | IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); | ||
698 | if (meshData != null) | ||
699 | { | ||
700 | |||
701 | int[] indices = meshData.getIndexListAsInt(); | ||
702 | List<OMV.Vector3> vertices = meshData.getVertexList(); | ||
703 | |||
704 | //format conversion from IMesh format to DecompDesc format | ||
705 | List<int> convIndices = new List<int>(); | ||
706 | List<float3> convVertices = new List<float3>(); | ||
707 | for (int ii = 0; ii < indices.GetLength(0); ii++) | ||
708 | { | ||
709 | convIndices.Add(indices[ii]); | ||
710 | } | ||
711 | foreach (OMV.Vector3 vv in vertices) | ||
712 | { | ||
713 | convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); | ||
714 | } | ||
715 | |||
716 | // setup and do convex hull conversion | ||
717 | m_hulls = new List<ConvexResult>(); | ||
718 | DecompDesc dcomp = new DecompDesc(); | ||
719 | dcomp.mIndices = convIndices; | ||
720 | dcomp.mVertices = convVertices; | ||
721 | ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); | ||
722 | // create the hull into the _hulls variable | ||
723 | convexBuilder.process(dcomp); | ||
724 | |||
725 | // Convert the vertices and indices for passing to unmanaged. | ||
726 | // The hull information is passed as a large floating point array. | ||
727 | // The format is: | ||
728 | // convHulls[0] = number of hulls | ||
729 | // convHulls[1] = number of vertices in first hull | ||
730 | // convHulls[2] = hull centroid X coordinate | ||
731 | // convHulls[3] = hull centroid Y coordinate | ||
732 | // convHulls[4] = hull centroid Z coordinate | ||
733 | // convHulls[5] = first hull vertex X | ||
734 | // convHulls[6] = first hull vertex Y | ||
735 | // convHulls[7] = first hull vertex Z | ||
736 | // convHulls[8] = second hull vertex X | ||
737 | // ... | ||
738 | // convHulls[n] = number of vertices in second hull | ||
739 | // convHulls[n+1] = second hull centroid X coordinate | ||
740 | // ... | ||
741 | // | ||
742 | // TODO: is is very inefficient. Someday change the convex hull generator to return | ||
743 | // data structures that do not need to be converted in order to pass to Bullet. | ||
744 | // And maybe put the values directly into pinned memory rather than marshaling. | ||
745 | int hullCount = m_hulls.Count; | ||
746 | int totalVertices = 1; // include one for the count of the hulls | ||
747 | foreach (ConvexResult cr in m_hulls) | ||
748 | { | ||
749 | totalVertices += 4; // add four for the vertex count and centroid | ||
750 | totalVertices += cr.HullIndices.Count * 3; // we pass just triangles | ||
751 | } | ||
752 | float[] convHulls = new float[totalVertices]; | ||
753 | |||
754 | convHulls[0] = (float)hullCount; | ||
755 | int jj = 1; | ||
756 | foreach (ConvexResult cr in m_hulls) | ||
757 | { | ||
758 | // copy vertices for index access | ||
759 | float3[] verts = new float3[cr.HullVertices.Count]; | ||
760 | int kk = 0; | ||
761 | foreach (float3 ff in cr.HullVertices) | ||
762 | { | ||
763 | verts[kk++] = ff; | ||
764 | } | ||
765 | |||
766 | // add to the array one hull's worth of data | ||
767 | convHulls[jj++] = cr.HullIndices.Count; | ||
768 | convHulls[jj++] = 0f; // centroid x,y,z | ||
769 | convHulls[jj++] = 0f; | ||
770 | convHulls[jj++] = 0f; | ||
771 | foreach (int ind in cr.HullIndices) | ||
772 | { | ||
773 | convHulls[jj++] = verts[ind].x; | ||
774 | convHulls[jj++] = verts[ind].y; | ||
775 | convHulls[jj++] = verts[ind].z; | ||
776 | } | ||
777 | } | ||
778 | // create the hull data structure in Bullet | ||
779 | hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls); | ||
780 | } | ||
781 | } | ||
782 | |||
783 | BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL); | ||
784 | newShape.shapeKey = newHullKey; | ||
785 | |||
786 | return newShape; // 'true' means a new shape has been added to this prim | ||
787 | } | ||
788 | |||
789 | // Callback from convex hull creater with a newly created hull. | ||
790 | // Just add it to our collection of hulls for this shape. | ||
791 | private void HullReturn(ConvexResult result) | ||
792 | { | ||
793 | m_hulls.Add(result); | ||
794 | return; | ||
795 | } | ||
796 | |||
797 | // Compound shapes are always built from scratch. | ||
798 | // This shouldn't be to bad since most of the parts will be meshes that had been built previously. | ||
799 | private bool GetReferenceToCompoundShape(BSPhysObject prim, ShapeDestructionCallback shapeCallback) | ||
800 | { | ||
801 | // Remove reference to the old shape | ||
802 | // Don't need to do this as the shape is freed when the new root shape is created below. | ||
803 | // DereferenceShape(prim.PhysShape, true, shapeCallback); | ||
804 | |||
805 | BulletShape cShape = new BulletShape( | ||
806 | BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr, false), ShapeData.PhysicsShapeType.SHAPE_COMPOUND); | ||
807 | |||
808 | // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape. | ||
809 | CreateGeomMeshOrHull(prim, shapeCallback); | ||
810 | BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, prim.PhysShape.ptr, OMV.Vector3.Zero, OMV.Quaternion.Identity); | ||
811 | DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}", | ||
812 | prim.LocalID, cShape, prim.PhysShape); | ||
813 | |||
814 | prim.PhysShape = cShape; | ||
815 | |||
816 | return true; | ||
817 | } | ||
818 | |||
819 | // Create a hash of all the shape parameters to be used as a key | ||
820 | // for this particular shape. | ||
821 | private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod) | ||
822 | { | ||
823 | // level of detail based on size and type of the object | ||
824 | float lod = PhysicsScene.MeshLOD; | ||
825 | if (pbs.SculptEntry) | ||
826 | lod = PhysicsScene.SculptLOD; | ||
827 | |||
828 | // Mega prims usually get more detail because one can interact with shape approximations at this size. | ||
829 | float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z)); | ||
830 | if (maxAxis > PhysicsScene.MeshMegaPrimThreshold) | ||
831 | lod = PhysicsScene.MeshMegaPrimLOD; | ||
832 | |||
833 | retLod = lod; | ||
834 | return pbs.GetMeshKey(size, lod); | ||
835 | } | ||
836 | // For those who don't want the LOD | ||
837 | private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs) | ||
838 | { | ||
839 | float lod; | ||
840 | return ComputeShapeKey(size, pbs, out lod); | ||
841 | } | ||
842 | |||
843 | // The creation of a mesh or hull can fail if an underlying asset is not available. | ||
844 | // There are two cases: 1) the asset is not in the cache and it needs to be fetched; | ||
845 | // and 2) the asset cannot be converted (like failed decompression of JPEG2000s). | ||
846 | // The first case causes the asset to be fetched. The second case requires | ||
847 | // us to not loop forever. | ||
848 | // Called after creating a physical mesh or hull. If the physical shape was created, | ||
849 | // just return. | ||
850 | private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim) | ||
851 | { | ||
852 | // If the shape was successfully created, nothing more to do | ||
853 | if (newShape.ptr != IntPtr.Zero) | ||
854 | return newShape; | ||
855 | |||
856 | // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset | ||
857 | if (prim.BaseShape.SculptEntry && !prim.LastAssetBuildFailed && prim.BaseShape.SculptTexture != OMV.UUID.Zero) | ||
858 | { | ||
859 | prim.LastAssetBuildFailed = true; | ||
860 | BSPhysObject xprim = prim; | ||
861 | DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lID={1},lastFailed={2}", | ||
862 | LogHeader, prim.LocalID, prim.LastAssetBuildFailed); | ||
863 | Util.FireAndForget(delegate | ||
864 | { | ||
865 | RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod; | ||
866 | if (assetProvider != null) | ||
867 | { | ||
868 | BSPhysObject yprim = xprim; // probably not necessary, but, just in case. | ||
869 | assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) | ||
870 | { | ||
871 | if (!yprim.BaseShape.SculptEntry) | ||
872 | return; | ||
873 | if (yprim.BaseShape.SculptTexture.ToString() != asset.ID) | ||
874 | return; | ||
875 | |||
876 | yprim.BaseShape.SculptData = asset.Data; | ||
877 | // This will cause the prim to see that the filler shape is not the right | ||
878 | // one and try again to build the object. | ||
879 | // No race condition with the normal shape setting since the rebuild is at taint time. | ||
880 | yprim.ForceBodyShapeRebuild(false); | ||
881 | |||
882 | }); | ||
883 | } | ||
884 | }); | ||
885 | } | ||
886 | else | ||
887 | { | ||
888 | if (prim.LastAssetBuildFailed) | ||
889 | { | ||
890 | PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}", | ||
891 | LogHeader, prim.LocalID, prim.BaseShape.SculptTexture); | ||
892 | } | ||
893 | } | ||
894 | |||
895 | // While we figure out the real problem, stick a simple native shape on the object. | ||
896 | BulletShape fillinShape = | ||
897 | BuildPhysicalNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_BOX, ShapeData.FixedShapeKey.KEY_BOX); | ||
898 | |||
899 | return fillinShape; | ||
900 | } | ||
901 | |||
902 | // Create a body object in Bullet. | ||
903 | // Updates prim.BSBody with the information about the new body if one is created. | ||
904 | // Returns 'true' if an object was actually created. | ||
905 | // Called at taint-time. | ||
906 | private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape, | ||
907 | BodyDestructionCallback bodyCallback) | ||
908 | { | ||
909 | bool ret = false; | ||
910 | |||
911 | // the mesh, hull or native shape must have already been created in Bullet | ||
912 | bool mustRebuild = (prim.PhysBody.ptr == IntPtr.Zero); | ||
913 | |||
914 | // If there is an existing body, verify it's of an acceptable type. | ||
915 | // If not a solid object, body is a GhostObject. Otherwise a RigidBody. | ||
916 | if (!mustRebuild) | ||
917 | { | ||
918 | CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.PhysBody.ptr); | ||
919 | if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY | ||
920 | || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT) | ||
921 | { | ||
922 | // If the collisionObject is not the correct type for solidness, rebuild what's there | ||
923 | mustRebuild = true; | ||
924 | } | ||
925 | } | ||
926 | |||
927 | if (mustRebuild || forceRebuild) | ||
928 | { | ||
929 | // Free any old body | ||
930 | DereferenceBody(prim.PhysBody, true, bodyCallback); | ||
931 | |||
932 | BulletBody aBody; | ||
933 | IntPtr bodyPtr = IntPtr.Zero; | ||
934 | if (prim.IsSolid) | ||
935 | { | ||
936 | bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr, | ||
937 | prim.LocalID, prim.RawPosition, prim.RawOrientation); | ||
938 | DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); | ||
939 | } | ||
940 | else | ||
941 | { | ||
942 | bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr, | ||
943 | prim.LocalID, prim.ForcePosition, prim.ForceOrientation); | ||
944 | DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); | ||
945 | } | ||
946 | aBody = new BulletBody(prim.LocalID, bodyPtr); | ||
947 | |||
948 | ReferenceBody(aBody, true); | ||
949 | |||
950 | prim.PhysBody = aBody; | ||
951 | |||
952 | ret = true; | ||
953 | } | ||
954 | |||
955 | return ret; | ||
956 | } | ||
957 | |||
958 | private bool TryGetMeshByPtr(IntPtr addr, out MeshDesc outDesc) | ||
959 | { | ||
960 | bool ret = false; | ||
961 | MeshDesc foundDesc = new MeshDesc(); | ||
962 | foreach (MeshDesc md in Meshes.Values) | ||
963 | { | ||
964 | if (md.ptr == addr) | ||
965 | { | ||
966 | foundDesc = md; | ||
967 | ret = true; | ||
968 | break; | ||
969 | } | ||
970 | |||
971 | } | ||
972 | outDesc = foundDesc; | ||
973 | return ret; | ||
974 | } | ||
975 | |||
976 | private bool TryGetHullByPtr(IntPtr addr, out HullDesc outDesc) | ||
977 | { | ||
978 | bool ret = false; | ||
979 | HullDesc foundDesc = new HullDesc(); | ||
980 | foreach (HullDesc hd in Hulls.Values) | ||
981 | { | ||
982 | if (hd.ptr == addr) | ||
983 | { | ||
984 | foundDesc = hd; | ||
985 | ret = true; | ||
986 | break; | ||
987 | } | ||
988 | |||
989 | } | ||
990 | outDesc = foundDesc; | ||
991 | return ret; | ||
992 | } | ||
993 | |||
994 | private void DetailLog(string msg, params Object[] args) | ||
995 | { | ||
996 | if (PhysicsScene.PhysicsLogging.Enabled) | ||
997 | PhysicsScene.DetailLog(msg, args); | ||
998 | } | ||
999 | } | ||
1000 | } | ||