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

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