* `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`
* 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
porting.cpp
profiler.cpp
quicktune.cpp
+ raycast.cpp
reflowscan.cpp
remoteplayer.cpp
- raycast.cpp
rollback.cpp
rollback_interface.cpp
serialization.cpp
{
}
- u16 getId()
+ u16 getId() const
{
return m_id;
}
}
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"
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,
- ¤t_intersection, ¤t_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();
+ ¤t_intersection, ¤t_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;
}
class ClientActiveObject;
class GenericCAO;
class LocalPlayer;
-struct PointedThing;
/*
The client-side environment.
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; }
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; }
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
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()
updateAttachments();
}
-ClientActiveObject* GenericCAO::getParent()
+ClientActiveObject* GenericCAO::getParent() const
{
ClientActiveObject *obj = NULL;
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
}
bool getCollisionBox(aabb3f *toset) const { return false; }
+
+ virtual bool getSelectionBox(aabb3f *toset) const { return false; }
+
bool collideWithObjects() const { return false; }
private:
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;
toset->MaxEdge += m_base_position;
return true;
}
+
+bool PlayerSAO::getSelectionBox(aabb3f *toset) const
+{
+ if (!m_prop.is_visible) {
+ return false;
+ }
+
+ getCollisionBox(toset);
+
+ return true;
+}
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();
}
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);
#include <fstream>
#include "environment.h"
#include "collision.h"
+#include "raycast.h"
#include "serverobject.h"
#include "scripting_server.h"
#include "server.h"
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);
class IGameDef;
class Map;
+struct PointedThing;
+class RaycastState;
class Environment
{
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;
#include "particles.h"
#include "profiler.h"
#include "quicktune_shortcutter.h"
+#include "raycast.h"
#include "server.h"
#include "settings.h"
#include "sky.h"
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) {
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);
#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.
protected:
friend class LuaItemStack;
friend class ModApiItemMod;
+ friend class LuaRaycast;
bool getItemCallback(const char *name, const char *callbackname);
void pushPointedThing(const PointedThing& pointed);
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;
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)
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);
#include "lua_api/l_base.h"
#include "serverenvironment.h"
+#include "raycast.h"
class ModApiEnvMod : public ModApiBase {
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);
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;
LuaPerlinNoiseMap::Register(L);
LuaPseudoRandom::Register(L);
LuaPcgRandom::Register(L);
+ LuaRaycast::Register(L);
LuaSecureRandom::Register(L);
LuaVoxelManip::Register(L);
NodeMetaRef::Register(L);
#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"
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,
+ ¤t_intersection, ¤t_normal)) {
+ objects.push_back(PointedThing(
+ (s16) objectIds[i], current_intersection, current_normal,
+ (current_intersection - shootline_on_map.start).getLengthSQ()));
+ }
+ }
+}
+
/*
************ Private methods *************
*/
*/
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
#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();
}
{
return !(*this == pt2);
}
-
* 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.
*/
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);
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
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;
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
* 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.
/*!
* 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