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