Compare commits

...

6 commits

15 changed files with 122 additions and 59 deletions

View file

@ -68,4 +68,8 @@ public:
DEF_DATA_OP Vector3 operator *(Vector3) const; DEF_DATA_OP Vector3 operator *(Vector3) const;
DEF_DATA_OP CFrame operator +(Vector3) const; DEF_DATA_OP CFrame operator +(Vector3) const;
DEF_DATA_OP CFrame operator -(Vector3) const; DEF_DATA_OP CFrame operator -(Vector3) const;
inline CFrame operator *=(CFrame otherFrame) { return *this = *this * otherFrame; }
inline CFrame operator +=(Vector3 offset) { return *this = *this + offset; }
inline CFrame operator -=(Vector3 offset) { return *this = *this - offset; }
}; };

View file

@ -60,12 +60,12 @@ public:
DEF_DATA_OP bool operator ==(Vector3) const; DEF_DATA_OP bool operator ==(Vector3) const;
// Augmented shorthands // Augmented shorthands
inline Vector3 operator *=(float factor) const { return *this * factor; } inline Vector3 operator *=(float factor) { return *this = *this * factor; }
inline Vector3 operator /=(float factor) const { return *this / factor; } inline Vector3 operator /=(float factor) { return *this = *this / factor; }
inline Vector3 operator *=(Vector3 factor) const { return *this * factor; } inline Vector3 operator *=(Vector3 factor) { return *this = *this * factor; }
inline Vector3 operator /=(Vector3 factor) const { return *this / factor; } inline Vector3 operator /=(Vector3 factor) { return *this = *this / factor; }
inline Vector3 operator +=(Vector3 vector) const { return *this + vector; } inline Vector3 operator +=(Vector3 vector) { return *this = *this + vector; }
inline Vector3 operator -=(Vector3 vector) const { return *this + vector; } inline Vector3 operator -=(Vector3 vector) { return *this = *this + vector; }
}; };
inline void printVec(Vector3 vec) { inline void printVec(Vector3 vec) {

View file

@ -3,6 +3,7 @@
#include "datatypes/cframe.h" #include "datatypes/cframe.h"
#include "datatypes/vector.h" #include "datatypes/vector.h"
#include "math_helper.h" #include "math_helper.h"
#include "partassembly.h"
#include <glm/ext/scalar_common.hpp> #include <glm/ext/scalar_common.hpp>
#include <memory> #include <memory>
#include <optional> #include <optional>
@ -106,14 +107,13 @@ static std::shared_ptr<Part> getFirstSelectedPart() {
} }
CFrame getLocalHandleCFrame(HandleFace face) { CFrame getLocalHandleCFrame(HandleFace face) {
glm::vec3 _boxPos, boxSize, _boxMin, _boxMax; PartAssembly assembly = PartAssembly::FromSelection();
int count = getAABBOfSelection(_boxPos, boxSize, _boxMin, _boxMax);
Vector3 size; Vector3 size;
if (count == 1 && !editorToolHandles.worldMode) if (editorToolHandles.worldMode)
size = getFirstSelectedPart()->size; size = assembly.bounds();
else else
size = boxSize; size = assembly.size();
// Since rotation displays rings, all handles must be the same distance from origin in order for the // Since rotation displays rings, all handles must be the same distance from origin in order for the
// rings to be circular // rings to be circular
@ -125,12 +125,10 @@ CFrame getLocalHandleCFrame(HandleFace face) {
} }
CFrame getHandleCFrame(HandleFace face) { CFrame getHandleCFrame(HandleFace face) {
glm::vec3 boxPos, boxSize, _boxMin, _boxMax; PartAssembly assembly = PartAssembly::FromSelection();
int count = getAABBOfSelection(boxPos, boxSize, _boxMin, _boxMax);
if (count == 1 && !editorToolHandles.worldMode) { if (editorToolHandles.worldMode) {
auto part = getFirstSelectedPart(); return getLocalHandleCFrame(face) + assembly.assemblyOrigin().Position();
return part->cframe * getLocalHandleCFrame(face);
} else } else
return getLocalHandleCFrame(face) + boxPos; return assembly.assemblyOrigin() * getLocalHandleCFrame(face);
} }

View file

@ -1,4 +1,4 @@
#include "model.h" #include "model.h"
Model::Model(): Instance(&TYPE) {} Model::Model(): PVInstance(&TYPE) {}
Model::~Model() = default; Model::~Model() = default;

View file

@ -2,11 +2,12 @@
#include "objects/annotation.h" #include "objects/annotation.h"
#include "objects/base/instance.h" #include "objects/base/instance.h"
#include "objects/pvinstance.h"
#include <memory> #include <memory>
// Group object for Parts // Group object for Parts
class DEF_INST_(explorer_icon="model") Model : public Instance { class DEF_INST_(explorer_icon="model") Model : public PVInstance {
AUTOGEN_PREAMBLE AUTOGEN_PREAMBLE
public: public:

View file

@ -22,7 +22,7 @@
Part::Part(): Part(PartConstructParams { .size = glm::vec3(2, 1.2, 4), .color = Color3(0.639216f, 0.635294f, 0.647059f) }) { Part::Part(): Part(PartConstructParams { .size = glm::vec3(2, 1.2, 4), .color = Color3(0.639216f, 0.635294f, 0.647059f) }) {
} }
Part::Part(PartConstructParams params): Instance(&TYPE), cframe(CFrame::FromEulerAnglesXYZ((Vector3)params.rotation) + params.position), Part::Part(PartConstructParams params): PVInstance(&TYPE), cframe(CFrame::FromEulerAnglesXYZ((Vector3)params.rotation) + params.position),
size(params.size), color(params.color), anchored(params.anchored), locked(params.locked) { size(params.size), color(params.color), anchored(params.anchored), locked(params.locked) {
} }

View file

@ -13,6 +13,7 @@
#include <reactphysics3d/reactphysics3d.h> #include <reactphysics3d/reactphysics3d.h>
#include <vector> #include <vector>
#include "annotation.h" #include "annotation.h"
#include "objects/pvinstance.h"
namespace rp = reactphysics3d; namespace rp = reactphysics3d;
@ -29,7 +30,7 @@ struct PartConstructParams {
class Workspace; class Workspace;
class DEF_INST_(explorer_icon="part") Part : public Instance { class DEF_INST_(explorer_icon="part") Part : public PVInstance {
AUTOGEN_PREAMBLE AUTOGEN_PREAMBLE
protected: protected:
// Joints where this part is Part0 // Joints where this part is Part0

View file

@ -0,0 +1,4 @@
#include "pvinstance.h"
PVInstance::PVInstance(const InstanceType* type): Instance(type) {}
PVInstance::~PVInstance() = default;

View file

@ -0,0 +1,13 @@
#pragma once
#include "objects/annotation.h"
#include "objects/base/instance.h"
class DEF_INST_ABSTRACT PVInstance : public Instance {
AUTOGEN_PREAMBLE
protected:
PVInstance(const InstanceType*);
public:
~PVInstance();
};

View file

@ -14,7 +14,8 @@ PartAssembly::PartAssembly(std::vector<std::shared_ptr<Part>> parts, bool worldM
if (parts.size() == 0) return; if (parts.size() == 0) return;
if (parts.size() == 1 && !worldMode) { if (parts.size() == 1 && !worldMode) {
_assemblyOrigin = parts[0]->cframe; _assemblyOrigin = parts[0]->cframe;
_bounds = parts[0]->size; _size = parts[0]->size;
_bounds = parts[0]->GetAABB();
return; return;
} }
@ -30,21 +31,35 @@ PartAssembly::PartAssembly(std::vector<std::shared_ptr<Part>> parts, bool worldM
getAABBCoords(pos, size, min, max); getAABBCoords(pos, size, min, max);
_assemblyOrigin = CFrame() + pos; _assemblyOrigin = CFrame() + pos;
_bounds = size; _size = _bounds = size;
} }
PartAssembly PartAssembly::FromSelection(std::vector<std::shared_ptr<Instance>> newSelection) { PartAssembly PartAssembly::FromSelection(std::vector<std::shared_ptr<Instance>> newSelection) {
std::vector<std::shared_ptr<Part>> selection; std::vector<std::shared_ptr<Part>> selection;
for (std::weak_ptr<Instance> obj : newSelection) { for (std::shared_ptr<Instance> obj : newSelection) {
if (obj.expired() || !obj.lock()->IsA<Part>()) continue; if (!obj->IsA<PVInstance>()) continue;
selection.push_back(obj.lock()->CastTo<Part>().expect()); if (obj->IsA<Part>())
selection.push_back(obj->CastTo<Part>().expect());
// Add object descendants
for (DescendantsIterator it = obj->GetDescendantsStart(); it != obj->GetDescendantsEnd(); it++) {
if (!(*it)->IsA<Part>()) continue;
selection.push_back((*it)->CastTo<Part>().expect());
}
} }
return PartAssembly(selection, editorToolHandles.worldMode); return PartAssembly(selection, editorToolHandles.worldMode);
} }
void PartAssembly::SetCollisionsEnabled(bool enabled) {
for (auto part : parts) {
part->rigidBody->getCollider(0)->setIsWorldQueryCollider(enabled);
}
}
void PartAssembly::SetOrigin(CFrame newOrigin) { void PartAssembly::SetOrigin(CFrame newOrigin) {
for (auto part : parts) { for (auto part : parts) {
part->cframe = newOrigin * (_assemblyOrigin.Inverse() * part->cframe); part->cframe = newOrigin * (_assemblyOrigin.Inverse() * part->cframe);

View file

@ -12,6 +12,7 @@ const std::vector<std::shared_ptr<Instance>> getSelection();
class PartAssembly { class PartAssembly {
CFrame _assemblyOrigin; CFrame _assemblyOrigin;
Vector3 _bounds; Vector3 _bounds;
Vector3 _size;
std::vector<std::shared_ptr<Part>> parts; std::vector<std::shared_ptr<Part>> parts;
public: public:
@ -21,6 +22,8 @@ public:
inline CFrame assemblyOrigin() { return _assemblyOrigin; }; inline CFrame assemblyOrigin() { return _assemblyOrigin; };
inline Vector3 bounds() { return _bounds; }; inline Vector3 bounds() { return _bounds; };
inline Vector3 size() { return _size; };
inline bool multipleSelected() { return parts.size() > 1; }
// Transforms the assembly such that newOrigin is now this assembly's new assemblyOrigin // Transforms the assembly such that newOrigin is now this assembly's new assemblyOrigin
void SetOrigin(CFrame newOrigin); void SetOrigin(CFrame newOrigin);
@ -32,4 +35,8 @@ public:
// If multiple parts are selected, finds the greatest scale factor of each component pair, and // If multiple parts are selected, finds the greatest scale factor of each component pair, and
// scales it up by that amount // scales it up by that amount
void Scale(Vector3 newSize, bool scaleUp = true); void Scale(Vector3 newSize, bool scaleUp = true);
// Update temporary collisions of rigidbodies. Useful for ignoring
// items for raycasts
void SetCollisionsEnabled(bool enabled);
}; };

View file

@ -271,7 +271,7 @@ void renderHandles() {
if (!editorToolHandles.active) return; if (!editorToolHandles.active) return;
auto assembly = PartAssembly::FromSelection(); auto assembly = PartAssembly::FromSelection();
if (assembly.bounds() == Vector3::ZERO) return; if (assembly.size() == Vector3::ZERO) return;
glDepthMask(GL_TRUE); glDepthMask(GL_TRUE);
glCullFace(GL_BACK); glCullFace(GL_BACK);
@ -475,11 +475,10 @@ void renderOutlines() {
} }
// Render AABB of selected parts // Render AABB of selected parts
if (count <= 1) return; PartAssembly selectionAssembly = PartAssembly::FromSelection();
if (!selectionAssembly.multipleSelected()) return;
glm::vec3 outlineSize, outlinePos; glm::vec3 outlineSize = selectionAssembly.bounds();
outlineSize = (max - min); glm::vec3 outlinePos = selectionAssembly.assemblyOrigin().Position();
outlinePos = (max + min) / 2.f;
glm::mat4 model = glm::translate(glm::mat4(1.0f), outlinePos); glm::mat4 model = glm::translate(glm::mat4(1.0f), outlinePos);
model = glm::scale(model, outlineSize + glm::vec3(0.1)); model = glm::scale(model, outlineSize + glm::vec3(0.1));
@ -517,9 +516,9 @@ void renderSelectionAssembly() {
outlineShader->set("color", glm::vec3(1.f, 0.f, 0.f)); outlineShader->set("color", glm::vec3(1.f, 0.f, 0.f));
glm::mat4 model = selectionAssembly.assemblyOrigin(); glm::mat4 model = selectionAssembly.assemblyOrigin();
model = glm::scale(model, (glm::vec3)selectionAssembly.bounds() + glm::vec3(0.1)); model = glm::scale(model, (glm::vec3)selectionAssembly.size() + glm::vec3(0.1));
outlineShader->set("model", model); outlineShader->set("model", model);
outlineShader->set("scale", (glm::vec3)selectionAssembly.bounds() + glm::vec3(0.05)); outlineShader->set("scale", (glm::vec3)selectionAssembly.size() + glm::vec3(0.05));
outlineShader->set("thickness", 0.2f); outlineShader->set("thickness", 0.2f);
OUTLINE_MESH->bind(); OUTLINE_MESH->bind();
@ -563,7 +562,7 @@ void renderRotationArcs() {
glm::mat4 model = assembly.assemblyOrigin() * CFrame(glm::vec3(0), face.normal, glm::vec3(0, 1.01, 0.1)); glm::mat4 model = assembly.assemblyOrigin() * CFrame(glm::vec3(0), face.normal, glm::vec3(0, 1.01, 0.1));
handleShader->set("model", model); handleShader->set("model", model);
float radius = glm::max(assembly.bounds().X(), assembly.bounds().Y(), assembly.bounds().Z()) / 2.f + 2.f; float radius = glm::max(assembly.size().X(), assembly.size().Y(), assembly.size().Z()) / 2.f + 2.f;
handleShader->set("material", Material { handleShader->set("material", Material {
.diffuse = glm::abs(face.normal), .diffuse = glm::abs(face.normal),

View file

@ -1,6 +1,7 @@
#include <GL/glew.h> #include <GL/glew.h>
#include <glm/common.hpp> #include <glm/common.hpp>
#include <glm/vector_relational.hpp> #include <glm/vector_relational.hpp>
#include <memory>
#include <qnamespace.h> #include <qnamespace.h>
#include <qsoundeffect.h> #include <qsoundeffect.h>
#include <string> #include <string>
@ -13,6 +14,7 @@
#include "common.h" #include "common.h"
#include "math_helper.h" #include "math_helper.h"
#include "objects/base/instance.h" #include "objects/base/instance.h"
#include "objects/pvinstance.h"
#include "partassembly.h" #include "partassembly.h"
#include "physics/util.h" #include "physics/util.h"
#include "rendering/renderer.h" #include "rendering/renderer.h"
@ -130,10 +132,10 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
QPoint position = evt->pos(); QPoint position = evt->pos();
initialAssembly.SetCollisionsEnabled(false);
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()));
std::optional<const RaycastResult> rayHit = gWorkspace()->CastRayNearest(camera.cameraPos, pointDir, 50000, [](std::shared_ptr<Part> part) { std::optional<const RaycastResult> rayHit = gWorkspace()->CastRayNearest(camera.cameraPos, pointDir, 50000);
return (part == draggingObject.lock()) ? FilterResult::PASS : FilterResult::TARGET; initialAssembly.SetCollisionsEnabled(true);
});
if (!rayHit) return; if (!rayHit) return;
@ -146,13 +148,13 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
Vector3 vec = rayHit->worldPoint + (tFormedInitialPos - tFormedHitPos); Vector3 vec = rayHit->worldPoint + (tFormedInitialPos - tFormedHitPos);
// The part being dragged's frame local to the hit target's frame, but without its position component // The part being dragged's frame local to the hit target's frame, but without its position component
// To find a world vector local to the new frame, use newFrame, not localFrame, as localFrame is localFrame is local to targetFrame in itself // To find a world vector local to the new frame, use newFrame, not localFrame, as localFrame is localFrame is local to targetFrame in itself
CFrame localFrame = (targetFrame.Inverse() * (draggingObject.lock()->cframe.Rotation() + vec)); CFrame localFrame = (targetFrame.Inverse() * (initialAssembly.assemblyOrigin().Rotation() + vec));
// Snap axis // Snap axis
localFrame = snapCFrame(localFrame); localFrame = snapCFrame(localFrame);
// Snap to studs // Snap to studs
Vector3 draggingPartSize = draggingObject.lock()->size; Vector3 draggingPartSize = initialAssembly.size();
glm::vec3 inverseNormalPartSize = (Vector3)(partSize - glm::vec3(localFrame.Rotation() * draggingPartSize)) * inverseSurfaceNormal / 2.f; glm::vec3 inverseNormalPartSize = (Vector3)(partSize - glm::vec3(localFrame.Rotation() * draggingPartSize)) * inverseSurfaceNormal / 2.f;
if (snappingFactor() > 0) if (snappingFactor() > 0)
localFrame = localFrame.Rotation() + glm::round(glm::vec3(localFrame.Position() * inverseSurfaceNormal - inverseNormalPartSize) / snappingFactor()) * snappingFactor() + inverseNormalPartSize localFrame = localFrame.Rotation() + glm::round(glm::vec3(localFrame.Position() * inverseSurfaceNormal - inverseNormalPartSize) / snappingFactor()) * snappingFactor() + inverseNormalPartSize
@ -162,14 +164,9 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
// Unsink the object // Unsink the object
// Get the normal of the surface relative to the part's frame, and get the size along that vector // Get the normal of the surface relative to the part's frame, and get the size along that vector
Vector3 unsinkOffset = newFrame.Rotation() * ((newFrame.Rotation().Inverse() * rayHit->worldNormal) * draggingObject.lock()->size / 2); Vector3 unsinkOffset = newFrame.Rotation() * ((newFrame.Rotation().Inverse() * rayHit->worldNormal) * initialAssembly.size() / 2);
draggingObject.lock()->cframe = newFrame + unsinkOffset; initialAssembly.SetOrigin(newFrame + unsinkOffset);
gWorkspace()->SyncPartPhysics(draggingObject.lock());
draggingObject.lock()->UpdateProperty("Position");
sendPropertyUpdatedSignal(draggingObject.lock(), "Position", draggingObject.lock()->position());
} }
inline glm::vec3 vec3fy(glm::vec4 vec) { inline glm::vec3 vec3fy(glm::vec4 vec) {
@ -183,7 +180,7 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
QPoint position = evt->pos(); QPoint position = evt->pos();
auto part = getHandleAdornee(); // auto part = getHandleAdornee();
// This was actually quite a difficult problem to solve, managing to get the handle to go underneath the cursor // This was actually quite a difficult problem to solve, managing to get the handle to go underneath the cursor
@ -195,7 +192,7 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
CFrame handleCFrame = getHandleCFrame(draggingHandle.value()) + dragStartHandleOffset; CFrame handleCFrame = getHandleCFrame(draggingHandle.value()) + dragStartHandleOffset;
// 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 + initialAssembly.assemblyOrigin().Position() : initialAssembly.assemblyOrigin().Rotation();
// Segment from axis stretching -4096 to +4096 rel to handle's position // Segment from axis stretching -4096 to +4096 rel to handle's position
glm::vec3 axisSegment0 = handleCFrame.Position() + (-handleCFrame.LookVector() * 4096.0f); glm::vec3 axisSegment0 = handleCFrame.Position() + (-handleCFrame.LookVector() * 4096.0f);
@ -227,17 +224,17 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
} else if (editorToolHandles.handlesType == ScaleHandles) { } else if (editorToolHandles.handlesType == ScaleHandles) {
if (evt->modifiers() & Qt::AltModifier) { if (evt->modifiers() & Qt::AltModifier) {
// If size gets too small, don't // If size gets too small, don't
if (glm::any(glm::lessThan(glm::vec3(selectionAssembly.bounds() + abs(draggingHandle->normal) * diff * 2.f), glm::vec3(0.001f)))) if (glm::any(glm::lessThan(glm::vec3(selectionAssembly.size() + abs(draggingHandle->normal) * diff * 2.f), glm::vec3(0.001f))))
return; return;
selectionAssembly.Scale(selectionAssembly.bounds() + abs(draggingHandle->normal) * diff * 2.f, diff > 0); selectionAssembly.Scale(selectionAssembly.size() + abs(draggingHandle->normal) * diff * 2.f, diff > 0);
} else { } else {
// If size gets too small, don't // If size gets too small, don't
if (glm::any(glm::lessThan(glm::vec3(selectionAssembly.bounds() + abs(draggingHandle->normal) * diff), glm::vec3(0.001f)))) if (glm::any(glm::lessThan(glm::vec3(selectionAssembly.size() + abs(draggingHandle->normal) * diff), glm::vec3(0.001f))))
return; return;
selectionAssembly.TransformBy(CFrame() + absDiff * 0.5f); selectionAssembly.TransformBy(CFrame() + absDiff * 0.5f);
selectionAssembly.Scale(selectionAssembly.bounds() + abs(draggingHandle->normal) * diff, diff > 0); selectionAssembly.Scale(selectionAssembly.size() + abs(draggingHandle->normal) * diff, diff > 0);
} }
} }
} }
@ -390,7 +387,19 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
if (!rayHit || !partFromBody(rayHit->body)) { setSelection({}); return; } if (!rayHit || !partFromBody(rayHit->body)) { setSelection({}); return; }
std::shared_ptr<Part> part = partFromBody(rayHit->body); std::shared_ptr<Part> part = partFromBody(rayHit->body);
if (part->locked) { setSelection({}); return; } if (part->locked) { setSelection({}); return; }
initialFrame = part->cframe;
std::shared_ptr<PVInstance> selObject = part;
// Traverse to the root model
if (~evt->modifiers() & Qt::AltModifier) {
std::optional<std::shared_ptr<Instance>> nextParent = selObject->GetParent();
while (nextParent.value() && nextParent.value()->IsA("Model")) {
selObject = std::dynamic_pointer_cast<PVInstance>(nextParent.value()); nextParent = selObject->GetParent();
}
}
initialAssembly = PartAssembly::FromSelection({selObject});
initialFrame = initialAssembly.assemblyOrigin();
initialHitPos = rayHit->worldPoint; initialHitPos = rayHit->worldPoint;
initialHitNormal = rayHit->worldNormal; initialHitNormal = rayHit->worldNormal;
@ -419,20 +428,20 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
//part.selected = true; //part.selected = true;
isMouseDragging = true; isMouseDragging = true;
draggingObject = part; draggingObject = part;
if (evt->modifiers() & Qt::ControlModifier) { if (evt->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier)) {
std::vector<std::shared_ptr<Instance>> currentSelection = getSelection(); std::vector<std::shared_ptr<Instance>> currentSelection = getSelection();
for (size_t i = 0; i < currentSelection.size(); i++) { for (size_t i = 0; i < currentSelection.size(); i++) {
std::shared_ptr<Instance> inst = currentSelection[i]; std::shared_ptr<Instance> inst = currentSelection[i];
if (inst == part) { if (inst == selObject) {
currentSelection.erase(currentSelection.begin() + i); currentSelection.erase(currentSelection.begin() + i);
goto skipAddPart; goto skipAddPart;
} }
} }
currentSelection.push_back(part); currentSelection.push_back(selObject);
skipAddPart: skipAddPart:
setSelection(currentSelection); setSelection(currentSelection);
} else } else
setSelection({ part }); setSelection({ selObject });
// Disable bit so that we can ignore the part while raycasting // Disable bit so that we can ignore the part while raycasting
// part->rigidBody->getCollider(0)->setCollisionCategoryBits(0b10); // part->rigidBody->getCollider(0)->setCollisionCategoryBits(0b10);

View file

@ -15,6 +15,7 @@
#include <qmessagebox.h> #include <qmessagebox.h>
#include <qmimedata.h> #include <qmimedata.h>
#include <qnamespace.h> #include <qnamespace.h>
#include <qsoundeffect.h>
#include <qstylefactory.h> #include <qstylefactory.h>
#include <qstylehints.h> #include <qstylehints.h>
#include <qmdisubwindow.h> #include <qmdisubwindow.h>
@ -61,6 +62,13 @@ void logQtMessage(QtMsgType type, const QMessageLogContext &context, const QStri
// if (defaultMessageHandler) defaultMessageHandler(type, context, msg); // if (defaultMessageHandler) defaultMessageHandler(type, context, msg);
} }
inline void playSound(QString path) {
QSoundEffect *sound = new QSoundEffect;
sound->setSource(QUrl::fromLocalFile(path));
sound->play();
sound->connect(sound, &QSoundEffect::playingChanged, [=]() { /* Thank you QSound source code! */ sound->deleteLater(); return false; });
}
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
, ui(new Ui::MainWindow) , ui(new Ui::MainWindow)
@ -383,6 +391,7 @@ void MainWindow::connectActionHandlers() {
model->SetParent(firstParent); model->SetParent(firstParent);
setSelection({ model }); setSelection({ model });
playSound("./assets/excluded/electronicpingshort.wav");
}); });
connect(ui->actionUngroupObjects, &QAction::triggered, this, [&]() { connect(ui->actionUngroupObjects, &QAction::triggered, this, [&]() {
@ -401,6 +410,7 @@ void MainWindow::connectActionHandlers() {
} }
setSelection(newSelection); setSelection(newSelection);
playSound("./assets/excluded/electronicpingshort.wav");
}); });
connect(ui->actionSaveModel, &QAction::triggered, this, [&]() { connect(ui->actionSaveModel, &QAction::triggered, this, [&]() {

View file

@ -41,13 +41,15 @@ void PlaceDocument::setRunState(RunState newState) {
std::shared_ptr<DataModel> newModel = editModeDataModel->CloneModel(); std::shared_ptr<DataModel> newModel = editModeDataModel->CloneModel();
gDataModel = newModel; gDataModel = newModel;
gDataModel->Init(true); gDataModel->Init(true);
setSelection({});
} else if (newState == RUN_PAUSED && _runState == RUN_RUNNING) { } else if (newState == RUN_PAUSED && _runState == RUN_RUNNING) {
_runState = RUN_PAUSED; _runState = RUN_PAUSED;
} else if (newState == RUN_STOPPED) { } else if (newState == RUN_STOPPED) {
_runState = RUN_STOPPED; _runState = RUN_STOPPED;
// GC: Check to make sure gDataModel gets properly garbage collected prior to this // TODO: GC: Check to make sure gDataModel gets properly garbage collected prior to this
gDataModel = editModeDataModel; gDataModel = editModeDataModel;
setSelection({});
} }
} }