1 /**
2 Copyright: Copyright (c) 2016-2017 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module voxelman.world.mesh.meshgenerator;
7 
8 import voxelman.log;
9 import std.conv : to;
10 import core.exception : Throwable;
11 
12 import voxelman.container.buffer;
13 import voxelman.math;
14 import voxelman.geometry.box;
15 import voxelman.geometry.cube;
16 
17 import voxelman.world.block;
18 import voxelman.world.blockentity;
19 import voxelman.world.mesh.chunkmesh;
20 import voxelman.world.storage.arraycopy;
21 import voxelman.core.config;
22 import voxelman.utils.worker;
23 import voxelman.world.storage;
24 
25 import voxelman.world.mesh.config;
26 import voxelman.world.mesh.utils;
27 import voxelman.world.mesh.extendedchunk;
28 
29 void genGeometry(const ref ExtendedChunk chunk,
30 	ChunkLayerItem[27] entityLayers,
31 	ChunkLayerItem[27] metadataLayers,
32 	BlockEntityInfoTable beInfos,
33 	SeparatedBlockInfoTable blockInfoTable,
34 	ref Buffer!MeshVertex[3] geometry)
35 {
36 	//const BlockInfo[] blockInfos = blockInfoTable.blockInfos;
37 	foreach (layer; entityLayers)
38 		assert(layer.type != StorageType.compressedArray, "[MESHING] Data needs to be uncompressed 2");
39 
40 	BlockEntityMap[27] maps;
41 	foreach (i, layer; entityLayers) maps[i] = getHashMapFromLayer(layer);
42 
43 	BlockEntityData getBlockEntity(ushort index, BlockEntityMap map) {
44 		ulong* entity = index in map;
45 		if (entity is null) return BlockEntityData.init;
46 		return BlockEntityData(*entity);
47 	}
48 
49 	BlockShape getEntityShape(BlockId blockId, ushort index)
50 	{
51 		ushort entityBlockIndex = blockEntityIndexFromBlockId(blockId);
52 		auto cb = chunkAndBlockAt27FromExt(index);
53 		BlockEntityData data = getBlockEntity(entityBlockIndex, maps[cb.chunk]);
54 		auto entityChunkPos = BlockChunkPos(entityBlockIndex);
55 		ivec3 blockChunkPos = ivec3(cb.bx, cb.by, cb.bz);
56 		ivec3 blockEntityPos = blockChunkPos - entityChunkPos.vector;
57 		return beInfos[data.id].blockShape(blockChunkPos, blockEntityPos, data);
58 	}
59 
60 	BlockShape getShape(BlockId blockId, ushort index)
61 	{
62 		if (isBlockEntity(blockId))
63 		{
64 			return getEntityShape(blockId, index);
65 		}
66 		else
67 		{
68 			if (blockInfoTable.shapeDependsOnMeta[blockId])
69 			{
70 				auto cb = chunkAndBlockAt27FromExt(index);
71 				auto blockMetadata = getLayerItemNoncompressed!BlockMetadata(metadataLayers[cb.chunk], BlockChunkIndex(cb.bx, cb.by, cb.bz));
72 				return blockInfoTable.shapeMetaHandler[blockId](blockMetadata);
73 			}
74 			else
75 			{
76 				return blockInfoTable.shape[blockId];
77 			}
78 		}
79 	}
80 
81 	pragma(inline, true)
82 	ShapeSideMask getSideMask(ushort index, ubyte side)
83 	{
84 		BlockId blockId = chunk.allBlocks.ptr[index];
85 		return getShape(blockId, index).sideMasks[side];
86 	}
87 
88 	pragma(inline, true)
89 	bool isSideRendered(size_t index, ubyte side, const ShapeSideMask currentMask)
90 	{
91 		return blockInfoTable.sideTable.get(currentMask, getSideMask(cast(ushort)index, side));
92 	}
93 
94 	ubyte checkSideSolidities(const ref ShapeSideMask[6] sideMasks, size_t index)
95 	{
96 		ubyte sides = 0;
97 
98 		sides |= isSideRendered(index + sideIndexOffsets[0], 1, sideMasks[0]) << 0;
99 		sides |= isSideRendered(index + sideIndexOffsets[1], 0, sideMasks[1]) << 1;
100 		sides |= isSideRendered(index + sideIndexOffsets[2], 3, sideMasks[2]) << 2;
101 		sides |= isSideRendered(index + sideIndexOffsets[3], 2, sideMasks[3]) << 3;
102 		sides |= isSideRendered(index + sideIndexOffsets[4], 5, sideMasks[4]) << 4;
103 		sides |= isSideRendered(index + sideIndexOffsets[5], 4, sideMasks[5]) << 5;
104 
105 		return sides;
106 	}
107 
108 	// assumes that block to the side has lower solidity than current block
109 	// 0--3 // corner numbering of face verticies
110 	// |\ |
111 	// 1--2
112 	pragma(inline, true)
113 	ubyte getCorners(size_t index)
114 	{
115 		BlockId blockId = chunk.allBlocks.ptr[index];
116 		return getShape(blockId, cast(ushort)index).corners;
117 	}
118 
119 	static if (AO_ENABLED)
120 	ubyte[4] calcFaceCornerOcclusions(ushort blockIndex, CubeSide side)
121 	{
122 		int index = blockIndex + sideIndexOffsets[side];
123 
124 		ubyte cornersC = getCorners(index);
125 		ubyte cornersT = getCorners(index + faceSideIndexOffset[side][0]);
126 		ubyte cornersL = getCorners(index + faceSideIndexOffset[side][1]);
127 		ubyte cornersB = getCorners(index + faceSideIndexOffset[side][2]);
128 		ubyte cornersR = getCorners(index + faceSideIndexOffset[side][3]);
129 		ubyte[16] faceCornersToAdjCorners = faceSideCorners4[side];
130 
131 		bool getCorner(ubyte corners, ubyte tableIndex)
132 		{
133 			return (corners & (1 << faceCornersToAdjCorners[tableIndex])) != 0;
134 		}
135 
136 		ubyte result0 = getCorner(cornersT, 1) | getCorner(cornersL, 3) << 1;
137 		if (result0 < 3) result0 |= getCorner(getCorners(index + faceSideIndexOffset[side][4]), 2) << 2;
138 		result0 |= getCorner(cornersC, 0) << 3;
139 
140 		ubyte result1 = getCorner(cornersL, 5) | getCorner(cornersB, 7) << 1;
141 		if (result1 < 3) result1 |= getCorner(getCorners(index + faceSideIndexOffset[side][5]), 6) << 2;
142 		result1 |= getCorner(cornersC, 4) << 3;
143 
144 		ubyte result2 = getCorner(cornersB, 9) | getCorner(cornersR, 11) << 1;
145 		if (result2 < 3) result2 |= getCorner(getCorners(index + faceSideIndexOffset[side][6]), 10) << 2;
146 		result2 |= getCorner(cornersC, 8) << 3;
147 
148 		ubyte result3 = getCorner(cornersR, 13) | getCorner(cornersT, 15) << 1;
149 		if (result3 < 3) result3 |= getCorner(getCorners(index + faceSideIndexOffset[side][7]), 14) << 2;
150 		result3 |= getCorner(cornersC, 12) << 3;
151 
152 		return [result0, result1, result2, result3];
153 	}
154 
155 	static if (!AO_ENABLED)
156 	ubyte[4] calcFaceCornerOcclusions(ushort blockIndex, CubeSide side)
157 	{
158 		return [0,0,0,0];
159 	}
160 
161 	void meshBlock(BlockId blockId, ushort index, ubvec3 bpos)
162 	{
163 		const BlockInfo binfo = blockInfoTable.blockInfos[blockId];
164 		if (binfo.isVisible)
165 		{
166 			auto shape = getShape(blockId, index);
167 			ubyte sides = checkSideSolidities(shape.sideMasks, index);
168 
169 			if ((sides != 0) || shape.hasInternalGeometry)
170 			{
171 				BlockMetadata blockMetadata;
172 				if (binfo.meshDependOnMeta)
173 				{
174 					auto cb = chunkAndBlockAt27FromExt(cast(ushort)index);
175 					blockMetadata = getLayerItemNoncompressed!BlockMetadata(metadataLayers[cb.chunk], BlockChunkIndex(cb.bx, cb.by, cb.bz));
176 				}
177 				auto data = BlockMeshingData(
178 					&geometry[binfo.solidity],
179 					&calcFaceCornerOcclusions,
180 					binfo.color,
181 					bpos,
182 					sides,
183 					index,
184 					blockMetadata);
185 				binfo.meshHandler(data);
186 			}
187 		}
188 	}
189 
190 	void meshBlockEntity(BlockId blockId, ushort index, ubvec3 bpos)
191 	{
192 		ubyte sides = checkSideSolidities(getShape(blockId, index).sideMasks, index);
193 		ushort entityBlockIndex = blockEntityIndexFromBlockId(blockId);
194 		BlockEntityData data = getBlockEntity(entityBlockIndex, maps[26]);
195 
196 		// entity chunk pos
197 		auto entityChunkPos = BlockChunkPos(entityBlockIndex);
198 
199 		ivec3 blockChunkPos = ivec3(bpos);
200 		ivec3 blockEntityPos = blockChunkPos - entityChunkPos.vector;
201 
202 		auto entityInfo = beInfos[data.id];
203 
204 		auto meshingData = BlockEntityMeshingData(
205 			geometry,
206 			&calcFaceCornerOcclusions,
207 			entityInfo.color,
208 			sides,
209 			blockChunkPos,
210 			blockEntityPos,
211 			data,
212 			index);
213 
214 		entityInfo.meshHandler(meshingData);
215 	}
216 
217 	size_t index = extendedChunkIndex(1, 1, 1);
218 
219 	foreach (ubyte y; 0..CHUNK_SIZE)
220 	{
221 		foreach (ubyte z; 0..CHUNK_SIZE)
222 		{
223 			foreach (ubyte x; 0..CHUNK_SIZE)
224 			{
225 				BlockId blockId = chunk.allBlocks.ptr[index];
226 				ubvec3 bpos = ubvec3(x, y, z);
227 
228 				if (isBlockEntity(blockId))
229 				{
230 					meshBlockEntity(blockId, cast(ushort)index, bpos);
231 				}
232 				else
233 				{
234 					meshBlock(blockId, cast(ushort)index, bpos);
235 				}
236 
237 				++index;
238 			}
239 			index +=2;
240 		}
241 		index += EXTENDED_CHUNK_SIZE*2;
242 	}
243 }
244 
245 struct SingleBlockMesher
246 {
247 	Buffer!MeshVertex geometry;
248 
249 	void meshBlock(const BlockInfo binfo, BlockMetadata blockMetadata)
250 	{
251 		ubyte[4] calcFaceCornerOcclusions(ushort blockIndex, CubeSide side)
252 		{
253 			return [0,0,0,0];
254 		}
255 
256 		auto data = BlockMeshingData(
257 			&geometry,
258 			&calcFaceCornerOcclusions,
259 			binfo.color,
260 			ubvec3(0,0,0),
261 			0b111111,
262 			0,
263 			blockMetadata);
264 		binfo.meshHandler(data);
265 	}
266 
267 	void reset()
268 	{
269 		geometry.clear();
270 	}
271 }