source: binary-improvements/MapRendering/MapRendering/MapRendering.cs@ 299

Last change on this file since 299 was 299, checked in by alloc, 9 years ago

Fixes

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