Compare commits
6 commits
8c2474abbf
...
86c6890ca1
Author | SHA1 | Date | |
---|---|---|---|
86c6890ca1 | |||
0da4d6d67f | |||
452c61ef90 | |||
1bdb9f146c | |||
1fdd0f4831 | |||
2569e3f56f |
21 changed files with 428 additions and 109 deletions
|
@ -4,6 +4,7 @@
|
|||
#include <map>
|
||||
#include "base.h"
|
||||
#include "datatypes/color3.h"
|
||||
#include "datatypes/ref.h"
|
||||
#include "vector.h"
|
||||
#include "cframe.h"
|
||||
|
||||
|
@ -24,7 +25,8 @@ namespace Data {
|
|||
String,
|
||||
Vector3,
|
||||
CFrame,
|
||||
Color3
|
||||
Color3,
|
||||
InstanceRef
|
||||
> __VARIANT_TYPE;
|
||||
|
||||
class Variant {
|
||||
|
|
34
core/src/datatypes/ref.cpp
Normal file
34
core/src/datatypes/ref.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include "datatypes/ref.h"
|
||||
#include "color3.h"
|
||||
#include "meta.h" // IWYU pragma: keep
|
||||
#include <memory>
|
||||
#include "objects/base/instance.h"
|
||||
|
||||
Data::InstanceRef::InstanceRef() {};
|
||||
Data::InstanceRef::InstanceRef(std::weak_ptr<Instance> instance) : ref(instance) {};
|
||||
Data::InstanceRef::~InstanceRef() = default;
|
||||
|
||||
const Data::TypeInfo Data::InstanceRef::TYPE = {
|
||||
.name = "Instance",
|
||||
// .deserializer = &Data::InstanceRef::Deserialize,
|
||||
};
|
||||
|
||||
const Data::TypeInfo& Data::InstanceRef::GetType() const { return Data::InstanceRef::TYPE; };
|
||||
|
||||
const Data::String Data::InstanceRef::ToString() const {
|
||||
return ref.expired() ? "" : ref.lock()->name;
|
||||
}
|
||||
|
||||
Data::InstanceRef::operator std::weak_ptr<Instance>() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
// Serialization
|
||||
|
||||
void Data::InstanceRef::Serialize(pugi::xml_node node) const {
|
||||
// node.text().set(this->ToHex());
|
||||
}
|
||||
|
||||
// Data::Variant Data::Color3::Deserialize(pugi::xml_node node) {
|
||||
// return Color3::FromHex(node.text().get());
|
||||
// }
|
25
core/src/datatypes/ref.h
Normal file
25
core/src/datatypes/ref.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include <memory>
|
||||
|
||||
class Instance;
|
||||
|
||||
namespace Data {
|
||||
class InstanceRef : Base {
|
||||
std::weak_ptr<Instance> ref;
|
||||
public:
|
||||
InstanceRef();
|
||||
InstanceRef(std::weak_ptr<Instance>);
|
||||
~InstanceRef();
|
||||
|
||||
virtual const TypeInfo& GetType() const override;
|
||||
static const TypeInfo TYPE;
|
||||
|
||||
operator std::weak_ptr<Instance>();
|
||||
|
||||
virtual const Data::String ToString() const override;
|
||||
virtual void Serialize(pugi::xml_node node) const override;
|
||||
// static Data::Variant Deserialize(pugi::xml_node node);
|
||||
};
|
||||
}
|
|
@ -42,7 +42,7 @@ public:
|
|||
bool isSuccess() { return std::holds_alternative<success_state>(value); }
|
||||
bool isError() { return std::holds_alternative<error_state>(value); }
|
||||
|
||||
std::optional<T_Result> success() { return isSuccess() ? std::get<success_state>(value).success : std::nullopt; }
|
||||
std::optional<T_Result> success() { return isSuccess() ? std::make_optional(std::get<success_state>(value).success) : std::nullopt; }
|
||||
std::optional<std::variant<T_Errors...>> error() { return isError() ? std::make_optional(std::get<error_state>(value).error) : std::nullopt; }
|
||||
|
||||
void logError(Logger::LogLevel logLevel = Logger::LogLevel::ERROR) {
|
||||
|
|
|
@ -56,6 +56,16 @@ bool operator ==(std::optional<std::weak_ptr<T>> a, std::optional<std::weak_ptr<
|
|||
|| (a.has_value() && !a.value().expired()) && (b.has_value() && !b.value().expired()) && a.value().lock() == b.value().lock();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator ==(std::weak_ptr<T> a, std::weak_ptr<T> b) {
|
||||
return a.expired() && b.expired() || (!a.expired() && !b.expired() && a.lock() == b.lock());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::weak_ptr<T> optional_to_weak(std::optional<std::shared_ptr<T>> a) {
|
||||
return a ? a.value() : std::weak_ptr<T>();
|
||||
}
|
||||
|
||||
// TODO: Test this
|
||||
bool Instance::ancestryContinuityCheck(std::optional<std::shared_ptr<Instance>> newParent) {
|
||||
for (std::optional<std::shared_ptr<Instance>> currentParent = newParent; currentParent.has_value(); currentParent = currentParent.value()->GetParent()) {
|
||||
|
@ -72,15 +82,15 @@ bool Instance::SetParent(std::optional<std::shared_ptr<Instance>> newParent) {
|
|||
auto lastParent = GetParent();
|
||||
if (hierarchyPreUpdateHandler.has_value()) hierarchyPreUpdateHandler.value()(this->shared_from_this(), lastParent, newParent);
|
||||
// If we currently have a parent, remove ourselves from it before adding ourselves to the new one
|
||||
if (this->parent.has_value() && !this->parent.value().expired()) {
|
||||
auto oldParent = this->parent.value().lock();
|
||||
if (!this->parent.expired()) {
|
||||
auto oldParent = this->parent.lock();
|
||||
oldParent->children.erase(std::find(oldParent->children.begin(), oldParent->children.end(), this->shared_from_this()));
|
||||
}
|
||||
// Add ourselves to the new parent
|
||||
if (newParent.has_value()) {
|
||||
newParent.value()->children.push_back(this->shared_from_this());
|
||||
}
|
||||
this->parent = newParent;
|
||||
this->parent = optional_to_weak(newParent);
|
||||
// TODO: Add code for sending signals for parent updates
|
||||
// TODO: Yeahhh maybe this isn't the best way of doing this?
|
||||
if (hierarchyPostUpdateHandler.has_value()) hierarchyPostUpdateHandler.value()(this->shared_from_this(), lastParent, newParent);
|
||||
|
@ -98,23 +108,23 @@ void Instance::updateAncestry(std::optional<std::shared_ptr<Instance>> updatedCh
|
|||
|
||||
// Update parent data model and workspace, if applicable
|
||||
if (GetParent()) {
|
||||
this->_dataModel = GetParent().value()->GetClass() == &DataModel::TYPE ? std::make_optional(std::dynamic_pointer_cast<DataModel>(GetParent().value())) : GetParent().value()->dataModel();
|
||||
this->_workspace = GetParent().value()->GetClass() == &Workspace::TYPE ? std::make_optional(std::dynamic_pointer_cast<Workspace>(GetParent().value())) : GetParent().value()->workspace();
|
||||
this->_dataModel = GetParent().value()->GetClass() == &DataModel::TYPE ? std::dynamic_pointer_cast<DataModel>(GetParent().value()) : GetParent().value()->_dataModel;
|
||||
this->_workspace = GetParent().value()->GetClass() == &Workspace::TYPE ? std::dynamic_pointer_cast<Workspace>(GetParent().value()) : GetParent().value()->_workspace;
|
||||
} else {
|
||||
this->_dataModel = std::nullopt;
|
||||
this->_workspace = std::nullopt;
|
||||
this->_dataModel = {};
|
||||
this->_workspace = {};
|
||||
}
|
||||
|
||||
OnAncestryChanged(updatedChild, newParent);
|
||||
|
||||
// Old workspace used to exist, and workspaces differ
|
||||
if (oldWorkspace.has_value() && !oldWorkspace->expired() && (!_workspace || _workspace->expired() || oldWorkspace->lock() != _workspace->lock())) {
|
||||
OnWorkspaceRemoved((oldWorkspace.has_value() && !oldWorkspace->expired()) ? std::make_optional(oldWorkspace->lock()) : std::nullopt);
|
||||
if (!oldWorkspace.expired() && oldWorkspace != _workspace) {
|
||||
OnWorkspaceRemoved(oldWorkspace.lock());
|
||||
}
|
||||
|
||||
// New workspace exists, and workspaces differ
|
||||
if (_workspace.has_value() && !_workspace->expired() && (!oldWorkspace || oldWorkspace->expired() || _workspace->lock() != oldWorkspace->lock())) {
|
||||
OnWorkspaceAdded((oldWorkspace.has_value() && !oldWorkspace->expired()) ? std::make_optional(oldWorkspace->lock()) : std::nullopt, _workspace->lock());
|
||||
if (!_workspace.expired() && (_workspace != oldWorkspace)) {
|
||||
OnWorkspaceAdded(!oldWorkspace.expired() ? std::make_optional(oldWorkspace.lock()) : std::nullopt, _workspace.lock());
|
||||
}
|
||||
|
||||
// Update ancestry in descendants
|
||||
|
@ -124,17 +134,16 @@ void Instance::updateAncestry(std::optional<std::shared_ptr<Instance>> updatedCh
|
|||
}
|
||||
|
||||
std::optional<std::shared_ptr<DataModel>> Instance::dataModel() {
|
||||
return (!_dataModel || _dataModel->expired()) ? std::nullopt : std::make_optional(_dataModel.value().lock());
|
||||
return (_dataModel.expired()) ? std::nullopt : std::make_optional(_dataModel.lock());
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<Workspace>> Instance::workspace() {
|
||||
return (!_workspace || _workspace->expired()) ? std::nullopt : std::make_optional(_workspace.value().lock());
|
||||
return (_workspace.expired()) ? std::nullopt : std::make_optional(_workspace.lock());
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<Instance>> Instance::GetParent() {
|
||||
if (!parent.has_value()) return std::nullopt;
|
||||
if (parent.value().expired()) return std::nullopt;
|
||||
return parent.value().lock();
|
||||
if (parent.expired()) return std::nullopt;
|
||||
return parent.lock();
|
||||
}
|
||||
|
||||
static std::shared_ptr<Instance> DUMMY_INSTANCE;
|
||||
|
@ -162,7 +171,7 @@ void Instance::OnWorkspaceAdded(std::optional<std::shared_ptr<Workspace>> oldWor
|
|||
// Empty stub
|
||||
}
|
||||
|
||||
void Instance::OnWorkspaceRemoved(std::optional<std::shared_ptr<Workspace>> oldWorkspace) {
|
||||
void Instance::OnWorkspaceRemoved(std::shared_ptr<Workspace> oldWorkspace) {
|
||||
// Empty stub
|
||||
}
|
||||
|
||||
|
|
|
@ -45,13 +45,13 @@ class DescendantsIterator;
|
|||
// https://stackoverflow.com/q/56415222/16255372
|
||||
class Instance : public std::enable_shared_from_this<Instance> {
|
||||
private:
|
||||
std::optional<std::weak_ptr<Instance>> parent;
|
||||
std::weak_ptr<Instance> parent;
|
||||
std::vector<std::shared_ptr<Instance>> children;
|
||||
|
||||
std::optional<std::vector<std::string>> cachedMemberList;
|
||||
|
||||
std::optional<std::weak_ptr<DataModel>> _dataModel;
|
||||
std::optional<std::weak_ptr<Workspace>> _workspace;
|
||||
std::weak_ptr<DataModel> _dataModel;
|
||||
std::weak_ptr<Workspace> _workspace;
|
||||
|
||||
bool ancestryContinuityCheck(std::optional<std::shared_ptr<Instance>> newParent);
|
||||
void updateAncestry(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent);
|
||||
|
@ -65,7 +65,7 @@ protected:
|
|||
virtual void OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent);
|
||||
virtual void OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent);
|
||||
virtual void OnWorkspaceAdded(std::optional<std::shared_ptr<Workspace>> oldWorkspace, std::shared_ptr<Workspace> newWorkspace);
|
||||
virtual void OnWorkspaceRemoved(std::optional<std::shared_ptr<Workspace>> oldWorkspace);
|
||||
virtual void OnWorkspaceRemoved(std::shared_ptr<Workspace> oldWorkspace);
|
||||
|
||||
// The root data model this object is a descendant of
|
||||
std::optional<std::shared_ptr<DataModel>> dataModel();
|
||||
|
|
|
@ -49,7 +49,7 @@ public:
|
|||
template <typename T>
|
||||
result<std::shared_ptr<T>, NoSuchService> GetService(std::string name) {
|
||||
if (services.count(name) != 0)
|
||||
return services[name];
|
||||
return std::dynamic_pointer_cast<T>(services[name]);
|
||||
|
||||
// TODO: Replace this with a result return type
|
||||
if (!INSTANCE_MAP[name] || (INSTANCE_MAP[name]->flags ^ (INSTANCE_NOTCREATABLE | INSTANCE_SERVICE)) != 0) {
|
||||
|
@ -59,13 +59,13 @@ public:
|
|||
services[name] = std::dynamic_pointer_cast<Service>(INSTANCE_MAP[name]->constructor());
|
||||
AddChild(std::dynamic_pointer_cast<Instance>(services[name]));
|
||||
|
||||
return services[name];
|
||||
return std::dynamic_pointer_cast<T>(services[name]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::optional<std::shared_ptr<T>> FindService() {
|
||||
if (services.count(name) != 0)
|
||||
return services[name];
|
||||
return std::dynamic_pointer_cast<T>(services[name]);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "common.h"
|
||||
#include "datatypes/cframe.h"
|
||||
#include "datatypes/vector.h"
|
||||
#include <glm/ext/scalar_common.hpp>
|
||||
#include <optional>
|
||||
#include <reactphysics3d/collision/RaycastInfo.h>
|
||||
#include <reactphysics3d/engine/PhysicsCommon.h>
|
||||
|
@ -37,9 +38,9 @@ Handles::Handles(): Instance(&TYPE) {
|
|||
}
|
||||
|
||||
Data::CFrame Handles::GetCFrameOfHandle(HandleFace face) {
|
||||
if (!adornee.has_value() || adornee->expired()) return Data::CFrame(glm::vec3(0,0,0), (Data::Vector3)glm::vec3(0,0,0));
|
||||
if (adornee.expired()) return Data::CFrame(glm::vec3(0,0,0), (Data::Vector3)glm::vec3(0,0,0));
|
||||
|
||||
Data::CFrame localFrame = worldMode ? Data::CFrame::IDENTITY + adornee->lock()->position() : adornee->lock()->cframe;
|
||||
Data::CFrame localFrame = worldMode ? Data::CFrame::IDENTITY + adornee.lock()->position() : adornee.lock()->cframe;
|
||||
|
||||
Data::Vector3 handleNormal = face.normal;
|
||||
if (nixAxes)
|
||||
|
@ -50,7 +51,8 @@ Data::CFrame Handles::GetCFrameOfHandle(HandleFace face) {
|
|||
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);
|
||||
glm::vec3 partSize = handlesType == HandlesType::RotateHandles ? glm::vec3(glm::max(adornee.lock()->size.x, adornee.lock()->size.y, adornee.lock()->size.z)) : adornee.lock()->size;
|
||||
Data::Vector3 handleOffset = this->worldMode ? ((Data::Vector3::ONE * 2.f) + adornee.lock()->GetAABB() * 0.5f) : Data::Vector3(2.f + partSize * 0.5f);
|
||||
Data::Vector3 handlePos = localFrame * (handleOffset * handleNormal);
|
||||
Data::CFrame cframe(handlePos, handlePos + localFrame.Rotation() * -handleNormal, upAxis);
|
||||
|
||||
|
@ -58,17 +60,17 @@ Data::CFrame Handles::GetCFrameOfHandle(HandleFace face) {
|
|||
}
|
||||
|
||||
Data::CFrame Handles::PartCFrameFromHandlePos(HandleFace face, Data::Vector3 newPos) {
|
||||
if (!adornee.has_value() || adornee->expired()) return Data::CFrame(glm::vec3(0,0,0), (Data::Vector3)glm::vec3(0,0,0));
|
||||
if (adornee.expired()) return Data::CFrame(glm::vec3(0,0,0), (Data::Vector3)glm::vec3(0,0,0));
|
||||
|
||||
Data::CFrame localFrame = worldMode ? Data::CFrame::IDENTITY + adornee->lock()->position() : adornee->lock()->cframe;
|
||||
Data::CFrame localFrame = worldMode ? Data::CFrame::IDENTITY + adornee.lock()->position() : adornee.lock()->cframe;
|
||||
Data::CFrame inverseFrame = localFrame.Inverse();
|
||||
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 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);
|
||||
|
||||
// glm::vec3 localPos = inverseFrame * newPos;
|
||||
glm::vec3 newPartPos = newPos - localFrame.Rotation() * (handleOffset * face.normal);
|
||||
return adornee->lock()->cframe.Rotation() + newPartPos;
|
||||
return adornee.lock()->cframe.Rotation() + newPartPos;
|
||||
}
|
||||
|
||||
std::optional<HandleFace> Handles::RaycastHandle(rp3d::Ray ray) {
|
||||
|
|
|
@ -36,11 +36,11 @@ public:
|
|||
|
||||
bool nixAxes = false; // XYZ -> ZXY, used with rotation
|
||||
bool active;
|
||||
std::optional<std::weak_ptr<Part>> adornee;
|
||||
std::weak_ptr<Part> adornee;
|
||||
HandlesType handlesType;
|
||||
|
||||
// inline std::optional<std::weak_ptr<Part>> GetAdornee() { return adornee; }
|
||||
// inline void SetAdornee(std::optional<std::weak_ptr<Part>> newAdornee) { this->adornee = newAdornee; updateAdornee(); };
|
||||
// inline std::weak_ptr<Part> GetAdornee() { return adornee; }
|
||||
// inline void SetAdornee(std::weak_ptr<Part> newAdornee) { this->adornee = newAdornee; updateAdornee(); };
|
||||
|
||||
Handles();
|
||||
|
||||
|
|
23
core/src/objects/jointsservice.cpp
Normal file
23
core/src/objects/jointsservice.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
#include "jointsservice.h"
|
||||
|
||||
const InstanceType JointsService::TYPE = {
|
||||
.super = &Instance::TYPE,
|
||||
.className = "JointsService",
|
||||
.constructor = &JointsService::Create,
|
||||
.flags = INSTANCE_NOTCREATABLE | INSTANCE_SERVICE,
|
||||
};
|
||||
|
||||
const InstanceType* JointsService::GetClass() {
|
||||
return &TYPE;
|
||||
}
|
||||
|
||||
|
||||
JointsService::JointsService(): Service(&TYPE) {
|
||||
}
|
||||
|
||||
JointsService::~JointsService() = default;
|
||||
|
||||
void JointsService::InitService() {
|
||||
if (initialized) return;
|
||||
initialized = true;
|
||||
}
|
18
core/src/objects/jointsservice.h
Normal file
18
core/src/objects/jointsservice.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "objects/base/service.h"
|
||||
|
||||
class JointsService : public Service {
|
||||
protected:
|
||||
void InitService() override;
|
||||
bool initialized = false;
|
||||
|
||||
public:
|
||||
const static InstanceType TYPE;
|
||||
|
||||
JointsService();
|
||||
~JointsService();
|
||||
|
||||
static inline std::shared_ptr<Instance> Create() { return std::make_shared<JointsService>(); };
|
||||
virtual const InstanceType* GetClass() override;
|
||||
};
|
|
@ -1,4 +1,5 @@
|
|||
#include "meta.h"
|
||||
#include "objects/jointsservice.h"
|
||||
#include "objects/part.h"
|
||||
#include "objects/snap.h"
|
||||
#include "objects/workspace.h"
|
||||
|
@ -9,4 +10,5 @@ std::map<std::string, const InstanceType*> INSTANCE_MAP = {
|
|||
{ "Workspace", &Workspace::TYPE },
|
||||
{ "DataModel", &DataModel::TYPE },
|
||||
{ "Snap", &Snap::TYPE },
|
||||
{ "JointsService", &JointsService::TYPE },
|
||||
};
|
|
@ -1,8 +1,13 @@
|
|||
#include "snap.h"
|
||||
|
||||
#include "datatypes/cframe.h"
|
||||
#include "datatypes/ref.h"
|
||||
#include "datatypes/vector.h"
|
||||
#include "objects/datamodel.h"
|
||||
#include "objects/jointsservice.h"
|
||||
#include "workspace.h"
|
||||
#include "part.h"
|
||||
#include <memory>
|
||||
#include <reactphysics3d/constraint/FixedJoint.h>
|
||||
|
||||
const InstanceType Snap::TYPE = {
|
||||
|
@ -16,30 +21,98 @@ const InstanceType* Snap::GetClass() {
|
|||
}
|
||||
|
||||
Snap::Snap(): Instance(&TYPE) {
|
||||
this->memberMap = std::make_unique<MemberMap>(MemberMap {
|
||||
.super = std::move(this->memberMap),
|
||||
.members = {
|
||||
{ "Part0", {
|
||||
.backingField = &part0,
|
||||
.type = &Data::InstanceRef::TYPE,
|
||||
.codec = fieldCodecOf<Data::InstanceRef, std::weak_ptr<Instance>>(),
|
||||
.updateCallback = memberFunctionOf(&Snap::onUpdated, this),
|
||||
}}, { "Part1", {
|
||||
.backingField = &part1,
|
||||
.type = &Data::InstanceRef::TYPE,
|
||||
.codec = fieldCodecOf<Data::InstanceRef, std::weak_ptr<Instance>>(),
|
||||
.updateCallback = memberFunctionOf(&Snap::onUpdated, this),
|
||||
}}, { "C0", {
|
||||
.backingField = &c0,
|
||||
.type = &Data::CFrame::TYPE,
|
||||
.codec = fieldCodecOf<Data::CFrame>(),
|
||||
.updateCallback = memberFunctionOf(&Snap::onUpdated, this),
|
||||
}}, { "C1", {
|
||||
.backingField = &c0,
|
||||
.type = &Data::CFrame::TYPE,
|
||||
.codec = fieldCodecOf<Data::CFrame>(),
|
||||
.updateCallback = memberFunctionOf(&Snap::onUpdated, this),
|
||||
}},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Snap::~Snap() {
|
||||
}
|
||||
|
||||
void Snap::OnWorkspaceAdded(std::optional<std::shared_ptr<Workspace>> oldWorkspace, std::shared_ptr<Workspace> newWorkspace) {
|
||||
if (!part0 || !part1 || part0->expired() || part1->expired()) return;
|
||||
|
||||
printVec((part0->lock()->cframe * (c1.Inverse() * c0)).Rotation().ToEulerAnglesXYZ());
|
||||
printVec(part1->lock()->cframe.Rotation().ToEulerAnglesXYZ());
|
||||
|
||||
// Update Part1's rotation and cframe prior to creating the joint as reactphysics3d locks rotation based on how it
|
||||
// used to be rather than specifying an anchor rotation, so whatever.
|
||||
Data::CFrame newFrame = part0->lock()->cframe * (c1.Inverse() * c0);
|
||||
part1->lock()->cframe = newFrame;
|
||||
newWorkspace->SyncPartPhysics(part1->lock());
|
||||
void Snap::OnAncestryChanged(std::optional<std::shared_ptr<Instance>>, std::optional<std::shared_ptr<Instance>>) {
|
||||
// If the old workspace existed, and the new one differs, delete the current joint
|
||||
if (this->joint && !this->oldWorkspace.expired() && (!workspace() || workspace().value() != this->oldWorkspace.lock())) {
|
||||
// printf("Broke joint - Removed from workspace\n");
|
||||
oldJointWorkspace.lock()->physicsWorld->destroyJoint(this->joint);
|
||||
this->joint = nullptr;
|
||||
}
|
||||
|
||||
rp::FixedJointInfo jointInfo(part0->lock()->rigidBody, part1->lock()->rigidBody, (c0.Inverse() * c1).Position());
|
||||
this->joint = dynamic_cast<rp::FixedJoint*>(workspace().value()->physicsWorld->createJoint(jointInfo));
|
||||
// If the previous parent was JointsService, and now it isn't, delete the joint
|
||||
if (this->joint && !oldParent.expired() && oldParent.lock()->GetClass() == &JointsService::TYPE && (!GetParent() || GetParent() != oldParent.lock())) {
|
||||
// printf("Broke joint - Removed from JointsService\n");
|
||||
oldJointWorkspace.lock()->physicsWorld->destroyJoint(this->joint);
|
||||
this->joint = nullptr;
|
||||
}
|
||||
|
||||
// If the new workspace exists, and the old one differs, create the joint
|
||||
if (!this->joint && workspace() && (oldWorkspace.expired() || oldWorkspace.lock() != workspace().value())) {
|
||||
// printf("Made joint - Added to workspace\n");
|
||||
buildJoint();
|
||||
}
|
||||
|
||||
// If the new parent is JointsService and the previous wasn't, then create the joint
|
||||
if (!this->joint && GetParent() && GetParent().value()->GetClass() == &JointsService::TYPE && (oldParent.expired() || GetParent() != oldParent.lock())) {
|
||||
// printf("Made joint - Added to JointsService\n");
|
||||
buildJoint();
|
||||
}
|
||||
|
||||
this->oldParent = !GetParent() ? std::weak_ptr<Instance>() : GetParent().value();
|
||||
this->oldWorkspace = !workspace() ? std::weak_ptr<Workspace>() : workspace().value();
|
||||
this->oldJointWorkspace = !jointWorkspace() ? std::weak_ptr<Workspace>() : jointWorkspace().value();
|
||||
}
|
||||
|
||||
void Snap::OnWorkspaceRemoved(std::optional<std::shared_ptr<Workspace>> oldWorkspace) {
|
||||
if (!this->joint || !oldWorkspace) return;
|
||||
void Snap::onUpdated(std::string property) {
|
||||
// We are not in the workspace, so we don't really care what values are currently set
|
||||
if (!jointWorkspace()) return;
|
||||
|
||||
oldWorkspace.value()->physicsWorld->destroyJoint(this->joint);
|
||||
this->joint = nullptr;
|
||||
// Workspace cannot have changed, so if the joint currently exists, it is in the present one
|
||||
if (this->joint)
|
||||
jointWorkspace().value()->physicsWorld->destroyJoint(this->joint);
|
||||
|
||||
buildJoint();
|
||||
}
|
||||
|
||||
void Snap::buildJoint() {
|
||||
if (part0.expired() || part1.expired() || !jointWorkspace()) return;
|
||||
|
||||
// Update Part1's rotation and cframe prior to creating the joint as reactphysics3d locks rotation based on how it
|
||||
// used to be rather than specifying an anchor rotation, so whatever.
|
||||
Data::CFrame newFrame = part0.lock()->cframe * (c1.Inverse() * c0);
|
||||
part1.lock()->cframe = newFrame;
|
||||
jointWorkspace().value()->SyncPartPhysics(part1.lock());
|
||||
|
||||
rp::FixedJointInfo jointInfo(part0.lock()->rigidBody, part1.lock()->rigidBody, (c0.Inverse() * c1).Position());
|
||||
this->joint = dynamic_cast<rp::FixedJoint*>(jointWorkspace().value()->physicsWorld->createJoint(jointInfo));
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<Workspace>> Snap::jointWorkspace() {
|
||||
if (workspace()) return workspace();
|
||||
|
||||
if (GetParent() && GetParent().value()->GetClass() == &JointsService::TYPE)
|
||||
return std::dynamic_pointer_cast<DataModel>(GetParent().value()->GetParent().value())->GetService<Workspace>("Workspace");
|
||||
|
||||
return {};
|
||||
}
|
|
@ -5,17 +5,28 @@
|
|||
#include <optional>
|
||||
|
||||
class Part;
|
||||
class Workspace;
|
||||
|
||||
class Snap : public Instance {
|
||||
rp::FixedJoint* joint;
|
||||
rp::FixedJoint* joint = nullptr;
|
||||
|
||||
std::weak_ptr<Instance> oldParent;
|
||||
// The actual workspace the joint is a part of
|
||||
std::weak_ptr<Workspace> oldWorkspace;
|
||||
// The pseudo-workspace the joint is a part of (including if parented to JointsService)
|
||||
std::weak_ptr<Workspace> oldJointWorkspace;
|
||||
protected:
|
||||
void OnWorkspaceAdded(std::optional<std::shared_ptr<Workspace>> oldWorkspace, std::shared_ptr<Workspace> newWorkspace) override;
|
||||
void OnWorkspaceRemoved(std::optional<std::shared_ptr<Workspace>> oldWorkspace) override;
|
||||
void OnAncestryChanged(std::optional<std::shared_ptr<Instance>>, std::optional<std::shared_ptr<Instance>>) override;
|
||||
|
||||
std::optional<std::shared_ptr<Workspace>> jointWorkspace();
|
||||
void onUpdated(std::string property);
|
||||
void buildJoint();
|
||||
void breakJoint();
|
||||
public:
|
||||
const static InstanceType TYPE;
|
||||
|
||||
std::optional<std::weak_ptr<Part>> part0;
|
||||
std::optional<std::weak_ptr<Part>> part1;
|
||||
std::weak_ptr<Part> part0;
|
||||
std::weak_ptr<Part> part1;
|
||||
Data::CFrame c0;
|
||||
Data::CFrame c1;
|
||||
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
#include <GL/glew.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <GL/gl.h>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <glm/ext.hpp>
|
||||
#include <glm/ext/matrix_float4x4.hpp>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <glm/trigonometric.hpp>
|
||||
#include <memory>
|
||||
|
||||
#include "datatypes/cframe.h"
|
||||
#include "objects/handles.h"
|
||||
#include "rendering/torus.h"
|
||||
#include "shader.h"
|
||||
#include "mesh.h"
|
||||
#include "defaultmeshes.h"
|
||||
|
@ -42,6 +46,8 @@ void renderInit(GLFWwindow* window, int width, int height) {
|
|||
viewportWidth = width, viewportHeight = height;
|
||||
glViewport(0, 0, width, height);
|
||||
|
||||
int argc = 1;
|
||||
char* argv = const_cast<char*>("");
|
||||
initMeshes();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
@ -205,7 +211,7 @@ void renderSkyBox() {
|
|||
}
|
||||
|
||||
void renderHandles() {
|
||||
if (!editorToolHandles->adornee.has_value() || !editorToolHandles->active) return;
|
||||
if (editorToolHandles->adornee.expired() || !editorToolHandles->active) return;
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glCullFace(GL_BACK);
|
||||
|
@ -396,6 +402,57 @@ void renderOutlines() {
|
|||
}
|
||||
}
|
||||
|
||||
void renderRotationArcs() {
|
||||
if (editorToolHandles->adornee.expired() || editorToolHandles->handlesType != HandlesType::RotateHandles) return;
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
glFrontFace(GL_CW);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Use shader
|
||||
handleShader->use();
|
||||
|
||||
handleShader->set("sunLight", DirLight {
|
||||
.direction = glm::vec3(-0.2f, -1.0f, -0.3f),
|
||||
.ambient = glm::vec3(0.2f, 0.2f, 0.2f),
|
||||
.diffuse = glm::vec3(0.5f, 0.5f, 0.5f),
|
||||
.specular = glm::vec3(1.0f, 1.0f, 1.0f),
|
||||
});
|
||||
|
||||
// view/projection transformations
|
||||
glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)viewportWidth / (float)viewportHeight, 0.1f, 1000.0f);
|
||||
glm::mat4 view = camera.getLookAt();
|
||||
handleShader->set("projection", projection);
|
||||
handleShader->set("view", view);
|
||||
|
||||
// Pass in the camera position
|
||||
handleShader->set("viewPos", camera.cameraPos);
|
||||
|
||||
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(getSelection()[0].lock());
|
||||
|
||||
for (HandleFace face : HandleFace::Faces) {
|
||||
if (glm::any(glm::lessThan(face.normal, glm::vec3(0)))) continue;
|
||||
glm::mat4 model = part->cframe * Data::CFrame(glm::vec3(0), face.normal, glm::vec3(0, 1.01, 0.1));
|
||||
handleShader->set("model", model);
|
||||
|
||||
float radius = glm::max(part->size.x, part->size.y, part->size.z) / 2.f + 2.f;
|
||||
|
||||
handleShader->set("material", Material {
|
||||
.diffuse = glm::abs(face.normal),
|
||||
.specular = glm::vec3(0.5f, 0.5f, 0.5f),
|
||||
.shininess = 16.0f,
|
||||
});
|
||||
glm::mat3 normalMatrix = glm::mat3(glm::transpose(glm::inverse(model)));
|
||||
handleShader->set("normalMatrix", normalMatrix);
|
||||
|
||||
genTorus(radius, 0.05f, 20, 20);
|
||||
}
|
||||
}
|
||||
|
||||
void render(GLFWwindow* window) {
|
||||
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
@ -404,6 +461,7 @@ void render(GLFWwindow* window) {
|
|||
renderHandles();
|
||||
renderParts();
|
||||
renderOutlines();
|
||||
renderRotationArcs();
|
||||
if (wireframeRendering)
|
||||
renderWireframe();
|
||||
// TODO: Make this a debug flag
|
||||
|
|
47
core/src/rendering/torus.cpp
Normal file
47
core/src/rendering/torus.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include "torus.h"
|
||||
#include <cmath>
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#define PI 3.1415926535f
|
||||
|
||||
struct vec { float x; float y; float z; };
|
||||
|
||||
void genTorusPoint(float outerRadius, float innerRadius, float tubeSides, float ringSides, int tube, int ring) {
|
||||
float angle = float(tube) / tubeSides * 2 * PI;
|
||||
float xL = innerRadius * cos(angle);
|
||||
float yL = innerRadius * sin(angle);
|
||||
|
||||
float outerAngle = float(ring) / ringSides * 2 * PI;
|
||||
float x = (outerRadius + xL) * cos(outerAngle);
|
||||
float y = (outerRadius + xL) * sin(outerAngle);
|
||||
float z = yL;
|
||||
|
||||
// return vec { x, y, z };
|
||||
glVertex3f(x, y, z);
|
||||
}
|
||||
|
||||
// made by yours truly
|
||||
void genTorus(float outerRadius, float innerRadius, int tubeSides, int ringSides) {
|
||||
glBegin(GL_TRIANGLES);
|
||||
|
||||
for (int i = 0; i < tubeSides; i++) {
|
||||
float tube = float(i) / tubeSides;
|
||||
for (int j = 0; j < ringSides; j++) {
|
||||
float ring = float(j) / ringSides;
|
||||
|
||||
int in = (i+1) % tubeSides;
|
||||
int jn = (j+1) % tubeSides;
|
||||
|
||||
genTorusPoint(outerRadius, innerRadius, tubeSides, ringSides, i, j);
|
||||
genTorusPoint(outerRadius, innerRadius, tubeSides, ringSides, in, j);
|
||||
genTorusPoint(outerRadius, innerRadius, tubeSides, ringSides, in, jn);
|
||||
|
||||
genTorusPoint(outerRadius, innerRadius, tubeSides, ringSides, in, jn);
|
||||
genTorusPoint(outerRadius, innerRadius, tubeSides, ringSides, i, jn);
|
||||
genTorusPoint(outerRadius, innerRadius, tubeSides, ringSides, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
glEnd();
|
||||
}
|
11
core/src/rendering/torus.h
Normal file
11
core/src/rendering/torus.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
Generates a torus made of circles extruded into cylinders and spun around the origin
|
||||
|
||||
float outerRadius - The radius of the "circle" from the origin at which the center of the cylinders lie
|
||||
float innerRadius - The radius of the cylinders
|
||||
int tubeSides - Number of vertices in each circle
|
||||
int ringSides - How many cylinder circles to draw around the torus
|
||||
*/
|
||||
void genTorus(float outerRadius, float innerRadius, int tubeSides, int ringSides);
|
|
@ -44,7 +44,7 @@ void MainGLWidget::resizeGL(int w, int h) {
|
|||
glm::vec2 firstPoint;
|
||||
glm::vec2 secondPoint;
|
||||
|
||||
extern std::optional<std::weak_ptr<Part>> draggingObject;
|
||||
extern std::weak_ptr<Part> draggingObject;
|
||||
extern std::optional<HandleFace> draggingHandle;
|
||||
extern Shader* shader;
|
||||
void MainGLWidget::paintGL() {
|
||||
|
@ -107,19 +107,19 @@ Data::CFrame snapCFrame(Data::CFrame frame) {
|
|||
}
|
||||
|
||||
bool isMouseDragging = false;
|
||||
std::optional<std::weak_ptr<Part>> draggingObject;
|
||||
std::weak_ptr<Part> draggingObject;
|
||||
std::optional<HandleFace> draggingHandle;
|
||||
Data::Vector3 initialHitPos;
|
||||
Data::Vector3 initialHitNormal;
|
||||
Data::CFrame initialFrame;
|
||||
void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
|
||||
if (!isMouseDragging || !draggingObject || mainWindow()->selectedTool >= TOOL_SMOOTH) return;
|
||||
if (!isMouseDragging || draggingObject.expired() || mainWindow()->selectedTool >= TOOL_SMOOTH) return;
|
||||
|
||||
QPoint position = evt->pos();
|
||||
|
||||
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) {
|
||||
return (part == draggingObject->lock()) ? FilterResult::PASS : FilterResult::TARGET;
|
||||
return (part == draggingObject.lock()) ? FilterResult::PASS : FilterResult::TARGET;
|
||||
});
|
||||
|
||||
if (!rayHit) return;
|
||||
|
@ -133,13 +133,13 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
|
|||
Data::Vector3 vec = rayHit->worldPoint + (tFormedInitialPos - tFormedHitPos);
|
||||
// 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
|
||||
Data::CFrame localFrame = (targetFrame.Inverse() * (draggingObject->lock()->cframe.Rotation() + vec));
|
||||
Data::CFrame localFrame = (targetFrame.Inverse() * (draggingObject.lock()->cframe.Rotation() + vec));
|
||||
|
||||
// Snap axis
|
||||
localFrame = snapCFrame(localFrame);
|
||||
|
||||
// Snap to studs
|
||||
Data::Vector3 draggingPartSize = draggingObject->lock()->size;
|
||||
Data::Vector3 draggingPartSize = draggingObject.lock()->size;
|
||||
glm::vec3 inverseNormalPartSize = (Data::Vector3)(partSize - glm::vec3(localFrame.Rotation() * draggingPartSize)) * inverseSurfaceNormal / 2.f;
|
||||
if (snappingFactor() > 0)
|
||||
localFrame = localFrame.Rotation() + glm::round(glm::vec3(localFrame.Position() * inverseSurfaceNormal - inverseNormalPartSize) / snappingFactor()) * snappingFactor() + inverseNormalPartSize
|
||||
|
@ -149,13 +149,13 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
|
|||
|
||||
// Unsink the object
|
||||
// Get the normal of the surface relative to the part's frame, and get the size along that vector
|
||||
Data::Vector3 unsinkOffset = newFrame.Rotation() * ((newFrame.Rotation().Inverse() * rayHit->worldNormal) * draggingObject->lock()->size / 2);
|
||||
Data::Vector3 unsinkOffset = newFrame.Rotation() * ((newFrame.Rotation().Inverse() * rayHit->worldNormal) * draggingObject.lock()->size / 2);
|
||||
|
||||
draggingObject->lock()->cframe = newFrame + unsinkOffset;
|
||||
draggingObject.lock()->cframe = newFrame + unsinkOffset;
|
||||
|
||||
|
||||
gWorkspace()->SyncPartPhysics(draggingObject->lock());
|
||||
sendPropertyUpdatedSignal(draggingObject->lock(), "Position", draggingObject->lock()->position());
|
||||
gWorkspace()->SyncPartPhysics(draggingObject.lock());
|
||||
sendPropertyUpdatedSignal(draggingObject.lock(), "Position", draggingObject.lock()->position());
|
||||
}
|
||||
|
||||
inline glm::vec3 vec3fy(glm::vec4 vec) {
|
||||
|
@ -164,11 +164,11 @@ inline glm::vec3 vec3fy(glm::vec4 vec) {
|
|||
|
||||
// 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;
|
||||
if (!isMouseDragging || !draggingHandle|| editorToolHandles->adornee.expired() || !editorToolHandles->active) return;
|
||||
|
||||
QPoint position = evt->pos();
|
||||
|
||||
auto part = editorToolHandles->adornee->lock();
|
||||
auto part = editorToolHandles->adornee.lock();
|
||||
|
||||
// This was actually quite a difficult problem to solve, managing to get the handle to go underneath the cursor
|
||||
|
||||
|
@ -196,7 +196,7 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
|
|||
glm::vec3 centerPoint = editorToolHandles->PartCFrameFromHandlePos(draggingHandle.value(), handlePoint).Position();
|
||||
|
||||
// Apply snapping in the current frame
|
||||
glm::vec3 diff = centerPoint - (glm::vec3)editorToolHandles->adornee->lock()->position();
|
||||
glm::vec3 diff = centerPoint - (glm::vec3)part->position();
|
||||
if (snappingFactor()) diff = frame.Rotation() * (glm::round(glm::vec3(frame.Inverse().Rotation() * diff) / snappingFactor()) * snappingFactor());
|
||||
|
||||
Data::Vector3 oldSize = part->size;
|
||||
|
@ -204,7 +204,7 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
|
|||
switch (mainWindow()->selectedTool) {
|
||||
case TOOL_MOVE: {
|
||||
// Add difference
|
||||
editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe + diff;
|
||||
part->cframe = part->cframe + diff;
|
||||
} break;
|
||||
|
||||
case TOOL_SCALE: {
|
||||
|
@ -241,18 +241,18 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
|
|||
if (mainWindow()->editSoundEffects && (oldSize != part->size) && QFile::exists("./assets/excluded/switch.wav"))
|
||||
playSound("./assets/excluded/switch.wav");
|
||||
|
||||
gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
|
||||
sendPropertyUpdatedSignal(draggingObject->lock(), "Position", draggingObject->lock()->position());
|
||||
sendPropertyUpdatedSignal(draggingObject->lock(), "Size", Data::Vector3(draggingObject->lock()->size));
|
||||
gWorkspace()->SyncPartPhysics(part);
|
||||
sendPropertyUpdatedSignal(part, "Position", part->position());
|
||||
sendPropertyUpdatedSignal(part, "Size", Data::Vector3(part->size));
|
||||
}
|
||||
|
||||
// Also implemented based on Godot: [c7ea8614](godot/editor/plugins/canvas_item_editor_plugin.cpp#L1490)
|
||||
glm::vec2 startPoint;
|
||||
void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
|
||||
if (!isMouseDragging || !draggingHandle || !editorToolHandles->adornee || !editorToolHandles->active) return;
|
||||
if (!isMouseDragging || !draggingHandle || editorToolHandles->adornee.expired() || !editorToolHandles->active) return;
|
||||
|
||||
glm::vec2 destPoint = glm::vec2(evt->pos().x(), evt->pos().y());
|
||||
auto part = editorToolHandles->adornee->lock();
|
||||
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);
|
||||
|
@ -284,12 +284,12 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
|
|||
|
||||
part->cframe = initialFrame * Data::CFrame::FromEulerAnglesXYZ(-angles);
|
||||
|
||||
gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
|
||||
sendPropertyUpdatedSignal(draggingObject->lock(), "Rotation", draggingObject->lock()->cframe.ToEulerAnglesXYZ());
|
||||
gWorkspace()->SyncPartPhysics(part);
|
||||
sendPropertyUpdatedSignal(part, "Rotation", part->cframe.ToEulerAnglesXYZ());
|
||||
}
|
||||
|
||||
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
|
||||
if (!editorToolHandles->adornee.has_value() || !editorToolHandles->active) return std::nullopt;
|
||||
if (editorToolHandles->adornee.expired() || !editorToolHandles->active) return std::nullopt;
|
||||
return editorToolHandles->RaycastHandle(rp3d::Ray(glmToRp(camera.cameraPos), glmToRp(glm::normalize(pointDir)) * 50000));
|
||||
}
|
||||
|
||||
|
@ -353,7 +353,7 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
|||
auto handle = raycastHandle(pointDir);
|
||||
if (handle.has_value()) {
|
||||
startPoint = glm::vec2(evt->pos().x(), evt->pos().y());
|
||||
initialFrame = editorToolHandles->adornee->lock()->cframe;
|
||||
initialFrame = editorToolHandles->adornee.lock()->cframe;
|
||||
isMouseDragging = true;
|
||||
draggingHandle = handle;
|
||||
return;
|
||||
|
@ -417,10 +417,10 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
|||
}
|
||||
|
||||
void MainGLWidget::mouseReleaseEvent(QMouseEvent* evt) {
|
||||
// if (isMouseDragging) draggingObject->lock()->rigidBody->getCollider(0)->setCollisionCategoryBits(0b11);
|
||||
// if (isMouseDragging) draggingObject.lock()->rigidBody->getCollider(0)->setCollisionCategoryBits(0b11);
|
||||
isMouseRightDragging = false;
|
||||
isMouseDragging = false;
|
||||
draggingObject = std::nullopt;
|
||||
draggingObject = {};
|
||||
draggingHandle = std::nullopt;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "./ui_mainwindow.h"
|
||||
#include "common.h"
|
||||
#include "logger.h"
|
||||
#include "objects/jointsservice.h"
|
||||
#include "objects/snap.h"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
@ -100,7 +101,7 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
|
||||
// Update handles
|
||||
addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) {
|
||||
editorToolHandles->adornee = std::nullopt;
|
||||
editorToolHandles->adornee = {};
|
||||
if (newSelection.size() == 0) return;
|
||||
InstanceRef inst = newSelection[0].lock();
|
||||
if (inst->GetClass() != &Part::TYPE) return;
|
||||
|
@ -151,25 +152,26 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
.color = glm::vec3(0.639216f, 0.635294f, 0.647059f),
|
||||
}));
|
||||
gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart);
|
||||
// auto part0 = ui->mainWidget->lastPart;
|
||||
auto part0 = ui->mainWidget->lastPart;
|
||||
|
||||
// gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({
|
||||
// .position = glm::vec3(1.6691498, 0.82489049, -0.73040605),
|
||||
// // .rotation = glm::vec3(0.5, 2, 1),
|
||||
// .rotation = glm::vec3(-2.6415927, 1.1415926, -2.141639),
|
||||
// .size = glm::vec3(4, 1.2, 2),
|
||||
// .color = glm::vec3(0.639216f, 0.635294f, 0.647059f),
|
||||
// }));
|
||||
// gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart);
|
||||
// auto part1 = ui->mainWidget->lastPart;
|
||||
gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({
|
||||
.position = glm::vec3(1.6691498, 0.82489049, -0.73040605),
|
||||
// .rotation = glm::vec3(0.5, 2, 1),
|
||||
.rotation = glm::vec3(-2.6415927, 1.1415926, -2.141639),
|
||||
.size = glm::vec3(4, 1.2, 2),
|
||||
.color = glm::vec3(0.639216f, 0.635294f, 0.647059f),
|
||||
}));
|
||||
gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart);
|
||||
auto part1 = ui->mainWidget->lastPart;
|
||||
|
||||
// auto snap = Snap::New();
|
||||
// snap->part0 = part0;
|
||||
// snap->part1 = part1;
|
||||
// snap->c0 = part1->cframe;
|
||||
// snap->c1 = part0->cframe;
|
||||
auto snap = Snap::New();
|
||||
snap->part0 = part0;
|
||||
snap->part1 = part1;
|
||||
snap->c0 = part1->cframe;
|
||||
snap->c1 = part0->cframe;
|
||||
|
||||
// gWorkspace()->AddChild(snap);
|
||||
gDataModel->GetService<JointsService>("JointsService").expect()->AddChild(snap);
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent* evt) {
|
||||
|
|
|
@ -36,8 +36,8 @@ public:
|
|||
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
|
||||
if (index.column() == 0) return nullptr;
|
||||
|
||||
if (!index.parent().isValid() || !view->currentInstance || view->currentInstance->expired()) return nullptr;
|
||||
InstanceRef inst = view->currentInstance->lock();
|
||||
if (!index.parent().isValid() || view->currentInstance.expired()) return nullptr;
|
||||
InstanceRef inst = view->currentInstance.lock();
|
||||
|
||||
// If the property is deeper than 1 layer, then it is considered composite
|
||||
// Handle specially
|
||||
|
@ -105,8 +105,8 @@ public:
|
|||
void setEditorData(QWidget *editor, const QModelIndex &index) const override {
|
||||
if (index.column() == 0) return;
|
||||
|
||||
if (!index.parent().isValid() || !view->currentInstance || view->currentInstance->expired()) return;
|
||||
InstanceRef inst = view->currentInstance->lock();
|
||||
if (!index.parent().isValid() || view->currentInstance.expired()) return;
|
||||
InstanceRef inst = view->currentInstance.lock();
|
||||
|
||||
bool isComposite = index.parent().parent().isValid();
|
||||
std::string componentName = isComposite ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString() : "";
|
||||
|
@ -157,8 +157,8 @@ public:
|
|||
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override {
|
||||
if (index.column() == 0) return;
|
||||
|
||||
if (!index.parent().isValid() || !view->currentInstance || view->currentInstance->expired()) return;
|
||||
InstanceRef inst = view->currentInstance->lock();
|
||||
if (!index.parent().isValid() || view->currentInstance.expired()) return;
|
||||
InstanceRef inst = view->currentInstance.lock();
|
||||
|
||||
bool isComposite = index.parent().parent().isValid();
|
||||
std::string componentName = isComposite ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString() : "";
|
||||
|
@ -268,6 +268,7 @@ void PropertiesView::drawBranches(QPainter *painter, const QRect &rect, const QM
|
|||
|
||||
void PropertiesView::setSelected(std::optional<InstanceRef> instance) {
|
||||
clear();
|
||||
currentInstance = {};
|
||||
if (!instance) return;
|
||||
InstanceRef inst = instance.value();
|
||||
currentInstance = inst;
|
||||
|
@ -289,6 +290,7 @@ void PropertiesView::setSelected(std::optional<InstanceRef> instance) {
|
|||
}
|
||||
|
||||
std::vector<std::string> properties = inst->GetProperties();
|
||||
std::sort(properties.begin(),properties.end(), [&](auto a, auto b) { return a < b; });
|
||||
|
||||
for (std::string property : properties) {
|
||||
PropertyMeta meta = inst->GetPropertyMeta(property).expect();
|
||||
|
@ -334,8 +336,8 @@ void PropertiesView::setSelected(std::optional<InstanceRef> instance) {
|
|||
}
|
||||
|
||||
void PropertiesView::propertyChanged(QTreeWidgetItem *item, int column) {
|
||||
if (!item->parent() || (item->parent() && item->parent()->parent()) || !currentInstance || currentInstance->expired()) return;
|
||||
InstanceRef inst = currentInstance->lock();
|
||||
if (!item->parent() || (item->parent() && item->parent()->parent()) || currentInstance.expired()) return;
|
||||
InstanceRef inst = currentInstance.lock();
|
||||
|
||||
std::string propertyName = item->data(0, Qt::DisplayRole).toString().toStdString();
|
||||
PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect();
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace Data { class Variant; };
|
|||
class PropertiesView : public QTreeWidget {
|
||||
Q_DECLARE_PRIVATE(QTreeView)
|
||||
|
||||
std::optional<InstanceRefWeak> currentInstance;
|
||||
InstanceRefWeak currentInstance;
|
||||
void propertyChanged(QTreeWidgetItem *item, int column);
|
||||
void activateProperty(QTreeWidgetItem *item, int column);
|
||||
void rebuildCompositeProperty(QTreeWidgetItem *item, const Data::TypeInfo*, Data::Variant);
|
||||
|
|
Loading…
Add table
Reference in a new issue