<commit_before>using log4net;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Physics.Manager;

/*
 * 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.Linq;
using System.Threading;
using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging;

namespace OpenSim.Region.Physics.BulletSPlugin
{
    public sealed class BSScene : PhysicsScene, IPhysicsParameters
    {
        public const uint CHILDTERRAIN_ID = 2;

        // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
        public const string DetailLogZero = "0000000000";

        public const uint GROUNDPLANE_ID = 1;
        public const uint TERRAIN_ID = 0;

        // A number to use for SimulationStep that is probably not any step value
        // Used by the collision code (which remembers the step when a collision happens) to remember not any simulation step.
        public static long NotASimulationStep = -1234;

        public string BulletSimVersion = "?";

        // All the collision processing is protected with this lock object
        public Object CollisionLock = new Object();

        // If the physics engine is running on a separate thread
        public Thread m_physicsThread;

        public IMesher mesher;

        // Keeping track of the objects with collisions so we can report begin and end of a collision
        public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>();

        public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>();
        public HashSet<BSPhysObject> ObjectsWithUpdates = new HashSet<BSPhysObject>();
        public BSAPITemplate PE;

        // Sometimes you just have to log everything.
        public Logging.LogWriter PhysicsLogging;

        public Dictionary<uint, BSPhysObject> PhysObjects;
        public BSShapeCollection Shapes;

        // Properties are updated here
        public Object UpdateLock = new Object();

        internal static readonly string LogHeader = "[BULLETS SCENE]";
        internal static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        internal CollisionDesc[] m_collisionArray;

        internal float m_fixedTimeStep;

        // Pinned memory used to pass step information between managed and unmanaged
        internal int m_maxCollisionsPerFrame;

        internal int m_maxSubSteps;

        internal int m_maxUpdatesPerFrame;

        // Simulation parameters
        //internal float m_physicsStepTime;   // if running independently, the interval simulated by default
        internal float m_simulatedTime;

        internal long m_simulationStep = 0;

        internal EntityProperties[] m_updateArray;

        // A pointer to an instance if this structure is passed to the C++ code
        // Used to pass basic configuration values to the unmanaged code.
        internal ConfigurationParameters[] UnmanagedParams;

        private List<TaintCallbackEntry> _postStepOperations;

        private Dictionary<string, TaintCallbackEntry> _postTaintOperations;

        private Object _taintLock = new Object();

        // lock for using the next object
        private List<TaintCallbackEntry> _taintOperations;

        // Keep track of all the avatars so we can send them a collision event
        //    every tick so OpenSim will update its animation.
        private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>();

        private ReaderWriterLock m_avatarsRwLock = new ReaderWriterLock();

        // True if initialized and ready to do simulation steps
        private bool m_initialized = false;

        private string m_physicsLoggingDir;

        private bool m_physicsLoggingDoFlush;

        private bool m_physicsLoggingEnabled;

        private int m_physicsLoggingFileMinutes;

        private string m_physicsLoggingPrefix;

        private bool m_physicsPhysicalDumpEnabled;

        public delegate void PostStepAction(float timeStep);

        // Physical objects can register for prestep or poststep events
        public delegate void PreStepAction(float timeStep);

        // When functions in the unmanaged code must be called, it is only
        //   done at a known time just before the simulation step. The taint
        //   system saves all these function calls and executes them in
        //   order before the simulation.
        public delegate void TaintCallback();

        public event PostStepAction AfterStep;

        public event PreStepAction BeforeStep;

        // The handle to the underlying managed or unmanaged version of Bullet being used.
        public string BulletEngineName { get; private set; }

        // All the constraints that have been allocated in this instance.
        public BSConstraintCollection Constraints { get; private set; }

        public Vector3 DefaultGravity
        {
            get { return new Vector3(0f, 0f, Params.gravity); }
        }

        // Just the Z value of the gravity
        public float DefaultGravityZ
        {
            get { return Params.gravity; }
        }

        // Flag which is true when processing taints.
        // Not guaranteed to be correct all the time (don't depend on this) but good for debugging.
        public bool InTaintTime { get; private set; }

        public override bool IsThreaded { get { return false; } }

        // let my minuions use my logger
        public ILog Logger { get { return m_log; } }

        public ConfigurationParameters Params
        {
            get { return UnmanagedParams[0]; }
        }

        public int PhysicsMetricDumpFrames { get; set; }

        // The name of the region we're working for.
        public string RegionName { get; private set; }

        public float SimpleWaterLevel { get; set; }

        // A value of the time 'now' so all the collision and update routines do not have to get their own
        // Set to 'now' just before all the prims and actors are called for collisions and updates
        public int SimulationNowTime { get; private set; }

        public long SimulationStep { get { return m_simulationStep; } }

        // OpenSim senses terrain with a localID of zero
        // Terrain allocated based on our mega-prim childre start here
        public BSTerrainManager TerrainManager { get; private set; }

        // 'true' of the vehicle code is to log lots of details
        public bool VehicleLoggingEnabled { get; private set; }

        public bool VehiclePhysicalLoggingEnabled { get; private set; }

        public BulletWorld World { get; private set; }

        public uint WorldID { get; private set; }

        // the time simulated previously. Used for physics framerate calc.

        // The current simulation step.
        internal float LastTimeStep { get; private set; }   // The simulation time from the last invocation of Simulate()

        internal float NominalFrameRate { get; set; }       // Parameterized ideal frame rate that simulation is scaled to

        // Invoke the detailed logger and output something if it's enabled.
        public void DetailLog(string msg, params Object[] args)
        {
            PhysicsLogging.Write(msg, args);
        }

        public override void GetResults()
        {
        }

        public override Dictionary<uint, float> GetTopColliders()
        {
            Dictionary<uint, float> topColliders;

            lock (PhysObjects)
            {
                foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
                {
                    kvp.Value.ComputeCollisionScore();
                }

                List<BSPhysObject> orderedPrims = new List<BSPhysObject>(PhysObjects.Values);
                orderedPrims.OrderByDescending(p => p.CollisionScore);
                topColliders = orderedPrims.Take(25).ToDictionary(p => p.LocalID, p => p.CollisionScore);
            }

            return topColliders;
        }

        private struct TaintCallbackEntry
        {
            public TaintCallback callback;
            public String ident;
            public String originator;

            public TaintCallbackEntry(string pIdent, TaintCallback pCallBack)
            {
                originator = BSScene.DetailLogZero;
                ident = pIdent;
                callback = pCallBack;
            }

            public TaintCallbackEntry(string pOrigin, string pIdent, TaintCallback pCallBack)
            {
                originator = pOrigin;
                ident = pIdent;
                callback = pCallBack;
            }
        }

        #region Construction and Initialization

        public BSScene(string engineType, string identifier)
        {
            m_initialized = false;

            // The name of the region we're working for is passed to us. Keep for identification.
            RegionName = identifier;

            // Set identifying variables in the PhysicsScene interface.
            EngineType = engineType;
            Name = EngineType + "/" + RegionName;
        }

        public override void Dispose()
        {
            // m_log.DebugFormat("{0}: Dispose()", LogHeader);

            // make sure no stepping happens while we're deleting stuff
            m_initialized = false;

            foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
            {
                kvp.Value.Destroy();
            }
            PhysObjects.Clear();

            // Now that the prims are all cleaned up, there should be no constraints left
            if (Constraints != null)
            {
                Constraints.Dispose();
                Constraints = null;
            }

            if (Shapes != null)
            {
                Shapes.Dispose();
                Shapes = null;
            }

            if (TerrainManager != null)
            {
                TerrainManager.ReleaseGroundPlaneAndTerrain();
                TerrainManager.Dispose();
                TerrainManager = null;
            }

            // Anything left in the unmanaged code should be cleaned out
            PE.Shutdown(World);

            // Not logging any more
            PhysicsLogging.Close();
        }

        // Old version of initialization that assumes legacy sized regions (256x256)
        public override void Initialise(IMesher meshmerizer, IConfigSource config)
        {
            m_log.ErrorFormat("{0} WARNING WARNING WARNING! BulletSim initialized without region extent specification. Terrain will be messed up.");
            Vector3 regionExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionSize);
            Initialise(meshmerizer, config, regionExtent);
        }

        public override void Initialise(IMesher meshmerizer, IConfigSource config, Vector3 regionExtent)
        {
            mesher = meshmerizer;
            _taintOperations = new List<TaintCallbackEntry>();
            _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
            _postStepOperations = new List<TaintCallbackEntry>();
            PhysObjects = new Dictionary<uint, BSPhysObject>();
            Shapes = new BSShapeCollection(this);

            m_simulatedTime = 0f;
            LastTimeStep = 0.1f;

            // Allocate pinned memory to pass parameters.
            UnmanagedParams = new ConfigurationParameters[1];

            // Set default values for physics parameters plus any overrides from the ini file
            GetInitialParameterValues(config);

            // Force some parameters to values depending on other configurations
            // Only use heightmap terrain implementation if terrain larger than legacy size
            if ((uint)regionExtent.X > Constants.RegionSize || (uint)regionExtent.Y > Constants.RegionSize)
            {
                m_log.WarnFormat("{0} Forcing terrain implementation to heightmap for large region", LogHeader);
                BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap;
            }

            // Get the connection to the physics engine (could be native or one of many DLLs)
            PE = SelectUnderlyingBulletEngine(BulletEngineName);

            // Enable very detailed logging.
            // By creating an empty logger when not logging, the log message invocation code
            //     can be left in and every call doesn't have to check for null.
            if (m_physicsLoggingEnabled)
            {
                PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes, m_physicsLoggingDoFlush);
                PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output its own error messages.
            }
            else
            {
                PhysicsLogging = new Logging.LogWriter();
            }

            // Allocate memory for returning of the updates and collisions from the physics engine
            m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame];
            m_updateArray = new EntityProperties[m_maxUpdatesPerFrame];

            // The bounding box for the simulated world. The origin is 0,0,0 unless we're
            //    a child in a mega-region.
            // Bullet actually doesn't care about the extents of the simulated
            //    area. It tracks active objects no matter where they are.
            Vector3 worldExtent = regionExtent;

            World = PE.Initialize(worldExtent, Params, m_maxCollisionsPerFrame, ref m_collisionArray, m_maxUpdatesPerFrame, ref m_updateArray);

            Constraints = new BSConstraintCollection(World);

            TerrainManager = new BSTerrainManager(this, worldExtent);
            TerrainManager.CreateInitialGroundPlaneAndTerrain();

            // Put some informational messages into the log file.
            m_log.InfoFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation);

            InTaintTime = false;
            m_initialized = true;

            // If the physics engine runs on its own thread, start same.
            if (BSParam.UseSeparatePhysicsThread)
            {
                // The physics simulation should happen independently of the heartbeat loop
                m_physicsThread = new Thread(BulletSPluginPhysicsThread);
                m_physicsThread.Name = BulletEngineName;
                m_physicsThread.Start();
            }
        }

        // All default parameter values are set here. There should be no values set in the
        // variable definitions.
        private void GetInitialParameterValues(IConfigSource config)
        {
            ConfigurationParameters parms = new ConfigurationParameters();
            UnmanagedParams[0] = parms;

            BSParam.SetParameterDefaultValues(this);

            if (config != null)
            {
                // If there are specifications in the ini file, use those values
                IConfig pConfig = config.Configs["BulletSim"];
                if (pConfig != null)
                {
                    BSParam.SetParameterConfigurationValues(this, pConfig);

                    // There are two Bullet implementations to choose from
                    BulletEngineName = pConfig.GetString("BulletEngine", "BulletUnmanaged");

                    // Very detailed logging for physics debugging
                    // TODO: the boolean values can be moved to the normal parameter processing.
                    m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
                    m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
                    m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
                    m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
                    m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
                    m_physicsPhysicalDumpEnabled = pConfig.GetBoolean("PhysicsPhysicalDumpEnabled", false);
                    // Very detailed logging for vehicle debugging
                    VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
                    VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);

                    // Do any replacements in the parameters
                    m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
                }
                else
                {
                    // Nothing in the configuration INI file so assume unmanaged and other defaults.
                    BulletEngineName = "BulletUnmanaged";
                    m_physicsLoggingEnabled = false;
                    VehicleLoggingEnabled = false;
                }

                // The material characteristics.
                BSMaterials.InitializeFromDefaults(Params);
                if (pConfig != null)
                {
                    // Let the user add new and interesting material property values.
                    BSMaterials.InitializefromParameters(pConfig);
                }
            }
        }

        // A helper function that handles a true/false parameter and returns the proper float number encoding
        private float ParamBoolean(IConfig config, string parmName, float deflt)
        {
            float ret = deflt;
            if (config.Contains(parmName))
            {
                ret = ConfigurationParameters.numericFalse;
                if (config.GetBoolean(parmName, false))
                {
                    ret = ConfigurationParameters.numericTrue;
                }
            }
            return ret;
        }

        // Select the connection to the actual Bullet implementation.
        // The main engine selection is the engineName up to the first hypen.
        // So "Bullet-2.80-OpenCL-Intel" specifies the 'bullet' class here and the whole name
        //     is passed to the engine to do its special selection, etc.
        private BSAPITemplate SelectUnderlyingBulletEngine(string engineName)
        {
            // For the moment, do a simple switch statement.
            // Someday do fancyness with looking up the interfaces in the assembly.
            BSAPITemplate ret = null;

            string selectionName = engineName.ToLower();
            int hyphenIndex = engineName.IndexOf("-");
            if (hyphenIndex > 0)
                selectionName = engineName.ToLower().Substring(0, hyphenIndex - 1);

            switch (selectionName)
            {
                case "bullet":
                case "bulletunmanaged":
                    ret = new BSAPIUnman(engineName, this);
                    break;
            }

            if (ret == null)
            {
                m_log.ErrorFormat("{0) COULD NOT SELECT BULLET ENGINE: '[BulletSim]PhysicsEngine' must be either 'BulletUnmanaged-*' or 'BulletXNA-*'", LogHeader);
            }
            else
            {
                m_log.InfoFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion);
            }

            return ret;
        }

        #endregion Construction and Initialization

        #region Prim and Avatar addition and removal

        public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
        {
            m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
            return null;
        }

        public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying)
        {
            // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);

            if (!m_initialized) return null;
<commit_msg>removed reference to removed XNA variant of Bullet in output<commit_after>using log4net;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Physics.Manager;

/*
 * 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.Linq;
using System.Threading;
using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging;

namespace OpenSim.Region.Physics.BulletSPlugin
{
    public sealed class BSScene : PhysicsScene, IPhysicsParameters
    {
        public const uint CHILDTERRAIN_ID = 2;

        // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
        public const string DetailLogZero = "0000000000";

        public const uint GROUNDPLANE_ID = 1;
        public const uint TERRAIN_ID = 0;

        // A number to use for SimulationStep that is probably not any step value
        // Used by the collision code (which remembers the step when a collision happens) to remember not any simulation step.
        public static long NotASimulationStep = -1234;

        public string BulletSimVersion = "?";

        // All the collision processing is protected with this lock object
        public Object CollisionLock = new Object();

        // If the physics engine is running on a separate thread
        public Thread m_physicsThread;

        public IMesher mesher;

        // Keeping track of the objects with collisions so we can report begin and end of a collision
        public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>();

        public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>();
        public HashSet<BSPhysObject> ObjectsWithUpdates = new HashSet<BSPhysObject>();
        public BSAPITemplate PE;

        // Sometimes you just have to log everything.
        public Logging.LogWriter PhysicsLogging;

        public Dictionary<uint, BSPhysObject> PhysObjects;
        public BSShapeCollection Shapes;

        // Properties are updated here
        public Object UpdateLock = new Object();

        internal static readonly string LogHeader = "[BULLETS SCENE]";
        internal static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        internal CollisionDesc[] m_collisionArray;

        internal float m_fixedTimeStep;

        // Pinned memory used to pass step information between managed and unmanaged
        internal int m_maxCollisionsPerFrame;

        internal int m_maxSubSteps;

        internal int m_maxUpdatesPerFrame;

        // Simulation parameters
        //internal float m_physicsStepTime;   // if running independently, the interval simulated by default
        internal float m_simulatedTime;

        internal long m_simulationStep = 0;

        internal EntityProperties[] m_updateArray;

        // A pointer to an instance if this structure is passed to the C++ code
        // Used to pass basic configuration values to the unmanaged code.
        internal ConfigurationParameters[] UnmanagedParams;

        private List<TaintCallbackEntry> _postStepOperations;

        private Dictionary<string, TaintCallbackEntry> _postTaintOperations;

        private Object _taintLock = new Object();

        // lock for using the next object
        private List<TaintCallbackEntry> _taintOperations;

        // Keep track of all the avatars so we can send them a collision event
        //    every tick so OpenSim will update its animation.
        private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>();

        private ReaderWriterLock m_avatarsRwLock = new ReaderWriterLock();

        // True if initialized and ready to do simulation steps
        private bool m_initialized = false;

        private string m_physicsLoggingDir;

        private bool m_physicsLoggingDoFlush;

        private bool m_physicsLoggingEnabled;

        private int m_physicsLoggingFileMinutes;

        private string m_physicsLoggingPrefix;

        private bool m_physicsPhysicalDumpEnabled;

        public delegate void PostStepAction(float timeStep);

        // Physical objects can register for prestep or poststep events
        public delegate void PreStepAction(float timeStep);

        // When functions in the unmanaged code must be called, it is only
        //   done at a known time just before the simulation step. The taint
        //   system saves all these function calls and executes them in
        //   order before the simulation.
        public delegate void TaintCallback();

        public event PostStepAction AfterStep;

        public event PreStepAction BeforeStep;

        // The handle to the underlying managed or unmanaged version of Bullet being used.
        public string BulletEngineName { get; private set; }

        // All the constraints that have been allocated in this instance.
        public BSConstraintCollection Constraints { get; private set; }

        public Vector3 DefaultGravity
        {
            get { return new Vector3(0f, 0f, Params.gravity); }
        }

        // Just the Z value of the gravity
        public float DefaultGravityZ
        {
            get { return Params.gravity; }
        }

        // Flag which is true when processing taints.
        // Not guaranteed to be correct all the time (don't depend on this) but good for debugging.
        public bool InTaintTime { get; private set; }

        public override bool IsThreaded { get { return false; } }

        // let my minuions use my logger
        public ILog Logger { get { return m_log; } }

        public ConfigurationParameters Params
        {
            get { return UnmanagedParams[0]; }
        }

        public int PhysicsMetricDumpFrames { get; set; }

        // The name of the region we're working for.
        public string RegionName { get; private set; }

        public float SimpleWaterLevel { get; set; }

        // A value of the time 'now' so all the collision and update routines do not have to get their own
        // Set to 'now' just before all the prims and actors are called for collisions and updates
        public int SimulationNowTime { get; private set; }

        public long SimulationStep { get { return m_simulationStep; } }

        // OpenSim senses terrain with a localID of zero
        // Terrain allocated based on our mega-prim childre start here
        public BSTerrainManager TerrainManager { get; private set; }

        // 'true' of the vehicle code is to log lots of details
        public bool VehicleLoggingEnabled { get; private set; }

        public bool VehiclePhysicalLoggingEnabled { get; private set; }

        public BulletWorld World { get; private set; }

        public uint WorldID { get; private set; }

        // the time simulated previously. Used for physics framerate calc.

        // The current simulation step.
        internal float LastTimeStep { get; private set; }   // The simulation time from the last invocation of Simulate()

        internal float NominalFrameRate { get; set; }       // Parameterized ideal frame rate that simulation is scaled to

        // Invoke the detailed logger and output something if it's enabled.
        public void DetailLog(string msg, params Object[] args)
        {
            PhysicsLogging.Write(msg, args);
        }

        public override void GetResults()
        {
        }

        public override Dictionary<uint, float> GetTopColliders()
        {
            Dictionary<uint, float> topColliders;

            lock (PhysObjects)
            {
                foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
                {
                    kvp.Value.ComputeCollisionScore();
                }

                List<BSPhysObject> orderedPrims = new List<BSPhysObject>(PhysObjects.Values);
                orderedPrims.OrderByDescending(p => p.CollisionScore);
                topColliders = orderedPrims.Take(25).ToDictionary(p => p.LocalID, p => p.CollisionScore);
            }

            return topColliders;
        }

        private struct TaintCallbackEntry
        {
            public TaintCallback callback;
            public String ident;
            public String originator;

            public TaintCallbackEntry(string pIdent, TaintCallback pCallBack)
            {
                originator = BSScene.DetailLogZero;
                ident = pIdent;
                callback = pCallBack;
            }

            public TaintCallbackEntry(string pOrigin, string pIdent, TaintCallback pCallBack)
            {
                originator = pOrigin;
                ident = pIdent;
                callback = pCallBack;
            }
        }

        #region Construction and Initialization

        public BSScene(string engineType, string identifier)
        {
            m_initialized = false;

            // The name of the region we're working for is passed to us. Keep for identification.
            RegionName = identifier;

            // Set identifying variables in the PhysicsScene interface.
            EngineType = engineType;
            Name = EngineType + "/" + RegionName;
        }

        public override void Dispose()
        {
            // m_log.DebugFormat("{0}: Dispose()", LogHeader);

            // make sure no stepping happens while we're deleting stuff
            m_initialized = false;

            foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
            {
                kvp.Value.Destroy();
            }
            PhysObjects.Clear();

            // Now that the prims are all cleaned up, there should be no constraints left
            if (Constraints != null)
            {
                Constraints.Dispose();
                Constraints = null;
            }

            if (Shapes != null)
            {
                Shapes.Dispose();
                Shapes = null;
            }

            if (TerrainManager != null)
            {
                TerrainManager.ReleaseGroundPlaneAndTerrain();
                TerrainManager.Dispose();
                TerrainManager = null;
            }

            // Anything left in the unmanaged code should be cleaned out
            PE.Shutdown(World);

            // Not logging any more
            PhysicsLogging.Close();
        }

        // Old version of initialization that assumes legacy sized regions (256x256)
        public override void Initialise(IMesher meshmerizer, IConfigSource config)
        {
            m_log.ErrorFormat("{0} WARNING WARNING WARNING! BulletSim initialized without region extent specification. Terrain will be messed up.");
            Vector3 regionExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionSize);
            Initialise(meshmerizer, config, regionExtent);
        }

        public override void Initialise(IMesher meshmerizer, IConfigSource config, Vector3 regionExtent)
        {
            mesher = meshmerizer;
            _taintOperations = new List<TaintCallbackEntry>();
            _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
            _postStepOperations = new List<TaintCallbackEntry>();
            PhysObjects = new Dictionary<uint, BSPhysObject>();
            Shapes = new BSShapeCollection(this);

            m_simulatedTime = 0f;
            LastTimeStep = 0.1f;

            // Allocate pinned memory to pass parameters.
            UnmanagedParams = new ConfigurationParameters[1];

            // Set default values for physics parameters plus any overrides from the ini file
            GetInitialParameterValues(config);

            // Force some parameters to values depending on other configurations
            // Only use heightmap terrain implementation if terrain larger than legacy size
            if ((uint)regionExtent.X > Constants.RegionSize || (uint)regionExtent.Y > Constants.RegionSize)
            {
                m_log.WarnFormat("{0} Forcing terrain implementation to heightmap for large region", LogHeader);
                BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap;
            }

            // Get the connection to the physics engine (could be native or one of many DLLs)
            PE = SelectUnderlyingBulletEngine(BulletEngineName);

            // Enable very detailed logging.
            // By creating an empty logger when not logging, the log message invocation code
            //     can be left in and every call doesn't have to check for null.
            if (m_physicsLoggingEnabled)
            {
                PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes, m_physicsLoggingDoFlush);
                PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output its own error messages.
            }
            else
            {
                PhysicsLogging = new Logging.LogWriter();
            }

            // Allocate memory for returning of the updates and collisions from the physics engine
            m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame];
            m_updateArray = new EntityProperties[m_maxUpdatesPerFrame];

            // The bounding box for the simulated world. The origin is 0,0,0 unless we're
            //    a child in a mega-region.
            // Bullet actually doesn't care about the extents of the simulated
            //    area. It tracks active objects no matter where they are.
            Vector3 worldExtent = regionExtent;

            World = PE.Initialize(worldExtent, Params, m_maxCollisionsPerFrame, ref m_collisionArray, m_maxUpdatesPerFrame, ref m_updateArray);

            Constraints = new BSConstraintCollection(World);

            TerrainManager = new BSTerrainManager(this, worldExtent);
            TerrainManager.CreateInitialGroundPlaneAndTerrain();

            // Put some informational messages into the log file.
            m_log.InfoFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation);

            InTaintTime = false;
            m_initialized = true;

            // If the physics engine runs on its own thread, start same.
            if (BSParam.UseSeparatePhysicsThread)
            {
                // The physics simulation should happen independently of the heartbeat loop
                m_physicsThread = new Thread(BulletSPluginPhysicsThread);
                m_physicsThread.Name = BulletEngineName;
                m_physicsThread.Start();
            }
        }

        // All default parameter values are set here. There should be no values set in the
        // variable definitions.
        private void GetInitialParameterValues(IConfigSource config)
        {
            ConfigurationParameters parms = new ConfigurationParameters();
            UnmanagedParams[0] = parms;

            BSParam.SetParameterDefaultValues(this);

            if (config != null)
            {
                // If there are specifications in the ini file, use those values
                IConfig pConfig = config.Configs["BulletSim"];
                if (pConfig != null)
                {
                    BSParam.SetParameterConfigurationValues(this, pConfig);

                    // There are two Bullet implementations to choose from
                    BulletEngineName = pConfig.GetString("BulletEngine", "BulletUnmanaged");

                    // Very detailed logging for physics debugging
                    // TODO: the boolean values can be moved to the normal parameter processing.
                    m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
                    m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
                    m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
                    m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
                    m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
                    m_physicsPhysicalDumpEnabled = pConfig.GetBoolean("PhysicsPhysicalDumpEnabled", false);
                    // Very detailed logging for vehicle debugging
                    VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
                    VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);

                    // Do any replacements in the parameters
                    m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
                }
                else
                {
                    // Nothing in the configuration INI file so assume unmanaged and other defaults.
                    BulletEngineName = "BulletUnmanaged";
                    m_physicsLoggingEnabled = false;
                    VehicleLoggingEnabled = false;
                }

                // The material characteristics.
                BSMaterials.InitializeFromDefaults(Params);
                if (pConfig != null)
                {
                    // Let the user add new and interesting material property values.
                    BSMaterials.InitializefromParameters(pConfig);
                }
            }
        }

        // A helper function that handles a true/false parameter and returns the proper float number encoding
        private float ParamBoolean(IConfig config, string parmName, float deflt)
        {
            float ret = deflt;
            if (config.Contains(parmName))
            {
                ret = ConfigurationParameters.numericFalse;
                if (config.GetBoolean(parmName, false))
                {
                    ret = ConfigurationParameters.numericTrue;
                }
            }
            return ret;
        }

        // Select the connection to the actual Bullet implementation.
        // The main engine selection is the engineName up to the first hypen.
        // So "Bullet-2.80-OpenCL-Intel" specifies the 'bullet' class here and the whole name
        //     is passed to the engine to do its special selection, etc.
        private BSAPITemplate SelectUnderlyingBulletEngine(string engineName)
        {
            // For the moment, do a simple switch statement.
            // Someday do fancyness with looking up the interfaces in the assembly.
            BSAPITemplate ret = null;

            string selectionName = engineName.ToLower();
            int hyphenIndex = engineName.IndexOf("-");
            if (hyphenIndex > 0)
                selectionName = engineName.ToLower().Substring(0, hyphenIndex - 1);

            switch (selectionName)
            {
                case "bullet":
                case "bulletunmanaged":
                    ret = new BSAPIUnman(engineName, this);
                    break;
            }

            if (ret == null)
            {
                m_log.ErrorFormat("{0) COULD NOT SELECT BULLET ENGINE: '[BulletSim]PhysicsEngine' must be either 'BulletUnmanaged-*'", LogHeader);
            }
            else
            {
                m_log.InfoFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion);
            }

            return ret;
        }

        #endregion Construction and Initialization

        #region Prim and Avatar addition and removal

        public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
        {
            m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
            return null;
        }

        public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying)
        {
            // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);

            if (!m_initialized) return null;

{'fraction_non_alphanumeric': 0.06249422205787187, 'fraction_numerical': 0.003143200517703615, 'mean_word_length': 2.14477796351479, 'pattern_counts': {'":': 4, '<': 47, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3862623', 'n_tokens_mistral': 10759, 'n_tokens_neox': 10081, 'n_words': 3693}