source: binary-improvements/7dtd-server-fixes/src/MapRendering/MapRendering.cs@ 196

Last change on this file since 196 was 192, checked in by alloc, 10 years ago

fixes

File size: 10.9 KB
Line 
1using AllocsFixes.JSON;
2using System;
3using System.Collections.Generic;
4using System.IO;
5using System.Text;
6using System.Threading;
7using UnityEngine;
8
9namespace AllocsFixes.MapRendering
10{
11 public class MapRendering
12 {
13 private static MapRendering instance;
14
15 public static MapRendering Instance {
16 get {
17 if (instance == null) {
18 instance = new MapRendering ();
19 }
20 return instance;
21 }
22 }
23
24 private static object lockObject = new object ();
25 private MapRenderBlockBuffer[] zoomLevelBuffers;
26 private Dictionary<Vector2i, Color[]> dirtyChunks = new Dictionary<Vector2i, Color[]> ();
27 private System.Timers.Timer chunkSaveTimer = new System.Timers.Timer (500);
28 private bool renderingFullMap = false;
29 public static bool renderingEnabled = true;
30 private MicroStopwatch msw = new MicroStopwatch ();
31
32 private MapRendering ()
33 {
34 Constants.MAP_DIRECTORY = StaticDirectories.GetSaveGameDir () + "/map";
35
36 lock (lockObject) {
37 if (!LoadMapInfo ())
38 WriteMapInfo ();
39 }
40
41 zoomLevelBuffers = new MapRenderBlockBuffer[Constants.ZOOMLEVELS];
42 for (int i = 0; i < Constants.ZOOMLEVELS; i++) {
43 zoomLevelBuffers [i] = new MapRenderBlockBuffer (i);
44 }
45
46 chunkSaveTimer.AutoReset = false;
47 chunkSaveTimer.Elapsed += new System.Timers.ElapsedEventHandler (TimedRendering);
48 }
49
50 public static void RenderSingleChunk (Chunk chunk)
51 {
52 if (renderingEnabled) {
53 ThreadPool.QueueUserWorkItem ((o) =>
54 {
55 try {
56 if (!Instance.renderingFullMap) {
57 lock (lockObject) {
58 Chunk c = (Chunk)o;
59 Vector3i cPos = c.GetWorldPos ();
60 Vector2i cPos2 = new Vector2i (cPos.x / Constants.MAP_CHUNK_SIZE, cPos.z / Constants.MAP_CHUNK_SIZE);
61
62 ushort[] mapColors = c.GetMapColors ();
63 if (mapColors != null) {
64 Color[] realColors = new Color[Constants.MAP_CHUNK_SIZE * Constants.MAP_CHUNK_SIZE];
65 for (int i_colors = 0; i_colors < mapColors.Length; i_colors++) {
66 realColors [i_colors] = shortColorToColor (mapColors [i_colors]);
67 }
68 Instance.dirtyChunks [cPos2] = realColors;
69 //Log.Out ("Add Dirty: " + cPos2);
70 Instance.chunkSaveTimer.Stop ();
71 Instance.chunkSaveTimer.Start ();
72 }
73 }
74 }
75 } catch (Exception e) {
76 Log.Out ("Exception in MapRendering.RenderSingleChunk(): " + e);
77 }
78 }, chunk);
79 }
80 }
81
82 public void RenderFullMap ()
83 {
84 MicroStopwatch microStopwatch = new MicroStopwatch ();
85
86 string regionSaveDir = StaticDirectories.GetSaveGameRegionDir ();
87 RegionFileManager rfm = new RegionFileManager (regionSaveDir, regionSaveDir, 0, false);
88 Texture2D fullMapTexture = null;
89
90 Vector2i minChunk = default(Vector2i), maxChunk = default(Vector2i);
91 Vector2i minPos = default(Vector2i), maxPos = default(Vector2i);
92 int widthChunks, heightChunks, widthPix, heightPix;
93 getWorldExtent (rfm, out minChunk, out maxChunk, out minPos, out maxPos, out widthChunks, out heightChunks, out widthPix, out heightPix);
94
95 Log.Out (String.Format ("RenderMap: min: {0}, max: {1}, minPos: {2}, maxPos: {3}, w/h: {4}/{5}, wP/hP: {6}/{7}",
96 minChunk.ToString (), maxChunk.ToString (),
97 minPos.ToString (), maxPos.ToString (),
98 widthChunks, heightChunks,
99 widthPix, heightPix)
100 );
101
102 lock (lockObject) {
103 for (int i = 0; i < Constants.ZOOMLEVELS; i++) {
104 zoomLevelBuffers [i].ResetBlock ();
105 }
106
107 if (Directory.Exists (Constants.MAP_DIRECTORY)) {
108 Directory.Delete (Constants.MAP_DIRECTORY, true);
109 }
110 WriteMapInfo ();
111
112 renderingFullMap = true;
113
114 if (widthPix <= 8000 && heightPix <= 8000)
115 fullMapTexture = new Texture2D (widthPix, heightPix);
116
117 Vector2i curFullMapPos = default(Vector2i);
118 Vector2i curChunkPos = default(Vector2i);
119 for (curFullMapPos.x = 0; curFullMapPos.x < widthPix; curFullMapPos.x += Constants.MAP_CHUNK_SIZE) {
120 for (curFullMapPos.y = 0; curFullMapPos.y < heightPix; curFullMapPos.y += Constants.MAP_CHUNK_SIZE) {
121 curChunkPos.x = (curFullMapPos.x / Constants.MAP_CHUNK_SIZE) + minChunk.x;
122 curChunkPos.y = (curFullMapPos.y / Constants.MAP_CHUNK_SIZE) + minChunk.y;
123
124 try {
125 long chunkKey = WorldChunkCache.MakeChunkKey (curChunkPos.x, curChunkPos.y);
126 if (rfm.ContainsChunkSync (chunkKey)) {
127 Chunk c = rfm.GetChunkSync (chunkKey);
128 ushort[] mapColors = c.GetMapColors ();
129 if (mapColors != null) {
130 Color[] realColors = new Color[Constants.MAP_CHUNK_SIZE * Constants.MAP_CHUNK_SIZE];
131 for (int i_colors = 0; i_colors < mapColors.Length; i_colors++) {
132 realColors [i_colors] = shortColorToColor (mapColors [i_colors]);
133 }
134 dirtyChunks [curChunkPos] = realColors;
135 if (fullMapTexture != null)
136 fullMapTexture.SetPixels (curFullMapPos.x, curFullMapPos.y, Constants.MAP_CHUNK_SIZE, Constants.MAP_CHUNK_SIZE, realColors);
137 }
138 }
139 } catch (Exception e) {
140 Log.Out ("Exception: " + e);
141 }
142 }
143
144 while (dirtyChunks.Count > 0) {
145 RenderDirtyChunks ();
146 }
147
148 Log.Out (String.Format ("RenderMap: {0}/{1} ({2}%)", curFullMapPos.x, widthPix, (int)((float)curFullMapPos.x / widthPix * 100)));
149 }
150 }
151
152 if (fullMapTexture != null) {
153 byte[] array = fullMapTexture.EncodeToPNG ();
154 File.WriteAllBytes (Constants.MAP_DIRECTORY + "/map.png", array);
155 Texture2D.Destroy (fullMapTexture);
156 fullMapTexture = null;
157 }
158
159 renderingFullMap = false;
160
161 Log.Out ("Generating map took: " + microStopwatch.ElapsedMilliseconds + " ms");
162 Log.Out ("World extent: " + minPos + " - " + maxPos);
163 }
164
165 private void SaveAllBlockMaps (object source, System.Timers.ElapsedEventArgs e)
166 {
167 for (int i = 0; i < Constants.ZOOMLEVELS; i++) {
168 zoomLevelBuffers [i].SaveBlock ();
169 }
170 }
171
172 private void TimedRendering (object source, System.Timers.ElapsedEventArgs e)
173 {
174 lock (lockObject) {
175 RenderDirtyChunks ();
176 if (dirtyChunks.Count > 0)
177 Instance.chunkSaveTimer.Start ();
178 }
179 }
180
181 private void RenderDirtyChunks ()
182 {
183 msw.ResetAndRestart ();
184
185 if (dirtyChunks.Count > 0) {
186 List<Vector2i> keys = new List<Vector2i> (dirtyChunks.Keys);
187 List<Vector2i> chunksDone = new List<Vector2i> ();
188
189 Vector2i chunkPos = keys [0];
190 chunksDone.Add (chunkPos);
191
192 //Log.Out ("Start Dirty: " + chunkPos);
193
194 Vector2i block = default(Vector2i), blockOffset = default(Vector2i);
195 getBlockNumber (chunkPos, out block, out blockOffset, Constants.MAP_BLOCK_TO_CHUNK_DIV, Constants.MAP_CHUNK_SIZE);
196
197 zoomLevelBuffers [Constants.ZOOMLEVELS - 1].LoadBlock (block);
198
199 Vector2i v_block = default(Vector2i), v_blockOffset = default(Vector2i);
200 foreach (Vector2i v in keys) {
201 getBlockNumber (v, out v_block, out v_blockOffset, Constants.MAP_BLOCK_TO_CHUNK_DIV, Constants.MAP_CHUNK_SIZE);
202 if (v_block.Equals (block)) {
203 //Log.Out ("Dirty: " + v + " render: true");
204 chunksDone.Add (v);
205 zoomLevelBuffers [Constants.ZOOMLEVELS - 1].SetPart (v_blockOffset, Constants.MAP_CHUNK_SIZE, dirtyChunks [v]);
206 } else {
207 //Log.Out ("Dirty: " + v + " render: false");
208 }
209 }
210
211 foreach (Vector2i v in chunksDone)
212 dirtyChunks.Remove (v);
213
214 RenderZoomLevel (Constants.ZOOMLEVELS - 1, block);
215
216 SaveAllBlockMaps (null, null);
217 }
218 }
219
220 private void RenderZoomLevel (int level, Vector2i innerBlock)
221 {
222 if (level > 0) {
223 Vector2i block = default(Vector2i), blockOffset = default(Vector2i);
224 getBlockNumber (innerBlock, out block, out blockOffset, 2, Constants.MAP_BLOCK_SIZE / 2);
225
226 zoomLevelBuffers [level - 1].LoadBlock (block);
227 zoomLevelBuffers [level - 1].SetPart (blockOffset, Constants.MAP_BLOCK_SIZE / 2, zoomLevelBuffers [level].GetHalfScaled ());
228
229 RenderZoomLevel (level - 1, block);
230 }
231 }
232
233 private void getBlockNumber (Vector2i innerPos, out Vector2i block, out Vector2i blockOffset, int scaleFactor, int offsetSize)
234 {
235 block = default(Vector2i);
236 blockOffset = default(Vector2i);
237 block.x = ((innerPos.x + 16777216) / scaleFactor) - (16777216 / scaleFactor);
238 block.y = ((innerPos.y + 16777216) / scaleFactor) - (16777216 / scaleFactor);
239 blockOffset.x = ((innerPos.x + 16777216) % scaleFactor) * offsetSize;
240 blockOffset.y = ((innerPos.y + 16777216) % scaleFactor) * offsetSize;
241 }
242
243 private void WriteMapInfo ()
244 {
245 JSONObject mapInfo = new JSONObject ();
246 mapInfo.Add ("blockSize", new JSONNumber (Constants.MAP_BLOCK_SIZE));
247 mapInfo.Add ("maxZoom", new JSONNumber (Constants.ZOOMLEVELS - 1));
248
249 Directory.CreateDirectory (Constants.MAP_DIRECTORY);
250 File.WriteAllText (Constants.MAP_DIRECTORY + "/mapinfo.json", mapInfo.ToString (), Encoding.UTF8);
251 }
252
253 private bool LoadMapInfo ()
254 {
255 if (File.Exists (Constants.MAP_DIRECTORY + "/mapinfo.json")) {
256 string json = File.ReadAllText (Constants.MAP_DIRECTORY + "/mapinfo.json", Encoding.UTF8);
257 try {
258 JSONNode node = Parser.Parse (json);
259 if (node is JSONObject) {
260 JSONObject jo = (JSONObject)node;
261 if (jo.ContainsKey ("blockSize"))
262 Constants.MAP_BLOCK_SIZE = ((JSONNumber)jo ["blockSize"]).GetInt ();
263 if (jo.ContainsKey ("maxZoom"))
264 Constants.ZOOMLEVELS = ((JSONNumber)jo ["maxZoom"]).GetInt () + 1;
265 return true;
266 }
267 } catch (MalformedJSONException e) {
268 Log.Out ("Exception in LoadMapInfo: " + e);
269 } catch (InvalidCastException e) {
270 Log.Out ("Exception in LoadMapInfo: " + e);
271 }
272 }
273 return false;
274 }
275
276 private void getWorldExtent (RegionFileManager rfm, out Vector2i minChunk, out Vector2i maxChunk,
277 out Vector2i minPos, out Vector2i maxPos,
278 out int widthChunks, out int heightChunks,
279 out int widthPix, out int heightPix)
280 {
281 minChunk = default(Vector2i);
282 maxChunk = default(Vector2i);
283 minPos = default(Vector2i);
284 maxPos = default(Vector2i);
285
286 long[] keys = rfm.GetAllChunkKeys ();
287 int minX = Int32.MaxValue;
288 int minY = Int32.MaxValue;
289 int maxX = Int32.MinValue;
290 int maxY = Int32.MinValue;
291 foreach (long key in keys) {
292 int x = WorldChunkCache.extractX (key);
293 int y = WorldChunkCache.extractZ (key);
294
295 if (x < minX)
296 minX = x;
297 if (x > maxX)
298 maxX = x;
299 if (y < minY)
300 minY = y;
301 if (y > maxY)
302 maxY = y;
303 }
304
305 minChunk.x = minX;
306 minChunk.y = minY;
307
308 maxChunk.x = maxX;
309 maxChunk.y = maxY;
310
311 minPos.x = minX * Constants.MAP_CHUNK_SIZE;
312 minPos.y = minY * Constants.MAP_CHUNK_SIZE;
313
314 maxPos.x = maxX * Constants.MAP_CHUNK_SIZE;
315 maxPos.y = maxY * Constants.MAP_CHUNK_SIZE;
316
317 widthChunks = maxX - minX + 1;
318 heightChunks = maxY - minY + 1;
319
320 widthPix = widthChunks * Constants.MAP_CHUNK_SIZE;
321 heightPix = heightChunks * Constants.MAP_CHUNK_SIZE;
322 }
323
324 private static Color shortColorToColor (ushort col)
325 {
326 return new Color (((float)(col >> 10 & 31) / 31f), ((float)(col >> 5 & 31) / 31f), ((float)(col & 31) / 31f), 255);
327 }
328
329 }
330}
Note: See TracBrowser for help on using the repository browser.