1 /**
2 Copyright: Copyright (c) 2013-2016 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module voxelman.core.meshgen;
7 
8 import std.experimental.logger;
9 import std.array : Appender;
10 import std.concurrency : Tid, send, receive;
11 import std.conv : to;
12 import std.variant : Variant;
13 import core.atomic : atomicLoad;
14 import core.exception : Throwable;
15 
16 import dlib.math.vector : ivec3;
17 
18 import voxelman.core.block;
19 import voxelman.core.config;
20 import voxelman.storage.chunk;
21 import voxelman.storage.coordinates;
22 
23 
24 struct MeshGenResult
25 {
26 	ubyte[] meshData;
27 	ChunkWorldPos position;
28 }
29 
30 void meshWorkerThread(Tid mainTid, immutable(Block*)[] blocks)
31 {
32 	try
33 	{
34 		shared(bool)* isRunning;
35 		bool isRunningLocal = true;
36 		receive( (shared(bool)* _isRunning){isRunning = _isRunning;} );
37 
38 		while (atomicLoad(*isRunning) && isRunningLocal)
39 		{
40 			receive(
41 				(shared(Chunk)* chunk)
42 				{
43 					//infof("worker: mesh chunk %s", chunk.position);
44 					chunkMeshWorker(cast(Chunk*)chunk, (cast(Chunk*)chunk).adjacent, blocks, mainTid);
45 				},
46 				(shared(Chunk)* chunk, ushort[2] changedBlocksRange)
47 				{
48 					//chunkMeshWorker(cast(Chunk*)chunk, (cast(Chunk*)chunk).adjacent, blocks, mainTid);
49 				},
50 				(Variant v){isRunningLocal = false;}
51 			);
52 		}
53 	}
54 	catch(Throwable t)
55 	{
56 		error(t.to!string, " from mesh worker");
57 		throw t;
58 	}
59 }
60 
61 void chunkMeshWorker(Chunk* chunk, Chunk*[6] adjacent, immutable(Block*)[] blocks, Tid mainThread)
62 in
63 {
64 	assert(chunk);
65 	assert(!chunk.hasWriter);
66 	foreach(a; adjacent)
67 	{
68 		assert(a !is null);
69 		assert(!a.hasWriter);
70 	}
71 }
72 body
73 {
74 	Appender!(ubyte[]) appender;
75 	ubyte bx, by, bz;
76 
77 	bool isVisibleBlock(uint id)
78 	{
79 		return blocks[id].isVisible;
80 	}
81 
82 	bool getTransparency(int tx, int ty, int tz, Side side)
83 	{
84 		ubyte x = cast(ubyte)tx;
85 		ubyte y = cast(ubyte)ty;
86 		ubyte z = cast(ubyte)tz;
87 
88 		if(tx == -1) // west
89 			return blocks[ adjacent[Side.west].getBlockType(CHUNK_SIZE-1, y, z) ].isSideTransparent(side);
90 		else if(tx == CHUNK_SIZE) // east
91 			return blocks[ adjacent[Side.east].getBlockType(0, y, z) ].isSideTransparent(side);
92 
93 		if(ty == -1) // bottom
94 		{
95 			assert(side == Side.top, to!string(side));
96 			return blocks[ adjacent[Side.bottom].getBlockType(x, CHUNK_SIZE-1, z) ].isSideTransparent(side);
97 		}
98 		else if(ty == CHUNK_SIZE) // top
99 		{
100 			return blocks[ adjacent[Side.top].getBlockType(x, 0, z) ].isSideTransparent(side);
101 		}
102 
103 		if(tz == -1) // north
104 			return blocks[ adjacent[Side.north].getBlockType(x, y, CHUNK_SIZE-1) ].isSideTransparent(side);
105 		else if(tz == CHUNK_SIZE) // south
106 			return blocks[ adjacent[Side.south].getBlockType(x, y, 0) ].isSideTransparent(side);
107 
108 		return blocks[ chunk.getBlockType(x, y, z) ].isSideTransparent(side);
109 	}
110 
111 	// Bit flags of sides to render
112 	ubyte sides = 0;
113 	// Num of sides to render
114 	ubyte sidenum = 0;
115 	// Offset to adjacent block
116 	byte[3] offset;
117 
118 	if (chunk.snapshot.blockData.uniform)
119 	{
120 		foreach (uint index; 0..CHUNK_SIZE_CUBE)
121 		{
122 			bx = index & CHUNK_SIZE_BITS;
123 			by = (index / CHUNK_SIZE_SQR) & CHUNK_SIZE_BITS;
124 			bz = (index / CHUNK_SIZE) & CHUNK_SIZE_BITS;
125 			sides = 0;
126 			sidenum = 0;
127 
128 			foreach(ubyte side; 0..6)
129 			{
130 				offset = sideOffsets[cast(Side)side];
131 
132 				if(getTransparency(bx+offset[0], by+offset[1], bz+offset[2], cast(Side)oppSide[side]))
133 				{
134 					sides |= 2^^(side);
135 					++sidenum;
136 				}
137 			}
138 
139 			appender ~= blocks[chunk.snapshot.blockData.uniformType]
140 							.mesh(bx, by, bz, sides, sidenum);
141 		} // foreach
142 	}
143 	else
144 	foreach (uint index, ref ubyte val; chunk.snapshot.blockData.blocks)
145 	{
146 		if (isVisibleBlock(val))
147 		{
148 			bx = index & CHUNK_SIZE_BITS;
149 			by = (index / CHUNK_SIZE_SQR) & CHUNK_SIZE_BITS;
150 			bz = (index / CHUNK_SIZE) & CHUNK_SIZE_BITS;
151 			sides = 0;
152 			sidenum = 0;
153 
154 			foreach(ubyte side; 0..6)
155 			{
156 				offset = sideOffsets[cast(Side)side];
157 
158 				if(getTransparency(bx+offset[0], by+offset[1], bz+offset[2], cast(Side)oppSide[side]))
159 				{
160 					sides |= 2^^(side);
161 					++sidenum;
162 				}
163 			}
164 
165 			appender ~= blocks[val].mesh(bx, by, bz, sides, sidenum);
166 		} // if(val != 0)
167 	} // foreach
168 
169 	auto result = cast(immutable(MeshGenResult)*)
170 		new MeshGenResult(cast(ubyte[])appender.data, chunk.position);
171 	mainThread.send(result);
172 }