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

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

fixes

File size: 11.1 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 AllocsFixes.FileCache.MapTileCache TileCache {
34 get { return 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.QueueUserWorkItem ((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 zoomLevelBuffers [Constants.ZOOMLEVELS - 1].SetPart (v_blockOffset, Constants.MAP_CHUNK_SIZE, dirtyChunks [v]);
213 } else {
214 //Log.Out ("Dirty: " + v + " render: false");
215 }
216 }
217
218 foreach (Vector2i v in chunksDone)
219 dirtyChunks.Remove (v);
220
221 RenderZoomLevel (Constants.ZOOMLEVELS - 1, block);
222
223 SaveAllBlockMaps (null, null);
224 }
225 }
226
227 private void RenderZoomLevel (int level, Vector2i innerBlock)
228 {
229 if (level > 0) {
230 Vector2i block = default(Vector2i), blockOffset = default(Vector2i);
231 getBlockNumber (innerBlock, out block, out blockOffset, 2, Constants.MAP_BLOCK_SIZE / 2);
232
233 zoomLevelBuffers [level - 1].LoadBlock (block);
234 zoomLevelBuffers [level - 1].SetPart (blockOffset, Constants.MAP_BLOCK_SIZE / 2, zoomLevelBuffers [level].GetHalfScaled ());
235
236 RenderZoomLevel (level - 1, block);
237 }
238 }
239
240 private void getBlockNumber (Vector2i innerPos, out Vector2i block, out Vector2i blockOffset, int scaleFactor, int offsetSize)
241 {
242 block = default(Vector2i);
243 blockOffset = default(Vector2i);
244 block.x = ((innerPos.x + 16777216) / scaleFactor) - (16777216 / scaleFactor);
245 block.y = ((innerPos.y + 16777216) / scaleFactor) - (16777216 / scaleFactor);
246 blockOffset.x = ((innerPos.x + 16777216) % scaleFactor) * offsetSize;
247 blockOffset.y = ((innerPos.y + 16777216) % scaleFactor) * offsetSize;
248 }
249
250 private void WriteMapInfo ()
251 {
252 JSONObject mapInfo = new JSONObject ();
253 mapInfo.Add ("blockSize", new JSONNumber (Constants.MAP_BLOCK_SIZE));
254 mapInfo.Add ("maxZoom", new JSONNumber (Constants.ZOOMLEVELS - 1));
255
256 Directory.CreateDirectory (Constants.MAP_DIRECTORY);
257 File.WriteAllText (Constants.MAP_DIRECTORY + "/mapinfo.json", mapInfo.ToString (), Encoding.UTF8);
258 }
259
260 private bool LoadMapInfo ()
261 {
262 if (File.Exists (Constants.MAP_DIRECTORY + "/mapinfo.json")) {
263 string json = File.ReadAllText (Constants.MAP_DIRECTORY + "/mapinfo.json", Encoding.UTF8);
264 try {
265 JSONNode node = Parser.Parse (json);
266 if (node is JSONObject) {
267 JSONObject jo = (JSONObject)node;
268 if (jo.ContainsKey ("blockSize"))
269 Constants.MAP_BLOCK_SIZE = ((JSONNumber)jo ["blockSize"]).GetInt ();
270 if (jo.ContainsKey ("maxZoom"))
271 Constants.ZOOMLEVELS = ((JSONNumber)jo ["maxZoom"]).GetInt () + 1;
272 return true;
273 }
274 } catch (MalformedJSONException e) {
275 Log.Out ("Exception in LoadMapInfo: " + e);
276 } catch (InvalidCastException e) {
277 Log.Out ("Exception in LoadMapInfo: " + e);
278 }
279 }
280 return false;
281 }
282
283 private void getWorldExtent (RegionFileManager rfm, out Vector2i minChunk, out Vector2i maxChunk,
284 out Vector2i minPos, out Vector2i maxPos,
285 out int widthChunks, out int heightChunks,
286 out int widthPix, out int heightPix)
287 {
288 minChunk = default(Vector2i);
289 maxChunk = default(Vector2i);
290 minPos = default(Vector2i);
291 maxPos = default(Vector2i);
292
293 long[] keys = rfm.GetAllChunkKeys ();
294 int minX = Int32.MaxValue;
295 int minY = Int32.MaxValue;
296 int maxX = Int32.MinValue;
297 int maxY = Int32.MinValue;
298 foreach (long key in keys) {
299 int x = WorldChunkCache.extractX (key);
300 int y = WorldChunkCache.extractZ (key);
301
302 if (x < minX)
303 minX = x;
304 if (x > maxX)
305 maxX = x;
306 if (y < minY)
307 minY = y;
308 if (y > maxY)
309 maxY = y;
310 }
311
312 minChunk.x = minX;
313 minChunk.y = minY;
314
315 maxChunk.x = maxX;
316 maxChunk.y = maxY;
317
318 minPos.x = minX * Constants.MAP_CHUNK_SIZE;
319 minPos.y = minY * Constants.MAP_CHUNK_SIZE;
320
321 maxPos.x = maxX * Constants.MAP_CHUNK_SIZE;
322 maxPos.y = maxY * Constants.MAP_CHUNK_SIZE;
323
324 widthChunks = maxX - minX + 1;
325 heightChunks = maxY - minY + 1;
326
327 widthPix = widthChunks * Constants.MAP_CHUNK_SIZE;
328 heightPix = heightChunks * Constants.MAP_CHUNK_SIZE;
329 }
330
331 private static Color shortColorToColor (ushort col)
332 {
333 return new Color (((float)(col >> 10 & 31) / 31f), ((float)(col >> 5 & 31) / 31f), ((float)(col & 31) / 31f), 255);
334 }
335
336 }
337}
Note: See TracBrowser for help on using the repository browser.