3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "mapblock_mesh.h"
24 #include "main.h" // for g_profiler
29 #include "content_mapblock.h"
33 #include "util/directiontables.h"
35 float srgb_linear_multiply(float f, float m, float max)
37 f = f * f; // SRGB -> Linear
39 f = sqrt(f); // Linear -> SRGB
49 MeshMakeData::MeshMakeData(IGameDef *gamedef):
51 m_blockpos(-1337,-1337,-1337),
52 m_crack_pos_relative(-1337, -1337, -1337),
53 m_smooth_lighting(false),
57 void MeshMakeData::fill(MapBlock *block)
59 m_blockpos = block->getPos();
61 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
67 // Allocate this block + neighbors
69 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
70 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
73 //TimeTaker timer("copy central block data");
77 block->copyTo(m_vmanip);
80 //TimeTaker timer("copy neighbor block data");
84 Copy neighbors. This is lightning fast.
85 Copying only the borders would be *very* slow.
89 Map *map = block->getParent();
91 for(u16 i=0; i<26; i++)
93 const v3s16 &dir = g_26dirs[i];
94 v3s16 bp = m_blockpos + dir;
95 MapBlock *b = map->getBlockNoCreateNoEx(bp);
102 void MeshMakeData::fillSingleNode(MapNode *node)
104 m_blockpos = v3s16(0,0,0);
106 v3s16 blockpos_nodes = v3s16(0,0,0);
107 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
108 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
109 s32 volume = area.getVolume();
110 s32 our_node_index = area.index(1,1,1);
112 // Allocate this block + neighbors
114 m_vmanip.addArea(area);
117 MapNode *data = new MapNode[volume];
118 for(s32 i = 0; i < volume; i++)
120 if(i == our_node_index)
126 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
129 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
133 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
136 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
139 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
141 m_smooth_lighting = smooth_lighting;
145 Light and vertex color functions
149 Calculate non-smooth lighting at interior of node.
152 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
155 INodeDefManager *ndef = data->m_gamedef->ndef();
156 u8 light = n.getLight(bank, ndef);
160 light = undiminish_light(light);
165 light = diminish_light(light);
169 return decode_light(light);
173 Calculate non-smooth lighting at interior of node.
176 u16 getInteriorLight(MapNode n, s32 increment, MeshMakeData *data)
178 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, data);
179 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, data);
180 return day | (night << 8);
184 Calculate non-smooth lighting at face of node.
187 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
188 v3s16 face_dir, MeshMakeData *data)
190 INodeDefManager *ndef = data->m_gamedef->ndef();
193 u8 l1 = n.getLight(bank, ndef);
194 u8 l2 = n2.getLight(bank, ndef);
200 // Boost light level for light sources
201 u8 light_source = MYMAX(ndef->get(n).light_source,
202 ndef->get(n2).light_source);
203 //if(light_source >= light)
204 //return decode_light(undiminish_light(light_source));
205 if(light_source > light)
206 //return decode_light(light_source);
207 light = light_source;
209 // Make some nice difference to different sides
211 // This makes light come from a corner
212 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
213 light = diminish_light(diminish_light(light));
214 else if(face_dir.X == -1 || face_dir.Z == -1)
215 light = diminish_light(light);*/
217 // All neighboring faces have different shade (like in minecraft)
218 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
219 light = diminish_light(diminish_light(light));
220 else if(face_dir.Z == 1 || face_dir.Z == -1)
221 light = diminish_light(light);
223 return decode_light(light);
227 Calculate non-smooth lighting at face of node.
230 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data)
232 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, data);
233 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, data);
234 return day | (night << 8);
238 Calculate smooth lighting at the XYZ- corner of p.
241 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
243 static v3s16 dirs8[8] = {
254 INodeDefManager *ndef = data->m_gamedef->ndef();
256 u16 ambient_occlusion = 0;
259 u8 light_source_max = 0;
260 for(u32 i=0; i<8; i++)
262 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
263 const ContentFeatures &f = ndef->get(n);
264 if(f.light_source > light_source_max)
265 light_source_max = f.light_source;
266 // Check f.solidness because fast-style leaves look
268 if(f.param_type == CPT_LIGHT && f.solidness != 2)
270 light += decode_light(n.getLight(bank, ndef));
273 else if(n.getContent() != CONTENT_IGNORE)
282 light /= light_count;
284 // Boost brightness around light sources
285 if(decode_light(light_source_max) >= light)
286 //return decode_light(undiminish_light(light_source_max));
287 return decode_light(light_source_max);
289 if(ambient_occlusion > 4)
291 //ambient_occlusion -= 4;
292 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
293 float light_amount = (8 - ambient_occlusion) / 4.0;
294 float light_f = (float)light / 255.0;
295 light_f = pow(light_f, 2.2f); // gamma -> linear space
296 light_f = light_f * light_amount;
297 light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
300 light = 255.0 * light_f + 0.5;
307 Calculate smooth lighting at the XYZ- corner of p.
310 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
312 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
313 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
314 return day | (night << 8);
318 Calculate smooth lighting at the given corner of p.
321 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
323 if(corner.X == 1) p.X += 1;
324 else assert(corner.X == -1);
325 if(corner.Y == 1) p.Y += 1;
326 else assert(corner.Y == -1);
327 if(corner.Z == 1) p.Z += 1;
328 else assert(corner.Z == -1);
330 return getSmoothLight(p, data);
334 Converts from day + night color values (0..255)
335 and a given daynight_ratio to the final SColor shown on screen.
337 static void finalColorBlend(video::SColor& result,
338 u8 day, u8 night, u32 daynight_ratio)
340 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
344 b += (day - night) / 13;
345 rg -= (day - night) / 23;
347 // Emphase blue a bit in darker places
348 // Each entry of this array represents a range of 8 blue levels
349 static u8 emphase_blue_when_dark[32] = {
350 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
351 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
357 b += emphase_blue_when_dark[b / 8];
359 // Artificial light is yellow-ish
360 static u8 emphase_yellow_when_artificial[16] = {
361 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
363 rg += emphase_yellow_when_artificial[night/16];
375 Mesh generation helpers
379 vertex_dirs: v3s16[4]
381 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
384 If looked from outside the node towards the face, the corners are:
390 if(dir == v3s16(0,0,1))
392 // If looking towards z+, this is the face that is behind
393 // the center point, facing towards z+.
394 vertex_dirs[0] = v3s16(-1,-1, 1);
395 vertex_dirs[1] = v3s16( 1,-1, 1);
396 vertex_dirs[2] = v3s16( 1, 1, 1);
397 vertex_dirs[3] = v3s16(-1, 1, 1);
399 else if(dir == v3s16(0,0,-1))
402 vertex_dirs[0] = v3s16( 1,-1,-1);
403 vertex_dirs[1] = v3s16(-1,-1,-1);
404 vertex_dirs[2] = v3s16(-1, 1,-1);
405 vertex_dirs[3] = v3s16( 1, 1,-1);
407 else if(dir == v3s16(1,0,0))
410 vertex_dirs[0] = v3s16( 1,-1, 1);
411 vertex_dirs[1] = v3s16( 1,-1,-1);
412 vertex_dirs[2] = v3s16( 1, 1,-1);
413 vertex_dirs[3] = v3s16( 1, 1, 1);
415 else if(dir == v3s16(-1,0,0))
418 vertex_dirs[0] = v3s16(-1,-1,-1);
419 vertex_dirs[1] = v3s16(-1,-1, 1);
420 vertex_dirs[2] = v3s16(-1, 1, 1);
421 vertex_dirs[3] = v3s16(-1, 1,-1);
423 else if(dir == v3s16(0,1,0))
425 // faces towards Y+ (assume Z- as "down" in texture)
426 vertex_dirs[0] = v3s16( 1, 1,-1);
427 vertex_dirs[1] = v3s16(-1, 1,-1);
428 vertex_dirs[2] = v3s16(-1, 1, 1);
429 vertex_dirs[3] = v3s16( 1, 1, 1);
431 else if(dir == v3s16(0,-1,0))
433 // faces towards Y- (assume Z+ as "down" in texture)
434 vertex_dirs[0] = v3s16( 1,-1, 1);
435 vertex_dirs[1] = v3s16(-1,-1, 1);
436 vertex_dirs[2] = v3s16(-1,-1,-1);
437 vertex_dirs[3] = v3s16( 1,-1,-1);
444 video::S3DVertex vertices[4]; // Precalculated vertices
447 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
448 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
452 // Position is at the center of the cube.
456 v3s16 vertex_dirs[4];
457 getNodeVertexDirs(dir, vertex_dirs);
458 for(u16 i=0; i<4; i++)
461 BS/2*vertex_dirs[i].X,
462 BS/2*vertex_dirs[i].Y,
463 BS/2*vertex_dirs[i].Z
467 for(u16 i=0; i<4; i++)
469 vertex_pos[i].X *= scale.X;
470 vertex_pos[i].Y *= scale.Y;
471 vertex_pos[i].Z *= scale.Z;
472 vertex_pos[i] += pos;
476 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
477 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
478 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
480 v3f normal(dir.X, dir.Y, dir.Z);
482 u8 alpha = tile.alpha;
484 float x0 = tile.texture.pos.X;
485 float y0 = tile.texture.pos.Y;
486 float w = tile.texture.size.X;
487 float h = tile.texture.size.Y;
489 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
490 MapBlock_LightColor(alpha, li0, light_source),
491 core::vector2d<f32>(x0+w*abs_scale, y0+h));
492 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
493 MapBlock_LightColor(alpha, li1, light_source),
494 core::vector2d<f32>(x0, y0+h));
495 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
496 MapBlock_LightColor(alpha, li2, light_source),
497 core::vector2d<f32>(x0, y0));
498 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
499 MapBlock_LightColor(alpha, li3, light_source),
500 core::vector2d<f32>(x0+w*abs_scale, y0));
504 dest.push_back(face);
508 Nodes make a face if contents differ and solidness differs.
511 1: Face uses m1's content
512 2: Face uses m2's content
513 equivalent: Whether the blocks share the same face (eg. water and glass)
515 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
517 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
518 INodeDefManager *ndef)
522 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
525 bool contents_differ = (m1 != m2);
527 const ContentFeatures &f1 = ndef->get(m1);
528 const ContentFeatures &f2 = ndef->get(m2);
530 // Contents don't differ for different forms of same liquid
531 if(f1.sameLiquid(f2))
532 contents_differ = false;
534 u8 c1 = f1.solidness;
535 u8 c2 = f2.solidness;
537 bool solidness_differs = (c1 != c2);
538 bool makes_face = contents_differ && solidness_differs;
540 if(makes_face == false)
544 c1 = f1.visual_solidness;
546 c2 = f2.visual_solidness;
550 // If same solidness, liquid takes precense
564 Gets nth node tile (0 <= n <= 5).
566 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
568 INodeDefManager *ndef = data->m_gamedef->ndef();
569 TileSpec spec = ndef->get(mn).tiles[tileindex];
570 // Apply temporary crack
571 if(p == data->m_crack_pos_relative)
573 spec.material_flags |= MATERIAL_FLAG_CRACK;
574 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
576 // If animated, replace tile texture with one without texture atlas
577 if(spec.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
579 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
585 Gets node tile given a face direction.
587 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
589 INodeDefManager *ndef = data->m_gamedef->ndef();
591 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
592 // (0,0,1), (0,0,-1) or (0,0,0)
593 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
595 // Convert direction to single integer for table lookup
600 // 4 = invalid, treat as (0,0,0)
604 u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
606 // Get rotation for things like chests
607 u8 facedir = mn.getFaceDir(ndef);
608 assert(facedir <= 3);
610 static const u8 dir_to_tile[4 * 8] =
612 // 0 +X +Y +Z 0 -Z -Y -X
613 0, 2, 0, 4, 0, 5, 1, 3, // facedir = 0
614 0, 4, 0, 3, 0, 2, 1, 5, // facedir = 1
615 0, 3, 0, 5, 0, 4, 1, 2, // facedir = 2
616 0, 5, 0, 2, 0, 3, 1, 4, // facedir = 3
618 u8 tileindex = dir_to_tile[facedir*8 + dir_i];
620 // If not rotated or is side tile, we're done
621 if(facedir == 0 || (tileindex != 0 && tileindex != 1))
622 return getNodeTileN(mn, p, tileindex, data);
624 // This is the top or bottom tile, and it shall be rotated; thus rotate it
625 TileSpec spec = getNodeTileN(mn, p, tileindex, data);
627 if(facedir == 1){ // -90
628 std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
629 name += "^[transformR270";
630 spec.texture = data->m_gamedef->tsrc()->getTexture(name);
632 else if(facedir == 2){ // 180
633 spec.texture.pos += spec.texture.size;
634 spec.texture.size *= -1;
636 else if(facedir == 3){ // 90
637 std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
638 name += "^[transformR90";
639 spec.texture = data->m_gamedef->tsrc()->getTexture(name);
642 else if(tileindex == 1){
643 if(facedir == 1){ // -90
644 std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
645 name += "^[transformR90";
646 spec.texture = data->m_gamedef->tsrc()->getTexture(name);
648 else if(facedir == 2){ // 180
649 spec.texture.pos += spec.texture.size;
650 spec.texture.size *= -1;
652 else if(facedir == 3){ // 90
653 std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
654 name += "^[transformR270";
655 spec.texture = data->m_gamedef->tsrc()->getTexture(name);
661 static void getTileInfo(
669 v3s16 &face_dir_corrected,
675 VoxelManipulator &vmanip = data->m_vmanip;
676 INodeDefManager *ndef = data->m_gamedef->ndef();
677 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
679 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
680 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
681 TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
682 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
685 bool equivalent = false;
686 u8 mf = face_contents(n0.getContent(), n1.getContent(),
701 face_dir_corrected = face_dir;
702 light_source = ndef->get(n0).light_source;
707 p_corrected = p + face_dir;
708 face_dir_corrected = -face_dir;
709 light_source = ndef->get(n1).light_source;
712 // eg. water and glass
714 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
716 if(data->m_smooth_lighting == false)
718 lights[0] = lights[1] = lights[2] = lights[3] =
719 getFaceLight(n0, n1, face_dir, data);
723 v3s16 vertex_dirs[4];
724 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
725 for(u16 i=0; i<4; i++)
727 lights[i] = getSmoothLight(
728 blockpos_nodes + p_corrected,
729 vertex_dirs[i], data);
738 translate_dir: unit vector with only one of x, y or z
739 face_dir: unit vector with only one of x, y or z
741 static void updateFastFaceRow(
748 std::vector<FastFace> &dest)
752 u16 continuous_tiles_count = 0;
754 bool makes_face = false;
756 v3s16 face_dir_corrected;
757 u16 lights[4] = {0,0,0,0};
760 getTileInfo(data, p, face_dir,
761 makes_face, p_corrected, face_dir_corrected,
762 lights, tile, light_source);
764 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
766 // If tiling can be done, this is set to false in the next step
767 bool next_is_different = true;
771 bool next_makes_face = false;
772 v3s16 next_p_corrected;
773 v3s16 next_face_dir_corrected;
774 u16 next_lights[4] = {0,0,0,0};
776 u8 next_light_source = 0;
778 // If at last position, there is nothing to compare to and
779 // the face must be drawn anyway
780 if(j != MAP_BLOCKSIZE - 1)
782 p_next = p + translate_dir;
784 getTileInfo(data, p_next, face_dir,
785 next_makes_face, next_p_corrected,
786 next_face_dir_corrected, next_lights,
787 next_tile, next_light_source);
789 if(next_makes_face == makes_face
790 && next_p_corrected == p_corrected + translate_dir
791 && next_face_dir_corrected == face_dir_corrected
792 && next_lights[0] == lights[0]
793 && next_lights[1] == lights[1]
794 && next_lights[2] == lights[2]
795 && next_lights[3] == lights[3]
797 && next_light_source == light_source)
799 next_is_different = false;
803 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
804 next_makes_face != makes_face ? 1 : 0);
805 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
806 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
807 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
808 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
809 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
810 (next_lights[0] != lights[0] ||
811 next_lights[0] != lights[0] ||
812 next_lights[0] != lights[0] ||
813 next_lights[0] != lights[0]) ? 1 : 0);
814 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
815 !(next_tile == tile) ? 1 : 0);
818 /*g_profiler->add("Meshgen: Total faces checked", 1);
820 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
823 g_profiler->add("Meshgen: diff: last position", 1);*/
826 continuous_tiles_count++;
828 // This is set to true if the texture doesn't allow more tiling
829 bool end_of_texture = false;
831 If there is no texture, it can be tiled infinitely.
832 If tiled==0, it means the texture can be tiled infinitely.
833 Otherwise check tiled agains continuous_tiles_count.
835 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
837 if(tile.texture.tiled <= continuous_tiles_count)
838 end_of_texture = true;
841 // Do this to disable tiling textures
842 //end_of_texture = true; //DEBUG
844 if(next_is_different || end_of_texture)
847 Create a face if there should be one
851 // Floating point conversion of the position vector
852 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
853 // Center point of face (kind of)
854 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
855 if(continuous_tiles_count != 1)
856 sp += translate_dir_f;
859 if(translate_dir.X != 0)
861 scale.X = continuous_tiles_count;
863 if(translate_dir.Y != 0)
865 scale.Y = continuous_tiles_count;
867 if(translate_dir.Z != 0)
869 scale.Z = continuous_tiles_count;
872 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
873 sp, face_dir_corrected, scale, light_source,
876 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
877 for(int i=1; i<continuous_tiles_count; i++){
878 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
882 continuous_tiles_count = 0;
884 makes_face = next_makes_face;
885 p_corrected = next_p_corrected;
886 face_dir_corrected = next_face_dir_corrected;
887 lights[0] = next_lights[0];
888 lights[1] = next_lights[1];
889 lights[2] = next_lights[2];
890 lights[3] = next_lights[3];
892 light_source = next_light_source;
899 static void updateAllFastFaceRows(MeshMakeData *data,
900 std::vector<FastFace> &dest)
903 Go through every y,z and get top(y+) faces in rows of x+
905 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
906 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
907 updateFastFaceRow(data,
911 v3s16(0,1,0), //face dir
918 Go through every x,y and get right(x+) faces in rows of z+
920 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
921 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
922 updateFastFaceRow(data,
926 v3s16(1,0,0), //face dir
933 Go through every y,z and get back(z+) faces in rows of x+
935 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
936 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
937 updateFastFaceRow(data,
941 v3s16(0,0,1), //face dir
952 MapBlockMesh::MapBlockMesh(MeshMakeData *data):
953 m_mesh(new scene::SMesh()),
954 m_gamedef(data->m_gamedef),
955 m_animation_force_timer(0), // force initial animation
958 m_last_daynight_ratio((u32) -1),
961 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
962 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
963 //TimeTaker timer1("MapBlockMesh()");
965 std::vector<FastFace> fastfaces_new;
968 We are including the faces of the trailing edges of the block.
969 This means that when something changes, the caller must
970 also update the meshes of the blocks at the leading edges.
972 NOTE: This is the slowest part of this method.
975 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
976 //TimeTaker timer2("updateAllFastFaceRows()");
977 updateAllFastFaceRows(data, fastfaces_new);
982 Convert FastFaces to MeshCollector
985 MeshCollector collector;
988 // avg 0ms (100ms spikes when loading textures the first time)
989 // (NOTE: probably outdated)
990 //TimeTaker timer2("MeshCollector building");
992 for(u32 i=0; i<fastfaces_new.size(); i++)
994 FastFace &f = fastfaces_new[i];
996 const u16 indices[] = {0,1,2,2,3,0};
997 const u16 indices_alternate[] = {0,1,3,2,3,1};
999 if(f.tile.texture.atlas == NULL)
1002 const u16 *indices_p = indices;
1005 Revert triangles for nicer looking gradient if vertices
1006 1 and 3 have same color or 0 and 2 have different color.
1007 getRed() is the day color.
1009 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1010 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1011 indices_p = indices_alternate;
1013 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1018 Add special graphics:
1025 mapblock_mesh_generate_special(data, collector);
1029 Convert MeshCollector to SMesh
1030 Also store animation info
1032 bool enable_shaders = (g_settings->getS32("enable_shaders") > 0);
1033 video::E_MATERIAL_TYPE shadermat1 = m_gamedef->getShaderSource()->
1034 getShader("test_shader_1").material;
1035 video::E_MATERIAL_TYPE shadermat2 = m_gamedef->getShaderSource()->
1036 getShader("test_shader_2").material;
1037 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1039 PreMeshBuffer &p = collector.prebuffers[i];
1040 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1041 <<", p.indices.size()="<<p.indices.size()
1044 // Generate animation data
1046 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1048 ITextureSource *tsrc = data->m_gamedef->tsrc();
1049 std::string crack_basename = tsrc->getTextureName(p.tile.texture.id);
1050 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1051 crack_basename += "^[cracko";
1053 crack_basename += "^[crack";
1054 m_crack_materials.insert(std::make_pair(i, crack_basename));
1056 // - Texture animation
1057 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1059 ITextureSource *tsrc = data->m_gamedef->tsrc();
1060 // Add to MapBlockMesh in order to animate these tiles
1061 m_animation_tiles[i] = p.tile;
1062 m_animation_frames[i] = 0;
1063 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1064 // Get starting position from noise
1065 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1066 data->m_blockpos.X, data->m_blockpos.Y,
1067 data->m_blockpos.Z, 0));
1069 // Play all synchronized
1070 m_animation_frame_offsets[i] = 0;
1072 // Replace tile texture with the first animation frame
1073 std::ostringstream os(std::ios::binary);
1074 os<<tsrc->getTextureName(p.tile.texture.id);
1075 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1076 p.tile.texture = tsrc->getTexture(os.str());
1078 // - Classic lighting (shaders handle this by themselves)
1081 for(u32 j = 0; j < p.vertices.size(); j++)
1083 video::SColor &vc = p.vertices[j].Color;
1084 // Set initial real color and store for later updates
1085 u8 day = vc.getRed();
1086 u8 night = vc.getGreen();
1087 finalColorBlend(vc, day, night, 1000);
1089 m_daynight_diffs[i][j] = std::make_pair(day, night);
1090 // Brighten topside (no shaders)
1091 if(p.vertices[j].Normal.Y > 0.5)
1093 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1094 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1095 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1101 video::SMaterial material;
1102 material.setFlag(video::EMF_LIGHTING, false);
1103 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1104 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1105 material.setFlag(video::EMF_FOG_ENABLE, true);
1106 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1107 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1108 material.MaterialType
1109 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1110 material.setTexture(0, p.tile.texture.atlas);
1112 p.tile.applyMaterialOptionsWithShaders(material, shadermat1, shadermat2);
1114 p.tile.applyMaterialOptions(material);
1116 // Create meshbuffer
1118 // This is a "Standard MeshBuffer",
1119 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1120 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1122 buf->Material = material;
1124 m_mesh->addMeshBuffer(buf);
1127 buf->append(&p.vertices[0], p.vertices.size(),
1128 &p.indices[0], p.indices.size());
1132 Do some stuff to the mesh
1135 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
1136 m_mesh->recalculateBoundingBox(); // translateMesh already does this
1141 // Usually 1-700 faces and 1-7 materials
1142 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1143 <<"and uses "<<m_mesh->getMeshBufferCount()
1144 <<" materials (meshbuffers)"<<std::endl;
1147 // Use VBO for mesh (this just would set this for ever buffer)
1148 // This will lead to infinite memory usage because or irrlicht.
1149 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1152 NOTE: If that is enabled, some kind of a queue to the main
1153 thread should be made which would call irrlicht to delete
1154 the hardware buffer and then delete the mesh
1158 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1160 // Check if animation is required for this mesh
1162 !m_crack_materials.empty() ||
1163 !m_daynight_diffs.empty() ||
1164 !m_animation_tiles.empty();
1167 MapBlockMesh::~MapBlockMesh()
1173 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1175 if(!m_has_animation)
1177 m_animation_force_timer = 100000;
1181 m_animation_force_timer = myrand_range(5, 100);
1184 if(crack != m_last_crack)
1186 for(std::map<u32, std::string>::iterator
1187 i = m_crack_materials.begin();
1188 i != m_crack_materials.end(); i++)
1190 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1191 std::string basename = i->second;
1193 // Create new texture name from original
1194 ITextureSource *tsrc = m_gamedef->getTextureSource();
1195 std::ostringstream os;
1196 os<<basename<<crack;
1197 AtlasPointer ap = tsrc->getTexture(os.str());
1198 buf->getMaterial().setTexture(0, ap.atlas);
1201 m_last_crack = crack;
1204 // Texture animation
1205 for(std::map<u32, TileSpec>::iterator
1206 i = m_animation_tiles.begin();
1207 i != m_animation_tiles.end(); i++)
1209 const TileSpec &tile = i->second;
1210 // Figure out current frame
1211 int frameoffset = m_animation_frame_offsets[i->first];
1212 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1213 + frameoffset) % tile.animation_frame_count;
1214 // If frame doesn't change, skip
1215 if(frame == m_animation_frames[i->first])
1218 m_animation_frames[i->first] = frame;
1220 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1221 ITextureSource *tsrc = m_gamedef->getTextureSource();
1223 // Create new texture name from original
1224 std::ostringstream os(std::ios::binary);
1225 os<<tsrc->getTextureName(tile.texture.id);
1226 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1228 AtlasPointer ap = tsrc->getTexture(os.str());
1229 buf->getMaterial().setTexture(0, ap.atlas);
1232 // Day-night transition
1233 if(daynight_ratio != m_last_daynight_ratio)
1235 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1236 i = m_daynight_diffs.begin();
1237 i != m_daynight_diffs.end(); i++)
1239 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1240 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1241 for(std::map<u32, std::pair<u8, u8 > >::iterator
1242 j = i->second.begin();
1243 j != i->second.end(); j++)
1245 u32 vertexIndex = j->first;
1246 u8 day = j->second.first;
1247 u8 night = j->second.second;
1248 finalColorBlend(vertices[vertexIndex].Color,
1249 day, night, daynight_ratio);
1250 // Brighten topside (no shaders)
1251 if(vertices[vertexIndex].Normal.Y > 0.5)
1253 video::SColor &vc = vertices[vertexIndex].Color;
1254 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1255 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1256 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1260 m_last_daynight_ratio = daynight_ratio;
1270 void MeshCollector::append(const TileSpec &tile,
1271 const video::S3DVertex *vertices, u32 numVertices,
1272 const u16 *indices, u32 numIndices)
1274 PreMeshBuffer *p = NULL;
1275 for(u32 i=0; i<prebuffers.size(); i++)
1277 PreMeshBuffer &pp = prebuffers[i];
1289 prebuffers.push_back(pp);
1290 p = &prebuffers[prebuffers.size()-1];
1293 u32 vertex_count = p->vertices.size();
1294 for(u32 i=0; i<numIndices; i++)
1296 u32 j = indices[i] + vertex_count;
1299 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
1300 // NOTE: Fix is to just add an another MeshBuffer
1302 p->indices.push_back(j);
1304 for(u32 i=0; i<numVertices; i++)
1306 p->vertices.push_back(vertices[i]);