This commit is contained in:
Wuzzy 2024-05-16 07:01:07 +02:00 committed by GitHub
commit 515191519f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 344 additions and 135 deletions

View File

@ -1327,16 +1327,20 @@ The function of `param2` is determined by `paramtype2` in node definition.
* 4dir modulo 4 = rotation
* Otherwise, behavior is identical to facedir
* `paramtype2 = "leveled"`
* Only valid for "nodebox" with 'type = "leveled"', and "plantlike_rooted".
* Leveled nodebox:
* Only valid for the drawtypes `nodebox`, `plantlike` and `plantlike_rooted`
* Nodebox:
* Nodebox `type` must be set to `"leveled"`
* The level of the top face of the nodebox is stored in `param2`.
* The other faces are defined by 'fixed = {}' like 'type = "fixed"'
nodeboxes.
* The nodebox height is (`param2` / 64) nodes.
* The maximum accepted value of `param2` is 127.
* Rooted plantlike:
* Boxes in '`leveled_fixed = {}`' will never change.
* Plantlike:
* The height of the 'plantlike' section is stored in `param2`.
* The height is (`param2` / 16) nodes.
* Rooted plantlike:
* Same as plantlike
* `paramtype2 = "degrotate"`
* Valid for `plantlike` and `mesh` drawtypes. The rotation of the node is
stored in `param2`.
@ -1535,8 +1539,29 @@ A nodebox is defined as any of:
-- by the node parameter 'leveled = ', or if 'paramtype2 == "leveled"'
-- by param2.
-- Other faces are defined by 'fixed = {}' as with 'type = "fixed"'.
-- Optionally add 'leveled_fixed = {}' for static boxes.
type = "leveled",
fixed = box OR {box1, box2, ...}
fixed = box OR {box1, box2, ...} -- top face is variable
leveled_fixed = box OR {box1, box2, ...} -- never changes
}
{
-- Same as leveled, but the box height is in sync with the 'plant'
-- of 'plantlike' nodes (steps of 1/16).
-- Recommended use for this is as selection box.
-- Be careful when using this as a collision box -- do not exceed the
-- max. permissible heights of collision boxes (see collision box
-- definition).
type = "leveled_plantlike",
fixed, leveled_fixed = -- see above
}
{
-- Same as leveled, but the box height is in sync with the graphical
-- representation of of 'plantlike_rooted' nodes (steps of 1/16).
-- The upper box bound begins at the top side of the base cube (0.5).
-- Recommended use for this is as selection box.
-- Be careful when using this as a collision box (see above).
type = "leveled_plantlike_rooted",
fixed, leveled_fixed = -- see above
}
{
-- A box like the selection box for torches
@ -9477,6 +9502,9 @@ Used by `minetest.register_node`.
-- Custom collision box definition. Multiple boxes can be defined.
-- If "nodebox" drawtype is used and collision_box is nil, then node_box
-- definition is used for the collision box.
-- Collision boxes that are larger than the node itself are allowed, but
-- only up to an absolute value of -0.5 + 127/64 on each axis. If you
-- exceed this limit, there's no guarantee that collisions still work.
-- Support maps made in and before January 2012
legacy_facedir_simple = false,

View File

@ -363,6 +363,11 @@ minetest.register_node("testnodes:plantlike_leveled", {
tiles = {
{ name = "testnodes_plantlike_leveled.png", tileable_vertical = true },
},
-- also test the 'leveled_plantlike' nodebox
selection_box = {
type = "leveled_plantlike",
fixed = { -0.4, -0.5, -0.4, 0.4, 0.4, 0.4 },
},
-- We set a default param2 here only for convenience, to make the "plant" visible after placement
@ -442,6 +447,14 @@ minetest.register_node("testnodes:plantlike_rooted_leveled", {
special_tiles = {
{ name = "testnodes_plantlike_rooted_leveled.png", tileable_vertical = true },
},
-- also test the 'leveled_plantlike_rooted' nodebox
selection_box = {
type = "leveled_plantlike_rooted",
-- variable box for plant
fixed = { -0.4, 0.5, -0.4, 0.4, 0.5, 0.4 },
-- fixed box for the base cube
leveled_fixed = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 },
},
-- We set a default param2 here only for convenience, to make the "plant" visible after placement

View File

@ -61,6 +61,30 @@ minetest.register_node("testnodes:nodebox_leveled", {
groups = {dig_immediate=3},
})
-- Leveled nodebox that also contains a static nodebox
minetest.register_node("testnodes:nodebox_leveled_fixed", {
description = S("Combined Leveled Nodebox Test Node").."\n"..
S("param2 = height of center box (0..127)").."\n"..
S("Other boxes are unaffected by param2"),
tiles = {"testnodes_nodebox.png"},
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "leveled",
node_box = {
type = "leveled",
fixed = {-0.25, 0.0, -0.25, 0.25, -0.499, 0.25},
leveled_fixed = {
{-0.5, -0.5, -0.5, -0.25, 0.0, -0.25},
{0.25, -0.5, 0.25, 0.5, 0.0, 0.5},
{-0.5, -0.5, 0.25, -0.25, 0.0, 0.5},
{0.25, -0.5, -0.5, 0.5, 0.0, -0.25},
},
},
groups = {dig_immediate=3},
})
local nodebox_wall = {

View File

@ -75,6 +75,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// Use floatToInt(p, BS) and intToFloat(p, BS).
#define BS 10.0f
// This number defines the safe max absolute X/Y/Z coordinate in which
// selection boxes still work safely. Beyond that limit, selection boxes
// might break.
#define SAFE_SELECTION_BOX_LIMIT (16.5f)
// Dimension of a MapBlock
#define MAP_BLOCKSIZE 16
// This makes mesh updates too slow, as many meshes are updated during

View File

@ -185,142 +185,174 @@ void MapNode::rotateAlongYAxis(const NodeDefManager *nodemgr, Rotation rot)
}
}
void buildFixedNodeBox(const MapNode &n, const NodeBox &nodebox, const NodeDefManager *nodemgr,
std::vector<aabb3f> &boxes, std::vector<aabb3f> input_boxes,
enum NodeBoxType nbt) {
u8 facedir = n.getFaceDir(nodemgr, true);
u8 axisdir = facedir>>2;
facedir &= 0x03;
for (aabb3f box : input_boxes) {
switch (nbt) {
case NODEBOX_LEVELED: {
box.MaxEdge.Y = (-0.5f + n.getLevel(nodemgr) / 64.0f) * BS;
break;
}
case NODEBOX_LEVELED_PLANTLIKE:
case NODEBOX_LEVELED_PLANTLIKE_ROOTED: {
u8 height = n.getParam2();
if (nbt == NODEBOX_LEVELED_PLANTLIKE_ROOTED) {
box.MaxEdge.Y = (0.5f + height / 16.0f) * BS;
} else {
box.MaxEdge.Y = (-0.5f + height / 16.0f) * BS;
}
if (box.MaxEdge.Y > SAFE_SELECTION_BOX_LIMIT * BS) {
box.MaxEdge.Y = SAFE_SELECTION_BOX_LIMIT * BS;
}
break;
}
default: {
break;
}
}
switch (axisdir) {
case 0:
if(facedir == 1)
{
box.MinEdge.rotateXZBy(-90);
box.MaxEdge.rotateXZBy(-90);
}
else if(facedir == 2)
{
box.MinEdge.rotateXZBy(180);
box.MaxEdge.rotateXZBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateXZBy(90);
box.MaxEdge.rotateXZBy(90);
}
break;
case 1: // z+
box.MinEdge.rotateYZBy(90);
box.MaxEdge.rotateYZBy(90);
if(facedir == 1)
{
box.MinEdge.rotateXYBy(90);
box.MaxEdge.rotateXYBy(90);
}
else if(facedir == 2)
{
box.MinEdge.rotateXYBy(180);
box.MaxEdge.rotateXYBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateXYBy(-90);
box.MaxEdge.rotateXYBy(-90);
}
break;
case 2: //z-
box.MinEdge.rotateYZBy(-90);
box.MaxEdge.rotateYZBy(-90);
if(facedir == 1)
{
box.MinEdge.rotateXYBy(-90);
box.MaxEdge.rotateXYBy(-90);
}
else if(facedir == 2)
{
box.MinEdge.rotateXYBy(180);
box.MaxEdge.rotateXYBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateXYBy(90);
box.MaxEdge.rotateXYBy(90);
}
break;
case 3: //x+
box.MinEdge.rotateXYBy(-90);
box.MaxEdge.rotateXYBy(-90);
if(facedir == 1)
{
box.MinEdge.rotateYZBy(90);
box.MaxEdge.rotateYZBy(90);
}
else if(facedir == 2)
{
box.MinEdge.rotateYZBy(180);
box.MaxEdge.rotateYZBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateYZBy(-90);
box.MaxEdge.rotateYZBy(-90);
}
break;
case 4: //x-
box.MinEdge.rotateXYBy(90);
box.MaxEdge.rotateXYBy(90);
if(facedir == 1)
{
box.MinEdge.rotateYZBy(-90);
box.MaxEdge.rotateYZBy(-90);
}
else if(facedir == 2)
{
box.MinEdge.rotateYZBy(180);
box.MaxEdge.rotateYZBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateYZBy(90);
box.MaxEdge.rotateYZBy(90);
}
break;
case 5:
box.MinEdge.rotateXYBy(-180);
box.MaxEdge.rotateXYBy(-180);
if(facedir == 1)
{
box.MinEdge.rotateXZBy(90);
box.MaxEdge.rotateXZBy(90);
}
else if(facedir == 2)
{
box.MinEdge.rotateXZBy(180);
box.MaxEdge.rotateXZBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateXZBy(-90);
box.MaxEdge.rotateXZBy(-90);
}
break;
default:
break;
}
box.repair();
boxes.push_back(box);
}
}
void transformNodeBox(const MapNode &n, const NodeBox &nodebox,
const NodeDefManager *nodemgr, std::vector<aabb3f> *p_boxes,
u8 neighbors = 0)
{
std::vector<aabb3f> &boxes = *p_boxes;
if (nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED) {
const auto &fixed = nodebox.fixed;
int facedir = n.getFaceDir(nodemgr, true);
u8 axisdir = facedir>>2;
facedir&=0x03;
boxes.reserve(boxes.size() + fixed.size());
for (aabb3f box : fixed) {
if (nodebox.type == NODEBOX_LEVELED)
box.MaxEdge.Y = (-0.5f + n.getLevel(nodemgr) / 64.0f) * BS;
switch (axisdir) {
case 0:
if(facedir == 1)
{
box.MinEdge.rotateXZBy(-90);
box.MaxEdge.rotateXZBy(-90);
}
else if(facedir == 2)
{
box.MinEdge.rotateXZBy(180);
box.MaxEdge.rotateXZBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateXZBy(90);
box.MaxEdge.rotateXZBy(90);
}
break;
case 1: // z+
box.MinEdge.rotateYZBy(90);
box.MaxEdge.rotateYZBy(90);
if(facedir == 1)
{
box.MinEdge.rotateXYBy(90);
box.MaxEdge.rotateXYBy(90);
}
else if(facedir == 2)
{
box.MinEdge.rotateXYBy(180);
box.MaxEdge.rotateXYBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateXYBy(-90);
box.MaxEdge.rotateXYBy(-90);
}
break;
case 2: //z-
box.MinEdge.rotateYZBy(-90);
box.MaxEdge.rotateYZBy(-90);
if(facedir == 1)
{
box.MinEdge.rotateXYBy(-90);
box.MaxEdge.rotateXYBy(-90);
}
else if(facedir == 2)
{
box.MinEdge.rotateXYBy(180);
box.MaxEdge.rotateXYBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateXYBy(90);
box.MaxEdge.rotateXYBy(90);
}
break;
case 3: //x+
box.MinEdge.rotateXYBy(-90);
box.MaxEdge.rotateXYBy(-90);
if(facedir == 1)
{
box.MinEdge.rotateYZBy(90);
box.MaxEdge.rotateYZBy(90);
}
else if(facedir == 2)
{
box.MinEdge.rotateYZBy(180);
box.MaxEdge.rotateYZBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateYZBy(-90);
box.MaxEdge.rotateYZBy(-90);
}
break;
case 4: //x-
box.MinEdge.rotateXYBy(90);
box.MaxEdge.rotateXYBy(90);
if(facedir == 1)
{
box.MinEdge.rotateYZBy(-90);
box.MaxEdge.rotateYZBy(-90);
}
else if(facedir == 2)
{
box.MinEdge.rotateYZBy(180);
box.MaxEdge.rotateYZBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateYZBy(90);
box.MaxEdge.rotateYZBy(90);
}
break;
case 5:
box.MinEdge.rotateXYBy(-180);
box.MaxEdge.rotateXYBy(-180);
if(facedir == 1)
{
box.MinEdge.rotateXZBy(90);
box.MaxEdge.rotateXZBy(90);
}
else if(facedir == 2)
{
box.MinEdge.rotateXZBy(180);
box.MaxEdge.rotateXZBy(180);
}
else if(facedir == 3)
{
box.MinEdge.rotateXZBy(-90);
box.MaxEdge.rotateXZBy(-90);
}
break;
default:
break;
}
box.repair();
boxes.push_back(box);
if (nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED ||
nodebox.type == NODEBOX_LEVELED_PLANTLIKE ||
nodebox.type == NODEBOX_LEVELED_PLANTLIKE_ROOTED) {
const std::vector<aabb3f> &fixed = nodebox.fixed;
const std::vector<aabb3f> &leveled_fixed = nodebox.leveled_fixed;
enum NodeBoxType nbt = nodebox.type;
if (nbt == NODEBOX_LEVELED || nbt == NODEBOX_LEVELED_PLANTLIKE ||
nbt == NODEBOX_LEVELED_PLANTLIKE_ROOTED) {
buildFixedNodeBox(n, nodebox, nodemgr, boxes, leveled_fixed, NODEBOX_FIXED);
}
buildFixedNodeBox(n, nodebox, nodemgr, boxes, fixed, nbt);
}
else if(nodebox.type == NODEBOX_WALLMOUNTED)
{

View File

@ -51,6 +51,7 @@ void NodeBox::reset()
type = NODEBOX_REGULAR;
// default is empty
fixed.clear();
leveled_fixed.clear();
// default is sign/ladder-like
wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
@ -65,6 +66,8 @@ void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
switch (type) {
case NODEBOX_LEVELED:
case NODEBOX_LEVELED_PLANTLIKE:
case NODEBOX_LEVELED_PLANTLIKE_ROOTED:
case NODEBOX_FIXED:
writeU8(os, type);
@ -73,6 +76,14 @@ void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
writeV3F32(os, nodebox.MinEdge);
writeV3F32(os, nodebox.MaxEdge);
}
if (type == NODEBOX_LEVELED || type == NODEBOX_LEVELED_PLANTLIKE ||
type == NODEBOX_LEVELED_PLANTLIKE_ROOTED) {
writeU16(os, leveled_fixed.size());
for (const aabb3f &nodebox : leveled_fixed) {
writeV3F32(os, nodebox.MinEdge);
writeV3F32(os, nodebox.MaxEdge);
}
}
break;
case NODEBOX_WALLMOUNTED:
writeU8(os, type);
@ -111,6 +122,7 @@ void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
WRITEBOX(c.disconnected_right);
WRITEBOX(c.disconnected);
WRITEBOX(c.disconnected_sides);
WRITEBOX(leveled_fixed);
break;
}
default:
@ -131,7 +143,10 @@ void NodeBox::deSerialize(std::istream &is)
case NODEBOX_REGULAR:
break;
case NODEBOX_FIXED:
case NODEBOX_LEVELED: {
case NODEBOX_LEVELED:
case NODEBOX_LEVELED_PLANTLIKE:
case NODEBOX_LEVELED_PLANTLIKE_ROOTED:
{
u16 fixed_count = readU16(is);
while(fixed_count--) {
aabb3f box;
@ -139,6 +154,18 @@ void NodeBox::deSerialize(std::istream &is)
box.MaxEdge = readV3F32(is);
fixed.push_back(box);
}
if(type == NODEBOX_LEVELED ||
type == NODEBOX_LEVELED_PLANTLIKE ||
type == NODEBOX_LEVELED_PLANTLIKE_ROOTED) {
u16 leveled_fixed_count = readU16(is);
while(leveled_fixed_count--)
{
aabb3f box;
box.MinEdge = readV3F32(is);
box.MaxEdge = readV3F32(is);
leveled_fixed.push_back(box);
}
}
break;
}
case NODEBOX_WALLMOUNTED:
@ -177,6 +204,7 @@ void NodeBox::deSerialize(std::istream &is)
READBOXES(c.disconnected_right);
READBOXES(c.disconnected);
READBOXES(c.disconnected_sides);
READBOXES(leveled_fixed);
break;
}
default:
@ -1204,6 +1232,54 @@ void boxVectorUnion(const std::vector<aabb3f> &boxes, aabb3f *box_union)
}
}
/*!
* Helper function for fixed/leveled nodeboxes in getBoxUnionReturns.
* Returns the smallest box that contains all boxes
* in the vector. Box_union is expanded.
* @param[in] features used to decide whether the nodebox
* can be rotated
* @param[in, out] box_union the union of the arguments
* @param to_add nodebox to add to union
* @param nbt node box type
*/
void rawUnionFixed(const ContentFeatures &features,
aabb3f *box_union,
const std::vector<aabb3f> &to_add, enum NodeBoxType nbt)
{
// Raw union (fixed)
aabb3f half_processed(0, 0, 0, 0, 0, 0);
boxVectorUnion(to_add, &half_processed);
// Set leveled boxes to maximal
if (nbt == NODEBOX_LEVELED) {
// -0.5 + 127/64
half_processed.MaxEdge.Y = 1.484375f * BS;
} else if (nbt == NODEBOX_LEVELED_PLANTLIKE ||
nbt == NODEBOX_LEVELED_PLANTLIKE_ROOTED) {
half_processed.MaxEdge.Y = SAFE_SELECTION_BOX_LIMIT * BS;
}
if (features.param_type_2 == CPT2_FACEDIR ||
features.param_type_2 == CPT2_COLORED_FACEDIR) {
// Get maximal coordinate
f32 coords[] = {
fabsf(half_processed.MinEdge.X),
fabsf(half_processed.MinEdge.Y),
fabsf(half_processed.MinEdge.Z),
fabsf(half_processed.MaxEdge.X),
fabsf(half_processed.MaxEdge.Y),
fabsf(half_processed.MaxEdge.Z) };
f32 max = 0;
for (float coord : coords) {
if (max < coord) {
max = coord;
}
}
// Add the union of all possible rotated boxes
box_union->addInternalPoint(-max, -max, -max);
box_union->addInternalPoint(+max, +max, +max);
} else {
box_union->addInternalBox(half_processed);
}
}
/*!
* Returns a box that contains the nodebox in every case.
@ -1250,6 +1326,16 @@ void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
} else {
box_union->addInternalBox(half_processed);
}
}
case NODEBOX_LEVELED_PLANTLIKE:
case NODEBOX_LEVELED_PLANTLIKE_ROOTED: {
NodeBoxType nbt = nodebox.type;
if (nbt == NODEBOX_LEVELED ||
nbt == NODEBOX_LEVELED_PLANTLIKE ||
nbt == NODEBOX_LEVELED_PLANTLIKE_ROOTED) {
rawUnionFixed(features, box_union, nodebox.leveled_fixed, NODEBOX_FIXED);
}
rawUnionFixed(features, box_union, nodebox.fixed, nbt);
break;
}
case NODEBOX_WALLMOUNTED: {

View File

@ -103,6 +103,8 @@ enum NodeBoxType : u8
NODEBOX_WALLMOUNTED, // Box for wall mounted nodes; (top, bottom, side)
NODEBOX_LEVELED, // Same as fixed, but with dynamic height from param2. for snow, ...
NODEBOX_CONNECTED, // optionally draws nodeboxes if a neighbor node attaches
NODEBOX_LEVELED_PLANTLIKE, // Same as leveled, but in sync with plantlike height
NODEBOX_LEVELED_PLANTLIKE_ROOTED, // Same as leveled, but in sync with plantlike_rooted height
};
struct NodeBoxConnected
@ -136,6 +138,8 @@ struct NodeBox
// NODEBOX_CONNECTED
// (kept externally to not bloat the structure)
std::shared_ptr<NodeBoxConnected> connected;
// NODEBOX_LEVELED
std::vector<aabb3f> leveled_fixed;
NodeBox()
{ reset(); }

View File

@ -1111,13 +1111,27 @@ void push_nodebox(lua_State *L, const NodeBox &box)
lua_pushstring(L, "regular");
lua_setfield(L, -2, "type");
break;
case NODEBOX_LEVELED:
case NODEBOX_FIXED:
lua_pushstring(L, "fixed");
lua_setfield(L, -2, "type");
push_aabb3f_vector(L, box.fixed);
lua_setfield(L, -2, "fixed");
break;
case NODEBOX_LEVELED:
case NODEBOX_LEVELED_PLANTLIKE:
case NODEBOX_LEVELED_PLANTLIKE_ROOTED:
if (box.type == NODEBOX_LEVELED)
lua_pushstring(L, "leveled");
else if (box.type == NODEBOX_LEVELED_PLANTLIKE)
lua_pushstring(L, "leveled_plantlike");
else if (box.type == NODEBOX_LEVELED_PLANTLIKE_ROOTED)
lua_pushstring(L, "leveled_plantlike_rooted");
lua_setfield(L, -2, "type");
push_aabb3f_vector(L, box.fixed);
lua_setfield(L, -2, "fixed");
push_aabb3f_vector(L, box.leveled_fixed);
lua_setfield(L, -2, "leveled_fixed");
break;
case NODEBOX_WALLMOUNTED:
lua_pushstring(L, "wallmounted");
lua_setfield(L, -2, "type");
@ -1286,6 +1300,7 @@ NodeBox read_nodebox(lua_State *L, int index)
NODEBOXREADVEC(c.disconnected, "disconnected");
NODEBOXREADVEC(c.disconnected_sides, "disconnected_sides");
}
NODEBOXREADVEC(nodebox.leveled_fixed, "leveled_fixed");
return nodebox;
}

View File

@ -93,6 +93,8 @@ struct EnumString ScriptApiNode::es_NodeBoxType[] =
{NODEBOX_WALLMOUNTED, "wallmounted"},
{NODEBOX_LEVELED, "leveled"},
{NODEBOX_CONNECTED, "connected"},
{NODEBOX_LEVELED_PLANTLIKE, "leveled_plantlike"},
{NODEBOX_LEVELED_PLANTLIKE_ROOTED, "leveled_plantlike_rooted"},
{0, NULL},
};