1 /**
2 Copyright: Copyright (c) 2015-2016 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module voxelman.storage.chunkstorage;
7 
8 import dlib.math.vector : vec3, ivec3;
9 import voxelman.core.block;
10 import voxelman.storage.chunk;
11 import voxelman.storage.coordinates;
12 
13 struct ChunkRemoveQueue
14 {
15 	Chunk* first; // head of slist. Follow 'next' pointer in chunk
16 	size_t length;
17 
18 	void add(Chunk* chunk)
19 	{
20 		assert(chunk);
21 		assert(chunk !is null);
22 
23 		// already queued
24 		if (chunk.isMarkedForDeletion) return;
25 
26 		chunk.isLoaded = false;
27 		chunk.next = first;
28 		if (first) first.prev = chunk;
29 		first = chunk;
30 		++length;
31 	}
32 
33 	void remove(Chunk* chunk)
34 	{
35 		assert(chunk);
36 		assert(chunk !is null);
37 
38 		if (chunk.prev)
39 			chunk.prev.next = chunk.next;
40 		else
41 			first = chunk.next;
42 
43 		if (chunk.next)
44 			chunk.next.prev = chunk.prev;
45 
46 		chunk.next = null;
47 		chunk.prev = null;
48 		--length;
49 	}
50 
51 	void process(void delegate(Chunk* chunk) chunkRemoveCallback)
52 	{
53 		Chunk* chunk = first;
54 
55 		while(chunk)
56 		{
57 			assert(chunk !is null);
58 
59 			if (!chunk.isUsed)
60 			{
61 				auto toRemove = chunk;
62 				chunk = chunk.next;
63 
64 				remove(toRemove);
65 				chunkRemoveCallback(toRemove);
66 			}
67 			else
68 			{
69 				auto c = chunk;
70 				chunk = chunk.next;
71 			}
72 		}
73 	}
74 }
75 
76 ///
77 struct ChunkStorage
78 {
79 	Chunk*[ChunkWorldPos] chunks;
80 	ChunkRemoveQueue removeQueue;
81 	void delegate(Chunk* chunk)[] onChunkAddedHandlers;
82 	void delegate(Chunk* chunk)[] onChunkRemovedHandlers;
83 
84 	Chunk* getChunk(ChunkWorldPos position)
85 	{
86 		return chunks.get(position, null);
87 	}
88 
89 	void update()
90 	{
91 		removeQueue.process(&removeChunk);
92 	}
93 
94 	private Chunk* createEmptyChunk(ChunkWorldPos position)
95 	{
96 		return new Chunk(position);
97 	}
98 
99 	bool loadChunk(ChunkWorldPos position)
100 	{
101 		if (auto chunk = chunks.get(position, null))
102 		{
103 			if (chunk.isMarkedForDeletion)
104 				removeQueue.remove(chunk);
105 			return chunk.isLoaded;
106 		}
107 
108 		Chunk* chunk = createEmptyChunk(position);
109 		addChunk(chunk);
110 
111 		return false;
112 	}
113 
114 	// Add already created chunk to storage
115 	// Sets up all adjacent
116 	private void addChunk(Chunk* emptyChunk)
117 	{
118 		assert(emptyChunk);
119 		chunks[emptyChunk.position] = emptyChunk;
120 		ChunkWorldPos position = emptyChunk.position;
121 
122 		void attachAdjacent(ubyte side)()
123 		{
124 			byte[3] offset = sideOffsets[side];
125 			ChunkWorldPos otherPosition = ivec3(cast(int)(position.x + offset[0]),
126 												cast(int)(position.y + offset[1]),
127 												cast(int)(position.z + offset[2]));
128 			Chunk* other = getChunk(otherPosition);
129 
130 			if (other !is null)
131 				other.adjacent[oppSide[side]] = emptyChunk;
132 			emptyChunk.adjacent[side] = other;
133 		}
134 
135 		// Attach all adjacent
136 		attachAdjacent!(0)();
137 		attachAdjacent!(1)();
138 		attachAdjacent!(2)();
139 		attachAdjacent!(3)();
140 		attachAdjacent!(4)();
141 		attachAdjacent!(5)();
142 
143 		foreach(handler; onChunkAddedHandlers)
144 			handler(emptyChunk);
145 	}
146 
147 	void removeChunk(Chunk* chunk)
148 	{
149 		assert(chunk);
150 		assert(!chunk.isUsed);
151 
152 		void detachAdjacent(ubyte side)()
153 		{
154 			if (chunk.adjacent[side] !is null)
155 			{
156 				chunk.adjacent[side].adjacent[oppSide[side]] = null;
157 			}
158 			chunk.adjacent[side] = null;
159 		}
160 
161 		// Detach all adjacent
162 		detachAdjacent!(0)();
163 		detachAdjacent!(1)();
164 		detachAdjacent!(2)();
165 		detachAdjacent!(3)();
166 		detachAdjacent!(4)();
167 		detachAdjacent!(5)();
168 
169 		chunks.remove(chunk.position);
170 		foreach(handler; onChunkRemovedHandlers)
171 			handler(chunk);
172 
173 		if (chunk.mesh)
174 			chunk.mesh.free();
175 		delete chunk.mesh;
176 		delete chunk;
177 	}
178 }