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

Last change on this file since 151 was 151, checked in by alloc, 7 years ago

Fixes

File size: 9.7 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 = default(Vector2i), maxChunk = default(Vector2i);
90                        Vector2i minPos = default(Vector2i), maxPos = default(Vector2i);
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 = default(Vector2i);
107                                Vector2i curChunkPos = default(Vector2i);
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 = default(Vector2i), blockOffset = default(Vector2i);
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 = default(Vector2i), v_blockOffset = default(Vector2i);
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 = default(Vector2i), blockOffset = default(Vector2i);
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 = default(Vector2i);
230                        blockOffset = default(Vector2i);
231                        block.x = ((innerPos.x + 16777216) / scaleFactor) - (16777216 / scaleFactor);
232                        block.y = ((innerPos.y + 16777216) / scaleFactor) - (16777216 / scaleFactor);
233                        blockOffset.x = ((innerPos.x + 16777216) % scaleFactor) * offsetSize;
234                        blockOffset.y = ((innerPos.y + 16777216) % scaleFactor) * offsetSize;
235                }
236
237                private void getWorldExtent (RegionFileManager rfm, out Vector2i minChunk, out Vector2i maxChunk,
238                                            out Vector2i minPos, out Vector2i maxPos,
239                                            out int widthChunks, out int heightChunks,
240                                            out int widthPix, out int heightPix)
241                {
242                        minChunk = default(Vector2i);
243                        maxChunk = default(Vector2i);
244                        minPos = default(Vector2i);
245                        maxPos = default(Vector2i);
246
247                        long[] keys = rfm.GetAllChunkKeys ();
248                        int minX = Int32.MaxValue;
249                        int minY = Int32.MaxValue;
250                        int maxX = Int32.MinValue;
251                        int maxY = Int32.MinValue;
252                        foreach (long key in keys) {
253                                int x = WorldChunkCache.extractX (key);
254                                int y = WorldChunkCache.extractZ (key);
255
256                                if (x < minX)
257                                        minX = x;
258                                if (x > maxX)
259                                        maxX = x;
260                                if (y < minY)
261                                        minY = y;
262                                if (y > maxY)
263                                        maxY = y;
264                        }
265
266                        minChunk.x = minX;
267                        minChunk.y = minY;
268
269                        maxChunk.x = maxX;
270                        maxChunk.y = maxY;
271
272                        minPos.x = minX * Constants.MAP_CHUNK_SIZE;
273                        minPos.y = minY * Constants.MAP_CHUNK_SIZE;
274
275                        maxPos.x = maxX * Constants.MAP_CHUNK_SIZE;
276                        maxPos.y = maxY * Constants.MAP_CHUNK_SIZE;
277
278                        widthChunks = maxX - minX + 1;
279                        heightChunks = maxY - minY + 1;
280
281                        widthPix = widthChunks * Constants.MAP_CHUNK_SIZE;
282                        heightPix = heightChunks * Constants.MAP_CHUNK_SIZE;
283                }
284
285                private static Color shortColorToColor (ushort col)
286                {
287                        return new Color (((float)(col >> 10 & 31) / 31f), ((float)(col >> 5 & 31) / 31f), ((float)(col & 31) / 31f), 255);
288                }
289
290        }
291}
Note: See TracBrowser for help on using the repository browser.