From 077627181ee2eac3c0dacc3d8dc49825837e474c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Sun, 30 Oct 2022 16:53:14 +0100 Subject: [PATCH] Allow rotating entity selectionboxes (#12379) --- doc/lua_api.txt | 34 ++++++- games/devtest/mods/testentities/init.lua | 1 + .../mods/testentities/selectionbox.lua | 96 +++++++++++++++++++ src/client/clientenvironment.cpp | 25 +++-- src/client/game.cpp | 14 ++- src/client/hud.cpp | 31 +++--- src/client/hud.h | 5 + src/environment.cpp | 14 +-- src/object_properties.cpp | 7 ++ src/object_properties.h | 1 + src/raycast.cpp | 23 ++++- src/raycast.h | 6 +- src/script/common/c_content.cpp | 10 +- src/server/luaentity_sao.cpp | 5 + src/server/unit_sao.h | 14 +++ src/serverenvironment.cpp | 25 +++-- src/util/pointedthing.cpp | 7 +- src/util/pointedthing.h | 11 ++- 18 files changed, 271 insertions(+), 58 deletions(-) create mode 100644 games/devtest/mods/testentities/selectionbox.lua diff --git a/doc/lua_api.txt b/doc/lua_api.txt index fd4e2d3e5..9872f09ac 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1609,6 +1609,8 @@ Exact pointing location (currently only `Raycast` supports these fields): * `pointed_thing.intersection_normal`: Unit vector, points outwards of the selected selection box. This specifies which face is pointed at. Is a null vector `vector.zero()` when the pointer is inside the selection box. + For entities with rotated selection boxes, this will be rotated properly + by the entity's rotation - it will always be in absolute world space. @@ -7075,6 +7077,8 @@ child will follow movement and rotation of that bone. * `set_rotation(rot)` * `rot` is a vector (radians). X is pitch (elevation), Y is yaw (heading) and Z is roll (bank). + * Does not reset rotation incurred through `automatic_rotate`. + Remove & readd your objects to force a certain rotation. * `get_rotation()`: returns the rotation, a vector (radians) * `set_yaw(yaw)`: sets the yaw in radians (heading). * `get_yaw()`: returns number in radians @@ -7554,6 +7558,22 @@ It can be created via `Raycast(pos1, pos2, objects, liquids)` or * `liquids`: if false, liquid nodes (`liquidtype ~= "none"`) won't be returned. Default is false. +### Limitations + +Raycasts don't always work properly for attached objects as the server has no knowledge of models & bones. + +**Rotated selectionboxes paired with `automatic_rotate` are not reliable** either since the server +can't reliably know the total rotation of the objects on different clients (which may differ on a per-client basis). +The server calculates the total rotation incurred through `automatic_rotate` as a "best guess" +assuming the object was active & rotating on the client all the time since its creation. +This may be significantly out of sync with what clients see. +Additionally, network latency and delayed property sending may create a mismatch of client- & server rotations. + +In singleplayer mode, raycasts on objects with rotated selectionboxes & automatic rotate will usually only be slightly off; +toggling automatic rotation may however cause errors to add up. + +In multiplayer mode, the error may be arbitrarily large. + ### Methods * `next()`: returns a `pointed_thing` with exact pointing location @@ -7670,11 +7690,15 @@ Player properties need to be saved manually. collide_with_objects = true, -- Collide with other objects if physical = true - collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, - selectionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, - -- Selection box uses collision box dimensions when not set. - -- For both boxes: {xmin, ymin, zmin, xmax, ymax, zmax} in nodes from - -- object position. + collisionbox = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }, -- default + selectionbox = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, rotate = false }, + -- { xmin, ymin, zmin, xmax, ymax, zmax } in nodes from object position. + -- Collision boxes cannot rotate, setting `rotate = true` on it has no effect. + -- If not set, the selection box copies the collision box, and will also not rotate. + -- If `rotate = false`, the selection box will not rotate with the object itself, remaining fixed to the axes. + -- If `rotate = true`, it will match the object's rotation and any attachment rotations. + -- Raycasts use the selection box and object's rotation, but do *not* obey attachment rotations. + pointable = true, -- Whether the object can be pointed at diff --git a/games/devtest/mods/testentities/init.lua b/games/devtest/mods/testentities/init.lua index 94d8d47c2..6e0f8acb8 100644 --- a/games/devtest/mods/testentities/init.lua +++ b/games/devtest/mods/testentities/init.lua @@ -1,2 +1,3 @@ dofile(minetest.get_modpath("testentities").."/visuals.lua") +dofile(minetest.get_modpath("testentities").."/selectionbox.lua") dofile(minetest.get_modpath("testentities").."/armor.lua") diff --git a/games/devtest/mods/testentities/selectionbox.lua b/games/devtest/mods/testentities/selectionbox.lua new file mode 100644 index 000000000..36a8daa41 --- /dev/null +++ b/games/devtest/mods/testentities/selectionbox.lua @@ -0,0 +1,96 @@ +local function color(hex) + return ("blank.png^[noalpha^[colorize:#%06X:255"):format(hex) +end + +local function random_color() + return color(math.random(0, 0xFFFFFF)) +end + +local function random_rotation() + return 2 * math.pi * vector.new(math.random(), math.random(), math.random()) +end + +local active_selectionbox_entities = 0 -- count active entities + +minetest.register_entity("testentities:selectionbox", { + initial_properties = { + visual = "cube", + infotext = "Punch to randomize rotation, rightclick to toggle rotation" + }, + on_activate = function(self) + active_selectionbox_entities = active_selectionbox_entities + 1 + + local w, h, l = math.random(), math.random(), math.random() + self.object:set_properties({ + textures = {random_color(), random_color(), random_color(), random_color(), random_color(), random_color()}, + selectionbox = {rotate = true, -w/2, -h/2, -l/2, w/2, h/2, l/2}, + visual_size = vector.new(w, h, l), + automatic_rotate = 2 * math.pi * (math.random() - 0.5) + }) + assert(self.object:get_properties().selectionbox.rotate) + self.object:set_armor_groups({punch_operable = 1}) + self.object:set_rotation(random_rotation()) + end, + on_deactivate = function() + active_selectionbox_entities = active_selectionbox_entities - 1 + end, + on_punch = function(self) + self.object:set_rotation(random_rotation()) + end, + on_rightclick = function(self) + self.object:set_properties({ + automatic_rotate = self.object:get_properties().automatic_rotate == 0 and 2 * math.pi * (math.random() - 0.5) or 0 + }) + end +}) + +local hud_ids = {} +minetest.register_globalstep(function() + if active_selectionbox_entities == 0 then + return + end + + for _, player in pairs(minetest.get_connected_players()) do + local offset = player:get_eye_offset() + offset.y = offset.y + player:get_properties().eye_height + local pos1 = vector.add(player:get_pos(), offset) + local raycast = minetest.raycast(pos1, vector.add(pos1, vector.multiply(player:get_look_dir(), 10)), true, false) + local pointed_thing = raycast() + if pointed_thing.ref == player then + pointed_thing = raycast() + end + local remove_hud_element = true + local pname = player:get_player_name() + local hud_id = hud_ids[pname] + if pointed_thing and pointed_thing.type == "object" then + local ent = pointed_thing.ref:get_luaentity() + if ent and ent.name == "testentities:selectionbox" then + hud_ids[pname] = hud_id or player:hud_add({ + hud_elem_type = "text", -- See HUD element types + position = {x=0.5, y=0.5}, + text = "X", + number = 0xFF0000, + alignment = {x=0, y=0}, + }) + local shade = math.random(0, 0xFF) + minetest.add_particle({ + -- Random shade of red for the intersection point + texture = color(0x10000 * shade), + pos = pointed_thing.intersection_point, + size = 0.1 + }) + minetest.add_particle({ + -- Same shade of green for the corresponding intersection normal + texture = color(0x100 * shade), + pos = vector.add(pointed_thing.intersection_point, pointed_thing.intersection_normal * 0.1), + size = 0.1 + }) + remove_hud_element = false + end + end + if remove_hud_element and hud_id then + player:hud_remove(hud_id) + hud_ids[pname] = nil + end + end +end) diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index 0b6f1a325..1ce443bcc 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -505,15 +505,24 @@ void ClientEnvironment::getSelectedActiveObjects( if (!obj->getSelectionBox(&selection_box)) continue; - const v3f &pos = obj->getPosition(); - 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.emplace_back((s16) obj->getId(), current_intersection, current_normal, + v3f current_normal, current_raw_normal; + const v3f rel_pos = shootline_on_map.start - obj->getPosition(); + bool collision; + GenericCAO* gcao = dynamic_cast(obj); + if (gcao != nullptr && gcao->getProperties().rotate_selectionbox) { + gcao->getSceneNode()->updateAbsolutePosition(); + const v3f deg = obj->getSceneNode()->getAbsoluteTransformation().getRotationDegrees(); + collision = boxLineCollision(selection_box, deg, + rel_pos, line_vector, ¤t_intersection, ¤t_normal, ¤t_raw_normal); + } else { + collision = boxLineCollision(selection_box, rel_pos, line_vector, + ¤t_intersection, ¤t_normal); + current_raw_normal = current_normal; + } + if (collision) { + current_intersection += obj->getPosition(); + objects.emplace_back(obj->getId(), current_intersection, current_normal, current_raw_normal, (current_intersection - shootline_on_map.start).getLengthSQ()); } } diff --git a/src/client/game.cpp b/src/client/game.cpp index 91c93ef7f..cdcfde759 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -3297,7 +3297,7 @@ PointedThing Game::updatePointedThing( { std::vector *selectionboxes = hud->getSelectionBoxes(); selectionboxes->clear(); - hud->setSelectedFaceNormal(v3f(0.0, 0.0, 0.0)); + hud->setSelectedFaceNormal(v3f()); static thread_local const bool show_entity_selectionbox = g_settings->getBool( "show_entity_selectionbox"); @@ -3321,7 +3321,13 @@ PointedThing Game::updatePointedThing( v3f pos = runData.selected_object->getPosition(); selectionboxes->push_back(aabb3f(selection_box)); hud->setSelectionPos(pos, camera_offset); + GenericCAO* gcao = dynamic_cast(runData.selected_object); + if (gcao != nullptr && gcao->getProperties().rotate_selectionbox) + hud->setSelectionRotation(gcao->getSceneNode()->getAbsoluteTransformation().getRotationDegrees()); + else + hud->setSelectionRotation(v3f()); } + hud->setSelectedFaceNormal(result.raw_intersection_normal); } else if (result.type == POINTEDTHING_NODE) { // Update selection boxes MapNode n = map.getNode(result.node_undersurface); @@ -3339,10 +3345,8 @@ PointedThing Game::updatePointedThing( } hud->setSelectionPos(intToFloat(result.node_undersurface, BS), camera_offset); - hud->setSelectedFaceNormal(v3f( - result.intersection_normal.X, - result.intersection_normal.Y, - result.intersection_normal.Z)); + hud->setSelectionRotation(v3f()); + hud->setSelectedFaceNormal(result.intersection_normal); } // Update selection mesh light level and vertex colors diff --git a/src/client/hud.cpp b/src/client/hud.cpp index e8ccdbb2b..58f5ec5f1 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -826,28 +826,31 @@ void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset) void Hud::drawSelectionMesh() { + if (m_mode == HIGHLIGHT_NONE || (m_mode == HIGHLIGHT_HALO && !m_selection_mesh)) + return; + const video::SMaterial oldmaterial = driver->getMaterial2D(); + driver->setMaterial(m_selection_material); + const core::matrix4 oldtransform = driver->getTransform(video::ETS_WORLD); + + core::matrix4 translate; + translate.setTranslation(m_selection_pos_with_offset); + core::matrix4 rotation; + rotation.setRotationDegrees(m_selection_rotation); + driver->setTransform(video::ETS_WORLD, translate * rotation); + if (m_mode == HIGHLIGHT_BOX) { // Draw 3D selection boxes - video::SMaterial oldmaterial = driver->getMaterial2D(); - driver->setMaterial(m_selection_material); for (auto & selection_box : m_selection_boxes) { - aabb3f box = aabb3f( - selection_box.MinEdge + m_selection_pos_with_offset, - selection_box.MaxEdge + m_selection_pos_with_offset); - u32 r = (selectionbox_argb.getRed() * m_selection_mesh_color.getRed() / 255); u32 g = (selectionbox_argb.getGreen() * m_selection_mesh_color.getGreen() / 255); u32 b = (selectionbox_argb.getBlue() * m_selection_mesh_color.getBlue() / 255); - driver->draw3DBox(box, video::SColor(255, r, g, b)); + driver->draw3DBox(selection_box, video::SColor(255, r, g, b)); } - driver->setMaterial(oldmaterial); } else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) { // Draw selection mesh - video::SMaterial oldmaterial = driver->getMaterial2D(); - driver->setMaterial(m_selection_material); setMeshColor(m_selection_mesh, m_selection_mesh_color); video::SColor face_color(0, MYMIN(255, m_selection_mesh_color.getRed() * 1.5), @@ -855,16 +858,14 @@ void Hud::drawSelectionMesh() MYMIN(255, m_selection_mesh_color.getBlue() * 1.5)); setMeshColorByNormal(m_selection_mesh, m_selected_face_normal, face_color); - scene::IMesh* mesh = cloneMesh(m_selection_mesh); - translateMesh(mesh, m_selection_pos_with_offset); u32 mc = m_selection_mesh->getMeshBufferCount(); for (u32 i = 0; i < mc; i++) { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); + scene::IMeshBuffer *buf = m_selection_mesh->getMeshBuffer(i); driver->drawMeshBuffer(buf); } - mesh->drop(); - driver->setMaterial(oldmaterial); } + driver->setMaterial(oldmaterial); + driver->setTransform(video::ETS_WORLD, oldtransform); } enum Hud::BlockBoundsMode Hud::toggleBlockBounds() diff --git a/src/client/hud.h b/src/client/hud.h index fd79183a0..b6ff84243 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -75,6 +75,10 @@ public: v3f getSelectionPos() const { return m_selection_pos; } + void setSelectionRotation(v3f rotation) { m_selection_rotation = rotation; } + + v3f getSelectionRotation() const { return m_selection_rotation; } + void setSelectionMeshColor(const video::SColor &color) { m_selection_mesh_color = color; @@ -126,6 +130,7 @@ private: std::vector m_halo_boxes; v3f m_selection_pos; v3f m_selection_pos_with_offset; + v3f m_selection_rotation; scene::IMesh *m_selection_mesh = nullptr; video::SColor m_selection_mesh_color; diff --git a/src/environment.cpp b/src/environment.cpp index b04f77557..514b15d01 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -202,21 +202,21 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result) // ID of the current box (loop counter) u16 id = 0; + // Do calculations relative to the node center + // to translate the ray rather than the boxes v3f npf = intToFloat(np, BS); - // This loop translates the boxes to their in-world place. + v3f rel_start = state->m_shootline.start - npf; for (aabb3f &box : boxes) { - box.MinEdge += npf; - box.MaxEdge += npf; - v3f intersection_point; - v3s16 intersection_normal; - if (!boxLineCollision(box, state->m_shootline.start, + v3f intersection_normal; + if (!boxLineCollision(box, rel_start, state->m_shootline.getVector(), &intersection_point, &intersection_normal)) { ++id; continue; } + intersection_point += npf; // translate back to world coords f32 distanceSq = (intersection_point - state->m_shootline.start).getLengthSQ(); // If this is the nearest collision, save it @@ -259,7 +259,7 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result) result.node_real_undersurface = floatToInt( fake_intersection, BS); result.node_abovesurface = result.node_real_undersurface - + result.intersection_normal; + + floatToInt(result.intersection_normal, 1.0f); // Push found PointedThing state->m_found.push(result); // If this is nearer than the old nearest object, diff --git a/src/object_properties.cpp b/src/object_properties.cpp index c7f6becf0..e4c656946 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -72,6 +72,7 @@ std::string ObjectProperties::dump() os << ", nametag_bgcolor=null "; os << ", selectionbox=" << PP(selectionbox.MinEdge) << "," << PP(selectionbox.MaxEdge); + os << ", rotate_selectionbox=" << rotate_selectionbox; os << ", pointable=" << pointable; os << ", static_save=" << static_save; os << ", eye_height=" << eye_height; @@ -169,6 +170,7 @@ void ObjectProperties::serialize(std::ostream &os) const else writeARGB8(os, nametag_bgcolor.value()); + writeU8(os, rotate_selectionbox); // Add stuff only at the bottom. // Never remove anything, because we don't want new versions of this } @@ -236,5 +238,10 @@ void ObjectProperties::deSerialize(std::istream &is) nametag_bgcolor = bgcolor; else nametag_bgcolor = nullopt; + + tmp = readU8(is); + if (is.eof()) + return; + rotate_selectionbox = tmp; } catch (SerializationError &e) {} } diff --git a/src/object_properties.h b/src/object_properties.h index 79866a22c..f4c425d7b 100644 --- a/src/object_properties.h +++ b/src/object_properties.h @@ -35,6 +35,7 @@ struct ObjectProperties // Values are BS=1 aabb3f collisionbox = aabb3f(-0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f); aabb3f selectionbox = aabb3f(-0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f); + bool rotate_selectionbox = false; bool pointable = true; std::string visual = "sprite"; std::string mesh = ""; diff --git a/src/raycast.cpp b/src/raycast.cpp index ebc40235d..ead024dd1 100644 --- a/src/raycast.cpp +++ b/src/raycast.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "raycast.h" #include "irr_v3d.h" #include "irr_aabb3d.h" +#include #include "constants.h" bool RaycastSort::operator() (const PointedThing &pt1, @@ -68,7 +69,7 @@ RaycastState::RaycastState(const core::line3d &shootline, bool boxLineCollision(const aabb3f &box, const v3f &start, - const v3f &dir, v3f *collision_point, v3s16 *collision_normal) + const v3f &dir, v3f *collision_point, v3f *collision_normal) { if (box.isPointInside(start)) { *collision_point = start; @@ -135,3 +136,23 @@ bool boxLineCollision(const aabb3f &box, const v3f &start, } return false; } + +bool boxLineCollision(const aabb3f &box, const v3f &rotation, + const v3f &start, const v3f &dir, + v3f *collision_point, v3f *collision_normal, v3f *raw_collision_normal) +{ + // Inversely transform the ray rather than rotating the box faces; + // this allows us to continue using a simple ray - AABB intersection + core::quaternion rot(rotation * core::DEGTORAD); + rot.makeInverse(); + + bool collision = boxLineCollision(box, rot * start, rot * dir, collision_point, collision_normal); + if (!collision) return collision; + + // Transform the results back + rot.makeInverse(); + *collision_point = rot * *collision_point; + *raw_collision_normal = *collision_normal; + *collision_normal = rot * *collision_normal; + return collision; +} diff --git a/src/raycast.h b/src/raycast.h index 734efd6ad..8da075738 100644 --- a/src/raycast.h +++ b/src/raycast.h @@ -74,4 +74,8 @@ public: * @returns true if a collision point was found */ bool boxLineCollision(const aabb3f &box, const v3f &start, const v3f &dir, - v3f *collision_point, v3s16 *collision_normal); + v3f *collision_point, v3f *collision_normal); + +bool boxLineCollision(const aabb3f &box, const v3f &box_rotation, + const v3f &start, const v3f &dir, + v3f *collision_point, v3f *collision_normal, v3f *raw_collision_normal); diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index cbe2d328c..6203ea78c 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -236,10 +236,12 @@ void read_object_properties(lua_State *L, int index, lua_pop(L, 1); lua_getfield(L, -1, "selectionbox"); - if (lua_istable(L, -1)) + if (lua_istable(L, -1)) { + getboolfield(L, -1, "rotate", prop->rotate_selectionbox); prop->selectionbox = read_aabb3f(L, -1, 1.0); - else if (collisionbox_defined) + } else if (collisionbox_defined) { prop->selectionbox = prop->collisionbox; + } lua_pop(L, 1); getboolfield(L, -1, "pointable", prop->pointable); @@ -377,6 +379,8 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) push_aabb3f(L, prop->collisionbox); lua_setfield(L, -2, "collisionbox"); push_aabb3f(L, prop->selectionbox); + lua_pushboolean(L, prop->rotate_selectionbox); + lua_setfield(L, -2, "rotate"); lua_setfield(L, -2, "selectionbox"); lua_pushboolean(L, prop->pointable); lua_setfield(L, -2, "pointable"); @@ -1880,7 +1884,7 @@ void push_pointed_thing(lua_State *L, const PointedThing &pointed, bool csm, if (hitpoint && (pointed.type != POINTEDTHING_NOTHING)) { push_v3f(L, pointed.intersection_point / BS); // convert to node coords lua_setfield(L, -2, "intersection_point"); - push_v3s16(L, pointed.intersection_normal); + push_v3f(L, pointed.intersection_normal); lua_setfield(L, -2, "intersection_normal"); lua_pushinteger(L, pointed.box_id + 1); // change to Lua array index lua_setfield(L, -2, "box_id"); diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index b25f19b85..5624c0f55 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -197,6 +197,11 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) } } + if (fabs(m_prop.automatic_rotate) > 0.001f) { + m_rotation_add_yaw = modulo360f(m_rotation_add_yaw + dtime * core::RADTODEG * + m_prop.automatic_rotate); + } + if(m_registered) { m_env->getScriptIface()->luaentity_Step(m_id, dtime, moveresult_p); } diff --git a/src/server/unit_sao.h b/src/server/unit_sao.h index a21e055c5..dedb1874e 100644 --- a/src/server/unit_sao.h +++ b/src/server/unit_sao.h @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "object_properties.h" #include "serveractiveobject.h" +#include +#include "util/numeric.h" class UnitSAO : public ServerActiveObject { @@ -36,6 +38,17 @@ public: // Rotation void setRotation(v3f rotation) { m_rotation = rotation; } const v3f &getRotation() const { return m_rotation; } + const v3f getTotalRotation() const { + // This replicates what happens clientside serverside + core::matrix4 rot; + setPitchYawRoll(rot, -m_rotation); + v3f res; + // First rotate by m_rotation, then rotate by the automatic rotate yaw + (core::quaternion(v3f(0, -m_rotation_add_yaw * core::DEGTORAD, 0)) + * core::quaternion(rot.getRotationDegrees() * core::DEGTORAD)) + .toEuler(res); + return res * core::RADTODEG; + } v3f getRadRotation() { return m_rotation * core::DEGTORAD; } // Deprecated @@ -95,6 +108,7 @@ protected: u16 m_hp = 1; v3f m_rotation; + f32 m_rotation_add_yaw = 0; ItemGroupList m_armor_groups; diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 4cdb21a4b..f893064de 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1771,16 +1771,27 @@ void ServerEnvironment::getSelectedActiveObjects( continue; v3f pos = obj->getBasePosition(); - - aabb3f offsetted_box(selection_box.MinEdge + pos, - selection_box.MaxEdge + pos); + v3f rel_pos = shootline_on_map.start - pos; v3f current_intersection; - v3s16 current_normal; - if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector, - ¤t_intersection, ¤t_normal)) { + v3f current_normal; + v3f current_raw_normal; + + ObjectProperties *props = obj->accessObjectProperties(); + bool collision; + UnitSAO* usao = dynamic_cast(obj); + if (props->rotate_selectionbox && usao != nullptr) { + collision = boxLineCollision(selection_box, usao->getTotalRotation(), + rel_pos, line_vector, ¤t_intersection, ¤t_normal, ¤t_raw_normal); + } else { + collision = boxLineCollision(selection_box, rel_pos, line_vector, + ¤t_intersection, ¤t_normal); + current_raw_normal = current_normal; + } + if (collision) { + current_intersection += pos; objects.emplace_back( - (s16) obj->getId(), current_intersection, current_normal, + (s16) obj->getId(), current_intersection, current_normal, current_raw_normal, (current_intersection - shootline_on_map.start).getLengthSQ()); } } diff --git a/src/util/pointedthing.cpp b/src/util/pointedthing.cpp index 6aa37dfe8..f6b4b77b6 100644 --- a/src/util/pointedthing.cpp +++ b/src/util/pointedthing.cpp @@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include PointedThing::PointedThing(const v3s16 &under, const v3s16 &above, - const v3s16 &real_under, const v3f &point, const v3s16 &normal, + const v3s16 &real_under, const v3f &point, const v3f &normal, u16 box_id, f32 distSq): type(POINTEDTHING_NODE), node_undersurface(under), @@ -36,12 +36,13 @@ PointedThing::PointedThing(const v3s16 &under, const v3s16 &above, distanceSq(distSq) {} -PointedThing::PointedThing(u16 id, const v3f &point, const v3s16 &normal, - f32 distSq) : +PointedThing::PointedThing(u16 id, const v3f &point, + const v3f &normal, const v3f &raw_normal, f32 distSq) : type(POINTEDTHING_OBJECT), object_id(id), intersection_point(point), intersection_normal(normal), + raw_intersection_normal(raw_normal), distanceSq(distSq) {} diff --git a/src/util/pointedthing.h b/src/util/pointedthing.h index 68b183195..e4f2eb20c 100644 --- a/src/util/pointedthing.h +++ b/src/util/pointedthing.h @@ -74,7 +74,12 @@ struct PointedThing * This is perpendicular to the face the ray hits, * points outside of the box and it's length is 1. */ - v3s16 intersection_normal; + v3f intersection_normal; + /*! + * Only valid if type is POINTEDTHING_OBJECT. + * Raw normal vector of the intersection before applying rotation. + */ + v3f raw_intersection_normal; /*! * Only valid if type isn't POINTEDTHING_NONE. * Indicates which selection box is selected, if there are more of them. @@ -90,10 +95,10 @@ struct PointedThing PointedThing() = default; //! Constructor for POINTEDTHING_NODE PointedThing(const v3s16 &under, const v3s16 &above, - const v3s16 &real_under, const v3f &point, const v3s16 &normal, + const v3s16 &real_under, const v3f &point, const v3f &normal, u16 box_id, f32 distSq); //! Constructor for POINTEDTHING_OBJECT - PointedThing(u16 id, const v3f &point, const v3s16 &normal, f32 distSq); + PointedThing(u16 id, const v3f &point, const v3f &normal, const v3f &raw_normal, f32 distSq); std::string dump() const; void serialize(std::ostream &os) const; void deSerialize(std::istream &is);