diff options
Diffstat (limited to 'OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs')
-rwxr-xr-x | OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs b/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs new file mode 100755 index 0000000..d1de844 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs | |||
@@ -0,0 +1,425 @@ | |||
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 | } | ||