3 Copyright (C) 2010-2011 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
15 You should have received a copy of the GNU 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"
35 MeshMakeData::MeshMakeData(IGameDef *gamedef):
37 m_blockpos(-1337,-1337,-1337),
38 m_crack_pos_relative(-1337, -1337, -1337),
39 m_smooth_lighting(false),
43 void MeshMakeData::fill(MapBlock *block)
45 m_blockpos = block->getPos();
47 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
53 // Allocate this block + neighbors
55 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
56 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
59 //TimeTaker timer("copy central block data");
63 block->copyTo(m_vmanip);
66 //TimeTaker timer("copy neighbor block data");
70 Copy neighbors. This is lightning fast.
71 Copying only the borders would be *very* slow.
75 Map *map = block->getParent();
77 for(u16 i=0; i<6; i++)
79 const v3s16 &dir = g_6dirs[i];
80 v3s16 bp = m_blockpos + dir;
81 MapBlock *b = map->getBlockNoCreateNoEx(bp);
88 void MeshMakeData::fillSingleNode(MapNode *node)
90 m_blockpos = v3s16(0,0,0);
92 v3s16 blockpos_nodes = v3s16(0,0,0);
93 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
94 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
95 s32 volume = area.getVolume();
96 s32 our_node_index = area.index(1,1,1);
98 // Allocate this block + neighbors
100 m_vmanip.addArea(area);
103 MapNode *data = new MapNode[volume];
104 for(s32 i = 0; i < volume; i++)
106 if(i == our_node_index)
112 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
115 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
119 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
122 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
125 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
127 m_smooth_lighting = smooth_lighting;
131 Light and vertex color functions
135 Calculate non-smooth lighting at interior of node.
138 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
141 INodeDefManager *ndef = data->m_gamedef->ndef();
142 u8 light = n.getLight(bank, ndef);
146 light = undiminish_light(light);
151 light = diminish_light(light);
155 return decode_light(light);
159 Calculate non-smooth lighting at interior of node.
162 u16 getInteriorLight(MapNode n, s32 increment, MeshMakeData *data)
164 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, data);
165 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, data);
166 return day | (night << 8);
170 Calculate non-smooth lighting at face of node.
173 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
174 v3s16 face_dir, MeshMakeData *data)
176 INodeDefManager *ndef = data->m_gamedef->ndef();
179 u8 l1 = n.getLight(bank, ndef);
180 u8 l2 = n2.getLight(bank, ndef);
186 // Boost light level for light sources
187 u8 light_source = MYMAX(ndef->get(n).light_source,
188 ndef->get(n2).light_source);
189 //if(light_source >= light)
190 //return decode_light(undiminish_light(light_source));
191 if(light_source > light)
192 //return decode_light(light_source);
193 light = light_source;
195 // Make some nice difference to different sides
197 // This makes light come from a corner
198 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
199 light = diminish_light(diminish_light(light));
200 else if(face_dir.X == -1 || face_dir.Z == -1)
201 light = diminish_light(light);*/
203 // All neighboring faces have different shade (like in minecraft)
204 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
205 light = diminish_light(diminish_light(light));
206 else if(face_dir.Z == 1 || face_dir.Z == -1)
207 light = diminish_light(light);
209 return decode_light(light);
213 Calculate non-smooth lighting at face of node.
216 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data)
218 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, data);
219 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, data);
220 return day | (night << 8);
224 Calculate smooth lighting at the XYZ- corner of p.
227 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
229 static v3s16 dirs8[8] = {
240 INodeDefManager *ndef = data->m_gamedef->ndef();
242 u16 ambient_occlusion = 0;
245 u8 light_source_max = 0;
246 for(u32 i=0; i<8; i++)
248 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
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));
259 else if(n.getContent() != CONTENT_IGNORE)
268 light /= light_count;
270 // Boost brightness around light sources
271 if(decode_light(light_source_max) >= light)
272 //return decode_light(undiminish_light(light_source_max));
273 return decode_light(light_source_max);
275 if(ambient_occlusion > 4)
277 //ambient_occlusion -= 4;
278 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
279 float light_amount = (8 - ambient_occlusion) / 4.0;
280 float light_f = (float)light / 255.0;
281 light_f = pow(light_f, 2.2); // gamma -> linear space
282 light_f = light_f * light_amount;
283 light_f = pow(light_f, 1.0/2.2); // linear -> gamma space
286 light = 255.0 * light_f + 0.5;
293 Calculate smooth lighting at the XYZ- corner of p.
296 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
298 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
299 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
300 return day | (night << 8);
304 Calculate smooth lighting at the given corner of p.
307 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
309 if(corner.X == 1) p.X += 1;
310 else assert(corner.X == -1);
311 if(corner.Y == 1) p.Y += 1;
312 else assert(corner.Y == -1);
313 if(corner.Z == 1) p.Z += 1;
314 else assert(corner.Z == -1);
316 return getSmoothLight(p, data);
320 Converts from day + night color values (0..255)
321 and a given daynight_ratio to the final SColor shown on screen.
323 static void finalColorBlend(video::SColor& result,
324 u8 day, u8 night, u32 daynight_ratio)
326 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
330 b += (day - night) / 13;
331 rg -= (day - night) / 23;
333 // Emphase blue a bit in darker places
334 // Each entry of this array represents a range of 8 blue levels
335 static u8 emphase_blue_when_dark[32] = {
336 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
337 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
343 b += emphase_blue_when_dark[b / 8];
345 // Artificial light is yellow-ish
346 static u8 emphase_yellow_when_artificial[16] = {
347 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
349 rg += emphase_yellow_when_artificial[night/16];
361 Mesh generation helpers
365 vertex_dirs: v3s16[4]
367 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
370 If looked from outside the node towards the face, the corners are:
376 if(dir == v3s16(0,0,1))
378 // If looking towards z+, this is the face that is behind
379 // the center point, facing towards z+.
380 vertex_dirs[0] = v3s16(-1,-1, 1);
381 vertex_dirs[1] = v3s16( 1,-1, 1);
382 vertex_dirs[2] = v3s16( 1, 1, 1);
383 vertex_dirs[3] = v3s16(-1, 1, 1);
385 else if(dir == v3s16(0,0,-1))
388 vertex_dirs[0] = v3s16( 1,-1,-1);
389 vertex_dirs[1] = v3s16(-1,-1,-1);
390 vertex_dirs[2] = v3s16(-1, 1,-1);
391 vertex_dirs[3] = v3s16( 1, 1,-1);
393 else if(dir == v3s16(1,0,0))
396 vertex_dirs[0] = v3s16( 1,-1, 1);
397 vertex_dirs[1] = v3s16( 1,-1,-1);
398 vertex_dirs[2] = v3s16( 1, 1,-1);
399 vertex_dirs[3] = v3s16( 1, 1, 1);
401 else if(dir == v3s16(-1,0,0))
404 vertex_dirs[0] = v3s16(-1,-1,-1);
405 vertex_dirs[1] = v3s16(-1,-1, 1);
406 vertex_dirs[2] = v3s16(-1, 1, 1);
407 vertex_dirs[3] = v3s16(-1, 1,-1);
409 else if(dir == v3s16(0,1,0))
411 // faces towards Y+ (assume Z- as "down" in texture)
412 vertex_dirs[0] = v3s16( 1, 1,-1);
413 vertex_dirs[1] = v3s16(-1, 1,-1);
414 vertex_dirs[2] = v3s16(-1, 1, 1);
415 vertex_dirs[3] = v3s16( 1, 1, 1);
417 else if(dir == v3s16(0,-1,0))
419 // faces towards Y- (assume Z+ as "down" in texture)
420 vertex_dirs[0] = v3s16( 1,-1, 1);
421 vertex_dirs[1] = v3s16(-1,-1, 1);
422 vertex_dirs[2] = v3s16(-1,-1,-1);
423 vertex_dirs[3] = v3s16( 1,-1,-1);
430 video::S3DVertex vertices[4]; // Precalculated vertices
433 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
434 v3f p, v3s16 dir, v3f scale, core::array<FastFace> &dest)
438 // Position is at the center of the cube.
442 v3s16 vertex_dirs[4];
443 getNodeVertexDirs(dir, vertex_dirs);
444 for(u16 i=0; i<4; i++)
447 BS/2*vertex_dirs[i].X,
448 BS/2*vertex_dirs[i].Y,
449 BS/2*vertex_dirs[i].Z
453 for(u16 i=0; i<4; i++)
455 vertex_pos[i].X *= scale.X;
456 vertex_pos[i].Y *= scale.Y;
457 vertex_pos[i].Z *= scale.Z;
458 vertex_pos[i] += pos;
462 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
463 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
464 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
466 v3f normal(dir.X, dir.Y, dir.Z);
468 u8 alpha = tile.alpha;
470 float x0 = tile.texture.pos.X;
471 float y0 = tile.texture.pos.Y;
472 float w = tile.texture.size.X;
473 float h = tile.texture.size.Y;
475 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
476 MapBlock_LightColor(alpha, li0),
477 core::vector2d<f32>(x0+w*abs_scale, y0+h));
478 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
479 MapBlock_LightColor(alpha, li1),
480 core::vector2d<f32>(x0, y0+h));
481 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
482 MapBlock_LightColor(alpha, li2),
483 core::vector2d<f32>(x0, y0));
484 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
485 MapBlock_LightColor(alpha, li3),
486 core::vector2d<f32>(x0+w*abs_scale, y0));
490 dest.push_back(face);
494 Nodes make a face if contents differ and solidness differs.
497 1: Face uses m1's content
498 2: Face uses m2's content
499 equivalent: Whether the blocks share the same face (eg. water and glass)
501 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
503 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
504 INodeDefManager *ndef)
508 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
511 bool contents_differ = (m1 != m2);
513 const ContentFeatures &f1 = ndef->get(m1);
514 const ContentFeatures &f2 = ndef->get(m2);
516 // Contents don't differ for different forms of same liquid
517 if(f1.sameLiquid(f2))
518 contents_differ = false;
520 u8 c1 = f1.solidness;
521 u8 c2 = f2.solidness;
523 bool solidness_differs = (c1 != c2);
524 bool makes_face = contents_differ && solidness_differs;
526 if(makes_face == false)
530 c1 = f1.visual_solidness;
532 c2 = f2.visual_solidness;
536 // If same solidness, liquid takes precense
550 Gets nth node tile (0 <= n <= 5).
552 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
554 INodeDefManager *ndef = data->m_gamedef->ndef();
555 TileSpec spec = ndef->get(mn).tiles[tileindex];
556 // Apply temporary crack
557 if(p == data->m_crack_pos_relative)
559 spec.material_flags |= MATERIAL_FLAG_CRACK;
560 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
566 Gets node tile given a face direction.
568 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
570 INodeDefManager *ndef = data->m_gamedef->ndef();
572 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
573 // (0,0,1), (0,0,-1) or (0,0,0)
574 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
576 // Convert direction to single integer for table lookup
581 // 4 = invalid, treat as (0,0,0)
585 u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
587 // Get rotation for things like chests
588 u8 facedir = mn.getFaceDir(ndef);
589 assert(facedir <= 3);
591 static const u8 dir_to_tile[4 * 8] =
593 // 0 +X +Y +Z 0 -Z -Y -X
594 0, 2, 0, 4, 0, 5, 1, 3, // facedir = 0
595 0, 4, 0, 3, 0, 2, 1, 5, // facedir = 1
596 0, 3, 0, 5, 0, 4, 1, 2, // facedir = 2
597 0, 5, 0, 2, 0, 3, 1, 4, // facedir = 3
599 u8 tileindex = dir_to_tile[facedir*8 + dir_i];
600 return getNodeTileN(mn, p, tileindex, data);
603 static void getTileInfo(
611 v3s16 &face_dir_corrected,
616 VoxelManipulator &vmanip = data->m_vmanip;
617 INodeDefManager *ndef = data->m_gamedef->ndef();
618 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
620 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
621 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
622 TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
623 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
626 bool equivalent = false;
627 u8 mf = face_contents(n0.getContent(), n1.getContent(),
642 face_dir_corrected = face_dir;
647 p_corrected = p + face_dir;
648 face_dir_corrected = -face_dir;
651 // eg. water and glass
653 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
655 if(data->m_smooth_lighting == false)
657 lights[0] = lights[1] = lights[2] = lights[3] =
658 getFaceLight(n0, n1, face_dir, data);
662 v3s16 vertex_dirs[4];
663 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
664 for(u16 i=0; i<4; i++)
666 lights[i] = getSmoothLight(
667 blockpos_nodes + p_corrected,
668 vertex_dirs[i], data);
677 translate_dir: unit vector with only one of x, y or z
678 face_dir: unit vector with only one of x, y or z
680 static void updateFastFaceRow(
687 core::array<FastFace> &dest)
691 u16 continuous_tiles_count = 0;
693 bool makes_face = false;
695 v3s16 face_dir_corrected;
696 u16 lights[4] = {0,0,0,0};
698 getTileInfo(data, p, face_dir,
699 makes_face, p_corrected, face_dir_corrected,
702 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
704 // If tiling can be done, this is set to false in the next step
705 bool next_is_different = true;
709 bool next_makes_face = false;
710 v3s16 next_p_corrected;
711 v3s16 next_face_dir_corrected;
712 u16 next_lights[4] = {0,0,0,0};
715 // If at last position, there is nothing to compare to and
716 // the face must be drawn anyway
717 if(j != MAP_BLOCKSIZE - 1)
719 p_next = p + translate_dir;
721 getTileInfo(data, p_next, face_dir,
722 next_makes_face, next_p_corrected,
723 next_face_dir_corrected, next_lights,
726 if(next_makes_face == makes_face
727 && next_p_corrected == p_corrected + translate_dir
728 && next_face_dir_corrected == face_dir_corrected
729 && next_lights[0] == lights[0]
730 && next_lights[1] == lights[1]
731 && next_lights[2] == lights[2]
732 && next_lights[3] == lights[3]
733 && next_tile == tile)
735 next_is_different = false;
739 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
740 next_makes_face != makes_face ? 1 : 0);
741 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
742 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
743 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
744 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
745 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
746 (next_lights[0] != lights[0] ||
747 next_lights[0] != lights[0] ||
748 next_lights[0] != lights[0] ||
749 next_lights[0] != lights[0]) ? 1 : 0);
750 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
751 !(next_tile == tile) ? 1 : 0);
754 /*g_profiler->add("Meshgen: Total faces checked", 1);
756 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
759 g_profiler->add("Meshgen: diff: last position", 1);*/
762 continuous_tiles_count++;
764 // This is set to true if the texture doesn't allow more tiling
765 bool end_of_texture = false;
767 If there is no texture, it can be tiled infinitely.
768 If tiled==0, it means the texture can be tiled infinitely.
769 Otherwise check tiled agains continuous_tiles_count.
771 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
773 if(tile.texture.tiled <= continuous_tiles_count)
774 end_of_texture = true;
777 // Do this to disable tiling textures
778 //end_of_texture = true; //DEBUG
780 if(next_is_different || end_of_texture)
783 Create a face if there should be one
787 // Floating point conversion of the position vector
788 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
789 // Center point of face (kind of)
790 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
791 if(continuous_tiles_count != 1)
792 sp += translate_dir_f;
795 if(translate_dir.X != 0)
797 scale.X = continuous_tiles_count;
799 if(translate_dir.Y != 0)
801 scale.Y = continuous_tiles_count;
803 if(translate_dir.Z != 0)
805 scale.Z = continuous_tiles_count;
808 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
809 sp, face_dir_corrected, scale,
812 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
813 for(int i=1; i<continuous_tiles_count; i++){
814 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
818 continuous_tiles_count = 0;
820 makes_face = next_makes_face;
821 p_corrected = next_p_corrected;
822 face_dir_corrected = next_face_dir_corrected;
823 lights[0] = next_lights[0];
824 lights[1] = next_lights[1];
825 lights[2] = next_lights[2];
826 lights[3] = next_lights[3];
834 static void updateAllFastFaceRows(MeshMakeData *data,
835 core::array<FastFace> &dest)
838 Go through every y,z and get top(y+) faces in rows of x+
840 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
841 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
842 updateFastFaceRow(data,
846 v3s16(0,1,0), //face dir
853 Go through every x,y and get right(x+) faces in rows of z+
855 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
856 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
857 updateFastFaceRow(data,
861 v3s16(1,0,0), //face dir
868 Go through every y,z and get back(z+) faces in rows of x+
870 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
871 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
872 updateFastFaceRow(data,
876 v3s16(0,0,1), //face dir
887 MapBlockMesh::MapBlockMesh(MeshMakeData *data):
888 m_mesh(new scene::SMesh()),
889 m_gamedef(data->m_gamedef),
890 m_animation_force_timer(0), // force initial animation
893 m_last_daynight_ratio((u32) -1),
896 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
897 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
898 //TimeTaker timer1("MapBlockMesh()");
900 core::array<FastFace> fastfaces_new;
903 We are including the faces of the trailing edges of the block.
904 This means that when something changes, the caller must
905 also update the meshes of the blocks at the leading edges.
907 NOTE: This is the slowest part of this method.
910 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
911 //TimeTaker timer2("updateAllFastFaceRows()");
912 updateAllFastFaceRows(data, fastfaces_new);
917 Convert FastFaces to MeshCollector
920 MeshCollector collector;
923 // avg 0ms (100ms spikes when loading textures the first time)
924 // (NOTE: probably outdated)
925 //TimeTaker timer2("MeshCollector building");
927 for(u32 i=0; i<fastfaces_new.size(); i++)
929 FastFace &f = fastfaces_new[i];
931 const u16 indices[] = {0,1,2,2,3,0};
932 const u16 indices_alternate[] = {0,1,3,2,3,1};
934 if(f.tile.texture.atlas == NULL)
937 const u16 *indices_p = indices;
940 Revert triangles for nicer looking gradient if vertices
941 1 and 3 have same color or 0 and 2 have different color.
942 getRed() is the day color.
944 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
945 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
946 indices_p = indices_alternate;
948 collector.append(f.tile, f.vertices, 4, indices_p, 6);
953 Add special graphics:
960 mapblock_mesh_generate_special(data, collector);
964 Convert MeshCollector to SMesh
965 Also store animation info
967 for(u32 i = 0; i < collector.prebuffers.size(); i++)
969 PreMeshBuffer &p = collector.prebuffers[i];
970 /*dstream<<"p.vertices.size()="<<p.vertices.size()
971 <<", p.indices.size()="<<p.indices.size()
974 // Generate animation data
976 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
978 ITextureSource *tsrc = data->m_gamedef->tsrc();
979 std::string crack_basename = tsrc->getTextureName(p.tile.texture.id);
980 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
981 crack_basename += "^[cracko";
983 crack_basename += "^[crack";
984 m_crack_materials.insert(std::make_pair(i, crack_basename));
987 for(u32 j = 0; j < p.vertices.size(); j++)
989 video::SColor &vc = p.vertices[j].Color;
990 u8 day = vc.getRed();
991 u8 night = vc.getGreen();
992 finalColorBlend(vc, day, night, 1000);
994 m_daynight_diffs[i][j] = std::make_pair(day, night);
999 video::SMaterial material;
1000 material.setFlag(video::EMF_LIGHTING, false);
1001 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1002 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1003 material.setFlag(video::EMF_FOG_ENABLE, true);
1004 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1005 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1006 material.MaterialType
1007 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1008 material.setTexture(0, p.tile.texture.atlas);
1009 p.tile.applyMaterialOptions(material);
1011 // Create meshbuffer
1013 // This is a "Standard MeshBuffer",
1014 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1015 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1017 buf->Material = material;
1019 m_mesh->addMeshBuffer(buf);
1022 buf->append(p.vertices.pointer(), p.vertices.size(),
1023 p.indices.pointer(), p.indices.size());
1027 Do some stuff to the mesh
1030 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
1031 m_mesh->recalculateBoundingBox(); // translateMesh already does this
1036 // Usually 1-700 faces and 1-7 materials
1037 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1038 <<"and uses "<<m_mesh->getMeshBufferCount()
1039 <<" materials (meshbuffers)"<<std::endl;
1042 // Use VBO for mesh (this just would set this for ever buffer)
1043 // This will lead to infinite memory usage because or irrlicht.
1044 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1047 NOTE: If that is enabled, some kind of a queue to the main
1048 thread should be made which would call irrlicht to delete
1049 the hardware buffer and then delete the mesh
1053 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1055 // Check if animation is required for this mesh
1057 !m_crack_materials.empty() ||
1058 !m_daynight_diffs.empty();
1061 MapBlockMesh::~MapBlockMesh()
1067 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1069 if(!m_has_animation)
1071 m_animation_force_timer = 100000;
1075 m_animation_force_timer = myrand_range(5, 100);
1078 if(crack != m_last_crack)
1080 for(std::map<u32, std::string>::iterator
1081 i = m_crack_materials.begin();
1082 i != m_crack_materials.end(); i++)
1084 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1085 std::string basename = i->second;
1087 // Create new texture name from original
1088 ITextureSource *tsrc = m_gamedef->getTextureSource();
1089 std::ostringstream os;
1090 os<<basename<<crack;
1091 AtlasPointer ap = tsrc->getTexture(os.str());
1092 buf->getMaterial().setTexture(0, ap.atlas);
1095 m_last_crack = crack;
1098 // Day-night transition
1099 if(daynight_ratio != m_last_daynight_ratio)
1101 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1102 i = m_daynight_diffs.begin();
1103 i != m_daynight_diffs.end(); i++)
1105 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1106 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1107 for(std::map<u32, std::pair<u8, u8 > >::iterator
1108 j = i->second.begin();
1109 j != i->second.end(); j++)
1111 u32 vertexIndex = j->first;
1112 u8 day = j->second.first;
1113 u8 night = j->second.second;
1114 finalColorBlend(vertices[vertexIndex].Color,
1115 day, night, daynight_ratio);
1118 m_last_daynight_ratio = daynight_ratio;
1128 void MeshCollector::append(const TileSpec &tile,
1129 const video::S3DVertex *vertices, u32 numVertices,
1130 const u16 *indices, u32 numIndices)
1132 PreMeshBuffer *p = NULL;
1133 for(u32 i=0; i<prebuffers.size(); i++)
1135 PreMeshBuffer &pp = prebuffers[i];
1147 prebuffers.push_back(pp);
1148 p = &prebuffers[prebuffers.size()-1];
1151 u32 vertex_count = p->vertices.size();
1152 for(u32 i=0; i<numIndices; i++)
1154 u32 j = indices[i] + vertex_count;
1157 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
1158 // NOTE: Fix is to just add an another MeshBuffer
1160 p->indices.push_back(j);
1162 for(u32 i=0; i<numVertices; i++)
1164 p->vertices.push_back(vertices[i]);