aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs')
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs339
1 files changed, 137 insertions, 202 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
index 7c34af2..cd623f1 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
@@ -40,6 +40,32 @@ using OpenMetaverse;
40 40
41namespace OpenSim.Region.Physics.BulletSPlugin 41namespace OpenSim.Region.Physics.BulletSPlugin
42{ 42{
43
44// The physical implementation of the terrain is wrapped in this class.
45public abstract class BSTerrainPhys : IDisposable
46{
47 public enum TerrainImplementation
48 {
49 Heightmap = 0,
50 Mesh = 1
51 }
52
53 public BSScene PhysicsScene { get; private set; }
54 // Base of the region in world coordinates. Coordinates inside the region are relative to this.
55 public Vector3 TerrainBase { get; private set; }
56 public uint ID { get; private set; }
57
58 public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id)
59 {
60 PhysicsScene = physicsScene;
61 TerrainBase = regionBase;
62 ID = id;
63 }
64 public abstract void Dispose();
65 public abstract float GetHeightAtXYZ(Vector3 pos);
66}
67
68// ==========================================================================================
43public sealed class BSTerrainManager 69public sealed class BSTerrainManager
44{ 70{
45 static string LogHeader = "[BULLETSIM TERRAIN MANAGER]"; 71 static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
@@ -54,8 +80,6 @@ public sealed class BSTerrainManager
54 // amount to make sure that a bounding box is built for the terrain. 80 // amount to make sure that a bounding box is built for the terrain.
55 public const float HEIGHT_EQUAL_FUDGE = 0.2f; 81 public const float HEIGHT_EQUAL_FUDGE = 0.2f;
56 82
57 public const float TERRAIN_COLLISION_MARGIN = 0.0f;
58
59 // Until the whole simulator is changed to pass us the region size, we rely on constants. 83 // Until the whole simulator is changed to pass us the region size, we rely on constants.
60 public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); 84 public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
61 85
@@ -67,11 +91,10 @@ public sealed class BSTerrainManager
67 91
68 // If doing mega-regions, if we're region zero we will be managing multiple 92 // If doing mega-regions, if we're region zero we will be managing multiple
69 // region terrains since region zero does the physics for the whole mega-region. 93 // region terrains since region zero does the physics for the whole mega-region.
70 private Dictionary<Vector2, BulletHeightMapInfo> m_heightMaps; 94 private Dictionary<Vector3, BSTerrainPhys> m_terrains;
71 95
72 // True of the terrain has been modified. 96 // Flags used to know when to recalculate the height.
73 // Used to force recalculation of terrain height after terrain has been modified 97 private bool m_terrainModified = false;
74 private bool m_terrainModified;
75 98
76 // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount. 99 // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount.
77 // This is incremented before assigning to new region so it is the last ID allocated. 100 // This is incremented before assigning to new region so it is the last ID allocated.
@@ -89,8 +112,7 @@ public sealed class BSTerrainManager
89 public BSTerrainManager(BSScene physicsScene) 112 public BSTerrainManager(BSScene physicsScene)
90 { 113 {
91 PhysicsScene = physicsScene; 114 PhysicsScene = physicsScene;
92 m_heightMaps = new Dictionary<Vector2,BulletHeightMapInfo>(); 115 m_terrains = new Dictionary<Vector3,BSTerrainPhys>();
93 m_terrainModified = false;
94 116
95 // Assume one region of default size 117 // Assume one region of default size
96 m_worldOffset = Vector3.Zero; 118 m_worldOffset = Vector3.Zero;
@@ -99,17 +121,15 @@ public sealed class BSTerrainManager
99 } 121 }
100 122
101 // Create the initial instance of terrain and the underlying ground plane. 123 // Create the initial instance of terrain and the underlying ground plane.
102 // The objects are allocated in the unmanaged space and the pointers are tracked
103 // by the managed code.
104 // The terrains and the groundPlane are not added to the list of PhysObjects.
105 // This is called from the initialization routine so we presume it is 124 // This is called from the initialization routine so we presume it is
106 // safe to call Bullet in real time. We hope no one is moving prims around yet. 125 // safe to call Bullet in real time. We hope no one is moving prims around yet.
107 public void CreateInitialGroundPlaneAndTerrain() 126 public void CreateInitialGroundPlaneAndTerrain()
108 { 127 {
109 // The ground plane is here to catch things that are trying to drop to negative infinity 128 // The ground plane is here to catch things that are trying to drop to negative infinity
110 BulletShape groundPlaneShape = new BulletShape( 129 BulletShape groundPlaneShape = new BulletShape(
111 BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN), 130 BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f,
112 ShapeData.PhysicsShapeType.SHAPE_GROUNDPLANE); 131 PhysicsScene.Params.terrainCollisionMargin),
132 BSPhysicsShapeType.SHAPE_GROUNDPLANE);
113 m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, 133 m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
114 BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID, 134 BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
115 Vector3.Zero, Quaternion.Identity)); 135 Vector3.Zero, Quaternion.Identity));
@@ -121,15 +141,9 @@ public sealed class BSTerrainManager
121 BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr, 141 BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr,
122 (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask); 142 (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask);
123 143
124 Vector3 minTerrainCoords = new Vector3(0f, 0f, HEIGHT_INITIALIZATION - HEIGHT_EQUAL_FUDGE); 144 // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
125 Vector3 maxTerrainCoords = new Vector3(DefaultRegionSize.X, DefaultRegionSize.Y, HEIGHT_INITIALIZATION); 145 BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize);
126 int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y; 146 m_terrains.Add(Vector3.Zero, initialTerrain);
127 float[] initialMap = new float[totalHeights];
128 for (int ii = 0; ii < totalHeights; ii++)
129 {
130 initialMap[ii] = HEIGHT_INITIALIZATION;
131 }
132 UpdateOrCreateTerrain(BSScene.TERRAIN_ID, initialMap, minTerrainCoords, maxTerrainCoords, true);
133 } 147 }
134 148
135 // Release all the terrain structures we might have allocated 149 // Release all the terrain structures we might have allocated
@@ -150,21 +164,22 @@ public sealed class BSTerrainManager
150 // Release all the terrain we have allocated 164 // Release all the terrain we have allocated
151 public void ReleaseTerrain() 165 public void ReleaseTerrain()
152 { 166 {
153 foreach (KeyValuePair<Vector2, BulletHeightMapInfo> kvp in m_heightMaps) 167 lock (m_terrains)
154 { 168 {
155 if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, kvp.Value.terrainBody.ptr)) 169 foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains)
156 { 170 {
157 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, kvp.Value.terrainBody.ptr); 171 kvp.Value.Dispose();
158 BulletSimAPI.ReleaseHeightMapInfo2(kvp.Value.Ptr);
159 } 172 }
173 m_terrains.Clear();
160 } 174 }
161 m_heightMaps.Clear();
162 } 175 }
163 176
164 // The simulator wants to set a new heightmap for the terrain. 177 // The simulator wants to set a new heightmap for the terrain.
165 public void SetTerrain(float[] heightMap) { 178 public void SetTerrain(float[] heightMap) {
166 float[] localHeightMap = heightMap; 179 float[] localHeightMap = heightMap;
167 PhysicsScene.TaintedObject("TerrainManager.SetTerrain", delegate() 180 // If there are multiple requests for changes to the same terrain between ticks,
181 // only do that last one.
182 PhysicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate()
168 { 183 {
169 if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null) 184 if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null)
170 { 185 {
@@ -176,8 +191,9 @@ public sealed class BSTerrainManager
176 { 191 {
177 DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", 192 DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
178 BSScene.DetailLogZero, m_worldOffset, m_worldMax); 193 BSScene.DetailLogZero, m_worldOffset, m_worldMax);
179 ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateOrCreateTerrain(BSScene.CHILDTERRAIN_ID, 194 ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain(
180 localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize, true); 195 BSScene.CHILDTERRAIN_ID, localHeightMap,
196 m_worldOffset, m_worldOffset + DefaultRegionSize, true);
181 } 197 }
182 } 198 }
183 else 199 else
@@ -185,7 +201,7 @@ public sealed class BSTerrainManager
185 // If not doing the mega-prim thing, just change the terrain 201 // If not doing the mega-prim thing, just change the terrain
186 DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero); 202 DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
187 203
188 UpdateOrCreateTerrain(BSScene.TERRAIN_ID, localHeightMap, 204 UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap,
189 m_worldOffset, m_worldOffset + DefaultRegionSize, true); 205 m_worldOffset, m_worldOffset + DefaultRegionSize, true);
190 } 206 }
191 }); 207 });
@@ -195,56 +211,64 @@ public sealed class BSTerrainManager
195 // based on the passed information. The 'id' should be either the terrain id or 211 // based on the passed information. The 'id' should be either the terrain id or
196 // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used. 212 // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
197 // The latter feature is for creating child terrains for mega-regions. 213 // The latter feature is for creating child terrains for mega-regions.
198 // If called with a mapInfo in m_heightMaps but the terrain has no body yet (mapInfo.terrainBody.Ptr == 0)
199 // then a new body and shape is created and the mapInfo is filled.
200 // This call is used for doing the initial terrain creation.
201 // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new 214 // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new
202 // terrain shape is created and added to the body. 215 // terrain shape is created and added to the body.
203 // This call is most often used to update the heightMap and parameters of the terrain. 216 // This call is most often used to update the heightMap and parameters of the terrain.
204 // (The above does suggest that some simplification/refactoring is in order.) 217 // (The above does suggest that some simplification/refactoring is in order.)
205 private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool inTaintTime) 218 // Called during taint-time.
219 private void UpdateTerrain(uint id, float[] heightMap,
220 Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
206 { 221 {
207 DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},inTaintTime={3}", 222 DetailLog("{0},BSTerrainManager.UpdateTerrain,call,minC={1},maxC={2},inTaintTime={3}",
208 BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime); 223 BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime);
209 224
225 // Find high and low points of passed heightmap.
226 // The min and max passed in is usually the area objects can be in (maximum
227 // object height, for instance). The terrain wants the bounding box for the
228 // terrain so replace passed min and max Z with the actual terrain min/max Z.
210 float minZ = float.MaxValue; 229 float minZ = float.MaxValue;
211 float maxZ = float.MinValue; 230 float maxZ = float.MinValue;
212 Vector2 terrainRegionBase = new Vector2(minCoords.X, minCoords.Y); 231 foreach (float height in heightMap)
213
214 int heightMapSize = heightMap.Length;
215 for (int ii = 0; ii < heightMapSize; ii++)
216 { 232 {
217 float height = heightMap[ii];
218 if (height < minZ) minZ = height; 233 if (height < minZ) minZ = height;
219 if (height > maxZ) maxZ = height; 234 if (height > maxZ) maxZ = height;
220 } 235 }
221 236 if (minZ == maxZ)
222 // The shape of the terrain is from its base to its extents. 237 {
238 // If min and max are the same, reduce min a little bit so a good bounding box is created.
239 minZ -= BSTerrainManager.HEIGHT_EQUAL_FUDGE;
240 }
223 minCoords.Z = minZ; 241 minCoords.Z = minZ;
224 maxCoords.Z = maxZ; 242 maxCoords.Z = maxZ;
225 243
226 BulletHeightMapInfo mapInfo; 244 Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f);
227 if (m_heightMaps.TryGetValue(terrainRegionBase, out mapInfo)) 245
246 lock (m_terrains)
228 { 247 {
229 // If this is terrain we know about, it's easy to update 248 BSTerrainPhys terrainPhys;
230 249 if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
231 mapInfo.heightMap = heightMap;
232 mapInfo.minCoords = minCoords;
233 mapInfo.maxCoords = maxCoords;
234 mapInfo.minZ = minZ;
235 mapInfo.maxZ = maxZ;
236 mapInfo.sizeX = maxCoords.X - minCoords.X;
237 mapInfo.sizeY = maxCoords.Y - minCoords.Y;
238 DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,call,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
239 BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);
240
241 PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateOrCreateTerrain:UpdateExisting", delegate()
242 { 250 {
243 if (MegaRegionParentPhysicsScene != null) 251 // There is already a terrain in this spot. Free the old and build the new.
252 DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}",
253 BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
254
255 // Remove old terrain from the collection
256 m_terrains.Remove(terrainRegionBase);
257 // Release any physical memory it may be using.
258 terrainPhys.Dispose();
259
260 if (MegaRegionParentPhysicsScene == null)
261 {
262 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
263 m_terrains.Add(terrainRegionBase, newTerrainPhys);
264
265 m_terrainModified = true;
266 }
267 else
244 { 268 {
245 // It's possible that Combine() was called after this code was queued. 269 // It's possible that Combine() was called after this code was queued.
246 // If we are a child of combined regions, we don't create any terrain for us. 270 // If we are a child of combined regions, we don't create any terrain for us.
247 DetailLog("{0},UpdateOrCreateTerrain:AmACombineChild,taint", BSScene.DetailLogZero); 271 DetailLog("{0},BSTerrainManager.UpdateTerrain:AmACombineChild,taint", BSScene.DetailLogZero);
248 272
249 // Get rid of any terrain that may have been allocated for us. 273 // Get rid of any terrain that may have been allocated for us.
250 ReleaseGroundPlaneAndTerrain(); 274 ReleaseGroundPlaneAndTerrain();
@@ -252,135 +276,56 @@ public sealed class BSTerrainManager
252 // I hate doing this, but just bail 276 // I hate doing this, but just bail
253 return; 277 return;
254 } 278 }
279 }
280 else
281 {
282 // We don't know about this terrain so either we are creating a new terrain or
283 // our mega-prim child is giving us a new terrain to add to the phys world
255 284
256 if (mapInfo.terrainBody.ptr != IntPtr.Zero) 285 // if this is a child terrain, calculate a unique terrain id
257 { 286 uint newTerrainID = id;
258 // Updating an existing terrain. 287 if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
259 DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,taint,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}", 288 newTerrainID = ++m_terrainCount;
260 BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);
261
262 // Remove from the dynamics world because we're going to mangle this object
263 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
264
265 // Get rid of the old terrain
266 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
267 BulletSimAPI.ReleaseHeightMapInfo2(mapInfo.Ptr);
268 mapInfo.Ptr = IntPtr.Zero;
269
270 /*
271 // NOTE: This routine is half here because I can't get the terrain shape replacement
272 // to work. In the short term, the above three lines completely delete the old
273 // terrain and the code below recreates one from scratch.
274 // Hopefully the Bullet community will help me out on this one.
275
276 // First, release the old collision shape (there is only one terrain)
277 BulletSimAPI.DeleteCollisionShape2(m_physicsScene.World.Ptr, mapInfo.terrainShape.Ptr);
278
279 // Fill the existing height map info with the new location and size information
280 BulletSimAPI.FillHeightMapInfo2(m_physicsScene.World.Ptr, mapInfo.Ptr, mapInfo.ID,
281 mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);
282
283 // Create a terrain shape based on the new info
284 mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr));
285
286 // Stuff the shape into the existing terrain body
287 BulletSimAPI.SetBodyShape2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr, mapInfo.terrainShape.Ptr);
288 */
289 }
290 // else
291 {
292 // Creating a new terrain.
293 DetailLog("{0},UpdateOrCreateTerrain:CreateNewTerrain,taint,baseX={1},baseY={2},minZ={3},maxZ={4}",
294 BSScene.DetailLogZero, mapInfo.minCoords.X, mapInfo.minCoords.Y, minZ, maxZ);
295
296 mapInfo.ID = id;
297 mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, mapInfo.ID,
298 mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);
299
300 // Create the terrain shape from the mapInfo
301 mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr),
302 ShapeData.PhysicsShapeType.SHAPE_TERRAIN);
303
304 // The terrain object initial position is at the center of the object
305 Vector3 centerPos;
306 centerPos.X = minCoords.X + (mapInfo.sizeX / 2f);
307 centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f);
308 centerPos.Z = minZ + ((maxZ - minZ) / 2f);
309
310 mapInfo.terrainBody = new BulletBody(mapInfo.ID,
311 BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.ptr,
312 id, centerPos, Quaternion.Identity));
313 }
314
315 // Make sure the entry is in the heightmap table
316 m_heightMaps[terrainRegionBase] = mapInfo;
317
318 // Set current terrain attributes
319 BulletSimAPI.SetFriction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainFriction);
320 BulletSimAPI.SetHitFraction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
321 BulletSimAPI.SetRestitution2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
322 BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
323
324 // Return the new terrain to the world of physical objects
325 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
326
327 // redo its bounding box now that it is in the world
328 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
329
330 BulletSimAPI.SetCollisionFilterMask2(mapInfo.terrainBody.ptr,
331 (uint)CollisionFilterGroups.TerrainFilter,
332 (uint)CollisionFilterGroups.TerrainMask);
333
334 // Make sure the new shape is processed.
335 // BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true);
336 // BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.ISLAND_SLEEPING);
337 BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
338
339 m_terrainModified = true;
340 });
341 }
342 else
343 {
344 // We don't know about this terrain so either we are creating a new terrain or
345 // our mega-prim child is giving us a new terrain to add to the phys world
346
347 // if this is a child terrain, calculate a unique terrain id
348 uint newTerrainID = id;
349 if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
350 newTerrainID = ++m_terrainCount;
351
352 float[] heightMapX = heightMap;
353 Vector3 minCoordsX = minCoords;
354 Vector3 maxCoordsX = maxCoords;
355
356 DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}",
357 BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
358 289
359 // Code that must happen at taint-time 290 DetailLog("{0},UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}",
360 PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateOrCreateTerrain:NewTerrain", delegate() 291 BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
361 { 292 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
362 DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,taint,baseX={1},baseY={2}", BSScene.DetailLogZero, minCoords.X, minCoords.Y); 293 m_terrains.Add(terrainRegionBase, newTerrainPhys);
363 // Create a new mapInfo that will be filled with the new info
364 mapInfo = new BulletHeightMapInfo(id, heightMapX,
365 BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, newTerrainID,
366 minCoordsX, maxCoordsX, heightMapX, TERRAIN_COLLISION_MARGIN));
367 // Put the unfilled heightmap info into the collection of same
368 m_heightMaps.Add(terrainRegionBase, mapInfo);
369 // Build the terrain
370 UpdateOrCreateTerrain(newTerrainID, heightMap, minCoords, maxCoords, true);
371 294
372 m_terrainModified = true; 295 m_terrainModified = true;
373 }); 296 }
374 } 297 }
375 } 298 }
376 299
377 // Someday we will have complex terrain with caves and tunnels 300 // TODO: redo terrain implementation selection to allow other base types than heightMap.
378 public float GetTerrainHeightAtXYZ(Vector3 loc) 301 private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
379 { 302 {
380 // For the moment, it's flat and convex 303 PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}",
381 return GetTerrainHeightAtXY(loc.X, loc.Y); 304 LogHeader, PhysicsScene.RegionName, terrainRegionBase,
305 (BSTerrainPhys.TerrainImplementation)PhysicsScene.Params.terrainImplementation);
306 BSTerrainPhys newTerrainPhys = null;
307 switch ((int)PhysicsScene.Params.terrainImplementation)
308 {
309 case (int)BSTerrainPhys.TerrainImplementation.Heightmap:
310 newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id,
311 heightMap, minCoords, maxCoords);
312 break;
313 case (int)BSTerrainPhys.TerrainImplementation.Mesh:
314 newTerrainPhys = new BSTerrainMesh(PhysicsScene, terrainRegionBase, id,
315 heightMap, minCoords, maxCoords);
316 break;
317 default:
318 PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}",
319 LogHeader,
320 (int)PhysicsScene.Params.terrainImplementation,
321 PhysicsScene.Params.terrainImplementation,
322 PhysicsScene.RegionName, terrainRegionBase);
323 break;
324 }
325 return newTerrainPhys;
382 } 326 }
383 327
328
384 // Given an X and Y, find the height of the terrain. 329 // Given an X and Y, find the height of the terrain.
385 // Since we could be handling multiple terrains for a mega-region, 330 // Since we could be handling multiple terrains for a mega-region,
386 // the base of the region is calcuated assuming all regions are 331 // the base of the region is calcuated assuming all regions are
@@ -390,12 +335,15 @@ public sealed class BSTerrainManager
390 private float lastHeightTX = 999999f; 335 private float lastHeightTX = 999999f;
391 private float lastHeightTY = 999999f; 336 private float lastHeightTY = 999999f;
392 private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT; 337 private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
393 private float GetTerrainHeightAtXY(float tX, float tY) 338 public float GetTerrainHeightAtXYZ(Vector3 loc)
394 { 339 {
340 float tX = loc.X;
341 float tY = loc.Y;
395 // You'd be surprized at the number of times this routine is called 342 // You'd be surprized at the number of times this routine is called
396 // with the same parameters as last time. 343 // with the same parameters as last time.
397 if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY) 344 if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY)
398 return lastHeight; 345 return lastHeight;
346 m_terrainModified = false;
399 347
400 lastHeightTX = tX; 348 lastHeightTX = tX;
401 lastHeightTY = tY; 349 lastHeightTY = tY;
@@ -403,34 +351,21 @@ public sealed class BSTerrainManager
403 351
404 int offsetX = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X; 352 int offsetX = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
405 int offsetY = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y; 353 int offsetY = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
406 Vector2 terrainBaseXY = new Vector2(offsetX, offsetY); 354 Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
407 355
408 BulletHeightMapInfo mapInfo; 356 lock (m_terrains)
409 if (m_heightMaps.TryGetValue(terrainBaseXY, out mapInfo))
410 { 357 {
411 float regionX = tX - offsetX; 358 BSTerrainPhys physTerrain;
412 float regionY = tY - offsetY; 359 if (m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain))
413 int mapIndex = (int)regionY * (int)mapInfo.sizeY + (int)regionX;
414 try
415 { 360 {
416 ret = mapInfo.heightMap[mapIndex]; 361 ret = physTerrain.GetHeightAtXYZ(loc - terrainBaseXYZ);
417 } 362 }
418 catch 363 else
419 { 364 {
420 // Sometimes they give us wonky values of X and Y. Give a warning and return something. 365 PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
421 PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, x={2}, y={3}", 366 LogHeader, PhysicsScene.RegionName, tX, tY);
422 LogHeader, terrainBaseXY, regionX, regionY);
423 ret = HEIGHT_GETHEIGHT_RET;
424 } 367 }
425 // DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXY,bX={1},baseY={2},szX={3},szY={4},regX={5},regY={6},index={7},ht={8}",
426 // BSScene.DetailLogZero, offsetX, offsetY, mapInfo.sizeX, mapInfo.sizeY, regionX, regionY, mapIndex, ret);
427 }
428 else
429 {
430 PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
431 LogHeader, PhysicsScene.RegionName, tX, tY);
432 } 368 }
433 m_terrainModified = false;
434 lastHeight = ret; 369 lastHeight = ret;
435 return ret; 370 return ret;
436 } 371 }
@@ -466,7 +401,7 @@ public sealed class BSTerrainManager
466 // Unhook all the combining that I know about. 401 // Unhook all the combining that I know about.
467 public void UnCombine(PhysicsScene pScene) 402 public void UnCombine(PhysicsScene pScene)
468 { 403 {
469 // Just like ODE, for the moment a NOP 404 // Just like ODE, we don't do anything yet.
470 DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero); 405 DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero);
471 } 406 }
472 407