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