aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/lua_api.txt26
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/activeobject.h23
-rw-r--r--src/clientenvironment.cpp239
-rw-r--r--src/clientenvironment.h36
-rw-r--r--src/clientobject.h2
-rw-r--r--src/content_cao.cpp23
-rw-r--r--src/content_cao.h4
-rw-r--r--src/content_sao.cpp26
-rw-r--r--src/content_sao.h2
-rw-r--r--src/environment.cpp174
-rw-r--r--src/environment.h22
-rw-r--r--src/game.cpp27
-rw-r--r--src/raycast.cpp38
-rw-r--r--src/raycast.h43
-rw-r--r--src/script/cpp_api/s_item.h1
-rw-r--r--src/script/lua_api/l_env.cpp105
-rw-r--r--src/script/lua_api/l_env.h45
-rw-r--r--src/script/scripting_server.cpp1
-rw-r--r--src/serverenvironment.cpp33
-rw-r--r--src/serverenvironment.h5
-rw-r--r--src/util/pointedthing.cpp46
-rw-r--r--src/util/pointedthing.h18
-rw-r--r--src/voxelalgorithms.cpp16
-rw-r--r--src/voxelalgorithms.h27
25 files changed, 671 insertions, 313 deletions
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 63d090ebe..56f8dbaae 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -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
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 75a835b89..a7e26af7d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -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
diff --git a/src/activeobject.h b/src/activeobject.h
index f349ddef3..4796e168c 100644
--- a/src/activeobject.h
+++ b/src/activeobject.h
@@ -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"
diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp
index 791b61531..9abb6a23d 100644
--- a/src/clientenvironment.cpp
+++ b/src/clientenvironment.cpp
@@ -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;
}
diff --git a/src/clientenvironment.h b/src/clientenvironment.h
index 05ec1908f..070ff95fb 100644
--- a/src/clientenvironment.h
+++ b/src/clientenvironment.h
@@ -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; }
diff --git a/src/clientobject.h b/src/clientobject.h
index f8075d65a..eeed2516d 100644
--- a/src/clientobject.h
+++ b/src/clientobject.h
@@ -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; }
diff --git a/src/content_cao.cpp b/src/content_cao.cpp
index aff143bf2..49c2049eb 100644
--- a/src/content_cao.cpp
+++ b/src/content_cao.cpp
@@ -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;
diff --git a/src/content_cao.h b/src/content_cao.h
index 526f10ea3..d6d5deac8 100644
--- a/src/content_cao.h
+++ b/src/content_cao.h
@@ -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
diff --git a/src/content_sao.cpp b/src/content_sao.cpp
index c6c00768e..5bbbd154d 100644
--- a/src/content_sao.cpp
+++ b/src/content_sao.cpp
@@ -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;
+}
diff --git a/src/content_sao.h b/src/content_sao.h
index 0a1ae5ecf..a986acab0 100644
--- a/src/content_sao.h
+++ b/src/content_sao.h
@@ -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);
diff --git a/src/environment.cpp b/src/environment.cpp
index 904bab6f3..e7ab626b4 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -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);
diff --git a/src/environment.h b/src/environment.h
index 11c2533ca..b45775f4c 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -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;
diff --git a/src/game.cpp b/src/game.cpp
index b6304f19e..88ae9c2b7 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -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) {
diff --git a/src/raycast.cpp b/src/raycast.cpp
index 58102c993..42cc22587 100644
--- a/src/raycast.cpp
+++ b/src/raycast.cpp
@@ -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);
diff --git a/src/raycast.h b/src/raycast.h
index d7ec8c843..d69d9339b 100644
--- a/src/raycast.h
+++ b/src/raycast.h
@@ -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.
diff --git a/src/script/cpp_api/s_item.h b/src/script/cpp_api/s_item.h
index 7350a71c5..6ceb4b559 100644
--- a/src/script/cpp_api/s_item.h
+++ b/src/script/cpp_api/s_item.h
@@ -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);
diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp
index e7284b035..3a4ba89f3 100644
--- a/src/script/lua_api/l_env.cpp
+++ b/src/script/lua_api/l_env.cpp
@@ -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);
diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h
index 7ce19b085..f380d8d6f 100644
--- a/src/script/lua_api/l_env.h
+++ b/src/script/lua_api/l_env.h
@@ -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;
diff --git a/src/script/scripting_server.cpp b/src/script/scripting_server.cpp
index 51e13f04d..01e8e2fb5 100644
--- a/src/script/scripting_server.cpp
+++ b/src/script/scripting_server.cpp
@@ -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);
diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp
index e8bdd2a28..11c71e28e 100644
--- a/src/serverenvironment.cpp
+++ b/src/serverenvironment.cpp
@@ -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 *************
*/
diff --git a/src/serverenvironment.h b/src/serverenvironment.h
index 375b28f8a..48eb5b318 100644
--- a/src/serverenvironment.h
+++ b/src/serverenvironment.h
@@ -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
diff --git a/src/util/pointedthing.cpp b/src/util/pointedthing.cpp
index f1f1d3f20..e5c5dcf4c 100644
--- a/src/util/pointedthing.cpp
+++ b/src/util/pointedthing.cpp
@@ -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);
}
-
diff --git a/src/util/pointedthing.h b/src/util/pointedthing.h
index 92c33968f..f63bcad9d 100644
--- a/src/util/pointedthing.h
+++ b/src/util/pointedthing.h
@@ -59,6 +59,11 @@ struct PointedThing
*/
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);
diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp
index cea339557..74c165dd3 100644
--- a/src/voxelalgorithms.cpp
+++ b/src/voxelalgorithms.cpp
@@ -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
diff --git a/src/voxelalgorithms.h b/src/voxelalgorithms.h
index 6e5fd5253..7203585e4 100644
--- a/src/voxelalgorithms.h
+++ b/src/voxelalgorithms.h
@@ -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