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 <memory>
|
||||
#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::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));
|
||||
|
||||
// Shitty solution
|
||||
static rp::PhysicsCommon common;
|
||||
static rp::PhysicsWorld* world = common.createPhysicsWorld();
|
||||
|
||||
std::shared_ptr<BasePart> getHandleAdornee() {
|
||||
std::shared_ptr<Selection> selection = gDataModel->GetService<Selection>();
|
||||
for (std::weak_ptr<Instance> inst : selection->Get()) {
|
||||
|
@ -52,21 +44,17 @@ CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos) {
|
|||
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) {
|
||||
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;
|
||||
if (body->raycast(ray, info)) {
|
||||
world->destroyRigidBody(body);
|
||||
Vector3 halfSize = (cframe.Rotation() * Vector3(handleSize(face) / 2.f)).Abs();
|
||||
Vector3 minB = cframe.Position() - halfSize, maxB = cframe.Position() + halfSize;
|
||||
|
||||
glm::vec3 hitPoint;
|
||||
bool hit = HitBoundingBox(minB, maxB, rayStart, (rayEnd - rayStart).Unit(), hitPoint);
|
||||
if (hit)
|
||||
return face;
|
||||
}
|
||||
|
||||
world->destroyRigidBody(body);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
|
|
|
@ -40,7 +40,7 @@ std::shared_ptr<BasePart> getHandleAdornee();
|
|||
CFrame getHandleCFrame(HandleFace face);
|
||||
CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos);
|
||||
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
|
||||
CFrame getLocalHandleCFrame(HandleFace face);
|
||||
|
|
|
@ -2,9 +2,65 @@
|
|||
|
||||
#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)
|
||||
// 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) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
// https://github.com/erich666/GraphicsGems/tree/master?tab=License-1-ov-file#readme
|
||||
// Note: The code below predates open source
|
||||
|
||||
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++) {
|
||||
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));
|
||||
#define RIGHT 0
|
||||
#define LEFT 1
|
||||
#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;
|
||||
}
|
||||
|
||||
/* Ray origin inside bounding box */
|
||||
if (inside) {
|
||||
coord = origin;
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
|
||||
void getAABBCoords(glm::vec3 &pos, glm::vec3 &size, glm::vec3 min, glm::vec3 max) {
|
||||
pos = (max + min) / 2.f;
|
||||
size = (max - min);
|
||||
|
||||
/* 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
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
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 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);
|
||||
|
||||
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 getAABBCoords(glm::vec3& pos, glm::vec3& size, glm::vec3 min, glm::vec3 max);
|
||||
/*
|
||||
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) {
|
||||
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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue