feat(editor): use algorithm for handle intersection rather than physics engine
This commit is contained in:
parent
d0635e7472
commit
3e6e1fad5f
5 changed files with 153 additions and 39 deletions
|
@ -8,10 +8,6 @@
|
||||||
#include <glm/ext/scalar_common.hpp>
|
#include <glm/ext/scalar_common.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <reactphysics3d/collision/RaycastInfo.h>
|
|
||||||
#include <reactphysics3d/engine/PhysicsCommon.h>
|
|
||||||
#include <reactphysics3d/engine/PhysicsWorld.h>
|
|
||||||
#include <reactphysics3d/mathematics/Transform.h>
|
|
||||||
|
|
||||||
HandleFace HandleFace::XPos(0, glm::vec3(1,0,0));
|
HandleFace HandleFace::XPos(0, glm::vec3(1,0,0));
|
||||||
HandleFace HandleFace::XNeg(1, glm::vec3(-1,0,0));
|
HandleFace HandleFace::XNeg(1, glm::vec3(-1,0,0));
|
||||||
|
@ -23,10 +19,6 @@ std::array<HandleFace, 6> HandleFace::Faces { HandleFace::XPos, HandleFace::XNeg
|
||||||
|
|
||||||
static CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0, 1));
|
static CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0, 1));
|
||||||
|
|
||||||
// Shitty solution
|
|
||||||
static rp::PhysicsCommon common;
|
|
||||||
static rp::PhysicsWorld* world = common.createPhysicsWorld();
|
|
||||||
|
|
||||||
std::shared_ptr<BasePart> getHandleAdornee() {
|
std::shared_ptr<BasePart> getHandleAdornee() {
|
||||||
std::shared_ptr<Selection> selection = gDataModel->GetService<Selection>();
|
std::shared_ptr<Selection> selection = gDataModel->GetService<Selection>();
|
||||||
for (std::weak_ptr<Instance> inst : selection->Get()) {
|
for (std::weak_ptr<Instance> inst : selection->Get()) {
|
||||||
|
@ -52,23 +44,19 @@ CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos) {
|
||||||
return adornee->cframe.Rotation() + newPartPos;
|
return adornee->cframe.Rotation() + newPartPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<HandleFace> raycastHandle(rp::Ray ray) {
|
std::optional<HandleFace> raycastHandle(Vector3 rayStart, Vector3 rayEnd) {
|
||||||
for (HandleFace face : HandleFace::Faces) {
|
for (HandleFace face : HandleFace::Faces) {
|
||||||
CFrame cframe = getHandleCFrame(face);
|
CFrame cframe = getHandleCFrame(face);
|
||||||
// Implement manual detection via boxes instead of... this shit
|
|
||||||
// This code also hardly works, and is not good at all... Hooo nope.
|
|
||||||
rp::RigidBody* body = world->createRigidBody(CFrame::IDENTITY + cframe.Position());
|
|
||||||
body->addCollider(common.createBoxShape((cframe.Rotation() * Vector3(handleSize(face) / 2.f)).Abs()), rp::Transform::identity());
|
|
||||||
|
|
||||||
rp::RaycastInfo info;
|
Vector3 halfSize = (cframe.Rotation() * Vector3(handleSize(face) / 2.f)).Abs();
|
||||||
if (body->raycast(ray, info)) {
|
Vector3 minB = cframe.Position() - halfSize, maxB = cframe.Position() + halfSize;
|
||||||
world->destroyRigidBody(body);
|
|
||||||
|
glm::vec3 hitPoint;
|
||||||
|
bool hit = HitBoundingBox(minB, maxB, rayStart, (rayEnd - rayStart).Unit(), hitPoint);
|
||||||
|
if (hit)
|
||||||
return face;
|
return face;
|
||||||
}
|
}
|
||||||
|
|
||||||
world->destroyRigidBody(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ std::shared_ptr<BasePart> getHandleAdornee();
|
||||||
CFrame getHandleCFrame(HandleFace face);
|
CFrame getHandleCFrame(HandleFace face);
|
||||||
CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos);
|
CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos);
|
||||||
Vector3 handleSize(HandleFace face);
|
Vector3 handleSize(HandleFace face);
|
||||||
std::optional<HandleFace> raycastHandle(rp::Ray ray);
|
std::optional<HandleFace> raycastHandle(Vector3 rayStart, Vector3 rayEnd);
|
||||||
|
|
||||||
// Gets the cframe of the handle local to the center of the selected objects
|
// Gets the cframe of the handle local to the center of the selected objects
|
||||||
CFrame getLocalHandleCFrame(HandleFace face);
|
CFrame getLocalHandleCFrame(HandleFace face);
|
||||||
|
|
|
@ -2,9 +2,65 @@
|
||||||
|
|
||||||
#define CMP_EPSILON 0.00001
|
#define CMP_EPSILON 0.00001
|
||||||
|
|
||||||
|
|
||||||
|
void expandAABB(glm::vec3& min, glm::vec3& max, glm::vec3 point) {
|
||||||
|
min = glm::vec3(glm::min(min.x, point.x), glm::min(min.y, point.y), glm::min(min.z, point.z));
|
||||||
|
max = glm::vec3(glm::max(max.x, point.x), glm::max(max.y, point.y), glm::max(max.z, point.z));
|
||||||
|
}
|
||||||
|
|
||||||
|
void computeAABBFromPoints(glm::vec3& min, glm::vec3& max, glm::vec3* points, int count) {
|
||||||
|
if (count == 0) return;
|
||||||
|
|
||||||
|
min = points[0];
|
||||||
|
max = points[0];
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
min = glm::vec3(glm::min(min.x, points[i].x), glm::min(min.y, points[i].y), glm::min(min.z, points[i].z));
|
||||||
|
max = glm::vec3(glm::max(max.x, points[i].x), glm::max(max.y, points[i].y), glm::max(max.z, points[i].z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void getAABBCoords(glm::vec3 &pos, glm::vec3 &size, glm::vec3 min, glm::vec3 max) {
|
||||||
|
pos = (max + min) / 2.f;
|
||||||
|
size = (max - min);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== THIRD-PARTY SOURCE CODE ==================== //
|
||||||
|
|
||||||
// After a long time researching, I was able to use and adapt Godot's implementation of movable handles (godot/editor/plugins/gizmos/gizmo_3d_helper.cpp)
|
// After a long time researching, I was able to use and adapt Godot's implementation of movable handles (godot/editor/plugins/gizmos/gizmo_3d_helper.cpp)
|
||||||
// All thanks goes to them and David Eberly for his algorithm.
|
// All thanks goes to them and David Eberly for his algorithm.
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/* geometry_3d.cpp */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
void get_closest_points_between_segments(const glm::vec3 &p_p0, const glm::vec3 &p_p1, const glm::vec3 &p_q0, const glm::vec3 &p_q1, glm::vec3 &r_ps, glm::vec3 &r_qt) {
|
void get_closest_points_between_segments(const glm::vec3 &p_p0, const glm::vec3 &p_p1, const glm::vec3 &p_q0, const glm::vec3 &p_q1, glm::vec3 &r_ps, glm::vec3 &r_qt) {
|
||||||
// Based on David Eberly's Computation of Distance Between Line Segments algorithm.
|
// Based on David Eberly's Computation of Distance Between Line Segments algorithm.
|
||||||
|
|
||||||
|
@ -102,24 +158,83 @@ void get_closest_points_between_segments(const glm::vec3 &p_p0, const glm::vec3
|
||||||
r_qt = (1 - t) * p_q0 + t * p_q1;
|
r_qt = (1 - t) * p_q0 + t * p_q1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void expandAABB(glm::vec3& min, glm::vec3& max, glm::vec3 point) {
|
// https://github.com/erich666/GraphicsGems/tree/master?tab=License-1-ov-file#readme
|
||||||
min = glm::vec3(glm::min(min.x, point.x), glm::min(min.y, point.y), glm::min(min.z, point.z));
|
// Note: The code below predates open source
|
||||||
max = glm::vec3(glm::max(max.x, point.x), glm::max(max.y, point.y), glm::max(max.z, point.z));
|
|
||||||
}
|
|
||||||
|
|
||||||
void computeAABBFromPoints(glm::vec3& min, glm::vec3& max, glm::vec3* points, int count) {
|
/*
|
||||||
if (count == 0) return;
|
* EULA: The Graphics Gems code is copyright-protected. In other words, you cannot claim the text of the code as your own
|
||||||
|
* and resell it. Using the code is permitted in any program, product, or library, non-commercial or commercial. Giving
|
||||||
|
* credit is not required, though is a nice gesture. The code comes as-is, and if there are any flaws or problems with
|
||||||
|
* any Gems code, nobody involved with Gems - authors, editors, publishers, or webmasters - are to be held responsible.
|
||||||
|
* Basically, don't be a jerk, and remember that anything free comes with no guarantee.
|
||||||
|
*/
|
||||||
|
|
||||||
min = points[0];
|
/*
|
||||||
max = points[0];
|
Fast Ray-Box Intersection
|
||||||
|
by Andrew Woo
|
||||||
|
from "Graphics Gems", Academic Press, 1990
|
||||||
|
*/
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
#define RIGHT 0
|
||||||
min = glm::vec3(glm::min(min.x, points[i].x), glm::min(min.y, points[i].y), glm::min(min.z, points[i].z));
|
#define LEFT 1
|
||||||
max = glm::vec3(glm::max(max.x, points[i].x), glm::max(max.y, points[i].y), glm::max(max.z, points[i].z));
|
#define MIDDLE 2
|
||||||
|
|
||||||
|
bool HitBoundingBox(
|
||||||
|
glm::vec3 minB, glm::vec3 maxB, /*box */
|
||||||
|
glm::vec3 origin, glm::vec3 dir, /*ray */
|
||||||
|
glm::vec3 &coord /* hit point */
|
||||||
|
) {
|
||||||
|
bool inside = true;
|
||||||
|
glm::vec3 quadrant;
|
||||||
|
int i;
|
||||||
|
int whichPlane;
|
||||||
|
glm::vec3 maxT;
|
||||||
|
glm::vec3 candidatePlane;
|
||||||
|
|
||||||
|
/* Find candidate planes; this loop can be avoided if
|
||||||
|
rays cast all from the eye(assume perpsective view) */
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
if(origin[i] < minB[i]) {
|
||||||
|
quadrant[i] = LEFT;
|
||||||
|
candidatePlane[i] = minB[i];
|
||||||
|
inside = false;
|
||||||
|
}else if (origin[i] > maxB[i]) {
|
||||||
|
quadrant[i] = RIGHT;
|
||||||
|
candidatePlane[i] = maxB[i];
|
||||||
|
inside = false;
|
||||||
|
}else {
|
||||||
|
quadrant[i] = MIDDLE;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void getAABBCoords(glm::vec3 &pos, glm::vec3 &size, glm::vec3 min, glm::vec3 max) {
|
/* Ray origin inside bounding box */
|
||||||
pos = (max + min) / 2.f;
|
if (inside) {
|
||||||
size = (max - min);
|
coord = origin;
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Calculate T distances to candidate planes */
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
if (quadrant[i] != MIDDLE && dir[i] !=0.)
|
||||||
|
maxT[i] = (candidatePlane[i]-origin[i]) / dir[i];
|
||||||
|
else
|
||||||
|
maxT[i] = -1.;
|
||||||
|
|
||||||
|
/* Get largest of the maxT's for final choice of intersection */
|
||||||
|
whichPlane = 0;
|
||||||
|
for (i = 1; i < 3; i++)
|
||||||
|
if (maxT[whichPlane] < maxT[i])
|
||||||
|
whichPlane = i;
|
||||||
|
|
||||||
|
/* Check final candidate actually inside box */
|
||||||
|
if (maxT[whichPlane] < 0.) return (false);
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
if (whichPlane != i) {
|
||||||
|
coord[i] = origin[i] + maxT[whichPlane] * dir[i];
|
||||||
|
if (coord[i] < minB[i] || coord[i] > maxB[i])
|
||||||
|
return (false);
|
||||||
|
} else {
|
||||||
|
coord[i] = candidatePlane[i];
|
||||||
|
}
|
||||||
|
return true; /* ray hits box */
|
||||||
}
|
}
|
|
@ -1,9 +1,20 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
// From godot/editor/plugins/gizmos/gizmo_3d_helper.h
|
|
||||||
void get_closest_points_between_segments(const glm::vec3 &p_p0, const glm::vec3 &p_p1, const glm::vec3 &p_q0, const glm::vec3 &p_q1, glm::vec3 &r_ps, glm::vec3 &r_qt);
|
|
||||||
|
|
||||||
void expandAABB(glm::vec3& min, glm::vec3& max, glm::vec3 point);
|
void expandAABB(glm::vec3& min, glm::vec3& max, glm::vec3 point);
|
||||||
void computeAABBFromPoints(glm::vec3& min, glm::vec3& max, glm::vec3* points, int count);
|
void computeAABBFromPoints(glm::vec3& min, glm::vec3& max, glm::vec3* points, int count);
|
||||||
void getAABBCoords(glm::vec3& pos, glm::vec3& size, glm::vec3 min, glm::vec3 max);
|
void getAABBCoords(glm::vec3& pos, glm::vec3& size, glm::vec3 min, glm::vec3 max);
|
||||||
|
|
||||||
|
// From godot/editor/plugins/gizmos/gizmo_3d_helper.h
|
||||||
|
void get_closest_points_between_segments(const glm::vec3 &p_p0, const glm::vec3 &p_p1, const glm::vec3 &p_q0, const glm::vec3 &p_q1, glm::vec3 &r_ps, glm::vec3 &r_qt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Fast Ray-Box Intersection
|
||||||
|
by Andrew Woo
|
||||||
|
from "Graphics Gems", Academic Press, 1990
|
||||||
|
*/
|
||||||
|
bool HitBoundingBox(
|
||||||
|
glm::vec3 minB, glm::vec3 maxB, /*box */
|
||||||
|
glm::vec3 origin, glm::vec3 dir, /*ray */
|
||||||
|
glm::vec3 &coord /* hit point */
|
||||||
|
);
|
|
@ -323,7 +323,7 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
|
||||||
|
|
||||||
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
|
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
|
||||||
if (!editorToolHandles.active) return std::nullopt;
|
if (!editorToolHandles.active) return std::nullopt;
|
||||||
return ::raycastHandle(rp::Ray(glmToRp(camera.cameraPos), glmToRp(glm::normalize(pointDir)) * 50000));
|
return ::raycastHandle(camera.cameraPos, glm::normalize(pointDir) * 50000.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainGLWidget::handleCursorChange(QMouseEvent* evt) {
|
void MainGLWidget::handleCursorChange(QMouseEvent* evt) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue