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

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

Fixes

File size: 9.7 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 = default(Vector2i), maxChunk = default(Vector2i);
90 Vector2i minPos = default(Vector2i), maxPos = default(Vector2i);
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 = default(Vector2i);
107 Vector2i curChunkPos = default(Vector2i);
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 = default(Vector2i), blockOffset = default(Vector2i);
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 = default(Vector2i), v_blockOffset = default(Vector2i);
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 = default(Vector2i), blockOffset = default(Vector2i);
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 = default(Vector2i);
230 blockOffset = default(Vector2i);
231 block.x = ((innerPos.x + 16777216) / scaleFactor) - (16777216 / scaleFactor);
232 block.y = ((innerPos.y + 16777216) / scaleFactor) - (16777216 / scaleFactor);
233 blockOffset.x = ((innerPos.x + 16777216) % scaleFactor) * offsetSize;
234 blockOffset.y = ((innerPos.y + 16777216) % scaleFactor) * offsetSize;
235 }
236
237 private void getWorldExtent (RegionFileManager rfm, out Vector2i minChunk, out Vector2i maxChunk,
238 out Vector2i minPos, out Vector2i maxPos,
239 out int widthChunks, out int heightChunks,
240 out int widthPix, out int heightPix)
241 {
242 minChunk = default(Vector2i);
243 maxChunk = default(Vector2i);
244 minPos = default(Vector2i);
245 maxPos = default(Vector2i);
246
247 long[] keys = rfm.GetAllChunkKeys ();
248 int minX = Int32.MaxValue;
249 int minY = Int32.MaxValue;
250 int maxX = Int32.MinValue;
251 int maxY = Int32.MinValue;
252 foreach (long key in keys) {
253 int x = WorldChunkCache.extractX (key);
254 int y = WorldChunkCache.extractZ (key);
255
256 if (x < minX)
257 minX = x;
258 if (x > maxX)
259 maxX = x;
260 if (y < minY)
261 minY = y;
262 if (y > maxY)
263 maxY = y;
264 }
265
266 minChunk.x = minX;
267 minChunk.y = minY;
268
269 maxChunk.x = maxX;
270 maxChunk.y = maxY;
271
272 minPos.x = minX * Constants.MAP_CHUNK_SIZE;
273 minPos.y = minY * Constants.MAP_CHUNK_SIZE;
274
275 maxPos.x = maxX * Constants.MAP_CHUNK_SIZE;
276 maxPos.y = maxY * Constants.MAP_CHUNK_SIZE;
277
278 widthChunks = maxX - minX + 1;
279 heightChunks = maxY - minY + 1;
280
281 widthPix = widthChunks * Constants.MAP_CHUNK_SIZE;
282 heightPix = heightChunks * Constants.MAP_CHUNK_SIZE;
283 }
284
285 private static Color shortColorToColor (ushort col)
286 {
287 return new Color (((float)(col >> 10 & 31) / 31f), ((float)(col >> 5 & 31) / 31f), ((float)(col & 31) / 31f), 255);
288 }
289
290 }
291}
Note: See TracBrowser for help on using the repository browser.