Skip to content

Commit e737b1c

Browse files
sofarShadowNinja
authored andcommitted
Nodebox: Allow nodeboxes to "connect"
We introduce a new nodebox type "connected", and allow these nodes to have optional nodeboxes that connect it to other connecting nodeboxes. This is all done at scenedraw time in the client. The client will inspect the surrounding nodes and if they are to be connected to, it will draw the appropriate connecting nodeboxes to make those connections. In the node_box definition, we have to specify separate nodeboxes for each valid connection. This allows us to make nodes that connect only horizontally (the common case) by providing optional nodeboxes for +x, -x, +z, -z directions. Or this allows us to make wires that can connect up and down, by providing nodeboxes that connect it up and down (+y, -y) as well. The optional nodeboxes can be arrays. They are named "connect_top, "connect_bottom", "connect_front", "connect_left", "connect_back" and "connect_right". Here, "front" means the south facing side of the node that has facedir = 0. Additionally, a "fixed" nodebox list present will always be drawn, so one can make a central post, for instance. This "fixed" nodebox can be omitted, or it can be an array of nodeboxes. Collision boxes are also updated in exactly the same fashion, which allows you to walk over the upper extremities of the individual node boxes, or stand really close to them. You can also walk up node noxes that are small in height, all as expected, and unlike the NDT_FENCELIKE nodes. I've posted a screenshot demonstrating the flexibility at http://i.imgur.com/zaJq8jo.png In the screenshot, all connecting nodes are of this new subtype. Transparent textures render incorrectly, Which I don't think is related to this text, as other nodeboxes also have issues with this. A protocol bump is performed in order to be able to send older clients a nodeblock that is usable for them. In order to avoid abuse of users we send older clients a "full-size" node, so that it's impossible for them to try and walk through a fence or wall that's created in this fashion. This was tested with a pre-bump client connected against a server running the new protocol. These nodes connect to other nodes, and you can select which ones those are by specifying node names (or group names) in the connects_to string array: connects_to = { "group:fence", "default:wood" } By default, nodes do not connect to anything, allowing you to create nodes that always have to be paired in order to connect. lua_api.txt is updated to reflect the extension to the node_box API. Example lua code needed to generate these nodes can be found here: https://gist.github.com/sofar/b381c8c192c8e53e6062
1 parent 8c951ca commit e737b1c

13 files changed

+335
-53
lines changed

doc/lua_api.txt

+16
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,18 @@ A nodebox is defined as any of:
617617
wall_bottom = box,
618618
wall_side = box
619619
}
620+
{
621+
-- A node that has optional boxes depending on neighbouring nodes'
622+
-- presence and type. See also `connects_to`.
623+
type = "connected",
624+
fixed = box OR {box1, box2, ...}
625+
connect_top = box OR {box1, box2, ...}
626+
connect_bottom = box OR {box1, box2, ...}
627+
connect_front = box OR {box1, box2, ...}
628+
connect_left = box OR {box1, box2, ...}
629+
connect_back = box OR {box1, box2, ...}
630+
connect_right = box OR {box1, box2, ...}
631+
}
620632

621633
A `box` is defined as:
622634

@@ -3461,6 +3473,10 @@ Definition tables
34613473
light_source = 0, -- Amount of light emitted by node
34623474
damage_per_second = 0, -- If player is inside node, this damage is caused
34633475
node_box = {type="regular"}, -- See "Node boxes"
3476+
connects_to = nodenames, --[[
3477+
* Used for nodebox nodes with the type == "connected"
3478+
* Specifies to what neighboring nodes connections will be drawn
3479+
* e.g. `{"group:fence", "default:wood"}` or `"default:stone"` ]]
34643480
mesh = "model",
34653481
selection_box = {type="regular"}, -- See "Node boxes" --[[
34663482
^ If drawtype "nodebox" is used and selection_box is nil, then node_box is used. ]]

src/collision.cpp

+38-2
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,13 @@ bool wouldCollideWithCeiling(
185185
return false;
186186
}
187187

188+
static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
189+
Map *map, MapNode n, int v, int *neighbors)
190+
{
191+
MapNode n2 = map->getNodeNoEx(p);
192+
if (nodedef->nodeboxConnects(n, n2))
193+
*neighbors |= v;
194+
}
188195

189196
collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
190197
f32 pos_max_d, const aabb3f &box_0,
@@ -261,12 +268,41 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
261268
// Object collides into walkable nodes
262269

263270
any_position_valid = true;
264-
const ContentFeatures &f = gamedef->getNodeDefManager()->get(n);
271+
INodeDefManager *nodedef = gamedef->getNodeDefManager();
272+
const ContentFeatures &f = nodedef->get(n);
265273
if(f.walkable == false)
266274
continue;
267275
int n_bouncy_value = itemgroup_get(f.groups, "bouncy");
268276

269-
std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(gamedef->ndef());
277+
int neighbors = 0;
278+
if (f.drawtype == NDT_NODEBOX && f.node_box.type == NODEBOX_CONNECTED) {
279+
v3s16 p2 = p;
280+
281+
p2.Y++;
282+
getNeighborConnectingFace(p2, nodedef, map, n, 1, &neighbors);
283+
284+
p2 = p;
285+
p2.Y--;
286+
getNeighborConnectingFace(p2, nodedef, map, n, 2, &neighbors);
287+
288+
p2 = p;
289+
p2.Z--;
290+
getNeighborConnectingFace(p2, nodedef, map, n, 4, &neighbors);
291+
292+
p2 = p;
293+
p2.X--;
294+
getNeighborConnectingFace(p2, nodedef, map, n, 8, &neighbors);
295+
296+
p2 = p;
297+
p2.Z++;
298+
getNeighborConnectingFace(p2, nodedef, map, n, 16, &neighbors);
299+
300+
p2 = p;
301+
p2.X++;
302+
getNeighborConnectingFace(p2, nodedef, map, n, 32, &neighbors);
303+
}
304+
std::vector<aabb3f> nodeboxes;
305+
n.getCollisionBoxes(gamedef->ndef(), &nodeboxes, neighbors);
270306
for(std::vector<aabb3f>::iterator
271307
i = nodeboxes.begin();
272308
i != nodeboxes.end(); ++i)

src/content_mapblock.cpp

+40-1
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,14 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
163163
}
164164
}
165165

166+
static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
167+
MeshMakeData *data, MapNode n, int v, int *neighbors)
168+
{
169+
MapNode n2 = data->m_vmanip.getNodeNoEx(p);
170+
if (nodedef->nodeboxConnects(n, n2))
171+
*neighbors |= v;
172+
}
173+
166174
/*
167175
TODO: Fix alpha blending for special nodes
168176
Currently only the last element rendered is blended correct
@@ -1501,7 +1509,38 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
15011509

15021510
v3f pos = intToFloat(p, BS);
15031511

1504-
std::vector<aabb3f> boxes = n.getNodeBoxes(nodedef);
1512+
int neighbors = 0;
1513+
1514+
// locate possible neighboring nodes to connect to
1515+
if (f.node_box.type == NODEBOX_CONNECTED) {
1516+
v3s16 p2 = p;
1517+
1518+
p2.Y++;
1519+
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 1, &neighbors);
1520+
1521+
p2 = p;
1522+
p2.Y--;
1523+
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 2, &neighbors);
1524+
1525+
p2 = p;
1526+
p2.Z--;
1527+
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 4, &neighbors);
1528+
1529+
p2 = p;
1530+
p2.X--;
1531+
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 8, &neighbors);
1532+
1533+
p2 = p;
1534+
p2.Z++;
1535+
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 16, &neighbors);
1536+
1537+
p2 = p;
1538+
p2.X++;
1539+
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 32, &neighbors);
1540+
}
1541+
1542+
std::vector<aabb3f> boxes;
1543+
n.getNodeBoxes(nodedef, &boxes, neighbors);
15051544
for(std::vector<aabb3f>::iterator
15061545
i = boxes.begin();
15071546
i != boxes.end(); ++i)

src/game.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,9 @@ PointedThing getPointedThing(Client *client, Hud *hud, const v3f &player_positio
358358
if (!isPointableNode(n, client, liquids_pointable)) {
359359
continue;
360360
}
361-
std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
361+
362+
std::vector<aabb3f> boxes;
363+
n.getSelectionBoxes(nodedef, &boxes);
362364

363365
v3s16 np(x, y, z);
364366
v3f npf = intToFloat(np, BS);
@@ -389,7 +391,8 @@ PointedThing getPointedThing(Client *client, Hud *hud, const v3f &player_positio
389391
f32 d = 0.001 * BS;
390392
MapNode n = map.getNodeNoEx(pointed_pos);
391393
v3f npf = intToFloat(pointed_pos, BS);
392-
std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
394+
std::vector<aabb3f> boxes;
395+
n.getSelectionBoxes(nodedef, &boxes);
393396
f32 face_min_distance = 1000 * BS;
394397
for (std::vector<aabb3f>::const_iterator
395398
i = boxes.begin();

src/localplayer.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
310310
if (sneak_node_found) {
311311
f32 cb_max = 0;
312312
MapNode n = map->getNodeNoEx(m_sneak_node);
313-
std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(nodemgr);
313+
std::vector<aabb3f> nodeboxes;
314+
n.getCollisionBoxes(nodemgr, &nodeboxes);
314315
for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
315316
it != nodeboxes.end(); ++it) {
316317
aabb3f box = *it;

src/mapnode.cpp

+52-13
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,12 @@ void MapNode::rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot)
214214
}
215215
}
216216

217-
static std::vector<aabb3f> transformNodeBox(const MapNode &n,
218-
const NodeBox &nodebox, INodeDefManager *nodemgr)
217+
void transformNodeBox(const MapNode &n, const NodeBox &nodebox,
218+
INodeDefManager *nodemgr, std::vector<aabb3f> *p_boxes, u8 neighbors = 0)
219219
{
220-
std::vector<aabb3f> boxes;
221-
if(nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED)
222-
{
220+
std::vector<aabb3f> &boxes = *p_boxes;
221+
222+
if (nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED) {
223223
const std::vector<aabb3f> &fixed = nodebox.fixed;
224224
int facedir = n.getFaceDir(nodemgr);
225225
u8 axisdir = facedir>>2;
@@ -395,32 +395,71 @@ static std::vector<aabb3f> transformNodeBox(const MapNode &n,
395395
boxes.push_back(box);
396396
}
397397
}
398+
else if (nodebox.type == NODEBOX_CONNECTED)
399+
{
400+
size_t boxes_size = boxes.size();
401+
boxes_size += nodebox.fixed.size();
402+
if (neighbors & 1)
403+
boxes_size += nodebox.connect_top.size();
404+
if (neighbors & 2)
405+
boxes_size += nodebox.connect_bottom.size();
406+
if (neighbors & 4)
407+
boxes_size += nodebox.connect_front.size();
408+
if (neighbors & 8)
409+
boxes_size += nodebox.connect_left.size();
410+
if (neighbors & 16)
411+
boxes_size += nodebox.connect_back.size();
412+
if (neighbors & 32)
413+
boxes_size += nodebox.connect_right.size();
414+
boxes.reserve(boxes_size);
415+
416+
#define BOXESPUSHBACK(c) do { \
417+
for (std::vector<aabb3f>::const_iterator \
418+
it = (c).begin(); \
419+
it != (c).end(); ++it) \
420+
(boxes).push_back(*it); \
421+
} while (0)
422+
423+
BOXESPUSHBACK(nodebox.fixed);
424+
425+
if (neighbors & 1)
426+
BOXESPUSHBACK(nodebox.connect_top);
427+
if (neighbors & 2)
428+
BOXESPUSHBACK(nodebox.connect_bottom);
429+
if (neighbors & 4)
430+
BOXESPUSHBACK(nodebox.connect_front);
431+
if (neighbors & 8)
432+
BOXESPUSHBACK(nodebox.connect_left);
433+
if (neighbors & 16)
434+
BOXESPUSHBACK(nodebox.connect_back);
435+
if (neighbors & 32)
436+
BOXESPUSHBACK(nodebox.connect_right);
437+
}
398438
else // NODEBOX_REGULAR
399439
{
400440
boxes.push_back(aabb3f(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2));
401441
}
402-
return boxes;
403442
}
404443

405-
std::vector<aabb3f> MapNode::getNodeBoxes(INodeDefManager *nodemgr) const
444+
void MapNode::getNodeBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors)
406445
{
407446
const ContentFeatures &f = nodemgr->get(*this);
408-
return transformNodeBox(*this, f.node_box, nodemgr);
447+
transformNodeBox(*this, f.node_box, nodemgr, boxes, neighbors);
409448
}
410449

411-
std::vector<aabb3f> MapNode::getCollisionBoxes(INodeDefManager *nodemgr) const
450+
void MapNode::getCollisionBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors)
412451
{
413452
const ContentFeatures &f = nodemgr->get(*this);
414453
if (f.collision_box.fixed.empty())
415-
return transformNodeBox(*this, f.node_box, nodemgr);
454+
transformNodeBox(*this, f.node_box, nodemgr, boxes, neighbors);
416455
else
417-
return transformNodeBox(*this, f.collision_box, nodemgr);
456+
transformNodeBox(*this, f.collision_box, nodemgr, boxes, neighbors);
418457
}
419458

420-
std::vector<aabb3f> MapNode::getSelectionBoxes(INodeDefManager *nodemgr) const
459+
void MapNode::getSelectionBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes)
421460
{
422461
const ContentFeatures &f = nodemgr->get(*this);
423-
return transformNodeBox(*this, f.selection_box, nodemgr);
462+
transformNodeBox(*this, f.selection_box, nodemgr, boxes);
424463
}
425464

426465
u8 MapNode::getMaxLevel(INodeDefManager *nodemgr) const

src/mapnode.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -240,17 +240,17 @@ struct MapNode
240240
/*
241241
Gets list of node boxes (used for rendering (NDT_NODEBOX))
242242
*/
243-
std::vector<aabb3f> getNodeBoxes(INodeDefManager *nodemgr) const;
243+
void getNodeBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors = 0);
244244

245245
/*
246246
Gets list of selection boxes
247247
*/
248-
std::vector<aabb3f> getSelectionBoxes(INodeDefManager *nodemgr) const;
248+
void getSelectionBoxes(INodeDefManager *nodemg, std::vector<aabb3f> *boxes);
249249

250250
/*
251251
Gets list of collision boxes
252252
*/
253-
std::vector<aabb3f> getCollisionBoxes(INodeDefManager *nodemgr) const;
253+
void getCollisionBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors = 0);
254254

255255
/*
256256
Liquid helpers

src/network/networkprotocol.h

+1
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
135135
PROTOCOL_VERSION 27:
136136
backface_culling: backwards compatibility for playing with
137137
newer client on pre-27 servers.
138+
Add nodedef v3 - connected nodeboxes
138139
*/
139140

140141
#define LATEST_PROTOCOL_VERSION 27

0 commit comments

Comments
 (0)