LCOV - code coverage report
Current view: top level - src - wieldmesh.cpp (source / functions) Hit Total Coverage
Test: report Lines: 160 193 82.9 %
Date: 2015-07-11 18:23:49 Functions: 16 19 84.2 %

          Line data    Source code
       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           5 : static scene::IMesh* createExtrusionMesh(int resolution_x, int resolution_y)
      41             : {
      42           5 :         const f32 r = 0.5;
      43             : 
      44           5 :         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
      45           5 :         video::SColor c(255,255,255,255);
      46           5 :         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           5 :                 };
      62           5 :                 u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4};
      63           5 :                 buf->append(vertices, 8, indices, 12);
      64             :         }
      65             : 
      66           5 :         f32 pixelsize_x = 1 / (f32) resolution_x;
      67           5 :         f32 pixelsize_y = 1 / (f32) resolution_y;
      68             : 
      69         997 :         for (int i = 0; i < resolution_x; ++i) {
      70         992 :                 f32 pixelpos_x = i * pixelsize_x - 0.5;
      71         992 :                 f32 x0 = pixelpos_x;
      72         992 :                 f32 x1 = pixelpos_x + pixelsize_x;
      73         992 :                 f32 tex0 = (i + 0.1) * pixelsize_x;
      74         992 :                 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         992 :                 };
      87         992 :                 u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4};
      88         992 :                 buf->append(vertices, 8, indices, 12);
      89             :         }
      90         997 :         for (int i = 0; i < resolution_y; ++i) {
      91         992 :                 f32 pixelpos_y = i * pixelsize_y - 0.5;
      92         992 :                 f32 y0 = -pixelpos_y - pixelsize_y;
      93         992 :                 f32 y1 = -pixelpos_y;
      94         992 :                 f32 tex0 = (i + 0.1) * pixelsize_y;
      95         992 :                 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         992 :                 };
     108         992 :                 u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4};
     109         992 :                 buf->append(vertices, 8, indices, 12);
     110             :         }
     111             : 
     112             :         // Create mesh object
     113           5 :         scene::SMesh *mesh = new scene::SMesh();
     114           5 :         mesh->addMeshBuffer(buf);
     115           5 :         buf->drop();
     116           5 :         scaleMesh(mesh, scale);  // also recalculates bounding box
     117           5 :         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           1 :         ExtrusionMeshCache()
     136           1 :         {
     137           6 :                 for (int resolution = MIN_EXTRUSION_MESH_RESOLUTION;
     138           6 :                                 resolution <= MAX_EXTRUSION_MESH_RESOLUTION;
     139             :                                 resolution *= 2) {
     140           5 :                         m_extrusion_meshes[resolution] =
     141           5 :                                 createExtrusionMesh(resolution, resolution);
     142             :                 }
     143           1 :                 m_cube = createCubeMesh(v3f(1.0, 1.0, 1.0));
     144           1 :         }
     145             :         // Destructor
     146           2 :         virtual ~ExtrusionMeshCache()
     147           2 :         {
     148          17 :                 for (std::map<int, scene::IMesh*>::iterator
     149           1 :                                 it = m_extrusion_meshes.begin();
     150          12 :                                 it != m_extrusion_meshes.end(); ++it) {
     151           5 :                         it->second->drop();
     152             :                 }
     153           1 :                 m_cube->drop();
     154           2 :         }
     155             :         // Get closest extrusion mesh for given image dimensions
     156             :         // Caller must drop the returned pointer
     157          14 :         scene::IMesh* create(core::dimension2d<u32> dim)
     158             :         {
     159             :                 // handle non-power of two textures inefficiently without cache
     160          14 :                 if (!is_power_of_two(dim.Width) || !is_power_of_two(dim.Height)) {
     161           0 :                         return createExtrusionMesh(dim.Width, dim.Height);
     162             :                 }
     163             : 
     164          14 :                 int maxdim = MYMAX(dim.Width, dim.Height);
     165             : 
     166             :                 std::map<int, scene::IMesh*>::iterator
     167          14 :                         it = m_extrusion_meshes.lower_bound(maxdim);
     168             : 
     169          14 :                 if (it == m_extrusion_meshes.end()) {
     170             :                         // no viable resolution found; use largest one
     171           0 :                         it = m_extrusion_meshes.find(MAX_EXTRUSION_MESH_RESOLUTION);
     172           0 :                         sanity_check(it != m_extrusion_meshes.end());
     173             :                 }
     174             : 
     175          14 :                 scene::IMesh *mesh = it->second;
     176          14 :                 mesh->grab();
     177          14 :                 return mesh;
     178             :         }
     179             :         // Returns a 1x1x1 cube mesh with one meshbuffer (material) per face
     180             :         // Caller must drop the returned pointer
     181          52 :         scene::IMesh* createCube()
     182             :         {
     183          52 :                 m_cube->grab();
     184          52 :                 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          26 : 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          26 :         m_bounding_box(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
     206             : {
     207          26 :         m_enable_shaders = g_settings->getBool("enable_shaders");
     208          26 :         m_anisotropic_filter = g_settings->getBool("anisotropic_filter");
     209          26 :         m_bilinear_filter = g_settings->getBool("bilinear_filter");
     210          26 :         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          26 :         if (g_extrusion_mesh_cache == NULL)
     215           1 :                 g_extrusion_mesh_cache = new ExtrusionMeshCache();
     216             :         else
     217          25 :                 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          26 :         setAutomaticCulling(scene::EAC_OFF);
     222             : 
     223             :         // Create the child scene node
     224          26 :         scene::IMesh *dummymesh = g_extrusion_mesh_cache->createCube();
     225          26 :         m_meshnode = SceneManager->addMeshSceneNode(dummymesh, this, -1);
     226          26 :         m_meshnode->setReadOnlyMaterials(false);
     227          26 :         m_meshnode->setVisible(false);
     228          26 :         dummymesh->drop(); // m_meshnode grabbed it
     229          26 : }
     230             : 
     231          78 : WieldMeshSceneNode::~WieldMeshSceneNode()
     232             : {
     233          26 :         sanity_check(g_extrusion_mesh_cache);
     234          26 :         if (g_extrusion_mesh_cache->drop())
     235           1 :                 g_extrusion_mesh_cache = NULL;
     236          52 : }
     237             : 
     238          26 : void WieldMeshSceneNode::setCube(const TileSpec tiles[6],
     239             :                         v3f wield_scale, ITextureSource *tsrc)
     240             : {
     241          26 :         scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube();
     242          26 :         changeToMesh(cubemesh);
     243          26 :         cubemesh->drop();
     244             : 
     245          26 :         m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR);
     246             : 
     247             :         // Customize materials
     248         182 :         for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) {
     249             :                 assert(i < 6);
     250         156 :                 video::SMaterial &material = m_meshnode->getMaterial(i);
     251         156 :                 if (tiles[i].animation_frame_count == 1) {
     252         156 :                         material.setTexture(0, tiles[i].texture);
     253             :                 } else {
     254           0 :                         FrameSpec animation_frame = tiles[i].frames[0];
     255           0 :                         material.setTexture(0, animation_frame.texture);
     256             :                 }
     257         156 :                 tiles[i].applyMaterialOptions(material);
     258             :         }
     259          26 : }
     260             : 
     261          14 : void WieldMeshSceneNode::setExtruded(const std::string &imagename,
     262             :                 v3f wield_scale, ITextureSource *tsrc, u8 num_frames)
     263             : {
     264          14 :         video::ITexture *texture = tsrc->getTexture(imagename);
     265          14 :         if (!texture) {
     266           0 :                 changeToMesh(NULL);
     267           0 :                 return;
     268             :         }
     269             : 
     270          14 :         core::dimension2d<u32> dim = texture->getSize();
     271             :         // Detect animation texture and pull off top frame instead of using entire thing
     272          14 :         if (num_frames > 1) {
     273           0 :                 u32 frame_height = dim.Height / num_frames;
     274           0 :                 dim = core::dimension2d<u32>(dim.Width, frame_height);
     275             :         }
     276          14 :         scene::IMesh *mesh = g_extrusion_mesh_cache->create(dim);
     277          14 :         changeToMesh(mesh);
     278          14 :         mesh->drop();
     279             : 
     280          14 :         m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR_EXTRUDED);
     281             : 
     282             :         // Customize material
     283          14 :         video::SMaterial &material = m_meshnode->getMaterial(0);
     284          14 :         material.setTexture(0, tsrc->getTextureForMesh(imagename));
     285          14 :         material.MaterialType = m_material_type;
     286          14 :         material.setFlag(video::EMF_BACK_FACE_CULLING, true);
     287             :         // Enable bi/trilinear filtering only for high resolution textures
     288          14 :         if (dim.Width > 32) {
     289           0 :                 material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
     290           0 :                 material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter);
     291             :         } else {
     292          14 :                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
     293          14 :                 material.setFlag(video::EMF_TRILINEAR_FILTER, false);
     294             :         }
     295          14 :         material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_anisotropic_filter);
     296             :         // mipmaps cause "thin black line" artifacts
     297             : #if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
     298          14 :         material.setFlag(video::EMF_USE_MIP_MAPS, false);
     299             : #endif
     300             : 
     301             : #if 0
     302             : //// TODO(RealBadAngel): Reactivate when shader is added for wield items
     303             :         if (m_enable_shaders)
     304             :                 material.setTexture(2, tsrc->getTexture("disable_img.png"));
     305             : #endif
     306             : }
     307             : 
     308          41 : void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef)
     309             : {
     310          41 :         ITextureSource *tsrc = gamedef->getTextureSource();
     311          41 :         IItemDefManager *idef = gamedef->getItemDefManager();
     312             :         //IShaderSource *shdrsrc = gamedef->getShaderSource();
     313          41 :         INodeDefManager *ndef = gamedef->getNodeDefManager();
     314          41 :         const ItemDefinition &def = item.getDefinition(idef);
     315          41 :         const ContentFeatures &f = ndef->get(def.name);
     316          41 :         content_t id = ndef->getId(def.name);
     317             : 
     318             : #if 0
     319             : //// TODO(RealBadAngel): Reactivate when shader is added for wield items
     320             :         if (m_enable_shaders) {
     321             :                 u32 shader_id = shdrsrc->getShader("nodes_shader", TILE_MATERIAL_BASIC, NDT_NORMAL);
     322             :                 m_material_type = shdrsrc->getShaderInfo(shader_id).material;
     323             :         }
     324             : #endif
     325             : 
     326             :         // If wield_image is defined, it overrides everything else
     327          41 :         if (def.wield_image != "") {
     328           6 :                 setExtruded(def.wield_image, def.wield_scale, tsrc, 1);
     329           6 :                 return;
     330             :         }
     331             :         // Handle nodes
     332             :         // See also CItemDefManager::createClientCached()
     333          35 :         else if (def.type == ITEM_NODE) {
     334          27 :                 if (f.mesh_ptr[0]) {
     335             :                         // e.g. mesh nodes and nodeboxes
     336           1 :                         changeToMesh(f.mesh_ptr[0]);
     337             :                         // mesh_ptr[0] is pre-scaled by BS * f->visual_scale
     338           3 :                         m_meshnode->setScale(
     339           2 :                                         def.wield_scale * WIELD_SCALE_FACTOR
     340           4 :                                         / (BS * f.visual_scale));
     341          26 :                 } else if (f.drawtype == NDT_AIRLIKE) {
     342           0 :                         changeToMesh(NULL);
     343          26 :                 } else if (f.drawtype == NDT_PLANTLIKE) {
     344           0 :                         setExtruded(tsrc->getTextureName(f.tiles[0].texture_id), def.wield_scale, tsrc, f.tiles[0].animation_frame_count);
     345          26 :                 } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) {
     346          26 :                         setCube(f.tiles, def.wield_scale, tsrc);
     347             :                 } else {
     348             :                         //// TODO: Change false in the following constructor args to
     349             :                         //// appropriate value when shader is added for wield items (if applicable)
     350           0 :                         MeshMakeData mesh_make_data(gamedef, false);
     351           0 :                         MapNode mesh_make_node(id, 255, 0);
     352           0 :                         mesh_make_data.fillSingleNode(&mesh_make_node);
     353           0 :                         MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
     354           0 :                         changeToMesh(mapblock_mesh.getMesh());
     355           0 :                         translateMesh(m_meshnode->getMesh(), v3f(-BS, -BS, -BS));
     356           0 :                         m_meshnode->setScale(
     357           0 :                                         def.wield_scale * WIELD_SCALE_FACTOR
     358           0 :                                         / (BS * f.visual_scale));
     359             :                 }
     360          27 :                 u32 material_count = m_meshnode->getMaterialCount();
     361          27 :                 if (material_count > 6) {
     362             :                         errorstream << "WieldMeshSceneNode::setItem: Invalid material "
     363           0 :                                 "count " << material_count << ", truncating to 6" << std::endl;
     364           0 :                         material_count = 6;
     365             :                 }
     366         184 :                 for (u32 i = 0; i < material_count; ++i) {
     367         157 :                         video::SMaterial &material = m_meshnode->getMaterial(i);
     368         157 :                         material.setFlag(video::EMF_BACK_FACE_CULLING, true);
     369         157 :                         material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
     370         157 :                         material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter);
     371         157 :                         bool animated = (f.tiles[i].animation_frame_count > 1);
     372         157 :                         if (animated) {
     373           0 :                                 FrameSpec animation_frame = f.tiles[i].frames[0];
     374           0 :                                 material.setTexture(0, animation_frame.texture);
     375             :                         } else {
     376         157 :                                 material.setTexture(0, f.tiles[i].texture);
     377             :                         }
     378         157 :                         material.MaterialType = m_material_type;
     379             : #if 0
     380             : //// TODO(RealBadAngel): Reactivate when shader is added for wield items
     381             :                         if (m_enable_shaders) {
     382             :                                 if (f.tiles[i].normal_texture) {
     383             :                                         if (animated) {
     384             :                                                 FrameSpec animation_frame = f.tiles[i].frames[0];
     385             :                                                 material.setTexture(1, animation_frame.normal_texture);
     386             :                                         } else {
     387             :                                                 material.setTexture(1, f.tiles[i].normal_texture);
     388             :                                         }
     389             :                                         material.setTexture(2, tsrc->getTexture("enable_img.png"));
     390             :                                 } else {
     391             :                                         material.setTexture(2, tsrc->getTexture("disable_img.png"));
     392             :                                 }
     393             :                         }
     394             : #endif
     395             :                 }
     396          27 :                 return;
     397             :         }
     398           8 :         else if (def.inventory_image != "") {
     399           8 :                 setExtruded(def.inventory_image, def.wield_scale, tsrc, 1);
     400           8 :                 return;
     401             :         }
     402             : 
     403             :         // no wield mesh found
     404           0 :         changeToMesh(NULL);
     405             : }
     406             : 
     407          40 : void WieldMeshSceneNode::setColor(video::SColor color)
     408             : {
     409             :         assert(!m_lighting);
     410          40 :         setMeshColor(m_meshnode->getMesh(), color);
     411          40 : }
     412             : 
     413           0 : void WieldMeshSceneNode::render()
     414             : {
     415             :         // note: if this method is changed to actually do something,
     416             :         // you probably should implement OnRegisterSceneNode as well
     417           0 : }
     418             : 
     419          41 : void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
     420             : {
     421          41 :         if (mesh == NULL) {
     422           0 :                 scene::IMesh *dummymesh = g_extrusion_mesh_cache->createCube();
     423           0 :                 m_meshnode->setVisible(false);
     424           0 :                 m_meshnode->setMesh(dummymesh);
     425           0 :                 dummymesh->drop();  // m_meshnode grabbed it
     426             :         } else {
     427          41 :                 if (m_lighting) {
     428          16 :                         m_meshnode->setMesh(mesh);
     429             :                 } else {
     430             :                         /*
     431             :                                 Lighting is disabled, this means the caller can (and probably will)
     432             :                                 call setColor later. We therefore need to clone the mesh so that
     433             :                                 setColor will only modify this scene node's mesh, not others'.
     434             :                         */
     435          25 :                         scene::IMeshManipulator *meshmanip = SceneManager->getMeshManipulator();
     436          25 :                         scene::IMesh *new_mesh = meshmanip->createMeshCopy(mesh);
     437          25 :                         m_meshnode->setMesh(new_mesh);
     438          25 :                         new_mesh->drop();  // m_meshnode grabbed it
     439             :                 }
     440             :         }
     441             : 
     442          41 :         m_meshnode->setMaterialFlag(video::EMF_LIGHTING, m_lighting);
     443             :         // need to normalize normals when lighting is enabled (because of setScale())
     444          41 :         m_meshnode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, m_lighting);
     445          41 :         m_meshnode->setVisible(true);
     446          44 : }

Generated by: LCOV version 1.11