clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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 #pragma warning disable 414
40 private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]";
41 #pragma warning restore 414
42  
43 private BSScene m_physicsScene { get; set; }
44  
45 private Object m_collectionActivityLock = new Object();
46  
47 private bool DDetail = false;
48  
49 public BSShapeCollection(BSScene physScene)
50 {
51 m_physicsScene = physScene;
52 // Set the next to 'true' for very detailed shape update detailed logging (detailed details?)
53 // While detailed debugging is still active, this is better than commenting out all the
54 // DetailLog statements. When debugging slows down, this and the protected logging
55 // statements can be commented/removed.
56 DDetail = true;
57 }
58  
59 public void Dispose()
60 {
61 // TODO!!!!!!!!!
62 }
63  
64 // Callbacks called just before either the body or shape is destroyed.
65 // Mostly used for changing bodies out from under Linksets.
66 // Useful for other cases where parameters need saving.
67 // Passing 'null' says no callback.
68 public delegate void PhysicalDestructionCallback(BulletBody pBody, BulletShape pShape);
69  
70 // Called to update/change the body and shape for an object.
71 // The object has some shape and body on it. Here we decide if that is the correct shape
72 // for the current state of the object (static/dynamic/...).
73 // If bodyCallback is not null, it is called if either the body or the shape are changed
74 // so dependencies (like constraints) can be removed before the physical object is dereferenced.
75 // Return 'true' if either the body or the shape changed.
76 // Called at taint-time.
77 public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim, PhysicalDestructionCallback bodyCallback)
78 {
79 m_physicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape");
80  
81 bool ret = false;
82  
83 // This lock could probably be pushed down lower but building shouldn't take long
84 lock (m_collectionActivityLock)
85 {
86 // Do we have the correct geometry for this type of object?
87 // Updates prim.BSShape with information/pointers to shape.
88 // Returns 'true' of BSShape is changed to a new shape.
89 bool newGeom = CreateGeom(forceRebuild, prim, bodyCallback);
90 // If we had to select a new shape geometry for the object,
91 // rebuild the body around it.
92 // Updates prim.BSBody with information/pointers to requested body
93 // Returns 'true' if BSBody was changed.
94 bool newBody = CreateBody((newGeom || forceRebuild), prim, m_physicsScene.World, bodyCallback);
95 ret = newGeom || newBody;
96 }
97 DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}",
98 prim.LocalID, forceRebuild, ret, prim.PhysBody, prim.PhysShape);
99  
100 return ret;
101 }
102  
103 public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim)
104 {
105 return GetBodyAndShape(forceRebuild, sim, prim, null);
106 }
107  
108 // If the existing prim's shape is to be replaced, remove the tie to the existing shape
109 // before replacing it.
110 private void DereferenceExistingShape(BSPhysObject prim, PhysicalDestructionCallback shapeCallback)
111 {
112 if (prim.PhysShape.HasPhysicalShape)
113 {
114 if (shapeCallback != null)
115 shapeCallback(prim.PhysBody, prim.PhysShape.physShapeInfo);
116 prim.PhysShape.Dereference(m_physicsScene);
117 }
118 prim.PhysShape = new BSShapeNull();
119 }
120  
121 // Create the geometry information in Bullet for later use.
122 // The objects needs a hull if it's physical otherwise a mesh is enough.
123 // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls,
124 // shared geometries will be used. If the parameters of the existing shape are the same
125 // as this request, the shape is not rebuilt.
126 // Info in prim.BSShape is updated to the new shape.
127 // Returns 'true' if the geometry was rebuilt.
128 // Called at taint-time!
129 public const int AvatarShapeCapsule = 0;
130 public const int AvatarShapeCube = 1;
131 public const int AvatarShapeOvoid = 2;
132 public const int AvatarShapeMesh = 3;
133 private bool CreateGeom(bool forceRebuild, BSPhysObject prim, PhysicalDestructionCallback shapeCallback)
134 {
135 bool ret = false;
136 bool haveShape = false;
137 bool nativeShapePossible = true;
138 PrimitiveBaseShape pbs = prim.BaseShape;
139  
140 // Kludge to create the capsule for the avatar.
141 // TDOD: Remove/redo this when BSShapeAvatar is working!!
142 BSCharacter theChar = prim as BSCharacter;
143 if (theChar != null)
144 {
145 DereferenceExistingShape(prim, shapeCallback);
146 switch (BSParam.AvatarShape)
147 {
148 case AvatarShapeCapsule:
149 prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
150 BSPhysicsShapeType.SHAPE_CAPSULE, FixedShapeKey.KEY_CAPSULE);
151 ret = true;
152 haveShape = true;
153 break;
154 case AvatarShapeCube:
155 prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
156 BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_CAPSULE);
157 ret = true;
158 haveShape = true;
159 break;
160 case AvatarShapeOvoid:
161 // Saddly, Bullet doesn't scale spheres so this doesn't work as an avatar shape
162 prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
163 BSPhysicsShapeType.SHAPE_SPHERE, FixedShapeKey.KEY_CAPSULE);
164 ret = true;
165 haveShape = true;
166 break;
167 case AvatarShapeMesh:
168 break;
169 default:
170 break;
171 }
172 }
173  
174 // If the prim attributes are simple, this could be a simple Bullet native shape
175 // Native shapes work whether to object is static or physical.
176 if (!haveShape
177 && nativeShapePossible
178 && pbs != null
179 && PrimHasNoCuts(pbs)
180 && ( !pbs.SculptEntry || (pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) )
181 )
182 {
183 // Get the scale of any existing shape so we can see if the new shape is same native type and same size.
184 OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero;
185 if (prim.PhysShape.HasPhysicalShape)
186 scaleOfExistingShape = m_physicsScene.PE.GetLocalScaling(prim.PhysShape.physShapeInfo);
187  
188 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}",
189 prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.physShapeInfo.shapeType);
190  
191 // It doesn't look like Bullet scales native spheres so make sure the scales are all equal
192 if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
193 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
194 {
195 haveShape = true;
196 if (forceRebuild
197 || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_SPHERE
198 )
199 {
200 DereferenceExistingShape(prim, shapeCallback);
201 prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
202 BSPhysicsShapeType.SHAPE_SPHERE, FixedShapeKey.KEY_SPHERE);
203 ret = true;
204 }
205 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},rebuilt={2},shape={3}",
206 prim.LocalID, forceRebuild, ret, prim.PhysShape);
207 }
208 // If we didn't make a sphere, maybe a box will work.
209 if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
210 {
211 haveShape = true;
212 if (forceRebuild
213 || prim.Scale != scaleOfExistingShape
214 || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_BOX
215 )
216 {
217 DereferenceExistingShape(prim, shapeCallback);
218 prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
219 BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
220 ret = true;
221 }
222 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},rebuilt={2},shape={3}",
223 prim.LocalID, forceRebuild, ret, prim.PhysShape);
224 }
225 }
226  
227 // If a simple shape is not happening, create a mesh and possibly a hull.
228 if (!haveShape && pbs != null)
229 {
230 ret = CreateGeomMeshOrHull(prim, shapeCallback);
231 }
232  
233 return ret;
234 }
235  
236 // return 'true' if this shape description does not include any cutting or twisting.
237 public static bool PrimHasNoCuts(PrimitiveBaseShape pbs)
238 {
239 return pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
240 && pbs.ProfileHollow == 0
241 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
242 && pbs.PathBegin == 0 && pbs.PathEnd == 0
243 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
244 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
245 && pbs.PathShearX == 0 && pbs.PathShearY == 0;
246 }
247  
248 // return 'true' if the prim's shape was changed.
249 private bool CreateGeomMeshOrHull(BSPhysObject prim, PhysicalDestructionCallback shapeCallback)
250 {
251  
252 bool ret = false;
253 // Note that if it's a native shape, the check for physical/non-physical is not
254 // made. Native shapes work in either case.
255 if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects)
256 {
257 // Use a simple, single mesh convex hull shape if the object is simple enough
258 BSShape potentialHull = null;
259  
260 PrimitiveBaseShape pbs = prim.BaseShape;
261 // Use a simple, one section convex shape for prims that are probably convex (no cuts or twists)
262 if (BSParam.ShouldUseSingleConvexHullForPrims
263 && pbs != null
264 && !pbs.SculptEntry
265 && PrimHasNoCuts(pbs)
266 )
267 {
268 potentialHull = BSShapeConvexHull.GetReference(m_physicsScene, false /* forceRebuild */, prim);
269 }
270 // Use the GImpact shape if it is a prim that has some concaveness
271 if (potentialHull == null
272 && BSParam.ShouldUseGImpactShapeForPrims
273 && pbs != null
274 && !pbs.SculptEntry
275 )
276 {
277 potentialHull = BSShapeGImpact.GetReference(m_physicsScene, false /* forceRebuild */, prim);
278 }
279 // If not any of the simple cases, just make a hull
280 if (potentialHull == null)
281 {
282 potentialHull = BSShapeHull.GetReference(m_physicsScene, false /*forceRebuild*/, prim);
283 }
284  
285 // If the current shape is not what is on the prim at the moment, time to change.
286 if (!prim.PhysShape.HasPhysicalShape
287 || potentialHull.ShapeType != prim.PhysShape.ShapeType
288 || potentialHull.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey)
289 {
290 DereferenceExistingShape(prim, shapeCallback);
291 prim.PhysShape = potentialHull;
292 ret = true;
293 }
294 else
295 {
296 // The current shape on the prim is the correct one. We don't need the potential reference.
297 potentialHull.Dereference(m_physicsScene);
298 }
299 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1}", prim.LocalID, prim.PhysShape);
300 }
301 else
302 {
303 // Non-physical objects should be just meshes.
304 BSShape potentialMesh = BSShapeMesh.GetReference(m_physicsScene, false /*forceRebuild*/, prim);
305 // If the current shape is not what is on the prim at the moment, time to change.
306 if (!prim.PhysShape.HasPhysicalShape
307 || potentialMesh.ShapeType != prim.PhysShape.ShapeType
308 || potentialMesh.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey)
309 {
310 DereferenceExistingShape(prim, shapeCallback);
311 prim.PhysShape = potentialMesh;
312 ret = true;
313 }
314 else
315 {
316 // We don't need this reference to the mesh that is already being using.
317 potentialMesh.Dereference(m_physicsScene);
318 }
319 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1}", prim.LocalID, prim.PhysShape);
320 }
321 return ret;
322 }
323  
324 // Track another user of a body.
325 // We presume the caller has allocated the body.
326 // Bodies only have one user so the body is just put into the world if not already there.
327 private void ReferenceBody(BulletBody body)
328 {
329 lock (m_collectionActivityLock)
330 {
331 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body);
332 if (!m_physicsScene.PE.IsInWorld(m_physicsScene.World, body))
333 {
334 m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, body);
335 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
336 }
337 }
338 }
339  
340 // Release the usage of a body.
341 // Called when releasing use of a BSBody. BSShape is handled separately.
342 // Called in taint time.
343 public void DereferenceBody(BulletBody body, PhysicalDestructionCallback bodyCallback )
344 {
345 if (!body.HasPhysicalBody)
346 return;
347  
348 m_physicsScene.AssertInTaintTime("BSShapeCollection.DereferenceBody");
349  
350 lock (m_collectionActivityLock)
351 {
352 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1}", body.ID, body);
353 // If the caller needs to know the old body is going away, pass the event up.
354 if (bodyCallback != null)
355 bodyCallback(body, null);
356  
357 // Removing an object not in the world is a NOOP
358 m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, body);
359  
360 // Zero any reference to the shape so it is not freed when the body is deleted.
361 m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, body, null);
362  
363 m_physicsScene.PE.DestroyObject(m_physicsScene.World, body);
364 }
365 }
366  
367 // Create a body object in Bullet.
368 // Updates prim.BSBody with the information about the new body if one is created.
369 // Returns 'true' if an object was actually created.
370 // Called at taint-time.
371 private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, PhysicalDestructionCallback bodyCallback)
372 {
373 bool ret = false;
374  
375 // the mesh, hull or native shape must have already been created in Bullet
376 bool mustRebuild = !prim.PhysBody.HasPhysicalBody;
377  
378 // If there is an existing body, verify it's of an acceptable type.
379 // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
380 if (!mustRebuild)
381 {
382 CollisionObjectTypes bodyType = (CollisionObjectTypes)m_physicsScene.PE.GetBodyType(prim.PhysBody);
383 if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY
384 || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
385 {
386 // If the collisionObject is not the correct type for solidness, rebuild what's there
387 mustRebuild = true;
388 if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,forceRebuildBecauseChangingBodyType,bodyType={1}", prim.LocalID, bodyType);
389 }
390 }
391  
392 if (mustRebuild || forceRebuild)
393 {
394 // Free any old body
395 DereferenceBody(prim.PhysBody, bodyCallback);
396  
397 BulletBody aBody;
398 if (prim.IsSolid)
399 {
400 aBody = m_physicsScene.PE.CreateBodyFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation);
401 if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,rigid,body={1}", prim.LocalID, aBody);
402 }
403 else
404 {
405 aBody = m_physicsScene.PE.CreateGhostFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation);
406 if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,body={1}", prim.LocalID, aBody);
407 }
408  
409 ReferenceBody(aBody);
410  
411 prim.PhysBody = aBody;
412  
413 ret = true;
414 }
415  
416 return ret;
417 }
418  
419 private void DetailLog(string msg, params Object[] args)
420 {
421 if (m_physicsScene.PhysicsLogging.Enabled)
422 m_physicsScene.DetailLog(msg, args);
423 }
424 }
425 }