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 <map>
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "datatypes/color3.h"
|
#include "datatypes/color3.h"
|
||||||
|
#include "datatypes/ref.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
#include "cframe.h"
|
#include "cframe.h"
|
||||||
|
|
||||||
|
@ -24,7 +25,8 @@ namespace Data {
|
||||||
String,
|
String,
|
||||||
Vector3,
|
Vector3,
|
||||||
CFrame,
|
CFrame,
|
||||||
Color3
|
Color3,
|
||||||
|
InstanceRef
|
||||||
> __VARIANT_TYPE;
|
> __VARIANT_TYPE;
|
||||||
|
|
||||||
class Variant {
|
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 isSuccess() { return std::holds_alternative<success_state>(value); }
|
||||||
bool isError() { return std::holds_alternative<error_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; }
|
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) {
|
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();
|
|| (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
|
// TODO: Test this
|
||||||
bool Instance::ancestryContinuityCheck(std::optional<std::shared_ptr<Instance>> newParent) {
|
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()) {
|
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();
|
auto lastParent = GetParent();
|
||||||
if (hierarchyPreUpdateHandler.has_value()) hierarchyPreUpdateHandler.value()(this->shared_from_this(), lastParent, newParent);
|
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 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()) {
|
if (!this->parent.expired()) {
|
||||||
auto oldParent = this->parent.value().lock();
|
auto oldParent = this->parent.lock();
|
||||||
oldParent->children.erase(std::find(oldParent->children.begin(), oldParent->children.end(), this->shared_from_this()));
|
oldParent->children.erase(std::find(oldParent->children.begin(), oldParent->children.end(), this->shared_from_this()));
|
||||||
}
|
}
|
||||||
// Add ourselves to the new parent
|
// Add ourselves to the new parent
|
||||||
if (newParent.has_value()) {
|
if (newParent.has_value()) {
|
||||||
newParent.value()->children.push_back(this->shared_from_this());
|
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: Add code for sending signals for parent updates
|
||||||
// TODO: Yeahhh maybe this isn't the best way of doing this?
|
// TODO: Yeahhh maybe this isn't the best way of doing this?
|
||||||
if (hierarchyPostUpdateHandler.has_value()) hierarchyPostUpdateHandler.value()(this->shared_from_this(), lastParent, newParent);
|
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
|
// Update parent data model and workspace, if applicable
|
||||||
if (GetParent()) {
|
if (GetParent()) {
|
||||||
this->_dataModel = GetParent().value()->GetClass() == &DataModel::TYPE ? std::make_optional(std::dynamic_pointer_cast<DataModel>(GetParent().value())) : GetParent().value()->dataModel();
|
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::make_optional(std::dynamic_pointer_cast<Workspace>(GetParent().value())) : GetParent().value()->workspace();
|
this->_workspace = GetParent().value()->GetClass() == &Workspace::TYPE ? std::dynamic_pointer_cast<Workspace>(GetParent().value()) : GetParent().value()->_workspace;
|
||||||
} else {
|
} else {
|
||||||
this->_dataModel = std::nullopt;
|
this->_dataModel = {};
|
||||||
this->_workspace = std::nullopt;
|
this->_workspace = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
OnAncestryChanged(updatedChild, newParent);
|
OnAncestryChanged(updatedChild, newParent);
|
||||||
|
|
||||||
// Old workspace used to exist, and workspaces differ
|
// Old workspace used to exist, and workspaces differ
|
||||||
if (oldWorkspace.has_value() && !oldWorkspace->expired() && (!_workspace || _workspace->expired() || oldWorkspace->lock() != _workspace->lock())) {
|
if (!oldWorkspace.expired() && oldWorkspace != _workspace) {
|
||||||
OnWorkspaceRemoved((oldWorkspace.has_value() && !oldWorkspace->expired()) ? std::make_optional(oldWorkspace->lock()) : std::nullopt);
|
OnWorkspaceRemoved(oldWorkspace.lock());
|
||||||
}
|
}
|
||||||
|
|
||||||
// New workspace exists, and workspaces differ
|
// New workspace exists, and workspaces differ
|
||||||
if (_workspace.has_value() && !_workspace->expired() && (!oldWorkspace || oldWorkspace->expired() || _workspace->lock() != oldWorkspace->lock())) {
|
if (!_workspace.expired() && (_workspace != oldWorkspace)) {
|
||||||
OnWorkspaceAdded((oldWorkspace.has_value() && !oldWorkspace->expired()) ? std::make_optional(oldWorkspace->lock()) : std::nullopt, _workspace->lock());
|
OnWorkspaceAdded(!oldWorkspace.expired() ? std::make_optional(oldWorkspace.lock()) : std::nullopt, _workspace.lock());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update ancestry in descendants
|
// 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() {
|
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() {
|
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() {
|
std::optional<std::shared_ptr<Instance>> Instance::GetParent() {
|
||||||
if (!parent.has_value()) return std::nullopt;
|
if (parent.expired()) return std::nullopt;
|
||||||
if (parent.value().expired()) return std::nullopt;
|
return parent.lock();
|
||||||
return parent.value().lock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::shared_ptr<Instance> DUMMY_INSTANCE;
|
static std::shared_ptr<Instance> DUMMY_INSTANCE;
|
||||||
|
@ -162,7 +171,7 @@ void Instance::OnWorkspaceAdded(std::optional<std::shared_ptr<Workspace>> oldWor
|
||||||
// Empty stub
|
// Empty stub
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::OnWorkspaceRemoved(std::optional<std::shared_ptr<Workspace>> oldWorkspace) {
|
void Instance::OnWorkspaceRemoved(std::shared_ptr<Workspace> oldWorkspace) {
|
||||||
// Empty stub
|
// Empty stub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,13 +45,13 @@ class DescendantsIterator;
|
||||||
// https://stackoverflow.com/q/56415222/16255372
|
// https://stackoverflow.com/q/56415222/16255372
|
||||||
class Instance : public std::enable_shared_from_this<Instance> {
|
class Instance : public std::enable_shared_from_this<Instance> {
|
||||||
private:
|
private:
|
||||||
std::optional<std::weak_ptr<Instance>> parent;
|
std::weak_ptr<Instance> parent;
|
||||||
std::vector<std::shared_ptr<Instance>> children;
|
std::vector<std::shared_ptr<Instance>> children;
|
||||||
|
|
||||||
std::optional<std::vector<std::string>> cachedMemberList;
|
std::optional<std::vector<std::string>> cachedMemberList;
|
||||||
|
|
||||||
std::optional<std::weak_ptr<DataModel>> _dataModel;
|
std::weak_ptr<DataModel> _dataModel;
|
||||||
std::optional<std::weak_ptr<Workspace>> _workspace;
|
std::weak_ptr<Workspace> _workspace;
|
||||||
|
|
||||||
bool ancestryContinuityCheck(std::optional<std::shared_ptr<Instance>> newParent);
|
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);
|
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 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 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 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
|
// The root data model this object is a descendant of
|
||||||
std::optional<std::shared_ptr<DataModel>> dataModel();
|
std::optional<std::shared_ptr<DataModel>> dataModel();
|
||||||
|
|
|
@ -49,7 +49,7 @@ public:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
result<std::shared_ptr<T>, NoSuchService> GetService(std::string name) {
|
result<std::shared_ptr<T>, NoSuchService> GetService(std::string name) {
|
||||||
if (services.count(name) != 0)
|
if (services.count(name) != 0)
|
||||||
return services[name];
|
return std::dynamic_pointer_cast<T>(services[name]);
|
||||||
|
|
||||||
// TODO: Replace this with a result return type
|
// TODO: Replace this with a result return type
|
||||||
if (!INSTANCE_MAP[name] || (INSTANCE_MAP[name]->flags ^ (INSTANCE_NOTCREATABLE | INSTANCE_SERVICE)) != 0) {
|
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());
|
services[name] = std::dynamic_pointer_cast<Service>(INSTANCE_MAP[name]->constructor());
|
||||||
AddChild(std::dynamic_pointer_cast<Instance>(services[name]));
|
AddChild(std::dynamic_pointer_cast<Instance>(services[name]));
|
||||||
|
|
||||||
return services[name];
|
return std::dynamic_pointer_cast<T>(services[name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::optional<std::shared_ptr<T>> FindService() {
|
std::optional<std::shared_ptr<T>> FindService() {
|
||||||
if (services.count(name) != 0)
|
if (services.count(name) != 0)
|
||||||
return services[name];
|
return std::dynamic_pointer_cast<T>(services[name]);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "datatypes/cframe.h"
|
#include "datatypes/cframe.h"
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
|
#include <glm/ext/scalar_common.hpp>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <reactphysics3d/collision/RaycastInfo.h>
|
#include <reactphysics3d/collision/RaycastInfo.h>
|
||||||
#include <reactphysics3d/engine/PhysicsCommon.h>
|
#include <reactphysics3d/engine/PhysicsCommon.h>
|
||||||
|
@ -37,9 +38,9 @@ Handles::Handles(): Instance(&TYPE) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Data::CFrame Handles::GetCFrameOfHandle(HandleFace face) {
|
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;
|
Data::Vector3 handleNormal = face.normal;
|
||||||
if (nixAxes)
|
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)
|
if (glm::abs(glm::dot(glm::vec3(localFrame.Rotation() * handleNormal), upAxis)) > 0.9999f)
|
||||||
upAxis = glm::vec3(0, 1, 0);
|
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::Vector3 handlePos = localFrame * (handleOffset * handleNormal);
|
||||||
Data::CFrame cframe(handlePos, handlePos + localFrame.Rotation() * -handleNormal, upAxis);
|
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) {
|
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::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);
|
Data::Vector3 handlePos = localFrame * (handleOffset * face.normal);
|
||||||
|
|
||||||
// glm::vec3 localPos = inverseFrame * newPos;
|
// glm::vec3 localPos = inverseFrame * newPos;
|
||||||
glm::vec3 newPartPos = newPos - localFrame.Rotation() * (handleOffset * face.normal);
|
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) {
|
std::optional<HandleFace> Handles::RaycastHandle(rp3d::Ray ray) {
|
||||||
|
|
|
@ -36,11 +36,11 @@ public:
|
||||||
|
|
||||||
bool nixAxes = false; // XYZ -> ZXY, used with rotation
|
bool nixAxes = false; // XYZ -> ZXY, used with rotation
|
||||||
bool active;
|
bool active;
|
||||||
std::optional<std::weak_ptr<Part>> adornee;
|
std::weak_ptr<Part> adornee;
|
||||||
HandlesType handlesType;
|
HandlesType handlesType;
|
||||||
|
|
||||||
// inline std::optional<std::weak_ptr<Part>> GetAdornee() { return adornee; }
|
// inline std::weak_ptr<Part> GetAdornee() { return adornee; }
|
||||||
// inline void SetAdornee(std::optional<std::weak_ptr<Part>> newAdornee) { this->adornee = newAdornee; updateAdornee(); };
|
// inline void SetAdornee(std::weak_ptr<Part> newAdornee) { this->adornee = newAdornee; updateAdornee(); };
|
||||||
|
|
||||||
Handles();
|
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 "meta.h"
|
||||||
|
#include "objects/jointsservice.h"
|
||||||
#include "objects/part.h"
|
#include "objects/part.h"
|
||||||
#include "objects/snap.h"
|
#include "objects/snap.h"
|
||||||
#include "objects/workspace.h"
|
#include "objects/workspace.h"
|
||||||
|
@ -9,4 +10,5 @@ std::map<std::string, const InstanceType*> INSTANCE_MAP = {
|
||||||
{ "Workspace", &Workspace::TYPE },
|
{ "Workspace", &Workspace::TYPE },
|
||||||
{ "DataModel", &DataModel::TYPE },
|
{ "DataModel", &DataModel::TYPE },
|
||||||
{ "Snap", &Snap::TYPE },
|
{ "Snap", &Snap::TYPE },
|
||||||
|
{ "JointsService", &JointsService::TYPE },
|
||||||
};
|
};
|
|
@ -1,8 +1,13 @@
|
||||||
#include "snap.h"
|
#include "snap.h"
|
||||||
|
|
||||||
|
#include "datatypes/cframe.h"
|
||||||
|
#include "datatypes/ref.h"
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
|
#include "objects/datamodel.h"
|
||||||
|
#include "objects/jointsservice.h"
|
||||||
#include "workspace.h"
|
#include "workspace.h"
|
||||||
#include "part.h"
|
#include "part.h"
|
||||||
|
#include <memory>
|
||||||
#include <reactphysics3d/constraint/FixedJoint.h>
|
#include <reactphysics3d/constraint/FixedJoint.h>
|
||||||
|
|
||||||
const InstanceType Snap::TYPE = {
|
const InstanceType Snap::TYPE = {
|
||||||
|
@ -16,30 +21,98 @@ const InstanceType* Snap::GetClass() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Snap::Snap(): Instance(&TYPE) {
|
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() {
|
Snap::~Snap() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Snap::OnWorkspaceAdded(std::optional<std::shared_ptr<Workspace>> oldWorkspace, std::shared_ptr<Workspace> newWorkspace) {
|
void Snap::OnAncestryChanged(std::optional<std::shared_ptr<Instance>>, std::optional<std::shared_ptr<Instance>>) {
|
||||||
if (!part0 || !part1 || part0->expired() || part1->expired()) return;
|
// 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())) {
|
||||||
printVec((part0->lock()->cframe * (c1.Inverse() * c0)).Rotation().ToEulerAnglesXYZ());
|
// printf("Broke joint - Removed from workspace\n");
|
||||||
printVec(part1->lock()->cframe.Rotation().ToEulerAnglesXYZ());
|
oldJointWorkspace.lock()->physicsWorld->destroyJoint(this->joint);
|
||||||
|
this->joint = nullptr;
|
||||||
// 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());
|
|
||||||
|
|
||||||
rp::FixedJointInfo jointInfo(part0->lock()->rigidBody, part1->lock()->rigidBody, (c0.Inverse() * c1).Position());
|
// If the previous parent was JointsService, and now it isn't, delete the joint
|
||||||
this->joint = dynamic_cast<rp::FixedJoint*>(workspace().value()->physicsWorld->createJoint(jointInfo));
|
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) {
|
void Snap::onUpdated(std::string property) {
|
||||||
if (!this->joint || !oldWorkspace) return;
|
// 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);
|
// Workspace cannot have changed, so if the joint currently exists, it is in the present one
|
||||||
this->joint = nullptr;
|
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>
|
#include <optional>
|
||||||
|
|
||||||
class Part;
|
class Part;
|
||||||
|
class Workspace;
|
||||||
|
|
||||||
class Snap : public Instance {
|
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:
|
protected:
|
||||||
void OnWorkspaceAdded(std::optional<std::shared_ptr<Workspace>> oldWorkspace, std::shared_ptr<Workspace> newWorkspace) override;
|
void OnAncestryChanged(std::optional<std::shared_ptr<Instance>>, std::optional<std::shared_ptr<Instance>>) override;
|
||||||
void OnWorkspaceRemoved(std::optional<std::shared_ptr<Workspace>> oldWorkspace) override;
|
|
||||||
|
std::optional<std::shared_ptr<Workspace>> jointWorkspace();
|
||||||
|
void onUpdated(std::string property);
|
||||||
|
void buildJoint();
|
||||||
|
void breakJoint();
|
||||||
public:
|
public:
|
||||||
const static InstanceType TYPE;
|
const static InstanceType TYPE;
|
||||||
|
|
||||||
std::optional<std::weak_ptr<Part>> part0;
|
std::weak_ptr<Part> part0;
|
||||||
std::optional<std::weak_ptr<Part>> part1;
|
std::weak_ptr<Part> part1;
|
||||||
Data::CFrame c0;
|
Data::CFrame c0;
|
||||||
Data::CFrame c1;
|
Data::CFrame c1;
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
#include <GL/glew.h>
|
#include <GL/glew.h>
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
#include <GL/gl.h>
|
#include <GL/gl.h>
|
||||||
|
#include <cmath>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <glm/ext.hpp>
|
#include <glm/ext.hpp>
|
||||||
#include <glm/ext/matrix_float4x4.hpp>
|
#include <glm/ext/matrix_float4x4.hpp>
|
||||||
#include <glm/ext/matrix_transform.hpp>
|
#include <glm/ext/matrix_transform.hpp>
|
||||||
#include <glm/ext/vector_float3.hpp>
|
#include <glm/ext/vector_float3.hpp>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/quaternion.hpp>
|
||||||
#include <glm/trigonometric.hpp>
|
#include <glm/trigonometric.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "datatypes/cframe.h"
|
#include "datatypes/cframe.h"
|
||||||
|
#include "objects/handles.h"
|
||||||
|
#include "rendering/torus.h"
|
||||||
#include "shader.h"
|
#include "shader.h"
|
||||||
#include "mesh.h"
|
#include "mesh.h"
|
||||||
#include "defaultmeshes.h"
|
#include "defaultmeshes.h"
|
||||||
|
@ -42,6 +46,8 @@ void renderInit(GLFWwindow* window, int width, int height) {
|
||||||
viewportWidth = width, viewportHeight = height;
|
viewportWidth = width, viewportHeight = height;
|
||||||
glViewport(0, 0, width, height);
|
glViewport(0, 0, width, height);
|
||||||
|
|
||||||
|
int argc = 1;
|
||||||
|
char* argv = const_cast<char*>("");
|
||||||
initMeshes();
|
initMeshes();
|
||||||
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
@ -205,7 +211,7 @@ void renderSkyBox() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderHandles() {
|
void renderHandles() {
|
||||||
if (!editorToolHandles->adornee.has_value() || !editorToolHandles->active) return;
|
if (editorToolHandles->adornee.expired() || !editorToolHandles->active) return;
|
||||||
|
|
||||||
glDepthMask(GL_TRUE);
|
glDepthMask(GL_TRUE);
|
||||||
glCullFace(GL_BACK);
|
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) {
|
void render(GLFWwindow* window) {
|
||||||
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
@ -404,6 +461,7 @@ void render(GLFWwindow* window) {
|
||||||
renderHandles();
|
renderHandles();
|
||||||
renderParts();
|
renderParts();
|
||||||
renderOutlines();
|
renderOutlines();
|
||||||
|
renderRotationArcs();
|
||||||
if (wireframeRendering)
|
if (wireframeRendering)
|
||||||
renderWireframe();
|
renderWireframe();
|
||||||
// TODO: Make this a debug flag
|
// 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 firstPoint;
|
||||||
glm::vec2 secondPoint;
|
glm::vec2 secondPoint;
|
||||||
|
|
||||||
extern std::optional<std::weak_ptr<Part>> draggingObject;
|
extern std::weak_ptr<Part> draggingObject;
|
||||||
extern std::optional<HandleFace> draggingHandle;
|
extern std::optional<HandleFace> draggingHandle;
|
||||||
extern Shader* shader;
|
extern Shader* shader;
|
||||||
void MainGLWidget::paintGL() {
|
void MainGLWidget::paintGL() {
|
||||||
|
@ -107,19 +107,19 @@ Data::CFrame snapCFrame(Data::CFrame frame) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isMouseDragging = false;
|
bool isMouseDragging = false;
|
||||||
std::optional<std::weak_ptr<Part>> draggingObject;
|
std::weak_ptr<Part> draggingObject;
|
||||||
std::optional<HandleFace> draggingHandle;
|
std::optional<HandleFace> draggingHandle;
|
||||||
Data::Vector3 initialHitPos;
|
Data::Vector3 initialHitPos;
|
||||||
Data::Vector3 initialHitNormal;
|
Data::Vector3 initialHitNormal;
|
||||||
Data::CFrame initialFrame;
|
Data::CFrame initialFrame;
|
||||||
void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
|
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();
|
QPoint position = evt->pos();
|
||||||
|
|
||||||
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, [](std::shared_ptr<Part> part) {
|
||||||
return (part == draggingObject->lock()) ? FilterResult::PASS : FilterResult::TARGET;
|
return (part == draggingObject.lock()) ? FilterResult::PASS : FilterResult::TARGET;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!rayHit) return;
|
if (!rayHit) return;
|
||||||
|
@ -133,13 +133,13 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
|
||||||
Data::Vector3 vec = rayHit->worldPoint + (tFormedInitialPos - tFormedHitPos);
|
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
|
// 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
|
||||||
Data::CFrame localFrame = (targetFrame.Inverse() * (draggingObject->lock()->cframe.Rotation() + vec));
|
Data::CFrame localFrame = (targetFrame.Inverse() * (draggingObject.lock()->cframe.Rotation() + vec));
|
||||||
|
|
||||||
// Snap axis
|
// Snap axis
|
||||||
localFrame = snapCFrame(localFrame);
|
localFrame = snapCFrame(localFrame);
|
||||||
|
|
||||||
// Snap to studs
|
// 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;
|
glm::vec3 inverseNormalPartSize = (Data::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
|
||||||
|
@ -149,13 +149,13 @@ 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
|
||||||
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());
|
gWorkspace()->SyncPartPhysics(draggingObject.lock());
|
||||||
sendPropertyUpdatedSignal(draggingObject->lock(), "Position", draggingObject->lock()->position());
|
sendPropertyUpdatedSignal(draggingObject.lock(), "Position", draggingObject.lock()->position());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline glm::vec3 vec3fy(glm::vec4 vec) {
|
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)
|
// Taken from Godot's implementation of moving handles (godot/editor/plugins/gizmos/gizmo_3d_helper.cpp)
|
||||||
void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
|
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();
|
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
|
// 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();
|
glm::vec3 centerPoint = editorToolHandles->PartCFrameFromHandlePos(draggingHandle.value(), handlePoint).Position();
|
||||||
|
|
||||||
// Apply snapping in the current frame
|
// 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());
|
if (snappingFactor()) diff = frame.Rotation() * (glm::round(glm::vec3(frame.Inverse().Rotation() * diff) / snappingFactor()) * snappingFactor());
|
||||||
|
|
||||||
Data::Vector3 oldSize = part->size;
|
Data::Vector3 oldSize = part->size;
|
||||||
|
@ -204,7 +204,7 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
|
||||||
switch (mainWindow()->selectedTool) {
|
switch (mainWindow()->selectedTool) {
|
||||||
case TOOL_MOVE: {
|
case TOOL_MOVE: {
|
||||||
// Add difference
|
// Add difference
|
||||||
editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe + diff;
|
part->cframe = part->cframe + diff;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case TOOL_SCALE: {
|
case TOOL_SCALE: {
|
||||||
|
@ -241,18 +241,18 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
|
||||||
if (mainWindow()->editSoundEffects && (oldSize != part->size) && QFile::exists("./assets/excluded/switch.wav"))
|
if (mainWindow()->editSoundEffects && (oldSize != part->size) && QFile::exists("./assets/excluded/switch.wav"))
|
||||||
playSound("./assets/excluded/switch.wav");
|
playSound("./assets/excluded/switch.wav");
|
||||||
|
|
||||||
gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
|
gWorkspace()->SyncPartPhysics(part);
|
||||||
sendPropertyUpdatedSignal(draggingObject->lock(), "Position", draggingObject->lock()->position());
|
sendPropertyUpdatedSignal(part, "Position", part->position());
|
||||||
sendPropertyUpdatedSignal(draggingObject->lock(), "Size", Data::Vector3(draggingObject->lock()->size));
|
sendPropertyUpdatedSignal(part, "Size", Data::Vector3(part->size));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also implemented based on Godot: [c7ea8614](godot/editor/plugins/canvas_item_editor_plugin.cpp#L1490)
|
// Also implemented based on Godot: [c7ea8614](godot/editor/plugins/canvas_item_editor_plugin.cpp#L1490)
|
||||||
glm::vec2 startPoint;
|
glm::vec2 startPoint;
|
||||||
void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
|
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());
|
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
|
// 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 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);
|
part->cframe = initialFrame * Data::CFrame::FromEulerAnglesXYZ(-angles);
|
||||||
|
|
||||||
gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
|
gWorkspace()->SyncPartPhysics(part);
|
||||||
sendPropertyUpdatedSignal(draggingObject->lock(), "Rotation", draggingObject->lock()->cframe.ToEulerAnglesXYZ());
|
sendPropertyUpdatedSignal(part, "Rotation", part->cframe.ToEulerAnglesXYZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
|
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));
|
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);
|
auto handle = raycastHandle(pointDir);
|
||||||
if (handle.has_value()) {
|
if (handle.has_value()) {
|
||||||
startPoint = glm::vec2(evt->pos().x(), evt->pos().y());
|
startPoint = glm::vec2(evt->pos().x(), evt->pos().y());
|
||||||
initialFrame = editorToolHandles->adornee->lock()->cframe;
|
initialFrame = editorToolHandles->adornee.lock()->cframe;
|
||||||
isMouseDragging = true;
|
isMouseDragging = true;
|
||||||
draggingHandle = handle;
|
draggingHandle = handle;
|
||||||
return;
|
return;
|
||||||
|
@ -417,10 +417,10 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainGLWidget::mouseReleaseEvent(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;
|
isMouseRightDragging = false;
|
||||||
isMouseDragging = false;
|
isMouseDragging = false;
|
||||||
draggingObject = std::nullopt;
|
draggingObject = {};
|
||||||
draggingHandle = std::nullopt;
|
draggingHandle = std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "./ui_mainwindow.h"
|
#include "./ui_mainwindow.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include "objects/jointsservice.h"
|
||||||
#include "objects/snap.h"
|
#include "objects/snap.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -100,7 +101,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
|
|
||||||
// Update handles
|
// Update handles
|
||||||
addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) {
|
addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) {
|
||||||
editorToolHandles->adornee = std::nullopt;
|
editorToolHandles->adornee = {};
|
||||||
if (newSelection.size() == 0) return;
|
if (newSelection.size() == 0) return;
|
||||||
InstanceRef inst = newSelection[0].lock();
|
InstanceRef inst = newSelection[0].lock();
|
||||||
if (inst->GetClass() != &Part::TYPE) return;
|
if (inst->GetClass() != &Part::TYPE) return;
|
||||||
|
@ -151,25 +152,26 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
.color = glm::vec3(0.639216f, 0.635294f, 0.647059f),
|
.color = glm::vec3(0.639216f, 0.635294f, 0.647059f),
|
||||||
}));
|
}));
|
||||||
gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart);
|
gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart);
|
||||||
// auto part0 = ui->mainWidget->lastPart;
|
auto part0 = ui->mainWidget->lastPart;
|
||||||
|
|
||||||
// gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({
|
gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({
|
||||||
// .position = glm::vec3(1.6691498, 0.82489049, -0.73040605),
|
.position = glm::vec3(1.6691498, 0.82489049, -0.73040605),
|
||||||
// // .rotation = glm::vec3(0.5, 2, 1),
|
// .rotation = glm::vec3(0.5, 2, 1),
|
||||||
// .rotation = glm::vec3(-2.6415927, 1.1415926, -2.141639),
|
.rotation = glm::vec3(-2.6415927, 1.1415926, -2.141639),
|
||||||
// .size = glm::vec3(4, 1.2, 2),
|
.size = glm::vec3(4, 1.2, 2),
|
||||||
// .color = glm::vec3(0.639216f, 0.635294f, 0.647059f),
|
.color = glm::vec3(0.639216f, 0.635294f, 0.647059f),
|
||||||
// }));
|
}));
|
||||||
// gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart);
|
gWorkspace()->SyncPartPhysics(ui->mainWidget->lastPart);
|
||||||
// auto part1 = ui->mainWidget->lastPart;
|
auto part1 = ui->mainWidget->lastPart;
|
||||||
|
|
||||||
// auto snap = Snap::New();
|
auto snap = Snap::New();
|
||||||
// snap->part0 = part0;
|
snap->part0 = part0;
|
||||||
// snap->part1 = part1;
|
snap->part1 = part1;
|
||||||
// snap->c0 = part1->cframe;
|
snap->c0 = part1->cframe;
|
||||||
// snap->c1 = part0->cframe;
|
snap->c1 = part0->cframe;
|
||||||
|
|
||||||
// gWorkspace()->AddChild(snap);
|
// gWorkspace()->AddChild(snap);
|
||||||
|
gDataModel->GetService<JointsService>("JointsService").expect()->AddChild(snap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::closeEvent(QCloseEvent* evt) {
|
void MainWindow::closeEvent(QCloseEvent* evt) {
|
||||||
|
|
|
@ -36,8 +36,8 @@ public:
|
||||||
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
|
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
|
||||||
if (index.column() == 0) return nullptr;
|
if (index.column() == 0) return nullptr;
|
||||||
|
|
||||||
if (!index.parent().isValid() || !view->currentInstance || view->currentInstance->expired()) return nullptr;
|
if (!index.parent().isValid() || view->currentInstance.expired()) return nullptr;
|
||||||
InstanceRef inst = view->currentInstance->lock();
|
InstanceRef inst = view->currentInstance.lock();
|
||||||
|
|
||||||
// If the property is deeper than 1 layer, then it is considered composite
|
// If the property is deeper than 1 layer, then it is considered composite
|
||||||
// Handle specially
|
// Handle specially
|
||||||
|
@ -105,8 +105,8 @@ public:
|
||||||
void setEditorData(QWidget *editor, const QModelIndex &index) const override {
|
void setEditorData(QWidget *editor, const QModelIndex &index) const override {
|
||||||
if (index.column() == 0) return;
|
if (index.column() == 0) return;
|
||||||
|
|
||||||
if (!index.parent().isValid() || !view->currentInstance || view->currentInstance->expired()) return;
|
if (!index.parent().isValid() || view->currentInstance.expired()) return;
|
||||||
InstanceRef inst = view->currentInstance->lock();
|
InstanceRef inst = view->currentInstance.lock();
|
||||||
|
|
||||||
bool isComposite = index.parent().parent().isValid();
|
bool isComposite = index.parent().parent().isValid();
|
||||||
std::string componentName = isComposite ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString() : "";
|
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 {
|
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override {
|
||||||
if (index.column() == 0) return;
|
if (index.column() == 0) return;
|
||||||
|
|
||||||
if (!index.parent().isValid() || !view->currentInstance || view->currentInstance->expired()) return;
|
if (!index.parent().isValid() || view->currentInstance.expired()) return;
|
||||||
InstanceRef inst = view->currentInstance->lock();
|
InstanceRef inst = view->currentInstance.lock();
|
||||||
|
|
||||||
bool isComposite = index.parent().parent().isValid();
|
bool isComposite = index.parent().parent().isValid();
|
||||||
std::string componentName = isComposite ? view->itemFromIndex(index)->data(0, Qt::DisplayRole).toString().toStdString() : "";
|
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) {
|
void PropertiesView::setSelected(std::optional<InstanceRef> instance) {
|
||||||
clear();
|
clear();
|
||||||
|
currentInstance = {};
|
||||||
if (!instance) return;
|
if (!instance) return;
|
||||||
InstanceRef inst = instance.value();
|
InstanceRef inst = instance.value();
|
||||||
currentInstance = inst;
|
currentInstance = inst;
|
||||||
|
@ -289,6 +290,7 @@ void PropertiesView::setSelected(std::optional<InstanceRef> instance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> properties = inst->GetProperties();
|
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) {
|
for (std::string property : properties) {
|
||||||
PropertyMeta meta = inst->GetPropertyMeta(property).expect();
|
PropertyMeta meta = inst->GetPropertyMeta(property).expect();
|
||||||
|
@ -334,8 +336,8 @@ void PropertiesView::setSelected(std::optional<InstanceRef> instance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertiesView::propertyChanged(QTreeWidgetItem *item, int column) {
|
void PropertiesView::propertyChanged(QTreeWidgetItem *item, int column) {
|
||||||
if (!item->parent() || (item->parent() && item->parent()->parent()) || !currentInstance || currentInstance->expired()) return;
|
if (!item->parent() || (item->parent() && item->parent()->parent()) || currentInstance.expired()) return;
|
||||||
InstanceRef inst = currentInstance->lock();
|
InstanceRef inst = currentInstance.lock();
|
||||||
|
|
||||||
std::string propertyName = item->data(0, Qt::DisplayRole).toString().toStdString();
|
std::string propertyName = item->data(0, Qt::DisplayRole).toString().toStdString();
|
||||||
PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect();
|
PropertyMeta meta = inst->GetPropertyMeta(propertyName).expect();
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace Data { class Variant; };
|
||||||
class PropertiesView : public QTreeWidget {
|
class PropertiesView : public QTreeWidget {
|
||||||
Q_DECLARE_PRIVATE(QTreeView)
|
Q_DECLARE_PRIVATE(QTreeView)
|
||||||
|
|
||||||
std::optional<InstanceRefWeak> currentInstance;
|
InstanceRefWeak currentInstance;
|
||||||
void propertyChanged(QTreeWidgetItem *item, int column);
|
void propertyChanged(QTreeWidgetItem *item, int column);
|
||||||
void activateProperty(QTreeWidgetItem *item, int column);
|
void activateProperty(QTreeWidgetItem *item, int column);
|
||||||
void rebuildCompositeProperty(QTreeWidgetItem *item, const Data::TypeInfo*, Data::Variant);
|
void rebuildCompositeProperty(QTreeWidgetItem *item, const Data::TypeInfo*, Data::Variant);
|
||||||
|
|
Loading…
Add table
Reference in a new issue