Performance fix + SAO factorization
[oweals/minetest.git] / src / wieldmesh.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2014 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 #include "settings.h"
21 #include "wieldmesh.h"
22 #include "inventory.h"
23 #include "client.h"
24 #include "itemdef.h"
25 #include "nodedef.h"
26 #include "mesh.h"
27 #include "mapblock_mesh.h"
28 #include "client/tile.h"
29 #include "log.h"
30 #include "util/numeric.h"
31 #include <map>
32 #include <IMeshManipulator.h>
33
34 #define WIELD_SCALE_FACTOR 30.0
35 #define WIELD_SCALE_FACTOR_EXTRUDED 40.0
36
37 #define MIN_EXTRUSION_MESH_RESOLUTION 16
38 #define MAX_EXTRUSION_MESH_RESOLUTION 512
39
40 static scene::IMesh *createExtrusionMesh(int resolution_x, int resolution_y)
41 {
42         const f32 r = 0.5;
43
44         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
45         video::SColor c(255,255,255,255);
46         v3f scale(1.0, 1.0, 0.1);
47
48         // Front and back
49         {
50                 video::S3DVertex vertices[8] = {
51                         // z-
52                         video::S3DVertex(-r,+r,-r, 0,0,-1, c, 0,0),
53                         video::S3DVertex(+r,+r,-r, 0,0,-1, c, 1,0),
54                         video::S3DVertex(+r,-r,-r, 0,0,-1, c, 1,1),
55                         video::S3DVertex(-r,-r,-r, 0,0,-1, c, 0,1),
56                         // z+
57                         video::S3DVertex(-r,+r,+r, 0,0,+1, c, 0,0),
58                         video::S3DVertex(-r,-r,+r, 0,0,+1, c, 0,1),
59                         video::S3DVertex(+r,-r,+r, 0,0,+1, c, 1,1),
60                         video::S3DVertex(+r,+r,+r, 0,0,+1, c, 1,0),
61                 };
62                 u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4};
63                 buf->append(vertices, 8, indices, 12);
64         }
65
66         f32 pixelsize_x = 1 / (f32) resolution_x;
67         f32 pixelsize_y = 1 / (f32) resolution_y;
68
69         for (int i = 0; i < resolution_x; ++i) {
70                 f32 pixelpos_x = i * pixelsize_x - 0.5;
71                 f32 x0 = pixelpos_x;
72                 f32 x1 = pixelpos_x + pixelsize_x;
73                 f32 tex0 = (i + 0.1) * pixelsize_x;
74                 f32 tex1 = (i + 0.9) * pixelsize_x;
75                 video::S3DVertex vertices[8] = {
76                         // x-
77                         video::S3DVertex(x0,-r,-r, -1,0,0, c, tex0,1),
78                         video::S3DVertex(x0,-r,+r, -1,0,0, c, tex1,1),
79                         video::S3DVertex(x0,+r,+r, -1,0,0, c, tex1,0),
80                         video::S3DVertex(x0,+r,-r, -1,0,0, c, tex0,0),
81                         // x+
82                         video::S3DVertex(x1,-r,-r, +1,0,0, c, tex0,1),
83                         video::S3DVertex(x1,+r,-r, +1,0,0, c, tex0,0),
84                         video::S3DVertex(x1,+r,+r, +1,0,0, c, tex1,0),
85                         video::S3DVertex(x1,-r,+r, +1,0,0, c, tex1,1),
86                 };
87                 u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4};
88                 buf->append(vertices, 8, indices, 12);
89         }
90         for (int i = 0; i < resolution_y; ++i) {
91                 f32 pixelpos_y = i * pixelsize_y - 0.5;
92                 f32 y0 = -pixelpos_y - pixelsize_y;
93                 f32 y1 = -pixelpos_y;
94                 f32 tex0 = (i + 0.1) * pixelsize_y;
95                 f32 tex1 = (i + 0.9) * pixelsize_y;
96                 video::S3DVertex vertices[8] = {
97                         // y-
98                         video::S3DVertex(-r,y0,-r, 0,-1,0, c, 0,tex0),
99                         video::S3DVertex(+r,y0,-r, 0,-1,0, c, 1,tex0),
100                         video::S3DVertex(+r,y0,+r, 0,-1,0, c, 1,tex1),
101                         video::S3DVertex(-r,y0,+r, 0,-1,0, c, 0,tex1),
102                         // y+
103                         video::S3DVertex(-r,y1,-r, 0,+1,0, c, 0,tex0),
104                         video::S3DVertex(-r,y1,+r, 0,+1,0, c, 0,tex1),
105                         video::S3DVertex(+r,y1,+r, 0,+1,0, c, 1,tex1),
106                         video::S3DVertex(+r,y1,-r, 0,+1,0, c, 1,tex0),
107                 };
108                 u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4};
109                 buf->append(vertices, 8, indices, 12);
110         }
111
112         // Create mesh object
113         scene::SMesh *mesh = new scene::SMesh();
114         mesh->addMeshBuffer(buf);
115         buf->drop();
116         scaleMesh(mesh, scale);  // also recalculates bounding box
117         return mesh;
118 }
119
120 /*
121         Caches extrusion meshes so that only one of them per resolution
122         is needed. Also caches one cube (for convenience).
123
124         E.g. there is a single extrusion mesh that is used for all
125         16x16 px images, another for all 256x256 px images, and so on.
126
127         WARNING: Not thread safe. This should not be a problem since
128         rendering related classes (such as WieldMeshSceneNode) will be
129         used from the rendering thread only.
130 */
131 class ExtrusionMeshCache: public IReferenceCounted
132 {
133 public:
134         // Constructor
135         ExtrusionMeshCache()
136         {
137                 for (int resolution = MIN_EXTRUSION_MESH_RESOLUTION;
138                                 resolution <= MAX_EXTRUSION_MESH_RESOLUTION;
139                                 resolution *= 2) {
140                         m_extrusion_meshes[resolution] =
141                                 createExtrusionMesh(resolution, resolution);
142                 }
143                 m_cube = createCubeMesh(v3f(1.0, 1.0, 1.0));
144         }
145         // Destructor
146         virtual ~ExtrusionMeshCache()
147         {
148                 for (std::map<int, scene::IMesh*>::iterator
149                                 it = m_extrusion_meshes.begin();
150                                 it != m_extrusion_meshes.end(); ++it) {
151                         it->second->drop();
152                 }
153                 m_cube->drop();
154         }
155         // Get closest extrusion mesh for given image dimensions
156         // Caller must drop the returned pointer
157         scene::IMesh* create(core::dimension2d<u32> dim)
158         {
159                 // handle non-power of two textures inefficiently without cache
160                 if (!is_power_of_two(dim.Width) || !is_power_of_two(dim.Height)) {
161                         return createExtrusionMesh(dim.Width, dim.Height);
162                 }
163
164                 int maxdim = MYMAX(dim.Width, dim.Height);
165
166                 std::map<int, scene::IMesh*>::iterator
167                         it = m_extrusion_meshes.lower_bound(maxdim);
168
169                 if (it == m_extrusion_meshes.end()) {
170                         // no viable resolution found; use largest one
171                         it = m_extrusion_meshes.find(MAX_EXTRUSION_MESH_RESOLUTION);
172                         sanity_check(it != m_extrusion_meshes.end());
173                 }
174
175                 scene::IMesh *mesh = it->second;
176                 mesh->grab();
177                 return mesh;
178         }
179         // Returns a 1x1x1 cube mesh with one meshbuffer (material) per face
180         // Caller must drop the returned pointer
181         scene::IMesh* createCube()
182         {
183                 m_cube->grab();
184                 return m_cube;
185         }
186
187 private:
188         std::map<int, scene::IMesh*> m_extrusion_meshes;
189         scene::IMesh *m_cube;
190 };
191
192 ExtrusionMeshCache *g_extrusion_mesh_cache = NULL;
193
194
195 WieldMeshSceneNode::WieldMeshSceneNode(
196                 scene::ISceneNode *parent,
197                 scene::ISceneManager *mgr,
198                 s32 id,
199                 bool lighting
200 ):
201         scene::ISceneNode(parent, mgr, id),
202         m_meshnode(NULL),
203         m_material_type(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF),
204         m_lighting(lighting),
205         m_bounding_box(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
206 {
207         m_enable_shaders = g_settings->getBool("enable_shaders");
208         m_anisotropic_filter = g_settings->getBool("anisotropic_filter");
209         m_bilinear_filter = g_settings->getBool("bilinear_filter");
210         m_trilinear_filter = g_settings->getBool("trilinear_filter");
211
212         // If this is the first wield mesh scene node, create a cache
213         // for extrusion meshes (and a cube mesh), otherwise reuse it
214         if (g_extrusion_mesh_cache == NULL)
215                 g_extrusion_mesh_cache = new ExtrusionMeshCache();
216         else
217                 g_extrusion_mesh_cache->grab();
218
219         // Disable bounding box culling for this scene node
220         // since we won't calculate the bounding box.
221         setAutomaticCulling(scene::EAC_OFF);
222
223         // Create the child scene node
224         scene::IMesh *dummymesh = g_extrusion_mesh_cache->createCube();
225         m_meshnode = SceneManager->addMeshSceneNode(dummymesh, this, -1);
226         m_meshnode->setReadOnlyMaterials(false);
227         m_meshnode->setVisible(false);
228         dummymesh->drop(); // m_meshnode grabbed it
229 }
230
231 WieldMeshSceneNode::~WieldMeshSceneNode()
232 {
233         sanity_check(g_extrusion_mesh_cache);
234         if (g_extrusion_mesh_cache->drop())
235                 g_extrusion_mesh_cache = NULL;
236 }
237
238 void WieldMeshSceneNode::setCube(const TileSpec tiles[6],
239                         v3f wield_scale, ITextureSource *tsrc)
240 {
241         scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube();
242         changeToMesh(cubemesh);
243         cubemesh->drop();
244
245         m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR);
246
247         // Customize materials
248         for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) {
249                 assert(i < 6);
250                 video::SMaterial &material = m_meshnode->getMaterial(i);
251                 if (tiles[i].animation_frame_count == 1) {
252                         material.setTexture(0, tiles[i].texture);
253                 } else {
254                         FrameSpec animation_frame = tiles[i].frames[0];
255                         material.setTexture(0, animation_frame.texture);
256                 }
257                 tiles[i].applyMaterialOptions(material);
258         }
259 }
260
261 void WieldMeshSceneNode::setExtruded(const std::string &imagename,
262                 v3f wield_scale, ITextureSource *tsrc, u8 num_frames)
263 {
264         video::ITexture *texture = tsrc->getTexture(imagename);
265         if (!texture) {
266                 changeToMesh(NULL);
267                 return;
268         }
269
270         core::dimension2d<u32> dim = texture->getSize();
271         // Detect animation texture and pull off top frame instead of using entire thing
272         if (num_frames > 1) {
273                 u32 frame_height = dim.Height / num_frames;
274                 dim = core::dimension2d<u32>(dim.Width, frame_height);
275         }
276         scene::IMesh *mesh = g_extrusion_mesh_cache->create(dim);
277         changeToMesh(mesh);
278         mesh->drop();
279
280         m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR_EXTRUDED);
281
282         // Customize material
283         video::SMaterial &material = m_meshnode->getMaterial(0);
284         material.setTexture(0, tsrc->getTextureForMesh(imagename));
285         material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
286         material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
287         material.MaterialType = m_material_type;
288         material.setFlag(video::EMF_BACK_FACE_CULLING, true);
289         // Enable bi/trilinear filtering only for high resolution textures
290         if (dim.Width > 32) {
291                 material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
292                 material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter);
293         } else {
294                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
295                 material.setFlag(video::EMF_TRILINEAR_FILTER, false);
296         }
297         material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_anisotropic_filter);
298         // mipmaps cause "thin black line" artifacts
299 #if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
300         material.setFlag(video::EMF_USE_MIP_MAPS, false);
301 #endif
302         if (m_enable_shaders) {
303                 material.setTexture(2, tsrc->getShaderFlagsTexture(false));
304         }
305 }
306
307 void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
308 {
309         ITextureSource *tsrc = client->getTextureSource();
310         IItemDefManager *idef = client->getItemDefManager();
311         IShaderSource *shdrsrc = client->getShaderSource();
312         INodeDefManager *ndef = client->getNodeDefManager();
313         const ItemDefinition &def = item.getDefinition(idef);
314         const ContentFeatures &f = ndef->get(def.name);
315         content_t id = ndef->getId(def.name);
316
317         if (m_enable_shaders) {
318                 u32 shader_id = shdrsrc->getShader("wielded_shader", TILE_MATERIAL_BASIC, NDT_NORMAL);
319                 m_material_type = shdrsrc->getShaderInfo(shader_id).material;
320         }
321
322         // If wield_image is defined, it overrides everything else
323         if (def.wield_image != "") {
324                 setExtruded(def.wield_image, def.wield_scale, tsrc, 1);
325                 return;
326         }
327         // Handle nodes
328         // See also CItemDefManager::createClientCached()
329         else if (def.type == ITEM_NODE) {
330                 if (f.mesh_ptr[0]) {
331                         // e.g. mesh nodes and nodeboxes
332                         changeToMesh(f.mesh_ptr[0]);
333                         // mesh_ptr[0] is pre-scaled by BS * f->visual_scale
334                         m_meshnode->setScale(
335                                         def.wield_scale * WIELD_SCALE_FACTOR
336                                         / (BS * f.visual_scale));
337                 } else if (f.drawtype == NDT_AIRLIKE) {
338                         changeToMesh(NULL);
339                 } else if (f.drawtype == NDT_PLANTLIKE) {
340                         setExtruded(tsrc->getTextureName(f.tiles[0].texture_id), def.wield_scale, tsrc, f.tiles[0].animation_frame_count);
341                 } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) {
342                         setCube(f.tiles, def.wield_scale, tsrc);
343                 } else {
344                         MeshMakeData mesh_make_data(client, false);
345                         MapNode mesh_make_node(id, 255, 0);
346                         mesh_make_data.fillSingleNode(&mesh_make_node);
347                         MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
348                         changeToMesh(mapblock_mesh.getMesh());
349                         translateMesh(m_meshnode->getMesh(), v3f(-BS, -BS, -BS));
350                         m_meshnode->setScale(
351                                         def.wield_scale * WIELD_SCALE_FACTOR
352                                         / (BS * f.visual_scale));
353                 }
354                 u32 material_count = m_meshnode->getMaterialCount();
355                 if (material_count > 6) {
356                         errorstream << "WieldMeshSceneNode::setItem: Invalid material "
357                                 "count " << material_count << ", truncating to 6" << std::endl;
358                         material_count = 6;
359                 }
360                 for (u32 i = 0; i < material_count; ++i) {
361                         video::SMaterial &material = m_meshnode->getMaterial(i);
362                         material.setFlag(video::EMF_BACK_FACE_CULLING, true);
363                         material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
364                         material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter);
365                         bool animated = (f.tiles[i].animation_frame_count > 1);
366                         if (animated) {
367                                 FrameSpec animation_frame = f.tiles[i].frames[0];
368                                 material.setTexture(0, animation_frame.texture);
369                         } else {
370                                 material.setTexture(0, f.tiles[i].texture);
371                         }
372                         material.MaterialType = m_material_type;
373                         if (m_enable_shaders) {
374                                 if (f.tiles[i].normal_texture) {
375                                         if (animated) {
376                                                 FrameSpec animation_frame = f.tiles[i].frames[0];
377                                                 material.setTexture(1, animation_frame.normal_texture);
378                                         } else {
379                                                 material.setTexture(1, f.tiles[i].normal_texture);
380                                         }
381                                 }
382                                 material.setTexture(2, f.tiles[i].flags_texture);
383                         }
384                 }
385                 return;
386         }
387         else if (def.inventory_image != "") {
388                 setExtruded(def.inventory_image, def.wield_scale, tsrc, 1);
389                 return;
390         }
391
392         // no wield mesh found
393         changeToMesh(NULL);
394 }
395
396 void WieldMeshSceneNode::setColor(video::SColor color)
397 {
398         assert(!m_lighting);
399         setMeshColor(m_meshnode->getMesh(), color);
400         shadeMeshFaces(m_meshnode->getMesh());
401 }
402
403 void WieldMeshSceneNode::render()
404 {
405         // note: if this method is changed to actually do something,
406         // you probably should implement OnRegisterSceneNode as well
407 }
408
409 void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
410 {
411         if (mesh == NULL) {
412                 scene::IMesh *dummymesh = g_extrusion_mesh_cache->createCube();
413                 m_meshnode->setVisible(false);
414                 m_meshnode->setMesh(dummymesh);
415                 dummymesh->drop();  // m_meshnode grabbed it
416         } else {
417                 if (m_lighting) {
418                         m_meshnode->setMesh(mesh);
419                 } else {
420                         /*
421                                 Lighting is disabled, this means the caller can (and probably will)
422                                 call setColor later. We therefore need to clone the mesh so that
423                                 setColor will only modify this scene node's mesh, not others'.
424                         */
425                         scene::IMeshManipulator *meshmanip = SceneManager->getMeshManipulator();
426                         scene::IMesh *new_mesh = meshmanip->createMeshCopy(mesh);
427                         m_meshnode->setMesh(new_mesh);
428                         new_mesh->drop();  // m_meshnode grabbed it
429                 }
430         }
431
432         m_meshnode->setMaterialFlag(video::EMF_LIGHTING, m_lighting);
433         // need to normalize normals when lighting is enabled (because of setScale())
434         m_meshnode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, m_lighting);
435         m_meshnode->setVisible(true);
436 }
437
438 scene::IMesh *getItemMesh(Client *client, const ItemStack &item)
439 {
440         ITextureSource *tsrc = client->getTextureSource();
441         IItemDefManager *idef = client->getItemDefManager();
442         INodeDefManager *ndef = client->getNodeDefManager();
443         const ItemDefinition &def = item.getDefinition(idef);
444         const ContentFeatures &f = ndef->get(def.name);
445         content_t id = ndef->getId(def.name);
446
447         if (!g_extrusion_mesh_cache) {
448                 g_extrusion_mesh_cache = new ExtrusionMeshCache();
449         } else {
450                 g_extrusion_mesh_cache->grab();
451         }
452
453         scene::IMesh *mesh;
454
455         // If inventory_image is defined, it overrides everything else
456         if (def.inventory_image != "") {
457                 mesh = getExtrudedMesh(tsrc, def.inventory_image);
458                 return mesh;
459         } else if (def.type == ITEM_NODE) {
460                 if (f.mesh_ptr[0]) {
461                         mesh = cloneMesh(f.mesh_ptr[0]);
462                         scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
463                         setMeshColor(mesh, video::SColor (255, 255, 255, 255));
464                 } else if (f.drawtype == NDT_PLANTLIKE) {
465                         mesh = getExtrudedMesh(tsrc,
466                                 tsrc->getTextureName(f.tiles[0].texture_id));
467                         return mesh;
468                 } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES
469                         || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
470                         mesh = cloneMesh(g_extrusion_mesh_cache->createCube());
471                         scaleMesh(mesh, v3f(1.2, 1.2, 1.2));
472                 } else {
473                         MeshMakeData mesh_make_data(client, false);
474                         MapNode mesh_make_node(id, 255, 0);
475                         mesh_make_data.fillSingleNode(&mesh_make_node);
476                         MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
477                         mesh = cloneMesh(mapblock_mesh.getMesh());
478                         translateMesh(mesh, v3f(-BS, -BS, -BS));
479                         scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
480                         rotateMeshXZby(mesh, -45);
481                         rotateMeshYZby(mesh, -30);
482
483                         u32 mc = mesh->getMeshBufferCount();
484                         for (u32 i = 0; i < mc; ++i) {
485                                 video::SMaterial &material1 =
486                                         mesh->getMeshBuffer(i)->getMaterial();
487                                 video::SMaterial &material2 =
488                                         mapblock_mesh.getMesh()->getMeshBuffer(i)->getMaterial();
489                                 material1.setTexture(0, material2.getTexture(0));
490                                 material1.setTexture(1, material2.getTexture(1));
491                                 material1.setTexture(2, material2.getTexture(2));
492                                 material1.setTexture(3, material2.getTexture(3));
493                                 material1.MaterialType = material2.MaterialType;
494                         }
495                         return mesh;
496                 }
497
498                 shadeMeshFaces(mesh);
499                 rotateMeshXZby(mesh, -45);
500                 rotateMeshYZby(mesh, -30);
501
502                 u32 mc = mesh->getMeshBufferCount();
503                 for (u32 i = 0; i < mc; ++i) {
504                         video::SMaterial &material = mesh->getMeshBuffer(i)->getMaterial();
505                         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
506                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
507                         material.setFlag(video::EMF_TRILINEAR_FILTER, false);
508                         material.setFlag(video::EMF_BACK_FACE_CULLING, true);
509                         material.setFlag(video::EMF_LIGHTING, false);
510                         if (f.tiles[i].animation_frame_count > 1) {
511                                 FrameSpec animation_frame = f.tiles[i].frames[0];
512                                 material.setTexture(0, animation_frame.texture);
513                         } else {
514                                 material.setTexture(0, f.tiles[i].texture);
515                         }
516                 }
517                 return mesh;
518         }
519         return NULL;
520 }
521
522 scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
523                 const std::string &imagename)
524 {
525         video::ITexture *texture = tsrc->getTextureForMesh(imagename);
526         if (!texture) {
527                 return NULL;
528         }
529
530         core::dimension2d<u32> dim = texture->getSize();
531         scene::IMesh *mesh = cloneMesh(g_extrusion_mesh_cache->create(dim));
532
533         // Customize material
534         video::SMaterial &material = mesh->getMeshBuffer(0)->getMaterial();
535         material.setTexture(0, tsrc->getTexture(imagename));
536         material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
537         material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
538         material.setFlag(video::EMF_BILINEAR_FILTER, false);
539         material.setFlag(video::EMF_TRILINEAR_FILTER, false);
540         material.setFlag(video::EMF_BACK_FACE_CULLING, true);
541         material.setFlag(video::EMF_LIGHTING, false);
542         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
543         scaleMesh(mesh, v3f(2.0, 2.0, 2.0));
544
545         return mesh;
546 }