mirror of
https://github.com/luanti-org/luanti.git
synced 2025-11-09 19:45:21 +01:00
Move client code from nodedef.h/cpp (#16615)
This commit is contained in:
@@ -66,6 +66,7 @@ set(client_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mesh.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mesh_generator_thread.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/minimap.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/node_visuals.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/particles.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/renderingengine.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shader.cpp
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "mapnode.h"
|
||||
#include "mapsector.h"
|
||||
#include "minimap.h"
|
||||
#include "node_visuals.h"
|
||||
#include "profiler.h"
|
||||
#include "shader.h"
|
||||
#include "translation.h"
|
||||
@@ -57,6 +58,7 @@
|
||||
#include "database/database-files.h"
|
||||
#include "database/database-sqlite3.h"
|
||||
|
||||
#include <IAnimatedMesh.h>
|
||||
#include <IFileSystem.h>
|
||||
#include <json/json.h>
|
||||
|
||||
@@ -1880,7 +1882,7 @@ void Client::afterContentReceived()
|
||||
TextureUpdateArgs tu_args;
|
||||
tu_args.last_time_ms = porting::getTimeMs();
|
||||
tu_args.text_base = wstrgettext("Initializing nodes");
|
||||
m_nodedef->updateTextures(this, &tu_args);
|
||||
NodeVisuals::fillNodeVisuals(m_nodedef, this, &tu_args);
|
||||
|
||||
// Start mesh update thread after setting up content definitions
|
||||
infostream<<"- Starting mesh update thread"<<std::endl;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <matrix4.h>
|
||||
#include "mapsector.h"
|
||||
#include "mapblock.h"
|
||||
#include "node_visuals.h"
|
||||
#include "nodedef.h"
|
||||
#include "player.h" // CameraMode
|
||||
#include "profiler.h"
|
||||
@@ -401,7 +402,7 @@ void ClientMap::updateDrawList()
|
||||
bool occlusion_culling_enabled = mesh_grid.cell_size < 4;
|
||||
if (m_control.allow_noclip) {
|
||||
MapNode n = getNode(cam_pos_nodes);
|
||||
if (n.getContent() == CONTENT_IGNORE || m_nodedef->get(n).solidness == 2)
|
||||
if (n.getContent() == CONTENT_IGNORE || m_nodedef->get(n).visuals->solidness == 2)
|
||||
occlusion_culling_enabled = false;
|
||||
}
|
||||
|
||||
@@ -1393,7 +1394,7 @@ void ClientMap::renderPostFx(CameraMode cam_mode)
|
||||
|
||||
// If the camera is in a solid node, make everything black.
|
||||
// (first person mode only)
|
||||
if (features.solidness == 2 && cam_mode == CAMERA_MODE_FIRST &&
|
||||
if (features.visuals->solidness == 2 && cam_mode == CAMERA_MODE_FIRST &&
|
||||
!m_control.allow_noclip) {
|
||||
post_color = video::SColor(255, 0, 0, 0);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "util/directiontables.h"
|
||||
#include "util/tracy_wrapper.h"
|
||||
#include "mapblock_mesh.h"
|
||||
#include "settings.h"
|
||||
#include "node_visuals.h"
|
||||
#include "nodedef.h"
|
||||
#include "client/tile.h"
|
||||
#include "mesh.h"
|
||||
@@ -98,7 +98,8 @@ void MapblockMeshGenerator::getTile(v3s16 direction, TileSpec *tile_ret)
|
||||
// Returns a special tile, ready for use, non-rotated.
|
||||
void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile_ret, bool apply_crack)
|
||||
{
|
||||
*tile_ret = cur_node.f->special_tiles[index];
|
||||
const ContentFeatures &f = *cur_node.f;
|
||||
*tile_ret = f.visuals->special_tiles[index];
|
||||
TileLayer *top_layer = nullptr;
|
||||
|
||||
for (auto &layernum : tile_ret->layers) {
|
||||
@@ -107,7 +108,7 @@ void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile_ret, bool a
|
||||
continue;
|
||||
top_layer = layer;
|
||||
if (!layer->has_color)
|
||||
cur_node.n.getColor(*cur_node.f, &layer->color);
|
||||
f.visuals->getColor(cur_node.n.param2, &layer->color);
|
||||
}
|
||||
|
||||
if (apply_crack)
|
||||
@@ -451,12 +452,12 @@ void MapblockMeshGenerator::drawSolidNode()
|
||||
continue;
|
||||
if (n2 != CONTENT_AIR) {
|
||||
const ContentFeatures &f2 = nodedef->get(n2);
|
||||
if (f2.solidness == 2)
|
||||
if (f2.visuals->solidness == 2)
|
||||
continue;
|
||||
if (cur_node.f->drawtype == NDT_LIQUID) {
|
||||
if (cur_node.f->sameLiquidRender(f2))
|
||||
continue;
|
||||
backface_culling = f2.solidness || f2.visual_solidness;
|
||||
backface_culling = f2.visuals->solidness || f2.visuals->visual_solidness;
|
||||
}
|
||||
}
|
||||
faces |= 1 << face;
|
||||
@@ -565,7 +566,7 @@ void MapblockMeshGenerator::prepareLiquidNodeDrawing()
|
||||
&& (nbottom.getContent() != cur_liquid.c_source);
|
||||
if (cur_liquid.draw_bottom) {
|
||||
const ContentFeatures &f2 = nodedef->get(nbottom.getContent());
|
||||
if (f2.solidness > 1)
|
||||
if (f2.visuals->solidness > 1)
|
||||
cur_liquid.draw_bottom = false;
|
||||
}
|
||||
|
||||
@@ -707,7 +708,7 @@ void MapblockMeshGenerator::drawLiquidSides()
|
||||
|
||||
const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
|
||||
// Don't draw face if neighbor is blocking the view
|
||||
if (neighbor_features.solidness == 2)
|
||||
if (neighbor_features.visuals->solidness == 2)
|
||||
continue;
|
||||
|
||||
video::S3DVertex vertices[4];
|
||||
@@ -1023,7 +1024,7 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
|
||||
// Liquid is textured with 1 tile defined in nodedef 'special_tiles'
|
||||
auto &cf = *cur_node.f;
|
||||
if (param2 > 0 && cf.param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
|
||||
!cf.special_tiles[0].layers[0].empty()) {
|
||||
!cf.visuals->special_tiles[0].layers[0].empty()) {
|
||||
// Internal liquid level has param2 range 0 .. 63,
|
||||
// convert it to -0.5 .. 0.5
|
||||
float vlev = (param2 / 63.0f) * 2.0f - 1.0f;
|
||||
@@ -1709,9 +1710,10 @@ void MapblockMeshGenerator::drawMeshNode()
|
||||
degrotate = cur_node.n.getDegRotate(nodedef);
|
||||
}
|
||||
|
||||
if (cur_node.f->mesh_ptr) {
|
||||
auto *mesh_ptr = cur_node.f->visuals->mesh_ptr;
|
||||
if (mesh_ptr) {
|
||||
// clone and rotate mesh
|
||||
mesh = cloneStaticMesh(cur_node.f->mesh_ptr);
|
||||
mesh = cloneStaticMesh(mesh_ptr);
|
||||
bool modified = true;
|
||||
if (facedir)
|
||||
rotateMeshBy6dFacedir(mesh, facedir);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "nodedef.h"
|
||||
#include "tile.h"
|
||||
|
||||
struct MeshMakeData;
|
||||
struct MeshCollector;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "client.h"
|
||||
#include "inventory.h"
|
||||
#include "shader.h"
|
||||
#include "client/tile.h"
|
||||
#include "localplayer.h"
|
||||
#include "camera.h"
|
||||
#include "fontengine.h"
|
||||
@@ -28,6 +27,7 @@
|
||||
#include "irrlicht_changes/CGUITTFont.h"
|
||||
#include "gui/drawItemStack.h"
|
||||
#include <ICameraSceneNode.h>
|
||||
#include <IMesh.h>
|
||||
|
||||
#define OBJECT_CROSSHAIR_LINE_SIZE 8
|
||||
#define CROSSHAIR_LINE_SIZE 10
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "texturesource.h"
|
||||
#include "itemdef.h"
|
||||
#include "inventory.h"
|
||||
#include <IMesh.h>
|
||||
|
||||
struct ItemVisualsManager::ItemVisuals
|
||||
{
|
||||
|
||||
@@ -6,19 +6,15 @@
|
||||
#include "CMeshBuffer.h"
|
||||
#include "client.h"
|
||||
#include "mapblock.h"
|
||||
#include "map.h"
|
||||
#include "noise.h"
|
||||
#include "profiler.h"
|
||||
#include "node_visuals.h"
|
||||
#include "shader.h"
|
||||
#include "mesh.h"
|
||||
#include "minimap.h"
|
||||
#include "content_mapblock.h"
|
||||
#include "util/directiontables.h"
|
||||
#include "util/tracy_wrapper.h"
|
||||
#include "client/meshgen/collector.h"
|
||||
#include "client/renderingengine.h"
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include "client/texturesource.h"
|
||||
#include <SMesh.h>
|
||||
@@ -166,7 +162,7 @@ static u16 getSmoothLightCombined(const v3s16 &p,
|
||||
if (f.light_source > light_source_max)
|
||||
light_source_max = f.light_source;
|
||||
// Check f.solidness because fast-style leaves look better this way
|
||||
if (f.param_type == CPT_LIGHT && f.solidness != 2) {
|
||||
if (f.param_type == CPT_LIGHT && f.visuals->solidness != 2) {
|
||||
u8 light_level_day = n.getLight(LIGHTBANK_DAY, f.getLightingFlags());
|
||||
u8 light_level_night = n.getLight(LIGHTBANK_NIGHT, f.getLightingFlags());
|
||||
if (light_level_day == LIGHT_SUN)
|
||||
@@ -335,13 +331,13 @@ void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data,
|
||||
{
|
||||
const NodeDefManager *ndef = data->m_nodedef;
|
||||
const ContentFeatures &f = ndef->get(mn);
|
||||
tile = f.tiles[tileindex];
|
||||
tile = f.visuals->tiles[tileindex];
|
||||
bool has_crack = p == data->m_crack_pos_relative;
|
||||
for (TileLayer &layer : tile.layers) {
|
||||
if (layer.empty())
|
||||
continue;
|
||||
if (!layer.has_color)
|
||||
mn.getColor(f, &(layer.color));
|
||||
f.visuals->getColor(mn.param2, &(layer.color));
|
||||
// Apply temporary crack
|
||||
if (has_crack)
|
||||
layer.material_flags |= MATERIAL_FLAG_CRACK;
|
||||
@@ -914,7 +910,7 @@ u8 get_solid_sides(MeshMakeData *data)
|
||||
|
||||
for (u8 k = 0; k < 6; k++) {
|
||||
const MapNode &top = data->m_vmanip.getNodeRefUnsafe(blockpos_nodes + positions[k]);
|
||||
if (ndef->get(top).solidness != 2)
|
||||
if (ndef->get(top).visuals->solidness != 2)
|
||||
result &= ~(1 << k);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#include "mesh.h"
|
||||
#include "IMeshBuffer.h"
|
||||
#include "SSkinMeshBuffer.h"
|
||||
#include "constants.h"
|
||||
#include "debug.h"
|
||||
#include "log.h"
|
||||
#include <cmath>
|
||||
#include <IAnimatedMesh.h>
|
||||
#include "S3DVertex.h"
|
||||
|
||||
@@ -4,14 +4,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "irrlichttypes_bloated.h"
|
||||
#include "SColor.h"
|
||||
#include "SMaterialLayer.h"
|
||||
#include "nodedef.h"
|
||||
|
||||
namespace scene {
|
||||
class IAnimatedMesh;
|
||||
class IMesh;
|
||||
class IMeshBuffer;
|
||||
struct SMesh;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "camera.h"
|
||||
#include "client.h"
|
||||
#include "mapblock.h" // getNodeBlockPos
|
||||
#include "node_visuals.h"
|
||||
#include "settings.h"
|
||||
#include "shader.h"
|
||||
#include "client/renderingengine.h"
|
||||
@@ -414,12 +415,13 @@ void Minimap::blitMinimapPixelsToImageSurface(
|
||||
} else if (overlay.name.empty() && tile.has_color) {
|
||||
tilecolor = tile.color;
|
||||
} else {
|
||||
mmpixel->n.getColor(f, &tilecolor);
|
||||
f.visuals->getColor(mmpixel->n.param2, &tilecolor);
|
||||
}
|
||||
// Multiply with pre-generated "color of texture"
|
||||
tilecolor.setRed(tilecolor.getRed() * f.minimap_color.getRed() / 255);
|
||||
tilecolor.setGreen(tilecolor.getGreen() * f.minimap_color.getGreen() / 255);
|
||||
tilecolor.setBlue(tilecolor.getBlue() * f.minimap_color.getBlue() / 255);
|
||||
video::SColor &minimap_color = f.visuals->minimap_color;
|
||||
tilecolor.setRed(tilecolor.getRed() * minimap_color.getRed() / 255);
|
||||
tilecolor.setGreen(tilecolor.getGreen() * minimap_color.getGreen() / 255);
|
||||
tilecolor.setBlue(tilecolor.getBlue() * minimap_color.getBlue() / 255);
|
||||
tilecolor.setAlpha(240);
|
||||
|
||||
map_image->setPixel(x, data->mode.map_size - z - 1, tilecolor);
|
||||
|
||||
656
src/client/node_visuals.cpp
Normal file
656
src/client/node_visuals.cpp
Normal file
@@ -0,0 +1,656 @@
|
||||
// Luanti
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
// Copyright (C) 2025 cx384
|
||||
|
||||
#include "node_visuals.h"
|
||||
|
||||
#include "mesh.h"
|
||||
#include "shader.h"
|
||||
#include "client.h"
|
||||
#include "renderingengine.h"
|
||||
#include "texturesource.h"
|
||||
#include "wieldmesh.h" // createAnimationFrames
|
||||
#include "tile.h"
|
||||
#include <IMeshManipulator.h>
|
||||
#include <SMesh.h>
|
||||
#include <SkinnedMesh.h>
|
||||
|
||||
struct TileAttribContext {
|
||||
ITextureSource *tsrc;
|
||||
PreLoadedTextures *texture_pool;
|
||||
video::SColor base_color;
|
||||
const TextureSettings &tsettings;
|
||||
};
|
||||
|
||||
struct ShaderIds {
|
||||
u32 normal;
|
||||
// Shader that will handle an array texture and texture_layer_idx
|
||||
u32 with_layers;
|
||||
};
|
||||
|
||||
/*
|
||||
Texture pool and related
|
||||
*/
|
||||
|
||||
struct PreLoadedTexture {
|
||||
video::ITexture *texture = nullptr;
|
||||
u32 texture_id = 0;
|
||||
u16 texture_layer_idx = 0;
|
||||
bool used = false; // For debugging
|
||||
};
|
||||
|
||||
struct PreLoadedTextures {
|
||||
std::unordered_map<std::string, PreLoadedTexture> pool;
|
||||
std::unordered_set<std::string> missed; // For debugging
|
||||
|
||||
PreLoadedTexture find(const std::string &name);
|
||||
void add(const std::string &name, const PreLoadedTexture &t);
|
||||
|
||||
void printStats(std::ostream &to) const;
|
||||
};
|
||||
|
||||
PreLoadedTexture PreLoadedTextures::find(const std::string &name)
|
||||
{
|
||||
auto it = pool.find(name);
|
||||
if (it == pool.end()) {
|
||||
missed.emplace(name);
|
||||
return {};
|
||||
}
|
||||
it->second.used = true;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void PreLoadedTextures::add(const std::string &name, const PreLoadedTexture &t)
|
||||
{
|
||||
assert(pool.find(name) == pool.end());
|
||||
pool[name] = t;
|
||||
}
|
||||
|
||||
void PreLoadedTextures::printStats(std::ostream &to) const
|
||||
{
|
||||
size_t unused = 0;
|
||||
for (auto &it : pool)
|
||||
unused += it.second.used ? 0 : 1;
|
||||
to << "PreLoadedTextures: " << pool.size() << "\n wasted: " << unused
|
||||
<< " missed: " << missed.size() << std::endl;
|
||||
}
|
||||
|
||||
|
||||
static void fillTileAttribs(TileLayer *layer, TileAttribContext context,
|
||||
const TileSpec &tile, const TileDef &tiledef,
|
||||
MaterialType material_type, ShaderIds shader)
|
||||
{
|
||||
auto *tsrc = context.tsrc;
|
||||
const auto &tsettings = context.tsettings;
|
||||
|
||||
std::string texture_image;
|
||||
if (!tiledef.name.empty()) {
|
||||
texture_image = tiledef.name;
|
||||
if (tsrc->needFilterForMesh())
|
||||
texture_image += tsrc->FILTER_FOR_MESH;
|
||||
} else {
|
||||
// Tile is empty, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
core::dimension2du texture_size;
|
||||
if (!texture_image.empty())
|
||||
texture_size = tsrc->getTextureDimensions(texture_image);
|
||||
if (!texture_size.Width || !texture_size.Height)
|
||||
texture_size = {1, 1}; // dummy if there's an error
|
||||
|
||||
// Scale
|
||||
bool has_scale = tiledef.scale > 0;
|
||||
bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
|
||||
(tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
|
||||
if (use_autoscale) {
|
||||
float base_size = tsettings.node_texture_size;
|
||||
float size = std::fmin(texture_size.Width, texture_size.Height);
|
||||
layer->scale = std::fmax(base_size, size) / base_size;
|
||||
} else if (has_scale) {
|
||||
layer->scale = tiledef.scale;
|
||||
} else {
|
||||
layer->scale = 1;
|
||||
}
|
||||
if (!tile.world_aligned)
|
||||
layer->scale = 1;
|
||||
|
||||
// Material
|
||||
layer->material_type = material_type;
|
||||
layer->material_flags = 0;
|
||||
if (tiledef.backface_culling)
|
||||
layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
|
||||
if (tiledef.animation.type != TAT_NONE)
|
||||
layer->material_flags |= MATERIAL_FLAG_ANIMATION;
|
||||
if (tiledef.tileable_horizontal)
|
||||
layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
|
||||
if (tiledef.tileable_vertical)
|
||||
layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
|
||||
|
||||
// Color
|
||||
layer->has_color = tiledef.has_color;
|
||||
if (tiledef.has_color)
|
||||
layer->color = tiledef.color;
|
||||
else
|
||||
layer->color = context.base_color;
|
||||
|
||||
// Animation
|
||||
if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
|
||||
int frame_length_ms = 0;
|
||||
std::vector<FrameSpec> frames = createAnimationFrames(
|
||||
tsrc, tiledef.name, tiledef.animation, frame_length_ms);
|
||||
if (frames.size() > 1) {
|
||||
layer->frames = new std::vector<FrameSpec>(frames);
|
||||
layer->animation_frame_count = layer->frames->size();
|
||||
layer->animation_frame_length_ms = frame_length_ms;
|
||||
|
||||
// Set default texture to first frame (not used practice)
|
||||
layer->texture = (*layer->frames)[0].texture;
|
||||
layer->texture_id = (*layer->frames)[0].texture_id;
|
||||
} else {
|
||||
layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(layer->material_flags & MATERIAL_FLAG_ANIMATION)) {
|
||||
// Grab texture
|
||||
auto tex = context.texture_pool->find(texture_image);
|
||||
if (!tex.texture) {
|
||||
// wasn't pre-loaded: create standard texture on the fly
|
||||
layer->texture = tsrc->getTexture(texture_image, &layer->texture_id);
|
||||
} else {
|
||||
layer->texture = tex.texture;
|
||||
layer->texture_id = tex.texture_id;
|
||||
layer->texture_layer_idx = tex.texture_layer_idx;
|
||||
}
|
||||
}
|
||||
|
||||
// Decide on shader to use
|
||||
if (layer->texture) {
|
||||
layer->shader_id = (layer->texture->getType() == video::ETT_2D_ARRAY) ?
|
||||
shader.with_layers : shader.normal;
|
||||
}
|
||||
}
|
||||
|
||||
static bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
|
||||
{
|
||||
if (style == ALIGN_STYLE_WORLD)
|
||||
return true;
|
||||
if (mode == WORLDALIGN_DISABLE)
|
||||
return false;
|
||||
if (style == ALIGN_STYLE_USER_DEFINED)
|
||||
return true;
|
||||
if (drawtype == NDT_NORMAL)
|
||||
return mode >= WORLDALIGN_FORCE;
|
||||
if (drawtype == NDT_NODEBOX)
|
||||
return mode >= WORLDALIGN_FORCE_NODEBOX;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @return maximum number of layers in array textures we can use (0 if unsupported)
|
||||
static size_t getArrayTextureMax(IShaderSource *shdsrc)
|
||||
{
|
||||
auto *driver = RenderingEngine::get_video_driver();
|
||||
// needs to support creating array textures
|
||||
if (!driver->queryFeature(video::EVDF_TEXTURE_2D_ARRAY))
|
||||
return 0;
|
||||
// must support sampling from them
|
||||
if (!shdsrc->supportsSampler2DArray())
|
||||
return 0;
|
||||
// shadow shaders can't handle array textures yet (TODO)
|
||||
if (g_settings->getBool("enable_dynamic_shadows"))
|
||||
return 0;
|
||||
|
||||
u32 n = driver->getLimits().MaxArrayTextureImages;
|
||||
constexpr u32 type_max = std::numeric_limits<decltype(TileLayer::texture_layer_idx)>::max();
|
||||
n = std::min(n, type_max);
|
||||
n = std::min(n, g_settings->getU32("array_texture_max"));
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
//// NodeVisuals
|
||||
|
||||
NodeVisuals::~NodeVisuals()
|
||||
{
|
||||
for (u16 j = 0; j < 6; j++) {
|
||||
delete tiles[j].layers[0].frames;
|
||||
delete tiles[j].layers[1].frames;
|
||||
}
|
||||
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
|
||||
delete special_tiles[j].layers[0].frames;
|
||||
delete special_tiles[j].layers[1].frames;
|
||||
}
|
||||
if (mesh_ptr)
|
||||
mesh_ptr->drop();
|
||||
}
|
||||
|
||||
void NodeVisuals::preUpdateTextures(ITextureSource *tsrc,
|
||||
std::unordered_set<std::string> &pool, const TextureSettings &tsettings)
|
||||
{
|
||||
// Find out the exact texture strings this node might use, and put them into the pool
|
||||
// (this should match updateTextures, but it's not the end of the world if
|
||||
// a mismatch occurs)
|
||||
std::string append;
|
||||
if (tsrc->needFilterForMesh())
|
||||
append = ITextureSource::FILTER_FOR_MESH;
|
||||
std::string append_overlay = append, append_special = append;
|
||||
bool use = true, use_overlay = true, use_special = true;
|
||||
|
||||
if (f->drawtype == NDT_ALLFACES_OPTIONAL) {
|
||||
use_special = (tsettings.leaves_style == LEAVES_SIMPLE);
|
||||
use = !use_special;
|
||||
if (tsettings.leaves_style == LEAVES_OPAQUE)
|
||||
append.insert(0, "^[noalpha");
|
||||
}
|
||||
|
||||
const auto &consider_tile = [&] (const TileDef &def, const std::string &append) {
|
||||
// Animations are chopped into frames later, so we won't actually need
|
||||
// the source texture
|
||||
if (!def.name.empty() && def.animation.type == TAT_NONE) {
|
||||
pool.insert(def.name + append);
|
||||
}
|
||||
};
|
||||
|
||||
for (u32 j = 0; j < 6; j++) {
|
||||
if (use)
|
||||
consider_tile(f->tiledef[j], append);
|
||||
}
|
||||
for (u32 j = 0; j < 6; j++) {
|
||||
if (use_overlay)
|
||||
consider_tile(f->tiledef_overlay[j], append_overlay);
|
||||
}
|
||||
for (u32 j = 0; j < CF_SPECIAL_COUNT; j++) {
|
||||
if (use_special)
|
||||
consider_tile(f->tiledef_special[j], append_special);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeVisuals::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, Client *client,
|
||||
PreLoadedTextures *texture_pool, const TextureSettings &tsettings)
|
||||
{
|
||||
// Things needed form ContentFeatures
|
||||
auto &alpha = f->alpha;
|
||||
auto &drawtype = f->drawtype;
|
||||
const auto &tiledef = f->tiledef;
|
||||
const auto &tiledef_overlay = f->tiledef_overlay;
|
||||
const auto &tiledef_special = f->tiledef_special;
|
||||
const auto &waving = f->waving;
|
||||
const auto &color = f->color;
|
||||
const auto ¶m_type_2 = f->param_type_2;
|
||||
const auto &palette_name = f->palette_name;
|
||||
|
||||
// Figure out the actual tiles to use
|
||||
TileDef tdef[6];
|
||||
for (u32 j = 0; j < 6; j++) {
|
||||
tdef[j] = tiledef[j];
|
||||
if (tdef[j].name.empty()) {
|
||||
tdef[j].name = "no_texture.png";
|
||||
tdef[j].backface_culling = false;
|
||||
}
|
||||
}
|
||||
// also the overlay tiles
|
||||
TileDef tdef_overlay[6];
|
||||
for (u32 j = 0; j < 6; j++)
|
||||
tdef_overlay[j] = tiledef_overlay[j];
|
||||
// also the special tiles
|
||||
TileDef tdef_spec[6];
|
||||
for (u32 j = 0; j < CF_SPECIAL_COUNT; j++) {
|
||||
tdef_spec[j] = tiledef_special[j];
|
||||
}
|
||||
|
||||
bool is_liquid = false;
|
||||
|
||||
MaterialType material_type = alpha_mode_to_material_type(alpha);
|
||||
|
||||
switch (drawtype) {
|
||||
default:
|
||||
case NDT_NORMAL:
|
||||
solidness = 2;
|
||||
break;
|
||||
case NDT_AIRLIKE:
|
||||
solidness = 0;
|
||||
break;
|
||||
case NDT_LIQUID:
|
||||
if (!tsettings.translucent_liquids)
|
||||
alpha = ALPHAMODE_OPAQUE;
|
||||
solidness = 1;
|
||||
is_liquid = true;
|
||||
break;
|
||||
case NDT_FLOWINGLIQUID:
|
||||
solidness = 0;
|
||||
if (!tsettings.translucent_liquids)
|
||||
alpha = ALPHAMODE_OPAQUE;
|
||||
is_liquid = true;
|
||||
break;
|
||||
case NDT_GLASSLIKE:
|
||||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
break;
|
||||
case NDT_GLASSLIKE_FRAMED:
|
||||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
break;
|
||||
case NDT_GLASSLIKE_FRAMED_OPTIONAL:
|
||||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
|
||||
break;
|
||||
case NDT_ALLFACES:
|
||||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
break;
|
||||
case NDT_ALLFACES_OPTIONAL:
|
||||
if (tsettings.leaves_style == LEAVES_FANCY) {
|
||||
drawtype = NDT_ALLFACES;
|
||||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
} else if (tsettings.leaves_style == LEAVES_SIMPLE) {
|
||||
for (u32 j = 0; j < 6; j++) {
|
||||
if (!tdef_spec[j].name.empty())
|
||||
tdef[j].name = tdef_spec[j].name;
|
||||
}
|
||||
drawtype = NDT_GLASSLIKE;
|
||||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
} else {
|
||||
if (waving >= 1) {
|
||||
// waving nodes must make faces so there are no gaps
|
||||
drawtype = NDT_ALLFACES;
|
||||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
} else {
|
||||
drawtype = NDT_NORMAL;
|
||||
solidness = 2;
|
||||
}
|
||||
for (TileDef &td : tdef)
|
||||
td.name += std::string("^[noalpha");
|
||||
}
|
||||
if (waving >= 1)
|
||||
material_type = TILE_MATERIAL_WAVING_LEAVES;
|
||||
break;
|
||||
case NDT_PLANTLIKE:
|
||||
solidness = 0;
|
||||
if (waving >= 1)
|
||||
material_type = TILE_MATERIAL_WAVING_PLANTS;
|
||||
break;
|
||||
case NDT_FIRELIKE:
|
||||
solidness = 0;
|
||||
break;
|
||||
case NDT_MESH:
|
||||
case NDT_NODEBOX:
|
||||
solidness = 0;
|
||||
if (waving == 1) {
|
||||
material_type = TILE_MATERIAL_WAVING_PLANTS;
|
||||
} else if (waving == 2) {
|
||||
material_type = TILE_MATERIAL_WAVING_LEAVES;
|
||||
} else if (waving == 3) {
|
||||
material_type = alpha == ALPHAMODE_OPAQUE ?
|
||||
TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
|
||||
TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
|
||||
}
|
||||
break;
|
||||
case NDT_TORCHLIKE:
|
||||
case NDT_SIGNLIKE:
|
||||
case NDT_FENCELIKE:
|
||||
case NDT_RAILLIKE:
|
||||
solidness = 0;
|
||||
break;
|
||||
case NDT_PLANTLIKE_ROOTED:
|
||||
solidness = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_liquid) {
|
||||
if (waving == 3) {
|
||||
material_type = alpha == ALPHAMODE_OPAQUE ?
|
||||
TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
|
||||
TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
|
||||
} else {
|
||||
material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
|
||||
TILE_MATERIAL_LIQUID_TRANSPARENT;
|
||||
}
|
||||
}
|
||||
|
||||
const bool texture_2d_array = getArrayTextureMax(shdsrc) > 1;
|
||||
const auto &getNodeShader = [&] (MaterialType my_material, NodeDrawType my_drawtype) {
|
||||
ShaderIds ret;
|
||||
ret.normal = shdsrc->getShader("nodes_shader", my_material, my_drawtype);
|
||||
// need to avoid generating the shader if unsupported
|
||||
if (texture_2d_array)
|
||||
ret.with_layers = shdsrc->getShader("nodes_shader", my_material, my_drawtype, true);
|
||||
return ret;
|
||||
};
|
||||
|
||||
ShaderIds tile_shader = getNodeShader(material_type, drawtype);
|
||||
|
||||
MaterialType overlay_material = material_type_with_alpha(material_type);
|
||||
|
||||
ShaderIds overlay_shader = getNodeShader(overlay_material, drawtype);
|
||||
|
||||
// minimap pixel color = average color of top tile
|
||||
if (tsettings.enable_minimap && drawtype != NDT_AIRLIKE && !tdef[0].name.empty())
|
||||
{
|
||||
if (!tdef_overlay[0].name.empty()) {
|
||||
// Merge overlay and base texture
|
||||
std::string combined = tdef[0].name + "^(" + tdef_overlay[0].name + ")";
|
||||
minimap_color = tsrc->getTextureAverageColor(combined);
|
||||
} else {
|
||||
minimap_color = tsrc->getTextureAverageColor(tdef[0].name);
|
||||
}
|
||||
}
|
||||
|
||||
// Tiles (fill in f->tiles[])
|
||||
bool any_polygon_offset = false;
|
||||
TileAttribContext tac{tsrc, texture_pool, color, tsettings};
|
||||
|
||||
for (u16 j = 0; j < 6; j++) {
|
||||
tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
|
||||
tsettings.world_aligned_mode, drawtype);
|
||||
fillTileAttribs(&tiles[j].layers[0], tac, tiles[j], tdef[j],
|
||||
material_type, tile_shader);
|
||||
if (!tdef_overlay[j].name.empty()) {
|
||||
tdef_overlay[j].backface_culling = tdef[j].backface_culling;
|
||||
fillTileAttribs(&tiles[j].layers[1], tac, tiles[j], tdef_overlay[j],
|
||||
overlay_material, overlay_shader);
|
||||
}
|
||||
|
||||
tiles[j].layers[0].need_polygon_offset = !tiles[j].layers[1].empty();
|
||||
any_polygon_offset |= tiles[j].layers[0].need_polygon_offset;
|
||||
}
|
||||
|
||||
if (drawtype == NDT_MESH && any_polygon_offset) {
|
||||
// Our per-tile polygon offset enablement workaround works fine for normal
|
||||
// nodes and anything else, where we know that different tiles are different
|
||||
// faces that couldn't possibly conflict with each other.
|
||||
// We can't assume this for mesh nodes, so apply it to all tiles (= materials)
|
||||
// then.
|
||||
for (u16 j = 0; j < 6; j++)
|
||||
tiles[j].layers[0].need_polygon_offset = true;
|
||||
}
|
||||
|
||||
MaterialType special_material = material_type;
|
||||
if (drawtype == NDT_PLANTLIKE_ROOTED) {
|
||||
if (waving == 1)
|
||||
special_material = TILE_MATERIAL_WAVING_PLANTS;
|
||||
else if (waving == 2)
|
||||
special_material = TILE_MATERIAL_WAVING_LEAVES;
|
||||
}
|
||||
|
||||
ShaderIds special_shader = getNodeShader(special_material, drawtype);
|
||||
|
||||
// Special tiles (fill in f->special_tiles[])
|
||||
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
|
||||
fillTileAttribs(&special_tiles[j].layers[0], tac,
|
||||
special_tiles[j], tdef_spec[j], special_material, special_shader);
|
||||
}
|
||||
|
||||
if (param_type_2 == CPT2_COLOR ||
|
||||
param_type_2 == CPT2_COLORED_FACEDIR ||
|
||||
param_type_2 == CPT2_COLORED_4DIR ||
|
||||
param_type_2 == CPT2_COLORED_WALLMOUNTED ||
|
||||
param_type_2 == CPT2_COLORED_DEGROTATE)
|
||||
palette = tsrc->getPalette(palette_name);
|
||||
}
|
||||
|
||||
void NodeVisuals::updateMesh(Client *client, const TextureSettings &tsettings)
|
||||
{
|
||||
auto *manip = client->getSceneManager()->getMeshManipulator();
|
||||
(void)tsettings;
|
||||
|
||||
const auto &mesh = f->mesh;
|
||||
if (f->drawtype != NDT_MESH || mesh.empty())
|
||||
return;
|
||||
|
||||
// Note: By freshly reading, we get an unencumbered mesh.
|
||||
if (scene::IMesh *src_mesh = client->getMesh(mesh)) {
|
||||
bool apply_bs = false;
|
||||
if (auto *skinned_mesh = dynamic_cast<scene::SkinnedMesh *>(src_mesh)) {
|
||||
// Compatibility: Animated meshes, as well as static gltf meshes, are not scaled by BS.
|
||||
// See https://github.com/luanti-org/luanti/pull/16112#issuecomment-2881860329
|
||||
bool is_gltf = skinned_mesh->getSourceFormat() ==
|
||||
scene::SkinnedMesh::SourceFormat::GLTF;
|
||||
apply_bs = skinned_mesh->isStatic() && !is_gltf;
|
||||
// Nodes do not support mesh animation, so we clone the static pose.
|
||||
// This simplifies working with the mesh: We can just scale the vertices
|
||||
// as transformations have already been applied.
|
||||
mesh_ptr = cloneStaticMesh(src_mesh);
|
||||
src_mesh->drop();
|
||||
} else {
|
||||
auto *static_mesh = dynamic_cast<scene::SMesh *>(src_mesh);
|
||||
assert(static_mesh);
|
||||
mesh_ptr = static_mesh;
|
||||
// Compatibility: Apply BS scaling to static meshes (.obj). See #15811.
|
||||
apply_bs = true;
|
||||
}
|
||||
scaleMesh(mesh_ptr, v3f((apply_bs ? BS : 1.0f) * f->visual_scale));
|
||||
recalculateBoundingBox(mesh_ptr);
|
||||
if (!checkMeshNormals(mesh_ptr)) {
|
||||
// TODO this should be done consistently when the mesh is loaded
|
||||
infostream << "ContentFeatures: recalculating normals for mesh "
|
||||
<< mesh << std::endl;
|
||||
manip->recalculateNormals(mesh_ptr, true, false);
|
||||
}
|
||||
} else {
|
||||
mesh_ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeVisuals::collectMaterials(std::vector<u32> &leaves_materials)
|
||||
{
|
||||
if (f->drawtype == NDT_AIRLIKE)
|
||||
return;
|
||||
|
||||
for (u16 j = 0; j < 6; j++) {
|
||||
auto &l = tiles[j].layers;
|
||||
if (!l[0].empty() && l[0].material_type == TILE_MATERIAL_WAVING_LEAVES)
|
||||
leaves_materials.push_back(l[0].shader_id);
|
||||
if (!l[1].empty() && l[1].material_type == TILE_MATERIAL_WAVING_LEAVES)
|
||||
leaves_materials.push_back(l[1].shader_id);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeVisuals::getColor(u8 param2, video::SColor *color) const
|
||||
{
|
||||
if (palette) {
|
||||
*color = (*palette)[param2];
|
||||
return;
|
||||
}
|
||||
*color = f->color;
|
||||
}
|
||||
|
||||
void NodeVisuals::fillNodeVisuals(NodeDefManager *ndef, Client *client, void *progress_callback_args)
|
||||
{
|
||||
infostream << "fillNodeVisuals: Updating "
|
||||
"textures in node definitions" << std::endl;
|
||||
ITextureSource *tsrc = client->tsrc();
|
||||
IShaderSource *shdsrc = client->getShaderSource();
|
||||
TextureSettings tsettings;
|
||||
tsettings.readSettings();
|
||||
|
||||
tsrc->setImageCaching(true);
|
||||
const u32 size = ndef->size();
|
||||
|
||||
/* collect all textures we might use */
|
||||
std::unordered_set<std::string> pool;
|
||||
ndef->applyFunction([&](ContentFeatures &f) {
|
||||
assert(!f.visuals);
|
||||
f.visuals = new NodeVisuals(&f);
|
||||
f.visuals->preUpdateTextures(tsrc, pool, tsettings);
|
||||
});
|
||||
|
||||
/* texture pre-loading stage */
|
||||
const size_t arraymax = getArrayTextureMax(shdsrc);
|
||||
// Group by size
|
||||
std::unordered_map<v2u32, std::vector<std::string_view>> sizes;
|
||||
if (arraymax > 1) {
|
||||
infostream << "Using array textures with " << arraymax << " layers" << std::endl;
|
||||
size_t i = 0;
|
||||
for (auto &image : pool) {
|
||||
core::dimension2du dim = tsrc->getTextureDimensions(image);
|
||||
client->showUpdateProgressTexture(progress_callback_args,
|
||||
0.33333f * ++i / pool.size());
|
||||
if (!dim.Width || !dim.Height) // error
|
||||
continue;
|
||||
sizes[v2u32(dim)].emplace_back(image);
|
||||
}
|
||||
}
|
||||
|
||||
// create array textures as far as possible
|
||||
size_t num_preloadable = 0, preload_progress = 0;
|
||||
for (auto &it : sizes) {
|
||||
if (it.second.size() < 2)
|
||||
continue;
|
||||
num_preloadable += it.second.size();
|
||||
}
|
||||
PreLoadedTextures plt;
|
||||
const auto &doBunch = [&] (const std::vector<std::string> &bunch) {
|
||||
PreLoadedTexture t;
|
||||
t.texture = tsrc->addArrayTexture(bunch, &t.texture_id);
|
||||
preload_progress += bunch.size();
|
||||
client->showUpdateProgressTexture(progress_callback_args,
|
||||
0.33333f + 0.33333f * preload_progress / num_preloadable);
|
||||
if (t.texture) {
|
||||
// Success: all of the images in this bunch can now refer to this texture
|
||||
for (size_t idx = 0; idx < bunch.size(); idx++) {
|
||||
t.texture_layer_idx = idx;
|
||||
plt.add(bunch[idx], t);
|
||||
}
|
||||
}
|
||||
};
|
||||
for (auto &it : sizes) {
|
||||
if (it.second.size() < 2)
|
||||
continue;
|
||||
std::vector<std::string> bunch;
|
||||
for (auto &image : it.second) {
|
||||
bunch.emplace_back(image);
|
||||
if (bunch.size() == arraymax) {
|
||||
doBunch(bunch);
|
||||
bunch.clear();
|
||||
}
|
||||
}
|
||||
if (!bunch.empty())
|
||||
doBunch(bunch);
|
||||
}
|
||||
// note that standard textures aren't preloaded
|
||||
|
||||
/* final step */
|
||||
u32 progress = 0;
|
||||
ndef->applyFunction([&](ContentFeatures &f) {
|
||||
auto *v = f.visuals;
|
||||
v->updateTextures(tsrc, shdsrc, client, &plt, tsettings);
|
||||
v->updateMesh(client, tsettings);
|
||||
v->collectMaterials(ndef->m_leaves_materials);
|
||||
|
||||
client->showUpdateProgressTexture(progress_callback_args,
|
||||
0.66666f + 0.33333f * progress / size);
|
||||
progress++;
|
||||
});
|
||||
|
||||
SORT_AND_UNIQUE(ndef->m_leaves_materials);
|
||||
verbosestream << "m_leaves_materials.size() = " << ndef->m_leaves_materials.size()
|
||||
<< std::endl;
|
||||
|
||||
plt.printStats(infostream);
|
||||
tsrc->setImageCaching(false);
|
||||
}
|
||||
87
src/client/node_visuals.h
Normal file
87
src/client/node_visuals.h
Normal file
@@ -0,0 +1,87 @@
|
||||
// Luanti
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
// Copyright (C) 2025 cx384
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_set>
|
||||
#include "nodedef.h" // CF_SPECIAL_COUNT
|
||||
#include "tile.h"
|
||||
|
||||
class Client;
|
||||
struct PreLoadedTextures;
|
||||
namespace scene
|
||||
{
|
||||
class IMeshManipulator;
|
||||
struct SMesh;
|
||||
}
|
||||
|
||||
// Stores client only data needed to draw nodes, like textures and meshes
|
||||
// Contained in ContentFeatures
|
||||
|
||||
struct NodeVisuals
|
||||
{
|
||||
// 0 1 2 3 4 5
|
||||
// up down right left back front
|
||||
TileSpec tiles[6];
|
||||
// Special tiles
|
||||
TileSpec special_tiles[CF_SPECIAL_COUNT];
|
||||
u8 solidness = 2; // Used when choosing which face is drawn
|
||||
u8 visual_solidness = 0; // When solidness=0, this tells how it looks like
|
||||
bool backface_culling = true;
|
||||
scene::SMesh *mesh_ptr = nullptr; // mesh in case of mesh node
|
||||
video::SColor minimap_color;
|
||||
std::vector<video::SColor> *palette = nullptr;
|
||||
|
||||
// alpha stays in ContentFeatures due to compatibility code that is necessary,
|
||||
// because it was part of the node definition table in the past.
|
||||
~NodeVisuals();
|
||||
|
||||
// Get color from palette or content features
|
||||
void getColor(u8 param2, video::SColor *color) const;
|
||||
|
||||
/*!
|
||||
* Creates NodeVisuals for every content feature in the passed NodeDefManager.
|
||||
* @param ndef the NodeDefManager.
|
||||
* @param client the Client.
|
||||
* @param progress_cbk called each time a node is loaded. Arguments:
|
||||
* `progress_cbk_args`, number of loaded ContentFeatures, number of
|
||||
* total ContentFeatures.
|
||||
* @param progress_cbk_args passed to the callback function
|
||||
*/
|
||||
static void fillNodeVisuals(NodeDefManager *ndef, Client *client,
|
||||
void *progress_callback_args);
|
||||
|
||||
DISABLE_CLASS_COPY(NodeVisuals);
|
||||
|
||||
private:
|
||||
NodeVisuals(ContentFeatures *features) : f{features} {}
|
||||
friend class DummyGameDef; // Unittests need constructor
|
||||
|
||||
ContentFeatures *f = nullptr;
|
||||
|
||||
// Functions needed for initialisation
|
||||
void preUpdateTextures(ITextureSource *tsrc,
|
||||
std::unordered_set<std::string> &pool, const TextureSettings &tsettings);
|
||||
// May override the alpha and drawtype of the content features
|
||||
void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, Client *client,
|
||||
PreLoadedTextures *texture_pool, const TextureSettings &tsettings);
|
||||
void updateMesh(Client *client, const TextureSettings &tsettings);
|
||||
void collectMaterials(std::vector<u32> &leaves_materials);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief get fitting material type for an alpha mode
|
||||
*/
|
||||
static inline MaterialType alpha_mode_to_material_type(AlphaMode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case ALPHAMODE_BLEND:
|
||||
return TILE_MATERIAL_ALPHA;
|
||||
case ALPHAMODE_OPAQUE:
|
||||
return TILE_MATERIAL_OPAQUE;
|
||||
case ALPHAMODE_CLIP:
|
||||
default:
|
||||
return TILE_MATERIAL_BASIC;
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,9 @@
|
||||
#include "util/numeric.h"
|
||||
#include "light.h"
|
||||
#include "localplayer.h"
|
||||
#include "environment.h"
|
||||
#include "clientmap.h"
|
||||
#include "mapnode.h"
|
||||
#include "node_visuals.h"
|
||||
#include "nodedef.h"
|
||||
#include "client.h"
|
||||
#include "settings.h"
|
||||
@@ -891,7 +891,7 @@ bool ParticleManager::getNodeParticleParams(Client *client, const MapNode &n,
|
||||
else
|
||||
texid = myrand_range(0,5);
|
||||
|
||||
const TileLayer &tile = f.tiles[texid].layers[0];
|
||||
const TileLayer &tile = f.visuals->tiles[texid].layers[0];
|
||||
*texture = extractTexture(f.tiledef[texid], tile, client->tsrc());
|
||||
p.texture.blendmode = f.alpha == ALPHAMODE_BLEND
|
||||
? BlendMode::alpha : BlendMode::clip;
|
||||
@@ -908,7 +908,7 @@ bool ParticleManager::getNodeParticleParams(Client *client, const MapNode &n,
|
||||
if (tile.has_color)
|
||||
*color = tile.color;
|
||||
else
|
||||
n.getColor(f, color);
|
||||
f.visuals->getColor(n.param2, color);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <map>
|
||||
#include <variant>
|
||||
#include "nodedef.h"
|
||||
#include "tile.h" // MaterialType
|
||||
|
||||
/*
|
||||
shader.{h,cpp}: Shader handling stuff.
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
#include <IMaterialRendererServices.h>
|
||||
#include <SMaterial.h>
|
||||
#include <S3DVertex.h>
|
||||
#include "client/shader.h"
|
||||
|
||||
class ShadowScreenQuad
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "inventory.h"
|
||||
#include "client.h"
|
||||
#include "itemdef.h"
|
||||
#include "node_visuals.h"
|
||||
#include "nodedef.h"
|
||||
#include "mesh.h"
|
||||
#include "content_mapblock.h"
|
||||
@@ -15,7 +16,6 @@
|
||||
#include "client/meshgen/collector.h"
|
||||
#include "client/tile.h"
|
||||
#include "client/texturesource.h"
|
||||
#include "log.h"
|
||||
#include "util/numeric.h"
|
||||
#include <map>
|
||||
#include <IMeshManipulator.h>
|
||||
@@ -430,6 +430,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
|
||||
const NodeDefManager *ndef = client->getNodeDefManager();
|
||||
const ItemDefinition &def = item.getDefinition(idef);
|
||||
const ContentFeatures &f = ndef->get(def.name);
|
||||
const NodeVisuals &v = *(f.visuals);
|
||||
|
||||
{
|
||||
// Initialize material type used by setExtruded
|
||||
@@ -504,13 +505,13 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
|
||||
v3f wscale = wield_scale;
|
||||
if (f.drawtype == NDT_FLOWINGLIQUID)
|
||||
wscale.Z *= 0.1f;
|
||||
setExtruded(f.tiledef[0], f.tiles[0].layers[0],
|
||||
f.tiledef_overlay[0], f.tiles[0].layers[1], wscale, tsrc);
|
||||
setExtruded(f.tiledef[0], v.tiles[0].layers[0],
|
||||
f.tiledef_overlay[0], v.tiles[0].layers[1], wscale, tsrc);
|
||||
break;
|
||||
}
|
||||
case NDT_PLANTLIKE_ROOTED: {
|
||||
// use the plant tile
|
||||
setExtruded(f.tiledef_special[0], f.special_tiles[0].layers[0],
|
||||
setExtruded(f.tiledef_special[0], v.special_tiles[0].layers[0],
|
||||
TileDef(), TileLayer(), wield_scale, tsrc);
|
||||
break;
|
||||
}
|
||||
@@ -646,6 +647,7 @@ void createItemMesh(Client *client, const ItemDefinition &def,
|
||||
IShaderSource *shdsrc = client->getShaderSource();
|
||||
const NodeDefManager *ndef = client->getNodeDefManager();
|
||||
const ContentFeatures &f = ndef->get(def.name);
|
||||
const NodeVisuals &v = *(f.visuals);
|
||||
assert(result);
|
||||
|
||||
FATAL_ERROR_IF(!g_extrusion_mesh_cache, "Extrusion mesh cache is not yet initialized");
|
||||
@@ -678,8 +680,8 @@ void createItemMesh(Client *client, const ItemDefinition &def,
|
||||
} else if (def.type == ITEM_NODE) {
|
||||
switch (f.drawtype) {
|
||||
case NDT_PLANTLIKE: {
|
||||
const TileLayer &l0 = f.tiles[0].layers[0];
|
||||
const TileLayer &l1 = f.tiles[0].layers[1];
|
||||
const TileLayer &l0 = v.tiles[0].layers[0];
|
||||
const TileLayer &l1 = v.tiles[0].layers[1];
|
||||
mesh = getExtrudedMesh(
|
||||
extractTexture(f.tiledef[0], l0, tsrc),
|
||||
extractTexture(f.tiledef[1], l1, tsrc));
|
||||
@@ -690,7 +692,7 @@ void createItemMesh(Client *client, const ItemDefinition &def,
|
||||
}
|
||||
case NDT_PLANTLIKE_ROOTED: {
|
||||
// Use the plant tile
|
||||
const TileLayer &l0 = f.special_tiles[0].layers[0];
|
||||
const TileLayer &l0 = v.special_tiles[0].layers[0];
|
||||
mesh = getExtrudedMesh(
|
||||
extractTexture(f.tiledef_special[0], l0, tsrc)
|
||||
);
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include "craftdef.h"
|
||||
#include "content/mods.h"
|
||||
#include "database/database-dummy.h"
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
#include "client/node_visuals.h"
|
||||
#endif
|
||||
|
||||
class DummyGameDef : public IGameDef {
|
||||
public:
|
||||
@@ -62,4 +65,8 @@ protected:
|
||||
NodeDefManager *m_nodedef = nullptr;
|
||||
ICraftDefManager *m_craftdef = nullptr;
|
||||
ModStorageDatabase *m_mod_storage_database = nullptr;
|
||||
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
static NodeVisuals *constructNodeVisuals(ContentFeatures *f) { return new NodeVisuals(f); }
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include "client/texturesource.h"
|
||||
#include "client/guiscalingfilter.h"
|
||||
#include "client/item_visuals_manager.h"
|
||||
#include <IMesh.h>
|
||||
#include <IMeshBuffer.h>
|
||||
|
||||
struct MeshTimeInfo {
|
||||
u64 time;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "constants.h"
|
||||
#include "noise.h"
|
||||
#include "nodedef.h"
|
||||
#include "util/string.h"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "constants.h"
|
||||
#include "objdef.h"
|
||||
#include "nodedef.h"
|
||||
#include "noise.h"
|
||||
|
||||
@@ -29,15 +29,6 @@ static const u8 rot_to_wallmounted[] = {
|
||||
MapNode
|
||||
*/
|
||||
|
||||
void MapNode::getColor(const ContentFeatures &f, video::SColor *color) const
|
||||
{
|
||||
if (f.palette) {
|
||||
*color = (*f.palette)[param2];
|
||||
return;
|
||||
}
|
||||
*color = f.color;
|
||||
}
|
||||
|
||||
u8 MapNode::getFaceDir(const NodeDefManager *nodemgr,
|
||||
bool allow_wallmounted) const
|
||||
{
|
||||
|
||||
@@ -188,14 +188,6 @@ struct alignas(u32) MapNode
|
||||
param2 = p;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns the color of the node.
|
||||
*
|
||||
* \param f content features of this node
|
||||
* \param color output, contains the node's color.
|
||||
*/
|
||||
void getColor(const ContentFeatures &f, video::SColor *color) const;
|
||||
|
||||
inline void setLight(LightBank bank, u8 a_light, ContentLightingFlags f) noexcept
|
||||
{
|
||||
// If node doesn't contain light data, ignore this
|
||||
|
||||
656
src/nodedef.cpp
656
src/nodedef.cpp
@@ -5,18 +5,6 @@
|
||||
#include "nodedef.h"
|
||||
|
||||
#include "itemdef.h"
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
#include "client/mesh.h"
|
||||
#include "client/shader.h"
|
||||
#include "client/client.h"
|
||||
#include "client/renderingengine.h"
|
||||
#include "client/texturesource.h"
|
||||
#include "client/tile.h"
|
||||
#include <IMeshManipulator.h>
|
||||
#include <SMesh.h>
|
||||
#include <SkinnedMesh.h>
|
||||
#include "client/wieldmesh.h" // createAnimationFrames
|
||||
#endif
|
||||
#include "log.h"
|
||||
#include "settings.h"
|
||||
#include "nameidmapping.h"
|
||||
@@ -27,9 +15,11 @@
|
||||
#include "debug.h"
|
||||
#include "gamedef.h"
|
||||
#include "mapnode.h"
|
||||
#include <fstream> // Used in applyTextureOverrides()
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
#include "client/node_visuals.h" // ~NodeVisuals
|
||||
#endif
|
||||
|
||||
/*
|
||||
NodeBox
|
||||
@@ -309,55 +299,6 @@ void TextureSettings::readSettings()
|
||||
autoscale_mode = AUTOSCALE_DISABLE;
|
||||
}
|
||||
|
||||
/*
|
||||
Texture pool and related
|
||||
*/
|
||||
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
struct PreLoadedTexture {
|
||||
video::ITexture *texture = nullptr;
|
||||
u32 texture_id = 0;
|
||||
u16 texture_layer_idx = 0;
|
||||
bool used = false; // For debugging
|
||||
};
|
||||
|
||||
struct PreLoadedTextures {
|
||||
std::unordered_map<std::string, PreLoadedTexture> pool;
|
||||
std::unordered_set<std::string> missed; // For debugging
|
||||
|
||||
PreLoadedTexture find(const std::string &name);
|
||||
void add(const std::string &name, const PreLoadedTexture &t);
|
||||
|
||||
void printStats(std::ostream &to) const;
|
||||
};
|
||||
|
||||
PreLoadedTexture PreLoadedTextures::find(const std::string &name)
|
||||
{
|
||||
auto it = pool.find(name);
|
||||
if (it == pool.end()) {
|
||||
missed.emplace(name);
|
||||
return {};
|
||||
}
|
||||
it->second.used = true;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void PreLoadedTextures::add(const std::string &name, const PreLoadedTexture &t)
|
||||
{
|
||||
assert(pool.find(name) == pool.end());
|
||||
pool[name] = t;
|
||||
}
|
||||
|
||||
void PreLoadedTextures::printStats(std::ostream &to) const
|
||||
{
|
||||
size_t unused = 0;
|
||||
for (auto &it : pool)
|
||||
unused += it.second.used ? 0 : 1;
|
||||
to << "PreLoadedTextures: " << pool.size() << "\n wasted: " << unused
|
||||
<< " missed: " << missed.size() << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
ContentFeatures
|
||||
@@ -371,14 +312,7 @@ ContentFeatures::ContentFeatures()
|
||||
ContentFeatures::~ContentFeatures()
|
||||
{
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
for (u16 j = 0; j < 6; j++) {
|
||||
delete tiles[j].layers[0].frames;
|
||||
delete tiles[j].layers[1].frames;
|
||||
}
|
||||
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
|
||||
delete special_tiles[j].layers[0].frames;
|
||||
delete special_tiles[j].layers[1].frames;
|
||||
}
|
||||
delete visuals;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -387,12 +321,6 @@ void ContentFeatures::reset()
|
||||
/*
|
||||
Cached stuff
|
||||
*/
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
solidness = 2;
|
||||
visual_solidness = 0;
|
||||
backface_culling = true;
|
||||
|
||||
#endif
|
||||
has_on_construct = false;
|
||||
has_on_destruct = false;
|
||||
has_after_destruct = false;
|
||||
@@ -410,10 +338,6 @@ void ContentFeatures::reset()
|
||||
groups["dig_immediate"] = 2;
|
||||
drawtype = NDT_NORMAL;
|
||||
mesh.clear();
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
mesh_ptr = nullptr;
|
||||
minimap_color = video::SColor(0, 0, 0, 0);
|
||||
#endif
|
||||
visual_scale = 1.0;
|
||||
for (auto &i : tiledef)
|
||||
i = TileDef();
|
||||
@@ -460,7 +384,6 @@ void ContentFeatures::reset()
|
||||
connect_sides = 0;
|
||||
color = video::SColor(0xFFFFFFFF);
|
||||
palette_name.clear();
|
||||
palette = NULL;
|
||||
node_dig_prediction = "air";
|
||||
move_resistance = 0;
|
||||
liquid_move_physics = false;
|
||||
@@ -725,491 +648,16 @@ void ContentFeatures::deSerialize(std::istream &is, u16 protocol_version)
|
||||
} catch (SerializationError &e) {};
|
||||
}
|
||||
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
struct TileAttribContext {
|
||||
ITextureSource *tsrc;
|
||||
PreLoadedTextures *texture_pool;
|
||||
video::SColor base_color;
|
||||
const TextureSettings &tsettings;
|
||||
};
|
||||
|
||||
struct ShaderIds {
|
||||
u32 normal;
|
||||
// Shader that will handle an array texture and texture_layer_idx
|
||||
u32 with_layers;
|
||||
};
|
||||
|
||||
static void fillTileAttribs(TileLayer *layer, TileAttribContext context,
|
||||
const TileSpec &tile, const TileDef &tiledef,
|
||||
MaterialType material_type, ShaderIds shader)
|
||||
{
|
||||
auto *tsrc = context.tsrc;
|
||||
const auto &tsettings = context.tsettings;
|
||||
|
||||
std::string texture_image;
|
||||
if (!tiledef.name.empty()) {
|
||||
texture_image = tiledef.name;
|
||||
if (tsrc->needFilterForMesh())
|
||||
texture_image += tsrc->FILTER_FOR_MESH;
|
||||
} else {
|
||||
// Tile is empty, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
core::dimension2du texture_size;
|
||||
if (!texture_image.empty())
|
||||
texture_size = tsrc->getTextureDimensions(texture_image);
|
||||
if (!texture_size.Width || !texture_size.Height)
|
||||
texture_size = {1, 1}; // dummy if there's an error
|
||||
|
||||
// Scale
|
||||
bool has_scale = tiledef.scale > 0;
|
||||
bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
|
||||
(tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
|
||||
if (use_autoscale) {
|
||||
float base_size = tsettings.node_texture_size;
|
||||
float size = std::fmin(texture_size.Width, texture_size.Height);
|
||||
layer->scale = std::fmax(base_size, size) / base_size;
|
||||
} else if (has_scale) {
|
||||
layer->scale = tiledef.scale;
|
||||
} else {
|
||||
layer->scale = 1;
|
||||
}
|
||||
if (!tile.world_aligned)
|
||||
layer->scale = 1;
|
||||
|
||||
// Material
|
||||
layer->material_type = material_type;
|
||||
layer->material_flags = 0;
|
||||
if (tiledef.backface_culling)
|
||||
layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
|
||||
if (tiledef.animation.type != TAT_NONE)
|
||||
layer->material_flags |= MATERIAL_FLAG_ANIMATION;
|
||||
if (tiledef.tileable_horizontal)
|
||||
layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
|
||||
if (tiledef.tileable_vertical)
|
||||
layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
|
||||
|
||||
// Color
|
||||
layer->has_color = tiledef.has_color;
|
||||
if (tiledef.has_color)
|
||||
layer->color = tiledef.color;
|
||||
else
|
||||
layer->color = context.base_color;
|
||||
|
||||
// Animation
|
||||
if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
|
||||
int frame_length_ms = 0;
|
||||
std::vector<FrameSpec> frames = createAnimationFrames(
|
||||
tsrc, tiledef.name, tiledef.animation, frame_length_ms);
|
||||
if (frames.size() > 1) {
|
||||
layer->frames = new std::vector<FrameSpec>(frames);
|
||||
layer->animation_frame_count = layer->frames->size();
|
||||
layer->animation_frame_length_ms = frame_length_ms;
|
||||
|
||||
// Set default texture to first frame (not used practice)
|
||||
layer->texture = (*layer->frames)[0].texture;
|
||||
layer->texture_id = (*layer->frames)[0].texture_id;
|
||||
} else {
|
||||
layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(layer->material_flags & MATERIAL_FLAG_ANIMATION)) {
|
||||
// Grab texture
|
||||
auto tex = context.texture_pool->find(texture_image);
|
||||
if (!tex.texture) {
|
||||
// wasn't pre-loaded: create standard texture on the fly
|
||||
layer->texture = tsrc->getTexture(texture_image, &layer->texture_id);
|
||||
} else {
|
||||
layer->texture = tex.texture;
|
||||
layer->texture_id = tex.texture_id;
|
||||
layer->texture_layer_idx = tex.texture_layer_idx;
|
||||
}
|
||||
}
|
||||
|
||||
// Decide on shader to use
|
||||
if (layer->texture) {
|
||||
layer->shader_id = (layer->texture->getType() == video::ETT_2D_ARRAY) ?
|
||||
shader.with_layers : shader.normal;
|
||||
}
|
||||
}
|
||||
|
||||
void ContentFeatures::preUpdateTextures(ITextureSource *tsrc,
|
||||
std::unordered_set<std::string> &pool, const TextureSettings &tsettings)
|
||||
{
|
||||
// Find out the exact texture strings this node might use, and put them into the pool
|
||||
// (this should match updateTextures, but it's not the end of the world if
|
||||
// a mismatch occurs)
|
||||
std::string append;
|
||||
if (tsrc->needFilterForMesh())
|
||||
append = ITextureSource::FILTER_FOR_MESH;
|
||||
std::string append_overlay = append, append_special = append;
|
||||
bool use = true, use_overlay = true, use_special = true;
|
||||
|
||||
if (drawtype == NDT_ALLFACES_OPTIONAL) {
|
||||
use_special = (tsettings.leaves_style == LEAVES_SIMPLE);
|
||||
use = !use_special;
|
||||
if (tsettings.leaves_style == LEAVES_OPAQUE)
|
||||
append.insert(0, "^[noalpha");
|
||||
}
|
||||
|
||||
const auto &consider_tile = [&] (const TileDef &def, const std::string &append) {
|
||||
// Animations are chopped into frames later, so we won't actually need
|
||||
// the source texture
|
||||
if (!def.name.empty() && def.animation.type == TAT_NONE) {
|
||||
pool.insert(def.name + append);
|
||||
}
|
||||
};
|
||||
|
||||
for (u32 j = 0; j < 6; j++) {
|
||||
if (use)
|
||||
consider_tile(tiledef[j], append);
|
||||
}
|
||||
for (u32 j = 0; j < 6; j++) {
|
||||
if (use_overlay)
|
||||
consider_tile(tiledef_overlay[j], append_overlay);
|
||||
}
|
||||
for (u32 j = 0; j < CF_SPECIAL_COUNT; j++) {
|
||||
if (use_special)
|
||||
consider_tile(tiledef_special[j], append_special);
|
||||
}
|
||||
}
|
||||
|
||||
static bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
|
||||
{
|
||||
if (style == ALIGN_STYLE_WORLD)
|
||||
return true;
|
||||
if (mode == WORLDALIGN_DISABLE)
|
||||
return false;
|
||||
if (style == ALIGN_STYLE_USER_DEFINED)
|
||||
return true;
|
||||
if (drawtype == NDT_NORMAL)
|
||||
return mode >= WORLDALIGN_FORCE;
|
||||
if (drawtype == NDT_NODEBOX)
|
||||
return mode >= WORLDALIGN_FORCE_NODEBOX;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @return maximum number of layers in array textures we can use (0 if unsupported)
|
||||
static size_t getArrayTextureMax(IShaderSource *shdsrc)
|
||||
{
|
||||
auto *driver = RenderingEngine::get_video_driver();
|
||||
// needs to support creating array textures
|
||||
if (!driver->queryFeature(video::EVDF_TEXTURE_2D_ARRAY))
|
||||
return 0;
|
||||
// must support sampling from them
|
||||
if (!shdsrc->supportsSampler2DArray())
|
||||
return 0;
|
||||
// shadow shaders can't handle array textures yet (TODO)
|
||||
if (g_settings->getBool("enable_dynamic_shadows"))
|
||||
return 0;
|
||||
|
||||
u32 n = driver->getLimits().MaxArrayTextureImages;
|
||||
constexpr u32 type_max = std::numeric_limits<decltype(TileLayer::texture_layer_idx)>::max();
|
||||
n = std::min(n, type_max);
|
||||
n = std::min(n, g_settings->getU32("array_texture_max"));
|
||||
return n;
|
||||
}
|
||||
|
||||
void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
|
||||
Client *client, PreLoadedTextures *texture_pool,
|
||||
const TextureSettings &tsettings)
|
||||
{
|
||||
// Figure out the actual tiles to use
|
||||
TileDef tdef[6];
|
||||
for (u32 j = 0; j < 6; j++) {
|
||||
tdef[j] = tiledef[j];
|
||||
if (tdef[j].name.empty()) {
|
||||
tdef[j].name = "no_texture.png";
|
||||
tdef[j].backface_culling = false;
|
||||
}
|
||||
}
|
||||
// also the overlay tiles
|
||||
TileDef tdef_overlay[6];
|
||||
for (u32 j = 0; j < 6; j++)
|
||||
tdef_overlay[j] = tiledef_overlay[j];
|
||||
// also the special tiles
|
||||
TileDef tdef_spec[6];
|
||||
for (u32 j = 0; j < CF_SPECIAL_COUNT; j++) {
|
||||
tdef_spec[j] = tiledef_special[j];
|
||||
}
|
||||
|
||||
bool is_liquid = false;
|
||||
|
||||
MaterialType material_type = alpha_mode_to_material_type(alpha);
|
||||
|
||||
switch (drawtype) {
|
||||
default:
|
||||
case NDT_NORMAL:
|
||||
solidness = 2;
|
||||
break;
|
||||
case NDT_AIRLIKE:
|
||||
solidness = 0;
|
||||
break;
|
||||
case NDT_LIQUID:
|
||||
if (!tsettings.translucent_liquids)
|
||||
alpha = ALPHAMODE_OPAQUE;
|
||||
solidness = 1;
|
||||
is_liquid = true;
|
||||
break;
|
||||
case NDT_FLOWINGLIQUID:
|
||||
solidness = 0;
|
||||
if (!tsettings.translucent_liquids)
|
||||
alpha = ALPHAMODE_OPAQUE;
|
||||
is_liquid = true;
|
||||
break;
|
||||
case NDT_GLASSLIKE:
|
||||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
break;
|
||||
case NDT_GLASSLIKE_FRAMED:
|
||||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
break;
|
||||
case NDT_GLASSLIKE_FRAMED_OPTIONAL:
|
||||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE;
|
||||
break;
|
||||
case NDT_ALLFACES:
|
||||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
break;
|
||||
case NDT_ALLFACES_OPTIONAL:
|
||||
if (tsettings.leaves_style == LEAVES_FANCY) {
|
||||
drawtype = NDT_ALLFACES;
|
||||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
} else if (tsettings.leaves_style == LEAVES_SIMPLE) {
|
||||
for (u32 j = 0; j < 6; j++) {
|
||||
if (!tdef_spec[j].name.empty())
|
||||
tdef[j].name = tdef_spec[j].name;
|
||||
}
|
||||
drawtype = NDT_GLASSLIKE;
|
||||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
} else {
|
||||
if (waving >= 1) {
|
||||
// waving nodes must make faces so there are no gaps
|
||||
drawtype = NDT_ALLFACES;
|
||||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
} else {
|
||||
drawtype = NDT_NORMAL;
|
||||
solidness = 2;
|
||||
}
|
||||
for (TileDef &td : tdef)
|
||||
td.name += std::string("^[noalpha");
|
||||
}
|
||||
if (waving >= 1)
|
||||
material_type = TILE_MATERIAL_WAVING_LEAVES;
|
||||
break;
|
||||
case NDT_PLANTLIKE:
|
||||
solidness = 0;
|
||||
if (waving >= 1)
|
||||
material_type = TILE_MATERIAL_WAVING_PLANTS;
|
||||
break;
|
||||
case NDT_FIRELIKE:
|
||||
solidness = 0;
|
||||
break;
|
||||
case NDT_MESH:
|
||||
case NDT_NODEBOX:
|
||||
solidness = 0;
|
||||
if (waving == 1) {
|
||||
material_type = TILE_MATERIAL_WAVING_PLANTS;
|
||||
} else if (waving == 2) {
|
||||
material_type = TILE_MATERIAL_WAVING_LEAVES;
|
||||
} else if (waving == 3) {
|
||||
material_type = alpha == ALPHAMODE_OPAQUE ?
|
||||
TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
|
||||
TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
|
||||
}
|
||||
break;
|
||||
case NDT_TORCHLIKE:
|
||||
case NDT_SIGNLIKE:
|
||||
case NDT_FENCELIKE:
|
||||
case NDT_RAILLIKE:
|
||||
solidness = 0;
|
||||
break;
|
||||
case NDT_PLANTLIKE_ROOTED:
|
||||
solidness = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_liquid) {
|
||||
if (waving == 3) {
|
||||
material_type = alpha == ALPHAMODE_OPAQUE ?
|
||||
TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
|
||||
TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
|
||||
} else {
|
||||
material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
|
||||
TILE_MATERIAL_LIQUID_TRANSPARENT;
|
||||
}
|
||||
}
|
||||
|
||||
const bool texture_2d_array = getArrayTextureMax(shdsrc) > 1;
|
||||
const auto &getNodeShader = [&] (MaterialType my_material, NodeDrawType my_drawtype) {
|
||||
ShaderIds ret;
|
||||
ret.normal = shdsrc->getShader("nodes_shader", my_material, my_drawtype);
|
||||
// need to avoid generating the shader if unsupported
|
||||
if (texture_2d_array)
|
||||
ret.with_layers = shdsrc->getShader("nodes_shader", my_material, my_drawtype, true);
|
||||
return ret;
|
||||
};
|
||||
|
||||
ShaderIds tile_shader = getNodeShader(material_type, drawtype);
|
||||
|
||||
MaterialType overlay_material = material_type_with_alpha(material_type);
|
||||
|
||||
ShaderIds overlay_shader = getNodeShader(overlay_material, drawtype);
|
||||
|
||||
// minimap pixel color = average color of top tile
|
||||
if (tsettings.enable_minimap && drawtype != NDT_AIRLIKE && !tdef[0].name.empty())
|
||||
{
|
||||
if (!tdef_overlay[0].name.empty()) {
|
||||
// Merge overlay and base texture
|
||||
std::string combined = tdef[0].name + "^(" + tdef_overlay[0].name + ")";
|
||||
minimap_color = tsrc->getTextureAverageColor(combined);
|
||||
} else {
|
||||
minimap_color = tsrc->getTextureAverageColor(tdef[0].name);
|
||||
}
|
||||
}
|
||||
|
||||
// Tiles (fill in f->tiles[])
|
||||
bool any_polygon_offset = false;
|
||||
TileAttribContext tac{tsrc, texture_pool, color, tsettings};
|
||||
|
||||
for (u16 j = 0; j < 6; j++) {
|
||||
tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
|
||||
tsettings.world_aligned_mode, drawtype);
|
||||
fillTileAttribs(&tiles[j].layers[0], tac, tiles[j], tdef[j],
|
||||
material_type, tile_shader);
|
||||
if (!tdef_overlay[j].name.empty()) {
|
||||
tdef_overlay[j].backface_culling = tdef[j].backface_culling;
|
||||
fillTileAttribs(&tiles[j].layers[1], tac, tiles[j], tdef_overlay[j],
|
||||
overlay_material, overlay_shader);
|
||||
}
|
||||
|
||||
tiles[j].layers[0].need_polygon_offset = !tiles[j].layers[1].empty();
|
||||
any_polygon_offset |= tiles[j].layers[0].need_polygon_offset;
|
||||
}
|
||||
|
||||
if (drawtype == NDT_MESH && any_polygon_offset) {
|
||||
// Our per-tile polygon offset enablement workaround works fine for normal
|
||||
// nodes and anything else, where we know that different tiles are different
|
||||
// faces that couldn't possibly conflict with each other.
|
||||
// We can't assume this for mesh nodes, so apply it to all tiles (= materials)
|
||||
// then.
|
||||
for (u16 j = 0; j < 6; j++)
|
||||
tiles[j].layers[0].need_polygon_offset = true;
|
||||
}
|
||||
|
||||
MaterialType special_material = material_type;
|
||||
if (drawtype == NDT_PLANTLIKE_ROOTED) {
|
||||
if (waving == 1)
|
||||
special_material = TILE_MATERIAL_WAVING_PLANTS;
|
||||
else if (waving == 2)
|
||||
special_material = TILE_MATERIAL_WAVING_LEAVES;
|
||||
}
|
||||
|
||||
ShaderIds special_shader = getNodeShader(special_material, drawtype);
|
||||
|
||||
// Special tiles (fill in f->special_tiles[])
|
||||
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
|
||||
fillTileAttribs(&special_tiles[j].layers[0], tac,
|
||||
special_tiles[j], tdef_spec[j], special_material, special_shader);
|
||||
}
|
||||
|
||||
if (param_type_2 == CPT2_COLOR ||
|
||||
param_type_2 == CPT2_COLORED_FACEDIR ||
|
||||
param_type_2 == CPT2_COLORED_4DIR ||
|
||||
param_type_2 == CPT2_COLORED_WALLMOUNTED ||
|
||||
param_type_2 == CPT2_COLORED_DEGROTATE)
|
||||
palette = tsrc->getPalette(palette_name);
|
||||
|
||||
}
|
||||
|
||||
void ContentFeatures::updateMesh(Client *client, const TextureSettings &tsettings)
|
||||
{
|
||||
auto *manip = client->getSceneManager()->getMeshManipulator();
|
||||
(void)tsettings;
|
||||
|
||||
if (drawtype == NDT_MESH && !mesh.empty()) {
|
||||
// Note: By freshly reading, we get an unencumbered mesh.
|
||||
if (scene::IMesh *src_mesh = client->getMesh(mesh)) {
|
||||
bool apply_bs = false;
|
||||
if (auto *skinned_mesh = dynamic_cast<scene::SkinnedMesh *>(src_mesh)) {
|
||||
// Compatibility: Animated meshes, as well as static gltf meshes, are not scaled by BS.
|
||||
// See https://github.com/luanti-org/luanti/pull/16112#issuecomment-2881860329
|
||||
bool is_gltf = skinned_mesh->getSourceFormat() ==
|
||||
scene::SkinnedMesh::SourceFormat::GLTF;
|
||||
apply_bs = skinned_mesh->isStatic() && !is_gltf;
|
||||
// Nodes do not support mesh animation, so we clone the static pose.
|
||||
// This simplifies working with the mesh: We can just scale the vertices
|
||||
// as transformations have already been applied.
|
||||
mesh_ptr = cloneStaticMesh(src_mesh);
|
||||
src_mesh->drop();
|
||||
} else {
|
||||
auto *static_mesh = dynamic_cast<scene::SMesh *>(src_mesh);
|
||||
assert(static_mesh);
|
||||
mesh_ptr = static_mesh;
|
||||
// Compatibility: Apply BS scaling to static meshes (.obj). See #15811.
|
||||
apply_bs = true;
|
||||
}
|
||||
scaleMesh(mesh_ptr, v3f((apply_bs ? BS : 1.0f) * visual_scale));
|
||||
recalculateBoundingBox(mesh_ptr);
|
||||
if (!checkMeshNormals(mesh_ptr)) {
|
||||
// TODO this should be done consistently when the mesh is loaded
|
||||
infostream << "ContentFeatures: recalculating normals for mesh "
|
||||
<< mesh << std::endl;
|
||||
manip->recalculateNormals(mesh_ptr, true, false);
|
||||
}
|
||||
} else {
|
||||
mesh_ptr = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContentFeatures::collectMaterials(std::vector<u32> &leaves_materials)
|
||||
{
|
||||
if (drawtype == NDT_AIRLIKE)
|
||||
return;
|
||||
|
||||
for (u16 j = 0; j < 6; j++) {
|
||||
auto &l = tiles[j].layers;
|
||||
if (!l[0].empty() && l[0].material_type == TILE_MATERIAL_WAVING_LEAVES)
|
||||
leaves_materials.push_back(l[0].shader_id);
|
||||
if (!l[1].empty() && l[1].material_type == TILE_MATERIAL_WAVING_LEAVES)
|
||||
leaves_materials.push_back(l[1].shader_id);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
NodeDefManager
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
NodeDefManager::NodeDefManager()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
NodeDefManager::~NodeDefManager()
|
||||
{
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
for (ContentFeatures &f : m_content_features) {
|
||||
if (f.mesh_ptr)
|
||||
f.mesh_ptr->drop();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void NodeDefManager::clear()
|
||||
{
|
||||
m_content_features.clear();
|
||||
@@ -1648,101 +1096,11 @@ void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &o
|
||||
}
|
||||
}
|
||||
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_args)
|
||||
void NodeDefManager::applyFunction(const std::function<void(ContentFeatures&)> &function)
|
||||
{
|
||||
infostream << "NodeDefManager::updateTextures(): Updating "
|
||||
"textures in node definitions" << std::endl;
|
||||
|
||||
Client *client = (Client *)gamedef;
|
||||
ITextureSource *tsrc = client->tsrc();
|
||||
IShaderSource *shdsrc = client->getShaderSource();
|
||||
TextureSettings tsettings;
|
||||
tsettings.readSettings();
|
||||
|
||||
tsrc->setImageCaching(true);
|
||||
const u32 size = m_content_features.size();
|
||||
|
||||
/* collect all textures we might use */
|
||||
std::unordered_set<std::string> pool;
|
||||
for (u32 i = 0; i < size; i++) {
|
||||
ContentFeatures *f = &(m_content_features[i]);
|
||||
f->preUpdateTextures(tsrc, pool, tsettings);
|
||||
}
|
||||
|
||||
/* texture pre-loading stage */
|
||||
const size_t arraymax = getArrayTextureMax(shdsrc);
|
||||
// Group by size
|
||||
std::unordered_map<v2u32, std::vector<std::string_view>> sizes;
|
||||
if (arraymax > 1) {
|
||||
infostream << "Using array textures with " << arraymax << " layers" << std::endl;
|
||||
size_t i = 0;
|
||||
for (auto &image : pool) {
|
||||
core::dimension2du dim = tsrc->getTextureDimensions(image);
|
||||
client->showUpdateProgressTexture(progress_callback_args,
|
||||
0.33333f * ++i / pool.size());
|
||||
if (!dim.Width || !dim.Height) // error
|
||||
continue;
|
||||
sizes[v2u32(dim)].emplace_back(image);
|
||||
}
|
||||
}
|
||||
|
||||
// create array textures as far as possible
|
||||
size_t num_preloadable = 0, preload_progress = 0;
|
||||
for (auto &it : sizes) {
|
||||
if (it.second.size() < 2)
|
||||
continue;
|
||||
num_preloadable += it.second.size();
|
||||
}
|
||||
PreLoadedTextures plt;
|
||||
const auto &doBunch = [&] (const std::vector<std::string> &bunch) {
|
||||
PreLoadedTexture t;
|
||||
t.texture = tsrc->addArrayTexture(bunch, &t.texture_id);
|
||||
preload_progress += bunch.size();
|
||||
client->showUpdateProgressTexture(progress_callback_args,
|
||||
0.33333f + 0.33333f * preload_progress / num_preloadable);
|
||||
if (t.texture) {
|
||||
// Success: all of the images in this bunch can now refer to this texture
|
||||
for (size_t idx = 0; idx < bunch.size(); idx++) {
|
||||
t.texture_layer_idx = idx;
|
||||
plt.add(bunch[idx], t);
|
||||
}
|
||||
}
|
||||
};
|
||||
for (auto &it : sizes) {
|
||||
if (it.second.size() < 2)
|
||||
continue;
|
||||
std::vector<std::string> bunch;
|
||||
for (auto &image : it.second) {
|
||||
bunch.emplace_back(image);
|
||||
if (bunch.size() == arraymax) {
|
||||
doBunch(bunch);
|
||||
bunch.clear();
|
||||
}
|
||||
}
|
||||
if (!bunch.empty())
|
||||
doBunch(bunch);
|
||||
}
|
||||
// note that standard textures aren't preloaded
|
||||
|
||||
/* final step */
|
||||
for (u32 i = 0; i < size; i++) {
|
||||
ContentFeatures *f = &(m_content_features[i]);
|
||||
f->updateTextures(tsrc, shdsrc, client, &plt, tsettings);
|
||||
f->updateMesh(client, tsettings);
|
||||
f->collectMaterials(m_leaves_materials);
|
||||
|
||||
client->showUpdateProgressTexture(progress_callback_args,
|
||||
0.66666f + 0.33333f * i / size);
|
||||
}
|
||||
SORT_AND_UNIQUE(m_leaves_materials);
|
||||
verbosestream << "m_leaves_materials.size() = " << m_leaves_materials.size()
|
||||
<< std::endl;
|
||||
|
||||
plt.printStats(infostream);
|
||||
tsrc->setImageCaching(false);
|
||||
for (ContentFeatures &f : m_content_features)
|
||||
function(f);
|
||||
}
|
||||
#endif
|
||||
|
||||
void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
|
||||
{
|
||||
|
||||
@@ -4,23 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "irrlichttypes_bloated.h"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <memory> // shared_ptr
|
||||
#include <map>
|
||||
#include "mapnode.h"
|
||||
#include "nameidmapping.h"
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
#include "client/tile.h"
|
||||
#include <IMeshManipulator.h>
|
||||
#include <unordered_set>
|
||||
class Client;
|
||||
struct PreLoadedTextures;
|
||||
#endif
|
||||
#include "itemgroup.h"
|
||||
#include "sound.h" // SoundSpec
|
||||
#include "constants.h" // BS
|
||||
#include "texture_override.h" // TextureOverride
|
||||
#include "tileanimation.h"
|
||||
#include "util/pointabilities.h"
|
||||
@@ -33,6 +24,9 @@ class NodeResolver;
|
||||
#if BUILD_UNITTESTS
|
||||
class TestSchematic;
|
||||
#endif
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
struct NodeVisuals;
|
||||
#endif
|
||||
|
||||
enum ContentParamType : u8
|
||||
{
|
||||
@@ -261,25 +255,6 @@ enum AlphaMode : u8 {
|
||||
AlphaMode_END // Dummy for validity check
|
||||
};
|
||||
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
/**
|
||||
* @brief get fitting material type for an alpha mode
|
||||
*/
|
||||
static inline MaterialType alpha_mode_to_material_type(AlphaMode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case ALPHAMODE_BLEND:
|
||||
return TILE_MATERIAL_ALPHA;
|
||||
case ALPHAMODE_OPAQUE:
|
||||
return TILE_MATERIAL_OPAQUE;
|
||||
case ALPHAMODE_CLIP:
|
||||
default:
|
||||
return TILE_MATERIAL_BASIC;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
Stand-alone definition of a TileSpec (basically a server-side TileSpec)
|
||||
*/
|
||||
@@ -321,20 +296,6 @@ struct ContentFeatures
|
||||
// write checks that depend directly on the protocol version instead.
|
||||
static const u8 CONTENTFEATURES_VERSION = 13;
|
||||
|
||||
/*
|
||||
Cached stuff
|
||||
*/
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
// 0 1 2 3 4 5
|
||||
// up down right left back front
|
||||
TileSpec tiles[6];
|
||||
// Special tiles
|
||||
TileSpec special_tiles[CF_SPECIAL_COUNT];
|
||||
u8 solidness; // Used when choosing which face is drawn
|
||||
u8 visual_solidness; // When solidness=0, this tells how it looks like
|
||||
bool backface_culling;
|
||||
#endif
|
||||
|
||||
// Server-side cached callback existence for fast skipping
|
||||
bool has_on_construct;
|
||||
bool has_on_destruct;
|
||||
@@ -360,10 +321,6 @@ struct ContentFeatures
|
||||
|
||||
enum NodeDrawType drawtype;
|
||||
std::string mesh;
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
scene::SMesh *mesh_ptr; // mesh in case of mesh node
|
||||
video::SColor minimap_color;
|
||||
#endif
|
||||
float visual_scale; // Misc. scale parameter
|
||||
TileDef tiledef[6];
|
||||
// These will be drawn over the base tiles.
|
||||
@@ -373,7 +330,6 @@ struct ContentFeatures
|
||||
// The color of the node.
|
||||
video::SColor color;
|
||||
std::string palette_name;
|
||||
std::vector<video::SColor> *palette;
|
||||
// Used for waving leaves/plants
|
||||
u8 waving;
|
||||
// for NDT_CONNECTED pairing
|
||||
@@ -388,6 +344,14 @@ struct ContentFeatures
|
||||
// Maximum value for leveled nodes
|
||||
u8 leveled_max;
|
||||
|
||||
// --- CLIENT ONLY ---
|
||||
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
// The Client class fills this for its NodeDefManager using fillNodeVisuals,
|
||||
// thus for ContentFeatures of a Client it is not a nullptr.
|
||||
NodeVisuals *visuals = nullptr;
|
||||
#endif
|
||||
|
||||
// --- LIGHTING-RELATED ---
|
||||
|
||||
bool light_propagates;
|
||||
@@ -524,16 +488,6 @@ struct ContentFeatures
|
||||
return itemgroup_get(groups, group);
|
||||
}
|
||||
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
void preUpdateTextures(ITextureSource *tsrc,
|
||||
std::unordered_set<std::string> &pool, const TextureSettings &tsettings);
|
||||
void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
|
||||
Client *client, PreLoadedTextures *texture_pool,
|
||||
const TextureSettings &tsettings);
|
||||
void updateMesh(Client *client, const TextureSettings &tsettings);
|
||||
void collectMaterials(std::vector<u32> &leaves_materials);
|
||||
#endif
|
||||
|
||||
private:
|
||||
void setAlphaFromLegacy(u8 legacy_alpha);
|
||||
|
||||
@@ -559,7 +513,6 @@ public:
|
||||
* \ref CONTENT_AIR, \ref CONTENT_UNKNOWN and \ref CONTENT_IGNORE.
|
||||
*/
|
||||
NodeDefManager();
|
||||
~NodeDefManager();
|
||||
|
||||
/*!
|
||||
* Returns the properties for the given content type.
|
||||
@@ -700,18 +653,21 @@ public:
|
||||
*/
|
||||
void applyTextureOverrides(const std::vector<TextureOverride> &overrides);
|
||||
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
/*!
|
||||
* Loads textures and shaders required for
|
||||
* rendering the nodes.
|
||||
* @param gamedef must be a Client.
|
||||
* @param progress_cbk called each time a node is loaded. Arguments:
|
||||
* `progress_cbk_args`, number of loaded ContentFeatures, number of
|
||||
* total ContentFeatures.
|
||||
* @param progress_cbk_args passed to the callback function
|
||||
* Applies a function to all Content Features.
|
||||
* Clients need this to make use of the visuals field.
|
||||
* @param function to apply
|
||||
*/
|
||||
void updateTextures(IGameDef *gamedef, void *progress_cbk_args);
|
||||
#endif
|
||||
void applyFunction(const std::function<void(ContentFeatures&)> &function);
|
||||
|
||||
/*!
|
||||
* Returns the amount of managed content IDs.
|
||||
* Invalid and removed IDs are also counted.
|
||||
*/
|
||||
inline u32 size() const
|
||||
{
|
||||
return m_content_features.size();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Writes the content of this manager to the given output stream.
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
#include <json/json.h>
|
||||
#include "mapgen/treegen.h"
|
||||
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
#include "client/node_visuals.h"
|
||||
#endif
|
||||
|
||||
struct EnumString es_TileAnimationType[] =
|
||||
{
|
||||
{TAT_NONE, "none"},
|
||||
@@ -1079,8 +1083,10 @@ void push_content_features(lua_State *L, const ContentFeatures &c)
|
||||
lua_setfield(L, -2, "mesh");
|
||||
}
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
push_ARGB8(L, c.minimap_color); // I know this is not set-able w/ register_node,
|
||||
lua_setfield(L, -2, "minimap_color"); // but the people need to know!
|
||||
if (c.visuals) {
|
||||
push_ARGB8(L, c.visuals->minimap_color); // I know this is not set-able w/ register_node,
|
||||
lua_setfield(L, -2, "minimap_color"); // but the people need to know!
|
||||
}
|
||||
#endif
|
||||
lua_pushnumber(L, c.visual_scale);
|
||||
lua_setfield(L, -2, "visual_scale");
|
||||
@@ -1093,8 +1099,12 @@ void push_content_features(lua_State *L, const ContentFeatures &c)
|
||||
lua_pushstring(L, c.palette_name.c_str());
|
||||
lua_setfield(L, -2, "palette_name");
|
||||
|
||||
push_palette(L, c.palette);
|
||||
lua_setfield(L, -2, "palette");
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
if (c.visuals) {
|
||||
push_palette(L, c.visuals->palette);
|
||||
lua_setfield(L, -2, "palette");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
lua_pushnumber(L, c.waving);
|
||||
lua_setfield(L, -2, "waving");
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "client/content_mapblock.h"
|
||||
#include "client/mapblock_mesh.h"
|
||||
#include "client/meshgen/collector.h"
|
||||
#include "client/node_visuals.h"
|
||||
#include "mesh_compare.h"
|
||||
|
||||
namespace {
|
||||
@@ -27,13 +28,27 @@ public:
|
||||
return const_cast<NodeDefManager *>(m_nodedef);
|
||||
}
|
||||
|
||||
content_t registerNode(ItemDefinition itemdef, ContentFeatures nodedef) {
|
||||
// ContentFeatures that doesn't destroy the visuals
|
||||
// Needed because nodedef.set(feature) creates a copy of the ContentFeatures and since
|
||||
// the NodeDefManager destructs its ContentFeatures, this prevents double free.
|
||||
// Should only be used if the visuals get freed somewhere else.
|
||||
struct CContentFeatures : public ContentFeatures {
|
||||
~CContentFeatures() { visuals = nullptr; }
|
||||
};
|
||||
|
||||
content_t registerNode(ItemDefinition itemdef, const CContentFeatures &nodedef) {
|
||||
item_mgr()->registerItem(itemdef);
|
||||
return node_mgr()->set(nodedef.name, nodedef);
|
||||
}
|
||||
|
||||
void finalize() {
|
||||
node_mgr()->resolveCrossrefs();
|
||||
|
||||
// Need to fill node visuals for predefined nodes
|
||||
node_mgr()->applyFunction([] (ContentFeatures &f) {
|
||||
if (!f.visuals)
|
||||
f.visuals = constructNodeVisuals(&f);
|
||||
});
|
||||
}
|
||||
|
||||
MeshMakeData makeSingleNodeMMD(bool smooth_lighting = true)
|
||||
@@ -57,14 +72,15 @@ public:
|
||||
itemdef.name = "test:" + name;
|
||||
itemdef.description = name;
|
||||
|
||||
ContentFeatures f;
|
||||
CContentFeatures f;
|
||||
f.visuals = constructNodeVisuals(&f);
|
||||
f.name = itemdef.name;
|
||||
f.drawtype = NDT_NORMAL;
|
||||
f.solidness = 2;
|
||||
f.visuals->solidness = 2;
|
||||
f.alpha = ALPHAMODE_OPAQUE;
|
||||
for (TileDef &tiledef : f.tiledef)
|
||||
tiledef.name = name + ".png";
|
||||
for (TileSpec &tile : f.tiles)
|
||||
for (TileSpec &tile : f.visuals->tiles)
|
||||
tile.layers[0].texture_id = texture;
|
||||
|
||||
return registerNode(itemdef, f);
|
||||
@@ -77,10 +93,11 @@ public:
|
||||
itemdef.name = "test:" + name + "_source";
|
||||
itemdef.description = name;
|
||||
|
||||
ContentFeatures f;
|
||||
CContentFeatures f;
|
||||
f.visuals = constructNodeVisuals(&f);
|
||||
f.name = itemdef.name;
|
||||
f.drawtype = NDT_LIQUID;
|
||||
f.solidness = 1;
|
||||
f.visuals->solidness = 1;
|
||||
f.alpha = ALPHAMODE_BLEND;
|
||||
f.light_propagates = true;
|
||||
f.param_type = CPT_LIGHT;
|
||||
@@ -91,7 +108,7 @@ public:
|
||||
f.liquid_alternative_flowing = "test:" + name + "_flowing";
|
||||
for (TileDef &tiledef : f.tiledef)
|
||||
tiledef.name = name + ".png";
|
||||
for (TileSpec &tile : f.tiles)
|
||||
for (TileSpec &tile : f.visuals->tiles)
|
||||
tile.layers[0].texture_id = texture;
|
||||
|
||||
return registerNode(itemdef, f);
|
||||
@@ -104,10 +121,11 @@ public:
|
||||
itemdef.name = "test:" + name + "_flowing";
|
||||
itemdef.description = name;
|
||||
|
||||
ContentFeatures f;
|
||||
CContentFeatures f;
|
||||
f.visuals = constructNodeVisuals(&f);
|
||||
f.name = itemdef.name;
|
||||
f.drawtype = NDT_FLOWINGLIQUID;
|
||||
f.solidness = 0;
|
||||
f.visuals->solidness = 0;
|
||||
f.alpha = ALPHAMODE_BLEND;
|
||||
f.light_propagates = true;
|
||||
f.param_type = CPT_LIGHT;
|
||||
@@ -118,8 +136,8 @@ public:
|
||||
f.liquid_alternative_flowing = "test:" + name + "_flowing";
|
||||
f.tiledef_special[0].name = name + "_top.png";
|
||||
f.tiledef_special[1].name = name + "_side.png";
|
||||
f.special_tiles[0].layers[0].texture_id = texture_top;
|
||||
f.special_tiles[1].layers[0].texture_id = texture_side;
|
||||
f.visuals->special_tiles[0].layers[0].texture_id = texture_top;
|
||||
f.visuals->special_tiles[1].layers[0].texture_id = texture_side;
|
||||
|
||||
return registerNode(itemdef, f);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user