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

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

fixes

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