1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-07-02 16:38:41 +00:00

Allow rotating entity selectionboxes (#12379)

This commit is contained in:
Lars Müller 2022-10-30 16:53:14 +01:00 committed by GitHub
parent b829231992
commit 077627181e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 271 additions and 58 deletions

View file

@ -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,
&current_intersection, &current_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<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, &current_intersection, &current_normal, &current_raw_normal);
} else {
collision = boxLineCollision(selection_box, rel_pos, line_vector,
&current_intersection, &current_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());
}
}

View file

@ -3297,7 +3297,7 @@ PointedThing Game::updatePointedThing(
{
std::vector<aabb3f> *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<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) {
// 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

View file

@ -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()

View file

@ -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<aabb3f> 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;

View file

@ -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,

View file

@ -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) {}
}

View file

@ -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 = "";

View file

@ -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 <quaternion.h>
#include "constants.h"
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,
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;
}

View file

@ -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);

View file

@ -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");

View file

@ -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);
}

View file

@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "object_properties.h"
#include "serveractiveobject.h"
#include <quaternion.h>
#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;

View file

@ -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,
&current_intersection, &current_normal)) {
v3f current_normal;
v3f current_raw_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, &current_intersection, &current_normal, &current_raw_normal);
} else {
collision = boxLineCollision(selection_box, rel_pos, line_vector,
&current_intersection, &current_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());
}
}

View file

@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <sstream>
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)
{}

View file

@ -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);