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_smooth_lighting(false),
53 m_use_shaders(use_shaders)
56 void MeshMakeData::fill(MapBlock *block)
58 m_blockpos = block->getPos();
60 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
66 // Allocate this block + neighbors
68 VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE,
69 blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1));
70 m_vmanip.addArea(voxel_area);
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,
153 INodeDefManager *ndef)
155 u8 light = n.getLight(bank, ndef);
159 light = undiminish_light(light);
164 light = diminish_light(light);
168 return decode_light(light);
172 Calculate non-smooth lighting at interior of node.
175 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
177 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
178 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
179 return day | (night << 8);
183 Calculate non-smooth lighting at face of node.
186 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
187 v3s16 face_dir, INodeDefManager *ndef)
190 u8 l1 = n.getLight(bank, ndef);
191 u8 l2 = n2.getLight(bank, ndef);
197 // Boost light level for light sources
198 u8 light_source = MYMAX(ndef->get(n).light_source,
199 ndef->get(n2).light_source);
200 if(light_source > light)
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 u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data)
223 static const v3s16 dirs8[8] = {
234 INodeDefManager *ndef = data->m_gamedef->ndef();
236 u16 ambient_occlusion = 0;
238 u8 light_source_max = 0;
242 for (u32 i = 0; i < 8; i++)
244 const MapNode &n = data->m_vmanip.getNodeRefUnsafeCheckFlags(p - dirs8[i]);
246 // if it's CONTENT_IGNORE we can't do any light calculations
247 if (n.getContent() == CONTENT_IGNORE) {
251 const ContentFeatures &f = ndef->get(n);
252 if (f.light_source > light_source_max)
253 light_source_max = f.light_source;
254 // Check f.solidness because fast-style leaves look better this way
255 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
256 light_day += decode_light(n.getLightNoChecks(LIGHTBANK_DAY, &f));
257 light_night += decode_light(n.getLightNoChecks(LIGHTBANK_NIGHT, &f));
267 light_day /= light_count;
268 light_night /= light_count;
270 // Boost brightness around light sources
271 bool skip_ambient_occlusion_day = false;
272 if(decode_light(light_source_max) >= light_day) {
273 light_day = decode_light(light_source_max);
274 skip_ambient_occlusion_day = true;
277 bool skip_ambient_occlusion_night = false;
278 if(decode_light(light_source_max) >= light_night) {
279 light_night = decode_light(light_source_max);
280 skip_ambient_occlusion_night = true;
283 if (ambient_occlusion > 4)
285 static const float ao_gamma = rangelim(
286 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
288 // Table of gamma space multiply factors.
289 static const float light_amount[3] = {
290 powf(0.75, 1.0 / ao_gamma),
291 powf(0.5, 1.0 / ao_gamma),
292 powf(0.25, 1.0 / ao_gamma)
295 //calculate table index for gamma space multiplier
296 ambient_occlusion -= 5;
298 if (!skip_ambient_occlusion_day)
299 light_day = rangelim(core::round32(light_day*light_amount[ambient_occlusion]), 0, 255);
300 if (!skip_ambient_occlusion_night)
301 light_night = rangelim(core::round32(light_night*light_amount[ambient_occlusion]), 0, 255);
304 return light_day | (light_night << 8);
308 Calculate smooth lighting at the given corner of p.
311 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
313 if(corner.X == 1) p.X += 1;
314 // else corner.X == -1
315 if(corner.Y == 1) p.Y += 1;
316 // else corner.Y == -1
317 if(corner.Z == 1) p.Z += 1;
318 // else corner.Z == -1
320 return getSmoothLightCombined(p, data);
324 Converts from day + night color values (0..255)
325 and a given daynight_ratio to the final SColor shown on screen.
327 void finalColorBlend(video::SColor& result,
328 u8 day, u8 night, u32 daynight_ratio)
330 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
334 b += (day - night) / 13;
335 rg -= (day - night) / 23;
337 // Emphase blue a bit in darker places
338 // Each entry of this array represents a range of 8 blue levels
339 static const u8 emphase_blue_when_dark[32] = {
340 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
341 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
343 b += emphase_blue_when_dark[irr::core::clamp(b, 0, 255) / 8];
344 b = irr::core::clamp(b, 0, 255);
346 // Artificial light is yellow-ish
347 static const u8 emphase_yellow_when_artificial[16] = {
348 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
350 rg += emphase_yellow_when_artificial[night/16];
351 rg = irr::core::clamp(rg, 0, 255);
359 Mesh generation helpers
363 vertex_dirs: v3s16[4]
365 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
368 If looked from outside the node towards the face, the corners are:
374 if(dir == v3s16(0,0,1))
376 // If looking towards z+, this is the face that is behind
377 // the center point, facing towards z+.
378 vertex_dirs[0] = v3s16(-1,-1, 1);
379 vertex_dirs[1] = v3s16( 1,-1, 1);
380 vertex_dirs[2] = v3s16( 1, 1, 1);
381 vertex_dirs[3] = v3s16(-1, 1, 1);
383 else if(dir == v3s16(0,0,-1))
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(1,0,0))
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(0,1,0))
409 // faces towards Y+ (assume Z- as "down" in texture)
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);
428 video::S3DVertex vertices[4]; // Precalculated vertices
431 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
432 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
434 // Position is at the center of the cube.
443 v3s16 vertex_dirs[4];
444 getNodeVertexDirs(dir, vertex_dirs);
448 switch (tile.rotation)
454 vertex_dirs[0] = vertex_dirs[3];
455 vertex_dirs[3] = vertex_dirs[2];
456 vertex_dirs[2] = vertex_dirs[1];
466 vertex_dirs[0] = vertex_dirs[2];
469 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];
492 vertex_dirs[0] = vertex_dirs[3];
493 vertex_dirs[3] = vertex_dirs[2];
494 vertex_dirs[2] = vertex_dirs[1];
506 vertex_dirs[0] = vertex_dirs[1];
507 vertex_dirs[1] = vertex_dirs[2];
508 vertex_dirs[2] = vertex_dirs[3];
520 vertex_dirs[0] = vertex_dirs[3];
521 vertex_dirs[3] = vertex_dirs[2];
522 vertex_dirs[2] = vertex_dirs[1];
534 vertex_dirs[0] = vertex_dirs[1];
535 vertex_dirs[1] = vertex_dirs[2];
536 vertex_dirs[2] = vertex_dirs[3];
558 for(u16 i=0; i<4; i++)
561 BS/2*vertex_dirs[i].X,
562 BS/2*vertex_dirs[i].Y,
563 BS/2*vertex_dirs[i].Z
567 for(u16 i=0; i<4; i++)
569 vertex_pos[i].X *= scale.X;
570 vertex_pos[i].Y *= scale.Y;
571 vertex_pos[i].Z *= scale.Z;
572 vertex_pos[i] += pos;
576 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
577 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
578 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
580 v3f normal(dir.X, dir.Y, dir.Z);
582 u8 alpha = tile.alpha;
584 dest.push_back(FastFace());
586 FastFace& face = *dest.rbegin();
588 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
589 MapBlock_LightColor(alpha, li0, light_source),
590 core::vector2d<f32>(x0+w*abs_scale, y0+h));
591 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
592 MapBlock_LightColor(alpha, li1, light_source),
593 core::vector2d<f32>(x0, y0+h));
594 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
595 MapBlock_LightColor(alpha, li2, light_source),
596 core::vector2d<f32>(x0, y0));
597 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
598 MapBlock_LightColor(alpha, li3, light_source),
599 core::vector2d<f32>(x0+w*abs_scale, y0));
605 Nodes make a face if contents differ and solidness differs.
608 1: Face uses m1's content
609 2: Face uses m2's content
610 equivalent: Whether the blocks share the same face (eg. water and glass)
612 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
614 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
615 INodeDefManager *ndef)
619 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
622 bool contents_differ = (m1 != m2);
624 const ContentFeatures &f1 = ndef->get(m1);
625 const ContentFeatures &f2 = ndef->get(m2);
627 // Contents don't differ for different forms of same liquid
628 if(f1.sameLiquid(f2))
629 contents_differ = false;
631 u8 c1 = f1.solidness;
632 u8 c2 = f2.solidness;
634 bool solidness_differs = (c1 != c2);
635 bool makes_face = contents_differ && solidness_differs;
637 if(makes_face == false)
641 c1 = f1.visual_solidness;
643 c2 = f2.visual_solidness;
647 // If same solidness, liquid takes precense
661 Gets nth node tile (0 <= n <= 5).
663 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
665 INodeDefManager *ndef = data->m_gamedef->ndef();
666 TileSpec spec = ndef->get(mn).tiles[tileindex];
667 // Apply temporary crack
668 if (p == data->m_crack_pos_relative)
669 spec.material_flags |= MATERIAL_FLAG_CRACK;
674 Gets node tile given a face direction.
676 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
678 INodeDefManager *ndef = data->m_gamedef->ndef();
680 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
681 // (0,0,1), (0,0,-1) or (0,0,0)
682 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
684 // Convert direction to single integer for table lookup
689 // 4 = invalid, treat as (0,0,0)
693 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
695 // Get rotation for things like chests
696 u8 facedir = mn.getFaceDir(ndef);
698 static const u16 dir_to_tile[24 * 16] =
700 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
701 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
702 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
703 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
704 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
706 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
707 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
708 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
709 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
711 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
712 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
713 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
714 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
716 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
717 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
718 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
719 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
721 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
722 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
723 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
724 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
726 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
727 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
728 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
729 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
732 u16 tile_index=facedir*16 + dir_i;
733 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
734 spec.rotation=dir_to_tile[tile_index + 1];
735 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
739 static void getTileInfo(
743 const v3s16 &face_dir,
747 v3s16 &face_dir_corrected,
753 VoxelManipulator &vmanip = data->m_vmanip;
754 INodeDefManager *ndef = data->m_gamedef->ndef();
755 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
757 MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
759 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
760 if (n0.getContent() == CONTENT_IGNORE) {
765 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
767 if (n1.getContent() == CONTENT_IGNORE) {
773 bool equivalent = false;
774 u8 mf = face_contents(n0.getContent(), n1.getContent(),
787 tile = getNodeTile(n0, p, face_dir, data);
789 face_dir_corrected = face_dir;
790 light_source = ndef->get(n0).light_source;
794 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
795 p_corrected = p + face_dir;
796 face_dir_corrected = -face_dir;
797 light_source = ndef->get(n1).light_source;
800 // eg. water and glass
802 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
804 if(data->m_smooth_lighting == false)
806 lights[0] = lights[1] = lights[2] = lights[3] =
807 getFaceLight(n0, n1, face_dir, ndef);
811 v3s16 vertex_dirs[4];
812 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
813 for(u16 i=0; i<4; i++)
815 lights[i] = getSmoothLight(
816 blockpos_nodes + p_corrected,
817 vertex_dirs[i], data);
826 translate_dir: unit vector with only one of x, y or z
827 face_dir: unit vector with only one of x, y or z
829 static void updateFastFaceRow(
836 std::vector<FastFace> &dest)
840 u16 continuous_tiles_count = 0;
842 bool makes_face = false;
844 v3s16 face_dir_corrected;
845 u16 lights[4] = {0,0,0,0};
848 getTileInfo(data, p, face_dir,
849 makes_face, p_corrected, face_dir_corrected,
850 lights, tile, light_source);
852 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
854 // If tiling can be done, this is set to false in the next step
855 bool next_is_different = true;
859 bool next_makes_face = false;
860 v3s16 next_p_corrected;
861 v3s16 next_face_dir_corrected;
862 u16 next_lights[4] = {0,0,0,0};
864 u8 next_light_source = 0;
866 // If at last position, there is nothing to compare to and
867 // the face must be drawn anyway
868 if(j != MAP_BLOCKSIZE - 1)
870 p_next = p + translate_dir;
872 getTileInfo(data, p_next, face_dir,
873 next_makes_face, next_p_corrected,
874 next_face_dir_corrected, next_lights,
875 next_tile, next_light_source);
877 if(next_makes_face == makes_face
878 && next_p_corrected == p_corrected + translate_dir
879 && next_face_dir_corrected == face_dir_corrected
880 && next_lights[0] == lights[0]
881 && next_lights[1] == lights[1]
882 && next_lights[2] == lights[2]
883 && next_lights[3] == lights[3]
885 && tile.rotation == 0
886 && next_light_source == light_source)
888 next_is_different = false;
892 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
893 next_makes_face != makes_face ? 1 : 0);
894 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
895 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
896 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
897 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
898 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
899 (next_lights[0] != lights[0] ||
900 next_lights[0] != lights[0] ||
901 next_lights[0] != lights[0] ||
902 next_lights[0] != lights[0]) ? 1 : 0);
903 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
904 !(next_tile == tile) ? 1 : 0);
907 /*g_profiler->add("Meshgen: Total faces checked", 1);
909 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
912 g_profiler->add("Meshgen: diff: last position", 1);*/
915 continuous_tiles_count++;
917 if(next_is_different)
920 Create a face if there should be one
924 // Floating point conversion of the position vector
925 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
926 // Center point of face (kind of)
927 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
928 if(continuous_tiles_count != 1)
929 sp += translate_dir_f;
932 if(translate_dir.X != 0) {
933 scale.X = continuous_tiles_count;
935 if(translate_dir.Y != 0) {
936 scale.Y = continuous_tiles_count;
938 if(translate_dir.Z != 0) {
939 scale.Z = continuous_tiles_count;
942 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
943 sp, face_dir_corrected, scale, light_source,
946 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
947 for(int i = 1; i < continuous_tiles_count; i++){
948 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
952 continuous_tiles_count = 0;
954 makes_face = next_makes_face;
955 p_corrected = next_p_corrected;
956 face_dir_corrected = next_face_dir_corrected;
957 lights[0] = next_lights[0];
958 lights[1] = next_lights[1];
959 lights[2] = next_lights[2];
960 lights[3] = next_lights[3];
962 light_source = next_light_source;
969 static void updateAllFastFaceRows(MeshMakeData *data,
970 std::vector<FastFace> &dest)
973 Go through every y,z and get top(y+) faces in rows of x+
975 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
976 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
977 updateFastFaceRow(data,
981 v3s16(0,1,0), //face dir
988 Go through every x,y and get right(x+) faces in rows of z+
990 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
991 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
992 updateFastFaceRow(data,
996 v3s16(1,0,0), //face dir
1003 Go through every y,z and get back(z+) faces in rows of x+
1005 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
1006 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1007 updateFastFaceRow(data,
1011 v3s16(0,0,1), //face dir
1022 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1023 m_mesh(new scene::SMesh()),
1024 m_minimap_mapblock(NULL),
1025 m_gamedef(data->m_gamedef),
1026 m_tsrc(m_gamedef->getTextureSource()),
1027 m_shdrsrc(m_gamedef->getShaderSource()),
1028 m_animation_force_timer(0), // force initial animation
1030 m_crack_materials(),
1031 m_last_daynight_ratio((u32) -1),
1034 m_enable_shaders = data->m_use_shaders;
1036 if (g_settings->getBool("enable_minimap")) {
1037 m_minimap_mapblock = new MinimapMapblock;
1038 m_minimap_mapblock->getMinimapNodes(
1039 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1042 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1043 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1044 //TimeTaker timer1("MapBlockMesh()");
1046 std::vector<FastFace> fastfaces_new;
1047 fastfaces_new.reserve(512);
1050 We are including the faces of the trailing edges of the block.
1051 This means that when something changes, the caller must
1052 also update the meshes of the blocks at the leading edges.
1054 NOTE: This is the slowest part of this method.
1057 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1058 //TimeTaker timer2("updateAllFastFaceRows()");
1059 updateAllFastFaceRows(data, fastfaces_new);
1064 Convert FastFaces to MeshCollector
1067 MeshCollector collector;
1070 // avg 0ms (100ms spikes when loading textures the first time)
1071 // (NOTE: probably outdated)
1072 //TimeTaker timer2("MeshCollector building");
1074 for(u32 i=0; i<fastfaces_new.size(); i++)
1076 FastFace &f = fastfaces_new[i];
1078 const u16 indices[] = {0,1,2,2,3,0};
1079 const u16 indices_alternate[] = {0,1,3,2,3,1};
1081 if(f.tile.texture == NULL)
1084 const u16 *indices_p = indices;
1087 Revert triangles for nicer looking gradient if vertices
1088 1 and 3 have same color or 0 and 2 have different color.
1089 getRed() is the day color.
1091 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1092 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1093 indices_p = indices_alternate;
1095 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1100 Add special graphics:
1107 mapblock_mesh_generate_special(data, collector);
1110 Convert MeshCollector to SMesh
1113 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1115 PreMeshBuffer &p = collector.prebuffers[i];
1117 // Generate animation data
1119 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1121 // Find the texture name plus ^[crack:N:
1122 std::ostringstream os(std::ios::binary);
1123 os<<m_tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1124 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1125 os<<"o"; // use ^[cracko
1126 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1127 m_crack_materials.insert(std::make_pair(i, os.str()));
1128 // Replace tile texture with the cracked one
1129 p.tile.texture = m_tsrc->getTextureForMesh(
1131 &p.tile.texture_id);
1133 // - Texture animation
1134 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1136 // Add to MapBlockMesh in order to animate these tiles
1137 m_animation_tiles[i] = p.tile;
1138 m_animation_frames[i] = 0;
1139 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1140 // Get starting position from noise
1141 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1142 data->m_blockpos.X, data->m_blockpos.Y,
1143 data->m_blockpos.Z, 0));
1145 // Play all synchronized
1146 m_animation_frame_offsets[i] = 0;
1148 // Replace tile texture with the first animation frame
1149 FrameSpec animation_frame = p.tile.frames[0];
1150 p.tile.texture = animation_frame.texture;
1153 for(u32 j = 0; j < p.vertices.size(); j++)
1155 video::S3DVertex *vertex = &p.vertices[j];
1156 // Note applyFacesShading second parameter is precalculated sqrt
1157 // value for speed improvement
1158 // Skip it for lightsources and top faces.
1159 video::SColor &vc = vertex->Color;
1160 if (!vc.getBlue()) {
1161 if (vertex->Normal.Y < -0.5) {
1162 applyFacesShading (vc, 0.447213);
1163 } else if (vertex->Normal.X > 0.5) {
1164 applyFacesShading (vc, 0.670820);
1165 } else if (vertex->Normal.X < -0.5) {
1166 applyFacesShading (vc, 0.670820);
1167 } else if (vertex->Normal.Z > 0.5) {
1168 applyFacesShading (vc, 0.836660);
1169 } else if (vertex->Normal.Z < -0.5) {
1170 applyFacesShading (vc, 0.836660);
1173 if(!m_enable_shaders)
1175 // - Classic lighting (shaders handle this by themselves)
1176 // Set initial real color and store for later updates
1177 u8 day = vc.getRed();
1178 u8 night = vc.getGreen();
1179 finalColorBlend(vc, day, night, 1000);
1181 m_daynight_diffs[i][j] = std::make_pair(day, night);
1186 video::SMaterial material;
1187 material.setFlag(video::EMF_LIGHTING, false);
1188 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1189 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1190 material.setFlag(video::EMF_FOG_ENABLE, true);
1191 material.setTexture(0, p.tile.texture);
1193 if (m_enable_shaders) {
1194 material.MaterialType = m_shdrsrc->getShaderInfo(p.tile.shader_id).material;
1195 p.tile.applyMaterialOptionsWithShaders(material);
1196 if (p.tile.normal_texture) {
1197 material.setTexture(1, p.tile.normal_texture);
1199 material.setTexture(2, p.tile.flags_texture);
1201 p.tile.applyMaterialOptions(material);
1204 // Create meshbuffer
1205 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1207 buf->Material = material;
1209 scene::SMesh *mesh = (scene::SMesh *)m_mesh;
1210 mesh->addMeshBuffer(buf);
1213 buf->append(&p.vertices[0], p.vertices.size(),
1214 &p.indices[0], p.indices.size());
1216 m_camera_offset = camera_offset;
1219 Do some stuff to the mesh
1222 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1224 if (m_enable_shaders) {
1225 scene::IMeshManipulator* meshmanip = m_gamedef->getSceneManager()->getMeshManipulator();
1226 scene::IMesh* tangentMesh = meshmanip->createMeshWithTangents(m_mesh);
1228 m_mesh = tangentMesh;
1234 // Usually 1-700 faces and 1-7 materials
1235 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1236 <<"and uses "<<m_mesh->getMeshBufferCount()
1237 <<" materials (meshbuffers)"<<std::endl;
1240 // Use VBO for mesh (this just would set this for ever buffer)
1241 // This will lead to infinite memory usage because or irrlicht.
1242 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1245 NOTE: If that is enabled, some kind of a queue to the main
1246 thread should be made which would call irrlicht to delete
1247 the hardware buffer and then delete the mesh
1251 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1253 // Check if animation is required for this mesh
1255 !m_crack_materials.empty() ||
1256 !m_daynight_diffs.empty() ||
1257 !m_animation_tiles.empty();
1260 MapBlockMesh::~MapBlockMesh()
1264 delete m_minimap_mapblock;
1267 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1269 if(!m_has_animation)
1271 m_animation_force_timer = 100000;
1275 m_animation_force_timer = myrand_range(5, 100);
1278 if(crack != m_last_crack)
1280 for(std::map<u32, std::string>::iterator
1281 i = m_crack_materials.begin();
1282 i != m_crack_materials.end(); ++i)
1284 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1285 std::string basename = i->second;
1287 // Create new texture name from original
1288 std::ostringstream os;
1289 os<<basename<<crack;
1290 u32 new_texture_id = 0;
1291 video::ITexture *new_texture =
1292 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1293 buf->getMaterial().setTexture(0, new_texture);
1295 // If the current material is also animated,
1296 // update animation info
1297 std::map<u32, TileSpec>::iterator anim_iter =
1298 m_animation_tiles.find(i->first);
1299 if(anim_iter != m_animation_tiles.end()){
1300 TileSpec &tile = anim_iter->second;
1301 tile.texture = new_texture;
1302 tile.texture_id = new_texture_id;
1303 // force animation update
1304 m_animation_frames[i->first] = -1;
1308 m_last_crack = crack;
1311 // Texture animation
1312 for(std::map<u32, TileSpec>::iterator
1313 i = m_animation_tiles.begin();
1314 i != m_animation_tiles.end(); ++i)
1316 const TileSpec &tile = i->second;
1317 // Figure out current frame
1318 int frameoffset = m_animation_frame_offsets[i->first];
1319 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1320 + frameoffset) % tile.animation_frame_count;
1321 // If frame doesn't change, skip
1322 if(frame == m_animation_frames[i->first])
1325 m_animation_frames[i->first] = frame;
1327 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1329 FrameSpec animation_frame = tile.frames[frame];
1330 buf->getMaterial().setTexture(0, animation_frame.texture);
1331 if (m_enable_shaders) {
1332 if (animation_frame.normal_texture) {
1333 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1335 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1339 // Day-night transition
1340 if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
1342 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1343 i = m_daynight_diffs.begin();
1344 i != m_daynight_diffs.end(); ++i)
1346 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1347 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1348 for(std::map<u32, std::pair<u8, u8 > >::iterator
1349 j = i->second.begin();
1350 j != i->second.end(); ++j)
1352 u8 day = j->second.first;
1353 u8 night = j->second.second;
1354 finalColorBlend(vertices[j->first].Color, day, night, daynight_ratio);
1357 m_last_daynight_ratio = daynight_ratio;
1363 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1365 if (camera_offset != m_camera_offset) {
1366 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1367 m_camera_offset = camera_offset;
1375 void MeshCollector::append(const TileSpec &tile,
1376 const video::S3DVertex *vertices, u32 numVertices,
1377 const u16 *indices, u32 numIndices)
1379 if (numIndices > 65535) {
1380 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1384 PreMeshBuffer *p = NULL;
1385 for (u32 i = 0; i < prebuffers.size(); i++) {
1386 PreMeshBuffer &pp = prebuffers[i];
1387 if (pp.tile != tile)
1389 if (pp.indices.size() + numIndices > 65535)
1399 prebuffers.push_back(pp);
1400 p = &prebuffers[prebuffers.size() - 1];
1403 u32 vertex_count = p->vertices.size();
1404 for (u32 i = 0; i < numIndices; i++) {
1405 u32 j = indices[i] + vertex_count;
1406 p->indices.push_back(j);
1409 for (u32 i = 0; i < numVertices; i++) {
1410 video::S3DVertex vert(vertices[i].Pos, vertices[i].Normal,
1411 vertices[i].Color, vertices[i].TCoords);
1412 p->vertices.push_back(vert);
1417 MeshCollector - for meshnodes and converted drawtypes.
1420 void MeshCollector::append(const TileSpec &tile,
1421 const video::S3DVertex *vertices, u32 numVertices,
1422 const u16 *indices, u32 numIndices,
1423 v3f pos, video::SColor c)
1425 if (numIndices > 65535) {
1426 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1430 PreMeshBuffer *p = NULL;
1431 for (u32 i = 0; i < prebuffers.size(); i++) {
1432 PreMeshBuffer &pp = prebuffers[i];
1435 if(pp.indices.size() + numIndices > 65535)
1445 prebuffers.push_back(pp);
1446 p = &prebuffers[prebuffers.size() - 1];
1449 u32 vertex_count = p->vertices.size();
1450 for (u32 i = 0; i < numIndices; i++) {
1451 u32 j = indices[i] + vertex_count;
1452 p->indices.push_back(j);
1455 for (u32 i = 0; i < numVertices; i++) {
1456 video::S3DVertex vert(vertices[i].Pos + pos, vertices[i].Normal,
1457 c, vertices[i].TCoords);
1458 p->vertices.push_back(vert);