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