mirror of
https://github.com/luanti-org/luanti.git
synced 2025-12-19 21:35:46 +01:00
Introduce array textures for node rendering (#16574)
This commit is contained in:
@@ -2057,6 +2057,10 @@ world_aligned_mode (World-aligned textures mode) enum enable disable,enable,forc
|
|||||||
# Warning: This option is EXPERIMENTAL!
|
# Warning: This option is EXPERIMENTAL!
|
||||||
autoscale_mode (Autoscaling mode) enum disable disable,enable,force
|
autoscale_mode (Autoscaling mode) enum disable disable,enable,force
|
||||||
|
|
||||||
|
# Caps the maximum number of layers used with array textures (if supported).
|
||||||
|
# This is only useful for debugging.
|
||||||
|
array_texture_max (Array texture max layers) int 65535 0 65535
|
||||||
|
|
||||||
# When using bilinear/trilinear filtering, low-resolution textures
|
# When using bilinear/trilinear filtering, low-resolution textures
|
||||||
# can be blurred, so this option automatically upscales them to preserve
|
# can be blurred, so this option automatically upscales them to preserve
|
||||||
# crisp pixels. This defines the minimum texture size for the upscaled textures;
|
# crisp pixels. This defines the minimum texture size for the upscaled textures;
|
||||||
|
|||||||
43
client/shaders/inventory_shader/opengl_fragment.glsl
Normal file
43
client/shaders/inventory_shader/opengl_fragment.glsl
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#ifdef USE_ARRAY_TEXTURE
|
||||||
|
uniform sampler2DArray baseTexture;
|
||||||
|
#else
|
||||||
|
uniform sampler2D baseTexture;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
varying vec3 vNormal;
|
||||||
|
varying vec3 vPosition;
|
||||||
|
#ifdef GL_ES
|
||||||
|
varying lowp vec4 varColor;
|
||||||
|
varying mediump vec2 varTexCoord;
|
||||||
|
varying float varTexLayer;
|
||||||
|
#else
|
||||||
|
centroid varying vec4 varColor;
|
||||||
|
centroid varying vec2 varTexCoord;
|
||||||
|
centroid varying float varTexLayer; // actually int
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
vec2 uv = varTexCoord.st;
|
||||||
|
|
||||||
|
#ifdef USE_ARRAY_TEXTURE
|
||||||
|
vec4 base = texture(baseTexture, vec3(uv, varTexLayer)).rgba;
|
||||||
|
#else
|
||||||
|
vec4 base = texture2D(baseTexture, uv).rgba;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Handle transparency by discarding pixel as appropriate.
|
||||||
|
#ifdef USE_DISCARD
|
||||||
|
if (base.a == 0.0)
|
||||||
|
discard;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DISCARD_REF
|
||||||
|
if (base.a < 0.5)
|
||||||
|
discard;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vec4 col = vec4(base.rgb * varColor.rgb, base.a);
|
||||||
|
|
||||||
|
gl_FragColor = col;
|
||||||
|
}
|
||||||
27
client/shaders/inventory_shader/opengl_vertex.glsl
Normal file
27
client/shaders/inventory_shader/opengl_vertex.glsl
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
varying vec3 vNormal;
|
||||||
|
varying vec3 vPosition;
|
||||||
|
#ifdef GL_ES
|
||||||
|
varying lowp vec4 varColor;
|
||||||
|
varying mediump vec2 varTexCoord;
|
||||||
|
varying float varTexLayer;
|
||||||
|
#else
|
||||||
|
centroid varying vec4 varColor;
|
||||||
|
centroid varying vec2 varTexCoord;
|
||||||
|
centroid varying float varTexLayer; // actually int
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
#ifdef USE_ARRAY_TEXTURE
|
||||||
|
varTexLayer = inVertexAux;
|
||||||
|
#endif
|
||||||
|
varTexCoord = inTexCoord0.st;
|
||||||
|
|
||||||
|
vec4 pos = inVertexPosition;
|
||||||
|
gl_Position = mWorldViewProj * pos;
|
||||||
|
vPosition = gl_Position.xyz;
|
||||||
|
vNormal = inVertexNormal;
|
||||||
|
|
||||||
|
vec4 color = inVertexColor;
|
||||||
|
varColor = clamp(color, 0.0, 1.0);
|
||||||
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
uniform sampler2D baseTexture;
|
#ifdef USE_ARRAY_TEXTURE
|
||||||
|
uniform sampler2DArray baseTexture;
|
||||||
|
#else
|
||||||
|
uniform sampler2D baseTexture;
|
||||||
|
#endif
|
||||||
#define crackTexture texture1
|
#define crackTexture texture1
|
||||||
uniform sampler2D crackTexture;
|
uniform sampler2D crackTexture;
|
||||||
|
|
||||||
@@ -48,10 +52,12 @@ varying vec3 worldPosition;
|
|||||||
#ifdef GL_ES
|
#ifdef GL_ES
|
||||||
varying lowp vec4 varColor;
|
varying lowp vec4 varColor;
|
||||||
varying mediump vec2 varTexCoord;
|
varying mediump vec2 varTexCoord;
|
||||||
|
varying float varTexLayer;
|
||||||
varying float nightRatio;
|
varying float nightRatio;
|
||||||
#else
|
#else
|
||||||
centroid varying lowp vec4 varColor;
|
centroid varying lowp vec4 varColor;
|
||||||
centroid varying vec2 varTexCoord;
|
centroid varying vec2 varTexCoord;
|
||||||
|
centroid varying float varTexLayer; // actually int
|
||||||
centroid varying float nightRatio;
|
centroid varying float nightRatio;
|
||||||
#endif
|
#endif
|
||||||
varying highp vec3 eyeVec;
|
varying highp vec3 eyeVec;
|
||||||
@@ -431,10 +437,13 @@ void main(void)
|
|||||||
{
|
{
|
||||||
vec2 uv = varTexCoord.st;
|
vec2 uv = varTexCoord.st;
|
||||||
|
|
||||||
|
#ifdef USE_ARRAY_TEXTURE
|
||||||
|
vec4 base = texture(baseTexture, vec3(uv, varTexLayer)).rgba;
|
||||||
|
#else
|
||||||
vec4 base = texture2D(baseTexture, uv).rgba;
|
vec4 base = texture2D(baseTexture, uv).rgba;
|
||||||
// If alpha is zero, we can just discard the pixel. This fixes transparency
|
#endif
|
||||||
// on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa,
|
|
||||||
// and also on GLES 2, where GL_ALPHA_TEST is missing entirely.
|
// Handle transparency by discarding pixel as appropriate.
|
||||||
#ifdef USE_DISCARD
|
#ifdef USE_DISCARD
|
||||||
if (base.a == 0.0)
|
if (base.a == 0.0)
|
||||||
discard;
|
discard;
|
||||||
|
|||||||
@@ -20,10 +20,12 @@ varying vec3 worldPosition;
|
|||||||
#ifdef GL_ES
|
#ifdef GL_ES
|
||||||
varying lowp vec4 varColor;
|
varying lowp vec4 varColor;
|
||||||
varying mediump vec2 varTexCoord;
|
varying mediump vec2 varTexCoord;
|
||||||
|
varying float varTexLayer;
|
||||||
varying float nightRatio;
|
varying float nightRatio;
|
||||||
#else
|
#else
|
||||||
centroid varying lowp vec4 varColor;
|
centroid varying vec4 varColor;
|
||||||
centroid varying vec2 varTexCoord;
|
centroid varying vec2 varTexCoord;
|
||||||
|
centroid varying float varTexLayer; // actually int
|
||||||
centroid varying float nightRatio;
|
centroid varying float nightRatio;
|
||||||
#endif
|
#endif
|
||||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||||
@@ -149,6 +151,9 @@ float snoise(vec3 p)
|
|||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
|
#ifdef USE_ARRAY_TEXTURE
|
||||||
|
varTexLayer = inVertexAux;
|
||||||
|
#endif
|
||||||
varTexCoord = inTexCoord0.st;
|
varTexCoord = inTexCoord0.st;
|
||||||
|
|
||||||
float disp_x;
|
float disp_x;
|
||||||
@@ -181,6 +186,7 @@ void main(void)
|
|||||||
pos.y += disp_z * 0.1;
|
pos.y += disp_z * 0.1;
|
||||||
pos.z += disp_z;
|
pos.z += disp_z;
|
||||||
#elif MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS && ENABLE_WAVING_PLANTS
|
#elif MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS && ENABLE_WAVING_PLANTS
|
||||||
|
// bottom of plant doesn't wave
|
||||||
if (varTexCoord.y < 0.05) {
|
if (varTexCoord.y < 0.05) {
|
||||||
pos.x += disp_x;
|
pos.x += disp_x;
|
||||||
pos.z += disp_z;
|
pos.z += disp_z;
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
uniform sampler2D baseTexture;
|
#ifdef USE_ARRAY_TEXTURE
|
||||||
|
uniform sampler2DArray baseTexture;
|
||||||
|
#else
|
||||||
|
uniform sampler2D baseTexture;
|
||||||
|
#endif
|
||||||
|
|
||||||
uniform vec3 dayLight;
|
uniform vec3 dayLight;
|
||||||
uniform lowp vec4 fogColor;
|
uniform lowp vec4 fogColor;
|
||||||
@@ -41,8 +45,10 @@ varying vec3 worldPosition;
|
|||||||
varying lowp vec4 varColor;
|
varying lowp vec4 varColor;
|
||||||
#ifdef GL_ES
|
#ifdef GL_ES
|
||||||
varying mediump vec2 varTexCoord;
|
varying mediump vec2 varTexCoord;
|
||||||
|
varying float varTexLayer;
|
||||||
#else
|
#else
|
||||||
centroid varying vec2 varTexCoord;
|
centroid varying vec2 varTexCoord;
|
||||||
|
centroid varying float varTexLayer; // actually int
|
||||||
#endif
|
#endif
|
||||||
varying highp vec3 eyeVec;
|
varying highp vec3 eyeVec;
|
||||||
varying float nightRatio;
|
varying float nightRatio;
|
||||||
@@ -365,10 +371,13 @@ void main(void)
|
|||||||
vec3 color;
|
vec3 color;
|
||||||
vec2 uv = varTexCoord.st;
|
vec2 uv = varTexCoord.st;
|
||||||
|
|
||||||
|
#ifdef USE_ARRAY_TEXTURE
|
||||||
|
vec4 base = texture(baseTexture, vec3(uv, varTexLayer)).rgba;
|
||||||
|
#else
|
||||||
vec4 base = texture2D(baseTexture, uv).rgba;
|
vec4 base = texture2D(baseTexture, uv).rgba;
|
||||||
// If alpha is zero, we can just discard the pixel. This fixes transparency
|
#endif
|
||||||
// on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa,
|
|
||||||
// and also on GLES 2, where GL_ALPHA_TEST is missing entirely.
|
// Handle transparency by discarding pixel as appropriate.
|
||||||
#ifdef USE_DISCARD
|
#ifdef USE_DISCARD
|
||||||
if (base.a == 0.0)
|
if (base.a == 0.0)
|
||||||
discard;
|
discard;
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ varying vec3 worldPosition;
|
|||||||
varying lowp vec4 varColor;
|
varying lowp vec4 varColor;
|
||||||
#ifdef GL_ES
|
#ifdef GL_ES
|
||||||
varying mediump vec2 varTexCoord;
|
varying mediump vec2 varTexCoord;
|
||||||
|
varying float varTexLayer;
|
||||||
#else
|
#else
|
||||||
centroid varying vec2 varTexCoord;
|
centroid varying vec2 varTexCoord;
|
||||||
|
centroid varying float varTexLayer; // actually int
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_DYNAMIC_SHADOWS
|
#ifdef ENABLE_DYNAMIC_SHADOWS
|
||||||
@@ -91,7 +93,11 @@ float directional_ambient(vec3 normal)
|
|||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
|
#ifdef USE_ARRAY_TEXTURE
|
||||||
|
varTexLayer = inVertexAux;
|
||||||
|
#endif
|
||||||
varTexCoord = (mTexture * vec4(inTexCoord0.xy, 1.0, 1.0)).st;
|
varTexCoord = (mTexture * vec4(inTexCoord0.xy, 1.0, 1.0)).st;
|
||||||
|
|
||||||
gl_Position = mWorldViewProj * inVertexPosition;
|
gl_Position = mWorldViewProj * inVertexPosition;
|
||||||
|
|
||||||
vPosition = gl_Position.xyz;
|
vPosition = gl_Position.xyz;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIXME missing array texture handling
|
||||||
uniform sampler2D ColorMapSampler;
|
uniform sampler2D ColorMapSampler;
|
||||||
varying vec4 tPos;
|
varying vec4 tPos;
|
||||||
|
|
||||||
@@ -10,7 +11,7 @@ centroid varying vec2 varTexCoord;
|
|||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec4 col = texture2D(ColorMapSampler, varTexCoord);
|
vec4 col = texture2D(ColorMapSampler, varTexCoord);
|
||||||
|
// FIXME: magic number???
|
||||||
if (col.a < 0.70)
|
if (col.a < 0.70)
|
||||||
discard;
|
discard;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIXME missing array texture handling
|
||||||
uniform sampler2D ColorMapSampler;
|
uniform sampler2D ColorMapSampler;
|
||||||
varying vec4 tPos;
|
varying vec4 tPos;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ enum E_VERTEX_ATTRIBUTES
|
|||||||
EVA_POSITION = 0,
|
EVA_POSITION = 0,
|
||||||
EVA_NORMAL,
|
EVA_NORMAL,
|
||||||
EVA_COLOR,
|
EVA_COLOR,
|
||||||
|
EVA_AUX,
|
||||||
EVA_TCOORD0,
|
EVA_TCOORD0,
|
||||||
EVA_TCOORD1,
|
EVA_TCOORD1,
|
||||||
EVA_TANGENT,
|
EVA_TANGENT,
|
||||||
@@ -21,6 +22,7 @@ const char *const sBuiltInVertexAttributeNames[] = {
|
|||||||
"inVertexPosition",
|
"inVertexPosition",
|
||||||
"inVertexNormal",
|
"inVertexNormal",
|
||||||
"inVertexColor",
|
"inVertexColor",
|
||||||
|
"inVertexAux",
|
||||||
"inTexCoord0",
|
"inTexCoord0",
|
||||||
"inTexCoord1",
|
"inTexCoord1",
|
||||||
"inVertexTangent",
|
"inVertexTangent",
|
||||||
|
|||||||
@@ -41,17 +41,17 @@ struct S3DVertex
|
|||||||
{
|
{
|
||||||
//! default constructor
|
//! default constructor
|
||||||
constexpr S3DVertex() :
|
constexpr S3DVertex() :
|
||||||
Color(0xffffffff) {}
|
Color(0xffffffff), Aux(0) {}
|
||||||
|
|
||||||
//! constructor
|
//! constructor
|
||||||
constexpr S3DVertex(f32 x, f32 y, f32 z, f32 nx, f32 ny, f32 nz, SColor c, f32 tu, f32 tv) :
|
constexpr S3DVertex(f32 x, f32 y, f32 z, f32 nx, f32 ny, f32 nz, SColor c, f32 tu, f32 tv, u16 a = 0) :
|
||||||
Pos(x, y, z), Normal(nx, ny, nz), Color(c), TCoords(tu, tv) {}
|
Pos(x, y, z), Normal(nx, ny, nz), Color(c), TCoords(tu, tv), Aux(a) {}
|
||||||
|
|
||||||
//! constructor
|
//! constructor
|
||||||
constexpr S3DVertex(const core::vector3df &pos, const core::vector3df &normal,
|
constexpr S3DVertex(const core::vector3df &pos, const core::vector3df &normal,
|
||||||
SColor color, const core::vector2df &tcoords) :
|
SColor color, const core::vector2df &tcoords, u16 aux = 0) :
|
||||||
Pos(pos),
|
Pos(pos),
|
||||||
Normal(normal), Color(color), TCoords(tcoords) {}
|
Normal(normal), Color(color), TCoords(tcoords), Aux(aux) {}
|
||||||
|
|
||||||
//! Position
|
//! Position
|
||||||
core::vector3df Pos;
|
core::vector3df Pos;
|
||||||
@@ -65,24 +65,42 @@ struct S3DVertex
|
|||||||
//! Texture coordinates
|
//! Texture coordinates
|
||||||
core::vector2df TCoords;
|
core::vector2df TCoords;
|
||||||
|
|
||||||
|
//! Auxiliary value (free to use)
|
||||||
|
u16 Aux;
|
||||||
|
|
||||||
constexpr bool operator==(const S3DVertex &other) const
|
constexpr bool operator==(const S3DVertex &other) const
|
||||||
{
|
{
|
||||||
return ((Pos == other.Pos) && (Normal == other.Normal) &&
|
return ((Pos == other.Pos) && (Normal == other.Normal) &&
|
||||||
(Color == other.Color) && (TCoords == other.TCoords));
|
(Color == other.Color) && (TCoords == other.TCoords) &&
|
||||||
|
(Aux == other.Aux));
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool operator!=(const S3DVertex &other) const
|
constexpr bool operator!=(const S3DVertex &other) const
|
||||||
{
|
{
|
||||||
return ((Pos != other.Pos) || (Normal != other.Normal) ||
|
return ((Pos != other.Pos) || (Normal != other.Normal) ||
|
||||||
(Color != other.Color) || (TCoords != other.TCoords));
|
(Color != other.Color) || (TCoords != other.TCoords) ||
|
||||||
|
(Aux != other.Aux));
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool operator<(const S3DVertex &other) const
|
constexpr bool operator<(const S3DVertex &other) const
|
||||||
{
|
{
|
||||||
return ((Pos < other.Pos) ||
|
if (Pos < other.Pos)
|
||||||
((Pos == other.Pos) && (Normal < other.Normal)) ||
|
return true;
|
||||||
((Pos == other.Pos) && (Normal == other.Normal) && (Color < other.Color)) ||
|
if (Pos != other.Pos)
|
||||||
((Pos == other.Pos) && (Normal == other.Normal) && (Color == other.Color) && (TCoords < other.TCoords)));
|
return false;
|
||||||
|
if (Normal < other.Normal)
|
||||||
|
return true;
|
||||||
|
if (Normal != other.Normal)
|
||||||
|
return false;
|
||||||
|
if (Color < other.Color)
|
||||||
|
return true;
|
||||||
|
if (Color != other.Color)
|
||||||
|
return false;
|
||||||
|
if (TCoords < other.TCoords)
|
||||||
|
return true;
|
||||||
|
if (TCoords != other.TCoords)
|
||||||
|
return false;
|
||||||
|
return Aux < other.Aux;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Get type of the class
|
//! Get type of the class
|
||||||
@@ -98,10 +116,13 @@ struct S3DVertex
|
|||||||
return S3DVertex(Pos.getInterpolated(other.Pos, d),
|
return S3DVertex(Pos.getInterpolated(other.Pos, d),
|
||||||
Normal.getInterpolated(other.Normal, d),
|
Normal.getInterpolated(other.Normal, d),
|
||||||
Color.getInterpolated(other.Color, d),
|
Color.getInterpolated(other.Color, d),
|
||||||
TCoords.getInterpolated(other.TCoords, d));
|
TCoords.getInterpolated(other.TCoords, d),
|
||||||
|
d == 0.0f ? other.Aux : Aux);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// FIXME: the following types don't handle `Aux`, but we don't use them in situations where it's relevant.
|
||||||
|
|
||||||
//! Vertex with two texture coordinates.
|
//! Vertex with two texture coordinates.
|
||||||
/** Usually used for geometry with lightmaps
|
/** Usually used for geometry with lightmaps
|
||||||
or other special materials.
|
or other special materials.
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ static const VertexType vtStandard = {
|
|||||||
{EVA_NORMAL, 3, GL_FLOAT, VertexAttribute::Mode::Regular, offsetof(S3DVertex, Normal)},
|
{EVA_NORMAL, 3, GL_FLOAT, VertexAttribute::Mode::Regular, offsetof(S3DVertex, Normal)},
|
||||||
{EVA_COLOR, 4, GL_UNSIGNED_BYTE, VertexAttribute::Mode::Normalized, offsetof(S3DVertex, Color)},
|
{EVA_COLOR, 4, GL_UNSIGNED_BYTE, VertexAttribute::Mode::Normalized, offsetof(S3DVertex, Color)},
|
||||||
{EVA_TCOORD0, 2, GL_FLOAT, VertexAttribute::Mode::Regular, offsetof(S3DVertex, TCoords)},
|
{EVA_TCOORD0, 2, GL_FLOAT, VertexAttribute::Mode::Regular, offsetof(S3DVertex, TCoords)},
|
||||||
|
{EVA_AUX, 1, GL_UNSIGNED_SHORT, VertexAttribute::Mode::Regular, offsetof(S3DVertex, Aux)},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -67,6 +68,7 @@ static const VertexType vtStandard = {
|
|||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||||
|
|
||||||
|
|
||||||
static const VertexType vt2TCoords = {
|
static const VertexType vt2TCoords = {
|
||||||
sizeof(S3DVertex2TCoords),
|
sizeof(S3DVertex2TCoords),
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1799,10 +1799,10 @@ struct TextureUpdateArgs {
|
|||||||
std::wstring text_base;
|
std::wstring text_base;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Client::showUpdateProgressTexture(void *args, u32 progress, u32 max_progress)
|
void Client::showUpdateProgressTexture(void *args, float progress)
|
||||||
{
|
{
|
||||||
auto *targs = reinterpret_cast<TextureUpdateArgs*>(args);
|
auto *targs = reinterpret_cast<TextureUpdateArgs*>(args);
|
||||||
u16 cur_percent = std::ceil(progress * 100.f / max_progress);
|
u16 cur_percent = std::ceil(100 * progress);
|
||||||
|
|
||||||
// Throttle menu drawing
|
// Throttle menu drawing
|
||||||
bool do_draw = false;
|
bool do_draw = false;
|
||||||
@@ -1817,7 +1817,8 @@ void Client::showUpdateProgressTexture(void *args, u32 progress, u32 max_progres
|
|||||||
|
|
||||||
std::wostringstream strm;
|
std::wostringstream strm;
|
||||||
strm << targs->text_base << L" " << cur_percent << L"%...";
|
strm << targs->text_base << L" " << cur_percent << L"%...";
|
||||||
int shown_progress = 72 + std::ceil(0.18f * cur_percent);
|
// 70% -> 99%
|
||||||
|
int shown_progress = 70 + std::ceil(0.29f * cur_percent);
|
||||||
m_rendering_engine->draw_load_screen(strm.str(), guienv, m_tsrc,
|
m_rendering_engine->draw_load_screen(strm.str(), guienv, m_tsrc,
|
||||||
0, shown_progress);
|
0, shown_progress);
|
||||||
}
|
}
|
||||||
@@ -1837,19 +1838,19 @@ void Client::afterContentReceived()
|
|||||||
// Rebuild inherited images and recreate textures
|
// Rebuild inherited images and recreate textures
|
||||||
infostream<<"- Rebuilding images and textures"<<std::endl;
|
infostream<<"- Rebuilding images and textures"<<std::endl;
|
||||||
m_rendering_engine->draw_load_screen(wstrgettext("Loading textures..."),
|
m_rendering_engine->draw_load_screen(wstrgettext("Loading textures..."),
|
||||||
guienv, m_tsrc, 0, 70);
|
guienv, m_tsrc, 0, 66);
|
||||||
m_tsrc->rebuildImagesAndTextures();
|
m_tsrc->rebuildImagesAndTextures();
|
||||||
|
|
||||||
// Rebuild shaders
|
// Rebuild shaders
|
||||||
infostream<<"- Rebuilding shaders"<<std::endl;
|
infostream<<"- Rebuilding shaders"<<std::endl;
|
||||||
m_rendering_engine->draw_load_screen(wstrgettext("Rebuilding shaders..."),
|
m_rendering_engine->draw_load_screen(wstrgettext("Rebuilding shaders..."),
|
||||||
guienv, m_tsrc, 0, 71);
|
guienv, m_tsrc, 0, 68);
|
||||||
m_shsrc->rebuildShaders();
|
m_shsrc->rebuildShaders();
|
||||||
|
|
||||||
// Update node aliases
|
// Update node aliases
|
||||||
infostream<<"- Updating node aliases"<<std::endl;
|
infostream<<"- Updating node aliases"<<std::endl;
|
||||||
m_rendering_engine->draw_load_screen(wstrgettext("Initializing nodes..."),
|
m_rendering_engine->draw_load_screen(wstrgettext("Initializing nodes..."),
|
||||||
guienv, m_tsrc, 0, 72);
|
guienv, m_tsrc, 0, 70);
|
||||||
m_nodedef->updateAliases(m_itemdef);
|
m_nodedef->updateAliases(m_itemdef);
|
||||||
for (const auto &path : getTextureDirs()) {
|
for (const auto &path : getTextureDirs()) {
|
||||||
TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
|
TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
|
||||||
|
|||||||
@@ -357,7 +357,7 @@ public:
|
|||||||
|
|
||||||
void drawLoadScreen(const std::wstring &text, float dtime, int percent);
|
void drawLoadScreen(const std::wstring &text, float dtime, int percent);
|
||||||
void afterContentReceived();
|
void afterContentReceived();
|
||||||
void showUpdateProgressTexture(void *args, u32 progress, u32 max_progress);
|
void showUpdateProgressTexture(void *args, float progress);
|
||||||
|
|
||||||
float getRTT();
|
float getRTT();
|
||||||
float getCurRate();
|
float getCurRate();
|
||||||
|
|||||||
@@ -1088,6 +1088,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
|||||||
u32 vertex_count = 0;
|
u32 vertex_count = 0;
|
||||||
u32 drawcall_count = 0;
|
u32 drawcall_count = 0;
|
||||||
u32 material_swaps = 0;
|
u32 material_swaps = 0;
|
||||||
|
u32 array_texture_use = 0;
|
||||||
|
|
||||||
// Render all mesh buffers in order
|
// Render all mesh buffers in order
|
||||||
drawcall_count += draw_order.size();
|
drawcall_count += draw_order.size();
|
||||||
@@ -1117,8 +1118,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
|||||||
layer.MagFilter = video::ETMAGF_NEAREST;
|
layer.MagFilter = video::ETMAGF_NEAREST;
|
||||||
layer.AnisotropicFilter = 0;
|
layer.AnisotropicFilter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
driver->setMaterial(material);
|
driver->setMaterial(material);
|
||||||
++material_swaps;
|
++material_swaps;
|
||||||
|
if (auto *tex = material.getTexture(0); tex && tex->getType() == video::ETT_2D_ARRAY)
|
||||||
|
++array_texture_use;
|
||||||
|
|
||||||
material.TextureLayers[ShadowRenderer::TEXTURE_LAYER_SHADOW].Texture = nullptr;
|
material.TextureLayers[ShadowRenderer::TEXTURE_LAYER_SHADOW].Texture = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1158,6 +1163,10 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
|||||||
g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
|
g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
|
||||||
g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
|
g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
|
||||||
g_profiler->avg(prefix + "material swaps [#]", material_swaps);
|
g_profiler->avg(prefix + "material swaps [#]", material_swaps);
|
||||||
|
if (material_swaps && array_texture_use) {
|
||||||
|
int percent = (100.0f * array_texture_use) / material_swaps;
|
||||||
|
g_profiler->avg(prefix + "array texture use [%]", percent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientMap::invalidateMapBlockMesh(MapBlockMesh *mesh)
|
void ClientMap::invalidateMapBlockMesh(MapBlockMesh *mesh)
|
||||||
@@ -1480,14 +1489,14 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
|
|||||||
|
|
||||||
bool translucent_foliage = g_settings->getBool("enable_translucent_foliage");
|
bool translucent_foliage = g_settings->getBool("enable_translucent_foliage");
|
||||||
|
|
||||||
video::E_MATERIAL_TYPE leaves_material = video::EMT_SOLID;
|
|
||||||
|
|
||||||
// For translucent leaves, we want to use backface culling instead of frontface.
|
// For translucent leaves, we want to use backface culling instead of frontface.
|
||||||
|
std::vector<video::E_MATERIAL_TYPE> leaves_material;
|
||||||
if (translucent_foliage) {
|
if (translucent_foliage) {
|
||||||
// this is the material leaves would use, compare to nodedef.cpp
|
auto *shdsrc = m_client->getShaderSource();
|
||||||
auto* shdsrc = m_client->getShaderSource();
|
// Find out all materials used by leaves so we can identify them
|
||||||
const u32 leaves_shader = shdsrc->getShader("nodes_shader", TILE_MATERIAL_WAVING_LEAVES, NDT_ALLFACES);
|
leaves_material.reserve(m_nodedef->m_leaves_materials.size());
|
||||||
leaves_material = shdsrc->getShaderInfo(leaves_shader).material;
|
for (u32 shader_id : m_nodedef->m_leaves_materials)
|
||||||
|
leaves_material.push_back(shdsrc->getShaderInfo(shader_id).material);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &descriptor : draw_order) {
|
for (auto &descriptor : draw_order) {
|
||||||
@@ -1502,7 +1511,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
|
|||||||
local_material.BackfaceCulling = material.BackfaceCulling;
|
local_material.BackfaceCulling = material.BackfaceCulling;
|
||||||
local_material.FrontfaceCulling = material.FrontfaceCulling;
|
local_material.FrontfaceCulling = material.FrontfaceCulling;
|
||||||
}
|
}
|
||||||
if (local_material.MaterialType == leaves_material && translucent_foliage) {
|
if (translucent_foliage && CONTAINS(leaves_material, local_material.MaterialType)) {
|
||||||
local_material.BackfaceCulling = true;
|
local_material.BackfaceCulling = true;
|
||||||
local_material.FrontfaceCulling = false;
|
local_material.FrontfaceCulling = false;
|
||||||
}
|
}
|
||||||
@@ -1522,7 +1531,8 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
|
|||||||
video::SMaterial clean;
|
video::SMaterial clean;
|
||||||
clean.BlendOperation = video::EBO_ADD;
|
clean.BlendOperation = video::EBO_ADD;
|
||||||
driver->setMaterial(clean); // reset material to defaults
|
driver->setMaterial(clean); // reset material to defaults
|
||||||
// FIXME: why is this here?
|
// This is somehow needed to fully reset the rendering state, or later operations
|
||||||
|
// will be broken.
|
||||||
driver->draw3DLine(v3f(), v3f(), video::SColor(0));
|
driver->draw3DLine(v3f(), v3f(), video::SColor(0));
|
||||||
|
|
||||||
g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
|
g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
|
||||||
|
|||||||
@@ -212,6 +212,8 @@ static scene::SMesh *generateNodeMesh(Client *client, MapNode n,
|
|||||||
MapblockMeshGenerator(&mmd, &collector).generate();
|
MapblockMeshGenerator(&mmd, &collector).generate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AlphaMode alpha_mode = ndef->get(n).alpha;
|
||||||
|
|
||||||
auto mesh = make_irr<scene::SMesh>();
|
auto mesh = make_irr<scene::SMesh>();
|
||||||
animation.clear();
|
animation.clear();
|
||||||
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
|
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
|
||||||
@@ -224,10 +226,8 @@ static scene::SMesh *generateNodeMesh(Client *client, MapNode n,
|
|||||||
p.applyTileColor();
|
p.applyTileColor();
|
||||||
|
|
||||||
if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
|
if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
|
||||||
const FrameSpec &frame = (*p.layer.frames)[0];
|
animation.emplace_back(MeshAnimationInfo{
|
||||||
p.layer.texture = frame.texture;
|
mesh->getMeshBufferCount(), 0, p.layer});
|
||||||
|
|
||||||
animation.emplace_back(MeshAnimationInfo{mesh->getMeshBufferCount(), 0, p.layer});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto buf = make_irr<scene::SMeshBuffer>();
|
auto buf = make_irr<scene::SMeshBuffer>();
|
||||||
@@ -236,9 +236,8 @@ static scene::SMesh *generateNodeMesh(Client *client, MapNode n,
|
|||||||
|
|
||||||
// Set up material
|
// Set up material
|
||||||
auto &mat = buf->Material;
|
auto &mat = buf->Material;
|
||||||
u32 shader_id = shdsrc->getShader("object_shader", p.layer.material_type, NDT_NORMAL);
|
|
||||||
mat.MaterialType = shdsrc->getShaderInfo(shader_id).material;
|
|
||||||
p.layer.applyMaterialOptions(mat, layer);
|
p.layer.applyMaterialOptions(mat, layer);
|
||||||
|
getAdHocNodeShader(mat, shdsrc, "object_shader", alpha_mode, layer == 1);
|
||||||
|
|
||||||
mesh->addMeshBuffer(buf.get());
|
mesh->addMeshBuffer(buf.get());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1021,8 +1021,9 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
|
|||||||
|
|
||||||
// Optionally render internal liquid level defined by param2
|
// Optionally render internal liquid level defined by param2
|
||||||
// Liquid is textured with 1 tile defined in nodedef 'special_tiles'
|
// Liquid is textured with 1 tile defined in nodedef 'special_tiles'
|
||||||
if (param2 > 0 && cur_node.f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
|
auto &cf = *cur_node.f;
|
||||||
cur_node.f->special_tiles[0].layers[0].texture) {
|
if (param2 > 0 && cf.param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
|
||||||
|
!cf.special_tiles[0].layers[0].empty()) {
|
||||||
// Internal liquid level has param2 range 0 .. 63,
|
// Internal liquid level has param2 range 0 .. 63,
|
||||||
// convert it to -0.5 .. 0.5
|
// convert it to -0.5 .. 0.5
|
||||||
float vlev = (param2 / 63.0f) * 2.0f - 1.0f;
|
float vlev = (param2 / 63.0f) * 2.0f - 1.0f;
|
||||||
|
|||||||
@@ -961,12 +961,12 @@ bool Game::createClient(const GameStartData &start_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pre-calculate crack length
|
// Pre-calculate crack length
|
||||||
video::ITexture *t = texture_src->getTexture("crack_anylength.png");
|
{
|
||||||
if (t) {
|
auto size = texture_src->getTextureDimensions("crack_anylength.png");
|
||||||
v2u32 size = t->getOriginalSize();
|
if (size.Width && size.Height)
|
||||||
crack_animation_length = size.Y / size.X;
|
crack_animation_length = size.Height / size.Width;
|
||||||
} else {
|
else
|
||||||
crack_animation_length = 5;
|
crack_animation_length = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
shader_src->addShaderConstantSetter(
|
shader_src->addShaderConstantSetter(
|
||||||
@@ -1283,7 +1283,8 @@ bool Game::getServerContent(bool *aborted)
|
|||||||
message << " (" << cur << ' ' << cur_unit << ")";
|
message << " (" << cur << ' ' << cur_unit << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
progress = 30 + client->mediaReceiveProgress() * 35 + 0.5;
|
// 30% -> 65%
|
||||||
|
progress = 30 + std::ceil(client->mediaReceiveProgress() * 35 + 0.5f);
|
||||||
m_rendering_engine->draw_load_screen(utf8_to_wide(message.str()), guienv,
|
m_rendering_engine->draw_load_screen(utf8_to_wide(message.str()), guienv,
|
||||||
texture_src, dtime, progress);
|
texture_src, dtime, progress);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -651,9 +651,6 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data):
|
|||||||
if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
|
if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
|
||||||
// Add to MapBlockMesh in order to animate these tiles
|
// Add to MapBlockMesh in order to animate these tiles
|
||||||
m_animation_info.emplace(std::make_pair(layer, i), AnimationInfo(p.layer));
|
m_animation_info.emplace(std::make_pair(layer, i), AnimationInfo(p.layer));
|
||||||
// Replace tile texture with the first animation frame
|
|
||||||
assert(p.layer.frames);
|
|
||||||
p.layer.texture = (*p.layer.frames)[0].texture;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create material
|
// Create material
|
||||||
|
|||||||
@@ -23,10 +23,12 @@ void MeshCollector::append(const TileLayer &layer, const video::S3DVertex *verti
|
|||||||
{
|
{
|
||||||
PreMeshBuffer &p = findBuffer(layer, layernum, numVertices);
|
PreMeshBuffer &p = findBuffer(layer, layernum, numVertices);
|
||||||
|
|
||||||
|
const u16 aux = layer.texture_layer_idx;
|
||||||
|
|
||||||
u32 vertex_count = p.vertices.size();
|
u32 vertex_count = p.vertices.size();
|
||||||
for (u32 i = 0; i < numVertices; i++) {
|
for (u32 i = 0; i < numVertices; i++) {
|
||||||
p.vertices.emplace_back(vertices[i].Pos + offset, vertices[i].Normal,
|
p.vertices.emplace_back(vertices[i].Pos + offset, vertices[i].Normal,
|
||||||
vertices[i].Color, vertices[i].TCoords);
|
vertices[i].Color, vertices[i].TCoords, aux);
|
||||||
m_bounding_radius_sq = std::max(m_bounding_radius_sq,
|
m_bounding_radius_sq = std::max(m_bounding_radius_sq,
|
||||||
(vertices[i].Pos - m_center_pos).getLengthSQ());
|
(vertices[i].Pos - m_center_pos).getLengthSQ());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,8 +42,11 @@ static video::ITexture *extractTexture(const TileDef &def, const TileLayer &laye
|
|||||||
{
|
{
|
||||||
// If animated take first frame from tile layer (so we don't have to handle
|
// If animated take first frame from tile layer (so we don't have to handle
|
||||||
// that manually), otherwise look up by name.
|
// that manually), otherwise look up by name.
|
||||||
if (!layer.empty() && (layer.material_flags & MATERIAL_FLAG_ANIMATION))
|
if (!layer.empty() && (layer.material_flags & MATERIAL_FLAG_ANIMATION)) {
|
||||||
return (*layer.frames)[0].texture;
|
auto *ret = (*layer.frames)[0].texture;
|
||||||
|
assert(ret->getType() == video::ETT_2D);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
if (!def.name.empty())
|
if (!def.name.empty())
|
||||||
return tsrc->getTexture(def.name);
|
return tsrc->getTexture(def.name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
@@ -666,9 +666,11 @@ void ShaderSource::generateShader(ShaderInfo &shaderinfo)
|
|||||||
uniform mediump mat4 mTexture;
|
uniform mediump mat4 mTexture;
|
||||||
|
|
||||||
attribute highp vec4 inVertexPosition;
|
attribute highp vec4 inVertexPosition;
|
||||||
attribute lowp vec4 inVertexColor;
|
|
||||||
attribute mediump vec2 inTexCoord0;
|
|
||||||
attribute mediump vec3 inVertexNormal;
|
attribute mediump vec3 inVertexNormal;
|
||||||
|
attribute lowp vec4 inVertexColor;
|
||||||
|
attribute mediump float inVertexAux;
|
||||||
|
attribute mediump vec2 inTexCoord0;
|
||||||
|
attribute mediump vec2 inTexCoord1;
|
||||||
attribute mediump vec4 inVertexTangent;
|
attribute mediump vec4 inVertexTangent;
|
||||||
attribute mediump vec4 inVertexBinormal;
|
attribute mediump vec4 inVertexBinormal;
|
||||||
)";
|
)";
|
||||||
@@ -787,11 +789,13 @@ void ShaderSource::generateShader(ShaderInfo &shaderinfo)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
u32 IShaderSource::getShader(const std::string &name,
|
u32 IShaderSource::getShader(const std::string &name,
|
||||||
MaterialType material_type, NodeDrawType drawtype)
|
MaterialType material_type, NodeDrawType drawtype, bool array_texture)
|
||||||
{
|
{
|
||||||
ShaderConstants input_const;
|
ShaderConstants input_const;
|
||||||
input_const["MATERIAL_TYPE"] = (int)material_type;
|
input_const["MATERIAL_TYPE"] = (int)material_type;
|
||||||
(void) drawtype; // unused
|
(void) drawtype; // unused
|
||||||
|
if (array_texture)
|
||||||
|
input_const["USE_ARRAY_TEXTURE"] = 1;
|
||||||
|
|
||||||
video::E_MATERIAL_TYPE base_mat = video::EMT_SOLID;
|
video::E_MATERIAL_TYPE base_mat = video::EMT_SOLID;
|
||||||
switch (material_type) {
|
switch (material_type) {
|
||||||
|
|||||||
@@ -267,7 +267,8 @@ public:
|
|||||||
|
|
||||||
/// @brief Helper: Generates or gets a shader suitable for nodes and entities
|
/// @brief Helper: Generates or gets a shader suitable for nodes and entities
|
||||||
u32 getShader(const std::string &name,
|
u32 getShader(const std::string &name,
|
||||||
MaterialType material_type, NodeDrawType drawtype = NDT_NORMAL);
|
MaterialType material_type, NodeDrawType drawtype = NDT_NORMAL,
|
||||||
|
bool array_texture = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper: Generates or gets a shader for common, general use.
|
* Helper: Generates or gets a shader for common, general use.
|
||||||
|
|||||||
@@ -14,17 +14,25 @@
|
|||||||
#include "texturepaths.h"
|
#include "texturepaths.h"
|
||||||
#include "util/thread.h"
|
#include "util/thread.h"
|
||||||
|
|
||||||
|
// Represents a to-be-generated texture for queuing purposes
|
||||||
struct TextureRequest
|
struct TextureRequest
|
||||||
{
|
{
|
||||||
std::string image;
|
video::E_TEXTURE_TYPE type = video::ETT_2D;
|
||||||
|
std::vector<std::string> images;
|
||||||
|
|
||||||
void print(std::ostream &to) const {
|
void print(std::ostream &to) const {
|
||||||
to << "image=\"" << image << "\"";
|
if (images.size() == 1) {
|
||||||
|
to << "image=\"" << images[0] << "\"";
|
||||||
|
} else {
|
||||||
|
to << "images={";
|
||||||
|
for (auto &image : images)
|
||||||
|
to << "\"" << image << "\" ";
|
||||||
|
to << "}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const TextureRequest &other) const {
|
bool operator==(const TextureRequest &other) const {
|
||||||
return image == other.image;
|
return type == other.type && images == other.images;
|
||||||
}
|
}
|
||||||
bool operator!=(const TextureRequest &other) const {
|
bool operator!=(const TextureRequest &other) const {
|
||||||
return !(*this == other);
|
return !(*this == other);
|
||||||
@@ -72,6 +80,9 @@ public:
|
|||||||
|
|
||||||
video::ITexture* getTexture(const std::string &name, u32 *id = nullptr);
|
video::ITexture* getTexture(const std::string &name, u32 *id = nullptr);
|
||||||
|
|
||||||
|
video::ITexture *addArrayTexture(
|
||||||
|
const std::vector<std::string> &images, u32 *id = nullptr);
|
||||||
|
|
||||||
bool needFilterForMesh() const {
|
bool needFilterForMesh() const {
|
||||||
return mesh_filter_needed;
|
return mesh_filter_needed;
|
||||||
}
|
}
|
||||||
@@ -104,6 +115,8 @@ public:
|
|||||||
|
|
||||||
video::SColor getTextureAverageColor(const std::string &name);
|
video::SColor getTextureAverageColor(const std::string &name);
|
||||||
|
|
||||||
|
core::dimension2du getTextureDimensions(const std::string &image);
|
||||||
|
|
||||||
void setImageCaching(bool enabled);
|
void setImageCaching(bool enabled);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -137,6 +150,9 @@ private:
|
|||||||
// Generate standard texture
|
// Generate standard texture
|
||||||
u32 generateTexture(const std::string &name);
|
u32 generateTexture(const std::string &name);
|
||||||
|
|
||||||
|
// Generate array texture
|
||||||
|
u32 generateArrayTexture(const std::vector<std::string> &names);
|
||||||
|
|
||||||
// Thread-safe cache of what source images are known (true = known)
|
// Thread-safe cache of what source images are known (true = known)
|
||||||
MutexedMap<std::string, bool> m_source_image_existence;
|
MutexedMap<std::string, bool> m_source_image_existence;
|
||||||
|
|
||||||
@@ -270,15 +286,97 @@ u32 TextureSource::getTextureId(const std::string &name)
|
|||||||
return n->second;
|
return n->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureRequest req{name};
|
TextureRequest req{video::ETT_2D, {name}};
|
||||||
|
|
||||||
return processRequestQueued(req);
|
return processRequestQueued(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
video::ITexture *TextureSource::addArrayTexture(
|
||||||
|
const std::vector<std::string> &images, u32 *ret_id)
|
||||||
|
{
|
||||||
|
if (images.empty())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
TextureRequest req{video::ETT_2D_ARRAY, images};
|
||||||
|
|
||||||
|
u32 id = processRequestQueued(req);
|
||||||
|
if (ret_id)
|
||||||
|
*ret_id = id;
|
||||||
|
return getTexture(id);
|
||||||
|
}
|
||||||
|
|
||||||
u32 TextureSource::processRequest(const TextureRequest &req)
|
u32 TextureSource::processRequest(const TextureRequest &req)
|
||||||
{
|
{
|
||||||
// No different types yet (TODO)
|
if (req.type == video::ETT_2D) {
|
||||||
return generateTexture(req.image);
|
assert(req.images.size() == 1);
|
||||||
|
return generateTexture(req.images[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.type == video::ETT_2D_ARRAY) {
|
||||||
|
assert(!req.images.empty());
|
||||||
|
return generateArrayTexture(req.images);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorstream << "TextureSource::processRequest(): unknown type "
|
||||||
|
<< (int)req.type << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 TextureSource::generateArrayTexture(const std::vector<std::string> &images)
|
||||||
|
{
|
||||||
|
std::set<std::string> source_image_names;
|
||||||
|
std::vector<video::IImage*> imgs;
|
||||||
|
const auto &drop_imgs = [&imgs] () {
|
||||||
|
for (auto *img : imgs) {
|
||||||
|
if (img)
|
||||||
|
img->drop();
|
||||||
|
}
|
||||||
|
imgs.clear();
|
||||||
|
};
|
||||||
|
for (auto &name : images) {
|
||||||
|
video::IImage *img = getOrGenerateImage(name, source_image_names);
|
||||||
|
if (!img) {
|
||||||
|
// Since the caller needs to make sure of the dimensions beforehand
|
||||||
|
// anyway, this should not ever happen. So the "unhelpful" error is ok.
|
||||||
|
errorstream << "generateArrayTexture(): one of " << images.size()
|
||||||
|
<< " images failed to generate, aborting." << std::endl;
|
||||||
|
drop_imgs();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
imgs.push_back(img);
|
||||||
|
}
|
||||||
|
assert(!imgs.empty());
|
||||||
|
|
||||||
|
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
|
||||||
|
sanity_check(driver);
|
||||||
|
assert(driver->queryFeature(video::EVDF_TEXTURE_2D_ARRAY));
|
||||||
|
|
||||||
|
MutexAutoLock lock(m_textureinfo_cache_mutex);
|
||||||
|
const u32 id = m_textureinfo_cache.size();
|
||||||
|
std::string name;
|
||||||
|
{ // automatically choose a name
|
||||||
|
char buf[64];
|
||||||
|
porting::mt_snprintf(buf, sizeof(buf), "array#%u %ux%ux%u", id,
|
||||||
|
imgs[0]->getDimension().Width, imgs[0]->getDimension().Height,
|
||||||
|
imgs.size());
|
||||||
|
name = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
video::ITexture *tex = driver->addArrayTexture(name, imgs.data(), imgs.size());
|
||||||
|
drop_imgs();
|
||||||
|
|
||||||
|
if (!tex) {
|
||||||
|
warningstream << "generateArrayTexture(): failed to upload texture \""
|
||||||
|
<< name << "\"" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add texture to caches (add NULL textures too)
|
||||||
|
|
||||||
|
TextureInfo ti{video::ETT_2D_ARRAY, name, images, tex, std::move(source_image_names)};
|
||||||
|
m_textureinfo_cache.emplace_back(std::move(ti));
|
||||||
|
m_name_to_id[name] = id;
|
||||||
|
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 TextureSource::generateTexture(const std::string &name)
|
u32 TextureSource::generateTexture(const std::string &name)
|
||||||
@@ -302,7 +400,6 @@ u32 TextureSource::generateTexture(const std::string &name)
|
|||||||
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
|
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
|
||||||
sanity_check(driver);
|
sanity_check(driver);
|
||||||
|
|
||||||
// passed into texture info for dynamic media tracking
|
|
||||||
std::set<std::string> source_image_names;
|
std::set<std::string> source_image_names;
|
||||||
video::IImage *img = getOrGenerateImage(name, source_image_names);
|
video::IImage *img = getOrGenerateImage(name, source_image_names);
|
||||||
|
|
||||||
@@ -314,12 +411,16 @@ u32 TextureSource::generateTexture(const std::string &name)
|
|||||||
guiScalingCache(io::path(name.c_str()), driver, img);
|
guiScalingCache(io::path(name.c_str()), driver, img);
|
||||||
img->drop();
|
img->drop();
|
||||||
}
|
}
|
||||||
|
if (!tex) {
|
||||||
|
warningstream << "generateTexture(): failed to upload texture \""
|
||||||
|
<< name << "\"" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
// Add texture to caches (add NULL textures too)
|
// Add texture to caches (add NULL textures too)
|
||||||
|
|
||||||
MutexAutoLock lock(m_textureinfo_cache_mutex);
|
MutexAutoLock lock(m_textureinfo_cache_mutex);
|
||||||
|
|
||||||
u32 id = m_textureinfo_cache.size();
|
const u32 id = m_textureinfo_cache.size();
|
||||||
TextureInfo ti{video::ETT_2D, name, {name}, tex, std::move(source_image_names)};
|
TextureInfo ti{video::ETT_2D, name, {name}, tex, std::move(source_image_names)};
|
||||||
m_textureinfo_cache.emplace_back(std::move(ti));
|
m_textureinfo_cache.emplace_back(std::move(ti));
|
||||||
m_name_to_id[name] = id;
|
m_name_to_id[name] = id;
|
||||||
@@ -535,6 +636,8 @@ void TextureSource::rebuildTexture(video::IVideoDriver *driver, TextureInfo &ti)
|
|||||||
video::SColor TextureSource::getTextureAverageColor(const std::string &name)
|
video::SColor TextureSource::getTextureAverageColor(const std::string &name)
|
||||||
{
|
{
|
||||||
assert(std::this_thread::get_id() == m_main_thread);
|
assert(std::this_thread::get_id() == m_main_thread);
|
||||||
|
if (name.empty())
|
||||||
|
return {0, 0, 0, 0};
|
||||||
|
|
||||||
std::set<std::string> unused;
|
std::set<std::string> unused;
|
||||||
auto *image = getOrGenerateImage(name, unused);
|
auto *image = getOrGenerateImage(name, unused);
|
||||||
@@ -547,6 +650,23 @@ video::SColor TextureSource::getTextureAverageColor(const std::string &name)
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
core::dimension2du TextureSource::getTextureDimensions(const std::string &name)
|
||||||
|
{
|
||||||
|
assert(std::this_thread::get_id() == m_main_thread);
|
||||||
|
|
||||||
|
core::dimension2du ret;
|
||||||
|
if (!name.empty()) {
|
||||||
|
std::set<std::string> unused;
|
||||||
|
auto *image = getOrGenerateImage(name, unused);
|
||||||
|
if (image) {
|
||||||
|
ret = image->getDimension();
|
||||||
|
image->drop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void TextureSource::setImageCaching(bool enabled)
|
void TextureSource::setImageCaching(bool enabled)
|
||||||
{
|
{
|
||||||
m_image_cache_enabled = enabled;
|
m_image_cache_enabled = enabled;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "irrlichttypes.h"
|
#include "irrlichttypes.h"
|
||||||
#include <SColor.h>
|
#include <SColor.h>
|
||||||
|
#include <dimension2d.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -59,8 +60,11 @@ public:
|
|||||||
/// @brief Returns existing texture by ID
|
/// @brief Returns existing texture by ID
|
||||||
virtual video::ITexture *getTexture(u32 id)=0;
|
virtual video::ITexture *getTexture(u32 id)=0;
|
||||||
|
|
||||||
/// @return true if getTextureForMesh will apply a filter
|
/// @brief Generates texture string(s) into an array texture
|
||||||
virtual bool needFilterForMesh() const = 0;
|
/// @note Unlike the other getters this will always add a *new* texture.
|
||||||
|
/// @return its ID
|
||||||
|
virtual video::ITexture *addArrayTexture(
|
||||||
|
const std::vector<std::string> &images, u32 *id = nullptr) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Generates a texture string into a standard texture
|
* @brief Generates a texture string into a standard texture
|
||||||
@@ -76,6 +80,9 @@ public:
|
|||||||
return getTexture(image, id);
|
return getTexture(image, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @return true if getTextureForMesh will apply a filter
|
||||||
|
virtual bool needFilterForMesh() const = 0;
|
||||||
|
|
||||||
/// Filter needed for mesh-suitable textures, including leading ^
|
/// Filter needed for mesh-suitable textures, including leading ^
|
||||||
static constexpr const char *FILTER_FOR_MESH = "^[applyfiltersformesh";
|
static constexpr const char *FILTER_FOR_MESH = "^[applyfiltersformesh";
|
||||||
|
|
||||||
@@ -90,6 +97,10 @@ public:
|
|||||||
/// @brief Check if given image name exists
|
/// @brief Check if given image name exists
|
||||||
virtual bool isKnownSourceImage(const std::string &name)=0;
|
virtual bool isKnownSourceImage(const std::string &name)=0;
|
||||||
|
|
||||||
|
/// @brief Return dimensions of a texture string
|
||||||
|
/// (will avoid actually creating the texture)
|
||||||
|
virtual core::dimension2du getTextureDimensions(const std::string &image)=0;
|
||||||
|
|
||||||
/// @brief Return average color of a texture string
|
/// @brief Return average color of a texture string
|
||||||
virtual video::SColor getTextureAverageColor(const std::string &image)=0;
|
virtual video::SColor getTextureAverageColor(const std::string &image)=0;
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,21 @@ enum MaterialType : u8 {
|
|||||||
TILE_MATERIAL_PLAIN_ALPHA
|
TILE_MATERIAL_PLAIN_ALPHA
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief change type so it has at least simple transparency
|
||||||
|
*/
|
||||||
|
static inline MaterialType material_type_with_alpha(MaterialType type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case TILE_MATERIAL_OPAQUE:
|
||||||
|
return TILE_MATERIAL_BASIC;
|
||||||
|
case TILE_MATERIAL_WAVING_LIQUID_OPAQUE:
|
||||||
|
return TILE_MATERIAL_WAVING_LIQUID_BASIC;
|
||||||
|
default:
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Material flags
|
// Material flags
|
||||||
// Should backface culling be enabled?
|
// Should backface culling be enabled?
|
||||||
#define MATERIAL_FLAG_BACKFACE_CULLING 0x01
|
#define MATERIAL_FLAG_BACKFACE_CULLING 0x01
|
||||||
@@ -39,6 +54,7 @@ enum MaterialType : u8 {
|
|||||||
This fully defines the looks of a tile.
|
This fully defines the looks of a tile.
|
||||||
The SMaterial of a tile is constructed according to this.
|
The SMaterial of a tile is constructed according to this.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct FrameSpec
|
struct FrameSpec
|
||||||
{
|
{
|
||||||
FrameSpec() = default;
|
FrameSpec() = default;
|
||||||
@@ -60,18 +76,18 @@ struct TileLayer
|
|||||||
TileLayer() = default;
|
TileLayer() = default;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Two layers are equal if they can be merged.
|
* Two layers are equal if they can be merged (same material).
|
||||||
*/
|
*/
|
||||||
bool operator==(const TileLayer &other) const
|
bool operator==(const TileLayer &other) const
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
texture_id == other.texture_id &&
|
texture_id == other.texture_id &&
|
||||||
material_type == other.material_type &&
|
shader_id == other.shader_id &&
|
||||||
material_flags == other.material_flags &&
|
material_flags == other.material_flags &&
|
||||||
has_color == other.has_color &&
|
has_color == other.has_color &&
|
||||||
color == other.color &&
|
color == other.color &&
|
||||||
scale == other.scale &&
|
|
||||||
need_polygon_offset == other.need_polygon_offset;
|
need_polygon_offset == other.need_polygon_offset;
|
||||||
|
// texture_layer_idx and scale are notably part of the vertex data
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -84,7 +100,7 @@ struct TileLayer
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set some material parameters accordingly.
|
* Set some material parameters accordingly.
|
||||||
* @note does not set `MaterialType`
|
* @note does not set `MaterialType`!
|
||||||
* @param material material to mody
|
* @param material material to mody
|
||||||
* @param layer index of this layer in the `TileSpec`
|
* @param layer index of this layer in the `TileSpec`
|
||||||
*/
|
*/
|
||||||
@@ -122,12 +138,16 @@ struct TileLayer
|
|||||||
u16 animation_frame_length_ms = 0;
|
u16 animation_frame_length_ms = 0;
|
||||||
u16 animation_frame_count = 1;
|
u16 animation_frame_count = 1;
|
||||||
|
|
||||||
|
/// Layer index to use, if the texture is an array texture
|
||||||
|
u16 texture_layer_idx = 0;
|
||||||
|
|
||||||
MaterialType material_type = TILE_MATERIAL_BASIC;
|
MaterialType material_type = TILE_MATERIAL_BASIC;
|
||||||
u8 material_flags =
|
u8 material_flags =
|
||||||
MATERIAL_FLAG_BACKFACE_CULLING |
|
MATERIAL_FLAG_BACKFACE_CULLING |
|
||||||
MATERIAL_FLAG_TILEABLE_HORIZONTAL|
|
MATERIAL_FLAG_TILEABLE_HORIZONTAL|
|
||||||
MATERIAL_FLAG_TILEABLE_VERTICAL;
|
MATERIAL_FLAG_TILEABLE_VERTICAL;
|
||||||
|
|
||||||
|
/// Texture scale in both directions (used for world-align)
|
||||||
u8 scale = 1;
|
u8 scale = 1;
|
||||||
|
|
||||||
/// does this tile need to have a positive polygon offset set?
|
/// does this tile need to have a positive polygon offset set?
|
||||||
|
|||||||
@@ -125,55 +125,32 @@ static video::ITexture *extractTexture(const TileDef &def, const TileLayer &laye
|
|||||||
{
|
{
|
||||||
// If animated take first frame from tile layer (so we don't have to handle
|
// If animated take first frame from tile layer (so we don't have to handle
|
||||||
// that manually), otherwise look up by name.
|
// that manually), otherwise look up by name.
|
||||||
if (!layer.empty() && (layer.material_flags & MATERIAL_FLAG_ANIMATION))
|
if (!layer.empty() && (layer.material_flags & MATERIAL_FLAG_ANIMATION)) {
|
||||||
return (*layer.frames)[0].texture;
|
auto *ret = (*layer.frames)[0].texture;
|
||||||
|
assert(ret->getType() == video::ETT_2D);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
if (!def.name.empty())
|
if (!def.name.empty())
|
||||||
return tsrc->getTextureForMesh(def.name);
|
return tsrc->getTextureForMesh(def.name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// (the function name represents the amount of time wasted on all of this)
|
void getAdHocNodeShader(video::SMaterial &mat, IShaderSource *shdsrc,
|
||||||
|
const char *shader, AlphaMode mode, int layer)
|
||||||
static void setAlphaBullshit(video::SMaterial &mat,
|
|
||||||
AlphaMode mode, bool overlay)
|
|
||||||
{
|
{
|
||||||
switch (mode) {
|
assert(shdsrc);
|
||||||
case ALPHAMODE_BLEND:
|
MaterialType type = alpha_mode_to_material_type(mode);
|
||||||
mat.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
|
if (layer == 1)
|
||||||
mat.MaterialTypeParam = 0;
|
type = material_type_with_alpha(type);
|
||||||
return;
|
|
||||||
case ALPHAMODE_OPAQUE:
|
|
||||||
if (!overlay) {
|
|
||||||
mat.MaterialType = video::EMT_SOLID;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[[fallthrough]];
|
|
||||||
case ALPHAMODE_CLIP:
|
|
||||||
default:
|
|
||||||
mat.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
|
|
||||||
mat.MaterialTypeParam = 0.5f;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setAlphaBullshit(video::SMaterial &mat, IShaderSource *shdsrc,
|
// Note: logic wise this duplicates what `ContentFeatures::updateTextures`
|
||||||
AlphaMode mode, bool overlay)
|
// and related functions do.
|
||||||
{
|
|
||||||
MaterialType mt;
|
|
||||||
switch (mode) {
|
|
||||||
case ALPHAMODE_BLEND:
|
|
||||||
mt = TILE_MATERIAL_ALPHA;
|
|
||||||
break;
|
|
||||||
case ALPHAMODE_OPAQUE:
|
|
||||||
mt = overlay ? TILE_MATERIAL_BASIC : TILE_MATERIAL_OPAQUE;
|
|
||||||
break;
|
|
||||||
case ALPHAMODE_CLIP:
|
|
||||||
default:
|
|
||||||
mt = TILE_MATERIAL_BASIC;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 shader_id = shdsrc->getShader("object_shader", mt, NDT_NORMAL);
|
bool array_texture = false;
|
||||||
|
if (mat.getTexture(0))
|
||||||
|
array_texture = mat.getTexture(0)->getType() == video::ETT_2D_ARRAY;
|
||||||
|
|
||||||
|
u32 shader_id = shdsrc->getShader(shader, type, NDT_NORMAL, array_texture);
|
||||||
mat.MaterialType = shdsrc->getShaderInfo(shader_id).material;
|
mat.MaterialType = shdsrc->getShaderInfo(shader_id).material;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,10 +364,6 @@ static scene::SMesh *createGenericNodeMesh(Client *client, MapNode n,
|
|||||||
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
|
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
|
||||||
auto &prebuffers = collector.prebuffers[layer];
|
auto &prebuffers = collector.prebuffers[layer];
|
||||||
for (PreMeshBuffer &p : prebuffers) {
|
for (PreMeshBuffer &p : prebuffers) {
|
||||||
if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
|
|
||||||
const FrameSpec &frame = (*p.layer.frames)[0];
|
|
||||||
p.layer.texture = frame.texture;
|
|
||||||
}
|
|
||||||
for (video::S3DVertex &v : p.vertices)
|
for (video::S3DVertex &v : p.vertices)
|
||||||
v.Color.setAlpha(255);
|
v.Color.setAlpha(255);
|
||||||
|
|
||||||
@@ -425,20 +398,19 @@ std::vector<FrameSpec> createAnimationFrames(ITextureSource *tsrc,
|
|||||||
return {{id, texture}};
|
return {{id, texture}};
|
||||||
}
|
}
|
||||||
|
|
||||||
video::ITexture *orginal_texture = tsrc->getTexture(image_name);
|
auto texture_size = tsrc->getTextureDimensions(image_name);
|
||||||
if (!orginal_texture)
|
if (!texture_size.Width || !texture_size.Height)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
int frame_count = 1;
|
int frame_count = 1;
|
||||||
auto orginal_size = orginal_texture->getOriginalSize();
|
animation.determineParams(texture_size, &frame_count, &result_frame_length_ms, nullptr);
|
||||||
animation.determineParams(orginal_size, &frame_count, &result_frame_length_ms, nullptr);
|
|
||||||
|
|
||||||
std::vector<FrameSpec> frames(frame_count);
|
std::vector<FrameSpec> frames(frame_count);
|
||||||
std::ostringstream os(std::ios::binary);
|
std::ostringstream os(std::ios::binary);
|
||||||
for (int i = 0; i < frame_count; i++) {
|
for (int i = 0; i < frame_count; i++) {
|
||||||
os.str("");
|
os.str("");
|
||||||
os << image_name;
|
os << image_name;
|
||||||
animation.getTextureModifer(os, orginal_size, i);
|
animation.getTextureModifer(os, texture_size, i);
|
||||||
|
|
||||||
u32 id;
|
u32 id;
|
||||||
frames[i].texture = tsrc->getTextureForMesh(os.str(), &id);
|
frames[i].texture = tsrc->getTextureForMesh(os.str(), &id);
|
||||||
@@ -559,7 +531,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
|
|||||||
for (u32 i = 0; i < material_count; ++i) {
|
for (u32 i = 0; i < material_count; ++i) {
|
||||||
video::SMaterial &material = m_meshnode->getMaterial(i);
|
video::SMaterial &material = m_meshnode->getMaterial(i);
|
||||||
// apply node's alpha mode
|
// apply node's alpha mode
|
||||||
setAlphaBullshit(material, shdsrc, f.alpha,
|
getAdHocNodeShader(material, shdsrc, "object_shader", f.alpha,
|
||||||
m_buffer_info[i].layer == 1);
|
m_buffer_info[i].layer == 1);
|
||||||
material.forEachTexture([this] (auto &tex) {
|
material.forEachTexture([this] (auto &tex) {
|
||||||
setMaterialFilters(tex, m_bilinear_filter, m_trilinear_filter,
|
setMaterialFilters(tex, m_bilinear_filter, m_trilinear_filter,
|
||||||
@@ -668,6 +640,7 @@ void createItemMesh(Client *client, const ItemDefinition &def,
|
|||||||
ItemMesh *result)
|
ItemMesh *result)
|
||||||
{
|
{
|
||||||
ITextureSource *tsrc = client->getTextureSource();
|
ITextureSource *tsrc = client->getTextureSource();
|
||||||
|
IShaderSource *shdsrc = client->getShaderSource();
|
||||||
const NodeDefManager *ndef = client->getNodeDefManager();
|
const NodeDefManager *ndef = client->getNodeDefManager();
|
||||||
const ContentFeatures &f = ndef->get(def.name);
|
const ContentFeatures &f = ndef->get(def.name);
|
||||||
assert(result);
|
assert(result);
|
||||||
@@ -676,8 +649,8 @@ void createItemMesh(Client *client, const ItemDefinition &def,
|
|||||||
|
|
||||||
scene::SMesh *mesh = nullptr;
|
scene::SMesh *mesh = nullptr;
|
||||||
|
|
||||||
// Shading is on by default
|
// Shading is off by default
|
||||||
result->needs_shading = true;
|
result->needs_shading = false;
|
||||||
|
|
||||||
video::ITexture *inventory_texture = animation_normal.getTexture(0.0f),
|
video::ITexture *inventory_texture = animation_normal.getTexture(0.0f),
|
||||||
*inventory_overlay_texture = animation_overlay.getTexture(0.0f);
|
*inventory_overlay_texture = animation_overlay.getTexture(0.0f);
|
||||||
@@ -691,8 +664,6 @@ void createItemMesh(Client *client, const ItemDefinition &def,
|
|||||||
// overlay is white, if present
|
// overlay is white, if present
|
||||||
result->buffer_info.emplace_back(1, &animation_overlay,
|
result->buffer_info.emplace_back(1, &animation_overlay,
|
||||||
true, video::SColor(0xFFFFFFFF));
|
true, video::SColor(0xFFFFFFFF));
|
||||||
// Items with inventory images do not need shading
|
|
||||||
result->needs_shading = false;
|
|
||||||
} else if (def.type == ITEM_NODE && f.drawtype == NDT_AIRLIKE) {
|
} else if (def.type == ITEM_NODE && f.drawtype == NDT_AIRLIKE) {
|
||||||
// Fallback image for airlike node
|
// Fallback image for airlike node
|
||||||
mesh = getExtrudedMesh(tsrc->getTexture("no_texture_airlike.png"),
|
mesh = getExtrudedMesh(tsrc->getTexture("no_texture_airlike.png"),
|
||||||
@@ -701,7 +672,6 @@ void createItemMesh(Client *client, const ItemDefinition &def,
|
|||||||
|
|
||||||
// overlay is white, if present
|
// overlay is white, if present
|
||||||
result->buffer_info.emplace_back(1, true, video::SColor(0xFFFFFFFF));
|
result->buffer_info.emplace_back(1, true, video::SColor(0xFFFFFFFF));
|
||||||
result->needs_shading = false;
|
|
||||||
} else if (def.type == ITEM_NODE) {
|
} else if (def.type == ITEM_NODE) {
|
||||||
switch (f.drawtype) {
|
switch (f.drawtype) {
|
||||||
case NDT_PLANTLIKE: {
|
case NDT_PLANTLIKE: {
|
||||||
@@ -732,6 +702,7 @@ void createItemMesh(Client *client, const ItemDefinition &def,
|
|||||||
|
|
||||||
mesh = createGenericNodeMesh(client, n, &result->buffer_info, f);
|
mesh = createGenericNodeMesh(client, n, &result->buffer_info, f);
|
||||||
scaleMesh(mesh, v3f(0.12f));
|
scaleMesh(mesh, v3f(0.12f));
|
||||||
|
result->needs_shading = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -740,7 +711,7 @@ void createItemMesh(Client *client, const ItemDefinition &def,
|
|||||||
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
|
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
|
||||||
video::SMaterial &material = buf->getMaterial();
|
video::SMaterial &material = buf->getMaterial();
|
||||||
// apply node's alpha mode
|
// apply node's alpha mode
|
||||||
setAlphaBullshit(material, f.alpha,
|
getAdHocNodeShader(material, shdsrc, "inventory_shader", f.alpha,
|
||||||
result->buffer_info[i].layer == 1);
|
result->buffer_info[i].layer == 1);
|
||||||
material.forEachTexture([] (auto &tex) {
|
material.forEachTexture([] (auto &tex) {
|
||||||
tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST;
|
tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <SColor.h>
|
#include <SColor.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "tile.h"
|
#include "tile.h"
|
||||||
|
#include "nodedef.h"
|
||||||
|
|
||||||
namespace scene
|
namespace scene
|
||||||
{
|
{
|
||||||
@@ -28,6 +29,7 @@ class Client;
|
|||||||
class ITextureSource;
|
class ITextureSource;
|
||||||
struct ItemDefinition;
|
struct ItemDefinition;
|
||||||
struct TileAnimationParams;
|
struct TileAnimationParams;
|
||||||
|
class IShaderSource;
|
||||||
class ShadowRenderer;
|
class ShadowRenderer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -180,6 +182,19 @@ std::vector<FrameSpec> createAnimationFrames(ITextureSource *tsrc,
|
|||||||
scene::SMesh *getExtrudedMesh(video::ITexture *texture,
|
scene::SMesh *getExtrudedMesh(video::ITexture *texture,
|
||||||
video::ITexture *overlay_texture = nullptr);
|
video::ITexture *overlay_texture = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the material's shader with a custom one while respecting the usual
|
||||||
|
* things expected of node rendering (texture type, alpha mode, overlay).
|
||||||
|
* Call this after `TileLayer::applyMaterialOptions`.
|
||||||
|
* @param mat material to modify
|
||||||
|
* @param shdsrc shader source
|
||||||
|
* @param shader name of shader
|
||||||
|
* @param mode alpha mode from nodedef
|
||||||
|
* @param layer index of this layer
|
||||||
|
*/
|
||||||
|
void getAdHocNodeShader(video::SMaterial &mat, IShaderSource *shdsrc,
|
||||||
|
const char *shader, AlphaMode mode, int layer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTE: The item mesh is only suitable for inventory rendering (due to its
|
* NOTE: The item mesh is only suitable for inventory rendering (due to its
|
||||||
* material types). In-world rendering of items must go through WieldMeshSceneNode.
|
* material types). In-world rendering of items must go through WieldMeshSceneNode.
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ void set_default_settings()
|
|||||||
settings->setDefault("connected_glass", "false");
|
settings->setDefault("connected_glass", "false");
|
||||||
settings->setDefault("smooth_lighting", "true");
|
settings->setDefault("smooth_lighting", "true");
|
||||||
settings->setDefault("performance_tradeoffs", "false");
|
settings->setDefault("performance_tradeoffs", "false");
|
||||||
|
settings->setDefault("array_texture_max", "65535");
|
||||||
settings->setDefault("lighting_alpha", "0.0");
|
settings->setDefault("lighting_alpha", "0.0");
|
||||||
settings->setDefault("lighting_beta", "1.5");
|
settings->setDefault("lighting_beta", "1.5");
|
||||||
settings->setDefault("display_gamma", "1.0");
|
settings->setDefault("display_gamma", "1.0");
|
||||||
|
|||||||
371
src/nodedef.cpp
371
src/nodedef.cpp
@@ -269,6 +269,10 @@ void TileDef::deSerialize(std::istream &is, NodeDrawType drawtype, u16 protocol_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
TextureSettings
|
||||||
|
*/
|
||||||
|
|
||||||
void TextureSettings::readSettings()
|
void TextureSettings::readSettings()
|
||||||
{
|
{
|
||||||
connected_glass = g_settings->getBool("connected_glass");
|
connected_glass = g_settings->getBool("connected_glass");
|
||||||
@@ -305,6 +309,56 @@ void TextureSettings::readSettings()
|
|||||||
autoscale_mode = AUTOSCALE_DISABLE;
|
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
|
ContentFeatures
|
||||||
*/
|
*/
|
||||||
@@ -321,8 +375,10 @@ ContentFeatures::~ContentFeatures()
|
|||||||
delete tiles[j].layers[0].frames;
|
delete tiles[j].layers[0].frames;
|
||||||
delete tiles[j].layers[1].frames;
|
delete tiles[j].layers[1].frames;
|
||||||
}
|
}
|
||||||
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
|
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
|
||||||
delete special_tiles[j].layers[0].frames;
|
delete special_tiles[j].layers[0].frames;
|
||||||
|
delete special_tiles[j].layers[1].frames;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,20 +726,47 @@ void ContentFeatures::deSerialize(std::istream &is, u16 protocol_version)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if CHECK_CLIENT_BUILD()
|
#if CHECK_CLIENT_BUILD()
|
||||||
static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
|
struct TileAttribContext {
|
||||||
const TileSpec &tile, const TileDef &tiledef, video::SColor color,
|
ITextureSource *tsrc;
|
||||||
MaterialType material_type, u32 shader_id, bool backface_culling,
|
PreLoadedTextures *texture_pool;
|
||||||
const TextureSettings &tsettings)
|
video::SColor base_color;
|
||||||
{
|
const TextureSettings &tsettings;
|
||||||
layer->shader_id = shader_id;
|
};
|
||||||
layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
|
|
||||||
layer->material_type = material_type;
|
|
||||||
|
|
||||||
|
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 has_scale = tiledef.scale > 0;
|
||||||
bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
|
bool use_autoscale = tsettings.autoscale_mode == AUTOSCALE_FORCE ||
|
||||||
(tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
|
(tsettings.autoscale_mode == AUTOSCALE_ENABLE && !has_scale);
|
||||||
if (use_autoscale && layer->texture) {
|
if (use_autoscale) {
|
||||||
auto texture_size = layer->texture->getOriginalSize();
|
|
||||||
float base_size = tsettings.node_texture_size;
|
float base_size = tsettings.node_texture_size;
|
||||||
float size = std::fmin(texture_size.Width, texture_size.Height);
|
float size = std::fmin(texture_size.Width, texture_size.Height);
|
||||||
layer->scale = std::fmax(base_size, size) / base_size;
|
layer->scale = std::fmax(base_size, size) / base_size;
|
||||||
@@ -695,9 +778,10 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
|
|||||||
if (!tile.world_aligned)
|
if (!tile.world_aligned)
|
||||||
layer->scale = 1;
|
layer->scale = 1;
|
||||||
|
|
||||||
// Material flags
|
// Material
|
||||||
|
layer->material_type = material_type;
|
||||||
layer->material_flags = 0;
|
layer->material_flags = 0;
|
||||||
if (backface_culling)
|
if (tiledef.backface_culling)
|
||||||
layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
|
layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
|
||||||
if (tiledef.animation.type != TAT_NONE)
|
if (tiledef.animation.type != TAT_NONE)
|
||||||
layer->material_flags |= MATERIAL_FLAG_ANIMATION;
|
layer->material_flags |= MATERIAL_FLAG_ANIMATION;
|
||||||
@@ -711,7 +795,7 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
|
|||||||
if (tiledef.has_color)
|
if (tiledef.has_color)
|
||||||
layer->color = tiledef.color;
|
layer->color = tiledef.color;
|
||||||
else
|
else
|
||||||
layer->color = color;
|
layer->color = context.base_color;
|
||||||
|
|
||||||
// Animation
|
// Animation
|
||||||
if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
|
if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
|
||||||
@@ -722,10 +806,74 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
|
|||||||
layer->frames = new std::vector<FrameSpec>(frames);
|
layer->frames = new std::vector<FrameSpec>(frames);
|
||||||
layer->animation_frame_count = layer->frames->size();
|
layer->animation_frame_count = layer->frames->size();
|
||||||
layer->animation_frame_length_ms = frame_length_ms;
|
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 {
|
} else {
|
||||||
layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
|
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)
|
static bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
|
||||||
@@ -743,8 +891,32 @@ static bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType d
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @return maximum number of layers in array textures we can use (0 if unsupported)
|
||||||
|
static size_t getArrayTextureMax(video::IVideoDriver *driver)
|
||||||
|
{
|
||||||
|
// needs to actually support array textures
|
||||||
|
if (!driver->queryFeature(video::EVDF_TEXTURE_2D_ARRAY))
|
||||||
|
return 0;
|
||||||
|
// must not be the legacy driver, due to custom vertex format
|
||||||
|
if (driver->getDriverType() == video::EDT_OPENGL)
|
||||||
|
return 0;
|
||||||
|
// doesn't work on GLES yet (TODO)
|
||||||
|
if (driver->getDriverType() == video::EDT_OGLES2)
|
||||||
|
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,
|
void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
|
||||||
scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
|
Client *client, PreLoadedTextures *texture_pool,
|
||||||
|
const TextureSettings &tsettings)
|
||||||
{
|
{
|
||||||
// Figure out the actual tiles to use
|
// Figure out the actual tiles to use
|
||||||
TileDef tdef[6];
|
TileDef tdef[6];
|
||||||
@@ -767,9 +939,7 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||||||
|
|
||||||
bool is_liquid = false;
|
bool is_liquid = false;
|
||||||
|
|
||||||
MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
|
MaterialType material_type = alpha_mode_to_material_type(alpha);
|
||||||
TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
|
|
||||||
TILE_MATERIAL_ALPHA);
|
|
||||||
|
|
||||||
switch (drawtype) {
|
switch (drawtype) {
|
||||||
default:
|
default:
|
||||||
@@ -880,15 +1050,21 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
|
const bool texture_2d_array = getArrayTextureMax(RenderingEngine::get_video_driver()) > 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;
|
||||||
|
};
|
||||||
|
|
||||||
MaterialType overlay_material = material_type;
|
ShaderIds tile_shader = getNodeShader(material_type, drawtype);
|
||||||
if (overlay_material == TILE_MATERIAL_OPAQUE)
|
|
||||||
overlay_material = TILE_MATERIAL_BASIC;
|
|
||||||
else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
|
|
||||||
overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
|
|
||||||
|
|
||||||
u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, 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
|
// minimap pixel color = average color of top tile
|
||||||
if (tsettings.enable_minimap && drawtype != NDT_AIRLIKE && !tdef[0].name.empty())
|
if (tsettings.enable_minimap && drawtype != NDT_AIRLIKE && !tdef[0].name.empty())
|
||||||
@@ -904,16 +1080,18 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||||||
|
|
||||||
// Tiles (fill in f->tiles[])
|
// Tiles (fill in f->tiles[])
|
||||||
bool any_polygon_offset = false;
|
bool any_polygon_offset = false;
|
||||||
|
TileAttribContext tac{tsrc, texture_pool, color, tsettings};
|
||||||
|
|
||||||
for (u16 j = 0; j < 6; j++) {
|
for (u16 j = 0; j < 6; j++) {
|
||||||
tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
|
tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
|
||||||
tsettings.world_aligned_mode, drawtype);
|
tsettings.world_aligned_mode, drawtype);
|
||||||
fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
|
fillTileAttribs(&tiles[j].layers[0], tac, tiles[j], tdef[j],
|
||||||
color, material_type, tile_shader,
|
material_type, tile_shader);
|
||||||
tdef[j].backface_culling, tsettings);
|
if (!tdef_overlay[j].name.empty()) {
|
||||||
if (!tdef_overlay[j].name.empty())
|
tdef_overlay[j].backface_culling = tdef[j].backface_culling;
|
||||||
fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
|
fillTileAttribs(&tiles[j].layers[1], tac, tiles[j], tdef_overlay[j],
|
||||||
color, overlay_material, overlay_shader,
|
overlay_material, overlay_shader);
|
||||||
tdef[j].backface_culling, tsettings);
|
}
|
||||||
|
|
||||||
tiles[j].layers[0].need_polygon_offset = !tiles[j].layers[1].empty();
|
tiles[j].layers[0].need_polygon_offset = !tiles[j].layers[1].empty();
|
||||||
any_polygon_offset |= tiles[j].layers[0].need_polygon_offset;
|
any_polygon_offset |= tiles[j].layers[0].need_polygon_offset;
|
||||||
@@ -936,13 +1114,14 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||||||
else if (waving == 2)
|
else if (waving == 2)
|
||||||
special_material = TILE_MATERIAL_WAVING_LEAVES;
|
special_material = TILE_MATERIAL_WAVING_LEAVES;
|
||||||
}
|
}
|
||||||
u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
|
|
||||||
|
ShaderIds special_shader = getNodeShader(special_material, drawtype);
|
||||||
|
|
||||||
// Special tiles (fill in f->special_tiles[])
|
// Special tiles (fill in f->special_tiles[])
|
||||||
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
|
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
|
||||||
fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
|
fillTileAttribs(&special_tiles[j].layers[0], tac,
|
||||||
color, special_material, special_shader,
|
special_tiles[j], tdef_spec[j], special_material, special_shader);
|
||||||
tdef_spec[j].backface_culling, tsettings);
|
}
|
||||||
|
|
||||||
if (param_type_2 == CPT2_COLOR ||
|
if (param_type_2 == CPT2_COLOR ||
|
||||||
param_type_2 == CPT2_COLORED_FACEDIR ||
|
param_type_2 == CPT2_COLORED_FACEDIR ||
|
||||||
@@ -951,6 +1130,13 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||||||
param_type_2 == CPT2_COLORED_DEGROTATE)
|
param_type_2 == CPT2_COLORED_DEGROTATE)
|
||||||
palette = tsrc->getPalette(palette_name);
|
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()) {
|
if (drawtype == NDT_MESH && !mesh.empty()) {
|
||||||
// Note: By freshly reading, we get an unencumbered mesh.
|
// Note: By freshly reading, we get an unencumbered mesh.
|
||||||
if (scene::IMesh *src_mesh = client->getMesh(mesh)) {
|
if (scene::IMesh *src_mesh = client->getMesh(mesh)) {
|
||||||
@@ -979,13 +1165,27 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||||||
// TODO this should be done consistently when the mesh is loaded
|
// TODO this should be done consistently when the mesh is loaded
|
||||||
infostream << "ContentFeatures: recalculating normals for mesh "
|
infostream << "ContentFeatures: recalculating normals for mesh "
|
||||||
<< mesh << std::endl;
|
<< mesh << std::endl;
|
||||||
meshmanip->recalculateNormals(mesh_ptr, true, false);
|
manip->recalculateNormals(mesh_ptr, true, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mesh_ptr = nullptr;
|
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
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1021,13 +1221,13 @@ void NodeDefManager::clear()
|
|||||||
m_next_id = 0;
|
m_next_id = 0;
|
||||||
m_selection_box_union.reset(0,0,0);
|
m_selection_box_union.reset(0,0,0);
|
||||||
m_selection_box_int_union.reset(0,0,0);
|
m_selection_box_int_union.reset(0,0,0);
|
||||||
|
#if CHECK_CLIENT_BUILD()
|
||||||
|
m_leaves_materials.clear();
|
||||||
|
#endif
|
||||||
|
|
||||||
resetNodeResolveState();
|
resetNodeResolveState();
|
||||||
|
|
||||||
u32 initial_length = 0;
|
constexpr u32 initial_length = std::max({CONTENT_UNKNOWN, CONTENT_AIR, CONTENT_IGNORE}) + 1;
|
||||||
initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
|
|
||||||
initial_length = MYMAX(initial_length, CONTENT_AIR + 1);
|
|
||||||
initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1);
|
|
||||||
m_content_features.resize(initial_length);
|
m_content_features.resize(initial_length);
|
||||||
|
|
||||||
// Set CONTENT_UNKNOWN
|
// Set CONTENT_UNKNOWN
|
||||||
@@ -1164,7 +1364,7 @@ content_t NodeDefManager::allocateId()
|
|||||||
* @param[in] boxes the vector containing the boxes
|
* @param[in] boxes the vector containing the boxes
|
||||||
* @param[in, out] box_union the union of the arguments
|
* @param[in, out] box_union the union of the arguments
|
||||||
*/
|
*/
|
||||||
void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
|
static void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
|
||||||
{
|
{
|
||||||
for (const aabb3f &box : boxes) {
|
for (const aabb3f &box : boxes) {
|
||||||
box_union->addInternalBox(box);
|
box_union->addInternalBox(box);
|
||||||
@@ -1180,7 +1380,7 @@ void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
|
|||||||
* can be rotated
|
* can be rotated
|
||||||
* @param[in, out] box_union the union of the arguments
|
* @param[in, out] box_union the union of the arguments
|
||||||
*/
|
*/
|
||||||
void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
|
static void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
|
||||||
aabb3f *box_union)
|
aabb3f *box_union)
|
||||||
{
|
{
|
||||||
switch(nodebox.type) {
|
switch(nodebox.type) {
|
||||||
@@ -1450,32 +1650,101 @@ void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &o
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if CHECK_CLIENT_BUILD()
|
||||||
void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_args)
|
void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_args)
|
||||||
{
|
{
|
||||||
#if CHECK_CLIENT_BUILD()
|
|
||||||
infostream << "NodeDefManager::updateTextures(): Updating "
|
infostream << "NodeDefManager::updateTextures(): Updating "
|
||||||
"textures in node definitions" << std::endl;
|
"textures in node definitions" << std::endl;
|
||||||
|
|
||||||
Client *client = (Client *)gamedef;
|
Client *client = (Client *)gamedef;
|
||||||
ITextureSource *tsrc = client->tsrc();
|
ITextureSource *tsrc = client->tsrc();
|
||||||
IShaderSource *shdsrc = client->getShaderSource();
|
IShaderSource *shdsrc = client->getShaderSource();
|
||||||
auto smgr = client->getSceneManager();
|
|
||||||
scene::IMeshManipulator *meshmanip = smgr->getMeshManipulator();
|
|
||||||
TextureSettings tsettings;
|
TextureSettings tsettings;
|
||||||
tsettings.readSettings();
|
tsettings.readSettings();
|
||||||
|
|
||||||
tsrc->setImageCaching(true);
|
tsrc->setImageCaching(true);
|
||||||
|
const u32 size = m_content_features.size();
|
||||||
|
|
||||||
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++) {
|
for (u32 i = 0; i < size; i++) {
|
||||||
ContentFeatures *f = &(m_content_features[i]);
|
ContentFeatures *f = &(m_content_features[i]);
|
||||||
f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
|
f->preUpdateTextures(tsrc, pool, tsettings);
|
||||||
client->showUpdateProgressTexture(progress_callback_args, i, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* texture pre-loading stage */
|
||||||
|
const size_t arraymax = getArrayTextureMax(RenderingEngine::get_video_driver());
|
||||||
|
// 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);
|
tsrc->setImageCaching(false);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
|
void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,7 +14,9 @@
|
|||||||
#if CHECK_CLIENT_BUILD()
|
#if CHECK_CLIENT_BUILD()
|
||||||
#include "client/tile.h"
|
#include "client/tile.h"
|
||||||
#include <IMeshManipulator.h>
|
#include <IMeshManipulator.h>
|
||||||
|
#include <unordered_set>
|
||||||
class Client;
|
class Client;
|
||||||
|
struct PreLoadedTextures;
|
||||||
#endif
|
#endif
|
||||||
#include "itemgroup.h"
|
#include "itemgroup.h"
|
||||||
#include "sound.h" // SoundSpec
|
#include "sound.h" // SoundSpec
|
||||||
@@ -259,6 +261,24 @@ enum AlphaMode : u8 {
|
|||||||
AlphaMode_END // Dummy for validity check
|
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)
|
Stand-alone definition of a TileSpec (basically a server-side TileSpec)
|
||||||
@@ -505,8 +525,13 @@ struct ContentFeatures
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if CHECK_CLIENT_BUILD()
|
#if CHECK_CLIENT_BUILD()
|
||||||
|
void preUpdateTextures(ITextureSource *tsrc,
|
||||||
|
std::unordered_set<std::string> &pool, const TextureSettings &tsettings);
|
||||||
void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
|
void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
|
||||||
scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings);
|
Client *client, PreLoadedTextures *texture_pool,
|
||||||
|
const TextureSettings &tsettings);
|
||||||
|
void updateMesh(Client *client, const TextureSettings &tsettings);
|
||||||
|
void collectMaterials(std::vector<u32> &leaves_materials);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -675,8 +700,9 @@ public:
|
|||||||
*/
|
*/
|
||||||
void applyTextureOverrides(const std::vector<TextureOverride> &overrides);
|
void applyTextureOverrides(const std::vector<TextureOverride> &overrides);
|
||||||
|
|
||||||
|
#if CHECK_CLIENT_BUILD()
|
||||||
/*!
|
/*!
|
||||||
* Only the client uses this. Loads textures and shaders required for
|
* Loads textures and shaders required for
|
||||||
* rendering the nodes.
|
* rendering the nodes.
|
||||||
* @param gamedef must be a Client.
|
* @param gamedef must be a Client.
|
||||||
* @param progress_cbk called each time a node is loaded. Arguments:
|
* @param progress_cbk called each time a node is loaded. Arguments:
|
||||||
@@ -685,6 +711,7 @@ public:
|
|||||||
* @param progress_cbk_args passed to the callback function
|
* @param progress_cbk_args passed to the callback function
|
||||||
*/
|
*/
|
||||||
void updateTextures(IGameDef *gamedef, void *progress_cbk_args);
|
void updateTextures(IGameDef *gamedef, void *progress_cbk_args);
|
||||||
|
#endif
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Writes the content of this manager to the given output stream.
|
* Writes the content of this manager to the given output stream.
|
||||||
@@ -728,6 +755,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
void resolveCrossrefs();
|
void resolveCrossrefs();
|
||||||
|
|
||||||
|
#if CHECK_CLIENT_BUILD()
|
||||||
|
// Set of all shader IDs used by leaves-like nodes
|
||||||
|
// (kind of a hack but is needed for dynamic shadows)
|
||||||
|
std::vector<u32> m_leaves_materials;
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*!
|
/*!
|
||||||
* Resets the manager to its initial state.
|
* Resets the manager to its initial state.
|
||||||
|
|||||||
Reference in New Issue
Block a user