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

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

fixes

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