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"
29 #include "content_mapblock.h"
33 #include "util/directiontables.h"
34 #include <IMeshManipulator.h>
36 static void applyFacesShading(video::SColor& color, float factor)
38 color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255));
39 color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255));
46 MeshMakeData::MeshMakeData(IGameDef *gamedef, bool use_shaders):
48 m_blockpos(-1337,-1337,-1337),
49 m_crack_pos_relative(-1337, -1337, -1337),
50 m_highlighted_pos_relative(-1337, -1337, -1337),
51 m_smooth_lighting(false),
53 m_highlight_mesh_color(255, 255, 255, 255),
55 m_use_shaders(use_shaders)
58 void MeshMakeData::fill(MapBlock *block)
60 m_blockpos = block->getPos();
62 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
68 // Allocate this block + neighbors
70 VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE,
71 blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1));
72 m_vmanip.addArea(voxel_area);
75 //TimeTaker timer("copy central block data");
79 block->copyTo(m_vmanip);
82 //TimeTaker timer("copy neighbor block data");
86 Copy neighbors. This is lightning fast.
87 Copying only the borders would be *very* slow.
91 Map *map = block->getParent();
93 for(u16 i=0; i<26; i++)
95 const v3s16 &dir = g_26dirs[i];
96 v3s16 bp = m_blockpos + dir;
97 MapBlock *b = map->getBlockNoCreateNoEx(bp);
104 void MeshMakeData::fillSingleNode(MapNode *node)
106 m_blockpos = v3s16(0,0,0);
108 v3s16 blockpos_nodes = v3s16(0,0,0);
109 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
110 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
111 s32 volume = area.getVolume();
112 s32 our_node_index = area.index(1,1,1);
114 // Allocate this block + neighbors
116 m_vmanip.addArea(area);
119 MapNode *data = new MapNode[volume];
120 for(s32 i = 0; i < volume; i++)
122 if(i == our_node_index)
128 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
131 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
135 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
138 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
141 void MeshMakeData::setHighlighted(v3s16 highlighted_pos, bool show_hud)
143 m_show_hud = show_hud;
144 m_highlighted_pos_relative = highlighted_pos - m_blockpos*MAP_BLOCKSIZE;
147 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
149 m_smooth_lighting = smooth_lighting;
153 Light and vertex color functions
157 Calculate non-smooth lighting at interior of node.
160 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
161 INodeDefManager *ndef)
163 u8 light = n.getLight(bank, ndef);
167 light = undiminish_light(light);
172 light = diminish_light(light);
176 return decode_light(light);
180 Calculate non-smooth lighting at interior of node.
183 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
185 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
186 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
187 return day | (night << 8);
191 Calculate non-smooth lighting at face of node.
194 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
195 v3s16 face_dir, INodeDefManager *ndef)
198 u8 l1 = n.getLight(bank, ndef);
199 u8 l2 = n2.getLight(bank, ndef);
205 // Boost light level for light sources
206 u8 light_source = MYMAX(ndef->get(n).light_source,
207 ndef->get(n2).light_source);
208 if(light_source > light)
209 light = light_source;
211 return decode_light(light);
215 Calculate non-smooth lighting at face of node.
218 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
220 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
221 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
222 return day | (night << 8);
226 Calculate smooth lighting at the XYZ- corner of p.
229 static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data)
231 static const v3s16 dirs8[8] = {
242 INodeDefManager *ndef = data->m_gamedef->ndef();
244 u16 ambient_occlusion = 0;
246 u8 light_source_max = 0;
250 for (u32 i = 0; i < 8; i++)
252 const MapNode &n = data->m_vmanip.getNodeRefUnsafeCheckFlags(p - dirs8[i]);
254 // if it's CONTENT_IGNORE we can't do any light calculations
255 if (n.getContent() == CONTENT_IGNORE) {
259 const ContentFeatures &f = ndef->get(n);
260 if (f.light_source > light_source_max)
261 light_source_max = f.light_source;
262 // Check f.solidness because fast-style leaves look better this way
263 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
264 light_day += decode_light(n.getLightNoChecks(LIGHTBANK_DAY, &f));
265 light_night += decode_light(n.getLightNoChecks(LIGHTBANK_NIGHT, &f));
275 light_day /= light_count;
276 light_night /= light_count;
278 // Boost brightness around light sources
279 bool skip_ambient_occlusion_day = false;
280 if(decode_light(light_source_max) >= light_day) {
281 light_day = decode_light(light_source_max);
282 skip_ambient_occlusion_day = true;
285 bool skip_ambient_occlusion_night = false;
286 if(decode_light(light_source_max) >= light_night) {
287 light_night = decode_light(light_source_max);
288 skip_ambient_occlusion_night = true;
291 if (ambient_occlusion > 4)
293 static const float ao_gamma = rangelim(
294 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
296 // Table of gamma space multiply factors.
297 static const float light_amount[3] = {
298 powf(0.75, 1.0 / ao_gamma),
299 powf(0.5, 1.0 / ao_gamma),
300 powf(0.25, 1.0 / ao_gamma)
303 //calculate table index for gamma space multiplier
304 ambient_occlusion -= 5;
306 if (!skip_ambient_occlusion_day)
307 light_day = rangelim(core::round32(light_day*light_amount[ambient_occlusion]), 0, 255);
308 if (!skip_ambient_occlusion_night)
309 light_night = rangelim(core::round32(light_night*light_amount[ambient_occlusion]), 0, 255);
312 return light_day | (light_night << 8);
316 Calculate smooth lighting at the given corner of p.
319 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
321 if(corner.X == 1) p.X += 1;
322 // else corner.X == -1
323 if(corner.Y == 1) p.Y += 1;
324 // else corner.Y == -1
325 if(corner.Z == 1) p.Z += 1;
326 // else corner.Z == -1
328 return getSmoothLightCombined(p, data);
332 Converts from day + night color values (0..255)
333 and a given daynight_ratio to the final SColor shown on screen.
335 void finalColorBlend(video::SColor& result,
336 u8 day, u8 night, u32 daynight_ratio)
338 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
342 b += (day - night) / 13;
343 rg -= (day - night) / 23;
345 // Emphase blue a bit in darker places
346 // Each entry of this array represents a range of 8 blue levels
347 static const u8 emphase_blue_when_dark[32] = {
348 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
349 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
351 b += emphase_blue_when_dark[irr::core::clamp(b, 0, 255) / 8];
352 b = irr::core::clamp(b, 0, 255);
354 // Artificial light is yellow-ish
355 static const u8 emphase_yellow_when_artificial[16] = {
356 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
358 rg += emphase_yellow_when_artificial[night/16];
359 rg = irr::core::clamp(rg, 0, 255);
367 Mesh generation helpers
371 vertex_dirs: v3s16[4]
373 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
376 If looked from outside the node towards the face, the corners are:
382 if(dir == v3s16(0,0,1))
384 // If looking towards z+, this is the face that is behind
385 // the center point, facing towards z+.
386 vertex_dirs[0] = v3s16(-1,-1, 1);
387 vertex_dirs[1] = v3s16( 1,-1, 1);
388 vertex_dirs[2] = v3s16( 1, 1, 1);
389 vertex_dirs[3] = v3s16(-1, 1, 1);
391 else if(dir == v3s16(0,0,-1))
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(1,0,0))
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(0,1,0))
417 // faces towards Y+ (assume Z- as "down" in texture)
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);
436 video::S3DVertex vertices[4]; // Precalculated vertices
439 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
440 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
442 // Position is at the center of the cube.
451 v3s16 vertex_dirs[4];
452 getNodeVertexDirs(dir, vertex_dirs);
456 switch (tile.rotation)
462 vertex_dirs[0] = vertex_dirs[3];
463 vertex_dirs[3] = vertex_dirs[2];
464 vertex_dirs[2] = vertex_dirs[1];
474 vertex_dirs[0] = vertex_dirs[2];
477 vertex_dirs[1] = vertex_dirs[3];
488 vertex_dirs[0] = vertex_dirs[1];
489 vertex_dirs[1] = vertex_dirs[2];
490 vertex_dirs[2] = vertex_dirs[3];
500 vertex_dirs[0] = vertex_dirs[3];
501 vertex_dirs[3] = vertex_dirs[2];
502 vertex_dirs[2] = vertex_dirs[1];
514 vertex_dirs[0] = vertex_dirs[1];
515 vertex_dirs[1] = vertex_dirs[2];
516 vertex_dirs[2] = vertex_dirs[3];
528 vertex_dirs[0] = vertex_dirs[3];
529 vertex_dirs[3] = vertex_dirs[2];
530 vertex_dirs[2] = vertex_dirs[1];
542 vertex_dirs[0] = vertex_dirs[1];
543 vertex_dirs[1] = vertex_dirs[2];
544 vertex_dirs[2] = vertex_dirs[3];
566 for(u16 i=0; i<4; i++)
569 BS/2*vertex_dirs[i].X,
570 BS/2*vertex_dirs[i].Y,
571 BS/2*vertex_dirs[i].Z
575 for(u16 i=0; i<4; i++)
577 vertex_pos[i].X *= scale.X;
578 vertex_pos[i].Y *= scale.Y;
579 vertex_pos[i].Z *= scale.Z;
580 vertex_pos[i] += pos;
584 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
585 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
586 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
588 v3f normal(dir.X, dir.Y, dir.Z);
590 u8 alpha = tile.alpha;
592 dest.push_back(FastFace());
594 FastFace& face = *dest.rbegin();
596 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
597 MapBlock_LightColor(alpha, li0, light_source),
598 core::vector2d<f32>(x0+w*abs_scale, y0+h));
599 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
600 MapBlock_LightColor(alpha, li1, light_source),
601 core::vector2d<f32>(x0, y0+h));
602 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
603 MapBlock_LightColor(alpha, li2, light_source),
604 core::vector2d<f32>(x0, y0));
605 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
606 MapBlock_LightColor(alpha, li3, light_source),
607 core::vector2d<f32>(x0+w*abs_scale, y0));
613 Nodes make a face if contents differ and solidness differs.
616 1: Face uses m1's content
617 2: Face uses m2's content
618 equivalent: Whether the blocks share the same face (eg. water and glass)
620 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
622 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
623 INodeDefManager *ndef)
627 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
630 bool contents_differ = (m1 != m2);
632 const ContentFeatures &f1 = ndef->get(m1);
633 const ContentFeatures &f2 = ndef->get(m2);
635 // Contents don't differ for different forms of same liquid
636 if(f1.sameLiquid(f2))
637 contents_differ = false;
639 u8 c1 = f1.solidness;
640 u8 c2 = f2.solidness;
642 bool solidness_differs = (c1 != c2);
643 bool makes_face = contents_differ && solidness_differs;
645 if(makes_face == false)
649 c1 = f1.visual_solidness;
651 c2 = f2.visual_solidness;
655 // If same solidness, liquid takes precense
669 Gets nth node tile (0 <= n <= 5).
671 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
673 INodeDefManager *ndef = data->m_gamedef->ndef();
674 TileSpec spec = ndef->get(mn).tiles[tileindex];
675 // Apply temporary crack
676 if (p == data->m_crack_pos_relative)
677 spec.material_flags |= MATERIAL_FLAG_CRACK;
682 Gets node tile given a face direction.
684 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
686 INodeDefManager *ndef = data->m_gamedef->ndef();
688 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
689 // (0,0,1), (0,0,-1) or (0,0,0)
690 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
692 // Convert direction to single integer for table lookup
697 // 4 = invalid, treat as (0,0,0)
701 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
703 // Get rotation for things like chests
704 u8 facedir = mn.getFaceDir(ndef);
706 static const u16 dir_to_tile[24 * 16] =
708 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
709 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
710 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
711 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
712 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
714 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
715 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
716 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
717 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
719 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
720 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
721 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
722 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
724 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
725 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
726 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
727 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
729 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
730 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
731 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
732 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
734 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
735 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
736 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
737 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
740 u16 tile_index=facedir*16 + dir_i;
741 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
742 spec.rotation=dir_to_tile[tile_index + 1];
743 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
747 static void getTileInfo(
751 const v3s16 &face_dir,
755 v3s16 &face_dir_corrected,
761 VoxelManipulator &vmanip = data->m_vmanip;
762 INodeDefManager *ndef = data->m_gamedef->ndef();
763 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
765 MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
767 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
768 if (n0.getContent() == CONTENT_IGNORE) {
773 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
775 if (n1.getContent() == CONTENT_IGNORE) {
781 bool equivalent = false;
782 u8 mf = face_contents(n0.getContent(), n1.getContent(),
795 tile = getNodeTile(n0, p, face_dir, data);
797 face_dir_corrected = face_dir;
798 light_source = ndef->get(n0).light_source;
802 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
803 p_corrected = p + face_dir;
804 face_dir_corrected = -face_dir;
805 light_source = ndef->get(n1).light_source;
808 // eg. water and glass
810 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
812 if(data->m_smooth_lighting == false)
814 lights[0] = lights[1] = lights[2] = lights[3] =
815 getFaceLight(n0, n1, face_dir, ndef);
819 v3s16 vertex_dirs[4];
820 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
821 for(u16 i=0; i<4; i++)
823 lights[i] = getSmoothLight(
824 blockpos_nodes + p_corrected,
825 vertex_dirs[i], data);
834 translate_dir: unit vector with only one of x, y or z
835 face_dir: unit vector with only one of x, y or z
837 static void updateFastFaceRow(
844 std::vector<FastFace> &dest)
848 u16 continuous_tiles_count = 0;
850 bool makes_face = false;
852 v3s16 face_dir_corrected;
853 u16 lights[4] = {0,0,0,0};
856 getTileInfo(data, p, face_dir,
857 makes_face, p_corrected, face_dir_corrected,
858 lights, tile, light_source);
860 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
862 // If tiling can be done, this is set to false in the next step
863 bool next_is_different = true;
867 bool next_makes_face = false;
868 v3s16 next_p_corrected;
869 v3s16 next_face_dir_corrected;
870 u16 next_lights[4] = {0,0,0,0};
872 u8 next_light_source = 0;
874 // If at last position, there is nothing to compare to and
875 // the face must be drawn anyway
876 if(j != MAP_BLOCKSIZE - 1)
878 p_next = p + translate_dir;
880 getTileInfo(data, p_next, face_dir,
881 next_makes_face, next_p_corrected,
882 next_face_dir_corrected, next_lights,
883 next_tile, next_light_source);
885 if(next_makes_face == makes_face
886 && next_p_corrected == p_corrected + translate_dir
887 && next_face_dir_corrected == face_dir_corrected
888 && next_lights[0] == lights[0]
889 && next_lights[1] == lights[1]
890 && next_lights[2] == lights[2]
891 && next_lights[3] == lights[3]
893 && tile.rotation == 0
894 && next_light_source == light_source)
896 next_is_different = false;
900 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
901 next_makes_face != makes_face ? 1 : 0);
902 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
903 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
904 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
905 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
906 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
907 (next_lights[0] != lights[0] ||
908 next_lights[0] != lights[0] ||
909 next_lights[0] != lights[0] ||
910 next_lights[0] != lights[0]) ? 1 : 0);
911 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
912 !(next_tile == tile) ? 1 : 0);
915 /*g_profiler->add("Meshgen: Total faces checked", 1);
917 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
920 g_profiler->add("Meshgen: diff: last position", 1);*/
923 continuous_tiles_count++;
925 if(next_is_different)
928 Create a face if there should be one
932 // Floating point conversion of the position vector
933 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
934 // Center point of face (kind of)
935 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
936 if(continuous_tiles_count != 1)
937 sp += translate_dir_f;
940 if(translate_dir.X != 0) {
941 scale.X = continuous_tiles_count;
943 if(translate_dir.Y != 0) {
944 scale.Y = continuous_tiles_count;
946 if(translate_dir.Z != 0) {
947 scale.Z = continuous_tiles_count;
950 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
951 sp, face_dir_corrected, scale, light_source,
954 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
955 for(int i = 1; i < continuous_tiles_count; i++){
956 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
960 continuous_tiles_count = 0;
962 makes_face = next_makes_face;
963 p_corrected = next_p_corrected;
964 face_dir_corrected = next_face_dir_corrected;
965 lights[0] = next_lights[0];
966 lights[1] = next_lights[1];
967 lights[2] = next_lights[2];
968 lights[3] = next_lights[3];
970 light_source = next_light_source;
977 static void updateAllFastFaceRows(MeshMakeData *data,
978 std::vector<FastFace> &dest)
981 Go through every y,z and get top(y+) faces in rows of x+
983 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
984 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
985 updateFastFaceRow(data,
989 v3s16(0,1,0), //face dir
996 Go through every x,y and get right(x+) faces in rows of z+
998 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
999 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1000 updateFastFaceRow(data,
1004 v3s16(1,0,0), //face dir
1011 Go through every y,z and get back(z+) faces in rows of x+
1013 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
1014 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1015 updateFastFaceRow(data,
1019 v3s16(0,0,1), //face dir
1030 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1031 m_mesh(new scene::SMesh()),
1032 m_minimap_mapblock(NULL),
1033 m_gamedef(data->m_gamedef),
1034 m_tsrc(m_gamedef->getTextureSource()),
1035 m_shdrsrc(m_gamedef->getShaderSource()),
1036 m_animation_force_timer(0), // force initial animation
1038 m_crack_materials(),
1039 m_highlighted_materials(),
1040 m_last_daynight_ratio((u32) -1),
1043 m_enable_shaders = data->m_use_shaders;
1044 m_enable_highlighting = g_settings->getBool("enable_node_highlighting");
1046 if (g_settings->getBool("enable_minimap")) {
1047 m_minimap_mapblock = new MinimapMapblock;
1048 m_minimap_mapblock->getMinimapNodes(
1049 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1052 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1053 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1054 //TimeTaker timer1("MapBlockMesh()");
1056 std::vector<FastFace> fastfaces_new;
1057 fastfaces_new.reserve(512);
1060 We are including the faces of the trailing edges of the block.
1061 This means that when something changes, the caller must
1062 also update the meshes of the blocks at the leading edges.
1064 NOTE: This is the slowest part of this method.
1067 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1068 //TimeTaker timer2("updateAllFastFaceRows()");
1069 updateAllFastFaceRows(data, fastfaces_new);
1074 Convert FastFaces to MeshCollector
1077 MeshCollector collector;
1080 // avg 0ms (100ms spikes when loading textures the first time)
1081 // (NOTE: probably outdated)
1082 //TimeTaker timer2("MeshCollector building");
1084 for(u32 i=0; i<fastfaces_new.size(); i++)
1086 FastFace &f = fastfaces_new[i];
1088 const u16 indices[] = {0,1,2,2,3,0};
1089 const u16 indices_alternate[] = {0,1,3,2,3,1};
1091 if(f.tile.texture == NULL)
1094 const u16 *indices_p = indices;
1097 Revert triangles for nicer looking gradient if vertices
1098 1 and 3 have same color or 0 and 2 have different color.
1099 getRed() is the day color.
1101 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1102 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1103 indices_p = indices_alternate;
1105 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1110 Add special graphics:
1117 mapblock_mesh_generate_special(data, collector);
1119 m_highlight_mesh_color = data->m_highlight_mesh_color;
1122 Convert MeshCollector to SMesh
1125 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1127 PreMeshBuffer &p = collector.prebuffers[i];
1129 // Generate animation data
1131 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1133 // Find the texture name plus ^[crack:N:
1134 std::ostringstream os(std::ios::binary);
1135 os<<m_tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1136 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1137 os<<"o"; // use ^[cracko
1138 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1139 m_crack_materials.insert(std::make_pair(i, os.str()));
1140 // Replace tile texture with the cracked one
1141 p.tile.texture = m_tsrc->getTextureForMesh(
1143 &p.tile.texture_id);
1145 // - Texture animation
1146 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1148 // Add to MapBlockMesh in order to animate these tiles
1149 m_animation_tiles[i] = p.tile;
1150 m_animation_frames[i] = 0;
1151 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1152 // Get starting position from noise
1153 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1154 data->m_blockpos.X, data->m_blockpos.Y,
1155 data->m_blockpos.Z, 0));
1157 // Play all synchronized
1158 m_animation_frame_offsets[i] = 0;
1160 // Replace tile texture with the first animation frame
1161 FrameSpec animation_frame = p.tile.frames[0];
1162 p.tile.texture = animation_frame.texture;
1165 if(m_enable_highlighting && p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED)
1166 m_highlighted_materials.push_back(i);
1168 for(u32 j = 0; j < p.vertices.size(); j++)
1170 video::S3DVertexTangents *vertex = &p.vertices[j];
1171 // Note applyFacesShading second parameter is precalculated sqrt
1172 // value for speed improvement
1173 // Skip it for lightsources and top faces.
1174 video::SColor &vc = vertex->Color;
1175 if (!vc.getBlue()) {
1176 if (vertex->Normal.Y < -0.5) {
1177 applyFacesShading (vc, 0.447213);
1178 } else if (vertex->Normal.X > 0.5) {
1179 applyFacesShading (vc, 0.670820);
1180 } else if (vertex->Normal.X < -0.5) {
1181 applyFacesShading (vc, 0.670820);
1182 } else if (vertex->Normal.Z > 0.5) {
1183 applyFacesShading (vc, 0.836660);
1184 } else if (vertex->Normal.Z < -0.5) {
1185 applyFacesShading (vc, 0.836660);
1188 if(!m_enable_shaders)
1190 // - Classic lighting (shaders handle this by themselves)
1191 // Set initial real color and store for later updates
1192 u8 day = vc.getRed();
1193 u8 night = vc.getGreen();
1194 finalColorBlend(vc, day, night, 1000);
1196 m_daynight_diffs[i][j] = std::make_pair(day, night);
1201 video::SMaterial material;
1202 material.setFlag(video::EMF_LIGHTING, false);
1203 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1204 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1205 material.setFlag(video::EMF_FOG_ENABLE, true);
1206 material.setTexture(0, p.tile.texture);
1208 if (p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) {
1209 material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
1211 if (m_enable_shaders) {
1212 material.MaterialType = m_shdrsrc->getShaderInfo(p.tile.shader_id).material;
1213 p.tile.applyMaterialOptionsWithShaders(material);
1214 if (p.tile.normal_texture) {
1215 material.setTexture(1, p.tile.normal_texture);
1217 material.setTexture(2, p.tile.flags_texture);
1219 p.tile.applyMaterialOptions(material);
1223 // Create meshbuffer
1224 scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
1226 buf->Material = material;
1228 m_mesh->addMeshBuffer(buf);
1231 buf->append(&p.vertices[0], p.vertices.size(),
1232 &p.indices[0], p.indices.size());
1234 m_camera_offset = camera_offset;
1237 Do some stuff to the mesh
1240 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1242 if (m_enable_shaders) {
1243 scene::IMeshManipulator* meshmanip = m_gamedef->getSceneManager()->getMeshManipulator();
1244 meshmanip->recalculateTangents(m_mesh, true, false, false);
1250 // Usually 1-700 faces and 1-7 materials
1251 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1252 <<"and uses "<<m_mesh->getMeshBufferCount()
1253 <<" materials (meshbuffers)"<<std::endl;
1256 // Use VBO for mesh (this just would set this for ever buffer)
1257 // This will lead to infinite memory usage because or irrlicht.
1258 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1261 NOTE: If that is enabled, some kind of a queue to the main
1262 thread should be made which would call irrlicht to delete
1263 the hardware buffer and then delete the mesh
1267 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1269 // Check if animation is required for this mesh
1271 !m_crack_materials.empty() ||
1272 !m_daynight_diffs.empty() ||
1273 !m_animation_tiles.empty() ||
1274 !m_highlighted_materials.empty();
1277 MapBlockMesh::~MapBlockMesh()
1281 delete m_minimap_mapblock;
1284 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1286 if(!m_has_animation)
1288 m_animation_force_timer = 100000;
1292 m_animation_force_timer = myrand_range(5, 100);
1295 if(crack != m_last_crack)
1297 for(std::map<u32, std::string>::iterator
1298 i = m_crack_materials.begin();
1299 i != m_crack_materials.end(); ++i)
1301 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1302 std::string basename = i->second;
1304 // Create new texture name from original
1305 std::ostringstream os;
1306 os<<basename<<crack;
1307 u32 new_texture_id = 0;
1308 video::ITexture *new_texture =
1309 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1310 buf->getMaterial().setTexture(0, new_texture);
1312 // If the current material is also animated,
1313 // update animation info
1314 std::map<u32, TileSpec>::iterator anim_iter =
1315 m_animation_tiles.find(i->first);
1316 if(anim_iter != m_animation_tiles.end()){
1317 TileSpec &tile = anim_iter->second;
1318 tile.texture = new_texture;
1319 tile.texture_id = new_texture_id;
1320 // force animation update
1321 m_animation_frames[i->first] = -1;
1325 m_last_crack = crack;
1328 // Texture animation
1329 for(std::map<u32, TileSpec>::iterator
1330 i = m_animation_tiles.begin();
1331 i != m_animation_tiles.end(); ++i)
1333 const TileSpec &tile = i->second;
1334 // Figure out current frame
1335 int frameoffset = m_animation_frame_offsets[i->first];
1336 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1337 + frameoffset) % tile.animation_frame_count;
1338 // If frame doesn't change, skip
1339 if(frame == m_animation_frames[i->first])
1342 m_animation_frames[i->first] = frame;
1344 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1346 FrameSpec animation_frame = tile.frames[frame];
1347 buf->getMaterial().setTexture(0, animation_frame.texture);
1348 if (m_enable_shaders) {
1349 if (animation_frame.normal_texture) {
1350 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1352 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1356 // Day-night transition
1357 if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
1359 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1360 i = m_daynight_diffs.begin();
1361 i != m_daynight_diffs.end(); ++i)
1363 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1364 video::S3DVertexTangents *vertices = (video::S3DVertexTangents *)buf->getVertices();
1365 for(std::map<u32, std::pair<u8, u8 > >::iterator
1366 j = i->second.begin();
1367 j != i->second.end(); ++j)
1369 u8 day = j->second.first;
1370 u8 night = j->second.second;
1371 finalColorBlend(vertices[j->first].Color, day, night, daynight_ratio);
1374 m_last_daynight_ratio = daynight_ratio;
1377 // Node highlighting
1378 if (m_enable_highlighting) {
1379 u8 day = m_highlight_mesh_color.getRed();
1380 u8 night = m_highlight_mesh_color.getGreen();
1382 finalColorBlend(hc, day, night, daynight_ratio);
1383 float sin_r = 0.07 * sin(1.5 * time);
1384 float sin_g = 0.07 * sin(1.5 * time + irr::core::PI * 0.5);
1385 float sin_b = 0.07 * sin(1.5 * time + irr::core::PI);
1386 hc.setRed(core::clamp(core::round32(hc.getRed() * (0.8 + sin_r)), 0, 255));
1387 hc.setGreen(core::clamp(core::round32(hc.getGreen() * (0.8 + sin_g)), 0, 255));
1388 hc.setBlue(core::clamp(core::round32(hc.getBlue() * (0.8 + sin_b)), 0, 255));
1390 for(std::list<u32>::iterator
1391 i = m_highlighted_materials.begin();
1392 i != m_highlighted_materials.end(); ++i)
1394 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i);
1395 video::S3DVertexTangents *vertices = (video::S3DVertexTangents*)buf->getVertices();
1396 for (u32 j = 0; j < buf->getVertexCount() ;j++)
1397 vertices[j].Color = hc;
1404 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1406 if (camera_offset != m_camera_offset) {
1407 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1408 m_camera_offset = camera_offset;
1416 void MeshCollector::append(const TileSpec &tile,
1417 const video::S3DVertex *vertices, u32 numVertices,
1418 const u16 *indices, u32 numIndices)
1420 if (numIndices > 65535) {
1421 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1425 PreMeshBuffer *p = NULL;
1426 for (u32 i = 0; i < prebuffers.size(); i++) {
1427 PreMeshBuffer &pp = prebuffers[i];
1428 if (pp.tile != tile)
1430 if (pp.indices.size() + numIndices > 65535)
1440 prebuffers.push_back(pp);
1441 p = &prebuffers[prebuffers.size() - 1];
1444 u32 vertex_count = p->vertices.size();
1445 for (u32 i = 0; i < numIndices; i++) {
1446 u32 j = indices[i] + vertex_count;
1447 p->indices.push_back(j);
1450 for (u32 i = 0; i < numVertices; i++) {
1451 video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal,
1452 vertices[i].Color, vertices[i].TCoords);
1453 p->vertices.push_back(vert);
1458 MeshCollector - for meshnodes and converted drawtypes.
1461 void MeshCollector::append(const TileSpec &tile,
1462 const video::S3DVertex *vertices, u32 numVertices,
1463 const u16 *indices, u32 numIndices,
1464 v3f pos, video::SColor c)
1466 if (numIndices > 65535) {
1467 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1471 PreMeshBuffer *p = NULL;
1472 for (u32 i = 0; i < prebuffers.size(); i++) {
1473 PreMeshBuffer &pp = prebuffers[i];
1476 if(pp.indices.size() + numIndices > 65535)
1486 prebuffers.push_back(pp);
1487 p = &prebuffers[prebuffers.size() - 1];
1490 u32 vertex_count = p->vertices.size();
1491 for (u32 i = 0; i < numIndices; i++) {
1492 u32 j = indices[i] + vertex_count;
1493 p->indices.push_back(j);
1496 for (u32 i = 0; i < numVertices; i++) {
1497 video::S3DVertexTangents vert(vertices[i].Pos + pos, vertices[i].Normal,
1498 c, vertices[i].TCoords);
1499 p->vertices.push_back(vert);