mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00: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
25 changed files with 671 additions and 313 deletions
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue