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

Last change on this file since 235 was 235, checked in by alloc, 10 years ago

Rev 87 + fixes

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