mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
Add node texture variants
This commit is contained in:
parent
81d62d01d1
commit
e638072e15
29 changed files with 984 additions and 208 deletions
|
@ -180,6 +180,25 @@ function core.strip_param2_color(param2, paramtype2)
|
|||
end
|
||||
end
|
||||
|
||||
function core.strip_param2_variant(param2, def)
|
||||
if not def or def.variant_count <= 1 or not def.param2_variant then
|
||||
return 0
|
||||
end
|
||||
local bf = def.param2_variant
|
||||
local right_mask = bit.lshift(1, bf.width) - 1
|
||||
return bit.band(bit.rshift(param2, bf.offset), right_mask) % def.variant_count
|
||||
end
|
||||
|
||||
function core.set_param2_variant(param2, variant, def)
|
||||
if not def or not def.param2_variant then
|
||||
return param2
|
||||
end
|
||||
local bf = def.param2_variant
|
||||
local mask = bit.lshift(bit.lshift(1, bf.width) - 1, bf.offset)
|
||||
local new_bits = bit.band(bit.lshift(variant, bf.offset), mask)
|
||||
return bit.bor(bit.band(param2, bit.bnot(mask)), new_bits)
|
||||
end
|
||||
|
||||
-- Content ID caching
|
||||
|
||||
local old_get_content_id = core.get_content_id
|
||||
|
|
|
@ -47,6 +47,7 @@ core.features = {
|
|||
particle_blend_clip = true,
|
||||
remove_item_match_meta = true,
|
||||
httpfetch_additional_methods = true,
|
||||
texture_variants = true,
|
||||
}
|
||||
|
||||
function core.has_feature(arg)
|
||||
|
|
|
@ -52,14 +52,18 @@ function core.get_node_drops(node, toolname)
|
|||
local ptype = def and def.paramtype2
|
||||
-- get color, if there is color (otherwise nil)
|
||||
local palette_index = core.strip_param2_color(param2, ptype)
|
||||
-- get variant (always a number)
|
||||
local variant = core.strip_param2_variant(param2, def)
|
||||
if drop == nil then
|
||||
-- default drop
|
||||
local itemstring = nodename
|
||||
if palette_index then
|
||||
local stack = ItemStack(nodename)
|
||||
stack:get_meta():set_int("palette_index", palette_index)
|
||||
return {stack:to_string()}
|
||||
itemstring = core.itemstring_with_palette(itemstring, palette_index)
|
||||
end
|
||||
return {nodename}
|
||||
if variant > 0 then
|
||||
itemstring = core.itemstring_with_variant(itemstring, variant)
|
||||
end
|
||||
return {itemstring}
|
||||
elseif type(drop) == "string" then
|
||||
-- itemstring drop
|
||||
return drop ~= "" and {drop} or {}
|
||||
|
@ -118,9 +122,10 @@ function core.get_node_drops(node, toolname)
|
|||
for _, add_item in ipairs(item.items) do
|
||||
-- add color, if necessary
|
||||
if item.inherit_color and palette_index then
|
||||
local stack = ItemStack(add_item)
|
||||
stack:get_meta():set_int("palette_index", palette_index)
|
||||
add_item = stack:to_string()
|
||||
add_item = core.itemstring_with_palette(add_item, palette_index)
|
||||
end
|
||||
if item.inherit_variant then
|
||||
add_item = core.itemstring_with_variant(add_item, variant)
|
||||
end
|
||||
got_items[#got_items+1] = add_item
|
||||
end
|
||||
|
@ -270,6 +275,13 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2,
|
|||
end
|
||||
end
|
||||
|
||||
-- Transfer variant
|
||||
if not def.place_param2 and def.variant_count > 1 then
|
||||
local variant = math.min(math.max(math.floor(metatable.variant or 0), 0),
|
||||
def.variant_count - 1)
|
||||
newnode.param2 = core.set_param2_variant(newnode.param2, variant, def)
|
||||
end
|
||||
|
||||
-- Check if the node is attached and if it can be placed there
|
||||
local an = core.get_item_group(def.name, "attached_node")
|
||||
if an ~= 0 and
|
||||
|
@ -604,6 +616,12 @@ function core.itemstring_with_color(item, colorstring)
|
|||
return stack:to_string()
|
||||
end
|
||||
|
||||
function core.itemstring_with_variant(item, variant)
|
||||
local stack = ItemStack(item) -- convert to ItemStack
|
||||
stack:get_meta():set_string("variant", variant > 0 and variant or "")
|
||||
return stack:to_string()
|
||||
end
|
||||
|
||||
-- This is used to allow mods to redefine core.item_place and so on
|
||||
-- NOTE: This is not the preferred way. Preferred way is to provide enough
|
||||
-- callbacks to not require redefining global functions. -celeron55
|
||||
|
@ -625,6 +643,7 @@ core.nodedef_default = {
|
|||
-- name intentionally not defined here
|
||||
description = "",
|
||||
groups = {},
|
||||
variant_count = 1,
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = vector.new(1, 1, 1),
|
||||
|
@ -655,6 +674,7 @@ core.nodedef_default = {
|
|||
post_effect_color = {a=0, r=0, g=0, b=0},
|
||||
paramtype = "none",
|
||||
paramtype2 = "none",
|
||||
param2_variant = {width = 0, offset = 0},
|
||||
is_ground_content = true,
|
||||
sunlight_propagates = false,
|
||||
walkable = true,
|
||||
|
@ -680,6 +700,7 @@ core.craftitemdef_default = {
|
|||
-- name intentionally not defined here
|
||||
description = "",
|
||||
groups = {},
|
||||
variant_count = 1,
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = vector.new(1, 1, 1),
|
||||
|
@ -700,6 +721,7 @@ core.tooldef_default = {
|
|||
-- name intentionally not defined here
|
||||
description = "",
|
||||
groups = {},
|
||||
variant_count = 1,
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = vector.new(1, 1, 1),
|
||||
|
@ -720,6 +742,7 @@ core.noneitemdef_default = { -- This is used for the hand and unknown items
|
|||
-- name intentionally not defined here
|
||||
description = "",
|
||||
groups = {},
|
||||
variant_count = 1,
|
||||
inventory_image = "",
|
||||
wield_image = "",
|
||||
wield_scale = vector.new(1, 1, 1),
|
||||
|
|
122
doc/lua_api.md
122
doc/lua_api.md
|
@ -1109,6 +1109,63 @@ core.register_node("default:dirt_with_grass", {
|
|||
})
|
||||
```
|
||||
|
||||
Variants
|
||||
--------
|
||||
|
||||
Items can have "variants", which are numbered states that can determine certain
|
||||
properties. The number of variants is specified with the item definition field
|
||||
`variant_count`. Each variant is numbered from 0 to `variant_count` - 1. Every
|
||||
item has at least one variant (numbered 0). Nodes can specify a part of param2
|
||||
to read as the variant number by setting the `param2_variant` field to a
|
||||
`BitField`.
|
||||
|
||||
The currently supported variant properties are:
|
||||
* `tiles`
|
||||
* `overlay_tiles`
|
||||
* `special_tiles`
|
||||
The `variants` table in the item definition is a mapping from
|
||||
variant numbers to variant tables. These tables can include the aforementioned
|
||||
fields to set the properties for particular variants. Variants not present in
|
||||
the mapping default to the values of the aforementioned fields specified in the
|
||||
item definition table. Old clients will receive only the variant 0 tiles.
|
||||
|
||||
Items with multiple variants can specify a variant number with the "variant" key
|
||||
in their metadata. The absence of the key indicates a variant number of 0, and
|
||||
this is the canonical representation of the 0 variant. The helper function
|
||||
`core.itemstring_with_variant` is a shortcut for creating such itemstrings.
|
||||
The variant is preserved when a node is placed or dug. Custom drops will inherit
|
||||
the variant only if `inherit_variant` is set to `true` in their specification.
|
||||
|
||||
Example node with variants:
|
||||
|
||||
core.register_node("mod:grass", {
|
||||
description = "Grass",
|
||||
drawtype = "plantlike",
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
walkable = false,
|
||||
groups = {dig_immediate = 3},
|
||||
-- There are 4 variants numbered 0 to 3.
|
||||
variant_count = 4,
|
||||
-- The lowest 2 bits store the variant number.
|
||||
param2_variant = {width = 2, offset = 0},
|
||||
-- These tiles will be used for variants not otherwise specified,
|
||||
-- in this case variant 0.
|
||||
tiles = {"mod_grass1.png"},
|
||||
-- Tiles for variants 1, 2, and 3 are specified here.
|
||||
variants = {
|
||||
{tiles = {"mod_grass2.png"}},
|
||||
{tiles = {"mod_grass3.png"}},
|
||||
{tiles = {"mod_grass4.png"}},
|
||||
},
|
||||
drop = {
|
||||
items = {
|
||||
-- The seeds will inherit the variant of the grass.
|
||||
{items = {"mod:seeds"}, inherit_variant = true},
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
Sounds
|
||||
|
@ -1991,6 +2048,13 @@ Exact pointing location (currently only `Raycast` supports these fields):
|
|||
For entities with rotated selection boxes, this will be rotated properly
|
||||
by the entity's rotation - it will always be in absolute world space.
|
||||
|
||||
`BitField`
|
||||
----------
|
||||
|
||||
A `BitField` specifies a part of an unsigned integer whose bits represent
|
||||
another unsigned integer. A `BitField` is represented as follows:
|
||||
|
||||
{width = <integer bit width>, offset = <offset from least significant bit>}
|
||||
|
||||
|
||||
|
||||
|
@ -2715,6 +2779,8 @@ Some of the values in the key-value store are handled specially:
|
|||
* `color`: A `ColorString`, which sets the stack's color.
|
||||
* `palette_index`: If the item has a palette, this is used to get the
|
||||
current color from the palette.
|
||||
* `variant`: If the item has more than one variant, this is the variant number.
|
||||
The canonical form of variant 0 is the absence of this key.
|
||||
* `count_meta`: Replace the displayed count with any string.
|
||||
* `count_alignment`: Set the alignment of the displayed count value. This is an
|
||||
int value. The lowest 2 bits specify the alignment in x-direction, the 3rd and
|
||||
|
@ -5810,6 +5876,8 @@ Utilities
|
|||
remove_item_match_meta = true,
|
||||
-- The HTTP API supports the HEAD and PATCH methods (5.12.0)
|
||||
httpfetch_additional_methods = true,
|
||||
-- Node/Item texture variants is supported (5.13.0)
|
||||
texture_variants = true,
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -6962,11 +7030,23 @@ Item handling
|
|||
given `param2` value.
|
||||
* Returns `nil` if the given `paramtype2` does not contain color
|
||||
information.
|
||||
<<<<<<< HEAD
|
||||
* `core.get_node_drops(node, toolname[, tool, digger, pos])`
|
||||
* Returns list of itemstrings that are dropped by `node` when dug with the
|
||||
item `toolname` (not limited to tools). The default implementation doesn't
|
||||
use `tool`, `digger`, and `pos`, but these are provided by `core.node_dig`
|
||||
since 5.12.0 for games/mods implementing customized drops.
|
||||
=======
|
||||
* `core.strip_param2_variant(param2, def)`
|
||||
* Returns the variant from `param2` with the given node definition `def`.
|
||||
* Always returns a non-negative integer less than `def.variant_count`.
|
||||
* `core.set_param2_variant(param2, variant, def)`
|
||||
* Returns a modified `param2` with the variant bitfield set to `variant`
|
||||
with the given node definition `def`.
|
||||
* `core.get_node_drops(node, toolname)`
|
||||
* Returns list of itemstrings that are dropped by `node` when dug
|
||||
with the item `toolname` (not limited to tools).
|
||||
>>>>>>> 324240af9 (Add node texture variants)
|
||||
* `node`: node as table or node name
|
||||
* `toolname`: name of the item used to dig (can be `nil`)
|
||||
* `tool`: `ItemStack` used to dig (can be `nil`)
|
||||
|
@ -7035,6 +7115,11 @@ Item handling
|
|||
* `item`: the item stack which becomes colored. Can be in string,
|
||||
table and native form.
|
||||
* `colorstring`: the new color of the item stack
|
||||
* `core.itemstring_with_variant(item, variant)`: returns an item string
|
||||
* Creates an item string with an associated item variant.
|
||||
* `item`: the item stack which is given the variant. Can be in string,
|
||||
table or native form.
|
||||
* `variant`: the new variant of the item stack
|
||||
|
||||
Rollback
|
||||
--------
|
||||
|
@ -9779,6 +9864,9 @@ Used by `core.register_node`, `core.register_craftitem`, and
|
|||
-- {bendy = 2, snappy = 1},
|
||||
-- {hard = 1, metal = 1, spikes = 1}
|
||||
|
||||
variant_count = 1,
|
||||
-- The number item variants, a positive integer.
|
||||
|
||||
inventory_image = "",
|
||||
-- Texture shown in the inventory GUI
|
||||
-- Defaults to a 3D rendering of the node if left empty.
|
||||
|
@ -10003,6 +10091,9 @@ Used by `core.register_node`.
|
|||
{
|
||||
-- <all fields allowed in item definitions>
|
||||
|
||||
param2_variant = BitField,
|
||||
-- The part of param2 from which to read the variant number.
|
||||
|
||||
drawtype = "normal", -- See "Node drawtypes"
|
||||
|
||||
visual_scale = 1.0,
|
||||
|
@ -10016,6 +10107,7 @@ Used by `core.register_node`.
|
|||
tiles = {tile definition 1, def2, def3, def4, def5, def6},
|
||||
-- Textures of node; +Y, -Y, +X, -X, +Z, -Z
|
||||
-- List can be shortened to needed length.
|
||||
-- This field is also used for Variant number 0, see "Variants" for details.
|
||||
|
||||
overlay_tiles = {tile definition 1, def2, def3, def4, def5, def6},
|
||||
-- Same as `tiles`, but these textures are drawn on top of the base
|
||||
|
@ -10023,10 +10115,37 @@ Used by `core.register_node`.
|
|||
-- texture. If the texture name is an empty string, that overlay is not
|
||||
-- drawn. Since such tiles are drawn twice, it is not recommended to use
|
||||
-- overlays on very common nodes.
|
||||
-- This field is also used for Variant number 0, see "Variants" for details.
|
||||
|
||||
special_tiles = {tile definition 1, Tile definition 2},
|
||||
-- Special textures of node; used rarely.
|
||||
-- List can be shortened to needed length.
|
||||
-- This field is also used for Variant number 0, see "Variants" for details.
|
||||
|
||||
-- See "Variants"
|
||||
-- This field is optional.
|
||||
variants = {
|
||||
-- Variant number 0 is created from fields
|
||||
-- tiles, overlay_tiles and special_tiles
|
||||
-- defined above (outside from variants table).
|
||||
{ -- Variant number 1.
|
||||
-- this field is reused from above definition if is not specified.
|
||||
tiles = {tile definition 1, def2, def3, def4, def5, def6},
|
||||
|
||||
-- this field is reused from above definition if is not specified.
|
||||
overlay_tiles = {def1, def2, def3, def4, def5, def6},
|
||||
|
||||
-- this field is reused from above definition if is not specified.
|
||||
special_tiles = {def1, def2},
|
||||
},
|
||||
{ -- Variant number 2.
|
||||
-- reuse tiles and special_tiles from variant number 0
|
||||
-- no overlay_tiles
|
||||
overlay_tiles = {},
|
||||
...
|
||||
},
|
||||
...
|
||||
},
|
||||
|
||||
color = ColorSpec,
|
||||
-- The node's original color will be multiplied with this color.
|
||||
|
@ -10311,6 +10430,9 @@ Used by `core.register_node`.
|
|||
-- hardware coloring palette color from the dug node.
|
||||
-- Default is 'false'.
|
||||
inherit_color = true,
|
||||
-- variant of the dug node.
|
||||
-- Default is 'false'.
|
||||
inherit_variant = true,
|
||||
},
|
||||
{
|
||||
-- Only drop if using an item whose name contains
|
||||
|
|
|
@ -25,6 +25,23 @@ core.register_node("testnodes:normal", {
|
|||
groups = { dig_immediate = 3 },
|
||||
})
|
||||
|
||||
-- A regular cube with tiles using color
|
||||
core.register_node("testnodes:normal_with_color", {
|
||||
description = S("\"normal\" Drawtype Test Node").."\n"..
|
||||
S("Opaque texture with color parameter"),
|
||||
drawtype = "normal",
|
||||
tiles = {
|
||||
{ name = "testnodes_normal.png", color = "#A00" },
|
||||
{ name = "testnodes_normal.png", color = "#00B" },
|
||||
{ name = "testnodes_normal.png", color = "#0C0" },
|
||||
{ name = "testnodes_normal.png", color = "#DD0" },
|
||||
{ name = "testnodes_normal.png", color = "#0EE" },
|
||||
{ name = "testnodes_normal.png", color = "#F0F" },
|
||||
},
|
||||
|
||||
groups = { dig_immediate = 3 },
|
||||
})
|
||||
|
||||
-- Standard glasslike node
|
||||
core.register_node("testnodes:glasslike", {
|
||||
description = S("\"glasslike\" Drawtype Test Node").."\n"..
|
||||
|
|
|
@ -10,4 +10,5 @@ dofile(path.."/liquids.lua")
|
|||
dofile(path.."/light.lua")
|
||||
dofile(path.."/textures.lua")
|
||||
dofile(path.."/overlays.lua")
|
||||
dofile(path.."/variants.lua")
|
||||
dofile(path.."/commands.lua")
|
||||
|
|
|
@ -7,6 +7,20 @@ core.register_node("testnodes:overlay", {
|
|||
overlay_tiles = {{name = "testnodes_overlay.png"}},
|
||||
groups = { dig_immediate = 2 },
|
||||
})
|
||||
core.register_node("testnodes:overlay_tile_colors", {
|
||||
description = S("Texture Overlay Test Node, Tile Colors") .. "\n" ..
|
||||
S("Uncolorized"),
|
||||
tiles = {{name = "testnodes_overlayable.png"}},
|
||||
overlay_tiles = {
|
||||
{name = "testnodes_overlay.png", color = "#F00"},
|
||||
{name = "testnodes_overlay.png", color = "#0F0"},
|
||||
{name = "testnodes_overlay.png", color = "#00F"},
|
||||
{name = "testnodes_overlay.png", color = "#FF0"},
|
||||
{name = "testnodes_overlay.png", color = "#0FF"},
|
||||
{name = "testnodes_overlay.png", color = "#F0F"},
|
||||
},
|
||||
groups = { dig_immediate = 2 },
|
||||
})
|
||||
core.register_node("testnodes:overlay_color_all", {
|
||||
description = S("Texture Overlay Test Node, Colorized") .. "\n" ..
|
||||
S("param2 changes color"),
|
||||
|
|
290
games/devtest/mods/testnodes/variants.lua
Normal file
290
games/devtest/mods/testnodes/variants.lua
Normal file
|
@ -0,0 +1,290 @@
|
|||
-- This file is for variant properties.
|
||||
|
||||
local S = core.get_translator("testnodes")
|
||||
|
||||
local animated_tile = {
|
||||
name = "testnodes_anim.png",
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 16,
|
||||
aspect_h = 16,
|
||||
length = 4.0,
|
||||
},
|
||||
}
|
||||
core.register_node("testnodes:variant_animated", {
|
||||
description = S("Variant Animated Test Node").."\n"..
|
||||
S("Tiles animate from A to D in 4s cycle").."\n"..
|
||||
S("Has two variants"),
|
||||
is_ground_content = false,
|
||||
groups = {dig_immediate = 3},
|
||||
variant_count = 2,
|
||||
param2_variant = {width = 1, offset = 0},
|
||||
tiles = {
|
||||
"testnodes_node.png", animated_tile,
|
||||
"testnodes_node.png", animated_tile,
|
||||
"testnodes_node.png", animated_tile,
|
||||
},
|
||||
variants = {
|
||||
{
|
||||
tiles = {
|
||||
animated_tile, "testnodes_node.png",
|
||||
animated_tile, "testnodes_node.png",
|
||||
animated_tile, "testnodes_node.png",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
core.register_node("testnodes:variant_color", {
|
||||
description = S("Variant Color Test Node").."\n"..
|
||||
S("param2 = color + 3 bit variant").."\n"..
|
||||
S("Has six unique variants").."\n"..
|
||||
S("Variants 6 and 7 are same as variant 0"),
|
||||
paramtype2 = "color",
|
||||
is_ground_content = false,
|
||||
groups = {dig_immediate = 3},
|
||||
variant_count = 8, -- Last two variants are the same as the first.
|
||||
param2_variant = {width = 3, offset = 0},
|
||||
tiles = {"testnodes_1g.png"},
|
||||
variants = {
|
||||
{tiles = {"testnodes_2g.png"}},
|
||||
{tiles = {"testnodes_3g.png"}},
|
||||
{tiles = {"testnodes_4g.png"}},
|
||||
{tiles = {"testnodes_5g.png"}},
|
||||
{tiles = {"testnodes_6g.png"}},
|
||||
},
|
||||
palette = "testnodes_palette_wallmounted.png",
|
||||
})
|
||||
|
||||
core.register_node("testnodes:variant_tile_color", {
|
||||
description = S("Variant Tile Color Test Node").."\n"..
|
||||
S("Has seven unique variants").."\n"..
|
||||
S("Variant 7 is same as variant 0 (red colored)"),
|
||||
is_ground_content = false,
|
||||
groups = {dig_immediate = 3},
|
||||
variant_count = 8, -- Last one variant are the same as the first.
|
||||
param2_variant = {width = 3, offset = 0},
|
||||
tiles = {{ name = "testnodes_1g.png", color = "#F00"}},
|
||||
variants = {
|
||||
{tiles = {{ name = "testnodes_1g.png", color = "#0F0"}}},
|
||||
{tiles = {{ name = "testnodes_1g.png", color = "#00F"}}},
|
||||
{tiles = {{ name = "testnodes_1g.png", color = "#FF0"}}},
|
||||
{tiles = {{ name = "testnodes_1g.png", color = "#0FF"}}},
|
||||
{tiles = {{ name ="testnodes_1g.png", color = "#F0F"}}},
|
||||
{tiles = {{ name ="testnodes_1g.png"}}},
|
||||
},
|
||||
})
|
||||
|
||||
core.register_node("testnodes:variant_drop", {
|
||||
description = S("Variant Drop Test Node").."\n"..
|
||||
S("Has five variants").."\n"..
|
||||
S("Variiant 1 is copied from variant 0").."\n"..
|
||||
S("Drops one node with an inherited variant and one without"),
|
||||
is_ground_content = false,
|
||||
groups = {dig_immediate = 3},
|
||||
variant_count = 5,
|
||||
param2_variant = {width = 3, offset = 0},
|
||||
tiles = {"testnodes_1.png"},
|
||||
variants = {
|
||||
nil,
|
||||
{tiles = {"testnodes_2.png"}},
|
||||
{tiles = {"testnodes_3.png"}},
|
||||
{tiles = {"testnodes_4.png"}},
|
||||
},
|
||||
drop = {
|
||||
max_items = 2,
|
||||
items = {
|
||||
{items = {"testnodes:variant_drop"}, inherit_variant = true},
|
||||
{items = {"testnodes:variant_drop"}, inherit_variant = false},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
core.register_node("testnodes:variant_facedir", {
|
||||
description = S("Variant Facedir Test Node").."\n"..
|
||||
S("param2 = 3 bit variant + facedir").."\n"..
|
||||
S("Has six variants"),
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
groups = {dig_immediate = 3},
|
||||
variant_count = 6,
|
||||
param2_variant = {width = 3, offset = 5},
|
||||
tiles = {"testnodes_1f.png"},
|
||||
variants = {
|
||||
{tiles = {"testnodes_2f.png"}},
|
||||
{tiles = {"testnodes_3f.png"}},
|
||||
{tiles = {"testnodes_4f.png"}},
|
||||
{tiles = {"testnodes_5f.png"}},
|
||||
{tiles = {"testnodes_6f.png"}},
|
||||
},
|
||||
})
|
||||
|
||||
core.register_node("testnodes:variant_falling", {
|
||||
description = S("Variant Falling Test Node").."\n"..
|
||||
S("Has six variants"),
|
||||
is_ground_content = false,
|
||||
groups = {dig_immediate = 3, falling_node = 1},
|
||||
variant_count = 6,
|
||||
param2_variant = {width = 3, offset = 0},
|
||||
tiles = {"testnodes_1.png"},
|
||||
variants = {
|
||||
{tiles = {"testnodes_2.png"}},
|
||||
{tiles = {"testnodes_3.png"}},
|
||||
{tiles = {"testnodes_4.png"}},
|
||||
{tiles = {"testnodes_5.png"}},
|
||||
{tiles = {"testnodes_6.png"}},
|
||||
},
|
||||
})
|
||||
|
||||
core.register_node("testnodes:variant_falling_torchlike", {
|
||||
description = S("Variant Falling Torchlike Test Node").."\n"..
|
||||
S("Has six variants"),
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
groups = {dig_immediate = 3, falling_node = 1},
|
||||
variant_count = 6,
|
||||
param2_variant = {width = 3, offset = 0},
|
||||
drawtype = "torchlike",
|
||||
tiles = {"testnodes_1.png"},
|
||||
variants = {
|
||||
{tiles = {"testnodes_2.png"}},
|
||||
{tiles = {"testnodes_3.png"}},
|
||||
{tiles = {"testnodes_4.png"}},
|
||||
{tiles = {"testnodes_5.png"}},
|
||||
{tiles = {"testnodes_6.png"}},
|
||||
},
|
||||
})
|
||||
|
||||
core.register_node("testnodes:variant_mesh", {
|
||||
description = S("Variant Mesh Test Node").."\n"..
|
||||
S("Has ten variants"),
|
||||
paramtype = "light",
|
||||
is_ground_content = false,
|
||||
groups = {dig_immediate = 3},
|
||||
variant_count = 10,
|
||||
param2_variant = {width = 8, offset = 0},
|
||||
drawtype = "mesh",
|
||||
mesh = "testnodes_ocorner.obj",
|
||||
tiles = {"testnodes_mesh_stripes.png"},
|
||||
variants = {
|
||||
{tiles = {"testnodes_mesh_stripes2.png"}},
|
||||
{tiles = {"testnodes_mesh_stripes3.png"}},
|
||||
{tiles = {"testnodes_mesh_stripes4.png"}},
|
||||
{tiles = {"testnodes_mesh_stripes5.png"}},
|
||||
{tiles = {"testnodes_mesh_stripes6.png"}},
|
||||
{tiles = {"testnodes_mesh_stripes7.png"}},
|
||||
{tiles = {"testnodes_mesh_stripes8.png"}},
|
||||
{tiles = {"testnodes_mesh_stripes9.png"}},
|
||||
{tiles = {"testnodes_mesh_stripes10.png"}},
|
||||
},
|
||||
})
|
||||
|
||||
core.register_node("testnodes:variant_overlay", {
|
||||
description = S("Variant Overlay Test Node").."\n"..
|
||||
S("Has four variants"),
|
||||
S("Variant 1 is same as variant 0"),
|
||||
is_ground_content = false,
|
||||
groups = {dig_immediate = 3},
|
||||
variant_count = 4,
|
||||
param2_variant = {width = 2, offset = 0},
|
||||
tiles = {"testnodes_node.png"},
|
||||
overlay_tiles = {{name = "testnodes_overlay.png", color = "#F00"}},
|
||||
variants = {
|
||||
{
|
||||
tiles = {"testnodes_node.png"},
|
||||
},
|
||||
{
|
||||
tiles = {"testnodes_node.png"},
|
||||
overlay_tiles = {{name = "testnodes_overlay.png", color = "#0FF"}},
|
||||
},
|
||||
{
|
||||
tiles = {"testnodes_node.png"},
|
||||
overlay_tiles = {},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
core.register_node("testnodes:variant_overlay_color", {
|
||||
description = S("Variant Overlay Test Node With Node Color").."\n"..
|
||||
S("Has four variants"),
|
||||
S("Variant 1 is same as variant 1"),
|
||||
is_ground_content = false,
|
||||
groups = {dig_immediate = 3},
|
||||
variant_count = 4,
|
||||
param2_variant = {width = 2, offset = 0},
|
||||
tiles = {"testnodes_node.png"},
|
||||
overlay_tiles = {{name = "testnodes_overlay.png", color = "#F00"}},
|
||||
color = "#00F",
|
||||
variants = {
|
||||
{
|
||||
tiles = {"testnodes_node.png"},
|
||||
},
|
||||
{
|
||||
tiles = {"testnodes_node.png"},
|
||||
overlay_tiles = {{name = "testnodes_overlay.png", color = "#0FF"}},
|
||||
},
|
||||
{
|
||||
tiles = {"testnodes_node.png"},
|
||||
overlay_tiles = {},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
core.register_node("testnodes:variant_plantlike_rooted", {
|
||||
description = S("Variant Plantlike Rooted Test Node").."\n"..
|
||||
S("Has six variants"),
|
||||
paramtype = "light",
|
||||
is_ground_content = false,
|
||||
groups = {dig_immediate = 3},
|
||||
drawtype = "plantlike_rooted",
|
||||
variant_count = 8,
|
||||
param2_variant = {width = 8, offset = 0},
|
||||
tiles = {"testnodes_1.png"},
|
||||
special_tiles = {"testnodes_6.png"},
|
||||
variants = {
|
||||
{tiles = {"testnodes_2.png"}, special_tiles = {"testnodes_5.png"}},
|
||||
{tiles = {"testnodes_3.png"}, special_tiles = {"testnodes_4.png"}},
|
||||
{tiles = {"testnodes_4.png"}, special_tiles = {"testnodes_3.png"}},
|
||||
{tiles = {"testnodes_5.png"}, special_tiles = {"testnodes_2.png"}},
|
||||
{tiles = {"testnodes_6.png"}, special_tiles = {"testnodes_1.png"}},
|
||||
{tiles = {"testnodes_6.png"}},
|
||||
{special_tiles = {"testnodes_1.png"}},
|
||||
},
|
||||
})
|
||||
|
||||
core.register_node("testnodes:variant_raillike", {
|
||||
description = S("Variant Raillike Test Node").."\n"..
|
||||
S("Has four variants"),
|
||||
paramtype = "light",
|
||||
is_ground_content = false,
|
||||
groups = {dig_immediate = 3},
|
||||
variant_count = 4,
|
||||
param2_variant = {width = 2, offset = 0},
|
||||
drawtype = "raillike",
|
||||
variants = {
|
||||
[0] = {tiles = {"testnodes_1.png"}},
|
||||
{tiles = {"testnodes_2.png"}},
|
||||
{tiles = {"testnodes_3.png"}},
|
||||
{tiles = {"testnodes_4.png"}},
|
||||
},
|
||||
})
|
||||
|
||||
core.register_node("testnodes:variant_wallmounted", {
|
||||
description = S("Variant Wallmounted Test Node").."\n"..
|
||||
S("Has six variants corresponding to direction"),
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
is_ground_content = false,
|
||||
groups = {dig_immediate = 3},
|
||||
variant_count = 6,
|
||||
param2_variant = {width = 3, offset = 0},
|
||||
tiles = {"testnodes_1w.png"},
|
||||
variants = {
|
||||
{tiles = {"testnodes_2w.png"}},
|
||||
{tiles = {"testnodes_3w.png"}},
|
||||
{tiles = {"testnodes_4w.png"}},
|
||||
{tiles = {"testnodes_5w.png"}},
|
||||
{tiles = {"testnodes_6w.png"}},
|
||||
},
|
||||
})
|
|
@ -98,7 +98,7 @@ void MapblockMeshGenerator::getTile(v3s16 direction, TileSpec *tile_ret)
|
|||
// Returns a special tile, ready for use, non-rotated.
|
||||
void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile_ret, bool apply_crack)
|
||||
{
|
||||
*tile_ret = cur_node.f->special_tiles[index];
|
||||
*tile_ret = cur_node.f->special_tiles[cur_node.n.getVariant(*cur_node.f)][index];
|
||||
TileLayer *top_layer = nullptr;
|
||||
|
||||
for (auto &layernum : tile_ret->layers) {
|
||||
|
@ -1023,7 +1023,7 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
|
|||
// Optionally render internal liquid level defined by param2
|
||||
// Liquid is textured with 1 tile defined in nodedef 'special_tiles'
|
||||
if (param2 > 0 && cur_node.f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
|
||||
cur_node.f->special_tiles[0].layers[0].texture) {
|
||||
cur_node.f->special_tiles[cur_node.n.getVariant(*cur_node.f)][0].layers[0].texture) {
|
||||
// Internal liquid level has param2 range 0 .. 63,
|
||||
// convert it to -0.5 .. 0.5
|
||||
float vlev = (param2 / 63.0f) * 2.0f - 1.0f;
|
||||
|
|
|
@ -3648,6 +3648,14 @@ bool Game::nodePlacement(const ItemDefinition &selected_def,
|
|||
}
|
||||
}
|
||||
|
||||
// Apply variant
|
||||
if (!place_param2 && predicted_f.variant_count > 1) {
|
||||
u16 variant = mystoi(selected_item.metadata.getString("variant"),
|
||||
0, predicted_f.variant_count - 1);
|
||||
predicted_node.setParam2(
|
||||
predicted_f.param2_variant.set(predicted_node.getParam2(), variant));
|
||||
}
|
||||
|
||||
// Add node to client map
|
||||
try {
|
||||
LocalPlayer *player = client->getEnv().getLocalPlayer();
|
||||
|
|
|
@ -15,8 +15,8 @@ ItemVisualsManager::ItemVisuals::~ItemVisuals() {
|
|||
wield_mesh.mesh->drop();
|
||||
}
|
||||
|
||||
ItemVisualsManager::ItemVisuals *ItemVisualsManager::createItemVisuals( const ItemStack &item,
|
||||
Client *client) const
|
||||
ItemVisualsManager::ItemVisuals *ItemVisualsManager::createItemVisuals(
|
||||
const ItemStack &item, Client *client, u16 &variant_count) const
|
||||
{
|
||||
// This is not thread-safe
|
||||
sanity_check(std::this_thread::get_id() == m_main_thread);
|
||||
|
@ -32,6 +32,8 @@ ItemVisualsManager::ItemVisuals *ItemVisualsManager::createItemVisuals( const It
|
|||
if (!inventory_overlay.empty())
|
||||
cache_key += ":" + inventory_overlay;
|
||||
|
||||
variant_count = client->ndef()->get(cache_key).variant_count;
|
||||
|
||||
// Skip if already in cache
|
||||
auto it = m_cached_item_visuals.find(cache_key);
|
||||
if (it != m_cached_item_visuals.end())
|
||||
|
@ -43,14 +45,23 @@ ItemVisualsManager::ItemVisuals *ItemVisualsManager::createItemVisuals( const It
|
|||
ITextureSource *tsrc = client->getTextureSource();
|
||||
|
||||
// Create new ItemVisuals
|
||||
auto cc = std::make_unique<ItemVisuals>();
|
||||
auto cc = std::make_unique<ItemVisuals[]>(variant_count);
|
||||
|
||||
cc->inventory_texture = NULL;
|
||||
for (u16 v = 0; v < variant_count; v++) {
|
||||
// Create an inventory texture
|
||||
cc[v].inventory_texture = NULL;
|
||||
if (!inventory_image.empty())
|
||||
cc->inventory_texture = tsrc->getTexture(inventory_image);
|
||||
getItemMesh(client, item, &(cc->wield_mesh));
|
||||
cc[v].inventory_texture = tsrc->getTexture(inventory_image);
|
||||
|
||||
cc->palette = tsrc->getPalette(def.palette_image);
|
||||
ItemStack item = ItemStack();
|
||||
item.name = def.name;
|
||||
if (v > 0)
|
||||
item.metadata.setString("variant", std::to_string(v));
|
||||
|
||||
getItemMesh(client, item, &(cc[v].wield_mesh));
|
||||
|
||||
cc[v].palette = tsrc->getPalette(def.palette_image);
|
||||
}
|
||||
|
||||
// Put in cache
|
||||
ItemVisuals *ptr = cc.get();
|
||||
|
@ -59,25 +70,29 @@ ItemVisualsManager::ItemVisuals *ItemVisualsManager::createItemVisuals( const It
|
|||
}
|
||||
|
||||
video::ITexture* ItemVisualsManager::getInventoryTexture(const ItemStack &item,
|
||||
Client *client) const
|
||||
u16 variant, Client *client) const
|
||||
{
|
||||
ItemVisuals *iv = createItemVisuals(item, client);
|
||||
if (!iv)
|
||||
u16 variant_count;
|
||||
ItemVisuals *iv = createItemVisuals(item, client, variant_count);
|
||||
if (!iv || (variant >= variant_count))
|
||||
return nullptr;
|
||||
return iv->inventory_texture;
|
||||
return iv[variant].inventory_texture;
|
||||
}
|
||||
|
||||
ItemMesh* ItemVisualsManager::getWieldMesh(const ItemStack &item, Client *client) const
|
||||
ItemMesh* ItemVisualsManager::getWieldMesh(const ItemStack &item, u16 variant,
|
||||
Client *client) const
|
||||
{
|
||||
ItemVisuals *iv = createItemVisuals(item, client);
|
||||
if (!iv)
|
||||
u16 variant_count;
|
||||
ItemVisuals *iv = createItemVisuals(item, client, variant_count);
|
||||
if (!iv || (variant >= variant_count))
|
||||
return nullptr;
|
||||
return &(iv->wield_mesh);
|
||||
return &(iv[variant].wield_mesh);
|
||||
}
|
||||
|
||||
Palette* ItemVisualsManager::getPalette(const ItemStack &item, Client *client) const
|
||||
{
|
||||
ItemVisuals *iv = createItemVisuals(item, client);
|
||||
u16 variant_count;
|
||||
ItemVisuals *iv = createItemVisuals(item, client, variant_count);
|
||||
if (!iv)
|
||||
return nullptr;
|
||||
return iv->palette;
|
||||
|
|
|
@ -30,11 +30,11 @@ struct ItemVisualsManager
|
|||
}
|
||||
|
||||
// Get item inventory texture
|
||||
video::ITexture* getInventoryTexture(const ItemStack &item, Client *client) const;
|
||||
video::ITexture* getInventoryTexture(const ItemStack &item, u16 variant, Client *client) const;
|
||||
|
||||
// Get item wield mesh
|
||||
// Once said to return nullptr if there is an inventory image, but this is wrong
|
||||
ItemMesh* getWieldMesh(const ItemStack &item, Client *client) const;
|
||||
ItemMesh* getWieldMesh(const ItemStack &item, u16 variant, Client *client) const;
|
||||
|
||||
// Get item palette
|
||||
Palette* getPalette(const ItemStack &item, Client *client) const;
|
||||
|
@ -63,7 +63,7 @@ private:
|
|||
// The id of the thread that is allowed to use irrlicht directly
|
||||
std::thread::id m_main_thread;
|
||||
// Cached textures and meshes
|
||||
mutable std::unordered_map<std::string, std::unique_ptr<ItemVisuals>> m_cached_item_visuals;
|
||||
mutable std::unordered_map<std::string, std::unique_ptr<ItemVisuals[]>> m_cached_item_visuals;
|
||||
|
||||
ItemVisuals* createItemVisuals(const ItemStack &item, Client *client) const;
|
||||
ItemVisuals* createItemVisuals(const ItemStack &item, Client *client, u16 &variant_count) const;
|
||||
};
|
||||
|
|
|
@ -344,7 +344,7 @@ void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data,
|
|||
{
|
||||
const NodeDefManager *ndef = data->m_nodedef;
|
||||
const ContentFeatures &f = ndef->get(mn);
|
||||
tile = f.tiles[tileindex];
|
||||
tile = f.tiles[mn.getVariant(f)][tileindex];
|
||||
bool has_crack = p == data->m_crack_pos_relative;
|
||||
for (TileLayer &layer : tile.layers) {
|
||||
if (layer.empty())
|
||||
|
|
|
@ -403,7 +403,9 @@ void Minimap::blitMinimapPixelsToImageSurface(
|
|||
MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->mode.map_size];
|
||||
|
||||
const ContentFeatures &f = m_ndef->get(mmpixel->n);
|
||||
const TileDef *tile = &f.tiledef[0];
|
||||
u16 v = mmpixel->n.getVariant(f);
|
||||
const TileDef *tile = &f.tiledef[v][0];
|
||||
video::SColor minimap_color = f.minimap_color[v];
|
||||
|
||||
// Color of the 0th tile (mostly this is the topmost)
|
||||
if(tile->has_color)
|
||||
|
@ -411,9 +413,9 @@ void Minimap::blitMinimapPixelsToImageSurface(
|
|||
else
|
||||
mmpixel->n.getColor(f, &tilecolor);
|
||||
|
||||
tilecolor.setRed(tilecolor.getRed() * f.minimap_color.getRed() / 255);
|
||||
tilecolor.setGreen(tilecolor.getGreen() * f.minimap_color.getGreen() / 255);
|
||||
tilecolor.setBlue(tilecolor.getBlue() * f.minimap_color.getBlue() / 255);
|
||||
tilecolor.setRed(tilecolor.getRed() * minimap_color.getRed() / 255);
|
||||
tilecolor.setGreen(tilecolor.getGreen() * minimap_color.getGreen() / 255);
|
||||
tilecolor.setBlue(tilecolor.getBlue() * minimap_color.getBlue() / 255);
|
||||
tilecolor.setAlpha(240);
|
||||
|
||||
map_image->setPixel(x, data->mode.map_size - z - 1, tilecolor);
|
||||
|
|
|
@ -867,7 +867,7 @@ bool ParticleManager::getNodeParticleParams(const MapNode &n,
|
|||
texid = tilenum - 1;
|
||||
else
|
||||
texid = myrand_range(0,5);
|
||||
const TileLayer &tile = f.tiles[texid].layers[0];
|
||||
const TileLayer &tile = f.tiles[n.getVariant(f)][texid].layers[0];
|
||||
p.animation.type = TAT_NONE;
|
||||
|
||||
// Only use first frame of animated texture
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "client/texturesource.h"
|
||||
#include "log.h"
|
||||
#include "util/numeric.h"
|
||||
#include "util/string.h"
|
||||
#include <map>
|
||||
#include <IMeshManipulator.h>
|
||||
#include "client/renderingengine.h"
|
||||
|
@ -292,7 +293,7 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
|
|||
}
|
||||
}
|
||||
|
||||
static scene::SMesh *createGenericNodeMesh(Client *client, MapNode n,
|
||||
static scene::SMesh *createGenericNodeMesh(Client *client, MapNode n, u16 variant,
|
||||
std::vector<ItemMeshBufferInfo> *buffer_info, const ContentFeatures &f)
|
||||
{
|
||||
n.setParam1(0xff);
|
||||
|
@ -310,6 +311,9 @@ static scene::SMesh *createGenericNodeMesh(Client *client, MapNode n,
|
|||
n.setParam2(1);
|
||||
}
|
||||
|
||||
if (f.variant_count > 1)
|
||||
n.setParam2(f.param2_variant.set(n.getParam2(), variant));
|
||||
|
||||
MeshCollector collector(v3f(0), v3f());
|
||||
{
|
||||
MeshMakeData mmd(client->ndef(), 1, MeshGrid{1});
|
||||
|
@ -354,6 +358,8 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
|
|||
const ItemDefinition &def = item.getDefinition(idef);
|
||||
const ContentFeatures &f = ndef->get(def.name);
|
||||
content_t id = ndef->getId(def.name);
|
||||
u16 variant = f.variant_count > 1 ?
|
||||
mystoi(item.metadata.getString("variant"), 0, f.variant_count - 1) : 0;
|
||||
|
||||
scene::SMesh *mesh = nullptr;
|
||||
|
||||
|
@ -395,8 +401,8 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
|
|||
v3f wscale = wield_scale;
|
||||
if (f.drawtype == NDT_FLOWINGLIQUID)
|
||||
wscale.Z *= 0.1f;
|
||||
const TileLayer &l0 = f.tiles[0].layers[0];
|
||||
const TileLayer &l1 = f.tiles[0].layers[1];
|
||||
const TileLayer &l0 = f.tiles[variant][0].layers[0];
|
||||
const TileLayer &l1 = f.tiles[variant][0].layers[1];
|
||||
setExtruded(tsrc->getTextureName(l0.texture_id),
|
||||
tsrc->getTextureName(l1.texture_id),
|
||||
wscale, tsrc,
|
||||
|
@ -408,7 +414,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
|
|||
}
|
||||
case NDT_PLANTLIKE_ROOTED: {
|
||||
// use the plant tile
|
||||
const TileLayer &l0 = f.special_tiles[0].layers[0];
|
||||
const TileLayer &l0 = f.special_tiles[variant][0].layers[0];
|
||||
setExtruded(tsrc->getTextureName(l0.texture_id),
|
||||
"", wield_scale, tsrc,
|
||||
l0.animation_frame_count);
|
||||
|
@ -421,7 +427,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
|
|||
if (def.place_param2)
|
||||
n.setParam2(*def.place_param2);
|
||||
|
||||
mesh = createGenericNodeMesh(client, n, &m_buffer_info, f);
|
||||
mesh = createGenericNodeMesh(client, n, variant, &m_buffer_info, f);
|
||||
changeToMesh(mesh);
|
||||
mesh->drop();
|
||||
m_meshnode->setScale(
|
||||
|
@ -544,6 +550,8 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
|
|||
const ItemDefinition &def = item.getDefinition(idef);
|
||||
const ContentFeatures &f = ndef->get(def.name);
|
||||
content_t id = ndef->getId(def.name);
|
||||
u16 variant = f.variant_count > 1 ?
|
||||
mystoi(item.metadata.getString("variant"), 0, f.variant_count - 1) : 0;
|
||||
|
||||
FATAL_ERROR_IF(!g_extrusion_mesh_cache, "Extrusion mesh cache is not yet initialized");
|
||||
|
||||
|
@ -569,8 +577,8 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
|
|||
} else if (def.type == ITEM_NODE) {
|
||||
switch (f.drawtype) {
|
||||
case NDT_PLANTLIKE: {
|
||||
const TileLayer &l0 = f.tiles[0].layers[0];
|
||||
const TileLayer &l1 = f.tiles[0].layers[1];
|
||||
const TileLayer &l0 = f.tiles[variant][0].layers[0];
|
||||
const TileLayer &l1 = f.tiles[variant][0].layers[1];
|
||||
mesh = getExtrudedMesh(tsrc,
|
||||
tsrc->getTextureName(l0.texture_id),
|
||||
tsrc->getTextureName(l1.texture_id));
|
||||
|
@ -581,7 +589,7 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
|
|||
}
|
||||
case NDT_PLANTLIKE_ROOTED: {
|
||||
// Use the plant tile
|
||||
const TileLayer &l0 = f.special_tiles[0].layers[0];
|
||||
const TileLayer &l0 = f.special_tiles[variant][0].layers[0];
|
||||
mesh = getExtrudedMesh(tsrc,
|
||||
tsrc->getTextureName(l0.texture_id), "");
|
||||
result->buffer_info.emplace_back(l0.has_color, l0.color);
|
||||
|
@ -593,7 +601,7 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
|
|||
if (def.place_param2)
|
||||
n.setParam2(*def.place_param2);
|
||||
|
||||
mesh = createGenericNodeMesh(client, n, &result->buffer_info, f);
|
||||
mesh = createGenericNodeMesh(client, n, variant, &result->buffer_info, f);
|
||||
scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,9 @@ void drawItemStack(
|
|||
auto *idef = client->idef();
|
||||
const ItemDefinition &def = item.getDefinition(idef);
|
||||
ItemVisualsManager* item_visuals = client->getItemVisualsManager();
|
||||
const ContentFeatures &f = client->ndef()->get(def.name);
|
||||
u16 variant = f.variant_count > 1 ?
|
||||
mystoi(item.metadata.getString("variant"), 0, f.variant_count - 1) : 0;
|
||||
|
||||
bool draw_overlay = false;
|
||||
|
||||
|
@ -60,7 +63,7 @@ void drawItemStack(
|
|||
|
||||
// Render as mesh if animated or no inventory image
|
||||
if ((enable_animations && rotation_kind < IT_ROT_NONE) || inventory_image.empty()) {
|
||||
imesh = item_visuals->getWieldMesh(item, client);
|
||||
imesh = item_visuals->getWieldMesh(item, variant, client);
|
||||
has_mesh = imesh && imesh->mesh;
|
||||
}
|
||||
if (has_mesh) {
|
||||
|
@ -155,7 +158,7 @@ void drawItemStack(
|
|||
|
||||
draw_overlay = def.type == ITEM_NODE && inventory_image.empty();
|
||||
} else { // Otherwise just draw as 2D
|
||||
video::ITexture *texture = item_visuals->getInventoryTexture(item, client);
|
||||
video::ITexture *texture = item_visuals->getInventoryTexture(item, variant, client);
|
||||
video::SColor color;
|
||||
if (texture) {
|
||||
color = item_visuals->getItemstackColor(item, client);
|
||||
|
|
|
@ -38,6 +38,11 @@ void MapNode::getColor(const ContentFeatures &f, video::SColor *color) const
|
|||
*color = f.color;
|
||||
}
|
||||
|
||||
u16 MapNode::getVariant(const ContentFeatures &f) const
|
||||
{
|
||||
return f.variant_count > 1 ? f.param2_variant.get(param2) % f.variant_count : 0;
|
||||
}
|
||||
|
||||
u8 MapNode::getFaceDir(const NodeDefManager *nodemgr,
|
||||
bool allow_wallmounted) const
|
||||
{
|
||||
|
|
|
@ -196,6 +196,12 @@ struct alignas(u32) MapNode
|
|||
*/
|
||||
void getColor(const ContentFeatures &f, video::SColor *color) const;
|
||||
|
||||
/*!
|
||||
* Returns the variant of the node.
|
||||
* \param f content features of this node
|
||||
*/
|
||||
u16 getVariant(const ContentFeatures &f) const;
|
||||
|
||||
inline void setLight(LightBank bank, u8 a_light, ContentLightingFlags f) noexcept
|
||||
{
|
||||
// If node doesn't contain light data, ignore this
|
||||
|
|
197
src/nodedef.cpp
197
src/nodedef.cpp
|
@ -316,12 +316,16 @@ ContentFeatures::ContentFeatures()
|
|||
ContentFeatures::~ContentFeatures()
|
||||
{
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
for (auto &t : tiles) {
|
||||
for (u16 j = 0; j < 6; j++) {
|
||||
delete tiles[j].layers[0].frames;
|
||||
delete tiles[j].layers[1].frames;
|
||||
delete t[j].layers[0].frames;
|
||||
delete t[j].layers[1].frames;
|
||||
}
|
||||
}
|
||||
for (auto &t : special_tiles) {
|
||||
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
|
||||
delete special_tiles[j].layers[0].frames;
|
||||
delete t[j].layers[0].frames;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -331,6 +335,8 @@ void ContentFeatures::reset()
|
|||
Cached stuff
|
||||
*/
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
tiles = std::vector<std::array<TileSpec, 6> >(1);
|
||||
special_tiles = std::vector<std::array<TileSpec, CF_SPECIAL_COUNT> >(1);
|
||||
solidness = 2;
|
||||
visual_solidness = 0;
|
||||
backface_culling = true;
|
||||
|
@ -355,17 +361,18 @@ void ContentFeatures::reset()
|
|||
mesh.clear();
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
mesh_ptr = nullptr;
|
||||
minimap_color = video::SColor(0, 0, 0, 0);
|
||||
minimap_color = std::vector<video::SColor>(1, video::SColor(0, 0, 0, 0));
|
||||
#endif
|
||||
visual_scale = 1.0;
|
||||
for (auto &i : tiledef)
|
||||
i = TileDef();
|
||||
for (auto &j : tiledef_special)
|
||||
j = TileDef();
|
||||
tiledef = std::vector<std::array<TileDef, 6> >(1);
|
||||
tiledef_overlay = std::vector<std::array<TileDef, 6> >(1);
|
||||
tiledef_special = std::vector<std::array<TileDef, CF_SPECIAL_COUNT> >(1);
|
||||
alpha = ALPHAMODE_OPAQUE;
|
||||
post_effect_color = video::SColor(0, 0, 0, 0);
|
||||
param_type = CPT_NONE;
|
||||
param_type_2 = CPT2_NONE;
|
||||
variant_count = 1;
|
||||
param2_variant = BitField<u8>();
|
||||
is_ground_content = false;
|
||||
light_propagates = false;
|
||||
sunlight_propagates = false;
|
||||
|
@ -457,12 +464,12 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
|
|||
os << serializeString16(mesh);
|
||||
writeF32(os, visual_scale);
|
||||
writeU8(os, 6);
|
||||
for (const TileDef &td : tiledef)
|
||||
for (const TileDef &td : tiledef[0])
|
||||
td.serialize(os, protocol_version);
|
||||
for (const TileDef &td : tiledef_overlay)
|
||||
for (const TileDef &td : tiledef_overlay[0])
|
||||
td.serialize(os, protocol_version);
|
||||
writeU8(os, CF_SPECIAL_COUNT);
|
||||
for (const TileDef &td : tiledef_special) {
|
||||
for (const TileDef &td : tiledef_special[0]) {
|
||||
td.serialize(os, protocol_version);
|
||||
}
|
||||
writeU8(os, getAlphaForLegacy());
|
||||
|
@ -535,6 +542,21 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
|
|||
writeU8(os, move_resistance);
|
||||
writeU8(os, liquid_move_physics);
|
||||
writeU8(os, post_effect_color_shaded);
|
||||
writeU16(os, variant_count);
|
||||
writeU8(os, param2_variant.getWidth());
|
||||
writeU8(os, param2_variant.getOffset());
|
||||
for (u16 v = 1; v < variant_count; v++) {
|
||||
for (const TileDef &td : tiledef[v])
|
||||
td.serialize(os, protocol_version);
|
||||
}
|
||||
for (u16 v = 1; v < variant_count; v++) {
|
||||
for (const TileDef &td : tiledef_overlay[v])
|
||||
td.serialize(os, protocol_version);
|
||||
}
|
||||
for (u16 v = 1; v < variant_count; v++) {
|
||||
for (const TileDef &td : tiledef_special[v])
|
||||
td.serialize(os, protocol_version);
|
||||
}
|
||||
}
|
||||
|
||||
void ContentFeatures::deSerialize(std::istream &is, u16 protocol_version)
|
||||
|
@ -568,13 +590,13 @@ void ContentFeatures::deSerialize(std::istream &is, u16 protocol_version)
|
|||
visual_scale = readF32(is);
|
||||
if (readU8(is) != 6)
|
||||
throw SerializationError("unsupported tile count");
|
||||
for (TileDef &td : tiledef)
|
||||
for (TileDef &td : tiledef[0])
|
||||
td.deSerialize(is, drawtype, protocol_version);
|
||||
for (TileDef &td : tiledef_overlay)
|
||||
for (TileDef &td : tiledef_overlay[0])
|
||||
td.deSerialize(is, drawtype, protocol_version);
|
||||
if (readU8(is) != CF_SPECIAL_COUNT)
|
||||
throw SerializationError("unsupported CF_SPECIAL_COUNT");
|
||||
for (TileDef &td : tiledef_special)
|
||||
for (TileDef &td : tiledef_special[0])
|
||||
td.deSerialize(is, drawtype, protocol_version);
|
||||
setAlphaFromLegacy(readU8(is));
|
||||
color.setRed(readU8(is));
|
||||
|
@ -665,6 +687,43 @@ void ContentFeatures::deSerialize(std::istream &is, u16 protocol_version)
|
|||
if (is.eof())
|
||||
throw SerializationError("");
|
||||
post_effect_color_shaded = tmp;
|
||||
|
||||
u16 tmp_variant_count = readU16(is);
|
||||
if (is.eof())
|
||||
throw SerializationError("");
|
||||
|
||||
// Reserve space first for memory safety.
|
||||
tiledef.reserve(variant_count);
|
||||
tiledef_overlay.reserve(variant_count);
|
||||
tiledef_special.reserve(variant_count);
|
||||
for (u16 v = 1; v < tmp_variant_count; v++) {
|
||||
tiledef.emplace_back();
|
||||
tiledef_overlay.emplace_back();
|
||||
tiledef_special.emplace_back();
|
||||
}
|
||||
|
||||
variant_count = tmp_variant_count;
|
||||
|
||||
tmp = readU8(is);
|
||||
u8 tmp2 = readU8(is);
|
||||
if (is.eof())
|
||||
throw SerializationError("");
|
||||
param2_variant = BitField<u8>(tmp, tmp2);
|
||||
|
||||
for (u16 v = 1; v < variant_count; v++) {
|
||||
for (TileDef &td : tiledef[v])
|
||||
td.deSerialize(is, drawtype, protocol_version);
|
||||
}
|
||||
|
||||
for (u16 v = 1; v < variant_count; v++) {
|
||||
for (TileDef &td : tiledef_overlay[v])
|
||||
td.deSerialize(is, drawtype, protocol_version);
|
||||
}
|
||||
|
||||
for (u16 v = 1; v < variant_count; v++) {
|
||||
for (TileDef &td : tiledef_special[v])
|
||||
td.deSerialize(is, drawtype, protocol_version);
|
||||
}
|
||||
} catch (SerializationError &e) {};
|
||||
}
|
||||
|
||||
|
@ -763,22 +822,27 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||
scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
|
||||
{
|
||||
// Figure out the actual tiles to use
|
||||
TileDef tdef[6];
|
||||
std::vector<std::array<TileDef, 6> > tdef(variant_count);
|
||||
for (u16 v = 0; v < variant_count; v++) {
|
||||
for (u32 j = 0; j < 6; j++) {
|
||||
tdef[j] = tiledef[j];
|
||||
if (tdef[j].name.empty()) {
|
||||
tdef[j].name = "no_texture.png";
|
||||
tdef[j].backface_culling = false;
|
||||
tdef[v][j] = tiledef[v][j];
|
||||
if (tdef[v][j].name.empty()) {
|
||||
tdef[v][j].name = "no_texture.png";
|
||||
tdef[v][j].backface_culling = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// also the overlay tiles
|
||||
TileDef tdef_overlay[6];
|
||||
std::vector<std::array<TileDef, 6> > tdef_overlay(variant_count);
|
||||
for (u16 v = 0; v < variant_count; v++) {
|
||||
for (u32 j = 0; j < 6; j++)
|
||||
tdef_overlay[j] = tiledef_overlay[j];
|
||||
tdef_overlay[v][j] = tiledef_overlay[v][j];
|
||||
}
|
||||
// also the special tiles
|
||||
TileDef tdef_spec[6];
|
||||
for (u32 j = 0; j < CF_SPECIAL_COUNT; j++) {
|
||||
tdef_spec[j] = tiledef_special[j];
|
||||
std::vector<std::array<TileDef, CF_SPECIAL_COUNT> > tdef_spec(variant_count);
|
||||
for (u16 v = 0; v < variant_count; v++) {
|
||||
for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
|
||||
tdef_spec[v][j] = tiledef_special[v][j];
|
||||
}
|
||||
|
||||
bool is_liquid = false;
|
||||
|
@ -830,9 +894,11 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||
solidness = 0;
|
||||
visual_solidness = 1;
|
||||
} else if (tsettings.leaves_style == LEAVES_SIMPLE) {
|
||||
for (u16 v = 0; v < variant_count; v++) {
|
||||
for (u32 j = 0; j < 6; j++) {
|
||||
if (!tdef_spec[j].name.empty())
|
||||
tdef[j].name = tdef_spec[j].name;
|
||||
if (!tdef_spec[v][j].name.empty())
|
||||
tdef[v][j].name = tdef_spec[v][j].name;
|
||||
}
|
||||
}
|
||||
drawtype = NDT_GLASSLIKE;
|
||||
solidness = 0;
|
||||
|
@ -847,9 +913,11 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||
drawtype = NDT_NORMAL;
|
||||
solidness = 2;
|
||||
}
|
||||
for (TileDef &td : tdef)
|
||||
for (u16 v = 0; v < variant_count; v++) {
|
||||
for (TileDef &td : tdef[v])
|
||||
td.name += std::string("^[noalpha");
|
||||
}
|
||||
}
|
||||
if (waving >= 1)
|
||||
material_type = TILE_MATERIAL_WAVING_LEAVES;
|
||||
break;
|
||||
|
@ -907,24 +975,31 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||
u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
|
||||
|
||||
// minimap pixel color = average color of top tile
|
||||
if (tsettings.enable_minimap && !tdef[0].name.empty() && drawtype != NDT_AIRLIKE)
|
||||
minimap_color = tsrc->getTextureAverageColor(tdef[0].name);
|
||||
if (tsettings.enable_minimap && !tdef[0][0].name.empty() && drawtype != NDT_AIRLIKE) {
|
||||
minimap_color = std::vector<video::SColor>(variant_count);
|
||||
for (u16 v = 0; v < variant_count; v++) {
|
||||
if (!tdef[v][0].name.empty())
|
||||
minimap_color[v] = tsrc->getTextureAverageColor(tdef[v][0].name);
|
||||
}
|
||||
}
|
||||
|
||||
// Tiles (fill in f->tiles[])
|
||||
bool any_polygon_offset = false;
|
||||
tiles = std::vector<std::array<TileSpec, 6> >(variant_count);
|
||||
for (u16 v = 0; v < variant_count; v++) {
|
||||
for (u16 j = 0; j < 6; j++) {
|
||||
tiles[j].world_aligned = isWorldAligned(tdef[j].align_style,
|
||||
tiles[v][j].world_aligned = isWorldAligned(tdef[v][j].align_style,
|
||||
tsettings.world_aligned_mode, drawtype);
|
||||
fillTileAttribs(tsrc, &tiles[j].layers[0], tiles[j], tdef[j],
|
||||
fillTileAttribs(tsrc, &tiles[v][j].layers[0], tiles[v][j], tdef[v][j],
|
||||
color, material_type, tile_shader,
|
||||
tdef[j].backface_culling, tsettings);
|
||||
if (!tdef_overlay[j].name.empty())
|
||||
fillTileAttribs(tsrc, &tiles[j].layers[1], tiles[j], tdef_overlay[j],
|
||||
tdef[v][j].backface_culling, tsettings);
|
||||
if (!tdef_overlay[v][j].name.empty())
|
||||
fillTileAttribs(tsrc, &tiles[v][j].layers[1], tiles[v][j], tdef_overlay[v][j],
|
||||
color, overlay_material, overlay_shader,
|
||||
tdef[j].backface_culling, tsettings);
|
||||
tdef[v][j].backface_culling, tsettings);
|
||||
|
||||
tiles[j].layers[0].need_polygon_offset = !tiles[j].layers[1].empty();
|
||||
any_polygon_offset |= tiles[j].layers[0].need_polygon_offset;
|
||||
tiles[v][j].layers[0].need_polygon_offset = !tiles[v][j].layers[1].empty();
|
||||
any_polygon_offset |= tiles[v][j].layers[0].need_polygon_offset;
|
||||
}
|
||||
|
||||
if (drawtype == NDT_MESH && any_polygon_offset) {
|
||||
|
@ -934,7 +1009,8 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||
// We can't assume this for mesh nodes, so apply it to all tiles (= materials)
|
||||
// then.
|
||||
for (u16 j = 0; j < 6; j++)
|
||||
tiles[j].layers[0].need_polygon_offset = true;
|
||||
tiles[v][j].layers[0].need_polygon_offset = true;
|
||||
}
|
||||
}
|
||||
|
||||
MaterialType special_material = material_type;
|
||||
|
@ -947,10 +1023,14 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||
u32 special_shader = shdsrc->getShader("nodes_shader", special_material, drawtype);
|
||||
|
||||
// Special tiles (fill in f->special_tiles[])
|
||||
special_tiles = std::vector<std::array<TileSpec, CF_SPECIAL_COUNT> >(variant_count);
|
||||
for (u16 v = 0; v < variant_count; v++) {
|
||||
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++)
|
||||
fillTileAttribs(tsrc, &special_tiles[j].layers[0], special_tiles[j], tdef_spec[j],
|
||||
fillTileAttribs(tsrc,
|
||||
&special_tiles[v][j].layers[0], special_tiles[v][j], tdef_spec[v][j],
|
||||
color, special_material, special_shader,
|
||||
tdef_spec[j].backface_culling, tsettings);
|
||||
tdef_spec[v][j].backface_culling, tsettings);
|
||||
}
|
||||
|
||||
if (param_type_2 == CPT2_COLOR ||
|
||||
param_type_2 == CPT2_COLORED_FACEDIR ||
|
||||
|
@ -1043,7 +1123,7 @@ void NodeDefManager::clear()
|
|||
ContentFeatures f;
|
||||
f.name = "unknown";
|
||||
for (int t = 0; t < 6; t++)
|
||||
f.tiledef[t].name = "unknown_node.png";
|
||||
f.tiledef[0][t].name = "unknown_node.png";
|
||||
// Insert directly into containers
|
||||
content_t c = CONTENT_UNKNOWN;
|
||||
m_content_features[c] = f;
|
||||
|
@ -1409,52 +1489,55 @@ void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &o
|
|||
|
||||
ContentFeatures &nodedef = m_content_features[id];
|
||||
|
||||
auto apply = [&] (TileDef &tile) {
|
||||
tile.name = texture_override.texture;
|
||||
// For now this works with tiledef_special since CF_SPECIAL_COUNT == 6.
|
||||
auto apply = [&] (std::vector<std::array<TileDef, 6> > &tiledef, u32 i) {
|
||||
for (u16 v = 0; v < nodedef.variant_count; v++) {
|
||||
tiledef[v][i].name = texture_override.texture;
|
||||
if (texture_override.world_scale > 0) {
|
||||
tile.align_style = ALIGN_STYLE_WORLD;
|
||||
tile.scale = texture_override.world_scale;
|
||||
tiledef[v][i].align_style = ALIGN_STYLE_WORLD;
|
||||
tiledef[v][i].scale = texture_override.world_scale;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Override tiles
|
||||
if (texture_override.hasTarget(OverrideTarget::TOP))
|
||||
apply(nodedef.tiledef[0]);
|
||||
apply(nodedef.tiledef, 0);
|
||||
|
||||
if (texture_override.hasTarget(OverrideTarget::BOTTOM))
|
||||
apply(nodedef.tiledef[1]);
|
||||
apply(nodedef.tiledef, 1);
|
||||
|
||||
if (texture_override.hasTarget(OverrideTarget::RIGHT))
|
||||
apply(nodedef.tiledef[2]);
|
||||
apply(nodedef.tiledef, 2);
|
||||
|
||||
if (texture_override.hasTarget(OverrideTarget::LEFT))
|
||||
apply(nodedef.tiledef[3]);
|
||||
apply(nodedef.tiledef, 3);
|
||||
|
||||
if (texture_override.hasTarget(OverrideTarget::BACK))
|
||||
apply(nodedef.tiledef[4]);
|
||||
apply(nodedef.tiledef, 4);
|
||||
|
||||
if (texture_override.hasTarget(OverrideTarget::FRONT))
|
||||
apply(nodedef.tiledef[5]);
|
||||
apply(nodedef.tiledef, 5);
|
||||
|
||||
|
||||
// Override special tiles, if applicable
|
||||
if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
|
||||
apply(nodedef.tiledef_special[0]);
|
||||
apply(nodedef.tiledef_special, 0);
|
||||
|
||||
if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
|
||||
apply(nodedef.tiledef_special[1]);
|
||||
apply(nodedef.tiledef_special, 1);
|
||||
|
||||
if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
|
||||
apply(nodedef.tiledef_special[2]);
|
||||
apply(nodedef.tiledef_special, 2);
|
||||
|
||||
if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
|
||||
apply(nodedef.tiledef_special[3]);
|
||||
apply(nodedef.tiledef_special, 3);
|
||||
|
||||
if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
|
||||
apply(nodedef.tiledef_special[4]);
|
||||
apply(nodedef.tiledef_special, 4);
|
||||
|
||||
if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
|
||||
apply(nodedef.tiledef_special[5]);
|
||||
apply(nodedef.tiledef_special, 5);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <iostream>
|
||||
#include <memory> // shared_ptr
|
||||
#include <map>
|
||||
#include <array>
|
||||
#include "mapnode.h"
|
||||
#include "nameidmapping.h"
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
|
@ -22,6 +23,7 @@ class Client;
|
|||
#include "texture_override.h" // TextureOverride
|
||||
#include "tileanimation.h"
|
||||
#include "util/pointabilities.h"
|
||||
#include "util/numeric.h"
|
||||
|
||||
class IItemDefManager;
|
||||
class ITextureSource;
|
||||
|
@ -308,9 +310,9 @@ struct ContentFeatures
|
|||
#if CHECK_CLIENT_BUILD()
|
||||
// 0 1 2 3 4 5
|
||||
// up down right left back front
|
||||
TileSpec tiles[6];
|
||||
std::vector<std::array<TileSpec, 6> > tiles; // Length is variant count
|
||||
// Special tiles
|
||||
TileSpec special_tiles[CF_SPECIAL_COUNT];
|
||||
std::vector<std::array<TileSpec, CF_SPECIAL_COUNT> > special_tiles;
|
||||
u8 solidness; // Used when choosing which face is drawn
|
||||
u8 visual_solidness; // When solidness=0, this tells how it looks like
|
||||
bool backface_culling;
|
||||
|
@ -336,6 +338,10 @@ struct ContentFeatures
|
|||
ContentParamType param_type;
|
||||
// Type of MapNode::param2
|
||||
ContentParamType2 param_type_2;
|
||||
// Number of node variants
|
||||
u16 variant_count = 1;
|
||||
// Bit field for variant in param2
|
||||
BitField<u8> param2_variant;
|
||||
|
||||
// --- VISUAL PROPERTIES ---
|
||||
|
||||
|
@ -343,13 +349,13 @@ struct ContentFeatures
|
|||
std::string mesh;
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
scene::SMesh *mesh_ptr; // mesh in case of mesh node
|
||||
video::SColor minimap_color;
|
||||
std::vector<video::SColor> minimap_color; // Length is variant count
|
||||
#endif
|
||||
float visual_scale; // Misc. scale parameter
|
||||
TileDef tiledef[6];
|
||||
std::vector<std::array<TileDef, 6> > tiledef; // Length is variant count
|
||||
// These will be drawn over the base tiles.
|
||||
TileDef tiledef_overlay[6];
|
||||
TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid
|
||||
std::vector<std::array<TileDef, 6> > tiledef_overlay; // Length is variant count
|
||||
std::vector<std::array<TileDef, CF_SPECIAL_COUNT> > tiledef_special; // eg. flowing liquid
|
||||
AlphaMode alpha;
|
||||
// The color of the node.
|
||||
video::SColor color;
|
||||
|
|
|
@ -201,6 +201,8 @@ void push_item_definition_full(lua_State *L, const ItemDefinition &i)
|
|||
{
|
||||
std::string type(enum_to_string(es_ItemType, i.type));
|
||||
|
||||
// variant_count is presently not included, unlike in the server-side definition table.
|
||||
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, i.name.c_str());
|
||||
lua_setfield(L, -2, "name");
|
||||
|
@ -660,6 +662,8 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype, bool special)
|
|||
// color = ...
|
||||
lua_getfield(L, index, "color");
|
||||
tiledef.has_color = read_color(L, -1, &tiledef.color);
|
||||
if (!tiledef.has_color)
|
||||
tiledef.color = video::SColor();
|
||||
lua_pop(L, 1);
|
||||
// animation = {}
|
||||
lua_getfield(L, index, "animation");
|
||||
|
@ -673,12 +677,76 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype, bool special)
|
|||
return tiledef;
|
||||
}
|
||||
|
||||
void read_tiledefs(lua_State *L, int index, u8 drawtype, bool special,
|
||||
std::array<TileDef, 6> &tiledefs)
|
||||
{
|
||||
if(index < 0)
|
||||
index = lua_gettop(L) + 1 + index;
|
||||
|
||||
if (lua_istable(L, index)) {
|
||||
lua_pushnil(L);
|
||||
int i = 0;
|
||||
while (lua_next(L, index) != 0) {
|
||||
// Read tiledef from value
|
||||
tiledefs[i] = read_tiledef(L, -1, drawtype, special);
|
||||
// removes value, keeps key for next iteration
|
||||
lua_pop(L, 1);
|
||||
i++;
|
||||
if (i == 6) {
|
||||
lua_pop(L, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Allow empthy {} definition of tile
|
||||
if (i==0) {
|
||||
tiledefs[0] = TileDef();
|
||||
i++;
|
||||
}
|
||||
// Copy last value to all remaining textures
|
||||
if (i >= 1) {
|
||||
TileDef lasttile = tiledefs[i - 1];
|
||||
while (i < 6) {
|
||||
tiledefs[i] = lasttile;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void read_default_tiledefs(lua_State *L, int index, u8 drawtype, bool special,
|
||||
std::vector<std::array<TileDef, 6> > &tiledefs)
|
||||
{
|
||||
read_tiledefs(L, index, drawtype, special, tiledefs[0]);
|
||||
for (u16 v = 1; v < tiledefs.size(); v++)
|
||||
tiledefs[v] = tiledefs[0];
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void read_content_features(lua_State *L, ContentFeatures &f, int index)
|
||||
{
|
||||
if(index < 0)
|
||||
index = lua_gettop(L) + 1 + index;
|
||||
|
||||
{
|
||||
int variant_count = getintfield_default(L, index, "variant_count", 1);
|
||||
if (variant_count < 1 || variant_count > UINT16_MAX)
|
||||
throw LuaError("Invalid variant_count " + variant_count);
|
||||
|
||||
if (variant_count > 1) {
|
||||
// Reserve memory first to avoid out-of-bounds indexing.
|
||||
f.tiledef = std::vector<std::array<TileDef, 6> >(variant_count);
|
||||
f.tiledef_overlay = std::vector<std::array<TileDef, 6> >(variant_count);
|
||||
f.tiledef_special =
|
||||
std::vector<std::array<TileDef, CF_SPECIAL_COUNT> >(variant_count);
|
||||
}
|
||||
|
||||
f.variant_count = variant_count;
|
||||
}
|
||||
|
||||
lua_getfield(L, index, "param2_variant");
|
||||
f.param2_variant = read_bitfield<u8>(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
/* Cache existence of some callbacks */
|
||||
lua_getfield(L, index, "on_construct");
|
||||
f.has_on_construct = !lua_isnil(L, -1);
|
||||
|
@ -718,76 +786,45 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index)
|
|||
|
||||
// tiles = {}
|
||||
lua_getfield(L, index, "tiles");
|
||||
if(lua_istable(L, -1)){
|
||||
int table = lua_gettop(L);
|
||||
lua_pushnil(L);
|
||||
int i = 0;
|
||||
while(lua_next(L, table) != 0){
|
||||
// Read tiledef from value
|
||||
f.tiledef[i] = read_tiledef(L, -1, f.drawtype, false);
|
||||
// removes value, keeps key for next iteration
|
||||
lua_pop(L, 1);
|
||||
i++;
|
||||
if(i==6){
|
||||
lua_pop(L, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Copy last value to all remaining textures
|
||||
if(i >= 1){
|
||||
TileDef lasttile = f.tiledef[i-1];
|
||||
while(i < 6){
|
||||
f.tiledef[i] = lasttile;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
read_default_tiledefs(L, -1, f.drawtype, false, f.tiledef);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// overlay_tiles = {}
|
||||
lua_getfield(L, index, "overlay_tiles");
|
||||
if (lua_istable(L, -1)) {
|
||||
int table = lua_gettop(L);
|
||||
lua_pushnil(L);
|
||||
int i = 0;
|
||||
while (lua_next(L, table) != 0) {
|
||||
// Read tiledef from value
|
||||
f.tiledef_overlay[i] = read_tiledef(L, -1, f.drawtype, false);
|
||||
// removes value, keeps key for next iteration
|
||||
lua_pop(L, 1);
|
||||
i++;
|
||||
if (i == 6) {
|
||||
lua_pop(L, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Copy last value to all remaining textures
|
||||
if (i >= 1) {
|
||||
TileDef lasttile = f.tiledef_overlay[i - 1];
|
||||
while (i < 6) {
|
||||
f.tiledef_overlay[i] = lasttile;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
read_default_tiledefs(L, -1, f.drawtype, false, f.tiledef_overlay);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// special_tiles = {}
|
||||
lua_getfield(L, index, "special_tiles");
|
||||
if(lua_istable(L, -1)){
|
||||
int table = lua_gettop(L);
|
||||
// This works as long as CF_SPECIAL_COUNT == 6.
|
||||
read_default_tiledefs(L, -1, f.drawtype, true, f.tiledef_special);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// variants = {}
|
||||
lua_getfield(L, index, "variants");
|
||||
if (lua_istable(L, -1)) {
|
||||
int variants_index = lua_gettop(L);
|
||||
lua_pushnil(L);
|
||||
int i = 0;
|
||||
while(lua_next(L, table) != 0){
|
||||
// Read tiledef from value
|
||||
f.tiledef_special[i] = read_tiledef(L, -1, f.drawtype, true);
|
||||
while (lua_next(L, variants_index) != 0) {
|
||||
size_t v = lua_tointeger(L, -2);
|
||||
if (v < f.variant_count && lua_istable(L, -1)) {
|
||||
// tiles = {}
|
||||
lua_getfield(L, -1, "tiles");
|
||||
read_tiledefs(L, -1, f.drawtype, false, f.tiledef[v]);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// overlay_tiles = {}
|
||||
lua_getfield(L, -1, "overlay_tiles");
|
||||
read_tiledefs(L, -1, f.drawtype, false, f.tiledef_overlay[v]);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// special_tiles = {}
|
||||
lua_getfield(L, -1, "special_tiles");
|
||||
read_tiledefs(L, -1, f.drawtype, true, f.tiledef_special[v]);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
// removes value, keeps key for next iteration
|
||||
lua_pop(L, 1);
|
||||
i++;
|
||||
if(i==CF_SPECIAL_COUNT){
|
||||
lua_pop(L, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
@ -1022,9 +1059,14 @@ void push_content_features(lua_State *L, const ContentFeatures &c)
|
|||
std::string drawtype(enum_to_string(ScriptApiNode::es_DrawType, c.drawtype));
|
||||
std::string liquid_type(enum_to_string(ScriptApiNode::es_LiquidType, c.liquid_type));
|
||||
|
||||
/* Missing "tiles" because I don't see a usecase (at least not yet). */
|
||||
// Missing "tiles" because I don't see a usecase (at least not yet).
|
||||
// "variants" is also missing as the only variant properties are tiles.
|
||||
|
||||
lua_newtable(L);
|
||||
lua_pushinteger(L, c.variant_count);
|
||||
lua_setfield(L, -2, "variant_count");
|
||||
push_bitfield(L, c.param2_variant);
|
||||
lua_setfield(L, -2, "param2_variant");
|
||||
lua_pushboolean(L, c.has_on_construct);
|
||||
lua_setfield(L, -2, "has_on_construct");
|
||||
lua_pushboolean(L, c.has_on_destruct);
|
||||
|
@ -1046,7 +1088,7 @@ void push_content_features(lua_State *L, const ContentFeatures &c)
|
|||
lua_setfield(L, -2, "mesh");
|
||||
}
|
||||
#if CHECK_CLIENT_BUILD()
|
||||
push_ARGB8(L, c.minimap_color); // I know this is not set-able w/ register_node,
|
||||
push_ARGB8(L, c.minimap_color[0]); // I know this is not set-able w/ register_node,
|
||||
lua_setfield(L, -2, "minimap_color"); // but the people need to know!
|
||||
#endif
|
||||
lua_pushnumber(L, c.visual_scale);
|
||||
|
|
|
@ -369,6 +369,15 @@ void push_aabb3f(lua_State *L, aabb3f box, f32 divisor)
|
|||
lua_rawseti(L, -2, 6);
|
||||
}
|
||||
|
||||
void push_bitfield_parts(lua_State *L, u8 width, u8 offset)
|
||||
{
|
||||
lua_createtable(L, 0, 2);
|
||||
lua_pushinteger(L, width);
|
||||
lua_setfield(L, -2, "width");
|
||||
lua_pushinteger(L, offset);
|
||||
lua_setfield(L, -2, "offset");
|
||||
}
|
||||
|
||||
std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
|
||||
{
|
||||
std::vector<aabb3f> boxes;
|
||||
|
@ -431,6 +440,16 @@ size_t read_stringlist(lua_State *L, int index, std::vector<std::string> *result
|
|||
return num_strings;
|
||||
}
|
||||
|
||||
void read_bitfield_parts(lua_State *L, int index, u8 *width, u8 *offset)
|
||||
{
|
||||
*width = 0;
|
||||
*offset = 0;
|
||||
if (lua_istable(L, index)) {
|
||||
getintfield(L, index, "width", *width);
|
||||
getintfield(L, index, "offset", *offset);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Table field getters
|
||||
*/
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include <string_view>
|
||||
|
||||
#include "irrlichttypes_bloated.h"
|
||||
//#include "common/c_types.h"
|
||||
#include "util/numeric.h"
|
||||
|
||||
extern "C" {
|
||||
#include <lua.h>
|
||||
|
@ -108,6 +110,15 @@ aabb3f read_aabb3f(lua_State *L, int index, f32 scale);
|
|||
std::vector<aabb3f> read_aabb3f_vector (lua_State *L, int index, f32 scale);
|
||||
size_t read_stringlist(lua_State *L, int index,
|
||||
std::vector<std::string> *result);
|
||||
void read_bitfield_parts (lua_State *L, int index, u8 *width, u8 *offset);
|
||||
|
||||
template<typename T>
|
||||
BitField<T> read_bitfield(lua_State *L, int index)
|
||||
{
|
||||
u8 width, offset;
|
||||
read_bitfield_parts(L, index, &width, &offset);
|
||||
return BitField<T>(width, offset);
|
||||
}
|
||||
|
||||
void push_v2s16(lua_State *L, v2s16 p);
|
||||
void push_v2s32(lua_State *L, v2s32 p);
|
||||
|
@ -120,6 +131,13 @@ void push_v3f(lua_State *L, v3f p);
|
|||
void push_v2f(lua_State *L, v2f p);
|
||||
void push_aabb3f_vector(lua_State *L, const std::vector<aabb3f> &boxes,
|
||||
f32 divisor = 1.0f);
|
||||
void push_bitfield_parts (lua_State *L, u8 width, u8 offset);
|
||||
|
||||
template<typename T>
|
||||
void push_bitfield(lua_State *L, const BitField<T> &bitfield)
|
||||
{
|
||||
push_bitfield_parts(L, bitfield.getWidth(), bitfield.getOffset());
|
||||
}
|
||||
|
||||
void warn_if_field_exists(lua_State *L, int table, const char *fieldname,
|
||||
std::string_view name, std::string_view message);
|
||||
|
|
|
@ -80,7 +80,7 @@ void TestGameDef::defineSomeNodes()
|
|||
"{default_stone.png";
|
||||
f = ContentFeatures();
|
||||
f.name = itemdef.name;
|
||||
for (TileDef &tiledef : f.tiledef)
|
||||
for (TileDef &tiledef : f.tiledef[0])
|
||||
tiledef.name = "default_stone.png";
|
||||
f.is_ground_content = true;
|
||||
idef->registerItem(itemdef);
|
||||
|
@ -98,10 +98,10 @@ void TestGameDef::defineSomeNodes()
|
|||
"{default_dirt.png&default_grass_side.png";
|
||||
f = ContentFeatures();
|
||||
f.name = itemdef.name;
|
||||
f.tiledef[0].name = "default_grass.png";
|
||||
f.tiledef[1].name = "default_dirt.png";
|
||||
f.tiledef[0][0].name = "default_grass.png";
|
||||
f.tiledef[0][1].name = "default_dirt.png";
|
||||
for(int i = 2; i < 6; i++)
|
||||
f.tiledef[i].name = "default_dirt.png^default_grass_side.png";
|
||||
f.tiledef[0][i].name = "default_dirt.png^default_grass_side.png";
|
||||
f.is_ground_content = true;
|
||||
idef->registerItem(itemdef);
|
||||
t_CONTENT_GRASS = ndef->set(f.name, f);
|
||||
|
@ -137,7 +137,7 @@ void TestGameDef::defineSomeNodes()
|
|||
f.liquid_viscosity = 4;
|
||||
f.is_ground_content = true;
|
||||
f.groups["liquids"] = 3;
|
||||
for (TileDef &tiledef : f.tiledef)
|
||||
for (TileDef &tiledef : f.tiledef[0])
|
||||
tiledef.name = "default_water.png";
|
||||
idef->registerItem(itemdef);
|
||||
t_CONTENT_WATER = ndef->set(f.name, f);
|
||||
|
@ -159,7 +159,7 @@ void TestGameDef::defineSomeNodes()
|
|||
f.light_source = LIGHT_MAX-1;
|
||||
f.is_ground_content = true;
|
||||
f.groups["liquids"] = 3;
|
||||
for (TileDef &tiledef : f.tiledef)
|
||||
for (TileDef &tiledef : f.tiledef[0])
|
||||
tiledef.name = "default_lava.png";
|
||||
idef->registerItem(itemdef);
|
||||
t_CONTENT_LAVA = ndef->set(f.name, f);
|
||||
|
@ -177,7 +177,7 @@ void TestGameDef::defineSomeNodes()
|
|||
"{default_brick.png";
|
||||
f = ContentFeatures();
|
||||
f.name = itemdef.name;
|
||||
for (TileDef &tiledef : f.tiledef)
|
||||
for (TileDef &tiledef : f.tiledef[0])
|
||||
tiledef.name = "default_brick.png";
|
||||
f.is_ground_content = true;
|
||||
idef->registerItem(itemdef);
|
||||
|
|
|
@ -62,9 +62,9 @@ public:
|
|||
f.drawtype = NDT_NORMAL;
|
||||
f.solidness = 2;
|
||||
f.alpha = ALPHAMODE_OPAQUE;
|
||||
for (TileDef &tiledef : f.tiledef)
|
||||
for (TileDef &tiledef : f.tiledef[0])
|
||||
tiledef.name = name + ".png";
|
||||
for (TileSpec &tile : f.tiles)
|
||||
for (TileSpec &tile : f.tiles[0])
|
||||
tile.layers[0].texture_id = texture;
|
||||
|
||||
return registerNode(itemdef, f);
|
||||
|
@ -89,9 +89,9 @@ public:
|
|||
f.groups["liquids"] = 3;
|
||||
f.liquid_alternative_source = "test:" + name + "_source";
|
||||
f.liquid_alternative_flowing = "test:" + name + "_flowing";
|
||||
for (TileDef &tiledef : f.tiledef)
|
||||
for (TileDef &tiledef : f.tiledef[0])
|
||||
tiledef.name = name + ".png";
|
||||
for (TileSpec &tile : f.tiles)
|
||||
for (TileSpec &tile : f.tiles[0])
|
||||
tile.layers[0].texture_id = texture;
|
||||
|
||||
return registerNode(itemdef, f);
|
||||
|
@ -116,10 +116,10 @@ public:
|
|||
f.groups["liquids"] = 3;
|
||||
f.liquid_alternative_source = "test:" + name + "_source";
|
||||
f.liquid_alternative_flowing = "test:" + name + "_flowing";
|
||||
f.tiledef_special[0].name = name + "_top.png";
|
||||
f.tiledef_special[1].name = name + "_side.png";
|
||||
f.special_tiles[0].layers[0].texture_id = texture_top;
|
||||
f.special_tiles[1].layers[0].texture_id = texture_side;
|
||||
f.tiledef_special[0][0].name = name + "_top.png";
|
||||
f.tiledef_special[0][1].name = name + "_side.png";
|
||||
f.special_tiles[0][0].layers[0].texture_id = texture_top;
|
||||
f.special_tiles[1][1].layers[0].texture_id = texture_side;
|
||||
|
||||
return registerNode(itemdef, f);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ TEST_CASE("Given a node definition, "
|
|||
{
|
||||
ContentFeatures f;
|
||||
f.name = "default:stone";
|
||||
for (TileDef &tiledef : f.tiledef)
|
||||
f.tiledef.emplace_back();
|
||||
for (TileDef &tiledef : f.tiledef[0])
|
||||
tiledef.name = "default_stone.png";
|
||||
f.is_ground_content = true;
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
void testSanitizeUntrusted();
|
||||
void testReadSeed();
|
||||
void testMyDoubleStringConversions();
|
||||
void testBitField();
|
||||
};
|
||||
|
||||
static TestUtilities g_test_instance;
|
||||
|
@ -87,6 +88,7 @@ void TestUtilities::runTests(IGameDef *gamedef)
|
|||
TEST(testSanitizeUntrusted);
|
||||
TEST(testReadSeed);
|
||||
TEST(testMyDoubleStringConversions);
|
||||
TEST(testBitField);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -800,3 +802,23 @@ void TestUtilities::testMyDoubleStringConversions()
|
|||
test_round_trip(0.3);
|
||||
test_round_trip(0.1 + 0.2);
|
||||
}
|
||||
|
||||
void TestUtilities::testBitField()
|
||||
{
|
||||
UASSERTEQ(int, BitField<u16>(5, 4).getWidth(), 5);
|
||||
UASSERTEQ(int, BitField<u16>(5, 4).getOffset(), 4);
|
||||
UASSERTEQ(u16, BitField<u16>(5, 4).get(0xEFDCU), 0x1DU);
|
||||
UASSERTEQ(u16, BitField<u16>(5, 4).set(0xEFDCU, 0xF0FU), 0xEEFCU);
|
||||
UASSERTEQ(int, BitField<u16>(17, 0).getWidth(), 16);
|
||||
UASSERTEQ(int, BitField<u16>(17, 0).getOffset(), 0);
|
||||
UASSERTEQ(u16, BitField<u16>(17, 0).get(0xEFDCU), 0xEFDCU);
|
||||
UASSERTEQ(u16, BitField<u16>(17, 0).set(0xEFDCU, 0xFFFFU), 0xFFFFU);
|
||||
UASSERTEQ(int, BitField<u16>(16, 17).getWidth(), 0);
|
||||
UASSERTEQ(int, BitField<u16>(16, 17).getOffset(), 0);
|
||||
UASSERTEQ(u16, BitField<u16>(16, 17).get(0xEFDCU), 0);
|
||||
UASSERTEQ(u16, BitField<u16>(16, 17).set(0xEFDCU, 0), 0xEFDCU);
|
||||
UASSERTEQ(int, BitField<u16>(8, 15).getWidth(), 1);
|
||||
UASSERTEQ(int, BitField<u16>(8, 15).getOffset(), 15);
|
||||
UASSERTEQ(u16, BitField<u16>(8, 15).get(0xEFDCU), 1);
|
||||
UASSERTEQ(u16, BitField<u16>(8, 15).set(0xEFDCU, 0), 0x6FDCU);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
#include <matrix4.h>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#if _MSC_VER
|
||||
#include <intrin.h> // For popcnt
|
||||
#endif
|
||||
|
||||
// Like std::clamp but allows mismatched types
|
||||
template <typename T, typename T2, typename T3>
|
||||
|
@ -277,6 +281,53 @@ inline void set_bits(u32 *x, u32 pos, u32 len, u32 val)
|
|||
*x |= (val & mask) << pos;
|
||||
}
|
||||
|
||||
inline unsigned popcount(unsigned b)
|
||||
{
|
||||
#if _MSC_VER
|
||||
return __popcnt(b);
|
||||
#else
|
||||
return __builtin_popcount(b);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class BitField {
|
||||
public:
|
||||
static_assert(static_cast<T>(-1) > static_cast<T>(0),
|
||||
"Bit fields only work with unsigned integer types");
|
||||
|
||||
BitField(): m_mask(0), m_offset(0) {}
|
||||
|
||||
BitField(u8 width, u8 offset)
|
||||
{
|
||||
// Truncate to the type width to avoid undefined behavior.
|
||||
unsigned bit_count = sizeof(T) * CHAR_BIT;
|
||||
offset = MYMIN(offset, bit_count);
|
||||
width = MYMIN(width, bit_count - offset);
|
||||
m_mask = ((width < bit_count ? 1 << width : 0) - 1) << (offset % bit_count);
|
||||
m_offset = offset % bit_count;
|
||||
}
|
||||
|
||||
inline u8 getWidth() const { return popcount(m_mask); }
|
||||
inline u8 getOffset() const { return m_offset; }
|
||||
|
||||
inline T get(T bits) const
|
||||
{
|
||||
return (bits & m_mask) >> m_offset;
|
||||
}
|
||||
|
||||
inline T set(T bits, T value) const
|
||||
{
|
||||
bits &= ~m_mask;
|
||||
bits |= (value << m_offset) & m_mask;
|
||||
return bits;
|
||||
}
|
||||
|
||||
private:
|
||||
T m_mask;
|
||||
u8 m_offset;
|
||||
};
|
||||
|
||||
inline u32 calc_parity(u32 v)
|
||||
{
|
||||
v ^= v >> 16;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue