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 }