// 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.
        private 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);
            m_groundPlane = PhysicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape,
                                        BSScene.GROUNDPLANE_ID, Vector3.Zero, 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);
        }