BSTerrainManager.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyrightD
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Text;
  30. using OpenSim.Framework;
  31. using OpenSim.Region.Framework;
  32. using OpenSim.Region.CoreModules;
  33. using OpenSim.Region.Physics.Manager;
  34. using Nini.Config;
  35. using log4net;
  36. using OpenMetaverse;
  37. namespace OpenSim.Region.Physics.BulletSPlugin
  38. {
  39. public sealed class BSTerrainManager
  40. {
  41. static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
  42. // These height values are fractional so the odd values will be
  43. // noticable when debugging.
  44. public const float HEIGHT_INITIALIZATION = 24.987f;
  45. public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f;
  46. public const float HEIGHT_GETHEIGHT_RET = 24.765f;
  47. // If the min and max height are equal, we reduce the min by this
  48. // amount to make sure that a bounding box is built for the terrain.
  49. public const float HEIGHT_EQUAL_FUDGE = 0.2f;
  50. public const float TERRAIN_COLLISION_MARGIN = 0.0f;
  51. // Until the whole simulator is changed to pass us the region size, we rely on constants.
  52. public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
  53. // The scene that I am part of
  54. private BSScene PhysicsScene { get; set; }
  55. // The ground plane created to keep thing from falling to infinity.
  56. private BulletBody m_groundPlane;
  57. // If doing mega-regions, if we're region zero we will be managing multiple
  58. // region terrains since region zero does the physics for the whole mega-region.
  59. private Dictionary<Vector2, BulletHeightMapInfo> m_heightMaps;
  60. // True of the terrain has been modified.
  61. // Used to force recalculation of terrain height after terrain has been modified
  62. private bool m_terrainModified;
  63. // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount.
  64. // This is incremented before assigning to new region so it is the last ID allocated.
  65. private uint m_terrainCount = BSScene.CHILDTERRAIN_ID - 1;
  66. public uint HighestTerrainID { get {return m_terrainCount; } }
  67. // If doing mega-regions, this holds our offset from region zero of
  68. // the mega-regions. "parentScene" points to the PhysicsScene of region zero.
  69. private Vector3 m_worldOffset;
  70. // If the parent region (region 0), this is the extent of the combined regions
  71. // relative to the origin of region zero
  72. private Vector3 m_worldMax;
  73. private PhysicsScene MegaRegionParentPhysicsScene { get; set; }
  74. public BSTerrainManager(BSScene physicsScene)
  75. {
  76. PhysicsScene = physicsScene;
  77. m_heightMaps = new Dictionary<Vector2,BulletHeightMapInfo>();
  78. m_terrainModified = false;
  79. // Assume one region of default size
  80. m_worldOffset = Vector3.Zero;
  81. m_worldMax = new Vector3(DefaultRegionSize);
  82. MegaRegionParentPhysicsScene = null;
  83. }
  84. // Create the initial instance of terrain and the underlying ground plane.
  85. // The objects are allocated in the unmanaged space and the pointers are tracked
  86. // by the managed code.
  87. // The terrains and the groundPlane are not added to the list of PhysObjects.
  88. // This is called from the initialization routine so we presume it is
  89. // safe to call Bullet in real time. We hope no one is moving prims around yet.
  90. public void CreateInitialGroundPlaneAndTerrain()
  91. {
  92. // The ground plane is here to catch things that are trying to drop to negative infinity
  93. BulletShape groundPlaneShape = new BulletShape(
  94. BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN),
  95. ShapeData.PhysicsShapeType.SHAPE_GROUNDPLANE);
  96. m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
  97. BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
  98. Vector3.Zero, Quaternion.Identity));
  99. BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr);
  100. BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_groundPlane.ptr);
  101. // Ground plane does not move
  102. BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION);
  103. // Everything collides with the ground plane.
  104. BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr,
  105. (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask);
  106. Vector3 minTerrainCoords = new Vector3(0f, 0f, HEIGHT_INITIALIZATION - HEIGHT_EQUAL_FUDGE);
  107. Vector3 maxTerrainCoords = new Vector3(DefaultRegionSize.X, DefaultRegionSize.Y, HEIGHT_INITIALIZATION);
  108. int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y;
  109. float[] initialMap = new float[totalHeights];
  110. for (int ii = 0; ii < totalHeights; ii++)
  111. {
  112. initialMap[ii] = HEIGHT_INITIALIZATION;
  113. }
  114. UpdateOrCreateTerrain(BSScene.TERRAIN_ID, initialMap, minTerrainCoords, maxTerrainCoords, true);
  115. }
  116. // Release all the terrain structures we might have allocated
  117. public void ReleaseGroundPlaneAndTerrain()
  118. {
  119. if (m_groundPlane.ptr != IntPtr.Zero)
  120. {
  121. if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr))
  122. {
  123. BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr);
  124. }
  125. m_groundPlane.ptr = IntPtr.Zero;
  126. }
  127. ReleaseTerrain();
  128. }
  129. // Release all the terrain we have allocated
  130. public void ReleaseTerrain()
  131. {
  132. foreach (KeyValuePair<Vector2, BulletHeightMapInfo> kvp in m_heightMaps)
  133. {
  134. if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, kvp.Value.terrainBody.ptr))
  135. {
  136. BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, kvp.Value.terrainBody.ptr);
  137. BulletSimAPI.ReleaseHeightMapInfo2(kvp.Value.Ptr);
  138. }
  139. }
  140. m_heightMaps.Clear();
  141. }
  142. // The simulator wants to set a new heightmap for the terrain.
  143. public void SetTerrain(float[] heightMap) {
  144. float[] localHeightMap = heightMap;
  145. PhysicsScene.TaintedObject("TerrainManager.SetTerrain", delegate()
  146. {
  147. if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null)
  148. {
  149. // If a child of a mega-region, we shouldn't have any terrain allocated for us
  150. ReleaseGroundPlaneAndTerrain();
  151. // If doing the mega-prim stuff and we are the child of the zero region,
  152. // the terrain is added to our parent
  153. if (MegaRegionParentPhysicsScene is BSScene)
  154. {
  155. DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
  156. BSScene.DetailLogZero, m_worldOffset, m_worldMax);
  157. ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateOrCreateTerrain(BSScene.CHILDTERRAIN_ID,
  158. localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize, true);
  159. }
  160. }
  161. else
  162. {
  163. // If not doing the mega-prim thing, just change the terrain
  164. DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
  165. UpdateOrCreateTerrain(BSScene.TERRAIN_ID, localHeightMap,
  166. m_worldOffset, m_worldOffset + DefaultRegionSize, true);
  167. }
  168. });
  169. }
  170. // If called with no mapInfo for the terrain, this will create a new mapInfo and terrain
  171. // based on the passed information. The 'id' should be either the terrain id or
  172. // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
  173. // The latter feature is for creating child terrains for mega-regions.
  174. // If called with a mapInfo in m_heightMaps but the terrain has no body yet (mapInfo.terrainBody.Ptr == 0)
  175. // then a new body and shape is created and the mapInfo is filled.
  176. // This call is used for doing the initial terrain creation.
  177. // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new
  178. // terrain shape is created and added to the body.
  179. // This call is most often used to update the heightMap and parameters of the terrain.
  180. // (The above does suggest that some simplification/refactoring is in order.)
  181. private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
  182. {
  183. DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},inTaintTime={3}",
  184. BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime);
  185. float minZ = float.MaxValue;
  186. float maxZ = float.MinValue;
  187. Vector2 terrainRegionBase = new Vector2(minCoords.X, minCoords.Y);
  188. int heightMapSize = heightMap.Length;
  189. for (int ii = 0; ii < heightMapSize; ii++)
  190. {
  191. float height = heightMap[ii];
  192. if (height < minZ) minZ = height;
  193. if (height > maxZ) maxZ = height;
  194. }
  195. // The shape of the terrain is from its base to its extents.
  196. minCoords.Z = minZ;
  197. maxCoords.Z = maxZ;
  198. BulletHeightMapInfo mapInfo;
  199. if (m_heightMaps.TryGetValue(terrainRegionBase, out mapInfo))
  200. {
  201. // If this is terrain we know about, it's easy to update
  202. mapInfo.heightMap = heightMap;
  203. mapInfo.minCoords = minCoords;
  204. mapInfo.maxCoords = maxCoords;
  205. mapInfo.minZ = minZ;
  206. mapInfo.maxZ = maxZ;
  207. mapInfo.sizeX = maxCoords.X - minCoords.X;
  208. mapInfo.sizeY = maxCoords.Y - minCoords.Y;
  209. DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,call,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
  210. BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);
  211. BSScene.TaintCallback rebuildOperation = delegate()
  212. {
  213. if (MegaRegionParentPhysicsScene != null)
  214. {
  215. // It's possible that Combine() was called after this code was queued.
  216. // If we are a child of combined regions, we don't create any terrain for us.
  217. DetailLog("{0},UpdateOrCreateTerrain:AmACombineChild,taint", BSScene.DetailLogZero);
  218. // Get rid of any terrain that may have been allocated for us.
  219. ReleaseGroundPlaneAndTerrain();
  220. // I hate doing this, but just bail
  221. return;
  222. }
  223. if (mapInfo.terrainBody.ptr != IntPtr.Zero)
  224. {
  225. // Updating an existing terrain.
  226. DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,taint,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
  227. BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);
  228. // Remove from the dynamics world because we're going to mangle this object
  229. BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
  230. // Get rid of the old terrain
  231. BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
  232. BulletSimAPI.ReleaseHeightMapInfo2(mapInfo.Ptr);
  233. mapInfo.Ptr = IntPtr.Zero;
  234. /*
  235. // NOTE: This routine is half here because I can't get the terrain shape replacement
  236. // to work. In the short term, the above three lines completely delete the old
  237. // terrain and the code below recreates one from scratch.
  238. // Hopefully the Bullet community will help me out on this one.
  239. // First, release the old collision shape (there is only one terrain)
  240. BulletSimAPI.DeleteCollisionShape2(m_physicsScene.World.Ptr, mapInfo.terrainShape.Ptr);
  241. // Fill the existing height map info with the new location and size information
  242. BulletSimAPI.FillHeightMapInfo2(m_physicsScene.World.Ptr, mapInfo.Ptr, mapInfo.ID,
  243. mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);
  244. // Create a terrain shape based on the new info
  245. mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr));
  246. // Stuff the shape into the existing terrain body
  247. BulletSimAPI.SetBodyShape2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr, mapInfo.terrainShape.Ptr);
  248. */
  249. }
  250. // else
  251. {
  252. // Creating a new terrain.
  253. DetailLog("{0},UpdateOrCreateTerrain:CreateNewTerrain,taint,baseX={1},baseY={2},minZ={3},maxZ={4}",
  254. BSScene.DetailLogZero, mapInfo.minCoords.X, mapInfo.minCoords.Y, minZ, maxZ);
  255. mapInfo.ID = id;
  256. mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, mapInfo.ID,
  257. mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);
  258. // Create the terrain shape from the mapInfo
  259. mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr),
  260. ShapeData.PhysicsShapeType.SHAPE_TERRAIN);
  261. // The terrain object initial position is at the center of the object
  262. Vector3 centerPos;
  263. centerPos.X = minCoords.X + (mapInfo.sizeX / 2f);
  264. centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f);
  265. centerPos.Z = minZ + ((maxZ - minZ) / 2f);
  266. mapInfo.terrainBody = new BulletBody(mapInfo.ID,
  267. BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.ptr,
  268. id, centerPos, Quaternion.Identity));
  269. }
  270. // Make sure the entry is in the heightmap table
  271. m_heightMaps[terrainRegionBase] = mapInfo;
  272. // Set current terrain attributes
  273. BulletSimAPI.SetFriction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainFriction);
  274. BulletSimAPI.SetHitFraction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
  275. BulletSimAPI.SetRestitution2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
  276. BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
  277. // Return the new terrain to the world of physical objects
  278. BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
  279. // redo its bounding box now that it is in the world
  280. BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
  281. BulletSimAPI.SetCollisionFilterMask2(mapInfo.terrainBody.ptr,
  282. (uint)CollisionFilterGroups.TerrainFilter,
  283. (uint)CollisionFilterGroups.TerrainMask);
  284. // Make sure the new shape is processed.
  285. // BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true);
  286. // BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.ISLAND_SLEEPING);
  287. BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
  288. m_terrainModified = true;
  289. };
  290. // There is the option to do the changes now (we're already in 'taint time'), or
  291. // to do the Bullet operations later.
  292. if (inTaintTime)
  293. rebuildOperation();
  294. else
  295. PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:UpdateExisting", rebuildOperation);
  296. }
  297. else
  298. {
  299. // We don't know about this terrain so either we are creating a new terrain or
  300. // our mega-prim child is giving us a new terrain to add to the phys world
  301. // if this is a child terrain, calculate a unique terrain id
  302. uint newTerrainID = id;
  303. if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
  304. newTerrainID = ++m_terrainCount;
  305. float[] heightMapX = heightMap;
  306. Vector3 minCoordsX = minCoords;
  307. Vector3 maxCoordsX = maxCoords;
  308. DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}",
  309. BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
  310. // Code that must happen at taint-time
  311. BSScene.TaintCallback createOperation = delegate()
  312. {
  313. DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,taint,baseX={1},baseY={2}", BSScene.DetailLogZero, minCoords.X, minCoords.Y);
  314. // Create a new mapInfo that will be filled with the new info
  315. mapInfo = new BulletHeightMapInfo(id, heightMapX,
  316. BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, newTerrainID,
  317. minCoordsX, maxCoordsX, heightMapX, TERRAIN_COLLISION_MARGIN));
  318. // Put the unfilled heightmap info into the collection of same
  319. m_heightMaps.Add(terrainRegionBase, mapInfo);
  320. // Build the terrain
  321. UpdateOrCreateTerrain(newTerrainID, heightMap, minCoords, maxCoords, true);
  322. m_terrainModified = true;
  323. };
  324. // If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time.
  325. if (inTaintTime)
  326. createOperation();
  327. else
  328. PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:NewTerrain", createOperation);
  329. }
  330. }
  331. // Someday we will have complex terrain with caves and tunnels
  332. public float GetTerrainHeightAtXYZ(Vector3 loc)
  333. {
  334. // For the moment, it's flat and convex
  335. return GetTerrainHeightAtXY(loc.X, loc.Y);
  336. }
  337. // Given an X and Y, find the height of the terrain.
  338. // Since we could be handling multiple terrains for a mega-region,
  339. // the base of the region is calcuated assuming all regions are
  340. // the same size and that is the default.
  341. // Once the heightMapInfo is found, we have all the information to
  342. // compute the offset into the array.
  343. private float lastHeightTX = 999999f;
  344. private float lastHeightTY = 999999f;
  345. private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
  346. private float GetTerrainHeightAtXY(float tX, float tY)
  347. {
  348. // You'd be surprized at the number of times this routine is called
  349. // with the same parameters as last time.
  350. if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY)
  351. return lastHeight;
  352. lastHeightTX = tX;
  353. lastHeightTY = tY;
  354. float ret = HEIGHT_GETHEIGHT_RET;
  355. int offsetX = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
  356. int offsetY = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
  357. Vector2 terrainBaseXY = new Vector2(offsetX, offsetY);
  358. BulletHeightMapInfo mapInfo;
  359. if (m_heightMaps.TryGetValue(terrainBaseXY, out mapInfo))
  360. {
  361. float regionX = tX - offsetX;
  362. float regionY = tY - offsetY;
  363. int mapIndex = (int)regionY * (int)mapInfo.sizeY + (int)regionX;
  364. try
  365. {
  366. ret = mapInfo.heightMap[mapIndex];
  367. }
  368. catch
  369. {
  370. // Sometimes they give us wonky values of X and Y. Give a warning and return something.
  371. PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, x={2}, y={3}",
  372. LogHeader, terrainBaseXY, regionX, regionY);
  373. ret = HEIGHT_GETHEIGHT_RET;
  374. }
  375. // DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXY,bX={1},baseY={2},szX={3},szY={4},regX={5},regY={6},index={7},ht={8}",
  376. // BSScene.DetailLogZero, offsetX, offsetY, mapInfo.sizeX, mapInfo.sizeY, regionX, regionY, mapIndex, ret);
  377. }
  378. else
  379. {
  380. PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
  381. LogHeader, PhysicsScene.RegionName, tX, tY);
  382. }
  383. m_terrainModified = false;
  384. lastHeight = ret;
  385. return ret;
  386. }
  387. // Although no one seems to check this, I do support combining.
  388. public bool SupportsCombining()
  389. {
  390. return true;
  391. }
  392. // This routine is called two ways:
  393. // One with 'offset' and 'pScene' zero and null but 'extents' giving the maximum
  394. // extent of the combined regions. This is to inform the parent of the size
  395. // of the combined regions.
  396. // and one with 'offset' as the offset of the child region to the base region,
  397. // 'pScene' pointing to the parent and 'extents' of zero. This informs the
  398. // child of its relative base and new parent.
  399. public void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
  400. {
  401. m_worldOffset = offset;
  402. m_worldMax = extents;
  403. MegaRegionParentPhysicsScene = pScene;
  404. if (pScene != null)
  405. {
  406. // We are a child.
  407. // We want m_worldMax to be the highest coordinate of our piece of terrain.
  408. m_worldMax = offset + DefaultRegionSize;
  409. }
  410. DetailLog("{0},BSTerrainManager.Combine,offset={1},extents={2},wOffset={3},wMax={4}",
  411. BSScene.DetailLogZero, offset, extents, m_worldOffset, m_worldMax);
  412. }
  413. // Unhook all the combining that I know about.
  414. public void UnCombine(PhysicsScene pScene)
  415. {
  416. // Just like ODE, for the moment a NOP
  417. DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero);
  418. }
  419. private void DetailLog(string msg, params Object[] args)
  420. {
  421. PhysicsScene.PhysicsLogging.Write(msg, args);
  422. }
  423. }
  424. }