From 1575448b1a71dd029a8d135d2aff9096483a9953 Mon Sep 17 00:00:00 2001 From: Kahrl Date: Mon, 19 Mar 2012 04:25:09 +0100 Subject: [PATCH] Custom boxy nodes (stairs, slabs) and collision changes --- games/minimal/mods/default/init.lua | 2 +- games/minimal/mods/stairs/depends.txt | 1 + games/minimal/mods/stairs/init.lua | 93 +++++ src/camera.cpp | 16 +- src/collision.cpp | 530 +++++++++++++++++++------- src/collision.h | 36 +- src/content_cao.cpp | 12 +- src/content_mapblock.cpp | 47 +++ src/content_sao.cpp | 19 +- src/game.cpp | 270 ++++--------- src/localplayer.cpp | 214 +---------- src/mapnode.cpp | 91 +++++ src/mapnode.h | 13 + src/nodedef.cpp | 80 +++- src/nodedef.h | 24 +- src/player.cpp | 4 + src/scriptapi.cpp | 142 ++++--- src/test.cpp | 149 ++++++++ src/tile.h | 8 + 19 files changed, 1105 insertions(+), 646 deletions(-) create mode 100644 games/minimal/mods/stairs/depends.txt create mode 100644 games/minimal/mods/stairs/init.lua diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua index 64dcbbdfe..cb424cb5b 100644 --- a/games/minimal/mods/default/init.lua +++ b/games/minimal/mods/default/init.lua @@ -926,7 +926,7 @@ minetest.register_node("default:rail", { walkable = false, selection_box = { type = "fixed", - --fixed = + fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, }, groups = {bendy=2,snappy=1,dig_immediate=2}, }) diff --git a/games/minimal/mods/stairs/depends.txt b/games/minimal/mods/stairs/depends.txt new file mode 100644 index 000000000..4ad96d515 --- /dev/null +++ b/games/minimal/mods/stairs/depends.txt @@ -0,0 +1 @@ +default diff --git a/games/minimal/mods/stairs/init.lua b/games/minimal/mods/stairs/init.lua new file mode 100644 index 000000000..4929d1370 --- /dev/null +++ b/games/minimal/mods/stairs/init.lua @@ -0,0 +1,93 @@ +stairs = {} + +-- Node will be called stairs:stair_ +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_ +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}_ +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") diff --git a/src/camera.cpp b/src/camera.cpp index 1ef594eea..32dd85e52 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -213,8 +213,22 @@ void Camera::step(f32 dtime) 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(); diff --git a/src/collision.cpp b/src/collision.cpp index f8db42d26..09a7df7c2 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -22,32 +22,249 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "nodedef.h" #include "gamedef.h" +#include "log.h" +#include +#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 &staticboxes, + const aabb3f &movingbox, + f32 y_increase, f32 d) +{ + //TimeTaker tt("wouldCollideWithCeiling"); + + assert(y_increase >= 0); + + for(std::vector::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 &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 cboxes; + std::vector is_unloaded; + std::vector 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 nodeboxes = n.getNodeBoxes(gamedef->ndef()); + for(std::vector::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 @@ -58,49 +275,129 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, // This should always apply, otherwise there are glitches assert(d > pos_max_d); - - /* - Calculate collision box - */ - core::aabbox3d box = box_0; - box.MaxEdge += pos_f; - box.MinEdge += pos_f; - core::aabbox3d 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) - continue; - } - catch(InvalidPositionException &e) + //TimeTaker tt3("collisionMoveSimple dtime loop"); + ScopeProfiler sp(g_profiler, "collisionMoveSimple dtime loop avg", SPT_AVG); + + // Avoid infinite loop + loopcount++; + if(loopcount >= 100) { - // Doing nothing here will block the object from - // walking over map borders + infostream<<"collisionMoveSimple: WARNING: Loop count exceeded, aborting to avoid infiniite loop"< nodebox = getNodeBox(v3s16(x,y,z), BS); - + 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; + } + + 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. @@ -111,112 +408,50 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, 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 &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; @@ -224,6 +459,16 @@ collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef, { 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) { @@ -242,17 +487,20 @@ collisionMoveResult collisionMovePrecise(Map *map, IGameDef *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) 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 diff --git a/src/collision.h b/src/collision.h index a4eca0dd8..243c4b294 100644 --- a/src/collision.h +++ b/src/collision.h @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define COLLISION_HEADER #include "irrlichttypes_bloated.h" +#include class Map; class IGameDef; @@ -29,22 +30,47 @@ struct collisionMoveResult { 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 &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 &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 &staticboxes, + const aabb3f &movingbox, + f32 y_increase, f32 d); + enum CollisionType { diff --git a/src/content_cao.cpp b/src/content_cao.cpp index c993f3f83..5702a73b6 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -881,22 +881,24 @@ public: 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; diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index ebf9fd59e..d87ae32fb 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -1027,6 +1027,53 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 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 boxes = n.getNodeBoxes(nodedef); + for(std::vector::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;} } } } diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 468a39110..e9b5782a9 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -206,9 +206,12 @@ public: 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; @@ -452,16 +455,18 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) 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; diff --git a/src/game.cpp b/src/game.cpp index c8defd82d..98ffac3d5 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -61,6 +61,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif #include "event_manager.h" #include +#include "util/directiontables.h" /* Text input system @@ -294,14 +295,12 @@ PointedThing getPointedThing(Client *client, v3f player_position, core::line3d shootline, f32 d, bool liquids_pointable, bool look_for_object, - core::aabbox3d &hilightbox, - bool &should_show_hilightbox, + std::vector &hilightboxes, ClientActiveObject *&selected_object) { PointedThing result; - hilightbox = core::aabbox3d(0,0,0,0,0,0); - should_show_hilightbox = false; + hilightboxes.clear(); selected_object = NULL; INodeDefManager *nodedef = client->getNodeDefManager(); @@ -312,27 +311,27 @@ PointedThing getPointedThing(Client *client, v3f player_position, { selected_object = client->getSelectedActiveObject(d*BS, camera_position, shootline); - } - if(selected_object != NULL) - { - core::aabbox3d *selection_box - = selected_object->getSelectionBox(); - // Box should exist because object was returned in the - // first place - assert(selection_box); - v3f pos = selected_object->getPosition(); + 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); - hilightbox = core::aabbox3d( - selection_box->MinEdge + pos, - selection_box->MaxEdge + pos - ); + 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 @@ -368,196 +367,64 @@ PointedThing getPointedThing(Client *client, v3f player_position, if(!isPointableNode(n, client, liquids_pointable)) continue; + std::vector 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::const_iterator + i = boxes.begin(); + i != boxes.end(); i++) { - core::aabbox3d 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 faceboxes[6] = { - // X- - core::aabbox3d( - box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z, - box.MinEdge.X+d, box.MaxEdge.Y, box.MaxEdge.Z - ), - // X+ - core::aabbox3d( - box.MaxEdge.X-d, box.MinEdge.Y, box.MinEdge.Z, - box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z - ), - // Y- - core::aabbox3d( - box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z, - box.MaxEdge.X, box.MinEdge.Y+d, box.MaxEdge.Z - ), - // Y+ - core::aabbox3d( - box.MinEdge.X, box.MaxEdge.Y-d, box.MinEdge.Z, - box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z - ), - // Z- - core::aabbox3d( - box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z, - box.MaxEdge.X, box.MaxEdge.Y, box.MinEdge.Z+d - ), - // Z+ - core::aabbox3d( - 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 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] = + hilightboxes.clear(); + for(std::vector::const_iterator + i2 = boxes.begin(); + i2 != boxes.end(); i2++) { - 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(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; + 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 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 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 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; @@ -1514,7 +1381,7 @@ void the_game( hotbar_imagesize = 64; // Hilight boxes collected during the loop and displayed - core::list< core::aabbox3d > hilightboxes; + std::vector hilightboxes; // Info text std::wstring infotext; @@ -2127,8 +1994,6 @@ void the_game( core::line3d shootline(camera_position, camera_position + camera_direction * BS * (d+1)); - core::aabbox3d hilightbox; - bool should_show_hilightbox = false; ClientActiveObject *selected_object = NULL; PointedThing pointed = getPointedThing( @@ -2137,7 +2002,7 @@ void the_game( camera_position, shootline, d, playeritem_liquids_pointable, !ldown_for_dig, // output - hilightbox, should_show_hilightbox, + hilightboxes, selected_object); if(pointed != pointed_old) @@ -2146,12 +2011,6 @@ void the_game( //dstream<<"Pointing at "<::Iterator i=hilightboxes.begin(); - i != hilightboxes.end(); i++) - { + for(std::vector::const_iterator + i = hilightboxes.begin(); + i != hilightboxes.end(); i++) + { /*infostream<<"hilightbox min=" <<"("<MinEdge.X<<","<MinEdge.Y<<","<MinEdge.Z<<")" <<" max=" diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 2bd62dabf..df668b36d 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -52,24 +52,15 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, 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=("<checkLocalPrivilege("fly"); bool free_move = fly_allowed && g_settings->getBool("free_move"); if(free_move) { + position += m_speed * dtime; setPosition(position); return; } @@ -78,9 +69,6 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, Collision detection */ - // Player position in nodes - v3s16 pos_i = floatToInt(position, BS); - /* Check if player is in water (the oscillating value) */ @@ -147,13 +135,6 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, // 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 @@ -170,22 +151,8 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, { 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); - } - }*/ } } @@ -193,22 +160,22 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, Calculate player collision box (new and old) */ core::aabbox3d 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 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. @@ -216,154 +183,9 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, Player is allowed to jump when this is true. */ bool touching_ground_was = touching_ground; - touching_ground = false; - - /*std::cout<<"Checking collisions for (" - < (" - <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 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 diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 4de84dd1d..e12f252fc 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -134,7 +134,98 @@ v3s16 MapNode::getWallMountedDir(INodeDefManager *nodemgr) const } } +static std::vector transformNodeBox(const MapNode &n, + const NodeBox &nodebox, INodeDefManager *nodemgr) +{ + std::vector boxes; + if(nodebox.type == NODEBOX_FIXED) + { + const std::vector &fixed = nodebox.fixed; + int facedir = n.getFaceDir(nodemgr); + for(std::vector::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 MapNode::getNodeBoxes(INodeDefManager *nodemgr) const +{ + const ContentFeatures &f = nodemgr->get(*this); + return transformNodeBox(*this, f.node_box, nodemgr); +} + +std::vector MapNode::getSelectionBoxes(INodeDefManager *nodemgr) const +{ + const ContentFeatures &f = nodemgr->get(*this); + return transformNodeBox(*this, f.selection_box, nodemgr); +} u32 MapNode::serializedLength(u8 version) { diff --git a/src/mapnode.h b/src/mapnode.h index 32e46b63f..751563135 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -22,7 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes.h" #include "irr_v3d.h" +#include "irr_aabb3d.h" #include "light.h" +#include class INodeDefManager; @@ -196,6 +198,17 @@ struct MapNode u8 getWallMounted(INodeDefManager *nodemgr) const; v3s16 getWallMountedDir(INodeDefManager *nodemgr) const; + /* + Gets list of node boxes (used for rendering (NDT_NODEBOX) + and collision) + */ + std::vector getNodeBoxes(INodeDefManager *nodemgr) const; + + /* + Gets list of selection boxes + */ + std::vector getSelectionBoxes(INodeDefManager *nodemgr) const; + /* Serialization functions */ diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 80bfae3e7..1b85a9558 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -33,34 +33,74 @@ with this program; if not, write to the Free Software Foundation, Inc., 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::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); + } } /* @@ -165,6 +205,7 @@ void ContentFeatures::reset() liquid_viscosity = 0; light_source = 0; damage_per_second = 0; + node_box = NodeBox(); selection_box = NodeBox(); legacy_facedir_simple = false; legacy_wallmounted = false; @@ -214,6 +255,7 @@ void ContentFeatures::serialize(std::ostream &os) 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); @@ -277,6 +319,7 @@ void ContentFeatures::deSerialize(std::istream &is) 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); @@ -577,6 +620,7 @@ public: case NDT_PLANTLIKE: case NDT_FENCELIKE: case NDT_RAILLIKE: + case NDT_NODEBOX: f->solidness = 0; break; } diff --git a/src/nodedef.h b/src/nodedef.h index fcd06be71..7c6931834 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -65,7 +65,7 @@ enum LiquidType 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) }; @@ -74,22 +74,16 @@ struct NodeBox enum NodeBoxType type; // NODEBOX_REGULAR (no parameters) // NODEBOX_FIXED - core::aabbox3d fixed; + std::vector fixed; // NODEBOX_WALLMOUNTED - core::aabbox3d wall_top; - core::aabbox3d wall_bottom; - core::aabbox3d wall_side; // being at the -X side + aabb3f wall_top; + aabb3f wall_bottom; + aabb3f 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) - {} + NodeBox() + { reset(); } + void reset(); void serialize(std::ostream &os) const; void deSerialize(std::istream &is); }; @@ -143,6 +137,7 @@ enum NodeDrawType NDT_PLANTLIKE, NDT_FENCELIKE, NDT_RAILLIKE, + NDT_NODEBOX, }; #define CF_SPECIAL_COUNT 2 @@ -217,6 +212,7 @@ struct ContentFeatures // 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' diff --git a/src/player.cpp b/src/player.cpp index d470fa6ff..999d842fa 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -166,6 +166,10 @@ void Player::deSerialize(std::istream &is) RemotePlayer */ + + + + void RemotePlayer::setPosition(const v3f &position) { Player::setPosition(position); diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index 289ad31f8..cf2a01c64 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -382,6 +382,7 @@ struct EnumString es_DrawType[] = {NDT_PLANTLIKE, "plantlike"}, {NDT_FENCELIKE, "fencelike"}, {NDT_RAILLIKE, "raillike"}, + {NDT_NODEBOX, "nodebox"}, {0, NULL}, }; @@ -595,52 +596,89 @@ static video::SColor readARGB8(lua_State *L, int index) return color; } -static core::aabbox3d read_aabbox3df32(lua_State *L, int index, f32 scale) +static aabb3f read_aabb3f(lua_State *L, int index, f32 scale) { - core::aabbox3d 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 MaterialProperties read_material_properties( - lua_State *L, int table) +static std::vector read_aabb3f_vector(lua_State *L, int index, f32 scale) { - 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; + std::vector 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; } -#endif /* Groups @@ -891,7 +929,7 @@ static void read_object_properties(lua_State *L, int index, 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); @@ -1203,33 +1241,16 @@ static ContentFeatures read_content_features(lua_State *L, int index) 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 @@ -5604,6 +5625,21 @@ void scriptapi_luaentity_get_properties(lua_State *L, u16 id, // 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); diff --git a/src/test.cpp b/src/test.cpp index 3dc6814b1..9b1346274 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "connection.h" #include "serialization.h" #include "voxel.h" +#include "collision.h" #include #include "porting.h" #include "content_mapnode.h" @@ -1075,6 +1076,153 @@ struct TestMapSector }; #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() @@ -1544,6 +1692,7 @@ void run_tests() TESTPARAMS(TestInventory, idef); //TEST(TestMapBlock); //TEST(TestMapSector); + TEST(TestCollision); if(INTERNET_SIMULATOR == false){ TEST(TestSocket); dout_con<<"=== BEGIN RUNNING UNIT TESTS FOR CONNECTION ==="<