mirror of
https://github.com/minetest/minetest.git
synced 2024-12-28 00:47:29 +01:00
Expose getPointedThing to Lua
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).
This commit is contained in:
parent
a80ecbee1e
commit
3caad3f3c9
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
¤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;
|
||||
}
|
||||
¤t_intersection, ¤t_normal)) {
|
||||
objects.push_back(PointedThing(
|
||||
(s16) obj->getId(), current_intersection, current_normal,
|
||||
(current_intersection - shootline_on_map.start).getLengthSQ()));
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
27
src/game.cpp
27
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) {
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
¤t_intersection, ¤t_normal)) {
|
||||
objects.push_back(PointedThing(
|
||||
(s16) objectIds[i], current_intersection, current_normal,
|
||||
(current_intersection - shootline_on_map.start).getLengthSQ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
************ Private methods *************
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user