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

Last change on this file since 325 was 325, checked in by alloc, 14 months ago

Code style cleanup (mostly whitespace changes, enforcing braces, using cleanup)

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