1 /*
2 mapblock_mesh.cpp
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 */
5 
6 /*
7 This file is part of Freeminer.
8 
9 Freeminer is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Freeminer  is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Freeminer.  If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 #include "mapblock_mesh.h"
24 #include "light.h"
25 #include "mapblock.h"
26 #include "map.h"
27 #include "main.h" // for g_profiler
28 #include "profiler.h"
29 #include "nodedef.h"
30 #include "gamedef.h"
31 #include "mesh.h"
32 #include "content_mapblock.h"
33 #include "noise.h"
34 #include "shader.h"
35 #include "settings.h"
36 #include "util/directiontables.h"
37 #include "clientmap.h"
38 #include "log_types.h"
39 
applyFacesShading(video::SColor & color,float factor)40 static void applyFacesShading(video::SColor& color, float factor)
41 {
42 	color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255));
43 	color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255));
44 }
45 
getFarmeshStep(MapDrawControl & draw_control,const v3POS & playerpos,const v3POS & blockpos)46 int getFarmeshStep(MapDrawControl& draw_control, const v3POS & playerpos, const v3POS & blockpos) {
47 	int range = radius_box(playerpos, blockpos);
48 	if (draw_control.farmesh) {
49 		if		(range >= draw_control.farmesh+draw_control.farmesh_step*3)	return 16;
50 		else if (range >= draw_control.farmesh+draw_control.farmesh_step*2)	return 8;
51 		else if (range >= draw_control.farmesh+draw_control.farmesh_step)	return 4;
52 		else if (range >= draw_control.farmesh)								return 2;
53 	}
54 	return 1;
55 };
56 
57 /*
58 	MeshMakeData
59 */
60 
MeshMakeData(IGameDef * gamedef,Map & map_,MapDrawControl & draw_control_)61 MeshMakeData::MeshMakeData(IGameDef *gamedef, Map & map_, MapDrawControl& draw_control_):
62 #if defined(MESH_ZEROCOPY)
63 	m_vmanip(map_),
64 #endif
65 	m_blockpos(-1337,-1337,-1337),
66 	m_crack_pos_relative(-1337, -1337, -1337),
67 	m_highlighted_pos_relative(-1337, -1337, -1337),
68 	m_smooth_lighting(false),
69 	m_show_hud(false),
70 	m_highlight_mesh_color(255, 255, 255, 255),
71 	m_gamedef(gamedef)
72 	,step(1),
73 	range(1),
74 	block(nullptr),
75 	map(map_),
76 	draw_control(draw_control_),
77 	debug(0),
78 	filled(false)
79 {}
80 
~MeshMakeData()81 MeshMakeData::~MeshMakeData() {
82 	//infostream<<"~MeshMakeData "<<m_blockpos<<std::endl;
83 }
84 
fill(MapBlock * block_)85 void MeshMakeData::fill(MapBlock *block_)
86 {
87 	block = block_;
88 	m_blockpos = block->getPos();
89 	timestamp = block->getTimestamp();
90 }
91 
fill_data()92 void MeshMakeData::fill_data()
93 {
94 	if (!block || filled)
95 		return;
96 	filled = true;
97 
98 #if !defined(MESH_ZEROCOPY)
99 	ScopeProfiler sp(g_profiler, "Client: Mesh data fill");
100 
101 	v3POS blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
102 
103 	/*
104 		Copy data
105 	*/
106 
107 	// Allocate this block + neighbors
108 	m_vmanip.clear();
109 	VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE,
110 			blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1));
111 	m_vmanip.addArea(voxel_area);
112 
113 	{
114 		//TimeTaker timer("copy central block data");
115 		// 0ms
116 
117 		// Copy our data
118 		block->copyTo(m_vmanip);
119 	}
120 	{
121 		//TimeTaker timer("copy neighbor block data");
122 		// 0ms
123 
124 		/*
125 			Copy neighbors. This is lightning fast.
126 			Copying only the borders would be *very* slow.
127 		*/
128 
129 		// Get map
130 		Map *map = block->getParent();
131 
132 		for(u16 i=0; i<26; i++)
133 		{
134 			const v3s16 &dir = g_26dirs[i];
135 			v3s16 bp = m_blockpos + dir;
136 			MapBlock *b = map->getBlockNoCreateNoEx(bp);
137 			if(b)
138 				b->copyTo(m_vmanip);
139 		}
140 	}
141 #endif
142 }
143 
fillSingleNode(MapNode * node)144 void MeshMakeData::fillSingleNode(MapNode *node)
145 {
146 	m_blockpos = v3s16(0,0,0);
147 
148 #if !defined(MESH_ZEROCOPY)
149 	v3s16 blockpos_nodes = v3s16(0,0,0);
150 	VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
151 			blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
152 	s32 volume = area.getVolume();
153 	s32 our_node_index = area.index(1,1,1);
154 
155 	// Allocate this block + neighbors
156 	m_vmanip.clear();
157 	m_vmanip.addArea(area);
158 
159 	// Fill in data
160 	MapNode *data = new MapNode[volume];
161 	for(s32 i = 0; i < volume; i++)
162 	{
163 		if(i == our_node_index)
164 		{
165 			data[i] = *node;
166 		}
167 		else
168 		{
169 			data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
170 		}
171 	}
172 	m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
173 	delete[] data;
174 #endif
175 }
176 
setCrack(int crack_level,v3s16 crack_pos)177 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
178 {
179 	if(crack_level >= 0)
180 		m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
181 }
182 
setHighlighted(v3s16 highlighted_pos,bool show_hud)183 void MeshMakeData::setHighlighted(v3s16 highlighted_pos, bool show_hud)
184 {
185 	m_show_hud = show_hud;
186 	m_highlighted_pos_relative = highlighted_pos - m_blockpos*MAP_BLOCKSIZE;
187 }
188 
setSmoothLighting(bool smooth_lighting)189 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
190 {
191 	m_smooth_lighting = smooth_lighting;
192 }
193 
194 /*
195 	Light and vertex color functions
196 */
197 
198 /*
199 	Calculate non-smooth lighting at interior of node.
200 	Single light bank.
201 */
getInteriorLight(enum LightBank bank,MapNode n,s32 increment,INodeDefManager * ndef)202 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
203 		INodeDefManager *ndef)
204 {
205 	u8 light = n.getLight(bank, ndef);
206 
207 	while(increment > 0)
208 	{
209 		light = undiminish_light(light);
210 		--increment;
211 	}
212 	while(increment < 0)
213 	{
214 		light = diminish_light(light);
215 		++increment;
216 	}
217 
218 	return decode_light(light);
219 }
220 
221 /*
222 	Calculate non-smooth lighting at interior of node.
223 	Both light banks.
224 */
getInteriorLight(MapNode n,s32 increment,INodeDefManager * ndef)225 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
226 {
227 	u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
228 	u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
229 	return day | (night << 8);
230 }
231 
232 /*
233 	Calculate non-smooth lighting at face of node.
234 	Single light bank.
235 */
getFaceLight(enum LightBank bank,MapNode n,MapNode n2,v3s16 face_dir,INodeDefManager * ndef)236 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
237 		v3s16 face_dir, INodeDefManager *ndef)
238 {
239 	u8 light;
240 	u8 l1 = n.getLight(bank, ndef);
241 	u8 l2 = n2.getLight(bank, ndef);
242 	if(l1 > l2)
243 		light = l1;
244 	else
245 		light = l2;
246 
247 	// Boost light level for light sources
248 	u8 light_source = MYMAX(ndef->get(n).light_source,
249 			ndef->get(n2).light_source);
250 	if(light_source > light)
251 		light = light_source;
252 
253 	return decode_light(light);
254 }
255 
256 /*
257 	Calculate non-smooth lighting at face of node.
258 	Both light banks.
259 */
getFaceLight(MapNode n,MapNode n2,v3s16 face_dir,INodeDefManager * ndef)260 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
261 {
262 	u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
263 	u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
264 	return day | (night << 8);
265 }
266 
267 /*
268 	Calculate smooth lighting at the XYZ- corner of p.
269 	Both light banks
270 */
getSmoothLightCombined(v3s16 p,MeshMakeData * data)271 static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data)
272 {
273 	static const v3s16 dirs8[8] = {
274 		v3s16(0,0,0),
275 		v3s16(0,0,1),
276 		v3s16(0,1,0),
277 		v3s16(0,1,1),
278 		v3s16(1,0,0),
279 		v3s16(1,1,0),
280 		v3s16(1,0,1),
281 		v3s16(1,1,1),
282 	};
283 
284 	INodeDefManager *ndef = data->m_gamedef->ndef();
285 
286 	u16 ambient_occlusion = 0;
287 	u16 light_count = 0;
288 	u8 light_source_max = 0;
289 	u16 light_day = 0;
290 	u16 light_night = 0;
291 
292 	for (u32 i = 0; i < 8; i++)
293 	{
294 		MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
295 
296 		// if it's CONTENT_IGNORE we can't do any light calculations
297 		if (n.getContent() == CONTENT_IGNORE) {
298 			continue;
299 		}
300 
301 		const ContentFeatures &f = ndef->get(n);
302 		if (f.light_source > light_source_max)
303 			light_source_max = f.light_source;
304 		// Check f.solidness because fast-style leaves look better this way
305 		if (f.param_type == CPT_LIGHT && f.solidness != 2) {
306 			light_day += decode_light(n.getLight(LIGHTBANK_DAY, ndef));
307 			light_night += decode_light(n.getLight(LIGHTBANK_NIGHT, ndef));
308 			light_count++;
309 		} else {
310 			ambient_occlusion++;
311 		}
312 	}
313 
314 	if(light_count == 0)
315 		return 0xffff;
316 
317 	light_day /= light_count;
318 	light_night /= light_count;
319 
320 	// Boost brightness around light sources
321 	bool skip_ambient_occlusion_day = false;
322 	if(decode_light(light_source_max) >= light_day) {
323 		light_day = decode_light(light_source_max);
324 		skip_ambient_occlusion_day = true;
325 	}
326 
327 	bool skip_ambient_occlusion_night = false;
328 	if(decode_light(light_source_max) >= light_night) {
329 		light_night = decode_light(light_source_max);
330 		skip_ambient_occlusion_night = true;
331 	}
332 
333 	if (ambient_occlusion > 4)
334 	{
335 		//table of precalculated gamma space multiply factors
336 		//light^2.2 * factor (0.75, 0.5, 0.25, 0.0), so table holds factor ^ (1 / 2.2)
337 		static const float light_amount[4] = { 0.877424315, 0.729740053, 0.532520545, 0.0 };
338 
339 		//calculate table index for gamma space multiplier
340 		ambient_occlusion -= 5;
341 
342 		if (!skip_ambient_occlusion_day)
343 			light_day = rangelim(core::round32(light_day*light_amount[ambient_occlusion]), 0, 255);
344 		if (!skip_ambient_occlusion_night)
345 			light_night = rangelim(core::round32(light_night*light_amount[ambient_occlusion]), 0, 255);
346 	}
347 
348 	return light_day | (light_night << 8);
349 }
350 
351 /*
352 	Calculate smooth lighting at the given corner of p.
353 	Both light banks.
354 */
getSmoothLight(v3s16 p,v3s16 corner,MeshMakeData * data)355 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
356 {
357 	if(corner.X == 1) p.X += 1;
358 	// else corner.X == -1
359 	if(corner.Y == 1) p.Y += 1;
360 	// else corner.Y == -1
361 	if(corner.Z == 1) p.Z += 1;
362 	// else corner.Z == -1
363 
364 	return getSmoothLightCombined(p, data);
365 }
366 
367 /*
368 	Converts from day + night color values (0..255)
369 	and a given daynight_ratio to the final SColor shown on screen.
370 */
finalColorBlend(video::SColor & result,u8 day,u8 night,u32 daynight_ratio)371 void finalColorBlend(video::SColor& result,
372 		u8 day, u8 night, u32 daynight_ratio)
373 {
374 	s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
375 	s32 b = rg;
376 
377 	// Moonlight is blue
378 	b += (day - night) / 13;
379 	rg -= (day - night) / 23;
380 
381 	// Emphase blue a bit in darker places
382 	// Each entry of this array represents a range of 8 blue levels
383 	static const u8 emphase_blue_when_dark[32] = {
384 		1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
385 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
386 	};
387 	b += emphase_blue_when_dark[b / 8];
388 	b = irr::core::clamp (b, 0, 255);
389 
390 	// Artificial light is yellow-ish
391 	static const u8 emphase_yellow_when_artificial[16] = {
392 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
393 	};
394 	rg += emphase_yellow_when_artificial[night/16];
395 	rg = irr::core::clamp (rg, 0, 255);
396 
397 	result.setRed(rg);
398 	result.setGreen(rg);
399 	result.setBlue(b);
400 }
401 
402 /*
403 	Mesh generation helpers
404 */
405 
406 /*
407 	vertex_dirs: v3s16[4]
408 */
getNodeVertexDirs(v3s16 dir,v3s16 * vertex_dirs)409 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
410 {
411 	/*
412 		If looked from outside the node towards the face, the corners are:
413 		0: bottom-right
414 		1: bottom-left
415 		2: top-left
416 		3: top-right
417 	*/
418 	if(dir == v3s16(0,0,1))
419 	{
420 		// If looking towards z+, this is the face that is behind
421 		// the center point, facing towards z+.
422 		vertex_dirs[0] = v3s16(-1,-1, 1);
423 		vertex_dirs[1] = v3s16( 1,-1, 1);
424 		vertex_dirs[2] = v3s16( 1, 1, 1);
425 		vertex_dirs[3] = v3s16(-1, 1, 1);
426 	}
427 	else if(dir == v3s16(0,0,-1))
428 	{
429 		// faces towards Z-
430 		vertex_dirs[0] = v3s16( 1,-1,-1);
431 		vertex_dirs[1] = v3s16(-1,-1,-1);
432 		vertex_dirs[2] = v3s16(-1, 1,-1);
433 		vertex_dirs[3] = v3s16( 1, 1,-1);
434 	}
435 	else if(dir == v3s16(1,0,0))
436 	{
437 		// faces towards X+
438 		vertex_dirs[0] = v3s16( 1,-1, 1);
439 		vertex_dirs[1] = v3s16( 1,-1,-1);
440 		vertex_dirs[2] = v3s16( 1, 1,-1);
441 		vertex_dirs[3] = v3s16( 1, 1, 1);
442 	}
443 	else if(dir == v3s16(-1,0,0))
444 	{
445 		// faces towards X-
446 		vertex_dirs[0] = v3s16(-1,-1,-1);
447 		vertex_dirs[1] = v3s16(-1,-1, 1);
448 		vertex_dirs[2] = v3s16(-1, 1, 1);
449 		vertex_dirs[3] = v3s16(-1, 1,-1);
450 	}
451 	else if(dir == v3s16(0,1,0))
452 	{
453 		// faces towards Y+ (assume Z- as "down" in texture)
454 		vertex_dirs[0] = v3s16( 1, 1,-1);
455 		vertex_dirs[1] = v3s16(-1, 1,-1);
456 		vertex_dirs[2] = v3s16(-1, 1, 1);
457 		vertex_dirs[3] = v3s16( 1, 1, 1);
458 	}
459 	else if(dir == v3s16(0,-1,0))
460 	{
461 		// faces towards Y- (assume Z+ as "down" in texture)
462 		vertex_dirs[0] = v3s16( 1,-1, 1);
463 		vertex_dirs[1] = v3s16(-1,-1, 1);
464 		vertex_dirs[2] = v3s16(-1,-1,-1);
465 		vertex_dirs[3] = v3s16( 1,-1,-1);
466 	}
467 }
468 
469 struct FastFace
470 {
471 	TileSpec tile;
472 	video::S3DVertex vertices[4]; // Precalculated vertices
473 };
474 
makeFastFace(TileSpec tile,u16 li0,u16 li1,u16 li2,u16 li3,v3f p,v3s16 dir,v3f scale,u8 light_source,std::vector<FastFace> & dest)475 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
476 		v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
477 {
478 	FastFace face;
479 
480 	// Position is at the center of the cube.
481 	v3f pos = p * BS;
482 
483 	float x0 = 0.0;
484 	float y0 = 0.0;
485 	float w = 1.0;
486 	float h = 1.0;
487 
488 	v3f vertex_pos[4];
489 	v3s16 vertex_dirs[4];
490 	getNodeVertexDirs(dir, vertex_dirs);
491 
492 	v3s16 t;
493 	u16 t1;
494 	switch (tile.rotation)
495 	{
496 	case 0:
497 		break;
498 	case 1: //R90
499 		t = vertex_dirs[0];
500 		vertex_dirs[0] = vertex_dirs[3];
501 		vertex_dirs[3] = vertex_dirs[2];
502 		vertex_dirs[2] = vertex_dirs[1];
503 		vertex_dirs[1] = t;
504 		t1=li0;
505 		li0=li3;
506 		li3=li2;
507 		li2=li1;
508 		li1=t1;
509 		break;
510 	case 2: //R180
511 		t = vertex_dirs[0];
512 		vertex_dirs[0] = vertex_dirs[2];
513 		vertex_dirs[2] = t;
514 		t = vertex_dirs[1];
515 		vertex_dirs[1] = vertex_dirs[3];
516 		vertex_dirs[3] = t;
517 		t1  = li0;
518 		li0 = li2;
519 		li2 = t1;
520 		t1  = li1;
521 		li1 = li3;
522 		li3 = t1;
523 		break;
524 	case 3: //R270
525 		t = vertex_dirs[0];
526 		vertex_dirs[0] = vertex_dirs[1];
527 		vertex_dirs[1] = vertex_dirs[2];
528 		vertex_dirs[2] = vertex_dirs[3];
529 		vertex_dirs[3] = t;
530 		t1  = li0;
531 		li0 = li1;
532 		li1 = li2;
533 		li2 = li3;
534 		li3 = t1;
535 		break;
536 	case 4: //FXR90
537 		t = vertex_dirs[0];
538 		vertex_dirs[0] = vertex_dirs[3];
539 		vertex_dirs[3] = vertex_dirs[2];
540 		vertex_dirs[2] = vertex_dirs[1];
541 		vertex_dirs[1] = t;
542 		t1  = li0;
543 		li0 = li3;
544 		li3 = li2;
545 		li2 = li1;
546 		li1 = t1;
547 		y0 += h;
548 		h *= -1;
549 		break;
550 	case 5: //FXR270
551 		t = vertex_dirs[0];
552 		vertex_dirs[0] = vertex_dirs[1];
553 		vertex_dirs[1] = vertex_dirs[2];
554 		vertex_dirs[2] = vertex_dirs[3];
555 		vertex_dirs[3] = t;
556 		t1  = li0;
557 		li0 = li1;
558 		li1 = li2;
559 		li2 = li3;
560 		li3 = t1;
561 		y0 += h;
562 		h *= -1;
563 		break;
564 	case 6: //FYR90
565 		t = vertex_dirs[0];
566 		vertex_dirs[0] = vertex_dirs[3];
567 		vertex_dirs[3] = vertex_dirs[2];
568 		vertex_dirs[2] = vertex_dirs[1];
569 		vertex_dirs[1] = t;
570 		t1  = li0;
571 		li0 = li3;
572 		li3 = li2;
573 		li2 = li1;
574 		li1 = t1;
575 		x0 += w;
576 		w *= -1;
577 		break;
578 	case 7: //FYR270
579 		t = vertex_dirs[0];
580 		vertex_dirs[0] = vertex_dirs[1];
581 		vertex_dirs[1] = vertex_dirs[2];
582 		vertex_dirs[2] = vertex_dirs[3];
583 		vertex_dirs[3] = t;
584 		t1  = li0;
585 		li0 = li1;
586 		li1 = li2;
587 		li2 = li3;
588 		li3 = t1;
589 		x0 += w;
590 		w *= -1;
591 		break;
592 	case 8: //FX
593 		y0 += h;
594 		h *= -1;
595 		break;
596 	case 9: //FY
597 		x0 += w;
598 		w *= -1;
599 		break;
600 	default:
601 		break;
602 	}
603 
604 	for(u16 i=0; i<4; i++)
605 	{
606 		vertex_pos[i] = v3f(
607 				BS/2*vertex_dirs[i].X,
608 				BS/2*vertex_dirs[i].Y,
609 				BS/2*vertex_dirs[i].Z
610 		);
611 	}
612 
613 	for(u16 i=0; i<4; i++)
614 	{
615 		vertex_pos[i].X *= scale.X;
616 		vertex_pos[i].Y *= scale.Y;
617 		vertex_pos[i].Z *= scale.Z;
618 		vertex_pos[i] += pos;
619 	}
620 
621 	f32 abs_scale = 1.0;
622 	if     (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
623 	else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
624 	else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
625 
626 	v3f normal(dir.X, dir.Y, dir.Z);
627 
628 	u8 alpha = tile.alpha;
629 
630 	face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
631 			MapBlock_LightColor(alpha, li0, light_source),
632 			core::vector2d<f32>(x0+w*abs_scale, y0+h));
633 	face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
634 			MapBlock_LightColor(alpha, li1, light_source),
635 			core::vector2d<f32>(x0, y0+h));
636 	face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
637 			MapBlock_LightColor(alpha, li2, light_source),
638 			core::vector2d<f32>(x0, y0));
639 	face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
640 			MapBlock_LightColor(alpha, li3, light_source),
641 			core::vector2d<f32>(x0+w*abs_scale, y0));
642 
643 	face.tile = tile;
644 	dest.push_back(face);
645 }
646 
647 /*
648 	Nodes make a face if contents differ and solidness differs.
649 	Return value:
650 		0: No face
651 		1: Face uses m1's content
652 		2: Face uses m2's content
653 	equivalent: Whether the blocks share the same face (eg. water and glass)
654 
655 	TODO: Add 3: Both faces drawn with backface culling, remove equivalent
656 */
face_contents(content_t m1,content_t m2,bool * equivalent,INodeDefManager * ndef)657 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
658 		INodeDefManager *ndef)
659 {
660 	*equivalent = false;
661 
662 	if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
663 		return 0;
664 
665 	bool contents_differ = (m1 != m2);
666 
667 	const ContentFeatures &f1 = ndef->get(m1);
668 	const ContentFeatures &f2 = ndef->get(m2);
669 
670 	// Contents don't differ for different forms of same liquid
671 	if(f1.sameLiquid(f2))
672 		contents_differ = false;
673 
674 	u8 c1 = f1.solidness;
675 	u8 c2 = f2.solidness;
676 
677 	bool solidness_differs = (c1 != c2);
678 	bool makes_face = contents_differ && solidness_differs;
679 
680 	if(makes_face == false)
681 		return 0;
682 
683 	if(c1 == 0)
684 		c1 = f1.visual_solidness;
685 	if(c2 == 0)
686 		c2 = f2.visual_solidness;
687 
688 	if(c1 == c2){
689 		*equivalent = true;
690 		// If same solidness, liquid takes precense
691 		if(f1.isLiquid())
692 			return 1;
693 		if(f2.isLiquid())
694 			return 2;
695 	}
696 
697 	if(c1 > c2)
698 		return 1;
699 	else
700 		return 2;
701 }
702 
703 /*
704 	Gets nth node tile (0 <= n <= 5).
705 */
getNodeTileN(MapNode mn,v3s16 p,u8 tileindex,MeshMakeData * data)706 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
707 {
708 	INodeDefManager *ndef = data->m_gamedef->ndef();
709 	TileSpec spec = ndef->get(mn).tiles[tileindex];
710 	// Apply temporary crack
711 	if (p == data->m_crack_pos_relative)
712 		spec.material_flags |= MATERIAL_FLAG_CRACK;
713 	return spec;
714 }
715 
716 /*
717 	Gets node tile given a face direction.
718 */
getNodeTile(MapNode mn,v3s16 p,v3s16 dir,MeshMakeData * data)719 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
720 {
721 	INodeDefManager *ndef = data->m_gamedef->ndef();
722 
723 	// Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
724 	// (0,0,1), (0,0,-1) or (0,0,0)
725 	assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
726 
727 	// Convert direction to single integer for table lookup
728 	//  0 = (0,0,0)
729 	//  1 = (1,0,0)
730 	//  2 = (0,1,0)
731 	//  3 = (0,0,1)
732 	//  4 = invalid, treat as (0,0,0)
733 	//  5 = (0,0,-1)
734 	//  6 = (0,-1,0)
735 	//  7 = (-1,0,0)
736 	u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
737 
738 	// Get rotation for things like chests
739 	u8 facedir = mn.getFaceDir(ndef);
740 	if (facedir > 23)
741 		facedir = 0;
742 	static const u16 dir_to_tile[24 * 16] =
743 	{
744 		// 0     +X    +Y    +Z           -Z    -Y    -X   ->   value=tile,rotation
745 		   0,0,  2,0 , 0,0 , 4,0 ,  0,0,  5,0 , 1,0 , 3,0 ,  // rotate around y+ 0 - 3
746 		   0,0,  4,0 , 0,3 , 3,0 ,  0,0,  2,0 , 1,1 , 5,0 ,
747 		   0,0,  3,0 , 0,2 , 5,0 ,  0,0,  4,0 , 1,2 , 2,0 ,
748 		   0,0,  5,0 , 0,1 , 2,0 ,  0,0,  3,0 , 1,3 , 4,0 ,
749 
750 		   0,0,  2,3 , 5,0 , 0,2 ,  0,0,  1,0 , 4,2 , 3,1 ,  // rotate around z+ 4 - 7
751 		   0,0,  4,3 , 2,0 , 0,1 ,  0,0,  1,1 , 3,2 , 5,1 ,
752 		   0,0,  3,3 , 4,0 , 0,0 ,  0,0,  1,2 , 5,2 , 2,1 ,
753 		   0,0,  5,3 , 3,0 , 0,3 ,  0,0,  1,3 , 2,2 , 4,1 ,
754 
755 		   0,0,  2,1 , 4,2 , 1,2 ,  0,0,  0,0 , 5,0 , 3,3 ,  // rotate around z- 8 - 11
756 		   0,0,  4,1 , 3,2 , 1,3 ,  0,0,  0,3 , 2,0 , 5,3 ,
757 		   0,0,  3,1 , 5,2 , 1,0 ,  0,0,  0,2 , 4,0 , 2,3 ,
758 		   0,0,  5,1 , 2,2 , 1,1 ,  0,0,  0,1 , 3,0 , 4,3 ,
759 
760 		   0,0,  0,3 , 3,3 , 4,1 ,  0,0,  5,3 , 2,3 , 1,3 ,  // rotate around x+ 12 - 15
761 		   0,0,  0,2 , 5,3 , 3,1 ,  0,0,  2,3 , 4,3 , 1,0 ,
762 		   0,0,  0,1 , 2,3 , 5,1 ,  0,0,  4,3 , 3,3 , 1,1 ,
763 		   0,0,  0,0 , 4,3 , 2,1 ,  0,0,  3,3 , 5,3 , 1,2 ,
764 
765 		   0,0,  1,1 , 2,1 , 4,3 ,  0,0,  5,1 , 3,1 , 0,1 ,  // rotate around x- 16 - 19
766 		   0,0,  1,2 , 4,1 , 3,3 ,  0,0,  2,1 , 5,1 , 0,0 ,
767 		   0,0,  1,3 , 3,1 , 5,3 ,  0,0,  4,1 , 2,1 , 0,3 ,
768 		   0,0,  1,0 , 5,1 , 2,3 ,  0,0,  3,1 , 4,1 , 0,2 ,
769 
770 		   0,0,  3,2 , 1,2 , 4,2 ,  0,0,  5,2 , 0,2 , 2,2 ,  // rotate around y- 20 - 23
771 		   0,0,  5,2 , 1,3 , 3,2 ,  0,0,  2,2 , 0,1 , 4,2 ,
772 		   0,0,  2,2 , 1,0 , 5,2 ,  0,0,  4,2 , 0,0 , 3,2 ,
773 		   0,0,  4,2 , 1,1 , 2,2 ,  0,0,  3,2 , 0,3 , 5,2
774 
775 	};
776 	u16 tile_index=facedir*16 + dir_i;
777 	TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
778 	spec.rotation=dir_to_tile[tile_index + 1];
779 	spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
780 	return spec;
781 }
782 
getTileInfo(MeshMakeData * data,v3s16 p,v3s16 face_dir,bool & makes_face,v3s16 & p_corrected,v3s16 & face_dir_corrected,u16 * lights,TileSpec & tile,u8 & light_source,int step)783 static void getTileInfo(
784 		// Input:
785 		MeshMakeData *data,
786 		v3s16 p,
787 		v3s16 face_dir,
788 		// Output:
789 		bool &makes_face,
790 		v3s16 &p_corrected,
791 		v3s16 &face_dir_corrected,
792 		u16 *lights,
793 		TileSpec &tile,
794 		u8 &light_source
795 		,int step
796 	)
797 {
798 	auto &vmanip = data->m_vmanip;
799 	INodeDefManager *ndef = data->m_gamedef->ndef();
800 	v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
801 
802 	MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p*step);
803 
804 	// Don't even try to get n1 if n0 is already CONTENT_IGNORE
805 	if (n0.getContent() == CONTENT_IGNORE ) {
806 		makes_face = false;
807 		return;
808 	}
809 	MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p*step + face_dir*step);
810 	// if(data->debug) infostream<<" GN "<<n0<< n1<< blockpos_nodes<<blockpos_nodes + p*step<<blockpos_nodes + p*step + face_dir*step<<std::endl;
811 
812 	// This is hackish
813 	bool equivalent = false;
814 	u8 mf = face_contents(n0.getContent(), n1.getContent(),
815 			&equivalent, ndef);
816 
817 	if(mf == 0)
818 	{
819 		makes_face = false;
820 		return;
821 	}
822 
823 	makes_face = true;
824 
825 	if(mf == 1)
826 	{
827 		tile = getNodeTile(n0, p, face_dir, data);
828 		p_corrected = p;
829 		face_dir_corrected = face_dir;
830 		light_source = ndef->get(n0).light_source;
831 	}
832 	else
833 	{
834 		tile = getNodeTile(n1, p + face_dir, -face_dir, data);
835 		p_corrected = p + face_dir;
836 		face_dir_corrected = -face_dir;
837 		light_source = ndef->get(n1).light_source;
838 	}
839 
840 	// eg. water and glass
841 	if(equivalent)
842 		tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
843 
844 	if(data->m_smooth_lighting == false || step > 1)
845 	{
846 		lights[0] = lights[1] = lights[2] = lights[3] =
847 				getFaceLight(n0, n1, face_dir, ndef);
848 	}
849 	else
850 	{
851 		v3s16 vertex_dirs[4];
852 		getNodeVertexDirs(face_dir_corrected, vertex_dirs);
853 		for(u16 i=0; i<4; i++)
854 		{
855 			lights[i] = getSmoothLight(
856 					blockpos_nodes + p_corrected,
857 					vertex_dirs[i], data);
858 		}
859 	}
860 
861 	return;
862 }
863 
864 /*
865 	startpos:
866 	translate_dir: unit vector with only one of x, y or z
867 	face_dir: unit vector with only one of x, y or z
868 */
updateFastFaceRow(MeshMakeData * data,v3s16 startpos,v3s16 translate_dir,v3f translate_dir_f,v3s16 face_dir,v3f face_dir_f,std::vector<FastFace> & dest,int step)869 static void updateFastFaceRow(
870 		MeshMakeData *data,
871 		v3s16 startpos,
872 		v3s16 translate_dir,
873 		v3f translate_dir_f,
874 		v3s16 face_dir,
875 		v3f face_dir_f,
876 		std::vector<FastFace> &dest,
877 		int step)
878 {
879 	v3s16 p = startpos;
880 
881 	u16 continuous_tiles_count = 0;
882 
883 	bool makes_face = false;
884 	v3s16 p_corrected;
885 	v3s16 face_dir_corrected;
886 	u16 lights[4] = {0,0,0,0};
887 	TileSpec tile;
888 	u8 light_source = 0;
889 	getTileInfo(data, p, face_dir,
890 			makes_face, p_corrected, face_dir_corrected,
891 			lights, tile, light_source, step);
892 
893 	auto prev_p_corrected = p_corrected;
894 
895 	u16 to = MAP_BLOCKSIZE/step;
896 	for(u16 j=0; j<to; j++)
897 	{
898 		// If tiling can be done, this is set to false in the next step
899 		bool next_is_different = true;
900 
901 		v3s16 p_next;
902 
903 		bool next_makes_face = false;
904 		v3s16 next_p_corrected;
905 		v3s16 next_face_dir_corrected;
906 		u16 next_lights[4] = {0,0,0,0};
907 		TileSpec next_tile;
908 		u8 next_light_source = 0;
909 
910 		// If at last position, there is nothing to compare to and
911 		// the face must be drawn anyway
912 		if(j != to - 1)
913 		{
914 			p_next = p + translate_dir;
915 
916 			getTileInfo(data, p_next, face_dir,
917 					next_makes_face, next_p_corrected,
918 					next_face_dir_corrected, next_lights,
919 					next_tile, next_light_source, step);
920 
921 			if(next_makes_face == makes_face
922 					&& next_p_corrected == prev_p_corrected + translate_dir
923 					&& next_face_dir_corrected == face_dir_corrected
924 					&& next_lights[0] == lights[0]
925 					&& next_lights[1] == lights[1]
926 					&& next_lights[2] == lights[2]
927 					&& next_lights[3] == lights[3]
928 					&& next_tile == tile
929 					&& tile.rotation == 0
930 					&& next_light_source == light_source)
931 			{
932 				next_is_different = false;
933 			}
934 			else{
935 				/*if(makes_face){
936 					g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
937 							next_makes_face != makes_face ? 1 : 0);
938 					g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
939 							(next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
940 					g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
941 							next_face_dir_corrected != face_dir_corrected ? 1 : 0);
942 					g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
943 							(next_lights[0] != lights[0] ||
944 							next_lights[0] != lights[0] ||
945 							next_lights[0] != lights[0] ||
946 							next_lights[0] != lights[0]) ? 1 : 0);
947 					g_profiler->add("Meshgen: diff: !(next_tile == tile)",
948 							!(next_tile == tile) ? 1 : 0);
949 				}*/
950 			}
951 			/*g_profiler->add("Meshgen: Total faces checked", 1);
952 			if(makes_face)
953 				g_profiler->add("Meshgen: Total makes_face checked", 1);*/
954 		} else {
955 			/*if(makes_face)
956 				g_profiler->add("Meshgen: diff: last position", 1);*/
957 		}
958 
959 		continuous_tiles_count++;
960 
961 		if(next_is_different)
962 		{
963 			/*
964 				Create a face if there should be one
965 			*/
966 			if(makes_face)
967 			{
968 				// Floating point conversion of the position vector
969 				v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
970 				// Center point of face (kind of)
971 				v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
972 				if(continuous_tiles_count > 1)
973 					sp += translate_dir_f * (continuous_tiles_count - 1);
974 				v3f scale(1,1,1);
975 
976 				if(translate_dir.X != 0) {
977 					scale.X = continuous_tiles_count;
978 				}
979 				if(translate_dir.Y != 0) {
980 					scale.Y = continuous_tiles_count;
981 				}
982 				if(translate_dir.Z != 0) {
983 					scale.Z = continuous_tiles_count;
984 				}
985 
986 				makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
987 						sp, face_dir_corrected, scale, light_source,
988 						dest);
989 
990 #if !defined(NDEBUG)
991 				g_profiler->avg("Meshgen: faces drawn by tiling", continuous_tiles_count);
992 #endif
993 			}
994 
995 			continuous_tiles_count = 0;
996 
997 			makes_face = next_makes_face;
998 			p_corrected = next_p_corrected;
999 			face_dir_corrected = next_face_dir_corrected;
1000 			lights[0] = next_lights[0];
1001 			lights[1] = next_lights[1];
1002 			lights[2] = next_lights[2];
1003 			lights[3] = next_lights[3];
1004 			tile = next_tile;
1005 			light_source = next_light_source;
1006 		}
1007 
1008 		p = p_next;
1009 		prev_p_corrected = next_p_corrected;
1010 	}
1011 }
1012 
updateAllFastFaceRows(MeshMakeData * data,std::vector<FastFace> & dest,int step)1013 static void updateAllFastFaceRows(MeshMakeData *data,
1014 		std::vector<FastFace> &dest, int step)
1015 {
1016 	s16 to = MAP_BLOCKSIZE/step;
1017 	/*
1018 		Go through every y,z and get top(y+) faces in rows of x+
1019 	*/
1020 	for(s16 y = 0; y < to; y++) {
1021 		for(s16 z = 0; z < to; z++) {
1022 			updateFastFaceRow(data,
1023 					v3s16(0,y,z),
1024 					v3s16(1,0,0), //dir
1025 					v3f  (1,0,0),
1026 					v3s16(0,1,0), //face dir
1027 					v3f  (0,1,0),
1028 					dest, step);
1029 		}
1030 	}
1031 
1032 	/*
1033 		Go through every x,y and get right(x+) faces in rows of z+
1034 	*/
1035 	for(s16 x = 0; x < to; x++) {
1036 		for(s16 y = 0; y < to; y++) {
1037 			updateFastFaceRow(data,
1038 					v3s16(x,y,0),
1039 					v3s16(0,0,1), //dir
1040 					v3f  (0,0,1),
1041 					v3s16(1,0,0), //face dir
1042 					v3f  (1,0,0),
1043 					dest, step);
1044 		}
1045 	}
1046 
1047 	/*
1048 		Go through every y,z and get back(z+) faces in rows of x+
1049 	*/
1050 	for(s16 z = 0; z < to; z++) {
1051 		for(s16 y = 0; y < to; y++) {
1052 			updateFastFaceRow(data,
1053 					v3s16(0,y,z),
1054 					v3s16(1,0,0), //dir
1055 					v3f  (1,0,0),
1056 					v3s16(0,0,1), //face dir
1057 					v3f  (0,0,1),
1058 					dest, step);
1059 		}
1060 	}
1061 }
1062 
1063 /*
1064 	MapBlockMesh
1065 */
1066 
MapBlockMesh(MeshMakeData * data,v3s16 camera_offset)1067 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1068 	clearHardwareBuffer(false),
1069 	step(data->step),
1070 	timestamp(data->timestamp),
1071 	m_mesh(new scene::SMesh()),
1072 	m_gamedef(data->m_gamedef),
1073 	m_animation_force_timer(0), // force initial animation
1074 	m_last_crack(-1),
1075 	m_crack_materials(),
1076 	m_highlighted_materials(),
1077 	m_last_daynight_ratio((u32) -1),
1078 	m_daynight_diffs(),
1079 	m_usage_timer(0)
1080 {
1081 	m_enable_shaders = g_settings->getBool("enable_shaders");
1082 	m_enable_highlighting = g_settings->getBool("enable_node_highlighting");
1083 
1084 	// 4-21ms for MAP_BLOCKSIZE=16  (NOTE: probably outdated)
1085 	// 24-155ms for MAP_BLOCKSIZE=32  (NOTE: probably outdated)
1086 	//TimeTaker timer1("MapBlockMesh()");
1087 
1088 	data->fill_data();
1089 
1090 	std::vector<FastFace> fastfaces_new;
1091 
1092 	/*
1093 		We are including the faces of the trailing edges of the block.
1094 		This means that when something changes, the caller must
1095 		also update the meshes of the blocks at the leading edges.
1096 
1097 		NOTE: This is the slowest part of this method.
1098 	*/
1099 	{
1100 		// 4-23ms for MAP_BLOCKSIZE=16  (NOTE: probably outdated)
1101 		//TimeTaker timer2("updateAllFastFaceRows()");
1102 		updateAllFastFaceRows(data, fastfaces_new, step);
1103 	}
1104 	// End of slow part
1105 
1106 	//if (data->debug) infostream<<" step="<<step<<" fastfaces_new.size="<<fastfaces_new.size()<<std::endl;
1107 
1108 	/*
1109 		Convert FastFaces to MeshCollector
1110 	*/
1111 
1112 	MeshCollector collector;
1113 
1114 	{
1115 		// avg 0ms (100ms spikes when loading textures the first time)
1116 		// (NOTE: probably outdated)
1117 		//TimeTaker timer2("MeshCollector building");
1118 
1119 		for(u32 i=0; i<fastfaces_new.size(); i++)
1120 		{
1121 			FastFace &f = fastfaces_new[i];
1122 
1123 			const u16 indices[] = {0,1,2,2,3,0};
1124 			const u16 indices_alternate[] = {0,1,3,2,3,1};
1125 
1126 			if(f.tile.texture == NULL)
1127 				continue;
1128 
1129 			const u16 *indices_p = indices;
1130 
1131 			/*
1132 				Revert triangles for nicer looking gradient if vertices
1133 				1 and 3 have same color or 0 and 2 have different color.
1134 				getRed() is the day color.
1135 			*/
1136 			if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1137 					|| f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1138 				indices_p = indices_alternate;
1139 
1140 			collector.append(f.tile, f.vertices, 4, indices_p, 6);
1141 		}
1142 	}
1143 
1144 	/*
1145 		Add special graphics:
1146 		- torches
1147 		- flowing water
1148 		- fences
1149 		- whatever
1150 	*/
1151 
1152 	if(step <= 1)
1153 	mapblock_mesh_generate_special(data, collector);
1154 
1155 	m_highlight_mesh_color = data->m_highlight_mesh_color;
1156 
1157 	/*
1158 		Convert MeshCollector to SMesh
1159 	*/
1160 	ITextureSource *tsrc = m_gamedef->tsrc();
1161 	IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1162 
1163 	for(u32 i = 0; i < collector.prebuffers.size(); i++)
1164 	{
1165 		PreMeshBuffer &p = collector.prebuffers[i];
1166 
1167 		if (step <= data->draw_control.farmesh || !data->draw_control.farmesh) {
1168 		// Generate animation data
1169 		// - Cracks
1170 		if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1171 		{
1172 			// Find the texture name plus ^[crack:N:
1173 			std::ostringstream os(std::ios::binary);
1174 			os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1175 			if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1176 				os<<"o";  // use ^[cracko
1177 			os<<":"<<(u32)p.tile.animation_frame_count<<":";
1178 			m_crack_materials.insert(std::make_pair(i, os.str()));
1179 			// Replace tile texture with the cracked one
1180 			p.tile.texture = tsrc->getTexture(
1181 					os.str()+"0",
1182 					&p.tile.texture_id);
1183 		}
1184 		// - Texture animation
1185 		if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1186 		{
1187 			// Add to MapBlockMesh in order to animate these tiles
1188 			m_animation_tiles[i] = p.tile;
1189 			m_animation_frames[i] = 0;
1190 			if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1191 				// Get starting position from noise
1192 				m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1193 						data->m_blockpos.X, data->m_blockpos.Y,
1194 						data->m_blockpos.Z, 0));
1195 			} else {
1196 				// Play all synchronized
1197 				m_animation_frame_offsets[i] = 0;
1198 			}
1199 			// Replace tile texture with the first animation frame
1200 			FrameSpec animation_frame = p.tile.frames.find(0)->second;
1201 			p.tile.texture = animation_frame.texture;
1202 		}
1203 		}
1204 
1205 		if(m_enable_highlighting && p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED)
1206 			m_highlighted_materials.push_back(i);
1207 
1208 		for(u32 j = 0; j < p.vertices.size(); j++)
1209 		{
1210 			// Note applyFacesShading second parameter is precalculated sqrt
1211 			// value for speed improvement
1212 			// Skip it for lightsources and top faces.
1213 			video::SColor &vc = p.vertices[j].Color;
1214 			if (!vc.getBlue()) {
1215 				if (p.vertices[j].Normal.Y < -0.5) {
1216 					applyFacesShading (vc, 0.447213);
1217 				} else if (p.vertices[j].Normal.X > 0.5) {
1218 					applyFacesShading (vc, 0.670820);
1219 				} else if (p.vertices[j].Normal.X < -0.5) {
1220 					applyFacesShading (vc, 0.670820);
1221 				} else if (p.vertices[j].Normal.Z > 0.5) {
1222 					applyFacesShading (vc, 0.836660);
1223 				} else if (p.vertices[j].Normal.Z < -0.5) {
1224 					applyFacesShading (vc, 0.836660);
1225 				}
1226 			}
1227 			// - Classic lighting
1228 			// Set initial real color and store for later updates
1229 			u8 day = vc.getRed();
1230 			u8 night = vc.getGreen();
1231 			finalColorBlend(vc, day, night, 1000);
1232 			m_daynight_diffs[i][j] = std::make_pair(day, night);
1233 		}
1234 
1235 		// Create material
1236 		video::SMaterial material;
1237 		material.setFlag(video::EMF_LIGHTING, false);
1238 		material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1239 		material.setFlag(video::EMF_BILINEAR_FILTER, false);
1240 		material.setFlag(video::EMF_FOG_ENABLE, true);
1241 		//material.setFlag(video::EMF_WIREFRAME, true);
1242 		material.setTexture(0, p.tile.texture);
1243 
1244 		if (p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) {
1245 			material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
1246 		} else {
1247 			if (m_enable_shaders) {
1248 				material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1249 				p.tile.applyMaterialOptionsWithShaders(material);
1250 				if (p.tile.normal_texture) {
1251 					material.setTexture(1, p.tile.normal_texture);
1252 					material.setTexture(2, tsrc->getTexture("enable_img.png"));
1253 				} else {
1254 					material.setTexture(2, tsrc->getTexture("disable_img.png"));
1255 				}
1256 			} else {
1257 				p.tile.applyMaterialOptions(material);
1258 			}
1259 		}
1260 
1261 		// Create meshbuffer
1262 		// This is a "Standard MeshBuffer",
1263 		// it's a typedeffed CMeshBuffer<video::S3DVertex>
1264 		scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1265 		// Set material
1266 		buf->Material = material;
1267 		// Add to mesh
1268 		m_mesh->addMeshBuffer(buf);
1269 		// Mesh grabbed it
1270 		buf->drop();
1271 		buf->append(&p.vertices[0], p.vertices.size(),
1272 				&p.indices[0], p.indices.size());
1273 	}
1274 
1275 	m_camera_offset = camera_offset;
1276 
1277 	/*
1278 		Do some stuff to the mesh
1279 	*/
1280 
1281 	v3f t = v3f(0,0,0);
1282 	if (step>1) {
1283 		scaleMesh(m_mesh, v3f(step,step,step));
1284 		// TODO: remove this wrong numbers, find formula   good test: fly above ocean
1285 		if (step == 2)	t = v3f(BS/2,		 BS/2,		BS/2);
1286 		if (step == 4)	t = v3f(BS*1.666,	-BS/3.0,	BS*1.666);
1287 		if (step == 8)	t = v3f(BS*2.666,	-BS*2.4,	BS*2.666);
1288 		if (step == 16)	t = v3f(BS*6.4,		-BS*6.4,	BS*6.4);
1289 	}
1290 	translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS) + t);
1291 
1292 	if(m_mesh)
1293 	{
1294 #if 0
1295 		// Usually 1-700 faces and 1-7 materials
1296 		infostream<<"Updated MapBlock mesh p="<<data->m_blockpos<<" has "<<fastfaces_new.size()<<" faces "
1297 				<<"and uses "<<m_mesh->getMeshBufferCount()
1298 				<<" materials "<<" step="<<step<<" range="<<data->range<< " mesh="<<m_mesh<<std::endl;
1299 #endif
1300 	}
1301 
1302 	//std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1303 
1304 	// Check if animation is required for this mesh
1305 	m_has_animation =
1306 		!m_crack_materials.empty() ||
1307 		!m_daynight_diffs.empty() ||
1308 		!m_animation_tiles.empty() ||
1309 		!m_highlighted_materials.empty();
1310 }
1311 
~MapBlockMesh()1312 MapBlockMesh::~MapBlockMesh()
1313 {
1314 	if(clearHardwareBuffer)
1315 		for(u32 i=0; i<m_mesh->getMeshBufferCount(); i++){
1316 			scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i);
1317 			m_gamedef->tsrc()->getDevice()->getVideoDriver()->removeHardwareBuffer(buf);
1318 		}
1319 	m_mesh->drop();
1320 	m_mesh = NULL;
1321 }
1322 
setStatic()1323 void MapBlockMesh::setStatic()
1324 {
1325 	if(g_settings->getBool("enable_vbo")){
1326 		m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1327 		clearHardwareBuffer = true;
1328 	}
1329 }
1330 
animate(bool faraway,float time,int crack,u32 daynight_ratio)1331 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1332 {
1333 
1334 	if(!m_has_animation)
1335 	{
1336 		m_animation_force_timer = 100000;
1337 		return false;
1338 	}
1339 
1340 	m_animation_force_timer = myrand_range(5, 100);
1341 
1342 	// Cracks
1343 	if(crack != m_last_crack)
1344 	{
1345 		for(std::map<u32, std::string>::iterator
1346 				i = m_crack_materials.begin();
1347 				i != m_crack_materials.end(); i++)
1348 		{
1349 			scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1350 			std::string basename = i->second;
1351 
1352 			// Create new texture name from original
1353 			ITextureSource *tsrc = m_gamedef->getTextureSource();
1354 			std::ostringstream os;
1355 			os<<basename<<crack;
1356 			u32 new_texture_id = 0;
1357 			video::ITexture *new_texture =
1358 				tsrc->getTexture(os.str(), &new_texture_id);
1359 			buf->getMaterial().setTexture(0, new_texture);
1360 
1361 			// If the current material is also animated,
1362 			// update animation info
1363 			std::map<u32, TileSpec>::iterator anim_iter =
1364 				m_animation_tiles.find(i->first);
1365 			if(anim_iter != m_animation_tiles.end()){
1366 				TileSpec &tile = anim_iter->second;
1367 				tile.texture = new_texture;
1368 				tile.texture_id = new_texture_id;
1369 				// force animation update
1370 				m_animation_frames[i->first] = -1;
1371 			}
1372 		}
1373 
1374 		m_last_crack = crack;
1375 	}
1376 
1377 	// Texture animation
1378 	for(std::map<u32, TileSpec>::iterator
1379 			i = m_animation_tiles.begin();
1380 			i != m_animation_tiles.end(); i++)
1381 	{
1382 		const TileSpec &tile = i->second;
1383 		// Figure out current frame
1384 		int frameoffset = m_animation_frame_offsets[i->first];
1385 		int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1386 				+ frameoffset) % (tile.animation_frame_count ? tile.animation_frame_count : 1);
1387 		// If frame doesn't change, skip
1388 		if(frame == m_animation_frames[i->first])
1389 			continue;
1390 
1391 		m_animation_frames[i->first] = frame;
1392 
1393 		scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1394 		ITextureSource *tsrc = m_gamedef->getTextureSource();
1395 
1396 		FrameSpec animation_frame = tile.frames.find(frame)->second;
1397 		buf->getMaterial().setTexture(0, animation_frame.texture);
1398 		if (m_enable_shaders) {
1399 			if (animation_frame.normal_texture) {
1400 				buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1401 				buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1402 			} else {
1403 				buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1404 			}
1405 		}
1406 	}
1407 
1408 	// Day-night transition
1409 	if(daynight_ratio != m_last_daynight_ratio)
1410 	{
1411 		for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1412 				i = m_daynight_diffs.begin();
1413 				i != m_daynight_diffs.end(); i++)
1414 		{
1415 			scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1416 			buf->setDirty(irr::scene::EBT_VERTEX);
1417 			video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1418 			for(std::map<u32, std::pair<u8, u8 > >::iterator
1419 					j = i->second.begin();
1420 					j != i->second.end(); j++)
1421 			{
1422 				u32 vertexIndex = j->first;
1423 				u8 day = j->second.first;
1424 				u8 night = j->second.second;
1425 				finalColorBlend(vertices[vertexIndex].Color,
1426 						day, night, daynight_ratio);
1427 			}
1428 		}
1429 		m_last_daynight_ratio = daynight_ratio;
1430 	}
1431 
1432 	// Node highlighting
1433 	if (m_enable_highlighting) {
1434 		u8 day = m_highlight_mesh_color.getRed();
1435 		u8 night = m_highlight_mesh_color.getGreen();
1436 		video::SColor hc;
1437 		finalColorBlend(hc, day, night, daynight_ratio);
1438 		float sin_r = 0.07 * sin(1.5 * time);
1439 		float sin_g = 0.07 * sin(1.5 * time + irr::core::PI * 0.5);
1440 		float sin_b = 0.07 * sin(1.5 * time + irr::core::PI);
1441 		hc.setRed(core::clamp(core::round32(hc.getRed() * (0.8 + sin_r)), 0, 255));
1442 		hc.setGreen(core::clamp(core::round32(hc.getGreen() * (0.8 + sin_g)), 0, 255));
1443 		hc.setBlue(core::clamp(core::round32(hc.getBlue() * (0.8 + sin_b)), 0, 255));
1444 
1445 		for(std::list<u32>::iterator
1446 			i = m_highlighted_materials.begin();
1447 			i != m_highlighted_materials.end(); i++)
1448 		{
1449 			scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i);
1450 			video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1451 			for (u32 j = 0; j < buf->getVertexCount() ;j++)
1452 				vertices[j].Color = hc;
1453 		}
1454 	}
1455 
1456 	return true;
1457 }
1458 
updateCameraOffset(v3s16 camera_offset)1459 bool MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1460 {
1461 	if (camera_offset != m_camera_offset) {
1462 		translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1463 		m_camera_offset = camera_offset;
1464 		return true;
1465 	}
1466 	return false;
1467 }
1468 
1469 /*
1470 	MeshCollector
1471 */
1472 
append(const TileSpec & tile,const video::S3DVertex * vertices,u32 numVertices,const u16 * indices,u32 numIndices)1473 void MeshCollector::append(const TileSpec &tile,
1474 		const video::S3DVertex *vertices, u32 numVertices,
1475 		const u16 *indices, u32 numIndices)
1476 {
1477 	if(numIndices > 65535)
1478 	{
1479 		dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1480 		return;
1481 	}
1482 
1483 	PreMeshBuffer *p = NULL;
1484 	for(u32 i=0; i<prebuffers.size(); i++)
1485 	{
1486 		PreMeshBuffer &pp = prebuffers[i];
1487 		if(pp.tile != tile)
1488 			continue;
1489 		if(pp.indices.size() + numIndices > 65535)
1490 			continue;
1491 
1492 		p = &pp;
1493 		break;
1494 	}
1495 
1496 	if(p == NULL)
1497 	{
1498 		PreMeshBuffer pp;
1499 		pp.tile = tile;
1500 		prebuffers.push_back(pp);
1501 		p = &prebuffers[prebuffers.size()-1];
1502 	}
1503 
1504 	u32 vertex_count = p->vertices.size();
1505 	for(u32 i=0; i<numIndices; i++)
1506 	{
1507 		u32 j = indices[i] + vertex_count;
1508 		p->indices.push_back(j);
1509 	}
1510 	for(u32 i=0; i<numVertices; i++)
1511 	{
1512 		p->vertices.push_back(vertices[i]);
1513 	}
1514 }
1515 
1516 /*
1517 	MeshCollector - for meshnodes and converted drawtypes.
1518 */
1519 
append(const TileSpec & tile,const video::S3DVertex * vertices,u32 numVertices,const u16 * indices,u32 numIndices,v3f pos,video::SColor c)1520 void MeshCollector::append(const TileSpec &tile,
1521 		const video::S3DVertex *vertices, u32 numVertices,
1522 		const u16 *indices, u32 numIndices,
1523 		v3f pos, video::SColor c)
1524 {
1525 	if(numIndices > 65535)
1526 	{
1527 		dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1528 		return;
1529 	}
1530 
1531 	PreMeshBuffer *p = NULL;
1532 	for(u32 i=0; i<prebuffers.size(); i++)
1533 	{
1534 		PreMeshBuffer &pp = prebuffers[i];
1535 		if(pp.tile != tile)
1536 			continue;
1537 		if(pp.indices.size() + numIndices > 65535)
1538 			continue;
1539 
1540 		p = &pp;
1541 		break;
1542 	}
1543 
1544 	if(p == NULL)
1545 	{
1546 		PreMeshBuffer pp;
1547 		pp.tile = tile;
1548 		prebuffers.push_back(pp);
1549 		p = &prebuffers[prebuffers.size()-1];
1550 	}
1551 
1552 	u32 vertex_count = p->vertices.size();
1553 	for(u32 i=0; i<numIndices; i++)
1554 	{
1555 		u32 j = indices[i] + vertex_count;
1556 		p->indices.push_back(j);
1557 	}
1558 	for(u32 i=0; i<numVertices; i++)
1559 	{
1560 		video::S3DVertex vert = vertices[i];
1561 		vert.Pos += pos;
1562 		vert.Color = c;
1563 		p->vertices.push_back(vert);
1564 	}
1565 }
1566