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_settings and g_texturesource
29 #include "content_mapblock.h"
31 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
33 m_daynight_ratio = daynight_ratio;
34 m_blockpos = block->getPos();
36 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
39 There is no harm not copying the TempMods of the neighbors
40 because they are already copied to this block
43 block->copyTempMods(m_temp_mods);
49 // Allocate this block + neighbors
51 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
52 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
55 //TimeTaker timer("copy central block data");
59 block->copyTo(m_vmanip);
62 //TimeTaker timer("copy neighbor block data");
66 Copy neighbors. This is lightning fast.
67 Copying only the borders would be *very* slow.
71 Map *map = block->getParent();
73 for(u16 i=0; i<6; i++)
75 const v3s16 &dir = g_6dirs[i];
76 v3s16 bp = m_blockpos + dir;
77 MapBlock *b = map->getBlockNoCreateNoEx(bp);
84 void MeshMakeData::fillSingleNode(u32 daynight_ratio, MapNode *node)
86 m_daynight_ratio = daynight_ratio;
87 m_blockpos = v3s16(0,0,0);
90 v3s16 blockpos_nodes = v3s16(0,0,0);
91 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
92 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
93 s32 volume = area.getVolume();
94 s32 our_node_index = area.index(1,1,1);
96 // Allocate this block + neighbors
98 m_vmanip.addArea(area);
101 MapNode *data = new MapNode[volume];
102 for(s32 i = 0; i < volume; i++)
104 if(i == our_node_index)
110 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
113 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
118 vertex_dirs: v3s16[4]
120 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
123 If looked from outside the node towards the face, the corners are:
129 if(dir == v3s16(0,0,1))
131 // If looking towards z+, this is the face that is behind
132 // the center point, facing towards z+.
133 vertex_dirs[0] = v3s16(-1,-1, 1);
134 vertex_dirs[1] = v3s16( 1,-1, 1);
135 vertex_dirs[2] = v3s16( 1, 1, 1);
136 vertex_dirs[3] = v3s16(-1, 1, 1);
138 else if(dir == v3s16(0,0,-1))
141 vertex_dirs[0] = v3s16( 1,-1,-1);
142 vertex_dirs[1] = v3s16(-1,-1,-1);
143 vertex_dirs[2] = v3s16(-1, 1,-1);
144 vertex_dirs[3] = v3s16( 1, 1,-1);
146 else if(dir == v3s16(1,0,0))
149 vertex_dirs[0] = v3s16( 1,-1, 1);
150 vertex_dirs[1] = v3s16( 1,-1,-1);
151 vertex_dirs[2] = v3s16( 1, 1,-1);
152 vertex_dirs[3] = v3s16( 1, 1, 1);
154 else if(dir == v3s16(-1,0,0))
157 vertex_dirs[0] = v3s16(-1,-1,-1);
158 vertex_dirs[1] = v3s16(-1,-1, 1);
159 vertex_dirs[2] = v3s16(-1, 1, 1);
160 vertex_dirs[3] = v3s16(-1, 1,-1);
162 else if(dir == v3s16(0,1,0))
164 // faces towards Y+ (assume Z- as "down" in texture)
165 vertex_dirs[0] = v3s16( 1, 1,-1);
166 vertex_dirs[1] = v3s16(-1, 1,-1);
167 vertex_dirs[2] = v3s16(-1, 1, 1);
168 vertex_dirs[3] = v3s16( 1, 1, 1);
170 else if(dir == v3s16(0,-1,0))
172 // faces towards Y- (assume Z+ as "down" in texture)
173 vertex_dirs[0] = v3s16( 1,-1, 1);
174 vertex_dirs[1] = v3s16(-1,-1, 1);
175 vertex_dirs[2] = v3s16(-1,-1,-1);
176 vertex_dirs[3] = v3s16( 1,-1,-1);
180 video::SColor MapBlock_LightColor(u8 alpha, u8 light)
183 return video::SColor(alpha,light,light,light);
185 //return video::SColor(alpha,light,light,MYMAX(0, (s16)light-25)+25);
186 /*return video::SColor(alpha,light,light,MYMAX(0,
187 pow((float)light/255.0, 0.8)*255.0));*/
189 // Emphase blue a bit in darker places
193 return video::SColor(alpha,light,light,light);
195 return video::SColor(alpha,light,light,MYMAX(0,
196 pow((float)light/lim, power)*lim));
203 video::S3DVertex vertices[4]; // Precalculated vertices
206 static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
207 v3s16 dir, v3f scale, v3f posRelative_f,
208 core::array<FastFace> &dest)
212 // Position is at the center of the cube.
217 v3s16 vertex_dirs[4];
218 getNodeVertexDirs(dir, vertex_dirs);
219 for(u16 i=0; i<4; i++)
222 BS/2*vertex_dirs[i].X,
223 BS/2*vertex_dirs[i].Y,
224 BS/2*vertex_dirs[i].Z
228 for(u16 i=0; i<4; i++)
230 vertex_pos[i].X *= scale.X;
231 vertex_pos[i].Y *= scale.Y;
232 vertex_pos[i].Z *= scale.Z;
233 vertex_pos[i] += pos + posRelative_f;
237 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
238 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
239 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
241 v3f normal(dir.X, dir.Y, dir.Z);
243 u8 alpha = tile.alpha;
245 if(tile.id == TILE_WATER)
246 alpha = WATER_ALPHA;*/
248 float x0 = tile.texture.pos.X;
249 float y0 = tile.texture.pos.Y;
250 float w = tile.texture.size.X;
251 float h = tile.texture.size.Y;
253 /*video::SColor c = MapBlock_LightColor(alpha, li);
255 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
256 core::vector2d<f32>(x0+w*abs_scale, y0+h));
257 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
258 core::vector2d<f32>(x0, y0+h));
259 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
260 core::vector2d<f32>(x0, y0));
261 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
262 core::vector2d<f32>(x0+w*abs_scale, y0));*/
264 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
265 MapBlock_LightColor(alpha, li0),
266 core::vector2d<f32>(x0+w*abs_scale, y0+h));
267 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
268 MapBlock_LightColor(alpha, li1),
269 core::vector2d<f32>(x0, y0+h));
270 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
271 MapBlock_LightColor(alpha, li2),
272 core::vector2d<f32>(x0, y0));
273 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
274 MapBlock_LightColor(alpha, li3),
275 core::vector2d<f32>(x0+w*abs_scale, y0));
279 //f->tile = TILE_STONE;
281 dest.push_back(face);
284 static TileSpec getTile(const MapNode &node, v3s16 dir, INodeDefManager *nodemgr)
286 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
287 // (0,0,1), (0,0,-1) or (0,0,0)
288 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
290 // Convert direction to single integer for table lookup
295 // 4 = invalid, treat as (0,0,0)
299 u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
301 // Get rotation for things like chests
302 u8 facedir = node.getFaceDir(nodemgr);
303 assert(facedir <= 3);
305 static const u8 dir_to_tile[4 * 8] =
307 // 0 +X +Y +Z 0 -Z -Y -X
308 0, 2, 0, 4, 0, 5, 1, 3, // facedir = 0
309 0, 4, 0, 3, 0, 2, 1, 5, // facedir = 1
310 0, 3, 0, 5, 0, 4, 1, 2, // facedir = 2
311 0, 5, 0, 2, 0, 3, 1, 4, // facedir = 3
314 return nodemgr->get(node).tiles[dir_to_tile[facedir*8 + dir_i]];
318 Gets node tile from any place relative to block.
319 Returns TILE_NODE if doesn't exist or should not be drawn.
321 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
322 NodeModMap *temp_mods, ITextureSource *tsrc, INodeDefManager *ndef)
325 spec = getTile(mn, face_dir, ndef);
328 Check temporary modifications on this node
330 /*core::map<v3s16, NodeMod>::Node *n;
331 n = m_temp_mods.find(p);
335 struct NodeMod mod = n->getValue();*/
337 if(temp_mods && temp_mods->get(p, &mod))
339 #if 0 // NODEMOD_CHANGECONTENT isn't used at the moment
340 if(mod.type == NODEMOD_CHANGECONTENT)
342 MapNode mn2(mod.param);
343 spec = getTile(mn2, face_dir, ndef);
346 if(mod.type == NODEMOD_CRACK)
349 Get texture id, translate it to name, append stuff to
353 // Get original texture name
354 u32 orig_id = spec.texture.id;
355 std::string orig_name = tsrc->getTextureName(orig_id);
357 // Create new texture name
358 std::ostringstream os;
359 os<<orig_name<<"^[crack"<<mod.param;
362 u32 new_id = tsrc->getTextureId(os.str());
364 /*dstream<<"MapBlock::getNodeTile(): Switching from "
365 <<orig_name<<" to "<<os.str()<<" ("
366 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
368 spec.texture = tsrc->getTexture(new_id);
375 static content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap *temp_mods)
378 Check temporary modifications on this node
380 #if 0 // NODEMOD_CHANGECONTENT isn't used at the moment
382 if(temp_mods && temp_mods->get(p, &mod))
384 if(mod.type == NODEMOD_CHANGECONTENT)
389 if(mod.type == NODEMOD_CRACK)
392 Content doesn't change.
394 face_contents works just like it should, because
395 there should not be faces between differently cracked
398 If a semi-transparent node is cracked in front an
399 another one, it really doesn't matter whether there
400 is a cracked face drawn in between or not.
406 return mn.getContent();
420 // Calculate lighting at the XYZ- corner of p
421 static u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio,
422 INodeDefManager *ndef)
424 u16 ambient_occlusion = 0;
427 for(u32 i=0; i<8; i++)
429 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
430 if(ndef->get(n).param_type == CPT_LIGHT
431 // Fast-style leaves look better this way
432 && ndef->get(n).solidness != 2)
434 light += decode_light(n.getLightBlend(daynight_ratio, ndef));
439 if(n.getContent() != CONTENT_IGNORE)
447 light /= light_count;
449 if(ambient_occlusion > 4)
451 ambient_occlusion -= 4;
452 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
458 // Calculate lighting at the given corner of p
459 static u8 getSmoothLight(v3s16 p, v3s16 corner,
460 VoxelManipulator &vmanip, u32 daynight_ratio, INodeDefManager *ndef)
462 if(corner.X == 1) p.X += 1;
463 else assert(corner.X == -1);
464 if(corner.Y == 1) p.Y += 1;
465 else assert(corner.Y == -1);
466 if(corner.Z == 1) p.Z += 1;
467 else assert(corner.Z == -1);
469 return getSmoothLight(p, vmanip, daynight_ratio, ndef);
472 static void getTileInfo(
474 v3s16 blockpos_nodes,
478 VoxelManipulator &vmanip,
479 NodeModMap *temp_mods,
480 bool smooth_lighting,
485 v3s16 &face_dir_corrected,
490 ITextureSource *tsrc = gamedef->tsrc();
491 INodeDefManager *ndef = gamedef->ndef();
493 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
494 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
495 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods, tsrc, ndef);
496 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods, tsrc, ndef);
499 content_t content0 = getNodeContent(p, n0, temp_mods);
500 content_t content1 = getNodeContent(p + face_dir, n1, temp_mods);
501 bool equivalent = false;
502 u8 mf = face_contents(content0, content1, &equivalent, ndef);
516 face_dir_corrected = face_dir;
521 p_corrected = p + face_dir;
522 face_dir_corrected = -face_dir;
525 // eg. water and glass
527 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
529 if(smooth_lighting == false)
531 lights[0] = lights[1] = lights[2] = lights[3] =
532 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir, ndef));
536 v3s16 vertex_dirs[4];
537 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
538 for(u16 i=0; i<4; i++)
540 lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
541 vertex_dirs[i], vmanip, daynight_ratio, ndef);
550 translate_dir: unit vector with only one of x, y or z
551 face_dir: unit vector with only one of x, y or z
553 static void updateFastFaceRow(
562 core::array<FastFace> &dest,
563 NodeModMap *temp_mods,
564 VoxelManipulator &vmanip,
565 v3s16 blockpos_nodes,
566 bool smooth_lighting,
571 u16 continuous_tiles_count = 0;
573 bool makes_face = false;
575 v3s16 face_dir_corrected;
576 u8 lights[4] = {0,0,0,0};
578 getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
579 vmanip, temp_mods, smooth_lighting, gamedef,
580 makes_face, p_corrected, face_dir_corrected, lights, tile);
582 for(u16 j=0; j<length; j++)
584 // If tiling can be done, this is set to false in the next step
585 bool next_is_different = true;
589 bool next_makes_face = false;
590 v3s16 next_p_corrected;
591 v3s16 next_face_dir_corrected;
592 u8 next_lights[4] = {0,0,0,0};
595 // If at last position, there is nothing to compare to and
596 // the face must be drawn anyway
599 p_next = p + translate_dir;
601 getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
602 vmanip, temp_mods, smooth_lighting, gamedef,
603 next_makes_face, next_p_corrected,
604 next_face_dir_corrected, next_lights,
607 if(next_makes_face == makes_face
608 && next_p_corrected == p_corrected + translate_dir
609 && next_face_dir_corrected == face_dir_corrected
610 && next_lights[0] == lights[0]
611 && next_lights[1] == lights[1]
612 && next_lights[2] == lights[2]
613 && next_lights[3] == lights[3]
614 && next_tile == tile)
616 next_is_different = false;
620 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
621 next_makes_face != makes_face ? 1 : 0);
622 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
623 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
624 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
625 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
626 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
627 (next_lights[0] != lights[0] ||
628 next_lights[0] != lights[0] ||
629 next_lights[0] != lights[0] ||
630 next_lights[0] != lights[0]) ? 1 : 0);
631 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
632 !(next_tile == tile) ? 1 : 0);
635 /*g_profiler->add("Meshgen: Total faces checked", 1);
637 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
640 g_profiler->add("Meshgen: diff: last position", 1);*/
643 continuous_tiles_count++;
645 // This is set to true if the texture doesn't allow more tiling
646 bool end_of_texture = false;
648 If there is no texture, it can be tiled infinitely.
649 If tiled==0, it means the texture can be tiled infinitely.
650 Otherwise check tiled agains continuous_tiles_count.
652 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
654 if(tile.texture.tiled <= continuous_tiles_count)
655 end_of_texture = true;
658 // Do this to disable tiling textures
659 //end_of_texture = true; //DEBUG
661 if(next_is_different || end_of_texture)
664 Create a face if there should be one
668 // Floating point conversion of the position vector
669 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
670 // Center point of face (kind of)
671 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
672 if(continuous_tiles_count != 1)
673 sp += translate_dir_f;
676 if(translate_dir.X != 0)
678 scale.X = continuous_tiles_count;
680 if(translate_dir.Y != 0)
682 scale.Y = continuous_tiles_count;
684 if(translate_dir.Z != 0)
686 scale.Z = continuous_tiles_count;
689 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
690 sp, face_dir_corrected, scale,
691 posRelative_f, dest);
693 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
694 for(int i=1; i<continuous_tiles_count; i++){
695 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
699 continuous_tiles_count = 0;
701 makes_face = next_makes_face;
702 p_corrected = next_p_corrected;
703 face_dir_corrected = next_face_dir_corrected;
704 lights[0] = next_lights[0];
705 lights[1] = next_lights[1];
706 lights[2] = next_lights[2];
707 lights[3] = next_lights[3];
715 scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef)
717 // 4-21ms for MAP_BLOCKSIZE=16
718 // 24-155ms for MAP_BLOCKSIZE=32
719 //TimeTaker timer1("makeMapBlockMesh()");
721 core::array<FastFace> fastfaces_new;
723 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
725 // floating point conversion
726 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
731 //bool new_style_water = g_settings->getBool("new_style_water");
732 //bool new_style_leaves = g_settings->getBool("new_style_leaves");
733 bool smooth_lighting = g_settings->getBool("smooth_lighting");
736 We are including the faces of the trailing edges of the block.
737 This means that when something changes, the caller must
738 also update the meshes of the blocks at the leading edges.
740 NOTE: This is the slowest part of this method.
744 // 4-23ms for MAP_BLOCKSIZE=16
745 //TimeTaker timer2("updateMesh() collect");
748 Go through every y,z and get top(y+) faces in rows of x+
750 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
751 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
752 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
753 v3s16(0,y,z), MAP_BLOCKSIZE,
756 v3s16(0,1,0), //face dir
767 Go through every x,y and get right(x+) faces in rows of z+
769 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
770 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
771 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
772 v3s16(x,y,0), MAP_BLOCKSIZE,
786 Go through every y,z and get back(z+) faces in rows of x+
788 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
789 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
790 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
791 v3s16(0,y,z), MAP_BLOCKSIZE,
809 Convert FastFaces to SMesh
812 MeshCollector collector;
814 if(fastfaces_new.size() > 0)
816 // avg 0ms (100ms spikes when loading textures the first time)
817 //TimeTaker timer2("updateMesh() mesh building");
819 video::SMaterial material;
820 material.setFlag(video::EMF_LIGHTING, false);
821 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
822 material.setFlag(video::EMF_BILINEAR_FILTER, false);
823 material.setFlag(video::EMF_FOG_ENABLE, true);
824 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
825 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
826 material.MaterialType
827 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
829 for(u32 i=0; i<fastfaces_new.size(); i++)
831 FastFace &f = fastfaces_new[i];
833 const u16 indices[] = {0,1,2,2,3,0};
834 const u16 indices_alternate[] = {0,1,3,2,3,1};
836 video::ITexture *texture = f.tile.texture.atlas;
840 material.setTexture(0, texture);
842 f.tile.applyMaterialOptions(material);
844 const u16 *indices_p = indices;
847 Revert triangles for nicer looking gradient if vertices
848 1 and 3 have same color or 0 and 2 have different color.
850 if(f.vertices[0].Color != f.vertices[2].Color
851 || f.vertices[1].Color == f.vertices[3].Color)
852 indices_p = indices_alternate;
854 collector.append(material, f.vertices, 4, indices_p, 6);
859 Add special graphics:
866 mapblock_mesh_generate_special(data, collector, gamedef);
869 Add stuff from collector to mesh
872 scene::SMesh *mesh_new = NULL;
873 mesh_new = new scene::SMesh();
875 collector.fillMesh(mesh_new);
878 Do some stuff to the mesh
881 mesh_new->recalculateBoundingBox();
884 Delete new mesh if it is empty
887 if(mesh_new->getMeshBufferCount() == 0)
896 // Usually 1-700 faces and 1-7 materials
897 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
898 <<"and uses "<<mesh_new->getMeshBufferCount()
899 <<" materials (meshbuffers)"<<std::endl;
902 // Use VBO for mesh (this just would set this for ever buffer)
903 // This will lead to infinite memory usage because or irrlicht.
904 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
907 NOTE: If that is enabled, some kind of a queue to the main
908 thread should be made which would call irrlicht to delete
909 the hardware buffer and then delete the mesh
915 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;