]> git.lizzy.rs Git - dragonfireclient.git/blob - src/wieldmesh.cpp
Hard-coded undersampling.
[dragonfireclient.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         // Color-related
323         m_colors.clear();
324         video::SColor basecolor = idef->getItemstackColor(item, client);
325
326         // If wield_image is defined, it overrides everything else
327         if (def.wield_image != "") {
328                 setExtruded(def.wield_image, def.wield_scale, tsrc, 1);
329                 m_colors.push_back(basecolor);
330                 return;
331         }
332         // Handle nodes
333         // See also CItemDefManager::createClientCached()
334         else if (def.type == ITEM_NODE) {
335                 if (f.mesh_ptr[0]) {
336                         // e.g. mesh nodes and nodeboxes
337                         changeToMesh(f.mesh_ptr[0]);
338                         // mesh_ptr[0] is pre-scaled by BS * f->visual_scale
339                         m_meshnode->setScale(
340                                         def.wield_scale * WIELD_SCALE_FACTOR
341                                         / (BS * f.visual_scale));
342                 } else if (f.drawtype == NDT_AIRLIKE) {
343                         changeToMesh(NULL);
344                 } else if (f.drawtype == NDT_PLANTLIKE) {
345                         setExtruded(tsrc->getTextureName(f.tiles[0].texture_id), def.wield_scale, tsrc, f.tiles[0].animation_frame_count);
346                 } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) {
347                         setCube(f.tiles, def.wield_scale, tsrc);
348                 } else {
349                         MeshMakeData mesh_make_data(client, false);
350                         MapNode mesh_make_node(id, 255, 0);
351                         mesh_make_data.fillSingleNode(&mesh_make_node);
352                         MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
353                         changeToMesh(mapblock_mesh.getMesh());
354                         translateMesh(m_meshnode->getMesh(), v3f(-BS, -BS, -BS));
355                         m_meshnode->setScale(
356                                         def.wield_scale * WIELD_SCALE_FACTOR
357                                         / (BS * f.visual_scale));
358                 }
359                 u32 material_count = m_meshnode->getMaterialCount();
360                 if (material_count > 6) {
361                         errorstream << "WieldMeshSceneNode::setItem: Invalid material "
362                                 "count " << material_count << ", truncating to 6" << std::endl;
363                         material_count = 6;
364                 }
365                 for (u32 i = 0; i < material_count; ++i) {
366                         const TileSpec *tile = &(f.tiles[i]);
367                         video::SMaterial &material = m_meshnode->getMaterial(i);
368                         material.setFlag(video::EMF_BACK_FACE_CULLING, true);
369                         material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
370                         material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter);
371                         bool animated = (tile->animation_frame_count > 1);
372                         if (animated) {
373                                 FrameSpec animation_frame = tile->frames[0];
374                                 material.setTexture(0, animation_frame.texture);
375                         } else {
376                                 material.setTexture(0, tile->texture);
377                         }
378                         m_colors.push_back(tile->has_color ? tile->color : basecolor);
379                         material.MaterialType = m_material_type;
380                         if (m_enable_shaders) {
381                                 if (tile->normal_texture) {
382                                         if (animated) {
383                                                 FrameSpec animation_frame = tile->frames[0];
384                                                 material.setTexture(1, animation_frame.normal_texture);
385                                         } else {
386                                                 material.setTexture(1, tile->normal_texture);
387                                         }
388                                 }
389                                 material.setTexture(2, tile->flags_texture);
390                         }
391                 }
392                 return;
393         }
394         else if (def.inventory_image != "") {
395                 setExtruded(def.inventory_image, def.wield_scale, tsrc, 1);
396                 m_colors.push_back(basecolor);
397                 return;
398         }
399
400         // no wield mesh found
401         changeToMesh(NULL);
402 }
403
404 void WieldMeshSceneNode::setColor(video::SColor c)
405 {
406         assert(!m_lighting);
407         scene::IMesh *mesh=m_meshnode->getMesh();
408         if (mesh == NULL)
409                 return;
410
411         u8 red = c.getRed();
412         u8 green = c.getGreen();
413         u8 blue = c.getBlue();
414         u32 mc = mesh->getMeshBufferCount();
415         for (u32 j = 0; j < mc; j++) {
416                 video::SColor bc(0xFFFFFFFF);
417                 if (m_colors.size() > j)
418                         bc = m_colors[j];
419                 video::SColor buffercolor(255,
420                         bc.getRed() * red / 255,
421                         bc.getGreen() * green / 255,
422                         bc.getBlue() * blue / 255);
423                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
424                 colorizeMeshBuffer(buf, &buffercolor);
425         }
426 }
427
428 void WieldMeshSceneNode::render()
429 {
430         // note: if this method is changed to actually do something,
431         // you probably should implement OnRegisterSceneNode as well
432 }
433
434 void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
435 {
436         if (mesh == NULL) {
437                 scene::IMesh *dummymesh = g_extrusion_mesh_cache->createCube();
438                 m_meshnode->setVisible(false);
439                 m_meshnode->setMesh(dummymesh);
440                 dummymesh->drop();  // m_meshnode grabbed it
441         } else {
442                 if (m_lighting) {
443                         m_meshnode->setMesh(mesh);
444                 } else {
445                         /*
446                                 Lighting is disabled, this means the caller can (and probably will)
447                                 call setColor later. We therefore need to clone the mesh so that
448                                 setColor will only modify this scene node's mesh, not others'.
449                         */
450                         scene::IMeshManipulator *meshmanip = SceneManager->getMeshManipulator();
451                         scene::IMesh *new_mesh = meshmanip->createMeshCopy(mesh);
452                         m_meshnode->setMesh(new_mesh);
453                         new_mesh->drop();  // m_meshnode grabbed it
454                 }
455         }
456
457         m_meshnode->setMaterialFlag(video::EMF_LIGHTING, m_lighting);
458         // need to normalize normals when lighting is enabled (because of setScale())
459         m_meshnode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, m_lighting);
460         m_meshnode->setVisible(true);
461 }
462
463 void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
464 {
465         ITextureSource *tsrc = client->getTextureSource();
466         IItemDefManager *idef = client->getItemDefManager();
467         INodeDefManager *ndef = client->getNodeDefManager();
468         const ItemDefinition &def = item.getDefinition(idef);
469         const ContentFeatures &f = ndef->get(def.name);
470         content_t id = ndef->getId(def.name);
471
472         if (!g_extrusion_mesh_cache) {
473                 g_extrusion_mesh_cache = new ExtrusionMeshCache();
474         } else {
475                 g_extrusion_mesh_cache->grab();
476         }
477
478         scene::IMesh *mesh;
479
480         // If inventory_image is defined, it overrides everything else
481         if (def.inventory_image != "") {
482                 mesh = getExtrudedMesh(tsrc, def.inventory_image);
483                 result->mesh = mesh;
484                 result->buffer_colors.push_back(
485                         std::pair<bool, video::SColor>(false, video::SColor(0xFFFFFFFF)));
486         } else if (def.type == ITEM_NODE) {
487                 if (f.mesh_ptr[0]) {
488                         mesh = cloneMesh(f.mesh_ptr[0]);
489                         scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
490                 } else if (f.drawtype == NDT_PLANTLIKE) {
491                         mesh = getExtrudedMesh(tsrc,
492                                 tsrc->getTextureName(f.tiles[0].texture_id));
493                 } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES
494                         || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
495                         mesh = cloneMesh(g_extrusion_mesh_cache->createCube());
496                         scaleMesh(mesh, v3f(1.2, 1.2, 1.2));
497                 } else {
498                         MeshMakeData mesh_make_data(client, false);
499                         MapNode mesh_make_node(id, 255, 0);
500                         mesh_make_data.fillSingleNode(&mesh_make_node);
501                         MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
502                         mesh = cloneMesh(mapblock_mesh.getMesh());
503                         translateMesh(mesh, v3f(-BS, -BS, -BS));
504                         scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
505
506                         u32 mc = mesh->getMeshBufferCount();
507                         for (u32 i = 0; i < mc; ++i) {
508                                 video::SMaterial &material1 =
509                                         mesh->getMeshBuffer(i)->getMaterial();
510                                 video::SMaterial &material2 =
511                                         mapblock_mesh.getMesh()->getMeshBuffer(i)->getMaterial();
512                                 material1.setTexture(0, material2.getTexture(0));
513                                 material1.setTexture(1, material2.getTexture(1));
514                                 material1.setTexture(2, material2.getTexture(2));
515                                 material1.setTexture(3, material2.getTexture(3));
516                                 material1.MaterialType = material2.MaterialType;
517                         }
518                 }
519
520                 u32 mc = mesh->getMeshBufferCount();
521                 for (u32 i = 0; i < mc; ++i) {
522                         const TileSpec *tile = &(f.tiles[i]);
523                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
524                         result->buffer_colors.push_back(
525                                 std::pair<bool, video::SColor>(tile->has_color, tile->color));
526                         colorizeMeshBuffer(buf, &tile->color);
527                         video::SMaterial &material = buf->getMaterial();
528                         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
529                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
530                         material.setFlag(video::EMF_TRILINEAR_FILTER, false);
531                         material.setFlag(video::EMF_BACK_FACE_CULLING, true);
532                         material.setFlag(video::EMF_LIGHTING, false);
533                         if (tile->animation_frame_count > 1) {
534                                 FrameSpec animation_frame = tile->frames[0];
535                                 material.setTexture(0, animation_frame.texture);
536                         } else {
537                                 material.setTexture(0, tile->texture);
538                         }
539                 }
540
541                 rotateMeshXZby(mesh, -45);
542                 rotateMeshYZby(mesh, -30);
543                 result->mesh = mesh;
544         }
545 }
546
547 scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
548                 const std::string &imagename)
549 {
550         video::ITexture *texture = tsrc->getTextureForMesh(imagename);
551         if (!texture) {
552                 return NULL;
553         }
554
555         core::dimension2d<u32> dim = texture->getSize();
556         scene::IMesh *mesh = cloneMesh(g_extrusion_mesh_cache->create(dim));
557
558         // Customize material
559         video::SMaterial &material = mesh->getMeshBuffer(0)->getMaterial();
560         material.setTexture(0, tsrc->getTexture(imagename));
561         material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
562         material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
563         material.setFlag(video::EMF_BILINEAR_FILTER, false);
564         material.setFlag(video::EMF_TRILINEAR_FILTER, false);
565         material.setFlag(video::EMF_BACK_FACE_CULLING, true);
566         material.setFlag(video::EMF_LIGHTING, false);
567         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
568         scaleMesh(mesh, v3f(2.0, 2.0, 2.0));
569
570         return mesh;
571 }