1 /**
2 Copyright: Copyright (c) 2013-2016 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module voxelman.client.chunkman;
7 
8 import std.experimental.logger;
9 import std.datetime : StopWatch, Duration;
10 
11 import dlib.math.vector : vec3, ivec3;
12 
13 import voxelman.client.chunkmeshman;
14 import voxelman.core.block;
15 import voxelman.core.blockman;
16 import voxelman.core.config;
17 import voxelman.storage.chunk;
18 import voxelman.storage.chunkstorage;
19 import voxelman.storage.coordinates;
20 import voxelman.storage.utils;
21 import voxelman.storage.volume;
22 
23 ///
24 struct ChunkMan
25 {
26 	ChunkStorage chunkStorage;
27 	alias chunkStorage this;
28 
29 	// Stats
30 	size_t totalLoadedChunks;
31 
32 	Volume visibleVolume;
33 	ChunkWorldPos observerPosition;
34 	int viewRadius = DEFAULT_VIEW_RADIUS;
35 
36 	BlockMan blockMan;
37 	ChunkMeshMan chunkMeshMan;
38 
39 	void init(uint numWorkers)
40 	{
41 		blockMan.loadBlockTypes();
42 		chunkMeshMan.init(&this, &blockMan, numWorkers);
43 		chunkStorage.onChunkRemovedHandlers ~= &chunkMeshMan.onChunkRemoved;
44 	}
45 
46 	void stop()
47 	{
48 		info("unloading chunks");
49 
50 		foreach(chunk; chunkStorage.chunks.byValue)
51 			chunkStorage.removeQueue.add(chunk);
52 
53 		while(chunkStorage.chunks.length > 0)
54 		{
55 			chunkStorage.update();
56 		}
57 
58 		chunkMeshMan.stop();
59 	}
60 
61 	void update()
62 	{
63 		chunkMeshMan.update();
64 		chunkStorage.update();
65 	}
66 
67 	void onChunkLoaded(ChunkWorldPos chunkPos, BlockData blockData)
68 	{
69 		Chunk* chunk = chunkStorage.getChunk(chunkPos);
70 
71 		// We can receive data for chunk that is already deleted.
72 		if (chunk is null || chunk.isMarkedForDeletion)
73 		{
74 			blockData.deleteBlocks();
75 			return;
76 		}
77 
78 		chunkMeshMan.onChunkLoaded(chunk, blockData);
79 	}
80 
81 	void onChunkChanged(Chunk* chunk, BlockChange[] changes)
82 	{
83 		chunkMeshMan.onChunkChanged(chunk, changes);
84 	}
85 
86 	Volume calcVolume(ChunkWorldPos position)
87 	{
88 		auto size = viewRadius*2 + 1;
89 		return Volume(cast(ivec3)(position.vector - viewRadius),
90 			ivec3(size, size, size));
91 	}
92 
93 	void updateObserverPosition(vec3 cameraPos)
94 	{
95 		ChunkWorldPos chunkPos = BlockWorldPos(cameraPos);
96 		observerPosition = chunkPos;
97 
98 		Volume newVolume = calcVolume(chunkPos);
99 		if (newVolume == visibleVolume) return;
100 
101 		updateVisibleVolume(newVolume);
102 	}
103 
104 	void updateVisibleVolume(Volume newVolume)
105 	{
106 		auto oldVolume = visibleVolume;
107 		visibleVolume = newVolume;
108 
109 		if (oldVolume.empty)
110 		{
111 			loadVolume(newVolume);
112 			return;
113 		}
114 
115 		auto trisectResult = trisect(oldVolume, newVolume);
116 		auto chunksToRemove = trisectResult.aPositions;
117 		auto chunksToLoad = trisectResult.bPositions;
118 
119 		// remove chunks
120 		foreach(chunkPos; chunksToRemove)
121 		{
122 			chunkStorage
123 				.removeQueue
124 				.add(chunkStorage.getChunk(ChunkWorldPos(chunkPos)));
125 		}
126 
127 		// load chunks
128 		foreach(chunkPos; chunksToLoad)
129 		{
130 			chunkStorage.loadChunk(ChunkWorldPos(chunkPos));
131 		}
132 	}
133 
134 	void loadVolume(Volume volume)
135 	{
136 		import std.algorithm : each;
137 		volume.positions.each!(pos => chunkStorage.loadChunk(ChunkWorldPos(pos)));
138 	}
139 }