walkable = false,
selection_box = {
type = "fixed",
- --fixed = <default>
+ fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
},
groups = {bendy=2,snappy=1,dig_immediate=2},
})
--- /dev/null
+stairs = {}
+
+-- Node will be called stairs:stair_<subname>
+function stairs.register_stair(subname, recipeitem, groups, images, description)
+ minetest.register_node("stairs:stair_" .. subname, {
+ description = description,
+ drawtype = "nodebox",
+ tile_images = images,
+ paramtype = "light",
+ paramtype2 = "facedir",
+ is_ground_content = true,
+ groups = groups,
+ 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},
+ },
+ },
+ })
+
+ 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, groups, images, description)
+ minetest.register_node("stairs:slab_" .. subname, {
+ description = description,
+ drawtype = "nodebox",
+ tile_images = images,
+ paramtype = "light",
+ is_ground_content = true,
+ groups = groups,
+ 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},
+ },
+ })
+
+ 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, groups, images, desc_stair, desc_slab)
+ stairs.register_stair(subname, recipeitem, groups, images, desc_stair)
+ stairs.register_slab(subname, recipeitem, groups, images, desc_slab)
+end
+
+stairs.register_stair_and_slab("wood", "default:wood",
+ {snappy=2,choppy=2,oddly_breakable_by_hand=2},
+ {"default_wood.png"},
+ "Wooden stair",
+ "Wooden slab")
+
+stairs.register_stair_and_slab("stone", "default:stone",
+ {cracky=3},
+ {"default_stone.png"},
+ "Stone stair",
+ "Stone slab")
+
+stairs.register_stair_and_slab("cobble", "default:cobble",
+ {cracky=3},
+ {"default_cobble.png"},
+ "Cobble stair",
+ "Cobble slab")
+
+stairs.register_stair_and_slab("brick", "default:brick",
+ {cracky=3},
+ {"default_brick.png"},
+ "Brick stair",
+ "Brick slab")
+
+stairs.register_stair_and_slab("sandstone", "default:sandstone",
+ {crumbly=2,cracky=2},
+ {"default_sandstone.png"},
+ "Sandstone stair",
+ "Sandstone slab")
void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize,
f32 tool_reload_ratio)
{
+ // Get player position
+ // Smooth the movement when walking up stairs
+ v3f old_player_position = m_playernode->getPosition();
+ v3f player_position = player->getPosition();
+ //if(player->touching_ground && player_position.Y > old_player_position.Y)
+ if(player->touching_ground &&
+ player_position.Y > old_player_position.Y)
+ {
+ f32 oldy = old_player_position.Y;
+ f32 newy = player_position.Y;
+ f32 t = exp(-23*frametime);
+ player_position.Y = oldy * t + newy * (1-t);
+ }
+
// Set player node transformation
- m_playernode->setPosition(player->getPosition());
+ m_playernode->setPosition(player_position);
m_playernode->setRotation(v3f(0, -1 * player->getYaw(), 0));
m_playernode->updateAbsolutePosition();
#include "map.h"
#include "nodedef.h"
#include "gamedef.h"
+#include "log.h"
+#include <vector>
+#include "util/timetaker.h"
+#include "main.h" // g_profiler
+#include "profiler.h"
+
+// 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,
- f32 pos_max_d, const core::aabbox3d<f32> &box_0,
- f32 dtime, v3f &pos_f, v3f &speed_f)
+ f32 pos_max_d, const aabb3f &box_0,
+ f32 stepheight, f32 dtime,
+ v3f &pos_f, v3f &speed_f, v3f &accel_f)
{
+ //TimeTaker tt("collisionMoveSimple");
+ ScopeProfiler sp(g_profiler, "collisionMoveSimple avg", SPT_AVG);
+
collisionMoveResult result;
- // If there is no speed, there are no collisions
+ /*
+ Calculate new velocity
+ */
+ speed_f += accel_f * dtime;
+
+ // If there is no speed, there are no collisions
if(speed_f.getLength() == 0)
return result;
- v3f oldpos_f = pos_f;
- v3s16 oldpos_i = floatToInt(oldpos_f, BS);
-
/*
- 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");
+ ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG);
+
+ 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
*/
-
- // position in nodes
- v3s16 pos_i = floatToInt(pos_f, BS);
-
+
/*
Collision uncertainty radius
Make it a bit larger than the maximum distance of movement
// This should always apply, otherwise there are glitches
assert(d > pos_max_d);
-
- /*
- 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;
- /*
- 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++)
+ int loopcount = 0;
+
+ while(dtime > BS*1e-10)
{
- try{
- // Object collides into walkable nodes
- MapNode n = map->getNode(v3s16(x,y,z));
- if(gamedef->getNodeDefManager()->get(n).walkable == false)
+ //TimeTaker tt3("collisionMoveSimple dtime loop");
+ ScopeProfiler sp(g_profiler, "collisionMoveSimple dtime loop avg", SPT_AVG);
+
+ // Avoid infinite loop
+ 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;
+
+ // Find nearest collision of the two boxes (raytracing-like)
+ f32 dtime_tmp;
+ int collided = axisAlignedCollision(
+ 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;
}
- catch(InvalidPositionException &e)
+
+ if(nearest_collided == -1)
{
- // Doing nothing here will block the object from
- // walking over map borders
+ // 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];
- core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS);
-
/*
See if the object is touching ground.
Use 0.15*BS so that it is easier to get on a node.
*/
if(
- //fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < d
- fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS
- && nodebox.MaxEdge.X-d > box.MinEdge.X
- && nodebox.MinEdge.X+d < box.MaxEdge.X
- && nodebox.MaxEdge.Z-d > box.MinEdge.Z
- && nodebox.MinEdge.Z+d < box.MaxEdge.Z
+ cbox.MaxEdge.X-d > box.MinEdge.X &&
+ cbox.MinEdge.X+d < box.MaxEdge.X &&
+ cbox.MaxEdge.Z-d > box.MinEdge.Z &&
+ cbox.MinEdge.Z+d < box.MaxEdge.Z
){
- result.touching_ground = 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(is_step_up[boxindex])
{
- 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;
- }
+ pos_f.Y += (cbox.MaxEdge.Y - box.MinEdge.Y);
+ box = box_0;
+ box.MinEdge += pos_f;
+ box.MaxEdge += pos_f;
}
-
- /*
- If this is a collision, revert the pos_f in the main
- direction.
- */
- if(other_axes_overlap && main_axis_collides)
+ if(fabs(cbox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS)
{
- 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;
+ result.touching_ground = true;
+ if(is_unloaded[boxindex])
+ result.standing_on_unloaded = true;
}
-
}
- } // xyz
-
+ }
+
return result;
}
+#if 0
+// This doesn't seem to work and isn't used
collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
- f32 pos_max_d, const core::aabbox3d<f32> &box_0,
- f32 dtime, v3f &pos_f, v3f &speed_f)
+ f32 pos_max_d, const aabb3f &box_0,
+ f32 stepheight, f32 dtime,
+ v3f &pos_f, v3f &speed_f, v3f &accel_f)
{
- collisionMoveResult final_result;
+ //TimeTaker tt("collisionMovePrecise");
+ ScopeProfiler sp(g_profiler, "collisionMovePrecise avg", SPT_AVG);
+ collisionMoveResult final_result;
+
// If there is no speed, there are no collisions
if(speed_f.getLength() == 0)
return 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
if(dtime > 2.0)
dtime = 2.0;
-
+
f32 dtime_downcount = dtime;
u32 loopcount = 0;
{
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;
if(dtime_downcount > dtime_max_increment)
{
}
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)
final_result.touching_ground = true;
if(result.collides)
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);
-
return final_result;
}
-
-
+#endif
#define COLLISION_HEADER
#include "irrlichttypes_bloated.h"
+#include <vector>
class Map;
class IGameDef;
{
bool touching_ground;
bool collides;
+ bool collides_xz;
+ bool standing_on_unloaded;
collisionMoveResult():
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
collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
- f32 pos_max_d, const core::aabbox3d<f32> &box_0,
- f32 dtime, v3f &pos_f, v3f &speed_f);
+ f32 pos_max_d, const aabb3f &box_0,
+ f32 stepheight, f32 dtime,
+ v3f &pos_f, v3f &speed_f, v3f &accel_f);
+#if 0
+// This doesn't seem to work and isn't used
// Moves using as many iterations as needed
collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
- f32 pos_max_d, const core::aabbox3d<f32> &box_0,
- f32 dtime, v3f &pos_f, v3f &speed_f);
+ f32 pos_max_d, const aabb3f &box_0,
+ f32 stepheight, f32 dtime,
+ v3f &pos_f, v3f &speed_f, v3f &accel_f);
+#endif
+
+// 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
{
box.MinEdge *= BS;
box.MaxEdge *= BS;
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_velocity = m_velocity;
+ v3f p_acceleration = m_acceleration;
IGameDef *gamedef = env->getGameDef();
- moveresult = collisionMovePrecise(&env->getMap(), gamedef,
- pos_max_d, box, dtime, p_pos, p_velocity);
+ moveresult = collisionMoveSimple(&env->getMap(), gamedef,
+ pos_max_d, box, stepheight, dtime,
+ p_pos, p_velocity, p_acceleration);
// Apply results
m_position = p_pos;
m_velocity = p_velocity;
+ m_acceleration = p_acceleration;
bool is_end_position = moveresult.collides;
pos_translator.update(m_position, is_end_position, dtime);
pos_translator.translate(dtime);
updateNodePos();
-
- m_velocity += dtime * m_acceleration;
} else {
m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
m_velocity += dtime * m_acceleration;
u16 indices[] = {0,1,2,2,3,0};
collector.append(tile, vertices, 4, indices, 6);
break;}
+ case NDT_NODEBOX:
+ {
+ TileSpec tiles[6];
+ for(int i = 0; i < 6; i++)
+ {
+ tiles[i] = getNodeTileN(n, p, i, data);
+ }
+
+ u16 l = getInteriorLight(n, 0, data);
+ video::SColor c = MapBlock_LightColor(255, l);
+
+ v3f pos = intToFloat(p, 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, tiles, 6, c, txc);
+ }
+ break;}
}
}
}
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f;
+ v3f accel_f = v3f(0,0,0);
+ f32 stepheight = 0;
IGameDef *gamedef = m_env->getGameDef();
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)
return;
box.MaxEdge *= BS;
collisionMoveResult moveresult;
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_acceleration = m_acceleration;
IGameDef *gamedef = m_env->getGameDef();
- moveresult = collisionMovePrecise(&m_env->getMap(), gamedef,
- pos_max_d, box, dtime, p_pos, p_velocity);
+ moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
+ pos_max_d, box, stepheight, dtime,
+ p_pos, p_velocity, p_acceleration);
// Apply results
- setBasePosition(p_pos);
+ m_base_position = p_pos;
m_velocity = p_velocity;
-
- m_velocity += dtime * m_acceleration;
+ m_acceleration = p_acceleration;
} else {
m_base_position += dtime * m_velocity + 0.5 * dtime
* dtime * m_acceleration;
#endif
#include "event_manager.h"
#include <list>
+#include "util/directiontables.h"
/*
Text input system
core::line3d<f32> shootline, f32 d,
bool liquids_pointable,
bool look_for_object,
- core::aabbox3d<f32> &hilightbox,
- bool &should_show_hilightbox,
+ std::vector<aabb3f> &hilightboxes,
ClientActiveObject *&selected_object)
{
PointedThing result;
- hilightbox = core::aabbox3d<f32>(0,0,0,0,0,0);
- should_show_hilightbox = false;
+ hilightboxes.clear();
selected_object = NULL;
INodeDefManager *nodedef = client->getNodeDefManager();
{
selected_object = client->getSelectedActiveObject(d*BS,
camera_position, shootline);
- }
- if(selected_object != NULL)
- {
- core::aabbox3d<f32> *selection_box
- = selected_object->getSelectionBox();
- // Box should exist because object was returned in the
- // first place
- assert(selection_box);
-
- v3f pos = selected_object->getPosition();
- hilightbox = core::aabbox3d<f32>(
- selection_box->MinEdge + pos,
- selection_box->MaxEdge + pos
- );
+ if(selected_object != NULL)
+ {
+ if(selected_object->doShowSelectionBox())
+ {
+ aabb3f *selection_box = selected_object->getSelectionBox();
+ // Box should exist because object was
+ // returned in the first place
+ assert(selection_box);
+
+ v3f pos = selected_object->getPosition();
+ hilightboxes.push_back(aabb3f(
+ selection_box->MinEdge + pos,
+ selection_box->MaxEdge + pos));
+ }
- should_show_hilightbox = selected_object->doShowSelectionBox();
- result.type = POINTEDTHING_OBJECT;
- result.object_id = selected_object->getId();
- return result;
+ result.type = POINTEDTHING_OBJECT;
+ result.object_id = selected_object->getId();
+ return result;
+ }
}
// That didn't work, try to find a pointed at node
if(!isPointableNode(n, client, liquids_pointable))
continue;
+ std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
+
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)
+
+ for(std::vector<aabb3f>::const_iterator
+ i = boxes.begin();
+ i != boxes.end(); i++)
{
- core::aabbox3d<f32> box = f.selection_box.fixed;
+ aabb3f box = *i;
box.MinEdge += npf;
box.MaxEdge += npf;
- v3s16 facedirs[6] = {
- v3s16(-1,0,0),
- v3s16(1,0,0),
- v3s16(0,-1,0),
- v3s16(0,1,0),
- v3s16(0,0,-1),
- v3s16(0,0,1),
- };
-
- core::aabbox3d<f32> faceboxes[6] = {
- // X-
- core::aabbox3d<f32>(
- box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
- box.MinEdge.X+d, box.MaxEdge.Y, box.MaxEdge.Z
- ),
- // X+
- core::aabbox3d<f32>(
- box.MaxEdge.X-d, box.MinEdge.Y, box.MinEdge.Z,
- box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
- ),
- // Y-
- core::aabbox3d<f32>(
- box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
- box.MaxEdge.X, box.MinEdge.Y+d, box.MaxEdge.Z
- ),
- // Y+
- core::aabbox3d<f32>(
- box.MinEdge.X, box.MaxEdge.Y-d, box.MinEdge.Z,
- box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
- ),
- // Z-
- core::aabbox3d<f32>(
- box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
- box.MaxEdge.X, box.MaxEdge.Y, box.MinEdge.Z+d
- ),
- // Z+
- core::aabbox3d<f32>(
- box.MinEdge.X, box.MinEdge.Y, box.MaxEdge.Z-d,
- 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 = npf + facedir_f * BS/2;
+ v3s16 facedir = g_6dirs[j];
+ aabb3f facebox = box;
+
+ f32 d = 0.001*BS;
+ if(facedir.X > 0)
+ facebox.MinEdge.X = facebox.MaxEdge.X-d;
+ else if(facedir.X < 0)
+ facebox.MaxEdge.X = facebox.MinEdge.X+d;
+ else if(facedir.Y > 0)
+ facebox.MinEdge.Y = facebox.MaxEdge.Y-d;
+ else if(facedir.Y < 0)
+ facebox.MaxEdge.Y = facebox.MinEdge.Y+d;
+ else if(facedir.Z > 0)
+ facebox.MinEdge.Z = facebox.MaxEdge.Z-d;
+ else if(facedir.Z < 0)
+ facebox.MaxEdge.Z = facebox.MinEdge.Z+d;
+
+ v3f centerpoint = facebox.getCenter();
f32 distance = (centerpoint - camera_position).getLength();
if(distance >= mindistance)
continue;
- if(!faceboxes[i].intersectsWithLine(shootline))
+ if(!facebox.intersectsWithLine(shootline))
continue;
+
+ v3s16 np_above = np + facedir;
+
result.type = POINTEDTHING_NODE;
result.node_undersurface = np;
- result.node_abovesurface = np+facedirs[i];
+ result.node_abovesurface = np_above;
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))
+ hilightboxes.clear();
+ for(std::vector<aabb3f>::const_iterator
+ i2 = boxes.begin();
+ i2 != boxes.end(); i2++)
{
- result.type = POINTEDTHING_NODE;
- result.node_undersurface = np;
- result.node_abovesurface = np;
- mindistance = distance;
- hilightbox = box;
- should_show_hilightbox = true;
+ aabb3f box = *i2;
+ box.MinEdge += npf + v3f(-d,-d,-d);
+ box.MaxEdge += npf + v3f(d,d,d);
+ hilightboxes.push_back(box);
}
}
}
- 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;
-
- //hilightbox = facebox;
-
- const float d = 0.502;
- core::aabbox3d<f32> nodebox
- (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
- v3f nodepos_f = intToFloat(np, BS);
- nodebox.MinEdge += nodepos_f;
- nodebox.MaxEdge += nodepos_f;
- hilightbox = nodebox;
- should_show_hilightbox = true;
- }
- } // if distance < mindistance
- } // for dirs
- } // regular block
} // for coords
return result;
hotbar_imagesize = 64;
// Hilight boxes collected during the loop and displayed
- core::list< core::aabbox3d<f32> > hilightboxes;
+ std::vector<aabb3f> hilightboxes;
// Info text
std::wstring infotext;
core::line3d<f32> shootline(camera_position,
camera_position + camera_direction * BS * (d+1));
- core::aabbox3d<f32> hilightbox;
- bool should_show_hilightbox = false;
ClientActiveObject *selected_object = NULL;
PointedThing pointed = getPointedThing(
camera_position, shootline, d,
playeritem_liquids_pointable, !ldown_for_dig,
// output
- hilightbox, should_show_hilightbox,
+ hilightboxes,
selected_object);
if(pointed != pointed_old)
//dstream<<"Pointing at "<<pointed.dump()<<std::endl;
}
- /*
- Visualize selection
- */
- if(should_show_hilightbox)
- hilightboxes.push_back(hilightbox);
-
/*
Stop digging when
- releasing left mouse button
if(show_hud)
{
- for(core::list<aabb3f>::Iterator i=hilightboxes.begin();
- i != hilightboxes.end(); i++)
- {
+ for(std::vector<aabb3f>::const_iterator
+ i = hilightboxes.begin();
+ i != hilightboxes.end(); i++)
+ {
/*infostream<<"hilightbox min="
<<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
<<" max="
INodeDefManager *nodemgr = m_gamedef->ndef();
v3f position = getPosition();
- v3f oldpos = position;
- v3s16 oldpos_i = floatToInt(oldpos, BS);
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
bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
bool free_move = fly_allowed && g_settings->getBool("free_move");
if(free_move)
{
+ position += m_speed * dtime;
setPosition(position);
return;
}
Collision detection
*/
- // Player position in nodes
- v3s16 pos_i = floatToInt(position, BS);
-
/*
Check if player is in water (the oscillating value)
*/
// Maximum distance over border for sneaking
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
fall off from it
{
position.Y = min_y;
- //v3f old_speed = m_speed;
-
if(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);
- }
- }*/
}
}
Calculate player collision box (new and old)
*/
core::aabbox3d<f32> playerbox(
- position.X - player_radius,
- position.Y - 0.0,
- position.Z - player_radius,
- position.X + player_radius,
- position.Y + player_height,
- position.Z + 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
+ -player_radius,
+ 0.0,
+ -player_radius,
+ player_radius,
+ player_height,
+ 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
set to true.
Player is allowed to jump when this is true.
*/
bool touching_ground_was = touching_ground;
- touching_ground = false;
-
- /*std::cout<<"Checking collisions for ("
- <<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
+ touching_ground = result.touching_ground;
+
+ bool standing_on_unloaded = result.standing_on_unloaded;
/*
Check the nodes under the player to see from which node the
}
}
+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)
{
#include "irrlichttypes.h"
#include "irr_v3d.h"
+#include "irr_aabb3d.h"
#include "light.h"
+#include <vector>
class INodeDefManager;
u8 getWallMounted(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
*/
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
{
- writeU8(os, 0); // version
+ writeU8(os, 1); // version
writeU8(os, type);
- writeV3F1000(os, fixed.MinEdge);
- writeV3F1000(os, fixed.MaxEdge);
- writeV3F1000(os, wall_top.MinEdge);
- writeV3F1000(os, wall_top.MaxEdge);
- writeV3F1000(os, wall_bottom.MinEdge);
- writeV3F1000(os, wall_bottom.MaxEdge);
- writeV3F1000(os, wall_side.MinEdge);
- writeV3F1000(os, wall_side.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.MaxEdge);
+ writeV3F1000(os, wall_bottom.MinEdge);
+ writeV3F1000(os, wall_bottom.MaxEdge);
+ writeV3F1000(os, wall_side.MinEdge);
+ writeV3F1000(os, wall_side.MaxEdge);
+ }
}
void NodeBox::deSerialize(std::istream &is)
{
int version = readU8(is);
- if(version != 0)
+ if(version != 1)
throw SerializationError("unsupported NodeBox version");
+
+ reset();
+
type = (enum NodeBoxType)readU8(is);
- fixed.MinEdge = readV3F1000(is);
- fixed.MaxEdge = readV3F1000(is);
- wall_top.MinEdge = readV3F1000(is);
- wall_top.MaxEdge = readV3F1000(is);
- wall_bottom.MinEdge = readV3F1000(is);
- wall_bottom.MaxEdge = readV3F1000(is);
- wall_side.MinEdge = readV3F1000(is);
- wall_side.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.MaxEdge = readV3F1000(is);
+ wall_bottom.MinEdge = readV3F1000(is);
+ wall_bottom.MaxEdge = readV3F1000(is);
+ wall_side.MinEdge = readV3F1000(is);
+ wall_side.MaxEdge = readV3F1000(is);
+ }
}
/*
liquid_viscosity = 0;
light_source = 0;
damage_per_second = 0;
+ node_box = NodeBox();
selection_box = NodeBox();
legacy_facedir_simple = false;
legacy_wallmounted = false;
writeU8(os, liquid_viscosity);
writeU8(os, light_source);
writeU32(os, damage_per_second);
+ node_box.serialize(os);
selection_box.serialize(os);
writeU8(os, legacy_facedir_simple);
writeU8(os, legacy_wallmounted);
liquid_viscosity = readU8(is);
light_source = readU8(is);
damage_per_second = readU32(is);
+ node_box.deSerialize(is);
selection_box.deSerialize(is);
legacy_facedir_simple = readU8(is);
legacy_wallmounted = readU8(is);
case NDT_PLANTLIKE:
case NDT_FENCELIKE:
case NDT_RAILLIKE:
+ case NDT_NODEBOX:
f->solidness = 0;
break;
}
enum NodeBoxType
{
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)
};
enum NodeBoxType type;
// NODEBOX_REGULAR (no parameters)
// NODEBOX_FIXED
- core::aabbox3d<f32> fixed;
+ std::vector<aabb3f> fixed;
// NODEBOX_WALLMOUNTED
- core::aabbox3d<f32> wall_top;
- core::aabbox3d<f32> wall_bottom;
- core::aabbox3d<f32> wall_side; // being at the -X side
-
- NodeBox():
- type(NODEBOX_REGULAR),
- // 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)
- {}
+ aabb3f wall_top;
+ aabb3f wall_bottom;
+ aabb3f wall_side; // being at the -X side
+ NodeBox()
+ { reset(); }
+
+ void reset();
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);
};
NDT_PLANTLIKE,
NDT_FENCELIKE,
NDT_RAILLIKE,
+ NDT_NODEBOX,
};
#define CF_SPECIAL_COUNT 2
// Amount of light the node emits
u8 light_source;
u32 damage_per_second;
+ NodeBox node_box;
NodeBox selection_box;
// Compatibility with old maps
// Set to true if paramtype used to be 'facedir_simple'
RemotePlayer
*/
+
+
+
+
void RemotePlayer::setPosition(const v3f &position)
{
Player::setPosition(position);
{NDT_PLANTLIKE, "plantlike"},
{NDT_FENCELIKE, "fencelike"},
{NDT_RAILLIKE, "raillike"},
+ {NDT_NODEBOX, "nodebox"},
{0, NULL},
};
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;
- if(lua_istable(L, -1)){
- lua_rawgeti(L, -1, 1);
+ aabb3f box;
+ if(lua_istable(L, index)){
+ lua_rawgeti(L, index, 1);
box.MinEdge.X = lua_tonumber(L, -1) * scale;
lua_pop(L, 1);
- lua_rawgeti(L, -1, 2);
+ lua_rawgeti(L, index, 2);
box.MinEdge.Y = lua_tonumber(L, -1) * scale;
lua_pop(L, 1);
- lua_rawgeti(L, -1, 3);
+ lua_rawgeti(L, index, 3);
box.MinEdge.Z = lua_tonumber(L, -1) * scale;
lua_pop(L, 1);
- lua_rawgeti(L, -1, 4);
+ lua_rawgeti(L, index, 4);
box.MaxEdge.X = lua_tonumber(L, -1) * scale;
lua_pop(L, 1);
- lua_rawgeti(L, -1, 5);
+ lua_rawgeti(L, index, 5);
box.MaxEdge.Y = lua_tonumber(L, -1) * scale;
lua_pop(L, 1);
- lua_rawgeti(L, -1, 6);
+ lua_rawgeti(L, index, 6);
box.MaxEdge.Z = lua_tonumber(L, -1) * scale;
lua_pop(L, 1);
}
return box;
}
-#if 0
-/*
- MaterialProperties
-*/
+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 MaterialProperties read_material_properties(
- lua_State *L, int table)
+static NodeBox read_nodebox(lua_State *L, int index)
{
- MaterialProperties prop;
- prop.diggability = (Diggability)getenumfield(L, -1, "diggability",
- es_Diggability, DIGGABLE_NORMAL);
- getfloatfield(L, -1, "constant_time", prop.constant_time);
- getfloatfield(L, -1, "weight", prop.weight);
- getfloatfield(L, -1, "crackiness", prop.crackiness);
- getfloatfield(L, -1, "crumbliness", prop.crumbliness);
- getfloatfield(L, -1, "cuttability", prop.cuttability);
- getfloatfield(L, -1, "flammability", prop.flammability);
- return prop;
+ 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;
}
-#endif
/*
Groups
lua_getfield(L, -1, "collisionbox");
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);
getstringfield(L, -1, "visual", prop->visual);
f.damage_per_second = getintfield_default(L, index,
"damage_per_second", f.damage_per_second);
- 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))
- f.selection_box.fixed = read_aabbox3df32(L, -1, BS);
- 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_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");
+ if(lua_istable(L, -1))
+ f.selection_box = read_nodebox(L, -1);
+ lua_pop(L, 1);
+
// Set to true if paramtype used to be 'facedir_simple'
getboolfield(L, index, "legacy_facedir_simple", f.legacy_facedir_simple);
// Set to true if wall_mounted used to be set to true
// Set default values that differ from ObjectProperties defaults
prop->hp_max = 10;
+ /* Read stuff */
+
+ prop->hp_max = getintfield_default(L, -1, "hp_max", 10);
+
+ getboolfield(L, -1, "physical", prop->physical);
+
+ getfloatfield(L, -1, "weight", prop->weight);
+
+ lua_getfield(L, -1, "collisionbox");
+ if(lua_istable(L, -1))
+ prop->collisionbox = read_aabb3f(L, -1, 1.0);
+ lua_pop(L, 1);
+
+ getstringfield(L, -1, "visual", prop->visual);
+
// Deprecated: read object properties directly
read_object_properties(L, -1, prop);
#include "connection.h"
#include "serialization.h"
#include "voxel.h"
+#include "collision.h"
#include <sstream>
#include "porting.h"
#include "content_mapnode.h"
};
#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
{
void Run()
TESTPARAMS(TestInventory, idef);
//TEST(TestMapBlock);
//TEST(TestMapSector);
+ TEST(TestCollision);
if(INTERNET_SIMULATOR == false){
TEST(TestSocket);
dout_con<<"=== BEGIN RUNNING UNIT TESTS FOR CONNECTION ==="<<std::endl;
v2f size; // Size in atlas
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(
u16 id_,
video::ITexture *atlas_=NULL,