123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- /*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyrightD
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the OpenSimulator Project nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- using System;
- using System.Collections.Generic;
- using System.Text;
- using OMV = OpenMetaverse;
- using OpenSim.Framework;
- using OpenSim.Region.Physics.Manager;
- using OpenSim.Region.Physics.ConvexDecompositionDotNet;
- namespace OpenSim.Region.Physics.BulletSPlugin
- {
- public sealed class BSShapeCollection : IDisposable
- {
- #pragma warning disable 414
- private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]";
- #pragma warning restore 414
- private BSScene m_physicsScene { get; set; }
- private Object m_collectionActivityLock = new Object();
- private bool DDetail = false;
- public BSShapeCollection(BSScene physScene)
- {
- m_physicsScene = physScene;
- // Set the next to 'true' for very detailed shape update detailed logging (detailed details?)
- // While detailed debugging is still active, this is better than commenting out all the
- // DetailLog statements. When debugging slows down, this and the protected logging
- // statements can be commented/removed.
- DDetail = true;
- }
- public void Dispose()
- {
- // TODO!!!!!!!!!
- }
- // Callbacks called just before either the body or shape is destroyed.
- // Mostly used for changing bodies out from under Linksets.
- // Useful for other cases where parameters need saving.
- // Passing 'null' says no callback.
- public delegate void PhysicalDestructionCallback(BulletBody pBody, BulletShape pShape);
- // Called to update/change the body and shape for an object.
- // The object has some shape and body on it. Here we decide if that is the correct shape
- // for the current state of the object (static/dynamic/...).
- // If bodyCallback is not null, it is called if either the body or the shape are changed
- // so dependencies (like constraints) can be removed before the physical object is dereferenced.
- // Return 'true' if either the body or the shape changed.
- // Called at taint-time.
- public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim, PhysicalDestructionCallback bodyCallback)
- {
- m_physicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape");
- bool ret = false;
- // This lock could probably be pushed down lower but building shouldn't take long
- lock (m_collectionActivityLock)
- {
- // Do we have the correct geometry for this type of object?
- // Updates prim.BSShape with information/pointers to shape.
- // Returns 'true' of BSShape is changed to a new shape.
- bool newGeom = CreateGeom(forceRebuild, prim, bodyCallback);
- // If we had to select a new shape geometry for the object,
- // rebuild the body around it.
- // Updates prim.BSBody with information/pointers to requested body
- // Returns 'true' if BSBody was changed.
- bool newBody = CreateBody((newGeom || forceRebuild), prim, m_physicsScene.World, bodyCallback);
- ret = newGeom || newBody;
- }
- DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}",
- prim.LocalID, forceRebuild, ret, prim.PhysBody, prim.PhysShape);
- return ret;
- }
- public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim)
- {
- return GetBodyAndShape(forceRebuild, sim, prim, null);
- }
- // If the existing prim's shape is to be replaced, remove the tie to the existing shape
- // before replacing it.
- private void DereferenceExistingShape(BSPhysObject prim, PhysicalDestructionCallback shapeCallback)
- {
- if (prim.PhysShape.HasPhysicalShape)
- {
- if (shapeCallback != null)
- shapeCallback(prim.PhysBody, prim.PhysShape.physShapeInfo);
- prim.PhysShape.Dereference(m_physicsScene);
- }
- prim.PhysShape = new BSShapeNull();
- }
- // Create the geometry information in Bullet for later use.
- // The objects needs a hull if it's physical otherwise a mesh is enough.
- // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls,
- // shared geometries will be used. If the parameters of the existing shape are the same
- // as this request, the shape is not rebuilt.
- // Info in prim.BSShape is updated to the new shape.
- // Returns 'true' if the geometry was rebuilt.
- // Called at taint-time!
- public const int AvatarShapeCapsule = 0;
- public const int AvatarShapeCube = 1;
- public const int AvatarShapeOvoid = 2;
- public const int AvatarShapeMesh = 3;
- private bool CreateGeom(bool forceRebuild, BSPhysObject prim, PhysicalDestructionCallback shapeCallback)
- {
- bool ret = false;
- bool haveShape = false;
- bool nativeShapePossible = true;
- PrimitiveBaseShape pbs = prim.BaseShape;
- // Kludge to create the capsule for the avatar.
- // TDOD: Remove/redo this when BSShapeAvatar is working!!
- BSCharacter theChar = prim as BSCharacter;
- if (theChar != null)
- {
- DereferenceExistingShape(prim, shapeCallback);
- switch (BSParam.AvatarShape)
- {
- case AvatarShapeCapsule:
- prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
- BSPhysicsShapeType.SHAPE_CAPSULE, FixedShapeKey.KEY_CAPSULE);
- ret = true;
- haveShape = true;
- break;
- case AvatarShapeCube:
- prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
- BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_CAPSULE);
- ret = true;
- haveShape = true;
- break;
- case AvatarShapeOvoid:
- // Saddly, Bullet doesn't scale spheres so this doesn't work as an avatar shape
- prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
- BSPhysicsShapeType.SHAPE_SPHERE, FixedShapeKey.KEY_CAPSULE);
- ret = true;
- haveShape = true;
- break;
- case AvatarShapeMesh:
- break;
- default:
- break;
- }
- }
- // If the prim attributes are simple, this could be a simple Bullet native shape
- // Native shapes work whether to object is static or physical.
- if (!haveShape
- && nativeShapePossible
- && pbs != null
- && PrimHasNoCuts(pbs)
- && ( !pbs.SculptEntry || (pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) )
- )
- {
- // Get the scale of any existing shape so we can see if the new shape is same native type and same size.
- OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero;
- if (prim.PhysShape.HasPhysicalShape)
- scaleOfExistingShape = m_physicsScene.PE.GetLocalScaling(prim.PhysShape.physShapeInfo);
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}",
- prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.physShapeInfo.shapeType);
- // It doesn't look like Bullet scales native spheres so make sure the scales are all equal
- if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
- && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
- {
- haveShape = true;
- if (forceRebuild
- || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_SPHERE
- )
- {
- DereferenceExistingShape(prim, shapeCallback);
- prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
- BSPhysicsShapeType.SHAPE_SPHERE, FixedShapeKey.KEY_SPHERE);
- ret = true;
- }
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},rebuilt={2},shape={3}",
- prim.LocalID, forceRebuild, ret, prim.PhysShape);
- }
- // If we didn't make a sphere, maybe a box will work.
- if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
- {
- haveShape = true;
- if (forceRebuild
- || prim.Scale != scaleOfExistingShape
- || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_BOX
- )
- {
- DereferenceExistingShape(prim, shapeCallback);
- prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
- BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
- ret = true;
- }
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},rebuilt={2},shape={3}",
- prim.LocalID, forceRebuild, ret, prim.PhysShape);
- }
- }
- // If a simple shape is not happening, create a mesh and possibly a hull.
- if (!haveShape && pbs != null)
- {
- ret = CreateGeomMeshOrHull(prim, shapeCallback);
- }
- return ret;
- }
- // return 'true' if this shape description does not include any cutting or twisting.
- public static bool PrimHasNoCuts(PrimitiveBaseShape pbs)
- {
- return pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
- && pbs.ProfileHollow == 0
- && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
- && pbs.PathBegin == 0 && pbs.PathEnd == 0
- && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
- && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
- && pbs.PathShearX == 0 && pbs.PathShearY == 0;
- }
- // return 'true' if the prim's shape was changed.
- private bool CreateGeomMeshOrHull(BSPhysObject prim, PhysicalDestructionCallback shapeCallback)
- {
- bool ret = false;
- // Note that if it's a native shape, the check for physical/non-physical is not
- // made. Native shapes work in either case.
- if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects)
- {
- // Use a simple, single mesh convex hull shape if the object is simple enough
- BSShape potentialHull = null;
- PrimitiveBaseShape pbs = prim.BaseShape;
- // Use a simple, one section convex shape for prims that are probably convex (no cuts or twists)
- if (BSParam.ShouldUseSingleConvexHullForPrims
- && pbs != null
- && !pbs.SculptEntry
- && PrimHasNoCuts(pbs)
- )
- {
- potentialHull = BSShapeConvexHull.GetReference(m_physicsScene, false /* forceRebuild */, prim);
- }
- // Use the GImpact shape if it is a prim that has some concaveness
- if (potentialHull == null
- && BSParam.ShouldUseGImpactShapeForPrims
- && pbs != null
- && !pbs.SculptEntry
- )
- {
- potentialHull = BSShapeGImpact.GetReference(m_physicsScene, false /* forceRebuild */, prim);
- }
- // If not any of the simple cases, just make a hull
- if (potentialHull == null)
- {
- potentialHull = BSShapeHull.GetReference(m_physicsScene, false /*forceRebuild*/, prim);
- }
- // If the current shape is not what is on the prim at the moment, time to change.
- if (!prim.PhysShape.HasPhysicalShape
- || potentialHull.ShapeType != prim.PhysShape.ShapeType
- || potentialHull.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey)
- {
- DereferenceExistingShape(prim, shapeCallback);
- prim.PhysShape = potentialHull;
- ret = true;
- }
- else
- {
- // The current shape on the prim is the correct one. We don't need the potential reference.
- potentialHull.Dereference(m_physicsScene);
- }
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1}", prim.LocalID, prim.PhysShape);
- }
- else
- {
- // Non-physical objects should be just meshes.
- BSShape potentialMesh = BSShapeMesh.GetReference(m_physicsScene, false /*forceRebuild*/, prim);
- // If the current shape is not what is on the prim at the moment, time to change.
- if (!prim.PhysShape.HasPhysicalShape
- || potentialMesh.ShapeType != prim.PhysShape.ShapeType
- || potentialMesh.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey)
- {
- DereferenceExistingShape(prim, shapeCallback);
- prim.PhysShape = potentialMesh;
- ret = true;
- }
- else
- {
- // We don't need this reference to the mesh that is already being using.
- potentialMesh.Dereference(m_physicsScene);
- }
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1}", prim.LocalID, prim.PhysShape);
- }
- return ret;
- }
- // Track another user of a body.
- // We presume the caller has allocated the body.
- // Bodies only have one user so the body is just put into the world if not already there.
- private void ReferenceBody(BulletBody body)
- {
- lock (m_collectionActivityLock)
- {
- if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body);
- if (!m_physicsScene.PE.IsInWorld(m_physicsScene.World, body))
- {
- m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, body);
- if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
- }
- }
- }
- // Release the usage of a body.
- // Called when releasing use of a BSBody. BSShape is handled separately.
- // Called in taint time.
- public void DereferenceBody(BulletBody body, PhysicalDestructionCallback bodyCallback )
- {
- if (!body.HasPhysicalBody)
- return;
- m_physicsScene.AssertInTaintTime("BSShapeCollection.DereferenceBody");
- lock (m_collectionActivityLock)
- {
- if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1}", body.ID, body);
- // If the caller needs to know the old body is going away, pass the event up.
- if (bodyCallback != null)
- bodyCallback(body, null);
- // Removing an object not in the world is a NOOP
- m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, body);
- // Zero any reference to the shape so it is not freed when the body is deleted.
- m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, body, null);
- m_physicsScene.PE.DestroyObject(m_physicsScene.World, body);
- }
- }
- // Create a body object in Bullet.
- // Updates prim.BSBody with the information about the new body if one is created.
- // Returns 'true' if an object was actually created.
- // Called at taint-time.
- private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, PhysicalDestructionCallback bodyCallback)
- {
- bool ret = false;
- // the mesh, hull or native shape must have already been created in Bullet
- bool mustRebuild = !prim.PhysBody.HasPhysicalBody;
- // If there is an existing body, verify it's of an acceptable type.
- // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
- if (!mustRebuild)
- {
- CollisionObjectTypes bodyType = (CollisionObjectTypes)m_physicsScene.PE.GetBodyType(prim.PhysBody);
- if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY
- || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
- {
- // If the collisionObject is not the correct type for solidness, rebuild what's there
- mustRebuild = true;
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,forceRebuildBecauseChangingBodyType,bodyType={1}", prim.LocalID, bodyType);
- }
- }
- if (mustRebuild || forceRebuild)
- {
- // Free any old body
- DereferenceBody(prim.PhysBody, bodyCallback);
- BulletBody aBody;
- if (prim.IsSolid)
- {
- aBody = m_physicsScene.PE.CreateBodyFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation);
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,rigid,body={1}", prim.LocalID, aBody);
- }
- else
- {
- aBody = m_physicsScene.PE.CreateGhostFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation);
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,body={1}", prim.LocalID, aBody);
- }
- ReferenceBody(aBody);
- prim.PhysBody = aBody;
- ret = true;
- }
- return ret;
- }
- private void DetailLog(string msg, params Object[] args)
- {
- if (m_physicsScene.PhysicsLogging.Enabled)
- m_physicsScene.DetailLog(msg, args);
- }
- }
- }
|