Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

BulletSim: Changes to terrain storage and management so mega-regions …

…work.

Moved all terrain code out of BSScene and into new BSTerrainManager.
Added logic to manage multiple terrains for mega-regions.
Added new functions to BulletSimAPI to match the library.
Moved all of the terrain creation and setup logic from C++ code to C# code.
    The unused code has not yet been removed from either place. Soon.
Moved checks for avatar above ground and in bounds into BSCharacter.
  • Loading branch information...
commit 7c140570db3b01eb83efc0d42a47715d3047e376 1 parent 7b6987c
Robert Adams authored August 25, 2012
37  OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
@@ -226,16 +226,37 @@ private bool PositionSanityCheck()
226 226
         bool ret = false;
227 227
         
228 228
         // If below the ground, move the avatar up
229  
-        float terrainHeight = Scene.GetTerrainHeightAtXYZ(_position);
230  
-        if (_position.Z < terrainHeight)
  229
+        float terrainHeight = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position);
  230
+        if (Position.Z < terrainHeight)
231 231
         {
232  
-            DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},orient={2}", LocalID, _position, _orientation);
233  
-            _position.Z = terrainHeight + 2.0f;
  232
+            DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
  233
+            Vector3 newPos = _position;
  234
+            newPos.Z = terrainHeight + 2.0f;
  235
+            _position = newPos;
234 236
             ret = true;
235 237
         }
236 238
 
237 239
         // TODO: check for out of bounds
  240
+        return ret;
  241
+    }
238 242
 
  243
+    // A version of the sanity check that also makes sure a new position value is
  244
+    //    pushed back to the physics engine. This routine would be used by anyone
  245
+    //    who is not already pushing the value.
  246
+    private bool PositionSanityCheck2()
  247
+    {
  248
+        bool ret = false;
  249
+        if (PositionSanityCheck())
  250
+        {
  251
+            // The new position value must be pushed into the physics engine but we can't
  252
+            //    just assign to "Position" because of potential call loops.
  253
+            _scene.TaintedObject("BSCharacter.PositionSanityCheck", delegate()
  254
+            {
  255
+                DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
  256
+                BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation);
  257
+            });
  258
+            ret = true;
  259
+        }
239 260
         return ret;
240 261
     }
241 262
 
@@ -500,9 +521,13 @@ public override void UpdateProperties(EntityProperties entprop)
500 521
         // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
501 522
         // base.RequestPhysicsterseUpdate();
502 523
 
503  
-        DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
  524
+        // Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
  525
+        PositionSanityCheck2();
  526
+
  527
+        float heightHere = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position);   // just for debug
  528
+        DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5},terrain={6}",
504 529
                 LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, 
505  
-                entprop.Acceleration, entprop.RotationalVelocity);
  530
+                entprop.Acceleration, entprop.RotationalVelocity, heightHere);
506 531
     }
507 532
 
508 533
     // Called by the scene when a collision with this object is reported
3  OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
@@ -48,11 +48,10 @@ public virtual void Dispose()
48 48
     {
49 49
         if (m_enabled)
50 50
         {
51  
-            // BulletSimAPI.RemoveConstraint(m_world.ID, m_body1.ID, m_body2.ID);
  51
+            m_enabled = false;
52 52
             bool success = BulletSimAPI.DestroyConstraint2(m_world.Ptr, m_constraint.Ptr);
53 53
             m_world.scene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success);
54 54
             m_constraint.Ptr = System.IntPtr.Zero;
55  
-            m_enabled = false;
56 55
         }
57 56
     }
58 57
 
9  OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
@@ -465,6 +465,7 @@ internal void ProcessTypeChange(Vehicle pType, float stepSize)
465 465
             }
466 466
         }//end SetDefaultsForType
467 467
 
  468
+        // One step of the vehicle properties for the next 'pTimestep' seconds.
468 469
         internal void Step(float pTimestep)
469 470
         {
470 471
             if (m_type == Vehicle.TYPE_NONE) return;
@@ -592,9 +593,9 @@ private void MoveLinear(float pTimestep)
592 593
             }
593 594
 
594 595
             // If below the terrain, move us above the ground a little.
595  
-            if (pos.Z < m_prim.Scene.GetTerrainHeightAtXYZ(pos))
  596
+            if (pos.Z < m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos))
596 597
             {
597  
-                pos.Z = m_prim.Scene.GetTerrainHeightAtXYZ(pos) + 2;
  598
+                pos.Z = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos) + 2;
598 599
                 m_prim.Position = pos;
599 600
                 VDetailLog("{0},MoveLinear,terrainHeight,pos={1}", m_prim.LocalID, pos);
600 601
             }
@@ -609,7 +610,7 @@ private void MoveLinear(float pTimestep)
609 610
                 }
610 611
                 if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
611 612
                 {
612  
-                    m_VhoverTargetHeight = m_prim.Scene.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight;
  613
+                    m_VhoverTargetHeight = m_prim.Scene.TerrainManager.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight;
613 614
                 }
614 615
                 if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
615 616
                 {
@@ -673,7 +674,7 @@ private void MoveLinear(float pTimestep)
673 674
                 {
674 675
                     grav.Z = (float)(grav.Z * 1.125);
675 676
                 }
676  
-                float terraintemp = m_prim.Scene.GetTerrainHeightAtXYZ(pos);
  677
+                float terraintemp = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos);
677 678
                 float postemp = (pos.Z - terraintemp);
678 679
                 if (postemp > 2.5f)
679 680
                 {
118  OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
... ...
@@ -1,58 +1,60 @@
1  
-/*
2  
- * Copyright (c) Contributors, http://opensimulator.org/
3  
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  
- *
5  
- * Redistribution and use in source and binary forms, with or without
6  
- * modification, are permitted provided that the following conditions are met:
7  
- *     * Redistributions of source code must retain the above copyright
8  
- *       notice, this list of conditions and the following disclaimer.
9  
- *     * Redistributions in binary form must reproduce the above copyrightD
10  
- *       notice, this list of conditions and the following disclaimer in the
11  
- *       documentation and/or other materials provided with the distribution.
12  
- *     * Neither the name of the OpenSimulator Project nor the
13  
- *       names of its contributors may be used to endorse or promote products
14  
- *       derived from this software without specific prior written permission.
15  
- *
16  
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  
- */
27  
-using System;
28  
-using System.Collections.Generic;
29  
-using System.Text;
30  
-
31  
-using OMV = OpenMetaverse;
32  
-using OpenSim.Framework;
33  
-using OpenSim.Region.Physics.Manager;
34  
-
35  
-namespace OpenSim.Region.Physics.BulletSPlugin
36  
-{
37  
-// Class to wrap all objects.
38  
-// The rest of BulletSim doesn't need to keep checking for avatars or prims
39  
-// unless the difference is significant.
40  
-public abstract class BSPhysObject : PhysicsActor
41  
-{
42  
-    public abstract BSLinkset Linkset { get; set; }
43  
-
44  
-    public abstract void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type,
45  
-            OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth);
46  
-    public abstract void SendCollisions();
47  
-
48  
-    // Return the object mass without calculating it or side effects
49  
-    public abstract float MassRaw { get; }
50  
-
51  
-    public abstract BulletBody Body { get; set; }
52  
-    public abstract void ZeroMotion();
53  
-
54  
-    public abstract void UpdateProperties(EntityProperties entprop);
55  
-
56  
-    public abstract void Destroy();
57  
-}
58  
-}
  1
+/*
  2
+ * Copyright (c) Contributors, http://opensimulator.org/
  3
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4
+ *
  5
+ * Redistribution and use in source and binary forms, with or without
  6
+ * modification, are permitted provided that the following conditions are met:
  7
+ *     * Redistributions of source code must retain the above copyright
  8
+ *       notice, this list of conditions and the following disclaimer.
  9
+ *     * Redistributions in binary form must reproduce the above copyrightD
  10
+ *       notice, this list of conditions and the following disclaimer in the
  11
+ *       documentation and/or other materials provided with the distribution.
  12
+ *     * Neither the name of the OpenSimulator Project nor the
  13
+ *       names of its contributors may be used to endorse or promote products
  14
+ *       derived from this software without specific prior written permission.
  15
+ *
  16
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26
+ */
  27
+using System;
  28
+using System.Collections.Generic;
  29
+using System.Text;
  30
+
  31
+using OMV = OpenMetaverse;
  32
+using OpenSim.Framework;
  33
+using OpenSim.Region.Physics.Manager;
  34
+
  35
+namespace OpenSim.Region.Physics.BulletSPlugin
  36
+{
  37
+// Class to wrap all objects.
  38
+// The rest of BulletSim doesn't need to keep checking for avatars or prims
  39
+// unless the difference is significant.
  40
+public abstract class BSPhysObject : PhysicsActor
  41
+{
  42
+    public abstract BSLinkset Linkset { get; set; }
  43
+
  44
+    public abstract void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type,
  45
+            OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth);
  46
+    public abstract void SendCollisions();
  47
+
  48
+    // Return the object mass without calculating it or side effects
  49
+    public abstract float MassRaw { get; }
  50
+
  51
+    public abstract BulletBody Body { get; set; }
  52
+    public abstract void ZeroMotion();
  53
+
  54
+    public virtual void StepVehicle(float timeStep) { }
  55
+
  56
+    public abstract void UpdateProperties(EntityProperties entprop);
  57
+
  58
+    public abstract void Destroy();
  59
+}
  60
+}
2  OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
@@ -377,7 +377,7 @@ public override void VehicleFlags(int param, bool remove)
377 377
 
378 378
     // Called each simulation step to advance vehicle characteristics.
379 379
     // Called from Scene when doing simulation step so we're in taint processing time.
380  
-    public void StepVehicle(float timeStep)
  380
+    public override void StepVehicle(float timeStep)
381 381
     {
382 382
         if (IsPhysical)
383 383
             _vehicle.Step(timeStep);
245  OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -39,8 +39,6 @@
39 39
 using OpenMetaverse;
40 40
 
41 41
 // TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
42  
-// Debug linkset 
43  
-// Test with multiple regions in one simulator 
44 42
 // Adjust character capsule size when height is adjusted (ScenePresence.SetHeight)
45 43
 // Test sculpties
46 44
 // Compute physics FPS reasonably
@@ -54,10 +52,8 @@
54 52
 // Use collision masks for collision with terrain and phantom objects 
55 53
 // Check out llVolumeDetect. Must do something for that.
56 54
 // Should prim.link() and prim.delink() membership checking happen at taint time?
57  
-// changing the position and orientation of a linked prim must rebuild the constraint with the root.
58 55
 // Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once
59 56
 // Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
60  
-// Implement the genCollisions feature in BulletSim::SetObjectProperties (don't pass up unneeded collisions)
61 57
 // Implement LockAngularMotion
62 58
 // Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
63 59
 // Does NeedsMeshing() really need to exclude all the different shapes?
@@ -85,18 +81,15 @@ public class BSScene : PhysicsScene, IPhysicsParameters
85 81
     //    moved to a better place.
86 82
     private HashSet<BSCharacter> m_avatarsWithCollisions = new HashSet<BSCharacter>();
87 83
 
88  
-    private List<BSPrim> m_vehicles = new List<BSPrim>();
89  
-
90  
-    private float[] m_heightMap;
91  
-    private float m_waterLevel;
92  
-    private uint m_worldID;
93  
-    public uint WorldID { get { return m_worldID; } }
  84
+    // List of all the objects that have vehicle properties and should be called
  85
+    //    to update each physics step.
  86
+    private List<BSPhysObject> m_vehicles = new List<BSPhysObject>();
94 87
 
95 88
     // let my minuions use my logger
96 89
     public ILog Logger { get { return m_log; } }
97 90
 
98  
-    private bool m_initialized = false;
99  
-
  91
+    // If non-zero, the number of simulation steps between calls to the physics
  92
+    //    engine to output detailed physics stats. Debug logging level must be on also.
100 93
     private int m_detailedStatsStep = 0;
101 94
 
102 95
     public IMesher mesher;
@@ -106,29 +99,31 @@ public class BSScene : PhysicsScene, IPhysicsParameters
106 99
     public float MeshMegaPrimThreshold { get; private set; }
107 100
     public float SculptLOD { get; private set; }
108 101
 
109  
-    private BulletSim m_worldSim;
110  
-    public BulletSim World
111  
-    {
112  
-        get { return m_worldSim; }
113  
-    }
114  
-    private BSConstraintCollection m_constraintCollection;
115  
-    public BSConstraintCollection Constraints
116  
-    { 
117  
-        get { return m_constraintCollection; }
118  
-    }
  102
+    public uint WorldID { get; private set; }
  103
+    public BulletSim World { get; private set; }
  104
+
  105
+    // All the constraints that have been allocated in this instance.
  106
+    public BSConstraintCollection Constraints { get; private set; }
119 107
 
  108
+    // Simulation parameters
120 109
     private int m_maxSubSteps;
121 110
     private float m_fixedTimeStep;
122 111
     private long m_simulationStep = 0;
123 112
     public long SimulationStep { get { return m_simulationStep; } }
124 113
 
  114
+    // The length of the last timestep we were asked to simulate.
  115
+    // This is used by the vehicle code. Since the vehicle code is called
  116
+    //    once per simulation step, its constants need to be scaled by this.
125 117
     public float LastSimulatedTimestep { get; private set; }
126 118
 
127 119
     // A value of the time now so all the collision and update routines do not have to get their own
128  
-    // Set to 'now' just before all the prims and actors are called for collisions and updates
129  
-    private int m_simulationNowTime;
130  
-    public int SimulationNowTime { get { return m_simulationNowTime; } }
  120
+    // Set to 'now' just before all the prims and actors are called for collisions and updates
  121
+    public int SimulationNowTime { get; private set; }
  122
+
  123
+    // True if initialized and ready to do simulation steps
  124
+    private bool m_initialized = false;
131 125
 
  126
+    // Pinned memory used to pass step information between managed and unmanaged
132 127
     private int m_maxCollisionsPerFrame;
133 128
     private CollisionDesc[] m_collisionArray;
134 129
     private GCHandle m_collisionArrayPinnedHandle;
@@ -145,6 +140,10 @@ public BSConstraintCollection Constraints
145 140
 
146 141
     public const uint TERRAIN_ID = 0;       // OpenSim senses terrain with a localID of zero
147 142
     public const uint GROUNDPLANE_ID = 1;
  143
+    public const uint CHILDTERRAIN_ID = 2;  // Terrain allocated based on our mega-prim childre start here
  144
+
  145
+    private float m_waterLevel;
  146
+    public BSTerrainManager TerrainManager { get; private set; }
148 147
 
149 148
     public ConfigurationParameters Params
150 149
     {
@@ -155,12 +154,12 @@ public Vector3 DefaultGravity
155 154
         get { return new Vector3(0f, 0f, Params.gravity); }
156 155
     }
157 156
 
158  
-    private float m_maximumObjectMass;
159  
-    public float MaximumObjectMass
160  
-    {
161  
-        get { return m_maximumObjectMass; }
162  
-    }
  157
+    public float MaximumObjectMass { get; private set; }
163 158
 
  159
+    // When functions in the unmanaged code must be called, it is only
  160
+    //   done at a known time just before the simulation step. The taint
  161
+    //   system saves all these function calls and executes them in
  162
+    //   order before the simulation.
164 163
     public delegate void TaintCallback();
165 164
     private struct TaintCallbackEntry
166 165
     {
@@ -176,6 +175,7 @@ public TaintCallbackEntry(string i, TaintCallback c)
176 175
     private Object _taintLock = new Object();
177 176
 
178 177
     // A pointer to an instance if this structure is passed to the C++ code
  178
+    // Used to pass basic configuration values to the unmanaged code.
179 179
     ConfigurationParameters[] m_params;
180 180
     GCHandle m_paramsHandle;
181 181
 
@@ -189,11 +189,11 @@ public TaintCallbackEntry(string i, TaintCallback c)
189 189
     private bool m_physicsLoggingEnabled;
190 190
     private string m_physicsLoggingDir;
191 191
     private string m_physicsLoggingPrefix;
192  
-    private int m_physicsLoggingFileMinutes;
193  
-
194  
-    private bool m_vehicleLoggingEnabled;
195  
-    public bool VehicleLoggingEnabled { get { return m_vehicleLoggingEnabled; } }
  192
+    private int m_physicsLoggingFileMinutes;
  193
+    // 'true' of the vehicle code is to log lots of details
  194
+    public bool VehicleLoggingEnabled { get; private set; }
196 195
 
  196
+    #region Construction and Initialization
197 197
     public BSScene(string identifier)
198 198
     {
199 199
         m_initialized = false;
@@ -216,6 +216,9 @@ public override void Initialise(IMesher meshmerizer, IConfigSource config)
216 216
         m_updateArray = new EntityProperties[m_maxUpdatesPerFrame];
217 217
         m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned);
218 218
 
  219
+        mesher = meshmerizer;
  220
+        _taintedObjects = new List<TaintCallbackEntry>();
  221
+
219 222
         // Enable very detailed logging.
220 223
         // By creating an empty logger when not logging, the log message invocation code
221 224
         // can be left in and every call doesn't have to check for null.
@@ -228,11 +231,6 @@ public override void Initialise(IMesher meshmerizer, IConfigSource config)
228 231
             PhysicsLogging = new Logging.LogWriter();
229 232
         }
230 233
 
231  
-        // Get the version of the DLL
232  
-        // TODO: this doesn't work yet. Something wrong with marshaling the returned string.
233  
-        // BulletSimVersion = BulletSimAPI.GetVersion();
234  
-        // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion);
235  
-
236 234
         // If Debug logging level, enable logging from the unmanaged code
237 235
         if (m_log.IsDebugEnabled || PhysicsLogging.Enabled)
238 236
         {
@@ -245,22 +243,32 @@ public override void Initialise(IMesher meshmerizer, IConfigSource config)
245 243
             BulletSimAPI.SetDebugLogCallback(m_DebugLogCallbackHandle);
246 244
         }
247 245
 
248  
-        _taintedObjects = new List<TaintCallbackEntry>();
249  
-
250  
-        mesher = meshmerizer;
  246
+        // Get the version of the DLL
  247
+        // TODO: this doesn't work yet. Something wrong with marshaling the returned string.
  248
+        // BulletSimVersion = BulletSimAPI.GetVersion();
  249
+        // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion);
251 250
 
252  
-        // The bounding box for the simulated world
  251
+        // The bounding box for the simulated world. The origin is 0,0,0 unless we're
  252
+        //    a child in a mega-region.
  253
+        // Turns out that Bullet really doesn't care about the extents of the simulated
  254
+        //    area. It tracks active objects no matter where they are.
253 255
         Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, 8192f);
254 256
 
255 257
         // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
256  
-        m_worldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
  258
+        WorldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
257 259
                                         m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(),
258 260
                                         m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject());
259 261
 
260 262
         // Initialization to support the transition to a new API which puts most of the logic
261 263
         //   into the C# code so it is easier to modify and add to.
262  
-        m_worldSim = new BulletSim(m_worldID, this, BulletSimAPI.GetSimHandle2(m_worldID));
263  
-        m_constraintCollection = new BSConstraintCollection(World);
  264
+        World = new BulletSim(WorldID, this, BulletSimAPI.GetSimHandle2(WorldID));
  265
+
  266
+        Constraints = new BSConstraintCollection(World);
  267
+
  268
+        // Note: choose one of the  two following lines
  269
+        // BulletSimAPI.CreateInitialGroundPlaneAndTerrain(WorldID);
  270
+        TerrainManager = new BSTerrainManager(this);
  271
+        TerrainManager.CreateInitialGroundPlaneAndTerrain();
264 272
 
265 273
         m_initialized = true;
266 274
     }
@@ -288,7 +296,7 @@ private void GetInitialParameterValues(IConfigSource config)
288 296
                 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
289 297
                 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
290 298
                 // Very detailed logging for vehicle debugging
291  
-                m_vehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
  299
+                VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
292 300
 
293 301
                 // Do any replacements in the parameters
294 302
                 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
@@ -323,6 +331,38 @@ private void BulletLoggerPhysLog(string msg)
323 331
         PhysicsLogging.Write("[BULLETS UNMANAGED]:" + msg);
324 332
     }
325 333
 
  334
+    public override void Dispose()
  335
+    {
  336
+        // m_log.DebugFormat("{0}: Dispose()", LogHeader);
  337
+
  338
+        // make sure no stepping happens while we're deleting stuff
  339
+        m_initialized = false;
  340
+
  341
+        TerrainManager.ReleaseGroundPlaneAndTerrain();
  342
+
  343
+        foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
  344
+        {
  345
+            kvp.Value.Destroy();
  346
+        }
  347
+        PhysObjects.Clear();
  348
+
  349
+        // Now that the prims are all cleaned up, there should be no constraints left
  350
+        if (Constraints != null)
  351
+        {
  352
+            Constraints.Dispose();
  353
+            Constraints = null;
  354
+        }
  355
+
  356
+        // Anything left in the unmanaged code should be cleaned out
  357
+        BulletSimAPI.Shutdown(WorldID);
  358
+
  359
+        // Not logging any more
  360
+        PhysicsLogging.Close();
  361
+    }
  362
+    #endregion // Construction and Initialization
  363
+
  364
+    #region Prim and Avatar addition and removal
  365
+
326 366
     public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
327 367
     {
328 368
         m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
@@ -413,6 +453,9 @@ public override void RemovePrim(PhysicsActor prim)
413 453
     // information call is not needed.
414 454
     public override void AddPhysicsActorTaint(PhysicsActor prim) { }
415 455
 
  456
+    #endregion // Prim and Avatar addition and removal
  457
+
  458
+    #region Simulation
416 459
     // Simulate one timestep
417 460
     public override float Simulate(float timeStep)
418 461
     {
@@ -428,7 +471,8 @@ public override float Simulate(float timeStep)
428 471
 
429 472
         int simulateStartTime = Util.EnvironmentTickCount();
430 473
 
431  
-        // update the prim states while we know the physics engine is not busy
  474
+        // update the prim states while we know the physics engine is not busy
  475
+        int numTaints = _taintedObjects.Count;
432 476
         ProcessTaints();
433 477
 
434 478
         // Some of the prims operate with special vehicle properties
@@ -440,14 +484,17 @@ public override float Simulate(float timeStep)
440 484
         int numSubSteps = 0;
441 485
         try
442 486
         {
443  
-            numSubSteps = BulletSimAPI.PhysicsStep(m_worldID, timeStep, m_maxSubSteps, m_fixedTimeStep,
  487
+            numSubSteps = BulletSimAPI.PhysicsStep(WorldID, timeStep, m_maxSubSteps, m_fixedTimeStep,
444 488
                         out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
445  
-            DetailLog("{0},Simulate,call, substeps={1}, updates={2}, colliders={3}", DetailLogZero, numSubSteps, updatedEntityCount, collidersCount); 
  489
+            DetailLog("{0},Simulate,call, nTaints= {1}, substeps={2}, updates={3}, colliders={4}", 
  490
+                        DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount); 
446 491
         }
447 492
         catch (Exception e)
448 493
         {
449  
-            m_log.WarnFormat("{0},PhysicsStep Exception: substeps={1}, updates={2}, colliders={3}, e={4}", LogHeader, numSubSteps, updatedEntityCount, collidersCount, e);
450  
-            // DetailLog("{0},PhysicsStepException,call, substeps={1}, updates={2}, colliders={3}", DetailLogZero, numSubSteps, updatedEntityCount, collidersCount);
  494
+            m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}", 
  495
+                        LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e);
  496
+            DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}", 
  497
+                        DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
451 498
             updatedEntityCount = 0;
452 499
             collidersCount = 0;
453 500
         }
@@ -456,7 +503,7 @@ public override float Simulate(float timeStep)
456 503
         // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in
457 504
 
458 505
         // Get a value for 'now' so all the collision and update routines don't have to get their own
459  
-        m_simulationNowTime = Util.EnvironmentTickCount();
  506
+        SimulationNowTime = Util.EnvironmentTickCount();
460 507
 
461 508
         // If there were collisions, process them by sending the event to the prim.
462 509
         // Collisions must be processed before updates.
@@ -527,7 +574,7 @@ public override float Simulate(float timeStep)
527 574
     // Something has collided
528 575
     private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration)
529 576
     {
530  
-        if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID)
  577
+        if (localID <= TerrainManager.HighestTerrainID)
531 578
         {
532 579
             return;         // don't send collisions to the terrain
533 580
         }
@@ -539,7 +586,7 @@ private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoin
539 586
         BSPhysObject collidee = null;
540 587
 
541 588
         ActorTypes type = ActorTypes.Prim;
542  
-        if (collidingWith == TERRAIN_ID || collidingWith == GROUNDPLANE_ID)
  589
+        if (collidingWith <= TerrainManager.HighestTerrainID)
543 590
         {
544 591
             type = ActorTypes.Ground;
545 592
         }
@@ -558,28 +605,14 @@ private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoin
558 605
         return;
559 606
     }
560 607
 
  608
+    #endregion // Simulation
  609
+
561 610
     public override void GetResults() { }
562 611
 
563  
-    public override void SetTerrain(float[] heightMap) {
564  
-        m_heightMap = heightMap;
565  
-        this.TaintedObject("BSScene.SetTerrain", delegate()
566  
-        {
567  
-            BulletSimAPI.SetHeightmap(m_worldID, m_heightMap);
568  
-        });
569  
-    }
  612
+    #region Terrain
570 613
 
571  
-    // Someday we will have complex terrain with caves and tunnels
572  
-    // For the moment, it's flat and convex
573  
-    public float GetTerrainHeightAtXYZ(Vector3 loc)
574  
-    {
575  
-        return GetTerrainHeightAtXY(loc.X, loc.Y);
576  
-    }
577  
-
578  
-    public float GetTerrainHeightAtXY(float tX, float tY)
579  
-    {
580  
-        if (tX < 0 || tX >= Constants.RegionSize || tY < 0 || tY >= Constants.RegionSize)
581  
-            return 30;
582  
-        return m_heightMap[((int)tX) * Constants.RegionSize + ((int)tY)];
  614
+    public override void SetTerrain(float[] heightMap) {
  615
+        TerrainManager.SetTerrain(heightMap);
583 616
     }
584 617
 
585 618
     public override void SetWaterLevel(float baseheight) 
@@ -595,35 +628,29 @@ public float GetWaterLevel()
595 628
     public override void DeleteTerrain() 
596 629
     {
597 630
         // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
  631
+    }
  632
+
  633
+    // Although no one seems to check this, I do support combining.
  634
+    public override bool SupportsCombining()
  635
+    {
  636
+        return TerrainManager.SupportsCombining();
598 637
     }
599  
-
600  
-    public override void Dispose()
601  
-    {
602  
-        // m_log.DebugFormat("{0}: Dispose()", LogHeader);
603  
-
604  
-        // make sure no stepping happens while we're deleting stuff
605  
-        m_initialized = false;
606  
-
607  
-        foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
608  
-        {
609  
-            kvp.Value.Destroy();
610  
-        }
611  
-        PhysObjects.Clear();
612  
-
613  
-        // Now that the prims are all cleaned up, there should be no constraints left
614  
-        if (m_constraintCollection != null)
615  
-        {
616  
-            m_constraintCollection.Dispose();
617  
-            m_constraintCollection = null;
618  
-        }
619  
-
620  
-        // Anything left in the unmanaged code should be cleaned out
621  
-        BulletSimAPI.Shutdown(WorldID);
622  
-
623  
-        // Not logging any more
624  
-        PhysicsLogging.Close();
  638
+    // This call says I am a child to region zero in a mega-region. 'pScene' is that
  639
+    //    of region zero, 'offset' is my offset from regions zero's origin, and
  640
+    //    'extents' is the largest XY that is handled in my region.
  641
+    public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
  642
+    {
  643
+        TerrainManager.Combine(pScene, offset, extents);
  644
+    }
  645
+
  646
+    // Unhook all the combining that I know about.
  647
+    public override void UnCombine(PhysicsScene pScene)
  648
+    {
  649
+        TerrainManager.UnCombine(pScene);
625 650
     }
626 651
 
  652
+    #endregion // Terrain
  653
+
627 654
     public override Dictionary<uint, float> GetTopColliders()
628 655
     {
629 656
         return new Dictionary<uint, float>();
@@ -833,14 +860,14 @@ public void RemoveVehiclePrim(BSPrim vehicle)
833 860
     // no locking because only called when physics engine is not busy
834 861
     private void ProcessVehicles(float timeStep)
835 862
     {
836  
-        foreach (BSPrim prim in m_vehicles)
  863
+        foreach (BSPhysObject pobj in m_vehicles)
837 864
         {
838  
-            prim.StepVehicle(timeStep);
  865
+            pobj.StepVehicle(timeStep);
839 866
         }
840 867
     }
841 868
     #endregion Vehicles
842 869
 
843  
-    #region Parameters
  870
+    #region INI and command line parameter processing
844 871
 
845 872
     delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
846 873
     delegate float ParamGet(BSScene scene);
@@ -943,9 +970,9 @@ public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, Param
943 970
             (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
944 971
         new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
945 972
             10000.01f,
946  
-            (s,cf,p,v) => { s.m_maximumObjectMass = cf.GetFloat(p, v); },
947  
-            (s) => { return (float)s.m_maximumObjectMass; },
948  
-            (s,p,l,v) => { s.m_maximumObjectMass = v; } ),
  973
+            (s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); },
  974
+            (s) => { return (float)s.MaximumObjectMass; },
  975
+            (s,p,l,v) => { s.MaximumObjectMass = v; } ),
949 976
 
950 977
         new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
951 978
             2200f,
@@ -1207,6 +1234,8 @@ private void SetParameterConfigurationValues(IConfig cfg)
1207 1234
 
1208 1235
     private PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1];
1209 1236
 
  1237
+    // This creates an array in the correct format for returning the list of
  1238
+    //    parameters. This is used by the 'list' option of the 'physics' command.
1210 1239
     private void BuildParameterTable()
1211 1240
     {
1212 1241
         if (SettableParameters.Length < ParameterDefinitions.Length)
@@ -1283,7 +1312,7 @@ protected void UpdateParameterSet(List<uint> lIDs, ref float defaultLoc, string
1283 1312
                 TaintedObject("BSScene.UpdateParameterSet", delegate() {
1284 1313
                     foreach (uint lID in objectIDs)
1285 1314
                     {
1286  
-                        BulletSimAPI.UpdateParameter(m_worldID, lID, xparm, xval);
  1315
+                        BulletSimAPI.UpdateParameter(WorldID, lID, xparm, xval);
1287 1316
                     }
1288 1317
                 });
1289 1318
                 break;
@@ -1301,7 +1330,7 @@ protected void TaintedUpdateParameter(string parm, uint localID, float val)
1301 1330
         string xparm = parm.ToLower();
1302 1331
         float xval = val;
1303 1332
         TaintedObject("BSScene.TaintedUpdateParameter", delegate() {
1304  
-            BulletSimAPI.UpdateParameter(m_worldID, xlocalID, xparm, xval);
  1333
+            BulletSimAPI.UpdateParameter(WorldID, xlocalID, xparm, xval);
1305 1334
         });
1306 1335
     }
1307 1336
 
307  OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
... ...
@@ -0,0 +1,307 @@
  1
+/*
  2
+ * Copyright (c) Contributors, http://opensimulator.org/
  3
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4
+ *
  5
+ * Redistribution and use in source and binary forms, with or without
  6
+ * modification, are permitted provided that the following conditions are met:
  7
+ *     * Redistributions of source code must retain the above copyright
  8
+ *       notice, this list of conditions and the following disclaimer.
  9
+ *     * Redistributions in binary form must reproduce the above copyrightD
  10
+ *       notice, this list of conditions and the following disclaimer in the
  11
+ *       documentation and/or other materials provided with the distribution.
  12
+ *     * Neither the name of the OpenSimulator Project nor the
  13
+ *       names of its contributors may be used to endorse or promote products
  14
+ *       derived from this software without specific prior written permission.
  15
+ *
  16
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26
+ */
  27
+using System;
  28
+using System.Collections.Generic;
  29
+using System.Text;
  30
+
  31
+using OpenSim.Framework;
  32
+using OpenSim.Region.Framework;
  33
+using OpenSim.Region.CoreModules;
  34
+using OpenSim.Region.Physics.Manager;
  35
+
  36
+using Nini.Config;
  37
+using log4net;
  38
+
  39
+using OpenMetaverse;
  40
+
  41
+namespace OpenSim.Region.Physics.BulletSPlugin
  42
+{
  43
+public class BSTerrainManager
  44
+{
  45
+    static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
  46
+
  47
+    BSScene m_physicsScene;
  48
+
  49
+    private BulletBody m_groundPlane;
  50
+    
  51
+    // If doing mega-regions, if we're region zero we will be managing multiple
  52
+    //    region terrains since region zero does the physics for the whole mega-region.
  53
+    private Dictionary<Vector2, BulletBody> m_terrains;
  54
+    private Dictionary<Vector2, BulletHeightMapInfo> m_heightMaps;
  55
+
  56
+    // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount.
  57
+    // This is incremented before assigning to new region so it is the last ID allocated.
  58
+    private uint m_terrainCount = BSScene.CHILDTERRAIN_ID - 1;
  59
+    public uint HighestTerrainID { get {return m_terrainCount; } }
  60
+
  61
+    // If doing mega-regions, this holds our offset from region zero of
  62
+    //     the mega-regions. "parentScene" points to the PhysicsScene of region zero.
  63
+    private Vector3 m_worldOffset = Vector3.Zero;
  64
+    public Vector2 WorldExtents = new Vector2((int)Constants.RegionSize, (int)Constants.RegionSize);
  65
+    private PhysicsScene m_parentScene = null;
  66
+
  67
+    public BSTerrainManager(BSScene physicsScene)
  68
+    {
  69
+        m_physicsScene = physicsScene;
  70
+        m_terrains = new Dictionary<Vector2,BulletBody>();
  71
+        m_heightMaps = new Dictionary<Vector2,BulletHeightMapInfo>();
  72
+    }
  73
+
  74
+    // Create the initial instance of terrain and the underlying ground plane.
  75
+    // The objects are allocated in the unmanaged space and the pointers are tracked
  76
+    //    by the managed code.
  77
+    // The terrains and the groundPlane are not added to the list of PhysObjects.
  78
+    // This is called from the initialization routine so we presume it is
  79
+    //    safe to call Bullet in real time. We hope no one is moving around prim yet.
  80
+    public void CreateInitialGroundPlaneAndTerrain()
  81
+    {
  82
+        // The ground plane is here to catch things that are trying to drop to negative infinity
  83
+        m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, 
  84
+                        BulletSimAPI.CreateGroundPlaneBody2(BSScene.GROUNDPLANE_ID, 1f, 0.4f));
  85
+        BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr);
  86
+
  87
+        Vector3 minTerrainCoords = new Vector3(0f, 0f, 24f);
  88
+        Vector3 maxTerrainCoords = new Vector3(Constants.RegionSize, Constants.RegionSize, 25f);
  89
+        int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y;
  90
+        float[] initialMap = new float[totalHeights];
  91
+        for (int ii = 0; ii < totalHeights; ii++)
  92
+        {
  93
+            initialMap[ii] = 25f;
  94
+        }
  95
+        CreateNewTerrainSegment(BSScene.TERRAIN_ID, initialMap, minTerrainCoords, maxTerrainCoords);
  96
+    }
  97
+
  98
+    public void ReleaseGroundPlaneAndTerrain()
  99
+    {
  100
+        if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr))
  101
+        {
  102
+            BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, m_groundPlane.Ptr);
  103
+        }
  104
+        m_groundPlane.Ptr = IntPtr.Zero;
  105
+
  106
+        foreach (KeyValuePair<Vector2, BulletBody> kvp in m_terrains)
  107
+        {
  108
+            if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, kvp.Value.Ptr))
  109
+            {
  110
+                BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, kvp.Value.Ptr);
  111
+                BulletSimAPI.ReleaseHeightmapInfo2(m_heightMaps[kvp.Key].Ptr);
  112
+            }
  113
+        }
  114
+        m_terrains.Clear();
  115
+        m_heightMaps.Clear();
  116
+    }
  117
+
  118
+    // Create a new terrain description. This is used for mega-regions where
  119
+    //    the children of region zero give region zero all of the terrain
  120
+    //    segments since region zero does all the physics for the mega-region.
  121
+    // Call at taint time!!
  122
+    public void CreateNewTerrainSegment(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
  123
+    {
  124
+        // The Z coordinates are recalculated to be the min and max height of the terrain
  125
+        //    itself. The caller may have passed us the real region extent.
  126
+        float minZ = float.MaxValue;
  127
+        float maxZ = float.MinValue;
  128
+        int hSize = heightMap.Length;
  129
+        for (int ii = 0; ii < hSize; ii++)
  130
+        {
  131
+            minZ = heightMap[ii] < minZ ? heightMap[ii] : minZ;
  132
+            maxZ = heightMap[ii] > maxZ ? heightMap[ii] : maxZ;
  133
+        }
  134
+        minCoords.Z = minZ;
  135
+        maxCoords.Z = maxZ;
  136
+        // If the terrain is flat, make a difference so we get a good bounding box
  137
+        if (minZ == maxZ) 
  138
+            minZ -= 0.2f;
  139
+        Vector2 terrainRegionBase = new Vector2(minCoords.X, minCoords.Y);
  140
+
  141
+        // Create the heightmap data structure in the unmanaged space
  142
+        BulletHeightMapInfo mapInfo = new BulletHeightMapInfo(
  143
+                            BulletSimAPI.CreateHeightmap2(minCoords, maxCoords, heightMap), heightMap);
  144
+        mapInfo.terrainRegionBase = terrainRegionBase;
  145
+        mapInfo.maxRegionExtent = maxCoords;
  146
+        mapInfo.minZ = minZ;
  147
+        mapInfo.maxZ = maxZ;
  148
+        mapInfo.sizeX = maxCoords.X - minCoords.X;
  149
+        mapInfo.sizeY = maxCoords.Y - minCoords.Y;
  150
+
  151
+        DetailLog("{0},BSScene.CreateNewTerrainSegment,call,minZ={1},maxZ={2},hMapPtr={3},minC={4},maxC={5}",
  152
+                    BSScene.DetailLogZero, minZ, maxZ, mapInfo.Ptr, minCoords, maxCoords);
  153
+        // Create the terrain body from that heightmap
  154
+        BulletBody terrainBody = new BulletBody(id, BulletSimAPI.CreateTerrainBody2(id, mapInfo.Ptr, 0.01f));
  155
+
  156
+        BulletSimAPI.SetFriction2(terrainBody.Ptr, m_physicsScene.Params.terrainFriction);
  157
+        BulletSimAPI.SetHitFraction2(terrainBody.Ptr, m_physicsScene.Params.terrainHitFraction);
  158
+        BulletSimAPI.SetRestitution2(terrainBody.Ptr, m_physicsScene.Params.terrainRestitution);
  159
+        BulletSimAPI.SetCollisionFlags2(terrainBody.Ptr, CollisionFlags.CF_STATIC_OBJECT);
  160
+        BulletSimAPI.Activate2(terrainBody.Ptr, true);
  161
+
  162
+        // Add the new terrain to the dynamics world
  163
+        BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, terrainBody.Ptr);
  164
+        BulletSimAPI.UpdateSingleAabb2(m_physicsScene.World.Ptr, terrainBody.Ptr);
  165
+
  166
+
  167
+        // Add the created terrain to the management set. If we are doing mega-regions,
  168
+        //    the terrains of our children will be added.
  169
+        m_terrains.Add(terrainRegionBase, terrainBody);
  170
+        m_heightMaps.Add(terrainRegionBase, mapInfo);
  171
+    }
  172
+
  173
+    public void SetTerrain(float[] heightMap) {
  174
+        if (m_worldOffset != Vector3.Zero && m_parentScene != null)
  175
+        {
  176
+            // If doing the mega-prim stuff and we are the child of the zero region,
  177
+            //    the terrain is really added to our parent
  178
+            if (m_parentScene is BSScene)
  179
+            {
  180
+                ((BSScene)m_parentScene).TerrainManager.SetTerrain(heightMap, m_worldOffset);
  181
+            }
  182
+        }
  183
+        else
  184
+        {
  185
+            // if not doing the mega-prim thing, just change the terrain
  186
+            SetTerrain(heightMap, m_worldOffset);
  187
+        }
  188
+    }
  189
+
  190
+    private void SetTerrain(float[] heightMap, Vector3 tOffset)
  191
+    {
  192
+        float minZ = float.MaxValue;
  193
+        float maxZ = float.MinValue;
  194
+
  195
+        // Copy heightMap local and compute some statistics.
  196
+        // Not really sure if we need to do this deep copy but, given
  197
+        //    the magic that happens to make the closure for taint
  198
+        //    below, I don't want there to be any problem with sharing
  199
+        //    locations of there are multiple calls to this routine
  200
+        //    within one tick.
  201
+        int heightMapSize = heightMap.Length;
  202
+        float[] localHeightMap = new float[heightMapSize];
  203
+        for (int ii = 0; ii < heightMapSize; ii++)
  204
+        {
  205
+            float height = heightMap[ii];
  206
+            if (height < minZ) minZ = height;
  207
+            if (height > maxZ) maxZ = height;
  208
+            localHeightMap[ii] = height;
  209
+        }
  210
+
  211
+        Vector2 terrainRegionBase = new Vector2(tOffset.X, tOffset.Y);
  212
+        BulletHeightMapInfo mapInfo;
  213
+        if (m_heightMaps.TryGetValue(terrainRegionBase, out mapInfo))
  214
+        {
  215
+            // If this is terrain we know about, it's easy to update
  216
+            mapInfo.heightMap = localHeightMap;
  217
+            m_physicsScene.TaintedObject("BSScene.SetTerrain:UpdateExisting", delegate()
  218
+            {
  219
+                DetailLog("{0},SetTerrain:UpdateExisting,baseX={1},baseY={2},minZ={3},maxZ={4}", 
  220
+                                    BSScene.DetailLogZero, tOffset.X, tOffset.Y, minZ, maxZ);
  221
+                BulletSimAPI.UpdateHeightMap2(m_physicsScene.World.Ptr, mapInfo.Ptr, mapInfo.heightMap);
  222
+            });
  223
+        }
  224
+        else
  225
+        {
  226
+            // Our mega-prim child is giving us a new terrain to add to the phys world
  227
+            uint newTerrainID = ++m_terrainCount;
  228
+
  229
+            Vector3 minCoords = tOffset;
  230
+            minCoords.Z = minZ;
  231
+            Vector3 maxCoords = new Vector3(tOffset.X + Constants.RegionSize,
  232
+                                    tOffset.Y + Constants.RegionSize,
  233
+                                    maxZ);
  234
+            m_physicsScene.TaintedObject("BSScene.SetTerrain:NewTerrain", delegate()
  235
+            {
  236
+                DetailLog("{0},SetTerrain:NewTerrain,baseX={1},baseY={2}", BSScene.DetailLogZero, tOffset.X, tOffset.Y);
  237
+                CreateNewTerrainSegment(newTerrainID, heightMap, minCoords, maxCoords);
  238
+            });
  239
+        }
  240
+    }
  241
+
  242
+    // Someday we will have complex terrain with caves and tunnels
  243
+    // For the moment, it's flat and convex
  244
+    public float GetTerrainHeightAtXYZ(Vector3 loc)
  245
+    {
  246
+        return GetTerrainHeightAtXY(loc.X, loc.Y);
  247
+    }
  248
+
  249
+    // Given an X and Y, find the height of the terrain.
  250
+    // Since we could be handling multiple terrains for a mega-region,
  251
+    //    the base of the region is calcuated assuming all regions are
  252
+    //    the same size and that is the default.
  253
+    // Once the heightMapInfo is found, we have all the information to
  254
+    //    compute the offset into the array.
  255
+    public float GetTerrainHeightAtXY(float tX, float tY)
  256
+    {
  257
+        float ret = 30f;
  258
+
  259
+        int offsetX = ((int)(tX / (int)Constants.RegionSize)) * (int)Constants.RegionSize;
  260
+        int offsetY = ((int)(tY / (int)Constants.RegionSize)) * (int)Constants.RegionSize;
  261
+        Vector2 terrainBaseXY = new Vector2(offsetX, offsetY);
  262
+
  263
+        BulletHeightMapInfo mapInfo;
  264
+        if (m_heightMaps.TryGetValue(terrainBaseXY, out mapInfo))
  265
+        {
  266
+            float regionX = tX - offsetX;
  267
+            float regionY = tY - offsetY;
  268
+            regionX = regionX > mapInfo.sizeX ? 0 : regionX;
  269
+            regionY = regionY > mapInfo.sizeY ? 0 : regionY;
  270
+            ret = mapInfo.heightMap[(int)(regionX * mapInfo.sizeX + regionY)];
  271
+        }
  272
+        else
  273
+        {
  274
+            m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: x={1}, y={2}",
  275
+                LogHeader, tX, tY);
  276
+        }
  277
+        return ret;
  278
+    }
  279
+
  280
+    // Although no one seems to check this, I do support combining.
  281
+    public bool SupportsCombining()
  282
+    {
  283
+        return true;
  284
+    }
  285
+    // This call says I am a child to region zero in a mega-region. 'pScene' is that
  286
+    //    of region zero, 'offset' is my offset from regions zero's origin, and
  287
+    //    'extents' is the largest XY that is handled in my region.
  288
+    public void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
  289
+    {
  290
+        m_worldOffset = offset;
  291
+        WorldExtents = new Vector2(extents.X, extents.Y);
  292
+        m_parentScene = pScene;
  293
+    }
  294
+
  295
+    // Unhook all the combining that I know about.
  296
+    public void UnCombine(PhysicsScene pScene)
  297
+    {
  298
+        // Just like ODE, for the moment a NOP
  299
+    }
  300
+
  301
+
  302
+    private void DetailLog(string msg, params Object[] args)
  303
+    {
  304
+        m_physicsScene.PhysicsLogging.Write(msg, args);
  305
+    }
  306
+}
  307
+}
61  OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
@@ -33,6 +33,9 @@
33 33
 namespace OpenSim.Region.Physics.BulletSPlugin {
34 34
 
35 35
 // Classes to allow some type checking for the API
  36
+// These hold pointers to allocated objects in the unmanaged space.
  37
+
  38
+// The physics engine controller class created at initialization
36 39
 public struct BulletSim
37 40
 {
38 41
     public BulletSim(uint id, BSScene bss, IntPtr xx) { ID = id; scene = bss;  Ptr = xx; }
@@ -42,6 +45,7 @@ public struct BulletSim
42 45
     public IntPtr Ptr;
43 46
 }
44 47
 
  48
+// An allocated Bullet btRigidBody
45 49
 public struct BulletBody
46 50
 {
47 51
     public BulletBody(uint id, IntPtr xx) { ID = id;  Ptr = xx; }
@@ -49,12 +53,35 @@ public struct BulletBody
49 53
     public uint ID;
50 54
 }
51 55
 
  56
+// An allocated Bullet btConstraint
52 57
 public struct BulletConstraint
53 58
 {
54 59
     public BulletConstraint(IntPtr xx) { Ptr = xx; }
55 60
     public IntPtr Ptr;
56 61
 }
57 62
 
  63
+// An allocated HeightMapThing which hold various heightmap info
  64
+// Made a class rather than a struct so there would be only one
  65
+//      instance of this and C# will pass around pointers rather