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

Last change on this file since 187 was 187, checked in by alloc, 11 years ago

fixes

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