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.cs314
1 files changed, 231 insertions, 83 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
index 23fcfd3..b2fb835 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
@@ -62,11 +62,12 @@ public abstract class BSTerrainPhys : IDisposable
62 ID = id; 62 ID = id;
63 } 63 }
64 public abstract void Dispose(); 64 public abstract void Dispose();
65 public abstract float GetHeightAtXYZ(Vector3 pos); 65 public abstract float GetTerrainHeightAtXYZ(Vector3 pos);
66 public abstract float GetWaterLevelAtXYZ(Vector3 pos);
66} 67}
67 68
68// ========================================================================================== 69// ==========================================================================================
69public sealed class BSTerrainManager 70public sealed class BSTerrainManager : IDisposable
70{ 71{
71 static string LogHeader = "[BULLETSIM TERRAIN MANAGER]"; 72 static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
72 73
@@ -75,13 +76,12 @@ public sealed class BSTerrainManager
75 public const float HEIGHT_INITIALIZATION = 24.987f; 76 public const float HEIGHT_INITIALIZATION = 24.987f;
76 public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f; 77 public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f;
77 public const float HEIGHT_GETHEIGHT_RET = 24.765f; 78 public const float HEIGHT_GETHEIGHT_RET = 24.765f;
79 public const float WATER_HEIGHT_GETHEIGHT_RET = 19.998f;
78 80
79 // If the min and max height are equal, we reduce the min by this 81 // If the min and max height are equal, we reduce the min by this
80 // amount to make sure that a bounding box is built for the terrain. 82 // amount to make sure that a bounding box is built for the terrain.
81 public const float HEIGHT_EQUAL_FUDGE = 0.2f; 83 public const float HEIGHT_EQUAL_FUDGE = 0.2f;
82 84
83 public const float TERRAIN_COLLISION_MARGIN = 0.0f;
84
85 // Until the whole simulator is changed to pass us the region size, we rely on constants. 85 // Until the whole simulator is changed to pass us the region size, we rely on constants.
86 public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); 86 public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
87 87
@@ -122,41 +122,49 @@ public sealed class BSTerrainManager
122 MegaRegionParentPhysicsScene = null; 122 MegaRegionParentPhysicsScene = null;
123 } 123 }
124 124
125 public void Dispose()
126 {
127 ReleaseGroundPlaneAndTerrain();
128 }
129
125 // Create the initial instance of terrain and the underlying ground plane. 130 // Create the initial instance of terrain and the underlying ground plane.
126 // This is called from the initialization routine so we presume it is 131 // This is called from the initialization routine so we presume it is
127 // safe to call Bullet in real time. We hope no one is moving prims around yet. 132 // safe to call Bullet in real time. We hope no one is moving prims around yet.
128 public void CreateInitialGroundPlaneAndTerrain() 133 public void CreateInitialGroundPlaneAndTerrain()
129 { 134 {
135 DetailLog("{0},BSTerrainManager.CreateInitialGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, PhysicsScene.RegionName);
130 // The ground plane is here to catch things that are trying to drop to negative infinity 136 // The ground plane is here to catch things that are trying to drop to negative infinity
131 BulletShape groundPlaneShape = new BulletShape( 137 BulletShape groundPlaneShape = PhysicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f, BSParam.TerrainCollisionMargin);
132 BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN), 138 m_groundPlane = PhysicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape,
133 BSPhysicsShapeType.SHAPE_GROUNDPLANE); 139 BSScene.GROUNDPLANE_ID, Vector3.Zero, Quaternion.Identity);
134 m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, 140
135 BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID, 141 PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, m_groundPlane);
136 Vector3.Zero, Quaternion.Identity)); 142 PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, m_groundPlane);
137 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr);
138 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_groundPlane.ptr);
139 // Ground plane does not move 143 // Ground plane does not move
140 BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION); 144 PhysicsScene.PE.ForceActivationState(m_groundPlane, ActivationState.DISABLE_SIMULATION);
141 // Everything collides with the ground plane. 145 // Everything collides with the ground plane.
142 BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr, 146 m_groundPlane.collisionType = CollisionType.Groundplane;
143 (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask); 147 m_groundPlane.ApplyCollisionMask(PhysicsScene);
144 148
145 // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
146 BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize); 149 BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize);
147 m_terrains.Add(Vector3.Zero, initialTerrain); 150 lock (m_terrains)
151 {
152 // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
153 m_terrains.Add(Vector3.Zero, initialTerrain);
154 }
148 } 155 }
149 156
150 // Release all the terrain structures we might have allocated 157 // Release all the terrain structures we might have allocated
151 public void ReleaseGroundPlaneAndTerrain() 158 public void ReleaseGroundPlaneAndTerrain()
152 { 159 {
153 if (m_groundPlane.ptr != IntPtr.Zero) 160 DetailLog("{0},BSTerrainManager.ReleaseGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, PhysicsScene.RegionName);
161 if (m_groundPlane.HasPhysicalBody)
154 { 162 {
155 if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr)) 163 if (PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, m_groundPlane))
156 { 164 {
157 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr); 165 PhysicsScene.PE.DestroyObject(PhysicsScene.World, m_groundPlane);
158 } 166 }
159 m_groundPlane.ptr = IntPtr.Zero; 167 m_groundPlane.Clear();
160 } 168 }
161 169
162 ReleaseTerrain(); 170 ReleaseTerrain();
@@ -165,17 +173,22 @@ public sealed class BSTerrainManager
165 // Release all the terrain we have allocated 173 // Release all the terrain we have allocated
166 public void ReleaseTerrain() 174 public void ReleaseTerrain()
167 { 175 {
168 foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains) 176 lock (m_terrains)
169 { 177 {
170 kvp.Value.Dispose(); 178 foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains)
179 {
180 kvp.Value.Dispose();
181 }
182 m_terrains.Clear();
171 } 183 }
172 m_terrains.Clear();
173 } 184 }
174 185
175 // The simulator wants to set a new heightmap for the terrain. 186 // The simulator wants to set a new heightmap for the terrain.
176 public void SetTerrain(float[] heightMap) { 187 public void SetTerrain(float[] heightMap) {
177 float[] localHeightMap = heightMap; 188 float[] localHeightMap = heightMap;
178 PhysicsScene.TaintedObject("TerrainManager.SetTerrain", delegate() 189 // If there are multiple requests for changes to the same terrain between ticks,
190 // only do that last one.
191 PhysicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate()
179 { 192 {
180 if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null) 193 if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null)
181 { 194 {
@@ -185,11 +198,16 @@ public sealed class BSTerrainManager
185 // the terrain is added to our parent 198 // the terrain is added to our parent
186 if (MegaRegionParentPhysicsScene is BSScene) 199 if (MegaRegionParentPhysicsScene is BSScene)
187 { 200 {
188 DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", 201 DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", BSScene.DetailLogZero, m_worldOffset, m_worldMax);
189 BSScene.DetailLogZero, m_worldOffset, m_worldMax); 202 // This looks really odd but this region is passing its terrain to its mega-region root region
190 ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain( 203 // and the creation of the terrain must happen on the root region's taint thread and not
191 BSScene.CHILDTERRAIN_ID, localHeightMap, 204 // my taint thread.
192 m_worldOffset, m_worldOffset + DefaultRegionSize, true); 205 ((BSScene)MegaRegionParentPhysicsScene).PostTaintObject("TerrainManager.SetTerrain.Mega-" + m_worldOffset.ToString(), 0, delegate()
206 {
207 ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain(
208 BSScene.CHILDTERRAIN_ID, localHeightMap,
209 m_worldOffset, m_worldOffset + DefaultRegionSize, true /* inTaintTime */);
210 });
193 } 211 }
194 } 212 }
195 else 213 else
@@ -198,29 +216,30 @@ public sealed class BSTerrainManager
198 DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero); 216 DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
199 217
200 UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap, 218 UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap,
201 m_worldOffset, m_worldOffset + DefaultRegionSize, true); 219 m_worldOffset, m_worldOffset + DefaultRegionSize, true /* inTaintTime */);
202 } 220 }
203 }); 221 });
204 } 222 }
205 223
206 // If called with no mapInfo for the terrain, this will create a new mapInfo and terrain 224 // If called for terrain has has not been previously allocated, a new terrain will be built
207 // based on the passed information. The 'id' should be either the terrain id or 225 // based on the passed information. The 'id' should be either the terrain id or
208 // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used. 226 // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
209 // The latter feature is for creating child terrains for mega-regions. 227 // The latter feature is for creating child terrains for mega-regions.
210 // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new 228 // If there is an existing terrain body, a new
211 // terrain shape is created and added to the body. 229 // terrain shape is created and added to the body.
212 // This call is most often used to update the heightMap and parameters of the terrain. 230 // This call is most often used to update the heightMap and parameters of the terrain.
213 // (The above does suggest that some simplification/refactoring is in order.) 231 // (The above does suggest that some simplification/refactoring is in order.)
232 // Called during taint-time.
214 private void UpdateTerrain(uint id, float[] heightMap, 233 private void UpdateTerrain(uint id, float[] heightMap,
215 Vector3 minCoords, Vector3 maxCoords, bool inTaintTime) 234 Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
216 { 235 {
217 DetailLog("{0},BSTerrainManager.UpdateTerrain,call,minC={1},maxC={2},inTaintTime={3}", 236 DetailLog("{0},BSTerrainManager.UpdateTerrain,call,id={1},minC={2},maxC={3},inTaintTime={4}",
218 BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime); 237 BSScene.DetailLogZero, id, minCoords, maxCoords, inTaintTime);
219 238
220 // Find high and low points of passed heightmap. 239 // Find high and low points of passed heightmap.
221 // The min and max passed in is usually the area objects can be in (maximum 240 // 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 241 // 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. 242 // terrain so replace passed min and max Z with the actual terrain min/max Z.
224 float minZ = float.MaxValue; 243 float minZ = float.MaxValue;
225 float maxZ = float.MinValue; 244 float maxZ = float.MinValue;
226 foreach (float height in heightMap) 245 foreach (float height in heightMap)
@@ -238,15 +257,15 @@ public sealed class BSTerrainManager
238 257
239 Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f); 258 Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f);
240 259
241 BSTerrainPhys terrainPhys; 260 lock (m_terrains)
242 if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
243 { 261 {
244 // There is already a terrain in this spot. Free the old and build the new. 262 BSTerrainPhys terrainPhys;
245 DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}", 263 if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
246 BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
247
248 PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateTerrain:UpdateExisting", delegate()
249 { 264 {
265 // There is already a terrain in this spot. Free the old and build the new.
266 DetailLog("{0},BSTErrainManager.UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}",
267 BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
268
250 // Remove old terrain from the collection 269 // Remove old terrain from the collection
251 m_terrains.Remove(terrainRegionBase); 270 m_terrains.Remove(terrainRegionBase);
252 // Release any physical memory it may be using. 271 // Release any physical memory it may be using.
@@ -254,6 +273,7 @@ public sealed class BSTerrainManager
254 273
255 if (MegaRegionParentPhysicsScene == null) 274 if (MegaRegionParentPhysicsScene == null)
256 { 275 {
276 // This terrain is not part of the mega-region scheme. Create vanilla terrain.
257 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords); 277 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
258 m_terrains.Add(terrainRegionBase, newTerrainPhys); 278 m_terrains.Add(terrainRegionBase, newTerrainPhys);
259 279
@@ -271,35 +291,24 @@ public sealed class BSTerrainManager
271 // I hate doing this, but just bail 291 // I hate doing this, but just bail
272 return; 292 return;
273 } 293 }
274 }); 294 }
275 } 295 else
276 else 296 {
277 { 297 // We don't know about this terrain so either we are creating a new terrain or
278 // We don't know about this terrain so either we are creating a new terrain or 298 // our mega-prim child is giving us a new terrain to add to the phys world
279 // our mega-prim child is giving us a new terrain to add to the phys world
280
281 // if this is a child terrain, calculate a unique terrain id
282 uint newTerrainID = id;
283 if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
284 newTerrainID = ++m_terrainCount;
285
286 float[] heightMapX = heightMap;
287 Vector3 minCoordsX = minCoords;
288 Vector3 maxCoordsX = maxCoords;
289 299
290 DetailLog("{0},UpdateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}", 300 // if this is a child terrain, calculate a unique terrain id
291 BSScene.DetailLogZero, newTerrainID, minCoords, minCoords); 301 uint newTerrainID = id;
302 if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
303 newTerrainID = ++m_terrainCount;
292 304
293 // Code that must happen at taint-time 305 DetailLog("{0},BSTerrainManager.UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}",
294 PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateTerrain:NewTerrain", delegate() 306 BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
295 {
296 DetailLog("{0},UpdateTerrain:NewTerrain,taint,baseX={1},baseY={2}",
297 BSScene.DetailLogZero, minCoordsX.X, minCoordsX.Y);
298 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords); 307 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
299 m_terrains.Add(terrainRegionBase, newTerrainPhys); 308 m_terrains.Add(terrainRegionBase, newTerrainPhys);
300 309
301 m_terrainModified = true; 310 m_terrainModified = true;
302 }); 311 }
303 } 312 }
304 } 313 }
305 314
@@ -308,9 +317,9 @@ public sealed class BSTerrainManager
308 { 317 {
309 PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}", 318 PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}",
310 LogHeader, PhysicsScene.RegionName, terrainRegionBase, 319 LogHeader, PhysicsScene.RegionName, terrainRegionBase,
311 (BSTerrainPhys.TerrainImplementation)PhysicsScene.Params.terrainImplementation); 320 (BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation);
312 BSTerrainPhys newTerrainPhys = null; 321 BSTerrainPhys newTerrainPhys = null;
313 switch ((int)PhysicsScene.Params.terrainImplementation) 322 switch ((int)BSParam.TerrainImplementation)
314 { 323 {
315 case (int)BSTerrainPhys.TerrainImplementation.Heightmap: 324 case (int)BSTerrainPhys.TerrainImplementation.Heightmap:
316 newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id, 325 newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id,
@@ -323,14 +332,68 @@ public sealed class BSTerrainManager
323 default: 332 default:
324 PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}", 333 PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}",
325 LogHeader, 334 LogHeader,
326 (int)PhysicsScene.Params.terrainImplementation, 335 (int)BSParam.TerrainImplementation,
327 PhysicsScene.Params.terrainImplementation, 336 BSParam.TerrainImplementation,
328 PhysicsScene.RegionName, terrainRegionBase); 337 PhysicsScene.RegionName, terrainRegionBase);
329 break; 338 break;
330 } 339 }
331 return newTerrainPhys; 340 return newTerrainPhys;
332 } 341 }
333 342
343 // Return 'true' of this position is somewhere in known physical terrain space
344 public bool IsWithinKnownTerrain(Vector3 pos)
345 {
346 Vector3 terrainBaseXYZ;
347 BSTerrainPhys physTerrain;
348 return GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ);
349 }
350
351 // Return a new position that is over known terrain if the position is outside our terrain.
352 public Vector3 ClampPositionIntoKnownTerrain(Vector3 pPos)
353 {
354 Vector3 ret = pPos;
355
356 // First, base addresses are never negative so correct for that possible problem.
357 if (ret.X < 0f || ret.Y < 0f)
358 {
359 ret.X = Util.Clamp<float>(ret.X, 0f, 1000000f);
360 ret.Y = Util.Clamp<float>(ret.Y, 0f, 1000000f);
361 DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,zeroingNegXorY,oldPos={1},newPos={2}",
362 BSScene.DetailLogZero, pPos, ret);
363 }
364
365 // Can't do this function if we don't know about any terrain.
366 if (m_terrains.Count == 0)
367 return ret;
368
369 int loopPrevention = 10;
370 Vector3 terrainBaseXYZ;
371 BSTerrainPhys physTerrain;
372 while (!GetTerrainPhysicalAtXYZ(ret, out physTerrain, out terrainBaseXYZ))
373 {
374 // The passed position is not within a known terrain area.
375 // NOTE that GetTerrainPhysicalAtXYZ will set 'terrainBaseXYZ' to the base of the unfound region.
376
377 // Must be off the top of a region. Find an adjacent region to move into.
378 Vector3 adjacentTerrainBase = FindAdjacentTerrainBase(terrainBaseXYZ);
379
380 ret.X = Math.Min(ret.X, adjacentTerrainBase.X + (ret.X % DefaultRegionSize.X));
381 ret.Y = Math.Min(ret.Y, adjacentTerrainBase.Y + (ret.X % DefaultRegionSize.Y));
382 DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,findingAdjacentRegion,adjacentRegBase={1},oldPos={2},newPos={3}",
383 BSScene.DetailLogZero, adjacentTerrainBase, pPos, ret);
384
385 if (loopPrevention-- < 0f)
386 {
387 // The 'while' is a little dangerous so this prevents looping forever if the
388 // mapping of the terrains ever gets messed up (like nothing at <0,0>) or
389 // the list of terrains is in transition.
390 DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,suppressingFindAdjacentRegionLoop", BSScene.DetailLogZero);
391 break;
392 }
393 }
394
395 return ret;
396 }
334 397
335 // Given an X and Y, find the height of the terrain. 398 // Given an X and Y, find the height of the terrain.
336 // Since we could be handling multiple terrains for a mega-region, 399 // Since we could be handling multiple terrains for a mega-region,
@@ -341,40 +404,125 @@ public sealed class BSTerrainManager
341 private float lastHeightTX = 999999f; 404 private float lastHeightTX = 999999f;
342 private float lastHeightTY = 999999f; 405 private float lastHeightTY = 999999f;
343 private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT; 406 private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
344 public float GetTerrainHeightAtXYZ(Vector3 loc) 407 public float GetTerrainHeightAtXYZ(Vector3 pos)
345 { 408 {
346 float tX = loc.X; 409 float tX = pos.X;
347 float tY = loc.Y; 410 float tY = pos.Y;
348 // You'd be surprized at the number of times this routine is called 411 // You'd be surprized at the number of times this routine is called
349 // with the same parameters as last time. 412 // with the same parameters as last time.
350 if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY) 413 if (!m_terrainModified && (lastHeightTX == tX) && (lastHeightTY == tY))
351 return lastHeight; 414 return lastHeight;
415 m_terrainModified = false;
352 416
353 lastHeightTX = tX; 417 lastHeightTX = tX;
354 lastHeightTY = tY; 418 lastHeightTY = tY;
355 float ret = HEIGHT_GETHEIGHT_RET; 419 float ret = HEIGHT_GETHEIGHT_RET;
356 420
357 int offsetX = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X; 421 Vector3 terrainBaseXYZ;
358 int offsetY = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
359 Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
360
361 BSTerrainPhys physTerrain; 422 BSTerrainPhys physTerrain;
362 if (m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain)) 423 if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
363 { 424 {
364 ret = physTerrain.GetHeightAtXYZ(loc - terrainBaseXYZ); 425 ret = physTerrain.GetTerrainHeightAtXYZ(pos - terrainBaseXYZ);
365 DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,loc={1},base={2},height={3}",
366 BSScene.DetailLogZero, loc, terrainBaseXYZ, ret);
367 } 426 }
368 else 427 else
369 { 428 {
370 PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", 429 PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
371 LogHeader, PhysicsScene.RegionName, tX, tY); 430 LogHeader, PhysicsScene.RegionName, tX, tY);
431 DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}",
432 BSScene.DetailLogZero, pos, terrainBaseXYZ);
372 } 433 }
373 m_terrainModified = false; 434
374 lastHeight = ret; 435 lastHeight = ret;
375 return ret; 436 return ret;
376 } 437 }
377 438
439 public float GetWaterLevelAtXYZ(Vector3 pos)
440 {
441 float ret = WATER_HEIGHT_GETHEIGHT_RET;
442
443 Vector3 terrainBaseXYZ;
444 BSTerrainPhys physTerrain;
445 if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
446 {
447 ret = physTerrain.GetWaterLevelAtXYZ(pos);
448 }
449 else
450 {
451 PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}",
452 LogHeader, PhysicsScene.RegionName, pos, terrainBaseXYZ, ret);
453 }
454 return ret;
455 }
456
457 // Given an address, return 'true' of there is a description of that terrain and output
458 // the descriptor class and the 'base' fo the addresses therein.
459 private bool GetTerrainPhysicalAtXYZ(Vector3 pos, out BSTerrainPhys outPhysTerrain, out Vector3 outTerrainBase)
460 {
461 bool ret = false;
462
463 Vector3 terrainBaseXYZ = Vector3.Zero;
464 if (pos.X < 0f || pos.Y < 0f)
465 {
466 // We don't handle negative addresses so just make up a base that will not be found.
467 terrainBaseXYZ = new Vector3(-DefaultRegionSize.X, -DefaultRegionSize.Y, 0f);
468 }
469 else
470 {
471 int offsetX = ((int)(pos.X / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
472 int offsetY = ((int)(pos.Y / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
473 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
474 }
475
476 BSTerrainPhys physTerrain = null;
477 lock (m_terrains)
478 {
479 ret = m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain);
480 }
481 outTerrainBase = terrainBaseXYZ;
482 outPhysTerrain = physTerrain;
483 return ret;
484 }
485
486 // Given a terrain base, return a terrain base for a terrain that is closer to <0,0> than
487 // this one. Usually used to return an out of bounds object to a known place.
488 private Vector3 FindAdjacentTerrainBase(Vector3 pTerrainBase)
489 {
490 Vector3 ret = pTerrainBase;
491
492 // Can't do this function if we don't know about any terrain.
493 if (m_terrains.Count == 0)
494 return ret;
495
496 // Just some sanity
497 ret.X = Util.Clamp<float>(ret.X, 0f, 1000000f);
498 ret.Y = Util.Clamp<float>(ret.Y, 0f, 1000000f);
499 ret.Z = 0f;
500
501 lock (m_terrains)
502 {
503 // Once down to the <0,0> region, we have to be done.
504 while (ret.X > 0f || ret.Y > 0f)
505 {
506 if (ret.X > 0f)
507 {
508 ret.X = Math.Max(0f, ret.X - DefaultRegionSize.X);
509 DetailLog("{0},BSTerrainManager.FindAdjacentTerrainBase,reducingX,terrainBase={1}", BSScene.DetailLogZero, ret);
510 if (m_terrains.ContainsKey(ret))
511 break;
512 }
513 if (ret.Y > 0f)
514 {
515 ret.Y = Math.Max(0f, ret.Y - DefaultRegionSize.Y);
516 DetailLog("{0},BSTerrainManager.FindAdjacentTerrainBase,reducingY,terrainBase={1}", BSScene.DetailLogZero, ret);
517 if (m_terrains.ContainsKey(ret))
518 break;
519 }
520 }
521 }
522
523 return ret;
524 }
525
378 // Although no one seems to check this, I do support combining. 526 // Although no one seems to check this, I do support combining.
379 public bool SupportsCombining() 527 public bool SupportsCombining()
380 { 528 {