diff options
Merge branch 'ubitwork' of ssh://3dhosting.de/var/git/careminster into ubitwork
Conflicts:
bin/Regions/Regions.ini.example
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs')
-rwxr-xr-x | OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs | 314 |
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 | // ========================================================================================== |
69 | public sealed class BSTerrainManager | 70 | public 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 | { |