diff options
Diffstat (limited to '')
-rwxr-xr-x | OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs | 139 |
1 files changed, 102 insertions, 37 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index 28c1940..733d9c2 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs | |||
@@ -44,8 +44,22 @@ public class BSTerrainManager | |||
44 | { | 44 | { |
45 | static string LogHeader = "[BULLETSIM TERRAIN MANAGER]"; | 45 | static string LogHeader = "[BULLETSIM TERRAIN MANAGER]"; |
46 | 46 | ||
47 | // These height values are fractional so the odd values will be | ||
48 | // noticable when debugging. | ||
49 | public const float HEIGHT_INITIALIZATION = 24.987f; | ||
50 | public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f; | ||
51 | public const float HEIGHT_GETHEIGHT_RET = 24.765f; | ||
52 | |||
53 | // If the min and max height are equal, we reduce the min by this | ||
54 | // amount to make sure that a bounding box is built for the terrain. | ||
55 | public const float HEIGHT_EQUAL_FUDGE = 0.2f; | ||
56 | |||
57 | public const float TERRAIN_COLLISION_MARGIN = 0.2f; | ||
58 | |||
59 | // The scene that I am part of | ||
47 | BSScene m_physicsScene; | 60 | BSScene m_physicsScene; |
48 | 61 | ||
62 | // The ground plane created to keep thing from falling to infinity. | ||
49 | private BulletBody m_groundPlane; | 63 | private BulletBody m_groundPlane; |
50 | 64 | ||
51 | // If doing mega-regions, if we're region zero we will be managing multiple | 65 | // If doing mega-regions, if we're region zero we will be managing multiple |
@@ -53,6 +67,10 @@ public class BSTerrainManager | |||
53 | private Dictionary<Vector2, BulletBody> m_terrains; | 67 | private Dictionary<Vector2, BulletBody> m_terrains; |
54 | private Dictionary<Vector2, BulletHeightMapInfo> m_heightMaps; | 68 | private Dictionary<Vector2, BulletHeightMapInfo> m_heightMaps; |
55 | 69 | ||
70 | // True of the terrain has been modified. | ||
71 | // Used to force recalculation of terrain height after terrain has been modified | ||
72 | private bool m_terrainModified; | ||
73 | |||
56 | // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount. | 74 | // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount. |
57 | // This is incremented before assigning to new region so it is the last ID allocated. | 75 | // This is incremented before assigning to new region so it is the last ID allocated. |
58 | private uint m_terrainCount = BSScene.CHILDTERRAIN_ID - 1; | 76 | private uint m_terrainCount = BSScene.CHILDTERRAIN_ID - 1; |
@@ -69,6 +87,7 @@ public class BSTerrainManager | |||
69 | m_physicsScene = physicsScene; | 87 | m_physicsScene = physicsScene; |
70 | m_terrains = new Dictionary<Vector2,BulletBody>(); | 88 | m_terrains = new Dictionary<Vector2,BulletBody>(); |
71 | m_heightMaps = new Dictionary<Vector2,BulletHeightMapInfo>(); | 89 | m_heightMaps = new Dictionary<Vector2,BulletHeightMapInfo>(); |
90 | m_terrainModified = false; | ||
72 | } | 91 | } |
73 | 92 | ||
74 | // Create the initial instance of terrain and the underlying ground plane. | 93 | // Create the initial instance of terrain and the underlying ground plane. |
@@ -80,17 +99,18 @@ public class BSTerrainManager | |||
80 | public void CreateInitialGroundPlaneAndTerrain() | 99 | public void CreateInitialGroundPlaneAndTerrain() |
81 | { | 100 | { |
82 | // The ground plane is here to catch things that are trying to drop to negative infinity | 101 | // The ground plane is here to catch things that are trying to drop to negative infinity |
102 | BulletShape groundPlaneShape = new BulletShape(BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN)); | ||
83 | m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, | 103 | m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, |
84 | BulletSimAPI.CreateGroundPlaneBody2(BSScene.GROUNDPLANE_ID, 1f, 0.4f)); | 104 | BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.Ptr, Vector3.Zero, Quaternion.Identity)); |
85 | BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr); | 105 | BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr); |
86 | 106 | ||
87 | Vector3 minTerrainCoords = new Vector3(0f, 0f, 24f); | 107 | Vector3 minTerrainCoords = new Vector3(0f, 0f, HEIGHT_INITIALIZATION - HEIGHT_EQUAL_FUDGE); |
88 | Vector3 maxTerrainCoords = new Vector3(Constants.RegionSize, Constants.RegionSize, 25f); | 108 | Vector3 maxTerrainCoords = new Vector3(Constants.RegionSize, Constants.RegionSize, HEIGHT_INITIALIZATION); |
89 | int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y; | 109 | int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y; |
90 | float[] initialMap = new float[totalHeights]; | 110 | float[] initialMap = new float[totalHeights]; |
91 | for (int ii = 0; ii < totalHeights; ii++) | 111 | for (int ii = 0; ii < totalHeights; ii++) |
92 | { | 112 | { |
93 | initialMap[ii] = 25f; | 113 | initialMap[ii] = HEIGHT_INITIALIZATION; |
94 | } | 114 | } |
95 | CreateNewTerrainSegment(BSScene.TERRAIN_ID, initialMap, minTerrainCoords, maxTerrainCoords); | 115 | CreateNewTerrainSegment(BSScene.TERRAIN_ID, initialMap, minTerrainCoords, maxTerrainCoords); |
96 | } | 116 | } |
@@ -108,7 +128,7 @@ public class BSTerrainManager | |||
108 | if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, kvp.Value.Ptr)) | 128 | if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, kvp.Value.Ptr)) |
109 | { | 129 | { |
110 | BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, kvp.Value.Ptr); | 130 | BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, kvp.Value.Ptr); |
111 | BulletSimAPI.ReleaseHeightmapInfo2(m_heightMaps[kvp.Key].Ptr); | 131 | BulletSimAPI.ReleaseHeightMapInfo2(m_heightMaps[kvp.Key].Ptr); |
112 | } | 132 | } |
113 | } | 133 | } |
114 | m_terrains.Clear(); | 134 | m_terrains.Clear(); |
@@ -128,30 +148,41 @@ public class BSTerrainManager | |||
128 | int hSize = heightMap.Length; | 148 | int hSize = heightMap.Length; |
129 | for (int ii = 0; ii < hSize; ii++) | 149 | for (int ii = 0; ii < hSize; ii++) |
130 | { | 150 | { |
131 | minZ = heightMap[ii] < minZ ? heightMap[ii] : minZ; | 151 | float height = heightMap[ii]; |
132 | maxZ = heightMap[ii] > maxZ ? heightMap[ii] : maxZ; | 152 | if (height < minZ) minZ = height; |
153 | if (height > maxZ) maxZ = height; | ||
133 | } | 154 | } |
155 | // If the terrain is flat, make a difference so we get a bounding box | ||
156 | if (minZ == maxZ) | ||
157 | minZ -= HEIGHT_EQUAL_FUDGE; | ||
158 | |||
134 | minCoords.Z = minZ; | 159 | minCoords.Z = minZ; |
135 | maxCoords.Z = maxZ; | 160 | maxCoords.Z = maxZ; |
136 | // If the terrain is flat, make a difference so we get a good bounding box | ||
137 | if (minZ == maxZ) | ||
138 | minZ -= 0.2f; | ||
139 | Vector2 terrainRegionBase = new Vector2(minCoords.X, minCoords.Y); | 161 | Vector2 terrainRegionBase = new Vector2(minCoords.X, minCoords.Y); |
140 | 162 | ||
141 | // Create the heightmap data structure in the unmanaged space | 163 | // Create the heightmap data structure in the unmanaged space |
142 | BulletHeightMapInfo mapInfo = new BulletHeightMapInfo( | 164 | BulletHeightMapInfo mapInfo = new BulletHeightMapInfo(id, heightMap, |
143 | BulletSimAPI.CreateHeightmap2(minCoords, maxCoords, heightMap), heightMap); | 165 | BulletSimAPI.CreateHeightMapInfo2(id, minCoords, maxCoords, heightMap, TERRAIN_COLLISION_MARGIN)); |
144 | mapInfo.terrainRegionBase = terrainRegionBase; | 166 | mapInfo.terrainRegionBase = terrainRegionBase; |
145 | mapInfo.maxRegionExtent = maxCoords; | 167 | mapInfo.minCoords = minCoords; |
168 | mapInfo.maxCoords = maxCoords; | ||
146 | mapInfo.minZ = minZ; | 169 | mapInfo.minZ = minZ; |
147 | mapInfo.maxZ = maxZ; | 170 | mapInfo.maxZ = maxZ; |
148 | mapInfo.sizeX = maxCoords.X - minCoords.X; | 171 | mapInfo.sizeX = maxCoords.X - minCoords.X; |
149 | mapInfo.sizeY = maxCoords.Y - minCoords.Y; | 172 | mapInfo.sizeY = maxCoords.Y - minCoords.Y; |
150 | 173 | ||
174 | Vector3 centerPos; | ||
175 | centerPos.X = minCoords.X + (mapInfo.sizeX / 2f); | ||
176 | centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f); | ||
177 | centerPos.Z = minZ + (maxZ - minZ) / 2f; | ||
178 | |||
151 | DetailLog("{0},BSScene.CreateNewTerrainSegment,call,minZ={1},maxZ={2},hMapPtr={3},minC={4},maxC={5}", | 179 | DetailLog("{0},BSScene.CreateNewTerrainSegment,call,minZ={1},maxZ={2},hMapPtr={3},minC={4},maxC={5}", |
152 | BSScene.DetailLogZero, minZ, maxZ, mapInfo.Ptr, minCoords, maxCoords); | 180 | BSScene.DetailLogZero, minZ, maxZ, mapInfo.Ptr, minCoords, maxCoords); |
153 | // Create the terrain body from that heightmap | 181 | // Create the terrain shape from the mapInfo |
154 | BulletBody terrainBody = new BulletBody(id, BulletSimAPI.CreateTerrainBody2(id, mapInfo.Ptr, 0.01f)); | 182 | BulletShape terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr)); |
183 | |||
184 | BulletBody terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2(terrainShape.Ptr, | ||
185 | centerPos, Quaternion.Identity)); | ||
155 | 186 | ||
156 | BulletSimAPI.SetFriction2(terrainBody.Ptr, m_physicsScene.Params.terrainFriction); | 187 | BulletSimAPI.SetFriction2(terrainBody.Ptr, m_physicsScene.Params.terrainFriction); |
157 | BulletSimAPI.SetHitFraction2(terrainBody.Ptr, m_physicsScene.Params.terrainHitFraction); | 188 | BulletSimAPI.SetHitFraction2(terrainBody.Ptr, m_physicsScene.Params.terrainHitFraction); |
@@ -163,11 +194,12 @@ public class BSTerrainManager | |||
163 | BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, terrainBody.Ptr); | 194 | BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, terrainBody.Ptr); |
164 | BulletSimAPI.UpdateSingleAabb2(m_physicsScene.World.Ptr, terrainBody.Ptr); | 195 | BulletSimAPI.UpdateSingleAabb2(m_physicsScene.World.Ptr, terrainBody.Ptr); |
165 | 196 | ||
166 | |||
167 | // Add the created terrain to the management set. If we are doing mega-regions, | 197 | // Add the created terrain to the management set. If we are doing mega-regions, |
168 | // the terrains of our children will be added. | 198 | // the terrains of our children will be added. |
169 | m_terrains.Add(terrainRegionBase, terrainBody); | 199 | m_terrains.Add(terrainRegionBase, terrainBody); |
170 | m_heightMaps.Add(terrainRegionBase, mapInfo); | 200 | m_heightMaps.Add(terrainRegionBase, mapInfo); |
201 | |||
202 | m_terrainModified = true; | ||
171 | } | 203 | } |
172 | 204 | ||
173 | public void SetTerrain(float[] heightMap) { | 205 | public void SetTerrain(float[] heightMap) { |
@@ -191,34 +223,57 @@ public class BSTerrainManager | |||
191 | { | 223 | { |
192 | float minZ = float.MaxValue; | 224 | float minZ = float.MaxValue; |
193 | float maxZ = float.MinValue; | 225 | float maxZ = float.MinValue; |
226 | Vector2 terrainRegionBase = new Vector2(tOffset.X, tOffset.Y); | ||
194 | 227 | ||
195 | // Copy heightMap local and compute some statistics. | ||
196 | // Not really sure if we need to do this deep copy but, given | ||
197 | // the magic that happens to make the closure for taint | ||
198 | // below, I don't want there to be any problem with sharing | ||
199 | // locations of there are multiple calls to this routine | ||
200 | // within one tick. | ||
201 | int heightMapSize = heightMap.Length; | 228 | int heightMapSize = heightMap.Length; |
202 | float[] localHeightMap = new float[heightMapSize]; | ||
203 | for (int ii = 0; ii < heightMapSize; ii++) | 229 | for (int ii = 0; ii < heightMapSize; ii++) |
204 | { | 230 | { |
205 | float height = heightMap[ii]; | 231 | float height = heightMap[ii]; |
206 | if (height < minZ) minZ = height; | 232 | if (height < minZ) minZ = height; |
207 | if (height > maxZ) maxZ = height; | 233 | if (height > maxZ) maxZ = height; |
208 | localHeightMap[ii] = height; | ||
209 | } | 234 | } |
210 | 235 | ||
211 | Vector2 terrainRegionBase = new Vector2(tOffset.X, tOffset.Y); | 236 | // The shape of the terrain is from its base to its extents. |
237 | Vector3 minCoords, maxCoords; | ||
238 | minCoords = tOffset; | ||
239 | minCoords.Z = minZ; | ||
240 | maxCoords = tOffset; | ||
241 | maxCoords.X += Constants.RegionSize; | ||
242 | maxCoords.Y += Constants.RegionSize; | ||
243 | maxCoords.Z = maxZ; | ||
244 | |||
245 | BulletBody terrainBody; | ||
212 | BulletHeightMapInfo mapInfo; | 246 | BulletHeightMapInfo mapInfo; |
213 | if (m_heightMaps.TryGetValue(terrainRegionBase, out mapInfo)) | 247 | if (m_heightMaps.TryGetValue(terrainRegionBase, out mapInfo)) |
214 | { | 248 | { |
249 | terrainBody = m_terrains[terrainRegionBase]; | ||
250 | // Copy heightMap local and compute some statistics. | ||
251 | for (int ii = 0; ii < heightMapSize; ii++) | ||
252 | { | ||
253 | mapInfo.heightMap[ii] = heightMap[ii]; | ||
254 | } | ||
255 | |||
215 | // If this is terrain we know about, it's easy to update | 256 | // If this is terrain we know about, it's easy to update |
216 | mapInfo.heightMap = localHeightMap; | ||
217 | m_physicsScene.TaintedObject("BSScene.SetTerrain:UpdateExisting", delegate() | 257 | m_physicsScene.TaintedObject("BSScene.SetTerrain:UpdateExisting", delegate() |
218 | { | 258 | { |
219 | DetailLog("{0},SetTerrain:UpdateExisting,baseX={1},baseY={2},minZ={3},maxZ={4}", | 259 | DetailLog("{0},SetTerrain:UpdateExisting,baseX={1},baseY={2},minZ={3},maxZ={4}", |
220 | BSScene.DetailLogZero, tOffset.X, tOffset.Y, minZ, maxZ); | 260 | BSScene.DetailLogZero, tOffset.X, tOffset.Y, minZ, maxZ); |
221 | BulletSimAPI.UpdateHeightMap2(m_physicsScene.World.Ptr, mapInfo.Ptr, mapInfo.heightMap); | 261 | // Fill the existing height map info with the new location and size information |
262 | BulletSimAPI.FillHeightMapInfo2(mapInfo.Ptr, mapInfo.ID, minCoords, maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN); | ||
263 | |||
264 | // Create a terrain shape based on the new info | ||
265 | BulletShape terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr)); | ||
266 | |||
267 | // Swap the shape in the terrain body (this also deletes the old shape) | ||
268 | bool success = BulletSimAPI.ReplaceBodyShape2(m_physicsScene.World.Ptr, terrainBody.Ptr, terrainShape.Ptr); | ||
269 | |||
270 | if (!success) | ||
271 | { | ||
272 | DetailLog("{0},SetTerrain:UpdateExisting,Failed", BSScene.DetailLogZero); | ||
273 | m_physicsScene.Logger.ErrorFormat("{0} Failed updating terrain heightmap. Region={1}", | ||
274 | LogHeader, m_physicsScene.RegionName); | ||
275 | |||
276 | } | ||
222 | }); | 277 | }); |
223 | } | 278 | } |
224 | else | 279 | else |
@@ -226,11 +281,6 @@ public class BSTerrainManager | |||
226 | // Our mega-prim child is giving us a new terrain to add to the phys world | 281 | // Our mega-prim child is giving us a new terrain to add to the phys world |
227 | uint newTerrainID = ++m_terrainCount; | 282 | uint newTerrainID = ++m_terrainCount; |
228 | 283 | ||
229 | Vector3 minCoords = tOffset; | ||
230 | minCoords.Z = minZ; | ||
231 | Vector3 maxCoords = new Vector3(tOffset.X + Constants.RegionSize, | ||
232 | tOffset.Y + Constants.RegionSize, | ||
233 | maxZ); | ||
234 | m_physicsScene.TaintedObject("BSScene.SetTerrain:NewTerrain", delegate() | 284 | m_physicsScene.TaintedObject("BSScene.SetTerrain:NewTerrain", delegate() |
235 | { | 285 | { |
236 | DetailLog("{0},SetTerrain:NewTerrain,baseX={1},baseY={2}", BSScene.DetailLogZero, tOffset.X, tOffset.Y); | 286 | DetailLog("{0},SetTerrain:NewTerrain,baseX={1},baseY={2}", BSScene.DetailLogZero, tOffset.X, tOffset.Y); |
@@ -240,9 +290,9 @@ public class BSTerrainManager | |||
240 | } | 290 | } |
241 | 291 | ||
242 | // Someday we will have complex terrain with caves and tunnels | 292 | // Someday we will have complex terrain with caves and tunnels |
243 | // For the moment, it's flat and convex | ||
244 | public float GetTerrainHeightAtXYZ(Vector3 loc) | 293 | public float GetTerrainHeightAtXYZ(Vector3 loc) |
245 | { | 294 | { |
295 | // For the moment, it's flat and convex | ||
246 | return GetTerrainHeightAtXY(loc.X, loc.Y); | 296 | return GetTerrainHeightAtXY(loc.X, loc.Y); |
247 | } | 297 | } |
248 | 298 | ||
@@ -252,9 +302,19 @@ public class BSTerrainManager | |||
252 | // the same size and that is the default. | 302 | // the same size and that is the default. |
253 | // Once the heightMapInfo is found, we have all the information to | 303 | // Once the heightMapInfo is found, we have all the information to |
254 | // compute the offset into the array. | 304 | // compute the offset into the array. |
305 | private float lastHeightTX = 999999f; | ||
306 | private float lastHeightTY = 999999f; | ||
307 | private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT; | ||
255 | public float GetTerrainHeightAtXY(float tX, float tY) | 308 | public float GetTerrainHeightAtXY(float tX, float tY) |
256 | { | 309 | { |
257 | float ret = 30f; | 310 | // You'd be surprized at the number of times this routine is called |
311 | // with the same parameters as last time. | ||
312 | if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY) | ||
313 | return lastHeight; | ||
314 | |||
315 | lastHeightTX = tX; | ||
316 | lastHeightTY = tY; | ||
317 | float ret = HEIGHT_GETHEIGHT_RET; | ||
258 | 318 | ||
259 | int offsetX = ((int)(tX / (int)Constants.RegionSize)) * (int)Constants.RegionSize; | 319 | int offsetX = ((int)(tX / (int)Constants.RegionSize)) * (int)Constants.RegionSize; |
260 | int offsetY = ((int)(tY / (int)Constants.RegionSize)) * (int)Constants.RegionSize; | 320 | int offsetY = ((int)(tY / (int)Constants.RegionSize)) * (int)Constants.RegionSize; |
@@ -265,15 +325,20 @@ public class BSTerrainManager | |||
265 | { | 325 | { |
266 | float regionX = tX - offsetX; | 326 | float regionX = tX - offsetX; |
267 | float regionY = tY - offsetY; | 327 | float regionY = tY - offsetY; |
268 | regionX = regionX > mapInfo.sizeX ? 0 : regionX; | 328 | if (regionX > mapInfo.sizeX) regionX = 0; |
269 | regionY = regionY > mapInfo.sizeY ? 0 : regionY; | 329 | if (regionY > mapInfo.sizeY) regionY = 0; |
270 | ret = mapInfo.heightMap[(int)(regionX * mapInfo.sizeX + regionY)]; | 330 | int mapIndex = (int)regionY * (int)mapInfo.sizeY + (int)regionX; |
331 | ret = mapInfo.heightMap[mapIndex]; | ||
332 | m_terrainModified = false; | ||
333 | DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXY,bX={1},baseY={2},szX={3},szY={4},regX={5},regY={6},index={7},ht={8}", | ||
334 | BSScene.DetailLogZero, offsetX, offsetY, mapInfo.sizeX, mapInfo.sizeY, regionX, regionY, mapIndex, ret); | ||
271 | } | 335 | } |
272 | else | 336 | else |
273 | { | 337 | { |
274 | m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: x={1}, y={2}", | 338 | m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: x={1}, y={2}", |
275 | LogHeader, tX, tY); | 339 | LogHeader, tX, tY); |
276 | } | 340 | } |
341 | lastHeight = ret; | ||
277 | return ret; | 342 | return ret; |
278 | } | 343 | } |
279 | 344 | ||