summaryrefslogtreecommitdiff
path: root/src/environment.cpp
diff options
context:
space:
mode:
authorDániel Juhász <juhdanad@gmail.com>2016-07-23 21:11:20 +0200
committerparamat <mat.gregory@virginmedia.com>2017-07-07 22:28:23 +0100
commit3caad3f3c9e319ca67d63231e8c64b2ace855fff (patch)
treef3cb283b7aa28958e2deec7c70dad3a85e1236d4 /src/environment.cpp
parenta80ecbee1e838491343af760539a37fac4232048 (diff)
downloadminetest-3caad3f3c9e319ca67d63231e8c64b2ace855fff.tar.gz
minetest-3caad3f3c9e319ca67d63231e8c64b2ace855fff.tar.bz2
minetest-3caad3f3c9e319ca67d63231e8c64b2ace855fff.zip
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).
Diffstat (limited to 'src/environment.cpp')
-rw-r--r--src/environment.cpp174
1 files changed, 174 insertions, 0 deletions
diff --git a/src/environment.cpp b/src/environment.cpp
index 904bab6f3..e7ab626b4 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <fstream>
#include "environment.h"
#include "collision.h"
+#include "raycast.h"
#include "serverobject.h"
#include "scripting_server.h"
#include "server.h"
@@ -83,6 +84,179 @@ float Environment::getTimeOfDayF()
return m_time_of_day_f;
}
+/*
+ Check if a node is pointable
+*/
+inline static bool isPointableNode(const MapNode &n,
+ INodeDefManager *nodedef , bool liquids_pointable)
+{
+ const ContentFeatures &features = nodedef->get(n);
+ return features.pointable ||
+ (liquids_pointable && features.isLiquid());
+}
+
+void Environment::continueRaycast(RaycastState *state, PointedThing *result)
+{
+ INodeDefManager *nodedef = getMap().getNodeDefManager();
+ if (state->m_initialization_needed) {
+ // Add objects
+ if (state->m_objects_pointable) {
+ std::vector<PointedThing> found;
+ getSelectedActiveObjects(state->m_shootline, found);
+ for (std::vector<PointedThing>::iterator pointed = found.begin();
+ pointed != found.end(); ++pointed) {
+ state->m_found.push(*pointed);
+ }
+ }
+ // Set search range
+ core::aabbox3d<s16> maximal_exceed = nodedef->getSelectionBoxIntUnion();
+ state->m_search_range.MinEdge = -maximal_exceed.MaxEdge;
+ state->m_search_range.MaxEdge = -maximal_exceed.MinEdge;
+ // Setting is done
+ state->m_initialization_needed = false;
+ }
+
+ // The index of the first pointed thing that was not returned
+ // before. The last index which needs to be tested.
+ s16 lastIndex = state->m_iterator.m_last_index;
+ if (!state->m_found.empty()) {
+ lastIndex = state->m_iterator.getIndex(
+ floatToInt(state->m_found.top().intersection_point, BS));
+ }
+
+ Map &map = getMap();
+ // If a node is found, this is the center of the
+ // first nodebox the shootline meets.
+ v3f found_boxcenter(0, 0, 0);
+ // The untested nodes are in this range.
+ core::aabbox3d<s16> new_nodes;
+ while (state->m_iterator.m_current_index <= lastIndex) {
+ // Test the nodes around the current node in search_range.
+ new_nodes = state->m_search_range;
+ new_nodes.MinEdge += state->m_iterator.m_current_node_pos;
+ new_nodes.MaxEdge += state->m_iterator.m_current_node_pos;
+
+ // Only check new nodes
+ v3s16 delta = state->m_iterator.m_current_node_pos
+ - state->m_previous_node;
+ if (delta.X > 0) {
+ new_nodes.MinEdge.X = new_nodes.MaxEdge.X;
+ } else if (delta.X < 0) {
+ new_nodes.MaxEdge.X = new_nodes.MinEdge.X;
+ } else if (delta.Y > 0) {
+ new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y;
+ } else if (delta.Y < 0) {
+ new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y;
+ } else if (delta.Z > 0) {
+ new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z;
+ } else if (delta.Z < 0) {
+ new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z;
+ }
+
+ // For each untested node
+ for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++)
+ for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++)
+ for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) {
+ MapNode n;
+ v3s16 np(x, y, z);
+ bool is_valid_position;
+
+ n = map.getNodeNoEx(np, &is_valid_position);
+ if (!(is_valid_position && isPointableNode(n, nodedef,
+ state->m_liquids_pointable))) {
+ continue;
+ }
+
+ PointedThing result;
+
+ std::vector<aabb3f> boxes;
+ n.getSelectionBoxes(nodedef, &boxes,
+ n.getNeighbors(np, &map));
+
+ // Is there a collision with a selection box?
+ bool is_colliding = false;
+ // Minimal distance of all collisions
+ float min_distance_sq = 10000000;
+
+ v3f npf = intToFloat(np, BS);
+ for (std::vector<aabb3f>::const_iterator i = boxes.begin();
+ i != boxes.end(); ++i) {
+ // Get current collision box
+ aabb3f box = *i;
+ box.MinEdge += npf;
+ box.MaxEdge += npf;
+
+ v3f intersection_point;
+ v3s16 intersection_normal;
+ if (!boxLineCollision(box, state->m_shootline.start,
+ state->m_shootline.getVector(), &intersection_point,
+ &intersection_normal))
+ continue;
+
+ f32 distanceSq = (intersection_point
+ - state->m_shootline.start).getLengthSQ();
+ // If this is the nearest collision, save it
+ if (min_distance_sq > distanceSq) {
+ min_distance_sq = distanceSq;
+ result.intersection_point = intersection_point;
+ result.intersection_normal = intersection_normal;
+ found_boxcenter = box.getCenter();
+ is_colliding = true;
+ }
+ }
+ // If there wasn't a collision, stop
+ if (!is_colliding) {
+ continue;
+ }
+ result.type = POINTEDTHING_NODE;
+ result.node_undersurface = np;
+ result.distanceSq = min_distance_sq;
+ // Set undersurface and abovesurface nodes
+ f32 d = 0.002 * BS;
+ v3f fake_intersection = result.intersection_point;
+ // Move intersection towards its source block.
+ if (fake_intersection.X < found_boxcenter.X) {
+ fake_intersection.X += d;
+ } else {
+ fake_intersection.X -= d;
+ }
+ if (fake_intersection.Y < found_boxcenter.Y) {
+ fake_intersection.Y += d;
+ } else {
+ fake_intersection.Y -= d;
+ }
+ if (fake_intersection.Z < found_boxcenter.Z) {
+ fake_intersection.Z += d;
+ } else {
+ fake_intersection.Z -= d;
+ }
+ result.node_real_undersurface = floatToInt(
+ fake_intersection, BS);
+ result.node_abovesurface = result.node_real_undersurface
+ + result.intersection_normal;
+ // Push found PointedThing
+ state->m_found.push(result);
+ // If this is nearer than the old nearest object,
+ // the search can be shorter
+ s16 newIndex = state->m_iterator.getIndex(
+ result.node_real_undersurface);
+ if (newIndex < lastIndex) {
+ lastIndex = newIndex;
+ }
+ }
+ // Next node
+ state->m_previous_node = state->m_iterator.m_current_node_pos;
+ state->m_iterator.next();
+ }
+ // Return empty PointedThing if nothing left on the ray
+ if (state->m_found.empty()) {
+ result->type = POINTEDTHING_NOTHING;
+ } else {
+ *result = state->m_found.top();
+ state->m_found.pop();
+ }
+}
+
void Environment::stepTimeOfDay(float dtime)
{
MutexAutoLock lock(this->m_time_lock);