1 /**
2 Copyright: Copyright (c) 2013-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.block.utils;
7 
8 import voxelman.log;
9 
10 import voxelman.container.buffer;
11 import voxelman.math;
12 import voxelman.geometry.cube;
13 import voxelman.core.config;
14 import voxelman.world.storage;
15 import voxelman.utils.mapping;
16 import voxelman.world.block;
17 import voxelman.world.mesh.chunkmesh;
18 
19 
20 struct ChunkAndBlockAdjacent
21 {
22 	ubyte[27] chunks;
23 	ubyte[3][27] blocks;
24 }
25 
26 struct ChunkAndBlockAt
27 {
28 	ubyte chunk;
29 	// position of block in chunk
30 	ubyte bx, by, bz;
31 }
32 
33 // 0-5 sides, or 6 if center
34 ChunkAndBlockAt chunkAndBlockAt6(int x, int y, int z)
35 {
36 	ubyte bx = cast(ubyte)x;
37 	ubyte by = cast(ubyte)y;
38 	ubyte bz = cast(ubyte)z;
39 	if(x == -1) return ChunkAndBlockAt(CubeSide.xneg, CHUNK_SIZE-1, by, bz);
40 	else if(x == CHUNK_SIZE) return ChunkAndBlockAt(CubeSide.xpos, 0, by, bz);
41 
42 	if(y == -1) return ChunkAndBlockAt(CubeSide.yneg, bx, CHUNK_SIZE-1, bz);
43 	else if(y == CHUNK_SIZE) return ChunkAndBlockAt(CubeSide.ypos, bx, 0, bz);
44 
45 	if(z == -1) return ChunkAndBlockAt(CubeSide.zneg, bx, by, CHUNK_SIZE-1);
46 	else if(z == CHUNK_SIZE) return ChunkAndBlockAt(CubeSide.zpos, bx, by, 0);
47 
48 	return ChunkAndBlockAt(26, bx, by, bz);
49 }
50 
51 // convert -1..33 -> 0..34 to use as index
52 ubyte[34] position_in_target_chunk = [CHUNK_SIZE-1, // CHUNK_SIZE-1 is in adjacent chunk
53 	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
54 	17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, 0]; // 0 is in adjacent chunk
55 // 0 chunk in neg direction, 1 this chunk, 1 pos dir
56 ubyte[34] target_chunk =
57 [0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2];
58 
59 ChunkAndBlockAt chunkAndBlockAt27(int x, int y, int z)
60 {
61 	ubyte bx = position_in_target_chunk[x+1];
62 	ubyte by = position_in_target_chunk[y+1];
63 	ubyte bz = position_in_target_chunk[z+1];
64 
65 	ubyte cx = target_chunk[x+1];
66 	ubyte cy = target_chunk[y+1];
67 	ubyte cz = target_chunk[z+1];
68 
69 	ubyte chunk_index = cast(ubyte)(cx + cz * 3 + cy * 9);
70 
71 	return ChunkAndBlockAt(chunk_index, bx, by, bz);
72 }
73 
74 CubeSide sideFromNormal(ivec3 normal)
75 {
76 	if (normal.x == 1)
77 		return CubeSide.xpos;
78 	else if (normal.x == -1)
79 		return CubeSide.xneg;
80 
81 	if (normal.y == 1)
82 		return CubeSide.ypos;
83 	else if (normal.y == -1)
84 		return CubeSide.yneg;
85 
86 	if (normal.z == 1)
87 		return CubeSide.zpos;
88 	else if (normal.z == -1)
89 		return CubeSide.zneg;
90 
91 	return CubeSide.zneg;
92 }
93 
94 struct MeshVertex2
95 {
96 	align(4):
97 	float x, y, z;
98 	ubyte[3] color;
99 }
100 
101 ushort packColor(ubvec3 c) {
102 	return (c.r>>3) | (c.g&31) << 5 | (c.b&31) << 10;
103 }
104 ushort packColor(ubyte r, ubyte g, ubyte b) {
105 	return (r>>3) | (g&31) << 5 | (b&31) << 10;
106 }
107 
108 alias BlockUpdateHandler = void delegate(BlockWorldPos bwp);
109 struct BlockMeshingData
110 {
111 	Buffer!MeshVertex* buffer;
112 	ubyte[4] delegate(ushort blockIndex, CubeSide side) occlusionHandler;
113 	ubvec3 color;
114 	ubvec3 chunkPos;
115 	ubyte sides;
116 	ushort blockIndex;
117 	BlockMetadata metadata;
118 }
119 alias MeshHandler = void function(BlockMeshingData);
120 alias ShapeMetaHandler = BlockShape function(BlockMetadata);
121 alias RotationHandler = void function(ref BlockMetadata, ubyte, CubeSide);
122 void makeNullMesh(BlockMeshingData) {}
123 
124 alias SideSolidityHandler = Solidity function(CubeSide);
125 Solidity transparentSideSolidity(CubeSide) { return Solidity.transparent; }
126 Solidity semitransparentSideSolidity(CubeSide) { return Solidity.semiTransparent; }
127 Solidity solidSideSolidity(CubeSide) { return Solidity.solid; }
128 
129 // solidity number increases with solidity
130 enum Solidity : ubyte
131 {
132 	transparent,
133 	semiTransparent,
134 	solid,
135 }
136 
137 bool isMoreSolidThan(Solidity first, Solidity second)
138 {
139 	return first > second;
140 }
141 
142 struct BlockInfo
143 {
144 	string name;
145 	MeshHandler meshHandler = &makeNullMesh;
146 	ubvec3 color;
147 	bool isVisible = true;
148 	Solidity solidity = Solidity.solid;
149 	BlockShape shape = fullShape;
150 	ShapeMetaHandler shapeMetaHandler;
151 	RotationHandler rotationHandler;
152 	bool shapeDependsOnMeta = false;
153 	bool meshDependOnMeta = false;
154 	size_t id;
155 }
156 
157 BlockInfo entityBlock = BlockInfo("Entity", &makeColoredFullBlockMesh);
158 struct BlockInfoTable
159 {
160 	immutable(BlockInfo)[] blockInfos;
161 	SideIntersectionTable sideTable;
162 
163 	size_t length() {return blockInfos.length; }
164 
165 	BlockInfo opIndex(BlockId blockId) {
166 		if (blockId >= blockInfos.length)
167 			return entityBlock;
168 		return blockInfos[blockId];
169 	}
170 }
171 
172 struct SeparatedBlockInfoTable
173 {
174 	this(BlockInfoTable infoTable)
175 	{
176 		sideTable = infoTable.sideTable;
177 		shape.length = infoTable.length;
178 		corners.length = infoTable.length;
179 		hasGeometry.length = infoTable.length;
180 		hasInternalGeometry.length = infoTable.length;
181 		sideMasks.length = infoTable.length;
182 		color.length = infoTable.length;
183 		meshHandler.length = infoTable.length;
184 		shapeMetaHandler.length = infoTable.length;
185 		shapeDependsOnMeta.length = infoTable.length;
186 		meshDependOnMeta.length = infoTable.length;
187 
188 		foreach(i, binfo; infoTable.blockInfos)
189 		{
190 			shape[i] = binfo.shape;
191 			corners[i] = binfo.shape.corners;
192 			hasGeometry[i] = binfo.shape.hasGeometry;
193 			hasInternalGeometry[i] = binfo.shape.hasInternalGeometry;
194 			sideMasks[i] = binfo.shape.sideMasks;
195 			color[i] = binfo.color;
196 			meshHandler[i] = binfo.meshHandler;
197 			shapeMetaHandler[i] = binfo.shapeMetaHandler;
198 			shapeDependsOnMeta[i] = binfo.shapeDependsOnMeta;
199 			meshDependOnMeta[i] = binfo.meshDependOnMeta;
200 		}
201 
202 		blockInfos = infoTable.blockInfos;
203 	}
204 
205 	immutable(BlockInfo)[] blockInfos;
206 	SideIntersectionTable sideTable;
207 	BlockShape[] shape;
208 	ubyte[] corners;
209 	bool[] hasGeometry;
210 	bool[] hasInternalGeometry;
211 	bool[] shapeDependsOnMeta;
212 	bool[] meshDependOnMeta;
213 	ShapeSideMask[6][] sideMasks;
214 	ubvec3[] color;
215 	MeshHandler[] meshHandler;
216 	ShapeMetaHandler[] shapeMetaHandler;
217 }
218 
219 /// Returned when registering block.
220 /// Use this to set block properties.
221 struct BlockInfoSetter
222 {
223 	private Mapping!(BlockInfo)* mapping;
224 	private size_t blockId;
225 	private ref BlockInfo info() {return (*mapping)[blockId]; }
226 
227 	ref BlockInfoSetter meshHandler(MeshHandler val) { info.meshHandler = val; return this; }
228 	ref BlockInfoSetter color(ubyte[3] color ...) { info.color = ubvec3(color); return this; }
229 	ref BlockInfoSetter colorHex(uint hex) { info.color = ubvec3((hex>>16)&0xFF,(hex>>8)&0xFF,hex&0xFF); return this; }
230 	ref BlockInfoSetter isVisible(bool val) { info.isVisible = val; return this; }
231 	ref BlockInfoSetter solidity(Solidity val) { info.solidity = val; return this; }
232 	ref BlockInfoSetter blockShape(BlockShape val) { info.shape = val; return this; }
233 	ref BlockInfoSetter shapeMetaHandler(ShapeMetaHandler val) {
234 		info.shapeMetaHandler = val;
235 		info.shapeDependsOnMeta = true;
236 		return this;
237 	}
238 	//ref BlockInfoSetter shapeDependsOnMeta(bool val) { info.shapeDependsOnMeta = val; return this; }
239 	ref BlockInfoSetter meshDependOnMeta(bool val) { info.meshDependOnMeta = val; return this; }
240 	ref BlockInfoSetter rotationHandler(RotationHandler val) { info.rotationHandler = val; return this; }
241 }
242 
243 import voxelman.world.mesh.blockmeshers.full;
244 import voxelman.world.mesh.blockmeshers.slope;
245 
246 void regBaseBlocks(BlockInfoSetter delegate(string name) regBlock)
247 {
248 	regBlock("unknown").color(0,0,0).isVisible(false).solidity(Solidity.solid).meshHandler(&makeNullMesh).blockShape(unknownShape);
249 	regBlock("air").color(0,0,0).isVisible(false).solidity(Solidity.transparent).meshHandler(&makeNullMesh).blockShape(emptyShape);
250 	regBlock("grass").colorHex(0x01A611).meshHandler(&makeColoredFullBlockMesh);
251 	regBlock("dirt").colorHex(0x835929).meshHandler(&makeColoredFullBlockMesh);
252 	regBlock("stone").colorHex(0x8B8D7A).meshHandler(&makeColoredFullBlockMesh);
253 	regBlock("sand").colorHex(0xA68117).meshHandler(&makeColoredFullBlockMesh);
254 	regBlock("water").colorHex(0x0055AA).meshHandler(&makeColoredFullBlockMesh).solidity(Solidity.semiTransparent).blockShape(waterShape);
255 	regBlock("lava").colorHex(0xFF6920).meshHandler(&makeColoredFullBlockMesh);
256 	regBlock("snow").colorHex(0xDBECF6).meshHandler(&makeColoredFullBlockMesh);
257 	regBlock("slope").colorHex(0x857FFF).meshHandler(&makeColoredSlopeBlockMesh).shapeMetaHandler(&slopeShapeFromMeta)
258 		.meshDependOnMeta(true).rotationHandler(&slopeRotationHandler);
259 }
260 
261 void setSideTable(ref SideIntersectionTable sideTable)
262 {
263 	sideTable.set(ShapeSideMask.full, ShapeSideMask.empty);
264 	sideTable.set(ShapeSideMask.water, ShapeSideMask.empty);
265 	sideTable.set(ShapeSideMask.full, ShapeSideMask.water);
266 
267 	sideTable.set(ShapeSideMask.water, ShapeSideMask.slope0);
268 	sideTable.set(ShapeSideMask.full, ShapeSideMask.slope0);
269 	sideTable.set(ShapeSideMask.water, ShapeSideMask.slope1);
270 	sideTable.set(ShapeSideMask.full, ShapeSideMask.slope1);
271 	sideTable.set(ShapeSideMask.water, ShapeSideMask.slope2);
272 	sideTable.set(ShapeSideMask.full, ShapeSideMask.slope2);
273 	sideTable.set(ShapeSideMask.water, ShapeSideMask.slope3);
274 	sideTable.set(ShapeSideMask.full, ShapeSideMask.slope3);
275 
276 	sideTable.set(ShapeSideMask.slope0, ShapeSideMask.slope0);
277 	sideTable.set(ShapeSideMask.slope0, ShapeSideMask.slope1);
278 	sideTable.set(ShapeSideMask.slope0, ShapeSideMask.slope2);
279 	sideTable.set(ShapeSideMask.slope0, ShapeSideMask.empty);
280 	sideTable.set(ShapeSideMask.slope0, ShapeSideMask.water);
281 
282 	sideTable.set(ShapeSideMask.slope1, ShapeSideMask.slope0);
283 	sideTable.set(ShapeSideMask.slope1, ShapeSideMask.slope1);
284 	sideTable.set(ShapeSideMask.slope1, ShapeSideMask.slope3);
285 	sideTable.set(ShapeSideMask.slope1, ShapeSideMask.empty);
286 	sideTable.set(ShapeSideMask.slope1, ShapeSideMask.water);
287 
288 	sideTable.set(ShapeSideMask.slope2, ShapeSideMask.slope0);
289 	sideTable.set(ShapeSideMask.slope2, ShapeSideMask.slope2);
290 	sideTable.set(ShapeSideMask.slope2, ShapeSideMask.slope3);
291 	sideTable.set(ShapeSideMask.slope2, ShapeSideMask.empty);
292 	sideTable.set(ShapeSideMask.slope2, ShapeSideMask.water);
293 
294 	sideTable.set(ShapeSideMask.slope3, ShapeSideMask.slope1);
295 	sideTable.set(ShapeSideMask.slope3, ShapeSideMask.slope2);
296 	sideTable.set(ShapeSideMask.slope3, ShapeSideMask.slope3);
297 	sideTable.set(ShapeSideMask.slope3, ShapeSideMask.empty);
298 	sideTable.set(ShapeSideMask.slope3, ShapeSideMask.water);
299 }
300 
301 void slopeRotationHandler(ref BlockMetadata meta, ubyte rotation, CubeSide side)
302 {
303 	import voxelman.world.mesh.tables.slope : slopeSideRotations;
304 	meta = slopeSideRotations[side][rotation];
305 }