mirror of
https://github.com/minetest/minetest.git
synced 2024-11-27 10:03:45 +01:00
Allow rotating entity selectionboxes (#12379)
This commit is contained in:
parent
b829231992
commit
077627181e
@ -1609,6 +1609,8 @@ Exact pointing location (currently only `Raycast` supports these fields):
|
|||||||
* `pointed_thing.intersection_normal`: Unit vector, points outwards of the
|
* `pointed_thing.intersection_normal`: Unit vector, points outwards of the
|
||||||
selected selection box. This specifies which face is pointed at.
|
selected selection box. This specifies which face is pointed at.
|
||||||
Is a null vector `vector.zero()` when the pointer is inside the selection box.
|
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)`
|
* `set_rotation(rot)`
|
||||||
* `rot` is a vector (radians). X is pitch (elevation), Y is yaw (heading)
|
* `rot` is a vector (radians). X is pitch (elevation), Y is yaw (heading)
|
||||||
and Z is roll (bank).
|
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)
|
* `get_rotation()`: returns the rotation, a vector (radians)
|
||||||
* `set_yaw(yaw)`: sets the yaw in radians (heading).
|
* `set_yaw(yaw)`: sets the yaw in radians (heading).
|
||||||
* `get_yaw()`: returns number in radians
|
* `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
|
* `liquids`: if false, liquid nodes (`liquidtype ~= "none"`) won't be
|
||||||
returned. Default is false.
|
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
|
### Methods
|
||||||
|
|
||||||
* `next()`: returns a `pointed_thing` with exact pointing location
|
* `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_objects = true,
|
||||||
-- Collide with other objects if physical = true
|
-- Collide with other objects if physical = true
|
||||||
|
|
||||||
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
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},
|
selectionbox = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, rotate = false },
|
||||||
-- Selection box uses collision box dimensions when not set.
|
-- { xmin, ymin, zmin, xmax, ymax, zmax } in nodes from object position.
|
||||||
-- For both boxes: {xmin, ymin, zmin, xmax, ymax, zmax} in nodes from
|
-- Collision boxes cannot rotate, setting `rotate = true` on it has no effect.
|
||||||
-- object position.
|
-- 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,
|
pointable = true,
|
||||||
-- Whether the object can be pointed at
|
-- Whether the object can be pointed at
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
dofile(minetest.get_modpath("testentities").."/visuals.lua")
|
dofile(minetest.get_modpath("testentities").."/visuals.lua")
|
||||||
|
dofile(minetest.get_modpath("testentities").."/selectionbox.lua")
|
||||||
dofile(minetest.get_modpath("testentities").."/armor.lua")
|
dofile(minetest.get_modpath("testentities").."/armor.lua")
|
||||||
|
96
games/devtest/mods/testentities/selectionbox.lua
Normal file
96
games/devtest/mods/testentities/selectionbox.lua
Normal file
@ -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)
|
@ -505,15 +505,24 @@ void ClientEnvironment::getSelectedActiveObjects(
|
|||||||
if (!obj->getSelectionBox(&selection_box))
|
if (!obj->getSelectionBox(&selection_box))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const v3f &pos = obj->getPosition();
|
|
||||||
aabb3f offsetted_box(selection_box.MinEdge + pos,
|
|
||||||
selection_box.MaxEdge + pos);
|
|
||||||
|
|
||||||
v3f current_intersection;
|
v3f current_intersection;
|
||||||
v3s16 current_normal;
|
v3f current_normal, current_raw_normal;
|
||||||
if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
|
const v3f rel_pos = shootline_on_map.start - obj->getPosition();
|
||||||
¤t_intersection, ¤t_normal)) {
|
bool collision;
|
||||||
objects.emplace_back((s16) obj->getId(), current_intersection, current_normal,
|
GenericCAO* gcao = dynamic_cast<GenericCAO*>(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());
|
(current_intersection - shootline_on_map.start).getLengthSQ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3297,7 +3297,7 @@ PointedThing Game::updatePointedThing(
|
|||||||
{
|
{
|
||||||
std::vector<aabb3f> *selectionboxes = hud->getSelectionBoxes();
|
std::vector<aabb3f> *selectionboxes = hud->getSelectionBoxes();
|
||||||
selectionboxes->clear();
|
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(
|
static thread_local const bool show_entity_selectionbox = g_settings->getBool(
|
||||||
"show_entity_selectionbox");
|
"show_entity_selectionbox");
|
||||||
|
|
||||||
@ -3321,7 +3321,13 @@ PointedThing Game::updatePointedThing(
|
|||||||
v3f pos = runData.selected_object->getPosition();
|
v3f pos = runData.selected_object->getPosition();
|
||||||
selectionboxes->push_back(aabb3f(selection_box));
|
selectionboxes->push_back(aabb3f(selection_box));
|
||||||
hud->setSelectionPos(pos, camera_offset);
|
hud->setSelectionPos(pos, camera_offset);
|
||||||
|
GenericCAO* gcao = dynamic_cast<GenericCAO*>(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) {
|
} else if (result.type == POINTEDTHING_NODE) {
|
||||||
// Update selection boxes
|
// Update selection boxes
|
||||||
MapNode n = map.getNode(result.node_undersurface);
|
MapNode n = map.getNode(result.node_undersurface);
|
||||||
@ -3339,10 +3345,8 @@ PointedThing Game::updatePointedThing(
|
|||||||
}
|
}
|
||||||
hud->setSelectionPos(intToFloat(result.node_undersurface, BS),
|
hud->setSelectionPos(intToFloat(result.node_undersurface, BS),
|
||||||
camera_offset);
|
camera_offset);
|
||||||
hud->setSelectedFaceNormal(v3f(
|
hud->setSelectionRotation(v3f());
|
||||||
result.intersection_normal.X,
|
hud->setSelectedFaceNormal(result.intersection_normal);
|
||||||
result.intersection_normal.Y,
|
|
||||||
result.intersection_normal.Z));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update selection mesh light level and vertex colors
|
// Update selection mesh light level and vertex colors
|
||||||
|
@ -826,28 +826,31 @@ void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
|
|||||||
|
|
||||||
void Hud::drawSelectionMesh()
|
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) {
|
if (m_mode == HIGHLIGHT_BOX) {
|
||||||
// Draw 3D selection boxes
|
// Draw 3D selection boxes
|
||||||
video::SMaterial oldmaterial = driver->getMaterial2D();
|
|
||||||
driver->setMaterial(m_selection_material);
|
|
||||||
for (auto & selection_box : m_selection_boxes) {
|
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() *
|
u32 r = (selectionbox_argb.getRed() *
|
||||||
m_selection_mesh_color.getRed() / 255);
|
m_selection_mesh_color.getRed() / 255);
|
||||||
u32 g = (selectionbox_argb.getGreen() *
|
u32 g = (selectionbox_argb.getGreen() *
|
||||||
m_selection_mesh_color.getGreen() / 255);
|
m_selection_mesh_color.getGreen() / 255);
|
||||||
u32 b = (selectionbox_argb.getBlue() *
|
u32 b = (selectionbox_argb.getBlue() *
|
||||||
m_selection_mesh_color.getBlue() / 255);
|
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) {
|
} else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
|
||||||
// Draw selection mesh
|
// Draw selection mesh
|
||||||
video::SMaterial oldmaterial = driver->getMaterial2D();
|
|
||||||
driver->setMaterial(m_selection_material);
|
|
||||||
setMeshColor(m_selection_mesh, m_selection_mesh_color);
|
setMeshColor(m_selection_mesh, m_selection_mesh_color);
|
||||||
video::SColor face_color(0,
|
video::SColor face_color(0,
|
||||||
MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
|
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));
|
MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
|
||||||
setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
|
setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
|
||||||
face_color);
|
face_color);
|
||||||
scene::IMesh* mesh = cloneMesh(m_selection_mesh);
|
|
||||||
translateMesh(mesh, m_selection_pos_with_offset);
|
|
||||||
u32 mc = m_selection_mesh->getMeshBufferCount();
|
u32 mc = m_selection_mesh->getMeshBufferCount();
|
||||||
for (u32 i = 0; i < mc; i++) {
|
for (u32 i = 0; i < mc; i++) {
|
||||||
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
|
scene::IMeshBuffer *buf = m_selection_mesh->getMeshBuffer(i);
|
||||||
driver->drawMeshBuffer(buf);
|
driver->drawMeshBuffer(buf);
|
||||||
}
|
}
|
||||||
mesh->drop();
|
|
||||||
driver->setMaterial(oldmaterial);
|
|
||||||
}
|
}
|
||||||
|
driver->setMaterial(oldmaterial);
|
||||||
|
driver->setTransform(video::ETS_WORLD, oldtransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Hud::BlockBoundsMode Hud::toggleBlockBounds()
|
enum Hud::BlockBoundsMode Hud::toggleBlockBounds()
|
||||||
|
@ -75,6 +75,10 @@ public:
|
|||||||
|
|
||||||
v3f getSelectionPos() const { return m_selection_pos; }
|
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)
|
void setSelectionMeshColor(const video::SColor &color)
|
||||||
{
|
{
|
||||||
m_selection_mesh_color = color;
|
m_selection_mesh_color = color;
|
||||||
@ -126,6 +130,7 @@ private:
|
|||||||
std::vector<aabb3f> m_halo_boxes;
|
std::vector<aabb3f> m_halo_boxes;
|
||||||
v3f m_selection_pos;
|
v3f m_selection_pos;
|
||||||
v3f m_selection_pos_with_offset;
|
v3f m_selection_pos_with_offset;
|
||||||
|
v3f m_selection_rotation;
|
||||||
|
|
||||||
scene::IMesh *m_selection_mesh = nullptr;
|
scene::IMesh *m_selection_mesh = nullptr;
|
||||||
video::SColor m_selection_mesh_color;
|
video::SColor m_selection_mesh_color;
|
||||||
|
@ -202,21 +202,21 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result)
|
|||||||
// ID of the current box (loop counter)
|
// ID of the current box (loop counter)
|
||||||
u16 id = 0;
|
u16 id = 0;
|
||||||
|
|
||||||
|
// Do calculations relative to the node center
|
||||||
|
// to translate the ray rather than the boxes
|
||||||
v3f npf = intToFloat(np, BS);
|
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) {
|
for (aabb3f &box : boxes) {
|
||||||
box.MinEdge += npf;
|
|
||||||
box.MaxEdge += npf;
|
|
||||||
|
|
||||||
v3f intersection_point;
|
v3f intersection_point;
|
||||||
v3s16 intersection_normal;
|
v3f intersection_normal;
|
||||||
if (!boxLineCollision(box, state->m_shootline.start,
|
if (!boxLineCollision(box, rel_start,
|
||||||
state->m_shootline.getVector(), &intersection_point,
|
state->m_shootline.getVector(), &intersection_point,
|
||||||
&intersection_normal)) {
|
&intersection_normal)) {
|
||||||
++id;
|
++id;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
intersection_point += npf; // translate back to world coords
|
||||||
f32 distanceSq = (intersection_point
|
f32 distanceSq = (intersection_point
|
||||||
- state->m_shootline.start).getLengthSQ();
|
- state->m_shootline.start).getLengthSQ();
|
||||||
// If this is the nearest collision, save it
|
// If this is the nearest collision, save it
|
||||||
@ -259,7 +259,7 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result)
|
|||||||
result.node_real_undersurface = floatToInt(
|
result.node_real_undersurface = floatToInt(
|
||||||
fake_intersection, BS);
|
fake_intersection, BS);
|
||||||
result.node_abovesurface = result.node_real_undersurface
|
result.node_abovesurface = result.node_real_undersurface
|
||||||
+ result.intersection_normal;
|
+ floatToInt(result.intersection_normal, 1.0f);
|
||||||
// Push found PointedThing
|
// Push found PointedThing
|
||||||
state->m_found.push(result);
|
state->m_found.push(result);
|
||||||
// If this is nearer than the old nearest object,
|
// If this is nearer than the old nearest object,
|
||||||
|
@ -72,6 +72,7 @@ std::string ObjectProperties::dump()
|
|||||||
os << ", nametag_bgcolor=null ";
|
os << ", nametag_bgcolor=null ";
|
||||||
|
|
||||||
os << ", selectionbox=" << PP(selectionbox.MinEdge) << "," << PP(selectionbox.MaxEdge);
|
os << ", selectionbox=" << PP(selectionbox.MinEdge) << "," << PP(selectionbox.MaxEdge);
|
||||||
|
os << ", rotate_selectionbox=" << rotate_selectionbox;
|
||||||
os << ", pointable=" << pointable;
|
os << ", pointable=" << pointable;
|
||||||
os << ", static_save=" << static_save;
|
os << ", static_save=" << static_save;
|
||||||
os << ", eye_height=" << eye_height;
|
os << ", eye_height=" << eye_height;
|
||||||
@ -169,6 +170,7 @@ void ObjectProperties::serialize(std::ostream &os) const
|
|||||||
else
|
else
|
||||||
writeARGB8(os, nametag_bgcolor.value());
|
writeARGB8(os, nametag_bgcolor.value());
|
||||||
|
|
||||||
|
writeU8(os, rotate_selectionbox);
|
||||||
// Add stuff only at the bottom.
|
// Add stuff only at the bottom.
|
||||||
// Never remove anything, because we don't want new versions of this
|
// 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;
|
nametag_bgcolor = bgcolor;
|
||||||
else
|
else
|
||||||
nametag_bgcolor = nullopt;
|
nametag_bgcolor = nullopt;
|
||||||
|
|
||||||
|
tmp = readU8(is);
|
||||||
|
if (is.eof())
|
||||||
|
return;
|
||||||
|
rotate_selectionbox = tmp;
|
||||||
} catch (SerializationError &e) {}
|
} catch (SerializationError &e) {}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ struct ObjectProperties
|
|||||||
// Values are BS=1
|
// Values are BS=1
|
||||||
aabb3f collisionbox = aabb3f(-0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f);
|
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);
|
aabb3f selectionbox = aabb3f(-0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f);
|
||||||
|
bool rotate_selectionbox = false;
|
||||||
bool pointable = true;
|
bool pointable = true;
|
||||||
std::string visual = "sprite";
|
std::string visual = "sprite";
|
||||||
std::string mesh = "";
|
std::string mesh = "";
|
||||||
|
@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "raycast.h"
|
#include "raycast.h"
|
||||||
#include "irr_v3d.h"
|
#include "irr_v3d.h"
|
||||||
#include "irr_aabb3d.h"
|
#include "irr_aabb3d.h"
|
||||||
|
#include <quaternion.h>
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
|
||||||
bool RaycastSort::operator() (const PointedThing &pt1,
|
bool RaycastSort::operator() (const PointedThing &pt1,
|
||||||
@ -68,7 +69,7 @@ RaycastState::RaycastState(const core::line3d<f32> &shootline,
|
|||||||
|
|
||||||
|
|
||||||
bool boxLineCollision(const aabb3f &box, const v3f &start,
|
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)) {
|
if (box.isPointInside(start)) {
|
||||||
*collision_point = start;
|
*collision_point = start;
|
||||||
@ -135,3 +136,23 @@ bool boxLineCollision(const aabb3f &box, const v3f &start,
|
|||||||
}
|
}
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
@ -74,4 +74,8 @@ public:
|
|||||||
* @returns true if a collision point was found
|
* @returns true if a collision point was found
|
||||||
*/
|
*/
|
||||||
bool boxLineCollision(const aabb3f &box, const v3f &start, const v3f &dir,
|
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);
|
||||||
|
@ -236,10 +236,12 @@ void read_object_properties(lua_State *L, int index,
|
|||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
lua_getfield(L, -1, "selectionbox");
|
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);
|
prop->selectionbox = read_aabb3f(L, -1, 1.0);
|
||||||
else if (collisionbox_defined)
|
} else if (collisionbox_defined) {
|
||||||
prop->selectionbox = prop->collisionbox;
|
prop->selectionbox = prop->collisionbox;
|
||||||
|
}
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
getboolfield(L, -1, "pointable", prop->pointable);
|
getboolfield(L, -1, "pointable", prop->pointable);
|
||||||
@ -377,6 +379,8 @@ void push_object_properties(lua_State *L, ObjectProperties *prop)
|
|||||||
push_aabb3f(L, prop->collisionbox);
|
push_aabb3f(L, prop->collisionbox);
|
||||||
lua_setfield(L, -2, "collisionbox");
|
lua_setfield(L, -2, "collisionbox");
|
||||||
push_aabb3f(L, prop->selectionbox);
|
push_aabb3f(L, prop->selectionbox);
|
||||||
|
lua_pushboolean(L, prop->rotate_selectionbox);
|
||||||
|
lua_setfield(L, -2, "rotate");
|
||||||
lua_setfield(L, -2, "selectionbox");
|
lua_setfield(L, -2, "selectionbox");
|
||||||
lua_pushboolean(L, prop->pointable);
|
lua_pushboolean(L, prop->pointable);
|
||||||
lua_setfield(L, -2, "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)) {
|
if (hitpoint && (pointed.type != POINTEDTHING_NOTHING)) {
|
||||||
push_v3f(L, pointed.intersection_point / BS); // convert to node coords
|
push_v3f(L, pointed.intersection_point / BS); // convert to node coords
|
||||||
lua_setfield(L, -2, "intersection_point");
|
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_setfield(L, -2, "intersection_normal");
|
||||||
lua_pushinteger(L, pointed.box_id + 1); // change to Lua array index
|
lua_pushinteger(L, pointed.box_id + 1); // change to Lua array index
|
||||||
lua_setfield(L, -2, "box_id");
|
lua_setfield(L, -2, "box_id");
|
||||||
|
@ -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) {
|
if(m_registered) {
|
||||||
m_env->getScriptIface()->luaentity_Step(m_id, dtime, moveresult_p);
|
m_env->getScriptIface()->luaentity_Step(m_id, dtime, moveresult_p);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
|
|
||||||
#include "object_properties.h"
|
#include "object_properties.h"
|
||||||
#include "serveractiveobject.h"
|
#include "serveractiveobject.h"
|
||||||
|
#include <quaternion.h>
|
||||||
|
#include "util/numeric.h"
|
||||||
|
|
||||||
class UnitSAO : public ServerActiveObject
|
class UnitSAO : public ServerActiveObject
|
||||||
{
|
{
|
||||||
@ -36,6 +38,17 @@ public:
|
|||||||
// Rotation
|
// Rotation
|
||||||
void setRotation(v3f rotation) { m_rotation = rotation; }
|
void setRotation(v3f rotation) { m_rotation = rotation; }
|
||||||
const v3f &getRotation() const { return m_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; }
|
v3f getRadRotation() { return m_rotation * core::DEGTORAD; }
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated
|
||||||
@ -95,6 +108,7 @@ protected:
|
|||||||
u16 m_hp = 1;
|
u16 m_hp = 1;
|
||||||
|
|
||||||
v3f m_rotation;
|
v3f m_rotation;
|
||||||
|
f32 m_rotation_add_yaw = 0;
|
||||||
|
|
||||||
ItemGroupList m_armor_groups;
|
ItemGroupList m_armor_groups;
|
||||||
|
|
||||||
|
@ -1771,16 +1771,27 @@ void ServerEnvironment::getSelectedActiveObjects(
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
v3f pos = obj->getBasePosition();
|
v3f pos = obj->getBasePosition();
|
||||||
|
v3f rel_pos = shootline_on_map.start - pos;
|
||||||
aabb3f offsetted_box(selection_box.MinEdge + pos,
|
|
||||||
selection_box.MaxEdge + pos);
|
|
||||||
|
|
||||||
v3f current_intersection;
|
v3f current_intersection;
|
||||||
v3s16 current_normal;
|
v3f current_normal;
|
||||||
if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
|
v3f current_raw_normal;
|
||||||
¤t_intersection, ¤t_normal)) {
|
|
||||||
|
ObjectProperties *props = obj->accessObjectProperties();
|
||||||
|
bool collision;
|
||||||
|
UnitSAO* usao = dynamic_cast<UnitSAO*>(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(
|
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());
|
(current_intersection - shootline_on_map.start).getLengthSQ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
PointedThing::PointedThing(const v3s16 &under, const v3s16 &above,
|
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):
|
u16 box_id, f32 distSq):
|
||||||
type(POINTEDTHING_NODE),
|
type(POINTEDTHING_NODE),
|
||||||
node_undersurface(under),
|
node_undersurface(under),
|
||||||
@ -36,12 +36,13 @@ PointedThing::PointedThing(const v3s16 &under, const v3s16 &above,
|
|||||||
distanceSq(distSq)
|
distanceSq(distSq)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
PointedThing::PointedThing(u16 id, const v3f &point, const v3s16 &normal,
|
PointedThing::PointedThing(u16 id, const v3f &point,
|
||||||
f32 distSq) :
|
const v3f &normal, const v3f &raw_normal, f32 distSq) :
|
||||||
type(POINTEDTHING_OBJECT),
|
type(POINTEDTHING_OBJECT),
|
||||||
object_id(id),
|
object_id(id),
|
||||||
intersection_point(point),
|
intersection_point(point),
|
||||||
intersection_normal(normal),
|
intersection_normal(normal),
|
||||||
|
raw_intersection_normal(raw_normal),
|
||||||
distanceSq(distSq)
|
distanceSq(distSq)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -74,7 +74,12 @@ struct PointedThing
|
|||||||
* This is perpendicular to the face the ray hits,
|
* This is perpendicular to the face the ray hits,
|
||||||
* points outside of the box and it's length is 1.
|
* 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.
|
* Only valid if type isn't POINTEDTHING_NONE.
|
||||||
* Indicates which selection box is selected, if there are more of them.
|
* Indicates which selection box is selected, if there are more of them.
|
||||||
@ -90,10 +95,10 @@ struct PointedThing
|
|||||||
PointedThing() = default;
|
PointedThing() = default;
|
||||||
//! Constructor for POINTEDTHING_NODE
|
//! Constructor for POINTEDTHING_NODE
|
||||||
PointedThing(const v3s16 &under, const v3s16 &above,
|
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);
|
u16 box_id, f32 distSq);
|
||||||
//! Constructor for POINTEDTHING_OBJECT
|
//! 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;
|
std::string dump() const;
|
||||||
void serialize(std::ostream &os) const;
|
void serialize(std::ostream &os) const;
|
||||||
void deSerialize(std::istream &is);
|
void deSerialize(std::istream &is);
|
||||||
|
Loading…
Reference in New Issue
Block a user