Expose getPointedThing to Lua
authorDániel Juhász <juhdanad@gmail.com>
Sat, 23 Jul 2016 19:11:20 +0000 (21:11 +0200)
committerparamat <mat.gregory@virginmedia.com>
Fri, 7 Jul 2017 21:28:23 +0000 (22:28 +0100)
This commit introduces Raycast, a Lua user object, which can be
used to perform a raycast on the map. The ray is continuable, so one can
also get hidden nodes (for example to see trough glass).

25 files changed:
doc/lua_api.txt
src/CMakeLists.txt
src/activeobject.h
src/clientenvironment.cpp
src/clientenvironment.h
src/clientobject.h
src/content_cao.cpp
src/content_cao.h
src/content_sao.cpp
src/content_sao.h
src/environment.cpp
src/environment.h
src/game.cpp
src/raycast.cpp
src/raycast.h
src/script/cpp_api/s_item.h
src/script/lua_api/l_env.cpp
src/script/lua_api/l_env.h
src/script/scripting_server.cpp
src/serverenvironment.cpp
src/serverenvironment.h
src/util/pointedthing.cpp
src/util/pointedthing.h
src/voxelalgorithms.cpp
src/voxelalgorithms.h

index 63d090ebe30b0cbcae02ad0c0a36814a26843707..56f8dbaaef13f427240f9011766752283bff78cb 100644 (file)
@@ -2557,6 +2557,12 @@ and `minetest.auth_reload` call the authetification handler.
     * `pos2`: Second position
     * `stepsize`: smaller gives more accurate results but requires more computing
       time. Default is `1`.
+* `minetest.raycast(pos1, pos2, objects, liquids)`: returns `Raycast`
+    * Creates a `Raycast` object.
+    * `pos1`: start of the ray
+    * `pos2`: end of the ray
+    * `objects` : if false, only nodes will be returned. Default is `true`.
+    * `liquids' : if false, liquid nodes won't be returned. Default is `false`.
 * `minetest.find_path(pos1,pos2,searchdistance,max_jump,max_drop,algorithm)`
     * returns table containing path
     * returns a table of 3D points representing a path from `pos1` to `pos2` or `nil`
@@ -3755,6 +3761,26 @@ It can be created via `Settings(filename)`.
     * Writes changes to file.
 * `to_table()`: returns `{[key1]=value1,...}`
 
+### `Raycast`
+A raycast on the map. It works with selection boxes.
+Can be used as an iterator in a for loop.
+
+The map is loaded as the ray advances. If the
+map is modified after the `Raycast` is created,
+the changes may or may not have an effect on
+the object.
+
+It can be created via `Raycast(pos1, pos2, objects, liquids)` or
+`minetest.raycast(pos1, pos2, objects, liquids)` where:
+    * `pos1`: start of the ray
+    * `pos2`: end of the ray
+    * `objects` : if false, only nodes will be returned. Default is true.
+    * `liquids' : if false, liquid nodes won't be returned. Default is false.
+
+#### Methods
+* `next()`: returns a `pointed_thing`
+    * Returns the next thing pointed by the ray or nil.
+
 Mapgen objects
 --------------
 A mapgen object is a construct used in map generation. Mapgen objects can be used
index 75a835b897d2a9c1a59bd3eff7b5b3566859fd28..a7e26af7da77c4c91daef5aaf166919047b75ec5 100644 (file)
@@ -428,9 +428,9 @@ set(common_SRCS
        porting.cpp
        profiler.cpp
        quicktune.cpp
+       raycast.cpp
        reflowscan.cpp
        remoteplayer.cpp
-       raycast.cpp
        rollback.cpp
        rollback_interface.cpp
        serialization.cpp
index f349ddef35742cc5fb35b0ce023ec313dd63437a..4796e168c42fa680b7c76424f35903f2de2d240b 100644 (file)
@@ -65,7 +65,7 @@ public:
        {
        }
 
-       u16 getId()
+       u16 getId() const
        {
                return m_id;
        }
@@ -76,7 +76,28 @@ public:
        }
 
        virtual ActiveObjectType getType() const = 0;
+
+
+       /*!
+        * Returns the collision box of the object.
+        * This box is translated by the object's
+        * location.
+        * The box's coordinates are world coordinates.
+        * @returns true if the object has a collision box.
+        */
        virtual bool getCollisionBox(aabb3f *toset) const = 0;
+
+
+       /*!
+        * Returns the selection box of the object.
+        * This box is not translated when the
+        * object moves.
+        * The box's coordinates are world coordinates.
+        * @returns true if the object has a selection box.
+        */
+       virtual bool getSelectionBox(aabb3f *toset) const = 0;
+
+
        virtual bool collideWithObjects() const = 0;
 protected:
        u16 m_id; // 0 is invalid, "no id"
index 791b61531a0c2be5b45babda220561043e7ca798..9abb6a23d6975d796539f58724e4a35d4dc10dc6 100644 (file)
@@ -604,240 +604,31 @@ ClientEnvEvent ClientEnvironment::getClientEnvEvent()
        return event;
 }
 
-ClientActiveObject * ClientEnvironment::getSelectedActiveObject(
-       const core::line3d<f32> &shootline_on_map, v3f *intersection_point,
-       v3s16 *intersection_normal)
+void ClientEnvironment::getSelectedActiveObjects(
+       const core::line3d<f32> &shootline_on_map,
+       std::vector<PointedThing> &objects)
 {
-       std::vector<DistanceSortedActiveObject> objects;
+       std::vector<DistanceSortedActiveObject> allObjects;
        getActiveObjects(shootline_on_map.start,
-               shootline_on_map.getLength() + 3, objects);
+               shootline_on_map.getLength() + 10.0f, allObjects);
        const v3f line_vector = shootline_on_map.getVector();
 
-       // Sort them.
-       // After this, the closest object is the first in the array.
-       std::sort(objects.begin(), objects.end());
-
-       /* Because objects can have different nodebox sizes,
-        * the object whose center is the nearest isn't necessarily
-        * the closest one. If an object is found, don't stop
-        * immediately. */
-
-       f32 d_min = shootline_on_map.getLength();
-       ClientActiveObject *nearest_obj = NULL;
-       for (u32 i = 0; i < objects.size(); i++) {
-               ClientActiveObject *obj = objects[i].obj;
-
-               aabb3f *selection_box = obj->getSelectionBox();
-               if (selection_box == NULL)
+       for (u32 i = 0; i < allObjects.size(); i++) {
+               ClientActiveObject *obj = allObjects[i].obj;
+               aabb3f selection_box;
+               if (!obj->getSelectionBox(&selection_box))
                        continue;
-
                v3f pos = obj->getPosition();
-
-               aabb3f offsetted_box(selection_box->MinEdge + pos,
-                       selection_box->MaxEdge + pos);
-
-               if (offsetted_box.getCenter().getDistanceFrom(
-                       shootline_on_map.start) > d_min + 9.6f*BS) {
-                       // Probably there is no active object that has bigger nodebox than
-                       // (-5.5,-5.5,-5.5,5.5,5.5,5.5)
-                       // 9.6 > 5.5*sqrt(3)
-                       break;
-               }
+               aabb3f offsetted_box(selection_box.MinEdge + pos,
+                       selection_box.MaxEdge + pos);
 
                v3f current_intersection;
                v3s16 current_normal;
                if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
-                       &current_intersection, &current_normal)) {
-                       f32 d_current = current_intersection.getDistanceFrom(
-                               shootline_on_map.start);
-                       if (d_current <= d_min) {
-                               d_min = d_current;
-                               nearest_obj = obj;
-                               *intersection_point = current_intersection;
-                               *intersection_normal = current_normal;
-                       }
-               }
-       }
-
-       return nearest_obj;
-}
-
-/*
-       Check if a node is pointable
-*/
-static inline bool isPointableNode(const MapNode &n,
-       INodeDefManager *ndef, bool liquids_pointable)
-{
-       const ContentFeatures &features = ndef->get(n);
-       return features.pointable ||
-               (liquids_pointable && features.isLiquid());
-}
-
-PointedThing ClientEnvironment::getPointedThing(
-       core::line3d<f32> shootline,
-       bool liquids_pointable,
-       bool look_for_object)
-{
-       PointedThing result;
-
-       INodeDefManager *nodedef = m_map->getNodeDefManager();
-
-       core::aabbox3d<s16> maximal_exceed = nodedef->getSelectionBoxIntUnion();
-       // The code needs to search these nodes
-       core::aabbox3d<s16> search_range(-maximal_exceed.MaxEdge,
-               -maximal_exceed.MinEdge);
-       // If a node is found, there might be a larger node behind.
-       // To find it, we have to go further.
-       s16 maximal_overcheck =
-               std::max(abs(search_range.MinEdge.X), abs(search_range.MaxEdge.X))
-                       + std::max(abs(search_range.MinEdge.Y), abs(search_range.MaxEdge.Y))
-                       + std::max(abs(search_range.MinEdge.Z), abs(search_range.MaxEdge.Z));
-
-       const v3f original_vector = shootline.getVector();
-       const f32 original_length = original_vector.getLength();
-
-       f32 min_distance = original_length;
-
-       // First try to find an active object
-       if (look_for_object) {
-               ClientActiveObject *selected_object = getSelectedActiveObject(
-                       shootline, &result.intersection_point,
-                       &result.intersection_normal);
-
-               if (selected_object != NULL) {
-                       min_distance =
-                               (result.intersection_point - shootline.start).getLength();
-
-                       result.type = POINTEDTHING_OBJECT;
-                       result.object_id = selected_object->getId();
+                               &current_intersection, &current_normal)) {
+                       objects.push_back(PointedThing(
+                               (s16) obj->getId(), current_intersection, current_normal,
+                               (current_intersection - shootline_on_map.start).getLengthSQ()));
                }
        }
-
-       // Reduce shootline
-       if (original_length > 0) {
-               shootline.end = shootline.start
-                       + shootline.getVector() / original_length * min_distance;
-       }
-
-       // Try to find a node that is closer than the selected active
-       // object (if it exists).
-
-       voxalgo::VoxelLineIterator iterator(shootline.start / BS,
-               shootline.getVector() / BS);
-       v3s16 oldnode = iterator.m_current_node_pos;
-       // Indicates that a node was found.
-       bool is_node_found = false;
-       // If a node is found, it is possible that there's a node
-       // behind it with a large nodebox, so continue the search.
-       u16 node_foundcounter = 0;
-       // If a node is found, this is the center of the
-       // first nodebox the shootline meets.
-       v3f found_boxcenter(0, 0, 0);
-       // The untested nodes are in this range.
-       core::aabbox3d<s16> new_nodes;
-       while (true) {
-               // Test the nodes around the current node in search_range.
-               new_nodes = search_range;
-               new_nodes.MinEdge += iterator.m_current_node_pos;
-               new_nodes.MaxEdge += iterator.m_current_node_pos;
-
-               // Only check new nodes
-               v3s16 delta = iterator.m_current_node_pos - oldnode;
-               if (delta.X > 0)
-                       new_nodes.MinEdge.X = new_nodes.MaxEdge.X;
-               else if (delta.X < 0)
-                       new_nodes.MaxEdge.X = new_nodes.MinEdge.X;
-               else if (delta.Y > 0)
-                       new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y;
-               else if (delta.Y < 0)
-                       new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y;
-               else if (delta.Z > 0)
-                       new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z;
-               else if (delta.Z < 0)
-                       new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z;
-
-               // For each untested node
-               for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) {
-                       for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) {
-                               for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) {
-                                       MapNode n;
-                                       v3s16 np(x, y, z);
-                                       bool is_valid_position;
-
-                                       n = m_map->getNodeNoEx(np, &is_valid_position);
-                                       if (!(is_valid_position &&
-                                               isPointableNode(n, nodedef, liquids_pointable))) {
-                                               continue;
-                                       }
-                                       std::vector<aabb3f> boxes;
-                                       n.getSelectionBoxes(nodedef, &boxes,
-                                               n.getNeighbors(np, m_map));
-
-                                       v3f npf = intToFloat(np, BS);
-                                       for (std::vector<aabb3f>::const_iterator i = boxes.begin();
-                                               i != boxes.end(); ++i) {
-                                               aabb3f box = *i;
-                                               box.MinEdge += npf;
-                                               box.MaxEdge += npf;
-                                               v3f intersection_point;
-                                               v3s16 intersection_normal;
-                                               if (!boxLineCollision(box, shootline.start, shootline.getVector(),
-                                                       &intersection_point, &intersection_normal)) {
-                                                       continue;
-                                               }
-                                               f32 distance = (intersection_point - shootline.start).getLength();
-                                               if (distance >= min_distance) {
-                                                       continue;
-                                               }
-                                               result.type = POINTEDTHING_NODE;
-                                               result.node_undersurface = np;
-                                               result.intersection_point = intersection_point;
-                                               result.intersection_normal = intersection_normal;
-                                               found_boxcenter = box.getCenter();
-                                               min_distance = distance;
-                                               is_node_found = true;
-                                       }
-                               }
-                       }
-               }
-               if (is_node_found) {
-                       node_foundcounter++;
-                       if (node_foundcounter > maximal_overcheck) {
-                               break;
-                       }
-               }
-               // Next node
-               if (iterator.hasNext()) {
-                       oldnode = iterator.m_current_node_pos;
-                       iterator.next();
-               } else {
-                       break;
-               }
-       }
-
-       if (is_node_found) {
-               // Set undersurface and abovesurface nodes
-               f32 d = 0.002 * BS;
-               v3f fake_intersection = result.intersection_point;
-               // Move intersection towards its source block.
-               if (fake_intersection.X < found_boxcenter.X)
-                       fake_intersection.X += d;
-               else
-                       fake_intersection.X -= d;
-
-               if (fake_intersection.Y < found_boxcenter.Y)
-                       fake_intersection.Y += d;
-               else
-                       fake_intersection.Y -= d;
-
-               if (fake_intersection.Z < found_boxcenter.Z)
-                       fake_intersection.Z += d;
-               else
-                       fake_intersection.Z -= d;
-
-               result.node_real_undersurface = floatToInt(fake_intersection, BS);
-               result.node_abovesurface = result.node_real_undersurface
-                       + result.intersection_normal;
-       }
-       return result;
 }
index 05ec1908ff10f2f24b8b7a85d498471503b4674d..070ff95fbae37cff588f806de2d25730e11da1d2 100644 (file)
@@ -30,7 +30,6 @@ class ClientScripting;
 class ClientActiveObject;
 class GenericCAO;
 class LocalPlayer;
-struct PointedThing;
 
 /*
        The client-side environment.
@@ -125,44 +124,15 @@ public:
                std::vector<DistanceSortedActiveObject> &dest);
 
        bool hasClientEnvEvents() const { return !m_client_event_queue.empty(); }
+
        // Get event from queue. If queue is empty, it triggers an assertion failure.
        ClientEnvEvent getClientEnvEvent();
 
-       /*!
-        * Gets closest object pointed by the shootline.
-        * Returns NULL if not found.
-        *
-        * \param[in]  shootline_on_map    the shootline for
-        * the test in world coordinates
-        * \param[out] intersection_point  the first point where
-        * the shootline meets the object. Valid only if
-        * not NULL is returned.
-        * \param[out] intersection_normal the normal vector of
-        * the intersection, pointing outwards. Zero vector if
-        * the shootline starts in an active object.
-        * Valid only if not NULL is returned.
-        */
-       ClientActiveObject * getSelectedActiveObject(
+       virtual void getSelectedActiveObjects(
                const core::line3d<f32> &shootline_on_map,
-               v3f *intersection_point,
-               v3s16 *intersection_normal
+               std::vector<PointedThing> &objects
        );
 
-       /*!
-        * Performs a raycast on the world.
-        * Returns the first thing the shootline meets.
-        *
-        * @param[in]  shootline         the shootline, starting from
-        * the camera position. This also gives the maximal distance
-        * of the search.
-        * @param[in]  liquids_pointable if false, liquids are ignored
-        * @param[in]  look_for_object   if false, objects are ignored
-        */
-       PointedThing getPointedThing(
-               core::line3d<f32> shootline,
-               bool liquids_pointable,
-               bool look_for_object);
-
        u16 attachement_parent_ids[USHRT_MAX + 1];
 
        const std::list<std::string> &getPlayerNames() { return m_player_names; }
index f8075d65a84a40ea206a9e05bc64b81f6cf287c2..eeed2516d1ed5b1fa86e385457d65538a7b2a9a7 100644 (file)
@@ -44,8 +44,8 @@ public:
        virtual void updateLight(u8 light_at_pos){}
        virtual void updateLightNoCheck(u8 light_at_pos){}
        virtual v3s16 getLightPosition(){return v3s16(0,0,0);}
-       virtual aabb3f *getSelectionBox() { return NULL; }
        virtual bool getCollisionBox(aabb3f *toset) const { return false; }
+       virtual bool getSelectionBox(aabb3f *toset) const { return false; }
        virtual bool collideWithObjects() const { return false; }
        virtual v3f getPosition(){ return v3f(0,0,0); }
        virtual float getYaw() const { return 0; }
index aff143bf2ddc95529485dfe73e0b9c7c466cb379..49c2049eb0df96d857c4cf6b69688d7883a0eb92 100644 (file)
@@ -283,8 +283,14 @@ public:
 
        void initialize(const std::string &data);
 
-       aabb3f *getSelectionBox()
-               {return &m_selection_box;}
+
+       virtual bool getSelectionBox(aabb3f *toset) const
+       {
+               *toset = m_selection_box;
+               return true;
+       }
+
+
        v3f getPosition()
                {return m_position;}
        inline float getYaw() const
@@ -605,11 +611,14 @@ GenericCAO::~GenericCAO()
        removeFromScene(true);
 }
 
-aabb3f *GenericCAO::getSelectionBox()
+bool GenericCAO::getSelectionBox(aabb3f *toset) const
 {
-       if(!m_prop.is_visible || !m_is_visible || m_is_local_player || getParent() != NULL)
-               return NULL;
-       return &m_selection_box;
+       if (!m_prop.is_visible || !m_is_visible || m_is_local_player
+                       || getParent() != NULL){
+               return false;
+       }
+       *toset = m_selection_box;
+       return true;
 }
 
 v3f GenericCAO::getPosition()
@@ -658,7 +667,7 @@ void GenericCAO::setAttachments()
        updateAttachments();
 }
 
-ClientActiveObject* GenericCAO::getParent()
+ClientActiveObject* GenericCAO::getParent() const
 {
        ClientActiveObject *obj = NULL;
 
index 526f10ea3b2f1274436f1f3fb4fc63b046663486..d6d5deac8fe5884599d8062d8409306210324f8c 100644 (file)
@@ -129,13 +129,13 @@ public:
 
        void processInitData(const std::string &data);
 
-       ClientActiveObject *getParent();
+       ClientActiveObject *getParent() const;
 
        bool getCollisionBox(aabb3f *toset) const;
 
        bool collideWithObjects() const;
 
-       aabb3f *getSelectionBox();
+       virtual bool getSelectionBox(aabb3f *toset) const;
 
        v3f getPosition();
        inline float getYaw() const
index c6c00768e0cdc3e80c04dcf9fecf14a87342c29c..5bbbd154d4a195ba3075a9661fb6749c2c9ced59 100644 (file)
@@ -91,6 +91,9 @@ public:
        }
 
        bool getCollisionBox(aabb3f *toset) const { return false; }
+
+       virtual bool getSelectionBox(aabb3f *toset) const { return false; }
+
        bool collideWithObjects() const { return false; }
 
 private:
@@ -746,6 +749,18 @@ bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
        return false;
 }
 
+bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
+{
+       if (!m_prop.is_visible) {
+               return false;
+       }
+
+       toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
+       toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
+
+       return true;
+}
+
 bool LuaEntitySAO::collideWithObjects() const
 {
        return m_prop.collideWithObjects;
@@ -1405,3 +1420,14 @@ bool PlayerSAO::getCollisionBox(aabb3f *toset) const
        toset->MaxEdge += m_base_position;
        return true;
 }
+
+bool PlayerSAO::getSelectionBox(aabb3f *toset) const
+{
+       if (!m_prop.is_visible) {
+               return false;
+       }
+
+       getCollisionBox(toset);
+
+       return true;
+}
index 0a1ae5ecfedd56ec9e9896b61d4bde196c4df90a..a986acab046f9b698862b05f117e713997bd44b4 100644 (file)
@@ -127,6 +127,7 @@ public:
                        bool select_horiz_by_yawpitch);
        std::string getName();
        bool getCollisionBox(aabb3f *toset) const;
+       bool getSelectionBox(aabb3f *toset) const;
        bool collideWithObjects() const;
 private:
        std::string getPropertyPacket();
@@ -357,6 +358,7 @@ public:
        }
 
        bool getCollisionBox(aabb3f *toset) const;
+       bool getSelectionBox(aabb3f *toset) const;
        bool collideWithObjects() const { return true; }
 
        void finalize(RemotePlayer *player, const std::set<std::string> &privs);
index 904bab6f31a0fbd1f9cdc7bed3d8cee38ef1537f..e7ab626b48847cf5a5bbb8ba236668531016cd96 100644 (file)
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <fstream>
 #include "environment.h"
 #include "collision.h"
+#include "raycast.h"
 #include "serverobject.h"
 #include "scripting_server.h"
 #include "server.h"
@@ -83,6 +84,179 @@ float Environment::getTimeOfDayF()
        return m_time_of_day_f;
 }
 
+/*
+       Check if a node is pointable
+*/
+inline static bool isPointableNode(const MapNode &n,
+                           INodeDefManager *nodedef , bool liquids_pointable)
+{
+       const ContentFeatures &features = nodedef->get(n);
+       return features.pointable ||
+              (liquids_pointable && features.isLiquid());
+}
+
+void Environment::continueRaycast(RaycastState *state, PointedThing *result)
+{
+       INodeDefManager *nodedef = getMap().getNodeDefManager();
+       if (state->m_initialization_needed) {
+               // Add objects
+               if (state->m_objects_pointable) {
+                       std::vector<PointedThing> found;
+                       getSelectedActiveObjects(state->m_shootline, found);
+                       for (std::vector<PointedThing>::iterator pointed = found.begin();
+                                       pointed != found.end(); ++pointed) {
+                               state->m_found.push(*pointed);
+                       }
+               }
+               // Set search range
+               core::aabbox3d<s16> maximal_exceed = nodedef->getSelectionBoxIntUnion();
+               state->m_search_range.MinEdge = -maximal_exceed.MaxEdge;
+               state->m_search_range.MaxEdge = -maximal_exceed.MinEdge;
+               // Setting is done
+               state->m_initialization_needed = false;
+       }
+
+       // The index of the first pointed thing that was not returned
+       // before. The last index which needs to be tested.
+       s16 lastIndex = state->m_iterator.m_last_index;
+       if (!state->m_found.empty()) {
+               lastIndex = state->m_iterator.getIndex(
+                       floatToInt(state->m_found.top().intersection_point, BS));
+       }
+
+       Map &map = getMap();
+       // If a node is found, this is the center of the
+       // first nodebox the shootline meets.
+       v3f found_boxcenter(0, 0, 0);
+       // The untested nodes are in this range.
+       core::aabbox3d<s16> new_nodes;
+       while (state->m_iterator.m_current_index <= lastIndex) {
+               // Test the nodes around the current node in search_range.
+               new_nodes = state->m_search_range;
+               new_nodes.MinEdge += state->m_iterator.m_current_node_pos;
+               new_nodes.MaxEdge += state->m_iterator.m_current_node_pos;
+
+               // Only check new nodes
+               v3s16 delta = state->m_iterator.m_current_node_pos
+                       - state->m_previous_node;
+               if (delta.X > 0) {
+                       new_nodes.MinEdge.X = new_nodes.MaxEdge.X;
+               } else if (delta.X < 0) {
+                       new_nodes.MaxEdge.X = new_nodes.MinEdge.X;
+               } else if (delta.Y > 0) {
+                       new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y;
+               } else if (delta.Y < 0) {
+                       new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y;
+               } else if (delta.Z > 0) {
+                       new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z;
+               } else if (delta.Z < 0) {
+                       new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z;
+               }
+
+               // For each untested node
+               for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++)
+               for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++)
+               for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) {
+                       MapNode n;
+                       v3s16 np(x, y, z);
+                       bool is_valid_position;
+
+                       n = map.getNodeNoEx(np, &is_valid_position);
+                       if (!(is_valid_position && isPointableNode(n, nodedef,
+                                       state->m_liquids_pointable))) {
+                               continue;
+                       }
+
+                       PointedThing result;
+
+                       std::vector<aabb3f> boxes;
+                       n.getSelectionBoxes(nodedef, &boxes,
+                               n.getNeighbors(np, &map));
+
+                       // Is there a collision with a selection box?
+                       bool is_colliding = false;
+                       // Minimal distance of all collisions
+                       float min_distance_sq = 10000000;
+
+                       v3f npf = intToFloat(np, BS);
+                       for (std::vector<aabb3f>::const_iterator i = boxes.begin();
+                                       i != boxes.end(); ++i) {
+                               // Get current collision box
+                               aabb3f box = *i;
+                               box.MinEdge += npf;
+                               box.MaxEdge += npf;
+
+                               v3f intersection_point;
+                               v3s16 intersection_normal;
+                               if (!boxLineCollision(box, state->m_shootline.start,
+                                               state->m_shootline.getVector(), &intersection_point,
+                                               &intersection_normal))
+                                       continue;
+
+                               f32 distanceSq = (intersection_point
+                                       - state->m_shootline.start).getLengthSQ();
+                               // If this is the nearest collision, save it
+                               if (min_distance_sq > distanceSq) {
+                                       min_distance_sq = distanceSq;
+                                       result.intersection_point = intersection_point;
+                                       result.intersection_normal = intersection_normal;
+                                       found_boxcenter = box.getCenter();
+                                       is_colliding = true;
+                               }
+                       }
+                       // If there wasn't a collision, stop
+                       if (!is_colliding) {
+                               continue;
+                       }
+                       result.type = POINTEDTHING_NODE;
+                       result.node_undersurface = np;
+                       result.distanceSq = min_distance_sq;
+                       // Set undersurface and abovesurface nodes
+                       f32 d = 0.002 * BS;
+                       v3f fake_intersection = result.intersection_point;
+                       // Move intersection towards its source block.
+                       if (fake_intersection.X < found_boxcenter.X) {
+                               fake_intersection.X += d;
+                       } else {
+                               fake_intersection.X -= d;
+                       }
+                       if (fake_intersection.Y < found_boxcenter.Y) {
+                               fake_intersection.Y += d;
+                       } else {
+                               fake_intersection.Y -= d;
+                       }
+                       if (fake_intersection.Z < found_boxcenter.Z) {
+                               fake_intersection.Z += d;
+                       } else {
+                               fake_intersection.Z -= d;
+                       }
+                       result.node_real_undersurface = floatToInt(
+                               fake_intersection, BS);
+                       result.node_abovesurface = result.node_real_undersurface
+                               + result.intersection_normal;
+                       // Push found PointedThing
+                       state->m_found.push(result);
+                       // If this is nearer than the old nearest object,
+                       // the search can be shorter
+                       s16 newIndex = state->m_iterator.getIndex(
+                               result.node_real_undersurface);
+                       if (newIndex < lastIndex) {
+                               lastIndex = newIndex;
+                       }
+               }
+               // Next node
+               state->m_previous_node = state->m_iterator.m_current_node_pos;
+               state->m_iterator.next();
+       }
+       // Return empty PointedThing if nothing left on the ray
+       if (state->m_found.empty()) {
+               result->type = POINTEDTHING_NOTHING;
+       } else {
+               *result = state->m_found.top();
+               state->m_found.pop();
+       }
+}
+
 void Environment::stepTimeOfDay(float dtime)
 {
        MutexAutoLock lock(this->m_time_lock);
index 11c2533ca7c9e54489fb3a03c5ab1a87464ae3b5..b45775f4ce65401c4a6690739c4093894295fe8d 100644 (file)
@@ -42,6 +42,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 class IGameDef;
 class Map;
+struct PointedThing;
+class RaycastState;
 
 class Environment
 {
@@ -76,6 +78,26 @@ public:
 
        u32 getDayCount();
 
+       /*!
+        * Gets the objects pointed by the shootline as
+        * pointed things.
+        * If this is a client environment, the local player
+        * won't be returned.
+        * @param[in]  shootline_on_map the shootline for
+        * the test in world coordinates
+        *
+        * @param[out] objects          found objects
+        */
+       virtual void getSelectedActiveObjects(const core::line3d<f32> &shootline_on_map,
+                       std::vector<PointedThing> &objects) = 0;
+
+       /*!
+        * Returns the next node or object the shootline meets.
+        * @param state current state of the raycast
+        * @result output, will contain the next pointed thing
+        */
+       void continueRaycast(RaycastState *state, PointedThing *result);
+
        // counter used internally when triggering ABMs
        u32 m_added_objects;
 
index b6304f19e7d3bf010ad12d83a7cc6d98a7c93cef..88ae9c2b766c6a3c29bb7328190dcd9b675bcac4 100644 (file)
@@ -50,6 +50,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "particles.h"
 #include "profiler.h"
 #include "quicktune_shortcutter.h"
+#include "raycast.h"
 #include "server.h"
 #include "settings.h"
 #include "sky.h"
@@ -3667,28 +3668,22 @@ PointedThing Game::updatePointedThing(
        static thread_local const bool show_entity_selectionbox = g_settings->getBool(
                "show_entity_selectionbox");
 
-       ClientMap &map = client->getEnv().getClientMap();
-       INodeDefManager *nodedef=client->getNodeDefManager();
+       ClientEnvironment &env = client->getEnv();
+       ClientMap &map = env.getClientMap();
+       INodeDefManager *nodedef = map.getNodeDefManager();
 
        runData.selected_object = NULL;
 
-       PointedThing result=client->getEnv().getPointedThing(
-               shootline, liquids_pointable, look_for_object);
+       RaycastState s(shootline, look_for_object, liquids_pointable);
+       PointedThing result;
+       env.continueRaycast(&s, &result);
        if (result.type == POINTEDTHING_OBJECT) {
                runData.selected_object = client->getEnv().getActiveObject(result.object_id);
-               if (show_entity_selectionbox && runData.selected_object->doShowSelectionBox()) {
-                       aabb3f *selection_box = runData.selected_object->getSelectionBox();
-
-                       // Box should exist because object was
-                       // returned in the first place
-
-                       assert(selection_box);
-
+               aabb3f selection_box;
+               if (show_entity_selectionbox && runData.selected_object->doShowSelectionBox() &&
+                               runData.selected_object->getSelectionBox(&selection_box)) {
                        v3f pos = runData.selected_object->getPosition();
-                       selectionboxes->push_back(aabb3f(
-                               selection_box->MinEdge, selection_box->MaxEdge));
-                       selectionboxes->push_back(
-                               aabb3f(selection_box->MinEdge, selection_box->MaxEdge));
+                       selectionboxes->push_back(aabb3f(selection_box));
                        hud->setSelectionPos(pos, camera_offset);
                }
        } else if (result.type == POINTEDTHING_NODE) {
index 58102c9936b850ac1771fd356f703425607faebf..42cc22587d69984d70ebd06d1af6c2c6db716e1d 100644 (file)
@@ -17,11 +17,47 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
+#include "raycast.h"
 #include "irr_v3d.h"
 #include "irr_aabb3d.h"
+#include "constants.h"
+
+bool RaycastSort::operator() (const PointedThing &pt1,
+       const PointedThing &pt2) const
+{
+       // "nothing" can not be sorted
+       assert(pt1.type != POINTEDTHING_NOTHING);
+       assert(pt2.type != POINTEDTHING_NOTHING);
+       // returns false if pt1 is nearer than pt2
+       if (pt1.distanceSq < pt2.distanceSq) {
+               return false;
+       } else if (pt1.distanceSq == pt2.distanceSq) {
+               // Sort them to allow only one order
+               if (pt1.type == POINTEDTHING_OBJECT)
+                       return (pt2.type == POINTEDTHING_OBJECT
+                               && pt1.object_id < pt2.object_id);
+               else
+                       return (pt2.type == POINTEDTHING_OBJECT
+                               || pt1.node_undersurface < pt2.node_undersurface);
+       }
+       return true;
+}
+
+
+RaycastState::RaycastState(const core::line3d<f32> &shootline,
+       bool objects_pointable, bool liquids_pointable) :
+       m_shootline(shootline),
+       m_iterator(shootline.start / BS, shootline.getVector() / BS),
+       m_previous_node(m_iterator.m_current_node_pos),
+       m_objects_pointable(objects_pointable),
+       m_liquids_pointable(liquids_pointable)
+{
+}
+
 
 bool boxLineCollision(const aabb3f &box, const v3f &start,
-               const v3f &dir, v3f *collision_point, v3s16 *collision_normal) {
+       const v3f &dir, v3f *collision_point, v3s16 *collision_normal)
+{
        if (box.isPointInside(start)) {
                *collision_point = start;
                collision_normal->set(0, 0, 0);
index d7ec8c843c5d3a712a8aa21ec89c549dfd7bc450..d69d9339b025db06f2f4d75892fac0ee9286804c 100644 (file)
@@ -20,6 +20,49 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #ifndef SRC_RAYCAST_H_
 #define SRC_RAYCAST_H_
 
+#include "voxelalgorithms.h"
+#include "util/pointedthing.h"
+
+//! Sorts PointedThings based on their distance.
+struct RaycastSort
+{
+       bool operator() (const PointedThing &pt1, const PointedThing &pt2) const;
+};
+
+//! Describes the state of a raycast.
+class RaycastState
+{
+public:
+       /*!
+        * Creates a raycast.
+        * @param objects_pointable if false, only nodes will be found
+        * @param liquids pointable if false, liquid nodes won't be found
+        */
+       RaycastState(const core::line3d<f32> &shootline, bool objects_pointable,
+               bool liquids_pointable);
+
+       //! Shootline of the raycast.
+       core::line3d<f32> m_shootline;
+       //! Iterator to store the progress of the raycast.
+       voxalgo::VoxelLineIterator m_iterator;
+       //! Previous tested node during the raycast.
+       v3s16 m_previous_node;
+
+       /*!
+        * This priority queue stores the found pointed things
+        * waiting to be returned.
+        */
+       std::priority_queue<PointedThing, std::vector<PointedThing>, RaycastSort> m_found;
+
+       bool m_objects_pointable;
+       bool m_liquids_pointable;
+
+       //! The code needs to search these nodes around the center node.
+       core::aabbox3d<s16> m_search_range { 0, 0, 0, 0, 0, 0 };
+
+       //! If true, the Environment will initialize this state.
+       bool m_initialization_needed = true;
+};
 
 /*!
  * Checks if a line and a box intersects.
index 7350a71c5e3d19d6e0d901d447e40d4b17dc193d..6ceb4b5598b90d14b2bba7804bd1103e1f8cdbae 100644 (file)
@@ -52,6 +52,7 @@ public:
 protected:
        friend class LuaItemStack;
        friend class ModApiItemMod;
+       friend class LuaRaycast;
 
        bool getItemCallback(const char *name, const char *callbackname);
        void pushPointedThing(const PointedThing& pointed);
index e7284b0354f352219e0a258945a54c5df1ce918b..3a4ba89f34587290cf36f3a644597c1582298536 100644 (file)
@@ -131,6 +131,105 @@ void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n)
        lua_pop(L, 1); // Pop error handler
 }
 
+int LuaRaycast::l_next(lua_State *L)
+{
+       MAP_LOCK_REQUIRED;
+
+       ScriptApiItem *script = getScriptApi<ScriptApiItem>(L);
+       GET_ENV_PTR;
+
+       LuaRaycast *o = checkobject(L, 1);
+       PointedThing pointed;
+       env->continueRaycast(&o->state, &pointed);
+       if (pointed.type == POINTEDTHING_NOTHING)
+               lua_pushnil(L);
+       else
+               script->pushPointedThing(pointed);
+
+       return 1;
+}
+
+int LuaRaycast::create_object(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+
+       bool objects = true;
+       bool liquids = false;
+
+       v3f pos1 = checkFloatPos(L, 1);
+       v3f pos2 = checkFloatPos(L, 2);
+       if (lua_isboolean(L, 3)) {
+               objects = lua_toboolean(L, 3);
+       }
+       if (lua_isboolean(L, 4)) {
+               liquids = lua_toboolean(L, 4);
+       }
+
+       LuaRaycast *o = new LuaRaycast(core::line3d<f32>(pos1, pos2),
+               objects, liquids);
+
+       *(void **) (lua_newuserdata(L, sizeof(void *))) = o;
+       luaL_getmetatable(L, className);
+       lua_setmetatable(L, -2);
+       return 1;
+}
+
+LuaRaycast *LuaRaycast::checkobject(lua_State *L, int narg)
+{
+       NO_MAP_LOCK_REQUIRED;
+
+       luaL_checktype(L, narg, LUA_TUSERDATA);
+       void *ud = luaL_checkudata(L, narg, className);
+       if (!ud)
+               luaL_typerror(L, narg, className);
+       return *(LuaRaycast **) ud;
+}
+
+int LuaRaycast::gc_object(lua_State *L)
+{
+       LuaRaycast *o = *(LuaRaycast **) (lua_touserdata(L, 1));
+       delete o;
+       return 0;
+}
+
+void LuaRaycast::Register(lua_State *L)
+{
+       lua_newtable(L);
+       int methodtable = lua_gettop(L);
+       luaL_newmetatable(L, className);
+       int metatable = lua_gettop(L);
+
+       lua_pushliteral(L, "__metatable");
+       lua_pushvalue(L, methodtable);
+       lua_settable(L, metatable);
+
+       lua_pushliteral(L, "__index");
+       lua_pushvalue(L, methodtable);
+       lua_settable(L, metatable);
+
+       lua_pushliteral(L, "__gc");
+       lua_pushcfunction(L, gc_object);
+       lua_settable(L, metatable);
+
+       lua_pushliteral(L, "__call");
+       lua_pushcfunction(L, l_next);
+       lua_settable(L, metatable);
+
+       lua_pop(L, 1);
+
+       luaL_openlib(L, 0, methods, 0);
+       lua_pop(L, 1);
+
+       lua_register(L, className, create_object);
+}
+
+const char LuaRaycast::className[] = "Raycast";
+const luaL_Reg LuaRaycast::methods[] =
+{
+       luamethod(LuaRaycast, next),
+       { 0, 0 }
+};
+
 void LuaEmergeAreaCallback(v3s16 blockpos, EmergeAction action, void *param)
 {
        ScriptCallbackState *state = (ScriptCallbackState *)param;
@@ -904,6 +1003,11 @@ int ModApiEnvMod::l_fix_light(lua_State *L)
        return 1;
 }
 
+int ModApiEnvMod::l_raycast(lua_State *L)
+{
+       return LuaRaycast::create_object(L);
+}
+
 // emerge_area(p1, p2, [callback, context])
 // emerge mapblocks in area p1..p2, calls callback with context upon completion
 int ModApiEnvMod::l_emerge_area(lua_State *L)
@@ -1155,6 +1259,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top)
        API_FCT(spawn_tree);
        API_FCT(find_path);
        API_FCT(line_of_sight);
+       API_FCT(raycast);
        API_FCT(transforming_liquid_add);
        API_FCT(forceload_block);
        API_FCT(forceload_free_block);
index 7ce19b085f7984e6a882cd482af117a4f350bfd7..f380d8d6f0d0f04d8de3219c3e7bb65dbeca41ae 100644 (file)
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "lua_api/l_base.h"
 #include "serverenvironment.h"
+#include "raycast.h"
 
 class ModApiEnvMod : public ModApiBase {
 private:
@@ -159,6 +160,9 @@ private:
        // line_of_sight(pos1, pos2, stepsize) -> true/false
        static int l_line_of_sight(lua_State *L);
 
+       // raycast(pos1, pos2, objects, liquids) -> Raycast
+       static int l_raycast(lua_State *L);
+
        // find_path(pos1, pos2, searchdistance,
        //     max_jump, max_drop, algorithm) -> table containing path
        static int l_find_path(lua_State *L);
@@ -245,6 +249,47 @@ public:
        virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n);
 };
 
+//! Lua wrapper for RaycastState objects
+class LuaRaycast : public ModApiBase
+{
+private:
+       static const char className[];
+       static const luaL_Reg methods[];
+       //! Inner state
+       RaycastState state;
+
+       // Exported functions
+
+       // garbage collector
+       static int gc_object(lua_State *L);
+
+       /*!
+        * Raycast:next() -> pointed_thing
+        * Returns the next pointed thing on the ray.
+        */
+       static int l_next(lua_State *L);
+public:
+       //! Constructor with the same arguments as RaycastState.
+       LuaRaycast(
+               const core::line3d<f32> &shootline,
+               bool objects_pointable,
+               bool liquids_pointable) :
+               state(shootline, objects_pointable, liquids_pointable)
+       {}
+
+       //! Creates a LuaRaycast and leaves it on top of the stack.
+       static int create_object(lua_State *L);
+
+       /*!
+        * Returns the Raycast from the stack or throws an error.
+        * @param narg location of the RaycastState in the stack
+        */
+       static LuaRaycast *checkobject(lua_State *L, int narg);
+
+       //! Registers Raycast as a Lua userdata type.
+       static void Register(lua_State *L);
+};
+
 struct ScriptCallbackState {
        ServerScripting *script;
        int callback_ref;
index 51e13f04dff195f7a98e8b331fd2880d06ca5f60..01e8e2fb501a6293f343abb43e7879126775de16 100644 (file)
@@ -92,6 +92,7 @@ void ServerScripting::InitializeModApi(lua_State *L, int top)
        LuaPerlinNoiseMap::Register(L);
        LuaPseudoRandom::Register(L);
        LuaPcgRandom::Register(L);
+       LuaRaycast::Register(L);
        LuaSecureRandom::Register(L);
        LuaVoxelManip::Register(L);
        NodeMetaRef::Register(L);
index e8bdd2a282416d5258dfd4c6c89f5a5ca468f04d..11c71e28efeeb9f10f6459fd9dc17528145a07cf 100644 (file)
@@ -30,7 +30,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "remoteplayer.h"
 #include "scripting_server.h"
 #include "server.h"
-#include "voxelalgorithms.h"
 #include "util/serialize.h"
 #include "util/basic_macros.h"
 #include "util/pointedthing.h"
@@ -1662,6 +1661,38 @@ ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
        return message;
 }
 
+void ServerEnvironment::getSelectedActiveObjects(
+       const core::line3d<f32> &shootline_on_map,
+       std::vector<PointedThing> &objects)
+{
+       std::vector<u16> objectIds;
+       getObjectsInsideRadius(objectIds, shootline_on_map.start,
+               shootline_on_map.getLength() + 10.0f);
+       const v3f line_vector = shootline_on_map.getVector();
+
+       for (u32 i = 0; i < objectIds.size(); i++) {
+               ServerActiveObject* obj = getActiveObject(objectIds[i]);
+
+               aabb3f selection_box;
+               if (!obj->getSelectionBox(&selection_box))
+                       continue;
+
+               v3f pos = obj->getBasePosition();
+
+               aabb3f offsetted_box(selection_box.MinEdge + pos,
+                       selection_box.MaxEdge + pos);
+
+               v3f current_intersection;
+               v3s16 current_normal;
+               if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
+                               &current_intersection, &current_normal)) {
+                       objects.push_back(PointedThing(
+                               (s16) objectIds[i], current_intersection, current_normal,
+                               (current_intersection - shootline_on_map.start).getLengthSQ()));
+               }
+       }
+}
+
 /*
        ************ Private methods *************
 */
index 375b28f8a858bf16f4f300e0c5ddb700e28b3d6b..48eb5b3185da29ba4a66a34b5d82c964868f03ce 100644 (file)
@@ -284,6 +284,11 @@ public:
        */
        ActiveObjectMessage getActiveObjectMessage();
 
+       virtual void getSelectedActiveObjects(
+               const core::line3d<f32> &shootline_on_map,
+               std::vector<PointedThing> &objects
+       );
+
        /*
                Activate objects and dynamically modify for the dtime determined
                from timestamp and additional_dtime
index f1f1d3f2090773ab21cec3ecdfb741816fa7e12a..e5c5dcf4c9747a04901394544399f6605d77fbb0 100644 (file)
@@ -23,20 +23,47 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "../exceptions.h"
 #include <sstream>
 
+PointedThing::PointedThing(const v3s16 &under, const v3s16 &above,
+       const v3s16 &real_under, const v3f &point, const v3s16 &normal,
+       f32 distSq):
+       type(POINTEDTHING_NODE),
+       node_undersurface(under),
+       node_abovesurface(above),
+       node_real_undersurface(real_under),
+       intersection_point(point),
+       intersection_normal(normal),
+       distanceSq(distSq)
+{}
+
+PointedThing::PointedThing(s16 id, const v3f &point, const v3s16 &normal,
+       f32 distSq) :
+       type(POINTEDTHING_OBJECT),
+       object_id(id),
+       intersection_point(point),
+       intersection_normal(normal),
+       distanceSq(distSq)
+{}
+
 std::string PointedThing::dump() const
 {
        std::ostringstream os(std::ios::binary);
-       if (type == POINTEDTHING_NOTHING) {
-               os<<"[nothing]";
-       } else if (type == POINTEDTHING_NODE) {
+       switch (type) {
+       case POINTEDTHING_NOTHING:
+               os << "[nothing]";
+               break;
+       case POINTEDTHING_NODE:
+       {
                const v3s16 &u = node_undersurface;
                const v3s16 &a = node_abovesurface;
-               os<<"[node under="<<u.X<<","<<u.Y<<","<<u.Z
-                       << " above="<<a.X<<","<<a.Y<<","<<a.Z<<"]";
-       } else if (type == POINTEDTHING_OBJECT) {
-               os<<"[object "<<object_id<<"]";
-       } else {
-               os<<"[unknown PointedThing]";
+               os << "[node under=" << u.X << "," << u.Y << "," << u.Z << " above="
+                       << a.X << "," << a.Y << "," << a.Z << "]";
+       }
+               break;
+       case POINTEDTHING_OBJECT:
+               os << "[object " << object_id << "]";
+               break;
+       default:
+               os << "[unknown PointedThing]";
        }
        return os.str();
 }
@@ -104,4 +131,3 @@ bool PointedThing::operator!=(const PointedThing &pt2) const
 {
        return !(*this == pt2);
 }
-
index 92c33968fc431ae47784ff5f39509b71a828e8b9..f63bcad9d2bf74d22705c409dcf1f7bf2eb49207 100644 (file)
@@ -58,6 +58,11 @@ struct PointedThing
         * point of the collision and the nodebox of the node.
         */
        v3s16 node_real_undersurface;
+       /*!
+        * Only valid if type is POINTEDTHING_OBJECT.
+        * The ID of the object the ray hit.
+        */
+       s16 object_id = -1;
        /*!
         * Only valid if type isn't POINTEDTHING_NONE.
         * First intersection point of the ray and the nodebox.
@@ -71,12 +76,19 @@ struct PointedThing
         */
        v3s16 intersection_normal;
        /*!
-        * Only valid if type is POINTEDTHING_OBJECT.
-        * The ID of the object the ray hit.
+        * Square of the distance between the pointing
+        * ray's start point and the intersection point.
         */
-       s16 object_id = -1;
+       f32 distanceSq = 0;
 
+       //! Constructor for POINTEDTHING_NOTHING
        PointedThing() {};
+       //! Constructor for POINTEDTHING_NODE
+       PointedThing(const v3s16 &under, const v3s16 &above,
+               const v3s16 &real_under, const v3f &point, const v3s16 &normal,
+               f32 distSq);
+       //! Constructor for POINTEDTHING_OBJECT
+       PointedThing(s16 id, const v3f &point, const v3s16 &normal, f32 distSq);
        std::string dump() const;
        void serialize(std::ostream &os) const;
        void deSerialize(std::istream &is);
index cea3395579513d5a86ba59a157bd6441662dc571..74c165dd345e928a0065bef0584bd43200b3cb9b 100644 (file)
@@ -1407,6 +1407,8 @@ VoxelLineIterator::VoxelLineIterator(const v3f &start_position, const v3f &line_
        m_line_vector(line_vector)
 {
        m_current_node_pos = floatToInt(m_start_position, 1);
+       m_start_node_pos = m_current_node_pos;
+       m_last_index = getIndex(floatToInt(start_position + line_vector, 1));
 
        if (m_line_vector.X > 0) {
                m_next_intersection_multi.X = (floorf(m_start_position.X - 0.5) + 1.5
@@ -1440,14 +1442,11 @@ VoxelLineIterator::VoxelLineIterator(const v3f &start_position, const v3f &line_
                m_intersection_multi_inc.Z = -1 / m_line_vector.Z;
                m_step_directions.Z = -1;
        }
-
-       m_has_next = (m_next_intersection_multi.X <= 1)
-               || (m_next_intersection_multi.Y <= 1)
-               || (m_next_intersection_multi.Z <= 1);
 }
 
 void VoxelLineIterator::next()
 {
+       m_current_index++;
        if ((m_next_intersection_multi.X < m_next_intersection_multi.Y)
                        && (m_next_intersection_multi.X < m_next_intersection_multi.Z)) {
                m_next_intersection_multi.X += m_intersection_multi_inc.X;
@@ -1459,10 +1458,13 @@ void VoxelLineIterator::next()
                m_next_intersection_multi.Z += m_intersection_multi_inc.Z;
                m_current_node_pos.Z += m_step_directions.Z;
        }
+}
 
-       m_has_next = (m_next_intersection_multi.X <= 1)
-                       || (m_next_intersection_multi.Y <= 1)
-                       || (m_next_intersection_multi.Z <= 1);
+s16 VoxelLineIterator::getIndex(v3s16 voxel){
+       return
+               abs(voxel.X - m_start_node_pos.X) +
+               abs(voxel.Y - m_start_node_pos.Y) +
+               abs(voxel.Z - m_start_node_pos.Z);
 }
 
 } // namespace voxalgo
index 6e5fd525306f803d3c98ae185fccef8237eba2bb..7203585e4857da9fe134f965e96f3562e75a3a33 100644 (file)
@@ -123,21 +123,25 @@ public:
         * which multiplying the line's vector gives a vector that ends
         * on the intersection of two nodes.
         */
-       v3f m_next_intersection_multi = v3f(10000.0f, 10000.0f, 10000.0f);
+       v3f m_next_intersection_multi { 10000.0f, 10000.0f, 10000.0f };
        /*!
         * Each component stores the smallest positive number, by which
         * m_next_intersection_multi's components can be increased.
         */
-       v3f m_intersection_multi_inc = v3f(10000.0f, 10000.0f, 10000.0f);
+       v3f m_intersection_multi_inc { 10000.0f, 10000.0f, 10000.0f };
        /*!
         * Direction of the line. Each component can be -1 or 1 (if a
         * component of the line's vector is 0, then there will be 1).
         */
-       v3s16 m_step_directions = v3s16(1, 1, 1);
+       v3s16 m_step_directions { 1, 1, 1 };
        //! Position of the current node.
        v3s16 m_current_node_pos;
-       //! If true, the next node will intersect the line, too.
-       bool m_has_next;
+       //! Index of the current node
+       s16 m_current_index = 0;
+       //! Position of the start node.
+       v3s16 m_start_node_pos;
+       //! Index of the last node
+       s16 m_last_index;
 
        /*!
         * Creates a voxel line iterator with the given line.
@@ -161,7 +165,18 @@ public:
        /*!
         * Returns true if the next voxel intersects the given line.
         */
-       inline bool hasNext() const { return m_has_next; }
+       inline bool hasNext() const
+       {
+               return m_current_index < m_last_index;
+       }
+
+       /*!
+        * Returns how many times next() must be called until
+        * voxel==m_current_node_pos.
+        * If voxel does not intersect with the line,
+        * the result is undefined.
+        */
+       s16 getIndex(v3s16 voxel);
 };
 
 } // namespace voxalgo