Inheritance: IDisposable
        // TODO: redo terrain implementation selection to allow other base types than heightMap.
        BSTerrainPhys BuildPhysicalTerrain(uint id, float[] heightMap)
        {
            // Find high and low points of passed heightmap.
            // The min and max passed in is usually the area objects can be in (maximum
            //     object height, for instance). The terrain wants the bounding box for the
            //     terrain so replace passed min and max Z with the actual terrain min/max Z.
            float minZ = float.MaxValue;
            float maxZ = float.MinValue;

            foreach (float height in heightMap)
            {
                if (height < minZ)
                {
                    minZ = height;
                }
                if (height > maxZ)
                {
                    maxZ = height;
                }
            }
            if (minZ == maxZ)
            {
                // If min and max are the same, reduce min a little bit so a good bounding box is created.
                minZ -= BSTerrainManager.HEIGHT_EQUAL_FUDGE;
            }
            Vector3 minCoords = new Vector3(0, 0, minZ);
            Vector3 maxCoords = new Vector3(PhysicsScene.Scene.RegionInfo.RegionSizeX,
                                            PhysicsScene.Scene.RegionInfo.RegionSizeY, maxZ);

            PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/ created with {2}",
                                            LogHeader, PhysicsScene.RegionName,
                                            (BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation);
            BSTerrainPhys newTerrainPhys = null;

            switch ((int)BSParam.TerrainImplementation)
            {
            case (int)BSTerrainPhys.TerrainImplementation.Heightmap:
                newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, id,
                                                        heightMap, minCoords, maxCoords);
                break;

            case (int)BSTerrainPhys.TerrainImplementation.Mesh:
                newTerrainPhys = new BSTerrainMesh(PhysicsScene, Vector3.Zero, id,
                                                   heightMap, minCoords, maxCoords);
                break;

            default:
                PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}",
                                                LogHeader,
                                                (int)BSParam.TerrainImplementation,
                                                BSParam.TerrainImplementation,
                                                PhysicsScene.RegionName);
                break;
            }
            return(newTerrainPhys);
        }
        // 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()
        {
            DetailLog("{0},BSTerrainManager.CreateInitialGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero,
                      PhysicsScene.RegionName);
            // The ground plane is here to catch things that are trying to drop to negative infinity
            BulletShape groundPlaneShape = PhysicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f,
                                                                                  BSParam.TerrainCollisionMargin);
            Vector3 groundPlaneAltitude = new Vector3(0f, 0f, BSParam.TerrainGroundPlane);

            m_groundPlane = PhysicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape,
                                                                             BSScene.GROUNDPLANE_ID, groundPlaneAltitude, Quaternion.Identity);

            PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, m_groundPlane);
            PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, m_groundPlane);
            // Ground plane does not move
            PhysicsScene.PE.ForceActivationState(m_groundPlane, ActivationState.DISABLE_SIMULATION);
            // Everything collides with the ground plane.
            m_groundPlane.collisionType = CollisionType.Groundplane;
            m_groundPlane.ApplyCollisionMask(PhysicsScene);

            m_terrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, m_worldMax);
        }
        // If called for terrain has has not been previously allocated, a new terrain will be built
        //     based on the passed information. The 'id' should be either the terrain id or
        //     BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
        //     The latter feature is for creating child terrains for mega-regions.
        // If there is an existing terrain body, a new
        //     terrain shape is created and added to the body.
        //     This call is most often used to update the heightMap and parameters of the terrain.
        // (The above does suggest that some simplification/refactoring is in order.)
        // Called during taint-time.
        void UpdateTerrain(uint id, float[] heightMap)
        {
            DetailLog("{0},BSTerrainManager.UpdateTerrain,call,id={1}",
                      BSScene.DetailLogZero, id);

            if (m_terrain != null)
            {
                // There is already a terrain in this spot. Free the old and build the new.
                DetailLog("{0},BSTErrainManager.UpdateTerrain:UpdateExisting,call,id={1}",
                          BSScene.DetailLogZero, id);

                // Release any physical memory it may be using.
                m_terrain.Dispose();

                m_terrain = BuildPhysicalTerrain(id, heightMap);

                m_terrainModified = true;
            }
            else
            {
                // We don't know about this terrain so either we are creating a new terrain or
                //    our mega-prim child is giving us a new terrain to add to the phys world

                // if this is a child terrain, calculate a unique terrain id
                uint newTerrainID = id;
                if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
                {
                    newTerrainID = ++m_terrainCount;
                }

                DetailLog("{0},BSTerrainManager.UpdateTerrain:NewTerrain,taint,newID={1}",
                          BSScene.DetailLogZero, newTerrainID);
                m_terrain = BuildPhysicalTerrain(id, heightMap);

                m_terrainModified = true;
            }
        }
        // If called for terrain has has not been previously allocated, a new terrain will be built
        //     based on the passed information. The 'id' should be either the terrain id or
        //     BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
        //     The latter feature is for creating child terrains for mega-regions.
        // If there is an existing terrain body, a new
        //     terrain shape is created and added to the body.
        //     This call is most often used to update the heightMap and parameters of the terrain.
        // (The above does suggest that some simplification/refactoring is in order.)
        // Called during taint-time.
        void UpdateTerrain(uint id, float[] heightMap)
        {
            DetailLog("{0},BSTerrainManager.UpdateTerrain,call,id={1}",
                BSScene.DetailLogZero, id);

            if (m_terrain != null)
            {
                // There is already a terrain in this spot. Free the old and build the new.
                DetailLog("{0},BSTErrainManager.UpdateTerrain:UpdateExisting,call,id={1}",
                    BSScene.DetailLogZero, id);

                // Release any physical memory it may be using.
                m_terrain.Dispose();

                m_terrain = BuildPhysicalTerrain(id, heightMap);

                m_terrainModified = true;
            }
            else
            {
                // We don't know about this terrain so either we are creating a new terrain or
                //    our mega-prim child is giving us a new terrain to add to the phys world

                // if this is a child terrain, calculate a unique terrain id
                uint newTerrainID = id;
                if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
                    newTerrainID = ++m_terrainCount;

                DetailLog("{0},BSTerrainManager.UpdateTerrain:NewTerrain,taint,newID={1}",
                    BSScene.DetailLogZero, newTerrainID);
                m_terrain = BuildPhysicalTerrain(id, heightMap);

                m_terrainModified = true;
            }
        }
        // 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()
        {
            DetailLog("{0},BSTerrainManager.CreateInitialGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero,
                PhysicsScene.RegionName);
            // The ground plane is here to catch things that are trying to drop to negative infinity
            BulletShape groundPlaneShape = PhysicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f,
                BSParam.TerrainCollisionMargin);
            Vector3 groundPlaneAltitude = new Vector3(0f, 0f, BSParam.TerrainGroundPlane);
            m_groundPlane = PhysicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape,
                BSScene.GROUNDPLANE_ID, groundPlaneAltitude, Quaternion.Identity);

            PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, m_groundPlane);
            PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, m_groundPlane);
            // Ground plane does not move
            PhysicsScene.PE.ForceActivationState(m_groundPlane, ActivationState.DISABLE_SIMULATION);
            // Everything collides with the ground plane.
            m_groundPlane.collisionType = CollisionType.Groundplane;
            m_groundPlane.ApplyCollisionMask(PhysicsScene);

            m_terrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, m_worldMax);
        }