wieldmesh.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. // Luanti
  2. // SPDX-License-Identifier: LGPL-2.1-or-later
  3. // Copyright (C) 2010-2014 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. #include "wieldmesh.h"
  5. #include "settings.h"
  6. #include "shader.h"
  7. #include "inventory.h"
  8. #include "client.h"
  9. #include "itemdef.h"
  10. #include "nodedef.h"
  11. #include "mesh.h"
  12. #include "content_mapblock.h"
  13. #include "mapblock_mesh.h"
  14. #include "client/meshgen/collector.h"
  15. #include "client/tile.h"
  16. #include "client/texturesource.h"
  17. #include "log.h"
  18. #include "util/numeric.h"
  19. #include <map>
  20. #include <IMeshManipulator.h>
  21. #include "client/renderingengine.h"
  22. #define WIELD_SCALE_FACTOR 30.0
  23. #define WIELD_SCALE_FACTOR_EXTRUDED 40.0
  24. #define MIN_EXTRUSION_MESH_RESOLUTION 16
  25. #define MAX_EXTRUSION_MESH_RESOLUTION 512
  26. static scene::IMesh *createExtrusionMesh(int resolution_x, int resolution_y)
  27. {
  28. const f32 r = 0.5;
  29. scene::IMeshBuffer *buf = new scene::SMeshBuffer();
  30. video::SColor c(255,255,255,255);
  31. v3f scale(1.0, 1.0, 0.1);
  32. // Front and back
  33. {
  34. video::S3DVertex vertices[8] = {
  35. // z-
  36. video::S3DVertex(-r,+r,-r, 0,0,-1, c, 0,0),
  37. video::S3DVertex(+r,+r,-r, 0,0,-1, c, 1,0),
  38. video::S3DVertex(+r,-r,-r, 0,0,-1, c, 1,1),
  39. video::S3DVertex(-r,-r,-r, 0,0,-1, c, 0,1),
  40. // z+
  41. video::S3DVertex(-r,+r,+r, 0,0,+1, c, 0,0),
  42. video::S3DVertex(-r,-r,+r, 0,0,+1, c, 0,1),
  43. video::S3DVertex(+r,-r,+r, 0,0,+1, c, 1,1),
  44. video::S3DVertex(+r,+r,+r, 0,0,+1, c, 1,0),
  45. };
  46. u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4};
  47. buf->append(vertices, 8, indices, 12);
  48. }
  49. f32 pixelsize_x = 1 / (f32) resolution_x;
  50. f32 pixelsize_y = 1 / (f32) resolution_y;
  51. for (int i = 0; i < resolution_x; ++i) {
  52. f32 pixelpos_x = i * pixelsize_x - 0.5;
  53. f32 x0 = pixelpos_x;
  54. f32 x1 = pixelpos_x + pixelsize_x;
  55. f32 tex0 = (i + 0.1) * pixelsize_x;
  56. f32 tex1 = (i + 0.9) * pixelsize_x;
  57. video::S3DVertex vertices[8] = {
  58. // x-
  59. video::S3DVertex(x0,-r,-r, -1,0,0, c, tex0,1),
  60. video::S3DVertex(x0,-r,+r, -1,0,0, c, tex1,1),
  61. video::S3DVertex(x0,+r,+r, -1,0,0, c, tex1,0),
  62. video::S3DVertex(x0,+r,-r, -1,0,0, c, tex0,0),
  63. // x+
  64. video::S3DVertex(x1,-r,-r, +1,0,0, c, tex0,1),
  65. video::S3DVertex(x1,+r,-r, +1,0,0, c, tex0,0),
  66. video::S3DVertex(x1,+r,+r, +1,0,0, c, tex1,0),
  67. video::S3DVertex(x1,-r,+r, +1,0,0, c, tex1,1),
  68. };
  69. u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4};
  70. buf->append(vertices, 8, indices, 12);
  71. }
  72. for (int i = 0; i < resolution_y; ++i) {
  73. f32 pixelpos_y = i * pixelsize_y - 0.5;
  74. f32 y0 = -pixelpos_y - pixelsize_y;
  75. f32 y1 = -pixelpos_y;
  76. f32 tex0 = (i + 0.1) * pixelsize_y;
  77. f32 tex1 = (i + 0.9) * pixelsize_y;
  78. video::S3DVertex vertices[8] = {
  79. // y-
  80. video::S3DVertex(-r,y0,-r, 0,-1,0, c, 0,tex0),
  81. video::S3DVertex(+r,y0,-r, 0,-1,0, c, 1,tex0),
  82. video::S3DVertex(+r,y0,+r, 0,-1,0, c, 1,tex1),
  83. video::S3DVertex(-r,y0,+r, 0,-1,0, c, 0,tex1),
  84. // y+
  85. video::S3DVertex(-r,y1,-r, 0,+1,0, c, 0,tex0),
  86. video::S3DVertex(-r,y1,+r, 0,+1,0, c, 0,tex1),
  87. video::S3DVertex(+r,y1,+r, 0,+1,0, c, 1,tex1),
  88. video::S3DVertex(+r,y1,-r, 0,+1,0, c, 1,tex0),
  89. };
  90. u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4};
  91. buf->append(vertices, 8, indices, 12);
  92. }
  93. // Create mesh object
  94. scene::SMesh *mesh = new scene::SMesh();
  95. mesh->addMeshBuffer(buf);
  96. buf->drop();
  97. scaleMesh(mesh, scale); // also recalculates bounding box
  98. return mesh;
  99. }
  100. /*
  101. Caches extrusion meshes so that only one of them per resolution
  102. is needed. Also caches one cube (for convenience).
  103. E.g. there is a single extrusion mesh that is used for all
  104. 16x16 px images, another for all 256x256 px images, and so on.
  105. WARNING: Not thread safe. This should not be a problem since
  106. rendering related classes (such as WieldMeshSceneNode) will be
  107. used from the rendering thread only.
  108. */
  109. class ExtrusionMeshCache: public IReferenceCounted
  110. {
  111. public:
  112. // Constructor
  113. ExtrusionMeshCache()
  114. {
  115. for (int resolution = MIN_EXTRUSION_MESH_RESOLUTION;
  116. resolution <= MAX_EXTRUSION_MESH_RESOLUTION;
  117. resolution *= 2) {
  118. m_extrusion_meshes[resolution] =
  119. createExtrusionMesh(resolution, resolution);
  120. }
  121. m_cube = createCubeMesh(v3f(1.0, 1.0, 1.0));
  122. }
  123. // Destructor
  124. virtual ~ExtrusionMeshCache()
  125. {
  126. for (auto &extrusion_meshe : m_extrusion_meshes) {
  127. extrusion_meshe.second->drop();
  128. }
  129. m_cube->drop();
  130. }
  131. // Get closest extrusion mesh for given image dimensions
  132. // Caller must drop the returned pointer
  133. scene::IMesh* create(core::dimension2d<u32> dim)
  134. {
  135. // handle non-power of two textures inefficiently without cache
  136. if (!is_power_of_two(dim.Width) || !is_power_of_two(dim.Height)) {
  137. return createExtrusionMesh(dim.Width, dim.Height);
  138. }
  139. int maxdim = MYMAX(dim.Width, dim.Height);
  140. std::map<int, scene::IMesh*>::iterator
  141. it = m_extrusion_meshes.lower_bound(maxdim);
  142. if (it == m_extrusion_meshes.end()) {
  143. // no viable resolution found; use largest one
  144. it = m_extrusion_meshes.find(MAX_EXTRUSION_MESH_RESOLUTION);
  145. sanity_check(it != m_extrusion_meshes.end());
  146. }
  147. scene::IMesh *mesh = it->second;
  148. mesh->grab();
  149. return mesh;
  150. }
  151. // Returns a 1x1x1 cube mesh with one meshbuffer (material) per face
  152. // Caller must drop the returned pointer
  153. scene::IMesh* createCube()
  154. {
  155. m_cube->grab();
  156. return m_cube;
  157. }
  158. private:
  159. std::map<int, scene::IMesh*> m_extrusion_meshes;
  160. scene::IMesh *m_cube;
  161. };
  162. static ExtrusionMeshCache *g_extrusion_mesh_cache = nullptr;
  163. WieldMeshSceneNode::WieldMeshSceneNode(scene::ISceneManager *mgr, s32 id):
  164. scene::ISceneNode(mgr->getRootSceneNode(), mgr, id),
  165. m_material_type(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF)
  166. {
  167. m_anisotropic_filter = g_settings->getBool("anisotropic_filter");
  168. m_bilinear_filter = g_settings->getBool("bilinear_filter");
  169. m_trilinear_filter = g_settings->getBool("trilinear_filter");
  170. // If this is the first wield mesh scene node, create a cache
  171. // for extrusion meshes (and a cube mesh), otherwise reuse it
  172. if (!g_extrusion_mesh_cache)
  173. g_extrusion_mesh_cache = new ExtrusionMeshCache();
  174. else
  175. g_extrusion_mesh_cache->grab();
  176. // Disable bounding box culling for this scene node
  177. // since we won't calculate the bounding box.
  178. setAutomaticCulling(scene::EAC_OFF);
  179. // Create the child scene node
  180. scene::IMesh *dummymesh = g_extrusion_mesh_cache->createCube();
  181. m_meshnode = SceneManager->addMeshSceneNode(dummymesh, this, -1);
  182. m_meshnode->setReadOnlyMaterials(false);
  183. m_meshnode->setVisible(false);
  184. dummymesh->drop(); // m_meshnode grabbed it
  185. m_shadow = RenderingEngine::get_shadow_renderer();
  186. if (m_shadow) {
  187. // Add mesh to shadow caster
  188. m_shadow->addNodeToShadowList(m_meshnode);
  189. }
  190. }
  191. WieldMeshSceneNode::~WieldMeshSceneNode()
  192. {
  193. sanity_check(g_extrusion_mesh_cache);
  194. // Remove node from shadow casters. m_shadow might be an invalid pointer!
  195. if (m_shadow)
  196. m_shadow->removeNodeFromShadowList(m_meshnode);
  197. if (g_extrusion_mesh_cache->drop())
  198. g_extrusion_mesh_cache = nullptr;
  199. }
  200. void WieldMeshSceneNode::setCube(const ContentFeatures &f,
  201. v3f wield_scale)
  202. {
  203. scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube();
  204. scene::SMesh *copy = cloneMesh(cubemesh);
  205. cubemesh->drop();
  206. postProcessNodeMesh(copy, f, false, &m_material_type, &m_colors, true);
  207. changeToMesh(copy);
  208. copy->drop();
  209. m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR);
  210. }
  211. void WieldMeshSceneNode::setExtruded(const std::string &imagename,
  212. const std::string &overlay_name, v3f wield_scale, ITextureSource *tsrc,
  213. u8 num_frames)
  214. {
  215. video::ITexture *texture = tsrc->getTexture(imagename);
  216. if (!texture) {
  217. changeToMesh(nullptr);
  218. return;
  219. }
  220. video::ITexture *overlay_texture =
  221. overlay_name.empty() ? NULL : tsrc->getTexture(overlay_name);
  222. core::dimension2d<u32> dim = texture->getSize();
  223. // Detect animation texture and pull off top frame instead of using entire thing
  224. if (num_frames > 1) {
  225. u32 frame_height = dim.Height / num_frames;
  226. dim = core::dimension2d<u32>(dim.Width, frame_height);
  227. }
  228. scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
  229. scene::SMesh *mesh = cloneMesh(original);
  230. original->drop();
  231. //set texture
  232. mesh->getMeshBuffer(0)->getMaterial().setTexture(0,
  233. tsrc->getTexture(imagename));
  234. if (overlay_texture) {
  235. scene::IMeshBuffer *copy = cloneMeshBuffer(mesh->getMeshBuffer(0));
  236. copy->getMaterial().setTexture(0, overlay_texture);
  237. mesh->addMeshBuffer(copy);
  238. copy->drop();
  239. }
  240. changeToMesh(mesh);
  241. mesh->drop();
  242. m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR_EXTRUDED);
  243. // Customize materials
  244. for (u32 layer = 0; layer < m_meshnode->getMaterialCount(); layer++) {
  245. video::SMaterial &material = m_meshnode->getMaterial(layer);
  246. material.TextureLayers[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
  247. material.TextureLayers[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
  248. material.MaterialType = m_material_type;
  249. material.MaterialTypeParam = 0.5f;
  250. material.BackfaceCulling = true;
  251. // Enable bi/trilinear filtering only for high resolution textures
  252. bool bilinear_filter = dim.Width > 32 && m_bilinear_filter;
  253. bool trilinear_filter = dim.Width > 32 && m_trilinear_filter;
  254. material.forEachTexture([=] (auto &tex) {
  255. setMaterialFilters(tex, bilinear_filter, trilinear_filter,
  256. m_anisotropic_filter);
  257. });
  258. // mipmaps cause "thin black line" artifacts
  259. material.UseMipMaps = false;
  260. }
  261. }
  262. static scene::SMesh *createSpecialNodeMesh(Client *client, MapNode n,
  263. std::vector<ItemPartColor> *colors, const ContentFeatures &f)
  264. {
  265. MeshMakeData mesh_make_data(client->ndef(), 1);
  266. MeshCollector collector(v3f(0.0f * BS), v3f());
  267. mesh_make_data.setSmoothLighting(false);
  268. MapblockMeshGenerator gen(&mesh_make_data, &collector,
  269. client->getSceneManager()->getMeshManipulator());
  270. if (n.getParam2()) {
  271. // keep it
  272. } else if (f.param_type_2 == CPT2_WALLMOUNTED ||
  273. f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
  274. if (f.drawtype == NDT_TORCHLIKE ||
  275. f.drawtype == NDT_SIGNLIKE ||
  276. f.drawtype == NDT_NODEBOX ||
  277. f.drawtype == NDT_MESH) {
  278. n.setParam2(4);
  279. }
  280. } else if (f.drawtype == NDT_SIGNLIKE || f.drawtype == NDT_TORCHLIKE) {
  281. n.setParam2(1);
  282. }
  283. gen.renderSingle(n.getContent(), n.getParam2());
  284. colors->clear();
  285. scene::SMesh *mesh = new scene::SMesh();
  286. for (auto &prebuffers : collector.prebuffers)
  287. for (PreMeshBuffer &p : prebuffers) {
  288. if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
  289. const FrameSpec &frame = (*p.layer.frames)[0];
  290. p.layer.texture = frame.texture;
  291. }
  292. for (video::S3DVertex &v : p.vertices) {
  293. v.Color.setAlpha(255);
  294. }
  295. scene::SMeshBuffer *buf = new scene::SMeshBuffer();
  296. buf->Material.setTexture(0, p.layer.texture);
  297. p.layer.applyMaterialOptions(buf->Material);
  298. mesh->addMeshBuffer(buf);
  299. buf->append(&p.vertices[0], p.vertices.size(),
  300. &p.indices[0], p.indices.size());
  301. buf->drop();
  302. colors->push_back(
  303. ItemPartColor(p.layer.has_color, p.layer.color));
  304. }
  305. return mesh;
  306. }
  307. void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool check_wield_image)
  308. {
  309. ITextureSource *tsrc = client->getTextureSource();
  310. IItemDefManager *idef = client->getItemDefManager();
  311. IShaderSource *shdrsrc = client->getShaderSource();
  312. const NodeDefManager *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. scene::SMesh *mesh = nullptr;
  317. u32 shader_id = shdrsrc->getShader("object_shader", TILE_MATERIAL_BASIC, NDT_NORMAL);
  318. m_material_type = shdrsrc->getShaderInfo(shader_id).material;
  319. // Color-related
  320. m_colors.clear();
  321. m_base_color = idef->getItemstackColor(item, client);
  322. const std::string wield_image = item.getWieldImage(idef);
  323. const std::string wield_overlay = item.getWieldOverlay(idef);
  324. const v3f wield_scale = item.getWieldScale(idef);
  325. // If wield_image needs to be checked and is defined, it overrides everything else
  326. if (!wield_image.empty() && check_wield_image) {
  327. setExtruded(wield_image, wield_overlay, wield_scale, tsrc,
  328. 1);
  329. m_colors.emplace_back();
  330. // overlay is white, if present
  331. m_colors.emplace_back(true, video::SColor(0xFFFFFFFF));
  332. // initialize the color
  333. setColor(video::SColor(0xFFFFFFFF));
  334. return;
  335. }
  336. // Handle nodes
  337. // See also CItemDefManager::createClientCached()
  338. if (def.type == ITEM_NODE) {
  339. bool cull_backface = f.needsBackfaceCulling();
  340. // Select rendering method
  341. switch (f.drawtype) {
  342. case NDT_AIRLIKE:
  343. setExtruded("no_texture_airlike.png", "",
  344. v3f(1.0, 1.0, 1.0), tsrc, 1);
  345. break;
  346. case NDT_SIGNLIKE:
  347. case NDT_TORCHLIKE:
  348. case NDT_RAILLIKE:
  349. case NDT_PLANTLIKE:
  350. case NDT_FLOWINGLIQUID: {
  351. v3f wscale = wield_scale;
  352. if (f.drawtype == NDT_FLOWINGLIQUID)
  353. wscale.Z *= 0.1f;
  354. setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id),
  355. tsrc->getTextureName(f.tiles[0].layers[1].texture_id),
  356. wscale, tsrc,
  357. f.tiles[0].layers[0].animation_frame_count);
  358. // Add color
  359. const TileLayer &l0 = f.tiles[0].layers[0];
  360. m_colors.emplace_back(l0.has_color, l0.color);
  361. const TileLayer &l1 = f.tiles[0].layers[1];
  362. m_colors.emplace_back(l1.has_color, l1.color);
  363. break;
  364. }
  365. case NDT_PLANTLIKE_ROOTED: {
  366. setExtruded(tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id),
  367. "", wield_scale, tsrc,
  368. f.special_tiles[0].layers[0].animation_frame_count);
  369. // Add color
  370. const TileLayer &l0 = f.special_tiles[0].layers[0];
  371. m_colors.emplace_back(l0.has_color, l0.color);
  372. break;
  373. }
  374. case NDT_NORMAL:
  375. case NDT_ALLFACES:
  376. case NDT_LIQUID:
  377. setCube(f, wield_scale);
  378. break;
  379. default: {
  380. // Render non-trivial drawtypes like the actual node
  381. MapNode n(id);
  382. if (def.place_param2)
  383. n.setParam2(*def.place_param2);
  384. mesh = createSpecialNodeMesh(client, n, &m_colors, f);
  385. changeToMesh(mesh);
  386. mesh->drop();
  387. m_meshnode->setScale(
  388. wield_scale * WIELD_SCALE_FACTOR
  389. / (BS * f.visual_scale));
  390. break;
  391. }
  392. }
  393. u32 material_count = m_meshnode->getMaterialCount();
  394. for (u32 i = 0; i < material_count; ++i) {
  395. video::SMaterial &material = m_meshnode->getMaterial(i);
  396. material.MaterialType = m_material_type;
  397. material.MaterialTypeParam = 0.5f;
  398. material.BackfaceCulling = cull_backface;
  399. material.forEachTexture([this] (auto &tex) {
  400. setMaterialFilters(tex, m_bilinear_filter, m_trilinear_filter,
  401. m_anisotropic_filter);
  402. });
  403. }
  404. // initialize the color
  405. setColor(video::SColor(0xFFFFFFFF));
  406. return;
  407. } else {
  408. const std::string inventory_image = item.getInventoryImage(idef);
  409. if (!inventory_image.empty()) {
  410. const std::string inventory_overlay = item.getInventoryOverlay(idef);
  411. setExtruded(inventory_image, inventory_overlay, def.wield_scale, tsrc, 1);
  412. } else {
  413. setExtruded("no_texture.png", "", def.wield_scale, tsrc, 1);
  414. }
  415. m_colors.emplace_back();
  416. // overlay is white, if present
  417. m_colors.emplace_back(true, video::SColor(0xFFFFFFFF));
  418. // initialize the color
  419. setColor(video::SColor(0xFFFFFFFF));
  420. return;
  421. }
  422. // no wield mesh found
  423. changeToMesh(nullptr);
  424. }
  425. void WieldMeshSceneNode::setColor(video::SColor c)
  426. {
  427. scene::IMesh *mesh = m_meshnode->getMesh();
  428. if (!mesh)
  429. return;
  430. u8 red = c.getRed();
  431. u8 green = c.getGreen();
  432. u8 blue = c.getBlue();
  433. const u32 mc = mesh->getMeshBufferCount();
  434. if (mc > m_colors.size())
  435. m_colors.resize(mc);
  436. for (u32 j = 0; j < mc; j++) {
  437. video::SColor bc(m_base_color);
  438. m_colors[j].applyOverride(bc);
  439. video::SColor buffercolor(255,
  440. bc.getRed() * red / 255,
  441. bc.getGreen() * green / 255,
  442. bc.getBlue() * blue / 255);
  443. scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
  444. if (m_colors[j].needColorize(buffercolor)) {
  445. buf->setDirty(scene::EBT_VERTEX);
  446. setMeshBufferColor(buf, buffercolor);
  447. }
  448. }
  449. }
  450. void WieldMeshSceneNode::setNodeLightColor(video::SColor color)
  451. {
  452. if (!m_meshnode)
  453. return;
  454. {
  455. for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) {
  456. video::SMaterial &material = m_meshnode->getMaterial(i);
  457. material.ColorParam = color;
  458. }
  459. }
  460. }
  461. void WieldMeshSceneNode::render()
  462. {
  463. // note: if this method is changed to actually do something,
  464. // you probably should implement OnRegisterSceneNode as well
  465. }
  466. void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
  467. {
  468. if (!mesh) {
  469. scene::IMesh *dummymesh = g_extrusion_mesh_cache->createCube();
  470. m_meshnode->setVisible(false);
  471. m_meshnode->setMesh(dummymesh);
  472. dummymesh->drop(); // m_meshnode grabbed it
  473. } else {
  474. m_meshnode->setMesh(mesh);
  475. mesh->setHardwareMappingHint(scene::EHM_STATIC);
  476. }
  477. m_meshnode->setVisible(true);
  478. }
  479. void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
  480. {
  481. ITextureSource *tsrc = client->getTextureSource();
  482. IItemDefManager *idef = client->getItemDefManager();
  483. const NodeDefManager *ndef = client->getNodeDefManager();
  484. const ItemDefinition &def = item.getDefinition(idef);
  485. const ContentFeatures &f = ndef->get(def.name);
  486. content_t id = ndef->getId(def.name);
  487. FATAL_ERROR_IF(!g_extrusion_mesh_cache, "Extrusion mesh cache is not yet initialized");
  488. scene::SMesh *mesh = nullptr;
  489. // Shading is on by default
  490. result->needs_shading = true;
  491. bool cull_backface = f.needsBackfaceCulling();
  492. // If inventory_image is defined, it overrides everything else
  493. const std::string inventory_image = item.getInventoryImage(idef);
  494. const std::string inventory_overlay = item.getInventoryOverlay(idef);
  495. if (!inventory_image.empty()) {
  496. mesh = getExtrudedMesh(tsrc, inventory_image, inventory_overlay);
  497. result->buffer_colors.emplace_back();
  498. // overlay is white, if present
  499. result->buffer_colors.emplace_back(true, video::SColor(0xFFFFFFFF));
  500. // Items with inventory images do not need shading
  501. result->needs_shading = false;
  502. } else if (def.type == ITEM_NODE && f.drawtype == NDT_AIRLIKE) {
  503. // Fallback image for airlike node
  504. mesh = getExtrudedMesh(tsrc, "no_texture_airlike.png", inventory_overlay);
  505. result->needs_shading = false;
  506. } else if (def.type == ITEM_NODE) {
  507. switch (f.drawtype) {
  508. case NDT_NORMAL:
  509. case NDT_ALLFACES:
  510. case NDT_LIQUID:
  511. case NDT_FLOWINGLIQUID: {
  512. scene::IMesh *cube = g_extrusion_mesh_cache->createCube();
  513. mesh = cloneMesh(cube);
  514. cube->drop();
  515. if (f.drawtype == NDT_FLOWINGLIQUID) {
  516. scaleMesh(mesh, v3f(1.2, 0.03, 1.2));
  517. translateMesh(mesh, v3f(0, -0.57, 0));
  518. } else
  519. scaleMesh(mesh, v3f(1.2, 1.2, 1.2));
  520. // add overlays
  521. postProcessNodeMesh(mesh, f, false, nullptr,
  522. &result->buffer_colors, true);
  523. if (f.drawtype == NDT_ALLFACES)
  524. scaleMesh(mesh, v3f(f.visual_scale));
  525. break;
  526. }
  527. case NDT_PLANTLIKE: {
  528. mesh = getExtrudedMesh(tsrc,
  529. tsrc->getTextureName(f.tiles[0].layers[0].texture_id),
  530. tsrc->getTextureName(f.tiles[0].layers[1].texture_id));
  531. // Add color
  532. const TileLayer &l0 = f.tiles[0].layers[0];
  533. result->buffer_colors.emplace_back(l0.has_color, l0.color);
  534. const TileLayer &l1 = f.tiles[0].layers[1];
  535. result->buffer_colors.emplace_back(l1.has_color, l1.color);
  536. break;
  537. }
  538. case NDT_PLANTLIKE_ROOTED: {
  539. mesh = getExtrudedMesh(tsrc,
  540. tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), "");
  541. // Add color
  542. const TileLayer &l0 = f.special_tiles[0].layers[0];
  543. result->buffer_colors.emplace_back(l0.has_color, l0.color);
  544. break;
  545. }
  546. default: {
  547. // Render non-trivial drawtypes like the actual node
  548. MapNode n(id);
  549. if (def.place_param2)
  550. n.setParam2(*def.place_param2);
  551. mesh = createSpecialNodeMesh(client, n, &result->buffer_colors, f);
  552. scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
  553. break;
  554. }
  555. }
  556. for (u32 i = 0; i < mesh->getMeshBufferCount(); ++i) {
  557. scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
  558. video::SMaterial &material = buf->getMaterial();
  559. material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
  560. material.MaterialTypeParam = 0.5f;
  561. material.forEachTexture([] (auto &tex) {
  562. tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST;
  563. tex.MagFilter = video::ETMAGF_NEAREST;
  564. });
  565. material.BackfaceCulling = cull_backface;
  566. }
  567. rotateMeshXZby(mesh, -45);
  568. rotateMeshYZby(mesh, -30);
  569. }
  570. // might need to be re-colorized, this is done only when needed
  571. if (mesh) {
  572. mesh->setHardwareMappingHint(scene::EHM_DYNAMIC, scene::EBT_VERTEX);
  573. mesh->setHardwareMappingHint(scene::EHM_STATIC, scene::EBT_INDEX);
  574. }
  575. result->mesh = mesh;
  576. }
  577. scene::SMesh *getExtrudedMesh(ITextureSource *tsrc,
  578. const std::string &imagename, const std::string &overlay_name)
  579. {
  580. // check textures
  581. video::ITexture *texture = tsrc->getTextureForMesh(imagename);
  582. if (!texture) {
  583. return NULL;
  584. }
  585. video::ITexture *overlay_texture =
  586. (overlay_name.empty()) ? NULL : tsrc->getTexture(overlay_name);
  587. // get mesh
  588. core::dimension2d<u32> dim = texture->getSize();
  589. scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
  590. scene::SMesh *mesh = cloneMesh(original);
  591. original->drop();
  592. //set texture
  593. mesh->getMeshBuffer(0)->getMaterial().setTexture(0,
  594. tsrc->getTexture(imagename));
  595. if (overlay_texture) {
  596. scene::IMeshBuffer *copy = cloneMeshBuffer(mesh->getMeshBuffer(0));
  597. copy->getMaterial().setTexture(0, overlay_texture);
  598. mesh->addMeshBuffer(copy);
  599. copy->drop();
  600. }
  601. // Customize materials
  602. for (u32 layer = 0; layer < mesh->getMeshBufferCount(); layer++) {
  603. video::SMaterial &material = mesh->getMeshBuffer(layer)->getMaterial();
  604. material.TextureLayers[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
  605. material.TextureLayers[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
  606. material.forEachTexture([] (auto &tex) {
  607. tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST;
  608. tex.MagFilter = video::ETMAGF_NEAREST;
  609. });
  610. material.BackfaceCulling = true;
  611. material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
  612. material.MaterialTypeParam = 0.5f;
  613. }
  614. scaleMesh(mesh, v3f(2.0, 2.0, 2.0));
  615. return mesh;
  616. }
  617. void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f,
  618. bool set_material, const video::E_MATERIAL_TYPE *mattype,
  619. std::vector<ItemPartColor> *colors, bool apply_scale)
  620. {
  621. const u32 mc = mesh->getMeshBufferCount();
  622. // Allocate colors for existing buffers
  623. colors->clear();
  624. colors->resize(mc);
  625. for (u32 i = 0; i < mc; ++i) {
  626. const TileSpec *tile = &(f.tiles[i]);
  627. scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
  628. for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
  629. const TileLayer *layer = &tile->layers[layernum];
  630. if (layer->texture_id == 0)
  631. continue;
  632. if (layernum != 0) {
  633. scene::IMeshBuffer *copy = cloneMeshBuffer(buf);
  634. copy->getMaterial() = buf->getMaterial();
  635. mesh->addMeshBuffer(copy);
  636. copy->drop();
  637. buf = copy;
  638. colors->emplace_back(layer->has_color, layer->color);
  639. } else {
  640. (*colors)[i] = ItemPartColor(layer->has_color, layer->color);
  641. }
  642. video::SMaterial &material = buf->getMaterial();
  643. if (set_material)
  644. layer->applyMaterialOptions(material);
  645. if (mattype) {
  646. material.MaterialType = *mattype;
  647. }
  648. if (layer->animation_frame_count > 1) {
  649. const FrameSpec &animation_frame = (*layer->frames)[0];
  650. material.setTexture(0, animation_frame.texture);
  651. } else {
  652. material.setTexture(0, layer->texture);
  653. }
  654. if (apply_scale && tile->world_aligned) {
  655. u32 n = buf->getVertexCount();
  656. for (u32 k = 0; k != n; ++k)
  657. buf->getTCoords(k) /= layer->scale;
  658. }
  659. }
  660. }
  661. }