refactor(handles): changed the way handle cframes are computed

This commit is contained in:
maelstrom 2025-05-18 19:06:01 +02:00
parent b06098e4c7
commit b9f68ee160
9 changed files with 95 additions and 35 deletions

View file

@ -64,7 +64,11 @@ const Data::String Data::CFrame::ToString() const {
} }
Data::CFrame Data::CFrame::pointToward(Vector3 position, Vector3 toward) { Data::CFrame Data::CFrame::pointToward(Vector3 position, Vector3 toward) {
return Data::CFrame(position, position + toward, (abs(glm::dot((glm::vec3)toward, glm::vec3(0, 1, 0))) > 0.999) ? glm::vec3(0, 0, 1) : glm::vec3(0, 1, 0)); return Data::CFrame(position, position + toward, (abs(toward.Dot(Vector3(0, 1, 0))) > 0.999) ? Vector3(0, 0, 1) : Vector3(0, 1, 0));
}
Data::CFrame Data::CFrame::pointAligned(Vector3 position, Vector3 toward, Vector3 up, Vector3 right) {
return Data::CFrame(position, position + toward, (abs(toward.Dot(up)) > 0.999) ? right : up);
} }
Data::CFrame::operator glm::mat4() const { Data::CFrame::operator glm::mat4() const {

View file

@ -30,6 +30,11 @@ namespace Data {
// Same as CFrame(position, position + toward), but makes sure that up and toward are not linearly dependant // Same as CFrame(position, position + toward), but makes sure that up and toward are not linearly dependant
static CFrame pointToward(Vector3 position, Vector3 toward); static CFrame pointToward(Vector3 position, Vector3 toward);
// Creates a cframe looking at position + toward, whilst aligning its up to up.
// If up and toward are approximately linearly dependent (their absolute dot product > 0.999),
// then the right is used instead
// Up and right must NOT be linearly dependent
static CFrame pointAligned(Vector3 position, Vector3 toward, Vector3 up, Vector3 right);
DEF_DATA_PROP static const CFrame IDENTITY; DEF_DATA_PROP static const CFrame IDENTITY;
static const CFrame YToZ; static const CFrame YToZ;

View file

@ -2,6 +2,7 @@
#include "common.h" #include "common.h"
#include "datatypes/cframe.h" #include "datatypes/cframe.h"
#include "datatypes/vector.h" #include "datatypes/vector.h"
#include "math_helper.h"
#include <glm/ext/scalar_common.hpp> #include <glm/ext/scalar_common.hpp>
#include <memory> #include <memory>
#include <optional> #include <optional>
@ -33,29 +34,6 @@ std::shared_ptr<Part> getHandleAdornee() {
return {}; return {};
} }
CFrame getCFrameOfHandle(HandleFace face) {
auto adornee = getHandleAdornee();
if (adornee == nullptr) return CFrame(glm::vec3(0,0,0), (Vector3)glm::vec3(0,0,0));
CFrame localFrame = editorToolHandles.worldMode ? CFrame::IDENTITY + adornee->position() : adornee->cframe;
Vector3 handleNormal = face.normal;
if (editorToolHandles.nixAxes)
handleNormal = XYZToZXY * face.normal;
// We don't want this to align with local * face.normal, or else we have problems.
glm::vec3 upAxis(0, 0, 1);
if (glm::abs(glm::dot(glm::vec3(localFrame.Rotation() * handleNormal), upAxis)) > 0.9999f)
upAxis = glm::vec3(0, 1, 0);
glm::vec3 partSize = editorToolHandles.handlesType == HandlesType::RotateHandles ? glm::vec3(glm::max(adornee->size.x, adornee->size.y, adornee->size.z)) : adornee->size;
Vector3 handleOffset = editorToolHandles.worldMode ? ((Vector3::ONE * 2.f) + adornee->GetAABB() * 0.5f) : Vector3(2.f + partSize * 0.5f);
Vector3 handlePos = localFrame * (handleOffset * handleNormal);
CFrame cframe(handlePos, handlePos + localFrame.Rotation() * -handleNormal, upAxis);
return cframe;
}
CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos) { CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos) {
auto adornee = getHandleAdornee(); auto adornee = getHandleAdornee();
if (adornee == nullptr) return CFrame(glm::vec3(0,0,0), (Vector3)glm::vec3(0,0,0)); if (adornee == nullptr) return CFrame(glm::vec3(0,0,0), (Vector3)glm::vec3(0,0,0));
@ -73,7 +51,7 @@ CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos) {
std::optional<HandleFace> raycastHandle(rp3d::Ray ray) { std::optional<HandleFace> raycastHandle(rp3d::Ray ray) {
for (HandleFace face : HandleFace::Faces) { for (HandleFace face : HandleFace::Faces) {
CFrame cframe = getCFrameOfHandle(face); CFrame cframe = getHandleCFrame(face);
// Implement manual detection via boxes instead of... this shit // Implement manual detection via boxes instead of... this shit
// This code also hardly works, and is not good at all... Hooo nope. // This code also hardly works, and is not good at all... Hooo nope.
rp3d::RigidBody* body = world->createRigidBody(CFrame::IDENTITY + cframe.Position()); rp3d::RigidBody* body = world->createRigidBody(CFrame::IDENTITY + cframe.Position());
@ -95,4 +73,64 @@ Vector3 handleSize(HandleFace face) {
if (editorToolHandles.handlesType == HandlesType::MoveHandles) if (editorToolHandles.handlesType == HandlesType::MoveHandles)
return glm::vec3(0.5f, 0.5f, 2.f); return glm::vec3(0.5f, 0.5f, 2.f);
return glm::vec3(1,1,1); return glm::vec3(1,1,1);
}
static int getAABBOfSelection(glm::vec3& pos, glm::vec3& size, glm::vec3& min, glm::vec3& max) {
int count = 0;
for (std::weak_ptr<Instance> inst : getSelection()) {
if (inst.expired() || !inst.lock()->IsA<Part>()) continue;
std::shared_ptr<Part> part = inst.lock()->CastTo<Part>().expect();
if (count == 0)
min = part->position(), max = part->position();
count++;
Vector3 aabbSize = part->GetAABB();
expandAABB(min, max, part->position() - aabbSize / 2.f);
expandAABB(min, max, part->position() + aabbSize / 2.f);
}
getAABBCoords(pos, size, min, max);
return count;
}
static std::shared_ptr<Part> getFirstSelectedPart() {
for (std::weak_ptr<Instance> inst : getSelection()) {
if (inst.expired() || !inst.lock()->IsA<Part>()) continue;
return inst.lock()->CastTo<Part>().expect();
}
return {};
}
CFrame getLocalHandleCFrame(HandleFace face) {
glm::vec3 _boxPos, boxSize, _boxMin, _boxMax;
int count = getAABBOfSelection(_boxPos, boxSize, _boxMin, _boxMax);
Vector3 size;
if (count == 1 && !editorToolHandles.worldMode)
size = getFirstSelectedPart()->size;
else
size = boxSize;
// Since rotation displays rings, all handles must be the same distance from origin in order for the
// rings to be circular
if (editorToolHandles.handlesType == HandlesType::RotateHandles)
size = Vector3::ONE * fmax(fmax(size.X(), size.Y()), size.Z());
CFrame cframe = CFrame::pointToward(face.normal * ((glm::vec3)size * 0.5f + 2.f), -face.normal);
return cframe;
}
CFrame getHandleCFrame(HandleFace face) {
glm::vec3 boxPos, boxSize, _boxMin, _boxMax;
int count = getAABBOfSelection(boxPos, boxSize, _boxMin, _boxMax);
if (count == 1 && !editorToolHandles.worldMode) {
auto part = getFirstSelectedPart();
return part->cframe * getLocalHandleCFrame(face);
} else
return getLocalHandleCFrame(face) + boxPos;
} }

View file

@ -37,7 +37,10 @@ struct Handles {
}; };
std::shared_ptr<Part> getHandleAdornee(); std::shared_ptr<Part> getHandleAdornee();
CFrame getCFrameOfHandle(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(rp3d::Ray ray); std::optional<HandleFace> raycastHandle(rp3d::Ray ray);
// Gets the cframe of the handle local to the center of the selected objects
CFrame getLocalHandleCFrame(HandleFace face);

View file

@ -117,4 +117,9 @@ void computeAABBFromPoints(glm::vec3& min, glm::vec3& max, glm::vec3* points, in
min = glm::vec3(glm::min(min.x, points[i].x), glm::min(min.y, points[i].y), glm::min(min.z, points[i].z)); 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)); 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);
} }

View file

@ -5,4 +5,5 @@
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);
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);

View file

@ -266,6 +266,8 @@ void renderSkyBox() {
glDrawArrays(GL_TRIANGLES, 0, 36); glDrawArrays(GL_TRIANGLES, 0, 36);
} }
static CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0, 1));
void renderHandles() { void renderHandles() {
if (!editorToolHandles.active) return; if (!editorToolHandles.active) return;
@ -293,11 +295,11 @@ void renderHandles() {
handleShader->set("viewPos", camera.cameraPos); handleShader->set("viewPos", camera.cameraPos);
for (auto face : HandleFace::Faces) { for (auto face : HandleFace::Faces) {
glm::mat4 model = getCFrameOfHandle(face); glm::mat4 model = getHandleCFrame(face);
model = glm::scale(model, (glm::vec3)handleSize(face)); model = glm::scale(model, (glm::vec3)handleSize(face));
handleShader->set("model", model); handleShader->set("model", model);
handleShader->set("material", Material { handleShader->set("material", Material {
.diffuse = glm::abs(face.normal), .diffuse = editorToolHandles.handlesType == HandlesType::RotateHandles ? (glm::vec3)(XYZToZXY * glm::abs(face.normal)) : glm::abs(face.normal),
.specular = glm::vec3(0.5f, 0.5f, 0.5f), .specular = glm::vec3(0.5f, 0.5f, 0.5f),
.shininess = 16.0f, .shininess = 16.0f,
}); });
@ -320,7 +322,7 @@ void renderHandles() {
identityShader->set("aColor", glm::vec3(0.f, 1.f, 1.f)); identityShader->set("aColor", glm::vec3(0.f, 1.f, 1.f));
for (auto face : HandleFace::Faces) { for (auto face : HandleFace::Faces) {
CFrame cframe = getCFrameOfHandle(face); CFrame cframe = getHandleCFrame(face);
glm::vec4 screenPos = projection * view * glm::vec4((glm::vec3)cframe.Position(), 1.0f); glm::vec4 screenPos = projection * view * glm::vec4((glm::vec3)cframe.Position(), 1.0f);
if (screenPos.z < 0) continue; if (screenPos.z < 0) continue;

View file

@ -183,7 +183,7 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height())); glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height()));
pointDir = glm::normalize(pointDir); pointDir = glm::normalize(pointDir);
CFrame handleCFrame = getCFrameOfHandle(draggingHandle.value()); CFrame handleCFrame = getHandleCFrame(draggingHandle.value());
// Current frame. Identity frame if worldMode == true, selected object's frame if worldMode == false // Current frame. Identity frame if worldMode == true, selected object's frame if worldMode == false
CFrame frame = editorToolHandles.worldMode ? CFrame::IDENTITY + part->position() : part->cframe.Rotation(); CFrame frame = editorToolHandles.worldMode ? CFrame::IDENTITY + part->position() : part->cframe.Rotation();
@ -283,14 +283,16 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
if (snappingFactor() > 0) if (snappingFactor() > 0)
angle = roundf(angle * 4 / PI / snappingFactor()) / 4 * PI * snappingFactor(); angle = roundf(angle * 4 / PI / snappingFactor()) / 4 * PI * snappingFactor();
glm::vec3 handleNormal = XYZToZXY * glm::abs(draggingHandle->normal);
// Checks if the rotation axis is facing towards, or away from the camera // Checks if the rotation axis is facing towards, or away from the camera
// If it pointing away from the camera, then we need to invert the angle change // If it pointing away from the camera, then we need to invert the angle change
glm::vec4 rotationAxis = projection * view * glm::vec4((glm::vec3)(initialFrame * glm::abs(draggingHandle->normal)), 1.f); glm::vec4 rotationAxis = projection * view * glm::vec4((glm::vec3)(initialFrame * handleNormal), 1.f);
rotationAxis /= rotationAxis.w; rotationAxis /= rotationAxis.w;
glm::vec4 signVec = glm::normalize(rotationAxis - partCenterRaw); glm::vec4 signVec = glm::normalize(rotationAxis - partCenterRaw);
float sign = -glm::sign(signVec.z); float sign = -glm::sign(signVec.z);
glm::vec3 angles = glm::abs(draggingHandle->normal) * sign * glm::vec3(angle); glm::vec3 angles = handleNormal * sign * glm::vec3(angle);
part->cframe = initialFrame * CFrame::FromEulerAnglesXYZ(-angles); part->cframe = initialFrame * CFrame::FromEulerAnglesXYZ(-angles);

View file

@ -133,5 +133,5 @@ void PlaceDocument::init() {
std::shared_ptr<Script> script = Script::New(); std::shared_ptr<Script> script = Script::New();
gWorkspace()->AddChild(script); gWorkspace()->AddChild(script);
MainWindow* mainWnd = dynamic_cast<MainWindow*>(window()); MainWindow* mainWnd = dynamic_cast<MainWindow*>(window());
mainWnd->openScriptDocument(script); // mainWnd->openScriptDocument(script);
} }