aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/TerrainData.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework/TerrainData.cs')
-rw-r--r--OpenSim/Framework/TerrainData.cs260
1 files changed, 260 insertions, 0 deletions
diff --git a/OpenSim/Framework/TerrainData.cs b/OpenSim/Framework/TerrainData.cs
new file mode 100644
index 0000000..103359b
--- /dev/null
+++ b/OpenSim/Framework/TerrainData.cs
@@ -0,0 +1,260 @@
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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Reflection;
32
33using OpenMetaverse;
34
35using log4net;
36
37namespace OpenSim.Framework
38{
39 public abstract class TerrainData
40 {
41 // Terrain always is a square
42 public int SizeX { get; protected set; }
43 public int SizeY { get; protected set; }
44 public int SizeZ { get; protected set; }
45
46 public abstract float this[int x, int y] { get; set; }
47 // Someday terrain will have caves
48 public abstract float this[int x, int y, int z] { get; set; }
49
50 public bool IsTainted { get; protected set; }
51 public abstract bool IsTaintedAt(int xx, int yy);
52 public abstract void ClearTaint();
53
54 // Return a representation of this terrain for storing as a blob in the database.
55 // Returns 'true' to say blob was stored in the 'out' locations.
56 public abstract bool GetDatabaseBlob(out int DBFormatRevisionCode, out Array blob);
57
58 // return a special compressed representation of the heightmap in shorts
59 public abstract short[] GetCompressedMap();
60 public abstract float CompressionFactor { get; }
61 public abstract void SetCompressedMap(short[] cmap, float pCompressionFactor);
62
63 public abstract TerrainData Clone();
64 }
65
66 // The terrain is stored in the database as a blob with a 'revision' field.
67 // Some implementations of terrain storage would fill the revision field with
68 // the time the terrain was stored. When real revisions were added and this
69 // feature removed, that left some old entries with the time in the revision
70 // field.
71 // Thus, if revision is greater than 'RevisionHigh' then terrain db entry is
72 // left over and it is presumed to be 'Legacy256'.
73 // Numbers are arbitrary and are chosen to to reduce possible mis-interpretation.
74 // If a revision does not match any of these, it is assumed to be Legacy256.
75 public enum DBTerrainRevision
76 {
77 // Terrain is 'double[256,256]'
78 Legacy256 = 11,
79 // Terrain is 'int32, int32, float[,]' where the shorts are X and Y dimensions
80 // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256.
81 Variable2D = 22,
82 // A revision that is not listed above or any revision greater than this value is 'Legacy256'.
83 RevisionHigh = 1234
84 }
85
86 // Version of terrain that is a heightmap.
87 // This should really be 'LLOptimizedHeightmapTerrainData' as it includes knowledge
88 // of 'patches' which are 16x16 terrain areas which can be sent separately to the viewer.
89 // The heighmap is kept as an array of short integers. The integer values are converted to
90 // and from floats by TerrainCompressionFactor. Shorts are used to limit storage used.
91 public class HeightmapTerrainData : TerrainData
92 {
93 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
94 private static string LogHeader = "[TERRAIN DATA]";
95
96 // TerrainData.this[x, y]
97 public override float this[int x, int y]
98 {
99 get { return FromCompressedHeight(m_heightmap[x, y]); }
100 set {
101 short newVal = ToCompressedHeight(value);
102 if (m_heightmap[x, y] != newVal)
103 {
104 m_heightmap[x, y] = newVal;
105 m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize] = true;
106 m_log.DebugFormat("{0} set[{1},{2}] to {3} ({4})", LogHeader, x, y, value, newVal);
107 }
108 }
109 }
110
111 // TerrainData.this[x, y, z]
112 public override float this[int x, int y, int z]
113 {
114 get { return this[x, y]; }
115 set { this[x, y] = value; }
116 }
117
118 // TerrainData.ClearTaint
119 public override void ClearTaint()
120 {
121 IsTainted = false;
122 for (int ii = 0; ii < m_taint.GetLength(0); ii++)
123 for (int jj = 0; jj < m_taint.GetLength(1); jj++)
124 m_taint[ii, jj] = false;
125 }
126
127 public override bool IsTaintedAt(int xx, int yy)
128 {
129 return m_taint[xx / Constants.TerrainPatchSize, yy / Constants.TerrainPatchSize];
130 }
131
132 // TerrainData.GetDatabaseBlob
133 // The user wants something to store in the database.
134 public override bool GetDatabaseBlob(out int DBRevisionCode, out Array blob)
135 {
136 DBRevisionCode = (int)DBTerrainRevision.Legacy256;
137 blob = LegacyTerrainSerialization();
138 return false;
139 }
140
141 // TerrainData.CompressionFactor
142 private float m_compressionFactor = 100.0f;
143 public override float CompressionFactor { get { return m_compressionFactor; } }
144
145 // TerrainData.GetCompressedMap
146 public override short[] GetCompressedMap()
147 {
148 short[] newMap = new short[SizeX * SizeY];
149
150 int ind = 0;
151 for (int xx = 0; xx < SizeX; xx++)
152 for (int yy = 0; yy < SizeY; yy++)
153 newMap[ind++] = m_heightmap[xx, yy];
154
155 return newMap;
156
157 }
158 // TerrainData.SetCompressedMap
159 public override void SetCompressedMap(short[] cmap, float pCompressionFactor)
160 {
161 m_compressionFactor = pCompressionFactor;
162
163 int ind = 0;
164 for (int xx = 0; xx < SizeX; xx++)
165 for (int yy = 0; yy < SizeY; yy++)
166 m_heightmap[xx, yy] = cmap[ind++];
167 }
168
169 // TerrainData.Clone
170 public override TerrainData Clone()
171 {
172 HeightmapTerrainData ret = new HeightmapTerrainData(SizeX, SizeY, SizeZ);
173 ret.m_heightmap = (short[,])this.m_heightmap.Clone();
174 return ret;
175 }
176
177 // =============================================================
178
179 private short[,] m_heightmap;
180 // Remember subregions of the heightmap that has changed.
181 private bool[,] m_taint;
182
183 // To save space (especially for large regions), keep the height as a short integer
184 // that is coded as the float height times the compression factor (usually '100'
185 // to make for two decimal points).
186 public short ToCompressedHeight(double pHeight)
187 {
188 return (short)(pHeight * CompressionFactor);
189 }
190
191 public float FromCompressedHeight(short pHeight)
192 {
193 return ((float)pHeight) / CompressionFactor;
194 }
195
196 // To keep with the legacy theme, create an instance of this class based on the
197 // way terrain used to be passed around.
198 public HeightmapTerrainData(double[,] pTerrain)
199 {
200 SizeX = pTerrain.GetLength(0);
201 SizeY = pTerrain.GetLength(1);
202 SizeZ = (int)Constants.RegionHeight;
203 m_compressionFactor = 100.0f;
204
205 m_heightmap = new short[SizeX, SizeY];
206 for (int ii = 0; ii < SizeX; ii++)
207 {
208 for (int jj = 0; jj < SizeY; jj++)
209 {
210 m_heightmap[ii, jj] = ToCompressedHeight(pTerrain[ii, jj]);
211
212 }
213 }
214
215 m_taint = new bool[SizeX / Constants.TerrainPatchSize, SizeY / Constants.TerrainPatchSize];
216 ClearTaint();
217 }
218
219 // Create underlying structures but don't initialize the heightmap assuming the caller will immediately do that
220 public HeightmapTerrainData(int pX, int pY, int pZ)
221 {
222 SizeX = pX;
223 SizeY = pY;
224 SizeZ = pZ;
225 m_compressionFactor = 100.0f;
226 m_heightmap = new short[SizeX, SizeY];
227 m_taint = new bool[SizeX / Constants.TerrainPatchSize, SizeY / Constants.TerrainPatchSize];
228 ClearTaint();
229 }
230
231 public HeightmapTerrainData(short[] cmap, float pCompressionFactor, int pX, int pY, int pZ) : this(pX, pY, pZ)
232 {
233 SetCompressedMap(cmap, pCompressionFactor);
234 }
235
236
237 // Just create an array of doubles. Presumes the caller implicitly knows the size.
238 public Array LegacyTerrainSerialization()
239 {
240 Array ret = null;
241 using (MemoryStream str = new MemoryStream(SizeX * SizeY * sizeof(double)))
242 {
243 using (BinaryWriter bw = new BinaryWriter(str))
244 {
245 // TODO: COMPATIBILITY - Add byte-order conversions
246 for (int ii = 0; ii < SizeX; ii++)
247 for (int jj = 0; jj < SizeY; jj++)
248 {
249 double height = this[ii, jj];
250 if (height == 0.0)
251 height = double.Epsilon;
252 bw.Write(height);
253 }
254 }
255 ret = str.ToArray();
256 }
257 return ret;
258 }
259 }
260}