Compare commits
7 commits
375d6c89b9
...
153fbba2d2
Author | SHA1 | Date | |
---|---|---|---|
153fbba2d2 | |||
92cff7478c | |||
bf88dc8f92 | |||
167ff7de07 | |||
4b79ae6f49 | |||
4805f717ac | |||
18985e6f86 |
9 changed files with 116 additions and 28 deletions
|
@ -3,6 +3,7 @@
|
|||
#include "physics/util.h"
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/gtc/matrix_inverse.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <glm/matrix.hpp>
|
||||
#include <reactphysics3d/mathematics/Transform.h>
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
|
@ -83,7 +84,7 @@ Data::Vector3 Data::CFrame::ToEulerAnglesXYZ() {
|
|||
|
||||
Data::CFrame Data::CFrame::FromEulerAnglesXYZ(Data::Vector3 vector) {
|
||||
glm::mat3 mat = glm::eulerAngleXYZ(vector.X(), vector.Y(), vector.Z());
|
||||
return Data::CFrame(Data::Vector3::ZERO, glm::column(mat, 2), (Data::Vector3)glm::column(mat, 1)); // Getting LookAt (3rd) and Up (2nd) vectors
|
||||
return Data::CFrame((glm::vec3)Data::Vector3::ZERO, mat);
|
||||
}
|
||||
|
||||
Data::CFrame Data::CFrame::Inverse() const {
|
||||
|
|
|
@ -51,3 +51,7 @@ namespace Data {
|
|||
bool operator ==(Data::Vector3) const;
|
||||
};
|
||||
}
|
||||
|
||||
inline void printVec(Data::Vector3 vec) {
|
||||
printf("(%f, %f, %f)\n", vec.X(), vec.Y(), vec.Z());
|
||||
}
|
|
@ -62,7 +62,7 @@ void DataModel::SaveToFile(std::optional<std::string> path) {
|
|||
doc.save(outStream);
|
||||
currentFile = target;
|
||||
name = target;
|
||||
Logger::info("Place saved succesfully");
|
||||
Logger::info("Place saved successfully");
|
||||
}
|
||||
|
||||
void DataModel::DeserializeService(pugi::xml_node* node) {
|
||||
|
|
|
@ -16,6 +16,8 @@ HandleFace HandleFace::ZPos(4, glm::vec3(0,0,1));
|
|||
HandleFace HandleFace::ZNeg(5, glm::vec3(0,0,-1));
|
||||
std::array<HandleFace, 6> HandleFace::Faces { HandleFace::XPos, HandleFace::XNeg, HandleFace::YPos, HandleFace::YNeg, HandleFace::ZPos, HandleFace::ZNeg };
|
||||
|
||||
static Data::CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0, 1));
|
||||
|
||||
// Shitty solution
|
||||
static rp3d::PhysicsCommon common;
|
||||
static rp3d::PhysicsWorld* world = common.createPhysicsWorld();
|
||||
|
@ -39,14 +41,18 @@ Data::CFrame Handles::GetCFrameOfHandle(HandleFace face) {
|
|||
|
||||
Data::CFrame localFrame = worldMode ? Data::CFrame::IDENTITY + adornee->lock()->position() : adornee->lock()->cframe;
|
||||
|
||||
Data::Vector3 handleNormal = face.normal;
|
||||
if (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() * face.normal), upAxis)) > 0.9999f)
|
||||
if (glm::abs(glm::dot(glm::vec3(localFrame.Rotation() * handleNormal), upAxis)) > 0.9999f)
|
||||
upAxis = glm::vec3(0, 1, 0);
|
||||
|
||||
Data::Vector3 handleOffset = this->worldMode ? ((Data::Vector3::ONE * 2.f) + adornee->lock()->GetAABB() * 0.5f) : Data::Vector3(2.f + adornee->lock()->size * 0.5f);
|
||||
Data::Vector3 handlePos = localFrame * (handleOffset * face.normal);
|
||||
Data::CFrame cframe(handlePos, handlePos + localFrame.Rotation() * -face.normal, upAxis);
|
||||
Data::Vector3 handlePos = localFrame * (handleOffset * handleNormal);
|
||||
Data::CFrame cframe(handlePos, handlePos + localFrame.Rotation() * -handleNormal, upAxis);
|
||||
|
||||
return cframe;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ class Handles : public Instance {
|
|||
public:
|
||||
const static InstanceType TYPE;
|
||||
|
||||
bool nixAxes = false; // XYZ -> ZXY, used with rotation
|
||||
bool active;
|
||||
std::optional<std::weak_ptr<Part>> adornee;
|
||||
HandlesType handlesType;
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
#include <glm/geometric.hpp>
|
||||
#include <glm/gtc/round.hpp>
|
||||
#include <glm/matrix.hpp>
|
||||
#include <glm/trigonometric.hpp>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <reactphysics3d/collision/RaycastInfo.h>
|
||||
#include <vector>
|
||||
|
||||
#include "datatypes/cframe.h"
|
||||
#include "datatypes/vector.h"
|
||||
#include "editorcommon.h"
|
||||
#include "logger.h"
|
||||
#include "mainwindow.h"
|
||||
|
@ -33,9 +35,10 @@
|
|||
#include "rendering/shader.h"
|
||||
|
||||
#include "mainglwidget.h"
|
||||
#include "../core/src/rendering/defaultmeshes.h"
|
||||
#include "math_helper.h"
|
||||
|
||||
static Data::CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0, 1));
|
||||
|
||||
MainGLWidget::MainGLWidget(QWidget* parent): QOpenGLWidget(parent) {
|
||||
setFocusPolicy(Qt::FocusPolicy::ClickFocus);
|
||||
setMouseTracking(true);
|
||||
|
@ -141,17 +144,18 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
|
|||
Data::CFrame targetFrame = partFromBody(rayHit->body)->cframe;
|
||||
Data::Vector3 surfaceNormal = targetFrame.Inverse().Rotation() * rayHit->worldNormal;
|
||||
// The part being dragged's frame local to the hit target's frame, but without its position component
|
||||
Data::CFrame localFrame = (targetFrame.Inverse() * draggingObject->lock()->cframe).Rotation();
|
||||
// To find a world vector local to the new frame, use newFrame, not localFrame, as localFrame is localFrame is local to targetFrame in itself
|
||||
Data::CFrame localFrame = (targetFrame.Inverse() * (draggingObject->lock()->cframe.Rotation() + vec));
|
||||
|
||||
// Snap axis
|
||||
localFrame = snapCFrame(localFrame);
|
||||
Data::CFrame newFrame = targetFrame * localFrame;
|
||||
|
||||
// Unsink the object
|
||||
// Get the normal of the surface relative to the part's frame, and get the size along that vector
|
||||
Data::Vector3 partOffset = localFrame * ((localFrame.Inverse() * rayHit->worldNormal) * draggingObject->lock()->size / 2);
|
||||
Data::Vector3 unsinkOffset = newFrame.Rotation() * ((newFrame.Rotation().Inverse() * rayHit->worldNormal) * draggingObject->lock()->size / 2);
|
||||
|
||||
Data::CFrame newFrame = targetFrame.Rotation() * localFrame;
|
||||
draggingObject->lock()->cframe = newFrame + vec + partOffset;
|
||||
draggingObject->lock()->cframe = newFrame + unsinkOffset;
|
||||
|
||||
syncPartPhysics(draggingObject->lock());
|
||||
}
|
||||
|
@ -160,11 +164,8 @@ inline glm::vec3 vec3fy(glm::vec4 vec) {
|
|||
return vec / vec.w;
|
||||
}
|
||||
|
||||
QPoint lastPoint;
|
||||
void MainGLWidget::handleHandleDrag(QMouseEvent* evt) {
|
||||
QPoint cLastPoint = lastPoint;
|
||||
lastPoint = evt->pos();
|
||||
|
||||
// Taken from Godot's implementation of moving handles (godot/editor/plugins/gizmos/gizmo_3d_helper.cpp)
|
||||
void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
|
||||
if (!isMouseDragging || !draggingHandle || !editorToolHandles->adornee || !editorToolHandles->active) return;
|
||||
|
||||
QPoint position = evt->pos();
|
||||
|
@ -203,7 +204,6 @@ void MainGLWidget::handleHandleDrag(QMouseEvent* evt) {
|
|||
// printf("Post-snap: (%f, %f, %f)\n", diff.x, diff.y, diff.z);
|
||||
|
||||
switch (mainWindow()->selectedTool) {
|
||||
case SelectedTool::SELECT: break;
|
||||
case SelectedTool::MOVE: {
|
||||
// Add difference
|
||||
editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe + diff;
|
||||
|
@ -227,14 +227,58 @@ void MainGLWidget::handleHandleDrag(QMouseEvent* evt) {
|
|||
part->cframe = part->cframe + diff * 0.5f;
|
||||
} break;
|
||||
|
||||
case SelectedTool::ROTATE: {
|
||||
// TODO: Implement rotation
|
||||
} break;
|
||||
default:
|
||||
Logger::error("Invalid tool was set to be handled by handleLinearTransform\n");
|
||||
}
|
||||
|
||||
syncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
|
||||
}
|
||||
|
||||
// Also implemented based on Godot: [c7ea8614](godot/editor/plugins/canvas_item_editor_plugin.cpp#L1490)
|
||||
glm::vec2 startPoint;
|
||||
QPoint _lastPoint;
|
||||
void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
|
||||
QPoint lastPoint = _lastPoint;
|
||||
_lastPoint = evt->pos();
|
||||
|
||||
glm::vec2 startPoint(lastPoint.x(), lastPoint.y());
|
||||
|
||||
if (!isMouseDragging || !draggingHandle || !editorToolHandles->adornee || !editorToolHandles->active) return;
|
||||
|
||||
glm::vec2 destPoint = glm::vec2(evt->pos().x(), evt->pos().y());
|
||||
auto part = editorToolHandles->adornee->lock();
|
||||
|
||||
// Calculate part pos as screen point
|
||||
glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)width() / (float)height(), 0.1f, 1000.0f);
|
||||
glm::mat4 view = camera.getLookAt();
|
||||
glm::vec4 screenPos = projection * view * glm::vec4((glm::vec3)part->cframe.Position(), 1.f);
|
||||
screenPos /= screenPos.w * 2;
|
||||
screenPos += 0.5f;
|
||||
screenPos = glm::vec4(screenPos.x, 1-screenPos.y, 0, 0);
|
||||
screenPos *= glm::vec4(width(), height(), 0, 0);
|
||||
|
||||
// https://wumbo.net/formulas/angle-between-two-vectors-2d/
|
||||
glm::vec2 initVec = glm::normalize(startPoint - (glm::vec2)screenPos);
|
||||
glm::vec2 destVec = glm::normalize(destPoint - (glm::vec2)screenPos);
|
||||
float angle = atan2f(initVec.x * destVec.y - initVec.y * destVec.x, initVec.x * destVec.x + initVec.y * destVec.y);
|
||||
|
||||
// Yes, it's messy. But it works so I don't wanna hear it.
|
||||
// Maybe I'll clean it up next week.
|
||||
// TODO: ?
|
||||
glm::vec4 pos1 = projection * view * glm::vec4((glm::vec3)part->cframe.Position(), 1.f);
|
||||
pos1 /= pos1.w;
|
||||
glm::vec4 otherVec = projection * view * glm::vec4((glm::vec3)(part->cframe * glm::abs(draggingHandle->normal)), 1.f);
|
||||
otherVec /= otherVec.w;
|
||||
glm::vec4 signVec = glm::normalize(otherVec - pos1);
|
||||
float sign = glm::sign(signVec.z);
|
||||
|
||||
glm::vec3 angles = glm::abs(draggingHandle->normal) * -sign * glm::vec3(angle);
|
||||
|
||||
part->cframe = part->cframe * Data::CFrame::FromEulerAnglesXYZ(-angles);
|
||||
|
||||
syncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
|
||||
}
|
||||
|
||||
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
|
||||
if (!editorToolHandles->adornee.has_value() || !editorToolHandles->active) return std::nullopt;
|
||||
return editorToolHandles->RaycastHandle(rp3d::Ray(glmToRp(camera.cameraPos), glmToRp(glm::normalize(pointDir)) * 50000));
|
||||
|
@ -262,8 +306,19 @@ void MainGLWidget::handleCursorChange(QMouseEvent* evt) {
|
|||
void MainGLWidget::mouseMoveEvent(QMouseEvent* evt) {
|
||||
handleCameraRotate(evt);
|
||||
handleObjectDrag(evt);
|
||||
handleHandleDrag(evt);
|
||||
handleCursorChange(evt);
|
||||
|
||||
switch (mainWindow()->selectedTool) {
|
||||
case SelectedTool::MOVE:
|
||||
case SelectedTool::SCALE:
|
||||
handleLinearTransform(evt);
|
||||
break;
|
||||
case SelectedTool::ROTATE:
|
||||
handleRotationalTransform(evt);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
||||
|
@ -281,6 +336,7 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
|||
// raycast handles
|
||||
auto handle = raycastHandle(pointDir);
|
||||
if (handle.has_value()) {
|
||||
startPoint = glm::vec2(evt->pos().x(), evt->pos().y());
|
||||
isMouseDragging = true;
|
||||
draggingHandle = handle;
|
||||
return;
|
||||
|
|
|
@ -23,7 +23,8 @@ protected:
|
|||
|
||||
void handleCameraRotate(QMouseEvent* evt);
|
||||
void handleObjectDrag(QMouseEvent* evt);
|
||||
void handleHandleDrag(QMouseEvent* evt);
|
||||
void handleLinearTransform(QMouseEvent* evt);
|
||||
void handleRotationalTransform(QMouseEvent* evt);
|
||||
void handleCursorChange(QMouseEvent* evt);
|
||||
std::optional<HandleFace> raycastHandle(glm::vec3 pointDir);
|
||||
|
||||
|
|
|
@ -118,14 +118,21 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
std::optional<std::string> path;
|
||||
if (!dataModel->HasFile())
|
||||
path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + dataModel->name));
|
||||
if (path == "") return;
|
||||
if (!path || path == "") return;
|
||||
|
||||
dataModel->SaveToFile(path);
|
||||
});
|
||||
|
||||
connect(ui->actionSaveAs, &QAction::triggered, this, [&]() {
|
||||
std::optional<std::string> path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save as " + dataModel->name));
|
||||
if (!path || path == "") return;
|
||||
|
||||
dataModel->SaveToFile(path);
|
||||
});
|
||||
|
||||
connect(ui->actionOpen, &QAction::triggered, this, [&]() {
|
||||
std::optional<std::string> path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptOpen);
|
||||
if (!path) return;
|
||||
if (!path || path == "") return;
|
||||
std::shared_ptr<DataModel> newModel = DataModel::LoadFromFile(path.value());
|
||||
dataModel = newModel;
|
||||
ui->explorerView->updateRoot(newModel);
|
||||
|
@ -316,11 +323,8 @@ void MainWindow::updateToolbars() {
|
|||
ui->actionGridSnap05->setChecked(snappingMode == GridSnappingMode::SNAP_05_STUDS);
|
||||
ui->actionGridSnapOff->setChecked(snappingMode == GridSnappingMode::SNAP_OFF);
|
||||
|
||||
// editorToolHandles->worldMode = false;
|
||||
if (selectedTool == SelectedTool::SCALE)
|
||||
editorToolHandles->worldMode = false;
|
||||
else
|
||||
editorToolHandles->worldMode = worldSpaceTransforms;
|
||||
editorToolHandles->worldMode = selectedTool == SelectedTool::SCALE ? false : worldSpaceTransforms;
|
||||
editorToolHandles->nixAxes = selectedTool == SelectedTool::ROTATE;
|
||||
|
||||
editorToolHandles->active = selectedTool != SelectedTool::SELECT;
|
||||
editorToolHandles->handlesType =
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
<addaction name="actionNew"/>
|
||||
<addaction name="actionOpen"/>
|
||||
<addaction name="actionSave"/>
|
||||
<addaction name="actionSaveAs"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionQuit"/>
|
||||
</widget>
|
||||
|
@ -480,6 +481,20 @@
|
|||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSaveAs">
|
||||
<property name="icon">
|
||||
<iconset theme="document-save-as"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save As...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+S</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
|
Loading…
Add table
Reference in a new issue