3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "mapblock_mesh.h"
24 #include "main.h" // for g_profiler
29 #include "content_mapblock.h"
33 #include "util/directiontables.h"
35 float srgb_linear_multiply(float f, float m, float max)
37 f = f * f; // SRGB -> Linear
39 f = sqrt(f); // Linear -> SRGB
49 MeshMakeData::MeshMakeData(IGameDef *gamedef):
51 m_blockpos(-1337,-1337,-1337),
52 m_crack_pos_relative(-1337, -1337, -1337),
53 m_smooth_lighting(false),
57 void MeshMakeData::fill(MapBlock *block)
59 m_blockpos = block->getPos();
61 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
67 // Allocate this block + neighbors
69 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
70 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
73 //TimeTaker timer("copy central block data");
77 block->copyTo(m_vmanip);
80 //TimeTaker timer("copy neighbor block data");
84 Copy neighbors. This is lightning fast.
85 Copying only the borders would be *very* slow.
89 Map *map = block->getParent();
91 for(u16 i=0; i<26; i++)
93 const v3s16 &dir = g_26dirs[i];
94 v3s16 bp = m_blockpos + dir;
95 MapBlock *b = map->getBlockNoCreateNoEx(bp);
102 void MeshMakeData::fillSingleNode(MapNode *node)
104 m_blockpos = v3s16(0,0,0);
106 v3s16 blockpos_nodes = v3s16(0,0,0);
107 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
108 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
109 s32 volume = area.getVolume();
110 s32 our_node_index = area.index(1,1,1);
112 // Allocate this block + neighbors
114 m_vmanip.addArea(area);
117 MapNode *data = new MapNode[volume];
118 for(s32 i = 0; i < volume; i++)
120 if(i == our_node_index)
126 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
129 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
133 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
136 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
139 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
141 m_smooth_lighting = smooth_lighting;
145 Light and vertex color functions
149 Calculate non-smooth lighting at interior of node.
152 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
155 INodeDefManager *ndef = data->m_gamedef->ndef();
156 u8 light = n.getLight(bank, ndef);
160 light = undiminish_light(light);
165 light = diminish_light(light);
169 return decode_light(light);
173 Calculate non-smooth lighting at interior of node.
176 u16 getInteriorLight(MapNode n, s32 increment, MeshMakeData *data)
178 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, data);
179 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, data);
180 return day | (night << 8);
184 Calculate non-smooth lighting at face of node.
187 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
188 v3s16 face_dir, MeshMakeData *data)
190 INodeDefManager *ndef = data->m_gamedef->ndef();
193 u8 l1 = n.getLight(bank, ndef);
194 u8 l2 = n2.getLight(bank, ndef);
200 // Boost light level for light sources
201 u8 light_source = MYMAX(ndef->get(n).light_source,
202 ndef->get(n2).light_source);
203 //if(light_source >= light)
204 //return decode_light(undiminish_light(light_source));
205 if(light_source > light)
206 //return decode_light(light_source);
207 light = light_source;
209 // Make some nice difference to different sides
211 // This makes light come from a corner
212 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
213 light = diminish_light(diminish_light(light));
214 else if(face_dir.X == -1 || face_dir.Z == -1)
215 light = diminish_light(light);*/
217 // All neighboring faces have different shade (like in minecraft)
218 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
219 light = diminish_light(diminish_light(light));
220 else if(face_dir.Z == 1 || face_dir.Z == -1)
221 light = diminish_light(light);
223 return decode_light(light);
227 Calculate non-smooth lighting at face of node.
230 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data)
232 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, data);
233 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, data);
234 return day | (night << 8);
238 Calculate smooth lighting at the XYZ- corner of p.
241 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
243 static v3s16 dirs8[8] = {
254 INodeDefManager *ndef = data->m_gamedef->ndef();
256 u16 ambient_occlusion = 0;
259 u8 light_source_max = 0;
260 for(u32 i=0; i<8; i++)
262 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
263 const ContentFeatures &f = ndef->get(n);
264 if(f.light_source > light_source_max)
265 light_source_max = f.light_source;
266 // Check f.solidness because fast-style leaves look
268 if(f.param_type == CPT_LIGHT && f.solidness != 2)
270 light += decode_light(n.getLight(bank, ndef));
273 else if(n.getContent() != CONTENT_IGNORE)
282 light /= light_count;
284 // Boost brightness around light sources
285 if(decode_light(light_source_max) >= light)
286 //return decode_light(undiminish_light(light_source_max));
287 return decode_light(light_source_max);
289 if(ambient_occlusion > 4)
291 //ambient_occlusion -= 4;
292 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
293 float light_amount = (8 - ambient_occlusion) / 4.0;
294 float light_f = (float)light / 255.0;
295 light_f = pow(light_f, 2.2f); // gamma -> linear space
296 light_f = light_f * light_amount;
297 light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
300 light = 255.0 * light_f + 0.5;
307 Calculate smooth lighting at the XYZ- corner of p.
310 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
312 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
313 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
314 return day | (night << 8);
318 Calculate smooth lighting at the given corner of p.
321 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
323 if(corner.X == 1) p.X += 1;
324 else assert(corner.X == -1);
325 if(corner.Y == 1) p.Y += 1;
326 else assert(corner.Y == -1);
327 if(corner.Z == 1) p.Z += 1;
328 else assert(corner.Z == -1);
330 return getSmoothLight(p, data);
334 Converts from day + night color values (0..255)
335 and a given daynight_ratio to the final SColor shown on screen.
337 static void finalColorBlend(video::SColor& result,
338 u8 day, u8 night, u32 daynight_ratio)
340 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
344 b += (day - night) / 13;
345 rg -= (day - night) / 23;
347 // Emphase blue a bit in darker places
348 // Each entry of this array represents a range of 8 blue levels
349 static u8 emphase_blue_when_dark[32] = {
350 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
351 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
357 b += emphase_blue_when_dark[b / 8];
359 // Artificial light is yellow-ish
360 static u8 emphase_yellow_when_artificial[16] = {
361 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
363 rg += emphase_yellow_when_artificial[night/16];
375 Mesh generation helpers
379 vertex_dirs: v3s16[4]
381 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
384 If looked from outside the node towards the face, the corners are:
390 if(dir == v3s16(0,0,1))
392 // If looking towards z+, this is the face that is behind
393 // the center point, facing towards z+.
394 vertex_dirs[0] = v3s16(-1,-1, 1);
395 vertex_dirs[1] = v3s16( 1,-1, 1);
396 vertex_dirs[2] = v3s16( 1, 1, 1);
397 vertex_dirs[3] = v3s16(-1, 1, 1);
399 else if(dir == v3s16(0,0,-1))
402 vertex_dirs[0] = v3s16( 1,-1,-1);
403 vertex_dirs[1] = v3s16(-1,-1,-1);
404 vertex_dirs[2] = v3s16(-1, 1,-1);
405 vertex_dirs[3] = v3s16( 1, 1,-1);
407 else if(dir == v3s16(1,0,0))
410 vertex_dirs[0] = v3s16( 1,-1, 1);
411 vertex_dirs[1] = v3s16( 1,-1,-1);
412 vertex_dirs[2] = v3s16( 1, 1,-1);
413 vertex_dirs[3] = v3s16( 1, 1, 1);
415 else if(dir == v3s16(-1,0,0))
418 vertex_dirs[0] = v3s16(-1,-1,-1);
419 vertex_dirs[1] = v3s16(-1,-1, 1);
420 vertex_dirs[2] = v3s16(-1, 1, 1);
421 vertex_dirs[3] = v3s16(-1, 1,-1);
423 else if(dir == v3s16(0,1,0))
425 // faces towards Y+ (assume Z- as "down" in texture)
426 vertex_dirs[0] = v3s16( 1, 1,-1);
427 vertex_dirs[1] = v3s16(-1, 1,-1);
428 vertex_dirs[2] = v3s16(-1, 1, 1);
429 vertex_dirs[3] = v3s16( 1, 1, 1);
431 else if(dir == v3s16(0,-1,0))
433 // faces towards Y- (assume Z+ as "down" in texture)
434 vertex_dirs[0] = v3s16( 1,-1, 1);
435 vertex_dirs[1] = v3s16(-1,-1, 1);
436 vertex_dirs[2] = v3s16(-1,-1,-1);
437 vertex_dirs[3] = v3s16( 1,-1,-1);
444 video::S3DVertex vertices[4]; // Precalculated vertices
447 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
448 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
452 // Position is at the center of the cube.
456 v3s16 vertex_dirs[4];
457 getNodeVertexDirs(dir, vertex_dirs);
459 switch (tile.rotation)
465 vertex_dirs[0] = vertex_dirs[3];
466 vertex_dirs[3] = vertex_dirs[2];
467 vertex_dirs[2] = vertex_dirs[1];
472 vertex_dirs[0] = vertex_dirs[2];
475 vertex_dirs[1] = vertex_dirs[3];
480 vertex_dirs[0] = vertex_dirs[1];
481 vertex_dirs[1] = vertex_dirs[2];
482 vertex_dirs[2] = vertex_dirs[3];
487 vertex_dirs[0] = vertex_dirs[3];
488 vertex_dirs[3] = vertex_dirs[2];
489 vertex_dirs[2] = vertex_dirs[1];
491 tile.texture.pos.Y += tile.texture.size.Y;
492 tile.texture.size.Y *= -1;
496 vertex_dirs[0] = vertex_dirs[1];
497 vertex_dirs[1] = vertex_dirs[2];
498 vertex_dirs[2] = vertex_dirs[3];
500 tile.texture.pos.Y += tile.texture.size.Y;
501 tile.texture.size.Y *= -1;
505 vertex_dirs[0] = vertex_dirs[3];
506 vertex_dirs[3] = vertex_dirs[2];
507 vertex_dirs[2] = vertex_dirs[1];
509 tile.texture.pos.X += tile.texture.size.X;
510 tile.texture.size.X *= -1;
514 vertex_dirs[0] = vertex_dirs[1];
515 vertex_dirs[1] = vertex_dirs[2];
516 vertex_dirs[2] = vertex_dirs[3];
518 tile.texture.pos.X += tile.texture.size.X;
519 tile.texture.size.X *= -1;
522 tile.texture.pos.Y += tile.texture.size.Y;
523 tile.texture.size.Y *= -1;
526 tile.texture.pos.X += tile.texture.size.X;
527 tile.texture.size.X *= -1;
532 for(u16 i=0; i<4; i++)
535 BS/2*vertex_dirs[i].X,
536 BS/2*vertex_dirs[i].Y,
537 BS/2*vertex_dirs[i].Z
541 for(u16 i=0; i<4; i++)
543 vertex_pos[i].X *= scale.X;
544 vertex_pos[i].Y *= scale.Y;
545 vertex_pos[i].Z *= scale.Z;
546 vertex_pos[i] += pos;
550 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
551 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
552 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
554 v3f normal(dir.X, dir.Y, dir.Z);
556 u8 alpha = tile.alpha;
558 float x0 = tile.texture.pos.X;
559 float y0 = tile.texture.pos.Y;
560 float w = tile.texture.size.X;
561 float h = tile.texture.size.Y;
563 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
564 MapBlock_LightColor(alpha, li0, light_source),
565 core::vector2d<f32>(x0+w*abs_scale, y0+h));
566 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
567 MapBlock_LightColor(alpha, li1, light_source),
568 core::vector2d<f32>(x0, y0+h));
569 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
570 MapBlock_LightColor(alpha, li2, light_source),
571 core::vector2d<f32>(x0, y0));
572 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
573 MapBlock_LightColor(alpha, li3, light_source),
574 core::vector2d<f32>(x0+w*abs_scale, y0));
578 dest.push_back(face);
582 Nodes make a face if contents differ and solidness differs.
585 1: Face uses m1's content
586 2: Face uses m2's content
587 equivalent: Whether the blocks share the same face (eg. water and glass)
589 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
591 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
592 INodeDefManager *ndef)
596 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
599 bool contents_differ = (m1 != m2);
601 const ContentFeatures &f1 = ndef->get(m1);
602 const ContentFeatures &f2 = ndef->get(m2);
604 // Contents don't differ for different forms of same liquid
605 if(f1.sameLiquid(f2))
606 contents_differ = false;
608 u8 c1 = f1.solidness;
609 u8 c2 = f2.solidness;
611 bool solidness_differs = (c1 != c2);
612 bool makes_face = contents_differ && solidness_differs;
614 if(makes_face == false)
618 c1 = f1.visual_solidness;
620 c2 = f2.visual_solidness;
624 // If same solidness, liquid takes precense
638 Gets nth node tile (0 <= n <= 5).
640 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
642 INodeDefManager *ndef = data->m_gamedef->ndef();
643 TileSpec spec = ndef->get(mn).tiles[tileindex];
644 // Apply temporary crack
645 if(p == data->m_crack_pos_relative)
647 spec.material_flags |= MATERIAL_FLAG_CRACK;
648 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
650 // If animated, replace tile texture with one without texture atlas
651 if(spec.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
653 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
659 Gets node tile given a face direction.
661 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
663 INodeDefManager *ndef = data->m_gamedef->ndef();
665 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
666 // (0,0,1), (0,0,-1) or (0,0,0)
667 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
669 // Convert direction to single integer for table lookup
674 // 4 = invalid, treat as (0,0,0)
678 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
680 // Get rotation for things like chests
681 u8 facedir = mn.getFaceDir(ndef);
682 assert(facedir <= 23);
683 static const u16 dir_to_tile[24 * 16] =
685 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
686 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
687 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
688 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
689 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
691 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
692 0,0, 4,3 , 2,0 , 0,3 , 0,0, 1,1 , 3,2 , 5,1 ,
693 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
694 0,0, 5,3 , 3,0 , 0,1 , 0,0, 1,3 , 2,2 , 4,1 ,
696 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
697 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
698 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
699 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
701 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
702 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
703 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
704 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
706 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
707 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
708 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
709 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
711 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
712 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
713 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
714 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
717 u16 tile_index=facedir*16 + dir_i;
718 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
719 spec.rotation=dir_to_tile[tile_index + 1];
720 std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
721 spec.texture = data->m_gamedef->tsrc()->getTexture(name);
725 static void getTileInfo(
733 v3s16 &face_dir_corrected,
739 VoxelManipulator &vmanip = data->m_vmanip;
740 INodeDefManager *ndef = data->m_gamedef->ndef();
741 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
743 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
744 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
745 TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
746 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
749 bool equivalent = false;
750 u8 mf = face_contents(n0.getContent(), n1.getContent(),
765 face_dir_corrected = face_dir;
766 light_source = ndef->get(n0).light_source;
771 p_corrected = p + face_dir;
772 face_dir_corrected = -face_dir;
773 light_source = ndef->get(n1).light_source;
776 // eg. water and glass
778 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
780 if(data->m_smooth_lighting == false)
782 lights[0] = lights[1] = lights[2] = lights[3] =
783 getFaceLight(n0, n1, face_dir, data);
787 v3s16 vertex_dirs[4];
788 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
789 for(u16 i=0; i<4; i++)
791 lights[i] = getSmoothLight(
792 blockpos_nodes + p_corrected,
793 vertex_dirs[i], data);
802 translate_dir: unit vector with only one of x, y or z
803 face_dir: unit vector with only one of x, y or z
805 static void updateFastFaceRow(
812 std::vector<FastFace> &dest)
816 u16 continuous_tiles_count = 0;
818 bool makes_face = false;
820 v3s16 face_dir_corrected;
821 u16 lights[4] = {0,0,0,0};
824 getTileInfo(data, p, face_dir,
825 makes_face, p_corrected, face_dir_corrected,
826 lights, tile, light_source);
828 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
830 // If tiling can be done, this is set to false in the next step
831 bool next_is_different = true;
835 bool next_makes_face = false;
836 v3s16 next_p_corrected;
837 v3s16 next_face_dir_corrected;
838 u16 next_lights[4] = {0,0,0,0};
840 u8 next_light_source = 0;
842 // If at last position, there is nothing to compare to and
843 // the face must be drawn anyway
844 if(j != MAP_BLOCKSIZE - 1)
846 p_next = p + translate_dir;
848 getTileInfo(data, p_next, face_dir,
849 next_makes_face, next_p_corrected,
850 next_face_dir_corrected, next_lights,
851 next_tile, next_light_source);
853 if(next_makes_face == makes_face
854 && next_p_corrected == p_corrected + translate_dir
855 && next_face_dir_corrected == face_dir_corrected
856 && next_lights[0] == lights[0]
857 && next_lights[1] == lights[1]
858 && next_lights[2] == lights[2]
859 && next_lights[3] == lights[3]
861 && tile.rotation == 0
862 && next_light_source == light_source)
864 next_is_different = false;
868 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
869 next_makes_face != makes_face ? 1 : 0);
870 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
871 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
872 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
873 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
874 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
875 (next_lights[0] != lights[0] ||
876 next_lights[0] != lights[0] ||
877 next_lights[0] != lights[0] ||
878 next_lights[0] != lights[0]) ? 1 : 0);
879 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
880 !(next_tile == tile) ? 1 : 0);
883 /*g_profiler->add("Meshgen: Total faces checked", 1);
885 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
888 g_profiler->add("Meshgen: diff: last position", 1);*/
891 continuous_tiles_count++;
893 // This is set to true if the texture doesn't allow more tiling
894 bool end_of_texture = false;
896 If there is no texture, it can be tiled infinitely.
897 If tiled==0, it means the texture can be tiled infinitely.
898 Otherwise check tiled agains continuous_tiles_count.
900 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
902 if(tile.texture.tiled <= continuous_tiles_count)
903 end_of_texture = true;
906 // Do this to disable tiling textures
907 //end_of_texture = true; //DEBUG
909 if(next_is_different || end_of_texture)
912 Create a face if there should be one
916 // Floating point conversion of the position vector
917 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
918 // Center point of face (kind of)
919 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
920 if(continuous_tiles_count != 1)
921 sp += translate_dir_f;
924 if(translate_dir.X != 0)
926 scale.X = continuous_tiles_count;
928 if(translate_dir.Y != 0)
930 scale.Y = continuous_tiles_count;
932 if(translate_dir.Z != 0)
934 scale.Z = continuous_tiles_count;
937 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
938 sp, face_dir_corrected, scale, light_source,
941 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
942 for(int i=1; i<continuous_tiles_count; i++){
943 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
947 continuous_tiles_count = 0;
949 makes_face = next_makes_face;
950 p_corrected = next_p_corrected;
951 face_dir_corrected = next_face_dir_corrected;
952 lights[0] = next_lights[0];
953 lights[1] = next_lights[1];
954 lights[2] = next_lights[2];
955 lights[3] = next_lights[3];
957 light_source = next_light_source;
964 static void updateAllFastFaceRows(MeshMakeData *data,
965 std::vector<FastFace> &dest)
968 Go through every y,z and get top(y+) faces in rows of x+
970 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
971 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
972 updateFastFaceRow(data,
976 v3s16(0,1,0), //face dir
983 Go through every x,y and get right(x+) faces in rows of z+
985 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
986 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
987 updateFastFaceRow(data,
991 v3s16(1,0,0), //face dir
998 Go through every y,z and get back(z+) faces in rows of x+
1000 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
1001 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1002 updateFastFaceRow(data,
1006 v3s16(0,0,1), //face dir
1017 MapBlockMesh::MapBlockMesh(MeshMakeData *data):
1018 m_mesh(new scene::SMesh()),
1019 m_gamedef(data->m_gamedef),
1020 m_animation_force_timer(0), // force initial animation
1022 m_crack_materials(),
1023 m_last_daynight_ratio((u32) -1),
1026 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1027 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1028 //TimeTaker timer1("MapBlockMesh()");
1030 std::vector<FastFace> fastfaces_new;
1033 We are including the faces of the trailing edges of the block.
1034 This means that when something changes, the caller must
1035 also update the meshes of the blocks at the leading edges.
1037 NOTE: This is the slowest part of this method.
1040 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1041 //TimeTaker timer2("updateAllFastFaceRows()");
1042 updateAllFastFaceRows(data, fastfaces_new);
1047 Convert FastFaces to MeshCollector
1050 MeshCollector collector;
1053 // avg 0ms (100ms spikes when loading textures the first time)
1054 // (NOTE: probably outdated)
1055 //TimeTaker timer2("MeshCollector building");
1057 for(u32 i=0; i<fastfaces_new.size(); i++)
1059 FastFace &f = fastfaces_new[i];
1061 const u16 indices[] = {0,1,2,2,3,0};
1062 const u16 indices_alternate[] = {0,1,3,2,3,1};
1064 if(f.tile.texture.atlas == NULL)
1067 const u16 *indices_p = indices;
1070 Revert triangles for nicer looking gradient if vertices
1071 1 and 3 have same color or 0 and 2 have different color.
1072 getRed() is the day color.
1074 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1075 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1076 indices_p = indices_alternate;
1078 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1083 Add special graphics:
1090 mapblock_mesh_generate_special(data, collector);
1094 Convert MeshCollector to SMesh
1095 Also store animation info
1097 bool enable_shaders = (g_settings->getS32("enable_shaders") > 0);
1098 video::E_MATERIAL_TYPE shadermat1 = m_gamedef->getShaderSource()->
1099 getShader("test_shader_1").material;
1100 video::E_MATERIAL_TYPE shadermat2 = m_gamedef->getShaderSource()->
1101 getShader("test_shader_2").material;
1102 video::E_MATERIAL_TYPE shadermat3 = m_gamedef->getShaderSource()->
1103 getShader("test_shader_3").material;
1104 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1106 PreMeshBuffer &p = collector.prebuffers[i];
1107 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1108 <<", p.indices.size()="<<p.indices.size()
1111 // Generate animation data
1113 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1115 ITextureSource *tsrc = data->m_gamedef->tsrc();
1116 std::string crack_basename = tsrc->getTextureName(p.tile.texture.id);
1117 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1118 crack_basename += "^[cracko";
1120 crack_basename += "^[crack";
1121 m_crack_materials.insert(std::make_pair(i, crack_basename));
1123 // - Texture animation
1124 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1126 ITextureSource *tsrc = data->m_gamedef->tsrc();
1127 // Add to MapBlockMesh in order to animate these tiles
1128 m_animation_tiles[i] = p.tile;
1129 m_animation_frames[i] = 0;
1130 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1131 // Get starting position from noise
1132 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1133 data->m_blockpos.X, data->m_blockpos.Y,
1134 data->m_blockpos.Z, 0));
1136 // Play all synchronized
1137 m_animation_frame_offsets[i] = 0;
1139 // Replace tile texture with the first animation frame
1140 std::ostringstream os(std::ios::binary);
1141 os<<tsrc->getTextureName(p.tile.texture.id);
1142 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1143 p.tile.texture = tsrc->getTexture(os.str());
1145 // - Classic lighting (shaders handle this by themselves)
1148 for(u32 j = 0; j < p.vertices.size(); j++)
1150 video::SColor &vc = p.vertices[j].Color;
1151 // Set initial real color and store for later updates
1152 u8 day = vc.getRed();
1153 u8 night = vc.getGreen();
1154 finalColorBlend(vc, day, night, 1000);
1156 m_daynight_diffs[i][j] = std::make_pair(day, night);
1157 // Brighten topside (no shaders)
1158 if(p.vertices[j].Normal.Y > 0.5)
1160 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1161 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1162 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1168 video::SMaterial material;
1169 material.setFlag(video::EMF_LIGHTING, false);
1170 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1171 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1172 material.setFlag(video::EMF_FOG_ENABLE, true);
1173 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1174 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1175 material.MaterialType
1176 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1177 material.setTexture(0, p.tile.texture.atlas);
1179 p.tile.applyMaterialOptionsWithShaders(material, shadermat1, shadermat2, shadermat3);
1181 p.tile.applyMaterialOptions(material);
1183 // Create meshbuffer
1185 // This is a "Standard MeshBuffer",
1186 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1187 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1189 buf->Material = material;
1191 m_mesh->addMeshBuffer(buf);
1194 buf->append(&p.vertices[0], p.vertices.size(),
1195 &p.indices[0], p.indices.size());
1199 Do some stuff to the mesh
1202 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
1203 m_mesh->recalculateBoundingBox(); // translateMesh already does this
1208 // Usually 1-700 faces and 1-7 materials
1209 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1210 <<"and uses "<<m_mesh->getMeshBufferCount()
1211 <<" materials (meshbuffers)"<<std::endl;
1214 // Use VBO for mesh (this just would set this for ever buffer)
1215 // This will lead to infinite memory usage because or irrlicht.
1216 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1219 NOTE: If that is enabled, some kind of a queue to the main
1220 thread should be made which would call irrlicht to delete
1221 the hardware buffer and then delete the mesh
1225 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1227 // Check if animation is required for this mesh
1229 !m_crack_materials.empty() ||
1230 !m_daynight_diffs.empty() ||
1231 !m_animation_tiles.empty();
1234 MapBlockMesh::~MapBlockMesh()
1240 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1242 if(!m_has_animation)
1244 m_animation_force_timer = 100000;
1248 m_animation_force_timer = myrand_range(5, 100);
1251 if(crack != m_last_crack)
1253 for(std::map<u32, std::string>::iterator
1254 i = m_crack_materials.begin();
1255 i != m_crack_materials.end(); i++)
1257 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1258 std::string basename = i->second;
1260 // Create new texture name from original
1261 ITextureSource *tsrc = m_gamedef->getTextureSource();
1262 std::ostringstream os;
1263 os<<basename<<crack;
1264 AtlasPointer ap = tsrc->getTexture(os.str());
1265 buf->getMaterial().setTexture(0, ap.atlas);
1268 m_last_crack = crack;
1271 // Texture animation
1272 for(std::map<u32, TileSpec>::iterator
1273 i = m_animation_tiles.begin();
1274 i != m_animation_tiles.end(); i++)
1276 const TileSpec &tile = i->second;
1277 // Figure out current frame
1278 int frameoffset = m_animation_frame_offsets[i->first];
1279 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1280 + frameoffset) % tile.animation_frame_count;
1281 // If frame doesn't change, skip
1282 if(frame == m_animation_frames[i->first])
1285 m_animation_frames[i->first] = frame;
1287 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1288 ITextureSource *tsrc = m_gamedef->getTextureSource();
1290 // Create new texture name from original
1291 std::ostringstream os(std::ios::binary);
1292 os<<tsrc->getTextureName(tile.texture.id);
1293 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1295 AtlasPointer ap = tsrc->getTexture(os.str());
1296 buf->getMaterial().setTexture(0, ap.atlas);
1299 // Day-night transition
1300 if(daynight_ratio != m_last_daynight_ratio)
1302 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1303 i = m_daynight_diffs.begin();
1304 i != m_daynight_diffs.end(); i++)
1306 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1307 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1308 for(std::map<u32, std::pair<u8, u8 > >::iterator
1309 j = i->second.begin();
1310 j != i->second.end(); j++)
1312 u32 vertexIndex = j->first;
1313 u8 day = j->second.first;
1314 u8 night = j->second.second;
1315 finalColorBlend(vertices[vertexIndex].Color,
1316 day, night, daynight_ratio);
1317 // Brighten topside (no shaders)
1318 if(vertices[vertexIndex].Normal.Y > 0.5)
1320 video::SColor &vc = vertices[vertexIndex].Color;
1321 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1322 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1323 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1327 m_last_daynight_ratio = daynight_ratio;
1337 void MeshCollector::append(const TileSpec &tile,
1338 const video::S3DVertex *vertices, u32 numVertices,
1339 const u16 *indices, u32 numIndices)
1341 PreMeshBuffer *p = NULL;
1342 for(u32 i=0; i<prebuffers.size(); i++)
1344 PreMeshBuffer &pp = prebuffers[i];
1356 prebuffers.push_back(pp);
1357 p = &prebuffers[prebuffers.size()-1];
1360 u32 vertex_count = p->vertices.size();
1361 for(u32 i=0; i<numIndices; i++)
1363 u32 j = indices[i] + vertex_count;
1366 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
1367 // NOTE: Fix is to just add an another MeshBuffer
1369 p->indices.push_back(j);
1371 for(u32 i=0; i<numVertices; i++)
1373 p->vertices.push_back(vertices[i]);