// Create the initial instance of terrain and the underlying ground plane.
    // This is called from the initialization routine so we presume it is
    //    safe to call Bullet in real time. We hope no one is moving prims around yet.
    public void CreateInitialGroundPlaneAndTerrain()
    {
        // The ground plane is here to catch things that are trying to drop to negative infinity
        BulletShape groundPlaneShape = new BulletShape(
                    BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, 
                                    BSParam.TerrainCollisionMargin),
                    BSPhysicsShapeType.SHAPE_GROUNDPLANE);
        m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
                        BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
                                                            Vector3.Zero, Quaternion.Identity));
        BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr, Vector3.Zero, Quaternion.Identity);
        BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_groundPlane.ptr);
        // Ground plane does not move
        BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION);
        // Everything collides with the ground plane.
        m_groundPlane.collisionType = CollisionType.Groundplane;
        m_groundPlane.ApplyCollisionMask();

        // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
        BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize);
        m_terrains.Add(Vector3.Zero, initialTerrain);
    }
        // Called before the simulation step to make sure the compound based linkset
        //    is all initialized.
        // Constraint linksets are rebuilt every time.
        // Note that this works for rebuilding just the root after a linkset is taken apart.
        // Called at taint time!!
        private void RecomputeLinksetCompound()
        {
            try
            {
                // Suppress rebuilding while rebuilding
                Rebuilding = true;

                // Cause the root shape to be rebuilt as a compound object with just the root in it
                LinksetRoot.ForceBodyShapeRebuild(true);

                DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}",
                          LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren);

                // Add a shape for each of the other children in the linkset
                ForEachMember(delegate(BSPhysObject cPrim)
                {
                    if (!IsRoot(cPrim))
                    {
                        // Compute the displacement of the child from the root of the linkset.
                        // This info is saved in the child prim so the relationship does not
                        //    change over time and the new child position can be computed
                        //    when the linkset is being disassembled (the linkset may have moved).
                        BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo;
                        if (lci == null)
                        {
                            // Each child position and rotation is given relative to the root.
                            OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
                            OMV.Vector3 displacementPos       = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation;
                            OMV.Quaternion displacementRot    = cPrim.RawOrientation * invRootOrientation;

                            // Save relative position for recomputing child's world position after moving linkset.
                            lci = new BSLinksetCompoundInfo(displacementPos, displacementRot);
                            cPrim.LinksetInfo = lci;
                            DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
                        }

                        DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}",
                                  LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, lci.OffsetPos, lci.OffsetRot);

                        if (cPrim.PhysShape.isNativeShape)
                        {
                            // A native shape is turning into a hull collision shape because native
                            //    shapes are not shared so we have to hullify it so it will be tracked
                            //    and freed at the correct time. This also solves the scaling problem
                            //    (native shapes scaled but hull/meshes are assumed to not be).
                            // TODO: decide of the native shape can just be used in the compound shape.
                            //    Use call to CreateGeomNonSpecial().
                            BulletShape saveShape = cPrim.PhysShape;
                            cPrim.PhysShape.Clear();    // Don't let the create free the child's shape
                            // PhysicsScene.Shapes.CreateGeomNonSpecial(true, cPrim, null);
                            PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null);
                            BulletShape newShape = cPrim.PhysShape;
                            cPrim.PhysShape      = saveShape;
                            BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, lci.OffsetPos, lci.OffsetRot);
                        }
                        else
                        {
                            // For the shared shapes (meshes and hulls), just use the shape in the child.
                            // The reference count added here will be decremented when the compound shape
                            //     is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced).
                            if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape))
                            {
                                PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}",
                                                                LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape);
                            }
                            BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, lci.OffsetPos, lci.OffsetRot);
                        }
                    }
                    return(false); // 'false' says to move onto the next child in the list
                });

                // With all of the linkset packed into the root prim, it has the mass of everyone.
                LinksetMass = LinksetMass;
                LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true);
            }
            finally
            {
                Rebuilding = false;
            }

            BulletSimAPI.RecalculateCompoundShapeLocalAabb2(LinksetRoot.PhysShape.ptr);

            // DEBUG: see of inter-linkset collisions are causing problems for constraint linksets.
            // BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
            //                     (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
        }
示例#3
0
    // Create terrain mesh from a heightmap.
    public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, 
                                                    Vector3 minCoords, Vector3 maxCoords)
        : base(physicsScene, regionBase, id)
    {
        int indicesCount;
        int[] indices;
        int verticesCount;
        float[] vertices;

        m_savedHeightMap = initialMap;

        m_sizeX = (int)(maxCoords.X - minCoords.X);
        m_sizeY = (int)(maxCoords.Y - minCoords.Y);

        if (!BSTerrainMesh.ConvertHeightmapToMesh(PhysicsScene, initialMap,
                            m_sizeX, m_sizeY,
                            (float)m_sizeX, (float)m_sizeY,
                            Vector3.Zero, 1.0f,
                            out indicesCount, out indices, out verticesCount, out vertices))
        {
            // DISASTER!!
            PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap", ID);
            PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase);
            // Something is very messed up and a crash is in our future.
            return;
        }
        PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,indices={1},indSz={2},vertices={3},vertSz={4}", 
                                ID, indicesCount, indices.Length, verticesCount, vertices.Length);

        m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
                                                    indicesCount, indices, verticesCount, vertices),
                                        BSPhysicsShapeType.SHAPE_MESH);
        if (!m_terrainShape.HasPhysicalShape)
        {
            // DISASTER!!
            PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID);
            physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase);
            // Something is very messed up and a crash is in our future.
            return;
        }

        Vector3 pos = regionBase;
        Quaternion rot = Quaternion.Identity;

        m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( m_terrainShape.ptr, ID, pos, rot));
        if (!m_terrainBody.HasPhysicalBody)
        {
            // DISASTER!!
            physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase);
            // Something is very messed up and a crash is in our future.
            return;
        }

        // Set current terrain attributes
        BulletSimAPI.SetFriction2(m_terrainBody.ptr, BSParam.TerrainFriction);
        BulletSimAPI.SetHitFraction2(m_terrainBody.ptr, BSParam.TerrainHitFraction);
        BulletSimAPI.SetRestitution2(m_terrainBody.ptr, BSParam.TerrainRestitution);
        BulletSimAPI.SetCollisionFlags2(m_terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);

        // Static objects are not very massive.
        BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero);

        // Put the new terrain to the world of physical objects
        BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr, pos, rot);

        // Redo its bounding box now that it is in the world
        BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr);

        m_terrainBody.collisionType = CollisionType.Terrain;
        m_terrainBody.ApplyCollisionMask();

        // Make it so the terrain will not move or be considered for movement.
        BulletSimAPI.ForceActivationState2(m_terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
    }
    // The creation of a mesh or hull can fail if an underlying asset is not available.
    // There are two cases: 1) the asset is not in the cache and it needs to be fetched;
    //     and 2) the asset cannot be converted (like failed decompression of JPEG2000s).
    //     The first case causes the asset to be fetched. The second case requires
    //     us to not loop forever.
    // Called after creating a physical mesh or hull. If the physical shape was created,
    //     just return.
    private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim)
    {
        // If the shape was successfully created, nothing more to do
        if (newShape.HasPhysicalShape)
            return newShape;

        // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
        if (prim.BaseShape.SculptEntry && !prim.LastAssetBuildFailed && prim.BaseShape.SculptTexture != OMV.UUID.Zero)
        {
            prim.LastAssetBuildFailed = true;
            BSPhysObject xprim = prim;
            DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lID={1},lastFailed={2}",
                            LogHeader, prim.LocalID, prim.LastAssetBuildFailed);
            Util.FireAndForget(delegate
                {
                    RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod;
                    if (assetProvider != null)
                    {
                        BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
                        assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
                        {
                            if (!yprim.BaseShape.SculptEntry)
                                return;
                            if (yprim.BaseShape.SculptTexture.ToString() != asset.ID)
                                return;

                            yprim.BaseShape.SculptData = asset.Data;
                            // This will cause the prim to see that the filler shape is not the right
                            //    one and try again to build the object.
                            // No race condition with the normal shape setting since the rebuild is at taint time.
                            yprim.ForceBodyShapeRebuild(false);

                        });
                    }
                });
        }
        else
        {
            if (prim.LastAssetBuildFailed)
            {
                PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}",
                                            LogHeader, prim.LocalID, prim.BaseShape.SculptTexture);
            }
        }

        // While we figure out the real problem, stick a simple native shape on the object.
        BulletShape fillinShape =
            BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);

        return fillinShape;
    }
    // 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, BulletShape shape,
                            BodyDestructionCallback 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)BulletSimAPI.GetBodyType2(prim.PhysBody.ptr);
            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 (mustRebuild || forceRebuild)
        {
            // Free any old body
            DereferenceBody(prim.PhysBody, true, bodyCallback);

            BulletBody aBody;
            Object bodyPtr = null;
            if (prim.IsSolid)
            {
                bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
                                        prim.LocalID, prim.RawPosition, prim.RawOrientation);
                if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString());
            }
            else
            {
                bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
                                        prim.LocalID, prim.RawPosition, prim.RawOrientation);
                if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString());
            }
            aBody = new BulletBody(prim.LocalID, bodyPtr);

            ReferenceBody(aBody, true);

            prim.PhysBody = aBody;

            ret = true;
        }

        return ret;
    }
    private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
    {

        Object hullPtr = null;
        HullDesc hullDesc;
        if (Hulls.TryGetValue(newHullKey, out hullDesc))
        {
            // If the hull shape already is created, just use it.
            hullPtr = hullDesc.ptr;
        }
        else
        {
            // Build a new hull in the physical world
            // Pass true for physicalness as this creates some sort of bounding box which we don't need
            IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false);
            if (meshData != null)
            {

                int[] indices = meshData.getIndexListAsInt();
                List<OMV.Vector3> vertices = meshData.getVertexList();

                //format conversion from IMesh format to DecompDesc format
                List<int> convIndices = new List<int>();
                List<float3> convVertices = new List<float3>();
                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<ConvexResult>();
                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
                hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
            }
        }

        BulletShape newShape = new BulletShape(hullPtr, BSPhysicsShapeType.SHAPE_HULL);
        newShape.shapeKey = newHullKey;

        return newShape;
    }
    // Compound shapes are always built from scratch.
    // This shouldn't be to bad since most of the parts will be meshes that had been built previously.
    private bool GetReferenceToCompoundShape(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
    {
        // Remove reference to the old shape
        // Don't need to do this as the shape is freed when the new root shape is created below.
        // DereferenceShape(prim.PhysShape, true, shapeCallback);

        BulletShape cShape = new BulletShape(
            BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr, false), BSPhysicsShapeType.SHAPE_COMPOUND);

        // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape.
        CreateGeomMeshOrHull(prim, shapeCallback);
        BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, prim.PhysShape.ptr, OMV.Vector3.Zero, OMV.Quaternion.Identity);
        if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}",
                                    prim.LocalID, cShape, prim.PhysShape);

        prim.PhysShape = cShape;

        return true;
    }
    // Builds a mesh shape in the physical world and updates prim.BSShape.
    // Dereferences previous shape in BSShape and adds a reference for this new shape.
    // Returns 'true' of a mesh was actually built. Otherwise .
    // Called at taint-time!
    private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
    {
        BulletShape newShape = new BulletShape();

        float lod;
        System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);

        // if this new shape is the same as last time, don't recreate the mesh
        if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_MESH)
            return false;

        if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}",
                                prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"));

        // Since we're recreating new, get rid of the reference to the previous shape
        DereferenceShape(prim.PhysShape, true, shapeCallback);

        newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, prim.BaseShape, prim.Size, lod);
        // Take evasive action if the mesh was not constructed.
        newShape = VerifyMeshCreated(newShape, prim);

        ReferenceShape(newShape);

        prim.PhysShape = newShape;

        return true;        // 'true' means a new shape has been added to this prim
    }
    private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
    {
        IMesh meshData = null;
        Object meshPtr = null;
        MeshDesc meshDesc;
        if (Meshes.TryGetValue(newMeshKey, out meshDesc))
        {
            // If the mesh has already been built just use it.
            meshPtr = meshDesc.ptr;
        }
        else
        {
            meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false);

            if (meshData != null)
            {
                int[] indices = meshData.getIndexListAsInt();
                List<OMV.Vector3> 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}: BSShapeCollection.CreatePhysicalMesh: 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);
            }
        }
        BulletShape newShape = new BulletShape(meshPtr, BSPhysicsShapeType.SHAPE_MESH);
        newShape.shapeKey = newMeshKey;

        return newShape;
    }
    // Sometimes we have a pointer to a collision shape but don't know what type it is.
    // Figure out type and call the correct dereference routine.
    // Called at taint-time.
    private void DereferenceAnonCollisionShape(Object cShape)
    {
        MeshDesc meshDesc;
        HullDesc hullDesc;

        BulletShape shapeInfo = new BulletShape(cShape);
        if (TryGetMeshByPtr(cShape, out meshDesc))
        {
            shapeInfo.type = BSPhysicsShapeType.SHAPE_MESH;
            shapeInfo.shapeKey = meshDesc.shapeKey;
        }
        else
        {
            if (TryGetHullByPtr(cShape, out hullDesc))
            {
                shapeInfo.type = BSPhysicsShapeType.SHAPE_HULL;
                shapeInfo.shapeKey = hullDesc.shapeKey;
            }
            else
            {
                if (BulletSimAPI.IsCompound2(cShape))
                {
                    shapeInfo.type = BSPhysicsShapeType.SHAPE_COMPOUND;
                }
                else
                {
                    if (BulletSimAPI.IsNativeShape2(cShape))
                    {
                        shapeInfo.isNativeShape = true;
                        shapeInfo.type = BSPhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter)
                    }
                }
            }
        }

        if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);

        if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN)
        {
            DereferenceShape(shapeInfo, true, null);
        }
        else
        {
            PhysicsScene.Logger.ErrorFormat("{0} Could not decypher shape type. Region={1}, addr={2}",
                                    LogHeader, PhysicsScene.RegionName, cShape.ToString());
        }
    }
    private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, BSPhysicsShapeType shapeType,
                                    FixedShapeKey shapeKey)
    {
        BulletShape newShape;
        // Need to make sure the passed shape information is for the native type.
        ShapeData nativeShapeData = new ShapeData();
        nativeShapeData.Type = shapeType;
        nativeShapeData.ID = prim.LocalID;
        nativeShapeData.Scale = prim.Scale;
        nativeShapeData.Size = prim.Scale;  // unneeded, I think.
        nativeShapeData.MeshKey = (ulong)shapeKey;
        nativeShapeData.HullKey = (ulong)shapeKey;

        if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
        {
            // The proper scale has been calculated in the prim.
            newShape = new BulletShape(
                        BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale)
                        , shapeType);
            if (DDetail) DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
        }
        else
        {
            // Native shapes are scaled in Bullet so set the scaling to the size
            newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType);
        }
        if (!newShape.HasPhysicalShape)
        {
            PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
                                    LogHeader, prim.LocalID, shapeType);
        }
        newShape.shapeKey = (System.UInt64)shapeKey;
        newShape.isNativeShape = true;

        return newShape;
    }
    // Remove a reference to a compound shape.
    // Taking a compound shape apart is a little tricky because if you just delete the
    //      physical shape, it will free all the underlying children. We can't do that because
    //      they could be shared. So, this removes each of the children from the compound and
    //      dereferences them separately before destroying the compound collision object itself.
    // Called at taint-time.
    private void DereferenceCompound(BulletShape shape, ShapeDestructionCallback shapeCallback)
    {
        if (!BulletSimAPI.IsCompound2(shape.ptr))
        {
            // Failed the sanity check!!
            PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}",
                                        LogHeader, shape.type, shape.ptr.ToString());
            if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}",
                                        BSScene.DetailLogZero, shape.type, shape.ptr.ToString());
            return;
        }

        int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr);
        if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren);

        for (int ii = numChildren - 1; ii >= 0; ii--)
        {
            Object childShape = BulletSimAPI.RemoveChildShapeFromCompoundShapeIndex2(shape.ptr, ii);
            DereferenceAnonCollisionShape(childShape);
        }
        BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
    }
    // Count down the reference count for a hull shape
    // Called at taint-time.
    private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback)
    {
        HullDesc hullDesc;
        if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
        {
            hullDesc.referenceCount--;
            // TODO: release the Bullet storage (aging old entries?)

            // Tell upper layers that, if they have dependencies on this shape, this link is going away
            if (shapeCallback != null) shapeCallback(shape);

            hullDesc.lastReferenced = System.DateTime.Now;
            Hulls[shape.shapeKey] = hullDesc;
            if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}",
                    BSScene.DetailLogZero, shape, hullDesc.referenceCount);
        }
    }
    // Count down the reference count for a mesh shape
    // Called at taint-time.
    private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback)
    {
        MeshDesc meshDesc;
        if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
        {
            meshDesc.referenceCount--;
            // TODO: release the Bullet storage
            if (shapeCallback != null) shapeCallback(shape);
            meshDesc.lastReferenced = System.DateTime.Now;
            Meshes[shape.shapeKey] = meshDesc;
            if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}",
                                BSScene.DetailLogZero, shape, meshDesc.referenceCount);

        }
    }
    // Release the usage of a shape.
    public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback)
    {
        if (!shape.HasPhysicalShape)
            return;

        PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceShape", delegate()
        {
            if (shape.HasPhysicalShape)
            {
                if (shape.isNativeShape)
                {
                    // Native shapes are not tracked and are released immediately
                    if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
                                    BSScene.DetailLogZero, shape.ptr.ToString(), inTaintTime);
                    if (shapeCallback != null) shapeCallback(shape);
                    BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
                }
                else
                {
                    switch (shape.type)
                    {
                        case BSPhysicsShapeType.SHAPE_HULL:
                            DereferenceHull(shape, shapeCallback);
                            break;
                        case BSPhysicsShapeType.SHAPE_MESH:
                            DereferenceMesh(shape, shapeCallback);
                            break;
                        case BSPhysicsShapeType.SHAPE_COMPOUND:
                            DereferenceCompound(shape, shapeCallback);
                            break;
                        case BSPhysicsShapeType.SHAPE_UNKNOWN:
                            break;
                        default:
                            break;
                    }
                }
            }
        });
    }
    // 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.
    // NOTE that native shapes are not added to the mesh list or removed.
    // Returns 'true' if this is the initial reference to the shape. Otherwise reused.
    public bool ReferenceShape(BulletShape shape)
    {
        bool ret = false;
        switch (shape.type)
        {
            case BSPhysicsShapeType.SHAPE_MESH:
                MeshDesc meshDesc;
                if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
                {
                    // There is an existing instance of this mesh.
                    meshDesc.referenceCount++;
                    if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}",
                                BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
                }
                else
                {
                    // This is a new reference to a mesh
                    meshDesc.ptr = shape.ptr;
                    meshDesc.shapeKey = shape.shapeKey;
                    // We keep a reference to the underlying IMesh data so a hull can be built
                    meshDesc.referenceCount = 1;
                    if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
                                BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
                    ret = true;
                }
                meshDesc.lastReferenced = System.DateTime.Now;
                Meshes[shape.shapeKey] = meshDesc;
                break;
            case BSPhysicsShapeType.SHAPE_HULL:
                HullDesc hullDesc;
                if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
                {
                    // There is an existing instance of this hull.
                    hullDesc.referenceCount++;
                    if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}",
                                BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
                }
                else
                {
                    // This is a new reference to a hull
                    hullDesc.ptr = shape.ptr;
                    hullDesc.shapeKey = shape.shapeKey;
                    hullDesc.referenceCount = 1;
                    if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
                                BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
                    ret = true;

                }
                hullDesc.lastReferenced = System.DateTime.Now;
                Hulls[shape.shapeKey] = hullDesc;
                break;
            case BSPhysicsShapeType.SHAPE_UNKNOWN:
                break;
            default:
                // Native shapes are not tracked and they don't go into any list
                break;
        }
        return ret;
    }