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

Last change on this file since 299 was 299, checked in by alloc, 3 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.