#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datatypes/cframe.h" #include "editorcommon.h" #include "mainwindow.h" #include "objects/handles.h" #include "physics/util.h" #include "qcursor.h" #include "qevent.h" #include "qnamespace.h" #include "qwindowdefs.h" #include "rendering/renderer.h" #include "physics/simulation.h" #include "camera.h" #include "common.h" #include "rendering/shader.h" #include "mainglwidget.h" #include "../core/src/rendering/defaultmeshes.h" #include "math_helper.h" MainGLWidget::MainGLWidget(QWidget* parent): QOpenGLWidget(parent) { setFocusPolicy(Qt::FocusPolicy::ClickFocus); setMouseTracking(true); } Shader* identityShader; void MainGLWidget::initializeGL() { glewInit(); renderInit(NULL, width(), height()); identityShader = new Shader("assets/shaders/identity.vs", "assets/shaders/identity.fs"); } extern int vpx, vpy; void MainGLWidget::resizeGL(int w, int h) { // Update projection matrix and other size related settings: // m_projection.setToIdentity(); // m_projection.perspective(45.0f, w / float(h), 0.01f, 100.0f); // ... // glViewport(0, 0, w, h); setViewport(w, h); } glm::vec2 firstPoint; glm::vec2 secondPoint; extern std::optional> draggingObject; extern std::optional draggingHandle; extern Shader* shader; void MainGLWidget::paintGL() { ::render(NULL); if (!editorToolHandles->adornee) return; for (HandleFace face : HandleFace::Faces) { // Ignore negatives (for now) if (face.normal != glm::abs(face.normal)) continue; glm::vec3 axisNormal = face.normal; // // glm::vec3 planeNormal = camera.cameraFront; glm::vec3 planeRight = glm::cross(axisNormal, glm::normalize(camera.cameraFront)); glm::vec3 planeNormal = glm::cross(glm::normalize(planeRight), axisNormal); auto a = axisNormal; auto b = planeRight; auto c = planeNormal; glm::mat3 matrix = { axisNormal, planeRight, -planeNormal, }; glm::mat4 model = glm::translate(glm::mat4(1.0f), (glm::vec3)editorToolHandles->adornee->lock()->position()) * glm::mat4(matrix); model = glm::scale(model, glm::vec3(5, 5, 0.2)); shader->set("model", model); shader->set("material", Material { .diffuse = glm::vec3(0.5, 1.f, 0.5), .specular = glm::vec3(0.5f, 0.5f, 0.5f), .shininess = 16.0f, }); glm::mat3 normalMatrix = glm::mat3(glm::transpose(glm::inverse(model))); shader->set("normalMatrix", normalMatrix); CUBE_MESH->bind(); // glDrawArrays(GL_TRIANGLES, 0, 36); } } bool isMouseRightDragging = false; QPoint lastMousePos; void MainGLWidget::handleCameraRotate(QMouseEvent* evt) { if (!isMouseRightDragging) return; camera.processRotation(evt->pos().x() - lastMousePos.x(), evt->pos().y() - lastMousePos.y()); lastMousePos = evt->pos(); // QCursor::setPos(lastMousePos); } bool isMouseDragging = false; std::optional> draggingObject; std::optional draggingHandle; void MainGLWidget::handleObjectDrag(QMouseEvent* evt) { if (!isMouseDragging || !draggingObject) return; QPoint position = evt->pos(); glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height())); std::optional rayHit = castRayNearest(camera.cameraPos, pointDir, 50000, [](std::shared_ptr part) { return (part == draggingObject->lock()) ? FilterResult::PASS : FilterResult::TARGET; }); if (!rayHit) return; Data::Vector3 vec = rayHit->worldPoint; vec = vec + Data::Vector3(rpToGlm(rayHit->worldNormal) * draggingObject->lock()->size / 2.f); draggingObject->lock()->cframe = draggingObject->lock()->cframe.Rotation() + vec; syncPartPhysics(draggingObject->lock()); } inline glm::vec3 vec3fy(glm::vec4 vec) { return vec / vec.w; } QPoint lastPoint; void MainGLWidget::handleHandleDrag(QMouseEvent* evt) { QPoint cLastPoint = lastPoint; lastPoint = evt->pos(); if (!isMouseDragging || !draggingHandle || !editorToolHandles->adornee || !editorToolHandles->active) return; QPoint position = evt->pos(); // This was actually quite a difficult problem to solve, managing to get the handle to go underneath the cursor glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height())); pointDir = glm::normalize(pointDir); Data::CFrame handleCFrame = editorToolHandles->GetCFrameOfHandle(draggingHandle.value()); // Segment from axis stretching -4096 to +4096 rel to handle's position glm::vec3 axisSegment0 = handleCFrame.Position() + glm::abs(draggingHandle->normal) * 4096.0f; glm::vec3 axisSegment1 = handleCFrame.Position() + glm::abs(draggingHandle->normal) * -4096.0f; // Segment from camera stretching 4096 forward glm::vec3 mouseSegment0 = camera.cameraPos; glm::vec3 mouseSegment1 = camera.cameraPos + pointDir * 4096.0f; // Closest point on the axis segment between the two segments glm::vec3 handlePoint, rb; get_closest_points_between_segments(axisSegment0, axisSegment1, mouseSegment0, mouseSegment1, handlePoint, rb); if (mainWindow()->selectedTool == SelectedTool::MOVE) { glm::vec3 newPos = editorToolHandles->PartCFrameFromHandlePos(draggingHandle.value(), handlePoint).Position(); glm::vec3 oldPos = editorToolHandles->adornee->lock()->cframe.Position(); glm::vec3 diff = newPos - oldPos; // Apply snapping if (snappingFactor()) diff = glm::floor(diff / snappingFactor()) * snappingFactor(); newPos = diff + oldPos; editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe.Rotation() + newPos; } else if (mainWindow()->selectedTool == SelectedTool::SCALE) { glm::vec3 handlePos = editorToolHandles->PartCFrameFromHandlePos(draggingHandle.value(), handlePoint).Position(); // Find change in handles, and negate difference in sign between axes glm::vec3 diff = handlePos - glm::vec3(editorToolHandles->adornee->lock()->position()); // Apply snapping if (snappingFactor()) diff = glm::floor(diff / snappingFactor()) * snappingFactor(); editorToolHandles->adornee->lock()->size += diff * glm::sign(draggingHandle->normal); // If ctrl is not pressed, also reposition the part such that only the dragged side gets lengthened if (!(evt->modifiers() & Qt::ControlModifier)) editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe + (diff / 2.0f); } syncPartPhysics(std::dynamic_pointer_cast(editorToolHandles->adornee->lock())); } std::optional 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)); } void MainGLWidget::handleCursorChange(QMouseEvent* evt) { QPoint position = evt->pos(); glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height())); if (raycastHandle(pointDir)) { setCursor(Qt::OpenHandCursor); return; }; std::optional rayHit = castRayNearest(camera.cameraPos, pointDir, 50000); if (rayHit && partFromBody(rayHit->body)->name != "Baseplate") { setCursor(Qt::OpenHandCursor); return; } setCursor(Qt::ArrowCursor); } void MainGLWidget::mouseMoveEvent(QMouseEvent* evt) { handleCameraRotate(evt); handleObjectDrag(evt); handleHandleDrag(evt); handleCursorChange(evt); } void MainGLWidget::mousePressEvent(QMouseEvent* evt) { switch(evt->button()) { // Camera drag case Qt::RightButton: { lastMousePos = evt->pos(); isMouseRightDragging = true; return; // Clicking on objects } case Qt::LeftButton: { QPoint position = evt->pos(); glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height())); // raycast handles auto handle = raycastHandle(pointDir); if (handle.has_value()) { isMouseDragging = true; draggingHandle = handle; return; } // raycast part std::optional rayHit = castRayNearest(camera.cameraPos, pointDir, 50000); if (!rayHit || !partFromBody(rayHit->body)) return; std::shared_ptr part = partFromBody(rayHit->body); if (part->name == "Baseplate") return; //part.selected = true; isMouseDragging = true; draggingObject = part; setSelection(std::vector { part }); // Disable bit so that we can ignore the part while raycasting // part->rigidBody->getCollider(0)->setCollisionCategoryBits(0b10); return; } default: return; } } void MainGLWidget::mouseReleaseEvent(QMouseEvent* evt) { // if (isMouseDragging) draggingObject->lock()->rigidBody->getCollider(0)->setCollisionCategoryBits(0b11); isMouseRightDragging = false; isMouseDragging = false; draggingObject = std::nullopt; draggingHandle = std::nullopt; } static int moveZ = 0; static int moveX = 0; static std::chrono::time_point lastTime = std::chrono::steady_clock::now(); void MainGLWidget::updateCycle() { float deltaTime = std::chrono::duration_cast>(std::chrono::steady_clock::now() - lastTime).count(); lastTime = std::chrono::steady_clock::now(); if (moveZ) camera.processMovement(moveZ == 1 ? DIRECTION_FORWARD : DIRECTION_BACKWARDS, deltaTime); if (moveX) camera.processMovement(moveX == 1 ? DIRECTION_LEFT : DIRECTION_RIGHT, deltaTime); } void MainGLWidget::keyPressEvent(QKeyEvent* evt) { if (evt->key() == Qt::Key_W) moveZ = 1; else if (evt->key() == Qt::Key_S) moveZ = -1; if (evt->key() == Qt::Key_A) moveX = 1; else if (evt->key() == Qt::Key_D) moveX = -1; if (evt->key() == Qt::Key_F) { workspace()->AddChild(lastPart = Part::New({ .position = camera.cameraPos + camera.cameraFront * glm::vec3(3), .rotation = glm::vec3(0), .size = glm::vec3(1, 1, 1), .color = glm::vec3(1.0f, 0.5f, 0.31f), })); syncPartPhysics(lastPart); } } void MainGLWidget::keyReleaseEvent(QKeyEvent* evt) { if (evt->key() == Qt::Key_W || evt->key() == Qt::Key_S) moveZ = 0; else if (evt->key() == Qt::Key_A || evt->key() == Qt::Key_D) moveX = 0; } MainWindow* MainGLWidget::mainWindow() { return dynamic_cast(window()); } float MainGLWidget::snappingFactor() { switch (mainWindow()->snappingMode) { case GridSnappingMode::SNAP_1_STUD: return 1; case GridSnappingMode::SNAP_05_STUDS: return 0.5; case GridSnappingMode::SNAP_OFF: return 0; } return 0; }