From 22290ef35aa13edb1501c69b3cce63a885302563 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Thu, 20 Sep 2012 10:12:51 -0700 Subject: BulletSim: complete code for managed code shape and body tracking. Not debugged. Eliminate some null exceptions created adding the above code. Add and remove some detailed logging statements. --- .../Physics/BulletSPlugin/BSShapeCollection.cs | 617 ++++++++++++++++++--- 1 file changed, 547 insertions(+), 70 deletions(-) (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index eb4b2ad..7470d23 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -1,70 +1,547 @@ -/* - * 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; - -namespace OpenSim.Region.Physics.BulletSPlugin -{ -public class BSShapeCollection : IDisposable -{ - protected BSScene PhysicsScene { get; set; } - - public BSShapeCollection(BSScene physScene) - { - PhysicsScene = physScene; - } - - public void Dispose() - { - } - - // Track another user of a body - public void ReferenceBody(BulletBody shape) - { - } - - // Release the usage of a body - public void DereferenceBody(BulletBody shape) - { - } - - // Track another user of the shape - public void ReferenceShape(BulletShape shape) - { - } - - // Release the usage of a shape - public void DereferenceShape(BulletShape shape) - { - } - - - -} -} +/* + * 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 class BSShapeCollection : IDisposable +{ + protected BSScene PhysicsScene { get; set; } + + private Object m_shapeActivityLock = new Object(); + + private struct MeshDesc + { + public IntPtr Ptr; + public int referenceCount; + public DateTime lastReferenced; + public IMesh meshData; + } + + private struct HullDesc + { + public IntPtr Ptr; + public int referenceCount; + public DateTime lastReferenced; + } + + private Dictionary Meshes = new Dictionary(); + private Dictionary Hulls = new Dictionary(); + + public BSShapeCollection(BSScene physScene) + { + PhysicsScene = physScene; + } + + public void Dispose() + { + } + + // Called to update/change the body and shape for an object. + // First checks the shape and updates that if necessary then makes + // sure the body is of the right type. + // Return 'true' if either the body or the shape changed. + // Called at taint-time!! + public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs) + { + bool ret = false; + + // Do we have the correct geometry for this type of object? + if (CreateGeom(forceRebuild, prim, shapeData, pbs)) + { + // If we had to select a new shape geometry for the object, + // rebuild the body around it. + CreateObject(true, prim, PhysicsScene.World, prim.BSShape, shapeData); + ret = true; + } + + return ret; + } + + // Track another user of a body + public void ReferenceBody(BulletBody shape) + { + } + + // Release the usage of a body + public void DereferenceBody(BulletBody shape) + { + } + + // Track another user of the shape + public void ReferenceShape(BulletShape shape) + { + ReferenceShape(shape, null); + } + + // Track the datastructures and use count for a shape. + // When creating a hull, this is called first to reference the mesh + // and then again to reference the hull. + // Meshes and hulls for the same shape have the same hash key. + private void ReferenceShape(BulletShape shape, IMesh meshData) + { + switch (shape.type) + { + case ShapeData.PhysicsShapeType.SHAPE_MESH: + MeshDesc meshDesc; + if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) + { + // There is an existing instance of this mesh. + meshDesc.referenceCount++; + } + else + { + // This is a new reference to a mesh + meshDesc.Ptr = shape.Ptr; + meshDesc.meshData = meshData; + meshDesc.referenceCount = 1; + + } + meshDesc.lastReferenced = System.DateTime.Now; + Meshes[shape.shapeKey] = meshDesc; + break; + case ShapeData.PhysicsShapeType.SHAPE_HULL: + HullDesc hullDesc; + if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) + { + // There is an existing instance of this mesh. + hullDesc.referenceCount++; + } + else + { + // This is a new reference to a mesh + hullDesc.Ptr = shape.Ptr; + hullDesc.referenceCount = 1; + + } + hullDesc.lastReferenced = System.DateTime.Now; + Hulls[shape.shapeKey] = hullDesc; + break; + default: + break; + } + } + + // Release the usage of a shape + public void DereferenceShape(BulletShape shape) + { + switch (shape.type) + { + case ShapeData.PhysicsShapeType.SHAPE_HULL: + DereferenceHull(shape); + // Hulls also include a mesh + DereferenceMesh(shape); + break; + case ShapeData.PhysicsShapeType.SHAPE_MESH: + DereferenceMesh(shape); + break; + default: + break; + } + } + + // Count down the reference count for a mesh shape + private void DereferenceMesh(BulletShape shape) + { + MeshDesc meshDesc; + if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) + { + meshDesc.referenceCount--; + // TODO: release the Bullet storage + meshDesc.lastReferenced = System.DateTime.Now; + Meshes[shape.shapeKey] = meshDesc; + } + } + + // Count down the reference count for a hull shape + private void DereferenceHull(BulletShape shape) + { + HullDesc hullDesc; + if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) + { + hullDesc.referenceCount--; + // TODO: release the Bullet storage (aging old entries?) + hullDesc.lastReferenced = System.DateTime.Now; + Hulls[shape.shapeKey] = hullDesc; + } + } + + // Create the geometry information in Bullet for later use. + // The objects needs a hull if it's physical otherwise a mesh is enough. + // No locking here because this is done when we know physics is not simulating. + // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used. + // Returns 'true' if the geometry was rebuilt. + // Called at taint-time! + private bool CreateGeom(bool forceRebuild, BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs) + { + bool ret = false; + bool haveShape = false; + bool nativeShapePossible = true; + + BulletShape newShape = new BulletShape(IntPtr.Zero); + + // If the object is dynamic, it must have a hull shape + if (prim.IsPhysical) + nativeShapePossible = false; + + // If the prim attributes are simple, this could be a simple Bullet native shape + if (nativeShapePossible + && ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim) + || (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) ) ) + { + if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) + { + haveShape = true; + if (forceRebuild || (prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE)) + { + DetailLog("{0},BSShapeCollection.CreateGeom,sphere (force={1}", prim.LocalID, forceRebuild); + newShape = AddNativeShapeToPrim(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_SPHERE); + + ret = true; + } + } + else + { + // m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, type={2}, size={3}", LogHeader, LocalID, _shapeType, _size); + haveShape = true; + if (forceRebuild || (prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX)) + { + DetailLog("{0},BSShapeCollection.CreateGeom,box (force={1})", prim.LocalID, forceRebuild); + newShape = AddNativeShapeToPrim(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_BOX); + + ret = true; + } + } + } + // If a simple shape isn't happening, create a mesh and possibly a hull + if (!haveShape) + { + if (prim.IsPhysical) + { + if (forceRebuild || !Hulls.ContainsKey(prim.BSShape.shapeKey)) + { + // physical objects require a hull for interaction. + // This also creates the mesh if it doesn't already exist + ret = CreateGeomHull(prim, shapeData, pbs); + } + } + else + { + if (forceRebuild || !Meshes.ContainsKey(prim.BSShape.shapeKey)) + { + // Static (non-physical) objects only need a mesh for bumping into + ret = CreateGeomMesh(prim, shapeData, pbs); + } + } + } + return ret; + } + + private BulletShape AddNativeShapeToPrim(BSPrim prim, ShapeData shapeData, ShapeData.PhysicsShapeType shapeType) + { + BulletShape newShape; + + // Bullet native objects are scaled by the Bullet engine so pass the size in + prim.Scale = shapeData.Size; + + // release any previous shape + DereferenceShape(prim.BSShape); + + MeshDesc existingShapeDesc; + if (Meshes.TryGetValue(shapeData.MeshKey, out existingShapeDesc)) + { + // If there is an existing allocated shape, use it + newShape = new BulletShape(existingShapeDesc.Ptr, shapeType); + } + else + { + // Shape of this discriptioin is not allocated. Create new. + newShape = new BulletShape( + BulletSimAPI.BuildNativeShape2(PhysicsScene.World.Ptr, + (float)shapeType, + PhysicsScene.Params.collisionMargin, + prim.Scale), + shapeType); + } + newShape.shapeKey = shapeData.MeshKey; + ReferenceShape(newShape); + prim.BSShape = newShape; + return newShape; + } + + // No locking here because this is done when we know physics is not simulating + // Returns 'true' of a mesh was actually rebuild (we could also have one of these specs). + // Called at taint-time! + private bool CreateGeomMesh(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs) + { + BulletShape newShape = new BulletShape(IntPtr.Zero); + + // level of detail based on size and type of the object + float lod = PhysicsScene.MeshLOD; + if (pbs.SculptEntry) + lod = PhysicsScene.SculptLOD; + + float maxAxis = Math.Max(shapeData.Size.X, Math.Max(shapeData.Size.Y, shapeData.Size.Z)); + if (maxAxis > PhysicsScene.MeshMegaPrimThreshold) + lod = PhysicsScene.MeshMegaPrimLOD; + + ulong newMeshKey = (ulong)pbs.GetMeshKey(shapeData.Size, lod); + // m_log.DebugFormat("{0}: CreateGeomMesh: lID={1}, oldKey={2}, newKey={3}", LogHeader, LocalID, _meshKey, newMeshKey); + + // if this new shape is the same as last time, don't recreate the mesh + if (prim.BSShape.shapeKey == newMeshKey) return false; + + DetailLog("{0},BSShapeCollection.CreateGeomMesh,create,key={1}", prim.LocalID, newMeshKey); + + // Since we're recreating new, get rid of the reference to the previous shape + DereferenceShape(prim.BSShape); + + IMesh meshData = null; + IntPtr meshPtr; + MeshDesc meshDesc; + if (Meshes.TryGetValue(newMeshKey, out meshDesc)) + { + // If the mesh has already been built just use it. + meshPtr = meshDesc.Ptr; + } + else + { + // always pass false for physicalness as this creates some sort of bounding box which we don't need + meshData = PhysicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, shapeData.Size, lod, false); + + int[] indices = meshData.getIndexListAsInt(); + List vertices = meshData.getVertexList(); + + float[] verticesAsFloats = new float[vertices.Count * 3]; + int vi = 0; + foreach (OMV.Vector3 vv in vertices) + { + verticesAsFloats[vi++] = vv.X; + verticesAsFloats[vi++] = vv.Y; + verticesAsFloats[vi++] = vv.Z; + } + + // m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}", + // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count); + + meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.Ptr, + indices.GetLength(0), indices, vertices.Count, verticesAsFloats); + } + newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH); + newShape.shapeKey = newMeshKey; + + ReferenceShape(newShape, meshData); + + // meshes are already scaled by the meshmerizer + prim.Scale = new OMV.Vector3(1f, 1f, 1f); + prim.BSShape = newShape; + return true; // 'true' means a new shape has been added to this prim + } + + // No locking here because this is done when we know physics is not simulating + // Returns 'true' of a mesh was actually rebuild (we could also have one of these specs). + List m_hulls; + private bool CreateGeomHull(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs) + { + BulletShape newShape; + + float lod = pbs.SculptEntry ? PhysicsScene.SculptLOD : PhysicsScene.MeshLOD; + ulong newHullKey = (ulong)pbs.GetMeshKey(shapeData.Size, lod); + // m_log.DebugFormat("{0}: CreateGeomHull: lID={1}, oldKey={2}, newKey={3}", LogHeader, LocalID, _hullKey, newHullKey); + + // if the hull hasn't changed, don't rebuild it + if (newHullKey == prim.BSShape.shapeKey) return false; + + DetailLog("{0},BSShapeCollection.CreateGeomHull,create,oldKey={1},newKey={2}", prim.LocalID, newHullKey, newHullKey); + + // remove references to any previous shape + DereferenceShape(prim.BSShape); + + // Make sure the underlying mesh exists and is correct + // Since we're in the hull code, we know CreateGeomMesh() will not create a native shape. + CreateGeomMesh(prim, shapeData, pbs); + MeshDesc meshDesc = Meshes[newHullKey]; + + IntPtr hullPtr; + HullDesc hullDesc; + if (Hulls.TryGetValue(newHullKey, out hullDesc)) + { + hullPtr = hullDesc.Ptr; + } + else + { + int[] indices = meshDesc.meshData.getIndexListAsInt(); + List vertices = meshDesc.meshData.getVertexList(); + + //format conversion from IMesh format to DecompDesc format + List convIndices = new List(); + List convVertices = new List(); + for (int ii = 0; ii < indices.GetLength(0); ii++) + { + convIndices.Add(indices[ii]); + } + foreach (OMV.Vector3 vv in vertices) + { + convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); + } + + // setup and do convex hull conversion + m_hulls = new List(); + DecompDesc dcomp = new DecompDesc(); + dcomp.mIndices = convIndices; + dcomp.mVertices = convVertices; + ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); + // create the hull into the _hulls variable + convexBuilder.process(dcomp); + + // Convert the vertices and indices for passing to unmanaged. + // The hull information is passed as a large floating point array. + // The format is: + // convHulls[0] = number of hulls + // convHulls[1] = number of vertices in first hull + // convHulls[2] = hull centroid X coordinate + // convHulls[3] = hull centroid Y coordinate + // convHulls[4] = hull centroid Z coordinate + // convHulls[5] = first hull vertex X + // convHulls[6] = first hull vertex Y + // convHulls[7] = first hull vertex Z + // convHulls[8] = second hull vertex X + // ... + // convHulls[n] = number of vertices in second hull + // convHulls[n+1] = second hull centroid X coordinate + // ... + // + // TODO: is is very inefficient. Someday change the convex hull generator to return + // data structures that do not need to be converted in order to pass to Bullet. + // And maybe put the values directly into pinned memory rather than marshaling. + int hullCount = m_hulls.Count; + int totalVertices = 1; // include one for the count of the hulls + foreach (ConvexResult cr in m_hulls) + { + totalVertices += 4; // add four for the vertex count and centroid + totalVertices += cr.HullIndices.Count * 3; // we pass just triangles + } + float[] convHulls = new float[totalVertices]; + + convHulls[0] = (float)hullCount; + int jj = 1; + foreach (ConvexResult cr in m_hulls) + { + // copy vertices for index access + float3[] verts = new float3[cr.HullVertices.Count]; + int kk = 0; + foreach (float3 ff in cr.HullVertices) + { + verts[kk++] = ff; + } + + // add to the array one hull's worth of data + convHulls[jj++] = cr.HullIndices.Count; + convHulls[jj++] = 0f; // centroid x,y,z + convHulls[jj++] = 0f; + convHulls[jj++] = 0f; + foreach (int ind in cr.HullIndices) + { + convHulls[jj++] = verts[ind].x; + convHulls[jj++] = verts[ind].y; + convHulls[jj++] = verts[ind].z; + } + } + // create the hull data structure in Bullet + // m_log.DebugFormat("{0}: CreateGeom: calling CreateHull. lid={1}, key={2}, hulls={3}", LogHeader, LocalID, _hullKey, hullCount); + hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.Ptr, hullCount, convHulls); + } + newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL); + newShape.shapeKey = newHullKey; + + ReferenceShape(newShape); + + // meshes are already scaled by the meshmerizer + prim.Scale = new OMV.Vector3(1f, 1f, 1f); + prim.BSShape = newShape; + return true; // 'true' means a new shape has been added to this prim + } + + // Callback from convex hull creater with a newly created hull. + // Just add it to the collection of hulls for this shape. + private void HullReturn(ConvexResult result) + { + m_hulls.Add(result); + return; + } + + // Create an object in Bullet if it has not already been created + // No locking here because this is done when the physics engine is not simulating + // Returns 'true' if an object was actually created. + private bool CreateObject(bool forceRebuild, BSPrim prim, BulletSim sim, BulletShape shape, ShapeData shapeData) + { + // the mesh or hull must have already been created in Bullet + // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, LocalID, shape.Type); + + DereferenceBody(prim.BSBody); + + BulletBody aBody; + IntPtr bodyPtr = IntPtr.Zero; + if (prim.IsSolid) + { + bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.Ptr, shape.Ptr, shapeData.Position, shapeData.Rotation); + } + else + { + bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.Ptr, shape.Ptr, shapeData.Position, shapeData.Rotation); + } + aBody = new BulletBody(shapeData.ID, bodyPtr); + + ReferenceBody(aBody); + + prim.BSBody = aBody; + return true; + } + + private void DetailLog(string msg, params Object[] args) + { + PhysicsScene.PhysicsLogging.Write(msg, args); + } + + + + + +} +} -- cgit v1.1