opensim – Blame information for rev 1

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