NDT_NODEBOX, collision code rewrite with step height, stairs mod -- rebased after...
authorKahrl <kahrl@gmx.net>
Thu, 2 Feb 2012 18:20:46 +0000 (19:20 +0100)
committerKahrl <kahrl@gmx.net>
Thu, 2 Feb 2012 18:20:52 +0000 (19:20 +0100)
16 files changed:
data/mods/stairs/depends.txt [new file with mode: 0644]
data/mods/stairs/init.lua [new file with mode: 0644]
src/collision.cpp
src/collision.h
src/content_cao.cpp
src/content_mapblock.cpp
src/content_sao.cpp
src/game.cpp
src/mapnode.cpp
src/mapnode.h
src/nodedef.cpp
src/nodedef.h
src/player.cpp
src/scriptapi.cpp
src/test.cpp
src/tile.h

diff --git a/data/mods/stairs/depends.txt b/data/mods/stairs/depends.txt
new file mode 100644 (file)
index 0000000..4ad96d5
--- /dev/null
@@ -0,0 +1 @@
+default
diff --git a/data/mods/stairs/init.lua b/data/mods/stairs/init.lua
new file mode 100644 (file)
index 0000000..cb3de3c
--- /dev/null
@@ -0,0 +1,93 @@
+stairs = {}
+
+-- Node will be called stairs:stair_<subname>
+function stairs.register_stair(subname, recipeitem, material, images, description)
+       minetest.register_node("stairs:stair_" .. subname, {
+               description = description,
+               drawtype = "nodebox",
+               tile_images = images,
+               paramtype = "light",
+               paramtype2 = "facedir",
+               is_ground_content = true,
+               node_box = {
+                       type = "fixed",
+                       fixed = {
+                               {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
+                               {-0.5, 0, 0, 0.5, 0.5, 0.5},
+                       },
+               },
+               material = material,
+       })
+
+       minetest.register_craft({
+               output = 'stairs:stair_' .. subname .. ' 4',
+               recipe = {
+                       {recipeitem, "", ""},
+                       {recipeitem, recipeitem, ""},
+                       {recipeitem, recipeitem, recipeitem},
+               },
+       })
+end
+
+-- Node will be called stairs:slab_<subname>
+function stairs.register_slab(subname, recipeitem, material, images, description)
+       minetest.register_node("stairs:slab_" .. subname, {
+               description = description,
+               drawtype = "nodebox",
+               tile_images = images,
+               paramtype = "light",
+               is_ground_content = true,
+               node_box = {
+                       type = "fixed",
+                       fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
+               },
+               selection_box = {
+                       type = "fixed",
+                       fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
+               },
+               material = material,
+       })
+
+       minetest.register_craft({
+               output = 'stairs:slab_' .. subname .. ' 3',
+               recipe = {
+                       {recipeitem, recipeitem, recipeitem},
+               },
+       })
+end
+
+-- Nodes will be called stairs:{stair,slab}_<subname>
+function stairs.register_stair_and_slab(subname, recipeitem, material, images, desc_stair, desc_slab)
+       stairs.register_stair(subname, recipeitem, material, images, desc_stair)
+       stairs.register_slab(subname, recipeitem, material, images, desc_slab)
+end
+
+stairs.register_stair_and_slab("wood", "default:wood",
+               minetest.digprop_woodlike(0.75),
+               {"default_wood.png"},
+               "Wooden stair",
+               "Wooden slab")
+
+stairs.register_stair_and_slab("stone", "default:stone",
+               minetest.digprop_stonelike(0.75),
+               {"default_stone.png"},
+               "Stone stair",
+               "Stone slab")
+
+stairs.register_stair_and_slab("cobble", "default:cobble",
+               minetest.digprop_stonelike(0.75),
+               {"default_cobble.png"},
+               "Cobble stair",
+               "Cobble slab")
+
+stairs.register_stair_and_slab("brick", "default:brick",
+               minetest.digprop_stonelike(0.75),
+               {"default_brick.png"},
+               "Brick stair",
+               "Brick slab")
+
+stairs.register_stair_and_slab("sandstone", "default:sandstone",
+               minetest.digprop_stonelike(0.75),
+               {"default_sandstone.png"},
+               "Sandstone stair",
+               "Sandstone slab")
index 3460b04fdb719f75072c444ae243f61a5957de0a..d131c7a20d0c1b25558cbab70587d9ce0a8a40e8 100644 (file)
@@ -22,28 +22,240 @@ 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 <vector>
+
+// Helper function:
+// Checks for collision of a moving aabbox with a static aabbox
+// Returns -1 if no collision, 0 if X collision, 1 if Y collision, 2 if Z collision
+// The time after which the collision occurs is stored in dtime.
+int axisAlignedCollision(
+               const aabb3f &staticbox, const aabb3f &movingbox,
+               const v3f &speed, f32 d, f32 &dtime)
+{
+       //TimeTaker tt("axisAlignedCollision");
+
+       f32 xsize = (staticbox.MaxEdge.X - staticbox.MinEdge.X);
+       f32 ysize = (staticbox.MaxEdge.Y - staticbox.MinEdge.Y);
+       f32 zsize = (staticbox.MaxEdge.Z - staticbox.MinEdge.Z);
+
+       aabb3f relbox(
+                       movingbox.MinEdge.X - staticbox.MinEdge.X,
+                       movingbox.MinEdge.Y - staticbox.MinEdge.Y,
+                       movingbox.MinEdge.Z - staticbox.MinEdge.Z,
+                       movingbox.MaxEdge.X - staticbox.MinEdge.X,
+                       movingbox.MaxEdge.Y - staticbox.MinEdge.Y,
+                       movingbox.MaxEdge.Z - staticbox.MinEdge.Z
+       );
+
+       if(speed.X > 0) // Check for collision with X- plane
+       {
+               if(relbox.MaxEdge.X <= d)
+               {
+                       dtime = - relbox.MaxEdge.X / speed.X;
+                       if((relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
+                                       (relbox.MaxEdge.Y + speed.Y * dtime > 0) &&
+                                       (relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
+                                       (relbox.MaxEdge.Z + speed.Z * dtime > 0))
+                               return 0;
+               }
+               else if(relbox.MinEdge.X > xsize)
+               {
+                       return -1;
+               }
+       }
+       else if(speed.X < 0) // Check for collision with X+ plane
+       {
+               if(relbox.MinEdge.X >= xsize - d)
+               {
+                       dtime = (xsize - relbox.MinEdge.X) / speed.X;
+                       if((relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
+                                       (relbox.MaxEdge.Y + speed.Y * dtime > 0) &&
+                                       (relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
+                                       (relbox.MaxEdge.Z + speed.Z * dtime > 0))
+                               return 0;
+               }
+               else if(relbox.MaxEdge.X < 0)
+               {
+                       return -1;
+               }
+       }
+
+       // NO else if here
+
+       if(speed.Y > 0) // Check for collision with Y- plane
+       {
+               if(relbox.MaxEdge.Y <= d)
+               {
+                       dtime = - relbox.MaxEdge.Y / speed.Y;
+                       if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
+                                       (relbox.MaxEdge.X + speed.X * dtime > 0) &&
+                                       (relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
+                                       (relbox.MaxEdge.Z + speed.Z * dtime > 0))
+                               return 1;
+               }
+               else if(relbox.MinEdge.Y > ysize)
+               {
+                       return -1;
+               }
+       }
+       else if(speed.Y < 0) // Check for collision with Y+ plane
+       {
+               if(relbox.MinEdge.Y >= ysize - d)
+               {
+                       dtime = (ysize - relbox.MinEdge.Y) / speed.Y;
+                       if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
+                                       (relbox.MaxEdge.X + speed.X * dtime > 0) &&
+                                       (relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
+                                       (relbox.MaxEdge.Z + speed.Z * dtime > 0))
+                               return 1;
+               }
+               else if(relbox.MaxEdge.Y < 0)
+               {
+                       return -1;
+               }
+       }
+
+       // NO else if here
+
+       if(speed.Z > 0) // Check for collision with Z- plane
+       {
+               if(relbox.MaxEdge.Z <= d)
+               {
+                       dtime = - relbox.MaxEdge.Z / speed.Z;
+                       if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
+                                       (relbox.MaxEdge.X + speed.X * dtime > 0) &&
+                                       (relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
+                                       (relbox.MaxEdge.Y + speed.Y * dtime > 0))
+                               return 2;
+               }
+               //else if(relbox.MinEdge.Z > zsize)
+               //{
+               //      return -1;
+               //}
+       }
+       else if(speed.Z < 0) // Check for collision with Z+ plane
+       {
+               if(relbox.MinEdge.Z >= zsize - d)
+               {
+                       dtime = (zsize - relbox.MinEdge.Z) / speed.Z;
+                       if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
+                                       (relbox.MaxEdge.X + speed.X * dtime > 0) &&
+                                       (relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
+                                       (relbox.MaxEdge.Y + speed.Y * dtime > 0))
+                               return 2;
+               }
+               //else if(relbox.MaxEdge.Z < 0)
+               //{
+               //      return -1;
+               //}
+       }
+
+       return -1;
+}
+
+// Helper function:
+// Checks if moving the movingbox up by the given distance would hit a ceiling.
+bool wouldCollideWithCeiling(
+               const std::vector<aabb3f> &staticboxes,
+               const aabb3f &movingbox,
+               f32 y_increase, f32 d)
+{
+       //TimeTaker tt("wouldCollideWithCeiling");
+
+       assert(y_increase >= 0);
+
+       for(std::vector<aabb3f>::const_iterator
+                       i = staticboxes.begin();
+                       i != staticboxes.end(); i++)
+       {
+               const aabb3f& staticbox = *i;
+               if((movingbox.MaxEdge.Y - d <= staticbox.MinEdge.Y) &&
+                               (movingbox.MaxEdge.Y + y_increase > staticbox.MinEdge.Y) &&
+                               (movingbox.MinEdge.X < staticbox.MaxEdge.X) &&
+                               (movingbox.MaxEdge.X > staticbox.MinEdge.X) &&
+                               (movingbox.MinEdge.Z < staticbox.MaxEdge.Z) &&
+                               (movingbox.MaxEdge.Z > staticbox.MinEdge.Z))
+                       return true;
+       }
+
+       return false;
+}
+
 
 collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
-               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");
+
        collisionMoveResult result;
 
-       v3f oldpos_f = pos_f;
-       v3s16 oldpos_i = floatToInt(oldpos_f, BS);
+       /*
+               Calculate new velocity
+       */
+       speed_f += accel_f * dtime;
 
        /*
-               Calculate new position
+               Collect node boxes in movement range
        */
-       pos_f += speed_f * dtime;
+       std::vector<aabb3f> cboxes;
+       std::vector<bool> is_unloaded;
+       std::vector<bool> is_step_up;
+       {
+       TimeTaker tt2("collisionMoveSimple collect boxes");
+
+       v3s16 oldpos_i = floatToInt(pos_f, BS);
+       v3s16 newpos_i = floatToInt(pos_f + speed_f * dtime, BS);
+       s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1;
+       s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1;
+       s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1;
+       s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1;
+       s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1;
+       s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1;
+
+       for(s16 x = min_x; x <= max_x; x++)
+       for(s16 y = min_y; y <= max_y; y++)
+       for(s16 z = min_z; z <= max_z; z++)
+       {
+               try{
+                       // Object collides into walkable nodes
+                       MapNode n = map->getNode(v3s16(x,y,z));
+                       if(gamedef->getNodeDefManager()->get(n).walkable == false)
+                               continue;
+
+                       std::vector<aabb3f> nodeboxes = n.getNodeBoxes(gamedef->ndef());
+                       for(std::vector<aabb3f>::iterator
+                                       i = nodeboxes.begin();
+                                       i != nodeboxes.end(); i++)
+                       {
+                               aabb3f box = *i;
+                               box.MinEdge += v3f(x, y, z)*BS;
+                               box.MaxEdge += v3f(x, y, z)*BS;
+                               cboxes.push_back(box);
+                               is_unloaded.push_back(false);
+                               is_step_up.push_back(false);
+                       }
+               }
+               catch(InvalidPositionException &e)
+               {
+                       // Collide with unloaded nodes
+                       aabb3f box = getNodeBox(v3s16(x,y,z), BS);
+                       cboxes.push_back(box);
+                       is_unloaded.push_back(true);
+                       is_step_up.push_back(false);
+               }
+       }
+       } // tt2
+
+       assert(cboxes.size() == is_unloaded.size());
+       assert(cboxes.size() == is_step_up.size());
 
        /*
                Collision detection
        */
-       
-       // position in nodes
-       v3s16 pos_i = floatToInt(pos_f, BS);
-       
+
        /*
                Collision uncertainty radius
                Make it a bit larger than the maximum distance of movement
@@ -54,49 +266,128 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
 
        // 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");
+
+               // 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.
 
@@ -107,108 +398,44 @@ 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;
 }
 
 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)
 {
+       TimeTaker tt("collisionMovePrecise");
+       infostream<<"start collisionMovePrecise\n";
+
        collisionMoveResult final_result;
 
-       // Maximum time increment (for collision detection etc)
-       // time = distance / speed
-       f32 dtime_max_increment = pos_max_d / speed_f.getLength();
-       
-       // Maximum time increment is 10ms or lower
-       if(dtime_max_increment > 0.01)
-               dtime_max_increment = 0.01;
-       
        // Don't allow overly huge dtime
        if(dtime > 2.0)
                dtime = 2.0;
-       
+
        f32 dtime_downcount = dtime;
 
        u32 loopcount = 0;
@@ -216,6 +443,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)
                {
@@ -234,17 +471,21 @@ 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);
-               
 
+
+       infostream<<"end collisionMovePrecise\n";
        return final_result;
 }
-
-
index e823a08fefb978825e4fe76f24508a818d398163..d4a3f749cabbfd44435c6a12464a8d7fe9d8d6cb 100644 (file)
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define COLLISION_HEADER
 
 #include "common_irrlicht.h"
+#include <vector>
 
 class Map;
 class IGameDef;
@@ -29,22 +30,44 @@ 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<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);
 
 // 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);
+
+// 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
 {
index a2708674b7295d86ba775a640a725b0a4bfca997..0b7cbede7c5e187af5a30d9c9cdbc9670a6b7d91 100644 (file)
@@ -1874,22 +1874,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);
+                                       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;
index dc1e1daed1e5045d74eed5ccb8877337f8e74161..b8369186287fd50ad23bc53ed8e70a30c869098e 100644 (file)
@@ -985,6 +985,72 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                        u16 indices[] = {0,1,2,2,3,0};
                        collector.append(material_rail, vertices, 4, indices, 6);
                break;}
+               case NDT_NODEBOX:
+               {
+                       v3s16 facedirs[6] = {
+                               v3s16(0,1,0),
+                               v3s16(0,-1,0),
+                               v3s16(1,0,0),
+                               v3s16(-1,0,0),
+                               v3s16(0,0,1),
+                               v3s16(0,0,-1),
+                       };
+                       video::SMaterial material_nodebox[6];
+                       AtlasPointer pa_nodebox[6];
+
+                       for(int i = 0; i < 6; i++)
+                       {
+                               material_nodebox[i].setFlag(video::EMF_LIGHTING, false);
+                               material_nodebox[i].setFlag(video::EMF_BILINEAR_FILTER, false);
+                               material_nodebox[i].setFlag(video::EMF_FOG_ENABLE, true);
+                               material_nodebox[i].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+                               TileSpec tile = getNodeTile(n, p, facedirs[i],
+                                               &data->m_temp_mods, tsrc, nodedef);
+                               pa_nodebox[i] = tile.texture;
+                               material_nodebox[i].setTexture(0, pa_nodebox[i].atlas);
+                       }
+
+                       u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
+                       video::SColor c = MapBlock_LightColor(255, l);
+
+                       v3f pos = intToFloat(p+blockpos_nodes, BS);
+
+                       std::vector<aabb3f> boxes = n.getNodeBoxes(nodedef);
+                       for(std::vector<aabb3f>::iterator
+                                       i = boxes.begin();
+                                       i != boxes.end(); i++)
+                       {
+                               aabb3f box = *i;
+                               box.MinEdge += pos;
+                               box.MaxEdge += pos;
+
+                               // Compute texture coords
+                               f32 tx1 = (i->MinEdge.X/BS)+0.5;
+                               f32 ty1 = (i->MinEdge.Y/BS)+0.5;
+                               f32 tz1 = (i->MinEdge.Z/BS)+0.5;
+                               f32 tx2 = (i->MaxEdge.X/BS)+0.5;
+                               f32 ty2 = (i->MaxEdge.Y/BS)+0.5;
+                               f32 tz2 = (i->MaxEdge.Z/BS)+0.5;
+                               f32 txc[24] = {
+                                       // up
+                                       tx1, 1-tz2, tx2, 1-tz1,
+                                       // down
+                                       tx1, tz1, tx2, tz2,
+                                       // right
+                                       tz1, 1-ty2, tz2, 1-ty1,
+                                       // left
+                                       1-tz2, 1-ty2, 1-tz1, 1-ty1,
+                                       // back
+                                       1-tx2, 1-ty2, 1-tx1, 1-ty1,
+                                       // front
+                                       tx1, 1-ty2, tx2, 1-ty1,
+                               };
+
+                               makeCuboid(&collector, box,
+                                               material_nodebox, pa_nodebox, 6,
+                                               c, txc);
+                       }
+               break;}
                }
        }
 }
index 02be64c64fd20b85fa1a5c5e2d724974abcd602f..cb6be2f2cc5dde787b3784c007740ed8727376aa 100644 (file)
@@ -163,9 +163,12 @@ void ItemSAO::step(float dtime, bool send_recommended)
                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;
@@ -404,9 +407,12 @@ void RatSAO::step(float dtime, bool send_recommended)
                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);
        m_touching_ground = moveresult.touching_ground;
        
        setBasePosition(pos_f);
@@ -652,9 +658,12 @@ void Oerkki1SAO::step(float dtime, bool send_recommended)
                m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);*/
        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 = collisionMovePrecise(&m_env->getMap(), gamedef,
-                       pos_max_d, box, dtime, pos_f, m_speed_f);
+                       pos_max_d, box, stepheight, dtime,
+                       pos_f, m_speed_f, accel_f);
        m_touching_ground = moveresult.touching_ground;
        
        // Do collision damage
@@ -899,9 +908,12 @@ void FireflySAO::step(float dtime, bool send_recommended)
                m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
        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);
        m_touching_ground = moveresult.touching_ground;
        
        setBasePosition(pos_f);
@@ -1618,16 +1630,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);
+                               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;
index a1f0fe07fd95288a7398ed496235ac52d975456d..df41e7da56d6226928bfdf4912ff66646402f587 100644 (file)
@@ -293,44 +293,40 @@ PointedThing getPointedThing(Client *client, v3f player_position,
                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();
-
        // First try to find a pointed at active object
        if(look_for_object)
        {
                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
@@ -366,196 +362,88 @@ PointedThing getPointedThing(Client *client, v3f player_position,
                if(!isPointableNode(n, client, liquids_pointable))
                        continue;
 
+               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),
+               };
+
+               std::vector<aabb3f> boxes = n.getSelectionBoxes(client->ndef());
+
                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] = {
+                       f32 d = 0.001*BS;
+                       aabb3f faceboxes[6] = {
                                // X-
-                               core::aabbox3d<f32>(
+                               aabb3f(
                                        box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
                                        box.MinEdge.X+d, box.MaxEdge.Y, box.MaxEdge.Z
                                ),
                                // X+
-                               core::aabbox3d<f32>(
+                               aabb3f(
                                        box.MaxEdge.X-d, box.MinEdge.Y, box.MinEdge.Z,
                                        box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
                                ),
                                // Y-
-                               core::aabbox3d<f32>(
+                               aabb3f(
                                        box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
                                        box.MaxEdge.X, box.MinEdge.Y+d, box.MaxEdge.Z
                                ),
                                // Y+
-                               core::aabbox3d<f32>(
+                               aabb3f(
                                        box.MinEdge.X, box.MaxEdge.Y-d, box.MinEdge.Z,
                                        box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
                                ),
                                // Z-
-                               core::aabbox3d<f32>(
+                               aabb3f(
                                        box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
                                        box.MaxEdge.X, box.MaxEdge.Y, box.MinEdge.Z+d
                                ),
                                // Z+
-                               core::aabbox3d<f32>(
+                               aabb3f(
                                        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;
+                               v3f centerpoint = faceboxes[j].getCenter();
                                f32 distance = (centerpoint - camera_position).getLength();
                                if(distance >= mindistance)
                                        continue;
-                               if(!faceboxes[i].intersectsWithLine(shootline))
+                               if(!faceboxes[j].intersectsWithLine(shootline))
                                        continue;
+
                                result.type = POINTEDTHING_NODE;
                                result.node_undersurface = np;
-                               result.node_abovesurface = np+facedirs[i];
+                               result.node_abovesurface = np+facedirs[j];
                                mindistance = distance;
-                               hilightbox = box;
-                               should_show_hilightbox = true;
-                       }
-               }
-               else if(f.selection_box.type == NODEBOX_WALLMOUNTED)
-               {
-                       v3s16 dir = n.getWallMountedDir(nodedef);
-                       v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
-                       dir_f *= BS/2 - BS/6 - BS/20;
-                       v3f cpf = npf + dir_f;
-                       f32 distance = (cpf - camera_position).getLength();
-
-                       core::aabbox3d<f32> box;
-                       
-                       // top
-                       if(dir == v3s16(0,1,0)){
-                               box = f.selection_box.wall_top;
-                       }
-                       // bottom
-                       else if(dir == v3s16(0,-1,0)){
-                               box = f.selection_box.wall_bottom;
-                       }
-                       // side
-                       else{
-                               v3f vertices[2] =
-                               {
-                                       f.selection_box.wall_side.MinEdge,
-                                       f.selection_box.wall_side.MaxEdge
-                               };
-
-                               for(s32 i=0; i<2; i++)
-                               {
-                                       if(dir == v3s16(-1,0,0))
-                                               vertices[i].rotateXZBy(0);
-                                       if(dir == v3s16(1,0,0))
-                                               vertices[i].rotateXZBy(180);
-                                       if(dir == v3s16(0,0,-1))
-                                               vertices[i].rotateXZBy(90);
-                                       if(dir == v3s16(0,0,1))
-                                               vertices[i].rotateXZBy(-90);
-                               }
 
-                               box = core::aabbox3d<f32>(vertices[0]);
-                               box.addInternalPoint(vertices[1]);
-                       }
-
-                       box.MinEdge += npf;
-                       box.MaxEdge += npf;
-                       
-                       if(distance < mindistance)
-                       {
-                               if(box.intersectsWithLine(shootline))
+                               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;
@@ -1112,9 +1000,6 @@ void the_game(
                else
                        hotbar_imagesize = 64;
                
-               // Hilight boxes collected during the loop and displayed
-               core::list< core::aabbox3d<f32> > hilightboxes;
-               
                // Info text
                std::wstring infotext;
 
@@ -1821,8 +1706,8 @@ void the_game(
                core::line3d<f32> shootline(camera_position,
                                camera_position + camera_direction * BS * (d+1));
 
-               core::aabbox3d<f32> hilightbox;
-               bool should_show_hilightbox = false;
+               // Hilight boxes collected during the loop and displayed
+               std::vector<aabb3f> hilightboxes;
                ClientActiveObject *selected_object = NULL;
 
                PointedThing pointed = getPointedThing(
@@ -1831,7 +1716,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)
@@ -1840,12 +1725,6 @@ void the_game(
                        //dstream<<"Pointing at "<<pointed.dump()<<std::endl;
                }
 
-               /*
-                       Visualize selection
-               */
-               if(should_show_hilightbox)
-                       hilightboxes.push_back(hilightbox);
-
                /*
                        Stop digging when
                        - releasing left mouse button
@@ -2470,9 +2349,10 @@ void the_game(
 
                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="
index 6cb9671b51b998671a920cdfa24f801f1677043b..3960091739eee6d01c28bf2944d8a43fe2152474 100644 (file)
@@ -132,7 +132,98 @@ v3s16 MapNode::getWallMountedDir(INodeDefManager *nodemgr) const
        }
 }
 
+static std::vector<aabb3f> transformNodeBox(const MapNode &n,
+               const NodeBox &nodebox, INodeDefManager *nodemgr)
+{
+       std::vector<aabb3f> boxes;
+       if(nodebox.type == NODEBOX_FIXED)
+       {
+               const std::vector<aabb3f> &fixed = nodebox.fixed;
+               int facedir = n.getFaceDir(nodemgr);
+               for(std::vector<aabb3f>::const_iterator
+                               i = fixed.begin();
+                               i != fixed.end(); i++)
+               {
+                       aabb3f box = *i;
+                       if(facedir == 1)
+                       {
+                               box.MinEdge.rotateXZBy(-90);
+                               box.MaxEdge.rotateXZBy(-90);
+                               box.repair();
+                       }
+                       else if(facedir == 2)
+                       {
+                               box.MinEdge.rotateXZBy(180);
+                               box.MaxEdge.rotateXZBy(180);
+                               box.repair();
+                       }
+                       else if(facedir == 3)
+                       {
+                               box.MinEdge.rotateXZBy(90);
+                               box.MaxEdge.rotateXZBy(90);
+                               box.repair();
+                       }
+                       boxes.push_back(box);
+               }
+       }
+       else if(nodebox.type == NODEBOX_WALLMOUNTED)
+       {
+               v3s16 dir = n.getWallMountedDir(nodemgr);
+
+               // top
+               if(dir == v3s16(0,1,0))
+               {
+                       boxes.push_back(nodebox.wall_top);
+               }
+               // bottom
+               else if(dir == v3s16(0,-1,0))
+               {
+                       boxes.push_back(nodebox.wall_bottom);
+               }
+               // side
+               else
+               {
+                       v3f vertices[2] =
+                       {
+                               nodebox.wall_side.MinEdge,
+                               nodebox.wall_side.MaxEdge
+                       };
+
+                       for(s32 i=0; i<2; i++)
+                       {
+                               if(dir == v3s16(-1,0,0))
+                                       vertices[i].rotateXZBy(0);
+                               if(dir == v3s16(1,0,0))
+                                       vertices[i].rotateXZBy(180);
+                               if(dir == v3s16(0,0,-1))
+                                       vertices[i].rotateXZBy(90);
+                               if(dir == v3s16(0,0,1))
+                                       vertices[i].rotateXZBy(-90);
+                       }
+
+                       aabb3f box = aabb3f(vertices[0]);
+                       box.addInternalPoint(vertices[1]);
+                       boxes.push_back(box);
+               }
+       }
+       else // NODEBOX_REGULAR
+       {
+               boxes.push_back(aabb3f(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2));
+       }
+       return boxes;
+}
+
+std::vector<aabb3f> MapNode::getNodeBoxes(INodeDefManager *nodemgr) const
+{
+       const ContentFeatures &f = nodemgr->get(*this);
+       return transformNodeBox(*this, f.node_box, nodemgr);
+}
 
+std::vector<aabb3f> MapNode::getSelectionBoxes(INodeDefManager *nodemgr) const
+{
+       const ContentFeatures &f = nodemgr->get(*this);
+       return transformNodeBox(*this, f.selection_box, nodemgr);
+}
 
 u32 MapNode::serializedLength(u8 version)
 {
index 5e066604bfc00ddc02bd23b7cd4b9739effb399d..d14db0dc560c08a632237a13c8feaa47a61efab6 100644 (file)
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "irrlichttypes.h"
 #include "light.h"
+#include <vector>
 
 class INodeDefManager;
 
@@ -195,6 +196,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<aabb3f> getNodeBoxes(INodeDefManager *nodemgr) const;
+
+       /*
+               Gets list of selection boxes
+       */
+       std::vector<aabb3f> getSelectionBoxes(INodeDefManager *nodemgr) const;
+
        /*
                Serialization functions
        */
index 0c2793a0ea04a456a58a1bfea18511f1f6add4b8..86be7c9114aaab72ae4253b4eda9899c9dc66574 100644 (file)
@@ -32,34 +32,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<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);
+       }
 }
 
 /*
@@ -143,6 +183,7 @@ void ContentFeatures::reset()
        liquid_viscosity = 0;
        light_source = 0;
        damage_per_second = 0;
+       node_box = NodeBox();
        selection_box = NodeBox();
        material = MaterialProperties();
        // Make unknown blocks diggable
@@ -154,7 +195,7 @@ void ContentFeatures::reset()
 
 void ContentFeatures::serialize(std::ostream &os)
 {
-       writeU8(os, 1); // version
+       writeU8(os, 2); // version
        os<<serializeString(name);
        writeU8(os, drawtype);
        writeF1000(os, visual_scale);
@@ -187,6 +228,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);
        material.serialize(os);
        writeU8(os, legacy_facedir_simple);
@@ -196,7 +238,7 @@ void ContentFeatures::serialize(std::ostream &os)
 void ContentFeatures::deSerialize(std::istream &is)
 {
        int version = readU8(is);
-       if(version != 1)
+       if(version != 2)
                throw SerializationError("unsupported ContentFeatures version");
        name = deSerializeString(is);
        drawtype = (enum NodeDrawType)readU8(is);
@@ -232,6 +274,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);
        material.deSerialize(is);
        legacy_facedir_simple = readU8(is);
@@ -509,6 +552,7 @@ public:
                        case NDT_PLANTLIKE:
                        case NDT_FENCELIKE:
                        case NDT_RAILLIKE:
+                       case NDT_NODEBOX:
                                f->solidness = 0;
                                break;
                        }
index 9524385cf075d6e8618b0ac65f6f83863fff7fc4..1645b3b06f30fc6bf7171b67eb2c2fd0fbb4648d 100644 (file)
@@ -62,7 +62,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)
 };
 
@@ -71,22 +71,16 @@ struct NodeBox
        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);
 };
@@ -122,6 +116,7 @@ enum NodeDrawType
        NDT_PLANTLIKE,
        NDT_FENCELIKE,
        NDT_RAILLIKE,
+       NDT_NODEBOX,
 };
 
 #define CF_SPECIAL_COUNT 2
@@ -193,6 +188,7 @@ struct ContentFeatures
        // Amount of light the node emits
        u8 light_source;
        u32 damage_per_second;
+       NodeBox node_box;
        NodeBox selection_box;
        MaterialProperties material;
        // Compatibility with old maps
index 6506c43c37293ef9234b6242003ade5cfd054f42..47f4f015b6538b4d360605ac889c261e75dae040 100644 (file)
@@ -205,23 +205,14 @@ 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=("<<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 free_move = g_settings->getBool("free_move");
        if(free_move)
        {
+               position += m_speed * dtime;
                setPosition(position);
                return;
        }
@@ -230,9 +221,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)
        */
@@ -282,30 +270,12 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                is_climbing = false;
        }
 
-       /*
-               Collision uncertainty radius
-               Make it a bit larger than the maximum distance of movement
-       */
-       //f32 d = pos_max_d * 1.1;
-       // A fairly large value in here makes moving smoother
-       f32 d = 0.15*BS;
-
-       // This should always apply, otherwise there are glitches
-       assert(d > pos_max_d);
-
        float player_radius = BS*0.35;
        float player_height = BS*1.7;
        
        // 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
@@ -321,23 +291,8 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                if(position.Y < min_y)
                {
                        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);
-                               }
-                       }*/
                }
        }
 
@@ -345,176 +300,31 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                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.
        */
-       touching_ground = false;
+       touching_ground = result.touching_ground;
 
-       /*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
+       bool standing_on_unloaded = result.standing_on_unloaded;
 
        /*
                Check the nodes under the player to see from which node the
index a064cd688a0b318cdac5c92fb5263c3389a333d6..eea93001fb8737337d16a60006317baa02656b46 100644 (file)
@@ -380,6 +380,7 @@ struct EnumString es_DrawType[] =
        {NDT_PLANTLIKE, "plantlike"},
        {NDT_FENCELIKE, "fencelike"},
        {NDT_RAILLIKE, "raillike"},
+       {NDT_NODEBOX, "nodebox"},
        {0, NULL},
 };
 
@@ -586,32 +587,93 @@ static video::SColor readARGB8(lua_State *L, int index)
        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;
 }
 
+static std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
+{
+       std::vector<aabb3f> boxes;
+       if(lua_istable(L, index)){
+               int n = lua_objlen(L, index);
+
+               // Check if it's a single box or a list of boxes
+               bool possibly_single_box = (n == 6);
+               for(int i = 1; i <= n && possibly_single_box; i++){
+                       lua_rawgeti(L, index, i);
+                       if(!lua_isnumber(L, -1))
+                               possibly_single_box = false;
+                       lua_pop(L, 1);
+               }
+
+               if(possibly_single_box){
+                       // Read a single box
+                       boxes.push_back(read_aabb3f(L, index, scale));
+               } else {
+                       // Read a list of boxes
+                       for(int i = 1; i <= n; i++)
+                       {
+                               lua_rawgeti(L, index, i);
+                               boxes.push_back(read_aabb3f(L, -1, scale));
+                               lua_pop(L, 1);
+                       }
+               }
+       }
+       return boxes;
+}
+
+static NodeBox read_nodebox(lua_State *L, int index)
+{
+       NodeBox nodebox;
+       if(lua_istable(L, -1)){
+               nodebox.type = (NodeBoxType)getenumfield(L, index, "type",
+                               es_NodeBoxType, NODEBOX_REGULAR);
+
+               lua_getfield(L, index, "fixed");
+               if(lua_istable(L, -1))
+                       nodebox.fixed = read_aabb3f_vector(L, -1, BS);
+               lua_pop(L, 1);
+
+               lua_getfield(L, index, "wall_top");
+               if(lua_istable(L, -1))
+                       nodebox.wall_top = read_aabb3f(L, -1, BS);
+               lua_pop(L, 1);
+
+               lua_getfield(L, index, "wall_bottom");
+               if(lua_istable(L, -1))
+                       nodebox.wall_bottom = read_aabb3f(L, -1, BS);
+               lua_pop(L, 1);
+
+               lua_getfield(L, index, "wall_side");
+               if(lua_istable(L, -1))
+                       nodebox.wall_side = read_aabb3f(L, -1, BS);
+               lua_pop(L, 1);
+       }
+       return nodebox;
+}
+
 /*
        MaterialProperties
 */
@@ -931,33 +993,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);
+
        lua_getfield(L, index, "material");
        if(lua_istable(L, -1)){
                f.material = read_material_properties(L, -1);
@@ -4274,7 +4319,7 @@ void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
 
        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);
index 4226df544befcfb706ebdcf52e7571e09743bd46..8bfce386c2f7efc3d3a6d3b31a027383a0716e65 100644 (file)
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "utility.h"
 #include "serialization.h"
 #include "voxel.h"
+#include "collision.h"
 #include <sstream>
 #include "porting.h"
 #include "content_mapnode.h"
@@ -816,6 +817,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()
@@ -1279,6 +1427,7 @@ void run_tests()
        TESTPARAMS(TestVoxelManipulator, ndef);
        //TEST(TestMapBlock);
        //TEST(TestMapSector);
+       TEST(TestCollision);
        if(INTERNET_SIMULATOR == false){
                TEST(TestSocket);
                dout_con<<"=== BEGIN RUNNING UNIT TESTS FOR CONNECTION ==="<<std::endl;
index c0d8914b09b6c09d288dc045f8caa9271588d0da..6d180dae32a7b3b77fc57459c2e119291cbfa2b0 100644 (file)
@@ -58,6 +58,14 @@ struct AtlasPointer
        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,