1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-01 17:38:41 +00:00

NDT_NODEBOX, collision code rewrite with step height, stairs mod -- rebased after itemdef

This commit is contained in:
Kahrl 2012-02-02 19:20:46 +01:00
parent a1eb2836c0
commit 6bf4931fc2
16 changed files with 1090 additions and 615 deletions

View file

@ -0,0 +1 @@
default

93
data/mods/stairs/init.lua Normal file
View file

@ -0,0 +1,93 @@
stairs = {}
-- Node will be called stairs:stair_<subname>
function stairs.register_stair(subname, recipeitem, material, images, description)
minetest.register_node("stairs:stair_" .. subname, {
description = description,
drawtype = "nodebox",
tile_images = images,
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = true,
node_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
{-0.5, 0, 0, 0.5, 0.5, 0.5},
},
},
material = material,
})
minetest.register_craft({
output = 'stairs:stair_' .. subname .. ' 4',
recipe = {
{recipeitem, "", ""},
{recipeitem, recipeitem, ""},
{recipeitem, recipeitem, recipeitem},
},
})
end
-- Node will be called stairs:slab_<subname>
function stairs.register_slab(subname, recipeitem, material, images, description)
minetest.register_node("stairs:slab_" .. subname, {
description = description,
drawtype = "nodebox",
tile_images = images,
paramtype = "light",
is_ground_content = true,
node_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
},
selection_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
},
material = material,
})
minetest.register_craft({
output = 'stairs:slab_' .. subname .. ' 3',
recipe = {
{recipeitem, recipeitem, recipeitem},
},
})
end
-- Nodes will be called stairs:{stair,slab}_<subname>
function stairs.register_stair_and_slab(subname, recipeitem, material, images, desc_stair, desc_slab)
stairs.register_stair(subname, recipeitem, material, images, desc_stair)
stairs.register_slab(subname, recipeitem, material, images, desc_slab)
end
stairs.register_stair_and_slab("wood", "default:wood",
minetest.digprop_woodlike(0.75),
{"default_wood.png"},
"Wooden stair",
"Wooden slab")
stairs.register_stair_and_slab("stone", "default:stone",
minetest.digprop_stonelike(0.75),
{"default_stone.png"},
"Stone stair",
"Stone slab")
stairs.register_stair_and_slab("cobble", "default:cobble",
minetest.digprop_stonelike(0.75),
{"default_cobble.png"},
"Cobble stair",
"Cobble slab")
stairs.register_stair_and_slab("brick", "default:brick",
minetest.digprop_stonelike(0.75),
{"default_brick.png"},
"Brick stair",
"Brick slab")
stairs.register_stair_and_slab("sandstone", "default:sandstone",
minetest.digprop_stonelike(0.75),
{"default_sandstone.png"},
"Sandstone stair",
"Sandstone slab")

View file

@ -22,28 +22,240 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map.h" #include "map.h"
#include "nodedef.h" #include "nodedef.h"
#include "gamedef.h" #include "gamedef.h"
#include "log.h"
#include <vector>
// Helper function:
// Checks for collision of a moving aabbox with a static aabbox
// Returns -1 if no collision, 0 if X collision, 1 if Y collision, 2 if Z collision
// The time after which the collision occurs is stored in dtime.
int axisAlignedCollision(
const aabb3f &staticbox, const aabb3f &movingbox,
const v3f &speed, f32 d, f32 &dtime)
{
//TimeTaker tt("axisAlignedCollision");
f32 xsize = (staticbox.MaxEdge.X - staticbox.MinEdge.X);
f32 ysize = (staticbox.MaxEdge.Y - staticbox.MinEdge.Y);
f32 zsize = (staticbox.MaxEdge.Z - staticbox.MinEdge.Z);
aabb3f relbox(
movingbox.MinEdge.X - staticbox.MinEdge.X,
movingbox.MinEdge.Y - staticbox.MinEdge.Y,
movingbox.MinEdge.Z - staticbox.MinEdge.Z,
movingbox.MaxEdge.X - staticbox.MinEdge.X,
movingbox.MaxEdge.Y - staticbox.MinEdge.Y,
movingbox.MaxEdge.Z - staticbox.MinEdge.Z
);
if(speed.X > 0) // Check for collision with X- plane
{
if(relbox.MaxEdge.X <= d)
{
dtime = - relbox.MaxEdge.X / speed.X;
if((relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
(relbox.MaxEdge.Y + speed.Y * dtime > 0) &&
(relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
(relbox.MaxEdge.Z + speed.Z * dtime > 0))
return 0;
}
else if(relbox.MinEdge.X > xsize)
{
return -1;
}
}
else if(speed.X < 0) // Check for collision with X+ plane
{
if(relbox.MinEdge.X >= xsize - d)
{
dtime = (xsize - relbox.MinEdge.X) / speed.X;
if((relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
(relbox.MaxEdge.Y + speed.Y * dtime > 0) &&
(relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
(relbox.MaxEdge.Z + speed.Z * dtime > 0))
return 0;
}
else if(relbox.MaxEdge.X < 0)
{
return -1;
}
}
// NO else if here
if(speed.Y > 0) // Check for collision with Y- plane
{
if(relbox.MaxEdge.Y <= d)
{
dtime = - relbox.MaxEdge.Y / speed.Y;
if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
(relbox.MaxEdge.X + speed.X * dtime > 0) &&
(relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
(relbox.MaxEdge.Z + speed.Z * dtime > 0))
return 1;
}
else if(relbox.MinEdge.Y > ysize)
{
return -1;
}
}
else if(speed.Y < 0) // Check for collision with Y+ plane
{
if(relbox.MinEdge.Y >= ysize - d)
{
dtime = (ysize - relbox.MinEdge.Y) / speed.Y;
if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
(relbox.MaxEdge.X + speed.X * dtime > 0) &&
(relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
(relbox.MaxEdge.Z + speed.Z * dtime > 0))
return 1;
}
else if(relbox.MaxEdge.Y < 0)
{
return -1;
}
}
// NO else if here
if(speed.Z > 0) // Check for collision with Z- plane
{
if(relbox.MaxEdge.Z <= d)
{
dtime = - relbox.MaxEdge.Z / speed.Z;
if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
(relbox.MaxEdge.X + speed.X * dtime > 0) &&
(relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
(relbox.MaxEdge.Y + speed.Y * dtime > 0))
return 2;
}
//else if(relbox.MinEdge.Z > zsize)
//{
// return -1;
//}
}
else if(speed.Z < 0) // Check for collision with Z+ plane
{
if(relbox.MinEdge.Z >= zsize - d)
{
dtime = (zsize - relbox.MinEdge.Z) / speed.Z;
if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
(relbox.MaxEdge.X + speed.X * dtime > 0) &&
(relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
(relbox.MaxEdge.Y + speed.Y * dtime > 0))
return 2;
}
//else if(relbox.MaxEdge.Z < 0)
//{
// return -1;
//}
}
return -1;
}
// Helper function:
// Checks if moving the movingbox up by the given distance would hit a ceiling.
bool wouldCollideWithCeiling(
const std::vector<aabb3f> &staticboxes,
const aabb3f &movingbox,
f32 y_increase, f32 d)
{
//TimeTaker tt("wouldCollideWithCeiling");
assert(y_increase >= 0);
for(std::vector<aabb3f>::const_iterator
i = staticboxes.begin();
i != staticboxes.end(); i++)
{
const aabb3f& staticbox = *i;
if((movingbox.MaxEdge.Y - d <= staticbox.MinEdge.Y) &&
(movingbox.MaxEdge.Y + y_increase > staticbox.MinEdge.Y) &&
(movingbox.MinEdge.X < staticbox.MaxEdge.X) &&
(movingbox.MaxEdge.X > staticbox.MinEdge.X) &&
(movingbox.MinEdge.Z < staticbox.MaxEdge.Z) &&
(movingbox.MaxEdge.Z > staticbox.MinEdge.Z))
return true;
}
return false;
}
collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
f32 pos_max_d, const core::aabbox3d<f32> &box_0, f32 pos_max_d, const aabb3f &box_0,
f32 dtime, v3f &pos_f, v3f &speed_f) f32 stepheight, f32 dtime,
v3f &pos_f, v3f &speed_f, v3f &accel_f)
{ {
TimeTaker tt("collisionMoveSimple");
collisionMoveResult result; collisionMoveResult result;
v3f oldpos_f = pos_f; /*
v3s16 oldpos_i = floatToInt(oldpos_f, BS); Calculate new velocity
*/
speed_f += accel_f * dtime;
/* /*
Calculate new position Collect node boxes in movement range
*/ */
pos_f += speed_f * dtime; std::vector<aabb3f> cboxes;
std::vector<bool> is_unloaded;
std::vector<bool> is_step_up;
{
TimeTaker tt2("collisionMoveSimple collect boxes");
v3s16 oldpos_i = floatToInt(pos_f, BS);
v3s16 newpos_i = floatToInt(pos_f + speed_f * dtime, BS);
s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1;
s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1;
s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1;
s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1;
s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1;
s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1;
for(s16 x = min_x; x <= max_x; x++)
for(s16 y = min_y; y <= max_y; y++)
for(s16 z = min_z; z <= max_z; z++)
{
try{
// Object collides into walkable nodes
MapNode n = map->getNode(v3s16(x,y,z));
if(gamedef->getNodeDefManager()->get(n).walkable == false)
continue;
std::vector<aabb3f> nodeboxes = n.getNodeBoxes(gamedef->ndef());
for(std::vector<aabb3f>::iterator
i = nodeboxes.begin();
i != nodeboxes.end(); i++)
{
aabb3f box = *i;
box.MinEdge += v3f(x, y, z)*BS;
box.MaxEdge += v3f(x, y, z)*BS;
cboxes.push_back(box);
is_unloaded.push_back(false);
is_step_up.push_back(false);
}
}
catch(InvalidPositionException &e)
{
// Collide with unloaded nodes
aabb3f box = getNodeBox(v3s16(x,y,z), BS);
cboxes.push_back(box);
is_unloaded.push_back(true);
is_step_up.push_back(false);
}
}
} // tt2
assert(cboxes.size() == is_unloaded.size());
assert(cboxes.size() == is_step_up.size());
/* /*
Collision detection Collision detection
*/ */
// position in nodes
v3s16 pos_i = floatToInt(pos_f, BS);
/* /*
Collision uncertainty radius Collision uncertainty radius
Make it a bit larger than the maximum distance of movement Make it a bit larger than the maximum distance of movement
@ -55,47 +267,126 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
// This should always apply, otherwise there are glitches // This should always apply, otherwise there are glitches
assert(d > pos_max_d); assert(d > pos_max_d);
/* int loopcount = 0;
Calculate collision box
*/
core::aabbox3d<f32> box = box_0;
box.MaxEdge += pos_f;
box.MinEdge += pos_f;
core::aabbox3d<f32> oldbox = box_0;
oldbox.MaxEdge += oldpos_f;
oldbox.MinEdge += oldpos_f;
/* while(dtime > BS*1e-10)
If the object lies on a walkable node, this is set to true.
*/
result.touching_ground = false;
/*
Go through every node around the object
*/
s16 min_x = (box_0.MinEdge.X / BS) - 2;
s16 min_y = (box_0.MinEdge.Y / BS) - 2;
s16 min_z = (box_0.MinEdge.Z / BS) - 2;
s16 max_x = (box_0.MaxEdge.X / BS) + 1;
s16 max_y = (box_0.MaxEdge.Y / BS) + 1;
s16 max_z = (box_0.MaxEdge.Z / BS) + 1;
for(s16 y = oldpos_i.Y + min_y; y <= oldpos_i.Y + max_y; y++)
for(s16 z = oldpos_i.Z + min_z; z <= oldpos_i.Z + max_z; z++)
for(s16 x = oldpos_i.X + min_x; x <= oldpos_i.X + max_x; x++)
{ {
try{ TimeTaker tt3("collisionMoveSimple dtime loop");
// Object collides into walkable nodes
MapNode n = map->getNode(v3s16(x,y,z)); // Avoid infinite loop
if(gamedef->getNodeDefManager()->get(n).walkable == false) loopcount++;
if(loopcount >= 100)
{
infostream<<"collisionMoveSimple: WARNING: Loop count exceeded, aborting to avoid infiniite loop"<<std::endl;
dtime = 0;
break;
}
aabb3f movingbox = box_0;
movingbox.MinEdge += pos_f;
movingbox.MaxEdge += pos_f;
int nearest_collided = -1;
f32 nearest_dtime = dtime;
u32 nearest_boxindex = -1;
/*
Go through every nodebox, find nearest collision
*/
for(u32 boxindex = 0; boxindex < cboxes.size(); boxindex++)
{
// Ignore if already stepped up this nodebox.
if(is_step_up[boxindex])
continue; continue;
}
catch(InvalidPositionException &e) // Find nearest collision of the two boxes (raytracing-like)
{ f32 dtime_tmp;
// Doing nothing here will block the object from int collided = axisAlignedCollision(
// walking over map borders cboxes[boxindex], movingbox, speed_f, d, dtime_tmp);
if(collided == -1 || dtime_tmp >= nearest_dtime)
continue;
nearest_dtime = dtime_tmp;
nearest_collided = collided;
nearest_boxindex = boxindex;
} }
core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS); if(nearest_collided == -1)
{
// No collision with any collision box.
pos_f += speed_f * dtime;
dtime = 0; // Set to 0 to avoid "infinite" loop due to small FP numbers
}
else
{
// Otherwise, a collision occurred.
const aabb3f& cbox = cboxes[nearest_boxindex];
// Check for stairs.
bool step_up = (nearest_collided != 1) && // must not be Y direction
(movingbox.MinEdge.Y < cbox.MaxEdge.Y) &&
(movingbox.MinEdge.Y + stepheight > cbox.MaxEdge.Y) &&
(!wouldCollideWithCeiling(cboxes, movingbox,
cbox.MaxEdge.Y - movingbox.MinEdge.Y,
d));
// Move to the point of collision and reduce dtime by nearest_dtime
if(nearest_dtime < 0)
{
// Handle negative nearest_dtime (can be caused by the d allowance)
if(!step_up)
{
if(nearest_collided == 0)
pos_f.X += speed_f.X * nearest_dtime;
if(nearest_collided == 1)
pos_f.Y += speed_f.Y * nearest_dtime;
if(nearest_collided == 2)
pos_f.Z += speed_f.Z * nearest_dtime;
}
}
else
{
pos_f += speed_f * nearest_dtime;
dtime -= nearest_dtime;
}
// Set the speed component that caused the collision to zero
if(step_up)
{
// Special case: Handle stairs
is_step_up[nearest_boxindex] = true;
}
else if(nearest_collided == 0) // X
{
speed_f.X = 0;
result.collides = true;
result.collides_xz = true;
}
else if(nearest_collided == 1) // Y
{
speed_f.Y = 0;
result.collides = true;
}
else if(nearest_collided == 2) // Z
{
speed_f.Z = 0;
result.collides = true;
result.collides_xz = true;
}
}
}
/*
Final touches: Check if standing on ground, step up stairs.
*/
aabb3f box = box_0;
box.MinEdge += pos_f;
box.MaxEdge += pos_f;
for(u32 boxindex = 0; boxindex < cboxes.size(); boxindex++)
{
const aabb3f& cbox = cboxes[boxindex];
/* /*
See if the object is touching ground. See if the object is touching ground.
@ -107,104 +398,40 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
Use 0.15*BS so that it is easier to get on a node. Use 0.15*BS so that it is easier to get on a node.
*/ */
if( if(
//fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < d cbox.MaxEdge.X-d > box.MinEdge.X &&
fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS cbox.MinEdge.X+d < box.MaxEdge.X &&
&& nodebox.MaxEdge.X-d > box.MinEdge.X cbox.MaxEdge.Z-d > box.MinEdge.Z &&
&& nodebox.MinEdge.X+d < box.MaxEdge.X cbox.MinEdge.Z+d < box.MaxEdge.Z
&& nodebox.MaxEdge.Z-d > box.MinEdge.Z
&& nodebox.MinEdge.Z+d < box.MaxEdge.Z
){ ){
if(is_step_up[boxindex])
{
pos_f.Y += (cbox.MaxEdge.Y - box.MinEdge.Y);
box = box_0;
box.MinEdge += pos_f;
box.MaxEdge += pos_f;
}
if(fabs(cbox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS)
{
result.touching_ground = true; result.touching_ground = true;
} if(is_unloaded[boxindex])
result.standing_on_unloaded = true;
// If object doesn't intersect with node, ignore node.
if(box.intersectsWithBox(nodebox) == false)
continue;
/*
Go through every axis
*/
v3f dirs[3] = {
v3f(0,0,1), // back-front
v3f(0,1,0), // top-bottom
v3f(1,0,0), // right-left
};
for(u16 i=0; i<3; i++)
{
/*
Calculate values along the axis
*/
f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
f32 objectmax = box.MaxEdge.dotProduct(dirs[i]);
f32 objectmin = box.MinEdge.dotProduct(dirs[i]);
f32 objectmax_old = oldbox.MaxEdge.dotProduct(dirs[i]);
f32 objectmin_old = oldbox.MinEdge.dotProduct(dirs[i]);
/*
Check collision for the axis.
Collision happens when object is going through a surface.
*/
bool negative_axis_collides =
(nodemax > objectmin && nodemax <= objectmin_old + d
&& speed_f.dotProduct(dirs[i]) < 0);
bool positive_axis_collides =
(nodemin < objectmax && nodemin >= objectmax_old - d
&& speed_f.dotProduct(dirs[i]) > 0);
bool main_axis_collides =
negative_axis_collides || positive_axis_collides;
/*
Check overlap of object and node in other axes
*/
bool other_axes_overlap = true;
for(u16 j=0; j<3; j++)
{
if(j == i)
continue;
f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
f32 objectmax = box.MaxEdge.dotProduct(dirs[j]);
f32 objectmin = box.MinEdge.dotProduct(dirs[j]);
if(!(nodemax - d > objectmin && nodemin + d < objectmax))
{
other_axes_overlap = false;
break;
} }
} }
/*
If this is a collision, revert the pos_f in the main
direction.
*/
if(other_axes_overlap && main_axis_collides)
{
speed_f -= speed_f.dotProduct(dirs[i]) * dirs[i];
pos_f -= pos_f.dotProduct(dirs[i]) * dirs[i];
pos_f += oldpos_f.dotProduct(dirs[i]) * dirs[i];
result.collides = true;
} }
}
} // xyz
return result; return result;
} }
collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef, collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
f32 pos_max_d, const core::aabbox3d<f32> &box_0, f32 pos_max_d, const aabb3f &box_0,
f32 dtime, v3f &pos_f, v3f &speed_f) f32 stepheight, f32 dtime,
v3f &pos_f, v3f &speed_f, v3f &accel_f)
{ {
TimeTaker tt("collisionMovePrecise");
infostream<<"start collisionMovePrecise\n";
collisionMoveResult final_result; collisionMoveResult final_result;
// Maximum time increment (for collision detection etc)
// time = distance / speed
f32 dtime_max_increment = pos_max_d / speed_f.getLength();
// Maximum time increment is 10ms or lower
if(dtime_max_increment > 0.01)
dtime_max_increment = 0.01;
// Don't allow overly huge dtime // Don't allow overly huge dtime
if(dtime > 2.0) if(dtime > 2.0)
dtime = 2.0; dtime = 2.0;
@ -216,6 +443,16 @@ collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
{ {
loopcount++; loopcount++;
// Maximum time increment (for collision detection etc)
// time = distance / speed
f32 dtime_max_increment = 1.0;
if(speed_f.getLength() != 0)
dtime_max_increment = pos_max_d / speed_f.getLength();
// Maximum time increment is 10ms or lower
if(dtime_max_increment > 0.01)
dtime_max_increment = 0.01;
f32 dtime_part; f32 dtime_part;
if(dtime_downcount > dtime_max_increment) if(dtime_downcount > dtime_max_increment)
{ {
@ -234,17 +471,21 @@ collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
} }
collisionMoveResult result = collisionMoveSimple(map, gamedef, collisionMoveResult result = collisionMoveSimple(map, gamedef,
pos_max_d, box_0, dtime_part, pos_f, speed_f); pos_max_d, box_0, stepheight, dtime_part,
pos_f, speed_f, accel_f);
if(result.touching_ground) if(result.touching_ground)
final_result.touching_ground = true; final_result.touching_ground = true;
if(result.collides) if(result.collides)
final_result.collides = true; final_result.collides = true;
if(result.collides_xz)
final_result.collides_xz = true;
if(result.standing_on_unloaded)
final_result.standing_on_unloaded = true;
} }
while(dtime_downcount > 0.001); while(dtime_downcount > 0.001);
infostream<<"end collisionMovePrecise\n";
return final_result; return final_result;
} }

View file

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define COLLISION_HEADER #define COLLISION_HEADER
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include <vector>
class Map; class Map;
class IGameDef; class IGameDef;
@ -29,22 +30,44 @@ struct collisionMoveResult
{ {
bool touching_ground; bool touching_ground;
bool collides; bool collides;
bool collides_xz;
bool standing_on_unloaded;
collisionMoveResult(): collisionMoveResult():
touching_ground(false), touching_ground(false),
collides(false) collides(false),
collides_xz(false),
standing_on_unloaded(false)
{} {}
}; };
// Moves using a single iteration; speed should not exceed pos_max_d/dtime // Moves using a single iteration; speed should not exceed pos_max_d/dtime
collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
f32 pos_max_d, const core::aabbox3d<f32> &box_0, f32 pos_max_d, const aabb3f &box_0,
f32 dtime, v3f &pos_f, v3f &speed_f); f32 stepheight, f32 dtime,
v3f &pos_f, v3f &speed_f, v3f &accel_f);
// Moves using as many iterations as needed // Moves using as many iterations as needed
collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef, collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
f32 pos_max_d, const core::aabbox3d<f32> &box_0, f32 pos_max_d, const aabb3f &box_0,
f32 dtime, v3f &pos_f, v3f &speed_f); f32 stepheight, f32 dtime,
v3f &pos_f, v3f &speed_f, v3f &accel_f);
// Helper function:
// Checks for collision of a moving aabbox with a static aabbox
// Returns -1 if no collision, 0 if X collision, 1 if Y collision, 2 if Z collision
// dtime receives time until first collision, invalid if -1 is returned
int axisAlignedCollision(
const aabb3f &staticbox, const aabb3f &movingbox,
const v3f &speed, f32 d, f32 &dtime);
// Helper function:
// Checks if moving the movingbox up by the given distance would hit a ceiling.
bool wouldCollideWithCeiling(
const std::vector<aabb3f> &staticboxes,
const aabb3f &movingbox,
f32 y_increase, f32 d);
enum CollisionType enum CollisionType
{ {

View file

@ -1874,22 +1874,24 @@ public:
box.MinEdge *= BS; box.MinEdge *= BS;
box.MaxEdge *= BS; box.MaxEdge *= BS;
collisionMoveResult moveresult; collisionMoveResult moveresult;
f32 pos_max_d = BS*0.25; // Distance per iteration f32 pos_max_d = BS*0.125; // Distance per iteration
f32 stepheight = 0;
v3f p_pos = m_position; v3f p_pos = m_position;
v3f p_velocity = m_velocity; v3f p_velocity = m_velocity;
v3f p_acceleration = m_acceleration;
IGameDef *gamedef = env->getGameDef(); IGameDef *gamedef = env->getGameDef();
moveresult = collisionMovePrecise(&env->getMap(), gamedef, moveresult = collisionMovePrecise(&env->getMap(), gamedef,
pos_max_d, box, dtime, p_pos, p_velocity); pos_max_d, box, stepheight, dtime,
p_pos, p_velocity, p_acceleration);
// Apply results // Apply results
m_position = p_pos; m_position = p_pos;
m_velocity = p_velocity; m_velocity = p_velocity;
m_acceleration = p_acceleration;
bool is_end_position = moveresult.collides; bool is_end_position = moveresult.collides;
pos_translator.update(m_position, is_end_position, dtime); pos_translator.update(m_position, is_end_position, dtime);
pos_translator.translate(dtime); pos_translator.translate(dtime);
updateNodePos(); updateNodePos();
m_velocity += dtime * m_acceleration;
} else { } else {
m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration; m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
m_velocity += dtime * m_acceleration; m_velocity += dtime * m_acceleration;

View file

@ -985,6 +985,72 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
u16 indices[] = {0,1,2,2,3,0}; u16 indices[] = {0,1,2,2,3,0};
collector.append(material_rail, vertices, 4, indices, 6); collector.append(material_rail, vertices, 4, indices, 6);
break;} break;}
case NDT_NODEBOX:
{
v3s16 facedirs[6] = {
v3s16(0,1,0),
v3s16(0,-1,0),
v3s16(1,0,0),
v3s16(-1,0,0),
v3s16(0,0,1),
v3s16(0,0,-1),
};
video::SMaterial material_nodebox[6];
AtlasPointer pa_nodebox[6];
for(int i = 0; i < 6; i++)
{
material_nodebox[i].setFlag(video::EMF_LIGHTING, false);
material_nodebox[i].setFlag(video::EMF_BILINEAR_FILTER, false);
material_nodebox[i].setFlag(video::EMF_FOG_ENABLE, true);
material_nodebox[i].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
TileSpec tile = getNodeTile(n, p, facedirs[i],
&data->m_temp_mods, tsrc, nodedef);
pa_nodebox[i] = tile.texture;
material_nodebox[i].setTexture(0, pa_nodebox[i].atlas);
}
u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
video::SColor c = MapBlock_LightColor(255, l);
v3f pos = intToFloat(p+blockpos_nodes, BS);
std::vector<aabb3f> boxes = n.getNodeBoxes(nodedef);
for(std::vector<aabb3f>::iterator
i = boxes.begin();
i != boxes.end(); i++)
{
aabb3f box = *i;
box.MinEdge += pos;
box.MaxEdge += pos;
// Compute texture coords
f32 tx1 = (i->MinEdge.X/BS)+0.5;
f32 ty1 = (i->MinEdge.Y/BS)+0.5;
f32 tz1 = (i->MinEdge.Z/BS)+0.5;
f32 tx2 = (i->MaxEdge.X/BS)+0.5;
f32 ty2 = (i->MaxEdge.Y/BS)+0.5;
f32 tz2 = (i->MaxEdge.Z/BS)+0.5;
f32 txc[24] = {
// up
tx1, 1-tz2, tx2, 1-tz1,
// down
tx1, tz1, tx2, tz2,
// right
tz1, 1-ty2, tz2, 1-ty1,
// left
1-tz2, 1-ty2, 1-tz1, 1-ty1,
// back
1-tx2, 1-ty2, 1-tx1, 1-ty1,
// front
tx1, 1-ty2, tx2, 1-ty1,
};
makeCuboid(&collector, box,
material_nodebox, pa_nodebox, 6,
c, txc);
}
break;}
} }
} }
} }

View file

@ -163,9 +163,12 @@ void ItemSAO::step(float dtime, bool send_recommended)
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
v3f pos_f = getBasePosition(); v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f; v3f pos_f_old = pos_f;
v3f accel_f = v3f(0,0,0);
f32 stepheight = 0;
IGameDef *gamedef = m_env->getGameDef(); IGameDef *gamedef = m_env->getGameDef();
moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
pos_max_d, box, dtime, pos_f, m_speed_f); pos_max_d, box, stepheight, dtime,
pos_f, m_speed_f, accel_f);
if(send_recommended == false) if(send_recommended == false)
return; return;
@ -404,9 +407,12 @@ void RatSAO::step(float dtime, bool send_recommended)
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
v3f pos_f = getBasePosition(); v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f; v3f pos_f_old = pos_f;
v3f accel_f = v3f(0,0,0);
f32 stepheight = 0;
IGameDef *gamedef = m_env->getGameDef(); IGameDef *gamedef = m_env->getGameDef();
moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
pos_max_d, box, dtime, pos_f, m_speed_f); pos_max_d, box, stepheight, dtime,
pos_f, m_speed_f, accel_f);
m_touching_ground = moveresult.touching_ground; m_touching_ground = moveresult.touching_ground;
setBasePosition(pos_f); setBasePosition(pos_f);
@ -652,9 +658,12 @@ void Oerkki1SAO::step(float dtime, bool send_recommended)
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);*/ m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);*/
v3f pos_f = getBasePosition(); v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f; v3f pos_f_old = pos_f;
v3f accel_f = v3f(0,0,0);
f32 stepheight = 0;
IGameDef *gamedef = m_env->getGameDef(); IGameDef *gamedef = m_env->getGameDef();
moveresult = collisionMovePrecise(&m_env->getMap(), gamedef, moveresult = collisionMovePrecise(&m_env->getMap(), gamedef,
pos_max_d, box, dtime, pos_f, m_speed_f); pos_max_d, box, stepheight, dtime,
pos_f, m_speed_f, accel_f);
m_touching_ground = moveresult.touching_ground; m_touching_ground = moveresult.touching_ground;
// Do collision damage // Do collision damage
@ -899,9 +908,12 @@ void FireflySAO::step(float dtime, bool send_recommended)
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
v3f pos_f = getBasePosition(); v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f; v3f pos_f_old = pos_f;
v3f accel_f = v3f(0,0,0);
f32 stepheight = 0;
IGameDef *gamedef = m_env->getGameDef(); IGameDef *gamedef = m_env->getGameDef();
moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
pos_max_d, box, dtime, pos_f, m_speed_f); pos_max_d, box, stepheight, dtime,
pos_f, m_speed_f, accel_f);
m_touching_ground = moveresult.touching_ground; m_touching_ground = moveresult.touching_ground;
setBasePosition(pos_f); setBasePosition(pos_f);
@ -1618,16 +1630,18 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
box.MaxEdge *= BS; box.MaxEdge *= BS;
collisionMoveResult moveresult; collisionMoveResult moveresult;
f32 pos_max_d = BS*0.25; // Distance per iteration f32 pos_max_d = BS*0.25; // Distance per iteration
v3f p_pos = getBasePosition(); f32 stepheight = 0; // Maximum climbable step height
v3f p_pos = m_base_position;
v3f p_velocity = m_velocity; v3f p_velocity = m_velocity;
v3f p_acceleration = m_acceleration;
IGameDef *gamedef = m_env->getGameDef(); IGameDef *gamedef = m_env->getGameDef();
moveresult = collisionMovePrecise(&m_env->getMap(), gamedef, moveresult = collisionMovePrecise(&m_env->getMap(), gamedef,
pos_max_d, box, dtime, p_pos, p_velocity); pos_max_d, box, stepheight, dtime,
p_pos, p_velocity, p_acceleration);
// Apply results // Apply results
setBasePosition(p_pos); m_base_position = p_pos;
m_velocity = p_velocity; m_velocity = p_velocity;
m_acceleration = p_acceleration;
m_velocity += dtime * m_acceleration;
} else { } else {
m_base_position += dtime * m_velocity + 0.5 * dtime m_base_position += dtime * m_velocity + 0.5 * dtime
* dtime * m_acceleration; * dtime * m_acceleration;

View file

@ -293,45 +293,41 @@ PointedThing getPointedThing(Client *client, v3f player_position,
core::line3d<f32> shootline, f32 d, core::line3d<f32> shootline, f32 d,
bool liquids_pointable, bool liquids_pointable,
bool look_for_object, bool look_for_object,
core::aabbox3d<f32> &hilightbox, std::vector<aabb3f> &hilightboxes,
bool &should_show_hilightbox,
ClientActiveObject *&selected_object) ClientActiveObject *&selected_object)
{ {
PointedThing result; PointedThing result;
hilightbox = core::aabbox3d<f32>(0,0,0,0,0,0); hilightboxes.clear();
should_show_hilightbox = false;
selected_object = NULL; selected_object = NULL;
INodeDefManager *nodedef = client->getNodeDefManager();
// First try to find a pointed at active object // First try to find a pointed at active object
if(look_for_object) if(look_for_object)
{ {
selected_object = client->getSelectedActiveObject(d*BS, selected_object = client->getSelectedActiveObject(d*BS,
camera_position, shootline); camera_position, shootline);
}
if(selected_object != NULL) if(selected_object != NULL)
{ {
core::aabbox3d<f32> *selection_box if(selected_object->doShowSelectionBox())
= selected_object->getSelectionBox(); {
// Box should exist because object was returned in the aabb3f *selection_box = selected_object->getSelectionBox();
// first place // Box should exist because object was
// returned in the first place
assert(selection_box); assert(selection_box);
v3f pos = selected_object->getPosition(); v3f pos = selected_object->getPosition();
hilightboxes.push_back(aabb3f(
hilightbox = core::aabbox3d<f32>(
selection_box->MinEdge + pos, selection_box->MinEdge + pos,
selection_box->MaxEdge + pos selection_box->MaxEdge + pos));
); }
should_show_hilightbox = selected_object->doShowSelectionBox();
result.type = POINTEDTHING_OBJECT; result.type = POINTEDTHING_OBJECT;
result.object_id = selected_object->getId(); result.object_id = selected_object->getId();
return result; return result;
} }
}
// That didn't work, try to find a pointed at node // That didn't work, try to find a pointed at node
@ -366,28 +362,6 @@ PointedThing getPointedThing(Client *client, v3f player_position,
if(!isPointableNode(n, client, liquids_pointable)) if(!isPointableNode(n, client, liquids_pointable))
continue; continue;
v3s16 np(x,y,z);
v3f npf = intToFloat(np, BS);
f32 d = 0.01;
v3s16 dirs[6] = {
v3s16(0,0,1), // back
v3s16(0,1,0), // top
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,-1,0), // bottom
v3s16(-1,0,0), // left
};
const ContentFeatures &f = nodedef->get(n);
if(f.selection_box.type == NODEBOX_FIXED)
{
core::aabbox3d<f32> box = f.selection_box.fixed;
box.MinEdge += npf;
box.MaxEdge += npf;
v3s16 facedirs[6] = { v3s16 facedirs[6] = {
v3s16(-1,0,0), v3s16(-1,0,0),
v3s16(1,0,0), v3s16(1,0,0),
@ -397,165 +371,79 @@ PointedThing getPointedThing(Client *client, v3f player_position,
v3s16(0,0,1), v3s16(0,0,1),
}; };
core::aabbox3d<f32> faceboxes[6] = { std::vector<aabb3f> boxes = n.getSelectionBoxes(client->ndef());
v3s16 np(x,y,z);
v3f npf = intToFloat(np, BS);
for(std::vector<aabb3f>::const_iterator
i = boxes.begin();
i != boxes.end(); i++)
{
aabb3f box = *i;
box.MinEdge += npf;
box.MaxEdge += npf;
f32 d = 0.001*BS;
aabb3f faceboxes[6] = {
// X- // X-
core::aabbox3d<f32>( aabb3f(
box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z, box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
box.MinEdge.X+d, box.MaxEdge.Y, box.MaxEdge.Z box.MinEdge.X+d, box.MaxEdge.Y, box.MaxEdge.Z
), ),
// X+ // X+
core::aabbox3d<f32>( aabb3f(
box.MaxEdge.X-d, box.MinEdge.Y, box.MinEdge.Z, box.MaxEdge.X-d, box.MinEdge.Y, box.MinEdge.Z,
box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
), ),
// Y- // Y-
core::aabbox3d<f32>( aabb3f(
box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z, box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
box.MaxEdge.X, box.MinEdge.Y+d, box.MaxEdge.Z box.MaxEdge.X, box.MinEdge.Y+d, box.MaxEdge.Z
), ),
// Y+ // Y+
core::aabbox3d<f32>( aabb3f(
box.MinEdge.X, box.MaxEdge.Y-d, box.MinEdge.Z, box.MinEdge.X, box.MaxEdge.Y-d, box.MinEdge.Z,
box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
), ),
// Z- // Z-
core::aabbox3d<f32>( aabb3f(
box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z, box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
box.MaxEdge.X, box.MaxEdge.Y, box.MinEdge.Z+d box.MaxEdge.X, box.MaxEdge.Y, box.MinEdge.Z+d
), ),
// Z+ // Z+
core::aabbox3d<f32>( aabb3f(
box.MinEdge.X, box.MinEdge.Y, box.MaxEdge.Z-d, box.MinEdge.X, box.MinEdge.Y, box.MaxEdge.Z-d,
box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
), ),
}; };
for(u16 i=0; i<6; i++) for(u16 j=0; j<6; j++)
{ {
v3f facedir_f(facedirs[i].X, facedirs[i].Y, facedirs[i].Z); v3f centerpoint = faceboxes[j].getCenter();
v3f centerpoint = npf + facedir_f * BS/2;
f32 distance = (centerpoint - camera_position).getLength(); f32 distance = (centerpoint - camera_position).getLength();
if(distance >= mindistance) if(distance >= mindistance)
continue; continue;
if(!faceboxes[i].intersectsWithLine(shootline)) if(!faceboxes[j].intersectsWithLine(shootline))
continue; continue;
result.type = POINTEDTHING_NODE; result.type = POINTEDTHING_NODE;
result.node_undersurface = np; result.node_undersurface = np;
result.node_abovesurface = np+facedirs[i]; result.node_abovesurface = np+facedirs[j];
mindistance = distance;
hilightbox = box;
should_show_hilightbox = true;
}
}
else if(f.selection_box.type == NODEBOX_WALLMOUNTED)
{
v3s16 dir = n.getWallMountedDir(nodedef);
v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
dir_f *= BS/2 - BS/6 - BS/20;
v3f cpf = npf + dir_f;
f32 distance = (cpf - camera_position).getLength();
core::aabbox3d<f32> box;
// top
if(dir == v3s16(0,1,0)){
box = f.selection_box.wall_top;
}
// bottom
else if(dir == v3s16(0,-1,0)){
box = f.selection_box.wall_bottom;
}
// side
else{
v3f vertices[2] =
{
f.selection_box.wall_side.MinEdge,
f.selection_box.wall_side.MaxEdge
};
for(s32 i=0; i<2; i++)
{
if(dir == v3s16(-1,0,0))
vertices[i].rotateXZBy(0);
if(dir == v3s16(1,0,0))
vertices[i].rotateXZBy(180);
if(dir == v3s16(0,0,-1))
vertices[i].rotateXZBy(90);
if(dir == v3s16(0,0,1))
vertices[i].rotateXZBy(-90);
}
box = core::aabbox3d<f32>(vertices[0]);
box.addInternalPoint(vertices[1]);
}
box.MinEdge += npf;
box.MaxEdge += npf;
if(distance < mindistance)
{
if(box.intersectsWithLine(shootline))
{
result.type = POINTEDTHING_NODE;
result.node_undersurface = np;
result.node_abovesurface = np;
mindistance = distance;
hilightbox = box;
should_show_hilightbox = true;
}
}
}
else // NODEBOX_REGULAR
{
for(u16 i=0; i<6; i++)
{
v3f dir_f = v3f(dirs[i].X,
dirs[i].Y, dirs[i].Z);
v3f centerpoint = npf + dir_f * BS/2;
f32 distance =
(centerpoint - camera_position).getLength();
if(distance < mindistance)
{
core::CMatrix4<f32> m;
m.buildRotateFromTo(v3f(0,0,1), dir_f);
// This is the back face
v3f corners[2] = {
v3f(BS/2, BS/2, BS/2),
v3f(-BS/2, -BS/2, BS/2+d)
};
for(u16 j=0; j<2; j++)
{
m.rotateVect(corners[j]);
corners[j] += npf;
}
core::aabbox3d<f32> facebox(corners[0]);
facebox.addInternalPoint(corners[1]);
if(facebox.intersectsWithLine(shootline))
{
result.type = POINTEDTHING_NODE;
result.node_undersurface = np;
result.node_abovesurface = np + dirs[i];
mindistance = distance; mindistance = distance;
//hilightbox = facebox; hilightboxes.clear();
for(std::vector<aabb3f>::const_iterator
const float d = 0.502; i2 = boxes.begin();
core::aabbox3d<f32> nodebox i2 != boxes.end(); i2++)
(-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d); {
v3f nodepos_f = intToFloat(np, BS); aabb3f box = *i2;
nodebox.MinEdge += nodepos_f; box.MinEdge += npf + v3f(-d,-d,-d);
nodebox.MaxEdge += nodepos_f; box.MaxEdge += npf + v3f(d,d,d);
hilightbox = nodebox; hilightboxes.push_back(box);
should_show_hilightbox = true; }
}
} }
} // if distance < mindistance
} // for dirs
} // regular block
} // for coords } // for coords
return result; return result;
@ -1112,9 +1000,6 @@ void the_game(
else else
hotbar_imagesize = 64; hotbar_imagesize = 64;
// Hilight boxes collected during the loop and displayed
core::list< core::aabbox3d<f32> > hilightboxes;
// Info text // Info text
std::wstring infotext; std::wstring infotext;
@ -1821,8 +1706,8 @@ void the_game(
core::line3d<f32> shootline(camera_position, core::line3d<f32> shootline(camera_position,
camera_position + camera_direction * BS * (d+1)); camera_position + camera_direction * BS * (d+1));
core::aabbox3d<f32> hilightbox; // Hilight boxes collected during the loop and displayed
bool should_show_hilightbox = false; std::vector<aabb3f> hilightboxes;
ClientActiveObject *selected_object = NULL; ClientActiveObject *selected_object = NULL;
PointedThing pointed = getPointedThing( PointedThing pointed = getPointedThing(
@ -1831,7 +1716,7 @@ void the_game(
camera_position, shootline, d, camera_position, shootline, d,
playeritem_liquids_pointable, !ldown_for_dig, playeritem_liquids_pointable, !ldown_for_dig,
// output // output
hilightbox, should_show_hilightbox, hilightboxes,
selected_object); selected_object);
if(pointed != pointed_old) if(pointed != pointed_old)
@ -1840,12 +1725,6 @@ void the_game(
//dstream<<"Pointing at "<<pointed.dump()<<std::endl; //dstream<<"Pointing at "<<pointed.dump()<<std::endl;
} }
/*
Visualize selection
*/
if(should_show_hilightbox)
hilightboxes.push_back(hilightbox);
/* /*
Stop digging when Stop digging when
- releasing left mouse button - releasing left mouse button
@ -2470,7 +2349,8 @@ void the_game(
if(show_hud) if(show_hud)
{ {
for(core::list<aabb3f>::Iterator i=hilightboxes.begin(); for(std::vector<aabb3f>::const_iterator
i = hilightboxes.begin();
i != hilightboxes.end(); i++) i != hilightboxes.end(); i++)
{ {
/*infostream<<"hilightbox min=" /*infostream<<"hilightbox min="

View file

@ -132,7 +132,98 @@ v3s16 MapNode::getWallMountedDir(INodeDefManager *nodemgr) const
} }
} }
static std::vector<aabb3f> transformNodeBox(const MapNode &n,
const NodeBox &nodebox, INodeDefManager *nodemgr)
{
std::vector<aabb3f> boxes;
if(nodebox.type == NODEBOX_FIXED)
{
const std::vector<aabb3f> &fixed = nodebox.fixed;
int facedir = n.getFaceDir(nodemgr);
for(std::vector<aabb3f>::const_iterator
i = fixed.begin();
i != fixed.end(); i++)
{
aabb3f box = *i;
if(facedir == 1)
{
box.MinEdge.rotateXZBy(-90);
box.MaxEdge.rotateXZBy(-90);
box.repair();
}
else if(facedir == 2)
{
box.MinEdge.rotateXZBy(180);
box.MaxEdge.rotateXZBy(180);
box.repair();
}
else if(facedir == 3)
{
box.MinEdge.rotateXZBy(90);
box.MaxEdge.rotateXZBy(90);
box.repair();
}
boxes.push_back(box);
}
}
else if(nodebox.type == NODEBOX_WALLMOUNTED)
{
v3s16 dir = n.getWallMountedDir(nodemgr);
// top
if(dir == v3s16(0,1,0))
{
boxes.push_back(nodebox.wall_top);
}
// bottom
else if(dir == v3s16(0,-1,0))
{
boxes.push_back(nodebox.wall_bottom);
}
// side
else
{
v3f vertices[2] =
{
nodebox.wall_side.MinEdge,
nodebox.wall_side.MaxEdge
};
for(s32 i=0; i<2; i++)
{
if(dir == v3s16(-1,0,0))
vertices[i].rotateXZBy(0);
if(dir == v3s16(1,0,0))
vertices[i].rotateXZBy(180);
if(dir == v3s16(0,0,-1))
vertices[i].rotateXZBy(90);
if(dir == v3s16(0,0,1))
vertices[i].rotateXZBy(-90);
}
aabb3f box = aabb3f(vertices[0]);
box.addInternalPoint(vertices[1]);
boxes.push_back(box);
}
}
else // NODEBOX_REGULAR
{
boxes.push_back(aabb3f(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2));
}
return boxes;
}
std::vector<aabb3f> MapNode::getNodeBoxes(INodeDefManager *nodemgr) const
{
const ContentFeatures &f = nodemgr->get(*this);
return transformNodeBox(*this, f.node_box, nodemgr);
}
std::vector<aabb3f> MapNode::getSelectionBoxes(INodeDefManager *nodemgr) const
{
const ContentFeatures &f = nodemgr->get(*this);
return transformNodeBox(*this, f.selection_box, nodemgr);
}
u32 MapNode::serializedLength(u8 version) u32 MapNode::serializedLength(u8 version)
{ {

View file

@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes.h" #include "irrlichttypes.h"
#include "light.h" #include "light.h"
#include <vector>
class INodeDefManager; class INodeDefManager;
@ -195,6 +196,17 @@ struct MapNode
u8 getWallMounted(INodeDefManager *nodemgr) const; u8 getWallMounted(INodeDefManager *nodemgr) const;
v3s16 getWallMountedDir(INodeDefManager *nodemgr) const; v3s16 getWallMountedDir(INodeDefManager *nodemgr) const;
/*
Gets list of node boxes (used for rendering (NDT_NODEBOX)
and collision)
*/
std::vector<aabb3f> getNodeBoxes(INodeDefManager *nodemgr) const;
/*
Gets list of selection boxes
*/
std::vector<aabb3f> getSelectionBoxes(INodeDefManager *nodemgr) const;
/* /*
Serialization functions Serialization functions
*/ */

View file

@ -32,12 +32,35 @@ with this program; if not, write to the Free Software Foundation, Inc.,
NodeBox NodeBox
*/ */
void NodeBox::reset()
{
type = NODEBOX_REGULAR;
// default is empty
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);
wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
}
void NodeBox::serialize(std::ostream &os) const void NodeBox::serialize(std::ostream &os) const
{ {
writeU8(os, 0); // version writeU8(os, 1); // version
writeU8(os, type); writeU8(os, type);
writeV3F1000(os, fixed.MinEdge);
writeV3F1000(os, fixed.MaxEdge); if(type == NODEBOX_FIXED)
{
writeU16(os, fixed.size());
for(std::vector<aabb3f>::const_iterator
i = fixed.begin();
i != fixed.end(); i++)
{
writeV3F1000(os, i->MinEdge);
writeV3F1000(os, i->MaxEdge);
}
}
else if(type == NODEBOX_WALLMOUNTED)
{
writeV3F1000(os, wall_top.MinEdge); writeV3F1000(os, wall_top.MinEdge);
writeV3F1000(os, wall_top.MaxEdge); writeV3F1000(os, wall_top.MaxEdge);
writeV3F1000(os, wall_bottom.MinEdge); writeV3F1000(os, wall_bottom.MinEdge);
@ -45,15 +68,31 @@ void NodeBox::serialize(std::ostream &os) const
writeV3F1000(os, wall_side.MinEdge); writeV3F1000(os, wall_side.MinEdge);
writeV3F1000(os, wall_side.MaxEdge); writeV3F1000(os, wall_side.MaxEdge);
} }
}
void NodeBox::deSerialize(std::istream &is) void NodeBox::deSerialize(std::istream &is)
{ {
int version = readU8(is); int version = readU8(is);
if(version != 0) if(version != 1)
throw SerializationError("unsupported NodeBox version"); throw SerializationError("unsupported NodeBox version");
reset();
type = (enum NodeBoxType)readU8(is); type = (enum NodeBoxType)readU8(is);
fixed.MinEdge = readV3F1000(is);
fixed.MaxEdge = readV3F1000(is); if(type == NODEBOX_FIXED)
{
u16 fixed_count = readU16(is);
while(fixed_count--)
{
aabb3f box;
box.MinEdge = readV3F1000(is);
box.MaxEdge = readV3F1000(is);
fixed.push_back(box);
}
}
else if(type == NODEBOX_WALLMOUNTED)
{
wall_top.MinEdge = readV3F1000(is); wall_top.MinEdge = readV3F1000(is);
wall_top.MaxEdge = readV3F1000(is); wall_top.MaxEdge = readV3F1000(is);
wall_bottom.MinEdge = readV3F1000(is); wall_bottom.MinEdge = readV3F1000(is);
@ -61,6 +100,7 @@ void NodeBox::deSerialize(std::istream &is)
wall_side.MinEdge = readV3F1000(is); wall_side.MinEdge = readV3F1000(is);
wall_side.MaxEdge = readV3F1000(is); wall_side.MaxEdge = readV3F1000(is);
} }
}
/* /*
MaterialSpec MaterialSpec
@ -143,6 +183,7 @@ void ContentFeatures::reset()
liquid_viscosity = 0; liquid_viscosity = 0;
light_source = 0; light_source = 0;
damage_per_second = 0; damage_per_second = 0;
node_box = NodeBox();
selection_box = NodeBox(); selection_box = NodeBox();
material = MaterialProperties(); material = MaterialProperties();
// Make unknown blocks diggable // Make unknown blocks diggable
@ -154,7 +195,7 @@ void ContentFeatures::reset()
void ContentFeatures::serialize(std::ostream &os) void ContentFeatures::serialize(std::ostream &os)
{ {
writeU8(os, 1); // version writeU8(os, 2); // version
os<<serializeString(name); os<<serializeString(name);
writeU8(os, drawtype); writeU8(os, drawtype);
writeF1000(os, visual_scale); writeF1000(os, visual_scale);
@ -187,6 +228,7 @@ void ContentFeatures::serialize(std::ostream &os)
writeU8(os, liquid_viscosity); writeU8(os, liquid_viscosity);
writeU8(os, light_source); writeU8(os, light_source);
writeU32(os, damage_per_second); writeU32(os, damage_per_second);
node_box.serialize(os);
selection_box.serialize(os); selection_box.serialize(os);
material.serialize(os); material.serialize(os);
writeU8(os, legacy_facedir_simple); writeU8(os, legacy_facedir_simple);
@ -196,7 +238,7 @@ void ContentFeatures::serialize(std::ostream &os)
void ContentFeatures::deSerialize(std::istream &is) void ContentFeatures::deSerialize(std::istream &is)
{ {
int version = readU8(is); int version = readU8(is);
if(version != 1) if(version != 2)
throw SerializationError("unsupported ContentFeatures version"); throw SerializationError("unsupported ContentFeatures version");
name = deSerializeString(is); name = deSerializeString(is);
drawtype = (enum NodeDrawType)readU8(is); drawtype = (enum NodeDrawType)readU8(is);
@ -232,6 +274,7 @@ void ContentFeatures::deSerialize(std::istream &is)
liquid_viscosity = readU8(is); liquid_viscosity = readU8(is);
light_source = readU8(is); light_source = readU8(is);
damage_per_second = readU32(is); damage_per_second = readU32(is);
node_box.deSerialize(is);
selection_box.deSerialize(is); selection_box.deSerialize(is);
material.deSerialize(is); material.deSerialize(is);
legacy_facedir_simple = readU8(is); legacy_facedir_simple = readU8(is);
@ -509,6 +552,7 @@ public:
case NDT_PLANTLIKE: case NDT_PLANTLIKE:
case NDT_FENCELIKE: case NDT_FENCELIKE:
case NDT_RAILLIKE: case NDT_RAILLIKE:
case NDT_NODEBOX:
f->solidness = 0; f->solidness = 0;
break; break;
} }

View file

@ -62,7 +62,7 @@ enum LiquidType
enum NodeBoxType enum NodeBoxType
{ {
NODEBOX_REGULAR, // Regular block; allows buildable_to NODEBOX_REGULAR, // Regular block; allows buildable_to
NODEBOX_FIXED, // Static separately defined box NODEBOX_FIXED, // Static separately defined box(es)
NODEBOX_WALLMOUNTED, // Box for wall mounted nodes; (top, bottom, side) NODEBOX_WALLMOUNTED, // Box for wall mounted nodes; (top, bottom, side)
}; };
@ -71,22 +71,16 @@ struct NodeBox
enum NodeBoxType type; enum NodeBoxType type;
// NODEBOX_REGULAR (no parameters) // NODEBOX_REGULAR (no parameters)
// NODEBOX_FIXED // NODEBOX_FIXED
core::aabbox3d<f32> fixed; std::vector<aabb3f> fixed;
// NODEBOX_WALLMOUNTED // NODEBOX_WALLMOUNTED
core::aabbox3d<f32> wall_top; aabb3f wall_top;
core::aabbox3d<f32> wall_bottom; aabb3f wall_bottom;
core::aabbox3d<f32> wall_side; // being at the -X side aabb3f wall_side; // being at the -X side
NodeBox(): NodeBox()
type(NODEBOX_REGULAR), { reset(); }
// default is rail-like
fixed(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2),
// default is sign/ladder-like
wall_top(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2),
wall_bottom(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2),
wall_side(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2)
{}
void reset();
void serialize(std::ostream &os) const; void serialize(std::ostream &os) const;
void deSerialize(std::istream &is); void deSerialize(std::istream &is);
}; };
@ -122,6 +116,7 @@ enum NodeDrawType
NDT_PLANTLIKE, NDT_PLANTLIKE,
NDT_FENCELIKE, NDT_FENCELIKE,
NDT_RAILLIKE, NDT_RAILLIKE,
NDT_NODEBOX,
}; };
#define CF_SPECIAL_COUNT 2 #define CF_SPECIAL_COUNT 2
@ -193,6 +188,7 @@ struct ContentFeatures
// Amount of light the node emits // Amount of light the node emits
u8 light_source; u8 light_source;
u32 damage_per_second; u32 damage_per_second;
NodeBox node_box;
NodeBox selection_box; NodeBox selection_box;
MaterialProperties material; MaterialProperties material;
// Compatibility with old maps // Compatibility with old maps

View file

@ -205,23 +205,14 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
INodeDefManager *nodemgr = m_gamedef->ndef(); INodeDefManager *nodemgr = m_gamedef->ndef();
v3f position = getPosition(); v3f position = getPosition();
v3f oldpos = position;
v3s16 oldpos_i = floatToInt(oldpos, BS);
v3f old_speed = m_speed; v3f old_speed = m_speed;
/*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
<<oldpos_i.Z<<")"<<std::endl;*/
/*
Calculate new position
*/
position += m_speed * dtime;
// Skip collision detection if a special movement mode is used // Skip collision detection if a special movement mode is used
bool free_move = g_settings->getBool("free_move"); bool free_move = g_settings->getBool("free_move");
if(free_move) if(free_move)
{ {
position += m_speed * dtime;
setPosition(position); setPosition(position);
return; return;
} }
@ -230,9 +221,6 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
Collision detection Collision detection
*/ */
// Player position in nodes
v3s16 pos_i = floatToInt(position, BS);
/* /*
Check if player is in water (the oscillating value) Check if player is in water (the oscillating value)
*/ */
@ -282,30 +270,12 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
is_climbing = false; is_climbing = false;
} }
/*
Collision uncertainty radius
Make it a bit larger than the maximum distance of movement
*/
//f32 d = pos_max_d * 1.1;
// A fairly large value in here makes moving smoother
f32 d = 0.15*BS;
// This should always apply, otherwise there are glitches
assert(d > pos_max_d);
float player_radius = BS*0.35; float player_radius = BS*0.35;
float player_height = BS*1.7; float player_height = BS*1.7;
// Maximum distance over border for sneaking // Maximum distance over border for sneaking
f32 sneak_max = BS*0.4; f32 sneak_max = BS*0.4;
/*
If sneaking, player has larger collision radius to keep from
falling
*/
/*if(control.sneak)
player_radius = sneak_max + d*1.1;*/
/* /*
If sneaking, keep in range from the last walked node and don't If sneaking, keep in range from the last walked node and don't
fall off from it fall off from it
@ -321,23 +291,8 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
if(position.Y < min_y) if(position.Y < min_y)
{ {
position.Y = min_y; position.Y = min_y;
//v3f old_speed = m_speed;
if(m_speed.Y < 0) if(m_speed.Y < 0)
m_speed.Y = 0; m_speed.Y = 0;
/*if(collision_info)
{
// Report fall collision
if(old_speed.Y < m_speed.Y - 0.1)
{
CollisionInfo info;
info.t = COLLISION_FALL;
info.speed = m_speed.Y - old_speed.Y;
collision_info->push_back(info);
}
}*/
} }
} }
@ -345,176 +300,31 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
Calculate player collision box (new and old) Calculate player collision box (new and old)
*/ */
core::aabbox3d<f32> playerbox( core::aabbox3d<f32> playerbox(
position.X - player_radius, -player_radius,
position.Y - 0.0, 0.0,
position.Z - player_radius, -player_radius,
position.X + player_radius, player_radius,
position.Y + player_height, player_height,
position.Z + player_radius player_radius
);
core::aabbox3d<f32> playerbox_old(
oldpos.X - player_radius,
oldpos.Y - 0.0,
oldpos.Z - player_radius,
oldpos.X + player_radius,
oldpos.Y + player_height,
oldpos.Z + player_radius
); );
float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
v3f accel_f = v3f(0,0,0);
collisionMoveResult result = collisionMoveSimple(&map, m_gamedef,
pos_max_d, playerbox, player_stepheight, dtime,
position, m_speed, accel_f);
/* /*
If the player's feet touch the topside of any node, this is If the player's feet touch the topside of any node, this is
set to true. set to true.
Player is allowed to jump when this is true. Player is allowed to jump when this is true.
*/ */
touching_ground = false; touching_ground = result.touching_ground;
/*std::cout<<"Checking collisions for (" bool standing_on_unloaded = result.standing_on_unloaded;
<<oldpos_i.X<<","<<oldpos_i.Y<<","<<oldpos_i.Z
<<") -> ("
<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z
<<"):"<<std::endl;*/
bool standing_on_unloaded = false;
/*
Go through every node around the player
*/
for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
{
bool is_unloaded = false;
try{
// Player collides into walkable nodes
if(nodemgr->get(map.getNode(v3s16(x,y,z))).walkable == false)
continue;
}
catch(InvalidPositionException &e)
{
is_unloaded = true;
// Doing nothing here will block the player from
// walking over map borders
}
core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS);
/*
See if the player is touching ground.
Player touches ground if player's minimum Y is near node's
maximum Y and player's X-Z-area overlaps with the node's
X-Z-area.
Use 0.15*BS so that it is easier to get on a node.
*/
if(
//fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < d
fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < 0.15*BS
&& nodebox.MaxEdge.X-d > playerbox.MinEdge.X
&& nodebox.MinEdge.X+d < playerbox.MaxEdge.X
&& nodebox.MaxEdge.Z-d > playerbox.MinEdge.Z
&& nodebox.MinEdge.Z+d < playerbox.MaxEdge.Z
){
touching_ground = true;
if(is_unloaded)
standing_on_unloaded = true;
}
// If player doesn't intersect with node, ignore node.
if(playerbox.intersectsWithBox(nodebox) == false)
continue;
/*
Go through every axis
*/
v3f dirs[3] = {
v3f(0,0,1), // back-front
v3f(0,1,0), // top-bottom
v3f(1,0,0), // right-left
};
for(u16 i=0; i<3; i++)
{
/*
Calculate values along the axis
*/
f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
f32 playermax = playerbox.MaxEdge.dotProduct(dirs[i]);
f32 playermin = playerbox.MinEdge.dotProduct(dirs[i]);
f32 playermax_old = playerbox_old.MaxEdge.dotProduct(dirs[i]);
f32 playermin_old = playerbox_old.MinEdge.dotProduct(dirs[i]);
/*
Check collision for the axis.
Collision happens when player is going through a surface.
*/
/*f32 neg_d = d;
f32 pos_d = d;
// Make it easier to get on top of a node
if(i == 1)
neg_d = 0.15*BS;
bool negative_axis_collides =
(nodemax > playermin && nodemax <= playermin_old + neg_d
&& m_speed.dotProduct(dirs[i]) < 0);
bool positive_axis_collides =
(nodemin < playermax && nodemin >= playermax_old - pos_d
&& m_speed.dotProduct(dirs[i]) > 0);*/
bool negative_axis_collides =
(nodemax > playermin && nodemax <= playermin_old + d
&& m_speed.dotProduct(dirs[i]) < 0);
bool positive_axis_collides =
(nodemin < playermax && nodemin >= playermax_old - d
&& m_speed.dotProduct(dirs[i]) > 0);
bool main_axis_collides =
negative_axis_collides || positive_axis_collides;
/*
Check overlap of player and node in other axes
*/
bool other_axes_overlap = true;
for(u16 j=0; j<3; j++)
{
if(j == i)
continue;
f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
f32 playermax = playerbox.MaxEdge.dotProduct(dirs[j]);
f32 playermin = playerbox.MinEdge.dotProduct(dirs[j]);
if(!(nodemax - d > playermin && nodemin + d < playermax))
{
other_axes_overlap = false;
break;
}
}
/*
If this is a collision, revert the position in the main
direction.
*/
if(other_axes_overlap && main_axis_collides)
{
//v3f old_speed = m_speed;
m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
position -= position.dotProduct(dirs[i]) * dirs[i];
position += oldpos.dotProduct(dirs[i]) * dirs[i];
/*if(collision_info)
{
// Report fall collision
if(old_speed.Y < m_speed.Y - 0.1)
{
CollisionInfo info;
info.t = COLLISION_FALL;
info.speed = m_speed.Y - old_speed.Y;
collision_info->push_back(info);
}
}*/
}
}
} // xyz
/* /*
Check the nodes under the player to see from which node the Check the nodes under the player to see from which node the

View file

@ -380,6 +380,7 @@ struct EnumString es_DrawType[] =
{NDT_PLANTLIKE, "plantlike"}, {NDT_PLANTLIKE, "plantlike"},
{NDT_FENCELIKE, "fencelike"}, {NDT_FENCELIKE, "fencelike"},
{NDT_RAILLIKE, "raillike"}, {NDT_RAILLIKE, "raillike"},
{NDT_NODEBOX, "nodebox"},
{0, NULL}, {0, NULL},
}; };
@ -586,32 +587,93 @@ static video::SColor readARGB8(lua_State *L, int index)
return color; return color;
} }
static core::aabbox3d<f32> read_aabbox3df32(lua_State *L, int index, f32 scale) static aabb3f read_aabb3f(lua_State *L, int index, f32 scale)
{ {
core::aabbox3d<f32> box; aabb3f box;
if(lua_istable(L, -1)){ if(lua_istable(L, index)){
lua_rawgeti(L, -1, 1); lua_rawgeti(L, index, 1);
box.MinEdge.X = lua_tonumber(L, -1) * scale; box.MinEdge.X = lua_tonumber(L, -1) * scale;
lua_pop(L, 1); lua_pop(L, 1);
lua_rawgeti(L, -1, 2); lua_rawgeti(L, index, 2);
box.MinEdge.Y = lua_tonumber(L, -1) * scale; box.MinEdge.Y = lua_tonumber(L, -1) * scale;
lua_pop(L, 1); lua_pop(L, 1);
lua_rawgeti(L, -1, 3); lua_rawgeti(L, index, 3);
box.MinEdge.Z = lua_tonumber(L, -1) * scale; box.MinEdge.Z = lua_tonumber(L, -1) * scale;
lua_pop(L, 1); lua_pop(L, 1);
lua_rawgeti(L, -1, 4); lua_rawgeti(L, index, 4);
box.MaxEdge.X = lua_tonumber(L, -1) * scale; box.MaxEdge.X = lua_tonumber(L, -1) * scale;
lua_pop(L, 1); lua_pop(L, 1);
lua_rawgeti(L, -1, 5); lua_rawgeti(L, index, 5);
box.MaxEdge.Y = lua_tonumber(L, -1) * scale; box.MaxEdge.Y = lua_tonumber(L, -1) * scale;
lua_pop(L, 1); lua_pop(L, 1);
lua_rawgeti(L, -1, 6); lua_rawgeti(L, index, 6);
box.MaxEdge.Z = lua_tonumber(L, -1) * scale; box.MaxEdge.Z = lua_tonumber(L, -1) * scale;
lua_pop(L, 1); lua_pop(L, 1);
} }
return box; return box;
} }
static std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
{
std::vector<aabb3f> boxes;
if(lua_istable(L, index)){
int n = lua_objlen(L, index);
// Check if it's a single box or a list of boxes
bool possibly_single_box = (n == 6);
for(int i = 1; i <= n && possibly_single_box; i++){
lua_rawgeti(L, index, i);
if(!lua_isnumber(L, -1))
possibly_single_box = false;
lua_pop(L, 1);
}
if(possibly_single_box){
// Read a single box
boxes.push_back(read_aabb3f(L, index, scale));
} else {
// Read a list of boxes
for(int i = 1; i <= n; i++)
{
lua_rawgeti(L, index, i);
boxes.push_back(read_aabb3f(L, -1, scale));
lua_pop(L, 1);
}
}
}
return boxes;
}
static NodeBox read_nodebox(lua_State *L, int index)
{
NodeBox nodebox;
if(lua_istable(L, -1)){
nodebox.type = (NodeBoxType)getenumfield(L, index, "type",
es_NodeBoxType, NODEBOX_REGULAR);
lua_getfield(L, index, "fixed");
if(lua_istable(L, -1))
nodebox.fixed = read_aabb3f_vector(L, -1, BS);
lua_pop(L, 1);
lua_getfield(L, index, "wall_top");
if(lua_istable(L, -1))
nodebox.wall_top = read_aabb3f(L, -1, BS);
lua_pop(L, 1);
lua_getfield(L, index, "wall_bottom");
if(lua_istable(L, -1))
nodebox.wall_bottom = read_aabb3f(L, -1, BS);
lua_pop(L, 1);
lua_getfield(L, index, "wall_side");
if(lua_istable(L, -1))
nodebox.wall_side = read_aabb3f(L, -1, BS);
lua_pop(L, 1);
}
return nodebox;
}
/* /*
MaterialProperties MaterialProperties
*/ */
@ -931,31 +993,14 @@ static ContentFeatures read_content_features(lua_State *L, int index)
f.damage_per_second = getintfield_default(L, index, f.damage_per_second = getintfield_default(L, index,
"damage_per_second", f.damage_per_second); "damage_per_second", f.damage_per_second);
lua_getfield(L, index, "node_box");
if(lua_istable(L, -1))
f.node_box = read_nodebox(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "selection_box"); lua_getfield(L, index, "selection_box");
if(lua_istable(L, -1)){
f.selection_box.type = (NodeBoxType)getenumfield(L, -1, "type",
es_NodeBoxType, NODEBOX_REGULAR);
lua_getfield(L, -1, "fixed");
if(lua_istable(L, -1)) if(lua_istable(L, -1))
f.selection_box.fixed = read_aabbox3df32(L, -1, BS); f.selection_box = read_nodebox(L, -1);
lua_pop(L, 1);
lua_getfield(L, -1, "wall_top");
if(lua_istable(L, -1))
f.selection_box.wall_top = read_aabbox3df32(L, -1, BS);
lua_pop(L, 1);
lua_getfield(L, -1, "wall_bottom");
if(lua_istable(L, -1))
f.selection_box.wall_bottom = read_aabbox3df32(L, -1, BS);
lua_pop(L, 1);
lua_getfield(L, -1, "wall_side");
if(lua_istable(L, -1))
f.selection_box.wall_side = read_aabbox3df32(L, -1, BS);
lua_pop(L, 1);
}
lua_pop(L, 1); lua_pop(L, 1);
lua_getfield(L, index, "material"); lua_getfield(L, index, "material");
@ -4274,7 +4319,7 @@ void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
lua_getfield(L, -1, "collisionbox"); lua_getfield(L, -1, "collisionbox");
if(lua_istable(L, -1)) if(lua_istable(L, -1))
prop->collisionbox = read_aabbox3df32(L, -1, 1.0); prop->collisionbox = read_aabb3f(L, -1, 1.0);
lua_pop(L, 1); lua_pop(L, 1);
getstringfield(L, -1, "visual", prop->visual); getstringfield(L, -1, "visual", prop->visual);

View file

@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "utility.h" #include "utility.h"
#include "serialization.h" #include "serialization.h"
#include "voxel.h" #include "voxel.h"
#include "collision.h"
#include <sstream> #include <sstream>
#include "porting.h" #include "porting.h"
#include "content_mapnode.h" #include "content_mapnode.h"
@ -816,6 +817,153 @@ struct TestMapSector
}; };
#endif #endif
struct TestCollision
{
void Run()
{
/*
axisAlignedCollision
*/
for(s16 bx = -3; bx <= 3; bx++)
for(s16 by = -3; by <= 3; by++)
for(s16 bz = -3; bz <= 3; bz++)
{
// X-
{
aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
aabb3f m(bx-2, by, bz, bx-1, by+1, bz+1);
v3f v(1, 0, 0);
f32 dtime = 0;
assert(axisAlignedCollision(s, m, v, 0, dtime) == 0);
assert(fabs(dtime - 1.000) < 0.001);
}
{
aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
aabb3f m(bx-2, by, bz, bx-1, by+1, bz+1);
v3f v(-1, 0, 0);
f32 dtime = 0;
assert(axisAlignedCollision(s, m, v, 0, dtime) == -1);
}
{
aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
aabb3f m(bx-2, by+1.5, bz, bx-1, by+2.5, bz-1);
v3f v(1, 0, 0);
f32 dtime;
assert(axisAlignedCollision(s, m, v, 0, dtime) == -1);
}
{
aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
aabb3f m(bx-2, by-1.5, bz, bx-1.5, by+0.5, bz+1);
v3f v(0.5, 0.1, 0);
f32 dtime;
assert(axisAlignedCollision(s, m, v, 0, dtime) == 0);
assert(fabs(dtime - 3.000) < 0.001);
}
{
aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
aabb3f m(bx-2, by-1.5, bz, bx-1.5, by+0.5, bz+1);
v3f v(0.5, 0.1, 0);
f32 dtime;
assert(axisAlignedCollision(s, m, v, 0, dtime) == 0);
assert(fabs(dtime - 3.000) < 0.001);
}
// X+
{
aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
aabb3f m(bx+2, by, bz, bx+3, by+1, bz+1);
v3f v(-1, 0, 0);
f32 dtime;
assert(axisAlignedCollision(s, m, v, 0, dtime) == 0);
assert(fabs(dtime - 1.000) < 0.001);
}
{
aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
aabb3f m(bx+2, by, bz, bx+3, by+1, bz+1);
v3f v(1, 0, 0);
f32 dtime;
assert(axisAlignedCollision(s, m, v, 0, dtime) == -1);
}
{
aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
aabb3f m(bx+2, by, bz+1.5, bx+3, by+1, bz+3.5);
v3f v(-1, 0, 0);
f32 dtime;
assert(axisAlignedCollision(s, m, v, 0, dtime) == -1);
}
{
aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
aabb3f m(bx+2, by-1.5, bz, bx+2.5, by-0.5, bz+1);
v3f v(-0.5, 0.2, 0);
f32 dtime;
assert(axisAlignedCollision(s, m, v, 0, dtime) == 1); // Y, not X!
assert(fabs(dtime - 2.500) < 0.001);
}
{
aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
aabb3f m(bx+2, by-1.5, bz, bx+2.5, by-0.5, bz+1);
v3f v(-0.5, 0.3, 0);
f32 dtime;
assert(axisAlignedCollision(s, m, v, 0, dtime) == 0);
assert(fabs(dtime - 2.000) < 0.001);
}
// TODO: Y-, Y+, Z-, Z+
// misc
{
aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
aabb3f m(bx+2.3, by+2.29, bz+2.29, bx+4.2, by+4.2, bz+4.2);
v3f v(-1./3, -1./3, -1./3);
f32 dtime;
assert(axisAlignedCollision(s, m, v, 0, dtime) == 0);
assert(fabs(dtime - 0.9) < 0.001);
}
{
aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
aabb3f m(bx+2.29, by+2.3, bz+2.29, bx+4.2, by+4.2, bz+4.2);
v3f v(-1./3, -1./3, -1./3);
f32 dtime;
assert(axisAlignedCollision(s, m, v, 0, dtime) == 1);
assert(fabs(dtime - 0.9) < 0.001);
}
{
aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
aabb3f m(bx+2.29, by+2.29, bz+2.3, bx+4.2, by+4.2, bz+4.2);
v3f v(-1./3, -1./3, -1./3);
f32 dtime;
assert(axisAlignedCollision(s, m, v, 0, dtime) == 2);
assert(fabs(dtime - 0.9) < 0.001);
}
{
aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.3, by-2.29, bz-2.29);
v3f v(1./7, 1./7, 1./7);
f32 dtime;
assert(axisAlignedCollision(s, m, v, 0, dtime) == 0);
assert(fabs(dtime - 16.1) < 0.001);
}
{
aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.29, by-2.3, bz-2.29);
v3f v(1./7, 1./7, 1./7);
f32 dtime;
assert(axisAlignedCollision(s, m, v, 0, dtime) == 1);
assert(fabs(dtime - 16.1) < 0.001);
}
{
aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.29, by-2.29, bz-2.3);
v3f v(1./7, 1./7, 1./7);
f32 dtime;
assert(axisAlignedCollision(s, m, v, 0, dtime) == 2);
assert(fabs(dtime - 16.1) < 0.001);
}
}
}
};
struct TestSocket struct TestSocket
{ {
void Run() void Run()
@ -1279,6 +1427,7 @@ void run_tests()
TESTPARAMS(TestVoxelManipulator, ndef); TESTPARAMS(TestVoxelManipulator, ndef);
//TEST(TestMapBlock); //TEST(TestMapBlock);
//TEST(TestMapSector); //TEST(TestMapSector);
TEST(TestCollision);
if(INTERNET_SIMULATOR == false){ if(INTERNET_SIMULATOR == false){
TEST(TestSocket); TEST(TestSocket);
dout_con<<"=== BEGIN RUNNING UNIT TESTS FOR CONNECTION ==="<<std::endl; dout_con<<"=== BEGIN RUNNING UNIT TESTS FOR CONNECTION ==="<<std::endl;

View file

@ -58,6 +58,14 @@ struct AtlasPointer
v2f size; // Size in atlas v2f size; // Size in atlas
u16 tiled; // X-wise tiling count. If 0, width of atlas is width of image. u16 tiled; // X-wise tiling count. If 0, width of atlas is width of image.
AtlasPointer():
id(0),
atlas(NULL),
pos(0,0),
size(1,1),
tiled(1)
{}
AtlasPointer( AtlasPointer(
u16 id_, u16 id_,
video::ITexture *atlas_=NULL, video::ITexture *atlas_=NULL,