Compare commits
5 commits
94898b4d9d
...
7b4a8947c5
Author | SHA1 | Date | |
---|---|---|---|
7b4a8947c5 | |||
b7e3061dee | |||
bea6f50f13 | |||
ff5eb969d9 | |||
56c991b667 |
19 changed files with 266 additions and 28 deletions
|
@ -12,10 +12,11 @@ std::shared_ptr<Handles> editorToolHandles = Handles::New();
|
||||||
|
|
||||||
|
|
||||||
std::vector<InstanceRefWeak> currentSelection;
|
std::vector<InstanceRefWeak> currentSelection;
|
||||||
std::vector<SelectionUpdateHandler> selectionUpdateHandlers;
|
std::vector<SelectionUpdateHandler> selectionUpdateListeners;
|
||||||
|
std::vector<PropertyUpdateHandler> propertyUpdatelisteners;
|
||||||
|
|
||||||
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer) {
|
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer) {
|
||||||
for (SelectionUpdateHandler handler : selectionUpdateHandlers) {
|
for (SelectionUpdateHandler handler : selectionUpdateListeners) {
|
||||||
handler(currentSelection, newSelection, fromExplorer);
|
handler(currentSelection, newSelection, fromExplorer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,5 +28,15 @@ const std::vector<InstanceRefWeak> getSelection() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void addSelectionListener(SelectionUpdateHandler handler) {
|
void addSelectionListener(SelectionUpdateHandler handler) {
|
||||||
selectionUpdateHandlers.push_back(handler);
|
selectionUpdateListeners.push_back(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendPropertyUpdatedSignal(InstanceRef instance, std::string property, Data::Variant newValue) {
|
||||||
|
for (PropertyUpdateHandler handler : propertyUpdatelisteners) {
|
||||||
|
handler(instance, property, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPropertyUpdateListener(PropertyUpdateHandler handler) {
|
||||||
|
propertyUpdatelisteners.push_back(handler);
|
||||||
}
|
}
|
|
@ -11,6 +11,7 @@ class Instance;
|
||||||
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPreUpdateHandler;
|
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPreUpdateHandler;
|
||||||
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPostUpdateHandler;
|
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPostUpdateHandler;
|
||||||
typedef std::function<void(std::vector<InstanceRefWeak> oldSelection, std::vector<InstanceRefWeak> newSelection, bool fromExplorer)> SelectionUpdateHandler;
|
typedef std::function<void(std::vector<InstanceRefWeak> oldSelection, std::vector<InstanceRefWeak> newSelection, bool fromExplorer)> SelectionUpdateHandler;
|
||||||
|
typedef std::function<void(InstanceRef instance, std::string property, Data::Variant newValue)> PropertyUpdateHandler;
|
||||||
|
|
||||||
// TEMPORARY COMMON DATA FOR VARIOUS INTERNAL COMPONENTS
|
// TEMPORARY COMMON DATA FOR VARIOUS INTERNAL COMPONENTS
|
||||||
|
|
||||||
|
@ -24,3 +25,6 @@ extern std::shared_ptr<Handles> editorToolHandles;
|
||||||
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer = false);
|
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer = false);
|
||||||
const std::vector<InstanceRefWeak> getSelection();
|
const std::vector<InstanceRefWeak> getSelection();
|
||||||
void addSelectionListener(SelectionUpdateHandler handler);
|
void addSelectionListener(SelectionUpdateHandler handler);
|
||||||
|
|
||||||
|
void sendPropertyUpdatedSignal(InstanceRef instance, std::string property, Data::Variant newValue);
|
||||||
|
void addPropertyUpdateListener(PropertyUpdateHandler handler);
|
|
@ -13,6 +13,8 @@
|
||||||
const Data::CFrame Data::CFrame::IDENTITY(glm::vec3(0, 0, 0), glm::mat3(1.f));
|
const Data::CFrame Data::CFrame::IDENTITY(glm::vec3(0, 0, 0), glm::mat3(1.f));
|
||||||
const Data::CFrame Data::CFrame::YToZ(glm::vec3(0, 0, 0), glm::mat3(glm::vec3(1, 0, 0), glm::vec3(0, 0, 1), glm::vec3(0, 1, 0)));
|
const Data::CFrame Data::CFrame::YToZ(glm::vec3(0, 0, 0), glm::mat3(glm::vec3(1, 0, 0), glm::vec3(0, 0, 1), glm::vec3(0, 1, 0)));
|
||||||
|
|
||||||
|
Data::CFrame::CFrame() : Data::CFrame::CFrame(glm::vec3(0, 0, 0), glm::mat3(1.f)) {}
|
||||||
|
|
||||||
Data::CFrame::CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22)
|
Data::CFrame::CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22)
|
||||||
: translation(x, y, z)
|
: translation(x, y, z)
|
||||||
, rotation({
|
, rotation({
|
||||||
|
|
|
@ -20,6 +20,7 @@ namespace Data {
|
||||||
// CFrame(float x, float y, float z);
|
// CFrame(float x, float y, float z);
|
||||||
// CFrame(const glm::vec3&);
|
// CFrame(const glm::vec3&);
|
||||||
// CFrame(const rp::Vector3&);
|
// CFrame(const rp::Vector3&);
|
||||||
|
CFrame();
|
||||||
CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22);
|
CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22);
|
||||||
CFrame(const rp::Transform&);
|
CFrame(const rp::Transform&);
|
||||||
CFrame(Data::Vector3 position, glm::quat quat);
|
CFrame(Data::Vector3 position, glm::quat quat);
|
||||||
|
|
|
@ -34,7 +34,7 @@ public:
|
||||||
if (isSuccess())
|
if (isSuccess())
|
||||||
return std::get<success_state>(value).success;
|
return std::get<success_state>(value).success;
|
||||||
std::visit([&](auto&& it) {
|
std::visit([&](auto&& it) {
|
||||||
Logger::fatalErrorf("Unwrapped a result with error value: [%s] %s\n\t%s", it.errorType(), it.message(), errMsg);
|
Logger::fatalErrorf("Unwrapped a result with error value: [%s] %s\n\t%s", it.errorType().c_str(), it.message().c_str(), errMsg.c_str());
|
||||||
}, error().value());
|
}, error().value());
|
||||||
panic();
|
panic();
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ Instance::Instance(const InstanceType* type) {
|
||||||
.super = std::nullopt,
|
.super = std::nullopt,
|
||||||
.members = {
|
.members = {
|
||||||
{ "Name", { .backingField = &name, .type = &Data::String::TYPE, .codec = fieldCodecOf<Data::String, std::string>() } },
|
{ "Name", { .backingField = &name, .type = &Data::String::TYPE, .codec = fieldCodecOf<Data::String, std::string>() } },
|
||||||
{ "ClassName", { .backingField = const_cast<InstanceType*>(type), .type = &Data::String::TYPE, .codec = classNameCodec(), .flags = PROP_READONLY } },
|
{ "ClassName", { .backingField = const_cast<InstanceType*>(type), .type = &Data::String::TYPE, .codec = classNameCodec(), .flags = (PropertyFlags)(PROP_READONLY | PROP_NOSAVE) } },
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -87,24 +87,16 @@ bool Instance::SetParent(std::optional<std::shared_ptr<Instance>> newParent) {
|
||||||
|
|
||||||
this->OnParentUpdated(lastParent, newParent);
|
this->OnParentUpdated(lastParent, newParent);
|
||||||
|
|
||||||
auto oldDataModel = _dataModel;
|
|
||||||
auto oldWorkspace = _workspace;
|
|
||||||
|
|
||||||
// Update parent data model and workspace, if applicable
|
|
||||||
if (newParent) {
|
|
||||||
this->_dataModel = newParent->get()->GetClass() == &DataModel::TYPE ? std::make_optional(std::dynamic_pointer_cast<DataModel>(newParent.value())) : newParent.value()->dataModel();
|
|
||||||
this->_workspace = newParent->get()->GetClass() == &Workspace::TYPE ? std::make_optional(std::dynamic_pointer_cast<Workspace>(newParent.value())) : newParent.value()->workspace();
|
|
||||||
} else {
|
|
||||||
this->_dataModel = std::nullopt;
|
|
||||||
this->_workspace = std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateAncestry(this->shared<Instance>(), newParent);
|
updateAncestry(this->shared<Instance>(), newParent);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::updateAncestry(std::optional<std::shared_ptr<Instance>> updatedChild, std::optional<std::shared_ptr<Instance>> newParent) {
|
void Instance::updateAncestry(std::optional<std::shared_ptr<Instance>> updatedChild, std::optional<std::shared_ptr<Instance>> newParent) {
|
||||||
|
auto oldDataModel = _dataModel;
|
||||||
|
auto oldWorkspace = _workspace;
|
||||||
|
|
||||||
|
// 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::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->_workspace = GetParent().value()->GetClass() == &Workspace::TYPE ? std::make_optional(std::dynamic_pointer_cast<Workspace>(GetParent().value())) : GetParent().value()->workspace();
|
||||||
|
@ -115,6 +107,16 @@ void Instance::updateAncestry(std::optional<std::shared_ptr<Instance>> updatedCh
|
||||||
|
|
||||||
OnAncestryChanged(updatedChild, newParent);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
|
||||||
// Update ancestry in descendants
|
// Update ancestry in descendants
|
||||||
for (InstanceRef child : children) {
|
for (InstanceRef child : children) {
|
||||||
child->updateAncestry(updatedChild, newParent);
|
child->updateAncestry(updatedChild, newParent);
|
||||||
|
@ -156,6 +158,14 @@ void Instance::OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child,
|
||||||
// Empty stub
|
// Empty stub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Instance::OnWorkspaceAdded(std::optional<std::shared_ptr<Workspace>> oldWorkspace, std::shared_ptr<Workspace> newWorkspace) {
|
||||||
|
// Empty stub
|
||||||
|
}
|
||||||
|
|
||||||
|
void Instance::OnWorkspaceRemoved(std::optional<std::shared_ptr<Workspace>> oldWorkspace) {
|
||||||
|
// Empty stub
|
||||||
|
}
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
result<Data::Variant, MemberNotFound> Instance::GetPropertyValue(std::string name) {
|
result<Data::Variant, MemberNotFound> Instance::GetPropertyValue(std::string name) {
|
||||||
|
@ -174,6 +184,7 @@ fallible<MemberNotFound, AssignToReadOnlyMember> Instance::SetPropertyValue(std:
|
||||||
|
|
||||||
meta.codec.write(value, meta.backingField);
|
meta.codec.write(value, meta.backingField);
|
||||||
if (meta.updateCallback) meta.updateCallback.value()(name);
|
if (meta.updateCallback) meta.updateCallback.value()(name);
|
||||||
|
sendPropertyUpdatedSignal(shared_from_this(), name, value);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -197,6 +208,12 @@ result<PropertyMeta, MemberNotFound> Instance::GetPropertyMeta(std::string name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Instance::UpdateProperty(std::string name) {
|
||||||
|
PropertyMeta meta = GetPropertyMeta(name).expect();
|
||||||
|
if (!meta.updateCallback) return; // Nothing to update, exit.
|
||||||
|
meta.updateCallback.value()(name);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> Instance::GetProperties() {
|
std::vector<std::string> Instance::GetProperties() {
|
||||||
if (cachedMemberList.has_value()) return cachedMemberList.value();
|
if (cachedMemberList.has_value()) return cachedMemberList.value();
|
||||||
|
|
||||||
|
@ -228,7 +245,7 @@ void Instance::Serialize(pugi::xml_node parent) {
|
||||||
pugi::xml_node propertiesNode = node.append_child("Properties");
|
pugi::xml_node propertiesNode = node.append_child("Properties");
|
||||||
for (std::string name : GetProperties()) {
|
for (std::string name : GetProperties()) {
|
||||||
PropertyMeta meta = GetPropertyMeta(name).expect("Meta of declared property is missing");
|
PropertyMeta meta = GetPropertyMeta(name).expect("Meta of declared property is missing");
|
||||||
if (meta.flags & PropertyFlags::PROP_NOSAVE) continue; // This property should not be serialized. Skip...
|
if (meta.flags & (PropertyFlags::PROP_NOSAVE | PropertyFlags::PROP_READONLY)) continue; // This property should not be serialized. Skip...
|
||||||
|
|
||||||
pugi::xml_node propertyNode = propertiesNode.append_child(meta.type->name);
|
pugi::xml_node propertyNode = propertiesNode.append_child(meta.type->name);
|
||||||
propertyNode.append_attribute("name").set_value(name);
|
propertyNode.append_attribute("name").set_value(name);
|
||||||
|
|
|
@ -64,6 +64,8 @@ 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 OnWorkspaceRemoved(std::optional<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();
|
||||||
|
@ -93,6 +95,8 @@ public:
|
||||||
result<Data::Variant, MemberNotFound> GetPropertyValue(std::string name);
|
result<Data::Variant, MemberNotFound> GetPropertyValue(std::string name);
|
||||||
fallible<MemberNotFound, AssignToReadOnlyMember> SetPropertyValue(std::string name, Data::Variant value);
|
fallible<MemberNotFound, AssignToReadOnlyMember> SetPropertyValue(std::string name, Data::Variant value);
|
||||||
result<PropertyMeta, MemberNotFound> GetPropertyMeta(std::string name);
|
result<PropertyMeta, MemberNotFound> GetPropertyMeta(std::string name);
|
||||||
|
// Manually trigger the update of a property. Useful internally when setting properties directly
|
||||||
|
void UpdateProperty(std::string name);
|
||||||
// Returning a list of property names feels kinda janky. Is this really the way to go?
|
// Returning a list of property names feels kinda janky. Is this really the way to go?
|
||||||
std::vector<std::string> GetProperties();
|
std::vector<std::string> GetProperties();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "meta.h"
|
#include "meta.h"
|
||||||
#include "objects/part.h"
|
#include "objects/part.h"
|
||||||
|
#include "objects/snap.h"
|
||||||
#include "objects/workspace.h"
|
#include "objects/workspace.h"
|
||||||
|
|
||||||
std::map<std::string, const InstanceType*> INSTANCE_MAP = {
|
std::map<std::string, const InstanceType*> INSTANCE_MAP = {
|
||||||
|
@ -7,4 +8,5 @@ std::map<std::string, const InstanceType*> INSTANCE_MAP = {
|
||||||
{ "Part", &Part::TYPE },
|
{ "Part", &Part::TYPE },
|
||||||
{ "Workspace", &Workspace::TYPE },
|
{ "Workspace", &Workspace::TYPE },
|
||||||
{ "DataModel", &DataModel::TYPE },
|
{ "DataModel", &DataModel::TYPE },
|
||||||
|
{ "Snap", &Snap::TYPE },
|
||||||
};
|
};
|
|
@ -61,7 +61,7 @@ const InstanceType* Part::GetClass() {
|
||||||
Part::Part(): Part(PartConstructParams { .color = Data::Color3(0.639216f, 0.635294f, 0.647059f) }) {
|
Part::Part(): Part(PartConstructParams { .color = Data::Color3(0.639216f, 0.635294f, 0.647059f) }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Part::Part(PartConstructParams params): Instance(&TYPE), cframe(Data::CFrame(params.position, params.rotation)),
|
Part::Part(PartConstructParams params): Instance(&TYPE), cframe(Data::CFrame::FromEulerAnglesXYZ((Data::Vector3)params.rotation) + params.position),
|
||||||
size(params.size), color(params.color), anchored(params.anchored), locked(params.locked) {
|
size(params.size), color(params.color), anchored(params.anchored), locked(params.locked) {
|
||||||
this->memberMap = std::make_unique<MemberMap>(MemberMap {
|
this->memberMap = std::make_unique<MemberMap>(MemberMap {
|
||||||
.super = std::move(this->memberMap),
|
.super = std::move(this->memberMap),
|
||||||
|
@ -111,15 +111,57 @@ Part::Part(PartConstructParams params): Instance(&TYPE), cframe(Data::CFrame(par
|
||||||
.codec = fieldCodecOf<Data::Float, float>(),
|
.codec = fieldCodecOf<Data::Float, float>(),
|
||||||
.flags = PROP_UNIT_FLOAT,
|
.flags = PROP_UNIT_FLOAT,
|
||||||
.category = PROP_CATEGORY_APPEARENCE,
|
.category = PROP_CATEGORY_APPEARENCE,
|
||||||
}}
|
}},
|
||||||
|
|
||||||
|
// Surfaces
|
||||||
|
|
||||||
|
{ "TopSurface", {
|
||||||
|
.backingField = &topSurface,
|
||||||
|
.type = &Data::Int::TYPE, // Replace with enum
|
||||||
|
.codec = fieldCodecOf<Data::Int, int>(),
|
||||||
|
.flags = PROP_HIDDEN,
|
||||||
|
.category = PROP_CATEGORY_SURFACE,
|
||||||
|
}}, { "BottomSurface", {
|
||||||
|
.backingField = &bottomSurface,
|
||||||
|
.type = &Data::Int::TYPE, // Replace with enum
|
||||||
|
.codec = fieldCodecOf<Data::Int, int>(),
|
||||||
|
.flags = PROP_HIDDEN,
|
||||||
|
.category = PROP_CATEGORY_SURFACE,
|
||||||
|
}}, { "FrontSurface", {
|
||||||
|
.backingField = &frontSurface,
|
||||||
|
.type = &Data::Int::TYPE, // Replace with enum
|
||||||
|
.codec = fieldCodecOf<Data::Int, int>(),
|
||||||
|
.flags = PROP_HIDDEN,
|
||||||
|
.category = PROP_CATEGORY_SURFACE,
|
||||||
|
}}, { "BackSurface", {
|
||||||
|
.backingField = &backSurface,
|
||||||
|
.type = &Data::Int::TYPE, // Replace with enum
|
||||||
|
.codec = fieldCodecOf<Data::Int, int>(),
|
||||||
|
.flags = PROP_HIDDEN,
|
||||||
|
.category = PROP_CATEGORY_SURFACE,
|
||||||
|
}}, { "RightSurface", {
|
||||||
|
.backingField = &rightSurface,
|
||||||
|
.type = &Data::Int::TYPE, // Replace with enum
|
||||||
|
.codec = fieldCodecOf<Data::Int, int>(),
|
||||||
|
.flags = PROP_HIDDEN,
|
||||||
|
.category = PROP_CATEGORY_SURFACE,
|
||||||
|
}}, { "LeftSurface", {
|
||||||
|
.backingField = &leftSurface,
|
||||||
|
.type = &Data::Int::TYPE, // Replace with enum
|
||||||
|
.codec = fieldCodecOf<Data::Int, int>(),
|
||||||
|
.flags = PROP_HIDDEN,
|
||||||
|
.category = PROP_CATEGORY_SURFACE,
|
||||||
|
}},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Part::~Part() {
|
Part::~Part() {
|
||||||
// This relies on physicsCommon still existing. Be very careful.
|
// This relies on physicsCommon still existing. Be very careful.
|
||||||
if (this->rigidBody && workspace())
|
if (this->rigidBody && workspace()) {
|
||||||
workspace().value()->DestroyRigidBody(rigidBody);
|
workspace().value()->DestroyRigidBody(rigidBody);
|
||||||
|
this->rigidBody = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace rp = reactphysics3d;
|
||||||
// For easy construction from C++. Maybe should be removed?
|
// For easy construction from C++. Maybe should be removed?
|
||||||
struct PartConstructParams {
|
struct PartConstructParams {
|
||||||
glm::vec3 position;
|
glm::vec3 position;
|
||||||
glm::quat rotation = glm::identity<glm::quat>();
|
glm::vec3 rotation;
|
||||||
glm::vec3 size;
|
glm::vec3 size;
|
||||||
Data::Color3 color;
|
Data::Color3 color;
|
||||||
|
|
||||||
|
|
45
core/src/objects/snap.cpp
Normal file
45
core/src/objects/snap.cpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#include "snap.h"
|
||||||
|
|
||||||
|
#include "datatypes/vector.h"
|
||||||
|
#include "workspace.h"
|
||||||
|
#include "part.h"
|
||||||
|
#include <reactphysics3d/constraint/FixedJoint.h>
|
||||||
|
|
||||||
|
const InstanceType Snap::TYPE = {
|
||||||
|
.super = &Instance::TYPE,
|
||||||
|
.className = "Snap",
|
||||||
|
.constructor = &Snap::Create,
|
||||||
|
};
|
||||||
|
|
||||||
|
const InstanceType* Snap::GetClass() {
|
||||||
|
return &TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Snap::Snap(): Instance(&TYPE) {
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
rp::FixedJointInfo jointInfo(part0->lock()->rigidBody, part1->lock()->rigidBody, (c0.Inverse() * c1).Position());
|
||||||
|
this->joint = dynamic_cast<rp::FixedJoint*>(workspace().value()->physicsWorld->createJoint(jointInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Snap::OnWorkspaceRemoved(std::optional<std::shared_ptr<Workspace>> oldWorkspace) {
|
||||||
|
if (!this->joint || !oldWorkspace) return;
|
||||||
|
|
||||||
|
oldWorkspace.value()->physicsWorld->destroyJoint(this->joint);
|
||||||
|
this->joint = nullptr;
|
||||||
|
}
|
28
core/src/objects/snap.h
Normal file
28
core/src/objects/snap.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "objects/base/instance.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
class Part;
|
||||||
|
|
||||||
|
class Snap : public Instance {
|
||||||
|
rp::FixedJoint* joint;
|
||||||
|
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;
|
||||||
|
public:
|
||||||
|
const static InstanceType TYPE;
|
||||||
|
|
||||||
|
std::optional<std::weak_ptr<Part>> part0;
|
||||||
|
std::optional<std::weak_ptr<Part>> part1;
|
||||||
|
Data::CFrame c0;
|
||||||
|
Data::CFrame c1;
|
||||||
|
|
||||||
|
Snap();
|
||||||
|
~Snap();
|
||||||
|
|
||||||
|
static inline std::shared_ptr<Snap> New() { return std::make_shared<Snap>(); };
|
||||||
|
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Snap>(); };
|
||||||
|
virtual const InstanceType* GetClass() override;
|
||||||
|
};
|
|
@ -15,7 +15,7 @@ const InstanceType* Workspace::GetClass() {
|
||||||
return &TYPE;
|
return &TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
rp::PhysicsCommon *physicsCommon = new rp::PhysicsCommon;
|
rp::PhysicsCommon* Workspace::physicsCommon = new rp::PhysicsCommon;
|
||||||
|
|
||||||
Workspace::Workspace(): Service(&TYPE) {
|
Workspace::Workspace(): Service(&TYPE) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,16 @@ enum FilterResult {
|
||||||
};
|
};
|
||||||
|
|
||||||
class Part;
|
class Part;
|
||||||
|
class Snap;
|
||||||
|
|
||||||
typedef std::function<FilterResult(std::shared_ptr<Part>)> RaycastFilter;
|
typedef std::function<FilterResult(std::shared_ptr<Part>)> RaycastFilter;
|
||||||
|
|
||||||
class Workspace : public Service {
|
class Workspace : public Service {
|
||||||
rp::PhysicsWorld* physicsWorld = nullptr;
|
rp::PhysicsWorld* physicsWorld = nullptr;
|
||||||
|
static rp::PhysicsCommon* physicsCommon;
|
||||||
|
|
||||||
|
friend Part;
|
||||||
|
friend Snap;
|
||||||
protected:
|
protected:
|
||||||
void InitService() override;
|
void InitService() override;
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
|
|
|
@ -260,6 +260,8 @@ void renderHandles() {
|
||||||
for (auto face : HandleFace::Faces) {
|
for (auto face : HandleFace::Faces) {
|
||||||
Data::CFrame cframe = editorToolHandles->GetCFrameOfHandle(face);
|
Data::CFrame cframe = editorToolHandles->GetCFrameOfHandle(face);
|
||||||
glm::vec4 screenPos = projection * view * glm::vec4((glm::vec3)cframe.Position(), 1.0f);
|
glm::vec4 screenPos = projection * view * glm::vec4((glm::vec3)cframe.Position(), 1.0f);
|
||||||
|
|
||||||
|
if (screenPos.z < 0) continue;
|
||||||
glm::vec3 ndcCoords = screenPos / screenPos.w;
|
glm::vec3 ndcCoords = screenPos / screenPos.w;
|
||||||
|
|
||||||
float rad = 5;
|
float rad = 5;
|
||||||
|
|
|
@ -147,6 +147,7 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline glm::vec3 vec3fy(glm::vec4 vec) {
|
inline glm::vec3 vec3fy(glm::vec4 vec) {
|
||||||
|
@ -224,6 +225,8 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
|
||||||
playSound("./assets/excluded/switch.wav");
|
playSound("./assets/excluded/switch.wav");
|
||||||
|
|
||||||
gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
|
@ -266,6 +269,7 @@ 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(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
|
||||||
|
sendPropertyUpdatedSignal(draggingObject->lock(), "Rotation", draggingObject->lock()->cframe.ToEulerAnglesXYZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
|
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "./ui_mainwindow.h"
|
#include "./ui_mainwindow.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "objects/snap.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <qclipboard.h>
|
#include <qclipboard.h>
|
||||||
#include <qmessagebox.h>
|
#include <qmessagebox.h>
|
||||||
|
@ -123,11 +124,30 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
|
|
||||||
gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({
|
gWorkspace()->AddChild(ui->mainWidget->lastPart = Part::New({
|
||||||
.position = glm::vec3(0),
|
.position = glm::vec3(0),
|
||||||
.rotation = glm::vec3(0.5, 2, 1),
|
.rotation = glm::vec3(-2.6415927, 1.1415926, 2.57075),
|
||||||
.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 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;
|
||||||
|
|
||||||
|
// auto snap = Snap::New();
|
||||||
|
// snap->part0 = part0;
|
||||||
|
// snap->part1 = part1;
|
||||||
|
// snap->c0 = part1->cframe;
|
||||||
|
// snap->c1 = part0->cframe;
|
||||||
|
|
||||||
|
gWorkspace()->AddChild(snap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::closeEvent(QCloseEvent* evt) {
|
void MainWindow::closeEvent(QCloseEvent* evt) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "panes/propertiesview.h"
|
#include "panes/propertiesview.h"
|
||||||
|
#include "common.h"
|
||||||
#include "datatypes/base.h"
|
#include "datatypes/base.h"
|
||||||
|
|
||||||
#include <QColorDialog>
|
#include <QColorDialog>
|
||||||
|
@ -6,6 +7,11 @@
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
#include <QStyledItemDelegate>
|
#include <QStyledItemDelegate>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
#include <QTime>
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <qnamespace.h>
|
||||||
|
#include <qtreewidget.h>
|
||||||
|
|
||||||
class PropertiesItemDelegate : public QStyledItemDelegate {
|
class PropertiesItemDelegate : public QStyledItemDelegate {
|
||||||
PropertiesView* view;
|
PropertiesView* view;
|
||||||
|
@ -231,6 +237,8 @@ PropertiesView::PropertiesView(QWidget* parent):
|
||||||
else if (item->parent())
|
else if (item->parent())
|
||||||
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addPropertyUpdateListener(std::bind(&PropertiesView::onPropertyUpdated, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertiesView::~PropertiesView() {
|
PropertiesView::~PropertiesView() {
|
||||||
|
@ -362,3 +370,45 @@ void PropertiesView::rebuildCompositeProperty(QTreeWidgetItem *item, const Data:
|
||||||
item->addChild(zItem);
|
item->addChild(zItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static auto lastUpdateTime = std::chrono::steady_clock::now();
|
||||||
|
void PropertiesView::onPropertyUpdated(InstanceRef inst, std::string property, Data::Variant newValue) {
|
||||||
|
// if (!currentInstance || currentInstance->expired() || instance != currentInstance->lock()) return;
|
||||||
|
// if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - lastUpdateTime).count() < 1000) return;
|
||||||
|
// lastUpdateTime = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
PropertyMeta meta = inst->GetPropertyMeta(property).expect();
|
||||||
|
Data::Variant currentValue = inst->GetPropertyValue(property).expect();
|
||||||
|
|
||||||
|
if (meta.type == &Data::CFrame::TYPE) return;
|
||||||
|
|
||||||
|
for (int categoryItemIdx = 0; categoryItemIdx < topLevelItemCount(); categoryItemIdx++) {
|
||||||
|
QTreeWidgetItem* categoryItem = topLevelItem(categoryItemIdx);
|
||||||
|
for (int itemIdx = 0; itemIdx < categoryItem->childCount(); itemIdx++) {
|
||||||
|
QTreeWidgetItem* item = categoryItem->child(itemIdx);
|
||||||
|
|
||||||
|
if (item->data(0, Qt::DisplayRole).toString().toStdString() != property) continue;
|
||||||
|
|
||||||
|
if (meta.type == &Data::Bool::TYPE) {
|
||||||
|
item->setCheckState(1, (bool)currentValue.get<Data::Bool>() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
|
||||||
|
} else if (meta.type == &Data::Color3::TYPE) {
|
||||||
|
Data::Color3 color = currentValue.get<Data::Color3>();
|
||||||
|
item->setData(1, Qt::DecorationRole, QColor::fromRgbF(color.R(), color.G(), color.B()));
|
||||||
|
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||||
|
} else if (meta.type == &Data::Vector3::TYPE) {
|
||||||
|
Data::Vector3 vector = currentValue.get<Data::Vector3>();
|
||||||
|
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||||
|
} else {
|
||||||
|
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meta.type != &Data::Color3::TYPE && (!meta.type->fromString || meta.flags & PROP_READONLY)) {
|
||||||
|
item->setDisabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
rebuildCompositeProperty(item, meta.type, currentValue);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,8 +5,8 @@
|
||||||
#include "objects/base/instance.h"
|
#include "objects/base/instance.h"
|
||||||
|
|
||||||
class Ui_MainWindow;
|
class Ui_MainWindow;
|
||||||
|
|
||||||
class PropertiesItemDelegate;
|
class PropertiesItemDelegate;
|
||||||
|
namespace Data { class Variant; };
|
||||||
|
|
||||||
class PropertiesView : public QTreeWidget {
|
class PropertiesView : public QTreeWidget {
|
||||||
Q_DECLARE_PRIVATE(QTreeView)
|
Q_DECLARE_PRIVATE(QTreeView)
|
||||||
|
@ -15,6 +15,7 @@ class PropertiesView : public QTreeWidget {
|
||||||
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);
|
||||||
|
void onPropertyUpdated(InstanceRef instance, std::string property, Data::Variant newValue);
|
||||||
|
|
||||||
friend PropertiesItemDelegate;
|
friend PropertiesItemDelegate;
|
||||||
protected:
|
protected:
|
||||||
|
|
Loading…
Add table
Reference in a new issue