TerrainData.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  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 copyright
  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;
  29. using System.IO;
  30. using System.IO.Compression;
  31. using System.Reflection;
  32. using OpenMetaverse;
  33. using log4net;
  34. namespace OpenSim.Framework
  35. {
  36. // The terrain is stored in the database as a blob with a 'revision' field.
  37. // Some implementations of terrain storage would fill the revision field with
  38. // the time the terrain was stored. When real revisions were added and this
  39. // feature removed, that left some old entries with the time in the revision
  40. // field.
  41. // Thus, if revision is greater than 'RevisionHigh' then terrain db entry is
  42. // left over and it is presumed to be 'Legacy256'.
  43. // Numbers are arbitrary and are chosen to to reduce possible mis-interpretation.
  44. // If a revision does not match any of these, it is assumed to be Legacy256.
  45. public enum DBTerrainRevision
  46. {
  47. // Terrain is 'double[256,256]'
  48. Legacy256 = 11,
  49. // Terrain is 'int32, int32, float[,]' where the ints are X and Y dimensions
  50. // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256.
  51. Variable2D = 22,
  52. Variable2DGzip = 23,
  53. // Terrain is 'int32, int32, int32, int16[]' where the ints are X and Y dimensions
  54. // and third int is the 'compression factor'. The heights are compressed as
  55. // "ushort compressedHeight = (ushort)(height * compressionFactor);"
  56. // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256.
  57. Compressed2D = 27,
  58. // A revision that is not listed above or any revision greater than this value is 'Legacy256'.
  59. RevisionHigh = 1234
  60. }
  61. public class TerrainData
  62. {
  63. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  64. private static string LogHeader = "[TERRAIN DATA]";
  65. private float[,] m_heightmap;
  66. // Remember subregions of the heightmap that has changed.
  67. private BitArray m_taint;
  68. private int m_taintSizeX;
  69. private int m_taintSizeY;
  70. // legacy CompressionFactor
  71. public float CompressionFactor { get; private set; }
  72. public int SizeX { get; protected set; }
  73. public int SizeY { get; protected set; }
  74. public int SizeZ { get; protected set; }
  75. // A height used when the user doesn't specify anything
  76. public const float DefaultTerrainHeight = 21f;
  77. // Given a revision code and a blob from the database, create and return the right type of TerrainData.
  78. // The sizes passed are the expected size of the region. The database info will be used to
  79. // initialize the heightmap of that sized region with as much data is in the blob.
  80. // Return created TerrainData or 'null' if unsuccessful.
  81. public static TerrainData CreateFromDatabaseBlobFactory(int pSizeX, int pSizeY, int pSizeZ, int pFormatCode, byte[] pBlob)
  82. {
  83. // For the moment, there is only one implementation class
  84. return new TerrainData(pSizeX, pSizeY, pSizeZ, pFormatCode, pBlob);
  85. }
  86. public float this[int x, int y]
  87. {
  88. get { return m_heightmap[x, y]; }
  89. set
  90. {
  91. if (m_heightmap[x, y] != value)
  92. {
  93. m_heightmap[x, y] = value;
  94. int yy = y / Constants.TerrainPatchSize;
  95. m_taint[x / Constants.TerrainPatchSize + yy * m_taintSizeX] = true;
  96. }
  97. }
  98. }
  99. public float this[int x, int y, int z]
  100. {
  101. get { return this[x, y]; }
  102. set { this[x, y] = value; }
  103. }
  104. public void ClearTaint()
  105. {
  106. m_taint.SetAll(false);
  107. }
  108. public void TaintAllTerrain()
  109. {
  110. m_taint.SetAll(true);
  111. }
  112. private void SetAllTaint(bool setting)
  113. {
  114. m_taint.SetAll(setting);
  115. }
  116. public void ClearLand()
  117. {
  118. ClearLand(DefaultTerrainHeight);
  119. }
  120. public void ClearLand(float pHeight)
  121. {
  122. for (int xx = 0; xx < SizeX; xx++)
  123. for (int yy = 0; yy < SizeY; yy++)
  124. m_heightmap[xx, yy] = pHeight;
  125. }
  126. // Return 'true' of the patch that contains these region coordinates has been modified.
  127. // Note that checking the taint clears it.
  128. // There is existing code that relies on this feature.
  129. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  130. public bool IsTaintedAt(int xx, int yy, bool clearOnTest)
  131. {
  132. yy /= Constants.TerrainPatchSize;
  133. int indx = xx / Constants.TerrainPatchSize + yy * m_taintSizeX;
  134. bool ret = m_taint[indx];
  135. if (ret && clearOnTest)
  136. m_taint[indx] = false;
  137. return ret;
  138. }
  139. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  140. public bool IsTaintedAt(int xx, int yy)
  141. {
  142. yy /= Constants.TerrainPatchSize;
  143. return m_taint[xx / Constants.TerrainPatchSize + yy * m_taintSizeX];
  144. }
  145. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  146. public bool IsTaintedAtPatch(int xx, int yy, bool clearOnTest)
  147. {
  148. int indx = xx + yy * m_taintSizeX;
  149. bool ret = m_taint[indx];
  150. if (ret && clearOnTest)
  151. m_taint[indx] = false;
  152. return ret;
  153. }
  154. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  155. public bool IsTaintedAtPatch(int xx, int yy)
  156. {
  157. return m_taint[xx + yy * m_taintSizeX];
  158. }
  159. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  160. public bool IsTaintedAtPatch(int indx, bool clearOnTest)
  161. {
  162. bool ret = m_taint[indx];
  163. if (ret && clearOnTest)
  164. m_taint[indx] = false;
  165. return ret;
  166. }
  167. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  168. public bool IsTaintedAtPatchWithClear(int indx)
  169. {
  170. if(m_taint[indx])
  171. {
  172. m_taint[indx] = false;
  173. return true;
  174. }
  175. return false;
  176. }
  177. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
  178. public bool IsTaintedAtPatch(int indx)
  179. {
  180. return m_taint[indx];
  181. }
  182. // TerrainData.GetDatabaseBlob
  183. // The user wants something to store in the database.
  184. public bool GetDatabaseBlob(out int DBRevisionCode, out Array blob)
  185. {
  186. DBRevisionCode = (int)DBTerrainRevision.Variable2DGzip;
  187. blob = ToCompressedTerrainSerializationV2DGzip();
  188. return true;
  189. }
  190. // TerrainData.GetCompressedMap
  191. public float[] GetCompressedMap()
  192. {
  193. float[] newMap = new float[SizeX * SizeY];
  194. int ind = 0;
  195. for (int xx = 0; xx < SizeX; xx++)
  196. for (int yy = 0; yy < SizeY; yy++)
  197. newMap[ind++] = m_heightmap[xx, yy];
  198. return newMap;
  199. }
  200. public TerrainData Clone()
  201. {
  202. TerrainData ret = new TerrainData(SizeX, SizeY, SizeZ);
  203. ret.m_heightmap = (float[,])this.m_heightmap.Clone();
  204. return ret;
  205. }
  206. public float[] GetFloatsSerialized()
  207. {
  208. int points = SizeX * SizeY;
  209. float[] heights = new float[points];
  210. int idx = 0;
  211. for (int jj = 0; jj < SizeY; jj++)
  212. for (int ii = 0; ii < SizeX; ii++)
  213. {
  214. heights[idx++] = m_heightmap[ii, jj];
  215. }
  216. return heights;
  217. }
  218. public double[,] GetDoubles()
  219. {
  220. double[,] ret = new double[SizeX, SizeY];
  221. for (int xx = 0; xx < SizeX; xx++)
  222. for (int yy = 0; yy < SizeY; yy++)
  223. ret[xx, yy] = (double)m_heightmap[xx, yy];
  224. return ret;
  225. }
  226. public unsafe void GetPatchMinMax(int px, int py, out float zmin, out float zmax)
  227. {
  228. zmax = float.MinValue;
  229. zmin = float.MaxValue;
  230. int stride = m_heightmap.GetLength(1);
  231. int startx = px * 16 * stride;
  232. int endx = (px + 1) * 16 * stride;
  233. int starty = py * 16;
  234. fixed (float* map = m_heightmap)
  235. {
  236. for (int i = startx; i < endx; i += stride)
  237. {
  238. float* p = &map[i];
  239. for (int j = starty; j < starty + 16; j++)
  240. {
  241. float val = p[j];
  242. if (val > zmax) zmax = val;
  243. if (val < zmin) zmin = val;
  244. }
  245. }
  246. }
  247. }
  248. public unsafe void GetPatchBlock(float* block, int px, int py, float sub, float premult)
  249. {
  250. int k = 0;
  251. int stride = m_heightmap.GetLength(1);
  252. int startX = px * 16 * stride;
  253. int endX = (px + 1) * 16 * stride;
  254. int startY = py * 16;
  255. fixed(float* map = m_heightmap)
  256. {
  257. for (int y = startY; y < startY + 16; y++)
  258. {
  259. for (int x = startX; x < endX; x += stride)
  260. {
  261. block[k++] = (map[x + y] - sub) * premult;
  262. }
  263. }
  264. }
  265. }
  266. /*
  267. // that is coded as the float height times the compression factor (usually '100'
  268. // to make for two decimal points).
  269. public short ToCompressedHeightshort(float pHeight)
  270. {
  271. // clamp into valid range
  272. pHeight *= CompressionFactor;
  273. if (pHeight < short.MinValue)
  274. return short.MinValue;
  275. else if (pHeight > short.MaxValue)
  276. return short.MaxValue;
  277. return (short)pHeight;
  278. }
  279. public ushort ToCompressedHeightushort(float pHeight)
  280. {
  281. // clamp into valid range
  282. pHeight *= CompressionFactor;
  283. if (pHeight < ushort.MinValue)
  284. return ushort.MinValue;
  285. else if (pHeight > ushort.MaxValue)
  286. return ushort.MaxValue;
  287. return (ushort)pHeight;
  288. }
  289. */
  290. public float FromCompressedHeight(short pHeight)
  291. {
  292. return ((float)pHeight) / CompressionFactor;
  293. }
  294. public float FromCompressedHeight(ushort pHeight)
  295. {
  296. return ((float)pHeight) / CompressionFactor;
  297. }
  298. // To keep with the legacy theme, create an instance of this class based on the
  299. // way terrain used to be passed around.
  300. public TerrainData(double[,] pTerrain)
  301. {
  302. SizeX = pTerrain.GetLength(0);
  303. SizeY = pTerrain.GetLength(1);
  304. m_taintSizeX = SizeX / Constants.TerrainPatchSize;
  305. m_taintSizeY = SizeY / Constants.TerrainPatchSize;
  306. SizeZ = (int)Constants.RegionHeight;
  307. CompressionFactor = 100.0f;
  308. m_heightmap = new float[SizeX, SizeY];
  309. for (int ii = 0; ii < SizeX; ii++)
  310. {
  311. for (int jj = 0; jj < SizeY; jj++)
  312. {
  313. m_heightmap[ii, jj] = (float)pTerrain[ii, jj];
  314. }
  315. }
  316. // m_log.DebugFormat("{0} new by doubles. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ);
  317. m_taint = new BitArray(m_taintSizeX * m_taintSizeY, false);
  318. }
  319. // Create underlying structures but don't initialize the heightmap assuming the caller will immediately do that
  320. public TerrainData(int pX, int pY, int pZ)
  321. {
  322. SizeX = pX;
  323. SizeY = pY;
  324. SizeZ = pZ;
  325. m_taintSizeX = SizeX / Constants.TerrainPatchSize;
  326. m_taintSizeY = SizeY / Constants.TerrainPatchSize;
  327. CompressionFactor = 100.0f;
  328. m_heightmap = new float[SizeX, SizeY];
  329. m_taint = new BitArray(m_taintSizeX * m_taintSizeY, false);
  330. // m_log.DebugFormat("{0} new by dimensions. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ);
  331. ClearLand(0f);
  332. }
  333. public TerrainData(float[] cmap, float pCompressionFactor, int pX, int pY, int pZ)
  334. : this(pX, pY, pZ)
  335. {
  336. CompressionFactor = pCompressionFactor;
  337. int ind = 0;
  338. for (int xx = 0; xx < SizeX; xx++)
  339. for (int yy = 0; yy < SizeY; yy++)
  340. m_heightmap[xx, yy] = cmap[ind++];
  341. // m_log.DebugFormat("{0} new by compressed map. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ);
  342. }
  343. // Create a heighmap from a database blob
  344. public TerrainData(int pSizeX, int pSizeY, int pSizeZ, int pFormatCode, byte[] pBlob)
  345. : this(pSizeX, pSizeY, pSizeZ)
  346. {
  347. switch ((DBTerrainRevision)pFormatCode)
  348. {
  349. case DBTerrainRevision.Variable2DGzip:
  350. FromCompressedTerrainSerializationV2DGZip(pBlob);
  351. m_log.DebugFormat("{0} HeightmapTerrainData create from Variable2DGzip serialization. Size=<{1},{2}>", LogHeader, SizeX, SizeY);
  352. break;
  353. case DBTerrainRevision.Variable2D:
  354. FromCompressedTerrainSerializationV2D(pBlob);
  355. m_log.DebugFormat("{0} HeightmapTerrainData create from Variable2D serialization. Size=<{1},{2}>", LogHeader, SizeX, SizeY);
  356. break;
  357. case DBTerrainRevision.Compressed2D:
  358. FromCompressedTerrainSerialization2D(pBlob);
  359. m_log.DebugFormat("{0} HeightmapTerrainData create from Compressed2D serialization. Size=<{1},{2}>", LogHeader, SizeX, SizeY);
  360. break;
  361. default:
  362. FromLegacyTerrainSerialization(pBlob);
  363. m_log.DebugFormat("{0} HeightmapTerrainData create from legacy serialization. Size=<{1},{2}>", LogHeader, SizeX, SizeY);
  364. break;
  365. }
  366. }
  367. // Just create an array of doubles. Presumes the caller implicitly knows the size.
  368. public Array ToLegacyTerrainSerialization()
  369. {
  370. Array ret = null;
  371. using (MemoryStream str = new MemoryStream((int)Constants.RegionSize * (int)Constants.RegionSize * sizeof(double)))
  372. {
  373. using (BinaryWriter bw = new BinaryWriter(str))
  374. {
  375. for (int xx = 0; xx < Constants.RegionSize; xx++)
  376. {
  377. for (int yy = 0; yy < Constants.RegionSize; yy++)
  378. {
  379. double height = this[xx, yy];
  380. if (height == 0.0)
  381. height = double.Epsilon;
  382. bw.Write(height);
  383. }
  384. }
  385. }
  386. ret = str.ToArray();
  387. }
  388. return ret;
  389. }
  390. // Presumes the caller implicitly knows the size.
  391. public void FromLegacyTerrainSerialization(byte[] pBlob)
  392. {
  393. // In case database info doesn't match real terrain size, initialize the whole terrain.
  394. ClearLand();
  395. try
  396. {
  397. using (MemoryStream mstr = new MemoryStream(pBlob))
  398. {
  399. using (BinaryReader br = new BinaryReader(mstr))
  400. {
  401. for (int xx = 0; xx < (int)Constants.RegionSize; xx++)
  402. {
  403. for (int yy = 0; yy < (int)Constants.RegionSize; yy++)
  404. {
  405. float val = (float)br.ReadDouble();
  406. if (xx < SizeX && yy < SizeY)
  407. m_heightmap[xx, yy] = val;
  408. }
  409. }
  410. }
  411. }
  412. }
  413. catch
  414. {
  415. ClearLand();
  416. }
  417. ClearTaint();
  418. }
  419. // stores as variable2D
  420. // int32 sizeX
  421. // int32 sizeY
  422. // float[,] array
  423. public Array ToCompressedTerrainSerializationV2D()
  424. {
  425. Array ret = null;
  426. try
  427. {
  428. using (MemoryStream str = new MemoryStream((2 * sizeof(Int32)) + (SizeX * SizeY * sizeof(float))))
  429. {
  430. using (BinaryWriter bw = new BinaryWriter(str))
  431. {
  432. bw.Write((Int32)SizeX);
  433. bw.Write((Int32)SizeY);
  434. for (int yy = 0; yy < SizeY; yy++)
  435. for (int xx = 0; xx < SizeX; xx++)
  436. {
  437. // reduce to 1cm resolution
  438. float val = (float)Math.Round(m_heightmap[xx, yy],2,MidpointRounding.ToEven);
  439. bw.Write(val);
  440. }
  441. }
  442. ret = str.ToArray();
  443. }
  444. }
  445. catch {}
  446. m_log.DebugFormat("{0} V2D {1} bytes", LogHeader, ret.Length);
  447. return ret;
  448. }
  449. // as above with Gzip compression
  450. public Array ToCompressedTerrainSerializationV2DGzip()
  451. {
  452. Array ret = null;
  453. try
  454. {
  455. using (MemoryStream inp = new MemoryStream((2 * sizeof(Int32)) + (SizeX * SizeY * sizeof(float))))
  456. {
  457. using (BinaryWriter bw = new BinaryWriter(inp))
  458. {
  459. bw.Write((Int32)SizeX);
  460. bw.Write((Int32)SizeY);
  461. for (int yy = 0; yy < SizeY; yy++)
  462. for (int xx = 0; xx < SizeX; xx++)
  463. {
  464. //bw.Write((float)m_heightmap[xx, yy]);
  465. bw.Write((float)Math.Round(m_heightmap[xx, yy], 3, MidpointRounding.AwayFromZero));
  466. }
  467. bw.Flush();
  468. inp.Seek(0, SeekOrigin.Begin);
  469. using (MemoryStream outputStream = new MemoryStream())
  470. {
  471. using (GZipStream compressionStream = new GZipStream(outputStream, CompressionMode.Compress))
  472. {
  473. inp.CopyStream(compressionStream, int.MaxValue);
  474. compressionStream.Close();
  475. ret = outputStream.ToArray();
  476. }
  477. }
  478. }
  479. }
  480. }
  481. catch {}
  482. m_log.DebugFormat("{0} V2DGzip {1} bytes", LogHeader, ret.Length);
  483. return ret;
  484. }
  485. // Initialize heightmap from blob consisting of:
  486. // int32, int32, int32, int32, int16[]
  487. // where the first int32 is format code, next two int32s are the X and y of heightmap data and
  488. // the forth int is the compression factor for the following int16s
  489. // This is just sets heightmap info. The actual size of the region was set on this instance's
  490. // creation and any heights not initialized by theis blob are set to the default height.
  491. public void FromCompressedTerrainSerialization2D(byte[] pBlob)
  492. {
  493. Int32 hmFormatCode, hmSizeX, hmSizeY, hmCompressionFactor;
  494. using (MemoryStream mstr = new MemoryStream(pBlob))
  495. {
  496. using (BinaryReader br = new BinaryReader(mstr))
  497. {
  498. hmFormatCode = br.ReadInt32();
  499. hmSizeX = br.ReadInt32();
  500. hmSizeY = br.ReadInt32();
  501. hmCompressionFactor = br.ReadInt32();
  502. CompressionFactor = hmCompressionFactor;
  503. // In case database info doesn't match real terrain size, initialize the whole terrain.
  504. ClearLand();
  505. for (int yy = 0; yy < hmSizeY; yy++)
  506. {
  507. for (int xx = 0; xx < hmSizeX; xx++)
  508. {
  509. float val = FromCompressedHeight(br.ReadInt16());
  510. if (xx < SizeX && yy < SizeY)
  511. m_heightmap[xx, yy] = val;
  512. }
  513. }
  514. }
  515. ClearTaint();
  516. m_log.DebugFormat("{0} Read (compressed2D) heightmap. Heightmap size=<{1},{2}>. Region size=<{3},{4}>. CompFact={5}",
  517. LogHeader, hmSizeX, hmSizeY, SizeX, SizeY, hmCompressionFactor);
  518. }
  519. }
  520. // Initialize heightmap from blob consisting of:
  521. // int32, int32, int32, float[]
  522. // where the first int32 is format code, next two int32s are the X and y of heightmap data
  523. // This is just sets heightmap info. The actual size of the region was set on this instance's
  524. // creation and any heights not initialized by theis blob are set to the default height.
  525. public void FromCompressedTerrainSerializationV2D(byte[] pBlob)
  526. {
  527. Int32 hmSizeX, hmSizeY;
  528. try
  529. {
  530. using (MemoryStream mstr = new MemoryStream(pBlob))
  531. {
  532. using (BinaryReader br = new BinaryReader(mstr))
  533. {
  534. hmSizeX = br.ReadInt32();
  535. hmSizeY = br.ReadInt32();
  536. // In case database info doesn't match real terrain size, initialize the whole terrain.
  537. ClearLand();
  538. for (int yy = 0; yy < hmSizeY; yy++)
  539. {
  540. for (int xx = 0; xx < hmSizeX; xx++)
  541. {
  542. float val = br.ReadSingle();
  543. if (xx < SizeX && yy < SizeY)
  544. m_heightmap[xx, yy] = val;
  545. }
  546. }
  547. }
  548. }
  549. }
  550. catch (Exception e)
  551. {
  552. ClearTaint();
  553. m_log.ErrorFormat("{0} 2D error: {1} - terrain may be damaged",
  554. LogHeader, e.Message);
  555. return;
  556. }
  557. ClearTaint();
  558. m_log.DebugFormat("{0} V2D Heightmap size=<{1},{2}>. Region size=<{3},{4}>",
  559. LogHeader, hmSizeX, hmSizeY, SizeX, SizeY);
  560. }
  561. // as above but Gzip compressed
  562. public void FromCompressedTerrainSerializationV2DGZip(byte[] pBlob)
  563. {
  564. m_log.InfoFormat("{0} VD2Gzip {1} bytes input",
  565. LogHeader, pBlob.Length);
  566. Int32 hmSizeX, hmSizeY;
  567. try
  568. {
  569. using (MemoryStream outputStream = new MemoryStream())
  570. {
  571. using (MemoryStream inputStream = new MemoryStream(pBlob))
  572. {
  573. using (GZipStream decompressionStream = new GZipStream(inputStream, CompressionMode.Decompress))
  574. {
  575. decompressionStream.Flush();
  576. decompressionStream.CopyTo(outputStream);
  577. }
  578. }
  579. outputStream.Seek(0, SeekOrigin.Begin);
  580. using (BinaryReader br = new BinaryReader(outputStream))
  581. {
  582. hmSizeX = br.ReadInt32();
  583. hmSizeY = br.ReadInt32();
  584. // In case database info doesn't match real terrain size, initialize the whole terrain.
  585. ClearLand();
  586. for (int yy = 0; yy < hmSizeY; yy++)
  587. {
  588. for (int xx = 0; xx < hmSizeX; xx++)
  589. {
  590. float val = br.ReadSingle();
  591. if (xx < SizeX && yy < SizeY)
  592. m_heightmap[xx, yy] = val;
  593. }
  594. }
  595. }
  596. }
  597. }
  598. catch( Exception e)
  599. {
  600. ClearTaint();
  601. m_log.ErrorFormat("{0} V2DGzip error: {1} - terrain may be damaged",
  602. LogHeader, e.Message);
  603. return;
  604. }
  605. ClearTaint();
  606. m_log.DebugFormat("{0} V2DGzip. Heightmap size=<{1},{2}>. Region size=<{3},{4}>",
  607. LogHeader, hmSizeX, hmSizeY, SizeX, SizeY);
  608. }
  609. }
  610. }