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 static void applyContrast(video::SColor& color, float factor)
37 color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255));
38 color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255));
39 color.setBlue(core::clamp(core::round32(color.getBlue()*factor), 0, 255));
46 MeshMakeData::MeshMakeData(IGameDef *gamedef):
48 m_blockpos(-1337,-1337,-1337),
49 m_crack_pos_relative(-1337, -1337, -1337),
50 m_smooth_lighting(false),
54 void MeshMakeData::fill(MapBlock *block)
56 m_blockpos = block->getPos();
58 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
64 // Allocate this block + neighbors
66 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
67 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
70 //TimeTaker timer("copy central block data");
74 block->copyTo(m_vmanip);
77 //TimeTaker timer("copy neighbor block data");
81 Copy neighbors. This is lightning fast.
82 Copying only the borders would be *very* slow.
86 Map *map = block->getParent();
88 for(u16 i=0; i<26; i++)
90 const v3s16 &dir = g_26dirs[i];
91 v3s16 bp = m_blockpos + dir;
92 MapBlock *b = map->getBlockNoCreateNoEx(bp);
99 void MeshMakeData::fillSingleNode(MapNode *node)
101 m_blockpos = v3s16(0,0,0);
103 v3s16 blockpos_nodes = v3s16(0,0,0);
104 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
105 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
106 s32 volume = area.getVolume();
107 s32 our_node_index = area.index(1,1,1);
109 // Allocate this block + neighbors
111 m_vmanip.addArea(area);
114 MapNode *data = new MapNode[volume];
115 for(s32 i = 0; i < volume; i++)
117 if(i == our_node_index)
123 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
126 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
130 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
133 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
136 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
138 m_smooth_lighting = smooth_lighting;
142 Light and vertex color functions
146 Calculate non-smooth lighting at interior of node.
149 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
150 INodeDefManager *ndef)
152 u8 light = n.getLight(bank, ndef);
156 light = undiminish_light(light);
161 light = diminish_light(light);
165 return decode_light(light);
169 Calculate non-smooth lighting at interior of node.
172 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
174 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
175 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
176 return day | (night << 8);
180 Calculate non-smooth lighting at face of node.
183 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
184 v3s16 face_dir, INodeDefManager *ndef)
187 u8 l1 = n.getLight(bank, ndef);
188 u8 l2 = n2.getLight(bank, ndef);
194 // Boost light level for light sources
195 u8 light_source = MYMAX(ndef->get(n).light_source,
196 ndef->get(n2).light_source);
197 //if(light_source >= light)
198 //return decode_light(undiminish_light(light_source));
199 if(light_source > light)
200 //return decode_light(light_source);
201 light = light_source;
203 return decode_light(light);
207 Calculate non-smooth lighting at face of node.
210 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
212 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
213 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
214 return day | (night << 8);
218 Calculate smooth lighting at the XYZ- corner of p.
221 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
223 static v3s16 dirs8[8] = {
234 INodeDefManager *ndef = data->m_gamedef->ndef();
236 u16 ambient_occlusion = 0;
239 u8 light_source_max = 0;
240 for(u32 i=0; i<8; i++)
242 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
244 // if it's CONTENT_IGNORE we can't do any light calculations
245 if (n.getContent() == CONTENT_IGNORE) {
249 const ContentFeatures &f = ndef->get(n);
250 if(f.light_source > light_source_max)
251 light_source_max = f.light_source;
252 // Check f.solidness because fast-style leaves look
254 if(f.param_type == CPT_LIGHT && f.solidness != 2)
256 light += decode_light(n.getLight(bank, ndef));
267 light /= light_count;
269 // Boost brightness around light sources
270 if(decode_light(light_source_max) >= light)
271 //return decode_light(undiminish_light(light_source_max));
272 return decode_light(light_source_max);
274 if(ambient_occlusion > 4)
276 //ambient_occlusion -= 4;
277 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
278 float light_amount = (8 - ambient_occlusion) / 4.0;
279 float light_f = (float)light / 255.0;
280 light_f = pow(light_f, 2.2f); // gamma -> linear space
281 light_f = light_f * light_amount;
282 light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
285 light = 255.0 * light_f + 0.5;
292 Calculate smooth lighting at the XYZ- corner of p.
295 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
297 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
298 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
299 return day | (night << 8);
303 Calculate smooth lighting at the given corner of p.
306 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
308 if(corner.X == 1) p.X += 1;
309 else assert(corner.X == -1);
310 if(corner.Y == 1) p.Y += 1;
311 else assert(corner.Y == -1);
312 if(corner.Z == 1) p.Z += 1;
313 else assert(corner.Z == -1);
315 return getSmoothLight(p, data);
319 Converts from day + night color values (0..255)
320 and a given daynight_ratio to the final SColor shown on screen.
322 static void finalColorBlend(video::SColor& result,
323 u8 day, u8 night, u32 daynight_ratio)
325 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
329 b += (day - night) / 13;
330 rg -= (day - night) / 23;
332 // Emphase blue a bit in darker places
333 // Each entry of this array represents a range of 8 blue levels
334 static u8 emphase_blue_when_dark[32] = {
335 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
336 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
338 b += emphase_blue_when_dark[b / 8];
339 b = irr::core::clamp (b, 0, 255);
341 // Artificial light is yellow-ish
342 static u8 emphase_yellow_when_artificial[16] = {
343 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
345 rg += emphase_yellow_when_artificial[night/16];
346 rg = irr::core::clamp (rg, 0, 255);
354 Mesh generation helpers
358 vertex_dirs: v3s16[4]
360 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
363 If looked from outside the node towards the face, the corners are:
369 if(dir == v3s16(0,0,1))
371 // If looking towards z+, this is the face that is behind
372 // the center point, facing towards z+.
373 vertex_dirs[0] = v3s16(-1,-1, 1);
374 vertex_dirs[1] = v3s16( 1,-1, 1);
375 vertex_dirs[2] = v3s16( 1, 1, 1);
376 vertex_dirs[3] = v3s16(-1, 1, 1);
378 else if(dir == v3s16(0,0,-1))
381 vertex_dirs[0] = v3s16( 1,-1,-1);
382 vertex_dirs[1] = v3s16(-1,-1,-1);
383 vertex_dirs[2] = v3s16(-1, 1,-1);
384 vertex_dirs[3] = v3s16( 1, 1,-1);
386 else if(dir == v3s16(1,0,0))
389 vertex_dirs[0] = v3s16( 1,-1, 1);
390 vertex_dirs[1] = v3s16( 1,-1,-1);
391 vertex_dirs[2] = v3s16( 1, 1,-1);
392 vertex_dirs[3] = v3s16( 1, 1, 1);
394 else if(dir == v3s16(-1,0,0))
397 vertex_dirs[0] = v3s16(-1,-1,-1);
398 vertex_dirs[1] = v3s16(-1,-1, 1);
399 vertex_dirs[2] = v3s16(-1, 1, 1);
400 vertex_dirs[3] = v3s16(-1, 1,-1);
402 else if(dir == v3s16(0,1,0))
404 // faces towards Y+ (assume Z- as "down" in texture)
405 vertex_dirs[0] = v3s16( 1, 1,-1);
406 vertex_dirs[1] = v3s16(-1, 1,-1);
407 vertex_dirs[2] = v3s16(-1, 1, 1);
408 vertex_dirs[3] = v3s16( 1, 1, 1);
410 else if(dir == v3s16(0,-1,0))
412 // faces towards Y- (assume Z+ as "down" in texture)
413 vertex_dirs[0] = v3s16( 1,-1, 1);
414 vertex_dirs[1] = v3s16(-1,-1, 1);
415 vertex_dirs[2] = v3s16(-1,-1,-1);
416 vertex_dirs[3] = v3s16( 1,-1,-1);
423 video::S3DVertex vertices[4]; // Precalculated vertices
426 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
427 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
431 // Position is at the center of the cube.
440 v3s16 vertex_dirs[4];
441 getNodeVertexDirs(dir, vertex_dirs);
445 switch (tile.rotation)
451 vertex_dirs[0] = vertex_dirs[3];
452 vertex_dirs[3] = vertex_dirs[2];
453 vertex_dirs[2] = vertex_dirs[1];
463 vertex_dirs[0] = vertex_dirs[2];
466 vertex_dirs[1] = vertex_dirs[3];
477 vertex_dirs[0] = vertex_dirs[1];
478 vertex_dirs[1] = vertex_dirs[2];
479 vertex_dirs[2] = vertex_dirs[3];
489 vertex_dirs[0] = vertex_dirs[3];
490 vertex_dirs[3] = vertex_dirs[2];
491 vertex_dirs[2] = vertex_dirs[1];
503 vertex_dirs[0] = vertex_dirs[1];
504 vertex_dirs[1] = vertex_dirs[2];
505 vertex_dirs[2] = vertex_dirs[3];
517 vertex_dirs[0] = vertex_dirs[3];
518 vertex_dirs[3] = vertex_dirs[2];
519 vertex_dirs[2] = vertex_dirs[1];
531 vertex_dirs[0] = vertex_dirs[1];
532 vertex_dirs[1] = vertex_dirs[2];
533 vertex_dirs[2] = vertex_dirs[3];
555 for(u16 i=0; i<4; i++)
558 BS/2*vertex_dirs[i].X,
559 BS/2*vertex_dirs[i].Y,
560 BS/2*vertex_dirs[i].Z
564 for(u16 i=0; i<4; i++)
566 vertex_pos[i].X *= scale.X;
567 vertex_pos[i].Y *= scale.Y;
568 vertex_pos[i].Z *= scale.Z;
569 vertex_pos[i] += pos;
573 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
574 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
575 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
577 v3f normal(dir.X, dir.Y, dir.Z);
579 u8 alpha = tile.alpha;
581 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
582 MapBlock_LightColor(alpha, li0, light_source),
583 core::vector2d<f32>(x0+w*abs_scale, y0+h));
584 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
585 MapBlock_LightColor(alpha, li1, light_source),
586 core::vector2d<f32>(x0, y0+h));
587 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
588 MapBlock_LightColor(alpha, li2, light_source),
589 core::vector2d<f32>(x0, y0));
590 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
591 MapBlock_LightColor(alpha, li3, light_source),
592 core::vector2d<f32>(x0+w*abs_scale, y0));
595 dest.push_back(face);
599 Nodes make a face if contents differ and solidness differs.
602 1: Face uses m1's content
603 2: Face uses m2's content
604 equivalent: Whether the blocks share the same face (eg. water and glass)
606 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
608 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
609 INodeDefManager *ndef)
613 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
616 bool contents_differ = (m1 != m2);
618 const ContentFeatures &f1 = ndef->get(m1);
619 const ContentFeatures &f2 = ndef->get(m2);
621 // Contents don't differ for different forms of same liquid
622 if(f1.sameLiquid(f2))
623 contents_differ = false;
625 u8 c1 = f1.solidness;
626 u8 c2 = f2.solidness;
628 bool solidness_differs = (c1 != c2);
629 bool makes_face = contents_differ && solidness_differs;
631 if(makes_face == false)
635 c1 = f1.visual_solidness;
637 c2 = f2.visual_solidness;
641 // If same solidness, liquid takes precense
655 Gets nth node tile (0 <= n <= 5).
657 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
659 INodeDefManager *ndef = data->m_gamedef->ndef();
660 TileSpec spec = ndef->get(mn).tiles[tileindex];
661 // Apply temporary crack
662 if(p == data->m_crack_pos_relative)
664 spec.material_flags |= MATERIAL_FLAG_CRACK;
670 Gets node tile given a face direction.
672 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
674 INodeDefManager *ndef = data->m_gamedef->ndef();
676 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
677 // (0,0,1), (0,0,-1) or (0,0,0)
678 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
680 // Convert direction to single integer for table lookup
685 // 4 = invalid, treat as (0,0,0)
689 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
691 // Get rotation for things like chests
692 u8 facedir = mn.getFaceDir(ndef);
695 static const u16 dir_to_tile[24 * 16] =
697 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
698 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
699 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
700 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
701 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
703 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
704 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
705 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
706 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
708 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
709 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
710 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
711 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
713 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
714 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
715 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
716 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
718 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
719 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
720 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
721 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
723 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
724 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
725 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
726 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
729 u16 tile_index=facedir*16 + dir_i;
730 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
731 spec.rotation=dir_to_tile[tile_index + 1];
732 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
736 static void getTileInfo(
744 v3s16 &face_dir_corrected,
750 VoxelManipulator &vmanip = data->m_vmanip;
751 INodeDefManager *ndef = data->m_gamedef->ndef();
752 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
754 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
756 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
757 if (n0.getContent() == CONTENT_IGNORE ) {
761 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
764 bool equivalent = false;
765 u8 mf = face_contents(n0.getContent(), n1.getContent(),
778 tile = getNodeTile(n0, p, face_dir, data);
780 face_dir_corrected = face_dir;
781 light_source = ndef->get(n0).light_source;
785 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
786 p_corrected = p + face_dir;
787 face_dir_corrected = -face_dir;
788 light_source = ndef->get(n1).light_source;
791 // eg. water and glass
793 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
795 if(data->m_smooth_lighting == false)
797 lights[0] = lights[1] = lights[2] = lights[3] =
798 getFaceLight(n0, n1, face_dir, ndef);
802 v3s16 vertex_dirs[4];
803 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
804 for(u16 i=0; i<4; i++)
806 lights[i] = getSmoothLight(
807 blockpos_nodes + p_corrected,
808 vertex_dirs[i], data);
817 translate_dir: unit vector with only one of x, y or z
818 face_dir: unit vector with only one of x, y or z
820 static void updateFastFaceRow(
827 std::vector<FastFace> &dest)
831 u16 continuous_tiles_count = 0;
833 bool makes_face = false;
835 v3s16 face_dir_corrected;
836 u16 lights[4] = {0,0,0,0};
839 getTileInfo(data, p, face_dir,
840 makes_face, p_corrected, face_dir_corrected,
841 lights, tile, light_source);
843 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
845 // If tiling can be done, this is set to false in the next step
846 bool next_is_different = true;
850 bool next_makes_face = false;
851 v3s16 next_p_corrected;
852 v3s16 next_face_dir_corrected;
853 u16 next_lights[4] = {0,0,0,0};
855 u8 next_light_source = 0;
857 // If at last position, there is nothing to compare to and
858 // the face must be drawn anyway
859 if(j != MAP_BLOCKSIZE - 1)
861 p_next = p + translate_dir;
863 getTileInfo(data, p_next, face_dir,
864 next_makes_face, next_p_corrected,
865 next_face_dir_corrected, next_lights,
866 next_tile, next_light_source);
868 if(next_makes_face == makes_face
869 && next_p_corrected == p_corrected + translate_dir
870 && next_face_dir_corrected == face_dir_corrected
871 && next_lights[0] == lights[0]
872 && next_lights[1] == lights[1]
873 && next_lights[2] == lights[2]
874 && next_lights[3] == lights[3]
876 && tile.rotation == 0
877 && next_light_source == light_source)
879 next_is_different = false;
883 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
884 next_makes_face != makes_face ? 1 : 0);
885 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
886 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
887 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
888 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
889 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
890 (next_lights[0] != lights[0] ||
891 next_lights[0] != lights[0] ||
892 next_lights[0] != lights[0] ||
893 next_lights[0] != lights[0]) ? 1 : 0);
894 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
895 !(next_tile == tile) ? 1 : 0);
898 /*g_profiler->add("Meshgen: Total faces checked", 1);
900 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
903 g_profiler->add("Meshgen: diff: last position", 1);*/
906 continuous_tiles_count++;
908 if(next_is_different)
911 Create a face if there should be one
915 // Floating point conversion of the position vector
916 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
917 // Center point of face (kind of)
918 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
919 if(continuous_tiles_count != 1)
920 sp += translate_dir_f;
923 if(translate_dir.X != 0)
925 scale.X = continuous_tiles_count;
927 if(translate_dir.Y != 0)
929 scale.Y = continuous_tiles_count;
931 if(translate_dir.Z != 0)
933 scale.Z = continuous_tiles_count;
936 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
937 sp, face_dir_corrected, scale, light_source,
940 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
941 for(int i=1; i<continuous_tiles_count; i++){
942 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
946 continuous_tiles_count = 0;
948 makes_face = next_makes_face;
949 p_corrected = next_p_corrected;
950 face_dir_corrected = next_face_dir_corrected;
951 lights[0] = next_lights[0];
952 lights[1] = next_lights[1];
953 lights[2] = next_lights[2];
954 lights[3] = next_lights[3];
956 light_source = next_light_source;
963 static void updateAllFastFaceRows(MeshMakeData *data,
964 std::vector<FastFace> &dest)
967 Go through every y,z and get top(y+) faces in rows of x+
969 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
970 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
971 updateFastFaceRow(data,
975 v3s16(0,1,0), //face dir
982 Go through every x,y and get right(x+) faces in rows of z+
984 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
985 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
986 updateFastFaceRow(data,
990 v3s16(1,0,0), //face dir
997 Go through every y,z and get back(z+) faces in rows of x+
999 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
1000 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1001 updateFastFaceRow(data,
1005 v3s16(0,0,1), //face dir
1016 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1017 m_mesh(new scene::SMesh()),
1018 m_gamedef(data->m_gamedef),
1019 m_animation_force_timer(0), // force initial animation
1021 m_crack_materials(),
1022 m_last_daynight_ratio((u32) -1),
1025 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1026 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1027 //TimeTaker timer1("MapBlockMesh()");
1029 std::vector<FastFace> fastfaces_new;
1032 We are including the faces of the trailing edges of the block.
1033 This means that when something changes, the caller must
1034 also update the meshes of the blocks at the leading edges.
1036 NOTE: This is the slowest part of this method.
1039 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1040 //TimeTaker timer2("updateAllFastFaceRows()");
1041 updateAllFastFaceRows(data, fastfaces_new);
1046 Convert FastFaces to MeshCollector
1049 MeshCollector collector;
1052 // avg 0ms (100ms spikes when loading textures the first time)
1053 // (NOTE: probably outdated)
1054 //TimeTaker timer2("MeshCollector building");
1056 for(u32 i=0; i<fastfaces_new.size(); i++)
1058 FastFace &f = fastfaces_new[i];
1060 const u16 indices[] = {0,1,2,2,3,0};
1061 const u16 indices_alternate[] = {0,1,3,2,3,1};
1063 if(f.tile.texture == NULL)
1066 const u16 *indices_p = indices;
1069 Revert triangles for nicer looking gradient if vertices
1070 1 and 3 have same color or 0 and 2 have different color.
1071 getRed() is the day color.
1073 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1074 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1075 indices_p = indices_alternate;
1077 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1082 Add special graphics:
1089 mapblock_mesh_generate_special(data, collector);
1093 Convert MeshCollector to SMesh
1095 ITextureSource *tsrc = m_gamedef->tsrc();
1096 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1098 bool enable_shaders = g_settings->getBool("enable_shaders");
1100 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1102 PreMeshBuffer &p = collector.prebuffers[i];
1103 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1104 <<", p.indices.size()="<<p.indices.size()
1107 // Generate animation data
1109 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1111 // Find the texture name plus ^[crack:N:
1112 std::ostringstream os(std::ios::binary);
1113 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1114 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1115 os<<"o"; // use ^[cracko
1116 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1117 m_crack_materials.insert(std::make_pair(i, os.str()));
1118 // Replace tile texture with the cracked one
1119 p.tile.texture = tsrc->getTexture(
1121 &p.tile.texture_id);
1123 // - Texture animation
1124 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1126 // Add to MapBlockMesh in order to animate these tiles
1127 m_animation_tiles[i] = p.tile;
1128 m_animation_frames[i] = 0;
1129 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1130 // Get starting position from noise
1131 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1132 data->m_blockpos.X, data->m_blockpos.Y,
1133 data->m_blockpos.Z, 0));
1135 // Play all synchronized
1136 m_animation_frame_offsets[i] = 0;
1138 // Replace tile texture with the first animation frame
1139 FrameSpec animation_frame = p.tile.frames.find(0)->second;
1140 p.tile.texture = animation_frame.texture;
1143 for(u32 j = 0; j < p.vertices.size(); j++)
1145 // Note applyContrast second parameter is precalculated sqrt from original
1146 // values for speed improvement
1147 video::SColor &vc = p.vertices[j].Color;
1148 if(p.vertices[j].Normal.Y > 0.5) {
1149 applyContrast (vc, 1.095445);
1150 } else if (p.vertices[j].Normal.Y < -0.5) {
1151 applyContrast (vc, 0.547723);
1152 } else if (p.vertices[j].Normal.X > 0.5) {
1153 applyContrast (vc, 0.707107);
1154 } else if (p.vertices[j].Normal.X < -0.5) {
1155 applyContrast (vc, 0.707107);
1156 } else if (p.vertices[j].Normal.Z > 0.5) {
1157 applyContrast (vc, 0.894427);
1158 } else if (p.vertices[j].Normal.Z < -0.5) {
1159 applyContrast (vc, 0.894427);
1163 // - Classic lighting (shaders handle this by themselves)
1164 // Set initial real color and store for later updates
1165 u8 day = vc.getRed();
1166 u8 night = vc.getGreen();
1167 finalColorBlend(vc, day, night, 1000);
1169 m_daynight_diffs[i][j] = std::make_pair(day, night);
1174 video::SMaterial material;
1175 material.setFlag(video::EMF_LIGHTING, false);
1176 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1177 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1178 material.setFlag(video::EMF_FOG_ENABLE, true);
1179 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1180 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1181 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1182 material.setTexture(0, p.tile.texture);
1184 if (enable_shaders) {
1185 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1186 p.tile.applyMaterialOptionsWithShaders(material);
1187 if (p.tile.normal_texture) {
1188 material.setTexture(1, p.tile.normal_texture);
1189 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1191 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1194 p.tile.applyMaterialOptions(material);
1197 // Create meshbuffer
1198 // This is a "Standard MeshBuffer",
1199 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1200 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1202 buf->Material = material;
1204 m_mesh->addMeshBuffer(buf);
1207 buf->append(&p.vertices[0], p.vertices.size(),
1208 &p.indices[0], p.indices.size());
1211 m_camera_offset = camera_offset;
1214 Do some stuff to the mesh
1217 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1222 // Usually 1-700 faces and 1-7 materials
1223 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1224 <<"and uses "<<m_mesh->getMeshBufferCount()
1225 <<" materials (meshbuffers)"<<std::endl;
1228 // Use VBO for mesh (this just would set this for ever buffer)
1229 // This will lead to infinite memory usage because or irrlicht.
1230 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1233 NOTE: If that is enabled, some kind of a queue to the main
1234 thread should be made which would call irrlicht to delete
1235 the hardware buffer and then delete the mesh
1239 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1241 // Check if animation is required for this mesh
1243 !m_crack_materials.empty() ||
1244 !m_daynight_diffs.empty() ||
1245 !m_animation_tiles.empty();
1248 MapBlockMesh::~MapBlockMesh()
1254 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1256 bool enable_shaders = g_settings->getBool("enable_shaders");
1258 if(!m_has_animation)
1260 m_animation_force_timer = 100000;
1264 m_animation_force_timer = myrand_range(5, 100);
1267 if(crack != m_last_crack)
1269 for(std::map<u32, std::string>::iterator
1270 i = m_crack_materials.begin();
1271 i != m_crack_materials.end(); i++)
1273 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1274 std::string basename = i->second;
1276 // Create new texture name from original
1277 ITextureSource *tsrc = m_gamedef->getTextureSource();
1278 std::ostringstream os;
1279 os<<basename<<crack;
1280 u32 new_texture_id = 0;
1281 video::ITexture *new_texture =
1282 tsrc->getTexture(os.str(), &new_texture_id);
1283 buf->getMaterial().setTexture(0, new_texture);
1285 // If the current material is also animated,
1286 // update animation info
1287 std::map<u32, TileSpec>::iterator anim_iter =
1288 m_animation_tiles.find(i->first);
1289 if(anim_iter != m_animation_tiles.end()){
1290 TileSpec &tile = anim_iter->second;
1291 tile.texture = new_texture;
1292 tile.texture_id = new_texture_id;
1293 // force animation update
1294 m_animation_frames[i->first] = -1;
1298 m_last_crack = crack;
1301 // Texture animation
1302 for(std::map<u32, TileSpec>::iterator
1303 i = m_animation_tiles.begin();
1304 i != m_animation_tiles.end(); i++)
1306 const TileSpec &tile = i->second;
1307 // Figure out current frame
1308 int frameoffset = m_animation_frame_offsets[i->first];
1309 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1310 + frameoffset) % tile.animation_frame_count;
1311 // If frame doesn't change, skip
1312 if(frame == m_animation_frames[i->first])
1315 m_animation_frames[i->first] = frame;
1317 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1318 ITextureSource *tsrc = m_gamedef->getTextureSource();
1320 FrameSpec animation_frame = tile.frames.find(frame)->second;
1321 buf->getMaterial().setTexture(0, animation_frame.texture);
1322 if (enable_shaders) {
1323 if (animation_frame.normal_texture) {
1324 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1325 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1327 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1332 // Day-night transition
1333 if(daynight_ratio != m_last_daynight_ratio)
1335 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1336 i = m_daynight_diffs.begin();
1337 i != m_daynight_diffs.end(); i++)
1339 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1340 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1341 for(std::map<u32, std::pair<u8, u8 > >::iterator
1342 j = i->second.begin();
1343 j != i->second.end(); j++)
1345 u32 vertexIndex = j->first;
1346 u8 day = j->second.first;
1347 u8 night = j->second.second;
1348 finalColorBlend(vertices[vertexIndex].Color,
1349 day, night, daynight_ratio);
1352 m_last_daynight_ratio = daynight_ratio;
1358 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1360 if (camera_offset != m_camera_offset) {
1361 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1362 m_camera_offset = camera_offset;
1370 void MeshCollector::append(const TileSpec &tile,
1371 const video::S3DVertex *vertices, u32 numVertices,
1372 const u16 *indices, u32 numIndices)
1374 if(numIndices > 65535)
1376 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1380 PreMeshBuffer *p = NULL;
1381 for(u32 i=0; i<prebuffers.size(); i++)
1383 PreMeshBuffer &pp = prebuffers[i];
1386 if(pp.indices.size() + numIndices > 65535)
1397 prebuffers.push_back(pp);
1398 p = &prebuffers[prebuffers.size()-1];
1401 u32 vertex_count = p->vertices.size();
1402 for(u32 i=0; i<numIndices; i++)
1404 u32 j = indices[i] + vertex_count;
1405 p->indices.push_back(j);
1407 for(u32 i=0; i<numVertices; i++)
1409 p->vertices.push_back(vertices[i]);