mirror of
https://github.com/minetest/minetest.git
synced 2024-12-27 16:37:30 +01:00
Real global textures (#6105)
* Real global textures * Add world-aligned textures * Update minimal to support world-aligned tiles * Update minimal
This commit is contained in:
parent
6bab695479
commit
75320e7e88
@ -431,7 +431,9 @@ texture_clean_transparent (Clean transparent textures) bool false
|
||||
# memory. Powers of 2 are recommended. Setting this higher than 1 may not
|
||||
# have a visible effect unless bilinear/trilinear/anisotropic filtering is
|
||||
# enabled.
|
||||
texture_min_size (Minimum texture size for filters) int 64
|
||||
# This is also used as the base node texture size for world-aligned
|
||||
# texture autoscaling.
|
||||
texture_min_size (Minimum texture size) int 64
|
||||
|
||||
# Experimental option, might cause visible spaces between blocks
|
||||
# when set to higher number than 0.
|
||||
@ -687,6 +689,22 @@ fog_start (Fog Start) float 0.4 0.0 0.99
|
||||
# Makes all liquids opaque
|
||||
opaque_water (Opaque liquids) bool false
|
||||
|
||||
# Textures on a node may be aligned either to the node or to the world.
|
||||
# The former mode suits better things like machines, furniture, etc., while
|
||||
# the latter makes stairs and microblocks fit surroundings better.
|
||||
# However, as this possibility is new, thus may not be used by older servers,
|
||||
# this option allows enforcing it for certain node types. Note though that
|
||||
# that is considered EXPERIMENTAL and may not work properly.
|
||||
world_aligned_mode (World-aligned textures mode) enum enable disable,enable,force_solid,force_nodebox
|
||||
|
||||
# World-aligned textures may be scaled to span several nodes. However,
|
||||
# the server may not send the scale you want, especially if you use
|
||||
# a specially-designed texture pack; with this option, the client tries
|
||||
# to determine the scale automatically basing on the texture size.
|
||||
# See also texture_min_size.
|
||||
# Warning: this option is EXPERIMENTAL!
|
||||
autoscale_mode (Autoscaling mode) enum disable disable,enable,force
|
||||
|
||||
# Show entity selection boxes
|
||||
show_entity_selectionbox (Show entity selection boxes) bool true
|
||||
|
||||
|
@ -284,11 +284,19 @@ on top of `cobble.png`.
|
||||
|
||||
### Advanced texture modifiers
|
||||
|
||||
#### `[crack:<n>:<p>`
|
||||
#### Crack
|
||||
* `[crack:<n>:<p>`
|
||||
* `[cracko:<n>:<p>`
|
||||
* `[crack:<t>:<n>:<p>`
|
||||
* `[cracko:<t>:<n>:<p>`
|
||||
|
||||
Parameters:
|
||||
* `<t>` = tile count (in each direction)
|
||||
* `<n>` = animation frame count
|
||||
* `<p>` = current animation frame
|
||||
|
||||
Draw a step of the crack animation on the texture.
|
||||
`crack` draws it normally, while `cracko` lays it over, keeping transparent pixels intact.
|
||||
|
||||
Example:
|
||||
|
||||
@ -4420,12 +4428,21 @@ Definition tables
|
||||
* `"image.png"`
|
||||
* `{name="image.png", animation={Tile Animation definition}}`
|
||||
* `{name="image.png", backface_culling=bool, tileable_vertical=bool,
|
||||
tileable_horizontal=bool}`
|
||||
tileable_horizontal=bool, align_style="node"/"world"/"user", scale=int}`
|
||||
* backface culling enabled by default for most nodes
|
||||
* tileable flags are info for shaders, how they should treat texture
|
||||
when displacement mapping is used
|
||||
Directions are from the point of view of the tile texture,
|
||||
not the node it's on
|
||||
* align style determines whether the texture will be rotated with the node
|
||||
or kept aligned with its surroundings. "user" means that client
|
||||
setting will be used, similar to `glasslike_framed_optional`.
|
||||
Note: supported by solid nodes and nodeboxes only.
|
||||
* scale is used to make texture span several (exactly `scale`) nodes,
|
||||
instead of just one, in each direction. Works for world-aligned
|
||||
textures only.
|
||||
Note that as the effect is applied on per-mapblock basis, `16` should
|
||||
be equally divisible by `scale` or you may get wrong results.
|
||||
* `{name="image.png", color=ColorSpec}`
|
||||
* the texture's color will be multiplied with this color.
|
||||
* the tile's color overrides the owning node's color in all cases.
|
||||
|
@ -1,2 +1,2 @@
|
||||
default
|
||||
|
||||
stairs
|
||||
|
@ -501,6 +501,57 @@ minetest.register_node("experimental:tester_node_1", {
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_node("experimental:tiled", {
|
||||
description = "Tiled stone",
|
||||
tiles = {{
|
||||
name = "experimental_tiled.png",
|
||||
align_style = "world",
|
||||
scale = 8,
|
||||
}},
|
||||
groups = {cracky=2},
|
||||
})
|
||||
|
||||
stairs.register_stair_and_slab("tiled_n", "experimental:tiled",
|
||||
{cracky=2},
|
||||
{{name="experimental_tiled.png", align_style="node", scale=8}},
|
||||
"Tiled stair (node-aligned)",
|
||||
"Tiled slab (node-aligned)")
|
||||
|
||||
stairs.register_stair_and_slab("tiled", "experimantal:tiled",
|
||||
{cracky=2},
|
||||
{{name="experimental_tiled.png", align_style="world", scale=8}},
|
||||
"Tiled stair",
|
||||
"Tiled slab")
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'experimental:tiled 4',
|
||||
recipe = {
|
||||
{'default:cobble', '', 'default:cobble'},
|
||||
{'', '', ''},
|
||||
{'default:cobble', '', 'default:cobble'},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'stairs:stair_tiled',
|
||||
recipe = {{'stairs:stair_tiled_n'}}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'stairs:stair_tiled_n',
|
||||
recipe = {{'stairs:stair_tiled'}}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'stairs:slab_tiled',
|
||||
recipe = {{'stairs:slab_tiled_n'}}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'stairs:slab_tiled_n',
|
||||
recipe = {{'stairs:slab_tiled'}}
|
||||
})
|
||||
|
||||
minetest.register_craftitem("experimental:tester_tool_1", {
|
||||
description = "Tester Tool 1",
|
||||
inventory_image = "experimental_tester_tool_1.png",
|
||||
|
BIN
games/minimal/mods/experimental/textures/experimental_tiled.png
Normal file
BIN
games/minimal/mods/experimental/textures/experimental_tiled.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
@ -2,7 +2,7 @@ stairs = {}
|
||||
|
||||
-- Node will be called stairs:stair_<subname>
|
||||
function stairs.register_stair(subname, recipeitem, groups, images, description)
|
||||
minetest.register_node("stairs:stair_" .. subname, {
|
||||
minetest.register_node(":stairs:stair_" .. subname, {
|
||||
description = description,
|
||||
drawtype = "nodebox",
|
||||
tiles = images,
|
||||
@ -31,7 +31,7 @@ end
|
||||
|
||||
-- Node will be called stairs:slab_<subname>
|
||||
function stairs.register_slab(subname, recipeitem, groups, images, description)
|
||||
minetest.register_node("stairs:slab_" .. subname, {
|
||||
minetest.register_node(":stairs:slab_" .. subname, {
|
||||
description = description,
|
||||
drawtype = "nodebox",
|
||||
tiles = images,
|
||||
|
@ -489,6 +489,8 @@
|
||||
# memory. Powers of 2 are recommended. Setting this higher than 1 may not
|
||||
# have a visible effect unless bilinear/trilinear/anisotropic filtering is
|
||||
# enabled.
|
||||
# This is also used as the base node texture size for world-aligned
|
||||
# texture autoscaling.
|
||||
# type: int
|
||||
# texture_min_size = 64
|
||||
|
||||
@ -808,6 +810,24 @@
|
||||
# type: bool
|
||||
# opaque_water = false
|
||||
|
||||
# Textures on a node may be aligned either to the node or to the world.
|
||||
# The former mode sutis better things like machines, furniture, etc., while
|
||||
# the latter makes stairs and microblocks fit surroundings better.
|
||||
# However, as this possibility is new, thus may not be used by older servers,
|
||||
# this option allows enforcing it for certain node types. Note though that
|
||||
# that is considered EXPERIMENTAL and may not work properly.
|
||||
# type: enum values: disable, enable, force_solid, force_nodebox
|
||||
# world_aligned_mode = enable
|
||||
|
||||
# World-aligned textures may be scaled to span several nodes. However,
|
||||
# the server may not send the scale you want, especially if you use
|
||||
# a specially-designed texture pack; with this option, the client tries
|
||||
# to determine the scale automatically basing on the texture size.
|
||||
# See also texture_min_size.
|
||||
# Warning: this option is EXPERIMENTAL!
|
||||
# type: enum values: disable, enable, force
|
||||
# autoscale_mode = disable
|
||||
|
||||
# Show entity selection boxes
|
||||
# type: bool
|
||||
# show_entity_selectionbox = true
|
||||
|
@ -545,7 +545,7 @@ static void apply_mask(video::IImage *mask, video::IImage *dst,
|
||||
// Draw or overlay a crack
|
||||
static void draw_crack(video::IImage *crack, video::IImage *dst,
|
||||
bool use_overlay, s32 frame_count, s32 progression,
|
||||
video::IVideoDriver *driver);
|
||||
video::IVideoDriver *driver, u8 tiles = 1);
|
||||
|
||||
// Brighten image
|
||||
void brighten(video::IImage *image);
|
||||
@ -1280,11 +1280,22 @@ bool TextureSource::generateImagePart(std::string part_of_name,
|
||||
}
|
||||
|
||||
// Crack image number and overlay option
|
||||
// Format: crack[o][:<tiles>]:<frame_count>:<frame>
|
||||
bool use_overlay = (part_of_name[6] == 'o');
|
||||
Strfnd sf(part_of_name);
|
||||
sf.next(":");
|
||||
s32 frame_count = stoi(sf.next(":"));
|
||||
s32 progression = stoi(sf.next(":"));
|
||||
s32 tiles = 1;
|
||||
// Check whether there is the <tiles> argument, that is,
|
||||
// whether there are 3 arguments. If so, shift values
|
||||
// as the first and not the last argument is optional.
|
||||
auto s = sf.next(":");
|
||||
if (!s.empty()) {
|
||||
tiles = frame_count;
|
||||
frame_count = progression;
|
||||
progression = stoi(s);
|
||||
}
|
||||
|
||||
if (progression >= 0) {
|
||||
/*
|
||||
@ -1299,7 +1310,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
|
||||
if (img_crack) {
|
||||
draw_crack(img_crack, baseimg,
|
||||
use_overlay, frame_count,
|
||||
progression, driver);
|
||||
progression, driver, tiles);
|
||||
img_crack->drop();
|
||||
}
|
||||
}
|
||||
@ -2102,74 +2113,78 @@ static void apply_mask(video::IImage *mask, video::IImage *dst,
|
||||
}
|
||||
}
|
||||
|
||||
video::IImage *create_crack_image(video::IImage *crack, s32 frame_index,
|
||||
core::dimension2d<u32> size, u8 tiles, video::IVideoDriver *driver)
|
||||
{
|
||||
core::dimension2d<u32> strip_size = crack->getDimension();
|
||||
core::dimension2d<u32> frame_size(strip_size.Width, strip_size.Width);
|
||||
core::dimension2d<u32> tile_size(size / tiles);
|
||||
s32 frame_count = strip_size.Height / strip_size.Width;
|
||||
if (frame_index >= frame_count)
|
||||
frame_index = frame_count - 1;
|
||||
core::rect<s32> frame(v2s32(0, frame_index * frame_size.Height), frame_size);
|
||||
video::IImage *result = nullptr;
|
||||
|
||||
// extract crack frame
|
||||
video::IImage *crack_tile = driver->createImage(video::ECF_A8R8G8B8, tile_size);
|
||||
if (!crack_tile)
|
||||
return nullptr;
|
||||
if (tile_size == frame_size) {
|
||||
crack->copyTo(crack_tile, v2s32(0, 0), frame);
|
||||
} else {
|
||||
video::IImage *crack_frame = driver->createImage(video::ECF_A8R8G8B8, frame_size);
|
||||
if (!crack_frame)
|
||||
goto exit__has_tile;
|
||||
crack->copyTo(crack_frame, v2s32(0, 0), frame);
|
||||
crack_frame->copyToScaling(crack_tile);
|
||||
crack_frame->drop();
|
||||
}
|
||||
if (tiles == 1)
|
||||
return crack_tile;
|
||||
|
||||
// tile it
|
||||
result = driver->createImage(video::ECF_A8R8G8B8, size);
|
||||
if (!result)
|
||||
goto exit__has_tile;
|
||||
result->fill({});
|
||||
for (u8 i = 0; i < tiles; i++)
|
||||
for (u8 j = 0; j < tiles; j++)
|
||||
crack_tile->copyTo(result, v2s32(i * tile_size.Width, j * tile_size.Height));
|
||||
|
||||
exit__has_tile:
|
||||
crack_tile->drop();
|
||||
return result;
|
||||
}
|
||||
|
||||
static void draw_crack(video::IImage *crack, video::IImage *dst,
|
||||
bool use_overlay, s32 frame_count, s32 progression,
|
||||
video::IVideoDriver *driver)
|
||||
video::IVideoDriver *driver, u8 tiles)
|
||||
{
|
||||
// Dimension of destination image
|
||||
core::dimension2d<u32> dim_dst = dst->getDimension();
|
||||
// Dimension of original image
|
||||
core::dimension2d<u32> dim_crack = crack->getDimension();
|
||||
// Count of crack stages
|
||||
s32 crack_count = dim_crack.Height / dim_crack.Width;
|
||||
// Limit frame_count
|
||||
if (frame_count > (s32) dim_dst.Height)
|
||||
frame_count = dim_dst.Height;
|
||||
if (frame_count < 1)
|
||||
frame_count = 1;
|
||||
// Limit progression
|
||||
if (progression > crack_count-1)
|
||||
progression = crack_count-1;
|
||||
// Dimension of a single crack stage
|
||||
core::dimension2d<u32> dim_crack_cropped(
|
||||
dim_crack.Width,
|
||||
dim_crack.Width
|
||||
);
|
||||
// Dimension of the scaled crack stage,
|
||||
// which is the same as the dimension of a single destination frame
|
||||
core::dimension2d<u32> dim_crack_scaled(
|
||||
core::dimension2d<u32> frame_size(
|
||||
dim_dst.Width,
|
||||
dim_dst.Height / frame_count
|
||||
);
|
||||
// Create cropped and scaled crack images
|
||||
video::IImage *crack_cropped = driver->createImage(
|
||||
video::ECF_A8R8G8B8, dim_crack_cropped);
|
||||
video::IImage *crack_scaled = driver->createImage(
|
||||
video::ECF_A8R8G8B8, dim_crack_scaled);
|
||||
video::IImage *crack_scaled = create_crack_image(crack, progression,
|
||||
frame_size, tiles, driver);
|
||||
if (!crack_scaled)
|
||||
return;
|
||||
|
||||
if (crack_cropped && crack_scaled)
|
||||
{
|
||||
// Crop crack image
|
||||
v2s32 pos_crack(0, progression*dim_crack.Width);
|
||||
crack->copyTo(crack_cropped,
|
||||
v2s32(0,0),
|
||||
core::rect<s32>(pos_crack, dim_crack_cropped));
|
||||
// Scale crack image by copying
|
||||
crack_cropped->copyToScaling(crack_scaled);
|
||||
// Copy or overlay crack image onto each frame
|
||||
for (s32 i = 0; i < frame_count; ++i)
|
||||
{
|
||||
v2s32 dst_pos(0, dim_crack_scaled.Height * i);
|
||||
if (use_overlay)
|
||||
{
|
||||
blit_with_alpha_overlay(crack_scaled, dst,
|
||||
v2s32(0,0), dst_pos,
|
||||
dim_crack_scaled);
|
||||
}
|
||||
else
|
||||
{
|
||||
blit_with_alpha(crack_scaled, dst,
|
||||
v2s32(0,0), dst_pos,
|
||||
dim_crack_scaled);
|
||||
}
|
||||
}
|
||||
auto blit = use_overlay ? blit_with_alpha_overlay : blit_with_alpha;
|
||||
for (s32 i = 0; i < frame_count; ++i) {
|
||||
v2s32 dst_pos(0, frame_size.Height * i);
|
||||
blit(crack_scaled, dst, v2s32(0,0), dst_pos, frame_size);
|
||||
}
|
||||
|
||||
if (crack_scaled)
|
||||
crack_scaled->drop();
|
||||
|
||||
if (crack_cropped)
|
||||
crack_cropped->drop();
|
||||
crack_scaled->drop();
|
||||
}
|
||||
|
||||
void brighten(video::IImage *image)
|
||||
|
@ -209,7 +209,8 @@ struct TileLayer
|
||||
texture_id == other.texture_id &&
|
||||
material_type == other.material_type &&
|
||||
material_flags == other.material_flags &&
|
||||
color == other.color;
|
||||
color == other.color &&
|
||||
scale == other.scale;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -298,6 +299,8 @@ struct TileLayer
|
||||
* a color then the color of the node owning this tile.
|
||||
*/
|
||||
video::SColor color;
|
||||
|
||||
u8 scale;
|
||||
};
|
||||
|
||||
/*!
|
||||
@ -325,6 +328,9 @@ struct TileSpec
|
||||
&& emissive_light == other.emissive_light;
|
||||
}
|
||||
|
||||
//! If true, the tile rotation is ignored.
|
||||
bool world_aligned = false;
|
||||
//! Tile rotation.
|
||||
u8 rotation = 0;
|
||||
//! This much light does the tile emit.
|
||||
u8 emissive_light = 0;
|
||||
|
@ -77,7 +77,7 @@ void MapblockMeshGenerator::useTile(int index, u8 set_flags, u8 reset_flags, boo
|
||||
if (special)
|
||||
getSpecialTile(index, &tile, p == data->m_crack_pos_relative);
|
||||
else
|
||||
getNodeTileN(n, p, index, data, tile);
|
||||
getTile(index, &tile);
|
||||
if (!data->m_smooth_lighting)
|
||||
color = encode_light(light, f->light_source);
|
||||
|
||||
@ -87,14 +87,19 @@ void MapblockMeshGenerator::useTile(int index, u8 set_flags, u8 reset_flags, boo
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a tile, ready for use, non-rotated.
|
||||
void MapblockMeshGenerator::getTile(int index, TileSpec *tile)
|
||||
{
|
||||
getNodeTileN(n, p, index, data, *tile);
|
||||
}
|
||||
|
||||
// Returns a tile, ready for use, rotated according to the node facedir.
|
||||
void MapblockMeshGenerator::getTile(v3s16 direction, TileSpec *tile)
|
||||
{
|
||||
getNodeTile(n, p, direction, data, *tile);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns the i-th special tile for a map node.
|
||||
*/
|
||||
// Returns a special tile, ready for use, non-rotated.
|
||||
void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile, bool apply_crack)
|
||||
{
|
||||
*tile = f->special_tiles[index];
|
||||
@ -1256,10 +1261,8 @@ void MapblockMeshGenerator::drawMeshNode()
|
||||
// Convert wallmounted to 6dfacedir.
|
||||
// When cache enabled, it is already converted.
|
||||
facedir = n.getWallMounted(nodedef);
|
||||
if (!enable_mesh_cache) {
|
||||
static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2};
|
||||
facedir = wm_to_6d[facedir];
|
||||
}
|
||||
if (!enable_mesh_cache)
|
||||
facedir = wallmounted_to_facedir[facedir];
|
||||
}
|
||||
|
||||
if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
|
||||
|
@ -63,6 +63,7 @@ public:
|
||||
|
||||
void useTile(int index = 0, u8 set_flags = MATERIAL_FLAG_CRACK_OVERLAY,
|
||||
u8 reset_flags = 0, bool special = false);
|
||||
void getTile(int index, TileSpec *tile);
|
||||
void getTile(v3s16 direction, TileSpec *tile);
|
||||
void getSpecialTile(int index, TileSpec *tile, bool apply_crack = false);
|
||||
|
||||
|
@ -139,6 +139,8 @@ void set_default_settings(Settings *settings)
|
||||
#endif
|
||||
settings->setDefault("fsaa", "0");
|
||||
settings->setDefault("undersampling", "0");
|
||||
settings->setDefault("world_aligned_mode", "enable");
|
||||
settings->setDefault("autoscale_mode", "disable");
|
||||
settings->setDefault("enable_fog", "true");
|
||||
settings->setDefault("fog_start", "0.4");
|
||||
settings->setDefault("3d_mode", "none");
|
||||
|
@ -466,6 +466,31 @@ static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
|
||||
}
|
||||
}
|
||||
|
||||
static void getNodeTextureCoords(v3f base, const v3f &scale, v3s16 dir, float *u, float *v)
|
||||
{
|
||||
if (dir.X > 0 || dir.Y > 0 || dir.Z < 0)
|
||||
base -= scale;
|
||||
if (dir == v3s16(0,0,1)) {
|
||||
*u = -base.X - 1;
|
||||
*v = -base.Y - 1;
|
||||
} else if (dir == v3s16(0,0,-1)) {
|
||||
*u = base.X + 1;
|
||||
*v = -base.Y - 2;
|
||||
} else if (dir == v3s16(1,0,0)) {
|
||||
*u = base.Z + 1;
|
||||
*v = -base.Y - 2;
|
||||
} else if (dir == v3s16(-1,0,0)) {
|
||||
*u = -base.Z - 1;
|
||||
*v = -base.Y - 1;
|
||||
} else if (dir == v3s16(0,1,0)) {
|
||||
*u = base.X + 1;
|
||||
*v = -base.Z - 2;
|
||||
} else if (dir == v3s16(0,-1,0)) {
|
||||
*u = base.X;
|
||||
*v = base.Z;
|
||||
}
|
||||
}
|
||||
|
||||
struct FastFace
|
||||
{
|
||||
TileLayer layer;
|
||||
@ -477,10 +502,11 @@ struct FastFace
|
||||
*/
|
||||
bool vertex_0_2_connected;
|
||||
u8 layernum;
|
||||
bool world_aligned;
|
||||
};
|
||||
|
||||
static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
|
||||
const v3f &p, v3s16 dir, v3f scale, std::vector<FastFace> &dest)
|
||||
v3f tp, v3f p, v3s16 dir, v3f scale, std::vector<FastFace> &dest)
|
||||
{
|
||||
// Position is at the center of the cube.
|
||||
v3f pos = p * BS;
|
||||
@ -493,6 +519,8 @@ static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li
|
||||
v3f vertex_pos[4];
|
||||
v3s16 vertex_dirs[4];
|
||||
getNodeVertexDirs(dir, vertex_dirs);
|
||||
if (tile.world_aligned)
|
||||
getNodeTextureCoords(tp, scale, dir, &x0, &y0);
|
||||
|
||||
v3s16 t;
|
||||
u16 t1;
|
||||
@ -671,6 +699,8 @@ static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li
|
||||
|
||||
face.layer = *layer;
|
||||
face.layernum = layernum;
|
||||
|
||||
face.world_aligned = tile.world_aligned;
|
||||
}
|
||||
}
|
||||
|
||||
@ -806,7 +836,7 @@ void getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data, TileSpec &t
|
||||
};
|
||||
u16 tile_index = facedir * 16 + dir_i;
|
||||
getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
|
||||
tile.rotation = dir_to_tile[tile_index + 1];
|
||||
tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
|
||||
}
|
||||
|
||||
static void getTileInfo(
|
||||
@ -965,7 +995,7 @@ static void updateFastFaceRow(
|
||||
scale.Z = continuous_tiles_count;
|
||||
|
||||
makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
|
||||
sp, face_dir_corrected, scale, dest);
|
||||
pf, sp, face_dir_corrected, scale, dest);
|
||||
|
||||
g_profiler->avg("Meshgen: faces drawn by tiling", 0);
|
||||
for (int i = 1; i < continuous_tiles_count; i++)
|
||||
@ -1092,7 +1122,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
|
||||
f.vertex_0_2_connected ? indices : indices_alternate;
|
||||
|
||||
collector.append(f.layer, f.vertices, 4, indices_p, 6,
|
||||
f.layernum);
|
||||
f.layernum, f.world_aligned);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1128,6 +1158,9 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
|
||||
os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
|
||||
if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
|
||||
os << "o"; // use ^[cracko
|
||||
u8 tiles = p.layer.scale;
|
||||
if (tiles > 1)
|
||||
os << ":" << (u32)tiles;
|
||||
os << ":" << (u32)p.layer.animation_frame_count << ":";
|
||||
m_crack_materials.insert(std::make_pair(
|
||||
std::pair<u8, u32>(layer, i), os.str()));
|
||||
@ -1392,13 +1425,15 @@ void MeshCollector::append(const TileSpec &tile,
|
||||
const TileLayer *layer = &tile.layers[layernum];
|
||||
if (layer->texture_id == 0)
|
||||
continue;
|
||||
append(*layer, vertices, numVertices, indices, numIndices, layernum);
|
||||
append(*layer, vertices, numVertices, indices, numIndices,
|
||||
layernum, tile.world_aligned);
|
||||
}
|
||||
}
|
||||
|
||||
void MeshCollector::append(const TileLayer &layer,
|
||||
const video::S3DVertex *vertices, u32 numVertices,
|
||||
const u16 *indices, u32 numIndices, u8 layernum)
|
||||
const u16 *indices, u32 numIndices, u8 layernum,
|
||||
bool use_scale)
|
||||
{
|
||||
if (numIndices > 65535) {
|
||||
dstream << "FIXME: MeshCollector::append() called with numIndices="
|
||||
@ -1422,20 +1457,24 @@ void MeshCollector::append(const TileLayer &layer,
|
||||
p = &(*buffers)[buffers->size() - 1];
|
||||
}
|
||||
|
||||
f32 scale = 1.0;
|
||||
if (use_scale)
|
||||
scale = 1.0 / layer.scale;
|
||||
|
||||
u32 vertex_count;
|
||||
if (m_use_tangent_vertices) {
|
||||
vertex_count = p->tangent_vertices.size();
|
||||
for (u32 i = 0; i < numVertices; i++) {
|
||||
|
||||
video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal,
|
||||
vertices[i].Color, vertices[i].TCoords);
|
||||
vertices[i].Color, scale * vertices[i].TCoords);
|
||||
p->tangent_vertices.push_back(vert);
|
||||
}
|
||||
} else {
|
||||
vertex_count = p->vertices.size();
|
||||
for (u32 i = 0; i < numVertices; i++) {
|
||||
video::S3DVertex vert(vertices[i].Pos, vertices[i].Normal,
|
||||
vertices[i].Color, vertices[i].TCoords);
|
||||
vertices[i].Color, scale * vertices[i].TCoords);
|
||||
|
||||
p->vertices.push_back(vert);
|
||||
}
|
||||
@ -1461,14 +1500,15 @@ void MeshCollector::append(const TileSpec &tile,
|
||||
if (layer->texture_id == 0)
|
||||
continue;
|
||||
append(*layer, vertices, numVertices, indices, numIndices, pos,
|
||||
c, light_source, layernum);
|
||||
c, light_source, layernum, tile.world_aligned);
|
||||
}
|
||||
}
|
||||
|
||||
void MeshCollector::append(const TileLayer &layer,
|
||||
const video::S3DVertex *vertices, u32 numVertices,
|
||||
const u16 *indices, u32 numIndices,
|
||||
v3f pos, video::SColor c, u8 light_source, u8 layernum)
|
||||
v3f pos, video::SColor c, u8 light_source, u8 layernum,
|
||||
bool use_scale)
|
||||
{
|
||||
if (numIndices > 65535) {
|
||||
dstream << "FIXME: MeshCollector::append() called with numIndices="
|
||||
@ -1492,6 +1532,10 @@ void MeshCollector::append(const TileLayer &layer,
|
||||
p = &(*buffers)[buffers->size() - 1];
|
||||
}
|
||||
|
||||
f32 scale = 1.0;
|
||||
if (use_scale)
|
||||
scale = 1.0 / layer.scale;
|
||||
|
||||
video::SColor original_c = c;
|
||||
u32 vertex_count;
|
||||
if (m_use_tangent_vertices) {
|
||||
@ -1502,7 +1546,7 @@ void MeshCollector::append(const TileLayer &layer,
|
||||
applyFacesShading(c, vertices[i].Normal);
|
||||
}
|
||||
video::S3DVertexTangents vert(vertices[i].Pos + pos,
|
||||
vertices[i].Normal, c, vertices[i].TCoords);
|
||||
vertices[i].Normal, c, scale * vertices[i].TCoords);
|
||||
p->tangent_vertices.push_back(vert);
|
||||
}
|
||||
} else {
|
||||
@ -1513,7 +1557,7 @@ void MeshCollector::append(const TileLayer &layer,
|
||||
applyFacesShading(c, vertices[i].Normal);
|
||||
}
|
||||
video::S3DVertex vert(vertices[i].Pos + pos, vertices[i].Normal, c,
|
||||
vertices[i].TCoords);
|
||||
scale * vertices[i].TCoords);
|
||||
p->vertices.push_back(vert);
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +200,8 @@ struct MeshCollector
|
||||
const u16 *indices, u32 numIndices);
|
||||
void append(const TileLayer &material,
|
||||
const video::S3DVertex *vertices, u32 numVertices,
|
||||
const u16 *indices, u32 numIndices, u8 layernum);
|
||||
const u16 *indices, u32 numIndices, u8 layernum,
|
||||
bool use_scale = false);
|
||||
void append(const TileSpec &material,
|
||||
const video::S3DVertex *vertices, u32 numVertices,
|
||||
const u16 *indices, u32 numIndices, v3f pos,
|
||||
@ -208,7 +209,8 @@ struct MeshCollector
|
||||
void append(const TileLayer &material,
|
||||
const video::S3DVertex *vertices, u32 numVertices,
|
||||
const u16 *indices, u32 numIndices, v3f pos,
|
||||
video::SColor c, u8 light_source, u8 layernum);
|
||||
video::SColor c, u8 light_source, u8 layernum,
|
||||
bool use_scale = false);
|
||||
/*!
|
||||
* Colorizes all vertices in the collector.
|
||||
*/
|
||||
|
@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "serialization.h" // For ser_ver_supported
|
||||
#include "util/serialize.h"
|
||||
#include "log.h"
|
||||
#include "util/directiontables.h"
|
||||
#include "util/numeric.h"
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
@ -152,12 +153,15 @@ bool MapNode::getLightBanks(u8 &lightday, u8 &lightnight, INodeDefManager *nodem
|
||||
return f.param_type == CPT_LIGHT || f.light_source != 0;
|
||||
}
|
||||
|
||||
u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const
|
||||
u8 MapNode::getFaceDir(INodeDefManager *nodemgr, bool allow_wallmounted) const
|
||||
{
|
||||
const ContentFeatures &f = nodemgr->get(*this);
|
||||
if (f.param_type_2 == CPT2_FACEDIR ||
|
||||
f.param_type_2 == CPT2_COLORED_FACEDIR)
|
||||
return (getParam2() & 0x1F) % 24;
|
||||
if (allow_wallmounted && (f.param_type_2 == CPT2_WALLMOUNTED ||
|
||||
f.param_type_2 == CPT2_COLORED_WALLMOUNTED))
|
||||
return wallmounted_to_facedir[getParam2() & 0x07];
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -246,7 +250,7 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox,
|
||||
|
||||
if (nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED) {
|
||||
const std::vector<aabb3f> &fixed = nodebox.fixed;
|
||||
int facedir = n.getFaceDir(nodemgr);
|
||||
int facedir = n.getFaceDir(nodemgr, true);
|
||||
u8 axisdir = facedir>>2;
|
||||
facedir&=0x03;
|
||||
for (aabb3f box : fixed) {
|
||||
|
@ -240,7 +240,7 @@ struct MapNode
|
||||
return blend_light(daylight_factor, lightday, lightnight);
|
||||
}
|
||||
|
||||
u8 getFaceDir(INodeDefManager *nodemgr) const;
|
||||
u8 getFaceDir(INodeDefManager *nodemgr, bool allow_wallmounted = false) const;
|
||||
u8 getWallMounted(INodeDefManager *nodemgr) const;
|
||||
v3s16 getWallMountedDir(INodeDefManager *nodemgr) const;
|
||||
|
||||
|
@ -180,6 +180,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
Backwards compatibility drop
|
||||
Add 'can_zoom' to player object properties
|
||||
Add glow to object properties
|
||||
Change TileDef serialization format.
|
||||
Add world-aligned tiles.
|
||||
Mod channels
|
||||
*/
|
||||
|
||||
|
205
src/nodedef.cpp
205
src/nodedef.cpp
@ -169,40 +169,72 @@ void NodeBox::deSerialize(std::istream &is)
|
||||
TileDef
|
||||
*/
|
||||
|
||||
#define TILE_FLAG_BACKFACE_CULLING (1 << 0)
|
||||
#define TILE_FLAG_TILEABLE_HORIZONTAL (1 << 1)
|
||||
#define TILE_FLAG_TILEABLE_VERTICAL (1 << 2)
|
||||
#define TILE_FLAG_HAS_COLOR (1 << 3)
|
||||
#define TILE_FLAG_HAS_SCALE (1 << 4)
|
||||
#define TILE_FLAG_HAS_ALIGN_STYLE (1 << 5)
|
||||
|
||||
void TileDef::serialize(std::ostream &os, u16 protocol_version) const
|
||||
{
|
||||
// protocol_version >= 36
|
||||
u8 version = 5;
|
||||
u8 version = 6;
|
||||
writeU8(os, version);
|
||||
|
||||
os << serializeString(name);
|
||||
animation.serialize(os, version);
|
||||
writeU8(os, backface_culling);
|
||||
writeU8(os, tileable_horizontal);
|
||||
writeU8(os, tileable_vertical);
|
||||
writeU8(os, has_color);
|
||||
bool has_scale = scale > 0;
|
||||
u16 flags = 0;
|
||||
if (backface_culling)
|
||||
flags |= TILE_FLAG_BACKFACE_CULLING;
|
||||
if (tileable_horizontal)
|
||||
flags |= TILE_FLAG_TILEABLE_HORIZONTAL;
|
||||
if (tileable_vertical)
|
||||
flags |= TILE_FLAG_TILEABLE_VERTICAL;
|
||||
if (has_color)
|
||||
flags |= TILE_FLAG_HAS_COLOR;
|
||||
if (has_scale)
|
||||
flags |= TILE_FLAG_HAS_SCALE;
|
||||
if (align_style != ALIGN_STYLE_NODE)
|
||||
flags |= TILE_FLAG_HAS_ALIGN_STYLE;
|
||||
writeU16(os, flags);
|
||||
if (has_color) {
|
||||
writeU8(os, color.getRed());
|
||||
writeU8(os, color.getGreen());
|
||||
writeU8(os, color.getBlue());
|
||||
}
|
||||
if (has_scale)
|
||||
writeU8(os, scale);
|
||||
if (align_style != ALIGN_STYLE_NODE)
|
||||
writeU8(os, align_style);
|
||||
}
|
||||
|
||||
void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
|
||||
NodeDrawType drawtype)
|
||||
{
|
||||
int version = readU8(is);
|
||||
if (version < 6)
|
||||
throw SerializationError("unsupported TileDef version");
|
||||
name = deSerializeString(is);
|
||||
animation.deSerialize(is, version);
|
||||
backface_culling = readU8(is);
|
||||
tileable_horizontal = readU8(is);
|
||||
tileable_vertical = readU8(is);
|
||||
has_color = readU8(is);
|
||||
u16 flags = readU16(is);
|
||||
backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
|
||||
tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL;
|
||||
tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL;
|
||||
has_color = flags & TILE_FLAG_HAS_COLOR;
|
||||
bool has_scale = flags & TILE_FLAG_HAS_SCALE;
|
||||
bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE;
|
||||
if (has_color) {
|
||||
color.setRed(readU8(is));
|
||||
color.setGreen(readU8(is));
|
||||
color.setBlue(readU8(is));
|
||||
}
|
||||
scale = has_scale ? readU8(is) : 0;
|
||||
if (has_align_style)
|
||||
align_style = static_cast<AlignStyle>(readU8(is));
|
||||
else
|
||||
align_style = ALIGN_STYLE_NODE;
|
||||
}
|
||||
|
||||
|
||||
@ -235,7 +267,10 @@ void TextureSettings::readSettings()
|
||||
bool smooth_lighting = g_settings->getBool("smooth_lighting");
|
||||
enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
|
||||
enable_minimap = g_settings->getBool("enable_minimap");
|
||||
node_texture_size = g_settings->getU16("texture_min_size");
|
||||
std::string leaves_style_str = g_settings->get("leaves_style");
|
||||
std::string world_aligned_mode_str = g_settings->get("world_aligned_mode");
|
||||
std::string autoscale_mode_str = g_settings->get("autoscale_mode");
|
||||
|
||||
// Mesh cache is not supported in combination with smooth lighting
|
||||
if (smooth_lighting)
|
||||
@ -250,6 +285,22 @@ void TextureSettings::readSettings()
|
||||
} else {
|
||||
leaves_style = LEAVES_OPAQUE;
|
||||
}
|
||||
|
||||
if (world_aligned_mode_str == "enable")
|
||||
world_aligned_mode = WORLDALIGN_ENABLE;
|
||||
else if (world_aligned_mode_str == "force_solid")
|
||||
world_aligned_mode = WORLDALIGN_FORCE;
|
||||
else if (world_aligned_mode_str == "force_nodebox")
|
||||
world_aligned_mode = WORLDALIGN_FORCE_NODEBOX;
|
||||
else
|
||||
world_aligned_mode = WORLDALIGN_DISABLE;
|
||||
|
||||
if (autoscale_mode_str == "enable")
|
||||
autoscale_mode = AUTOSCALE_ENABLE;
|
||||
else if (autoscale_mode_str == "force")
|
||||
autoscale_mode = AUTOSCALE_FORCE;
|
||||
else
|
||||
autoscale_mode = AUTOSCALE_DISABLE;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -541,77 +592,108 @@ void ContentFeatures::deSerialize(std::istream &is)
|
||||
}
|
||||
|
||||
#ifndef SERVER
|
||||
void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile,
|
||||
TileDef *tiledef, u32 shader_id, bool use_normal_texture,
|
||||
bool backface_culling, u8 material_type)
|
||||
static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
|
||||
const TileSpec &tile, const TileDef &tiledef, video::SColor color,
|
||||
u8 material_type, u32 shader_id, bool backface_culling,
|
||||
const TextureSettings &tsettings)
|
||||
{
|
||||
tile->shader_id = shader_id;
|
||||
tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
|
||||
tile->material_type = material_type;
|
||||
layer->shader_id = shader_id;
|
||||
layer->texture = tsrc->getTextureForMesh(tiledef.name, &layer->texture_id);
|
||||
layer->material_type = material_type;
|
||||
|
||||
bool has_scale = tiledef.scale > 0;
|
||||
if (((tsettings.autoscale_mode == AUTOSCALE_ENABLE) && !has_scale) ||
|
||||
(tsettings.autoscale_mode == AUTOSCALE_FORCE)) {
|
||||
auto texture_size = layer->texture->getOriginalSize();
|
||||
float base_size = tsettings.node_texture_size;
|
||||
float size = std::fmin(texture_size.Width, texture_size.Height);
|
||||
layer->scale = std::fmax(base_size, size) / base_size;
|
||||
} else if (has_scale) {
|
||||
layer->scale = tiledef.scale;
|
||||
} else {
|
||||
layer->scale = 1;
|
||||
}
|
||||
if (!tile.world_aligned)
|
||||
layer->scale = 1;
|
||||
|
||||
// Normal texture and shader flags texture
|
||||
if (use_normal_texture) {
|
||||
tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
|
||||
if (tsettings.use_normal_texture) {
|
||||
layer->normal_texture = tsrc->getNormalTexture(tiledef.name);
|
||||
}
|
||||
tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
|
||||
layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
|
||||
|
||||
// Material flags
|
||||
tile->material_flags = 0;
|
||||
layer->material_flags = 0;
|
||||
if (backface_culling)
|
||||
tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
|
||||
if (tiledef->animation.type != TAT_NONE)
|
||||
tile->material_flags |= MATERIAL_FLAG_ANIMATION;
|
||||
if (tiledef->tileable_horizontal)
|
||||
tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
|
||||
if (tiledef->tileable_vertical)
|
||||
tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
|
||||
layer->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
|
||||
if (tiledef.animation.type != TAT_NONE)
|
||||
layer->material_flags |= MATERIAL_FLAG_ANIMATION;
|
||||
if (tiledef.tileable_horizontal)
|
||||
layer->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
|
||||
if (tiledef.tileable_vertical)
|
||||
layer->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
|
||||
|
||||
// Color
|
||||
tile->has_color = tiledef->has_color;
|
||||
if (tiledef->has_color)
|
||||
tile->color = tiledef->color;
|
||||
layer->has_color = tiledef.has_color;
|
||||
if (tiledef.has_color)
|
||||
layer->color = tiledef.color;
|
||||
else
|
||||
tile->color = color;
|
||||
layer->color = color;
|
||||
|
||||
// Animation parameters
|
||||
int frame_count = 1;
|
||||
if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
|
||||
if (layer->material_flags & MATERIAL_FLAG_ANIMATION) {
|
||||
int frame_length_ms;
|
||||
tiledef->animation.determineParams(tile->texture->getOriginalSize(),
|
||||
tiledef.animation.determineParams(layer->texture->getOriginalSize(),
|
||||
&frame_count, &frame_length_ms, NULL);
|
||||
tile->animation_frame_count = frame_count;
|
||||
tile->animation_frame_length_ms = frame_length_ms;
|
||||
layer->animation_frame_count = frame_count;
|
||||
layer->animation_frame_length_ms = frame_length_ms;
|
||||
}
|
||||
|
||||
if (frame_count == 1) {
|
||||
tile->material_flags &= ~MATERIAL_FLAG_ANIMATION;
|
||||
layer->material_flags &= ~MATERIAL_FLAG_ANIMATION;
|
||||
} else {
|
||||
std::ostringstream os(std::ios::binary);
|
||||
if (!tile->frames) {
|
||||
tile->frames = std::make_shared<std::vector<FrameSpec>>();
|
||||
if (!layer->frames) {
|
||||
layer->frames = std::make_shared<std::vector<FrameSpec>>();
|
||||
}
|
||||
tile->frames->resize(frame_count);
|
||||
layer->frames->resize(frame_count);
|
||||
|
||||
for (int i = 0; i < frame_count; i++) {
|
||||
|
||||
FrameSpec frame;
|
||||
|
||||
os.str("");
|
||||
os << tiledef->name;
|
||||
tiledef->animation.getTextureModifer(os,
|
||||
tile->texture->getOriginalSize(), i);
|
||||
os << tiledef.name;
|
||||
tiledef.animation.getTextureModifer(os,
|
||||
layer->texture->getOriginalSize(), i);
|
||||
|
||||
frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
|
||||
if (tile->normal_texture)
|
||||
if (layer->normal_texture)
|
||||
frame.normal_texture = tsrc->getNormalTexture(os.str());
|
||||
frame.flags_texture = tile->flags_texture;
|
||||
(*tile->frames)[i] = frame;
|
||||
frame.flags_texture = layer->flags_texture;
|
||||
(*layer->frames)[i] = frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SERVER
|
||||
bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
|
||||
{
|
||||
if (style == ALIGN_STYLE_WORLD)
|
||||
return true;
|
||||
if (mode == WORLDALIGN_DISABLE)
|
||||
return false;
|
||||
if (style == ALIGN_STYLE_USER_DEFINED)
|
||||
return true;
|
||||
if (drawtype == NDT_NORMAL)
|
||||
return mode >= WORLDALIGN_FORCE;
|
||||
if (drawtype == NDT_NODEBOX)
|
||||
return mode >= WORLDALIGN_FORCE_NODEBOX;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
|
||||
scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
|
||||
{
|
||||
@ -751,13 +833,15 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
||||
|
||||
// Tiles (fill in f->tiles[])
|
||||
for (u16 j = 0; j < 6; j++) {
|
||||
fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader,
|
||||
tsettings.use_normal_texture,
|
||||
tdef[j].backface_culling, material_type);
|
||||
tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
|
||||
tsettings.world_aligned_mode, drawtype);
|
||||
fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
|
||||
color, material_type, tile_shader,
|
||||
tdef[j].backface_culling, tsettings);
|
||||
if (!tdef_overlay[j].name.empty())
|
||||
fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j],
|
||||
overlay_shader, tsettings.use_normal_texture,
|
||||
tdef[j].backface_culling, overlay_material);
|
||||
fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
|
||||
color, overlay_material, overlay_shader,
|
||||
tdef[j].backface_culling, tsettings);
|
||||
}
|
||||
|
||||
u8 special_material = material_type;
|
||||
@ -770,11 +854,10 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
||||
u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
|
||||
|
||||
// Special tiles (fill in f->special_tiles[])
|
||||
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
|
||||
fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tdef_spec[j],
|
||||
special_shader, tsettings.use_normal_texture,
|
||||
tdef_spec[j].backface_culling, special_material);
|
||||
}
|
||||
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
|
||||
fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
|
||||
color, special_material, special_shader,
|
||||
tdef_spec[j].backface_culling, tsettings);
|
||||
|
||||
if (param_type_2 == CPT2_COLOR ||
|
||||
param_type_2 == CPT2_COLORED_FACEDIR ||
|
||||
@ -791,18 +874,6 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
||||
recalculateBoundingBox(mesh_ptr[0]);
|
||||
meshmanip->recalculateNormals(mesh_ptr[0], true, false);
|
||||
}
|
||||
} else if ((drawtype == NDT_NODEBOX) &&
|
||||
((node_box.type == NODEBOX_REGULAR) ||
|
||||
(node_box.type == NODEBOX_FIXED)) &&
|
||||
(!node_box.fixed.empty())) {
|
||||
//Convert regular nodebox nodes to meshnodes
|
||||
//Change the drawtype and apply scale
|
||||
drawtype = NDT_MESH;
|
||||
mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed);
|
||||
v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale;
|
||||
scaleMesh(mesh_ptr[0], scale);
|
||||
recalculateBoundingBox(mesh_ptr[0]);
|
||||
meshmanip->recalculateNormals(mesh_ptr[0], true, false);
|
||||
}
|
||||
|
||||
//Cache 6dfacedir and wallmounted rotated clones of meshes
|
||||
|
@ -125,9 +125,25 @@ enum LeavesStyle {
|
||||
LEAVES_OPAQUE,
|
||||
};
|
||||
|
||||
enum AutoScale : u8 {
|
||||
AUTOSCALE_DISABLE,
|
||||
AUTOSCALE_ENABLE,
|
||||
AUTOSCALE_FORCE,
|
||||
};
|
||||
|
||||
enum WorldAlignMode : u8 {
|
||||
WORLDALIGN_DISABLE,
|
||||
WORLDALIGN_ENABLE,
|
||||
WORLDALIGN_FORCE,
|
||||
WORLDALIGN_FORCE_NODEBOX,
|
||||
};
|
||||
|
||||
class TextureSettings {
|
||||
public:
|
||||
LeavesStyle leaves_style;
|
||||
WorldAlignMode world_aligned_mode;
|
||||
AutoScale autoscale_mode;
|
||||
int node_texture_size;
|
||||
bool opaque_water;
|
||||
bool connected_glass;
|
||||
bool use_normal_texture;
|
||||
@ -198,6 +214,12 @@ enum PlantlikeStyle {
|
||||
PLANT_STYLE_HASH2,
|
||||
};
|
||||
|
||||
enum AlignStyle : u8 {
|
||||
ALIGN_STYLE_NODE,
|
||||
ALIGN_STYLE_WORLD,
|
||||
ALIGN_STYLE_USER_DEFINED,
|
||||
};
|
||||
|
||||
/*
|
||||
Stand-alone definition of a TileSpec (basically a server-side TileSpec)
|
||||
*/
|
||||
@ -212,6 +234,8 @@ struct TileDef
|
||||
bool has_color = false;
|
||||
//! The color of the tile.
|
||||
video::SColor color = video::SColor(0xFFFFFFFF);
|
||||
AlignStyle align_style = ALIGN_STYLE_NODE;
|
||||
u8 scale = 0;
|
||||
|
||||
struct TileAnimationParams animation;
|
||||
|
||||
@ -401,9 +425,6 @@ struct ContentFeatures
|
||||
}
|
||||
|
||||
#ifndef SERVER
|
||||
void fillTileAttribs(ITextureSource *tsrc, TileLayer *tile, TileDef *tiledef,
|
||||
u32 shader_id, bool use_normal_texture, bool backface_culling,
|
||||
u8 material_type);
|
||||
void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
|
||||
scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings);
|
||||
#endif
|
||||
|
@ -598,6 +598,8 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef,
|
||||
|
||||
float size = rand() % 64 / 512.;
|
||||
float visual_size = BS * size;
|
||||
if (tile.scale)
|
||||
size /= tile.scale;
|
||||
v2f texsize(size * 2, size * 2);
|
||||
v2f texpos;
|
||||
texpos.X = ((rand() % 64) / 64. - texsize.X);
|
||||
|
@ -431,6 +431,16 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype)
|
||||
L, index, "tileable_horizontal", default_tiling);
|
||||
tiledef.tileable_vertical = getboolfield_default(
|
||||
L, index, "tileable_vertical", default_tiling);
|
||||
std::string align_style;
|
||||
if (getstringfield(L, index, "align_style", align_style)) {
|
||||
if (align_style == "user")
|
||||
tiledef.align_style = ALIGN_STYLE_USER_DEFINED;
|
||||
else if (align_style == "world")
|
||||
tiledef.align_style = ALIGN_STYLE_WORLD;
|
||||
else
|
||||
tiledef.align_style = ALIGN_STYLE_NODE;
|
||||
}
|
||||
tiledef.scale = getintfield_default(L, index, "scale", 0);
|
||||
// color = ...
|
||||
lua_getfield(L, index, "color");
|
||||
tiledef.has_color = read_color(L, -1, &tiledef.color);
|
||||
|
@ -172,12 +172,13 @@ fake_function() {
|
||||
gettext("Use trilinear filtering when scaling textures.");
|
||||
gettext("Clean transparent textures");
|
||||
gettext("Filtered textures can blend RGB values with fully-transparent neighbors,\nwhich PNG optimizers usually discard, sometimes resulting in a dark or\nlight edge to transparent textures. Apply this filter to clean that up\nat texture load time.");
|
||||
gettext("Minimum texture size for filters");
|
||||
gettext("When using bilinear/trilinear/anisotropic filters, low-resolution textures\ncan be blurred, so automatically upscale them with nearest-neighbor\ninterpolation to preserve crisp pixels. This sets the minimum texture size\nfor the upscaled textures; higher values look sharper, but require more\nmemory. Powers of 2 are recommended. Setting this higher than 1 may not\nhave a visible effect unless bilinear/trilinear/anisotropic filtering is\nenabled.");
|
||||
gettext("Minimum texture size");
|
||||
gettext("When using bilinear/trilinear/anisotropic filters, low-resolution textures\ncan be blurred, so automatically upscale them with nearest-neighbor\ninterpolation to preserve crisp pixels. This sets the minimum texture size\nfor the upscaled textures; higher values look sharper, but require more\nmemory. Powers of 2 are recommended. Setting this higher than 1 may not\nhave a visible effect unless bilinear/trilinear/anisotropic filtering is\nenabled.\nThis is also used as the base node texture size for world-aligned\ntexture autoscaling.");
|
||||
gettext("FSAA");
|
||||
gettext("Experimental option, might cause visible spaces between blocks\nwhen set to higher number than 0.");
|
||||
gettext("Undersampling");
|
||||
gettext("Undersampling is similar to using lower screen resolution, but it applies\nto the game world only, keeping the GUI intact.\nIt should give significant performance boost at the cost of less detailed image.");
|
||||
gettext("Autoscaling mode");
|
||||
gettext("Shaders");
|
||||
gettext("Shaders");
|
||||
gettext("Shaders allow advanced visual effects and may increase performance on some video cards.\nThis only works with the OpenGL video backend.");
|
||||
@ -303,6 +304,10 @@ fake_function() {
|
||||
gettext("Fraction of the visible distance at which fog starts to be rendered");
|
||||
gettext("Opaque liquids");
|
||||
gettext("Makes all liquids opaque");
|
||||
gettext("World-aligned textures mode");
|
||||
gettext("Textures on a node may be aligned either to the node or to the world.\nThe former mode sutis better things like machines, furniture, etc., while\nthe latter makes stairs and microblocks fit surroundings better.\nHowever, as this possibility is new, thus may not be used by older servers,\nthis option allows enforcing it for certain node types. Note though that\nthat is considered EXPERIMENTAL and may not work properly.");
|
||||
gettext("Autoscaling mode");
|
||||
gettext("World-aligned textures may be scaled to span several nodes. However,\nthe server may not send the scale you want, especially if you use\na specially-designed texture pack; with this option, the client tries\nto determine the scale automatically basing on the texture size.\nSee also min_texture_size.\nWarning: this option is EXPERIMENTAL!");
|
||||
gettext("Menus");
|
||||
gettext("Clouds in menu");
|
||||
gettext("Use a cloud animation for the main menu background.");
|
||||
|
@ -110,3 +110,11 @@ const v3s16 g_27dirs[27] =
|
||||
v3s16(0,0,0),
|
||||
};
|
||||
|
||||
constexpr u8 wallmounted_to_facedir[6] = {
|
||||
20,
|
||||
0,
|
||||
16 + 1,
|
||||
12 + 3,
|
||||
8,
|
||||
4 + 2
|
||||
};
|
||||
|
@ -31,6 +31,8 @@ extern const v3s16 g_26dirs[26];
|
||||
// 26th is (0,0,0)
|
||||
extern const v3s16 g_27dirs[27];
|
||||
|
||||
extern const u8 wallmounted_to_facedir[6];
|
||||
|
||||
/// Direction in the 6D format. g_27dirs contains corresponding vectors.
|
||||
/// Here P means Positive, N stands for Negative.
|
||||
enum Direction6D {
|
||||
|
@ -233,7 +233,7 @@ void WieldMeshSceneNode::setCube(const ContentFeatures &f,
|
||||
scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube();
|
||||
scene::SMesh *copy = cloneMesh(cubemesh);
|
||||
cubemesh->drop();
|
||||
postProcessNodeMesh(copy, f, false, true, &m_material_type, &m_colors);
|
||||
postProcessNodeMesh(copy, f, false, true, &m_material_type, &m_colors, true);
|
||||
changeToMesh(copy);
|
||||
copy->drop();
|
||||
m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR);
|
||||
@ -554,7 +554,7 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
|
||||
// add overlays (since getMesh() returns
|
||||
// the base layer only)
|
||||
postProcessNodeMesh(mesh, f, false, false, nullptr,
|
||||
&result->buffer_colors);
|
||||
&result->buffer_colors, f.drawtype == NDT_NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -622,7 +622,7 @@ scene::SMesh *getExtrudedMesh(ITextureSource *tsrc,
|
||||
|
||||
void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f,
|
||||
bool use_shaders, bool set_material, const video::E_MATERIAL_TYPE *mattype,
|
||||
std::vector<ItemPartColor> *colors)
|
||||
std::vector<ItemPartColor> *colors, bool apply_scale)
|
||||
{
|
||||
u32 mc = mesh->getMeshBufferCount();
|
||||
// Allocate colors for existing buffers
|
||||
@ -670,6 +670,11 @@ void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f,
|
||||
}
|
||||
material.setTexture(2, layer->flags_texture);
|
||||
}
|
||||
if (apply_scale && tile->world_aligned) {
|
||||
u32 n = buf->getVertexCount();
|
||||
for (u32 k = 0; k != n; ++k)
|
||||
buf->getTCoords(k) /= layer->scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,4 +137,4 @@ scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename
|
||||
*/
|
||||
void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f, bool use_shaders,
|
||||
bool set_material, const video::E_MATERIAL_TYPE *mattype,
|
||||
std::vector<ItemPartColor> *colors);
|
||||
std::vector<ItemPartColor> *colors, bool apply_scale = false);
|
||||
|
Loading…
Reference in New Issue
Block a user