aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs139
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