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

Last change on this file since 144 was 144, checked in by alloc, 6 years ago

Fixes

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