refactor(handles): changed the way handle cframes are computed
This commit is contained in:
parent
b06098e4c7
commit
b9f68ee160
9 changed files with 95 additions and 35 deletions
|
@ -64,7 +64,11 @@ const Data::String Data::CFrame::ToString() const {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -30,6 +30,11 @@ namespace Data {
|
|||
|
||||
// Same as CFrame(position, position + toward), but makes sure that up and toward are not linearly dependant
|
||||
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;
|
||||
static const CFrame YToZ;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "common.h"
|
||||
#include "datatypes/cframe.h"
|
||||
#include "datatypes/vector.h"
|
||||
#include "math_helper.h"
|
||||
#include <glm/ext/scalar_common.hpp>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
@ -33,29 +34,6 @@ std::shared_ptr<Part> getHandleAdornee() {
|
|||
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) {
|
||||
auto adornee = getHandleAdornee();
|
||||
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) {
|
||||
for (HandleFace face : HandleFace::Faces) {
|
||||
CFrame cframe = getCFrameOfHandle(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.
|
||||
rp3d::RigidBody* body = world->createRigidBody(CFrame::IDENTITY + cframe.Position());
|
||||
|
@ -95,4 +73,64 @@ Vector3 handleSize(HandleFace face) {
|
|||
if (editorToolHandles.handlesType == HandlesType::MoveHandles)
|
||||
return glm::vec3(0.5f, 0.5f, 2.f);
|
||||
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;
|
||||
}
|
|
@ -37,7 +37,10 @@ struct Handles {
|
|||
};
|
||||
|
||||
std::shared_ptr<Part> getHandleAdornee();
|
||||
CFrame getCFrameOfHandle(HandleFace face);
|
||||
CFrame getHandleCFrame(HandleFace face);
|
||||
CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos);
|
||||
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);
|
||||
|
|
|
@ -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));
|
||||
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);
|
||||
}
|
|
@ -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 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);
|
|
@ -266,6 +266,8 @@ void renderSkyBox() {
|
|||
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() {
|
||||
if (!editorToolHandles.active) return;
|
||||
|
||||
|
@ -293,11 +295,11 @@ void renderHandles() {
|
|||
handleShader->set("viewPos", camera.cameraPos);
|
||||
|
||||
for (auto face : HandleFace::Faces) {
|
||||
glm::mat4 model = getCFrameOfHandle(face);
|
||||
glm::mat4 model = getHandleCFrame(face);
|
||||
model = glm::scale(model, (glm::vec3)handleSize(face));
|
||||
handleShader->set("model", model);
|
||||
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),
|
||||
.shininess = 16.0f,
|
||||
});
|
||||
|
@ -320,7 +322,7 @@ void renderHandles() {
|
|||
identityShader->set("aColor", glm::vec3(0.f, 1.f, 1.f));
|
||||
|
||||
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);
|
||||
|
||||
if (screenPos.z < 0) continue;
|
||||
|
|
|
@ -183,7 +183,7 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
|
|||
glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height()));
|
||||
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
|
||||
CFrame frame = editorToolHandles.worldMode ? CFrame::IDENTITY + part->position() : part->cframe.Rotation();
|
||||
|
@ -283,14 +283,16 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
|
|||
if (snappingFactor() > 0)
|
||||
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
|
||||
// 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;
|
||||
glm::vec4 signVec = glm::normalize(rotationAxis - partCenterRaw);
|
||||
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);
|
||||
|
||||
|
|
|
@ -133,5 +133,5 @@ void PlaceDocument::init() {
|
|||
std::shared_ptr<Script> script = Script::New();
|
||||
gWorkspace()->AddChild(script);
|
||||
MainWindow* mainWnd = dynamic_cast<MainWindow*>(window());
|
||||
mainWnd->openScriptDocument(script);
|
||||
// mainWnd->openScriptDocument(script);
|
||||
}
|
Loading…
Add table
Reference in a new issue