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