]> git.lizzy.rs Git - dragonfireclient.git/blob - src/wieldmesh.cpp
Fix build since: "Remove referenced schematics from Decorations on clear"
[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 "gamedef.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 32   // not 16: causes too many "holes"
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)
263 {
264         video::ITexture *texture = tsrc->getTexture(imagename);
265         if (!texture) {
266                 changeToMesh(NULL);
267                 return;
268         }
269         core::dimension2d<u32> dim = texture->getSize();
270         scene::IMesh *mesh = g_extrusion_mesh_cache->create(dim);
271         changeToMesh(mesh);
272         mesh->drop();
273
274         m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR_EXTRUDED);
275
276         // Customize material
277         video::SMaterial &material = m_meshnode->getMaterial(0);
278         material.setTexture(0, tsrc->getTextureForMesh(imagename));
279         material.MaterialType = m_material_type;
280         material.setFlag(video::EMF_BACK_FACE_CULLING, true);
281         // Enable bi/trilinear filtering only for high resolution textures
282         if (dim.Width > 32) {
283                 material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
284                 material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter);
285         } else {
286                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
287                 material.setFlag(video::EMF_TRILINEAR_FILTER, false);
288         }
289         material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_anisotropic_filter);
290         // mipmaps cause "thin black line" artifacts
291 #if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
292         material.setFlag(video::EMF_USE_MIP_MAPS, false);
293 #endif
294
295 #if 0
296 //// TODO(RealBadAngel): Reactivate when shader is added for wield items
297         if (m_enable_shaders)
298                 material.setTexture(2, tsrc->getTexture("disable_img.png"));
299 #endif
300 }
301
302 void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef)
303 {
304         ITextureSource *tsrc = gamedef->getTextureSource();
305         IItemDefManager *idef = gamedef->getItemDefManager();
306         //IShaderSource *shdrsrc = gamedef->getShaderSource();
307         INodeDefManager *ndef = gamedef->getNodeDefManager();
308         const ItemDefinition &def = item.getDefinition(idef);
309         const ContentFeatures &f = ndef->get(def.name);
310         content_t id = ndef->getId(def.name);
311
312 #if 0
313 //// TODO(RealBadAngel): Reactivate when shader is added for wield items
314         if (m_enable_shaders) {
315                 u32 shader_id = shdrsrc->getShader("nodes_shader", TILE_MATERIAL_BASIC, NDT_NORMAL);
316                 m_material_type = shdrsrc->getShaderInfo(shader_id).material;
317         }
318 #endif
319
320         // If wield_image is defined, it overrides everything else
321         if (def.wield_image != "") {
322                 setExtruded(def.wield_image, def.wield_scale, tsrc);
323                 return;
324         }
325         // Handle nodes
326         // See also CItemDefManager::createClientCached()
327         else if (def.type == ITEM_NODE) {
328                 if (f.mesh_ptr[0]) {
329                         // e.g. mesh nodes and nodeboxes
330                         changeToMesh(f.mesh_ptr[0]);
331                         // mesh_ptr[0] is pre-scaled by BS * f->visual_scale
332                         m_meshnode->setScale(
333                                         def.wield_scale * WIELD_SCALE_FACTOR
334                                         / (BS * f.visual_scale));
335                 } else if (f.drawtype == NDT_AIRLIKE) {
336                         changeToMesh(NULL);
337                 } else if (f.drawtype == NDT_PLANTLIKE) {
338                         setExtruded(tsrc->getTextureName(f.tiles[0].texture_id), def.wield_scale, tsrc);
339                 } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) {
340                         setCube(f.tiles, def.wield_scale, tsrc);
341                 } else {
342                         //// TODO: Change false in the following constructor args to
343                         //// appropriate value when shader is added for wield items (if applicable)
344                         MeshMakeData mesh_make_data(gamedef, 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                 for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) {
355                         assert(i < 6);
356                         video::SMaterial &material = m_meshnode->getMaterial(i);
357                         material.setFlag(video::EMF_BACK_FACE_CULLING, true);
358                         material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
359                         material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter);
360                         bool animated = (f.tiles[i].animation_frame_count > 1);
361                         if (animated) {
362                                 FrameSpec animation_frame = f.tiles[i].frames[0];
363                                 material.setTexture(0, animation_frame.texture);
364                         } else {
365                                 material.setTexture(0, f.tiles[i].texture);
366                         }
367                         material.MaterialType = m_material_type;
368 #if 0
369 //// TODO(RealBadAngel): Reactivate when shader is added for wield items
370                         if (m_enable_shaders) {
371                                 if (f.tiles[i].normal_texture) {
372                                         if (animated) {
373                                                 FrameSpec animation_frame = f.tiles[i].frames[0];
374                                                 material.setTexture(1, animation_frame.normal_texture);
375                                         } else {
376                                                 material.setTexture(1, f.tiles[i].normal_texture);
377                                         }
378                                         material.setTexture(2, tsrc->getTexture("enable_img.png"));
379                                 } else {
380                                         material.setTexture(2, tsrc->getTexture("disable_img.png"));
381                                 }
382                         }
383 #endif
384                 }
385                 return;
386         }
387         else if (def.inventory_image != "") {
388                 setExtruded(def.inventory_image, def.wield_scale, tsrc);
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 }
401
402 void WieldMeshSceneNode::render()
403 {
404         // note: if this method is changed to actually do something,
405         // you probably should implement OnRegisterSceneNode as well
406 }
407
408 void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
409 {
410         if (mesh == NULL) {
411                 scene::IMesh *dummymesh = g_extrusion_mesh_cache->createCube();
412                 m_meshnode->setVisible(false);
413                 m_meshnode->setMesh(dummymesh);
414                 dummymesh->drop();  // m_meshnode grabbed it
415         } else {
416                 if (m_lighting) {
417                         m_meshnode->setMesh(mesh);
418                 } else {
419                         /*
420                                 Lighting is disabled, this means the caller can (and probably will)
421                                 call setColor later. We therefore need to clone the mesh so that
422                                 setColor will only modify this scene node's mesh, not others'.
423                         */
424                         scene::IMeshManipulator *meshmanip = SceneManager->getMeshManipulator();
425                         scene::IMesh *new_mesh = meshmanip->createMeshCopy(mesh);
426                         m_meshnode->setMesh(new_mesh);
427                         new_mesh->drop();  // m_meshnode grabbed it
428                 }
429         }
430
431         m_meshnode->setMaterialFlag(video::EMF_LIGHTING, m_lighting);
432         // need to normalize normals when lighting is enabled (because of setScale())
433         m_meshnode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, m_lighting);
434         m_meshnode->setVisible(true);
435 }