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