<commit_before>/*
 * 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 OpenSim.Framework;
using OpenSim.Region.Physics.ConvexDecompositionDotNet;
using OpenSim.Region.Physics.Manager;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using OMV = OpenMetaverse;

namespace OpenSim.Region.Physics.BulletSPlugin
{
public abstract class BSShape
{
    private static string LogHeader = "[BULLETSIM SHAPE]";

    public int referenceCount { get; set; }
    public DateTime lastReferenced { get; set; }
    public BulletShape physShapeInfo { get; set; }

    public BSShape()
    {
        referenceCount = 1;
        lastReferenced = DateTime.Now;
        physShapeInfo = new BulletShape();
    }
    public BSShape(BulletShape pShape)
    {
        referenceCount = 1;
        lastReferenced = DateTime.Now;
        physShapeInfo = pShape;
    }

    // Get another reference to this shape.
    public abstract BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim);

    // Called when this shape is being used again.
    // Used internally. External callers should call instance.GetReference() to properly copy/reference
    //       the shape.
    protected virtual void IncrementReference()
    {
        referenceCount++;
        lastReferenced = DateTime.Now;
    }

    // Called when this shape is done being used.
    protected virtual void DecrementReference()
    {
        referenceCount--;
        lastReferenced = DateTime.Now;
    }

    // Release the use of a physical shape.
    public abstract void Dereference(BSScene physicsScene);

    // Return 'true' if there is an allocated physics physical shape under this class instance.
    public virtual bool HasPhysicalShape
    {
        get
        {
            if (physShapeInfo != null)
                return physShapeInfo.HasPhysicalShape;
            return false;
        }
    }
    public virtual BSPhysicsShapeType ShapeType
    {
        get
        {
            BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN;
            if (physShapeInfo != null && physShapeInfo.HasPhysicalShape)
                ret = physShapeInfo.shapeType;
            return ret;
        }
    }

    // Returns a string for debugging that uniquily identifies the memory used by this instance
    public virtual string AddrString
    {
        get
        {
            if (physShapeInfo != null)
                return physShapeInfo.AddrString;
            return "unknown";
        }
    }

    public override string ToString()
    {
        StringBuilder buff = new StringBuilder();
        if (physShapeInfo == null)
        {
            buff.Append("<noPhys");
        }
        else
        {
            buff.Append("<phy=");
            buff.Append(physShapeInfo.ToString());
        }
        buff.Append(",c=");
        buff.Append(referenceCount.ToString());
        buff.Append(">");
        return buff.ToString();
    }

    #region Common shape routines
    // Create a hash of all the shape parameters to be used as a key for this particular shape.
    public static System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod)
    {
        // level of detail based on size and type of the object
        float lod = BSParam.MeshLOD;
        if (pbs.SculptEntry)
            lod = BSParam.SculptLOD;

        // Mega prims usually get more detail because one can interact with shape approximations at this size.
        float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z));
        if (maxAxis > BSParam.MeshMegaPrimThreshold)
            lod = BSParam.MeshMegaPrimLOD;

        retLod = lod;
        return pbs.GetMeshKey(size, lod);
    }

    // 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.
    public static BulletShape VerifyMeshCreated(BSScene physicsScene, BulletShape newShape, BSPhysObject prim)
    {
        // If the shape was successfully created, nothing more to do
        if (newShape.HasPhysicalShape)
            return newShape;

        // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been
        //    fetched but we end up here again, the meshing of the asset must have failed.
        // Prevent trying to keep fetching the mesh by declaring failure.
        if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched)
        {
            prim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedMeshing;
            physicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. prim={1}, texture={2}",
                                            LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
            physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,setFailed,prim={1},tex={2}",
                                            prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
        }
        else
        {
            // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
            if (prim.BaseShape.SculptEntry
                && !prim.AssetFailed()
                && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting
                && prim.BaseShape.SculptTexture != OMV.UUID.Zero
                )
            {
                physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAsset,objNam={1},tex={2}",
                                            prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture);
                // Multiple requestors will know we're waiting for this asset
                prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting;

                BSPhysObject xprim = prim;
                Util.FireAndForget(delegate
                    {
                        // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,inFireAndForget", xprim.LocalID);
                RequestAssetDelegate assetProvider = physicsScene.RequestAssetMethod;
                if (assetProvider != null)
                {
                    BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
                    assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
                    {
                        // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,assetProviderCallback", xprim.LocalID);
                        bool assetFound = false;
                        string mismatchIDs = String.Empty;  // DEBUG DEBUG
                        if (asset != null && yprim.BaseShape.SculptEntry)
                        {
                            if (yprim.BaseShape.SculptTexture.ToString() == asset.ID)
                            {
                                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.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched;
                                yprim.ForceBodyShapeRebuild(false /* inTaintTime */);
                                assetFound = true;
                            }
                            else
                            {
                                mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID;
                            }
                        }
                        if (!assetFound)
                        {
                            yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedAssetFetch;
                        }
                        physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAssetCallback,found={1},isSculpt={2},ids={3}",
                                    yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs );
                    });
                }
                else
                {
                    xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedAssetFetch;
                    physicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}",
                                                LogHeader, physicsScene.Name);
                }
                    });
            }
            else
            {
                if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedAssetFetch)
                {
                    physicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. prim={1}, texture={2}",
                                                LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
                    physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,wasFailed,prim={1},tex={2}",
                                                prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
                }
                if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedMeshing)
                {
                    physicsScene.Logger.WarnFormat("{0} Mesh asset would not mesh. prim={1}, texture={2}",
                                                LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
                    physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,wasFailedMeshing,prim={1},tex={2}",
                                                prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
                }
            }
         }

        // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object.
        BSShape fillShape = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
        physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,boxTempShape", prim.LocalID);

        return fillShape.physShapeInfo;
     }

    public static String UsefulPrimInfo(BSScene pScene, BSPhysObject prim)
    {
        StringBuilder buff = new StringBuilder(prim.PhysObjectName);
        buff.Append("/pos=");
        buff.Append(prim.RawPosition.ToString());
        if (pScene != null)
        {
            buff.Append("/rgn=");
            buff.Append(pScene.Name);
        }
        return buff.ToString();
    }

    #endregion // Common shape routines
}

// ============================================================================================================
public class BSShapeNull : BSShape
{
    public BSShapeNull() : base()
    {
    }
    public static BSShape GetReference() { return new BSShapeNull();  }
    public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) { return new BSShapeNull();  }
    public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ }
}

// ============================================================================================================
public class BSShapeNative : BSShape
{
    private static string LogHeader = "[BULLETSIM SHAPE NATIVE]";
    public BSShapeNative(BulletShape pShape) : base(pShape)
    {
    }

    public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim,
                                            BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
    {
        // Native shapes are not shared and are always built anew.
        return new BSShapeNative(CreatePhysicalNativeShape(physicsScene, prim, shapeType, shapeKey));
    }

    public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
    {
        // Native shapes are not shared so we return a new shape.
        BSShape ret = null;
        lock (physShapeInfo)
        {
            ret = new BSShapeNative(CreatePhysicalNativeShape(pPhysicsScene, pPrim,
                                        physShapeInfo.shapeType, (FixedShapeKey)physShapeInfo.shapeKey));
        }
        return ret;
    }

    // Make this reference to the physical shape go away since native shapes are not shared.
    public override void Dereference(BSScene physicsScene)
    {
        // Native shapes are not tracked and are released immediately
        lock (physShapeInfo)
        {
            if (physShapeInfo.HasPhysicalShape)
            {
                physicsScene.DetailLog("{0},BSShapeNative.Dereference,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this);
                physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo);
            }
            physShapeInfo.Clear();
            // Garbage collection will free up this instance.
        }
    }

    private static BulletShape CreatePhysicalNativeShape(BSScene physicsScene, BSPhysObject prim,
                                            BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
    {
        BulletShape newShape;

        ShapeData nativeShapeData = new ShapeData();
        nativeShapeData.Type = shapeType;
        nativeShapeData.ID = prim.LocalID;
        nativeShapeData.Scale = prim.Scale;
        nativeShapeData.Size = prim.Scale;
        nativeShapeData.MeshKey = (ulong)shapeKey;
        nativeShapeData.HullKey = (ulong)shapeKey;

        if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
        {
            newShape = physicsScene.PE.BuildCapsuleShape(physicsScene.World, 1f, 1f, prim.Scale);
            physicsScene.DetailLog("{0},BSShapeNative,capsule,scale={1}", prim.LocalID, prim.Scale);
        }
        else
        {
            newShape = physicsScene.PE.BuildNativeShape(physicsScene.World, nativeShapeData);
        }
        if (!newShape.HasPhysicalShape)
        {
            physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
                                    LogHeader, prim.LocalID, shapeType);
        }
        newShape.shapeType = shapeType;
        newShape.isNativeShape = true;
        newShape.shapeKey = (UInt64)shapeKey;
        return newShape;
    }

}

// ============================================================================================================
public class BSShapeMesh : BSShape
{
    private static string LogHeader = "[BULLETSIM SHAPE MESH]";
    private static Dictionary<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>();
    private static ReaderWriterLock MeshesRwLock = new ReaderWriterLock();

    public BSShapeMesh(BulletShape pShape) : base(pShape)
    {
    }
    public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
    {
        float lod;
        System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod);

        BSShapeMesh retMesh = null;
        MeshesRwLock.AcquireWriterLock(-1);
        try
        {
            if (Meshes.TryGetValue(newMeshKey, out retMesh))
            {
                // The mesh has already been created. Return a new reference to same.
                retMesh.IncrementReference();
            }
            else
            {
                retMesh = new BSShapeMesh(new BulletShape());
                // An instance of this mesh has not been created. Build and remember same.
                BulletShape newShape = retMesh.CreatePhysicalMesh(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod);

                // Check to see if mesh was created (might require an asset).
                newShape = VerifyMeshCreated(physicsScene, newShape, prim);
                if (!newShape.isNativeShape
                            || prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedMeshing
                            || prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedAssetFetch)
                {
                    // If a mesh was what was created, remember the built shape for later sharing.
                    // Also note that if meshing failed we put it in the mesh list as there is nothing else to do about the mesh.
                    Meshes.Add(newMeshKey, retMesh);
                }

                retMesh.physShapeInfo = newShape;
            }
        }
        finally
        {
            MeshesRwLock.ReleaseWriterLock();
        }
        physicsScene.DetailLog("{0},BSShapeMesh,getReference,mesh={1},size={2},lod={3}", prim.LocalID, retMesh, prim.Size, lod);
        return retMesh;
    }
    public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
    {
        BSShape ret = null;
        // If the underlying shape is native, the actual shape has not been build (waiting for asset)
        //     and we must create a copy of the native shape since they are never shared.
        if (physShapeInfo.HasPhysicalShape && physShapeInfo.isNativeShape)
        {
            // TODO: decide when the native shapes should be freed. Check in Dereference?<commit_msg>a small correction related to that reference counting stuff<commit_after>/*
 * 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 OpenSim.Framework;
using OpenSim.Region.Physics.ConvexDecompositionDotNet;
using OpenSim.Region.Physics.Manager;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using OMV = OpenMetaverse;

namespace OpenSim.Region.Physics.BulletSPlugin
{
public abstract class BSShape
{
    private static string LogHeader = "[BULLETSIM SHAPE]";

    public int referenceCount;
    public DateTime lastReferenced { get; set; }
    public BulletShape physShapeInfo { get; set; }

    public BSShape()
    {
        referenceCount = 1;
        lastReferenced = DateTime.Now;
        physShapeInfo = new BulletShape();
    }
    public BSShape(BulletShape pShape)
    {
        Interlocked.Increment(ref referenceCount);
        lastReferenced = DateTime.Now;
        physShapeInfo = pShape;
    }

    // Get another reference to this shape.
    public abstract BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim);

    // Called when this shape is being used again.
    // Used internally. External callers should call instance.GetReference() to properly copy/reference
    //       the shape.
    protected virtual void IncrementReference()
    {
        Interlocked.Increment(ref referenceCount);
        lastReferenced = DateTime.Now;
    }

    // Called when this shape is done being used.
    protected virtual void DecrementReference()
    {
        Interlocked.Decrement(ref referenceCount);
        lastReferenced = DateTime.Now;
    }

    // Release the use of a physical shape.
    public abstract void Dereference(BSScene physicsScene);

    // Return 'true' if there is an allocated physics physical shape under this class instance.
    public virtual bool HasPhysicalShape
    {
        get
        {
            if (physShapeInfo != null)
                return physShapeInfo.HasPhysicalShape;
            return false;
        }
    }
    public virtual BSPhysicsShapeType ShapeType
    {
        get
        {
            BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN;
            if (physShapeInfo != null && physShapeInfo.HasPhysicalShape)
                ret = physShapeInfo.shapeType;
            return ret;
        }
    }

    // Returns a string for debugging that uniquily identifies the memory used by this instance
    public virtual string AddrString
    {
        get
        {
            if (physShapeInfo != null)
                return physShapeInfo.AddrString;
            return "unknown";
        }
    }

    public override string ToString()
    {
        StringBuilder buff = new StringBuilder();
        if (physShapeInfo == null)
        {
            buff.Append("<noPhys");
        }
        else
        {
            buff.Append("<phy=");
            buff.Append(physShapeInfo.ToString());
        }
        buff.Append(",c=");
        buff.Append(referenceCount.ToString());
        buff.Append(">");
        return buff.ToString();
    }

    #region Common shape routines
    // Create a hash of all the shape parameters to be used as a key for this particular shape.
    public static System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod)
    {
        // level of detail based on size and type of the object
        float lod = BSParam.MeshLOD;
        if (pbs.SculptEntry)
            lod = BSParam.SculptLOD;

        // Mega prims usually get more detail because one can interact with shape approximations at this size.
        float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z));
        if (maxAxis > BSParam.MeshMegaPrimThreshold)
            lod = BSParam.MeshMegaPrimLOD;

        retLod = lod;
        return pbs.GetMeshKey(size, lod);
    }

    // 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.
    public static BulletShape VerifyMeshCreated(BSScene physicsScene, BulletShape newShape, BSPhysObject prim)
    {
        // If the shape was successfully created, nothing more to do
        if (newShape.HasPhysicalShape)
            return newShape;

        // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been
        //    fetched but we end up here again, the meshing of the asset must have failed.
        // Prevent trying to keep fetching the mesh by declaring failure.
        if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched)
        {
            prim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedMeshing;
            physicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. prim={1}, texture={2}",
                                            LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
            physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,setFailed,prim={1},tex={2}",
                                            prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
        }
        else
        {
            // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
            if (prim.BaseShape.SculptEntry
                && !prim.AssetFailed()
                && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting
                && prim.BaseShape.SculptTexture != OMV.UUID.Zero
                )
            {
                physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAsset,objNam={1},tex={2}",
                                            prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture);
                // Multiple requestors will know we're waiting for this asset
                prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting;

                BSPhysObject xprim = prim;
                Util.FireAndForget(delegate
                    {
                        // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,inFireAndForget", xprim.LocalID);
                RequestAssetDelegate assetProvider = physicsScene.RequestAssetMethod;
                if (assetProvider != null)
                {
                    BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
                    assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
                    {
                        // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,assetProviderCallback", xprim.LocalID);
                        bool assetFound = false;
                        string mismatchIDs = String.Empty;  // DEBUG DEBUG
                        if (asset != null && yprim.BaseShape.SculptEntry)
                        {
                            if (yprim.BaseShape.SculptTexture.ToString() == asset.ID)
                            {
                                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.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched;
                                yprim.ForceBodyShapeRebuild(false /* inTaintTime */);
                                assetFound = true;
                            }
                            else
                            {
                                mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID;
                            }
                        }
                        if (!assetFound)
                        {
                            yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedAssetFetch;
                        }
                        physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAssetCallback,found={1},isSculpt={2},ids={3}",
                                    yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs );
                    });
                }
                else
                {
                    xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedAssetFetch;
                    physicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}",
                                                LogHeader, physicsScene.Name);
                }
                    });
            }
            else
            {
                if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedAssetFetch)
                {
                    physicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. prim={1}, texture={2}",
                                                LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
                    physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,wasFailed,prim={1},tex={2}",
                                                prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
                }
                if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedMeshing)
                {
                    physicsScene.Logger.WarnFormat("{0} Mesh asset would not mesh. prim={1}, texture={2}",
                                                LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
                    physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,wasFailedMeshing,prim={1},tex={2}",
                                                prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
                }
            }
         }

        // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object.
        BSShape fillShape = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
        physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,boxTempShape", prim.LocalID);

        return fillShape.physShapeInfo;
     }

    public static String UsefulPrimInfo(BSScene pScene, BSPhysObject prim)
    {
        StringBuilder buff = new StringBuilder(prim.PhysObjectName);
        buff.Append("/pos=");
        buff.Append(prim.RawPosition.ToString());
        if (pScene != null)
        {
            buff.Append("/rgn=");
            buff.Append(pScene.Name);
        }
        return buff.ToString();
    }

    #endregion // Common shape routines
}

// ============================================================================================================
public class BSShapeNull : BSShape
{
    public BSShapeNull() : base()
    {
    }
    public static BSShape GetReference() { return new BSShapeNull();  }
    public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) { return new BSShapeNull();  }
    public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ }
}

// ============================================================================================================
public class BSShapeNative : BSShape
{
    private static string LogHeader = "[BULLETSIM SHAPE NATIVE]";
    public BSShapeNative(BulletShape pShape) : base(pShape)
    {
    }

    public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim,
                                            BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
    {
        // Native shapes are not shared and are always built anew.
        return new BSShapeNative(CreatePhysicalNativeShape(physicsScene, prim, shapeType, shapeKey));
    }

    public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
    {
        // Native shapes are not shared so we return a new shape.
        BSShape ret = null;
        lock (physShapeInfo)
        {
            ret = new BSShapeNative(CreatePhysicalNativeShape(pPhysicsScene, pPrim,
                                        physShapeInfo.shapeType, (FixedShapeKey)physShapeInfo.shapeKey));
        }
        return ret;
    }

    // Make this reference to the physical shape go away since native shapes are not shared.
    public override void Dereference(BSScene physicsScene)
    {
        // Native shapes are not tracked and are released immediately
        lock (physShapeInfo)
        {
            if (physShapeInfo.HasPhysicalShape)
            {
                physicsScene.DetailLog("{0},BSShapeNative.Dereference,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this);
                physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo);
            }
            physShapeInfo.Clear();
            // Garbage collection will free up this instance.
        }
    }

    private static BulletShape CreatePhysicalNativeShape(BSScene physicsScene, BSPhysObject prim,
                                            BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
    {
        BulletShape newShape;

        ShapeData nativeShapeData = new ShapeData();
        nativeShapeData.Type = shapeType;
        nativeShapeData.ID = prim.LocalID;
        nativeShapeData.Scale = prim.Scale;
        nativeShapeData.Size = prim.Scale;
        nativeShapeData.MeshKey = (ulong)shapeKey;
        nativeShapeData.HullKey = (ulong)shapeKey;

        if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
        {
            newShape = physicsScene.PE.BuildCapsuleShape(physicsScene.World, 1f, 1f, prim.Scale);
            physicsScene.DetailLog("{0},BSShapeNative,capsule,scale={1}", prim.LocalID, prim.Scale);
        }
        else
        {
            newShape = physicsScene.PE.BuildNativeShape(physicsScene.World, nativeShapeData);
        }
        if (!newShape.HasPhysicalShape)
        {
            physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
                                    LogHeader, prim.LocalID, shapeType);
        }
        newShape.shapeType = shapeType;
        newShape.isNativeShape = true;
        newShape.shapeKey = (UInt64)shapeKey;
        return newShape;
    }

}

// ============================================================================================================
public class BSShapeMesh : BSShape
{
    private static string LogHeader = "[BULLETSIM SHAPE MESH]";
    private static Dictionary<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>();
    private static ReaderWriterLock MeshesRwLock = new ReaderWriterLock();

    public BSShapeMesh(BulletShape pShape) : base(pShape)
    {
    }
    public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
    {
        float lod;
        System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod);

        BSShapeMesh retMesh = null;
        MeshesRwLock.AcquireWriterLock(-1);
        try
        {
            if (Meshes.TryGetValue(newMeshKey, out retMesh))
            {
                // The mesh has already been created. Return a new reference to same.
                retMesh.IncrementReference();
            }
            else
            {
                retMesh = new BSShapeMesh(new BulletShape());
                // An instance of this mesh has not been created. Build and remember same.
                BulletShape newShape = retMesh.CreatePhysicalMesh(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod);

                // Check to see if mesh was created (might require an asset).
                newShape = VerifyMeshCreated(physicsScene, newShape, prim);
                        if (!newShape.isNativeShape
                                    || prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedMeshing
                                    || prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedAssetFetch)
                        {
                            // If a mesh was what was created, remember the built shape for later sharing.
                            // Also note that if meshing failed we put it in the mesh list as there is nothing else to do about the mesh.
                            Meshes.Add(newMeshKey, retMesh);
                        }

                        retMesh.physShapeInfo = newShape;
                    }
                }
                finally
                {
            MeshesRwLock.ReleaseWriterLock();
                }
        physicsScene.DetailLog("{0},BSShapeMesh,getReference,mesh={1},size={2},lod={3}", prim.LocalID, retMesh, prim.Size, lod);
        return retMesh;
    }
    public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
    {
        BSShape ret = null;
        // If the underlying shape is native, the actual shape has not been build (waiting for asset)
        //     and we must create a copy of the native shape since they are never shared.
        if (physShapeInfo.HasPhysicalShape && physShapeInfo.isNativeShape)
        {
            // TODO: decide when the native shapes should be freed. Check in Dereference?
{'fraction_non_alphanumeric': 0.09175295471185023, 'fraction_numerical': 0.003268486559983265, 'mean_word_length': 2.0466820680315463, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 60, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7241946', 'n_tokens_mistral': 9864, 'n_tokens_neox': 9268, 'n_words': 2763}