diff --git a/src/OpenSimCreator/CMakeLists.txt b/src/OpenSimCreator/CMakeLists.txt index ca8d2831c..10e644e2f 100644 --- a/src/OpenSimCreator/CMakeLists.txt +++ b/src/OpenSimCreator/CMakeLists.txt @@ -57,9 +57,12 @@ add_library(OpenSimCreator STATIC ModelGraph/CrossrefDescriptor.hpp ModelGraph/CrossrefDirection.hpp + ModelGraph/ISceneElLookup.hpp ModelGraph/ModelGraphIDs.cpp ModelGraph/ModelGraphIDs.hpp ModelGraph/ModelGraphStrings.hpp + ModelGraph/SceneEl.cpp + ModelGraph/SceneEl.hpp ModelGraph/SceneElClass.hpp ModelGraph/SceneElFlags.hpp ModelGraph/SceneElVariant.hpp @@ -234,7 +237,7 @@ add_library(OpenSimCreator STATIC Utils/TPS3D.hpp Utils/UndoableModelActions.cpp Utils/UndoableModelActions.hpp - "ModelGraph/ModelGraphIDs.cpp") + "ModelGraph/ModelGraphIDs.cpp" "ModelGraph/SceneEl.cpp") # OpenSimCreatorConfig.hpp # diff --git a/src/OpenSimCreator/ModelGraph/CrossrefDescriptor.hpp b/src/OpenSimCreator/ModelGraph/CrossrefDescriptor.hpp index 9d9aa1f7a..f0bfad71d 100644 --- a/src/OpenSimCreator/ModelGraph/CrossrefDescriptor.hpp +++ b/src/OpenSimCreator/ModelGraph/CrossrefDescriptor.hpp @@ -39,4 +39,4 @@ namespace osc CStringView m_Label; CrossrefDirection m_Direction; }; -} \ No newline at end of file +} diff --git a/src/OpenSimCreator/ModelGraph/CrossrefDirection.hpp b/src/OpenSimCreator/ModelGraph/CrossrefDirection.hpp index 5c6dc8bb1..e94157cde 100644 --- a/src/OpenSimCreator/ModelGraph/CrossrefDirection.hpp +++ b/src/OpenSimCreator/ModelGraph/CrossrefDirection.hpp @@ -21,4 +21,4 @@ namespace osc { return (osc::to_underlying(lhs) & osc::to_underlying(rhs)) != 0; } -} \ No newline at end of file +} diff --git a/src/OpenSimCreator/ModelGraph/ISceneElLookup.hpp b/src/OpenSimCreator/ModelGraph/ISceneElLookup.hpp new file mode 100644 index 000000000..069d2a97f --- /dev/null +++ b/src/OpenSimCreator/ModelGraph/ISceneElLookup.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace osc +{ + // virtual interface to something that can be used to lookup scene elements in + // some larger document + class SceneEl; + class ISceneElLookup { + protected: + ISceneElLookup() = default; + ISceneElLookup(ISceneElLookup const&) = default; + ISceneElLookup(ISceneElLookup&&) noexcept = default; + ISceneElLookup& operator=(ISceneElLookup const&) = default; + ISceneElLookup& operator=(ISceneElLookup&&) noexcept = default; + public: + virtual ~ISceneElLookup() noexcept = default; + + SceneEl const* find(UID id) const + { + return implFind(id); + } + private: + virtual SceneEl const* implFind(UID) const = 0; + }; +} diff --git a/src/OpenSimCreator/ModelGraph/SceneEl.cpp b/src/OpenSimCreator/ModelGraph/SceneEl.cpp new file mode 100644 index 000000000..85da67ad7 --- /dev/null +++ b/src/OpenSimCreator/ModelGraph/SceneEl.cpp @@ -0,0 +1,30 @@ +#include "SceneEl.hpp" + +#include + +#include + +void osc::SceneEl::applyRotation( + ISceneElLookup const& lookup, + Vec3 const& eulerAngles, + Vec3 const& rotationCenter) +{ + Transform t = getXForm(lookup); + ApplyWorldspaceRotation(t, eulerAngles, rotationCenter); + setXform(lookup, t); +} + +bool osc::SceneEl::isCrossReferencing( + UID id, + CrossrefDirection direction) const +{ + auto const crossRefs = implGetCrossReferences(); + return std::any_of( + crossRefs.begin(), + crossRefs.end(), + [id, direction](CrossrefDescriptor const& desc) + { + return desc.getConnecteeID() == id && (desc.getDirection() & direction); + } + ); +} diff --git a/src/OpenSimCreator/ModelGraph/SceneEl.hpp b/src/OpenSimCreator/ModelGraph/SceneEl.hpp new file mode 100644 index 000000000..c4890b818 --- /dev/null +++ b/src/OpenSimCreator/ModelGraph/SceneEl.hpp @@ -0,0 +1,230 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace osc { class ISceneElLookup; } +namespace osc { class SceneElClass; } + +namespace osc +{ + // virtual scene element support + // + // the editor UI uses custom scene elements, rather than OpenSim types, because they have to + // support: + // + // - visitor patterns (custom UI elements tailored to each known type) + // - value semantics (undo/redo, rollbacks, etc.) + // - groundspace manipulation (3D gizmos, drag and drop) + // - easy UI integration (GLM datatypes, designed to be easy to dump into OpenGL, etc.) + class SceneEl { + protected: + SceneEl() = default; + SceneEl(SceneEl const&) = default; + SceneEl(SceneEl&&) noexcept = default; + SceneEl& operator=(SceneEl const&) = default; + SceneEl& operator=(SceneEl&&) noexcept = default; + public: + virtual ~SceneEl() noexcept = default; + + SceneElClass const& getClass() const + { + return implGetClass(); + } + + std::unique_ptr clone() const + { + return implClone(); + } + + ConstSceneElVariant toVariant() const + { + return implToVariant(); + } + + SceneElVariant toVariant() + { + return implToVariant(); + } + + int getNumCrossReferences() const + { + return static_cast(implGetCrossReferences().size()); + } + + UID getCrossReferenceConnecteeID(int i) const + { + return implGetCrossReferences().at(i).getConnecteeID(); + } + + void setCrossReferenceConnecteeID(int i, UID newID) + { + implSetCrossReferenceConnecteeID(i, newID); + } + + CStringView getCrossReferenceLabel(int i) const + { + return implGetCrossReferences().at(i).getLabel(); + } + + CrossrefDirection getCrossReferenceDirection(int i) const + { + return implGetCrossReferences().at(i).getDirection(); + } + + UID getID() const + { + return implGetID(); + } + + std::ostream& operator<<(std::ostream& o) const + { + return implWriteToStream(o); + } + + CStringView getLabel() const + { + return implGetLabel(); + } + + void setLabel(std::string_view newLabel) + { + implSetLabel(newLabel); + } + + Transform getXForm(ISceneElLookup const& lookup) const + { + return implGetXform(lookup); + } + void setXform(ISceneElLookup const& lookup, Transform const& newTransform) + { + implSetXform(lookup, newTransform); + } + + Vec3 getPos(ISceneElLookup const& lookup) const + { + return getXForm(lookup).position; + } + void setPos(ISceneElLookup const& lookup, Vec3 const& newPos) + { + setXform(lookup, getXForm(lookup).withPosition(newPos)); + } + + Vec3 getScale(ISceneElLookup const& lookup) const + { + return getXForm(lookup).scale; + } + + void setScale(ISceneElLookup const& lookup, Vec3 const& newScale) + { + setXform(lookup, getXForm(lookup).withScale(newScale)); + } + + Quat getRotation(ISceneElLookup const& lookup) const + { + return getXForm(lookup).rotation; + } + + void setRotation(ISceneElLookup const& lookup, Quat const& newRotation) + { + setXform(lookup, getXForm(lookup).withRotation(newRotation)); + } + + AABB calcBounds(ISceneElLookup const& lookup) const + { + return implCalcBounds(lookup); + } + + void applyTranslation(ISceneElLookup const& lookup, Vec3 const& translation) + { + setPos(lookup, getPos(lookup) + translation); + } + + void applyRotation( + ISceneElLookup const& lookup, + Vec3 const& eulerAngles, + Vec3 const& rotationCenter + ); + + void applyScale(ISceneElLookup const& lookup, Vec3 const& scaleFactors) + { + setScale(lookup, getScale(lookup) * scaleFactors); + } + + bool canChangeLabel() const + { + return implGetFlags() & SceneElFlags::CanChangeLabel; + } + + bool canChangePosition() const + { + return implGetFlags() & SceneElFlags::CanChangePosition; + } + + bool canChangeRotation() const + { + return implGetFlags() & SceneElFlags::CanChangeRotation; + } + + bool canChangeScale() const + { + return implGetFlags() & SceneElFlags::CanChangeScale; + } + + bool canDelete() const + { + return implGetFlags() & SceneElFlags::CanDelete; + } + + bool canSelect() const + { + return implGetFlags() & SceneElFlags::CanSelect; + } + + bool hasPhysicalSize() const + { + return implGetFlags() & SceneElFlags::HasPhysicalSize; + } + + bool isCrossReferencing( + UID id, + CrossrefDirection direction = CrossrefDirection::Both + ) const; + + private: + virtual SceneElClass const& implGetClass() const = 0; + virtual std::unique_ptr implClone() const = 0; + virtual ConstSceneElVariant implToVariant() const = 0; + virtual SceneElVariant implToVariant() = 0; + virtual SceneElFlags implGetFlags() const = 0; + + virtual std::vector implGetCrossReferences() const { return {}; } + virtual void implSetCrossReferenceConnecteeID(int, UID) {} + + virtual std::ostream& implWriteToStream(std::ostream&) const = 0; + + virtual UID implGetID() const = 0; + + virtual CStringView implGetLabel() const = 0; + virtual void implSetLabel(std::string_view) {} + + virtual Transform implGetXform(ISceneElLookup const&) const = 0; + virtual void implSetXform(ISceneElLookup const&, Transform const&) {} + + virtual AABB implCalcBounds(ISceneElLookup const&) const = 0; + }; +} diff --git a/src/OpenSimCreator/ModelGraph/SceneElFlags.hpp b/src/OpenSimCreator/ModelGraph/SceneElFlags.hpp index fd76ee6e4..71e0c442e 100644 --- a/src/OpenSimCreator/ModelGraph/SceneElFlags.hpp +++ b/src/OpenSimCreator/ModelGraph/SceneElFlags.hpp @@ -28,4 +28,4 @@ namespace osc { return static_cast(osc::to_underlying(lhs) | osc::to_underlying(rhs)); } -} \ No newline at end of file +} diff --git a/src/OpenSimCreator/UI/Tabs/MeshImporterTab.cpp b/src/OpenSimCreator/UI/Tabs/MeshImporterTab.cpp index c5e89473d..99ea316b7 100644 --- a/src/OpenSimCreator/UI/Tabs/MeshImporterTab.cpp +++ b/src/OpenSimCreator/UI/Tabs/MeshImporterTab.cpp @@ -5,8 +5,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -147,6 +149,7 @@ using osc::CStringView; using osc::DerivedFrom; using osc::Identity; using osc::Invocable; +using osc::ISceneElLookup; using osc::Line; using osc::Mat3; using osc::Mat4; @@ -189,237 +192,8 @@ using osc::UID; // - value semantics (undo/redo, rollbacks, etc.) // - groundspace manipulation (3D gizmos, drag and drop) // - easy UI integration (GLM datatypes, designed to be easy to dump into OpenGL, etc.) -namespace +namespace osc { - // virtual interface to something that can be used to lookup scene elements in - // some larger document - class SceneEl; - class ISceneElLookup { - protected: - ISceneElLookup() = default; - ISceneElLookup(ISceneElLookup const&) = default; - ISceneElLookup(ISceneElLookup&&) noexcept = default; - ISceneElLookup& operator=(ISceneElLookup const&) = default; - ISceneElLookup& operator=(ISceneElLookup&&) noexcept = default; - public: - virtual ~ISceneElLookup() noexcept = default; - - SceneEl const* find(UID id) const - { - return implFind(id); - } - private: - virtual SceneEl const* implFind(UID) const = 0; - }; - - // base class for all scene elements - class SceneEl { - protected: - SceneEl() = default; - SceneEl(SceneEl const&) = default; - SceneEl(SceneEl&&) noexcept = default; - SceneEl& operator=(SceneEl const&) = default; - SceneEl& operator=(SceneEl&&) noexcept = default; - public: - virtual ~SceneEl() noexcept = default; - - SceneElClass const& getClass() const - { - return implGetClass(); - } - - std::unique_ptr clone() const - { - return implClone(); - } - - ConstSceneElVariant toVariant() const - { - return implToVariant(); - } - - SceneElVariant toVariant() - { - return implToVariant(); - } - - int getNumCrossReferences() const - { - return static_cast(implGetCrossReferences().size()); - } - - UID getCrossReferenceConnecteeID(int i) const - { - return implGetCrossReferences().at(i).getConnecteeID(); - } - - void setCrossReferenceConnecteeID(int i, UID newID) - { - implSetCrossReferenceConnecteeID(i, newID); - } - - CStringView getCrossReferenceLabel(int i) const - { - return implGetCrossReferences().at(i).getLabel(); - } - - CrossrefDirection getCrossReferenceDirection(int i) const - { - return implGetCrossReferences().at(i).getDirection(); - } - - UID getID() const - { - return implGetID(); - } - - std::ostream& operator<<(std::ostream& o) const - { - return implWriteToStream(o); - } - - CStringView getLabel() const - { - return implGetLabel(); - } - - void setLabel(std::string_view newLabel) - { - implSetLabel(newLabel); - } - - Transform getXForm(ISceneElLookup const& lookup) const - { - return implGetXform(lookup); - } - void setXform(ISceneElLookup const& lookup, Transform const& newTransform) - { - implSetXform(lookup, newTransform); - } - - Vec3 getPos(ISceneElLookup const& lookup) const - { - return getXForm(lookup).position; - } - void setPos(ISceneElLookup const& lookup, Vec3 const& newPos) - { - setXform(lookup, getXForm(lookup).withPosition(newPos)); - } - - Vec3 getScale(ISceneElLookup const& lookup) const - { - return getXForm(lookup).scale; - } - - void setScale(ISceneElLookup const& lookup, Vec3 const& newScale) - { - setXform(lookup, getXForm(lookup).withScale(newScale)); - } - - Quat getRotation(ISceneElLookup const& lookup) const - { - return getXForm(lookup).rotation; - } - - void setRotation(ISceneElLookup const& lookup, Quat const& newRotation) - { - setXform(lookup, getXForm(lookup).withRotation(newRotation)); - } - - AABB calcBounds(ISceneElLookup const& lookup) const - { - return implCalcBounds(lookup); - } - - void applyTranslation(ISceneElLookup const& lookup, Vec3 const& translation) - { - setPos(lookup, getPos(lookup) + translation); - } - - void applyRotation( - ISceneElLookup const& lookup, - Vec3 const& eulerAngles, - Vec3 const& rotationCenter) - { - Transform t = getXForm(lookup); - ApplyWorldspaceRotation(t, eulerAngles, rotationCenter); - setXform(lookup, t); - } - - void applyScale(ISceneElLookup const& lookup, Vec3 const& scaleFactors) - { - setScale(lookup, getScale(lookup) * scaleFactors); - } - - bool canChangeLabel() const - { - return implGetFlags() & SceneElFlags::CanChangeLabel; - } - - bool canChangePosition() const - { - return implGetFlags() & SceneElFlags::CanChangePosition; - } - - bool canChangeRotation() const - { - return implGetFlags() & SceneElFlags::CanChangeRotation; - } - - bool canChangeScale() const - { - return implGetFlags() & SceneElFlags::CanChangeScale; - } - - bool canDelete() const - { - return implGetFlags() & SceneElFlags::CanDelete; - } - - bool canSelect() const - { - return implGetFlags() & SceneElFlags::CanSelect; - } - - bool hasPhysicalSize() const - { - return implGetFlags() & SceneElFlags::HasPhysicalSize; - } - - bool isCrossReferencing( - UID id, - CrossrefDirection direction = CrossrefDirection::Both) const - { - auto const crossRefs = implGetCrossReferences(); - return std::any_of(crossRefs.begin(), crossRefs.end(), [id, direction](CrossrefDescriptor const& desc) - { - return desc.getConnecteeID() == id && (desc.getDirection() & direction); - }); - } - - private: - virtual SceneElClass const& implGetClass() const = 0; - virtual std::unique_ptr implClone() const = 0; - virtual ConstSceneElVariant implToVariant() const = 0; - virtual SceneElVariant implToVariant() = 0; - virtual SceneElFlags implGetFlags() const = 0; - - virtual std::vector implGetCrossReferences() const { return {}; } - virtual void implSetCrossReferenceConnecteeID(int, UID) {} - - virtual std::ostream& implWriteToStream(std::ostream&) const = 0; - - virtual UID implGetID() const = 0; - - virtual CStringView implGetLabel() const = 0; - virtual void implSetLabel(std::string_view) {} - - virtual Transform implGetXform(ISceneElLookup const&) const = 0; - virtual void implSetXform(ISceneElLookup const&, Transform const&) {} - - virtual AABB implCalcBounds(ISceneElLookup const&) const = 0; - }; - // Curiously Recurring Template Pattern (CRTP) for SceneEl // // automatically defines parts of the SceneEl API using CRTP, so that @@ -468,6 +242,8 @@ namespace }; } +using osc::SceneEl; + // concrete scene element support // // these are concrete implementors of the virtual scene element API @@ -6870,7 +6646,7 @@ class osc::MeshImporterTab::Impl final : public LayerHost { if (ImGui::MenuItem(ICON_FA_WEIGHT " at mesh mass center")) { - Vec3 const location = MassCenter(*meshEl); + Vec3 const location = ::MassCenter(*meshEl); AddBody(m_Shared->updCommittableModelGraph(), location, meshEl->getID()); } osc::DrawTooltipIfItemHovered("Add body", ModelGraphStrings::c_BodyDescription.c_str());