From 0ac6e4e69ec8cbfa71ae8cf49fc29f3b9fac237f Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Mon, 16 Feb 2026 10:16:52 +0000 Subject: [PATCH 01/35] refactor: add Emitter/EmitterSet getters to PtclRes --- include/ptcl/ptcl.h | 11 ++++++-- src/editor/mainWindow.cpp | 46 ++++++++------------------------- src/ptcl/ptcl.cpp | 54 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 69 insertions(+), 42 deletions(-) diff --git a/include/ptcl/ptcl.h b/include/ptcl/ptcl.h index 3b1214d..cdadb44 100644 --- a/include/ptcl/ptcl.h +++ b/include/ptcl/ptcl.h @@ -129,7 +129,12 @@ class PtclRes { void setName(const QString& name); const EmitterSetList& getEmitterSets() const; - EmitterSetList& getEmitterSets(); + + EmitterSet* emitterSet(s32 index); + const EmitterSet* emitterSet(s32 index) const; + + Emitter* emitter(s32 setIndex, s32 emitterIndex); + const Emitter* emitter(s32 setIndex, s32 emitterIndex) const; const TextureList& textures() const; TextureList& textures(); @@ -138,7 +143,9 @@ class PtclRes { void removeEmitterSet(s32 setIndex); u32 emitterSetCount() const; - u32 emitterCount() const; + + u32 emitterCount(s32 setIndex) const; + u32 totalEmitterCount() const; const std::unique_ptr& appendEmitterSet(std::unique_ptr& newSet); diff --git a/src/editor/mainWindow.cpp b/src/editor/mainWindow.cpp index bcedb86..a6eedc8 100644 --- a/src/editor/mainWindow.cpp +++ b/src/editor/mainWindow.cpp @@ -419,8 +419,7 @@ void MainWindow::loadPtclRes(const QString& path) { QSignalBlocker b1(mProjNameLineEdit); mProjNameLineEdit.setText(mPtclRes->name()); - const auto& sets = mPtclRes->getEmitterSets(); - if (!sets.empty()) { + if (mPtclRes->emitterSetCount() != 0) { selectEmitter(0, 0); } @@ -437,17 +436,12 @@ void MainWindow::selectEmitterSet(s32 setIndex) { mCurEmitterSetIdx = setIndex; mCurEmitterIdx = 0; - if (!mPtclRes || mCurEmitterIdx < 0 || mCurEmitterIdx < 0) { - return; - } - - const auto& emitterSets = mPtclRes->getEmitterSets(); - if (mCurEmitterSetIdx >= emitterSets.size()) { + if (!mPtclRes) { return; } - const auto& emitterSet = emitterSets[mCurEmitterSetIdx]; - mEmitterSetWidget.setEmitterSet(emitterSet.get()); + const auto& emitterSet = mPtclRes->emitterSet(mCurEmitterSetIdx); + mEmitterSetWidget.setEmitterSet(emitterSet); if (!mEmitterSetWidget.isEnabled()) { mEmitterSetWidget.setEnabled(true); @@ -458,23 +452,12 @@ void MainWindow::selectEmitter(s32 setIndex, s32 emitterIndex) { mCurEmitterSetIdx = setIndex; mCurEmitterIdx = emitterIndex; - if (!mPtclRes || mCurEmitterIdx < 0 || mCurEmitterIdx < 0) { - return; - } - - const auto& emitterSets = mPtclRes->getEmitterSets(); - if (mCurEmitterSetIdx >= emitterSets.size()) { - return; - } - const auto& emitterSet = emitterSets[mCurEmitterSetIdx]; - - const auto& emitters = emitterSet->emitters(); - if (mCurEmitterIdx >= emitters.size()) { + if (!mPtclRes) { return; } - const auto& emitter = emitters[mCurEmitterIdx]; - mEmitterWidget.setEmitter(emitter.get()); + const auto& emitter = mPtclRes->emitter(mCurEmitterSetIdx, mCurEmitterIdx); + mEmitterWidget.setEmitter(emitter); if (!mEmitterWidget.isEnabled()) { mEmitterWidget.setEnabled(true); @@ -514,21 +497,12 @@ void MainWindow::setPropertiesView(PropertiesView view) { void MainWindow::updatePropertiesStatus() { mPropertiesGroup.setTitle({}); - if (!mPtclRes || mCurEmitterIdx < 0 || mCurEmitterIdx < 0) { - return; - } - - const auto& emitterSets = mPtclRes->getEmitterSets(); - if (mCurEmitterSetIdx >= emitterSets.size()) { + if (!mPtclRes) { return; } - const auto& emitterSet = emitterSets[mCurEmitterSetIdx]; - const auto& emitters = emitterSet->emitters(); - if (mCurEmitterIdx >= emitters.size()) { - return; - } - const auto& emitter = emitters[mCurEmitterIdx]; + const auto& emitterSet = mPtclRes->emitterSet(mCurEmitterSetIdx); + const auto& emitter = mPtclRes->emitter(mCurEmitterSetIdx, mCurEmitterIdx); QString title = emitterSet->name(); diff --git a/src/ptcl/ptcl.cpp b/src/ptcl/ptcl.cpp index 8c3af77..e3efc3e 100644 --- a/src/ptcl/ptcl.cpp +++ b/src/ptcl/ptcl.cpp @@ -265,7 +265,7 @@ void PtclBinaryWriter::buildHeader(const PtclRes& res) { constexpr u32 headerBasePos = 0; constexpr u32 emitterSetsBasePos = headerBasePos + sizeof(BinHeaderData); const u32 emitterTblDataBasePos = emitterSetsBasePos + (res.emitterSetCount() * sizeof(BinEmitterSetData)); - const u32 emitterDataBasePos = emitterTblDataBasePos + (res.emitterCount() * sizeof(BinEmitterTblData)); + const u32 emitterDataBasePos = emitterTblDataBasePos + (res.totalEmitterCount() * sizeof(BinEmitterTblData)); mEmitterSetsCurOffset = emitterSetsBasePos; mEmitterTblCurOffset = emitterTblDataBasePos; @@ -449,7 +449,15 @@ u32 PtclRes::emitterSetCount() const { return mEmitterSets.size(); } -u32 PtclRes::emitterCount() const { +u32 PtclRes::emitterCount(s32 setIndex) const { + if (setIndex < 0 || setIndex >= mEmitterSets.size()) { + return 0; + } + + return mEmitterSets[setIndex]->emitterCount(); +} + +u32 PtclRes::totalEmitterCount() const { u32 count = 0; for (auto& emitterSet : mEmitterSets) { @@ -478,8 +486,46 @@ const EmitterSetList& PtclRes::getEmitterSets() const { return mEmitterSets; } -EmitterSetList& PtclRes::getEmitterSets() { - return mEmitterSets; +EmitterSet* PtclRes::emitterSet(s32 index) { + if (index < 0 || index >= mEmitterSets.size()) { + return nullptr; + } + + return mEmitterSets[index].get(); +} + +const EmitterSet* PtclRes::emitterSet(s32 index) const { + if (index < 0 || index >= mEmitterSets.size()) { + return nullptr; + } + + return mEmitterSets[index].get(); +} + +Emitter* PtclRes::emitter(s32 setIndex, s32 emitterIndex) { + if (setIndex < 0 || setIndex >= mEmitterSets.size()) { + return nullptr; + } + + auto& set = mEmitterSets[setIndex]; + if (emitterIndex < 0 || emitterIndex >= set->emitters().size()) { + return nullptr; + } + + return set->emitters()[emitterIndex].get(); +} + +const Emitter* PtclRes::emitter(s32 setIndex, s32 emitterIndex) const { + if (setIndex < 0 || setIndex >= mEmitterSets.size()) { + return nullptr; + } + + auto& set = mEmitterSets[setIndex]; + if (emitterIndex < 0 || emitterIndex >= set->emitters().size()) { + return nullptr; + } + + return set->emitters()[emitterIndex].get(); } const TextureList& PtclRes::textures() const { From f15267c073601d338a4672f6f821281b02b58c64 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Thu, 19 Feb 2026 12:59:39 +0000 Subject: [PATCH 02/35] refactor: Begin migrating widgets to use shared document & selection models --- CMakeLists.txt | 2 + include/editor/emitterSetWidget.h | 10 +- include/editor/emitterWidget/emitterWidget.h | 17 +- include/editor/mainWindow.h | 10 +- include/editor/ptclListWidget.h | 18 +- include/editor/textureListWidget.h | 5 +- include/ptcl/ptcl.h | 5 +- include/ptcl/ptclDocument.h | 81 +++++++ src/editor/emitterSetWidget.cpp | 39 ++-- src/editor/emitterWidget/emitterWidget.cpp | 222 +++++++++--------- src/editor/mainWindow.cpp | 171 ++++++-------- src/editor/ptclListWidget.cpp | 228 ++++++++++--------- src/editor/textureListWidget.cpp | 27 ++- src/ptcl/ptcl.cpp | 4 +- src/ptcl/ptclDocument.cpp | 43 ++++ 15 files changed, 500 insertions(+), 382 deletions(-) create mode 100644 include/ptcl/ptclDocument.h create mode 100644 src/ptcl/ptclDocument.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c9aa5e4..0cdb3fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,7 @@ set(PROJECT_SOURCES src/ptcl/ptcl.cpp src/ptcl/ptclBinary.cpp src/ptcl/ptclChildData.cpp + src/ptcl/ptclDocument.cpp src/ptcl/ptclEmitter.cpp src/ptcl/ptclEmitterSet.cpp src/ptcl/ptclFieldData.cpp @@ -149,6 +150,7 @@ set(PROJECT_HEADERS include/ptcl/ptcl.h include/ptcl/ptclBinary.h include/ptcl/ptclChildData.h + include/ptcl/ptclDocument.h include/ptcl/ptclEmitter.h include/ptcl/ptclEmitterSet.h include/ptcl/ptclEnum.h diff --git a/include/editor/emitterSetWidget.h b/include/editor/emitterSetWidget.h index a2dfeb0..8f4f6a6 100644 --- a/include/editor/emitterSetWidget.h +++ b/include/editor/emitterSetWidget.h @@ -1,6 +1,7 @@ #pragma once #include "components/sizedSpinBox.h" +#include "ptcl/ptclDocument.h" #include "ptcl/ptclEmitterSet.h" #include @@ -19,8 +20,8 @@ class EmitterSetWidget final : public QWidget { public: explicit EmitterSetWidget(QWidget* parent = nullptr); - void setEmitterSet(Ptcl::EmitterSet* emitterSet); - void clear(); + void setDocument(Ptcl::Document* document); + void setSelection(Ptcl::Selection* selection); signals: void emitterSetNamedChanged(); @@ -31,8 +32,9 @@ class EmitterSetWidget final : public QWidget { void setupConnections(); private: - Ptcl::EmitterSet* mEmitterSetPtr{nullptr}; - s32 mCurEmitterIdx{}; + Ptcl::Document* mDocument{nullptr}; + const Ptcl::Selection* mSelection{nullptr}; + Ptcl::EmitterSet* mEmitterSet{nullptr}; QLineEdit mNameLineEdit{}; SizedSpinBox mUserDataSpinBox{}; diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index 28b4c0c..999d8b4 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -6,6 +6,7 @@ #include "editor/fieldEditor/fieldEditorWidget.h" #include "editor/stripeEditorWidget.h" +#include "ptcl/ptclDocument.h" #include "ptcl/ptclEmitter.h" #include "ptcl/ptcl.h" @@ -30,14 +31,8 @@ class EmitterWidget final : public QWidget { public: explicit EmitterWidget(QWidget* parent = nullptr); - void setEmitter(Ptcl::Emitter* emitter); - void setTextureList(const Ptcl::TextureList* textureList); - void showStandardEditor(); - void showChildEditor(); - void showFluctuationEditor(); - void showFieldEditor(); - - void clear(); + void setDocument(Ptcl::Document* document); + void setSelection(Ptcl::Selection* selection); signals: void nameUpdated(const QString& name); @@ -50,6 +45,7 @@ class EmitterWidget final : public QWidget { void setupStandardLayout(QVBoxLayout* mainLayout); void setupConnections(); void updateStripeVisibility(); + void populateProperties(); private: class BasicPropertiesWidget; @@ -68,7 +64,10 @@ class EmitterWidget final : public QWidget { class CombinerPropertiesWidget; private: - Ptcl::Emitter* mEmitterPtr{nullptr}; + Ptcl::Document* mDocument{nullptr}; + const Ptcl::Selection* mSelection{nullptr}; + Ptcl::Emitter* mEmitter{nullptr}; + const Ptcl::TextureList* mTextureList{nullptr}; BasicPropertiesWidget* mBasicProperties{nullptr}; diff --git a/include/editor/mainWindow.h b/include/editor/mainWindow.h index c707b84..443991c 100644 --- a/include/editor/mainWindow.h +++ b/include/editor/mainWindow.h @@ -2,6 +2,7 @@ #include "typedefs.h" #include "ptcl/ptcl.h" +#include "ptcl/ptclDocument.h" #include "editor/emitterSetWidget.h" #include "editor/emitterWidget/emitterWidget.h" #include "editor/ptclListWidget.h" @@ -54,8 +55,6 @@ private slots: private: void updateRecentFileList(); void loadPtclRes(const QString& path); - void selectEmitterSet(s32 setIndex); - void selectEmitter(s32 setIndex, s32 emitterIndex); void setupUi(); void setupConnections(); @@ -69,11 +68,8 @@ private slots: void setDirty(bool dirty); private: - std::unique_ptr mPtclRes{}; - QString mCurrentFilePath{}; - - s32 mCurEmitterSetIdx{}; - s32 mCurEmitterIdx{}; + std::unique_ptr mDocument{}; + Ptcl::Selection mSelection{}; QAction mOpenAction{}; QAction mSaveAction{}; diff --git a/include/editor/ptclListWidget.h b/include/editor/ptclListWidget.h index 45e2a5f..89c4a55 100644 --- a/include/editor/ptclListWidget.h +++ b/include/editor/ptclListWidget.h @@ -1,7 +1,7 @@ #pragma once #include "util/bitflagUtil.h" -#include "ptcl/ptcl.h" +#include "ptcl/ptclDocument.h" #include #include @@ -70,10 +70,8 @@ class PtclList : public QWidget { public: explicit PtclList(QWidget* parent = nullptr); - void setPtclRes(Ptcl::PtclRes* ptclRes); - - void selectEmitter(s32 setIndex, s32 emitterIndex); - void selectEmitterSet(s32 setIndex); + void setDocument(Ptcl::Document* document); + void setSelection(Ptcl::Selection* selection); void updateEmitter(s32 setIndex, s32 emitterIndex); void updateEmitterName(s32 setIndex, s32 emitterIndex); @@ -81,12 +79,6 @@ class PtclList : public QWidget { void refresh(); signals: - void selectedEmitterSetChanged(u32 index); - void selectedEmitterChanged(u32 setIndex, u32 emitterIndex); - void selectedChildData(u32 setIndex, u32 emitterIndex); - void selectedFluctuation(u32 setIndex, u32 emitterIndex); - void selectedField(u32 setIndex, u32 emitterIndex); - void itemAdded(); void itemRemoved(); @@ -103,6 +95,7 @@ private slots: void updateToolbarForSelection(const QStandardItem* item); + QStandardItem* findItem(s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) const; static QStandardItem* findChildByType(QStandardItem* parent, NodeType type); void insertEmitterNode(QStandardItem* setItem, s32 setIndex, s32 emitterIndex); @@ -124,7 +117,8 @@ private slots: void pasteItem(); private: - Ptcl::PtclRes* mResPtr{nullptr}; + Ptcl::Document* mDocument{nullptr}; + Ptcl::Selection* mSelection{nullptr}; QStandardItemModel mListModel{}; QTreeView mTreeView{}; diff --git a/include/editor/textureListWidget.h b/include/editor/textureListWidget.h index 02e6fdb..1eb5659 100644 --- a/include/editor/textureListWidget.h +++ b/include/editor/textureListWidget.h @@ -2,6 +2,7 @@ #include "components/thumbnailWidget.h" #include "ptcl/ptcl.h" +#include "ptcl/ptclDocument.h" #include #include @@ -93,8 +94,7 @@ class TextureListWidget final : public QWidget { public: explicit TextureListWidget(QWidget* parent = nullptr); - void setTextures(Ptcl::TextureList* textures); - void clear(); + void setDocument(Ptcl::Document* document); private slots: void exportAll(); @@ -110,6 +110,7 @@ private slots: void setupSelectionHandling(); private: + Ptcl::Document* mDocument{nullptr}; Ptcl::TextureList* mTexturesPtr{nullptr}; QToolBar mToolbar{}; diff --git a/include/ptcl/ptcl.h b/include/ptcl/ptcl.h index cdadb44..1ea0f81 100644 --- a/include/ptcl/ptcl.h +++ b/include/ptcl/ptcl.h @@ -142,9 +142,8 @@ class PtclRes { void addNewEmitterSet(); void removeEmitterSet(s32 setIndex); - u32 emitterSetCount() const; - - u32 emitterCount(s32 setIndex) const; + s32 emitterSetCount() const; + s32 emitterCount(s32 setIndex) const; u32 totalEmitterCount() const; const std::unique_ptr& appendEmitterSet(std::unique_ptr& newSet); diff --git a/include/ptcl/ptclDocument.h b/include/ptcl/ptclDocument.h new file mode 100644 index 0000000..fb8c489 --- /dev/null +++ b/include/ptcl/ptclDocument.h @@ -0,0 +1,81 @@ +#pragma once + +#include "ptcl/ptcl.h" + +#include + + +namespace Ptcl { + + +// ========================================================================== // + + +class Selection final : public QObject { + Q_OBJECT +public: + enum class Type { + None, + EmitterSet, + Emitter, + EmitterChild, + EmitterFlux, + EmitterField + }; + +public: + explicit Selection(QObject* parent = nullptr); + + void set(s32 setIndex, s32 emitterIndex, Type type); + + s32 emitterSetIndex() const { return mSetIndex; } + s32 emitterIndex() const { return mEmitterIndex; } + Type type() const { return mType; } + +signals: + void selectionChanged(s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type); + +private: + s32 mSetIndex{-1}; + s32 mEmitterIndex{-1}; + Type mType{Selection::Type::None}; +}; + + +// ========================================================================== // + + +class Document final : public QObject { + Q_OBJECT +public: + explicit Document(QObject* parent = nullptr); + + bool load(const QString& filePath); + bool save(const QString& filePath); + + PtclRes& data() { return mData; } + const PtclRes& data() const { return mData; } + + EmitterSet* emitterSet(s32 index) { return mData.emitterSet(index); } + const EmitterSet* emitterSet(s32 index) const { return mData.emitterSet(index); } + + Emitter* emitter(s32 setIndex, s32 emitterIndex) { return mData.emitter(setIndex, emitterIndex); } + const Emitter* emitter(s32 setIndex, s32 emitterIndex) const { return mData.emitter(setIndex, emitterIndex); } + + TextureList& textures() { return mData.textures(); } + const TextureList& textures() const { return mData.textures(); } + + bool isModified() const { return mModified; } + QString filePath() const { return mFilePath; } + +private: + PtclRes mData{}; + QString mFilePath{}; + bool mModified{false}; +}; + + +// ========================================================================== // + + +} // namespace Ptcl diff --git a/src/editor/emitterSetWidget.cpp b/src/editor/emitterSetWidget.cpp index 1a03b9a..32f8568 100644 --- a/src/editor/emitterSetWidget.cpp +++ b/src/editor/emitterSetWidget.cpp @@ -29,44 +29,55 @@ EmitterSetWidget::EmitterSetWidget(QWidget* parent) : void EmitterSetWidget::setupConnections() { // Name Edit connect(&mNameLineEdit, &QLineEdit::textEdited, this, [this](const QString& text) { - if (!mEmitterSetPtr) { + if (!mEmitterSet) { return; } - mEmitterSetPtr->setName(text); + mEmitterSet->setName(text); emit emitterSetNamedChanged(); emit propertiesChanged(); }); // User Data connect(&mUserDataSpinBox, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { - mEmitterSetPtr->setUserData(static_cast(value)); + mEmitterSet->setUserData(static_cast(value)); emit propertiesChanged(); }); // Last Update connect(&mLastUpdateSpinBox, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { - mEmitterSetPtr->setLastUpdateDate(static_cast(value)); + mEmitterSet->setLastUpdateDate(static_cast(value)); emit propertiesChanged(); }); } -void EmitterSetWidget::setEmitterSet(Ptcl::EmitterSet* emitterSet) { - setEnabled(true); - mEmitterSetPtr = emitterSet; - populateProperties(); +void EmitterSetWidget::setDocument(Ptcl::Document* document) { + mDocument = document; } -void EmitterSetWidget::clear() { - setEnabled(false); - mEmitterSetPtr = nullptr; +void EmitterSetWidget::setSelection(Ptcl::Selection* selection) { + mSelection = selection; + + connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex) { + Q_UNUSED(emitterIndex); + + if (!mDocument) { + mEmitterSet = nullptr; + setEnabled(false); + return; + } + + mEmitterSet = mDocument->emitterSet(setIndex); + setEnabled(true); + populateProperties(); + }); } void EmitterSetWidget::populateProperties() { blockSignals(true); - mNameLineEdit.setText(mEmitterSetPtr->name()); - mUserDataSpinBox.setValue(mEmitterSetPtr->userData()); - mLastUpdateSpinBox.setValue(mEmitterSetPtr->lastUpdateDate()); + mNameLineEdit.setText(mEmitterSet->name()); + mUserDataSpinBox.setValue(mEmitterSet->userData()); + mLastUpdateSpinBox.setValue(mEmitterSet->lastUpdateDate()); blockSignals(false); } diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index 968795f..f3e9f99 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -115,172 +115,215 @@ void EmitterWidget::setupStandardLayout(QVBoxLayout* mainLayout) { void EmitterWidget::setupConnections() { // Basic Properties connect(mBasicProperties, &BasicPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::BasicProperties& properties) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setBasicProperties(properties); + if (!mEmitter) { return; } + mEmitter->setBasicProperties(properties); updateStripeVisibility(); emit propertiesChanged(); }); connect(mBasicProperties, &BasicPropertiesWidget::emitterTypeChanged, this, [this]() { - if (!mEmitterPtr) { return; } + if (!mEmitter) { return; } updateStripeVisibility(); emit emitterTypeChanged(); emit propertiesChanged(); }); connect(mBasicProperties, &BasicPropertiesWidget::emitterNameChanged, this, [this]() { - if (!mEmitterPtr) { return; } + if (!mEmitter) { return; } emit emitterNameChanged(); emit propertiesChanged(); }); // Texture Properties connect(mTextureProperties, &TexturePropertiesWidget::textureUpdated, this, [this](const std::shared_ptr& oldTexture, const std::shared_ptr& newTexture) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setTexture(newTexture); + if (!mEmitter) { return; } + mEmitter->setTexture(newTexture); mCombinerProperties->updateCombinerPreview(); emit propertiesChanged(); }); connect(mTextureProperties, &TexturePropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::TextureProperties& properties) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setTextureProperties(properties); + if (!mEmitter) { return; } + mEmitter->setTextureProperties(properties); emit propertiesChanged(); }); // Gravity Properties connect(mGravityProperties, &GravityPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::GravityProperties& properties) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setGravityProperties(properties); + if (!mEmitter) { return; } + mEmitter->setGravityProperties(properties); emit propertiesChanged(); }); // Lifespan Properties connect(mLifespanProperties, &LifespanPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::LifespanProperties& properties) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setLifespanProperties(properties); + if (!mEmitter) { return; } + mEmitter->setLifespanProperties(properties); emit propertiesChanged(); }); // Termination Properties connect(mTerminationProperties, &TerminationPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::TerminationProperties& properties) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setTerminationProperties(properties); + if (!mEmitter) { return; } + mEmitter->setTerminationProperties(properties); emit propertiesChanged(); }); // Emission Properties connect(mEmissionProperties, &EmissionPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::EmissionProperties& properties) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setEmissionProperties(properties); + if (!mEmitter) { return; } + mEmitter->setEmissionProperties(properties); emit propertiesChanged(); }); // Volume Properties connect(mVolumeProperties, &VolumePropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::VolumeProperties& properties) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setVolumeProperties(properties); + if (!mEmitter) { return; } + mEmitter->setVolumeProperties(properties); emit propertiesChanged(); }); // Velocity Properties connect(mVelocityProperties, &VelocityPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::VelocityProperties& properties) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setVelocityProperties(properties); + if (!mEmitter) { return; } + mEmitter->setVelocityProperties(properties); emit propertiesChanged(); }); // Transform Properties connect(mTransformProperties, &TransformPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::TransformProperties& properties) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setTransformProperties(properties); + if (!mEmitter) { return; } + mEmitter->setTransformProperties(properties); emit propertiesChanged(); }); // Color Properties connect(mColorProperties, &ColorPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::ColorProperties& properties) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setColorProperties(properties); + if (!mEmitter) { return; } + mEmitter->setColorProperties(properties); mCombinerProperties->updateCombinerPreview(); - mChildEditorWidget->setParentColor0(mEmitterPtr->colorProperties().color0[0]); + mChildEditorWidget->setParentColor0(mEmitter->colorProperties().color0[0]); emit propertiesChanged(); }); // Combiner Properties connect(mCombinerProperties, &CombinerPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::CombinerProperties& properties) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setCombinerProperties(properties); + if (!mEmitter) { return; } + mEmitter->setCombinerProperties(properties); emit propertiesChanged(); }); // Alpha Properties connect(mAlphaProperties, &AlphaPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::AlphaProperties& properties) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setAlphaProperties(properties); + if (!mEmitter) { return; } + mEmitter->setAlphaProperties(properties); emit propertiesChanged(); }); // Rotation Properties connect(mRotationProperties, &RotationPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::RotationProperties& properties) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setRotationProperties(properties); + if (!mEmitter) { return; } + mEmitter->setRotationProperties(properties); emit propertiesChanged(); }); // Scale Properties connect(mScaleProperties, &ScalePropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::ScaleProperties& properties) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setScaleProperties(properties); + if (!mEmitter) { return; } + mEmitter->setScaleProperties(properties); emit propertiesChanged(); }); // Child Editor Widget connect(mChildEditorWidget, &ChildEditorWidget::flagsUpdated, this, [this](const BitFlag& childFlags) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setChildFlags(childFlags); + if (!mEmitter) { return; } + mEmitter->setChildFlags(childFlags); emit complexFlagsChanged(); emit propertiesChanged(); }); // Fluctuation Editor Widget connect(mFluctuationEditorWidget, &FluctuationEditorWidget::dataUpdated, this, [this](const Ptcl::FluctuationData& data) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setFluctuationData(data); + if (!mEmitter) { return; } + mEmitter->setFluctuationData(data); emit propertiesChanged(); }); connect(mFluctuationEditorWidget, &FluctuationEditorWidget::flagsUpdated, this, [this](const BitFlag& fluxFlags) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setFluctuationFlags(fluxFlags); + if (!mEmitter) { return; } + mEmitter->setFluctuationFlags(fluxFlags); complexFlagsChanged(); emit propertiesChanged(); }); // Field Editor Widget connect(mFieldEditorWidget, &FieldEditorWidget::flagsUpdated, this, [this](const BitFlag& fieldFlags) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setFieldFlags(fieldFlags); + if (!mEmitter) { return; } + mEmitter->setFieldFlags(fieldFlags); complexFlagsChanged(); emit propertiesChanged(); }); // Stripe Editor Widget connect(mStripeEditorWidget, &StripeEditorWidget::dataUpdated, this, [this](const Ptcl::StripeData& data) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setStripeData(data); + if (!mEmitter) { return; } + mEmitter->setStripeData(data); emit propertiesChanged(); }); connect(mStripeEditorWidget, &StripeEditorWidget::flagsUpdated, this, [this](const BitFlag& stripeFlags) { - if (!mEmitterPtr) { return; } - mEmitterPtr->setStripeFlags(stripeFlags); + if (!mEmitter) { return; } + mEmitter->setStripeFlags(stripeFlags); complexFlagsChanged(); emit propertiesChanged(); }); } -void EmitterWidget::setEmitter(Ptcl::Emitter* emitter) { +void EmitterWidget::setDocument(Ptcl::Document* document) { + mDocument = document; +} + +void EmitterWidget::setSelection(Ptcl::Selection* selection) { + mSelection = selection; + + connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { + if (!mDocument) { + mEmitter = nullptr; + setEnabled(false); + return; + } + + mEmitter = mDocument->emitter(setIndex, emitterIndex); + + switch (type) { + case Ptcl::Selection::Type::Emitter: + mStackedWidget->setCurrentIndex(0); + break; + case Ptcl::Selection::Type::EmitterChild: + mStackedWidget->setCurrentWidget(mChildEditorWidget); + break; + case Ptcl::Selection::Type::EmitterFlux: + mStackedWidget->setCurrentWidget(mFluctuationEditorWidget); + break; + case Ptcl::Selection::Type::EmitterField: + mStackedWidget->setCurrentWidget(mFieldEditorWidget); + break; + default: + break; + } + + // TODO: Have child widgets handle this themselves + mTextureList = &mDocument->textures(); + mTextureProperties->setTextureList(mTextureList); + mChildEditorWidget->setTextureList(mTextureList); + + setEnabled(true); + populateProperties(); + }); +} + +void EmitterWidget::populateProperties() { QSignalBlocker b1(mBasicProperties); QSignalBlocker b2(mGravityProperties); QSignalBlocker b3(mTransformProperties); @@ -300,76 +343,39 @@ void EmitterWidget::setEmitter(Ptcl::Emitter* emitter) { QSignalBlocker b17(mFieldEditorWidget); QSignalBlocker b18(mStripeEditorWidget); - mEmitterPtr = emitter; - - mBasicProperties->setProperties(mEmitterPtr->basicProperties()); - mGravityProperties->setProperties(mEmitterPtr->gravityProperties()); - mTransformProperties->setProperties(mEmitterPtr->transformProperties()); - mLifespanProperties->setProperties(mEmitterPtr->lifespanProperties()); - mTerminationProperties->setProperties(mEmitterPtr->terminationProperties()); - mEmissionProperties->setProperties(mEmitterPtr->emissionProperties()); - mVelocityProperties->setProperties(mEmitterPtr->velocityProperties()); - mVolumeProperties->setProperties(mEmitterPtr->volumeProperties()); - mColorProperties->setProperties(mEmitterPtr->colorProperties()); - mAlphaProperties->setProperties(mEmitterPtr->alphaProperties()); - mScaleProperties->setProperties(mEmitterPtr->scaleProperties()); - mRotationProperties->setProperties(mEmitterPtr->rotationProperties()); - mTextureProperties->setProperties(mEmitterPtr->textureProperties(), mEmitterPtr->textureHandle().get()); - mCombinerProperties->setProperties(mEmitterPtr->combinerProperties()); - mCombinerProperties->setCombinerSrc(&mEmitterPtr->textureHandle(), &mEmitterPtr->colorProperties().color1, &mEmitterPtr->colorProperties().color0[0]); - - mChildEditorWidget->setChildData(&mEmitterPtr->childData(), mEmitterPtr->complexProperties().childFlags); - mChildEditorWidget->setParentColor0(mEmitterPtr->colorProperties().color0[0]); - - mFluctuationEditorWidget->setData(mEmitterPtr->fluctuationData(), mEmitterPtr->complexProperties().fluctuationFlags); - mFieldEditorWidget->setData(&mEmitterPtr->fieldData(), mEmitterPtr->complexProperties().fieldFlags); - mStripeEditorWidget->setData(mEmitterPtr->stripeData(), mEmitterPtr->complexProperties().stripeFlags); + mBasicProperties->setProperties(mEmitter->basicProperties()); + mGravityProperties->setProperties(mEmitter->gravityProperties()); + mTransformProperties->setProperties(mEmitter->transformProperties()); + mLifespanProperties->setProperties(mEmitter->lifespanProperties()); + mTerminationProperties->setProperties(mEmitter->terminationProperties()); + mEmissionProperties->setProperties(mEmitter->emissionProperties()); + mVelocityProperties->setProperties(mEmitter->velocityProperties()); + mVolumeProperties->setProperties(mEmitter->volumeProperties()); + mColorProperties->setProperties(mEmitter->colorProperties()); + mAlphaProperties->setProperties(mEmitter->alphaProperties()); + mScaleProperties->setProperties(mEmitter->scaleProperties()); + mRotationProperties->setProperties(mEmitter->rotationProperties()); + mTextureProperties->setProperties(mEmitter->textureProperties(), mEmitter->textureHandle().get()); + mCombinerProperties->setProperties(mEmitter->combinerProperties()); + mCombinerProperties->setCombinerSrc(&mEmitter->textureHandle(), &mEmitter->colorProperties().color1, &mEmitter->colorProperties().color0[0]); + + mChildEditorWidget->setChildData(&mEmitter->childData(), mEmitter->complexProperties().childFlags); + mChildEditorWidget->setParentColor0(mEmitter->colorProperties().color0[0]); + + mFluctuationEditorWidget->setData(mEmitter->fluctuationData(), mEmitter->complexProperties().fluctuationFlags); + mFieldEditorWidget->setData(&mEmitter->fieldData(), mEmitter->complexProperties().fieldFlags); + mStripeEditorWidget->setData(mEmitter->stripeData(), mEmitter->complexProperties().stripeFlags); updateStripeVisibility(); - - setEnabled(true); -} - -void EmitterWidget::setTextureList(const Ptcl::TextureList* textureList) { - mTextureList = textureList; - mTextureProperties->setTextureList(textureList); - mChildEditorWidget->setTextureList(textureList); -} - -void EmitterWidget::showStandardEditor() { - mStackedWidget->setCurrentIndex(0); -} - -void EmitterWidget::showChildEditor() { - if (mStackedWidget) { - mStackedWidget->setCurrentWidget(mChildEditorWidget); - } -} - -void EmitterWidget::showFluctuationEditor() { - if (mStackedWidget) { - mStackedWidget->setCurrentWidget(mFluctuationEditorWidget); - } -} - -void EmitterWidget::showFieldEditor() { - if (mStackedWidget) { - mStackedWidget->setCurrentWidget(mFieldEditorWidget); - } -} - -void EmitterWidget::clear() { - setEnabled(false); - mEmitterPtr = nullptr; } void EmitterWidget::updateStripeVisibility() { - if (!mEmitterPtr || !mStripeSection) { + if (!mEmitter || !mStripeSection) { return; } - const auto eType = mEmitterPtr->type(); - const auto bbType = mEmitterPtr->basicProperties().billboardType; + const auto eType = mEmitter->type(); + const auto bbType = mEmitter->basicProperties().billboardType; const bool show = (eType == Ptcl::EmitterType::Complex || eType == Ptcl::EmitterType::Compact) && (bbType == Ptcl::BillboardType::Stripe || bbType == Ptcl::BillboardType::ComplexStripe); diff --git a/src/editor/mainWindow.cpp b/src/editor/mainWindow.cpp index a6eedc8..28fed22 100644 --- a/src/editor/mainWindow.cpp +++ b/src/editor/mainWindow.cpp @@ -78,12 +78,15 @@ void MainWindow::setupUi() { // Ptcl List mPtclList.setEnabled(false); mPtclList.setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + mPtclList.setSelection(&mSelection); // Emitter Widget mEmitterWidget.setEnabled(false); + mEmitterWidget.setSelection(&mSelection); // EmitterSet Widget mEmitterSetWidget.setEnabled(false); + mEmitterSetWidget.setSelection(&mSelection); // Texture Widget mTextureWidget.setEnabled(false); @@ -106,38 +109,36 @@ void MainWindow::setupUi() { void MainWindow::setupConnections() { // Proj Name connect(&mProjNameLineEdit, &QLineEdit::textChanged, this, [this](const QString& text) { - mPtclRes->setName(text); + mDocument->data().setName(text); setDirty(true); }); - // Ptcl List - connect(&mPtclList, &PtclList::selectedEmitterSetChanged, this, [this](s32 index) { - selectEmitterSet(index); - setPropertiesView(PropertiesView::EmitterSet); - updatePropertiesStatus(); - }); - - connect(&mPtclList, &PtclList::selectedEmitterChanged, this, [this](s32 setIndex, s32 emitterIndex) { - selectEmitter(setIndex, emitterIndex); - setPropertiesView(PropertiesView::Emitter); - updatePropertiesStatus(); - }); - - connect(&mPtclList, &PtclList::selectedChildData, this, [this](s32 setIndex, s32 emitterIndex) { - selectEmitter(setIndex, emitterIndex); - setPropertiesView(PropertiesView::EmitterChild); - updatePropertiesStatus(); - }); + connect(&mSelection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { + if (!mDocument) { + return; + } - connect(&mPtclList, &PtclList::selectedFluctuation, this, [this](s32 setIndex, s32 emitterIndex) { - selectEmitter(setIndex, emitterIndex); - setPropertiesView(PropertiesView::EmitterFlux); - updatePropertiesStatus(); - }); + switch (type) { + case Ptcl::Selection::Type::EmitterSet: + setPropertiesView(PropertiesView::EmitterSet); + break; + case Ptcl::Selection::Type::Emitter: + setPropertiesView(PropertiesView::Emitter); + break; + case Ptcl::Selection::Type::EmitterChild: + setPropertiesView(PropertiesView::EmitterChild); + break; + case Ptcl::Selection::Type::EmitterFlux: + setPropertiesView(PropertiesView::EmitterFlux); + break; + case Ptcl::Selection::Type::EmitterField: + setPropertiesView(PropertiesView::EmitterField); + break; + case Ptcl::Selection::Type::None: + // TODO + break; + } - connect(&mPtclList, &PtclList::selectedField, this, [this](s32 setIndex, s32 emitterIndex) { - selectEmitter(setIndex, emitterIndex); - setPropertiesView(PropertiesView::EmitterField); updatePropertiesStatus(); }); @@ -150,16 +151,16 @@ void MainWindow::setupConnections() { }); connect(&mEmitterWidget, &EmitterWidget::emitterNameChanged, this, [this]() { - mPtclList.updateEmitterName(mCurEmitterSetIdx, mCurEmitterIdx); + mPtclList.updateEmitterName(mSelection.emitterSetIndex(), mSelection.emitterIndex()); updatePropertiesStatus(); }); connect(&mEmitterWidget, &EmitterWidget::emitterTypeChanged, this, [this]() { - mPtclList.updateEmitter(mCurEmitterSetIdx, mCurEmitterIdx); + mPtclList.updateEmitter(mSelection.emitterSetIndex(), mSelection.emitterIndex()); }); connect(&mEmitterWidget, &EmitterWidget::complexFlagsChanged, this, [this]() { - mPtclList.updateEmitter(mCurEmitterSetIdx, mCurEmitterIdx); + mPtclList.updateEmitter(mSelection.emitterSetIndex(), mSelection.emitterIndex()); }); connect(&mEmitterWidget, &EmitterWidget::propertiesChanged, this, [this]() { @@ -168,7 +169,7 @@ void MainWindow::setupConnections() { // EmitterSet Widget connect(&mEmitterSetWidget, &EmitterSetWidget::emitterSetNamedChanged, this, [this]() { - mPtclList.updateEmitterSetName(mCurEmitterSetIdx); + mPtclList.updateEmitterSetName(mSelection.emitterSetIndex()); updatePropertiesStatus(); }); @@ -203,8 +204,8 @@ void MainWindow::setupMenus() { mRecentFilesMenu.setIcon(QIcon(":/res/icons/recent.png")); // Recent Files Actions - int maxRecentFiles = SettingsUtil::SettingsMgr::instance().maxRecentFiles(); - for (int i = 0; i < maxRecentFiles; ++i) { + s32 maxRecentFiles = SettingsUtil::SettingsMgr::instance().maxRecentFiles(); + for (s32 i = 0; i < maxRecentFiles; ++i) { QAction* recentFileAction = mRecentFilesMenu.addAction(""); recentFileAction->setVisible(false); connect(recentFileAction, &QAction::triggered, this, &MainWindow::openRecentFile); @@ -224,7 +225,7 @@ void MainWindow::setupMenus() { } void MainWindow::closeEvent(QCloseEvent* event) { - if (!mPtclRes || !mHasUnsavedChanges) { + if (!mDocument || !mHasUnsavedChanges) { auto& settings = SettingsUtil::SettingsMgr::instance(); settings.setWindowGeometry(saveGeometry()); settings.setWindowState(saveState()); @@ -303,26 +304,26 @@ void MainWindow::openFile() { } void MainWindow::saveFile() { - if (!mPtclRes) { + if (!mDocument) { return; } - if (mCurrentFilePath.isEmpty()) { + if (mDocument->filePath().isEmpty()) { saveFileAs(); return; } - mPtclRes->save(mCurrentFilePath); + mDocument->save(mDocument->filePath()); setDirty(false); statusBar()->showMessage("File Saved", 2000); - SettingsUtil::SettingsMgr::instance().addRecentFile(mCurrentFilePath); + SettingsUtil::SettingsMgr::instance().addRecentFile(mDocument->filePath()); updateRecentFileList(); } void MainWindow::saveFileAs() { - if (!mPtclRes) { + if (!mDocument) { return; } @@ -346,12 +347,12 @@ void MainWindow::saveFileAs() { filePath += ".ptcl"; } - mPtclRes->save(filePath); + mDocument->save(filePath); setDirty(false); statusBar()->showMessage("File Saved", 2000); - mCurrentFilePath = filePath; + mDocument->filePath() = filePath; SettingsUtil::SettingsMgr::instance().addRecentFile(filePath); SettingsUtil::SettingsMgr::instance().setLastSavePath(QFileInfo(filePath).absolutePath()); updateRecentFileList(); @@ -372,10 +373,10 @@ void MainWindow::openRecentFile() { void MainWindow::updateRecentFileList() { const auto recentFiles = SettingsUtil::SettingsMgr::instance().recentFiles(); - int maxRecentFiles = SettingsUtil::SettingsMgr::instance().maxRecentFiles(); + s32 maxRecentFiles = SettingsUtil::SettingsMgr::instance().maxRecentFiles(); qsizetype numRecentFiles = qMin(recentFiles.size(), maxRecentFiles); - for (int i = 0; i < numRecentFiles; ++i) { + for (s32 i = 0; i < numRecentFiles; ++i) { auto& file = recentFiles[i]; auto& action = mRecentFileActions[i]; @@ -393,77 +394,45 @@ void MainWindow::updateRecentFileList() { } void MainWindow::loadPtclRes(const QString& path) { - mEmitterSetWidget.clear(); - mPtclList.setEnabled(false); - mTextureWidget.setEnabled(false); + mPtclList.setDocument(nullptr); + mEmitterWidget.setDocument(nullptr); + mEmitterSetWidget.setDocument(nullptr); + mTextureWidget.setDocument(nullptr); + mProjNameLineEdit.setEnabled(false); mSaveAsAction.setEnabled(false); - mPtclRes = std::make_unique(); - if (!mPtclRes->load(path)) { - mPtclRes.reset(); + mDocument = std::make_unique(); + if (!mDocument->load(path)) { + mDocument.reset(); return; } - mCurrentFilePath = path; setDirty(false); SettingsUtil::SettingsMgr::instance().addRecentFile(path); SettingsUtil::SettingsMgr::instance().setLastOpenPath(QFileInfo(path).absolutePath()); updateRecentFileList(); - mPtclList.setPtclRes(mPtclRes.get()); - mTextureWidget.setTextures(&mPtclRes->textures()); - mEmitterWidget.setTextureList(&mPtclRes->textures()); + mPtclList.setDocument(mDocument.get()); + mEmitterWidget.setDocument(mDocument.get()); + mEmitterSetWidget.setDocument(mDocument.get()); + mTextureWidget.setDocument(mDocument.get()); QSignalBlocker b1(mProjNameLineEdit); - mProjNameLineEdit.setText(mPtclRes->name()); + mProjNameLineEdit.setText(mDocument->data().name()); - if (mPtclRes->emitterSetCount() != 0) { - selectEmitter(0, 0); + if (mDocument->data().emitterSetCount() != 0) { + mSelection.set(0, 0, Ptcl::Selection::Type::EmitterSet); } updateStatusBar(); updateWindowTitle(); - mPtclList.setEnabled(true); - mTextureWidget.setEnabled(true); mProjNameLineEdit.setEnabled(true); mSaveAsAction.setEnabled(true); } -void MainWindow::selectEmitterSet(s32 setIndex) { - mCurEmitterSetIdx = setIndex; - mCurEmitterIdx = 0; - - if (!mPtclRes) { - return; - } - - const auto& emitterSet = mPtclRes->emitterSet(mCurEmitterSetIdx); - mEmitterSetWidget.setEmitterSet(emitterSet); - - if (!mEmitterSetWidget.isEnabled()) { - mEmitterSetWidget.setEnabled(true); - } -} - -void MainWindow::selectEmitter(s32 setIndex, s32 emitterIndex) { - mCurEmitterSetIdx = setIndex; - mCurEmitterIdx = emitterIndex; - - if (!mPtclRes) { - return; - } - - const auto& emitter = mPtclRes->emitter(mCurEmitterSetIdx, mCurEmitterIdx); - mEmitterWidget.setEmitter(emitter); - - if (!mEmitterWidget.isEnabled()) { - mEmitterWidget.setEnabled(true); - } -} - void MainWindow::setPropertiesView(PropertiesView view) { if (view == mCurPropertiesView) { return; @@ -476,20 +445,10 @@ void MainWindow::setPropertiesView(PropertiesView view) { mPropertiesStack->setCurrentWidget(&mEmitterSetWidget); break; case PropertiesView::Emitter: - mPropertiesStack->setCurrentWidget(&mEmitterWidget); - mEmitterWidget.showStandardEditor(); - break; case PropertiesView::EmitterChild: - mPropertiesStack->setCurrentWidget(&mEmitterWidget); - mEmitterWidget.showChildEditor(); - break; case PropertiesView::EmitterFlux: - mPropertiesStack->setCurrentWidget(&mEmitterWidget); - mEmitterWidget.showFluctuationEditor(); - break; case PropertiesView::EmitterField: mPropertiesStack->setCurrentWidget(&mEmitterWidget); - mEmitterWidget.showFieldEditor(); break; } } @@ -497,12 +456,12 @@ void MainWindow::setPropertiesView(PropertiesView view) { void MainWindow::updatePropertiesStatus() { mPropertiesGroup.setTitle({}); - if (!mPtclRes) { + if (!mDocument) { return; } - const auto& emitterSet = mPtclRes->emitterSet(mCurEmitterSetIdx); - const auto& emitter = mPtclRes->emitter(mCurEmitterSetIdx, mCurEmitterIdx); + const auto& emitterSet = mDocument->emitterSet(mSelection.emitterSetIndex()); + const auto& emitter = mDocument->emitter(mSelection.emitterSetIndex(), mSelection.emitterIndex()); QString title = emitterSet->name(); @@ -531,8 +490,8 @@ void MainWindow::updatePropertiesStatus() { void MainWindow::updateWindowTitle() { QString title = "PTCLTool"; - if (!mCurrentFilePath.isEmpty()) { - title += " - " + QFileInfo(mCurrentFilePath).fileName(); + if (mDocument && !mDocument->filePath().isEmpty()) { + title += " - " + QFileInfo(mDocument->filePath()).fileName(); } if (mHasUnsavedChanges) { title += " *"; @@ -545,7 +504,7 @@ void MainWindow::updateStatusBar() { return; } - if (!mPtclRes) { + if (!mDocument) { mStatusLabel->setText("No file loaded"); } else if (mHasUnsavedChanges) { mStatusLabel->setText("Unsaved changes"); diff --git a/src/editor/ptclListWidget.cpp b/src/editor/ptclListWidget.cpp index 17577c4..6604451 100644 --- a/src/editor/ptclListWidget.cpp +++ b/src/editor/ptclListWidget.cpp @@ -223,14 +223,53 @@ void PtclList::setupFilterMenu() { connect(compactAction, &QAction::toggled, this, updateFilter); } -void PtclList::setPtclRes(Ptcl::PtclRes* ptclRes) { - if (mResPtr == ptclRes) { +void PtclList::setDocument(Ptcl::Document* document) { + if (mDocument == document) { + return; + } + + mDocument = document; + + if (!mDocument) { + mListModel.clear(); + setEnabled(false); return; } mListModel.clear(); - mResPtr = ptclRes; populateList(); + setEnabled(true); +} + +void PtclList::setSelection(Ptcl::Selection* selection) { + mSelection = selection; + + connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { + if (!mDocument) { + return; + } + + QSignalBlocker b(mTreeView.selectionModel()); + + const QStandardItem* item = findItem(setIndex, emitterIndex, type); + if (!item) { + mTreeView.clearSelection(); + return; + } + + const QModelIndex sourceIndex = mListModel.indexFromItem(item); + const QModelIndex proxyIndex = mProxyModel.mapFromSource(sourceIndex); + + if (!proxyIndex.isValid()) { + mTreeView.clearSelection(); + return; + } + + auto* selectionModel = mTreeView.selectionModel(); + selectionModel->clearSelection(); + selectionModel->setCurrentIndex(proxyIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + mTreeView.scrollTo(proxyIndex); + }); } void PtclEditor::PtclList::refresh() { @@ -238,13 +277,13 @@ void PtclEditor::PtclList::refresh() { } void PtclList::populateList() { - if (!mResPtr) { + if (!mDocument) { return; } mListModel.clear(); - const auto& sets = mResPtr->getEmitterSets(); - for (u32 setIndex = 0; setIndex < sets.size(); ++setIndex) { + const auto& sets = mDocument->data().getEmitterSets(); + for (s32 setIndex = 0; setIndex < sets.size(); ++setIndex) { insertEmitterSetNode(setIndex); } @@ -253,7 +292,7 @@ void PtclList::populateList() { } void PtclList::insertEmitterSetNode(s32 setIndex) { - const auto& set = mResPtr->getEmitterSets()[setIndex]; + const auto& set = mDocument->emitterSet(setIndex); QString setName = QString("%1: %2").arg(setIndex).arg(set->name()); auto* setItem = new QStandardItem(setName); @@ -263,14 +302,14 @@ void PtclList::insertEmitterSetNode(s32 setIndex) { setItem->setIcon(QIcon(":/res/icons/emitterset.png")); // Emitters - for (u32 emitterIndex = 0; emitterIndex < set->emitters().size(); ++emitterIndex) { + for (s32 emitterIndex = 0; emitterIndex < set->emitters().size(); ++emitterIndex) { insertEmitterNode(setItem, setIndex, emitterIndex); } mListModel.appendRow(setItem); } void PtclList::insertEmitterNode(QStandardItem* setItem, s32 setIndex, s32 emitterIndex) { - const auto& emitter = mResPtr->getEmitterSets()[setIndex]->emitters()[emitterIndex]; + const auto& emitter = mDocument->emitter(setIndex, emitterIndex); QString emitterName = QString("%1: %2").arg(emitterIndex).arg(emitter->name()); auto* emitterItem = new QStandardItem(emitterName); @@ -311,39 +350,39 @@ void PtclList::selectionChanged(const QItemSelection& selection) { switch (type) { case NodeType::EmitterSet: { - const u32 setIndex = sourceIndex.row(); - emit selectedEmitterSetChanged(setIndex); + const s32 setIndex = sourceIndex.row(); + mSelection->set(setIndex, 0, Ptcl::Selection::Type::EmitterSet); break; } case NodeType::Emitter: { - const u32 emitterIndex = sourceIndex.row(); - const u32 setIndex = sourceIndex.parent().row(); - emit selectedEmitterChanged(setIndex, emitterIndex); + const s32 emitterIndex = sourceIndex.row(); + const s32 setIndex = sourceIndex.parent().row(); + mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::Emitter); break; } case NodeType::ChildData: { - const u32 emitterIndex = sourceIndex.parent().row(); - const u32 setIndex = sourceIndex.parent().parent().row(); - emit selectedChildData(setIndex, emitterIndex); + const s32 emitterIndex = sourceIndex.parent().row(); + const s32 setIndex = sourceIndex.parent().parent().row(); + mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::EmitterChild); break; } case NodeType::Fluctuation: { - const u32 emitterIndex = sourceIndex.parent().row(); - const u32 setIndex = sourceIndex.parent().parent().row(); - emit selectedFluctuation(setIndex, emitterIndex); + const s32 emitterIndex = sourceIndex.parent().row(); + const s32 setIndex = sourceIndex.parent().parent().row(); + mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::EmitterFlux); break; } case NodeType::Field: { - const u32 emitterIndex = sourceIndex.parent().row(); - const u32 setIndex = sourceIndex.parent().parent().row(); - emit selectedField(setIndex, emitterIndex); + const s32 emitterIndex = sourceIndex.parent().row(); + const s32 setIndex = sourceIndex.parent().parent().row(); + mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::EmitterField); break; } } } void PtclList::addComplexNodes(QStandardItem* emitterItem, s32 setIndex, s32 emitterIndex) { - const auto& emitter = mResPtr->getEmitterSets()[setIndex]->emitters()[emitterIndex]; + const auto& emitter = mDocument->emitter(setIndex, emitterIndex); const auto& props = emitter->complexProperties(); // ChildData @@ -377,6 +416,35 @@ void PtclList::addComplexNodes(QStandardItem* emitterItem, s32 setIndex, s32 emi ); } +QStandardItem* PtclList::findItem(s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) const { + auto* setItem = mListModel.item(setIndex); + if (!setItem) { + return nullptr; + } + + if (type == Ptcl::Selection::Type::EmitterSet) { + return setItem; + } + + auto* emitterItem = setItem->child(emitterIndex); + if (!emitterItem) { + return nullptr; + } + + switch (type) { + case Ptcl::Selection::Type::Emitter: + return emitterItem; + case Ptcl::Selection::Type::EmitterChild: + return findChildByType(emitterItem, NodeType::ChildData); + case Ptcl::Selection::Type::EmitterFlux: + return findChildByType(emitterItem, NodeType::Fluctuation); + case Ptcl::Selection::Type::EmitterField: + return findChildByType(emitterItem, NodeType::Field); + default: + return nullptr; + } +} + QStandardItem* PtclList::findChildByType(QStandardItem* parent, NodeType type) { if (!parent) { return nullptr; @@ -410,51 +478,6 @@ void PtclList::ensureComplexNode(QStandardItem* emitterItem, NodeType type, cons item->setData(enabled, sRoleEnabled); } -void PtclList::selectEmitter(s32 setIndex, s32 emitterIndex) { - const QStandardItem* setItem = mListModel.item(setIndex); - if (!setItem) { - return; - } - - const QStandardItem* emitterItem = setItem->child(emitterIndex); - if (!emitterItem) { - return; - } - - const QModelIndex sourceIndex = mListModel.indexFromItem(emitterItem); - const QModelIndex proxyIndex = mProxyModel.mapFromSource(sourceIndex); - - if (!proxyIndex.isValid()) { - return; - } - - auto* selection = mTreeView.selectionModel(); - - selection->clearSelection(); - selection->setCurrentIndex(proxyIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); - mTreeView.scrollTo(proxyIndex); -} - -void PtclList::selectEmitterSet(s32 setIndex) { - const QStandardItem* setItem = mListModel.item(setIndex); - if (!setItem) { - return; - } - - const QModelIndex sourceIndex = mListModel.indexFromItem(setItem); - const QModelIndex proxyIndex = mProxyModel.mapFromSource(sourceIndex); - - if (!proxyIndex.isValid()) { - return; - } - - auto* selection = mTreeView.selectionModel(); - - selection->clearSelection(); - selection->setCurrentIndex(proxyIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); - mTreeView.scrollTo(proxyIndex); -} - void PtclList::updateEmitter(s32 setIndex, s32 emitterIndex) { const QStandardItem* setItem = mListModel.item(setIndex); if (!setItem) { @@ -466,7 +489,7 @@ void PtclList::updateEmitter(s32 setIndex, s32 emitterIndex) { return; } - const auto& emitter = mResPtr->getEmitterSets()[setIndex]->emitters()[emitterIndex]; + const auto& emitter = mDocument->emitter(setIndex, emitterIndex); emitterItem->setData(static_cast(emitter->type()), sRoleEmitterType); if (emitter->type() == Ptcl::EmitterType::Simple) { @@ -481,9 +504,7 @@ void PtclList::updateEmitterName(s32 setIndex, s32 emitterIndex) { const QStandardItem* setItem = mListModel.item(setIndex); QStandardItem* emitterItem = setItem->child(emitterIndex); - const auto& sets = mResPtr->getEmitterSets(); - const auto& set = sets[setIndex]; - const auto* emitter = set->emitters()[emitterIndex].get(); + const auto& emitter = mDocument->emitter(setIndex, emitterIndex); QString emitterName = QString("%1: %2").arg(emitterIndex).arg(emitter->name()); emitterItem->setText(emitterName); @@ -492,8 +513,7 @@ void PtclList::updateEmitterName(s32 setIndex, s32 emitterIndex) { void PtclList::updateEmitterSetName(s32 setIndex) { QStandardItem* setItem = mListModel.item(setIndex); - const auto& sets = mResPtr->getEmitterSets(); - const auto& set = sets[setIndex]; + const auto& set = mDocument->emitterSet(setIndex); QString setName = QString("%1: %2").arg(setIndex).arg(set->name()); setItem->setText(setName); @@ -554,11 +574,11 @@ void PtclList::addEmitterSet() { return; } - const s32 setIndex = mResPtr->emitterSetCount(); - mResPtr->addNewEmitterSet(); + const s32 setIndex = mDocument->data().emitterSetCount(); + mDocument->data().addNewEmitterSet(); insertEmitterSetNode(setIndex); - selectEmitterSet(setIndex); + mSelection->set(setIndex, 0, Ptcl::Selection::Type::EmitterSet); expandSourceIndex(mListModel.index(setIndex, 0)); emit itemAdded(); } @@ -578,14 +598,14 @@ void PtclList::addEmitter() { setItem = item->parent(); } - u32 setIndex = setItem->data(sRoleSetIdx).toUInt(); - auto& emitterSet = mResPtr->getEmitterSets()[setIndex]; + s32 setIndex = setItem->data(sRoleSetIdx).toInt(); + const auto& emitterSet = mDocument->emitterSet(setIndex); const s32 emitterIndex = emitterSet->emitterCount(); emitterSet->addNewEmitter(); insertEmitterNode(setItem, setIndex, emitterIndex); - selectEmitter(setIndex, emitterIndex); + mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::Emitter); expandSourceIndex(mListModel.indexFromItem(setItem)); emit itemAdded(); } @@ -615,11 +635,11 @@ void PtclList::removeEmitter(QStandardItem* setItem, QStandardItem* emitterItem) return; } - const s32 setIndex = setItem->data(sRoleSetIdx).toUInt(); - const s32 emitterIndex = emitterItem->data(sRoleEmitterIdx).toUInt(); + const s32 setIndex = setItem->data(sRoleSetIdx).toInt(); + const s32 emitterIndex = emitterItem->data(sRoleEmitterIdx).toInt(); - const auto& emitterSet = mResPtr->getEmitterSets()[setIndex]; - const auto& emitter = mResPtr->getEmitterSets()[setIndex]->emitters()[emitterIndex]; + const auto& emitterSet = mDocument->emitterSet(setIndex); + const auto& emitter = mDocument->emitter(setIndex, emitterIndex); const auto confirmationMessage = QString("Are you sure you want to remove the Emitter '%1'?").arg(emitter->name()); if (QMessageBox::question(this, "Remove Emitter", confirmationMessage) != QMessageBox::Yes) { @@ -633,7 +653,7 @@ void PtclList::removeEmitter(QStandardItem* setItem, QStandardItem* emitterItem) const s32 remainingCount = setItem->rowCount(); if (remainingCount > 0) { const s32 nextIndex = std::min(emitterIndex, remainingCount - 1); - selectEmitter(setIndex, nextIndex); + mSelection->set(setIndex, nextIndex, Ptcl::Selection::Type::Emitter); } } @@ -642,15 +662,15 @@ void PtclList::removeEmitterSet(QStandardItem* setItem) { return; } - const s32 setIndex = setItem->data(sRoleSetIdx).toUInt(); - const auto& emitterSet = mResPtr->getEmitterSets()[setIndex]; + const s32 setIndex = setItem->data(sRoleSetIdx).toInt(); + const auto& emitterSet = mDocument->emitterSet(setIndex); const auto confirmationMessage = QString("Are you sure you want to remove the EmitterSet '%1'?").arg(emitterSet->name()); if (QMessageBox::question(this, "Remove EmitterSet", confirmationMessage) != QMessageBox::Yes) { return; } - mResPtr->removeEmitterSet(setIndex); + mDocument->data().removeEmitterSet(setIndex); mListModel.removeRow(setIndex); reindexEmitterSets(); @@ -658,7 +678,7 @@ void PtclList::removeEmitterSet(QStandardItem* setItem) { if (remainingCount > 0) { const s32 nextIndex = std::min(setIndex, remainingCount - 1); - selectEmitterSet(nextIndex); + mSelection->set(nextIndex, 0, Ptcl::Selection::Type::EmitterSet); } } @@ -674,7 +694,7 @@ void PtclList::reindexEmitters(QStandardItem* setItem, s32 setIndex) { } emitterItem->setData(i, sRoleEmitterIdx); - const auto& emitter = mResPtr->getEmitterSets()[setIndex]->emitters()[i]; + const auto& emitter = mDocument->emitter(setIndex, i); emitterItem->setText(QString("%1: %2").arg(i).arg(emitter->name())); for (s32 c = 0; c < emitterItem->rowCount(); ++c) { @@ -694,7 +714,7 @@ void PtclList::reindexEmitterSets() { } setItem->setData(i, sRoleSetIdx); - const auto& set = mResPtr->getEmitterSets()[i]; + const auto& set = mDocument->emitterSet(i); setItem->setText(QString("%1: %2").arg(i).arg(set->name())); reindexEmitters(setItem, i); } @@ -721,12 +741,12 @@ void PtclList::copyItem() { mClipboardEmitter.reset(); if (type == NodeType::EmitterSet) { - const s32 setIndex = item->data(sRoleSetIdx).toUInt(); - mClipboardSet = mResPtr->getEmitterSets()[setIndex]->clone(); + const s32 setIndex = item->data(sRoleSetIdx).toInt(); + mClipboardSet = mDocument->data().emitterSet(setIndex)->clone(); } else if (type == NodeType::Emitter) { - const s32 setIndex = item->parent()->data(sRoleSetIdx).toUInt(); - const s32 emitterIndex = item->data(sRoleEmitterIdx).toUInt(); - mClipboardEmitter = mResPtr->getEmitterSets()[setIndex]->emitters()[emitterIndex]->clone(); + const s32 setIndex = item->parent()->data(sRoleSetIdx).toInt(); + const s32 emitterIndex = item->data(sRoleEmitterIdx).toInt(); + mClipboardEmitter = mDocument->data().emitter(setIndex, emitterIndex)->clone(); } updateToolbarForSelection(item); @@ -744,25 +764,25 @@ void PtclList::pasteItem() { const auto type = static_cast(item->data(sRoleNodeType).toUInt()); if (mClipboardSet && type == NodeType::EmitterSet) { - auto& newSet = mResPtr->appendEmitterSet(mClipboardSet); + auto& newSet = mDocument->data().appendEmitterSet(mClipboardSet); mClipboardSet = newSet->clone(); - const s32 setIndex = mResPtr->emitterSetCount() - 1; + const s32 setIndex = mDocument->data().emitterSetCount() - 1; insertEmitterSetNode(setIndex); - selectEmitterSet(setIndex); + mSelection->set(setIndex, 0, Ptcl::Selection::Type::EmitterSet); } else if (mClipboardEmitter && type == NodeType::Emitter) { const auto& setItem = item->parent(); - const s32 setIndex = item->data(sRoleSetIdx).toUInt(); - auto& set = mResPtr->getEmitterSets()[setIndex]; + const s32 setIndex = item->data(sRoleSetIdx).toInt(); + auto set = mDocument->data().emitterSet(setIndex); auto& newEmitter = set->appendEmitter(mClipboardEmitter); mClipboardEmitter = newEmitter->clone(); const s32 emitterIndex = set->emitterCount() - 1; insertEmitterNode(setItem, setIndex, emitterIndex); - selectEmitter(setIndex, emitterIndex); - } + mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::Emitter); + } } // ========================================================================== // diff --git a/src/editor/textureListWidget.cpp b/src/editor/textureListWidget.cpp index 62e63fe..2bdfa45 100644 --- a/src/editor/textureListWidget.cpp +++ b/src/editor/textureListWidget.cpp @@ -288,22 +288,27 @@ void TextureListWidget::setupSelectionHandling() { }); } -void TextureListWidget::setTextures(Ptcl::TextureList* textures) { - if (mTexturesPtr == textures) { +void TextureListWidget::setDocument(Ptcl::Document* document) { + mDocument = document; + + if (!mDocument) { + mTexturesPtr = nullptr; + mModel.setTextures(nullptr); + + mActionExportAll->setEnabled(false); + mActionImportTexture->setEnabled(false); + + setEnabled(false); return; } - mTexturesPtr = textures; - mModel.setTextures(textures); + mTexturesPtr = &mDocument->textures(); + mModel.setTextures(mTexturesPtr); - const bool hasData = (textures != nullptr); - mActionExportAll->setEnabled(hasData); - mActionImportTexture->setEnabled(hasData); -} + mActionExportAll->setEnabled(true); + mActionImportTexture->setEnabled(true); -void TextureListWidget::clear() { - setTextures(nullptr); - mDetailsPanel.setTexture(nullptr); + setEnabled(true); } void TextureListWidget::exportAll() { diff --git a/src/ptcl/ptcl.cpp b/src/ptcl/ptcl.cpp index e3efc3e..12e35ed 100644 --- a/src/ptcl/ptcl.cpp +++ b/src/ptcl/ptcl.cpp @@ -445,11 +445,11 @@ bool PtclRes::load(const QString& filePath) { return true; } -u32 PtclRes::emitterSetCount() const { +s32 PtclRes::emitterSetCount() const { return mEmitterSets.size(); } -u32 PtclRes::emitterCount(s32 setIndex) const { +s32 PtclRes::emitterCount(s32 setIndex) const { if (setIndex < 0 || setIndex >= mEmitterSets.size()) { return 0; } diff --git a/src/ptcl/ptclDocument.cpp b/src/ptcl/ptclDocument.cpp new file mode 100644 index 0000000..6b5a967 --- /dev/null +++ b/src/ptcl/ptclDocument.cpp @@ -0,0 +1,43 @@ +#include "ptcl/ptclDocument.h" + + +namespace Ptcl { + + +// ========================================================================== // + + +Selection::Selection(QObject* parent) : + QObject{parent} {} + +void Selection::set(s32 setIndex, s32 emitterIndex, Type type) { + mSetIndex = setIndex; + mEmitterIndex = emitterIndex; + mType = type; + emit selectionChanged(mSetIndex, mEmitterIndex, mType); +} + + +// ========================================================================== // + + +Document::Document(QObject* parent) : + QObject{parent} {} + +bool Document::load(const QString& filePath) { + mFilePath = filePath; + mModified = false; + return mData.load(filePath); +} + +bool Document::save(const QString& filePath) { + mFilePath = filePath; + mModified = false; + return mData.save(filePath); +} + + +// ========================================================================== // + + +} // namespace Ptcl From 6374df7f363bd1b17f3e920be17d7ac0d1454f71 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Sat, 21 Feb 2026 14:42:11 +0000 Subject: [PATCH 03/35] feat(ui): implement undo/redo for Emitter basicProperties --- CMakeLists.txt | 1 + .../emitterWidget/basicPropertiesWidget.h | 24 ++- include/editor/mainWindow.h | 11 +- include/ptcl/ptclCommand.h | 92 +++++++++ include/ptcl/ptclDocument.h | 24 +++ include/ptcl/ptclEmitter.h | 28 ++- include/ptcl/ptclSeed.h | 2 + .../emitterWidget/basicPropertiesWidget.cpp | 174 +++++++++++++----- src/editor/emitterWidget/emitterWidget.cpp | 11 +- src/editor/mainWindow.cpp | 61 +++++- src/ptcl/ptcl.cpp | 2 +- src/ptcl/ptclCommand.cpp | 25 +++ src/ptcl/ptclEmitter.cpp | 4 - 13 files changed, 393 insertions(+), 66 deletions(-) create mode 100644 include/ptcl/ptclCommand.h create mode 100644 src/ptcl/ptclCommand.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cdb3fc..6460509 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,6 +150,7 @@ set(PROJECT_HEADERS include/ptcl/ptcl.h include/ptcl/ptclBinary.h include/ptcl/ptclChildData.h + include/ptcl/ptclCommand.h include/ptcl/ptclDocument.h include/ptcl/ptclEmitter.h include/ptcl/ptclEmitterSet.h diff --git a/include/editor/emitterWidget/basicPropertiesWidget.h b/include/editor/emitterWidget/basicPropertiesWidget.h index 21ae272..f44bbed 100644 --- a/include/editor/emitterWidget/basicPropertiesWidget.h +++ b/include/editor/emitterWidget/basicPropertiesWidget.h @@ -44,7 +44,8 @@ class EmitterWidget::BasicPropertiesWidget final : public QWidget { public: explicit BasicPropertiesWidget(QWidget* parent = nullptr); - void setProperties(const Ptcl::Emitter::BasicProperties& properties); + void setDocument(Ptcl::Document* document); + void setSelection(Ptcl::Selection* selection); signals: void propertiesUpdated(const Ptcl::Emitter::BasicProperties& properties); @@ -52,18 +53,35 @@ class EmitterWidget::BasicPropertiesWidget final : public QWidget { void emitterNameChanged(); private: + void populateProperties(); void setupConnections(); void updateShapeRowVisibility(); void updateBillboardRowVisibility(); void syncBillboardSelection(); - void applyBillboardType(Ptcl::BillboardType type); + void setBillboardType(Ptcl::BillboardType type, const QString& label, const QString& key); + + QString formatLabel(const QString& label) const; + + template + void setEmitterProperty(const QString& label, QString key, Getter getter, Setter setter, const T& value) { + if (!mDocument || !mSelection) { + return; + } + + mDocument->setEmitterProperty(mSelection->emitterSetIndex(), mSelection->emitterIndex(), formatLabel(label), key, getter, setter, value); + } static bool isBillboardAllowedForShape(Ptcl::BillboardType billboard, ShapeType shape); static ShapeType shapeFromBillboard(Ptcl::BillboardType type); +private slots: + void onEmitterChanged(s32 setIndex, s32 emitterIndex); + private: - Ptcl::Emitter::BasicProperties mProps{}; + Ptcl::Document* mDocument{nullptr}; + const Ptcl::Selection* mSelection{nullptr}; + Ptcl::Emitter* mEmitter{nullptr}; QFormLayout* mMainLayout{nullptr}; diff --git a/include/editor/mainWindow.h b/include/editor/mainWindow.h index 443991c..6327221 100644 --- a/include/editor/mainWindow.h +++ b/include/editor/mainWindow.h @@ -1,7 +1,5 @@ #pragma once -#include "typedefs.h" -#include "ptcl/ptcl.h" #include "ptcl/ptclDocument.h" #include "editor/emitterSetWidget.h" #include "editor/emitterWidget/emitterWidget.h" @@ -16,6 +14,7 @@ #include #include #include +#include #include @@ -67,6 +66,8 @@ private slots: void updateStatusBar(); void setDirty(bool dirty); + void bindUndoStack(); + private: std::unique_ptr mDocument{}; Ptcl::Selection mSelection{}; @@ -76,8 +77,12 @@ private slots: QAction mSaveAsAction{}; std::vector mRecentFileActions{}; + QAction* mUndoAction{nullptr}; + QAction* mRedoAction{nullptr}; + QMenu mFileMenu{}; QMenu mRecentFilesMenu{}; + QMenu mEditMenu{}; QSplitter* mTopSplitter{nullptr}; QSplitter* mBottomSplitter{nullptr}; @@ -87,6 +92,8 @@ private slots: QStackedWidget* mPropertiesStack{nullptr}; QGroupBox mPropertiesGroup{}; + QUndoView mUndoView{}; + PtclEditor::PtclList mPtclList{}; PtclEditor::EmitterWidget mEmitterWidget{}; PtclEditor::EmitterSetWidget mEmitterSetWidget{}; diff --git a/include/ptcl/ptclCommand.h b/include/ptcl/ptclCommand.h new file mode 100644 index 0000000..3654a09 --- /dev/null +++ b/include/ptcl/ptclCommand.h @@ -0,0 +1,92 @@ +#pragma once + +#include "ptcl/ptclEmitter.h" +#include "ptcl/ptclDocument.h" + + +#include + + +namespace Ptcl { + + +// ========================================================================== // + + +template +class SetEmitterPropertyCommand final : public QUndoCommand { +public: + using Getter = std::function; + using Setter = std::function; + + SetEmitterPropertyCommand(Document* document, s32 setIndex, s32 emitterIndex, QString label, QString key, Getter getter, Setter setter, const T& newValue, QUndoCommand* parent = nullptr) : + QUndoCommand{std::move(label), parent}, mDocument{document}, mSetIndex{setIndex}, mEmitterIndex{emitterIndex}, mPropertyKey{std::move(key)}, mGetter{std::move(getter)}, mSetter{std::move(setter)}, mNewValue{newValue} { + auto& emitter = getEmitter(); + mOldValue = mGetter(emitter); + + if (mOldValue == mNewValue) { + setObsolete(true); + } + } + + s32 id() const override { + return qHash(mPropertyKey); + } + + bool mergeWith(const QUndoCommand* other) override { + auto otherCmd = dynamic_cast(other); + if (!otherCmd) { + return false; + } + + if (mDocument != otherCmd->mDocument || mSetIndex != otherCmd->mSetIndex || + mEmitterIndex != otherCmd->mEmitterIndex || mPropertyKey != otherCmd->mPropertyKey) { + return false; + } + + mNewValue = otherCmd->mNewValue; + return true; + } + + void undo() override { apply(mOldValue); } + void redo() override { apply(mNewValue); } + +private: + Emitter& getEmitter() const; + void apply(const T& value); + +private: + Document* mDocument{}; + s32 mSetIndex{}; + s32 mEmitterIndex{}; + + QString mPropertyKey{}; + + Getter mGetter{}; + Setter mSetter{}; + + T mOldValue{}; + T mNewValue{}; +}; + + +// ========================================================================== // + + +template +Emitter& SetEmitterPropertyCommand::getEmitter() const { + return *mDocument->emitter(mSetIndex, mEmitterIndex); +} + +template +void SetEmitterPropertyCommand::apply(const T& value) { + auto& emitter = getEmitter(); + mSetter(emitter, value); + mDocument->notifyEmitterChanged(mSetIndex, mEmitterIndex); +} + + +// ========================================================================== // + + +} // namespace Ptcl diff --git a/include/ptcl/ptclDocument.h b/include/ptcl/ptclDocument.h index fb8c489..18d66ec 100644 --- a/include/ptcl/ptclDocument.h +++ b/include/ptcl/ptclDocument.h @@ -3,6 +3,7 @@ #include "ptcl/ptcl.h" #include +#include namespace Ptcl { @@ -45,6 +46,13 @@ class Selection final : public QObject { // ========================================================================== // +template +class SetEmitterPropertyCommand; + + +// ========================================================================== // + + class Document final : public QObject { Q_OBJECT public: @@ -68,9 +76,23 @@ class Document final : public QObject { bool isModified() const { return mModified; } QString filePath() const { return mFilePath; } + QUndoStack* undoStack() { return &mUndoStack; } + + template + void setEmitterProperty(s32 setIndex, s32 emitterIndex, QString label, QString key, Getter getter, Setter setter, const T& value) { + mUndoStack.push(new SetEmitterPropertyCommand(this, setIndex, emitterIndex, label, key, getter, setter, value)); + } + + void notifyEmitterChanged(s32 setIndex, s32 emitterIndex) { emit emitterChanged(setIndex, emitterIndex); } + +signals: + void emitterChanged(s32 setIndex, s32 emitterIndex); + private: PtclRes mData{}; QString mFilePath{}; + QUndoStack mUndoStack{}; + bool mModified{false}; }; @@ -79,3 +101,5 @@ class Document final : public QObject { } // namespace Ptcl + +#include "ptcl/ptclCommand.h" diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index 5432a55..aca347a 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -186,14 +186,36 @@ class Emitter { std::unique_ptr clone() const; - EmitterType type() const; + EmitterType type() const { return mBasicProperties.type; } + void setType(EmitterType type) { mBasicProperties.type = type; } - BitFlag& flags(); - const BitFlag& flags() const; + FollowType followType() const { return mBasicProperties.followType; } + void setFollowType(FollowType type) { + mBasicProperties.followType = type; + + // TODO: Check if this should also be set for Ptcl::FollowType::PosOnly + mBasicProperties.isFollow = (type == Ptcl::FollowType::All); + } const QString& name() const; void setName(const QString &name); + const PtclSeed& randomSeed() const { return mBasicProperties.randomSeed; } + void setRandomSeed(PtclSeed seed) { mBasicProperties.randomSeed = seed; } + + BillboardType billboardType() const { return mBasicProperties.billboardType; } + void setBillboardType(BillboardType type) { + mBasicProperties.billboardType = type; + mBasicProperties.isPolygon = (type == Ptcl::BillboardType::PolygonXY || type == Ptcl::BillboardType::PolygonXZ); + mBasicProperties.isVelLook = (type == Ptcl::BillboardType::VelLook || type == Ptcl::BillboardType::VelLookPolygon); + } + + bool isEmitterBillboardMtx() const { return mBasicProperties.isEmitterBillboardMtx; } + void setIsEmitterBillboardMtx(bool isEmitterBillboardMtx) { mBasicProperties.isEmitterBillboardMtx = isEmitterBillboardMtx; } + + BitFlag& flags(); + const BitFlag& flags() const; + TextureHandle& textureHandle(); const TextureHandle& textureHandle() const; void setTexture(const std::shared_ptr& texture); diff --git a/include/ptcl/ptclSeed.h b/include/ptcl/ptclSeed.h index 3a54f90..0044f6b 100644 --- a/include/ptcl/ptclSeed.h +++ b/include/ptcl/ptclSeed.h @@ -27,6 +27,8 @@ class PtclSeed { u32 raw() const; void setRaw(u32 raw); + bool operator==(const PtclSeed&) const = default; + private: u32 mValue = 1; }; diff --git a/src/editor/emitterWidget/basicPropertiesWidget.cpp b/src/editor/emitterWidget/basicPropertiesWidget.cpp index 9a717e1..cde4074 100644 --- a/src/editor/emitterWidget/basicPropertiesWidget.cpp +++ b/src/editor/emitterWidget/basicPropertiesWidget.cpp @@ -60,98 +60,135 @@ EmitterWidget::BasicPropertiesWidget::BasicPropertiesWidget(QWidget* parent) : void EmitterWidget::BasicPropertiesWidget::setupConnections() { // Emitter Name connect(&mNameLineEdit, &QLineEdit::textChanged, this, [this](const QString& text) { - mProps.name = text; - emit propertiesUpdated(mProps); - emit emitterNameChanged(); + setEmitterProperty( + "Set Name", + "EmitterName", + &Ptcl::Emitter::name, + &Ptcl::Emitter::setName, + text + ); }); // Emitter Type connect(&mTypeComboBox, &QComboBox::currentIndexChanged, this, [this]() { - mProps.type = mTypeComboBox.currentEnum(); - - if (mProps.type == Ptcl::EmitterType::Simple) { - if (!isBillboardAllowedForShape(mProps.billboardType, ShapeType::Particle)) { - applyBillboardType(Ptcl::BillboardType::Billboard); + auto* stack = mDocument->undoStack(); + stack->beginMacro(formatLabel("Set Type")); + + setEmitterProperty( + "Set Type", + "EmitterType", + &Ptcl::Emitter::type, + &Ptcl::Emitter::setType, + mTypeComboBox.currentEnum() + ); + + if (mEmitter->type() == Ptcl::EmitterType::Simple) { + if (!isBillboardAllowedForShape(mEmitter->billboardType(), ShapeType::Particle)) { + setBillboardType(Ptcl::BillboardType::Billboard, "Auto Fix Billboard Type", "BillboardType"); syncBillboardSelection(); } } + stack->endMacro(); + updateShapeRowVisibility(); updateBillboardRowVisibility(); - emit propertiesUpdated(mProps); - emit emitterTypeChanged(); }); // Random Seed Mode connect(&mRandomSeedMode, &QComboBox::currentIndexChanged, this, [this]() { - auto& seed = mProps.randomSeed; + auto seed = mEmitter->randomSeed(); auto mode = static_cast(mRandomSeedMode.currentData().toUInt()); seed.setMode(mode); + + setEmitterProperty( + "Set Random Seed Mode", + "RandomSeedMode", + &Ptcl::Emitter::randomSeed, + &Ptcl::Emitter::setRandomSeed, + seed + ); + mRandomSeedSpinBox.setEnabled(mode == PtclSeed::Mode::ConstantSeed); - emit propertiesUpdated(mProps); }); // Random Seed connect(&mRandomSeedSpinBox, &SizedSpinBoxBase::valueChanged, this, [this](int value) { - auto& seed = mProps.randomSeed; + auto seed = mEmitter->randomSeed(); if (seed.mode() == PtclSeed::Mode::ConstantSeed) { seed.setConstantSeed(static_cast(value)); } - emit propertiesUpdated(mProps); + + setEmitterProperty( + "Set Random Seed", + "RandomSeed", + &Ptcl::Emitter::randomSeed, + &Ptcl::Emitter::setRandomSeed, + seed + ); }); // Follow Type connect(&mFollowTypeComboBox, &QComboBox::currentIndexChanged, this, [this]() { const auto type = mFollowTypeComboBox.currentEnum(); - mProps.followType = type; - // TODO: Check if this should also be set for Ptcl::FollowType::PosOnly - mProps.isFollow = (type == Ptcl::FollowType::All); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Follow Type", + "FollowType", + &Ptcl::Emitter::followType, + &Ptcl::Emitter::setFollowType, + type + ); }); // Billboard Type connect(&mBillboardComboBox, &QComboBox::currentIndexChanged, this, [this]() { - applyBillboardType(mBillboardComboBox.currentData().value()); - emit propertiesUpdated(mProps); + setBillboardType(mBillboardComboBox.currentData().value(), "Set Billboard Type", "BillboardType"); }); - // Shape Type + // Shape Type connect(&mShapeComboBox, &QComboBox::currentIndexChanged, this, [this]() { const auto type = mShapeComboBox.currentData().value(); - if (type == ShapeType::Particle) { - applyBillboardType(Ptcl::BillboardType::Billboard); - } else if (type == ShapeType::Primitive) { - applyBillboardType(Ptcl::BillboardType::Primitive); - } else { - applyBillboardType(Ptcl::BillboardType::Stripe); + Ptcl::BillboardType newBillboard; + switch (type) { + case ShapeType::Particle: newBillboard = Ptcl::BillboardType::Billboard; break; + case ShapeType::Primitive: newBillboard = Ptcl::BillboardType::Primitive; break; + case ShapeType::Stripe: newBillboard = Ptcl::BillboardType::Stripe; break; } + setBillboardType(newBillboard, "Set Shape Type", "ShapeType"); syncBillboardSelection(); updateBillboardRowVisibility(); updateShapeRowVisibility(); - emit propertiesUpdated(mProps); }); // Stripe Type connect(&mStripeTypeComboBox, &QComboBox::currentIndexChanged, this, [this]() { - applyBillboardType(mStripeTypeComboBox.currentData().value()); - emit propertiesUpdated(mProps); + setBillboardType(mStripeTypeComboBox.currentData().value(), "Set Stripe Type", "StripeType"); }); // Is Billboard Mtx connect(&mIsBillboardMtxCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - mProps.isEmitterBillboardMtx = checked; - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Billboard Matrix", + "IsBillboardMtx", + &Ptcl::Emitter::isEmitterBillboardMtx, + &Ptcl::Emitter::setIsEmitterBillboardMtx, + checked + ); }); } -void EmitterWidget::BasicPropertiesWidget::applyBillboardType(Ptcl::BillboardType type) { - mProps.billboardType = type; - mProps.isPolygon = (type == Ptcl::BillboardType::PolygonXY || type == Ptcl::BillboardType::PolygonXZ); - mProps.isVelLook = (type == Ptcl::BillboardType::VelLook || type == Ptcl::BillboardType::VelLookPolygon); +void EmitterWidget::BasicPropertiesWidget::setBillboardType(Ptcl::BillboardType type, const QString& label, const QString& key) { + setEmitterProperty( + label, + key, + &Ptcl::Emitter::billboardType, + &Ptcl::Emitter::setBillboardType, + type + ); } bool EmitterWidget::BasicPropertiesWidget::isBillboardAllowedForShape(Ptcl::BillboardType billboard, ShapeType shape) { @@ -181,7 +218,43 @@ EmitterWidget::BasicPropertiesWidget::ShapeType EmitterWidget::BasicPropertiesWi return ShapeType::Particle; } -void EmitterWidget::BasicPropertiesWidget::setProperties(const Ptcl::Emitter::BasicProperties& properties) { +void EmitterWidget::BasicPropertiesWidget::setDocument(Ptcl::Document* document) { + mDocument = document; + mDocument->disconnect(); + connect(mDocument, &Ptcl::Document::emitterChanged, this, &BasicPropertiesWidget::onEmitterChanged); +} + +void EmitterWidget::BasicPropertiesWidget::setSelection(Ptcl::Selection* selection) { + mSelection = selection; + + mSelection->disconnect(); + connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { + if (!mDocument) { + mEmitter = nullptr; + setEnabled(false); + return; + } + + mEmitter = mDocument->emitter(setIndex, emitterIndex); + + setEnabled(true); + populateProperties(); + }); +} + +void EmitterWidget::BasicPropertiesWidget::onEmitterChanged(s32 setIndex, s32 emitterIndex) { + if (!mEmitter) { + return; + } + + if (setIndex != mSelection->emitterSetIndex() || emitterIndex != mSelection->emitterIndex()) { + return; + } + + populateProperties(); +} + +void EmitterWidget::BasicPropertiesWidget::populateProperties() { QSignalBlocker b1(mNameLineEdit); QSignalBlocker b2(mTypeComboBox); QSignalBlocker b3(mRandomSeedMode); @@ -192,27 +265,25 @@ void EmitterWidget::BasicPropertiesWidget::setProperties(const Ptcl::Emitter::Ba QSignalBlocker b8(mStripeTypeComboBox); QSignalBlocker b9(mIsBillboardMtxCheckBox); - mProps = properties; - - mNameLineEdit.setText(mProps.name); - mTypeComboBox.setCurrentEnum(mProps.type); + mNameLineEdit.setText(mEmitter->name()); + mTypeComboBox.setCurrentEnum(mEmitter->type()); - auto& randomSeed = mProps.randomSeed; + auto& randomSeed = mEmitter->randomSeed(); auto seedMode = randomSeed.mode(); - int index = mRandomSeedMode.findData(static_cast(seedMode)); + s32 index = mRandomSeedMode.findData(static_cast(seedMode)); if (index != -1) { mRandomSeedMode.setCurrentIndex(index); } mRandomSeedSpinBox.setValue(randomSeed.constantSeed()); mRandomSeedSpinBox.setEnabled(seedMode == PtclSeed::Mode::ConstantSeed); - mFollowTypeComboBox.setCurrentEnum(mProps.followType); + mFollowTypeComboBox.setCurrentEnum(mEmitter->followType()); - const auto shapeType = shapeFromBillboard(mProps.billboardType); + const auto shapeType = shapeFromBillboard(mEmitter->billboardType()); const s32 shapeIndex = mShapeComboBox.findData(QVariant::fromValue(shapeType)); mShapeComboBox.setCurrentIndex(shapeIndex); - mIsBillboardMtxCheckBox.setChecked(mProps.isEmitterBillboardMtx); + mIsBillboardMtxCheckBox.setChecked(mEmitter->isEmitterBillboardMtx()); syncBillboardSelection(); updateBillboardRowVisibility(); @@ -220,7 +291,7 @@ void EmitterWidget::BasicPropertiesWidget::setProperties(const Ptcl::Emitter::Ba } void EmitterWidget::BasicPropertiesWidget::updateShapeRowVisibility() { - if (mProps.type == Ptcl::EmitterType::Simple) { + if (mEmitter->type() == Ptcl::EmitterType::Simple) { mMainLayout->setRowVisible(&mShapeComboBox, false); } else { mMainLayout->setRowVisible(&mShapeComboBox, true); @@ -237,7 +308,7 @@ void EmitterWidget::BasicPropertiesWidget::syncBillboardSelection() { QSignalBlocker b1(mBillboardComboBox); QSignalBlocker b2(mStripeTypeComboBox); - const QVariant value = QVariant::fromValue(mProps.billboardType); + const QVariant value = QVariant::fromValue(mEmitter->billboardType()); const s32 billboardIndex = mBillboardComboBox.findData(value); if (billboardIndex != -1) { @@ -250,6 +321,13 @@ void EmitterWidget::BasicPropertiesWidget::syncBillboardSelection() { } } +QString EmitterWidget::BasicPropertiesWidget::formatLabel(const QString& label) const { + return QString("Set %1, Emitter %2 - %3") + .arg(mSelection->emitterSetIndex()) + .arg(mSelection->emitterIndex()) + .arg(label); +} + // ========================================================================== // diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index f3e9f99..1fcaa0a 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -252,7 +252,7 @@ void EmitterWidget::setupConnections() { connect(mFluctuationEditorWidget, &FluctuationEditorWidget::flagsUpdated, this, [this](const BitFlag& fluxFlags) { if (!mEmitter) { return; } mEmitter->setFluctuationFlags(fluxFlags); - complexFlagsChanged(); + emit complexFlagsChanged(); emit propertiesChanged(); }); @@ -260,7 +260,7 @@ void EmitterWidget::setupConnections() { connect(mFieldEditorWidget, &FieldEditorWidget::flagsUpdated, this, [this](const BitFlag& fieldFlags) { if (!mEmitter) { return; } mEmitter->setFieldFlags(fieldFlags); - complexFlagsChanged(); + emit complexFlagsChanged(); emit propertiesChanged(); }); @@ -274,7 +274,7 @@ void EmitterWidget::setupConnections() { connect(mStripeEditorWidget, &StripeEditorWidget::flagsUpdated, this, [this](const BitFlag& stripeFlags) { if (!mEmitter) { return; } mEmitter->setStripeFlags(stripeFlags); - complexFlagsChanged(); + emit complexFlagsChanged(); emit propertiesChanged(); }); @@ -282,11 +282,15 @@ void EmitterWidget::setupConnections() { void EmitterWidget::setDocument(Ptcl::Document* document) { mDocument = document; + + mBasicProperties->setDocument(document); } void EmitterWidget::setSelection(Ptcl::Selection* selection) { mSelection = selection; + mBasicProperties->setSelection(selection); + connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { mEmitter = nullptr; @@ -343,7 +347,6 @@ void EmitterWidget::populateProperties() { QSignalBlocker b17(mFieldEditorWidget); QSignalBlocker b18(mStripeEditorWidget); - mBasicProperties->setProperties(mEmitter->basicProperties()); mGravityProperties->setProperties(mEmitter->gravityProperties()); mTransformProperties->setProperties(mEmitter->transformProperties()); mLifespanProperties->setProperties(mEmitter->lifespanProperties()); diff --git a/src/editor/mainWindow.cpp b/src/editor/mainWindow.cpp index 28fed22..6593671 100644 --- a/src/editor/mainWindow.cpp +++ b/src/editor/mainWindow.cpp @@ -68,10 +68,16 @@ void MainWindow::setupUi() { mTopSplitter->setStretchFactor(0, 0); mTopSplitter->setStretchFactor(1, 1); + // Bottom Container + auto* bottomContainer = new QWidget(this); + auto* bottomLayout = new QHBoxLayout(bottomContainer); + bottomLayout->addWidget(&mUndoView, 0); + bottomLayout->addWidget(&mTextureWidget, 1); + // Bottom Splitter mBottomSplitter = new QSplitter(Qt::Vertical, this); mBottomSplitter->addWidget(mTopSplitter); - mBottomSplitter->addWidget(&mTextureWidget); + mBottomSplitter->addWidget(bottomContainer); mBottomSplitter->setStretchFactor(0, 1); mBottomSplitter->setStretchFactor(1, 0); @@ -94,6 +100,9 @@ void MainWindow::setupUi() { setCentralWidget(mBottomSplitter); setupMenus(); + // Undo View + mUndoView.setEmptyLabel(""); + // StatusBar auto* status = statusBar(); mStatusLabel = new QLabel("No file loaded", status); @@ -199,6 +208,16 @@ void MainWindow::setupMenus() { mSaveAsAction.setEnabled(false); connect(&mSaveAsAction, &QAction::triggered, this, &MainWindow::saveFileAs); + // Undo + mUndoAction = new QAction("Undo", this); + mUndoAction->setShortcut(QKeySequence::Undo); + mUndoAction->setEnabled(false); + + // Redo + mRedoAction = new QAction("Redo", this); + mRedoAction->setShortcut(QKeySequence::Redo); + mRedoAction->setEnabled(false); + // Recent Files Menu mRecentFilesMenu.setTitle("Recent Files"); mRecentFilesMenu.setIcon(QIcon(":/res/icons/recent.png")); @@ -220,8 +239,14 @@ void MainWindow::setupMenus() { mFileMenu.addSeparator(); mFileMenu.addMenu(&mRecentFilesMenu); + // Edit Menu + mEditMenu.setTitle("Edit"); + mEditMenu.addAction(mUndoAction); + mEditMenu.addAction(mRedoAction); + // Menu Bar menuBar()->addMenu(&mFileMenu); + menuBar()->addMenu(&mEditMenu); } void MainWindow::closeEvent(QCloseEvent* event) { @@ -405,9 +430,11 @@ void MainWindow::loadPtclRes(const QString& path) { mDocument = std::make_unique(); if (!mDocument->load(path)) { mDocument.reset(); + bindUndoStack(); return; } + bindUndoStack(); setDirty(false); SettingsUtil::SettingsMgr::instance().addRecentFile(path); @@ -520,6 +547,38 @@ void MainWindow::setDirty(bool dirty) { updateWindowTitle(); } +void MainWindow::bindUndoStack() { + if (!mDocument) { + mUndoAction->setEnabled(false); + mRedoAction->setEnabled(false); + return; + } + + auto* stack = mDocument->undoStack(); + + mUndoAction->disconnect(); + mRedoAction->disconnect(); + + connect(mUndoAction, &QAction::triggered, stack, &QUndoStack::undo); + connect(mRedoAction, &QAction::triggered, stack, &QUndoStack::redo); + + connect(stack, &QUndoStack::canUndoChanged, mUndoAction, &QAction::setEnabled); + connect(stack, &QUndoStack::canRedoChanged, mRedoAction, &QAction::setEnabled); + + connect(stack, &QUndoStack::undoTextChanged, this, [this](const QString& text) { + mUndoAction->setText(text.isEmpty() ? "Undo" : "Undo " + text); + }); + + connect(stack, &QUndoStack::redoTextChanged, this, [this](const QString& text) { + mRedoAction->setText(text.isEmpty() ? "Redo" : "Redo " + text); + }); + + mUndoAction->setEnabled(stack->canUndo()); + mRedoAction->setEnabled(stack->canRedo()); + + mUndoView.setStack(stack); +} + // ========================================================================== // diff --git a/src/ptcl/ptcl.cpp b/src/ptcl/ptcl.cpp index 12e35ed..70ea7a4 100644 --- a/src/ptcl/ptcl.cpp +++ b/src/ptcl/ptcl.cpp @@ -276,7 +276,7 @@ void PtclBinaryWriter::buildHeader(const PtclRes& res) { mHeader = { .magic = {'S', 'P', 'B', 'D'}, .version = 11, - .numEmitterSet = res.emitterSetCount(), + .numEmitterSet = static_cast(res.emitterSetCount()), .namePos = appendName(res.name()), .nameTblPos = 0, .textureTblPos = 0, diff --git a/src/ptcl/ptclCommand.cpp b/src/ptcl/ptclCommand.cpp new file mode 100644 index 0000000..3eec9eb --- /dev/null +++ b/src/ptcl/ptclCommand.cpp @@ -0,0 +1,25 @@ +#include "ptcl/ptclCommand.h" +#include "ptcl/ptclDocument.h" + +namespace Ptcl { + + +// ========================================================================== // + +template +Emitter& SetEmitterPropertyCommand::getEmitter() const { + return *mDocument->emitter(mSetIndex, mEmitterIndex); +} + +template +void SetEmitterPropertyCommand::apply(const T& value) { + auto& emitter = getEmitter(); + mSetter(emitter, value); + mDocument->notifyEmitterChanged(mSetIndex, mEmitterIndex); +} + + +// ========================================================================== // + + +} // namespace Ptcl diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index e7395d9..83c3b52 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -33,10 +33,6 @@ std::unique_ptr Emitter::clone() const { return newEmitter; } -EmitterType Emitter::type() const { - return mBasicProperties.type; -} - BitFlag& Emitter::flags() { return mFlag; } From 757097ce783ac4629c7f28888a3693fa5c2fdaf1 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Thu, 5 Mar 2026 01:28:34 +0000 Subject: [PATCH 04/35] feat(ui): implement undo/redo for Emitter gravityProperties --- CMakeLists.txt | 2 + .../emitterWidget/basicPropertiesWidget.h | 35 +-------- include/editor/emitterWidget/emitterWidget.h | 4 +- .../editor/emitterWidget/emitterWidgetBase.h | 51 +++++++++++++ .../emitterWidget/gravityPropertiesWidget.h | 16 ++-- include/math/vector.h | 9 +++ include/ptcl/ptclEmitter.h | 22 ++++-- .../emitterWidget/basicPropertiesWidget.cpp | 62 +++------------- src/editor/emitterWidget/emitterWidget.cpp | 36 +-------- .../emitterWidget/emitterWidgetBase.cpp | 73 +++++++++++++++++++ .../emitterWidget/gravityPropertiesWidget.cpp | 37 +++++++--- src/ptcl/ptclBinary.cpp | 20 ++--- src/ptcl/ptclEmitter.cpp | 16 ---- 13 files changed, 210 insertions(+), 173 deletions(-) create mode 100644 include/editor/emitterWidget/emitterWidgetBase.h create mode 100644 src/editor/emitterWidget/emitterWidgetBase.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6460509..f9ed727 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ set(PROJECT_SOURCES src/editor/emitterWidget/combinerPropertiesWidget.cpp src/editor/emitterWidget/emissionPropertiesWidget.cpp src/editor/emitterWidget/emitterWidget.cpp + src/editor/emitterWidget/emitterWidgetBase.cpp src/editor/emitterWidget/gravityPropertiesWidget.cpp src/editor/emitterWidget/lifespanPropertiesWidget.cpp src/editor/emitterWidget/rotationPropertiesWidget.cpp @@ -127,6 +128,7 @@ set(PROJECT_HEADERS include/editor/emitterWidget/combinerPropertiesWidget.h include/editor/emitterWidget/emissionPropertiesWidget.h include/editor/emitterWidget/emitterWidget.h + include/editor/emitterWidget/emitterWidgetBase.h include/editor/emitterWidget/gravityPropertiesWidget.h include/editor/emitterWidget/lifespanPropertiesWidget.h include/editor/emitterWidget/rotationPropertiesWidget.h diff --git a/include/editor/emitterWidget/basicPropertiesWidget.h b/include/editor/emitterWidget/basicPropertiesWidget.h index f44bbed..ca0411a 100644 --- a/include/editor/emitterWidget/basicPropertiesWidget.h +++ b/include/editor/emitterWidget/basicPropertiesWidget.h @@ -2,9 +2,7 @@ #include "editor/components/enumComboBox.h" #include "editor/components/sizedSpinBox.h" -#include "editor/emitterWidget/emitterWidget.h" - -#include "ptcl/ptclEmitter.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include #include @@ -18,7 +16,7 @@ namespace PtclEditor { // ========================================================================== // -class EmitterWidget::BasicPropertiesWidget final : public QWidget { +class BasicPropertiesWidget final : public EmitterWidgetBase { Q_OBJECT private: static constexpr std::array sBillboardTypes{ @@ -44,16 +42,9 @@ class EmitterWidget::BasicPropertiesWidget final : public QWidget { public: explicit BasicPropertiesWidget(QWidget* parent = nullptr); - void setDocument(Ptcl::Document* document); - void setSelection(Ptcl::Selection* selection); - -signals: - void propertiesUpdated(const Ptcl::Emitter::BasicProperties& properties); - void emitterTypeChanged(); - void emitterNameChanged(); - private: - void populateProperties(); + void populateProperties() final; + void setupConnections(); void updateShapeRowVisibility(); void updateBillboardRowVisibility(); @@ -61,28 +52,10 @@ class EmitterWidget::BasicPropertiesWidget final : public QWidget { void setBillboardType(Ptcl::BillboardType type, const QString& label, const QString& key); - QString formatLabel(const QString& label) const; - - template - void setEmitterProperty(const QString& label, QString key, Getter getter, Setter setter, const T& value) { - if (!mDocument || !mSelection) { - return; - } - - mDocument->setEmitterProperty(mSelection->emitterSetIndex(), mSelection->emitterIndex(), formatLabel(label), key, getter, setter, value); - } - static bool isBillboardAllowedForShape(Ptcl::BillboardType billboard, ShapeType shape); static ShapeType shapeFromBillboard(Ptcl::BillboardType type); -private slots: - void onEmitterChanged(s32 setIndex, s32 emitterIndex); - private: - Ptcl::Document* mDocument{nullptr}; - const Ptcl::Selection* mSelection{nullptr}; - Ptcl::Emitter* mEmitter{nullptr}; - QFormLayout* mMainLayout{nullptr}; QLineEdit mNameLineEdit{}; diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index 999d8b4..ad76c08 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -22,6 +22,8 @@ namespace PtclEditor { +class BasicPropertiesWidget; +class GravityPropertiesWidget; // ========================================================================== // @@ -48,8 +50,6 @@ class EmitterWidget final : public QWidget { void populateProperties(); private: - class BasicPropertiesWidget; - class GravityPropertiesWidget; class TransformPropertiesWidget; class LifespanPropertiesWidget; class TerminationPropertiesWidget; diff --git a/include/editor/emitterWidget/emitterWidgetBase.h b/include/editor/emitterWidget/emitterWidgetBase.h new file mode 100644 index 0000000..17f8aab --- /dev/null +++ b/include/editor/emitterWidget/emitterWidgetBase.h @@ -0,0 +1,51 @@ +#pragma once + +#include "ptcl/ptclEmitter.h" +#include "ptcl/ptclDocument.h" + +#include +#include + + +namespace PtclEditor { + + +// ========================================================================== // + + +class EmitterWidgetBase : public QWidget { + Q_OBJECT +public: + explicit EmitterWidgetBase(QWidget* parent = nullptr); + + virtual void setDocument(Ptcl::Document* document); + virtual void setSelection(Ptcl::Selection* selection); + +protected: + virtual void populateProperties() = 0; + + QString formatLabel(const QString& label) const; + + template + void setEmitterProperty(const QString& label, QString key, Getter getter, Setter setter, const T& value) { + if (!mDocument || !mSelection) { + return; + } + + mDocument->setEmitterProperty(mSelection->emitterSetIndex(), mSelection->emitterIndex(), formatLabel(label), key, getter, setter, value); + } + +protected slots: + virtual void onEmitterChanged(s32 setIndex, s32 emitterIndex); + +protected: + Ptcl::Document* mDocument{nullptr}; + const Ptcl::Selection* mSelection{nullptr}; + Ptcl::Emitter* mEmitter{nullptr}; +}; + + +// ========================================================================== // + + +} // namespace PtclEditor diff --git a/include/editor/emitterWidget/gravityPropertiesWidget.h b/include/editor/emitterWidget/gravityPropertiesWidget.h index b32eb1d..abbf4c4 100644 --- a/include/editor/emitterWidget/gravityPropertiesWidget.h +++ b/include/editor/emitterWidget/gravityPropertiesWidget.h @@ -1,9 +1,7 @@ #pragma once #include "editor/components/vectorSpinBox.h" -#include "editor/emitterWidget/emitterWidget.h" - -#include "ptcl/ptclEmitter.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include #include @@ -15,22 +13,18 @@ namespace PtclEditor { // ========================================================================== // -class EmitterWidget::GravityPropertiesWidget final : public QWidget { +class GravityPropertiesWidget final : public EmitterWidgetBase { Q_OBJECT public: explicit GravityPropertiesWidget(QWidget* parent = nullptr); - void setProperties(const Ptcl::Emitter::GravityProperties& properties); - -signals: - void propertiesUpdated(const Ptcl::Emitter::GravityProperties& properties); - private: - Ptcl::Emitter::GravityProperties mProps{}; + void populateProperties() final; + void setupConnections(); +private: QCheckBox mIsDirectionalCheckBox{}; VectorSpinBox mGravitySpinBox{}; - }; diff --git a/include/math/vector.h b/include/math/vector.h index c829b55..b221a2f 100644 --- a/include/math/vector.h +++ b/include/math/vector.h @@ -24,9 +24,12 @@ class Vector2 { Vector2(const Vector2&) = default; Vector2(Vector2&&) = default; + Vector2& operator=(const Vector2&) = default; Vector2& operator=(Vector2&&) = default; + bool operator==(const Vector2&) const = default; + T& operator[](std::size_t i) { assert(i < sAxisCount); return (&mX)[i]; @@ -71,9 +74,12 @@ class Vector3 { Vector3(const Vector3&) = default; Vector3(Vector3&&) = default; + Vector3& operator=(const Vector3&) = default; Vector3& operator=(Vector3&&) = default; + bool operator==(const Vector3&) const = default; + T& operator[](std::size_t i) { assert(i < sAxisCount); return (&mX)[i]; @@ -121,9 +127,12 @@ class Vector4 { Vector4(const Vector4&) = default; Vector4(Vector4&&) = default; + Vector4& operator=(const Vector4&) = default; Vector4& operator=(Vector4&&) = default; + bool operator==(const Vector4&) const = default; + T& operator[](std::size_t i) { assert(i < sAxisCount); return (&mX)[i]; diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index aca347a..67e3e34 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -186,6 +186,8 @@ class Emitter { std::unique_ptr clone() const; + // Basic Properties + EmitterType type() const { return mBasicProperties.type; } void setType(EmitterType type) { mBasicProperties.type = type; } @@ -198,7 +200,7 @@ class Emitter { } const QString& name() const; - void setName(const QString &name); + void setName(const QString& name); const PtclSeed& randomSeed() const { return mBasicProperties.randomSeed; } void setRandomSeed(PtclSeed seed) { mBasicProperties.randomSeed = seed; } @@ -213,6 +215,18 @@ class Emitter { bool isEmitterBillboardMtx() const { return mBasicProperties.isEmitterBillboardMtx; } void setIsEmitterBillboardMtx(bool isEmitterBillboardMtx) { mBasicProperties.isEmitterBillboardMtx = isEmitterBillboardMtx; } + bool isPolygon() const { return mBasicProperties.isPolygon; } + bool isFollow() const { return mBasicProperties.isFollow; } + bool isVelLook() const { return mBasicProperties.isVelLook; } + + // Gravity Properties + + bool isDirectional() const { return mGravityProperties.isDirectional; } + void setDirectional(bool isDirectional) { mGravityProperties.isDirectional = isDirectional; } + + const Math::Vector3f& gravity() const { return mGravityProperties.gravity; } + void setGravity(const Math::Vector3f& gravity) { mGravityProperties.gravity = gravity; } + BitFlag& flags(); const BitFlag& flags() const; @@ -220,12 +234,6 @@ class Emitter { const TextureHandle& textureHandle() const; void setTexture(const std::shared_ptr& texture); - const BasicProperties &basicProperties() const; - void setBasicProperties(const BasicProperties &basicProperties); - - const GravityProperties& gravityProperties() const; - void setGravityProperties(const GravityProperties& gravityProperties); - const LifespanProperties& lifespanProperties() const; void setLifespanProperties(const LifespanProperties& lifespanProperties); diff --git a/src/editor/emitterWidget/basicPropertiesWidget.cpp b/src/editor/emitterWidget/basicPropertiesWidget.cpp index cde4074..834bbd9 100644 --- a/src/editor/emitterWidget/basicPropertiesWidget.cpp +++ b/src/editor/emitterWidget/basicPropertiesWidget.cpp @@ -9,8 +9,8 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::BasicPropertiesWidget::BasicPropertiesWidget(QWidget* parent) : - QWidget{parent} { +BasicPropertiesWidget::BasicPropertiesWidget(QWidget* parent) : + EmitterWidgetBase{parent} { for (Ptcl::BillboardType type : sBillboardTypes) { mBillboardComboBox.addItem(Ptcl::toString(type), QVariant::fromValue(type)); @@ -57,7 +57,7 @@ EmitterWidget::BasicPropertiesWidget::BasicPropertiesWidget(QWidget* parent) : setupConnections(); } -void EmitterWidget::BasicPropertiesWidget::setupConnections() { +void BasicPropertiesWidget::setupConnections() { // Emitter Name connect(&mNameLineEdit, &QLineEdit::textChanged, this, [this](const QString& text) { setEmitterProperty( @@ -181,7 +181,7 @@ void EmitterWidget::BasicPropertiesWidget::setupConnections() { }); } -void EmitterWidget::BasicPropertiesWidget::setBillboardType(Ptcl::BillboardType type, const QString& label, const QString& key) { +void BasicPropertiesWidget::setBillboardType(Ptcl::BillboardType type, const QString& label, const QString& key) { setEmitterProperty( label, key, @@ -191,7 +191,7 @@ void EmitterWidget::BasicPropertiesWidget::setBillboardType(Ptcl::BillboardType ); } -bool EmitterWidget::BasicPropertiesWidget::isBillboardAllowedForShape(Ptcl::BillboardType billboard, ShapeType shape) { +bool BasicPropertiesWidget::isBillboardAllowedForShape(Ptcl::BillboardType billboard, ShapeType shape) { switch (shape) { case ShapeType::Particle: return billboard != Ptcl::BillboardType::Stripe && @@ -206,7 +206,7 @@ bool EmitterWidget::BasicPropertiesWidget::isBillboardAllowedForShape(Ptcl::Bill return false; } -EmitterWidget::BasicPropertiesWidget::ShapeType EmitterWidget::BasicPropertiesWidget::shapeFromBillboard(Ptcl::BillboardType type) { +BasicPropertiesWidget::ShapeType BasicPropertiesWidget::shapeFromBillboard(Ptcl::BillboardType type) { if (type == Ptcl::BillboardType::Stripe || type == Ptcl::BillboardType::ComplexStripe) { return ShapeType::Stripe; } @@ -218,43 +218,7 @@ EmitterWidget::BasicPropertiesWidget::ShapeType EmitterWidget::BasicPropertiesWi return ShapeType::Particle; } -void EmitterWidget::BasicPropertiesWidget::setDocument(Ptcl::Document* document) { - mDocument = document; - mDocument->disconnect(); - connect(mDocument, &Ptcl::Document::emitterChanged, this, &BasicPropertiesWidget::onEmitterChanged); -} - -void EmitterWidget::BasicPropertiesWidget::setSelection(Ptcl::Selection* selection) { - mSelection = selection; - - mSelection->disconnect(); - connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { - if (!mDocument) { - mEmitter = nullptr; - setEnabled(false); - return; - } - - mEmitter = mDocument->emitter(setIndex, emitterIndex); - - setEnabled(true); - populateProperties(); - }); -} - -void EmitterWidget::BasicPropertiesWidget::onEmitterChanged(s32 setIndex, s32 emitterIndex) { - if (!mEmitter) { - return; - } - - if (setIndex != mSelection->emitterSetIndex() || emitterIndex != mSelection->emitterIndex()) { - return; - } - - populateProperties(); -} - -void EmitterWidget::BasicPropertiesWidget::populateProperties() { +void BasicPropertiesWidget::populateProperties() { QSignalBlocker b1(mNameLineEdit); QSignalBlocker b2(mTypeComboBox); QSignalBlocker b3(mRandomSeedMode); @@ -290,7 +254,7 @@ void EmitterWidget::BasicPropertiesWidget::populateProperties() { updateShapeRowVisibility(); } -void EmitterWidget::BasicPropertiesWidget::updateShapeRowVisibility() { +void BasicPropertiesWidget::updateShapeRowVisibility() { if (mEmitter->type() == Ptcl::EmitterType::Simple) { mMainLayout->setRowVisible(&mShapeComboBox, false); } else { @@ -298,13 +262,13 @@ void EmitterWidget::BasicPropertiesWidget::updateShapeRowVisibility() { } } -void EmitterWidget::BasicPropertiesWidget::updateBillboardRowVisibility() { +void BasicPropertiesWidget::updateBillboardRowVisibility() { const auto shape = mShapeComboBox.currentData().value(); mMainLayout->setRowVisible(&mBillboardComboBox, shape == ShapeType::Particle); mMainLayout->setRowVisible(&mStripeTypeComboBox, shape == ShapeType::Stripe); } -void EmitterWidget::BasicPropertiesWidget::syncBillboardSelection() { +void BasicPropertiesWidget::syncBillboardSelection() { QSignalBlocker b1(mBillboardComboBox); QSignalBlocker b2(mStripeTypeComboBox); @@ -321,12 +285,6 @@ void EmitterWidget::BasicPropertiesWidget::syncBillboardSelection() { } } -QString EmitterWidget::BasicPropertiesWidget::formatLabel(const QString& label) const { - return QString("Set %1, Emitter %2 - %3") - .arg(mSelection->emitterSetIndex()) - .arg(mSelection->emitterIndex()) - .arg(label); -} // ========================================================================== // diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index 1fcaa0a..4a6822c 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -113,27 +113,6 @@ void EmitterWidget::setupStandardLayout(QVBoxLayout* mainLayout) { } void EmitterWidget::setupConnections() { - // Basic Properties - connect(mBasicProperties, &BasicPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::BasicProperties& properties) { - if (!mEmitter) { return; } - mEmitter->setBasicProperties(properties); - updateStripeVisibility(); - emit propertiesChanged(); - }); - - connect(mBasicProperties, &BasicPropertiesWidget::emitterTypeChanged, this, [this]() { - if (!mEmitter) { return; } - updateStripeVisibility(); - emit emitterTypeChanged(); - emit propertiesChanged(); - }); - - connect(mBasicProperties, &BasicPropertiesWidget::emitterNameChanged, this, [this]() { - if (!mEmitter) { return; } - emit emitterNameChanged(); - emit propertiesChanged(); - }); - // Texture Properties connect(mTextureProperties, &TexturePropertiesWidget::textureUpdated, this, [this](const std::shared_ptr& oldTexture, const std::shared_ptr& newTexture) { if (!mEmitter) { return; } @@ -148,13 +127,6 @@ void EmitterWidget::setupConnections() { emit propertiesChanged(); }); - // Gravity Properties - connect(mGravityProperties, &GravityPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::GravityProperties& properties) { - if (!mEmitter) { return; } - mEmitter->setGravityProperties(properties); - emit propertiesChanged(); - }); - // Lifespan Properties connect(mLifespanProperties, &LifespanPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::LifespanProperties& properties) { if (!mEmitter) { return; } @@ -284,12 +256,14 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mDocument = document; mBasicProperties->setDocument(document); + mGravityProperties->setDocument(document); } void EmitterWidget::setSelection(Ptcl::Selection* selection) { mSelection = selection; mBasicProperties->setSelection(selection); + mGravityProperties->setSelection(selection); connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -328,8 +302,6 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { } void EmitterWidget::populateProperties() { - QSignalBlocker b1(mBasicProperties); - QSignalBlocker b2(mGravityProperties); QSignalBlocker b3(mTransformProperties); QSignalBlocker b4(mLifespanProperties); QSignalBlocker b5(mTerminationProperties); @@ -341,13 +313,11 @@ void EmitterWidget::populateProperties() { QSignalBlocker b11(mRotationProperties); QSignalBlocker b12(mTextureProperties); QSignalBlocker b13(mCombinerProperties); - QSignalBlocker b14(mBasicProperties); QSignalBlocker b15(mChildEditorWidget); QSignalBlocker b16(mFluctuationEditorWidget); QSignalBlocker b17(mFieldEditorWidget); QSignalBlocker b18(mStripeEditorWidget); - mGravityProperties->setProperties(mEmitter->gravityProperties()); mTransformProperties->setProperties(mEmitter->transformProperties()); mLifespanProperties->setProperties(mEmitter->lifespanProperties()); mTerminationProperties->setProperties(mEmitter->terminationProperties()); @@ -378,7 +348,7 @@ void EmitterWidget::updateStripeVisibility() { } const auto eType = mEmitter->type(); - const auto bbType = mEmitter->basicProperties().billboardType; + const auto bbType = mEmitter->billboardType(); const bool show = (eType == Ptcl::EmitterType::Complex || eType == Ptcl::EmitterType::Compact) && (bbType == Ptcl::BillboardType::Stripe || bbType == Ptcl::BillboardType::ComplexStripe); diff --git a/src/editor/emitterWidget/emitterWidgetBase.cpp b/src/editor/emitterWidget/emitterWidgetBase.cpp new file mode 100644 index 0000000..deea4e3 --- /dev/null +++ b/src/editor/emitterWidget/emitterWidgetBase.cpp @@ -0,0 +1,73 @@ +#include "editor/emitterWidget/emitterWidgetBase.h" + +#include + + +namespace PtclEditor { + + +// ========================================================================== // + + +EmitterWidgetBase::EmitterWidgetBase(QWidget* parent) : + QWidget{parent} {} + +void EmitterWidgetBase::setDocument(Ptcl::Document* document) { + if (mDocument) { + mDocument->disconnect(this); + } + + mDocument = document; + + if (mDocument) { + connect(mDocument, &Ptcl::Document::emitterChanged, this, &EmitterWidgetBase::onEmitterChanged); + } +} + +void EmitterWidgetBase::setSelection(Ptcl::Selection* selection) { + if (mSelection) { + mSelection->disconnect(this); + } + + mSelection = selection; + + if (mSelection) { + connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { + if (!mDocument) { + mEmitter = nullptr; + setEnabled(false); + return; + } + + mEmitter = mDocument->emitter(setIndex, emitterIndex); + + setEnabled(true); + populateProperties(); + }); + } +} + +QString EmitterWidgetBase::formatLabel(const QString& label) const { + return QString("Set %1, Emitter %2 - %3") + .arg(mSelection->emitterSetIndex()) + .arg(mSelection->emitterIndex()) + .arg(label); +} + + +void EmitterWidgetBase::onEmitterChanged(s32 setIndex, s32 emitterIndex) { + if (!mEmitter) { + return; + } + + if (setIndex != mSelection->emitterSetIndex() || emitterIndex != mSelection->emitterIndex()) { + return; + } + + populateProperties(); +} + +// ========================================================================== // + + +} // namespace PtclEditor diff --git a/src/editor/emitterWidget/gravityPropertiesWidget.cpp b/src/editor/emitterWidget/gravityPropertiesWidget.cpp index fea750c..a2e02c8 100644 --- a/src/editor/emitterWidget/gravityPropertiesWidget.cpp +++ b/src/editor/emitterWidget/gravityPropertiesWidget.cpp @@ -9,8 +9,8 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::GravityPropertiesWidget::GravityPropertiesWidget(QWidget* parent) : - QWidget{parent} { +GravityPropertiesWidget::GravityPropertiesWidget(QWidget* parent) : + EmitterWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); @@ -19,25 +19,40 @@ EmitterWidget::GravityPropertiesWidget::GravityPropertiesWidget(QWidget* parent) mainLayout->addRow("Coordinates:", &mIsDirectionalCheckBox); mainLayout->addRow("Gravity Direction:", &mGravitySpinBox); + setupConnections(); +} + +void GravityPropertiesWidget::setupConnections() { + // Is Directional connect(&mIsDirectionalCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - mProps.isDirectional = checked; - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Gravity Directional", + "GravDirectional", + &Ptcl::Emitter::isDirectional, + &Ptcl::Emitter::setDirectional, + checked + ); }); + // Gravity connect(&mGravitySpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mProps.gravity = mGravitySpinBox.getVector(); - emit propertiesUpdated(mProps); + const auto gravity = mGravitySpinBox.getVector(); + setEmitterProperty( + "Set Gravity Direction", + "GravDir", + &Ptcl::Emitter::gravity, + &Ptcl::Emitter::setGravity, + gravity + ); }); } -void EmitterWidget::GravityPropertiesWidget::setProperties(const Ptcl::Emitter::GravityProperties& properties) { +void GravityPropertiesWidget::populateProperties() { QSignalBlocker b1(mIsDirectionalCheckBox); QSignalBlocker b2(mGravitySpinBox); - mProps = properties; - - mIsDirectionalCheckBox.setChecked(mProps.isDirectional); - mGravitySpinBox.setVector(mProps.gravity); + mIsDirectionalCheckBox.setChecked(mEmitter->isDirectional()); + mGravitySpinBox.setVector(mEmitter->gravity()); } diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index 009e0a8..080f4bb 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -312,9 +312,9 @@ void BinTextureRes::printData(u32 indentationLevel) { BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { - type = emitter.basicProperties().type; + type = emitter.type(); flag = emitter.flags(); - randomSeed = emitter.basicProperties().randomSeed.raw(); + randomSeed = emitter.randomSeed().raw(); namePos = 0; // To be assigned after construction... namePtr = 0; @@ -331,12 +331,12 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { textureSize = 0; // To be assigned after construction... texturePos = 0; // To be assigned after construction... textureHandlePtr = 0; - isPolygon = emitter.basicProperties().isPolygon; - isFollow = emitter.basicProperties().isFollow; - isEmitterBillboardMtx = emitter.basicProperties().isEmitterBillboardMtx; - isDirectional = emitter.gravityProperties().isDirectional; + isPolygon = emitter.isPolygon(); + isFollow = emitter.isFollow(); + isEmitterBillboardMtx = emitter.isEmitterBillboardMtx(); + isDirectional = emitter.isDirectional(); isTexPatAnim = emitter.textureProperties().isTexPatAnim; - isVelLook = emitter.basicProperties().isVelLook; + isVelLook = emitter.isVelLook(); volumeTblIndex = emitter.volumeProperties().volumeTblIndex; isStopEmitInFade = emitter.terminationProperties().isStopEmitInFade; volumeType = emitter.volumeProperties().volumeType; @@ -359,9 +359,9 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { ptclLifeRnd = emitter.lifespanProperties().ptclLifeRnd; airResistance = emitter.velocityProperties().airResistance; blendFunc = emitter.combinerProperties().blendFunc; - billboardType = emitter.basicProperties().billboardType; + billboardType = emitter.billboardType(); depthFunc = emitter.combinerProperties().depthFunc; - gravity = emitter.gravityProperties().gravity; + gravity = emitter.gravity(); color0 = emitter.colorProperties().color0; color1 = emitter.colorProperties().color1; colorSection1 = emitter.colorProperties().colorSection1; @@ -382,7 +382,7 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { scaleRand = emitter.scaleProperties().scaleRand; rotCalcType = static_cast(emitter.rotationProperties().rotType) + 5 * static_cast(emitter.colorProperties().colorCalcType); - followType = emitter.basicProperties().followType; + followType = emitter.followType(); colorCombinerFunc = emitter.combinerProperties().combinerFunc; initRot = emitter.rotationProperties().initRot; initRotRand = emitter.rotationProperties().initRotRand; diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index 83c3b52..353f8dd 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -61,22 +61,6 @@ void Emitter::setTexture(const std::shared_ptr& texture) { mTextureHandle.set(texture); } -const Emitter::BasicProperties& Emitter::basicProperties() const { - return mBasicProperties; -} - -void Emitter::setBasicProperties(const BasicProperties& basicProperties) { - mBasicProperties = basicProperties; -} - -const Emitter::GravityProperties& Emitter::gravityProperties() const { - return mGravityProperties; -} - -void Emitter::setGravityProperties(const GravityProperties& gravityProperties) { - mGravityProperties = gravityProperties; -} - const Emitter::TransformProperties& Emitter::transformProperties() const { return mTransformProperties; } From b0b2609c10184cf9cc074ba0a15ea52ff79fd71e Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Mon, 9 Mar 2026 02:18:41 +0000 Subject: [PATCH 05/35] feat(ui): implement undo/redo for Emitter transformProperties --- include/editor/emitterWidget/emitterWidget.h | 3 +- .../emitterWidget/transformPropertiesWidget.h | 22 ++--- include/ptcl/ptclEmitter.h | 44 +++++++++ src/editor/emitterWidget/emitterWidget.cpp | 11 +-- .../transformPropertiesWidget.cpp | 89 +++++++------------ 5 files changed, 89 insertions(+), 80 deletions(-) diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index ad76c08..8e174c9 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -24,6 +24,8 @@ namespace PtclEditor { class BasicPropertiesWidget; class GravityPropertiesWidget; +class TransformPropertiesWidget; + // ========================================================================== // @@ -50,7 +52,6 @@ class EmitterWidget final : public QWidget { void populateProperties(); private: - class TransformPropertiesWidget; class LifespanPropertiesWidget; class TerminationPropertiesWidget; class EmissionPropertiesWidget; diff --git a/include/editor/emitterWidget/transformPropertiesWidget.h b/include/editor/emitterWidget/transformPropertiesWidget.h index eb0a1ef..a6cf6a8 100644 --- a/include/editor/emitterWidget/transformPropertiesWidget.h +++ b/include/editor/emitterWidget/transformPropertiesWidget.h @@ -1,9 +1,7 @@ #pragma once #include "editor/components/vectorSpinBox.h" -#include "editor/emitterWidget/emitterWidget.h" - -#include "ptcl/ptclEmitter.h" +#include "editor/emitterWidget/emitterWidgetBase.h" namespace PtclEditor { @@ -12,25 +10,19 @@ namespace PtclEditor { // ========================================================================== // -class EmitterWidget::TransformPropertiesWidget final : public QWidget { +class TransformPropertiesWidget final : public EmitterWidgetBase { Q_OBJECT public: explicit TransformPropertiesWidget(QWidget* parent = nullptr); - void setProperties(const Ptcl::Emitter::TransformProperties& properties); - -signals: - void propertiesUpdated(const Ptcl::Emitter::TransformProperties& properties); - private: - void rebuildMatrices(); + void populateProperties() final; + void setupConnections(); private: - Ptcl::Emitter::TransformProperties mProps{}; - - VectorSpinBox mScaleSpinBox; - VectorSpinBox mRotationSpinBox; - VectorSpinBox mTranslationSpinBox; + VectorSpinBox mScaleSpinBox{}; + VectorSpinBox mRotationSpinBox{}; + VectorSpinBox mTranslationSpinBox{}; }; diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index 67e3e34..f50de21 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -1,6 +1,7 @@ #pragma once #include "math/matrix.h" +#include "math/util.h" #include "ptcl/ptclBinary.h" #include "ptcl/ptclChildData.h" @@ -227,6 +228,49 @@ class Emitter { const Math::Vector3f& gravity() const { return mGravityProperties.gravity; } void setGravity(const Math::Vector3f& gravity) { mGravityProperties.gravity = gravity; } + // Transform Properties + const Math::Matrix34f& transformRT() const { return mTransformProperties.transformRT; } + const Math::Matrix34f& transformSRT() const { return mTransformProperties.transformSRT; } + + void setTransform(const Math::Vector3f& rotation, const Math::Vector3f& translation, const Math::Vector3f& scale) { + const auto mtxR = Math::Util::eulerToRotationMatrix(rotation); + + Math::Matrix34f mtxRT; + for (s32 r = 0; r < 3; ++r) { + for (s32 c = 0; c < 3; ++c) { + mtxRT(r, c) = mtxR(r, c); + } + mtxRT(r, 3) = translation[r]; + } + + mTransformProperties.transformRT = mtxRT; + + Math::Matrix34f mtxSRT; + for (s32 r = 0; r < 3; ++r) { + mtxSRT(r, 0) = mtxRT(r, 0) * scale.getX(); + mtxSRT(r, 1) = mtxRT(r, 1) * scale.getY(); + mtxSRT(r, 2) = mtxRT(r, 2) * scale.getZ(); + mtxSRT(r, 3) = mtxRT(r, 3); + } + + mTransformProperties.transformSRT = mtxSRT; + } + + Math::Vector3f translation() const { return Math::Util::getTranslation(mTransformProperties.transformRT); } + void setTranslation(const Math::Vector3f& translation) { setTransform(rotation(), translation, scale()); } + + Math::Vector3f rotation() const { + auto rot = Math::Util::getRotationEuler(mTransformProperties.transformRT); + rot.setX(Math::Util::to180(rot.getX())); + rot.setY(Math::Util::to180(rot.getY())); + rot.setZ(Math::Util::to180(rot.getZ())); + return rot; + } + void setRotation(const Math::Vector3f& rotation) { setTransform(rotation, translation(), scale()); } + + Math::Vector3f scale() const { return Math::Util::getScale(mTransformProperties.transformSRT); } + void setScale(const Math::Vector3f& scale) { setTransform(rotation(), translation(), scale); } + BitFlag& flags(); const BitFlag& flags() const; diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index 4a6822c..f2d5461 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -162,13 +162,6 @@ void EmitterWidget::setupConnections() { emit propertiesChanged(); }); - // Transform Properties - connect(mTransformProperties, &TransformPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::TransformProperties& properties) { - if (!mEmitter) { return; } - mEmitter->setTransformProperties(properties); - emit propertiesChanged(); - }); - // Color Properties connect(mColorProperties, &ColorPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::ColorProperties& properties) { if (!mEmitter) { return; } @@ -257,6 +250,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mBasicProperties->setDocument(document); mGravityProperties->setDocument(document); + mTransformProperties->setDocument(document); } void EmitterWidget::setSelection(Ptcl::Selection* selection) { @@ -264,6 +258,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mBasicProperties->setSelection(selection); mGravityProperties->setSelection(selection); + mTransformProperties->setSelection(selection); connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -302,7 +297,6 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { } void EmitterWidget::populateProperties() { - QSignalBlocker b3(mTransformProperties); QSignalBlocker b4(mLifespanProperties); QSignalBlocker b5(mTerminationProperties); QSignalBlocker b6(mEmissionProperties); @@ -318,7 +312,6 @@ void EmitterWidget::populateProperties() { QSignalBlocker b17(mFieldEditorWidget); QSignalBlocker b18(mStripeEditorWidget); - mTransformProperties->setProperties(mEmitter->transformProperties()); mLifespanProperties->setProperties(mEmitter->lifespanProperties()); mTerminationProperties->setProperties(mEmitter->terminationProperties()); mEmissionProperties->setProperties(mEmitter->emissionProperties()); diff --git a/src/editor/emitterWidget/transformPropertiesWidget.cpp b/src/editor/emitterWidget/transformPropertiesWidget.cpp index 33bf329..2f1fdde 100644 --- a/src/editor/emitterWidget/transformPropertiesWidget.cpp +++ b/src/editor/emitterWidget/transformPropertiesWidget.cpp @@ -1,7 +1,5 @@ #include "editor/emitterWidget/transformPropertiesWidget.h" -#include "math/util.h" - #include @@ -11,8 +9,8 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::TransformPropertiesWidget::TransformPropertiesWidget(QWidget* parent) : - QWidget{parent} { +TransformPropertiesWidget::TransformPropertiesWidget(QWidget* parent) : + EmitterWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); @@ -20,71 +18,52 @@ EmitterWidget::TransformPropertiesWidget::TransformPropertiesWidget(QWidget* par mainLayout->addRow("Rotation", &mRotationSpinBox); mainLayout->addRow("Scale", &mScaleSpinBox); + setupConnections(); +} + +void TransformPropertiesWidget::setupConnections() { connect(&mTranslationSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - rebuildMatrices(); - emit propertiesUpdated(mProps); + const auto translation = mTranslationSpinBox.getVector(); + setEmitterProperty( + "Set Translation", + "SetTranslation", + &Ptcl::Emitter::translation, + &Ptcl::Emitter::setTranslation, + translation + ); }); connect(&mRotationSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - rebuildMatrices(); - emit propertiesUpdated(mProps); + const auto rotation = mRotationSpinBox.getVector(); + setEmitterProperty( + "Set Rotation", + "SetRotation", + &Ptcl::Emitter::rotation, + &Ptcl::Emitter::setRotation, + rotation + ); }); connect(&mScaleSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - rebuildMatrices(); - emit propertiesUpdated(mProps); + const auto scale = mScaleSpinBox.getVector(); + setEmitterProperty( + "Set Scale", + "SetScale", + &Ptcl::Emitter::scale, + &Ptcl::Emitter::setScale, + scale + ); }); } -void EmitterWidget::TransformPropertiesWidget::setProperties(const Ptcl::Emitter::TransformProperties& properties) { +void TransformPropertiesWidget::populateProperties() { QSignalBlocker b1(mTranslationSpinBox); QSignalBlocker b2(mRotationSpinBox); QSignalBlocker b3(mScaleSpinBox); - mProps = properties; - - auto translation = Math::Util::getTranslation(mProps.transformRT); - mTranslationSpinBox.setVector(translation); - - auto rotation = Math::Util::getRotationEuler(mProps.transformRT); - - rotation.setX(Math::Util::to180(rotation.getX())); - rotation.setY(Math::Util::to180(rotation.getY())); - rotation.setZ(Math::Util::to180(rotation.getZ())); - - mRotationSpinBox.setVector(rotation); - - auto scale = Math::Util::getScale(mProps.transformSRT); - mScaleSpinBox.setVector(scale); -} - -void EmitterWidget::TransformPropertiesWidget::rebuildMatrices() { - const auto rotation = mRotationSpinBox.getVector(); - const auto translation = mTranslationSpinBox.getVector(); - - const auto mtxR = Math::Util::eulerToRotationMatrix(rotation); - - Math::Matrix34f mtxRT; - for (s32 r = 0; r < 3; ++r) { - for (s32 c = 0; c < 3; ++c) { - mtxRT(r, c) = mtxR(r, c); - } - mtxRT(r, 3) = translation[r]; - } - - mProps.transformRT = mtxRT; - - const auto scale = mScaleSpinBox.getVector(); - - Math::Matrix34f mtxSRT; - for (s32 r = 0; r < 3; ++r) { - mtxSRT(r, 0) = mtxRT(r, 0) * scale.getX(); - mtxSRT(r, 1) = mtxRT(r, 1) * scale.getY(); - mtxSRT(r, 2) = mtxRT(r, 2) * scale.getZ(); - mtxSRT(r, 3) = mtxRT(r, 3); - } - - mProps.transformSRT = mtxSRT; + mTranslationSpinBox.setVector(mEmitter->translation()); + mRotationSpinBox.setVector(mEmitter->rotation()); + mScaleSpinBox.setVector(mEmitter->scale()); } From f1b476f75afa92d42d1a4ab0db39e02384ebc9f1 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Mon, 9 Mar 2026 03:54:37 +0000 Subject: [PATCH 06/35] feat(ui): implement undo/redo for Emitter scaleProperties --- include/editor/emitterWidget/emitterWidget.h | 3 +- .../emitterWidget/scalePropertiesWidget.h | 17 +- include/ptcl/ptclEmitter.h | 149 +++++++++--------- src/editor/emitterWidget/emitterWidget.cpp | 11 +- .../emitterWidget/scalePropertiesWidget.cpp | 98 ++++++++---- src/ptcl/ptclBinary.cpp | 16 +- src/ptcl/ptclEmitter.cpp | 102 ++++++------ 7 files changed, 208 insertions(+), 188 deletions(-) diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index 8e174c9..f11e702 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -25,7 +25,7 @@ namespace PtclEditor { class BasicPropertiesWidget; class GravityPropertiesWidget; class TransformPropertiesWidget; - +class ScalePropertiesWidget; // ========================================================================== // @@ -60,7 +60,6 @@ class EmitterWidget final : public QWidget { class ColorPropertiesWidget; class AlphaPropertiesWidget; class RotationPropertiesWidget; - class ScalePropertiesWidget; class TexturePropertiesWidget; class CombinerPropertiesWidget; diff --git a/include/editor/emitterWidget/scalePropertiesWidget.h b/include/editor/emitterWidget/scalePropertiesWidget.h index 5eaedf2..9ee3d82 100644 --- a/include/editor/emitterWidget/scalePropertiesWidget.h +++ b/include/editor/emitterWidget/scalePropertiesWidget.h @@ -1,9 +1,7 @@ #pragma once #include "editor/components/animGraph.h" -#include "editor/emitterWidget/emitterWidget.h" - -#include "ptcl/ptclEmitter.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include @@ -14,28 +12,23 @@ namespace PtclEditor { // ========================================================================== // -class EmitterWidget::ScalePropertiesWidget final : public QWidget { +class ScalePropertiesWidget final : public EmitterWidgetBase { Q_OBJECT public: explicit ScalePropertiesWidget(QWidget* parent = nullptr); - void setProperties(const Ptcl::Emitter::ScaleProperties& properties); - -signals: - void propertiesUpdated(const Ptcl::Emitter::ScaleProperties& properties); - private: + void populateProperties() final; + void setupConnections(); + void updateAnimPoint(s32 pointIndex, const AnimGraph::GraphPoint& point, f32 (Math::Vector2f::*get)() const); void updateGraphs(); private: - Ptcl::Emitter::ScaleProperties mProps{}; - AnimGraph mGraphX{}; AnimGraph mGraphY{}; QDoubleSpinBox mRandSpinbox{}; - }; diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index f50de21..4a58aaf 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -30,25 +30,14 @@ namespace Ptcl { class Emitter { public: - struct BasicProperties { - EmitterType type{EmitterType::Simple}; - FollowType followType{FollowType::All}; - QString name{"Emitter"}; - PtclSeed randomSeed{}; - BillboardType billboardType{BillboardType::Billboard}; - bool isPolygon{false}; - bool isVelLook{false}; - bool isEmitterBillboardMtx{false}; - bool isFollow{false}; - }; - - struct ScaleProperties { + struct ScaleAnim { Math::Vector2f initScale{1.0f, 1.0f}; Math::Vector2f diffScale21{0.0f, 0.0f}; Math::Vector2f diffScale32{0.0f, 0.0f}; s32 scaleSection1{0}; s32 scaleSection2{0}; - f32 scaleRand{0.0f}; + + bool operator==(const ScaleAnim&) const = default; }; struct RotationProperties { @@ -121,24 +110,6 @@ class Emitter { s32 ptclLifeRnd{0}; }; - struct TransformProperties { - Math::Matrix34f transformSRT{ - {1.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 1.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 1.0f, 0.0f}, - }; - Math::Matrix34f transformRT{ - {1.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 1.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 1.0f, 0.0f}, - }; - }; - - struct GravityProperties { - bool isDirectional{false}; - Math::Vector3f gravity{0.0f, -1.0f, 0.0f}; - }; - struct TextureProperties { TextureWrap textureWrapT{TextureWrap::ClampToEdge}; TextureWrap textureWrapS{TextureWrap::ClampToEdge}; @@ -187,50 +158,51 @@ class Emitter { std::unique_ptr clone() const; - // Basic Properties + // ----- Basic Properties ----- \\ - EmitterType type() const { return mBasicProperties.type; } - void setType(EmitterType type) { mBasicProperties.type = type; } + EmitterType type() const { return mType; } + void setType(EmitterType type) { mType = type; } - FollowType followType() const { return mBasicProperties.followType; } + FollowType followType() const { return mFollowType; } void setFollowType(FollowType type) { - mBasicProperties.followType = type; + mFollowType = type; // TODO: Check if this should also be set for Ptcl::FollowType::PosOnly - mBasicProperties.isFollow = (type == Ptcl::FollowType::All); + mIsFollow = (type == Ptcl::FollowType::All); } - const QString& name() const; - void setName(const QString& name); + const QString& name() const { return mName; } + void setName(const QString& name) { mName = name; } - const PtclSeed& randomSeed() const { return mBasicProperties.randomSeed; } - void setRandomSeed(PtclSeed seed) { mBasicProperties.randomSeed = seed; } + const PtclSeed& randomSeed() const { return mRandomSeed; } + void setRandomSeed(PtclSeed seed) { mRandomSeed = seed; } - BillboardType billboardType() const { return mBasicProperties.billboardType; } + BillboardType billboardType() const { return mBillboardType; } void setBillboardType(BillboardType type) { - mBasicProperties.billboardType = type; - mBasicProperties.isPolygon = (type == Ptcl::BillboardType::PolygonXY || type == Ptcl::BillboardType::PolygonXZ); - mBasicProperties.isVelLook = (type == Ptcl::BillboardType::VelLook || type == Ptcl::BillboardType::VelLookPolygon); + mBillboardType = type; + mIsPolygon = (type == Ptcl::BillboardType::PolygonXY || type == Ptcl::BillboardType::PolygonXZ); + mIsVelLook = (type == Ptcl::BillboardType::VelLook || type == Ptcl::BillboardType::VelLookPolygon); } - bool isEmitterBillboardMtx() const { return mBasicProperties.isEmitterBillboardMtx; } - void setIsEmitterBillboardMtx(bool isEmitterBillboardMtx) { mBasicProperties.isEmitterBillboardMtx = isEmitterBillboardMtx; } + bool isEmitterBillboardMtx() const { return mIsEmitterBillboardMtx; } + void setIsEmitterBillboardMtx(bool isEmitterBillboardMtx) { mIsEmitterBillboardMtx = isEmitterBillboardMtx; } - bool isPolygon() const { return mBasicProperties.isPolygon; } - bool isFollow() const { return mBasicProperties.isFollow; } - bool isVelLook() const { return mBasicProperties.isVelLook; } + bool isPolygon() const { return mIsPolygon; } + bool isFollow() const { return mIsFollow; } + bool isVelLook() const { return mIsVelLook; } - // Gravity Properties + // ----- Gravity Properties ----- \\ - bool isDirectional() const { return mGravityProperties.isDirectional; } - void setDirectional(bool isDirectional) { mGravityProperties.isDirectional = isDirectional; } + bool isDirectional() const { return mIsDirectional; } + void setDirectional(bool isDirectional) { mIsDirectional = isDirectional; } - const Math::Vector3f& gravity() const { return mGravityProperties.gravity; } - void setGravity(const Math::Vector3f& gravity) { mGravityProperties.gravity = gravity; } + const Math::Vector3f& gravity() const { return mGravity; } + void setGravity(const Math::Vector3f& gravity) { mGravity = gravity; } - // Transform Properties - const Math::Matrix34f& transformRT() const { return mTransformProperties.transformRT; } - const Math::Matrix34f& transformSRT() const { return mTransformProperties.transformSRT; } + // ----- Transform Properties ----- \\ + + const Math::Matrix34f& transformRT() const { return mTransformRT; } + const Math::Matrix34f& transformSRT() const { return mTransformSRT; } void setTransform(const Math::Vector3f& rotation, const Math::Vector3f& translation, const Math::Vector3f& scale) { const auto mtxR = Math::Util::eulerToRotationMatrix(rotation); @@ -243,7 +215,7 @@ class Emitter { mtxRT(r, 3) = translation[r]; } - mTransformProperties.transformRT = mtxRT; + mTransformRT = mtxRT; Math::Matrix34f mtxSRT; for (s32 r = 0; r < 3; ++r) { @@ -253,14 +225,14 @@ class Emitter { mtxSRT(r, 3) = mtxRT(r, 3); } - mTransformProperties.transformSRT = mtxSRT; + mTransformSRT = mtxSRT; } - Math::Vector3f translation() const { return Math::Util::getTranslation(mTransformProperties.transformRT); } + Math::Vector3f translation() const { return Math::Util::getTranslation(mTransformRT); } void setTranslation(const Math::Vector3f& translation) { setTransform(rotation(), translation, scale()); } Math::Vector3f rotation() const { - auto rot = Math::Util::getRotationEuler(mTransformProperties.transformRT); + auto rot = Math::Util::getRotationEuler(mTransformRT); rot.setX(Math::Util::to180(rot.getX())); rot.setY(Math::Util::to180(rot.getY())); rot.setZ(Math::Util::to180(rot.getZ())); @@ -268,9 +240,17 @@ class Emitter { } void setRotation(const Math::Vector3f& rotation) { setTransform(rotation, translation(), scale()); } - Math::Vector3f scale() const { return Math::Util::getScale(mTransformProperties.transformSRT); } + Math::Vector3f scale() const { return Math::Util::getScale(mTransformSRT); } void setScale(const Math::Vector3f& scale) { setTransform(rotation(), translation(), scale); } + // ----- Scale Properties ----- \\ + + const ScaleAnim& scaleAnim() const { return mScaleAnim; } + void setScaleAnim(const ScaleAnim& scaleAnim) { mScaleAnim = scaleAnim; } + + f32 scaleRand() const { return mScaleRand; } + void setScaleRand(f32 scaleRand) { mScaleRand = scaleRand; } + BitFlag& flags(); const BitFlag& flags() const; @@ -299,9 +279,6 @@ class Emitter { const AlphaProperties& alphaProperties() const; void setAlphaProperties(const AlphaProperties& alphaProperties); - const ScaleProperties& scaleProperties() const; - void setScaleProperties(const ScaleProperties& scaleProperties); - const TextureProperties& textureProperties() const; void setTextureProperties(const TextureProperties& textureProperties); @@ -311,9 +288,6 @@ class Emitter { const CombinerProperties& combinerProperties() const; void setCombinerProperties(const CombinerProperties& combinerProperties); - const TransformProperties& transformProperties() const; - void setTransformProperties(const TransformProperties& transformProperties); - const ComplexProperties& complexProperties() const; void setComplexProperties(const ComplexProperties& complexProperties); @@ -345,9 +319,37 @@ class Emitter { private: BitFlag mFlag{}; - BasicProperties mBasicProperties{}; - GravityProperties mGravityProperties{}; - TransformProperties mTransformProperties{}; + // Basic Properties + EmitterType mType{EmitterType::Simple}; + FollowType mFollowType{FollowType::All}; + QString mName{"Emitter"}; + PtclSeed mRandomSeed{}; + BillboardType mBillboardType{BillboardType::Billboard}; + bool mIsPolygon{false}; + bool mIsVelLook{false}; + bool mIsEmitterBillboardMtx{false}; + bool mIsFollow{false}; + + // Gravity Properties + bool mIsDirectional{false}; + Math::Vector3f mGravity{0.0f, -1.0f, 0.0f}; + + // Transform Properties + Math::Matrix34f mTransformSRT{ + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + }; + Math::Matrix34f mTransformRT{ + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + }; + + // Scale Properties + ScaleAnim mScaleAnim{}; + f32 mScaleRand{0.0f}; + LifespanProperties mLifespanProperties{}; TerminationProperties mTerminationProperties{}; EmissionProperties mEmissionProperties{}; @@ -355,7 +357,6 @@ class Emitter { VolumeProperties mVolumeProperties{}; ColorProperties mColorProperties{}; AlphaProperties mAlphaProperties{}; - ScaleProperties mScaleProperties{}; RotationProperties mRotationProperties{}; TextureProperties mTextureProperties{}; CombinerProperties mCombinerProperties{}; diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index f2d5461..75ce4a6 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -192,13 +192,6 @@ void EmitterWidget::setupConnections() { emit propertiesChanged(); }); - // Scale Properties - connect(mScaleProperties, &ScalePropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::ScaleProperties& properties) { - if (!mEmitter) { return; } - mEmitter->setScaleProperties(properties); - emit propertiesChanged(); - }); - // Child Editor Widget connect(mChildEditorWidget, &ChildEditorWidget::flagsUpdated, this, [this](const BitFlag& childFlags) { if (!mEmitter) { return; } @@ -251,6 +244,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mBasicProperties->setDocument(document); mGravityProperties->setDocument(document); mTransformProperties->setDocument(document); + mScaleProperties->setDocument(document); } void EmitterWidget::setSelection(Ptcl::Selection* selection) { @@ -259,6 +253,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mBasicProperties->setSelection(selection); mGravityProperties->setSelection(selection); mTransformProperties->setSelection(selection); + mScaleProperties->setSelection(selection); connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -303,7 +298,6 @@ void EmitterWidget::populateProperties() { QSignalBlocker b7(mVelocityProperties); QSignalBlocker b8(mColorProperties); QSignalBlocker b9(mAlphaProperties); - QSignalBlocker b10(mScaleProperties); QSignalBlocker b11(mRotationProperties); QSignalBlocker b12(mTextureProperties); QSignalBlocker b13(mCombinerProperties); @@ -319,7 +313,6 @@ void EmitterWidget::populateProperties() { mVolumeProperties->setProperties(mEmitter->volumeProperties()); mColorProperties->setProperties(mEmitter->colorProperties()); mAlphaProperties->setProperties(mEmitter->alphaProperties()); - mScaleProperties->setProperties(mEmitter->scaleProperties()); mRotationProperties->setProperties(mEmitter->rotationProperties()); mTextureProperties->setProperties(mEmitter->textureProperties(), mEmitter->textureHandle().get()); mCombinerProperties->setProperties(mEmitter->combinerProperties()); diff --git a/src/editor/emitterWidget/scalePropertiesWidget.cpp b/src/editor/emitterWidget/scalePropertiesWidget.cpp index 6a4b36f..5101d4b 100644 --- a/src/editor/emitterWidget/scalePropertiesWidget.cpp +++ b/src/editor/emitterWidget/scalePropertiesWidget.cpp @@ -9,13 +9,13 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::ScalePropertiesWidget::ScalePropertiesWidget(QWidget* parent) : - QWidget{parent} { +ScalePropertiesWidget::ScalePropertiesWidget(QWidget* parent) : + EmitterWidgetBase{parent} { // TODO: Move these somewhere else static constexpr QColor sColorAxisX = { 238, 51, 79 }; static constexpr QColor sColorAxisY = { 42, 125, 212 }; - static constexpr QColor sColorAxisZ = { 137, 214, 1 }; + // static constexpr QColor sColorAxisZ = { 137, 214, 1 }; mGraphX.setLineColor(sColorAxisX); mGraphY.setLineColor(sColorAxisY); @@ -28,6 +28,10 @@ EmitterWidget::ScalePropertiesWidget::ScalePropertiesWidget(QWidget* parent) : // TODO: Check this is valid mRandSpinbox.setRange(0.0f, 1.0f); + setupConnections(); +} + +void ScalePropertiesWidget::setupConnections() { connect(&mGraphX, &AnimGraph::pointEdited, this, [this](s32 pointIndex, const AnimGraph::GraphPoint& point) { updateAnimPoint(pointIndex, point, &Math::Vector2f::getX); }); @@ -37,12 +41,19 @@ EmitterWidget::ScalePropertiesWidget::ScalePropertiesWidget(QWidget* parent) : }); connect(&mRandSpinbox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mProps.scaleRand = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Scale Random", + "SetScaleRand", + &Ptcl::Emitter::scaleRand, + &Ptcl::Emitter::setScaleRand, + static_cast(value) + ); }); } -void EmitterWidget::ScalePropertiesWidget::updateAnimPoint(s32 pointIndex, const AnimGraph::GraphPoint& point, f32 (Math::Vector2f::*get)() const) { +void ScalePropertiesWidget::updateAnimPoint(s32 pointIndex, const AnimGraph::GraphPoint& point, f32 (Math::Vector2f::*get)() const) { + auto anim = mEmitter->scaleAnim(); + auto set = [&](Math::Vector2f& v, f32 val) { if (get == &Math::Vector2f::getX) { v.setX(val); @@ -57,20 +68,20 @@ void EmitterWidget::ScalePropertiesWidget::updateAnimPoint(s32 pointIndex, const mGraphY.getPoints(); }; - const f32 oldP0 = (mProps.initScale.*get)(); - const f32 oldP1 = oldP0 + (mProps.diffScale21.*get)(); + const f32 oldP0 = (anim.initScale.*get)(); + const f32 oldP1 = oldP0 + (anim.diffScale21.*get)(); const f32 oldP2 = oldP1; - const f32 oldP3 = oldP2 + (mProps.diffScale32.*get)(); + const f32 oldP3 = oldP2 + (anim.diffScale32.*get)(); auto updateScaleSection = [&](s32* section, bool isSection2 = false) { - set(mProps.diffScale21, point.value - oldP0); - set(mProps.diffScale32, oldP3 - point.value); + set(anim.diffScale21, point.value - oldP0); + set(anim.diffScale32, oldP3 - point.value); const f32 sec1 = getGraphPoints()[1].position; const f32 sec2 = getGraphPoints()[2].position; if (std::abs(sec1 - sec2) < std::numeric_limits::epsilon()) { - mProps.scaleSection1 = -127; // Disable section1 if handles overlap + anim.scaleSection1 = -127; // Disable section1 if handles overlap if (isSection2) { *section = static_cast(point.position); } } else { *section = static_cast(point.position); @@ -79,37 +90,66 @@ void EmitterWidget::ScalePropertiesWidget::updateAnimPoint(s32 pointIndex, const switch (pointIndex) { case 0: - set(mProps.initScale, point.value); - set(mProps.diffScale21, oldP1 - point.value); + set(anim.initScale, point.value); + set(anim.diffScale21, oldP1 - point.value); break; case 1: - updateScaleSection(&mProps.scaleSection1); + updateScaleSection(&anim.scaleSection1); break; case 2: - updateScaleSection(&mProps.scaleSection2, true); + updateScaleSection(&anim.scaleSection2, true); break; case 3: - set(mProps.diffScale32, point.value - oldP1); + set(anim.diffScale32, point.value - oldP1); break; } - emit propertiesUpdated(mProps); + const QString axis = (get == &Math::Vector2f::getX) ? "X" : "Y"; + + QString handleName; + switch (pointIndex) { + case 0: handleName = "Start"; break; + case 1: handleName = "Section1"; break; + case 2: handleName = "Section2"; break; + case 3: handleName = "End"; break; + } + + QString key; + QString label; + if (pointIndex == 1 || pointIndex == 2) { + label = QString("Move Scale %1").arg(handleName); + key = QString("ScaleGraph_%1").arg(pointIndex); + } else { + label = QString("Move Scale %1 (%2)").arg(handleName, axis); + key = QString("ScaleAnim_%1_%2").arg(pointIndex).arg(axis); + } + + setEmitterProperty( + label, + key, + &Ptcl::Emitter::scaleAnim, + &Ptcl::Emitter::setScaleAnim, + anim + ); + updateGraphs(); } -void EmitterWidget::ScalePropertiesWidget::updateGraphs() { - auto updateGraph = [this](AnimGraph& graph, f32 (Math::Vector2f::*get)() const) { +void ScalePropertiesWidget::updateGraphs() { + const auto& anim = mEmitter->scaleAnim(); + + auto updateGraph = [&](AnimGraph& graph, f32 (Math::Vector2f::*get)() const) { QSignalBlocker blocker(graph); - const f32 p0 = (mProps.initScale.*get)(); - const f32 p1 = p0 + (mProps.diffScale21.*get)(); + const f32 p0 = (anim.initScale.*get)(); + const f32 p1 = p0 + (anim.diffScale21.*get)(); const f32 p2 = p1; - const f32 p3 = p2 + (mProps.diffScale32.*get)(); + const f32 p3 = p2 + (anim.diffScale32.*get)(); - const bool disabled = mProps.scaleSection1 == -127; + const bool disabled = anim.scaleSection1 == -127; - const f32 sec1 = disabled ? static_cast(mProps.scaleSection2) : static_cast(mProps.scaleSection1); - const f32 sec2 = static_cast(mProps.scaleSection2); + const f32 sec1 = disabled ? static_cast(anim.scaleSection2) : static_cast(anim.scaleSection1); + const f32 sec2 = static_cast(anim.scaleSection2); AnimGraph::PointList points = { { 0.0f, p0, AnimGraph::HandleType::Locked }, @@ -124,13 +164,11 @@ void EmitterWidget::ScalePropertiesWidget::updateGraphs() { updateGraph(mGraphY, &Math::Vector2f::getY); } -void EmitterWidget::ScalePropertiesWidget::setProperties(const Ptcl::Emitter::ScaleProperties& properties) { - mProps = properties; - +void ScalePropertiesWidget::populateProperties() { updateGraphs(); QSignalBlocker blockerRand(mRandSpinbox); - mRandSpinbox.setValue(mProps.scaleRand); + mRandSpinbox.setValue(mEmitter->scaleRand()); update(); } diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index 080f4bb..2ddffec 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -374,12 +374,12 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { alphaSection1 = emitter.alphaProperties().alphaSection1; alphaSection2 = emitter.alphaProperties().alphaSection2; - initScale = emitter.scaleProperties().initScale; - diffScale21 = emitter.scaleProperties().diffScale21; - diffScale32 = emitter.scaleProperties().diffScale32; - scaleSection1 = emitter.scaleProperties().scaleSection1; - scaleSection2 = emitter.scaleProperties().scaleSection2; - scaleRand = emitter.scaleProperties().scaleRand; + initScale = emitter.scaleAnim().initScale; + diffScale21 = emitter.scaleAnim().diffScale21; + diffScale32 = emitter.scaleAnim().diffScale32; + scaleSection1 = emitter.scaleAnim().scaleSection1; + scaleSection2 = emitter.scaleAnim().scaleSection2; + scaleRand = emitter.scaleRand(); rotCalcType = static_cast(emitter.rotationProperties().rotType) + 5 * static_cast(emitter.colorProperties().colorCalcType); followType = emitter.followType(); @@ -389,8 +389,8 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { rotVel = emitter.rotationProperties().rotVel; rotVelRand = emitter.rotationProperties().rotVelRand; rotBasis = emitter.rotationProperties().rotBasis; - transformSRT = emitter.transformProperties().transformSRT; - transformRT = emitter.transformProperties().transformRT; + transformSRT = emitter.transformSRT(); + transformRT = emitter.transformRT(); alphaAddInFade = emitter.terminationProperties().alphaAddInFade; numTexPat = emitter.textureProperties().numTexPat; numTexDivX = emitter.textureProperties().numTexDivX; diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index 353f8dd..a2989ba 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -10,9 +10,26 @@ std::unique_ptr Emitter::clone() const { auto newEmitter = std::make_unique(); newEmitter->mFlag = mFlag; - newEmitter->mBasicProperties = mBasicProperties; - newEmitter->mGravityProperties = mGravityProperties; - newEmitter->mTransformProperties = mTransformProperties; + + // Basic Properties + newEmitter->mType = mType; + newEmitter->mFollowType = mFollowType; + newEmitter->mName = mName; + newEmitter->mRandomSeed = mRandomSeed; + newEmitter->mBillboardType = mBillboardType; + newEmitter->mIsPolygon = mIsPolygon; + newEmitter->mIsVelLook = mIsVelLook; + newEmitter->mIsEmitterBillboardMtx = mIsEmitterBillboardMtx; + newEmitter->mIsFollow = mIsFollow; + + // Gravity Properties + newEmitter->mIsDirectional = mIsDirectional; + newEmitter->mGravity = mGravity; + + // Transform Properties + newEmitter->mTransformSRT = mTransformSRT; + newEmitter->mTransformRT = mTransformRT; + newEmitter->mLifespanProperties = mLifespanProperties; newEmitter->mTerminationProperties = mTerminationProperties; newEmitter->mEmissionProperties = mEmissionProperties; @@ -20,7 +37,11 @@ std::unique_ptr Emitter::clone() const { newEmitter->mVolumeProperties = mVolumeProperties; newEmitter->mColorProperties = mColorProperties; newEmitter->mAlphaProperties = mAlphaProperties; - newEmitter->mScaleProperties = mScaleProperties; + + // Scale Properties + newEmitter->mScaleAnim = mScaleAnim; + newEmitter->mScaleRand = mScaleRand; + newEmitter->mRotationProperties = mRotationProperties; newEmitter->mTextureProperties = mTextureProperties; newEmitter->mCombinerProperties = mCombinerProperties; @@ -41,14 +62,6 @@ const BitFlag& Emitter::flags() const { return mFlag; } -const QString& Emitter::name() const { - return mBasicProperties.name; -} - -void Emitter::setName(const QString& name) { - mBasicProperties.name = name; -} - const TextureHandle& Emitter::textureHandle() const { return mTextureHandle; } @@ -61,14 +74,6 @@ void Emitter::setTexture(const std::shared_ptr& texture) { mTextureHandle.set(texture); } -const Emitter::TransformProperties& Emitter::transformProperties() const { - return mTransformProperties; -} - -void Emitter::setTransformProperties(const TransformProperties& transformProperties) { - mTransformProperties = transformProperties; -} - const Emitter::LifespanProperties& Emitter::lifespanProperties() const { return mLifespanProperties; } @@ -128,14 +133,6 @@ void Emitter::setAlphaProperties(const AlphaProperties& alphaProperties) { mAlphaProperties = alphaProperties; } -const Emitter::ScaleProperties& Emitter::scaleProperties() const { - return mScaleProperties; -} - -void Emitter::setScaleProperties(const ScaleProperties& scaleProperties) { - mScaleProperties = scaleProperties; -} - const Emitter::TextureProperties& Emitter::textureProperties() const { return mTextureProperties; } @@ -242,9 +239,9 @@ void Emitter::initStripeData(const BinStripeData& stripeData) { } bool Emitter::hasStripeData() const { - const auto bType = mBasicProperties.billboardType; + const auto bType = mBillboardType; const bool isBillboardStripe = bType == BillboardType::Stripe || bType == BillboardType::ComplexStripe; - return mBasicProperties.type != EmitterType::Simple && isBillboardStripe; + return mType != EmitterType::Simple && isBillboardStripe; } void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { @@ -266,27 +263,24 @@ void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { .isTexPatAnim = emitterData.isTexPatAnim }; - mBasicProperties = { - .type = emitterData.type, - .followType = emitterData.followType, - // name - must be set after initialization - .randomSeed = PtclSeed{emitterData.randomSeed}, - .billboardType = emitterData.billboardType, - .isPolygon = emitterData.isPolygon, - .isVelLook = emitterData.isVelLook, - .isEmitterBillboardMtx = emitterData.isEmitterBillboardMtx, - .isFollow = emitterData.isFollow - }; - - mGravityProperties = { - .isDirectional = emitterData.isDirectional, - .gravity = Math::Vector3f(emitterData.gravity.x, emitterData.gravity.y, emitterData.gravity.z) - }; - - mTransformProperties = { - .transformSRT = emitterData.transformSRT.toMatrix34f(), - .transformRT = emitterData.transformRT.toMatrix34f() - }; + // Basic Properties + mType = emitterData.type; + mFollowType = emitterData.followType; + // name - must be set after initialization + mRandomSeed = PtclSeed{emitterData.randomSeed}; + mBillboardType = emitterData.billboardType; + mIsPolygon = emitterData.isPolygon; + mIsVelLook = emitterData.isVelLook; + mIsEmitterBillboardMtx = emitterData.isEmitterBillboardMtx; + mIsFollow = emitterData.isFollow; + + // Gravity Properties + mIsDirectional = emitterData.isDirectional; + mGravity = Math::Vector3f(emitterData.gravity.x, emitterData.gravity.y, emitterData.gravity.z); + + // Transform Properties + mTransformSRT = emitterData.transformSRT.toMatrix34f(); + mTransformRT = emitterData.transformRT.toMatrix34f(); mLifespanProperties = { .ptclLife = emitterData.ptclLife, @@ -343,15 +337,17 @@ void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { .alphaSection2 = emitterData.alphaSection2, }; - mScaleProperties = { + // Scale Properties + mScaleAnim = { .initScale = Math::Vector2f(emitterData.initScale.x, emitterData.initScale.y), .diffScale21 = Math::Vector2f(emitterData.diffScale21.x, emitterData.diffScale21.y), .diffScale32 = Math::Vector2f(emitterData.diffScale32.x, emitterData.diffScale32.y), .scaleSection1 = emitterData.scaleSection1, .scaleSection2 = emitterData.scaleSection2, - .scaleRand = emitterData.scaleRand }; + mScaleRand = emitterData.scaleRand; + mRotationProperties = { .rotType = static_cast(emitterData.rotCalcType % 5), .initRot = Math::Vector3i(emitterData.initRot.x, emitterData.initRot.y, emitterData.initRot.z), From 319c5b503dba70825fe46e6e635f2c9f0d60e329 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Mon, 9 Mar 2026 16:02:07 +0000 Subject: [PATCH 07/35] feat(ui): implement undo/redo for Emitter lifespanProperties --- include/editor/emitterWidget/emitterWidget.h | 2 +- .../emitterWidget/lifespanPropertiesWidget.h | 15 ++---- include/ptcl/ptclEmitter.h | 21 ++++---- src/editor/emitterWidget/emitterWidget.cpp | 11 +--- .../lifespanPropertiesWidget.cpp | 50 ++++++++++++------- src/ptcl/ptclBinary.cpp | 4 +- src/ptcl/ptclEmitter.cpp | 22 +++----- 7 files changed, 62 insertions(+), 63 deletions(-) diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index f11e702..a39b7b4 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -25,6 +25,7 @@ namespace PtclEditor { class BasicPropertiesWidget; class GravityPropertiesWidget; class TransformPropertiesWidget; +class LifespanPropertiesWidget; class ScalePropertiesWidget; // ========================================================================== // @@ -52,7 +53,6 @@ class EmitterWidget final : public QWidget { void populateProperties(); private: - class LifespanPropertiesWidget; class TerminationPropertiesWidget; class EmissionPropertiesWidget; class VelocityPropertiesWidget; diff --git a/include/editor/emitterWidget/lifespanPropertiesWidget.h b/include/editor/emitterWidget/lifespanPropertiesWidget.h index 6ab5b77..c41703f 100644 --- a/include/editor/emitterWidget/lifespanPropertiesWidget.h +++ b/include/editor/emitterWidget/lifespanPropertiesWidget.h @@ -1,8 +1,6 @@ #pragma once -#include "editor/emitterWidget/emitterWidget.h" - -#include "ptcl/ptclEmitter.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include #include @@ -16,19 +14,16 @@ namespace PtclEditor { // ========================================================================== // -class EmitterWidget::LifespanPropertiesWidget final : public QWidget { +class LifespanPropertiesWidget final : public EmitterWidgetBase { Q_OBJECT public: explicit LifespanPropertiesWidget(QWidget* parent = nullptr); - void setProperties(const Ptcl::Emitter::LifespanProperties& properties); - -signals: - void propertiesUpdated(const Ptcl::Emitter::LifespanProperties& properties); - private: - Ptcl::Emitter::LifespanProperties mProps{}; + void populateProperties() final; + void setupConnections(); +private: QCheckBox mInfiniteLifeCheckBox; QSpinBox mLifeSpanSpinBox; QSpinBox mLifeSpanRndSpinBox; diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index 4a58aaf..54f5435 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -105,11 +105,6 @@ class Emitter { f32 alphaAddInFade{0.0f}; }; - struct LifespanProperties { - s32 ptclLife{100}; - s32 ptclLifeRnd{0}; - }; - struct TextureProperties { TextureWrap textureWrapT{TextureWrap::ClampToEdge}; TextureWrap textureWrapS{TextureWrap::ClampToEdge}; @@ -199,6 +194,14 @@ class Emitter { const Math::Vector3f& gravity() const { return mGravity; } void setGravity(const Math::Vector3f& gravity) { mGravity = gravity; } + // ----- Lifespan Properties ----- \\ + + s32 ptclLife() const { return mPtclLife; } + void setPtclLife(const s32 ptclLife) { mPtclLife = ptclLife; } + + s32 ptclLifeRandom() const { return mPtclLifeRnd; } + void setPtclLifeRandom(const s32 lifeRandom) { mPtclLifeRnd = lifeRandom; } + // ----- Transform Properties ----- \\ const Math::Matrix34f& transformRT() const { return mTransformRT; } @@ -258,9 +261,6 @@ class Emitter { const TextureHandle& textureHandle() const; void setTexture(const std::shared_ptr& texture); - const LifespanProperties& lifespanProperties() const; - void setLifespanProperties(const LifespanProperties& lifespanProperties); - const TerminationProperties& terminationProperties() const; void setTerminationProperties(const TerminationProperties& terminationProperties); @@ -334,6 +334,10 @@ class Emitter { bool mIsDirectional{false}; Math::Vector3f mGravity{0.0f, -1.0f, 0.0f}; + // Lifespan Properties + s32 mPtclLife{100}; + s32 mPtclLifeRnd{0}; + // Transform Properties Math::Matrix34f mTransformSRT{ {1.0f, 0.0f, 0.0f, 0.0f}, @@ -350,7 +354,6 @@ class Emitter { ScaleAnim mScaleAnim{}; f32 mScaleRand{0.0f}; - LifespanProperties mLifespanProperties{}; TerminationProperties mTerminationProperties{}; EmissionProperties mEmissionProperties{}; VelocityProperties mVelocityProperties{}; diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index 75ce4a6..ee2fca8 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -127,13 +127,6 @@ void EmitterWidget::setupConnections() { emit propertiesChanged(); }); - // Lifespan Properties - connect(mLifespanProperties, &LifespanPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::LifespanProperties& properties) { - if (!mEmitter) { return; } - mEmitter->setLifespanProperties(properties); - emit propertiesChanged(); - }); - // Termination Properties connect(mTerminationProperties, &TerminationPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::TerminationProperties& properties) { if (!mEmitter) { return; } @@ -244,6 +237,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mBasicProperties->setDocument(document); mGravityProperties->setDocument(document); mTransformProperties->setDocument(document); + mLifespanProperties->setDocument(document); mScaleProperties->setDocument(document); } @@ -253,6 +247,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mBasicProperties->setSelection(selection); mGravityProperties->setSelection(selection); mTransformProperties->setSelection(selection); + mLifespanProperties->setSelection(selection); mScaleProperties->setSelection(selection); connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { @@ -292,7 +287,6 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { } void EmitterWidget::populateProperties() { - QSignalBlocker b4(mLifespanProperties); QSignalBlocker b5(mTerminationProperties); QSignalBlocker b6(mEmissionProperties); QSignalBlocker b7(mVelocityProperties); @@ -306,7 +300,6 @@ void EmitterWidget::populateProperties() { QSignalBlocker b17(mFieldEditorWidget); QSignalBlocker b18(mStripeEditorWidget); - mLifespanProperties->setProperties(mEmitter->lifespanProperties()); mTerminationProperties->setProperties(mEmitter->terminationProperties()); mEmissionProperties->setProperties(mEmitter->emissionProperties()); mVelocityProperties->setProperties(mEmitter->velocityProperties()); diff --git a/src/editor/emitterWidget/lifespanPropertiesWidget.cpp b/src/editor/emitterWidget/lifespanPropertiesWidget.cpp index 802cebd..efd8f64 100644 --- a/src/editor/emitterWidget/lifespanPropertiesWidget.cpp +++ b/src/editor/emitterWidget/lifespanPropertiesWidget.cpp @@ -9,8 +9,8 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::LifespanPropertiesWidget::LifespanPropertiesWidget(QWidget* parent) : - QWidget{parent} { +LifespanPropertiesWidget::LifespanPropertiesWidget(QWidget* parent) : + EmitterWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); @@ -27,42 +27,56 @@ EmitterWidget::LifespanPropertiesWidget::LifespanPropertiesWidget(QWidget* paren mainLayout->addRow("Lifespan:", &mLifeSpanSpinBox); mainLayout->addRow("Lifespan Randomness:", &mLifeSpanRndSpinBox); + setupConnections(); +} + +void LifespanPropertiesWidget::setupConnections() { connect(&mLifeSpanSpinBox, &QSpinBox::valueChanged, this, [this](u64 value) { - mProps.ptclLife = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set LifeSpan", + "SetLifeSpan", + &Ptcl::Emitter::ptclLife, + &Ptcl::Emitter::setPtclLife, + static_cast(value) + ); }); - connect(&mInfiniteLifeCheckBox, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state) { - QSignalBlocker b1(mLifeSpanSpinBox); + connect(&mInfiniteLifeCheckBox, &QCheckBox::clicked, this, [this](bool checked) { + const s32 newLife = checked ? sLifeInfinite : 100; - const bool isInfinite = (state == Qt::CheckState::Checked); - mProps.ptclLife = isInfinite ? sLifeInfinite : 100; - mLifeSpanSpinBox.setValue(mProps.ptclLife); - mLifeSpanSpinBox.setEnabled(!isInfinite); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Toggle Infinite Life", + "SetParticleLife", + &Ptcl::Emitter::ptclLife, + &Ptcl::Emitter::setPtclLife, + newLife + ); }); connect(&mLifeSpanRndSpinBox, &QSpinBox::valueChanged, this, [this](u64 value) { - mProps.ptclLifeRnd = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set LifeSpan Random", + "SetLifeSpanRand", + &Ptcl::Emitter::ptclLifeRandom, + &Ptcl::Emitter::setPtclLifeRandom, + static_cast(value) + ); }); } -void EmitterWidget::LifespanPropertiesWidget::setProperties(const Ptcl::Emitter::LifespanProperties& properties) { +void LifespanPropertiesWidget::populateProperties() { QSignalBlocker b1(mInfiniteLifeCheckBox); QSignalBlocker b2(mLifeSpanSpinBox); QSignalBlocker b3(mLifeSpanRndSpinBox); - mProps = properties; - - const s32 lifeSpan = mProps.ptclLife; + const s32 lifeSpan = mEmitter->ptclLife(); const bool infiniteLife = (lifeSpan == sLifeInfinite); mLifeSpanSpinBox.setValue(lifeSpan); mLifeSpanSpinBox.setDisabled(infiniteLife); mInfiniteLifeCheckBox.setChecked(infiniteLife); - mLifeSpanRndSpinBox.setValue(mProps.ptclLifeRnd); + mLifeSpanRndSpinBox.setValue(mEmitter->ptclLifeRandom()); } diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index 2ddffec..506bacd 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -355,8 +355,8 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { lifeStepRnd = emitter.emissionProperties().lifeStepRnd; emitRate = emitter.emissionProperties().emitRate; - ptclLife = emitter.lifespanProperties().ptclLife; - ptclLifeRnd = emitter.lifespanProperties().ptclLifeRnd; + ptclLife = emitter.ptclLife(); + ptclLifeRnd = emitter.ptclLifeRandom(); airResistance = emitter.velocityProperties().airResistance; blendFunc = emitter.combinerProperties().blendFunc; billboardType = emitter.billboardType(); diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index a2989ba..11a7f6d 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -26,11 +26,14 @@ std::unique_ptr Emitter::clone() const { newEmitter->mIsDirectional = mIsDirectional; newEmitter->mGravity = mGravity; + // Lifespan Properties + newEmitter->mPtclLife = mPtclLife; + newEmitter->mPtclLifeRnd = mPtclLifeRnd; + // Transform Properties newEmitter->mTransformSRT = mTransformSRT; newEmitter->mTransformRT = mTransformRT; - newEmitter->mLifespanProperties = mLifespanProperties; newEmitter->mTerminationProperties = mTerminationProperties; newEmitter->mEmissionProperties = mEmissionProperties; newEmitter->mVelocityProperties = mVelocityProperties; @@ -74,14 +77,6 @@ void Emitter::setTexture(const std::shared_ptr& texture) { mTextureHandle.set(texture); } -const Emitter::LifespanProperties& Emitter::lifespanProperties() const { - return mLifespanProperties; -} - -void Emitter::setLifespanProperties(const LifespanProperties& lifespanProperties) { - mLifespanProperties = lifespanProperties; -} - const Emitter::TerminationProperties& Emitter::terminationProperties() const { return mTerminationProperties; } @@ -278,15 +273,14 @@ void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { mIsDirectional = emitterData.isDirectional; mGravity = Math::Vector3f(emitterData.gravity.x, emitterData.gravity.y, emitterData.gravity.z); + // Lifespan Properties + mPtclLife = emitterData.ptclLife; + mPtclLifeRnd = emitterData.ptclLifeRnd; + // Transform Properties mTransformSRT = emitterData.transformSRT.toMatrix34f(); mTransformRT = emitterData.transformRT.toMatrix34f(); - mLifespanProperties = { - .ptclLife = emitterData.ptclLife, - .ptclLifeRnd = emitterData.ptclLifeRnd - }; - mTerminationProperties = { .isStopEmitInFade = emitterData.isStopEmitInFade, .alphaAddInFade = emitterData.alphaAddInFade From b87033624cccb2548a0a9a937fd632f82e90e909 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Mon, 9 Mar 2026 16:17:42 +0000 Subject: [PATCH 08/35] feat(ui): implement undo/redo for Emitter terminationProperties --- include/editor/emitterWidget/emitterWidget.h | 2 +- .../terminationPropertiesWidget.h | 15 +++----- include/ptcl/ptclEmitter.h | 23 +++++++----- src/editor/emitterWidget/emitterWidget.cpp | 11 ++---- .../terminationPropertiesWidget.cpp | 36 ++++++++++++------- src/ptcl/ptclBinary.cpp | 4 +-- src/ptcl/ptclEmitter.cpp | 20 ++++------- 7 files changed, 55 insertions(+), 56 deletions(-) diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index a39b7b4..368dc51 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -27,6 +27,7 @@ class GravityPropertiesWidget; class TransformPropertiesWidget; class LifespanPropertiesWidget; class ScalePropertiesWidget; +class TerminationPropertiesWidget; // ========================================================================== // @@ -53,7 +54,6 @@ class EmitterWidget final : public QWidget { void populateProperties(); private: - class TerminationPropertiesWidget; class EmissionPropertiesWidget; class VelocityPropertiesWidget; class VolumePropertiesWidget; diff --git a/include/editor/emitterWidget/terminationPropertiesWidget.h b/include/editor/emitterWidget/terminationPropertiesWidget.h index 1e3561a..b08ba8c 100644 --- a/include/editor/emitterWidget/terminationPropertiesWidget.h +++ b/include/editor/emitterWidget/terminationPropertiesWidget.h @@ -1,8 +1,6 @@ #pragma once -#include "editor/emitterWidget/emitterWidget.h" - -#include "ptcl/ptclEmitter.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include #include @@ -16,19 +14,16 @@ namespace PtclEditor { // ========================================================================== // -class EmitterWidget::TerminationPropertiesWidget final : public QWidget { +class TerminationPropertiesWidget final : public EmitterWidgetBase { Q_OBJECT public: explicit TerminationPropertiesWidget(QWidget* parent = nullptr); - void setProperties(const Ptcl::Emitter::TerminationProperties& properties); - -signals: - void propertiesUpdated(const Ptcl::Emitter::TerminationProperties& properties); - private: - Ptcl::Emitter::TerminationProperties mProps{}; + void populateProperties() final; + void setupConnections(); +private: QCheckBox mIsStopEmitCheckBox; QDoubleSpinBox mAlphaAddInSpinBox; }; diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index 54f5435..fcad91d 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -100,11 +100,6 @@ class Emitter { s32 emitRate{1}; }; - struct TerminationProperties { - bool isStopEmitInFade{true}; - f32 alphaAddInFade{0.0f}; - }; - struct TextureProperties { TextureWrap textureWrapT{TextureWrap::ClampToEdge}; TextureWrap textureWrapS{TextureWrap::ClampToEdge}; @@ -202,6 +197,14 @@ class Emitter { s32 ptclLifeRandom() const { return mPtclLifeRnd; } void setPtclLifeRandom(const s32 lifeRandom) { mPtclLifeRnd = lifeRandom; } + // ----- Termination Properties ----- \\ + + bool isStopEmitInFade() const { return mIsStopEmitInFade; } + void setIsStopEmitInFade(bool isStop) { mIsStopEmitInFade = isStop; } + + f32 alphaAddInFade() const { return mAlphaAddInFade; } + void setAlphaAddInFade(f32 alpha) { mAlphaAddInFade = alpha; } + // ----- Transform Properties ----- \\ const Math::Matrix34f& transformRT() const { return mTransformRT; } @@ -261,9 +264,6 @@ class Emitter { const TextureHandle& textureHandle() const; void setTexture(const std::shared_ptr& texture); - const TerminationProperties& terminationProperties() const; - void setTerminationProperties(const TerminationProperties& terminationProperties); - const EmissionProperties& emissionProperties() const; void setEmissionProperties(const EmissionProperties& emissionProperties); @@ -338,6 +338,10 @@ class Emitter { s32 mPtclLife{100}; s32 mPtclLifeRnd{0}; + // Termination Properties + bool mIsStopEmitInFade{true}; + f32 mAlphaAddInFade{0.0f}; + // Transform Properties Math::Matrix34f mTransformSRT{ {1.0f, 0.0f, 0.0f, 0.0f}, @@ -354,7 +358,8 @@ class Emitter { ScaleAnim mScaleAnim{}; f32 mScaleRand{0.0f}; - TerminationProperties mTerminationProperties{}; + + EmissionProperties mEmissionProperties{}; VelocityProperties mVelocityProperties{}; VolumeProperties mVolumeProperties{}; diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index ee2fca8..25bdab1 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -127,13 +127,6 @@ void EmitterWidget::setupConnections() { emit propertiesChanged(); }); - // Termination Properties - connect(mTerminationProperties, &TerminationPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::TerminationProperties& properties) { - if (!mEmitter) { return; } - mEmitter->setTerminationProperties(properties); - emit propertiesChanged(); - }); - // Emission Properties connect(mEmissionProperties, &EmissionPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::EmissionProperties& properties) { if (!mEmitter) { return; } @@ -239,6 +232,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mTransformProperties->setDocument(document); mLifespanProperties->setDocument(document); mScaleProperties->setDocument(document); + mTerminationProperties->setDocument(document); } void EmitterWidget::setSelection(Ptcl::Selection* selection) { @@ -249,6 +243,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mTransformProperties->setSelection(selection); mLifespanProperties->setSelection(selection); mScaleProperties->setSelection(selection); + mTerminationProperties->setSelection(selection); connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -287,7 +282,6 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { } void EmitterWidget::populateProperties() { - QSignalBlocker b5(mTerminationProperties); QSignalBlocker b6(mEmissionProperties); QSignalBlocker b7(mVelocityProperties); QSignalBlocker b8(mColorProperties); @@ -300,7 +294,6 @@ void EmitterWidget::populateProperties() { QSignalBlocker b17(mFieldEditorWidget); QSignalBlocker b18(mStripeEditorWidget); - mTerminationProperties->setProperties(mEmitter->terminationProperties()); mEmissionProperties->setProperties(mEmitter->emissionProperties()); mVelocityProperties->setProperties(mEmitter->velocityProperties()); mVolumeProperties->setProperties(mEmitter->volumeProperties()); diff --git a/src/editor/emitterWidget/terminationPropertiesWidget.cpp b/src/editor/emitterWidget/terminationPropertiesWidget.cpp index eea48c8..7e2c079 100644 --- a/src/editor/emitterWidget/terminationPropertiesWidget.cpp +++ b/src/editor/emitterWidget/terminationPropertiesWidget.cpp @@ -9,8 +9,8 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::TerminationPropertiesWidget::TerminationPropertiesWidget(QWidget* parent) : - QWidget{parent} { +TerminationPropertiesWidget::TerminationPropertiesWidget(QWidget* parent) : + EmitterWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); @@ -19,25 +19,37 @@ EmitterWidget::TerminationPropertiesWidget::TerminationPropertiesWidget(QWidget* mainLayout->addRow("Stop Emission During Fade:", &mIsStopEmitCheckBox); mainLayout->addRow("Alpha Add During Fade:", &mAlphaAddInSpinBox); - connect(&mIsStopEmitCheckBox, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state) { - mProps.isStopEmitInFade = (state == Qt::CheckState::Checked); - emit propertiesUpdated(mProps); + setupConnections(); +} + +void TerminationPropertiesWidget::setupConnections() { + connect(&mIsStopEmitCheckBox, &QCheckBox::checkStateChanged, this, [this](bool checked) { + setEmitterProperty( + "Set Stop Emission", + "SetStopEmission", + &Ptcl::Emitter::isStopEmitInFade, + &Ptcl::Emitter::setIsStopEmitInFade, + checked + ); }); connect(&mAlphaAddInSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mProps.alphaAddInFade = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Alpha Add In Fade", + "SetAlphaAddIn", + &Ptcl::Emitter::alphaAddInFade, + &Ptcl::Emitter::setAlphaAddInFade, + value + ); }); } -void EmitterWidget::TerminationPropertiesWidget::setProperties(const Ptcl::Emitter::TerminationProperties& properties) { +void TerminationPropertiesWidget::populateProperties() { QSignalBlocker b1(mIsStopEmitCheckBox); QSignalBlocker b2(mAlphaAddInSpinBox); - mProps = properties; - - mIsStopEmitCheckBox.setChecked(mProps.isStopEmitInFade); - mAlphaAddInSpinBox.setValue(mProps.alphaAddInFade); + mIsStopEmitCheckBox.setChecked(mEmitter->isStopEmitInFade()); + mAlphaAddInSpinBox.setValue(mEmitter->alphaAddInFade()); } diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index 506bacd..604fc0f 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -338,7 +338,7 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { isTexPatAnim = emitter.textureProperties().isTexPatAnim; isVelLook = emitter.isVelLook(); volumeTblIndex = emitter.volumeProperties().volumeTblIndex; - isStopEmitInFade = emitter.terminationProperties().isStopEmitInFade; + isStopEmitInFade = emitter.isStopEmitInFade(); volumeType = emitter.volumeProperties().volumeType; volumeRadius = emitter.volumeProperties().volumeRadius; volumeSweepStart = emitter.volumeProperties().volumeSweepStart; @@ -391,7 +391,7 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { rotBasis = emitter.rotationProperties().rotBasis; transformSRT = emitter.transformSRT(); transformRT = emitter.transformRT(); - alphaAddInFade = emitter.terminationProperties().alphaAddInFade; + alphaAddInFade = emitter.alphaAddInFade(); numTexPat = emitter.textureProperties().numTexPat; numTexDivX = emitter.textureProperties().numTexDivX; numTexDivY = emitter.textureProperties().numTexDivY; diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index 11a7f6d..9173771 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -34,7 +34,10 @@ std::unique_ptr Emitter::clone() const { newEmitter->mTransformSRT = mTransformSRT; newEmitter->mTransformRT = mTransformRT; - newEmitter->mTerminationProperties = mTerminationProperties; + // Termination Properties + newEmitter->mIsStopEmitInFade = mIsStopEmitInFade; + newEmitter->mAlphaAddInFade = mAlphaAddInFade; + newEmitter->mEmissionProperties = mEmissionProperties; newEmitter->mVelocityProperties = mVelocityProperties; newEmitter->mVolumeProperties = mVolumeProperties; @@ -77,14 +80,6 @@ void Emitter::setTexture(const std::shared_ptr& texture) { mTextureHandle.set(texture); } -const Emitter::TerminationProperties& Emitter::terminationProperties() const { - return mTerminationProperties; -} - -void Emitter::setTerminationProperties(const TerminationProperties& terminationProperties) { - mTerminationProperties = terminationProperties; -} - const Emitter::EmissionProperties& Emitter::emissionProperties() const { return mEmissionProperties; } @@ -281,10 +276,9 @@ void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { mTransformSRT = emitterData.transformSRT.toMatrix34f(); mTransformRT = emitterData.transformRT.toMatrix34f(); - mTerminationProperties = { - .isStopEmitInFade = emitterData.isStopEmitInFade, - .alphaAddInFade = emitterData.alphaAddInFade - }; + // Termination Properties + mIsStopEmitInFade = emitterData.isStopEmitInFade; + mAlphaAddInFade = emitterData.alphaAddInFade; mEmissionProperties = { .startFrame = emitterData.startFrame, From 55fac1dd0013b636b57871ea9db9acb4ad315c37 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Mon, 9 Mar 2026 16:50:11 +0000 Subject: [PATCH 09/35] feat(ui): implement undo/redo for Emitter emissionProperties --- .../emitterWidget/emissionPropertiesWidget.h | 27 +++--- include/editor/emitterWidget/emitterWidget.h | 3 +- include/ptcl/ptclEmitter.h | 36 +++++--- .../emissionPropertiesWidget.cpp | 87 ++++++++++++------- src/editor/emitterWidget/emitterWidget.cpp | 11 +-- src/ptcl/ptclBinary.cpp | 10 +-- src/ptcl/ptclEmitter.cpp | 29 +++---- 7 files changed, 113 insertions(+), 90 deletions(-) diff --git a/include/editor/emitterWidget/emissionPropertiesWidget.h b/include/editor/emitterWidget/emissionPropertiesWidget.h index 9e48865..a9d056f 100644 --- a/include/editor/emitterWidget/emissionPropertiesWidget.h +++ b/include/editor/emitterWidget/emissionPropertiesWidget.h @@ -1,8 +1,6 @@ #pragma once -#include "editor/emitterWidget/emitterWidget.h" - -#include "ptcl/ptclEmitter.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include #include @@ -16,25 +14,22 @@ namespace PtclEditor { // ========================================================================== // -class EmitterWidget::EmissionPropertiesWidget final : public QWidget { +class EmissionPropertiesWidget final : public EmitterWidgetBase { Q_OBJECT public: explicit EmissionPropertiesWidget(QWidget* parent = nullptr); - void setProperties(const Ptcl::Emitter::EmissionProperties& properties); - -signals: - void propertiesUpdated(const Ptcl::Emitter::EmissionProperties& properties); +private: + void populateProperties() final; + void setupConnections(); private: - Ptcl::Emitter::EmissionProperties mProps{}; - - QSpinBox mStartFrameSpinBox; - QSpinBox mEndFrameSpinBox; - QCheckBox mInfiniteEmitCheckBox; - QSpinBox mLifeStepSpinBox; - QSpinBox mLifeStepRndSpinBox; - QSpinBox mEmitRateSpinBox; + QSpinBox mStartFrameSpinBox{}; + QSpinBox mEndFrameSpinBox{}; + QCheckBox mInfiniteEmitCheckBox{}; + QSpinBox mLifeStepSpinBox{}; + QSpinBox mLifeStepRndSpinBox{}; + QSpinBox mEmitRateSpinBox{}; static constexpr s32 sEmitInfinite = std::numeric_limits::max(); }; diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index 368dc51..bc5bfc8 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -28,6 +28,8 @@ class TransformPropertiesWidget; class LifespanPropertiesWidget; class ScalePropertiesWidget; class TerminationPropertiesWidget; +class EmissionPropertiesWidget; + // ========================================================================== // @@ -54,7 +56,6 @@ class EmitterWidget final : public QWidget { void populateProperties(); private: - class EmissionPropertiesWidget; class VelocityPropertiesWidget; class VolumePropertiesWidget; class ColorPropertiesWidget; diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index fcad91d..7214928 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -92,14 +92,6 @@ class Emitter { f32 airResistance{1.0f}; }; - struct EmissionProperties { - s32 startFrame{0}; - s32 endFrame{1}; - s32 lifeStep{10}; - s32 lifeStepRnd{1}; - s32 emitRate{1}; - }; - struct TextureProperties { TextureWrap textureWrapT{TextureWrap::ClampToEdge}; TextureWrap textureWrapS{TextureWrap::ClampToEdge}; @@ -257,6 +249,23 @@ class Emitter { f32 scaleRand() const { return mScaleRand; } void setScaleRand(f32 scaleRand) { mScaleRand = scaleRand; } + // ----- Emission Properties ----- \\ + + s32 emitStartFrame() const { return mEmitStartFrame; } + void setEmitStartFrame(s32 startFrame) { mEmitStartFrame = startFrame; } + + s32 emitEndFrame() const { return mEmitEndFrame; } + void setEmitEndFrame(s32 endFrame) { mEmitEndFrame = endFrame; } + + s32 lifeStep() const { return mLifeStep; } + void setLifeStep(s32 lifeStep) { mLifeStep = lifeStep; } + + s32 lifeStepRandom() const { return mLifeStepRnd; } + void setLifeStepRandom(s32 random) { mLifeStepRnd = random; } + + s32 emitRate() const { return mEmitRate; } + void setEmitRate(s32 emitRate) { mEmitRate = emitRate; } + BitFlag& flags(); const BitFlag& flags() const; @@ -264,9 +273,6 @@ class Emitter { const TextureHandle& textureHandle() const; void setTexture(const std::shared_ptr& texture); - const EmissionProperties& emissionProperties() const; - void setEmissionProperties(const EmissionProperties& emissionProperties); - const VelocityProperties& velocityProperties() const; void setVelocityProperties(const VelocityProperties& velocityProperties); @@ -358,9 +364,13 @@ class Emitter { ScaleAnim mScaleAnim{}; f32 mScaleRand{0.0f}; + // Emission Properties + s32 mEmitStartFrame{0}; + s32 mEmitEndFrame{1}; + s32 mLifeStep{10}; + s32 mLifeStepRnd{1}; + s32 mEmitRate{1}; - - EmissionProperties mEmissionProperties{}; VelocityProperties mVelocityProperties{}; VolumeProperties mVolumeProperties{}; ColorProperties mColorProperties{}; diff --git a/src/editor/emitterWidget/emissionPropertiesWidget.cpp b/src/editor/emitterWidget/emissionPropertiesWidget.cpp index 7a696b7..608c97e 100644 --- a/src/editor/emitterWidget/emissionPropertiesWidget.cpp +++ b/src/editor/emitterWidget/emissionPropertiesWidget.cpp @@ -10,8 +10,8 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::EmissionPropertiesWidget::EmissionPropertiesWidget(QWidget* parent) : - QWidget{parent} { +EmissionPropertiesWidget::EmissionPropertiesWidget(QWidget* parent) : + EmitterWidgetBase{parent} { auto* mainLayout = new QGridLayout(this); @@ -21,66 +21,95 @@ EmitterWidget::EmissionPropertiesWidget::EmissionPropertiesWidget(QWidget* paren mLifeStepRndSpinBox.setRange(0, std::numeric_limits::max()); mEmitRateSpinBox.setRange(0, std::numeric_limits::max()); - // Row 0: Infinite Emission mainLayout->addWidget(&mInfiniteEmitCheckBox, 0, 1); mainLayout->addWidget(new QLabel("Infinite Emission:"), 0, 0); - // Row 1: Start and End Frame mainLayout->addWidget(new QLabel("Emission Start Frame:"), 1, 0); mainLayout->addWidget(&mStartFrameSpinBox, 1, 1); mainLayout->addWidget(new QLabel("Emission End Frame:"), 1, 2); mainLayout->addWidget(&mEndFrameSpinBox, 1, 3); - // Row 2: Interval and Interval Rand mainLayout->addWidget(new QLabel("Emission Interval:"), 2, 0); mainLayout->addWidget(&mLifeStepSpinBox, 2, 1); mainLayout->addWidget(new QLabel("Random Emission Interval:"), 2, 2); mainLayout->addWidget(&mLifeStepRndSpinBox, 2, 3); - // Row 3: Emission Rate mainLayout->addWidget(new QLabel("Emission Rate:"), 3, 0); mainLayout->addWidget(&mEmitRateSpinBox, 3, 1); mainLayout->setColumnStretch(1, 1); mainLayout->setColumnStretch(3, 1); + setupConnections(); +} + +void EmissionPropertiesWidget::setupConnections() { connect(&mStartFrameSpinBox, &QSpinBox::valueChanged, this, [this](u64 value) { - mProps.startFrame = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Emission Start Frame", + "SetEmitStartFrame", + &Ptcl::Emitter::emitStartFrame, + &Ptcl::Emitter::setEmitStartFrame, + static_cast(value) + ); }); connect(&mEndFrameSpinBox, &QSpinBox::valueChanged, this, [this](u64 value) { - mProps.endFrame = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Emission End Frame", + "SetEmitEndFrame", + &Ptcl::Emitter::emitEndFrame, + &Ptcl::Emitter::setEmitEndFrame, + static_cast(value) + ); }); - connect(&mInfiniteEmitCheckBox, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state) { + connect(&mInfiniteEmitCheckBox, &QCheckBox::checkStateChanged, this, [this](bool checked) { QSignalBlocker b1(mEndFrameSpinBox); - const bool isInfinite = (state == Qt::CheckState::Checked); - mProps.endFrame = isInfinite ? sEmitInfinite : 1; - mEndFrameSpinBox.setValue(mProps.endFrame); - mEndFrameSpinBox.setEnabled(!isInfinite); - emit propertiesUpdated(mProps); + const s32 endFrame = checked ? sEmitInfinite : 1; + + setEmitterProperty( + "Toggle Infinite Emission", + "SetEmitInfinite", + &Ptcl::Emitter::emitEndFrame, + &Ptcl::Emitter::setEmitEndFrame, + endFrame + ); }); connect(&mLifeStepSpinBox, &QSpinBox::valueChanged, this, [this](u64 value) { - mProps.lifeStep = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Emission Interval", + "SetEmitLifeStep", + &Ptcl::Emitter::lifeStep, + &Ptcl::Emitter::setLifeStep, + static_cast(value) + ); }); connect(&mLifeStepRndSpinBox, &QSpinBox::valueChanged, this, [this](u64 value) { - mProps.lifeStepRnd = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Emission Random Interval", + "SetEmitLifeStepRandom", + &Ptcl::Emitter::lifeStepRandom, + &Ptcl::Emitter::setLifeStepRandom, + static_cast(value) + ); }); connect(&mEmitRateSpinBox, &QSpinBox::valueChanged, this, [this](u64 value) { - mProps.emitRate = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Emission Rate", + "SetEmitRate", + &Ptcl::Emitter::emitRate, + &Ptcl::Emitter::setEmitRate, + static_cast(value) + ); }); } -void EmitterWidget::EmissionPropertiesWidget::setProperties(const Ptcl::Emitter::EmissionProperties& properties) { +void EmissionPropertiesWidget::populateProperties() { QSignalBlocker b1(mStartFrameSpinBox); QSignalBlocker b2(mEndFrameSpinBox); QSignalBlocker b3(mLifeStepSpinBox); @@ -88,21 +117,19 @@ void EmitterWidget::EmissionPropertiesWidget::setProperties(const Ptcl::Emitter: QSignalBlocker b5(mInfiniteEmitCheckBox); QSignalBlocker b6(mEmitRateSpinBox); - mProps = properties; - - mStartFrameSpinBox.setValue(mProps.startFrame); + mStartFrameSpinBox.setValue(mEmitter->emitStartFrame()); - const s32 endFrame = mProps.endFrame; + const s32 endFrame = mEmitter->emitEndFrame(); const bool infiniteEmission = (endFrame == sEmitInfinite); mEndFrameSpinBox.setValue(endFrame); mEndFrameSpinBox.setDisabled(infiniteEmission); mInfiniteEmitCheckBox.setChecked(infiniteEmission); - mLifeStepSpinBox.setValue(mProps.lifeStep); - mLifeStepRndSpinBox.setValue(mProps.lifeStepRnd); + mLifeStepSpinBox.setValue(mEmitter->lifeStep()); + mLifeStepRndSpinBox.setValue(mEmitter->lifeStepRandom()); - mEmitRateSpinBox.setValue(mProps.emitRate); + mEmitRateSpinBox.setValue(mEmitter->emitRate()); } diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index 25bdab1..70614fa 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -127,13 +127,6 @@ void EmitterWidget::setupConnections() { emit propertiesChanged(); }); - // Emission Properties - connect(mEmissionProperties, &EmissionPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::EmissionProperties& properties) { - if (!mEmitter) { return; } - mEmitter->setEmissionProperties(properties); - emit propertiesChanged(); - }); - // Volume Properties connect(mVolumeProperties, &VolumePropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::VolumeProperties& properties) { if (!mEmitter) { return; } @@ -233,6 +226,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mLifespanProperties->setDocument(document); mScaleProperties->setDocument(document); mTerminationProperties->setDocument(document); + mEmissionProperties->setDocument(document); } void EmitterWidget::setSelection(Ptcl::Selection* selection) { @@ -244,6 +238,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mLifespanProperties->setSelection(selection); mScaleProperties->setSelection(selection); mTerminationProperties->setSelection(selection); + mEmissionProperties->setSelection(selection); connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -282,7 +277,6 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { } void EmitterWidget::populateProperties() { - QSignalBlocker b6(mEmissionProperties); QSignalBlocker b7(mVelocityProperties); QSignalBlocker b8(mColorProperties); QSignalBlocker b9(mAlphaProperties); @@ -294,7 +288,6 @@ void EmitterWidget::populateProperties() { QSignalBlocker b17(mFieldEditorWidget); QSignalBlocker b18(mStripeEditorWidget); - mEmissionProperties->setProperties(mEmitter->emissionProperties()); mVelocityProperties->setProperties(mEmitter->velocityProperties()); mVolumeProperties->setProperties(mEmitter->volumeProperties()); mColorProperties->setProperties(mEmitter->colorProperties()); diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index 604fc0f..1fb03ac 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -349,12 +349,12 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { initVelRnd = emitter.velocityProperties().initVelRnd; spreadVec = emitter.velocityProperties().spreadVec; - startFrame = emitter.emissionProperties().startFrame; - endFrame = emitter.emissionProperties().endFrame; - lifeStep = emitter.emissionProperties().lifeStep; - lifeStepRnd = emitter.emissionProperties().lifeStepRnd; + startFrame = emitter.emitStartFrame(); + endFrame = emitter.emitEndFrame(); + lifeStep = emitter.lifeStep(); + lifeStepRnd = emitter.lifeStepRandom(); - emitRate = emitter.emissionProperties().emitRate; + emitRate = emitter.emitRate(); ptclLife = emitter.ptclLife(); ptclLifeRnd = emitter.ptclLifeRandom(); airResistance = emitter.velocityProperties().airResistance; diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index 9173771..f8929e7 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -38,7 +38,13 @@ std::unique_ptr Emitter::clone() const { newEmitter->mIsStopEmitInFade = mIsStopEmitInFade; newEmitter->mAlphaAddInFade = mAlphaAddInFade; - newEmitter->mEmissionProperties = mEmissionProperties; + // Emission Properties + newEmitter->mEmitStartFrame = mEmitStartFrame; + newEmitter->mEmitEndFrame = mEmitEndFrame; + newEmitter->mLifeStep = mLifeStep; + newEmitter->mLifeStepRnd = mLifeStepRnd; + newEmitter->mEmitRate = mEmitRate; + newEmitter->mVelocityProperties = mVelocityProperties; newEmitter->mVolumeProperties = mVolumeProperties; newEmitter->mColorProperties = mColorProperties; @@ -80,14 +86,6 @@ void Emitter::setTexture(const std::shared_ptr& texture) { mTextureHandle.set(texture); } -const Emitter::EmissionProperties& Emitter::emissionProperties() const { - return mEmissionProperties; -} - -void Emitter::setEmissionProperties(const EmissionProperties& emissionProperties) { - mEmissionProperties = emissionProperties; -} - const Emitter::VelocityProperties& Emitter::velocityProperties() const { return mVelocityProperties; } @@ -280,13 +278,12 @@ void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { mIsStopEmitInFade = emitterData.isStopEmitInFade; mAlphaAddInFade = emitterData.alphaAddInFade; - mEmissionProperties = { - .startFrame = emitterData.startFrame, - .endFrame = emitterData.endFrame, - .lifeStep = emitterData.lifeStep, - .lifeStepRnd = emitterData.lifeStepRnd, - .emitRate = emitterData.emitRate - }; + // Emission Properties + mEmitStartFrame = emitterData.startFrame; + mEmitEndFrame = emitterData.endFrame; + mLifeStep = emitterData.lifeStep; + mLifeStepRnd = emitterData.lifeStepRnd; + mEmitRate = emitterData.emitRate; mVelocityProperties = { .figureVel = emitterData.figureVel, From 12bbd408508da183549310e500b674c95db788d2 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Mon, 9 Mar 2026 18:39:25 +0000 Subject: [PATCH 10/35] feat(ui): implement undo/redo for Emitter velocityProperties --- include/editor/emitterWidget/emitterWidget.h | 2 +- .../emitterWidget/velocityPropertiesWidget.h | 15 ++-- include/ptcl/ptclEmitter.h | 41 +++++++--- src/editor/emitterWidget/emitterWidget.cpp | 11 +-- .../velocityPropertiesWidget.cpp | 82 +++++++++++++------ src/ptcl/ptclBinary.cpp | 12 +-- src/ptcl/ptclEmitter.cpp | 32 ++++---- 7 files changed, 116 insertions(+), 79 deletions(-) diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index bc5bfc8..8391ea4 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -29,6 +29,7 @@ class LifespanPropertiesWidget; class ScalePropertiesWidget; class TerminationPropertiesWidget; class EmissionPropertiesWidget; +class VelocityPropertiesWidget; // ========================================================================== // @@ -56,7 +57,6 @@ class EmitterWidget final : public QWidget { void populateProperties(); private: - class VelocityPropertiesWidget; class VolumePropertiesWidget; class ColorPropertiesWidget; class AlphaPropertiesWidget; diff --git a/include/editor/emitterWidget/velocityPropertiesWidget.h b/include/editor/emitterWidget/velocityPropertiesWidget.h index 8dbd46e..d32a7f7 100644 --- a/include/editor/emitterWidget/velocityPropertiesWidget.h +++ b/include/editor/emitterWidget/velocityPropertiesWidget.h @@ -1,9 +1,7 @@ #pragma once #include "editor/components/vectorSpinBox.h" -#include "editor/emitterWidget/emitterWidget.h" - -#include "ptcl/ptclEmitter.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include #include @@ -16,19 +14,16 @@ namespace PtclEditor { // ========================================================================== // -class EmitterWidget::VelocityPropertiesWidget final : public QWidget { +class VelocityPropertiesWidget final : public EmitterWidgetBase { Q_OBJECT public: explicit VelocityPropertiesWidget(QWidget* parent = nullptr); - void setProperties(const Ptcl::Emitter::VelocityProperties& properties); - -signals: - void propertiesUpdated(const Ptcl::Emitter::VelocityProperties& properties); - private: - Ptcl::Emitter::VelocityProperties mProps{}; + void populateProperties() final; + void setupConnections(); +private: QDoubleSpinBox mFigureVelSpinbox{}; VectorSpinBox mVelDirSpinbox{}; QDoubleSpinBox mInitVelSpinbox{}; diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index 7214928..882274b 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -83,15 +83,6 @@ class Emitter { s32 volumeSweepParam{0}; }; - struct VelocityProperties { - f32 figureVel{0.1f}; - Math::Vector3f emitterVelDir{0.0f, 1.0f, 0.0f}; - f32 initVel{0.0f}; - f32 initVelRnd{0.0f}; - Math::Vector3f spreadVec{0.0f, 0.0f, 0.0f}; - f32 airResistance{1.0f}; - }; - struct TextureProperties { TextureWrap textureWrapT{TextureWrap::ClampToEdge}; TextureWrap textureWrapS{TextureWrap::ClampToEdge}; @@ -266,6 +257,26 @@ class Emitter { s32 emitRate() const { return mEmitRate; } void setEmitRate(s32 emitRate) { mEmitRate = emitRate; } + // ----- Velocity Properties ----- \\ + + f32 figureVelocity() const { return mFigureVelocity; } + void setFigureVelocity(f32 velocity) { mFigureVelocity = velocity; } + + const Math::Vector3f& velocityDirection() const { return mVelocityDir; } + void setVelocityDirection(const Math::Vector3f& direction) { mVelocityDir = direction; } + + f32 initialVelocity() const { return mInitVelocity; } + void setInitialVelocity(f32 velocity) { mInitVelocity = velocity; } + + f32 initialVelocityRandom() const { return mInitVelocityRnd; } + void setInitialVelocityRandom(f32 random) { mInitVelocityRnd = random; } + + const Math::Vector3f& spreadVector() const { return mSpreadVec; } + void setSpreadVector(const Math::Vector3f& vector) { mSpreadVec = vector; } + + f32 airResistance() const { return mAirResistance; } + void setAirResistance(f32 resistance) { mAirResistance = resistance; } + BitFlag& flags(); const BitFlag& flags() const; @@ -273,9 +284,6 @@ class Emitter { const TextureHandle& textureHandle() const; void setTexture(const std::shared_ptr& texture); - const VelocityProperties& velocityProperties() const; - void setVelocityProperties(const VelocityProperties& velocityProperties); - const VolumeProperties& volumeProperties() const; void setVolumeProperties(const VolumeProperties& volumeProperties); @@ -371,7 +379,14 @@ class Emitter { s32 mLifeStepRnd{1}; s32 mEmitRate{1}; - VelocityProperties mVelocityProperties{}; + // Velocity Properties + f32 mFigureVelocity{0.1f}; + Math::Vector3f mVelocityDir{0.0f, 1.0f, 0.0f}; + f32 mInitVelocity{0.0f}; + f32 mInitVelocityRnd{0.0f}; + Math::Vector3f mSpreadVec{0.0f, 0.0f, 0.0f}; + f32 mAirResistance{1.0f}; + VolumeProperties mVolumeProperties{}; ColorProperties mColorProperties{}; AlphaProperties mAlphaProperties{}; diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index 70614fa..c94ddcc 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -134,13 +134,6 @@ void EmitterWidget::setupConnections() { emit propertiesChanged(); }); - // Velocity Properties - connect(mVelocityProperties, &VelocityPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::VelocityProperties& properties) { - if (!mEmitter) { return; } - mEmitter->setVelocityProperties(properties); - emit propertiesChanged(); - }); - // Color Properties connect(mColorProperties, &ColorPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::ColorProperties& properties) { if (!mEmitter) { return; } @@ -227,6 +220,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mScaleProperties->setDocument(document); mTerminationProperties->setDocument(document); mEmissionProperties->setDocument(document); + mVelocityProperties->setDocument(document); } void EmitterWidget::setSelection(Ptcl::Selection* selection) { @@ -239,6 +233,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mScaleProperties->setSelection(selection); mTerminationProperties->setSelection(selection); mEmissionProperties->setSelection(selection); + mVelocityProperties->setSelection(selection); connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -277,7 +272,6 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { } void EmitterWidget::populateProperties() { - QSignalBlocker b7(mVelocityProperties); QSignalBlocker b8(mColorProperties); QSignalBlocker b9(mAlphaProperties); QSignalBlocker b11(mRotationProperties); @@ -288,7 +282,6 @@ void EmitterWidget::populateProperties() { QSignalBlocker b17(mFieldEditorWidget); QSignalBlocker b18(mStripeEditorWidget); - mVelocityProperties->setProperties(mEmitter->velocityProperties()); mVolumeProperties->setProperties(mEmitter->volumeProperties()); mColorProperties->setProperties(mEmitter->colorProperties()); mAlphaProperties->setProperties(mEmitter->alphaProperties()); diff --git a/src/editor/emitterWidget/velocityPropertiesWidget.cpp b/src/editor/emitterWidget/velocityPropertiesWidget.cpp index e6aca9e..863fc78 100644 --- a/src/editor/emitterWidget/velocityPropertiesWidget.cpp +++ b/src/editor/emitterWidget/velocityPropertiesWidget.cpp @@ -9,8 +9,8 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::VelocityPropertiesWidget::VelocityPropertiesWidget(QWidget* parent) : - QWidget{parent} { +VelocityPropertiesWidget::VelocityPropertiesWidget(QWidget* parent) : + EmitterWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); @@ -26,38 +26,76 @@ EmitterWidget::VelocityPropertiesWidget::VelocityPropertiesWidget(QWidget* paren mainLayout->addRow("Spread Vector:", &mSpreadVecSpinbox); mainLayout->addRow("Air Resistance:", &mAirResistanceSpinbox); + setupConnections(); +} + +void VelocityPropertiesWidget::setupConnections() { connect(&mFigureVelSpinbox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mProps.figureVel = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Figure Velocity", + "SetFigureVelocity", + &Ptcl::Emitter::figureVelocity, + &Ptcl::Emitter::setFigureVelocity, + static_cast(value) + ); }); connect(&mVelDirSpinbox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mProps.emitterVelDir = mVelDirSpinbox.getVector(); - emit propertiesUpdated(mProps); + const auto direction = mVelDirSpinbox.getVector(); + + setEmitterProperty( + "Set Velocity Direction", + "SetVelocityDir", + &Ptcl::Emitter::velocityDirection, + &Ptcl::Emitter::setVelocityDirection, + direction + ); }); connect(&mInitVelSpinbox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mProps.initVel = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Initial Velocity", + "SetInitialVelocity", + &Ptcl::Emitter::initialVelocity, + &Ptcl::Emitter::setInitialVelocity, + static_cast(value) + ); }); connect(&mVelRandomSpinbox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mProps.initVelRnd = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Initial Velocity Random", + "SetInitVelocityRand", + &Ptcl::Emitter::initialVelocityRandom, + &Ptcl::Emitter::setInitialVelocityRandom, + static_cast(value) + ); }); connect(&mSpreadVecSpinbox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mProps.spreadVec = mSpreadVecSpinbox.getVector(); - emit propertiesUpdated(mProps); + const auto spread = mSpreadVecSpinbox.getVector(); + + setEmitterProperty( + "Set Spread Vector", + "SetSpreadVector", + &Ptcl::Emitter::spreadVector, + &Ptcl::Emitter::setSpreadVector, + spread + ); }); connect(&mAirResistanceSpinbox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mProps.airResistance = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Air Resistance", + "SetAirResistance", + &Ptcl::Emitter::airResistance, + &Ptcl::Emitter::setAirResistance, + static_cast(value) + ); }); } -void EmitterWidget::VelocityPropertiesWidget::setProperties(const Ptcl::Emitter::VelocityProperties& properties) { +void VelocityPropertiesWidget::populateProperties() { QSignalBlocker b1(mFigureVelSpinbox); QSignalBlocker b2(mVelDirSpinbox); QSignalBlocker b3(mInitVelSpinbox); @@ -65,14 +103,12 @@ void EmitterWidget::VelocityPropertiesWidget::setProperties(const Ptcl::Emitter: QSignalBlocker b5(mSpreadVecSpinbox); QSignalBlocker b6(mAirResistanceSpinbox); - mProps = properties; - - mFigureVelSpinbox.setValue(mProps.figureVel); - mVelDirSpinbox.setVector(mProps.emitterVelDir); - mInitVelSpinbox.setValue(mProps.initVel); - mVelRandomSpinbox.setValue(mProps.initVelRnd); - mSpreadVecSpinbox.setVector(mProps.spreadVec); - mAirResistanceSpinbox.setValue(mProps.airResistance); + mFigureVelSpinbox.setValue(mEmitter->figureVelocity()); + mVelDirSpinbox.setVector(mEmitter->velocityDirection()); + mInitVelSpinbox.setValue(mEmitter->initialVelocity()); + mVelRandomSpinbox.setValue(mEmitter->initialVelocityRandom()); + mSpreadVecSpinbox.setVector(mEmitter->spreadVector()); + mAirResistanceSpinbox.setValue(mEmitter->airResistance()); update(); } diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index 1fb03ac..4d3b6d9 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -343,11 +343,11 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { volumeRadius = emitter.volumeProperties().volumeRadius; volumeSweepStart = emitter.volumeProperties().volumeSweepStart; volumeSweepParam = emitter.volumeProperties().volumeSweepParam; - figureVel = emitter.velocityProperties().figureVel; - emitterVelDir = emitter.velocityProperties().emitterVelDir; - initVel = emitter.velocityProperties().initVel; - initVelRnd = emitter.velocityProperties().initVelRnd; - spreadVec = emitter.velocityProperties().spreadVec; + figureVel = emitter.figureVelocity(); + emitterVelDir = emitter.velocityDirection(); + initVel = emitter.initialVelocity(); + initVelRnd = emitter.initialVelocityRandom(); + spreadVec = emitter.spreadVector(); startFrame = emitter.emitStartFrame(); endFrame = emitter.emitEndFrame(); @@ -357,7 +357,7 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { emitRate = emitter.emitRate(); ptclLife = emitter.ptclLife(); ptclLifeRnd = emitter.ptclLifeRandom(); - airResistance = emitter.velocityProperties().airResistance; + airResistance = emitter.airResistance(); blendFunc = emitter.combinerProperties().blendFunc; billboardType = emitter.billboardType(); depthFunc = emitter.combinerProperties().depthFunc; diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index f8929e7..7487910 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -45,7 +45,14 @@ std::unique_ptr Emitter::clone() const { newEmitter->mLifeStepRnd = mLifeStepRnd; newEmitter->mEmitRate = mEmitRate; - newEmitter->mVelocityProperties = mVelocityProperties; + // Velocity Properties + newEmitter->mFigureVelocity = mFigureVelocity; + newEmitter->mVelocityDir = mVelocityDir; + newEmitter->mInitVelocity = mInitVelocity; + newEmitter->mInitVelocityRnd = mInitVelocityRnd; + newEmitter->mSpreadVec = mSpreadVec; + newEmitter->mAirResistance = mAirResistance; + newEmitter->mVolumeProperties = mVolumeProperties; newEmitter->mColorProperties = mColorProperties; newEmitter->mAlphaProperties = mAlphaProperties; @@ -86,14 +93,6 @@ void Emitter::setTexture(const std::shared_ptr& texture) { mTextureHandle.set(texture); } -const Emitter::VelocityProperties& Emitter::velocityProperties() const { - return mVelocityProperties; -} - -void Emitter::setVelocityProperties(const VelocityProperties& velocityProperties) { - mVelocityProperties = velocityProperties; -} - const Emitter::VolumeProperties& Emitter::volumeProperties() const { return mVolumeProperties; } @@ -285,14 +284,13 @@ void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { mLifeStepRnd = emitterData.lifeStepRnd; mEmitRate = emitterData.emitRate; - mVelocityProperties = { - .figureVel = emitterData.figureVel, - .emitterVelDir = Math::Vector3f(emitterData.emitterVelDir.x, emitterData.emitterVelDir.y, emitterData.emitterVelDir.z), - .initVel = emitterData.initVel, - .initVelRnd = emitterData.initVelRnd, - .spreadVec = Math::Vector3f(emitterData.spreadVec.x, emitterData.spreadVec.y, emitterData.spreadVec.z), - .airResistance = emitterData.airResistance - }; + // Velocity Properties + mFigureVelocity = emitterData.figureVel; + mVelocityDir = Math::Vector3f(emitterData.emitterVelDir.x, emitterData.emitterVelDir.y, emitterData.emitterVelDir.z); + mInitVelocity = emitterData.initVel; + mInitVelocityRnd = emitterData.initVelRnd; + mSpreadVec = Math::Vector3f(emitterData.spreadVec.x, emitterData.spreadVec.y, emitterData.spreadVec.z); + mAirResistance = emitterData.airResistance; mVolumeProperties = { .volumeTblIndex = emitterData.volumeTblIndex, From 1eac636657deff6a71c531699a73ce46bc8d7172 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Mon, 9 Mar 2026 21:37:47 +0000 Subject: [PATCH 11/35] feat(ui): implement undo/redo for Emitter volumeProperties --- include/editor/emitterWidget/emitterWidget.h | 2 +- .../emitterWidget/volumePropertiesWidget.h | 41 +++---- include/ptcl/ptclEmitter.h | 36 ++++-- src/editor/emitterWidget/emitterWidget.cpp | 10 +- .../emitterWidget/volumePropertiesWidget.cpp | 111 +++++++++++------- src/ptcl/ptclBinary.cpp | 10 +- src/ptcl/ptclEmitter.cpp | 29 ++--- 7 files changed, 128 insertions(+), 111 deletions(-) diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index 8391ea4..d0331ac 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -30,6 +30,7 @@ class ScalePropertiesWidget; class TerminationPropertiesWidget; class EmissionPropertiesWidget; class VelocityPropertiesWidget; +class VolumePropertiesWidget; // ========================================================================== // @@ -57,7 +58,6 @@ class EmitterWidget final : public QWidget { void populateProperties(); private: - class VolumePropertiesWidget; class ColorPropertiesWidget; class AlphaPropertiesWidget; class RotationPropertiesWidget; diff --git a/include/editor/emitterWidget/volumePropertiesWidget.h b/include/editor/emitterWidget/volumePropertiesWidget.h index c459602..288fa5b 100644 --- a/include/editor/emitterWidget/volumePropertiesWidget.h +++ b/include/editor/emitterWidget/volumePropertiesWidget.h @@ -2,9 +2,7 @@ #include "editor/components/enumComboBox.h" #include "editor/components/vectorSpinBox.h" -#include "editor/emitterWidget/emitterWidget.h" - -#include "ptcl/ptclEmitter.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include #include @@ -17,41 +15,34 @@ namespace PtclEditor { // ========================================================================== // -class EmitterWidget::VolumePropertiesWidget : public QWidget { +class VolumePropertiesWidget : public EmitterWidgetBase { Q_OBJECT public: explicit VolumePropertiesWidget(QWidget* parent = nullptr); - void setProperties(const Ptcl::Emitter::VolumeProperties& properties); - -signals: - void propertiesUpdated(const Ptcl::Emitter::VolumeProperties& properties); - private: + void populateProperties() final; + void setupConnections(); void setupUi(); - void setupSignals(); - void populateWidgets(); void updateFieldVisibility(Ptcl::VolumeType volumeType); private: struct VolumeField { - QWidget* widget; - std::function label; - std::function isVisible; + QWidget* widget{}; + std::function label{}; + std::function isVisible{}; }; private: - Ptcl::Emitter::VolumeProperties mProps{}; - - std::vector mFields; - std::unordered_map mFieldLabels; - - QComboBox mVolumeTblIndexComboBox; - EnumComboBox mTypeComboBox; - VectorSpinBox mRadiusSpinBox; - QDoubleSpinBox mSweepStartSpinBox; - QDoubleSpinBox mSweepParamSpinBox; - QDoubleSpinBox mLengthSpinBox; + std::vector mFields{}; + std::unordered_map mFieldLabels{}; + + QComboBox mVolumeTblIndexComboBox{}; + EnumComboBox mTypeComboBox{}; + VectorSpinBox mRadiusSpinBox{}; + QDoubleSpinBox mSweepStartSpinBox{}; + QDoubleSpinBox mSweepParamSpinBox{}; + QDoubleSpinBox mLengthSpinBox{}; }; diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index 882274b..76c88e5 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -75,14 +75,6 @@ class Emitter { s32 alphaSection2{100}; }; - struct VolumeProperties { - u8 volumeTblIndex{0}; - VolumeType volumeType{VolumeType::Point}; - Math::Vector3f volumeRadius{1.0f, 1.0f, 1.0f}; - s32 volumeSweepStart{0}; - s32 volumeSweepParam{0}; - }; - struct TextureProperties { TextureWrap textureWrapT{TextureWrap::ClampToEdge}; TextureWrap textureWrapS{TextureWrap::ClampToEdge}; @@ -277,6 +269,23 @@ class Emitter { f32 airResistance() const { return mAirResistance; } void setAirResistance(f32 resistance) { mAirResistance = resistance; } + // ----- Volume Properties ----- \\ + + u8 volumeTblIndex() const { return mVolumeTblIndex; } + void setVolumeTblIndex(u8 index) { mVolumeTblIndex = index; } + + VolumeType volumeType() const { return mVolumeType; } + void setVolumeType(VolumeType type) { mVolumeType = type; } + + const Math::Vector3f& volumeRadius() const { return mVolumeRadius; } + void setVolumeRadius(const Math::Vector3f& radius) { mVolumeRadius = radius; } + + s32 volumeSweepStart() const { return mVolumeSweepStart; } + void setVolumeSweepStart(s32 sweepStart) { mVolumeSweepStart = sweepStart; } + + s32 volumeSweepParam() const { return mVolumeSweepParam; } + void setVolumeSweepParam(s32 sweepParam) { mVolumeSweepParam = sweepParam; } + BitFlag& flags(); const BitFlag& flags() const; @@ -284,9 +293,6 @@ class Emitter { const TextureHandle& textureHandle() const; void setTexture(const std::shared_ptr& texture); - const VolumeProperties& volumeProperties() const; - void setVolumeProperties(const VolumeProperties& volumeProperties); - const ColorProperties& colorProperties() const; void setColorProperties(const ColorProperties& colorProperties); @@ -387,7 +393,13 @@ class Emitter { Math::Vector3f mSpreadVec{0.0f, 0.0f, 0.0f}; f32 mAirResistance{1.0f}; - VolumeProperties mVolumeProperties{}; + // Volume Properties + u8 mVolumeTblIndex{0}; + VolumeType mVolumeType{VolumeType::Point}; + Math::Vector3f mVolumeRadius{1.0f, 1.0f, 1.0f}; + s32 mVolumeSweepStart{0}; + s32 mVolumeSweepParam{0}; + ColorProperties mColorProperties{}; AlphaProperties mAlphaProperties{}; RotationProperties mRotationProperties{}; diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index c94ddcc..0d6a2f8 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -127,13 +127,6 @@ void EmitterWidget::setupConnections() { emit propertiesChanged(); }); - // Volume Properties - connect(mVolumeProperties, &VolumePropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::VolumeProperties& properties) { - if (!mEmitter) { return; } - mEmitter->setVolumeProperties(properties); - emit propertiesChanged(); - }); - // Color Properties connect(mColorProperties, &ColorPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::ColorProperties& properties) { if (!mEmitter) { return; } @@ -221,6 +214,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mTerminationProperties->setDocument(document); mEmissionProperties->setDocument(document); mVelocityProperties->setDocument(document); + mVolumeProperties->setDocument(document); } void EmitterWidget::setSelection(Ptcl::Selection* selection) { @@ -234,6 +228,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mTerminationProperties->setSelection(selection); mEmissionProperties->setSelection(selection); mVelocityProperties->setSelection(selection); + mVolumeProperties->setSelection(selection); connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -282,7 +277,6 @@ void EmitterWidget::populateProperties() { QSignalBlocker b17(mFieldEditorWidget); QSignalBlocker b18(mStripeEditorWidget); - mVolumeProperties->setProperties(mEmitter->volumeProperties()); mColorProperties->setProperties(mEmitter->colorProperties()); mAlphaProperties->setProperties(mEmitter->alphaProperties()); mRotationProperties->setProperties(mEmitter->rotationProperties()); diff --git a/src/editor/emitterWidget/volumePropertiesWidget.cpp b/src/editor/emitterWidget/volumePropertiesWidget.cpp index 0390a35..cd7df8c 100644 --- a/src/editor/emitterWidget/volumePropertiesWidget.cpp +++ b/src/editor/emitterWidget/volumePropertiesWidget.cpp @@ -11,14 +11,14 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::VolumePropertiesWidget::VolumePropertiesWidget(QWidget* parent) : - QWidget{parent} { +VolumePropertiesWidget::VolumePropertiesWidget(QWidget* parent) : + EmitterWidgetBase{parent} { setupUi(); - setupSignals(); + setupConnections(); } -void EmitterWidget::VolumePropertiesWidget::setupUi() { +void VolumePropertiesWidget::setupUi() { mRadiusSpinBox.setOrientation(Qt::Vertical); mSweepStartSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); @@ -106,57 +106,80 @@ void EmitterWidget::VolumePropertiesWidget::setupUi() { } } -void EmitterWidget::VolumePropertiesWidget::setupSignals() { - connect(&mTypeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, [this]() { - auto type = mTypeComboBox.currentEnum(); - mProps.volumeType = type; - updateFieldVisibility(type); - emit propertiesUpdated(mProps); +void VolumePropertiesWidget::setupConnections() { + connect(&mTypeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, [this]() { + const auto type = mTypeComboBox.currentEnum(); + + setEmitterProperty( + "Set Volume Type", + "SetVolumeType", + &Ptcl::Emitter::volumeType, + &Ptcl::Emitter::setVolumeType, + type + ); }); - connect(&mVolumeTblIndexComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { - mProps.volumeTblIndex = index; - emit propertiesUpdated(mProps); + connect(&mVolumeTblIndexComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, [this](s32 index) { + setEmitterProperty( + "Set Volume Table Index", + "SetVolumeTblIdx", + &Ptcl::Emitter::volumeTblIndex, + &Ptcl::Emitter::setVolumeTblIndex, + index + ); }); connect(&mRadiusSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - auto radius = mRadiusSpinBox.getVector(); - - mProps.volumeRadius = radius; - // Keep length spinbox in sync - const QSignalBlocker block(&mLengthSpinBox); - mLengthSpinBox.setValue(radius.getZ()); - emit propertiesUpdated(mProps); + const auto radius = mRadiusSpinBox.getVector(); + + setEmitterProperty( + "Set Volume Radius", + "SetVolumeRadius", + &Ptcl::Emitter::volumeRadius, + &Ptcl::Emitter::setVolumeRadius, + radius + ); }); - connect(&mLengthSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), this, [this](double value) { + connect(&mLengthSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), this, [this](f64 value) { auto radius = mRadiusSpinBox.getVector(); radius.setZ(static_cast(value)); - mProps.volumeRadius = radius; - // Keep radius spinbox in sync - const QSignalBlocker block(&mRadiusSpinBox); - mRadiusSpinBox.setVector(radius); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Volume Length", + "SetVolumeLength", + &Ptcl::Emitter::volumeRadius, + &Ptcl::Emitter::setVolumeRadius, + radius + ); }); - connect(&mSweepStartSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), this, [this](double value) { - mProps.volumeSweepStart = Math::Util::deg2idx(Math::Util::to180(static_cast(value))); - emit propertiesUpdated(mProps); - }); + connect(&mSweepStartSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), this, [this](f64 value) { + const auto sweepStart = Math::Util::deg2idx(Math::Util::to180(static_cast(value))); - connect(&mSweepParamSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), this, [this](double value) { - mProps.volumeSweepParam = Math::Util::deg2idx(Math::Util::to180(static_cast(value))); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Sweep Start", + "SetSweepStart", + &Ptcl::Emitter::volumeSweepStart, + &Ptcl::Emitter::setVolumeSweepStart, + sweepStart + ); }); -} -void EmitterWidget::VolumePropertiesWidget::setProperties(const Ptcl::Emitter::VolumeProperties& properties) { - mProps = properties; - populateWidgets(); + connect(&mSweepParamSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), this, [this](f64 value) { + const auto sweepParam = Math::Util::deg2idx(Math::Util::to180(static_cast(value))); + + setEmitterProperty( + "Set Sweep Param", + "SetSweepParam", + &Ptcl::Emitter::volumeSweepParam, + &Ptcl::Emitter::setVolumeSweepParam, + sweepParam + ); + }); } -void EmitterWidget::VolumePropertiesWidget::populateWidgets() { +void VolumePropertiesWidget::populateProperties() { QSignalBlocker b1(mVolumeTblIndexComboBox); QSignalBlocker b2(mTypeComboBox); QSignalBlocker b3(mRadiusSpinBox); @@ -164,22 +187,22 @@ void EmitterWidget::VolumePropertiesWidget::populateWidgets() { QSignalBlocker b5(mSweepStartSpinBox); QSignalBlocker b6(mSweepParamSpinBox); - auto volumeType = mProps.volumeType; + auto volumeType = mEmitter->volumeType(); - mVolumeTblIndexComboBox.setCurrentIndex(mProps.volumeTblIndex); + mVolumeTblIndexComboBox.setCurrentIndex(mEmitter->volumeTblIndex()); mTypeComboBox.setCurrentEnum(volumeType); - auto& radius = mProps.volumeRadius; + auto& radius = mEmitter->volumeRadius(); mRadiusSpinBox.setVector(radius); mLengthSpinBox.setValue(radius.getZ()); - mSweepStartSpinBox.setValue(Math::Util::to360(Math::Util::idx2deg(mProps.volumeSweepStart))); - mSweepParamSpinBox.setValue(Math::Util::to360(Math::Util::idx2deg(mProps.volumeSweepParam))); + mSweepStartSpinBox.setValue(Math::Util::to360(Math::Util::idx2deg(mEmitter->volumeSweepStart()))); + mSweepParamSpinBox.setValue(Math::Util::to360(Math::Util::idx2deg(mEmitter->volumeSweepParam()))); updateFieldVisibility(volumeType); } -void EmitterWidget::VolumePropertiesWidget::updateFieldVisibility(Ptcl::VolumeType type) { +void VolumePropertiesWidget::updateFieldVisibility(Ptcl::VolumeType type) { for (const auto& field : mFields) { bool visible = field.isVisible(type); QWidget* widget = field.widget; diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index 4d3b6d9..c1c00f8 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -337,12 +337,12 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { isDirectional = emitter.isDirectional(); isTexPatAnim = emitter.textureProperties().isTexPatAnim; isVelLook = emitter.isVelLook(); - volumeTblIndex = emitter.volumeProperties().volumeTblIndex; + volumeTblIndex = emitter.volumeTblIndex(); isStopEmitInFade = emitter.isStopEmitInFade(); - volumeType = emitter.volumeProperties().volumeType; - volumeRadius = emitter.volumeProperties().volumeRadius; - volumeSweepStart = emitter.volumeProperties().volumeSweepStart; - volumeSweepParam = emitter.volumeProperties().volumeSweepParam; + volumeType = emitter.volumeType(); + volumeRadius = emitter.volumeRadius(); + volumeSweepStart = emitter.volumeSweepStart(); + volumeSweepParam = emitter.volumeSweepParam(); figureVel = emitter.figureVelocity(); emitterVelDir = emitter.velocityDirection(); initVel = emitter.initialVelocity(); diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index 7487910..f05f46e 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -53,7 +53,13 @@ std::unique_ptr Emitter::clone() const { newEmitter->mSpreadVec = mSpreadVec; newEmitter->mAirResistance = mAirResistance; - newEmitter->mVolumeProperties = mVolumeProperties; + // Volume Properties + newEmitter->mVolumeTblIndex = mVolumeTblIndex; + newEmitter->mVolumeType = mVolumeType; + newEmitter->mVolumeRadius = mVolumeRadius; + newEmitter->mVolumeSweepStart = mVolumeSweepStart; + newEmitter->mVolumeSweepParam = mVolumeSweepParam; + newEmitter->mColorProperties = mColorProperties; newEmitter->mAlphaProperties = mAlphaProperties; @@ -93,14 +99,6 @@ void Emitter::setTexture(const std::shared_ptr& texture) { mTextureHandle.set(texture); } -const Emitter::VolumeProperties& Emitter::volumeProperties() const { - return mVolumeProperties; -} - -void Emitter::setVolumeProperties(const VolumeProperties& volumeProperties) { - mVolumeProperties = volumeProperties; -} - const Emitter::ColorProperties& Emitter::colorProperties() const { return mColorProperties; } @@ -292,13 +290,12 @@ void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { mSpreadVec = Math::Vector3f(emitterData.spreadVec.x, emitterData.spreadVec.y, emitterData.spreadVec.z); mAirResistance = emitterData.airResistance; - mVolumeProperties = { - .volumeTblIndex = emitterData.volumeTblIndex, - .volumeType = emitterData.volumeType, - .volumeRadius = Math::Vector3f(emitterData.volumeRadius.x, emitterData.volumeRadius.y, emitterData.volumeRadius.z), - .volumeSweepStart = emitterData.volumeSweepStart, - .volumeSweepParam = emitterData.volumeSweepParam, - }; + // Volume Properties + mVolumeTblIndex = emitterData.volumeTblIndex; + mVolumeType = emitterData.volumeType; + mVolumeRadius = Math::Vector3f(emitterData.volumeRadius.x, emitterData.volumeRadius.y, emitterData.volumeRadius.z); + mVolumeSweepStart = emitterData.volumeSweepStart; + mVolumeSweepParam = emitterData.volumeSweepParam; mColorProperties = { .color0 = emitterData.color0, From 78cabe66fddcca5be4c56e081f710f714b7592c7 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Mon, 9 Mar 2026 23:58:27 +0000 Subject: [PATCH 12/35] feat(ui): implement undo/redo for Emitter rotationProperties --- include/editor/emitterWidget/emitterWidget.h | 2 +- .../emitterWidget/rotationPropertiesWidget.h | 16 +-- include/ptcl/ptclEmitter.h | 41 ++++-- src/editor/emitterWidget/emitterWidget.cpp | 11 +- .../rotationPropertiesWidget.cpp | 136 +++++++++++------- src/ptcl/ptclBinary.cpp | 12 +- src/ptcl/ptclEmitter.cpp | 32 ++--- 7 files changed, 139 insertions(+), 111 deletions(-) diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index d0331ac..400bb1a 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -31,6 +31,7 @@ class TerminationPropertiesWidget; class EmissionPropertiesWidget; class VelocityPropertiesWidget; class VolumePropertiesWidget; +class RotationPropertiesWidget; // ========================================================================== // @@ -60,7 +61,6 @@ class EmitterWidget final : public QWidget { private: class ColorPropertiesWidget; class AlphaPropertiesWidget; - class RotationPropertiesWidget; class TexturePropertiesWidget; class CombinerPropertiesWidget; diff --git a/include/editor/emitterWidget/rotationPropertiesWidget.h b/include/editor/emitterWidget/rotationPropertiesWidget.h index 637ea69..937d110 100644 --- a/include/editor/emitterWidget/rotationPropertiesWidget.h +++ b/include/editor/emitterWidget/rotationPropertiesWidget.h @@ -2,9 +2,7 @@ #include "editor/components/enumComboBox.h" #include "editor/components/vectorSpinBox.h" -#include "editor/emitterWidget/emitterWidget.h" - -#include "ptcl/ptclEmitter.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include #include @@ -17,23 +15,17 @@ namespace PtclEditor { // ========================================================================== // -class EmitterWidget::RotationPropertiesWidget final : public QWidget { +class RotationPropertiesWidget final : public EmitterWidgetBase { Q_OBJECT public: explicit RotationPropertiesWidget(QWidget* parent = nullptr); - void setProperties(const Ptcl::Emitter::RotationProperties& properties); - -signals: - void propertiesUpdated(const Ptcl::Emitter::RotationProperties& properties); - private: - void populateWidgets(); + void populateProperties() final; + void setupConnections(); void updateAxis(); private: - Ptcl::Emitter::RotationProperties mProps{}; - EnumComboBox mRotTypeSpinBox{}; VectorSpinBox mInitRotSpinBox{}; VectorSpinBox mInitRotRandSpinBox{}; diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index 76c88e5..14b6f92 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -40,15 +40,6 @@ class Emitter { bool operator==(const ScaleAnim&) const = default; }; - struct RotationProperties { - RotType rotType{RotType::None}; - Math::Vector3i initRot{0, 0, 0}; - Math::Vector3i initRotRand{0, 0, 0}; - Math::Vector3i rotVel{0, 0, 0}; - Math::Vector3i rotVelRand{0, 0, 0}; - Math::Vector2f rotBasis{0.0f, 0.0f}; - }; - struct ColorProperties { std::array color0{ binColor4f{1.0f, 1.0f, 1.0f, 1.0f}, @@ -286,6 +277,26 @@ class Emitter { s32 volumeSweepParam() const { return mVolumeSweepParam; } void setVolumeSweepParam(s32 sweepParam) { mVolumeSweepParam = sweepParam; } + // ----- Rotation Properties ----- \\ + + RotType rotationType() const { return mRotType; } + void setRotationType(RotType type) { mRotType = type; } + + const Math::Vector3i& initialRotation() const { return mInitRot; } + void setInitialRotation(const Math::Vector3i& rotation) { mInitRot = rotation; } + + const Math::Vector3i& initialRotationRandom() const { return mInitRotRand; } + void setInitialRotationRandom(const Math::Vector3i& rotation) { mInitRotRand = rotation; } + + const Math::Vector3i& rotationVelocity() const { return mRotVel; } + void setRotationVelocity(const Math::Vector3i& velocity) { mRotVel = velocity; } + + const Math::Vector3i& rotationVelocityRandom() const { return mRotVelRand; } + void setRotationVelocityRandom(const Math::Vector3i& random) { mRotVelRand = random; } + + const Math::Vector2f& rotationBasis() const { return mRotBasis; } + void setRotationBasis(const Math::Vector2f& basis) { mRotBasis = basis; } + BitFlag& flags(); const BitFlag& flags() const; @@ -302,9 +313,6 @@ class Emitter { const TextureProperties& textureProperties() const; void setTextureProperties(const TextureProperties& textureProperties); - const RotationProperties& rotationProperties() const; - void setRotationProperties(const RotationProperties& rotationProperties); - const CombinerProperties& combinerProperties() const; void setCombinerProperties(const CombinerProperties& combinerProperties); @@ -400,9 +408,16 @@ class Emitter { s32 mVolumeSweepStart{0}; s32 mVolumeSweepParam{0}; + // Rotation Properties + RotType mRotType{RotType::None}; + Math::Vector3i mInitRot{0, 0, 0}; + Math::Vector3i mInitRotRand{0, 0, 0}; + Math::Vector3i mRotVel{0, 0, 0}; + Math::Vector3i mRotVelRand{0, 0, 0}; + Math::Vector2f mRotBasis{0.0f, 0.0f}; + ColorProperties mColorProperties{}; AlphaProperties mAlphaProperties{}; - RotationProperties mRotationProperties{}; TextureProperties mTextureProperties{}; CombinerProperties mCombinerProperties{}; diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index 0d6a2f8..3746551 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -150,13 +150,6 @@ void EmitterWidget::setupConnections() { emit propertiesChanged(); }); - // Rotation Properties - connect(mRotationProperties, &RotationPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::RotationProperties& properties) { - if (!mEmitter) { return; } - mEmitter->setRotationProperties(properties); - emit propertiesChanged(); - }); - // Child Editor Widget connect(mChildEditorWidget, &ChildEditorWidget::flagsUpdated, this, [this](const BitFlag& childFlags) { if (!mEmitter) { return; } @@ -215,6 +208,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mEmissionProperties->setDocument(document); mVelocityProperties->setDocument(document); mVolumeProperties->setDocument(document); + mRotationProperties->setDocument(document); } void EmitterWidget::setSelection(Ptcl::Selection* selection) { @@ -229,6 +223,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mEmissionProperties->setSelection(selection); mVelocityProperties->setSelection(selection); mVolumeProperties->setSelection(selection); + mRotationProperties->setSelection(selection); connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -269,7 +264,6 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { void EmitterWidget::populateProperties() { QSignalBlocker b8(mColorProperties); QSignalBlocker b9(mAlphaProperties); - QSignalBlocker b11(mRotationProperties); QSignalBlocker b12(mTextureProperties); QSignalBlocker b13(mCombinerProperties); QSignalBlocker b15(mChildEditorWidget); @@ -279,7 +273,6 @@ void EmitterWidget::populateProperties() { mColorProperties->setProperties(mEmitter->colorProperties()); mAlphaProperties->setProperties(mEmitter->alphaProperties()); - mRotationProperties->setProperties(mEmitter->rotationProperties()); mTextureProperties->setProperties(mEmitter->textureProperties(), mEmitter->textureHandle().get()); mCombinerProperties->setProperties(mEmitter->combinerProperties()); mCombinerProperties->setCombinerSrc(&mEmitter->textureHandle(), &mEmitter->colorProperties().color1, &mEmitter->colorProperties().color0[0]); diff --git a/src/editor/emitterWidget/rotationPropertiesWidget.cpp b/src/editor/emitterWidget/rotationPropertiesWidget.cpp index 3fb038f..fa4d09e 100644 --- a/src/editor/emitterWidget/rotationPropertiesWidget.cpp +++ b/src/editor/emitterWidget/rotationPropertiesWidget.cpp @@ -11,17 +11,33 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::RotationPropertiesWidget::RotationPropertiesWidget(QWidget* parent) : - QWidget{parent} { +RotationPropertiesWidget::RotationPropertiesWidget(QWidget* parent) : + EmitterWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); - // RotType mainLayout->addRow("Rotation Type", &mRotTypeSpinBox); + mainLayout->addRow("Initial Rotation", &mInitRotSpinBox); + mainLayout->addRow("Initial Rotation Random", &mInitRotRandSpinBox); + mainLayout->addRow("Rotation Speed", &mRotVelSpinBox); + mainLayout->addRow("Rotation Speed Random", &mRotVelRandSpinBox); + mainLayout->addRow("Rotation Pivot Offset", &mRotBasisSpinBox); + + setLayout(mainLayout); + setupConnections(); +} + +void RotationPropertiesWidget::setupConnections() { connect(&mRotTypeSpinBox, &QComboBox::currentIndexChanged, this, [this]() { - mProps.rotType = mRotTypeSpinBox.currentEnum(); - updateAxis(); - emit propertiesUpdated(mProps); + const auto type = mRotTypeSpinBox.currentEnum(); + + setEmitterProperty( + "Set Rotation type", + "SetRotType", + &Ptcl::Emitter::rotationType, + &Ptcl::Emitter::setRotationType, + type + ); }); auto deg2idxVec = [](const Math::Vector3f& v) { @@ -33,51 +49,76 @@ EmitterWidget::RotationPropertiesWidget::RotationPropertiesWidget(QWidget* paren return result; }; - // Initial Rotation - mainLayout->addRow("Initial Rotation", &mInitRotSpinBox); connect(&mInitRotSpinBox, &VectorSpinBoxBase::valueChanged, this, [this, deg2idxVec]() { - mProps.initRot = deg2idxVec(mInitRotSpinBox.getVector()); - emit propertiesUpdated(mProps); + const auto rotation = deg2idxVec(mInitRotSpinBox.getVector()); + + setEmitterProperty( + "Set Initial Rotation", + "SetInitRot", + &Ptcl::Emitter::initialRotation, + &Ptcl::Emitter::setInitialRotation, + rotation + ); }); - // Initial Rotation Rand - mainLayout->addRow("Initial Rotation Random", &mInitRotRandSpinBox); connect(&mInitRotRandSpinBox, &VectorSpinBoxBase::valueChanged, this, [this, deg2idxVec]() { - mProps.initRotRand = deg2idxVec(mInitRotRandSpinBox.getVector()); - emit propertiesUpdated(mProps); + const auto rand = deg2idxVec(mInitRotRandSpinBox.getVector()); + + setEmitterProperty( + "Set Initial Rotation Random", + "SetInitRotRand", + &Ptcl::Emitter::initialRotationRandom, + &Ptcl::Emitter::setInitialRotationRandom, + rand + ); }); - // Rotation Speed - mainLayout->addRow("Rotation Speed", &mRotVelSpinBox); connect(&mRotVelSpinBox, &VectorSpinBoxBase::valueChanged, this, [this, deg2idxVec]() { - mProps.rotVel = deg2idxVec(mRotVelSpinBox.getVector()); - emit propertiesUpdated(mProps); + const auto velocity = deg2idxVec(mRotVelSpinBox.getVector()); + + setEmitterProperty( + "Set Rotation Speed", + "SetRotVel", + &Ptcl::Emitter::rotationVelocity, + &Ptcl::Emitter::setRotationVelocity, + velocity + ); }); - // Rotation Speed Rand - mainLayout->addRow("Rotation Speed Random", &mRotVelRandSpinBox); connect(&mRotVelRandSpinBox, &VectorSpinBoxBase::valueChanged, this, [this, deg2idxVec]() { - mProps.rotVelRand = deg2idxVec(mRotVelRandSpinBox.getVector()); - emit propertiesUpdated(mProps); + const auto rand = deg2idxVec(mRotVelRandSpinBox.getVector()); + + setEmitterProperty( + "Set Rotation Speed Random", + "SetRotVelRand", + &Ptcl::Emitter::rotationVelocityRandom, + &Ptcl::Emitter::setRotationVelocityRandom, + rand + ); }); - // Rotation Pivot - mainLayout->addRow("Rotation Pivot Offset", &mRotBasisSpinBox); connect(&mRotBasisSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mProps.rotBasis = mRotBasisSpinBox.getVector(); - emit propertiesUpdated(mProps); + const auto basis = mRotBasisSpinBox.getVector(); + + setEmitterProperty( + "Set Rotation Pivot", + "SetRotBasis", + &Ptcl::Emitter::rotationBasis, + &Ptcl::Emitter::setRotationBasis, + basis + ); }); - - setLayout(mainLayout); - } -void EmitterWidget::RotationPropertiesWidget::setProperties(const Ptcl::Emitter::RotationProperties& properties) { - mProps = properties; - populateWidgets(); -} -void EmitterWidget::RotationPropertiesWidget::populateWidgets() { +void RotationPropertiesWidget::populateProperties() { + QSignalBlocker b1(mRotTypeSpinBox); + QSignalBlocker b2(mInitRotSpinBox); + QSignalBlocker b3(mInitRotRandSpinBox); + QSignalBlocker b4(mRotVelSpinBox); + QSignalBlocker b5(mRotVelRandSpinBox); + QSignalBlocker b6(mRotBasisSpinBox); + auto idx2degVec = [](const Math::Vector3i& v) { return Math::Vector3f { Math::Util::to180(Math::Util::idx2deg(v.getX())), @@ -86,31 +127,20 @@ void EmitterWidget::RotationPropertiesWidget::populateWidgets() { }; }; - QSignalBlocker b1(mRotTypeSpinBox); - mRotTypeSpinBox.setCurrentEnum(mProps.rotType); - - QSignalBlocker b2(mInitRotSpinBox); - mInitRotSpinBox.setVector(idx2degVec(mProps.initRot)); - - QSignalBlocker b3(mInitRotRandSpinBox); - mInitRotRandSpinBox.setVector(idx2degVec(mProps.initRotRand)); - - QSignalBlocker b4(mRotVelSpinBox); - mRotVelSpinBox.setVector(idx2degVec(mProps.rotVel)); - - QSignalBlocker b5(mRotVelRandSpinBox); - mRotVelRandSpinBox.setVector(idx2degVec(mProps.rotVelRand)); - - QSignalBlocker b6(mRotBasisSpinBox); - mRotBasisSpinBox.setVector(mProps.rotBasis); + mRotTypeSpinBox.setCurrentEnum(mEmitter->rotationType()); + mInitRotSpinBox.setVector(idx2degVec(mEmitter->initialRotation())); + mInitRotRandSpinBox.setVector(idx2degVec(mEmitter->initialRotationRandom())); + mRotVelSpinBox.setVector(idx2degVec(mEmitter->rotationVelocity())); + mRotVelRandSpinBox.setVector(idx2degVec(mEmitter->rotationVelocityRandom())); + mRotBasisSpinBox.setVector(mEmitter->rotationBasis()); updateAxis(); } -void EmitterWidget::RotationPropertiesWidget::updateAxis() { +void RotationPropertiesWidget::updateAxis() { using Axis = VectorSpinBoxBase::Axis; - switch (mProps.rotType) { + switch (mEmitter->rotationType()) { case Ptcl::RotType::None: mInitRotSpinBox.setEnabledAxis(Axis::None); mInitRotRandSpinBox.setEnabledAxis(Axis::None); diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index c1c00f8..68b3822 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -381,14 +381,14 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { scaleSection2 = emitter.scaleAnim().scaleSection2; scaleRand = emitter.scaleRand(); - rotCalcType = static_cast(emitter.rotationProperties().rotType) + 5 * static_cast(emitter.colorProperties().colorCalcType); + rotCalcType = static_cast(emitter.rotationType()) + 5 * static_cast(emitter.colorProperties().colorCalcType); followType = emitter.followType(); colorCombinerFunc = emitter.combinerProperties().combinerFunc; - initRot = emitter.rotationProperties().initRot; - initRotRand = emitter.rotationProperties().initRotRand; - rotVel = emitter.rotationProperties().rotVel; - rotVelRand = emitter.rotationProperties().rotVelRand; - rotBasis = emitter.rotationProperties().rotBasis; + initRot = emitter.initialRotation(); + initRotRand = emitter.initialRotationRandom(); + rotVel = emitter.rotationVelocity(); + rotVelRand = emitter.rotationVelocityRandom(); + rotBasis = emitter.rotationBasis(); transformSRT = emitter.transformSRT(); transformRT = emitter.transformRT(); alphaAddInFade = emitter.alphaAddInFade(); diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index f05f46e..508de2f 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -67,7 +67,14 @@ std::unique_ptr Emitter::clone() const { newEmitter->mScaleAnim = mScaleAnim; newEmitter->mScaleRand = mScaleRand; - newEmitter->mRotationProperties = mRotationProperties; + // Rotation Properties + newEmitter->mRotType = mRotType; + newEmitter->mInitRot = mInitRot; + newEmitter->mInitRotRand = mInitRotRand; + newEmitter->mRotVel = mRotVel; + newEmitter->mRotVelRand = mRotVelRand; + newEmitter->mRotBasis = mRotBasis; + newEmitter->mTextureProperties = mTextureProperties; newEmitter->mCombinerProperties = mCombinerProperties; newEmitter->mTextureHandle = mTextureHandle.clone(); @@ -126,14 +133,6 @@ void Emitter::setTextureProperties(const TextureProperties& textureProperties) { mTextureProperties = textureProperties; } -const Emitter::RotationProperties& Emitter::rotationProperties() const { - return mRotationProperties; -} - -void Emitter::setRotationProperties(const RotationProperties& rotationProperties) { - mRotationProperties = rotationProperties; -} - const Emitter::CombinerProperties& Emitter::combinerProperties() const { return mCombinerProperties; } @@ -328,14 +327,13 @@ void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { mScaleRand = emitterData.scaleRand; - mRotationProperties = { - .rotType = static_cast(emitterData.rotCalcType % 5), - .initRot = Math::Vector3i(emitterData.initRot.x, emitterData.initRot.y, emitterData.initRot.z), - .initRotRand = Math::Vector3i(emitterData.initRotRand.x, emitterData.initRotRand.y, emitterData.initRotRand.z), - .rotVel = Math::Vector3i(emitterData.rotVel.x, emitterData.rotVel.y, emitterData.rotVel.z), - .rotVelRand = Math::Vector3i(emitterData.rotVelRand.x, emitterData.rotVelRand.y, emitterData.rotVelRand.z), - .rotBasis = Math::Vector2f(emitterData.rotBasis.x, emitterData.rotBasis.y) - }; + // Rotation Properties + mRotType = static_cast(emitterData.rotCalcType % 5); + mInitRot = Math::Vector3i(emitterData.initRot.x, emitterData.initRot.y, emitterData.initRot.z); + mInitRotRand = Math::Vector3i(emitterData.initRotRand.x, emitterData.initRotRand.y, emitterData.initRotRand.z); + mRotVel = Math::Vector3i(emitterData.rotVel.x, emitterData.rotVel.y, emitterData.rotVel.z); + mRotVelRand = Math::Vector3i(emitterData.rotVelRand.x, emitterData.rotVelRand.y, emitterData.rotVelRand.z); + mRotBasis = Math::Vector2f(emitterData.rotBasis.x, emitterData.rotBasis.y); mCombinerProperties = { .blendFunc = emitterData.blendFunc, From 694914174f7b0e01402b793f446bf5cff059049f Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Tue, 10 Mar 2026 00:28:52 +0000 Subject: [PATCH 13/35] feat(ui): implement undo/redo for Emitter alphaProperties --- .../emitterWidget/alphaPropertiesWidget.h | 15 +-- include/editor/emitterWidget/emitterWidget.h | 2 +- include/ptcl/ptclEmitter.h | 16 ++- .../emitterWidget/alphaPropertiesWidget.cpp | 119 +++++++++++------- src/editor/emitterWidget/emitterWidget.cpp | 11 +- src/ptcl/ptclBinary.cpp | 10 +- src/ptcl/ptclEmitter.cpp | 15 +-- 7 files changed, 100 insertions(+), 88 deletions(-) diff --git a/include/editor/emitterWidget/alphaPropertiesWidget.h b/include/editor/emitterWidget/alphaPropertiesWidget.h index 33c3092..9443949 100644 --- a/include/editor/emitterWidget/alphaPropertiesWidget.h +++ b/include/editor/emitterWidget/alphaPropertiesWidget.h @@ -1,9 +1,7 @@ #pragma once #include "editor/components/animGraph.h" -#include "editor/emitterWidget/emitterWidget.h" - -#include "ptcl/ptclEmitter.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include @@ -14,19 +12,16 @@ namespace PtclEditor { // ========================================================================== // -class EmitterWidget::AlphaPropertiesWidget final : public QWidget { +class AlphaPropertiesWidget final : public EmitterWidgetBase { Q_OBJECT public: explicit AlphaPropertiesWidget(QWidget* parent = nullptr); - void setProperties(const Ptcl::Emitter::AlphaProperties& properties); - -signals: - void propertiesUpdated(const Ptcl::Emitter::AlphaProperties& properties); - private: - Ptcl::Emitter::AlphaProperties mProps{}; + void populateProperties() final; + void updateAnimPoint(s32 pointIndex, const AnimGraph::GraphPoint& point); +private: AnimGraph mGraphA{}; }; diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index 400bb1a..c834e46 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -32,6 +32,7 @@ class EmissionPropertiesWidget; class VelocityPropertiesWidget; class VolumePropertiesWidget; class RotationPropertiesWidget; +class AlphaPropertiesWidget; // ========================================================================== // @@ -60,7 +61,6 @@ class EmitterWidget final : public QWidget { private: class ColorPropertiesWidget; - class AlphaPropertiesWidget; class TexturePropertiesWidget; class CombinerPropertiesWidget; diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index 14b6f92..ae97649 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -58,12 +58,14 @@ class Emitter { binColor3f color1{255.0f, 255.0f, 255.0f}; }; - struct AlphaProperties { + struct AlphaAnim { f32 initAlpha{1.0f}; f32 diffAlpha21{0.0f}; f32 diffAlpha32{0.0f}; s32 alphaSection1{0}; s32 alphaSection2{100}; + + bool operator==(const AlphaAnim&) const = default; }; struct TextureProperties { @@ -297,6 +299,11 @@ class Emitter { const Math::Vector2f& rotationBasis() const { return mRotBasis; } void setRotationBasis(const Math::Vector2f& basis) { mRotBasis = basis; } + // ----- Alpha Properties ----- \\ + + const AlphaAnim& alphaAnim() const { return mAlphaAnim; } + void setAlphaAnim(const AlphaAnim& alphaAnim) { mAlphaAnim = alphaAnim; } + BitFlag& flags(); const BitFlag& flags() const; @@ -307,9 +314,6 @@ class Emitter { const ColorProperties& colorProperties() const; void setColorProperties(const ColorProperties& colorProperties); - const AlphaProperties& alphaProperties() const; - void setAlphaProperties(const AlphaProperties& alphaProperties); - const TextureProperties& textureProperties() const; void setTextureProperties(const TextureProperties& textureProperties); @@ -416,8 +420,10 @@ class Emitter { Math::Vector3i mRotVelRand{0, 0, 0}; Math::Vector2f mRotBasis{0.0f, 0.0f}; + // Alpha properties + AlphaAnim mAlphaAnim{}; + ColorProperties mColorProperties{}; - AlphaProperties mAlphaProperties{}; TextureProperties mTextureProperties{}; CombinerProperties mCombinerProperties{}; diff --git a/src/editor/emitterWidget/alphaPropertiesWidget.cpp b/src/editor/emitterWidget/alphaPropertiesWidget.cpp index 56c9e20..43b7272 100644 --- a/src/editor/emitterWidget/alphaPropertiesWidget.cpp +++ b/src/editor/emitterWidget/alphaPropertiesWidget.cpp @@ -9,8 +9,8 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::AlphaPropertiesWidget::AlphaPropertiesWidget(QWidget* parent) : - QWidget{parent} { +AlphaPropertiesWidget::AlphaPropertiesWidget(QWidget* parent) : + EmitterWidgetBase{parent} { mGraphA.setLineColor(QColor{20, 40, 60}); mGraphA.setTickStepSize(0.1f); @@ -21,61 +21,84 @@ EmitterWidget::AlphaPropertiesWidget::AlphaPropertiesWidget(QWidget* parent) : mainLayout->addRow("Alpha Anim:", &mGraphA); connect(&mGraphA, &AnimGraph::pointEdited, this, [this](s32 pointIndex, const AnimGraph::GraphPoint& point) { - const f32 oldP0 = mProps.initAlpha; - const f32 oldP1 = oldP0 + mProps.diffAlpha21; - const f32 oldP2 = oldP1; - const f32 oldP3 = oldP2 + mProps.diffAlpha32; - - auto updateAlphaSection = [&](s32* section, bool isSection2 = false) { - mProps.diffAlpha21 = point.value - oldP0; - mProps.diffAlpha32 = oldP3 - point.value; - - const f32 sec1 = mGraphA.getPoints()[1].position; - const f32 sec2 = mGraphA.getPoints()[2].position; - - if (std::abs(sec1 - sec2) < std::numeric_limits::epsilon()) { - mProps.alphaSection1 = -127; // Disable section1 if handles overlap - if (isSection2) { *section = static_cast(point.position); } - } else { - *section = (std::abs(sec2 - 100.0f) < std::numeric_limits::epsilon()) ? 128 : static_cast(point.position); - } - }; - - switch (pointIndex) { - case 0: - mProps.initAlpha = point.value; - mProps.diffAlpha21 = oldP1 - point.value; - break; - case 1: - updateAlphaSection(&mProps.alphaSection1); - break; - case 2: - updateAlphaSection(&mProps.alphaSection2, true); - break; - case 3: - mProps.diffAlpha32 = point.value - oldP1; - break; - } - - emit propertiesUpdated(mProps); + updateAnimPoint(pointIndex, point); }); } -void EmitterWidget::AlphaPropertiesWidget::setProperties(const Ptcl::Emitter::AlphaProperties& properties) { - mProps = properties; +void AlphaPropertiesWidget::updateAnimPoint(s32 pointIndex, const AnimGraph::GraphPoint& point) { + auto anim = mEmitter->alphaAnim(); + + const f32 oldP0 = anim.initAlpha; + const f32 oldP1 = oldP0 + anim.diffAlpha21; + const f32 oldP2 = oldP1; + const f32 oldP3 = oldP2 + anim.diffAlpha32; + auto updateAlphaSection = [&](s32* section, bool isSection2 = false) { + anim.diffAlpha21 = point.value - oldP0; + anim.diffAlpha32 = oldP3 - point.value; + + const f32 sec1 = mGraphA.getPoints()[1].position; + const f32 sec2 = mGraphA.getPoints()[2].position; + + if (std::abs(sec1 - sec2) < std::numeric_limits::epsilon()) { + anim.alphaSection1 = -127; // Disable section1 if handles overlap + if (isSection2) { *section = static_cast(point.position); } + } else { + *section = (std::abs(sec2 - 100.0f) < std::numeric_limits::epsilon()) ? 128 : static_cast(point.position); + } + }; + + switch (pointIndex) { + case 0: + anim.initAlpha = point.value; + anim.diffAlpha21 = oldP1 - point.value; + break; + case 1: + updateAlphaSection(&anim.alphaSection1); + break; + case 2: + updateAlphaSection(&anim.alphaSection2, true); + break; + case 3: + anim.diffAlpha32 = point.value - oldP1; + break; + } + + QString handleName; + switch (pointIndex) { + case 0: handleName = "Start"; break; + case 1: handleName = "Section1"; break; + case 2: handleName = "Section2"; break; + case 3: handleName = "End"; break; + } + + const auto label = QString("Move Alpha %1").arg(handleName); + const auto key = QString("AlphaGraph_%1").arg(pointIndex); + + setEmitterProperty( + label, + key, + &Ptcl::Emitter::alphaAnim, + &Ptcl::Emitter::setAlphaAnim, + anim + ); +} + +void AlphaPropertiesWidget::populateProperties() { QSignalBlocker blocker(mGraphA); - const f32 p0 = mProps.initAlpha; - const f32 p1 = p0 + mProps.diffAlpha21; + const auto& anim = mEmitter->alphaAnim(); + + const f32 p0 = anim.initAlpha; + const f32 p1 = p0 + anim.diffAlpha21; const f32 p2 = p1; - const f32 p3 = p2 + mProps.diffAlpha32; + const f32 p3 = p2 + anim.diffAlpha32; - const bool disabled = mProps.alphaSection1 == -127; - const bool fullLife = mProps.alphaSection2 == 128; + const bool disabled = anim.alphaSection1 == -127; + const bool fullLife = anim.alphaSection2 == 128; - const f32 sec2 = fullLife ? 100.0f : static_cast(mProps.alphaSection2); - const f32 sec1 = disabled ? sec2 : static_cast(mProps.alphaSection1); + const f32 sec2 = fullLife ? 100.0f : static_cast(anim.alphaSection2); + const f32 sec1 = disabled ? sec2 : static_cast(anim.alphaSection1); AnimGraph::PointList points = { { 0.0f, p0, AnimGraph::HandleType::Locked }, diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index 3746551..18b8c09 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -143,13 +143,6 @@ void EmitterWidget::setupConnections() { emit propertiesChanged(); }); - // Alpha Properties - connect(mAlphaProperties, &AlphaPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::AlphaProperties& properties) { - if (!mEmitter) { return; } - mEmitter->setAlphaProperties(properties); - emit propertiesChanged(); - }); - // Child Editor Widget connect(mChildEditorWidget, &ChildEditorWidget::flagsUpdated, this, [this](const BitFlag& childFlags) { if (!mEmitter) { return; } @@ -209,6 +202,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mVelocityProperties->setDocument(document); mVolumeProperties->setDocument(document); mRotationProperties->setDocument(document); + mAlphaProperties->setDocument(document); } void EmitterWidget::setSelection(Ptcl::Selection* selection) { @@ -224,6 +218,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mVelocityProperties->setSelection(selection); mVolumeProperties->setSelection(selection); mRotationProperties->setSelection(selection); + mAlphaProperties->setSelection(selection); connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -263,7 +258,6 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { void EmitterWidget::populateProperties() { QSignalBlocker b8(mColorProperties); - QSignalBlocker b9(mAlphaProperties); QSignalBlocker b12(mTextureProperties); QSignalBlocker b13(mCombinerProperties); QSignalBlocker b15(mChildEditorWidget); @@ -272,7 +266,6 @@ void EmitterWidget::populateProperties() { QSignalBlocker b18(mStripeEditorWidget); mColorProperties->setProperties(mEmitter->colorProperties()); - mAlphaProperties->setProperties(mEmitter->alphaProperties()); mTextureProperties->setProperties(mEmitter->textureProperties(), mEmitter->textureHandle().get()); mCombinerProperties->setProperties(mEmitter->combinerProperties()); mCombinerProperties->setCombinerSrc(&mEmitter->textureHandle(), &mEmitter->colorProperties().color1, &mEmitter->colorProperties().color0[0]); diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index 68b3822..6745bc2 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -368,11 +368,11 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { colorSection2 = emitter.colorProperties().colorSection2; colorSection3 = emitter.colorProperties().colorSection3; colorNumRepeat = emitter.colorProperties().colorNumRepeat; - initAlpha = emitter.alphaProperties().initAlpha; - diffAlpha21 = emitter.alphaProperties().diffAlpha21; - diffAlpha32 = emitter.alphaProperties().diffAlpha32; - alphaSection1 = emitter.alphaProperties().alphaSection1; - alphaSection2 = emitter.alphaProperties().alphaSection2; + initAlpha = emitter.alphaAnim().initAlpha; + diffAlpha21 = emitter.alphaAnim().diffAlpha21; + diffAlpha32 = emitter.alphaAnim().diffAlpha32; + alphaSection1 = emitter.alphaAnim().alphaSection1; + alphaSection2 = emitter.alphaAnim().alphaSection2; initScale = emitter.scaleAnim().initScale; diffScale21 = emitter.scaleAnim().diffScale21; diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index 508de2f..c9dac57 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -61,7 +61,9 @@ std::unique_ptr Emitter::clone() const { newEmitter->mVolumeSweepParam = mVolumeSweepParam; newEmitter->mColorProperties = mColorProperties; - newEmitter->mAlphaProperties = mAlphaProperties; + + // Alpha Properties + newEmitter->mAlphaAnim = mAlphaAnim; // Scale Properties newEmitter->mScaleAnim = mScaleAnim; @@ -117,14 +119,6 @@ void Emitter::setColorProperties(const ColorProperties& colorProperties) { mFlag.set(EmitterFlag::ColorAnimation, mColorProperties.colorAnimation); } -const Emitter::AlphaProperties& Emitter::alphaProperties() const { - return mAlphaProperties; -} - -void Emitter::setAlphaProperties(const AlphaProperties& alphaProperties) { - mAlphaProperties = alphaProperties; -} - const Emitter::TextureProperties& Emitter::textureProperties() const { return mTextureProperties; } @@ -308,7 +302,8 @@ void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { .color1 = emitterData.color1 }; - mAlphaProperties = { + // Alpha Properties + mAlphaAnim = { .initAlpha = emitterData.initAlpha, .diffAlpha21 = emitterData.diffAlpha21, .diffAlpha32 = emitterData.diffAlpha32, From c032434c683c02db63e55207d55a829bc4bff7ed Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Wed, 11 Mar 2026 16:29:34 +0000 Subject: [PATCH 14/35] feat(ui): implement undo/redo for Emitter combinerProperties --- .../emitterWidget/combinerPropertiesWidget.h | 16 ++--- include/editor/emitterWidget/emitterWidget.h | 2 +- include/ptcl/ptclEmitter.h | 30 +++++--- .../combinerPropertiesWidget.cpp | 70 ++++++++++++------- src/editor/emitterWidget/emitterWidget.cpp | 12 +--- src/ptcl/ptclBinary.cpp | 6 +- src/ptcl/ptclEmitter.cpp | 25 +++---- 7 files changed, 84 insertions(+), 77 deletions(-) diff --git a/include/editor/emitterWidget/combinerPropertiesWidget.h b/include/editor/emitterWidget/combinerPropertiesWidget.h index 9c7192b..ddecbbe 100644 --- a/include/editor/emitterWidget/combinerPropertiesWidget.h +++ b/include/editor/emitterWidget/combinerPropertiesWidget.h @@ -2,9 +2,7 @@ #include "editor/components/combinerPreviewWidget.h" #include "editor/components/enumComboBox.h" -#include "editor/emitterWidget/emitterWidget.h" - -#include "ptcl/ptclEmitter.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include #include @@ -16,22 +14,18 @@ namespace PtclEditor { // ========================================================================== // -class EmitterWidget::CombinerPropertiesWidget final : public QWidget { +class CombinerPropertiesWidget final : public EmitterWidgetBase { Q_OBJECT public: explicit CombinerPropertiesWidget(QWidget* parent = nullptr); - void setProperties(const Ptcl::Emitter::CombinerProperties& properties); - void setCombinerSrc(const Ptcl::TextureHandle* texture, const Ptcl::binColor3f* constant, const Ptcl::binColor4f* primary); - void updateCombinerPreview(); -signals: - void propertiesUpdated(const Ptcl::Emitter::CombinerProperties& properties); - private: - Ptcl::Emitter::CombinerProperties mProps{}; + void populateProperties() final; + void setupConnections(); +private: QCheckBox mFogCheckBox{}; EnumComboBox mBlendFuncComboBox{}; EnumComboBox mDepthFuncComboBox{}; diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index c834e46..100b787 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -33,6 +33,7 @@ class VelocityPropertiesWidget; class VolumePropertiesWidget; class RotationPropertiesWidget; class AlphaPropertiesWidget; +class CombinerPropertiesWidget; // ========================================================================== // @@ -62,7 +63,6 @@ class EmitterWidget final : public QWidget { private: class ColorPropertiesWidget; class TexturePropertiesWidget; - class CombinerPropertiesWidget; private: Ptcl::Document* mDocument{nullptr}; diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index ae97649..9e36403 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -85,13 +85,6 @@ class Emitter { bool isTexPatAnim{false}; }; - struct CombinerProperties { - BlendFuncType blendFunc{BlendFuncType::Translucent}; - DepthFuncType depthFunc{DepthFuncType::Unk0}; - ColorCombinerFuncType combinerFunc{ColorCombinerFuncType::CombinerConfig0}; - bool isFogEnabled{false}; - }; - struct ComplexProperties { BitFlag childFlags{ ChildFlag::AlphaInherit, @@ -304,6 +297,20 @@ class Emitter { const AlphaAnim& alphaAnim() const { return mAlphaAnim; } void setAlphaAnim(const AlphaAnim& alphaAnim) { mAlphaAnim = alphaAnim; } + // ----- Combiner Properties ----- \\ + + BlendFuncType blendFunction() const { return mBlendFunc; } + void setBlendFunction(BlendFuncType blendFunction) { mBlendFunc = blendFunction; } + + DepthFuncType depthFunction() const { return mDepthFunc; } + void setDepthFunction(DepthFuncType depthFunction) { mDepthFunc = depthFunction; } + + ColorCombinerFuncType combinerFunction() const { return mCombinerFunc; } + void setCombinerFunction(ColorCombinerFuncType combineFunc) { mCombinerFunc = combineFunc; } + + bool isFogEnabled() const { return mFlag.isSet(EmitterFlag::EnableFog); } + void setIsFogEnabled(bool enabled) { mFlag.set(EmitterFlag::EnableFog, enabled); } + BitFlag& flags(); const BitFlag& flags() const; @@ -317,9 +324,6 @@ class Emitter { const TextureProperties& textureProperties() const; void setTextureProperties(const TextureProperties& textureProperties); - const CombinerProperties& combinerProperties() const; - void setCombinerProperties(const CombinerProperties& combinerProperties); - const ComplexProperties& complexProperties() const; void setComplexProperties(const ComplexProperties& complexProperties); @@ -423,9 +427,13 @@ class Emitter { // Alpha properties AlphaAnim mAlphaAnim{}; + // Combiner Properties + BlendFuncType mBlendFunc{BlendFuncType::Translucent}; + DepthFuncType mDepthFunc{DepthFuncType::Unk0}; + ColorCombinerFuncType mCombinerFunc{ColorCombinerFuncType::CombinerConfig0}; + ColorProperties mColorProperties{}; TextureProperties mTextureProperties{}; - CombinerProperties mCombinerProperties{}; TextureHandle mTextureHandle{}; diff --git a/src/editor/emitterWidget/combinerPropertiesWidget.cpp b/src/editor/emitterWidget/combinerPropertiesWidget.cpp index 3eb8403..f33deb0 100644 --- a/src/editor/emitterWidget/combinerPropertiesWidget.cpp +++ b/src/editor/emitterWidget/combinerPropertiesWidget.cpp @@ -8,8 +8,8 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::CombinerPropertiesWidget::CombinerPropertiesWidget(QWidget* parent) : - QWidget{parent} { +CombinerPropertiesWidget::CombinerPropertiesWidget(QWidget* parent) : + EmitterWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); @@ -21,49 +21,69 @@ EmitterWidget::CombinerPropertiesWidget::CombinerPropertiesWidget(QWidget* paren mainLayout->addRow("Combiner Function:", &mCombinerFuncComboBox); mainLayout->addWidget(&mCombinerPreview); + setupConnections(); +} + +void CombinerPropertiesWidget::setupConnections() { connect(&mFogCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - mProps.isFogEnabled = checked; - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Fog Enabled", + "SetFogEnabled", + &Ptcl::Emitter::isFogEnabled, + &Ptcl::Emitter::setIsFogEnabled, + checked + ); }); connect(&mBlendFuncComboBox, &QComboBox::currentIndexChanged, this, [this]() { - mProps.blendFunc = mBlendFuncComboBox.currentEnum(); - emit propertiesUpdated(mProps); + const auto func = mBlendFuncComboBox.currentEnum(); + setEmitterProperty( + "Set Blend Function", + "SetBlendFunction", + &Ptcl::Emitter::blendFunction, + &Ptcl::Emitter::setBlendFunction, + func + ); }); connect(&mDepthFuncComboBox, &QComboBox::currentIndexChanged, this, [this]() { - mProps.depthFunc = mDepthFuncComboBox.currentEnum(); - emit propertiesUpdated(mProps); + const auto func = mDepthFuncComboBox.currentEnum(); + setEmitterProperty( + "Set Depth Function", + "SetDepthFunction", + &Ptcl::Emitter::depthFunction, + &Ptcl::Emitter::setDepthFunction, + func + ); }); connect(&mCombinerFuncComboBox, &QComboBox::currentIndexChanged, this, [this]() { - auto value = mCombinerFuncComboBox.currentEnum(); - mProps.combinerFunc = value; - mCombinerPreview.setConfig(static_cast(value)); - emit propertiesUpdated(mProps); + const auto func = mCombinerFuncComboBox.currentEnum(); + setEmitterProperty( + "Set Combiner Function", + "SetCombinerFunction", + &Ptcl::Emitter::combinerFunction, + &Ptcl::Emitter::setCombinerFunction, + func + ); }); } -void EmitterWidget::CombinerPropertiesWidget::setProperties(const Ptcl::Emitter::CombinerProperties& properties) { +void CombinerPropertiesWidget::populateProperties() { QSignalBlocker b1(mBlendFuncComboBox); QSignalBlocker b2(mDepthFuncComboBox); QSignalBlocker b3(mCombinerFuncComboBox); QSignalBlocker b4(mFogCheckBox); - mProps = properties; - - mFogCheckBox.setChecked(mProps.isFogEnabled); - mBlendFuncComboBox.setCurrentEnum(mProps.blendFunc); - mDepthFuncComboBox.setCurrentEnum(mProps.depthFunc); - mCombinerFuncComboBox.setCurrentEnum(mProps.combinerFunc); - mCombinerPreview.setConfig(static_cast(mProps.combinerFunc)); -} - -void EmitterWidget::CombinerPropertiesWidget::setCombinerSrc(const Ptcl::TextureHandle* texture, const Ptcl::binColor3f* constant, const Ptcl::binColor4f* primary) { - mCombinerPreview.setCombinerSrc(texture, constant, primary); + mFogCheckBox.setChecked(mEmitter->isFogEnabled()); + mBlendFuncComboBox.setCurrentEnum(mEmitter->blendFunction()); + mDepthFuncComboBox.setCurrentEnum(mEmitter->depthFunction()); + mCombinerFuncComboBox.setCurrentEnum(mEmitter->combinerFunction()); + mCombinerPreview.setConfig(static_cast(mEmitter->combinerFunction())); + mCombinerPreview.setCombinerSrc(&mEmitter->textureHandle(), &mEmitter->colorProperties().color1, &mEmitter->colorProperties().color0[0]); } -void EmitterWidget::CombinerPropertiesWidget::updateCombinerPreview() { +void CombinerPropertiesWidget::updateCombinerPreview() { mCombinerPreview.updateStages(); } diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index 18b8c09..d7d0994 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -136,13 +136,6 @@ void EmitterWidget::setupConnections() { emit propertiesChanged(); }); - // Combiner Properties - connect(mCombinerProperties, &CombinerPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::CombinerProperties& properties) { - if (!mEmitter) { return; } - mEmitter->setCombinerProperties(properties); - emit propertiesChanged(); - }); - // Child Editor Widget connect(mChildEditorWidget, &ChildEditorWidget::flagsUpdated, this, [this](const BitFlag& childFlags) { if (!mEmitter) { return; } @@ -203,6 +196,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mVolumeProperties->setDocument(document); mRotationProperties->setDocument(document); mAlphaProperties->setDocument(document); + mCombinerProperties->setDocument(document); } void EmitterWidget::setSelection(Ptcl::Selection* selection) { @@ -219,6 +213,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mVolumeProperties->setSelection(selection); mRotationProperties->setSelection(selection); mAlphaProperties->setSelection(selection); + mCombinerProperties->setSelection(selection); connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -259,7 +254,6 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { void EmitterWidget::populateProperties() { QSignalBlocker b8(mColorProperties); QSignalBlocker b12(mTextureProperties); - QSignalBlocker b13(mCombinerProperties); QSignalBlocker b15(mChildEditorWidget); QSignalBlocker b16(mFluctuationEditorWidget); QSignalBlocker b17(mFieldEditorWidget); @@ -267,8 +261,6 @@ void EmitterWidget::populateProperties() { mColorProperties->setProperties(mEmitter->colorProperties()); mTextureProperties->setProperties(mEmitter->textureProperties(), mEmitter->textureHandle().get()); - mCombinerProperties->setProperties(mEmitter->combinerProperties()); - mCombinerProperties->setCombinerSrc(&mEmitter->textureHandle(), &mEmitter->colorProperties().color1, &mEmitter->colorProperties().color0[0]); mChildEditorWidget->setChildData(&mEmitter->childData(), mEmitter->complexProperties().childFlags); mChildEditorWidget->setParentColor0(mEmitter->colorProperties().color0[0]); diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index 6745bc2..23b2e9e 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -358,9 +358,9 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { ptclLife = emitter.ptclLife(); ptclLifeRnd = emitter.ptclLifeRandom(); airResistance = emitter.airResistance(); - blendFunc = emitter.combinerProperties().blendFunc; + blendFunc = emitter.blendFunction(); billboardType = emitter.billboardType(); - depthFunc = emitter.combinerProperties().depthFunc; + depthFunc = emitter.depthFunction(); gravity = emitter.gravity(); color0 = emitter.colorProperties().color0; color1 = emitter.colorProperties().color1; @@ -383,7 +383,7 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { rotCalcType = static_cast(emitter.rotationType()) + 5 * static_cast(emitter.colorProperties().colorCalcType); followType = emitter.followType(); - colorCombinerFunc = emitter.combinerProperties().combinerFunc; + colorCombinerFunc = emitter.combinerFunction(); initRot = emitter.initialRotation(); initRotRand = emitter.initialRotationRandom(); rotVel = emitter.rotationVelocity(); diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index c9dac57..7c19647 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -77,8 +77,12 @@ std::unique_ptr Emitter::clone() const { newEmitter->mRotVelRand = mRotVelRand; newEmitter->mRotBasis = mRotBasis; + // Combiner Properties + newEmitter->mBlendFunc = mBlendFunc; + newEmitter->mDepthFunc = mDepthFunc; + newEmitter->mCombinerFunc = mCombinerFunc; + newEmitter->mTextureProperties = mTextureProperties; - newEmitter->mCombinerProperties = mCombinerProperties; newEmitter->mTextureHandle = mTextureHandle.clone(); newEmitter->mComplexProperties = mComplexProperties; newEmitter->mChildData = std::move(*mChildData.clone()); @@ -127,15 +131,6 @@ void Emitter::setTextureProperties(const TextureProperties& textureProperties) { mTextureProperties = textureProperties; } -const Emitter::CombinerProperties& Emitter::combinerProperties() const { - return mCombinerProperties; -} - -void Emitter::setCombinerProperties(const CombinerProperties& combinerProperties) { - mCombinerProperties = combinerProperties; - mFlag.set(EmitterFlag::EnableFog, mCombinerProperties.isFogEnabled); -} - const Emitter::ComplexProperties& Emitter::complexProperties() const { return mComplexProperties; } @@ -330,12 +325,10 @@ void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { mRotVelRand = Math::Vector3i(emitterData.rotVelRand.x, emitterData.rotVelRand.y, emitterData.rotVelRand.z); mRotBasis = Math::Vector2f(emitterData.rotBasis.x, emitterData.rotBasis.y); - mCombinerProperties = { - .blendFunc = emitterData.blendFunc, - .depthFunc = emitterData.depthFunc, - .combinerFunc = emitterData.colorCombinerFunc, - .isFogEnabled = emitterData.flag.isSet(EmitterFlag::EnableFog) - }; + // Combiner Properties + mBlendFunc = emitterData.blendFunc; + mDepthFunc = emitterData.depthFunc; + mCombinerFunc = emitterData.colorCombinerFunc; } void Emitter::initComplexFromBinary(const BinComplexEmitterData& emitterData) { From 00fbcddb320e367c3913d1747bfe20986b706370 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Thu, 12 Mar 2026 17:24:21 +0000 Subject: [PATCH 15/35] feat(ui): implement undo/redo for Emitter colorProperties --- .../emitterWidget/colorPropertiesWidget.h | 49 +- include/editor/emitterWidget/emitterWidget.h | 3 +- include/ptcl/ptclBinary.h | 4 + include/ptcl/ptclEmitter.h | 84 +++- .../emitterWidget/colorPropertiesWidget.cpp | 436 +++++++++++------- .../combinerPropertiesWidget.cpp | 2 +- src/editor/emitterWidget/emitterWidget.cpp | 15 +- src/ptcl/ptclBinary.cpp | 14 +- src/ptcl/ptclEmitter.cpp | 39 +- 9 files changed, 396 insertions(+), 250 deletions(-) diff --git a/include/editor/emitterWidget/colorPropertiesWidget.h b/include/editor/emitterWidget/colorPropertiesWidget.h index ab7932f..70a877c 100644 --- a/include/editor/emitterWidget/colorPropertiesWidget.h +++ b/include/editor/emitterWidget/colorPropertiesWidget.h @@ -4,9 +4,7 @@ #include "editor/components/enumComboBox.h" #include "editor/components/rgbaColorWidget.h" #include "editor/components/sizedSpinBox.h" -#include "editor/emitterWidget/emitterWidget.h" - -#include "ptcl/ptclEmitter.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include #include @@ -20,7 +18,7 @@ namespace PtclEditor { enum class Behavior { Constant = 0, Random = 1, Animation = 2 }; -static Behavior behaviorFromIndex(int index) { +static Behavior behaviorFromIndex(s32 index) { switch (index) { case 1: return Behavior::Random; case 2: return Behavior::Animation; @@ -28,48 +26,49 @@ static Behavior behaviorFromIndex(int index) { } } -static int behaviorToIndex(Behavior behavior) { - return static_cast(behavior); +static s32 behaviorToIndex(Behavior behavior) { + return static_cast(behavior); } // ========================================================================== // -class EmitterWidget::ColorPropertiesWidget final : public QWidget { +class ColorPropertiesWidget final : public EmitterWidgetBase { Q_OBJECT public: explicit ColorPropertiesWidget(QWidget* parent = nullptr); - void setProperties(const Ptcl::Emitter::ColorProperties& properties); - void populateWidgets(); - -signals: - void propertiesUpdated(const Ptcl::Emitter::ColorProperties& properties); - private slots: - void handleColorChanged(); void updateColorSection(ColorGradientEditor::HandleType handleType); - void handleBehaviorChanged(int index); + void handleBehaviorChanged(s32 index); private: - void updateUiFromFlags(); - void applyBehaviorToUI(Behavior behavior); - void showColorWidgets(std::array visibility); - void setColorLabels(const std::array& labels); + void populateProperties() final; + void setupConnections(); private: - Ptcl::Emitter::ColorProperties mProps{}; - QComboBox mColorBehavior{}; - std::array mColorLabels; - std::array mColor0Widgets; + + RGBAColorWidget mPrimaryColorWidget{}; + QWidget* mPrimaryColorUi{nullptr}; + + RGBAColorWidget mRandomColorAWidget{}; + RGBAColorWidget mRandomColorBWidget{}; + RGBAColorWidget mRandomColorCWidget{}; + QWidget* mRandomColorUi{nullptr}; + + RGBAColorWidget mStartColorWidget{}; + RGBAColorWidget mMidColorWidget{}; + RGBAColorWidget mEndColorWidget{}; + QWidget* mAnimColorUi{nullptr}; + + RGBAColorWidget mSecondaryColorWidget{}; + ColorGradientEditor mColorSections{}; SizedSpinBox mColorNumRepeatSpinBox{}; QLabel mRepetitionCountLabel{}; EnumComboBox mColorCalcTypeSpinBox{}; - - RGBAColorWidget mColor1Widget{}; }; diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index 100b787..0bc9d8d 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -34,7 +34,7 @@ class VolumePropertiesWidget; class RotationPropertiesWidget; class AlphaPropertiesWidget; class CombinerPropertiesWidget; - +class ColorPropertiesWidget; // ========================================================================== // @@ -61,7 +61,6 @@ class EmitterWidget final : public QWidget { void populateProperties(); private: - class ColorPropertiesWidget; class TexturePropertiesWidget; private: diff --git a/include/ptcl/ptclBinary.h b/include/ptcl/ptclBinary.h index 817ce33..cc764db 100644 --- a/include/ptcl/ptclBinary.h +++ b/include/ptcl/ptclBinary.h @@ -128,6 +128,8 @@ struct alignas(4) binColor4f { friend QDataStream& operator<<(QDataStream& out, const binColor4f& item); friend QDebug operator<<(QDebug dbg, const binColor4f& item); + + bool operator==(const binColor4f&) const = default; }; static_assert(sizeof(binColor4f) == 0x10, "binColor4f is incorrect size."); @@ -150,6 +152,8 @@ struct alignas(4) binColor3f { friend QDataStream& operator<<(QDataStream& out, const binColor3f& item); friend QDebug operator<<(QDebug dbg, const binColor3f& item); + + bool operator==(const binColor3f&) const = default; }; static_assert(sizeof(binColor3f) == 0x0C, "binColor3f is incorrect size."); diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index 9e36403..ad3b409 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -40,24 +40,6 @@ class Emitter { bool operator==(const ScaleAnim&) const = default; }; - struct ColorProperties { - std::array color0{ - binColor4f{1.0f, 1.0f, 1.0f, 1.0f}, - binColor4f{1.0f, 1.0f, 1.0f, 1.0f}, - binColor4f{1.0f, 1.0f, 1.0f, 1.0f}, - }; - s32 colorSection1{20}; - s32 colorSection2{60}; - s32 colorSection3{80}; - s32 colorNumRepeat{1}; - - bool colorRandom{false}; - bool colorAnimation{false}; - ColorCalcType colorCalcType{ColorCalcType::None}; - - binColor3f color1{255.0f, 255.0f, 255.0f}; - }; - struct AlphaAnim { f32 initAlpha{1.0f}; f32 diffAlpha21{0.0f}; @@ -311,6 +293,55 @@ class Emitter { bool isFogEnabled() const { return mFlag.isSet(EmitterFlag::EnableFog); } void setIsFogEnabled(bool enabled) { mFlag.set(EmitterFlag::EnableFog, enabled); } + // ----- Color Properties ----- \\ + + const std::array& color0() const { return mColor0; } + + const binColor4f& primaryColor() const { return mColor0[0]; } + void setPrimaryColor(const binColor4f& color) { mColor0[0] = color; } + + const binColor4f& randomColorA() const { return mColor0[0]; } + void setRandomColorA(const binColor4f& color) { mColor0[0] = color; } + + const binColor4f& randomColorB() const { return mColor0[1]; } + void setRandomColorB(const binColor4f& color) { mColor0[1] = color; } + + const binColor4f& randomColorC() const { return mColor0[2]; } + void setRandomColorC(const binColor4f& color) { mColor0[2] = color; } + + const binColor4f& startColor() const { return mColor0[0]; } + void setStartColor(const binColor4f& color) { mColor0[0] = color; } + + const binColor4f& midColor() const { return mColor0[1]; } + void setMidColor(const binColor4f& color) { mColor0[1] = color; } + + const binColor4f& endColor() const { return mColor0[2]; } + void setEndColor(const binColor4f& color) { mColor0[2] = color; } + + s32 colorSection1() const { return mColorSection1; } + void setColorSection1(s32 section) { mColorSection1 = section; } + + s32 colorSection2() const { return mColorSection2; } + void setColorSection2(s32 section) { mColorSection2 = section; } + + s32 colorSection3() const { return mColorSection3; } + void setColorSection3(s32 section) { mColorSection3 = section; } + + s32 colorNumRepeat() const { return mColorNumRepeat; } + void setColorNumRepeat(s32 num) { mColorNumRepeat = num; } + + bool isColorRandom() const { return mFlag.isSet(EmitterFlag::ColorRandom); } + void setIsColorRandom(bool isRandom) { mFlag.set(EmitterFlag::ColorRandom, isRandom); } + + bool isColorAnimation() const { return mFlag.isSet(EmitterFlag::ColorAnimation); } + void setIsColorAnimation(bool isAnim) { mFlag.set(EmitterFlag::ColorAnimation, isAnim); } + + ColorCalcType colorCalcType() const { return mColorCalcType; } + void setColorCalcType(ColorCalcType type) { mColorCalcType = type; } + + const binColor3f& secondaryColor() const { return mColor1; } + void setSecondaryColor(const binColor3f& color) { mColor1 = color; } + BitFlag& flags(); const BitFlag& flags() const; @@ -318,8 +349,6 @@ class Emitter { const TextureHandle& textureHandle() const; void setTexture(const std::shared_ptr& texture); - const ColorProperties& colorProperties() const; - void setColorProperties(const ColorProperties& colorProperties); const TextureProperties& textureProperties() const; void setTextureProperties(const TextureProperties& textureProperties); @@ -432,7 +461,20 @@ class Emitter { DepthFuncType mDepthFunc{DepthFuncType::Unk0}; ColorCombinerFuncType mCombinerFunc{ColorCombinerFuncType::CombinerConfig0}; - ColorProperties mColorProperties{}; + // Color Properties + std::array mColor0{ + binColor4f{1.0f, 1.0f, 1.0f, 1.0f}, + binColor4f{1.0f, 1.0f, 1.0f, 1.0f}, + binColor4f{1.0f, 1.0f, 1.0f, 1.0f}, + }; + s32 mColorSection1{20}; + s32 mColorSection2{60}; + s32 mColorSection3{80}; + s32 mColorNumRepeat{1}; + ColorCalcType mColorCalcType{ColorCalcType::None}; + + binColor3f mColor1{255.0f, 255.0f, 255.0f}; + TextureProperties mTextureProperties{}; TextureHandle mTextureHandle{}; diff --git a/src/editor/emitterWidget/colorPropertiesWidget.cpp b/src/editor/emitterWidget/colorPropertiesWidget.cpp index 07ec4a7..fe7a00e 100644 --- a/src/editor/emitterWidget/colorPropertiesWidget.cpp +++ b/src/editor/emitterWidget/colorPropertiesWidget.cpp @@ -10,8 +10,8 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::ColorPropertiesWidget::ColorPropertiesWidget(QWidget* parent) : - QWidget{parent} { +ColorPropertiesWidget::ColorPropertiesWidget(QWidget* parent) : + EmitterWidgetBase{parent} { // Color Behavior Type auto colorBehaviorLayout = new QHBoxLayout; mColorBehavior.addItem("Constant", QVariant::fromValue(Behavior::Constant)); @@ -20,42 +20,179 @@ EmitterWidget::ColorPropertiesWidget::ColorPropertiesWidget(QWidget* parent) : colorBehaviorLayout->addWidget(new QLabel("Color Behavior")); colorBehaviorLayout->addWidget(&mColorBehavior); colorBehaviorLayout->addStretch(); - connect(&mColorBehavior, &QComboBox::currentIndexChanged, this, &ColorPropertiesWidget::handleBehaviorChanged); - - // Color Properties - auto colorLabelLayout = new QHBoxLayout; - auto colorsLayout = new QHBoxLayout; - for (s32 i = 0; i < mColor0Widgets.size(); ++i) { - colorsLayout->addWidget(&mColor0Widgets[i]); - mColor0Widgets[i].setProperty("colorIndex", i); - connect(&mColor0Widgets[i], &RGBAColorWidget::colorChanged, this, &ColorPropertiesWidget::handleColorChanged); - colorLabelLayout->addWidget(&mColorLabels[i]); - } - - // Color Sections - connect(&mColorSections, &ColorGradientEditor::handleMoved, this, &ColorPropertiesWidget::updateColorSection); - // Color Repeat + // Primary Color Ui + mPrimaryColorUi = new QWidget(this); + auto* mPrimaryColorLayout = new QVBoxLayout(mPrimaryColorUi); + mPrimaryColorLayout->addWidget(new QLabel("Primary Color")); + mPrimaryColorLayout->addWidget(&mPrimaryColorWidget); + + // Random Color Ui + mRandomColorUi = new QWidget(this); + auto* mRandomColorLayout = new QVBoxLayout(mRandomColorUi); + mRandomColorLayout->addWidget(new QLabel("Random Color A")); + mRandomColorLayout->addWidget(&mRandomColorAWidget); + mRandomColorLayout->addWidget(new QLabel("Random Color B")); + mRandomColorLayout->addWidget(&mRandomColorBWidget); + mRandomColorLayout->addWidget(new QLabel("Random Color C")); + mRandomColorLayout->addWidget(&mRandomColorCWidget); + + // Color Repetition auto colorRepeatLayout = new QHBoxLayout; mRepetitionCountLabel.setText("Repetition Count"); colorRepeatLayout->addWidget(&mRepetitionCountLabel); colorRepeatLayout->addWidget(&mColorNumRepeatSpinBox); colorRepeatLayout->addStretch(); + + // Anim Color Ui + mAnimColorUi = new QWidget(this); + auto* mAnimColorLayout = new QVBoxLayout(mAnimColorUi); + mAnimColorLayout->addWidget(new QLabel("Anim Start Color")); + mAnimColorLayout->addWidget(&mStartColorWidget); + mAnimColorLayout->addWidget(new QLabel("Anim Mid Color")); + mAnimColorLayout->addWidget(&mMidColorWidget); + mAnimColorLayout->addWidget(new QLabel("Anim End Color")); + mAnimColorLayout->addWidget(&mEndColorWidget); + mAnimColorLayout->addWidget(&mColorSections); + mAnimColorLayout->addLayout(colorRepeatLayout); + + // Secondary Color + mSecondaryColorWidget.enableAlpha(false); + + // Main Layout + auto mainLayout = new QVBoxLayout(this); + mainLayout->addLayout(colorBehaviorLayout); + + mainLayout->addWidget(mPrimaryColorUi); + mainLayout->addWidget(mRandomColorUi); + mainLayout->addWidget(mAnimColorUi); + + mainLayout->addWidget(new QLabel("Secondary Color")); + mainLayout->addWidget(&mSecondaryColorWidget); + mainLayout->addWidget(new QLabel("Color Calc Type")); + mainLayout->addWidget(&mColorCalcTypeSpinBox); + + setLayout(mainLayout); + setupConnections(); +} + +void ColorPropertiesWidget::setupConnections() { + // Behavior Type + connect(&mColorBehavior, &QComboBox::currentIndexChanged, this, &ColorPropertiesWidget::handleBehaviorChanged); + + // Color Sections + connect(&mColorSections, &ColorGradientEditor::handleMoved, this, &ColorPropertiesWidget::updateColorSection); + + // Color Repeat connect(&mColorNumRepeatSpinBox, &SizedSpinBoxBase::valueChanged, this, [this](s32 value) { - mColorSections.setRepetitionCount(value); - mProps.colorNumRepeat = value; + setEmitterProperty( + "Set Color Repetitions", + "SetColorRepetitions", + &Ptcl::Emitter::colorNumRepeat, + &Ptcl::Emitter::setColorNumRepeat, + value + ); }); // Color Calc Type connect(&mColorCalcTypeSpinBox, &QComboBox::currentIndexChanged, this, [this]() { - mProps.colorCalcType = mColorCalcTypeSpinBox.currentEnum(); - emit propertiesUpdated(mProps); + const auto type = mColorCalcTypeSpinBox.currentEnum(); + setEmitterProperty( + "Set Color Calc Type", + "SetColorCalcType", + &Ptcl::Emitter::colorCalcType, + &Ptcl::Emitter::setColorCalcType, + type + ); + }); + + // Primary Color + connect(&mPrimaryColorWidget, &RGBAColorWidget::colorChanged, this, [this]() { + const auto& color = mPrimaryColorWidget.color(); + setEmitterProperty( + "Set Primary Color", + "SetPrimaryColor", + &Ptcl::Emitter::primaryColor, + &Ptcl::Emitter::setPrimaryColor, + color + ); + }); + + // Random Color A + connect(&mRandomColorAWidget, &RGBAColorWidget::colorChanged, this, [this]() { + const auto& color = mRandomColorAWidget.color(); + setEmitterProperty( + "Set Random Color A", + "SetRandomColorA", + &Ptcl::Emitter::randomColorA, + &Ptcl::Emitter::setRandomColorA, + color + ); + }); + + // Random Color B + connect(&mRandomColorBWidget, &RGBAColorWidget::colorChanged, this, [this]() { + const auto& color = mRandomColorBWidget.color(); + setEmitterProperty( + "Set Random Color B", + "SetRandomColorB", + &Ptcl::Emitter::randomColorB, + &Ptcl::Emitter::setRandomColorB, + color + ); + }); + + // Random Color C + connect(&mRandomColorCWidget, &RGBAColorWidget::colorChanged, this, [this]() { + const auto& color = mRandomColorCWidget.color(); + setEmitterProperty( + "Set Random Color C", + "SetRandomColorC", + &Ptcl::Emitter::randomColorC, + &Ptcl::Emitter::setRandomColorC, + color + ); }); - // Color1 - mColor1Widget.enableAlpha(false); - connect(&mColor1Widget, &RGBAColorWidget::colorChanged, this, [this]() { - const auto& color = mColor1Widget.color(); + // Anim Start Color + connect(&mStartColorWidget, &RGBAColorWidget::colorChanged, this, [this]() { + const auto& color = mStartColorWidget.color(); + setEmitterProperty( + "Set Start Color", + "SetStartColor", + &Ptcl::Emitter::startColor, + &Ptcl::Emitter::setStartColor, + color + ); + }); + + // Anim Mid Color + connect(&mMidColorWidget, &RGBAColorWidget::colorChanged, this, [this]() { + const auto& color = mMidColorWidget.color(); + setEmitterProperty( + "Set Mid Color", + "SetMidColor", + &Ptcl::Emitter::midColor, + &Ptcl::Emitter::setMidColor, + color + ); + }); + + // Anim End Color + connect(&mEndColorWidget, &RGBAColorWidget::colorChanged, this, [this]() { + const auto& color = mEndColorWidget.color(); + setEmitterProperty( + "Set End Color", + "SetEndColor", + &Ptcl::Emitter::endColor, + &Ptcl::Emitter::setEndColor, + color + ); + }); + + // Secondary Color + connect(&mSecondaryColorWidget, &RGBAColorWidget::colorChanged, this, [this]() { + const auto& color = mSecondaryColorWidget.color(); Ptcl::binColor3f newColor{ color.r * 255.0f, @@ -63,184 +200,165 @@ EmitterWidget::ColorPropertiesWidget::ColorPropertiesWidget(QWidget* parent) : color.b * 255.0f }; - mProps.color1 = newColor; - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Secondary Color", + "SetSecondaryColor", + &Ptcl::Emitter::secondaryColor, + &Ptcl::Emitter::setSecondaryColor, + newColor + ); }); - - // Main Layout - auto mainLayout = new QVBoxLayout(this); - mainLayout->addLayout(colorBehaviorLayout); - mainLayout->addLayout(colorLabelLayout); - mainLayout->addLayout(colorsLayout); - mainLayout->addWidget(&mColorSections); - mainLayout->addLayout(colorRepeatLayout); - mainLayout->addWidget(new QLabel("Color1:")); - mainLayout->addWidget(&mColor1Widget); - mainLayout->addWidget(new QLabel("Calc Type:")); - mainLayout->addWidget(&mColorCalcTypeSpinBox); - - setLayout(mainLayout); -} - -void EmitterWidget::ColorPropertiesWidget::setProperties(const Ptcl::Emitter::ColorProperties& properties) { - mProps = properties; - populateWidgets(); } -void EmitterWidget::ColorPropertiesWidget::populateWidgets() { +void ColorPropertiesWidget::populateProperties() { QSignalBlocker b1(mColorSections); QSignalBlocker b2(mColorNumRepeatSpinBox); QSignalBlocker b3(mColorCalcTypeSpinBox); - QSignalBlocker b4(mColor1Widget); + QSignalBlocker b4(mPrimaryColorWidget); + QSignalBlocker b5(mRandomColorAWidget); + QSignalBlocker b6(mRandomColorBWidget); + QSignalBlocker b7(mRandomColorCWidget); + QSignalBlocker b8(mStartColorWidget); + QSignalBlocker b9(mMidColorWidget); + QSignalBlocker b10(mEndColorWidget); + QSignalBlocker b11(mColorBehavior); - QSignalBlocker bColor0(mColor0Widgets[0]); - QSignalBlocker bColor1(mColor0Widgets[1]); - QSignalBlocker bColor2(mColor0Widgets[2]); + mPrimaryColorWidget.setColor(mEmitter->primaryColor()); - for (s32 i = 0; i < mColor0Widgets.size(); ++i) { - mColor0Widgets[i].setColor(mProps.color0[i]); - } + mRandomColorAWidget.setColor(mEmitter->randomColorA()); + mRandomColorBWidget.setColor(mEmitter->randomColorB()); + mRandomColorCWidget.setColor(mEmitter->randomColorC()); + + mStartColorWidget.setColor(mEmitter->startColor()); + mMidColorWidget.setColor(mEmitter->midColor()); + mEndColorWidget.setColor(mEmitter->endColor()); mColorSections.setTimings( - mProps.colorSection1, - mProps.colorSection2, - mProps.colorSection3 + mEmitter->colorSection1(), + mEmitter->colorSection2(), + mEmitter->colorSection3() ); - const auto& colors = mProps.color0; - mColorSections.setInitialColor(QColor::fromRgbF(colors[0].r, colors[0].g, colors[0].b)); - mColorSections.setPeakColor(QColor::fromRgbF(colors[1].r, colors[1].g, colors[1].b)); - mColorSections.setEndColor(QColor::fromRgbF(colors[2].r, colors[2].g, colors[2].b)); + const auto& startColor = mEmitter->startColor(); + mColorSections.setInitialColor(QColor::fromRgbF(startColor.r, startColor.g, startColor.b)); - mColorSections.setRepetitionCount(mProps.colorNumRepeat); - mColorNumRepeatSpinBox.setValue(mProps.colorNumRepeat); - mColorCalcTypeSpinBox.setCurrentEnum(mProps.colorCalcType); + const auto& midColor = mEmitter->midColor(); + mColorSections.setPeakColor(QColor::fromRgbF(midColor.r, midColor.g, midColor.b)); - Ptcl::binColor4f color1{ - std::clamp(mProps.color1.r / 255.0f, 0.0f, 1.0f), - std::clamp(mProps.color1.g / 255.0f, 0.0f, 1.0f), - std::clamp(mProps.color1.b / 255.0f, 0.0f, 1.0f), - 1.0f - }; - mColor1Widget.setColor(color1); + const auto& endColor = mEmitter->endColor(); + mColorSections.setEndColor(QColor::fromRgbF(endColor.r, endColor.g, endColor.b)); - updateUiFromFlags(); -} + mColorSections.setRepetitionCount(mEmitter->colorNumRepeat()); + mColorNumRepeatSpinBox.setValue(mEmitter->colorNumRepeat()); + mColorCalcTypeSpinBox.setCurrentEnum(mEmitter->colorCalcType()); -void EmitterWidget::ColorPropertiesWidget::updateUiFromFlags() { + Ptcl::binColor4f secondaryColor{ + std::clamp(mEmitter->secondaryColor().r / 255.0f, 0.0f, 1.0f), + std::clamp(mEmitter->secondaryColor().g / 255.0f, 0.0f, 1.0f), + std::clamp(mEmitter->secondaryColor().b / 255.0f, 0.0f, 1.0f), + 1.0f + }; + mSecondaryColorWidget.setColor(secondaryColor); Behavior behavior = Behavior::Constant; - if (mProps.colorRandom) { + if (mEmitter->isColorRandom()) { behavior = Behavior::Random; - } else if (mProps.colorAnimation) { + mPrimaryColorUi->setVisible(false); + mRandomColorUi->setVisible(true); + mAnimColorUi->setVisible(false); + } else if (mEmitter->isColorAnimation()) { behavior = Behavior::Animation; + mPrimaryColorUi->setVisible(false); + mRandomColorUi->setVisible(false); + mAnimColorUi->setVisible(true); + } else { + mPrimaryColorUi->setVisible(true); + mRandomColorUi->setVisible(false); + mAnimColorUi->setVisible(false); } - QSignalBlocker blocker(mColorBehavior); mColorBehavior.setCurrentIndex(behaviorToIndex(behavior)); - applyBehaviorToUI(behavior); -} - -void EmitterWidget::ColorPropertiesWidget::applyBehaviorToUI(Behavior behavior) { - switch (behavior) { - case Behavior::Constant: - showColorWidgets({true, false, false}); - setColorLabels({"Constant Color", "", ""}); - mColorSections.setVisible(false); - break; - case Behavior::Random: - showColorWidgets({true, true, true}); - setColorLabels({"Random Color 1", "Random Color 2", "Random Color 3"}); - mColorSections.setVisible(false); - break; - case Behavior::Animation: - showColorWidgets({true, true, true}); - setColorLabels({"Initial Color", "Peak Color", "Out Color"}); - mColorSections.setVisible(true); - break; - } - - const bool showRepeat = (behavior == Behavior::Animation); - mColorNumRepeatSpinBox.setVisible(showRepeat); - mRepetitionCountLabel.setVisible(showRepeat); } -void EmitterWidget::ColorPropertiesWidget::showColorWidgets(std::array visibility) { - for (s32 i = 0; i < mColor0Widgets.size(); ++i) { - mColor0Widgets[i].setVisible(visibility[i]); - } -} - -void EmitterWidget::ColorPropertiesWidget::setColorLabels(const std::array& labels) { - for (s32 i = 0; i < mColorLabels.size(); ++i) { - mColorLabels[i].setText(labels[i]); - mColorLabels[i].setVisible(!labels[i].isEmpty()); - } -} - -void EmitterWidget::ColorPropertiesWidget::handleBehaviorChanged(s32 index) { +void ColorPropertiesWidget::handleBehaviorChanged(s32 index) { auto behavior = behaviorFromIndex(index); - applyBehaviorToUI(behavior); + bool isColorRandom; + bool isColorAnim; + QString label; switch (behavior) { case Behavior::Constant: - mProps.colorRandom = false; - mProps.colorAnimation = false; + label = "Set Color Behavior Constant"; + isColorRandom = false; + isColorAnim = false; break; case Behavior::Random: - mProps.colorRandom = true; - mProps.colorAnimation = false; + label = "Set Color Behavior Random"; + isColorRandom = true; + isColorAnim = false; break; case Behavior::Animation: - mProps.colorRandom = false; - mProps.colorAnimation = true; + label = "Set Color Behavior Animation"; + isColorRandom = false; + isColorAnim = true; break; } - emit propertiesUpdated(mProps); -} - -void EmitterWidget::ColorPropertiesWidget::handleColorChanged() { - auto* widget = qobject_cast(sender()); - if (!widget) { - return; - } - - s32 index = widget->property("colorIndex").toInt(); - if (index < 0 || index >= mColor0Widgets.size()) { - return; - } + mDocument->undoStack()->beginMacro(formatLabel(label)); - const auto& color = widget->color(); - mProps.color0[index] = color; + setEmitterProperty( + "Set Color Random", + "SetColorRandom", + &Ptcl::Emitter::isColorRandom, + &Ptcl::Emitter::setIsColorRandom, + isColorRandom + ); - QColor qcolor = QColor::fromRgbF(color.r, color.g, color.b); - if (index == 0) { - mColorSections.setInitialColor(qcolor); - } else if (index == 1) { - mColorSections.setPeakColor(qcolor); - } else if (index == 2) { - mColorSections.setEndColor(qcolor); - } + setEmitterProperty( + "Set Color Anim", + "SetColorAnim", + &Ptcl::Emitter::isColorAnimation, + &Ptcl::Emitter::setIsColorAnimation, + isColorAnim + ); - emit propertiesUpdated(mProps); + mDocument->undoStack()->endMacro(); } -void EmitterWidget::ColorPropertiesWidget::updateColorSection(ColorGradientEditor::HandleType handleType) { +void ColorPropertiesWidget::updateColorSection(ColorGradientEditor::HandleType handleType) { switch (handleType) { - case ColorGradientEditor::HandleType::InCompletedHandle: - mProps.colorSection1 = mColorSections.inCompletedTiming(); - break; - case ColorGradientEditor::HandleType::PeakHandle: - mProps.colorSection2 = mColorSections.peakTiming(); - break; - case ColorGradientEditor::HandleType::OutStartHandle: - mProps.colorSection3 = mColorSections.outStartTiming(); - break; + case ColorGradientEditor::HandleType::InCompletedHandle: { + setEmitterProperty( + "Set Color Anim In Timing", + "SetColorAnimSection1", + &Ptcl::Emitter::colorSection1, + &Ptcl::Emitter::setColorSection1, + mColorSections.inCompletedTiming() + ); + } + break; + case ColorGradientEditor::HandleType::PeakHandle: { + setEmitterProperty( + "Set Color Anim Peak Timing", + "SetColorAnimSection2", + &Ptcl::Emitter::colorSection2, + &Ptcl::Emitter::setColorSection2, + mColorSections.peakTiming() + ); + } + break; + case ColorGradientEditor::HandleType::OutStartHandle: { + setEmitterProperty( + "Set Color Anim Out Timing", + "SetColorAnimSection3", + &Ptcl::Emitter::colorSection3, + &Ptcl::Emitter::setColorSection3, + mColorSections.outStartTiming() + ); + } + break; } - - emit propertiesUpdated(mProps); } diff --git a/src/editor/emitterWidget/combinerPropertiesWidget.cpp b/src/editor/emitterWidget/combinerPropertiesWidget.cpp index f33deb0..7b3bec0 100644 --- a/src/editor/emitterWidget/combinerPropertiesWidget.cpp +++ b/src/editor/emitterWidget/combinerPropertiesWidget.cpp @@ -80,7 +80,7 @@ void CombinerPropertiesWidget::populateProperties() { mDepthFuncComboBox.setCurrentEnum(mEmitter->depthFunction()); mCombinerFuncComboBox.setCurrentEnum(mEmitter->combinerFunction()); mCombinerPreview.setConfig(static_cast(mEmitter->combinerFunction())); - mCombinerPreview.setCombinerSrc(&mEmitter->textureHandle(), &mEmitter->colorProperties().color1, &mEmitter->colorProperties().color0[0]); + mCombinerPreview.setCombinerSrc(&mEmitter->textureHandle(), &mEmitter->secondaryColor(), &mEmitter->primaryColor()); } void CombinerPropertiesWidget::updateCombinerPreview() { diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index d7d0994..b4b4a23 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -127,15 +127,6 @@ void EmitterWidget::setupConnections() { emit propertiesChanged(); }); - // Color Properties - connect(mColorProperties, &ColorPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::ColorProperties& properties) { - if (!mEmitter) { return; } - mEmitter->setColorProperties(properties); - mCombinerProperties->updateCombinerPreview(); - mChildEditorWidget->setParentColor0(mEmitter->colorProperties().color0[0]); - emit propertiesChanged(); - }); - // Child Editor Widget connect(mChildEditorWidget, &ChildEditorWidget::flagsUpdated, this, [this](const BitFlag& childFlags) { if (!mEmitter) { return; } @@ -197,6 +188,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mRotationProperties->setDocument(document); mAlphaProperties->setDocument(document); mCombinerProperties->setDocument(document); + mColorProperties->setDocument(document); } void EmitterWidget::setSelection(Ptcl::Selection* selection) { @@ -214,6 +206,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mRotationProperties->setSelection(selection); mAlphaProperties->setSelection(selection); mCombinerProperties->setSelection(selection); + mColorProperties->setSelection(selection); connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -252,18 +245,16 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { } void EmitterWidget::populateProperties() { - QSignalBlocker b8(mColorProperties); QSignalBlocker b12(mTextureProperties); QSignalBlocker b15(mChildEditorWidget); QSignalBlocker b16(mFluctuationEditorWidget); QSignalBlocker b17(mFieldEditorWidget); QSignalBlocker b18(mStripeEditorWidget); - mColorProperties->setProperties(mEmitter->colorProperties()); mTextureProperties->setProperties(mEmitter->textureProperties(), mEmitter->textureHandle().get()); mChildEditorWidget->setChildData(&mEmitter->childData(), mEmitter->complexProperties().childFlags); - mChildEditorWidget->setParentColor0(mEmitter->colorProperties().color0[0]); + mChildEditorWidget->setParentColor0(mEmitter->primaryColor()); mFluctuationEditorWidget->setData(mEmitter->fluctuationData(), mEmitter->complexProperties().fluctuationFlags); mFieldEditorWidget->setData(&mEmitter->fieldData(), mEmitter->complexProperties().fieldFlags); diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index 23b2e9e..c0ab145 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -362,12 +362,12 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { billboardType = emitter.billboardType(); depthFunc = emitter.depthFunction(); gravity = emitter.gravity(); - color0 = emitter.colorProperties().color0; - color1 = emitter.colorProperties().color1; - colorSection1 = emitter.colorProperties().colorSection1; - colorSection2 = emitter.colorProperties().colorSection2; - colorSection3 = emitter.colorProperties().colorSection3; - colorNumRepeat = emitter.colorProperties().colorNumRepeat; + color0 = emitter.color0(); + color1 = emitter.secondaryColor(); + colorSection1 = emitter.colorSection1(); + colorSection2 = emitter.colorSection2(); + colorSection3 = emitter.colorSection3(); + colorNumRepeat = emitter.colorNumRepeat(); initAlpha = emitter.alphaAnim().initAlpha; diffAlpha21 = emitter.alphaAnim().diffAlpha21; diffAlpha32 = emitter.alphaAnim().diffAlpha32; @@ -381,7 +381,7 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { scaleSection2 = emitter.scaleAnim().scaleSection2; scaleRand = emitter.scaleRand(); - rotCalcType = static_cast(emitter.rotationType()) + 5 * static_cast(emitter.colorProperties().colorCalcType); + rotCalcType = static_cast(emitter.rotationType()) + 5 * static_cast(emitter.colorCalcType()); followType = emitter.followType(); colorCombinerFunc = emitter.combinerFunction(); initRot = emitter.initialRotation(); diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index 7c19647..d69bd73 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -60,7 +60,14 @@ std::unique_ptr Emitter::clone() const { newEmitter->mVolumeSweepStart = mVolumeSweepStart; newEmitter->mVolumeSweepParam = mVolumeSweepParam; - newEmitter->mColorProperties = mColorProperties; + // Color Properties + newEmitter->mColor0 = mColor0; + newEmitter->mColorSection1 = mColorSection1; + newEmitter->mColorSection2 = mColorSection2; + newEmitter->mColorSection3 = mColorSection3; + newEmitter->mColorNumRepeat = mColorNumRepeat; + newEmitter->mColorCalcType = mColorCalcType; + newEmitter->mColor1 = mColor1; // Alpha Properties newEmitter->mAlphaAnim = mAlphaAnim; @@ -112,17 +119,6 @@ void Emitter::setTexture(const std::shared_ptr& texture) { mTextureHandle.set(texture); } -const Emitter::ColorProperties& Emitter::colorProperties() const { - return mColorProperties; -} - -void Emitter::setColorProperties(const ColorProperties& colorProperties) { - mColorProperties = colorProperties; - // Sync color flags with mFlag - mFlag.set(EmitterFlag::ColorRandom, mColorProperties.colorRandom); - mFlag.set(EmitterFlag::ColorAnimation, mColorProperties.colorAnimation); -} - const Emitter::TextureProperties& Emitter::textureProperties() const { return mTextureProperties; } @@ -285,17 +281,14 @@ void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { mVolumeSweepStart = emitterData.volumeSweepStart; mVolumeSweepParam = emitterData.volumeSweepParam; - mColorProperties = { - .color0 = emitterData.color0, - .colorSection1 = emitterData.colorSection1, - .colorSection2 = emitterData.colorSection2, - .colorSection3 = emitterData.colorSection3, - .colorNumRepeat = emitterData.colorNumRepeat, - .colorRandom = emitterData.flag.isSet(EmitterFlag::ColorRandom), - .colorAnimation = emitterData.flag.isSet(EmitterFlag::ColorAnimation), - .colorCalcType = static_cast(emitterData.rotCalcType / 5), - .color1 = emitterData.color1 - }; + // Color Properties + mColor0 = emitterData.color0; + mColorSection1 = emitterData.colorSection1; + mColorSection2 = emitterData.colorSection2; + mColorSection3 = emitterData.colorSection3; + mColorNumRepeat = emitterData.colorNumRepeat; + mColorCalcType = static_cast(emitterData.rotCalcType / 5); + mColor1 = emitterData.color1; // Alpha Properties mAlphaAnim = { From 5b82182e0b4becaca946e0012e6286e126937f0a Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Fri, 13 Mar 2026 16:00:38 +0000 Subject: [PATCH 16/35] feat(ui): implement undo/redo for Emitter textureProperties --- .../emitterWidget/combinerPropertiesWidget.h | 2 - include/editor/emitterWidget/emitterWidget.h | 5 +- .../emitterWidget/texturePropertiesWidget.h | 26 +- include/ptcl/ptclEmitter.h | 98 +++- .../combinerPropertiesWidget.cpp | 4 - src/editor/emitterWidget/emitterWidget.cpp | 21 +- .../emitterWidget/texturePropertiesWidget.cpp | 450 ++++++++++-------- src/ptcl/ptclBinary.cpp | 24 +- src/ptcl/ptclEmitter.cpp | 65 ++- 9 files changed, 372 insertions(+), 323 deletions(-) diff --git a/include/editor/emitterWidget/combinerPropertiesWidget.h b/include/editor/emitterWidget/combinerPropertiesWidget.h index ddecbbe..60ab67a 100644 --- a/include/editor/emitterWidget/combinerPropertiesWidget.h +++ b/include/editor/emitterWidget/combinerPropertiesWidget.h @@ -19,8 +19,6 @@ class CombinerPropertiesWidget final : public EmitterWidgetBase { public: explicit CombinerPropertiesWidget(QWidget* parent = nullptr); - void updateCombinerPreview(); - private: void populateProperties() final; void setupConnections(); diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index 0bc9d8d..c999493 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -35,6 +35,8 @@ class RotationPropertiesWidget; class AlphaPropertiesWidget; class CombinerPropertiesWidget; class ColorPropertiesWidget; +class TexturePropertiesWidget; + // ========================================================================== // @@ -60,9 +62,6 @@ class EmitterWidget final : public QWidget { void updateStripeVisibility(); void populateProperties(); -private: - class TexturePropertiesWidget; - private: Ptcl::Document* mDocument{nullptr}; const Ptcl::Selection* mSelection{nullptr}; diff --git a/include/editor/emitterWidget/texturePropertiesWidget.h b/include/editor/emitterWidget/texturePropertiesWidget.h index 9c22626..dd3f362 100644 --- a/include/editor/emitterWidget/texturePropertiesWidget.h +++ b/include/editor/emitterWidget/texturePropertiesWidget.h @@ -3,10 +3,7 @@ #include "editor/components/enumComboBox.h" #include "editor/components/sizedSpinBox.h" #include "editor/components/thumbnailWidget.h" -#include "editor/emitterWidget/emitterWidget.h" - -#include "ptcl/ptcl.h" -#include "ptcl/ptclEmitter.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include #include @@ -20,7 +17,7 @@ namespace PtclEditor { // ========================================================================== // -class EmitterWidget::TexturePropertiesWidget final : public QWidget { +class TexturePropertiesWidget final : public EmitterWidgetBase { Q_OBJECT public: enum class AnimMode { @@ -39,22 +36,14 @@ class EmitterWidget::TexturePropertiesWidget final : public QWidget { public: explicit TexturePropertiesWidget(QWidget* parent = nullptr); - void setProperties(const Ptcl::Emitter::TextureProperties& properties, const std::shared_ptr& texture); - - void populateWidgets(); - void setTextureList(const Ptcl::TextureList* textureList); - -signals: - void propertiesUpdated(const Ptcl::Emitter::TextureProperties& properties); - void textureUpdated(const std::shared_ptr& oldTexture, const std::shared_ptr& newTexture); - private slots: void changeTexture(); private: - void updateTextureDetails(); + void populateProperties() final; + void setupConnections(); + void updateTexPatTblColumns(); - void updateUVScale(); std::optional calcFrameUVOffset(s32 frame) const; QImage getFrameTexture(s32 frame) const; @@ -64,11 +53,6 @@ private slots: s32 maxFrameCount() const; private: - Ptcl::Emitter::TextureProperties mProps{}; - std::shared_ptr mTexture{}; - - const Ptcl::TextureList* mTextureList{nullptr}; - ThumbnailWidget mTexturePreview{}; EnumComboBox mWrapTComboBox{}; diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index ad3b409..a7cd894 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -50,23 +50,6 @@ class Emitter { bool operator==(const AlphaAnim&) const = default; }; - struct TextureProperties { - TextureWrap textureWrapT{TextureWrap::ClampToEdge}; - TextureWrap textureWrapS{TextureWrap::ClampToEdge}; - TextureFilter textureMagFilter{TextureFilter::Nearest}; - TextureFilter textureMinFilter{TextureFilter::Nearest}; - TextureMipFilter textureMipFilter{TextureMipFilter::None}; - - u16 numTexPat{1}; - u8 numTexDivX{1}; - u8 numTexDivY{1}; - Math::Vector2f texUVScale{1.0f, 1.0f}; - std::array texPatTbl{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - u16 texPatFreq{0}; - u16 texPatTblUse{2}; - bool isTexPatAnim{false}; - }; - struct ComplexProperties { BitFlag childFlags{ ChildFlag::AlphaInherit, @@ -342,16 +325,67 @@ class Emitter { const binColor3f& secondaryColor() const { return mColor1; } void setSecondaryColor(const binColor3f& color) { mColor1 = color; } - BitFlag& flags(); - const BitFlag& flags() const; + // ----- Texture Properties ----- \\ + + TextureWrap textureWrapT() const { return mTextureWrapT; } + void setTextureWrapT(TextureWrap wrap) { mTextureWrapT = wrap; } + + TextureWrap textureWrapS() const { return mTextureWrapS; } + void setTextureWrapS(TextureWrap wrap) { mTextureWrapS = wrap; } + + TextureFilter textureMagFilter() const { return mTextureMagFilter; } + void setTextureMagFilter(TextureFilter filter) { mTextureMagFilter = filter; } + + TextureFilter textureMinFilter() const { return mTextureMinFilter; } + void setTextureMinFilter(TextureFilter filter) { mTextureMinFilter = filter; } + + TextureMipFilter textureMipFilter() const { return mTextureMipFilter; } + void setTextureMipFilter(TextureMipFilter filter) { mTextureMipFilter = filter; } + + u16 numTexturePattern() const { return mNumTexturePattern; } + void setNumTexturePattern(u16 num) { mNumTexturePattern = num; } + + u8 numTextureDivisionX() const { return mNumTextureDivisionX; } + void setNumTextureDivisionX(u8 num) { + mTextureUVScale.setX(static_cast(numTextureRepetitionsX()) / static_cast(num)); + mNumTextureDivisionX = num; + } - TextureHandle& textureHandle(); - const TextureHandle& textureHandle() const; - void setTexture(const std::shared_ptr& texture); + u8 numTextureDivisionY() const { return mNumTextureDivisionY; } + void setNumTextureDivisionY(u8 num) { + mTextureUVScale.setY(static_cast(numTextureRepetitionsY()) / static_cast(num)); + mNumTextureDivisionY = num; + } + + s32 numTextureRepetitionsX() const { return static_cast(std::round(mTextureUVScale.getX() * static_cast(mNumTextureDivisionX))); } + void setNumTextureRepetitionsX(s32 num) { mTextureUVScale.setX(static_cast(num) / static_cast(mNumTextureDivisionX)); } + + s32 numTextureRepetitionsY() const { return static_cast(std::round(mTextureUVScale.getY() * static_cast(mNumTextureDivisionY))); } + void setNumTextureRepetitionsY(s32 num) { mTextureUVScale.setY(static_cast(num) / static_cast(mNumTextureDivisionY)); } + + const Math::Vector2f& textureUVScale() const { return mTextureUVScale; } + void setTextureUVScale(const Math::Vector2f& scale) { mTextureUVScale = scale; } + + const std::array& texturePatternTable() const { return mTexturePatternTbl; } + void setTexturePatternTable(const std::array& table) { mTexturePatternTbl = table; } + + u16 texturePatternFrequency() const { return mTexturePatternFrequency; } + void setTexturePatternFrequency(u16 frequency) { mTexturePatternFrequency = frequency; } + u16 texturePatternTableUse() const { return mTexturePatternTblUse; } + void setTexturePatternTableUse(u16 use) { mTexturePatternTblUse = use; } - const TextureProperties& textureProperties() const; - void setTextureProperties(const TextureProperties& textureProperties); + bool isTexturePatternAnim() const { return mIsTexturePatternAnim; } + void setIsTexturePatternAnim(bool isAnim) { mIsTexturePatternAnim = isAnim; } + + const TextureHandle& textureHandle() const { return mTextureHandle; } + // TextureHandle& textureHandle() { return mTextureHandle; } + + std::shared_ptr texture() const { return mTextureHandle.get(); } + void setTexture(const std::shared_ptr& texture) { mTextureHandle.set(texture); } + + BitFlag& flags(); + const BitFlag& flags() const; const ComplexProperties& complexProperties() const; void setComplexProperties(const ComplexProperties& complexProperties); @@ -475,8 +509,20 @@ class Emitter { binColor3f mColor1{255.0f, 255.0f, 255.0f}; - TextureProperties mTextureProperties{}; - + // Texture Properties + TextureWrap mTextureWrapT{TextureWrap::ClampToEdge}; + TextureWrap mTextureWrapS{TextureWrap::ClampToEdge}; + TextureFilter mTextureMagFilter{TextureFilter::Nearest}; + TextureFilter mTextureMinFilter{TextureFilter::Nearest}; + TextureMipFilter mTextureMipFilter{TextureMipFilter::None}; + u16 mNumTexturePattern{1}; + u8 mNumTextureDivisionX{1}; + u8 mNumTextureDivisionY{1}; + Math::Vector2f mTextureUVScale{1.0f, 1.0f}; + std::array mTexturePatternTbl{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + u16 mTexturePatternFrequency{0}; + u16 mTexturePatternTblUse{2}; + bool mIsTexturePatternAnim{false}; TextureHandle mTextureHandle{}; ComplexProperties mComplexProperties{}; diff --git a/src/editor/emitterWidget/combinerPropertiesWidget.cpp b/src/editor/emitterWidget/combinerPropertiesWidget.cpp index 7b3bec0..09d353a 100644 --- a/src/editor/emitterWidget/combinerPropertiesWidget.cpp +++ b/src/editor/emitterWidget/combinerPropertiesWidget.cpp @@ -83,10 +83,6 @@ void CombinerPropertiesWidget::populateProperties() { mCombinerPreview.setCombinerSrc(&mEmitter->textureHandle(), &mEmitter->secondaryColor(), &mEmitter->primaryColor()); } -void CombinerPropertiesWidget::updateCombinerPreview() { - mCombinerPreview.updateStages(); -} - // ========================================================================== // diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index b4b4a23..6e092a7 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -113,20 +113,6 @@ void EmitterWidget::setupStandardLayout(QVBoxLayout* mainLayout) { } void EmitterWidget::setupConnections() { - // Texture Properties - connect(mTextureProperties, &TexturePropertiesWidget::textureUpdated, this, [this](const std::shared_ptr& oldTexture, const std::shared_ptr& newTexture) { - if (!mEmitter) { return; } - mEmitter->setTexture(newTexture); - mCombinerProperties->updateCombinerPreview(); - emit propertiesChanged(); - }); - - connect(mTextureProperties, &TexturePropertiesWidget::propertiesUpdated, this, [this](const Ptcl::Emitter::TextureProperties& properties) { - if (!mEmitter) { return; } - mEmitter->setTextureProperties(properties); - emit propertiesChanged(); - }); - // Child Editor Widget connect(mChildEditorWidget, &ChildEditorWidget::flagsUpdated, this, [this](const BitFlag& childFlags) { if (!mEmitter) { return; } @@ -189,6 +175,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mAlphaProperties->setDocument(document); mCombinerProperties->setDocument(document); mColorProperties->setDocument(document); + mTextureProperties->setDocument(document); } void EmitterWidget::setSelection(Ptcl::Selection* selection) { @@ -207,6 +194,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mAlphaProperties->setSelection(selection); mCombinerProperties->setSelection(selection); mColorProperties->setSelection(selection); + mTextureProperties->setSelection(selection); connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -235,8 +223,6 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { } // TODO: Have child widgets handle this themselves - mTextureList = &mDocument->textures(); - mTextureProperties->setTextureList(mTextureList); mChildEditorWidget->setTextureList(mTextureList); setEnabled(true); @@ -245,14 +231,11 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { } void EmitterWidget::populateProperties() { - QSignalBlocker b12(mTextureProperties); QSignalBlocker b15(mChildEditorWidget); QSignalBlocker b16(mFluctuationEditorWidget); QSignalBlocker b17(mFieldEditorWidget); QSignalBlocker b18(mStripeEditorWidget); - mTextureProperties->setProperties(mEmitter->textureProperties(), mEmitter->textureHandle().get()); - mChildEditorWidget->setChildData(&mEmitter->childData(), mEmitter->complexProperties().childFlags); mChildEditorWidget->setParentColor0(mEmitter->primaryColor()); diff --git a/src/editor/emitterWidget/texturePropertiesWidget.cpp b/src/editor/emitterWidget/texturePropertiesWidget.cpp index e07c101..a164a2f 100644 --- a/src/editor/emitterWidget/texturePropertiesWidget.cpp +++ b/src/editor/emitterWidget/texturePropertiesWidget.cpp @@ -17,85 +17,16 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::TexturePropertiesWidget::TexturePropertiesWidget(QWidget* parent) : - QWidget{parent} { - // Wrap T - connect(&mWrapTComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { - Q_UNUSED(index); - mProps.textureWrapT = mWrapTComboBox.currentEnum(); - emit propertiesUpdated(mProps); - }); - - // Wrap S - connect(&mWrapSComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { - Q_UNUSED(index); - mProps.textureWrapS = mWrapSComboBox.currentEnum(); - emit propertiesUpdated(mProps); - }); - - // Mag Filter - connect(&mMagFilterComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { - Q_UNUSED(index); - mProps.textureMagFilter = mMagFilterComboBox.currentEnum(); - emit propertiesUpdated(mProps); - }); - - // Min Filter - connect(&mMinFilterComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { - Q_UNUSED(index); - mProps.textureMinFilter = mMinFilterComboBox.currentEnum(); - emit propertiesUpdated(mProps); - }); - - // Mipmap Filter - connect(&mMipFilterComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { - Q_UNUSED(index); - mProps.textureMipFilter = mMipFilterComboBox.currentEnum(); - emit propertiesUpdated(mProps); - }); +TexturePropertiesWidget::TexturePropertiesWidget(QWidget* parent) : + EmitterWidgetBase{parent} { // Texture Preview mTexturePreview.setThumbnailSize(QSize(64, 64)); - connect(&mTexturePreview, &ThumbnailWidget::clicked, this, &TexturePropertiesWidget::changeTexture); - - // Texture pattern count - connect(&mNumTexPat, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { - mProps.numTexPat = value; - emit propertiesUpdated(mProps); - }); - - // Texture Division X mTexDivX.setRange(1, 4); - connect(&mTexDivX, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { - mProps.numTexDivX = value; - updateUVScale(); - updateTexPatTblColumns(); - emit propertiesUpdated(mProps); - }); - - // Texture Division Y mTexDivY.setRange(1, 4); - connect(&mTexDivY, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { - mProps.numTexDivY = value; - updateUVScale(); - updateTexPatTblColumns(); - emit propertiesUpdated(mProps); - }); - - // Texture pattern Frequency mTexPatFreq.setMinimum(1); - connect(&mTexPatFreq, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { - mProps.texPatFreq = value; - emit propertiesUpdated(mProps); - }); - - // Texture pattern table use mTexPatTblUse.setRange(2, 16); - connect(&mTexPatTblUse, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { - mProps.texPatTblUse = value; - updateTexPatTblColumns(); - emit propertiesUpdated(mProps); - }); + // Texture pattern table const s32 referenceHeight = mWrapTComboBox.sizeHint().height(); // Make TexPat table same hight as a comboBox @@ -112,51 +43,11 @@ EmitterWidget::TexturePropertiesWidget::TexturePropertiesWidget(QWidget* parent) for (s32 i = 0; i < 16; ++i) { auto* item = new QTableWidgetItem("0"); item->setTextAlignment(Qt::AlignCenter); - - if (i < mProps.numTexPat) { - item->setIcon(QIcon(createFramePreview(mProps.texPatTbl[i]))); - } - mTexPatTbl.setItem(0, i, item); } - connect(&mTexPatTbl, &QTableWidget::itemChanged, this, [this](QTableWidgetItem* item) { - QSignalBlocker b1(mTexPatTbl); - - bool ok = false; - s32 value = item->text().toInt(&ok); - - const bool valid = ok && value >= 0 && value < maxFrameCount(); - - if (!valid) { - value = 0; - item->setText("0"); - } - - mProps.texPatTbl[item->column()] = value; - item->setIcon(valid ? QIcon(createFramePreview(value)) : QIcon()); - - emit propertiesUpdated(mProps); - }); - - // Texture Repetitions X mTexRepetitionsX.setMinimum(1); - connect(&mTexRepetitionsX, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { - f32 uvX = static_cast(value) / static_cast(mProps.numTexDivX); - mProps.texUVScale.setX(uvX); - updateTexPatTblColumns(); - emit propertiesUpdated(mProps); - }); - - // Texture Repetitions Y mTexRepetitionsY.setMinimum(1); - connect(&mTexRepetitionsY, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { - f32 uvY = static_cast(value) / static_cast(mProps.numTexDivY); - mProps.texUVScale.setY(uvY); - updateTexPatTblColumns(); - emit propertiesUpdated(mProps); - }); - // Texture Settings Layout auto settingsLayout = new QGridLayout; @@ -181,27 +72,10 @@ EmitterWidget::TexturePropertiesWidget::TexturePropertiesWidget(QWidget* parent) // Anim Mode mAnimModeComboBox.addItems({"Fit to Speed", "Fit to Life"}); - connect(&mAnimModeComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { - auto mode = static_cast(index); - - if (mode == AnimMode::FitToLife) { - mTexPatFreq.setDisabled(true); - mProps.texPatFreq = 0; - } else { - mTexPatFreq.setDisabled(false); - mProps.texPatFreq = 1; - } - - emit propertiesUpdated(mProps); - }); // Texture Pattern Anim mTexPatGroupBox.setTitle("Texture Pattern Animation"); mTexPatGroupBox.setCheckable(true); - connect(&mTexPatGroupBox, &QGroupBox::clicked, this, [this](bool checked) { - mProps.isTexPatAnim = checked; - emit propertiesUpdated(mProps); - }); auto texPatSettingsLayout = new QGridLayout; texPatSettingsLayout->addWidget(new QLabel("Animation Mode:"), 0, 0); @@ -225,16 +99,209 @@ EmitterWidget::TexturePropertiesWidget::TexturePropertiesWidget(QWidget* parent) mainLayout->addWidget(&mNumTexPat, 3, 1, 1, 1); setLayout(mainLayout); - + setupConnections(); } -void EmitterWidget::TexturePropertiesWidget::setProperties(const Ptcl::Emitter::TextureProperties& properties, const std::shared_ptr& texture) { - mProps = properties; - mTexture = texture; - populateWidgets(); +void TexturePropertiesWidget::setupConnections() { + connect(&mTexturePreview, &ThumbnailWidget::clicked, this, &TexturePropertiesWidget::changeTexture); + + // Wrap T + connect(&mWrapTComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { + Q_UNUSED(index); + const auto wrap = mWrapTComboBox.currentEnum(); + setEmitterProperty( + "Set Texture Wrap U", + "SetTexWrapU", + &Ptcl::Emitter::textureWrapT, + &Ptcl::Emitter::setTextureWrapT, + wrap + ); + }); + + // Wrap S + connect(&mWrapSComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { + Q_UNUSED(index); + const auto wrap = mWrapSComboBox.currentEnum(); + setEmitterProperty( + "Set Texture Wrap V", + "SetTexWrapV", + &Ptcl::Emitter::textureWrapS, + &Ptcl::Emitter::setTextureWrapS, + wrap + ); + }); + + // Mag Filter + connect(&mMagFilterComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { + Q_UNUSED(index); + const auto filter = mMagFilterComboBox.currentEnum(); + setEmitterProperty( + "Set Texture Mag Filter", + "SetTexMagFilter", + &Ptcl::Emitter::textureMagFilter, + &Ptcl::Emitter::setTextureMagFilter, + filter + ); + }); + + // Min Filter + connect(&mMinFilterComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { + Q_UNUSED(index); + const auto filter = mMinFilterComboBox.currentEnum(); + setEmitterProperty( + "Set Texture Min Filter", + "SetTexMinFilter", + &Ptcl::Emitter::textureMinFilter, + &Ptcl::Emitter::setTextureMinFilter, + filter + ); + }); + + // Mipmap Filter + connect(&mMipFilterComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { + Q_UNUSED(index); + const auto filter = mMipFilterComboBox.currentEnum(); + setEmitterProperty( + "Set Texture Mipmap Filter", + "SetTexMipFilter", + &Ptcl::Emitter::textureMipFilter, + &Ptcl::Emitter::setTextureMipFilter, + filter + ); + }); + + // Texture pattern count + connect(&mNumTexPat, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { + setEmitterProperty( + "Set Texture Pattern Count", + "SetNumTexPat", + &Ptcl::Emitter::numTexturePattern, + &Ptcl::Emitter::setNumTexturePattern, + static_cast(value) + ); + }); + + connect(&mTexDivX, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { + setEmitterProperty( + "Set Texture Divisions X", + "SetTexDivX", + &Ptcl::Emitter::numTextureDivisionX, + &Ptcl::Emitter::setNumTextureDivisionX, + static_cast(value) + ); + }); + + connect(&mTexDivY, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { + setEmitterProperty( + "Set Texture Divisions Y", + "SetTexDivY", + &Ptcl::Emitter::numTextureDivisionY, + &Ptcl::Emitter::setNumTextureDivisionY, + static_cast(value) + ); + }); + + connect(&mTexPatFreq, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { + setEmitterProperty( + "Set Texture Anim Frame Step", + "SetTexPatFreq", + &Ptcl::Emitter::texturePatternFrequency, + &Ptcl::Emitter::setTexturePatternFrequency, + static_cast(value) + ); + }); + + connect(&mTexPatTblUse, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { + setEmitterProperty( + "Set Texture Anim Frame Count", + "SetTexPatTblUse", + &Ptcl::Emitter::texturePatternTableUse, + &Ptcl::Emitter::setTexturePatternTableUse, + static_cast(value) + ); + }); + + connect(&mTexPatTbl, &QTableWidget::itemChanged, this, [this](QTableWidgetItem* item) { + QSignalBlocker b1(mTexPatTbl); + + bool ok = false; + s32 value = item->text().toInt(&ok); + + const bool valid = ok && value >= 0 && value < maxFrameCount(); + if (!valid) { + value = 0; + item->setText("0"); + } + + auto table = mEmitter->texturePatternTable(); + const s32 col = item->column(); + + if (table[col] == value) { + return; + } + + table[col] = value; + + setEmitterProperty( + "Set Texture Anim Frame Order", + QString("SetTexPatTbl_%1").arg(col), + &Ptcl::Emitter::texturePatternTable, + &Ptcl::Emitter::setTexturePatternTable, + table + ); + }); + + connect(&mTexRepetitionsX, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { + setEmitterProperty( + "Set Texture X Repetition", + "SetTexRepX", + &Ptcl::Emitter::numTextureRepetitionsX, + &Ptcl::Emitter::setNumTextureRepetitionsX, + static_cast(value) + ); + }); + + connect(&mTexRepetitionsY, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { + setEmitterProperty( + "Set Texture Y Repetition", + "SetTexRepY", + &Ptcl::Emitter::numTextureRepetitionsY, + &Ptcl::Emitter::setNumTextureRepetitionsY, + static_cast(value) + ); + }); + + connect(&mAnimModeComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { + auto mode = static_cast(index); + + u16 freq; + if (mode == AnimMode::FitToLife) { + freq = 0; + } else { + freq = 1; + } + + setEmitterProperty( + "Set Texture Anim Mode", + "SetTexAnimMode", + &Ptcl::Emitter::texturePatternFrequency, + &Ptcl::Emitter::setTexturePatternFrequency, + freq + ); + }); + + connect(&mTexPatGroupBox, &QGroupBox::clicked, this, [this](bool checked) { + setEmitterProperty( + "Toggle Texture Anim", + "ToggleTexAnim", + &Ptcl::Emitter::isTexturePatternAnim, + &Ptcl::Emitter::setIsTexturePatternAnim, + checked + ); + }); } -void EmitterWidget::TexturePropertiesWidget::populateWidgets() { +void TexturePropertiesWidget::populateProperties() { QSignalBlocker b1(mWrapTComboBox); QSignalBlocker b2(mWrapSComboBox); QSignalBlocker b3(mMagFilterComboBox); @@ -249,25 +316,26 @@ void EmitterWidget::TexturePropertiesWidget::populateWidgets() { QSignalBlocker b12(mTexPatGroupBox); QSignalBlocker b13(mTexRepetitionsX); QSignalBlocker b14(mTexRepetitionsY); + QSignalBlocker b15(mAnimModeComboBox); - if (mTexture) { - mTexturePreview.setPixmap(QPixmap::fromImage(mTexture->textureData())); + if (mEmitter->textureHandle().isValid()) { + mTexturePreview.setPixmap(QPixmap::fromImage(mEmitter->textureHandle()->textureData())); } - mWrapTComboBox.setCurrentEnum(mProps.textureWrapT); - mWrapSComboBox.setCurrentEnum(mProps.textureWrapS); - mMagFilterComboBox.setCurrentEnum(mProps.textureMagFilter); - mMinFilterComboBox.setCurrentEnum(mProps.textureMinFilter); - mMipFilterComboBox.setCurrentEnum(mProps.textureMipFilter); + mWrapTComboBox.setCurrentEnum(mEmitter->textureWrapT()); + mWrapSComboBox.setCurrentEnum(mEmitter->textureWrapS()); + mMagFilterComboBox.setCurrentEnum(mEmitter->textureMagFilter()); + mMinFilterComboBox.setCurrentEnum(mEmitter->textureMinFilter()); + mMipFilterComboBox.setCurrentEnum(mEmitter->textureMipFilter()); - mNumTexPat.setValue(mProps.numTexPat); - mTexDivX.setValue(mProps.numTexDivX); - mTexDivY.setValue(mProps.numTexDivY); + mNumTexPat.setValue(mEmitter->numTexturePattern()); + mTexDivX.setValue(mEmitter->numTextureDivisionX()); + mTexDivY.setValue(mEmitter->numTextureDivisionY()); - const auto freq = mProps.texPatFreq; + const auto freq = mEmitter->texturePatternFrequency(); const auto mode = freqToAnimMode(freq); - mTexPatFreq.setValue(mProps.texPatFreq); + mTexPatFreq.setValue(freq); mAnimModeComboBox.setCurrentIndex(static_cast(mode)); if (mode == AnimMode::FitToLife) { @@ -276,38 +344,27 @@ void EmitterWidget::TexturePropertiesWidget::populateWidgets() { mTexPatFreq.setDisabled(false); } - mTexPatTblUse.setValue(mProps.texPatTblUse); + mTexPatTblUse.setValue(mEmitter->texturePatternTableUse()); - const auto& tbl = mProps.texPatTbl; + const auto& tbl = mEmitter->texturePatternTable(); for (s32 i = 0; i < tbl.size(); ++i) { QTableWidgetItem* item = mTexPatTbl.item(0, i); item->setText(QString::number(tbl[i])); - if (i < mProps.numTexPat) { - item->setIcon(QIcon(createFramePreview(mProps.texPatTbl[i]))); + if (i < mEmitter->numTexturePattern()) { + item->setIcon(QIcon(createFramePreview(tbl[i]))); } } - mTexPatGroupBox.setChecked(mProps.isTexPatAnim); + mTexPatGroupBox.setChecked(mEmitter->isTexturePatternAnim()); - const f32 divX = static_cast(mProps.numTexDivX); - const f32 divY = static_cast(mProps.numTexDivY); - - mTexRepetitionsX.setValue(static_cast(std::round(mProps.texUVScale.getX() * divX))); - mTexRepetitionsY.setValue(static_cast(std::round(mProps.texUVScale.getY() * divY))); + mTexRepetitionsX.setValue(mEmitter->numTextureRepetitionsX()); + mTexRepetitionsY.setValue(mEmitter->numTextureRepetitionsY()); updateTexPatTblColumns(); } -void EmitterWidget::TexturePropertiesWidget::setTextureList(const Ptcl::TextureList* textureList) { - mTextureList = textureList; -} - -void EmitterWidget::TexturePropertiesWidget::updateTextureDetails() { - mTexturePreview.setPixmap(QPixmap::fromImage(mTexture->textureData())); -} - -void EmitterWidget::TexturePropertiesWidget::updateTexPatTblColumns() { +void TexturePropertiesWidget::updateTexPatTblColumns() { const QPalette& palette = mTexPatTbl.palette(); for (s32 i = 0; i < 16; ++i) { @@ -316,12 +373,12 @@ void EmitterWidget::TexturePropertiesWidget::updateTexPatTblColumns() { continue; } - if (i >= mProps.texPatTblUse) { + if (i >= mEmitter->texturePatternTableUse()) { item->setFlags(Qt::NoItemFlags); item->setBackground(palette.color(QPalette::Disabled, QPalette::Base)); item->setForeground(palette.color(QPalette::Disabled, QPalette::Text)); - s32 frame = mProps.texPatTbl[i]; + s32 frame = mEmitter->texturePatternTable()[i]; item->setText(QString::number(frame)); item->setIcon(QIcon()); } else { @@ -329,46 +386,39 @@ void EmitterWidget::TexturePropertiesWidget::updateTexPatTblColumns() { item->setBackground(palette.color(QPalette::Base)); item->setForeground(palette.color(QPalette::Text)); - s32 frame = mProps.texPatTbl[i]; + s32 frame = mEmitter->texturePatternTable()[i]; item->setText(QString::number(frame)); item->setIcon(QIcon(createFramePreview(frame))); } } } -void EmitterWidget::TexturePropertiesWidget::updateUVScale() { - mProps.texUVScale.setX(static_cast(mTexRepetitionsX.value()) / static_cast(mProps.numTexDivX)); - mProps.texUVScale.setY(static_cast(mTexRepetitionsY.value()) / static_cast(mProps.numTexDivY)); - -} - -void EmitterWidget::TexturePropertiesWidget::changeTexture() { - if (!mTextureList) { - return; - } - - auto oldTexture = mTexture; +void TexturePropertiesWidget::changeTexture() { + const auto textureList = mDocument->textures(); - TextureSelectDialog dialog(*mTextureList, this); + TextureSelectDialog dialog(textureList, this); if (dialog.exec() == QDialog::Accepted) { s32 selectedInded = dialog.selectedIndex(); - if (selectedInded >= 0 && static_cast(selectedInded) < mTextureList->size()) { - mTexture = mTextureList->at(selectedInded); - updateTextureDetails(); - emit textureUpdated(oldTexture, mTexture); + if (selectedInded >= 0 && static_cast(selectedInded) < textureList.size()) { + const auto& texture = textureList.at(selectedInded); + setEmitterProperty( + "Set Texture", + "SetEmitterTexture", + &Ptcl::Emitter::texture, + &Ptcl::Emitter::setTexture, + texture + ); } } - - updateTexPatTblColumns(); } -s32 EmitterWidget::TexturePropertiesWidget::maxFrameCount() const { - return mProps.numTexDivX * mProps.numTexDivY; +s32 TexturePropertiesWidget::maxFrameCount() const { + return mEmitter->numTextureDivisionX() * mEmitter->numTextureDivisionY(); } -std::optional EmitterWidget::TexturePropertiesWidget::calcFrameUVOffset(s32 frame) const { - const auto divX = mProps.numTexDivX; - const auto divY = mProps.numTexDivY; +std::optional TexturePropertiesWidget::calcFrameUVOffset(s32 frame) const { + const auto divX = mEmitter->numTextureDivisionX(); + const auto divY = mEmitter->numTextureDivisionY(); if (frame < 0 || divX <= 0 || divY <= 0) { return std::nullopt; @@ -378,7 +428,7 @@ std::optional EmitterWidget::TexturePropertiesWidget::calcFrameU return std::nullopt; } - const auto& uvScale = mProps.texUVScale; + const auto& uvScale = mEmitter->textureUVScale(); const s32 frameX = frame % divX; const s32 frameY = frame / divX; @@ -391,8 +441,8 @@ std::optional EmitterWidget::TexturePropertiesWidget::calcFrameU return uvOffset; } -QImage EmitterWidget::TexturePropertiesWidget::getFrameTexture(s32 frame) const { - if (!mTexture) { +QImage TexturePropertiesWidget::getFrameTexture(s32 frame) const { + if (!mEmitter->textureHandle().isValid()) { return {}; } @@ -402,8 +452,8 @@ QImage EmitterWidget::TexturePropertiesWidget::getFrameTexture(s32 frame) const } const auto& uvOffset = *uv; - const auto& uvScale = mProps.texUVScale; - const auto& src = mTexture->textureData(); + const auto& uvScale = mEmitter->textureUVScale(); + const auto& src = mEmitter->textureHandle()->textureData(); const f32 texW = static_cast(src.width()); const f32 texH = static_cast(src.height()); @@ -419,7 +469,7 @@ QImage EmitterWidget::TexturePropertiesWidget::getFrameTexture(s32 frame) const return rect.isValid() ? src.copy(rect) : QImage{}; } -QImage EmitterWidget::TexturePropertiesWidget::applyUVRepetition(const QImage& image, f32 repeatX, f32 repeatY) const { +QImage TexturePropertiesWidget::applyUVRepetition(const QImage& image, f32 repeatX, f32 repeatY) const { if (image.isNull()) { return {}; } @@ -454,7 +504,7 @@ QImage EmitterWidget::TexturePropertiesWidget::applyUVRepetition(const QImage& i return out; } -QPixmap EmitterWidget::TexturePropertiesWidget::createFramePreview(s32 frame) const { +QPixmap TexturePropertiesWidget::createFramePreview(s32 frame) const { QImage base = getFrameTexture(frame); if (base.isNull()) { return {}; diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index c0ab145..caf5aab 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -322,10 +322,10 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { .width = static_cast(emitter.textureHandle()->textureData().width()), .height = static_cast(emitter.textureHandle()->textureData().height()), .format = emitter.textureHandle()->textureFormat(), - .wrapT = emitter.textureProperties().textureWrapT, - .wrapS = emitter.textureProperties().textureWrapS, - .magFilter = emitter.textureProperties().textureMagFilter, - .minMipFilter = static_cast((static_cast(emitter.textureProperties().textureMinFilter) & 0x1) | ((static_cast(emitter.textureProperties().textureMipFilter) & 0x3) << 1)), + .wrapT = emitter.textureWrapT(), + .wrapS = emitter.textureWrapS(), + .magFilter = emitter.textureMagFilter(), + .minMipFilter = static_cast((static_cast(emitter.textureMinFilter()) & 0x1) | ((static_cast(emitter.textureMipFilter()) & 0x3) << 1)), }; textureSize = 0; // To be assigned after construction... @@ -335,7 +335,7 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { isFollow = emitter.isFollow(); isEmitterBillboardMtx = emitter.isEmitterBillboardMtx(); isDirectional = emitter.isDirectional(); - isTexPatAnim = emitter.textureProperties().isTexPatAnim; + isTexPatAnim = emitter.isTexturePatternAnim(); isVelLook = emitter.isVelLook(); volumeTblIndex = emitter.volumeTblIndex(); isStopEmitInFade = emitter.isStopEmitInFade(); @@ -392,13 +392,13 @@ BinCommonEmitterData::BinCommonEmitterData(const Ptcl::Emitter& emitter) { transformSRT = emitter.transformSRT(); transformRT = emitter.transformRT(); alphaAddInFade = emitter.alphaAddInFade(); - numTexPat = emitter.textureProperties().numTexPat; - numTexDivX = emitter.textureProperties().numTexDivX; - numTexDivY = emitter.textureProperties().numTexDivY; - texUVScale = emitter.textureProperties().texUVScale; - std::copy(emitter.textureProperties().texPatTbl.begin(), emitter.textureProperties().texPatTbl.end(), texPatTbl.data()); - texPatFreq = emitter.textureProperties().texPatFreq; - texPatTblUse = emitter.textureProperties().texPatTblUse; + numTexPat = emitter.numTexturePattern(); + numTexDivX = emitter.numTextureDivisionX(); + numTexDivY = emitter.numTextureDivisionY(); + texUVScale = emitter.textureUVScale(); + std::copy(emitter.texturePatternTable().begin(), emitter.texturePatternTable().end(), texPatTbl.data()); + texPatFreq = emitter.texturePatternFrequency(); + texPatTblUse = emitter.texturePatternTableUse(); } QDataStream& operator>>(QDataStream& in, BinCommonEmitterData& item) { diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index d69bd73..51f582b 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -89,8 +89,22 @@ std::unique_ptr Emitter::clone() const { newEmitter->mDepthFunc = mDepthFunc; newEmitter->mCombinerFunc = mCombinerFunc; - newEmitter->mTextureProperties = mTextureProperties; + // Texture Properties + newEmitter->mTextureWrapT = mTextureWrapT; + newEmitter->mTextureWrapS = mTextureWrapS; + newEmitter->mTextureMagFilter = mTextureMagFilter; + newEmitter->mTextureMinFilter = mTextureMinFilter; + newEmitter->mTextureMipFilter = mTextureMipFilter; + newEmitter->mNumTexturePattern = mNumTexturePattern; + newEmitter->mNumTextureDivisionX = mNumTextureDivisionX; + newEmitter->mNumTextureDivisionY = mNumTextureDivisionY; + newEmitter->mTextureUVScale = mTextureUVScale; + newEmitter->mTexturePatternTbl = mTexturePatternTbl; + newEmitter->mTexturePatternFrequency = mTexturePatternFrequency; + newEmitter->mTexturePatternTblUse = mTexturePatternTblUse; + newEmitter->mIsTexturePatternAnim = mIsTexturePatternAnim; newEmitter->mTextureHandle = mTextureHandle.clone(); + newEmitter->mComplexProperties = mComplexProperties; newEmitter->mChildData = std::move(*mChildData.clone()); newEmitter->mFluctuationData = mFluctuationData; @@ -107,26 +121,6 @@ const BitFlag& Emitter::flags() const { return mFlag; } -const TextureHandle& Emitter::textureHandle() const { - return mTextureHandle; -} - -TextureHandle& Emitter::textureHandle() { - return mTextureHandle; -} - -void Emitter::setTexture(const std::shared_ptr& texture) { - mTextureHandle.set(texture); -} - -const Emitter::TextureProperties& Emitter::textureProperties() const { - return mTextureProperties; -} - -void Emitter::setTextureProperties(const TextureProperties& textureProperties) { - mTextureProperties = textureProperties; -} - const Emitter::ComplexProperties& Emitter::complexProperties() const { return mComplexProperties; } @@ -216,21 +210,20 @@ bool Emitter::hasStripeData() const { void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { mFlag = emitterData.flag; - mTextureProperties = { - .textureWrapT = emitterData.textureRes.wrapT, - .textureWrapS = emitterData.textureRes.wrapS, - .textureMagFilter = emitterData.textureRes.magFilter, - .textureMinFilter = static_cast(emitterData.textureRes.minMipFilter & 0x1), - .textureMipFilter = static_cast((emitterData.textureRes.minMipFilter >> 1) & 0x3), - .numTexPat = emitterData.numTexPat, - .numTexDivX = emitterData.numTexDivX, - .numTexDivY = emitterData.numTexDivY, - .texUVScale = { emitterData.texUVScale.x, emitterData.texUVScale.y }, - .texPatTbl = emitterData.texPatTbl, - .texPatFreq = emitterData.texPatFreq, - .texPatTblUse = emitterData.texPatTblUse, - .isTexPatAnim = emitterData.isTexPatAnim - }; + // Texture Properties + mTextureWrapT = emitterData.textureRes.wrapT; + mTextureWrapS = emitterData.textureRes.wrapS; + mTextureMagFilter = emitterData.textureRes.magFilter; + mTextureMinFilter = static_cast(emitterData.textureRes.minMipFilter & 0x1); + mTextureMipFilter = static_cast((emitterData.textureRes.minMipFilter >> 1) & 0x3); + mNumTexturePattern = emitterData.numTexPat; + mNumTextureDivisionX = emitterData.numTexDivX; + mNumTextureDivisionY = emitterData.numTexDivY; + mTextureUVScale = { emitterData.texUVScale.x, emitterData.texUVScale.y }; + mTexturePatternTbl = emitterData.texPatTbl; + mTexturePatternFrequency = emitterData.texPatFreq; + mTexturePatternTblUse = emitterData.texPatTblUse; + mIsTexturePatternAnim = emitterData.isTexPatAnim; // Basic Properties mType = emitterData.type; From b1177c8aff2e11408a8b439e2d4b10ca809564e2 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Tue, 17 Mar 2026 01:02:55 +0000 Subject: [PATCH 17/35] feat(ui): implement undo/redo for Emitter fluctuationProperties --- CMakeLists.txt | 5 +- include/editor/emitterWidget/emitterWidget.h | 2 +- .../fluctuationEditorWidget.h | 16 +--- include/ptcl/ptclBinary.h | 2 +- include/ptcl/ptclEmitter.h | 49 ++++++++---- include/ptcl/ptclFluctuationData.h | 21 ----- src/editor/emitterWidget/emitterWidget.cpp | 19 +---- .../fluctuationEditorWidget.cpp | 76 +++++++++++++------ src/editor/ptclListWidget.cpp | 2 +- src/ptcl/ptcl.cpp | 4 +- src/ptcl/ptclBinary.cpp | 10 +-- src/ptcl/ptclEmitter.cpp | 30 +++----- 12 files changed, 116 insertions(+), 120 deletions(-) rename include/editor/{ => emitterWidget}/fluctuationEditorWidget.h (60%) delete mode 100644 include/ptcl/ptclFluctuationData.h rename src/editor/{ => emitterWidget}/fluctuationEditorWidget.cpp (55%) diff --git a/CMakeLists.txt b/CMakeLists.txt index f9ed727..16d719c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,6 @@ set(PROJECT_SOURCES src/main.cpp src/editor/mainWindow.cpp src/editor/emitterSetWidget.cpp - src/editor/fluctuationEditorWidget.cpp src/editor/ptclListWidget.cpp src/editor/stripeEditorWidget.cpp src/editor/textureListWidget.cpp @@ -57,6 +56,7 @@ set(PROJECT_SOURCES src/editor/emitterWidget/emissionPropertiesWidget.cpp src/editor/emitterWidget/emitterWidget.cpp src/editor/emitterWidget/emitterWidgetBase.cpp + src/editor/emitterWidget/fluctuationEditorWidget.cpp src/editor/emitterWidget/gravityPropertiesWidget.cpp src/editor/emitterWidget/lifespanPropertiesWidget.cpp src/editor/emitterWidget/rotationPropertiesWidget.cpp @@ -93,7 +93,6 @@ set(PROJECT_HEADERS include/typedefs.h include/editor/mainWindow.h include/editor/emitterSetWidget.h - include/editor/fluctuationEditorWidget.h include/editor/ptclListWidget.h include/editor/stripeEditorWidget.h include/editor/textureImportDialog.h @@ -129,6 +128,7 @@ set(PROJECT_HEADERS include/editor/emitterWidget/emissionPropertiesWidget.h include/editor/emitterWidget/emitterWidget.h include/editor/emitterWidget/emitterWidgetBase.h + include/editor/emitterWidget/fluctuationEditorWidget.h include/editor/emitterWidget/gravityPropertiesWidget.h include/editor/emitterWidget/lifespanPropertiesWidget.h include/editor/emitterWidget/rotationPropertiesWidget.h @@ -158,7 +158,6 @@ set(PROJECT_HEADERS include/ptcl/ptclEmitterSet.h include/ptcl/ptclEnum.h include/ptcl/ptclFieldData.h - include/ptcl/ptclFluctuationData.h include/ptcl/ptclSeed.h include/ptcl/ptclStripeData.h include/ptcl/ptclTexture.h diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index c999493..a5fb711 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -2,7 +2,6 @@ #include "editor/childEditor/childEditorWidget.h" #include "editor/components/collapsibleWidget.h" -#include "editor/fluctuationEditorWidget.h" #include "editor/fieldEditor/fieldEditorWidget.h" #include "editor/stripeEditorWidget.h" @@ -36,6 +35,7 @@ class AlphaPropertiesWidget; class CombinerPropertiesWidget; class ColorPropertiesWidget; class TexturePropertiesWidget; +class FluctuationEditorWidget; // ========================================================================== // diff --git a/include/editor/fluctuationEditorWidget.h b/include/editor/emitterWidget/fluctuationEditorWidget.h similarity index 60% rename from include/editor/fluctuationEditorWidget.h rename to include/editor/emitterWidget/fluctuationEditorWidget.h index 5ea3ec0..d684025 100644 --- a/include/editor/fluctuationEditorWidget.h +++ b/include/editor/emitterWidget/fluctuationEditorWidget.h @@ -1,8 +1,6 @@ #pragma once -#include "ptcl/ptclEnum.h" -#include "ptcl/ptclFluctuationData.h" -#include "util/bitflagUtil.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include #include @@ -16,24 +14,16 @@ namespace PtclEditor { // ========================================================================== // -class FluctuationEditorWidget final : public QWidget { +class FluctuationEditorWidget final : public EmitterWidgetBase { Q_OBJECT public: explicit FluctuationEditorWidget(QWidget* parent = nullptr); - void setData(const Ptcl::FluctuationData& data, const BitFlag& fluxFlag); - -signals: - void dataUpdated(const Ptcl::FluctuationData& data); - void flagsUpdated(const BitFlag& fluxFlags); - private: + void populateProperties() final; void setupConnections(); private: - Ptcl::FluctuationData mData{}; - BitFlag mFluxFlag{}; - QWidget* mControlsContainer{nullptr}; QDoubleSpinBox mScaleSpinBox{}; QDoubleSpinBox mFreqSpinBox{}; diff --git a/include/ptcl/ptclBinary.h b/include/ptcl/ptclBinary.h index cc764db..ef80156 100644 --- a/include/ptcl/ptclBinary.h +++ b/include/ptcl/ptclBinary.h @@ -515,7 +515,7 @@ struct alignas(4) BinFluctuationData { s32 fluctuationPhaseRnd; // 0x08 BinFluctuationData() = default; - BinFluctuationData(const Ptcl::FluctuationData& fluctuationData); + BinFluctuationData(const Ptcl::Emitter& emitterData); friend QDataStream& operator>>(QDataStream& in, BinFluctuationData& item); friend QDataStream& operator<<(QDataStream& out, const BinFluctuationData& item); diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index a7cd894..cbc478d 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -7,7 +7,6 @@ #include "ptcl/ptclChildData.h" #include "ptcl/ptclEnum.h" #include "ptcl/ptclFieldData.h" -#include "ptclFluctuationData.h" #include "ptcl/ptclStripeData.h" #include "ptcl/ptclSeed.h" #include "ptcl/ptclTexture.h" @@ -59,10 +58,6 @@ class Emitter { ChildFlag::Unk80 }; BitFlag fieldFlags{}; - BitFlag fluctuationFlags{ - FluctuationFlag::ApplyAlpha, - FluctuationFlag::ApplyScale - }; BitFlag stripeFlags{}; }; @@ -379,11 +374,35 @@ class Emitter { void setIsTexturePatternAnim(bool isAnim) { mIsTexturePatternAnim = isAnim; } const TextureHandle& textureHandle() const { return mTextureHandle; } - // TextureHandle& textureHandle() { return mTextureHandle; } std::shared_ptr texture() const { return mTextureHandle.get(); } void setTexture(const std::shared_ptr& texture) { mTextureHandle.set(texture); } + // ----- Fluctuation Properties ----- \\ + + void initFluctuationData(const BinFluctuationData& fluctuationData); + + const BitFlag& fluctuationFlags() const { return mFluctuationFlags; } + void setFluctuationFlags(const BitFlag& fluxFlags) { mFluctuationFlags = fluxFlags; } + + f32 fluctuationScale() const { return mFluctuationScale; } + void setFluctuationScale(f32 scale) { mFluctuationScale = scale; } + + f32 fluctuationFrequency() const { return mFluctuationFreq; } + void setFluctuationFrequency(f32 frequency) { mFluctuationFreq = frequency; } + + bool isFluctuationPhaseRandom() const { return mFluctuationPhaseRnd; } + void setFluctuationPhaseRandom(bool random) { mFluctuationPhaseRnd = random; } + + bool isFluctuationEnabled() const { return mFluctuationFlags.isSet(Ptcl::FluctuationFlag::Enabled); } + void setFluctuationEnabled(bool enabled) { mFluctuationFlags.set(Ptcl::FluctuationFlag::Enabled, enabled); } + + bool isFluctuationApplyAlpha() const { return mFluctuationFlags.isSet(Ptcl::FluctuationFlag::ApplyAlpha); } + void setFluctuationApplyAlpha(bool apply) { mFluctuationFlags.set(Ptcl::FluctuationFlag::ApplyAlpha, apply); } + + bool isFluctuationApplyScale() const { return mFluctuationFlags.isSet(Ptcl::FluctuationFlag::ApplyScale); } + void setFluctuationApplyScale(bool apply) { mFluctuationFlags.set(Ptcl::FluctuationFlag::ApplyScale, apply); } + BitFlag& flags(); const BitFlag& flags() const; @@ -391,17 +410,12 @@ class Emitter { void setComplexProperties(const ComplexProperties& complexProperties); void setChildFlags(const BitFlag& childFlags); - void setFluctuationFlags(const BitFlag& fluxFlags); void setFieldFlags(const BitFlag& fieldFlags); void setStripeFlags(const BitFlag& stripeFlags); const ChildData& childData() const; ChildData& childData(); - const FluctuationData& fluctuationData() const; - void setFluctuationData(const FluctuationData& fluctuationData); - void initFluctuationData(const BinFluctuationData& fluctuationData); - const FieldData& fieldData() const; FieldData& fieldData(); void setFieldData(const FieldData& fieldData); @@ -525,12 +539,21 @@ class Emitter { bool mIsTexturePatternAnim{false}; TextureHandle mTextureHandle{}; + // ----- Complex Properties ----- \\ + ComplexProperties mComplexProperties{}; + // Fluctuation Properties + f32 mFluctuationScale{1.0f}; + f32 mFluctuationFreq{20.0f}; + bool mFluctuationPhaseRnd{false}; + BitFlag mFluctuationFlags{ + FluctuationFlag::ApplyAlpha, + FluctuationFlag::ApplyScale + }; + ChildData mChildData{}; - FluctuationData mFluctuationData{}; FieldData mFieldData{}; - StripeData mStripeData{}; }; diff --git a/include/ptcl/ptclFluctuationData.h b/include/ptcl/ptclFluctuationData.h deleted file mode 100644 index 876169f..0000000 --- a/include/ptcl/ptclFluctuationData.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "typedefs.h" - -namespace Ptcl { - - -// ========================================================================== // - - -struct FluctuationData { - f32 fluctuationScale{1.0f}; - f32 fluctuationFreq{20.0f}; - bool fluctuationPhaseRnd{false}; -}; - - -// ========================================================================== // - - -} // namespace Ptcl diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index 6e092a7..6b9dbdd 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -6,6 +6,7 @@ #include "editor/emitterWidget/colorPropertiesWidget.h" #include "editor/emitterWidget/combinerPropertiesWidget.h" #include "editor/emitterWidget/emissionPropertiesWidget.h" +#include "editor/emitterWidget/fluctuationEditorWidget.h" #include "editor/emitterWidget/gravityPropertiesWidget.h" #include "editor/emitterWidget/lifespanPropertiesWidget.h" #include "editor/emitterWidget/rotationPropertiesWidget.h" @@ -121,20 +122,6 @@ void EmitterWidget::setupConnections() { emit propertiesChanged(); }); - // Fluctuation Editor Widget - connect(mFluctuationEditorWidget, &FluctuationEditorWidget::dataUpdated, this, [this](const Ptcl::FluctuationData& data) { - if (!mEmitter) { return; } - mEmitter->setFluctuationData(data); - emit propertiesChanged(); - }); - - connect(mFluctuationEditorWidget, &FluctuationEditorWidget::flagsUpdated, this, [this](const BitFlag& fluxFlags) { - if (!mEmitter) { return; } - mEmitter->setFluctuationFlags(fluxFlags); - emit complexFlagsChanged(); - emit propertiesChanged(); - }); - // Field Editor Widget connect(mFieldEditorWidget, &FieldEditorWidget::flagsUpdated, this, [this](const BitFlag& fieldFlags) { if (!mEmitter) { return; } @@ -176,6 +163,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mCombinerProperties->setDocument(document); mColorProperties->setDocument(document); mTextureProperties->setDocument(document); + mFluctuationEditorWidget->setDocument(document); } void EmitterWidget::setSelection(Ptcl::Selection* selection) { @@ -195,6 +183,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mCombinerProperties->setSelection(selection); mColorProperties->setSelection(selection); mTextureProperties->setSelection(selection); + mFluctuationEditorWidget->setSelection(selection); connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -232,14 +221,12 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { void EmitterWidget::populateProperties() { QSignalBlocker b15(mChildEditorWidget); - QSignalBlocker b16(mFluctuationEditorWidget); QSignalBlocker b17(mFieldEditorWidget); QSignalBlocker b18(mStripeEditorWidget); mChildEditorWidget->setChildData(&mEmitter->childData(), mEmitter->complexProperties().childFlags); mChildEditorWidget->setParentColor0(mEmitter->primaryColor()); - mFluctuationEditorWidget->setData(mEmitter->fluctuationData(), mEmitter->complexProperties().fluctuationFlags); mFieldEditorWidget->setData(&mEmitter->fieldData(), mEmitter->complexProperties().fieldFlags); mStripeEditorWidget->setData(mEmitter->stripeData(), mEmitter->complexProperties().stripeFlags); diff --git a/src/editor/fluctuationEditorWidget.cpp b/src/editor/emitterWidget/fluctuationEditorWidget.cpp similarity index 55% rename from src/editor/fluctuationEditorWidget.cpp rename to src/editor/emitterWidget/fluctuationEditorWidget.cpp index 7289e64..adf0bd0 100644 --- a/src/editor/fluctuationEditorWidget.cpp +++ b/src/editor/emitterWidget/fluctuationEditorWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/fluctuationEditorWidget.h" +#include "editor/emitterWidget/fluctuationEditorWidget.h" #include @@ -9,7 +9,7 @@ namespace PtclEditor { FluctuationEditorWidget::FluctuationEditorWidget(QWidget* parent) : - QWidget{parent} { + EmitterWidgetBase{parent} { // TODO: Determine better ranges? mScaleSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); mFreqSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); @@ -38,43 +38,72 @@ FluctuationEditorWidget::FluctuationEditorWidget(QWidget* parent) : void FluctuationEditorWidget::setupConnections() { // Enabled connect(&mEnabledCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - mControlsContainer->setEnabled(checked); - mFluxFlag.set(Ptcl::FluctuationFlag::Enabled, checked); - emit flagsUpdated(mFluxFlag); + setEmitterProperty( + "Toggle Fluctuation", + "SetFluxEnable", + &Ptcl::Emitter::isFluctuationEnabled, + &Ptcl::Emitter::setFluctuationEnabled, + checked + ); }); // Apply to alpha connect(&mApplyAlphaCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - mFluxFlag.set(Ptcl::FluctuationFlag::ApplyAlpha, checked); - emit flagsUpdated(mFluxFlag); + setEmitterProperty( + "Toggle Fluctuation Apply Alpha", + "SetFluxApplyAlpha", + &Ptcl::Emitter::isFluctuationApplyAlpha, + &Ptcl::Emitter::setFluctuationApplyAlpha, + checked + ); }); // Apply to scale connect(&mApplyScaleCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - mFluxFlag.set(Ptcl::FluctuationFlag::ApplyScale, checked); - emit flagsUpdated(mFluxFlag); + setEmitterProperty( + "Toggle Fluctuation Apply Scale", + "SetFluxApplyScale", + &Ptcl::Emitter::isFluctuationApplyScale, + &Ptcl::Emitter::setFluctuationApplyScale, + checked + ); }); // Scale connect(&mScaleSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mData.fluctuationScale = static_cast(value); - emit dataUpdated(mData); + setEmitterProperty( + "Set Fluctuation Scale", + "SetFluxScale", + &Ptcl::Emitter::fluctuationScale, + &Ptcl::Emitter::setFluctuationScale, + static_cast(value) + ); }); // Freq connect(&mFreqSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mData.fluctuationFreq = static_cast(value); - emit dataUpdated(mData); + setEmitterProperty( + "Set Fluctuation Frequency", + "SetFluxFreq", + &Ptcl::Emitter::fluctuationFrequency, + &Ptcl::Emitter::setFluctuationFrequency, + static_cast(value) + ); }); // Phase Rnd connect(&mPhaseRndCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - mData.fluctuationPhaseRnd = checked; - emit dataUpdated(mData); + setEmitterProperty( + "Toggle Fluctuation Phase Randomness", + "SetFluxRandom", + &Ptcl::Emitter::isFluctuationPhaseRandom, + &Ptcl::Emitter::setFluctuationPhaseRandom, + checked + ); }); } -void FluctuationEditorWidget::setData(const Ptcl::FluctuationData& data, const BitFlag& fluxFlag) { +void FluctuationEditorWidget::populateProperties() { QSignalBlocker b1(mScaleSpinBox); QSignalBlocker b2(mFreqSpinBox); QSignalBlocker b3(mPhaseRndCheckBox); @@ -82,17 +111,14 @@ void FluctuationEditorWidget::setData(const Ptcl::FluctuationData& data, const B QSignalBlocker b5(mApplyScaleCheckBox); QSignalBlocker b6(mEnabledCheckBox); - mData = data; - mFluxFlag = fluxFlag; + mScaleSpinBox.setValue(mEmitter->fluctuationScale()); + mFreqSpinBox.setValue(mEmitter->fluctuationFrequency()); + mPhaseRndCheckBox.setChecked(mEmitter->isFluctuationPhaseRandom()); - mScaleSpinBox.setValue(mData.fluctuationScale); - mFreqSpinBox.setValue(mData.fluctuationFreq); - mPhaseRndCheckBox.setChecked(mData.fluctuationPhaseRnd); + mApplyAlphaCheckBox.setChecked(mEmitter->isFluctuationApplyAlpha()); + mApplyScaleCheckBox.setChecked(mEmitter->isFluctuationApplyScale()); - mApplyAlphaCheckBox.setChecked(mFluxFlag.isSet(Ptcl::FluctuationFlag::ApplyAlpha)); - mApplyScaleCheckBox.setChecked( mFluxFlag.isSet(Ptcl::FluctuationFlag::ApplyScale)); - - const bool isEnabled = mFluxFlag.isSet(Ptcl::FluctuationFlag::Enabled); + const bool isEnabled = mEmitter->isFluctuationEnabled(); mEnabledCheckBox.setChecked(isEnabled); mControlsContainer->setEnabled(isEnabled); } diff --git a/src/editor/ptclListWidget.cpp b/src/editor/ptclListWidget.cpp index 6604451..61ed274 100644 --- a/src/editor/ptclListWidget.cpp +++ b/src/editor/ptclListWidget.cpp @@ -402,7 +402,7 @@ void PtclList::addComplexNodes(QStandardItem* emitterItem, s32 setIndex, s32 emi "Fluctuation", setIndex, emitterIndex, - props.fluctuationFlags.isSet(Ptcl::FluctuationFlag::Enabled) + emitter->isFluctuationEnabled() ); // Field diff --git a/src/ptcl/ptcl.cpp b/src/ptcl/ptcl.cpp index 70ea7a4..4510150 100644 --- a/src/ptcl/ptcl.cpp +++ b/src/ptcl/ptcl.cpp @@ -365,7 +365,7 @@ void PtclBinaryWriter::writeComplexEmitter(const Emitter& emitter) { if (hasPosAdd) { emitterDataSize += sizeof(BinFieldPosAddData); } // Fluctuation - const bool hasFluctuation = emitter.complexProperties().fluctuationFlags.isSet(FluctuationFlag::Enabled); + const bool hasFluctuation = emitter.fluctuationFlags().isSet(FluctuationFlag::Enabled); if (hasFluctuation) { emitterData.fluctuationDataOffset = emitterDataSize; @@ -393,7 +393,7 @@ void PtclBinaryWriter::writeComplexEmitter(const Emitter& emitter) { if (hasCollision) { mEmitterData.emplace_back(BinFieldCollisionData{emitter.fieldData().collisionData()}); } if (hasConvergence) { mEmitterData.emplace_back(BinFieldConvergenceData{emitter.fieldData().convergenceData()}); } if (hasPosAdd) { mEmitterData.emplace_back(BinFieldPosAddData{emitter.fieldData().posAddData()}); } - if (hasFluctuation) { mEmitterData.emplace_back(BinFluctuationData{emitter.fluctuationData()}); } + if (hasFluctuation) { mEmitterData.emplace_back(BinFluctuationData{emitter}); } if (hasStripe) { mEmitterData.emplace_back(BinStripeData{emitter.stripeData()}); } } diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index caf5aab..5a79c90 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -654,7 +654,7 @@ BinComplexEmitterData::BinComplexEmitterData(const Ptcl::Emitter& emitter) : BinCommonEmitterData{emitter} { childFlag = emitter.complexProperties().childFlags; fieldFlag = emitter.complexProperties().fieldFlags; - fluctuationFlag = emitter.complexProperties().fluctuationFlags; + fluctuationFlag = emitter.fluctuationFlags(); stripeFlag = emitter.complexProperties().stripeFlags; childDataOffset = 0; @@ -984,10 +984,10 @@ QDataStream& operator<<(QDataStream& out, const BinFieldPosAddData& item) { // ========================================================================== // -BinFluctuationData::BinFluctuationData(const Ptcl::FluctuationData& fluctuationData) { - fluctuationScale = fluctuationData.fluctuationScale; - fluctuationFreq = fluctuationData.fluctuationFreq; - fluctuationPhaseRnd = fluctuationData.fluctuationPhaseRnd; +BinFluctuationData::BinFluctuationData(const Ptcl::Emitter& emitterData) { + fluctuationScale = emitterData.fluctuationScale(); + fluctuationFreq = emitterData.fluctuationFrequency(); + fluctuationPhaseRnd = emitterData.isFluctuationPhaseRandom(); } QDataStream& operator>>(QDataStream& in, BinFluctuationData& item) { diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index 51f582b..0ce540f 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -107,7 +107,12 @@ std::unique_ptr Emitter::clone() const { newEmitter->mComplexProperties = mComplexProperties; newEmitter->mChildData = std::move(*mChildData.clone()); - newEmitter->mFluctuationData = mFluctuationData; + + // Fluctuation Properties + newEmitter->mFluctuationScale = mFluctuationScale; + newEmitter->mFluctuationFreq = mFluctuationFreq; + newEmitter->mFluctuationPhaseRnd = mFluctuationPhaseRnd; + newEmitter->mFieldData = mFieldData; newEmitter->mStripeData = mStripeData; return newEmitter; @@ -133,10 +138,6 @@ void Emitter::setChildFlags(const BitFlag& childFlags) { mComplexProperties.childFlags = childFlags; } -void Emitter::setFluctuationFlags(const BitFlag& fluxFlags) { - mComplexProperties.fluctuationFlags = fluxFlags; -} - void Emitter::setFieldFlags(const BitFlag& fieldFlags) { mComplexProperties.fieldFlags = fieldFlags; } @@ -153,20 +154,10 @@ ChildData& Emitter::childData() { return mChildData; } -const FluctuationData& Emitter::fluctuationData() const { - return mFluctuationData; -} - -void Emitter::setFluctuationData(const FluctuationData& fluctuationData) { - mFluctuationData = fluctuationData; -} - void Emitter::initFluctuationData(const BinFluctuationData& fluctuationData) { - mFluctuationData = { - .fluctuationScale = fluctuationData.fluctuationScale, - .fluctuationFreq = fluctuationData.fluctuationFreq, - .fluctuationPhaseRnd = static_cast(fluctuationData.fluctuationPhaseRnd) - }; + mFluctuationScale = fluctuationData.fluctuationScale; + mFluctuationFreq = fluctuationData.fluctuationFreq; + mFluctuationPhaseRnd = static_cast(fluctuationData.fluctuationPhaseRnd); } const FieldData& Emitter::fieldData() const { @@ -321,9 +312,10 @@ void Emitter::initComplexFromBinary(const BinComplexEmitterData& emitterData) { mComplexProperties = { .childFlags = emitterData.childFlag, .fieldFlags = emitterData.fieldFlag, - .fluctuationFlags = emitterData.fluctuationFlag, .stripeFlags = emitterData.stripeFlag }; + + mFluctuationFlags = emitterData.fluctuationFlag; } From 5b12a6fde0ea79cecb39c916265e5c0d492223cb Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Tue, 17 Mar 2026 02:09:47 +0000 Subject: [PATCH 18/35] feat(ui): implement undo/redo for Emitter stripeProperties --- CMakeLists.txt | 5 +- include/editor/emitterWidget/emitterWidget.h | 2 +- .../{ => emitterWidget}/stripeEditorWidget.h | 17 +-- include/ptcl/ptclBinary.h | 2 +- include/ptcl/ptclEmitter.h | 53 +++++++-- include/ptcl/ptclStripeData.h | 29 ----- src/editor/emitterWidget/emitterWidget.cpp | 109 ++++++++++-------- .../stripeEditorWidget.cpp | 99 +++++++++++----- src/ptcl/ptcl.cpp | 2 +- src/ptcl/ptclBinary.cpp | 18 +-- src/ptcl/ptclEmitter.cpp | 42 +++---- 11 files changed, 209 insertions(+), 169 deletions(-) rename include/editor/{ => emitterWidget}/stripeEditorWidget.h (65%) delete mode 100644 include/ptcl/ptclStripeData.h rename src/editor/{ => emitterWidget}/stripeEditorWidget.cpp (54%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 16d719c..bf14fbd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,6 @@ set(PROJECT_SOURCES src/editor/mainWindow.cpp src/editor/emitterSetWidget.cpp src/editor/ptclListWidget.cpp - src/editor/stripeEditorWidget.cpp src/editor/textureListWidget.cpp src/editor/textureImportDialog.cpp src/editor/textureSelectDialog.cpp @@ -61,6 +60,7 @@ set(PROJECT_SOURCES src/editor/emitterWidget/lifespanPropertiesWidget.cpp src/editor/emitterWidget/rotationPropertiesWidget.cpp src/editor/emitterWidget/scalePropertiesWidget.cpp + src/editor/emitterWidget/stripeEditorWidget.cpp src/editor/emitterWidget/terminationPropertiesWidget.cpp src/editor/emitterWidget/texturePropertiesWidget.cpp src/editor/emitterWidget/transformPropertiesWidget.cpp @@ -94,7 +94,6 @@ set(PROJECT_HEADERS include/editor/mainWindow.h include/editor/emitterSetWidget.h include/editor/ptclListWidget.h - include/editor/stripeEditorWidget.h include/editor/textureImportDialog.h include/editor/textureListWidget.h include/editor/textureSelectDialog.h @@ -133,6 +132,7 @@ set(PROJECT_HEADERS include/editor/emitterWidget/lifespanPropertiesWidget.h include/editor/emitterWidget/rotationPropertiesWidget.h include/editor/emitterWidget/scalePropertiesWidget.h + include/editor/emitterWidget/stripeEditorWidget.h include/editor/emitterWidget/terminationPropertiesWidget.h include/editor/emitterWidget/texturePropertiesWidget.h include/editor/emitterWidget/transformPropertiesWidget.h @@ -159,7 +159,6 @@ set(PROJECT_HEADERS include/ptcl/ptclEnum.h include/ptcl/ptclFieldData.h include/ptcl/ptclSeed.h - include/ptcl/ptclStripeData.h include/ptcl/ptclTexture.h include/util/bitflagUtil.h include/util/imageUtil.h diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index a5fb711..4a55c6c 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -3,7 +3,6 @@ #include "editor/childEditor/childEditorWidget.h" #include "editor/components/collapsibleWidget.h" #include "editor/fieldEditor/fieldEditorWidget.h" -#include "editor/stripeEditorWidget.h" #include "ptcl/ptclDocument.h" #include "ptcl/ptclEmitter.h" @@ -36,6 +35,7 @@ class CombinerPropertiesWidget; class ColorPropertiesWidget; class TexturePropertiesWidget; class FluctuationEditorWidget; +class StripeEditorWidget; // ========================================================================== // diff --git a/include/editor/stripeEditorWidget.h b/include/editor/emitterWidget/stripeEditorWidget.h similarity index 65% rename from include/editor/stripeEditorWidget.h rename to include/editor/emitterWidget/stripeEditorWidget.h index 2c6661a..b3cf3a1 100644 --- a/include/editor/stripeEditorWidget.h +++ b/include/editor/emitterWidget/stripeEditorWidget.h @@ -1,9 +1,7 @@ #pragma once #include "editor/components/vectorSpinBox.h" -#include "ptcl/ptclEnum.h" -#include "ptcl/ptclStripeData.h" -#include "util/bitflagUtil.h" +#include "editor/emitterWidget/emitterWidgetBase.h" #include #include @@ -18,24 +16,16 @@ namespace PtclEditor { // ========================================================================== // -class StripeEditorWidget final : public QWidget { +class StripeEditorWidget final : public EmitterWidgetBase { Q_OBJECT public: explicit StripeEditorWidget(QWidget* parent = nullptr); - void setData(const Ptcl::StripeData& data, const BitFlag& stripeFlag); - -signals: - void dataUpdated(const Ptcl::StripeData& data); - void flagsUpdated(const BitFlag& stripeFlags); - private: + void populateProperties() final; void setupConnections(); private: - Ptcl::StripeData mData{}; - BitFlag mStripeFlag{}; - QComboBox mTypeComboBox{}; QSpinBox mNumHistSpinBox{}; QDoubleSpinBox mStartAlphaSpinBox{}; @@ -46,6 +36,7 @@ class StripeEditorWidget final : public QWidget { QCheckBox mEmitterCoordCheckBox{}; }; + // ========================================================================== // diff --git a/include/ptcl/ptclBinary.h b/include/ptcl/ptclBinary.h index ef80156..5928cd1 100644 --- a/include/ptcl/ptclBinary.h +++ b/include/ptcl/ptclBinary.h @@ -538,7 +538,7 @@ struct alignas(4) BinStripeData { f32 stripeDirInterpolate; // 0x1C BinStripeData() = default; - BinStripeData(const Ptcl::StripeData& stripeData); + BinStripeData(const Ptcl::Emitter& emitterData); friend QDataStream& operator>>(QDataStream& in, BinStripeData& item); friend QDataStream& operator<<(QDataStream& out, const BinStripeData& item); diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index cbc478d..3b2b01e 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -7,7 +7,6 @@ #include "ptcl/ptclChildData.h" #include "ptcl/ptclEnum.h" #include "ptcl/ptclFieldData.h" -#include "ptcl/ptclStripeData.h" #include "ptcl/ptclSeed.h" #include "ptcl/ptclTexture.h" @@ -58,7 +57,6 @@ class Emitter { ChildFlag::Unk80 }; BitFlag fieldFlags{}; - BitFlag stripeFlags{}; }; public: @@ -403,6 +401,39 @@ class Emitter { bool isFluctuationApplyScale() const { return mFluctuationFlags.isSet(Ptcl::FluctuationFlag::ApplyScale); } void setFluctuationApplyScale(bool apply) { mFluctuationFlags.set(Ptcl::FluctuationFlag::ApplyScale, apply); } + // ----- Stripe Properties ----- \\ + + void initStripeData(const BinStripeData& stripeData); + + const BitFlag& stripeFlags() const { return mStripeFlags; } + void setStripeFlags(const BitFlag& stripeFlags) { mStripeFlags = stripeFlags; } + + bool hasStripeData() const; + + StripeType stripeType() const { return mStripeType; } + void setStripeType(StripeType type) { mStripeType = type; } + + s32 stripeNumHistory() const { return mStripeNumHistory; } + void setStripeNumHistory(s32 num) { mStripeNumHistory = num; } + + f32 stripeStartAlpha() const { return mStripeStartAlpha; } + void setStripeStartAlpha(f32 alpha) { mStripeStartAlpha = alpha; } + + f32 stripeEndAlpha() const { return mStripeEndAlpha; } + void setStripeEndAlpha(f32 alpha) { mStripeEndAlpha = alpha; } + + const Math::Vector2f& stripeUVScrollSpeed() const { return mStripeUVScrollSpeed; } + void setStripeUVScrollSpeed(const Math::Vector2f& speed) { mStripeUVScrollSpeed = speed; } + + s32 stripeHistoryStep() const { return mStripeHistoryStep; } + void setStripeHistoryStep(s32 step) { mStripeHistoryStep = step; } + + f32 stripeDirInterpolate() const { return mStripeDirInterpolate; } + void setStripeDirInterpolate(f32 interp) { mStripeDirInterpolate = interp; } + + bool isStripeEmitterCoord() const { return mStripeFlags.isSet(Ptcl::StripeFlag::EmitterCoord); } + void setStripeEmitterCoord(bool emitterCoord) { mStripeFlags.set(Ptcl::StripeFlag::EmitterCoord, emitterCoord); } + BitFlag& flags(); const BitFlag& flags() const; @@ -411,7 +442,6 @@ class Emitter { void setChildFlags(const BitFlag& childFlags); void setFieldFlags(const BitFlag& fieldFlags); - void setStripeFlags(const BitFlag& stripeFlags); const ChildData& childData() const; ChildData& childData(); @@ -420,12 +450,6 @@ class Emitter { FieldData& fieldData(); void setFieldData(const FieldData& fieldData); - const StripeData& stripeData() const; - void setStripeData(const StripeData& stripeData); - void initStripeData(const BinStripeData& stripeData); - - bool hasStripeData() const; - void initFromBinary(const BinCommonEmitterData& emitterData); void initComplexFromBinary(const BinComplexEmitterData& emitterData); @@ -552,9 +576,18 @@ class Emitter { FluctuationFlag::ApplyScale }; + // Stripe Properties + StripeType mStripeType{StripeType::Billboard}; + s32 mStripeNumHistory{60}; + f32 mStripeStartAlpha{1.0f}; + f32 mStripeEndAlpha{1.0f}; + Math::Vector2f mStripeUVScrollSpeed{0.0f, 0.0f}; + s32 mStripeHistoryStep{1}; + f32 mStripeDirInterpolate{1.0f}; + BitFlag mStripeFlags{}; + ChildData mChildData{}; FieldData mFieldData{}; - StripeData mStripeData{}; }; diff --git a/include/ptcl/ptclStripeData.h b/include/ptcl/ptclStripeData.h deleted file mode 100644 index 80c9eae..0000000 --- a/include/ptcl/ptclStripeData.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "math/vector.h" -#include "ptcl/ptclEnum.h" -#include "typedefs.h" - - -namespace Ptcl { - - -// ========================================================================== // - - -struct StripeData -{ - StripeType type{StripeType::Billboard}; - s32 numHistory{60}; - f32 startAlpha{1.0f}; - f32 endAlpha{1.0f}; - Math::Vector2f uvScrollSpeed{0.0f, 0.0f}; - s32 historyStep{1}; - f32 dirInterpolate{1.0f}; -}; - - -// ========================================================================== // - - -} // namespace Ptcl diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index 6b9dbdd..090c589 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -11,6 +11,7 @@ #include "editor/emitterWidget/lifespanPropertiesWidget.h" #include "editor/emitterWidget/rotationPropertiesWidget.h" #include "editor/emitterWidget/scalePropertiesWidget.h" +#include "editor/emitterWidget/stripeEditorWidget.h" #include "editor/emitterWidget/terminationPropertiesWidget.h" #include "editor/emitterWidget/texturePropertiesWidget.h" #include "editor/emitterWidget/transformPropertiesWidget.h" @@ -129,24 +130,13 @@ void EmitterWidget::setupConnections() { emit complexFlagsChanged(); emit propertiesChanged(); }); - - // Stripe Editor Widget - connect(mStripeEditorWidget, &StripeEditorWidget::dataUpdated, this, [this](const Ptcl::StripeData& data) { - if (!mEmitter) { return; } - mEmitter->setStripeData(data); - emit propertiesChanged(); - }); - - connect(mStripeEditorWidget, &StripeEditorWidget::flagsUpdated, this, [this](const BitFlag& stripeFlags) { - if (!mEmitter) { return; } - mEmitter->setStripeFlags(stripeFlags); - emit complexFlagsChanged(); - emit propertiesChanged(); - }); - } void EmitterWidget::setDocument(Ptcl::Document* document) { + if (mDocument) { + mDocument->disconnect(this); + } + mDocument = document; mBasicProperties->setDocument(document); @@ -164,9 +154,28 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { mColorProperties->setDocument(document); mTextureProperties->setDocument(document); mFluctuationEditorWidget->setDocument(document); + mStripeEditorWidget->setDocument(document); + + if (mDocument) { + connect(mDocument, &Ptcl::Document::emitterChanged, this, [this](s32 setIndex, s32 emitterIndex) { + if (!mEmitter) { + return; + } + + if (setIndex != mSelection->emitterSetIndex() || emitterIndex != mSelection->emitterIndex()) { + return; + } + + populateProperties(); + }); + } } void EmitterWidget::setSelection(Ptcl::Selection* selection) { + if (mSelection) { + mSelection->disconnect(this); + } + mSelection = selection; mBasicProperties->setSelection(selection); @@ -184,39 +193,42 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mColorProperties->setSelection(selection); mTextureProperties->setSelection(selection); mFluctuationEditorWidget->setSelection(selection); - - connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { - if (!mDocument) { - mEmitter = nullptr; - setEnabled(false); - return; - } - - mEmitter = mDocument->emitter(setIndex, emitterIndex); - - switch (type) { - case Ptcl::Selection::Type::Emitter: - mStackedWidget->setCurrentIndex(0); - break; - case Ptcl::Selection::Type::EmitterChild: - mStackedWidget->setCurrentWidget(mChildEditorWidget); - break; - case Ptcl::Selection::Type::EmitterFlux: - mStackedWidget->setCurrentWidget(mFluctuationEditorWidget); - break; - case Ptcl::Selection::Type::EmitterField: - mStackedWidget->setCurrentWidget(mFieldEditorWidget); - break; - default: - break; - } - - // TODO: Have child widgets handle this themselves - mChildEditorWidget->setTextureList(mTextureList); - - setEnabled(true); - populateProperties(); - }); + mStripeEditorWidget->setSelection(selection); + + if (mSelection) { + connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { + if (!mDocument) { + mEmitter = nullptr; + setEnabled(false); + return; + } + + mEmitter = mDocument->emitter(setIndex, emitterIndex); + + switch (type) { + case Ptcl::Selection::Type::Emitter: + mStackedWidget->setCurrentIndex(0); + break; + case Ptcl::Selection::Type::EmitterChild: + mStackedWidget->setCurrentWidget(mChildEditorWidget); + break; + case Ptcl::Selection::Type::EmitterFlux: + mStackedWidget->setCurrentWidget(mFluctuationEditorWidget); + break; + case Ptcl::Selection::Type::EmitterField: + mStackedWidget->setCurrentWidget(mFieldEditorWidget); + break; + default: + break; + } + + // TODO: Have child widgets handle this themselves + mChildEditorWidget->setTextureList(mTextureList); + + setEnabled(true); + populateProperties(); + }); + } } void EmitterWidget::populateProperties() { @@ -228,7 +240,6 @@ void EmitterWidget::populateProperties() { mChildEditorWidget->setParentColor0(mEmitter->primaryColor()); mFieldEditorWidget->setData(&mEmitter->fieldData(), mEmitter->complexProperties().fieldFlags); - mStripeEditorWidget->setData(mEmitter->stripeData(), mEmitter->complexProperties().stripeFlags); updateStripeVisibility(); } diff --git a/src/editor/stripeEditorWidget.cpp b/src/editor/emitterWidget/stripeEditorWidget.cpp similarity index 54% rename from src/editor/stripeEditorWidget.cpp rename to src/editor/emitterWidget/stripeEditorWidget.cpp index 710e70a..4860f49 100644 --- a/src/editor/stripeEditorWidget.cpp +++ b/src/editor/emitterWidget/stripeEditorWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/stripeEditorWidget.h" +#include "editor/emitterWidget/stripeEditorWidget.h" #include @@ -9,7 +9,7 @@ namespace PtclEditor { StripeEditorWidget::StripeEditorWidget(QWidget* parent) : - QWidget{parent} { + EmitterWidgetBase{parent} { mTypeComboBox.addItem("Billboard Stripe", QVariant::fromValue(Ptcl::StripeType::Billboard)); mTypeComboBox.addItem("Emitter Maxtrix", QVariant::fromValue(Ptcl::StripeType::EmitterMatrix)); @@ -39,55 +39,97 @@ StripeEditorWidget::StripeEditorWidget(QWidget* parent) : void StripeEditorWidget::setupConnections() { // Type connect(&mTypeComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { + Q_UNUSED(index); const auto type = mTypeComboBox.currentData().value(); - mData.type = type; - emit dataUpdated(mData); + setEmitterProperty( + "Set Stripe Type", + "SetStripeType", + &Ptcl::Emitter::stripeType, + &Ptcl::Emitter::setStripeType, + type + ); }); // Num Hist connect(&mNumHistSpinBox, &QSpinBox::valueChanged, this, [this](s32 value) { - mData.numHistory = value; - emit dataUpdated(mData); + setEmitterProperty( + "Set Stripe History Size", + "SetStripeNumHist", + &Ptcl::Emitter::stripeNumHistory, + &Ptcl::Emitter::setStripeNumHistory, + value + ); }); // Start Alpha connect(&mStartAlphaSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mData.startAlpha = static_cast(value); - emit dataUpdated(mData); + setEmitterProperty( + "Set Stripe Start Alpha", + "SetStripeStartAlpha", + &Ptcl::Emitter::stripeStartAlpha, + &Ptcl::Emitter::setStripeStartAlpha, + static_cast(value) + ); }); // End Alpha connect(&mEndAlphaSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mData.endAlpha = static_cast(value); - emit dataUpdated(mData); + setEmitterProperty( + "Set Stripe End Alpha", + "SetStripeEndAlpha", + &Ptcl::Emitter::stripeEndAlpha, + &Ptcl::Emitter::setStripeEndAlpha, + static_cast(value) + ); }); // UV Scroll Speed connect(&mUVScrollSpeedSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mData.uvScrollSpeed = mUVScrollSpeedSpinBox.getVector(); - emit dataUpdated(mData); + const auto speed = mUVScrollSpeedSpinBox.getVector(); + setEmitterProperty( + "Set Stripe UV Scroll Speed", + "SetStripeUVSpeed", + &Ptcl::Emitter::stripeUVScrollSpeed, + &Ptcl::Emitter::setStripeUVScrollSpeed, + speed + ); }); // Hist Step connect(&mHistStepSpinBox, &QSpinBox::valueChanged, this, [this](s32 value) { - mData.historyStep = value; - emit dataUpdated(mData); + setEmitterProperty( + "Set Stripe History Spacing", + "SetStripeHistStep", + &Ptcl::Emitter::stripeHistoryStep, + &Ptcl::Emitter::setStripeHistoryStep, + value + ); }); // Dir Interpolate connect(&mDirIntepolateSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mData.dirInterpolate = static_cast(value); - emit dataUpdated(mData); + setEmitterProperty( + "Set Stripe Interpolation Ratio", + "SetStripeDirInterp", + &Ptcl::Emitter::stripeDirInterpolate, + &Ptcl::Emitter::setStripeDirInterpolate, + static_cast(value) + ); }); // Emitter Coord connect(&mEmitterCoordCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - mStripeFlag.set(Ptcl::StripeFlag::EmitterCoord, checked); - emit flagsUpdated(mStripeFlag); + setEmitterProperty( + "Toggle Stripe Follow Emitter", + "SetStripeEmitterCoord", + &Ptcl::Emitter::isStripeEmitterCoord, + &Ptcl::Emitter::setStripeEmitterCoord, + checked + ); }); } -void StripeEditorWidget::setData(const Ptcl::StripeData& data, const BitFlag& stripeFlag) { +void StripeEditorWidget::populateProperties() { QSignalBlocker b1(mTypeComboBox); QSignalBlocker b2(mNumHistSpinBox); QSignalBlocker b3(mStartAlphaSpinBox); @@ -97,19 +139,16 @@ void StripeEditorWidget::setData(const Ptcl::StripeData& data, const BitFlagstripeType())); mTypeComboBox.setCurrentIndex(typeIndex); - mNumHistSpinBox.setValue(mData.numHistory); - mStartAlphaSpinBox.setValue(mData.startAlpha); - mEndAlphaSpinBox.setValue(mData.endAlpha); - mUVScrollSpeedSpinBox.setVector(mData.uvScrollSpeed); - mHistStepSpinBox.setValue(mData.historyStep); - mDirIntepolateSpinBox.setValue(mData.dirInterpolate); - mEmitterCoordCheckBox.setChecked(stripeFlag.isSet(Ptcl::StripeFlag::EmitterCoord)); + mNumHistSpinBox.setValue(mEmitter->stripeNumHistory()); + mStartAlphaSpinBox.setValue(mEmitter->stripeStartAlpha()); + mEndAlphaSpinBox.setValue(mEmitter->stripeEndAlpha()); + mUVScrollSpeedSpinBox.setVector(mEmitter->stripeUVScrollSpeed()); + mHistStepSpinBox.setValue(mEmitter->stripeHistoryStep()); + mDirIntepolateSpinBox.setValue(mEmitter->stripeDirInterpolate()); + mEmitterCoordCheckBox.setChecked(mEmitter->isStripeEmitterCoord()); } diff --git a/src/ptcl/ptcl.cpp b/src/ptcl/ptcl.cpp index 4510150..66a66e8 100644 --- a/src/ptcl/ptcl.cpp +++ b/src/ptcl/ptcl.cpp @@ -394,7 +394,7 @@ void PtclBinaryWriter::writeComplexEmitter(const Emitter& emitter) { if (hasConvergence) { mEmitterData.emplace_back(BinFieldConvergenceData{emitter.fieldData().convergenceData()}); } if (hasPosAdd) { mEmitterData.emplace_back(BinFieldPosAddData{emitter.fieldData().posAddData()}); } if (hasFluctuation) { mEmitterData.emplace_back(BinFluctuationData{emitter}); } - if (hasStripe) { mEmitterData.emplace_back(BinStripeData{emitter.stripeData()}); } + if (hasStripe) { mEmitterData.emplace_back(BinStripeData{emitter}); } } void PtclBinaryWriter::writeFile() { diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index 5a79c90..811e66a 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -655,7 +655,7 @@ BinComplexEmitterData::BinComplexEmitterData(const Ptcl::Emitter& emitter) : childFlag = emitter.complexProperties().childFlags; fieldFlag = emitter.complexProperties().fieldFlags; fluctuationFlag = emitter.fluctuationFlags(); - stripeFlag = emitter.complexProperties().stripeFlags; + stripeFlag = emitter.stripeFlags(); childDataOffset = 0; fieldDataOffset = 0; @@ -1008,14 +1008,14 @@ QDataStream& operator<<(QDataStream& out, const BinFluctuationData& item) { // ========================================================================== // -BinStripeData::BinStripeData(const Ptcl::StripeData& stripeData) { - stripeType = stripeData.type; - stripeNumHistory = stripeData.numHistory; - stripeStartAlpha = stripeData.startAlpha; - stripeEndAlpha = stripeData.endAlpha; - uvScrollSpeed = stripeData.uvScrollSpeed; - stripeHistoryStep = stripeData.historyStep; - stripeDirInterpolate = stripeData.dirInterpolate; +BinStripeData::BinStripeData(const Ptcl::Emitter& emitterData) { + stripeType = emitterData.stripeType(); + stripeNumHistory = emitterData.stripeNumHistory(); + stripeStartAlpha = emitterData.stripeStartAlpha(); + stripeEndAlpha = emitterData.stripeEndAlpha(); + uvScrollSpeed = emitterData.stripeUVScrollSpeed(); + stripeHistoryStep = emitterData.stripeHistoryStep(); + stripeDirInterpolate = emitterData.stripeDirInterpolate(); } QDataStream& operator>>(QDataStream& in, BinStripeData& item) { diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index 0ce540f..ef74da4 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -109,12 +109,22 @@ std::unique_ptr Emitter::clone() const { newEmitter->mChildData = std::move(*mChildData.clone()); // Fluctuation Properties + newEmitter->mFluctuationFlags = mFluctuationFlags; newEmitter->mFluctuationScale = mFluctuationScale; newEmitter->mFluctuationFreq = mFluctuationFreq; newEmitter->mFluctuationPhaseRnd = mFluctuationPhaseRnd; + // Stripe Properties + newEmitter->mStripeFlags = mStripeFlags; + newEmitter->mStripeType = mStripeType; + newEmitter->mStripeNumHistory = mStripeNumHistory; + newEmitter->mStripeStartAlpha = mStripeStartAlpha; + newEmitter->mStripeEndAlpha = mStripeEndAlpha; + newEmitter->mStripeUVScrollSpeed = mStripeUVScrollSpeed; + newEmitter->mStripeHistoryStep = mStripeHistoryStep; + newEmitter->mStripeDirInterpolate = mStripeDirInterpolate; + newEmitter->mFieldData = mFieldData; - newEmitter->mStripeData = mStripeData; return newEmitter; } @@ -142,10 +152,6 @@ void Emitter::setFieldFlags(const BitFlag& fieldFlags) { mComplexProperties.fieldFlags = fieldFlags; } -void Emitter::setStripeFlags(const BitFlag& stripeFlags) { - mComplexProperties.stripeFlags = stripeFlags; -} - const ChildData& Emitter::childData() const { return mChildData; } @@ -172,24 +178,14 @@ void Emitter::setFieldData(const FieldData& fieldData) { mFieldData = fieldData; } -const StripeData& Emitter::stripeData() const { - return mStripeData; -} - -void Emitter::setStripeData(const StripeData& stripeData) { - mStripeData = stripeData; -} - void Emitter::initStripeData(const BinStripeData& stripeData) { - mStripeData = { - .type = stripeData.stripeType, - .numHistory = stripeData.stripeNumHistory, - .startAlpha = stripeData.stripeStartAlpha, - .endAlpha = stripeData.stripeEndAlpha, - .uvScrollSpeed = {stripeData.uvScrollSpeed.x, stripeData.uvScrollSpeed.y}, - .historyStep = stripeData.stripeHistoryStep, - .dirInterpolate = stripeData.stripeDirInterpolate - }; + mStripeType = stripeData.stripeType; + mStripeNumHistory = stripeData.stripeNumHistory; + mStripeStartAlpha = stripeData.stripeStartAlpha; + mStripeEndAlpha = stripeData.stripeEndAlpha; + mStripeUVScrollSpeed = {stripeData.uvScrollSpeed.x, stripeData.uvScrollSpeed.y}; + mStripeHistoryStep = stripeData.stripeHistoryStep; + mStripeDirInterpolate = stripeData.stripeDirInterpolate; } bool Emitter::hasStripeData() const { @@ -312,10 +308,10 @@ void Emitter::initComplexFromBinary(const BinComplexEmitterData& emitterData) { mComplexProperties = { .childFlags = emitterData.childFlag, .fieldFlags = emitterData.fieldFlag, - .stripeFlags = emitterData.stripeFlag }; mFluctuationFlags = emitterData.fluctuationFlag; + mStripeFlags = emitterData.stripeFlag; } From 9520774ce36cc0e3a4627393b55d897eb0ca74e0 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Tue, 17 Mar 2026 04:13:43 +0000 Subject: [PATCH 19/35] feat(ui): rework EmitterWidget to use tabbed layout --- include/editor/emitterWidget/emitterWidget.h | 19 ++- src/editor/emitterWidget/emitterWidget.cpp | 166 +++++++++---------- 2 files changed, 93 insertions(+), 92 deletions(-) diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/emitterWidget.h index 4a55c6c..2dd5435 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/emitterWidget.h @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include @@ -57,11 +57,17 @@ class EmitterWidget final : public QWidget { void propertiesChanged(); private: - void setupStandardLayout(QVBoxLayout* mainLayout); void setupConnections(); - void updateStripeVisibility(); void populateProperties(); + void rebuildTabs(); + void buildEmitterTabs(); + void buildChildTabs(); + void buildFluxTabs(); + void buildFieldTabs(); + + QWidget* wrapInScroll(QWidget* widget); + private: Ptcl::Document* mDocument{nullptr}; const Ptcl::Selection* mSelection{nullptr}; @@ -83,12 +89,13 @@ class EmitterWidget final : public QWidget { ScalePropertiesWidget* mScaleProperties{nullptr}; TexturePropertiesWidget* mTextureProperties{nullptr}; CombinerPropertiesWidget* mCombinerProperties{nullptr}; + StripeEditorWidget* mStripeEditorWidget{nullptr}; + FluctuationEditorWidget* mFluctuationEditorWidget{nullptr}; + + QTabWidget* mTabWidget{nullptr}; - QStackedWidget* mStackedWidget{nullptr}; ChildEditorWidget* mChildEditorWidget{nullptr}; - FluctuationEditorWidget* mFluctuationEditorWidget{nullptr}; FieldEditorWidget* mFieldEditorWidget{nullptr}; - StripeEditorWidget* mStripeEditorWidget{nullptr}; CollapsibleWidget* mStripeSection{nullptr}; }; diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/emitterWidget.cpp index 090c589..337d19f 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/emitterWidget.cpp @@ -45,73 +45,96 @@ EmitterWidget::EmitterWidget(QWidget* parent) : mCombinerProperties = new CombinerPropertiesWidget(this); mStripeEditorWidget = new StripeEditorWidget(this); - mStackedWidget = new QStackedWidget(this); + mChildEditorWidget = new ChildEditorWidget(this); + mFluctuationEditorWidget = new FluctuationEditorWidget(this); + mFieldEditorWidget = new FieldEditorWidget(this); - // Standard Widget - auto* standardWidget = new QWidget(this); - auto* mainLayout = new QVBoxLayout(standardWidget); - standardWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); - standardWidget->setMinimumWidth(400); + mTabWidget = new QTabWidget(this); + mTabWidget->setTabPosition(QTabWidget::West); - auto* scrollArea = new QScrollArea(this); - scrollArea->setWidget(standardWidget); - scrollArea->setWidgetResizable(true); - scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - scrollArea->setFrameShape(QFrame::NoFrame); + auto* layout = new QVBoxLayout(this); + layout->addWidget(mTabWidget); + layout->setContentsMargins(0, 0, 0, 0); - mStackedWidget->addWidget(scrollArea); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - // Child editor - mChildEditorWidget = new ChildEditorWidget(this); - mStackedWidget->addWidget(mChildEditorWidget); + setupConnections(); +} - // Fluctuation editor - mFluctuationEditorWidget = new FluctuationEditorWidget(this); - mStackedWidget->addWidget(mFluctuationEditorWidget); +void EmitterWidget::rebuildTabs() { + if (!mSelection) { + return; + } - // Field editor - mFieldEditorWidget = new FieldEditorWidget(this); - mStackedWidget->addWidget(mFieldEditorWidget); + mTabWidget->clear(); + + switch (mSelection->type()) { + case Ptcl::Selection::Type::Emitter: + buildEmitterTabs(); + break; + case Ptcl::Selection::Type::EmitterChild: + buildChildTabs(); + break; + case Ptcl::Selection::Type::EmitterFlux: + buildFluxTabs(); + break; + case Ptcl::Selection::Type::EmitterField: + buildFieldTabs(); + break; + default: + break; + } +} - mStackedWidget->setCurrentIndex(0); +QWidget* EmitterWidget::wrapInScroll(QWidget* widget) { + auto* container = new QWidget; + auto* layout = new QVBoxLayout(container); + layout->addWidget(widget); + layout->addStretch(); - auto* outerLayout = new QVBoxLayout(this); - outerLayout->addWidget(mStackedWidget); - outerLayout->setContentsMargins(0, 0, 0, 0); - setLayout(outerLayout); + auto* scroll = new QScrollArea; + scroll->setWidget(container); + scroll->setWidgetResizable(true); + scroll->setFrameShape(QFrame::NoFrame); + scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + return scroll; +} - setupStandardLayout(mainLayout); - setupConnections(); +void EmitterWidget::buildEmitterTabs() { + mTabWidget->addTab(wrapInScroll(mBasicProperties), "General"); + + if (mEmitter->hasStripeData()) { + mTabWidget->addTab(wrapInScroll(mStripeEditorWidget), "Stripe"); + } + + mTabWidget->addTab(wrapInScroll(mLifespanProperties), "Life"); + mTabWidget->addTab(wrapInScroll(mTerminationProperties), "Termination"); + mTabWidget->addTab(wrapInScroll(mGravityProperties), "Gravity"); + mTabWidget->addTab(wrapInScroll(mEmissionProperties), "Emission"); + mTabWidget->addTab(wrapInScroll(mVolumeProperties), "Volume"); + mTabWidget->addTab(wrapInScroll(mVelocityProperties), "Velocity"); + mTabWidget->addTab(wrapInScroll(mTextureProperties), "Texture"); + mTabWidget->addTab(wrapInScroll(mColorProperties), "Color"); + mTabWidget->addTab(wrapInScroll(mCombinerProperties), "Combiner"); + mTabWidget->addTab(wrapInScroll(mAlphaProperties), "Alpha"); + mTabWidget->addTab(wrapInScroll(mTransformProperties), "Transform"); + mTabWidget->addTab(wrapInScroll(mRotationProperties), "Rotation"); + mTabWidget->addTab(wrapInScroll(mScaleProperties), "Scale"); +} + +void EmitterWidget::buildChildTabs() { + mTabWidget->addTab(wrapInScroll(mChildEditorWidget), "Child"); + // TODO +} + +void EmitterWidget::buildFluxTabs() { + mTabWidget->addTab(wrapInScroll(mFluctuationEditorWidget), "Fluctuation"); } -void EmitterWidget::setupStandardLayout(QVBoxLayout* mainLayout) { - auto addSection = [mainLayout](const QString& title, QWidget* widget) { - auto* section = new CollapsibleWidget(title); - section->setContent(widget); - mainLayout->addWidget(section); - return section; - }; - - addSection("Basic properties", mBasicProperties); - mStripeSection = addSection("Stripe properties", mStripeEditorWidget); - addSection("Lifespan properties", mLifespanProperties); - addSection("Termination properties", mTerminationProperties); - addSection("Gravity properties", mGravityProperties); - addSection("Emission properties", mEmissionProperties); - addSection("Volume properties", mVolumeProperties); - addSection("Velocity properties", mVelocityProperties); - addSection("Texture properties", mTextureProperties); - addSection("Color properties", mColorProperties); - addSection("Combiner properties", mCombinerProperties); - addSection("Alpha properties", mAlphaProperties); - addSection("Transform properties", mTransformProperties); - addSection("Rotation properties", mRotationProperties); - addSection("Scale properties", mScaleProperties); - - mainLayout->addStretch(); +void EmitterWidget::buildFieldTabs() { + mTabWidget->addTab(wrapInScroll(mFieldEditorWidget), "Field"); + // TODO } void EmitterWidget::setupConnections() { @@ -167,6 +190,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { } populateProperties(); + rebuildTabs(); }); } } @@ -205,22 +229,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { mEmitter = mDocument->emitter(setIndex, emitterIndex); - switch (type) { - case Ptcl::Selection::Type::Emitter: - mStackedWidget->setCurrentIndex(0); - break; - case Ptcl::Selection::Type::EmitterChild: - mStackedWidget->setCurrentWidget(mChildEditorWidget); - break; - case Ptcl::Selection::Type::EmitterFlux: - mStackedWidget->setCurrentWidget(mFluctuationEditorWidget); - break; - case Ptcl::Selection::Type::EmitterField: - mStackedWidget->setCurrentWidget(mFieldEditorWidget); - break; - default: - break; - } + rebuildTabs(); // TODO: Have child widgets handle this themselves mChildEditorWidget->setTextureList(mTextureList); @@ -240,23 +249,8 @@ void EmitterWidget::populateProperties() { mChildEditorWidget->setParentColor0(mEmitter->primaryColor()); mFieldEditorWidget->setData(&mEmitter->fieldData(), mEmitter->complexProperties().fieldFlags); - - updateStripeVisibility(); } -void EmitterWidget::updateStripeVisibility() { - if (!mEmitter || !mStripeSection) { - return; - } - - const auto eType = mEmitter->type(); - const auto bbType = mEmitter->billboardType(); - - const bool show = (eType == Ptcl::EmitterType::Complex || eType == Ptcl::EmitterType::Compact) && - (bbType == Ptcl::BillboardType::Stripe || bbType == Ptcl::BillboardType::ComplexStripe); - - mStripeSection->setVisible(show); -} // ========================================================================== // From 16b1c107b8e7e748e36cc4df8d006fb233707fdc Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Tue, 17 Mar 2026 04:23:43 +0000 Subject: [PATCH 20/35] refactor: rename EmitterWidget -> InspectorPanel --- CMakeLists.txt | 4 +-- .../{emitterWidget.h => inspectorPanel.h} | 4 +-- include/editor/mainWindow.h | 4 +-- .../{emitterWidget.cpp => inspectorPanel.cpp} | 24 ++++++++--------- src/editor/mainWindow.cpp | 27 +++++++------------ 5 files changed, 27 insertions(+), 36 deletions(-) rename include/editor/emitterWidget/{emitterWidget.h => inspectorPanel.h} (96%) rename src/editor/emitterWidget/{emitterWidget.cpp => inspectorPanel.cpp} (93%) diff --git a/CMakeLists.txt b/CMakeLists.txt index bf14fbd..4ba82b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,7 @@ set(PROJECT_SOURCES src/editor/emitterWidget/colorPropertiesWidget.cpp src/editor/emitterWidget/combinerPropertiesWidget.cpp src/editor/emitterWidget/emissionPropertiesWidget.cpp - src/editor/emitterWidget/emitterWidget.cpp + src/editor/emitterWidget/inspectorPanel.cpp src/editor/emitterWidget/emitterWidgetBase.cpp src/editor/emitterWidget/fluctuationEditorWidget.cpp src/editor/emitterWidget/gravityPropertiesWidget.cpp @@ -125,7 +125,7 @@ set(PROJECT_HEADERS include/editor/emitterWidget/colorPropertiesWidget.h include/editor/emitterWidget/combinerPropertiesWidget.h include/editor/emitterWidget/emissionPropertiesWidget.h - include/editor/emitterWidget/emitterWidget.h + include/editor/emitterWidget/inspectorPanel.h include/editor/emitterWidget/emitterWidgetBase.h include/editor/emitterWidget/fluctuationEditorWidget.h include/editor/emitterWidget/gravityPropertiesWidget.h diff --git a/include/editor/emitterWidget/emitterWidget.h b/include/editor/emitterWidget/inspectorPanel.h similarity index 96% rename from include/editor/emitterWidget/emitterWidget.h rename to include/editor/emitterWidget/inspectorPanel.h index 2dd5435..3f3e401 100644 --- a/include/editor/emitterWidget/emitterWidget.h +++ b/include/editor/emitterWidget/inspectorPanel.h @@ -41,10 +41,10 @@ class StripeEditorWidget; // ========================================================================== // -class EmitterWidget final : public QWidget { +class InspectorPanel final : public QWidget { Q_OBJECT public: - explicit EmitterWidget(QWidget* parent = nullptr); + explicit InspectorPanel(QWidget* parent = nullptr); void setDocument(Ptcl::Document* document); void setSelection(Ptcl::Selection* selection); diff --git a/include/editor/mainWindow.h b/include/editor/mainWindow.h index 6327221..c894252 100644 --- a/include/editor/mainWindow.h +++ b/include/editor/mainWindow.h @@ -2,7 +2,7 @@ #include "ptcl/ptclDocument.h" #include "editor/emitterSetWidget.h" -#include "editor/emitterWidget/emitterWidget.h" +#include "editor/emitterWidget/inspectorPanel.h" #include "editor/ptclListWidget.h" #include "editor/textureListWidget.h" @@ -95,7 +95,7 @@ private slots: QUndoView mUndoView{}; PtclEditor::PtclList mPtclList{}; - PtclEditor::EmitterWidget mEmitterWidget{}; + PtclEditor::InspectorPanel mInspector{}; PtclEditor::EmitterSetWidget mEmitterSetWidget{}; PtclEditor::TextureListWidget mTextureWidget{}; diff --git a/src/editor/emitterWidget/emitterWidget.cpp b/src/editor/emitterWidget/inspectorPanel.cpp similarity index 93% rename from src/editor/emitterWidget/emitterWidget.cpp rename to src/editor/emitterWidget/inspectorPanel.cpp index 337d19f..494360a 100644 --- a/src/editor/emitterWidget/emitterWidget.cpp +++ b/src/editor/emitterWidget/inspectorPanel.cpp @@ -1,5 +1,5 @@ -#include "editor/emitterWidget/emitterWidget.h" +#include "editor/emitterWidget/inspectorPanel.h" #include "editor/emitterWidget/alphaPropertiesWidget.h" #include "editor/emitterWidget/basicPropertiesWidget.h" @@ -26,7 +26,7 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidget::EmitterWidget(QWidget* parent) : +InspectorPanel::InspectorPanel(QWidget* parent) : QWidget{parent} { mBasicProperties = new BasicPropertiesWidget(this); @@ -61,7 +61,7 @@ EmitterWidget::EmitterWidget(QWidget* parent) : setupConnections(); } -void EmitterWidget::rebuildTabs() { +void InspectorPanel::rebuildTabs() { if (!mSelection) { return; } @@ -86,7 +86,7 @@ void EmitterWidget::rebuildTabs() { } } -QWidget* EmitterWidget::wrapInScroll(QWidget* widget) { +QWidget* InspectorPanel::wrapInScroll(QWidget* widget) { auto* container = new QWidget; auto* layout = new QVBoxLayout(container); layout->addWidget(widget); @@ -101,7 +101,7 @@ QWidget* EmitterWidget::wrapInScroll(QWidget* widget) { return scroll; } -void EmitterWidget::buildEmitterTabs() { +void InspectorPanel::buildEmitterTabs() { mTabWidget->addTab(wrapInScroll(mBasicProperties), "General"); if (mEmitter->hasStripeData()) { @@ -123,21 +123,21 @@ void EmitterWidget::buildEmitterTabs() { mTabWidget->addTab(wrapInScroll(mScaleProperties), "Scale"); } -void EmitterWidget::buildChildTabs() { +void InspectorPanel::buildChildTabs() { mTabWidget->addTab(wrapInScroll(mChildEditorWidget), "Child"); // TODO } -void EmitterWidget::buildFluxTabs() { +void InspectorPanel::buildFluxTabs() { mTabWidget->addTab(wrapInScroll(mFluctuationEditorWidget), "Fluctuation"); } -void EmitterWidget::buildFieldTabs() { +void InspectorPanel::buildFieldTabs() { mTabWidget->addTab(wrapInScroll(mFieldEditorWidget), "Field"); // TODO } -void EmitterWidget::setupConnections() { +void InspectorPanel::setupConnections() { // Child Editor Widget connect(mChildEditorWidget, &ChildEditorWidget::flagsUpdated, this, [this](const BitFlag& childFlags) { if (!mEmitter) { return; } @@ -155,7 +155,7 @@ void EmitterWidget::setupConnections() { }); } -void EmitterWidget::setDocument(Ptcl::Document* document) { +void InspectorPanel::setDocument(Ptcl::Document* document) { if (mDocument) { mDocument->disconnect(this); } @@ -195,7 +195,7 @@ void EmitterWidget::setDocument(Ptcl::Document* document) { } } -void EmitterWidget::setSelection(Ptcl::Selection* selection) { +void InspectorPanel::setSelection(Ptcl::Selection* selection) { if (mSelection) { mSelection->disconnect(this); } @@ -240,7 +240,7 @@ void EmitterWidget::setSelection(Ptcl::Selection* selection) { } } -void EmitterWidget::populateProperties() { +void InspectorPanel::populateProperties() { QSignalBlocker b15(mChildEditorWidget); QSignalBlocker b17(mFieldEditorWidget); QSignalBlocker b18(mStripeEditorWidget); diff --git a/src/editor/mainWindow.cpp b/src/editor/mainWindow.cpp index 6593671..8f9f1b1 100644 --- a/src/editor/mainWindow.cpp +++ b/src/editor/mainWindow.cpp @@ -36,7 +36,7 @@ void MainWindow::setupUi() { // Properties mPropertiesStack = new QStackedWidget(this); mPropertiesStack->addWidget(&mEmitterSetWidget); - mPropertiesStack->addWidget(&mEmitterWidget); + mPropertiesStack->addWidget(&mInspector); mPropertiesStack->setCurrentIndex(0); mPropertiesGroup.setFlat(false); @@ -86,9 +86,9 @@ void MainWindow::setupUi() { mPtclList.setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); mPtclList.setSelection(&mSelection); - // Emitter Widget - mEmitterWidget.setEnabled(false); - mEmitterWidget.setSelection(&mSelection); + // Inspector + mInspector.setEnabled(false); + mInspector.setSelection(&mSelection); // EmitterSet Widget mEmitterSetWidget.setEnabled(false); @@ -159,20 +159,11 @@ void MainWindow::setupConnections() { setDirty(true); }); - connect(&mEmitterWidget, &EmitterWidget::emitterNameChanged, this, [this]() { - mPtclList.updateEmitterName(mSelection.emitterSetIndex(), mSelection.emitterIndex()); - updatePropertiesStatus(); - }); - - connect(&mEmitterWidget, &EmitterWidget::emitterTypeChanged, this, [this]() { - mPtclList.updateEmitter(mSelection.emitterSetIndex(), mSelection.emitterIndex()); - }); - - connect(&mEmitterWidget, &EmitterWidget::complexFlagsChanged, this, [this]() { + connect(&mInspector, &InspectorPanel::complexFlagsChanged, this, [this]() { mPtclList.updateEmitter(mSelection.emitterSetIndex(), mSelection.emitterIndex()); }); - connect(&mEmitterWidget, &EmitterWidget::propertiesChanged, this, [this]() { + connect(&mInspector, &InspectorPanel::propertiesChanged, this, [this]() { setDirty(true); }); @@ -420,7 +411,7 @@ void MainWindow::updateRecentFileList() { void MainWindow::loadPtclRes(const QString& path) { mPtclList.setDocument(nullptr); - mEmitterWidget.setDocument(nullptr); + mInspector.setDocument(nullptr); mEmitterSetWidget.setDocument(nullptr); mTextureWidget.setDocument(nullptr); @@ -442,7 +433,7 @@ void MainWindow::loadPtclRes(const QString& path) { updateRecentFileList(); mPtclList.setDocument(mDocument.get()); - mEmitterWidget.setDocument(mDocument.get()); + mInspector.setDocument(mDocument.get()); mEmitterSetWidget.setDocument(mDocument.get()); mTextureWidget.setDocument(mDocument.get()); @@ -475,7 +466,7 @@ void MainWindow::setPropertiesView(PropertiesView view) { case PropertiesView::EmitterChild: case PropertiesView::EmitterFlux: case PropertiesView::EmitterField: - mPropertiesStack->setCurrentWidget(&mEmitterWidget); + mPropertiesStack->setCurrentWidget(&mInspector); break; } } From 0514c4e7e667eb255f0e6a64706e0cc670a95800 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Tue, 17 Mar 2026 04:37:08 +0000 Subject: [PATCH 21/35] refactor: change /emitterWidget to /inspector --- CMakeLists.txt | 72 +++++++++---------- .../alphaPropertiesWidget.h | 4 +- .../basicPropertiesWidget.h | 4 +- .../colorPropertiesWidget.h | 4 +- .../combinerPropertiesWidget.h | 4 +- .../emissionPropertiesWidget.h | 4 +- .../fluctuationEditorWidget.h | 4 +- .../gravityPropertiesWidget.h | 4 +- .../inspectorPanel.h | 0 .../inspectorWidgetBase.h} | 8 +-- .../lifespanPropertiesWidget.h | 4 +- .../rotationPropertiesWidget.h | 4 +- .../scalePropertiesWidget.h | 4 +- .../stripeEditorWidget.h | 4 +- .../terminationPropertiesWidget.h | 4 +- .../texturePropertiesWidget.h | 4 +- .../transformPropertiesWidget.h | 4 +- .../velocityPropertiesWidget.h | 4 +- .../volumePropertiesWidget.h | 4 +- include/editor/mainWindow.h | 2 +- .../alphaPropertiesWidget.cpp | 4 +- .../basicPropertiesWidget.cpp | 6 +- .../colorPropertiesWidget.cpp | 6 +- .../combinerPropertiesWidget.cpp | 4 +- .../emissionPropertiesWidget.cpp | 4 +- .../fluctuationEditorWidget.cpp | 4 +- .../gravityPropertiesWidget.cpp | 4 +- .../inspectorPanel.cpp | 36 +++++----- .../inspectorWidgetBase.cpp} | 14 ++-- .../lifespanPropertiesWidget.cpp | 4 +- .../rotationPropertiesWidget.cpp | 4 +- .../scalePropertiesWidget.cpp | 4 +- .../stripeEditorWidget.cpp | 4 +- .../terminationPropertiesWidget.cpp | 4 +- .../texturePropertiesWidget.cpp | 4 +- .../transformPropertiesWidget.cpp | 4 +- .../velocityPropertiesWidget.cpp | 4 +- .../volumePropertiesWidget.cpp | 4 +- 38 files changed, 132 insertions(+), 132 deletions(-) rename include/editor/{emitterWidget => inspector}/alphaPropertiesWidget.h (82%) rename include/editor/{emitterWidget => inspector}/basicPropertiesWidget.h (94%) rename include/editor/{emitterWidget => inspector}/colorPropertiesWidget.h (94%) rename include/editor/{emitterWidget => inspector}/combinerPropertiesWidget.h (87%) rename include/editor/{emitterWidget => inspector}/emissionPropertiesWidget.h (86%) rename include/editor/{emitterWidget => inspector}/fluctuationEditorWidget.h (86%) rename include/editor/{emitterWidget => inspector}/gravityPropertiesWidget.h (83%) rename include/editor/{emitterWidget => inspector}/inspectorPanel.h (100%) rename include/editor/{emitterWidget/emitterWidgetBase.h => inspector/inspectorWidgetBase.h} (80%) rename include/editor/{emitterWidget => inspector}/lifespanPropertiesWidget.h (85%) rename include/editor/{emitterWidget => inspector}/rotationPropertiesWidget.h (88%) rename include/editor/{emitterWidget => inspector}/scalePropertiesWidget.h (85%) rename include/editor/{emitterWidget => inspector}/stripeEditorWidget.h (88%) rename include/editor/{emitterWidget => inspector}/terminationPropertiesWidget.h (82%) rename include/editor/{emitterWidget => inspector}/texturePropertiesWidget.h (94%) rename include/editor/{emitterWidget => inspector}/transformPropertiesWidget.h (83%) rename include/editor/{emitterWidget => inspector}/velocityPropertiesWidget.h (87%) rename include/editor/{emitterWidget => inspector}/volumePropertiesWidget.h (91%) rename src/editor/{emitterWidget => inspector}/alphaPropertiesWidget.cpp (97%) rename src/editor/{emitterWidget => inspector}/basicPropertiesWidget.cpp (98%) rename src/editor/{emitterWidget => inspector}/colorPropertiesWidget.cpp (98%) rename src/editor/{emitterWidget => inspector}/combinerPropertiesWidget.cpp (96%) rename src/editor/{emitterWidget => inspector}/emissionPropertiesWidget.cpp (97%) rename src/editor/{emitterWidget => inspector}/fluctuationEditorWidget.cpp (97%) rename src/editor/{emitterWidget => inspector}/gravityPropertiesWidget.cpp (94%) rename src/editor/{emitterWidget => inspector}/inspectorPanel.cpp (89%) rename src/editor/{emitterWidget/emitterWidgetBase.cpp => inspector/inspectorWidgetBase.cpp} (75%) rename src/editor/{emitterWidget => inspector}/lifespanPropertiesWidget.cpp (96%) rename src/editor/{emitterWidget => inspector}/rotationPropertiesWidget.cpp (98%) rename src/editor/{emitterWidget => inspector}/scalePropertiesWidget.cpp (98%) rename src/editor/{emitterWidget => inspector}/stripeEditorWidget.cpp (98%) rename src/editor/{emitterWidget => inspector}/terminationPropertiesWidget.cpp (94%) rename src/editor/{emitterWidget => inspector}/texturePropertiesWidget.cpp (99%) rename src/editor/{emitterWidget => inspector}/transformPropertiesWidget.cpp (95%) rename src/editor/{emitterWidget => inspector}/velocityPropertiesWidget.cpp (97%) rename src/editor/{emitterWidget => inspector}/volumePropertiesWidget.cpp (98%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ba82b3..12392e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,24 +48,24 @@ set(PROJECT_SOURCES src/editor/childEditor/scalePropertiesWidget.cpp src/editor/childEditor/texturePropertiesWidget.cpp src/editor/childEditor/velocityPropertiesWidget.cpp - src/editor/emitterWidget/alphaPropertiesWidget.cpp - src/editor/emitterWidget/basicPropertiesWidget.cpp - src/editor/emitterWidget/colorPropertiesWidget.cpp - src/editor/emitterWidget/combinerPropertiesWidget.cpp - src/editor/emitterWidget/emissionPropertiesWidget.cpp - src/editor/emitterWidget/inspectorPanel.cpp - src/editor/emitterWidget/emitterWidgetBase.cpp - src/editor/emitterWidget/fluctuationEditorWidget.cpp - src/editor/emitterWidget/gravityPropertiesWidget.cpp - src/editor/emitterWidget/lifespanPropertiesWidget.cpp - src/editor/emitterWidget/rotationPropertiesWidget.cpp - src/editor/emitterWidget/scalePropertiesWidget.cpp - src/editor/emitterWidget/stripeEditorWidget.cpp - src/editor/emitterWidget/terminationPropertiesWidget.cpp - src/editor/emitterWidget/texturePropertiesWidget.cpp - src/editor/emitterWidget/transformPropertiesWidget.cpp - src/editor/emitterWidget/velocityPropertiesWidget.cpp - src/editor/emitterWidget/volumePropertiesWidget.cpp + src/editor/inspector/alphaPropertiesWidget.cpp + src/editor/inspector/basicPropertiesWidget.cpp + src/editor/inspector/colorPropertiesWidget.cpp + src/editor/inspector/combinerPropertiesWidget.cpp + src/editor/inspector/emissionPropertiesWidget.cpp + src/editor/inspector/inspectorPanel.cpp + src/editor/inspector/inspectorWidgetBase.cpp + src/editor/inspector/fluctuationEditorWidget.cpp + src/editor/inspector/gravityPropertiesWidget.cpp + src/editor/inspector/lifespanPropertiesWidget.cpp + src/editor/inspector/rotationPropertiesWidget.cpp + src/editor/inspector/scalePropertiesWidget.cpp + src/editor/inspector/stripeEditorWidget.cpp + src/editor/inspector/terminationPropertiesWidget.cpp + src/editor/inspector/texturePropertiesWidget.cpp + src/editor/inspector/transformPropertiesWidget.cpp + src/editor/inspector/velocityPropertiesWidget.cpp + src/editor/inspector/volumePropertiesWidget.cpp src/editor/fieldEditor/collisionDataWidget.cpp src/editor/fieldEditor/convergenceDataWidget.cpp src/editor/fieldEditor/fieldEditorWidget.cpp @@ -120,24 +120,24 @@ set(PROJECT_HEADERS include/editor/childEditor/scalePropertiesWidget.h include/editor/childEditor/texturePropertiesWidget.h include/editor/childEditor/velocityPropertiesWidget.h - include/editor/emitterWidget/alphaPropertiesWidget.h - include/editor/emitterWidget/basicPropertiesWidget.h - include/editor/emitterWidget/colorPropertiesWidget.h - include/editor/emitterWidget/combinerPropertiesWidget.h - include/editor/emitterWidget/emissionPropertiesWidget.h - include/editor/emitterWidget/inspectorPanel.h - include/editor/emitterWidget/emitterWidgetBase.h - include/editor/emitterWidget/fluctuationEditorWidget.h - include/editor/emitterWidget/gravityPropertiesWidget.h - include/editor/emitterWidget/lifespanPropertiesWidget.h - include/editor/emitterWidget/rotationPropertiesWidget.h - include/editor/emitterWidget/scalePropertiesWidget.h - include/editor/emitterWidget/stripeEditorWidget.h - include/editor/emitterWidget/terminationPropertiesWidget.h - include/editor/emitterWidget/texturePropertiesWidget.h - include/editor/emitterWidget/transformPropertiesWidget.h - include/editor/emitterWidget/velocityPropertiesWidget.h - include/editor/emitterWidget/volumePropertiesWidget.h + include/editor/inspector/alphaPropertiesWidget.h + include/editor/inspector/basicPropertiesWidget.h + include/editor/inspector/colorPropertiesWidget.h + include/editor/inspector/combinerPropertiesWidget.h + include/editor/inspector/emissionPropertiesWidget.h + include/editor/inspector/inspectorPanel.h + include/editor/inspector/inspectorWidgetBase.h + include/editor/inspector/fluctuationEditorWidget.h + include/editor/inspector/gravityPropertiesWidget.h + include/editor/inspector/lifespanPropertiesWidget.h + include/editor/inspector/rotationPropertiesWidget.h + include/editor/inspector/scalePropertiesWidget.h + include/editor/inspector/stripeEditorWidget.h + include/editor/inspector/terminationPropertiesWidget.h + include/editor/inspector/texturePropertiesWidget.h + include/editor/inspector/transformPropertiesWidget.h + include/editor/inspector/velocityPropertiesWidget.h + include/editor/inspector/volumePropertiesWidget.h include/editor/fieldEditor/collisionDataWidget.h include/editor/fieldEditor/convergenceDataWidget.h include/editor/fieldEditor/fieldEditorWidget.h diff --git a/include/editor/emitterWidget/alphaPropertiesWidget.h b/include/editor/inspector/alphaPropertiesWidget.h similarity index 82% rename from include/editor/emitterWidget/alphaPropertiesWidget.h rename to include/editor/inspector/alphaPropertiesWidget.h index 9443949..5cc495f 100644 --- a/include/editor/emitterWidget/alphaPropertiesWidget.h +++ b/include/editor/inspector/alphaPropertiesWidget.h @@ -1,7 +1,7 @@ #pragma once #include "editor/components/animGraph.h" -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include @@ -12,7 +12,7 @@ namespace PtclEditor { // ========================================================================== // -class AlphaPropertiesWidget final : public EmitterWidgetBase { +class AlphaPropertiesWidget final : public InspectorWidgetBase { Q_OBJECT public: explicit AlphaPropertiesWidget(QWidget* parent = nullptr); diff --git a/include/editor/emitterWidget/basicPropertiesWidget.h b/include/editor/inspector/basicPropertiesWidget.h similarity index 94% rename from include/editor/emitterWidget/basicPropertiesWidget.h rename to include/editor/inspector/basicPropertiesWidget.h index ca0411a..8ed17ac 100644 --- a/include/editor/emitterWidget/basicPropertiesWidget.h +++ b/include/editor/inspector/basicPropertiesWidget.h @@ -2,7 +2,7 @@ #include "editor/components/enumComboBox.h" #include "editor/components/sizedSpinBox.h" -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -16,7 +16,7 @@ namespace PtclEditor { // ========================================================================== // -class BasicPropertiesWidget final : public EmitterWidgetBase { +class BasicPropertiesWidget final : public InspectorWidgetBase { Q_OBJECT private: static constexpr std::array sBillboardTypes{ diff --git a/include/editor/emitterWidget/colorPropertiesWidget.h b/include/editor/inspector/colorPropertiesWidget.h similarity index 94% rename from include/editor/emitterWidget/colorPropertiesWidget.h rename to include/editor/inspector/colorPropertiesWidget.h index 70a877c..c2bc6ff 100644 --- a/include/editor/emitterWidget/colorPropertiesWidget.h +++ b/include/editor/inspector/colorPropertiesWidget.h @@ -4,7 +4,7 @@ #include "editor/components/enumComboBox.h" #include "editor/components/rgbaColorWidget.h" #include "editor/components/sizedSpinBox.h" -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -34,7 +34,7 @@ static s32 behaviorToIndex(Behavior behavior) { // ========================================================================== // -class ColorPropertiesWidget final : public EmitterWidgetBase { +class ColorPropertiesWidget final : public InspectorWidgetBase { Q_OBJECT public: explicit ColorPropertiesWidget(QWidget* parent = nullptr); diff --git a/include/editor/emitterWidget/combinerPropertiesWidget.h b/include/editor/inspector/combinerPropertiesWidget.h similarity index 87% rename from include/editor/emitterWidget/combinerPropertiesWidget.h rename to include/editor/inspector/combinerPropertiesWidget.h index 60ab67a..cbb8cf4 100644 --- a/include/editor/emitterWidget/combinerPropertiesWidget.h +++ b/include/editor/inspector/combinerPropertiesWidget.h @@ -2,7 +2,7 @@ #include "editor/components/combinerPreviewWidget.h" #include "editor/components/enumComboBox.h" -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -14,7 +14,7 @@ namespace PtclEditor { // ========================================================================== // -class CombinerPropertiesWidget final : public EmitterWidgetBase { +class CombinerPropertiesWidget final : public InspectorWidgetBase { Q_OBJECT public: explicit CombinerPropertiesWidget(QWidget* parent = nullptr); diff --git a/include/editor/emitterWidget/emissionPropertiesWidget.h b/include/editor/inspector/emissionPropertiesWidget.h similarity index 86% rename from include/editor/emitterWidget/emissionPropertiesWidget.h rename to include/editor/inspector/emissionPropertiesWidget.h index a9d056f..8d635e9 100644 --- a/include/editor/emitterWidget/emissionPropertiesWidget.h +++ b/include/editor/inspector/emissionPropertiesWidget.h @@ -1,6 +1,6 @@ #pragma once -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -14,7 +14,7 @@ namespace PtclEditor { // ========================================================================== // -class EmissionPropertiesWidget final : public EmitterWidgetBase { +class EmissionPropertiesWidget final : public InspectorWidgetBase { Q_OBJECT public: explicit EmissionPropertiesWidget(QWidget* parent = nullptr); diff --git a/include/editor/emitterWidget/fluctuationEditorWidget.h b/include/editor/inspector/fluctuationEditorWidget.h similarity index 86% rename from include/editor/emitterWidget/fluctuationEditorWidget.h rename to include/editor/inspector/fluctuationEditorWidget.h index d684025..a5e5155 100644 --- a/include/editor/emitterWidget/fluctuationEditorWidget.h +++ b/include/editor/inspector/fluctuationEditorWidget.h @@ -1,6 +1,6 @@ #pragma once -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -14,7 +14,7 @@ namespace PtclEditor { // ========================================================================== // -class FluctuationEditorWidget final : public EmitterWidgetBase { +class FluctuationEditorWidget final : public InspectorWidgetBase { Q_OBJECT public: explicit FluctuationEditorWidget(QWidget* parent = nullptr); diff --git a/include/editor/emitterWidget/gravityPropertiesWidget.h b/include/editor/inspector/gravityPropertiesWidget.h similarity index 83% rename from include/editor/emitterWidget/gravityPropertiesWidget.h rename to include/editor/inspector/gravityPropertiesWidget.h index abbf4c4..9167c72 100644 --- a/include/editor/emitterWidget/gravityPropertiesWidget.h +++ b/include/editor/inspector/gravityPropertiesWidget.h @@ -1,7 +1,7 @@ #pragma once #include "editor/components/vectorSpinBox.h" -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -13,7 +13,7 @@ namespace PtclEditor { // ========================================================================== // -class GravityPropertiesWidget final : public EmitterWidgetBase { +class GravityPropertiesWidget final : public InspectorWidgetBase { Q_OBJECT public: explicit GravityPropertiesWidget(QWidget* parent = nullptr); diff --git a/include/editor/emitterWidget/inspectorPanel.h b/include/editor/inspector/inspectorPanel.h similarity index 100% rename from include/editor/emitterWidget/inspectorPanel.h rename to include/editor/inspector/inspectorPanel.h diff --git a/include/editor/emitterWidget/emitterWidgetBase.h b/include/editor/inspector/inspectorWidgetBase.h similarity index 80% rename from include/editor/emitterWidget/emitterWidgetBase.h rename to include/editor/inspector/inspectorWidgetBase.h index 17f8aab..b0e4c91 100644 --- a/include/editor/emitterWidget/emitterWidgetBase.h +++ b/include/editor/inspector/inspectorWidgetBase.h @@ -13,10 +13,10 @@ namespace PtclEditor { // ========================================================================== // -class EmitterWidgetBase : public QWidget { +class InspectorWidgetBase : public QWidget { Q_OBJECT public: - explicit EmitterWidgetBase(QWidget* parent = nullptr); + explicit InspectorWidgetBase(QWidget* parent = nullptr); virtual void setDocument(Ptcl::Document* document); virtual void setSelection(Ptcl::Selection* selection); @@ -24,7 +24,7 @@ class EmitterWidgetBase : public QWidget { protected: virtual void populateProperties() = 0; - QString formatLabel(const QString& label) const; + QString formatHistoryLabel(const QString& label) const; template void setEmitterProperty(const QString& label, QString key, Getter getter, Setter setter, const T& value) { @@ -32,7 +32,7 @@ class EmitterWidgetBase : public QWidget { return; } - mDocument->setEmitterProperty(mSelection->emitterSetIndex(), mSelection->emitterIndex(), formatLabel(label), key, getter, setter, value); + mDocument->setEmitterProperty(mSelection->emitterSetIndex(), mSelection->emitterIndex(), formatHistoryLabel(label), key, getter, setter, value); } protected slots: diff --git a/include/editor/emitterWidget/lifespanPropertiesWidget.h b/include/editor/inspector/lifespanPropertiesWidget.h similarity index 85% rename from include/editor/emitterWidget/lifespanPropertiesWidget.h rename to include/editor/inspector/lifespanPropertiesWidget.h index c41703f..b9041cb 100644 --- a/include/editor/emitterWidget/lifespanPropertiesWidget.h +++ b/include/editor/inspector/lifespanPropertiesWidget.h @@ -1,6 +1,6 @@ #pragma once -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -14,7 +14,7 @@ namespace PtclEditor { // ========================================================================== // -class LifespanPropertiesWidget final : public EmitterWidgetBase { +class LifespanPropertiesWidget final : public InspectorWidgetBase { Q_OBJECT public: explicit LifespanPropertiesWidget(QWidget* parent = nullptr); diff --git a/include/editor/emitterWidget/rotationPropertiesWidget.h b/include/editor/inspector/rotationPropertiesWidget.h similarity index 88% rename from include/editor/emitterWidget/rotationPropertiesWidget.h rename to include/editor/inspector/rotationPropertiesWidget.h index 937d110..49cab26 100644 --- a/include/editor/emitterWidget/rotationPropertiesWidget.h +++ b/include/editor/inspector/rotationPropertiesWidget.h @@ -2,7 +2,7 @@ #include "editor/components/enumComboBox.h" #include "editor/components/vectorSpinBox.h" -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -15,7 +15,7 @@ namespace PtclEditor { // ========================================================================== // -class RotationPropertiesWidget final : public EmitterWidgetBase { +class RotationPropertiesWidget final : public InspectorWidgetBase { Q_OBJECT public: explicit RotationPropertiesWidget(QWidget* parent = nullptr); diff --git a/include/editor/emitterWidget/scalePropertiesWidget.h b/include/editor/inspector/scalePropertiesWidget.h similarity index 85% rename from include/editor/emitterWidget/scalePropertiesWidget.h rename to include/editor/inspector/scalePropertiesWidget.h index 9ee3d82..a1f57df 100644 --- a/include/editor/emitterWidget/scalePropertiesWidget.h +++ b/include/editor/inspector/scalePropertiesWidget.h @@ -1,7 +1,7 @@ #pragma once #include "editor/components/animGraph.h" -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include @@ -12,7 +12,7 @@ namespace PtclEditor { // ========================================================================== // -class ScalePropertiesWidget final : public EmitterWidgetBase { +class ScalePropertiesWidget final : public InspectorWidgetBase { Q_OBJECT public: explicit ScalePropertiesWidget(QWidget* parent = nullptr); diff --git a/include/editor/emitterWidget/stripeEditorWidget.h b/include/editor/inspector/stripeEditorWidget.h similarity index 88% rename from include/editor/emitterWidget/stripeEditorWidget.h rename to include/editor/inspector/stripeEditorWidget.h index b3cf3a1..913f4e9 100644 --- a/include/editor/emitterWidget/stripeEditorWidget.h +++ b/include/editor/inspector/stripeEditorWidget.h @@ -1,7 +1,7 @@ #pragma once #include "editor/components/vectorSpinBox.h" -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -16,7 +16,7 @@ namespace PtclEditor { // ========================================================================== // -class StripeEditorWidget final : public EmitterWidgetBase { +class StripeEditorWidget final : public InspectorWidgetBase { Q_OBJECT public: explicit StripeEditorWidget(QWidget* parent = nullptr); diff --git a/include/editor/emitterWidget/terminationPropertiesWidget.h b/include/editor/inspector/terminationPropertiesWidget.h similarity index 82% rename from include/editor/emitterWidget/terminationPropertiesWidget.h rename to include/editor/inspector/terminationPropertiesWidget.h index b08ba8c..ce282a6 100644 --- a/include/editor/emitterWidget/terminationPropertiesWidget.h +++ b/include/editor/inspector/terminationPropertiesWidget.h @@ -1,6 +1,6 @@ #pragma once -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -14,7 +14,7 @@ namespace PtclEditor { // ========================================================================== // -class TerminationPropertiesWidget final : public EmitterWidgetBase { +class TerminationPropertiesWidget final : public InspectorWidgetBase { Q_OBJECT public: explicit TerminationPropertiesWidget(QWidget* parent = nullptr); diff --git a/include/editor/emitterWidget/texturePropertiesWidget.h b/include/editor/inspector/texturePropertiesWidget.h similarity index 94% rename from include/editor/emitterWidget/texturePropertiesWidget.h rename to include/editor/inspector/texturePropertiesWidget.h index dd3f362..a757253 100644 --- a/include/editor/emitterWidget/texturePropertiesWidget.h +++ b/include/editor/inspector/texturePropertiesWidget.h @@ -3,7 +3,7 @@ #include "editor/components/enumComboBox.h" #include "editor/components/sizedSpinBox.h" #include "editor/components/thumbnailWidget.h" -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -17,7 +17,7 @@ namespace PtclEditor { // ========================================================================== // -class TexturePropertiesWidget final : public EmitterWidgetBase { +class TexturePropertiesWidget final : public InspectorWidgetBase { Q_OBJECT public: enum class AnimMode { diff --git a/include/editor/emitterWidget/transformPropertiesWidget.h b/include/editor/inspector/transformPropertiesWidget.h similarity index 83% rename from include/editor/emitterWidget/transformPropertiesWidget.h rename to include/editor/inspector/transformPropertiesWidget.h index a6cf6a8..067adc6 100644 --- a/include/editor/emitterWidget/transformPropertiesWidget.h +++ b/include/editor/inspector/transformPropertiesWidget.h @@ -1,7 +1,7 @@ #pragma once #include "editor/components/vectorSpinBox.h" -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" namespace PtclEditor { @@ -10,7 +10,7 @@ namespace PtclEditor { // ========================================================================== // -class TransformPropertiesWidget final : public EmitterWidgetBase { +class TransformPropertiesWidget final : public InspectorWidgetBase { Q_OBJECT public: explicit TransformPropertiesWidget(QWidget* parent = nullptr); diff --git a/include/editor/emitterWidget/velocityPropertiesWidget.h b/include/editor/inspector/velocityPropertiesWidget.h similarity index 87% rename from include/editor/emitterWidget/velocityPropertiesWidget.h rename to include/editor/inspector/velocityPropertiesWidget.h index d32a7f7..f16077c 100644 --- a/include/editor/emitterWidget/velocityPropertiesWidget.h +++ b/include/editor/inspector/velocityPropertiesWidget.h @@ -1,7 +1,7 @@ #pragma once #include "editor/components/vectorSpinBox.h" -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -14,7 +14,7 @@ namespace PtclEditor { // ========================================================================== // -class VelocityPropertiesWidget final : public EmitterWidgetBase { +class VelocityPropertiesWidget final : public InspectorWidgetBase { Q_OBJECT public: explicit VelocityPropertiesWidget(QWidget* parent = nullptr); diff --git a/include/editor/emitterWidget/volumePropertiesWidget.h b/include/editor/inspector/volumePropertiesWidget.h similarity index 91% rename from include/editor/emitterWidget/volumePropertiesWidget.h rename to include/editor/inspector/volumePropertiesWidget.h index 288fa5b..3e56df6 100644 --- a/include/editor/emitterWidget/volumePropertiesWidget.h +++ b/include/editor/inspector/volumePropertiesWidget.h @@ -2,7 +2,7 @@ #include "editor/components/enumComboBox.h" #include "editor/components/vectorSpinBox.h" -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -15,7 +15,7 @@ namespace PtclEditor { // ========================================================================== // -class VolumePropertiesWidget : public EmitterWidgetBase { +class VolumePropertiesWidget final : public InspectorWidgetBase { Q_OBJECT public: explicit VolumePropertiesWidget(QWidget* parent = nullptr); diff --git a/include/editor/mainWindow.h b/include/editor/mainWindow.h index c894252..b9f64f4 100644 --- a/include/editor/mainWindow.h +++ b/include/editor/mainWindow.h @@ -2,7 +2,7 @@ #include "ptcl/ptclDocument.h" #include "editor/emitterSetWidget.h" -#include "editor/emitterWidget/inspectorPanel.h" +#include "editor/inspector/inspectorPanel.h" #include "editor/ptclListWidget.h" #include "editor/textureListWidget.h" diff --git a/src/editor/emitterWidget/alphaPropertiesWidget.cpp b/src/editor/inspector/alphaPropertiesWidget.cpp similarity index 97% rename from src/editor/emitterWidget/alphaPropertiesWidget.cpp rename to src/editor/inspector/alphaPropertiesWidget.cpp index 43b7272..22a225f 100644 --- a/src/editor/emitterWidget/alphaPropertiesWidget.cpp +++ b/src/editor/inspector/alphaPropertiesWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/alphaPropertiesWidget.h" +#include "editor/inspector/alphaPropertiesWidget.h" #include @@ -10,7 +10,7 @@ namespace PtclEditor { AlphaPropertiesWidget::AlphaPropertiesWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { mGraphA.setLineColor(QColor{20, 40, 60}); mGraphA.setTickStepSize(0.1f); diff --git a/src/editor/emitterWidget/basicPropertiesWidget.cpp b/src/editor/inspector/basicPropertiesWidget.cpp similarity index 98% rename from src/editor/emitterWidget/basicPropertiesWidget.cpp rename to src/editor/inspector/basicPropertiesWidget.cpp index 834bbd9..1168b5a 100644 --- a/src/editor/emitterWidget/basicPropertiesWidget.cpp +++ b/src/editor/inspector/basicPropertiesWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/basicPropertiesWidget.h" +#include "editor/inspector/basicPropertiesWidget.h" #include "util/nameValidator.h" #include @@ -10,7 +10,7 @@ namespace PtclEditor { BasicPropertiesWidget::BasicPropertiesWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { for (Ptcl::BillboardType type : sBillboardTypes) { mBillboardComboBox.addItem(Ptcl::toString(type), QVariant::fromValue(type)); @@ -72,7 +72,7 @@ void BasicPropertiesWidget::setupConnections() { // Emitter Type connect(&mTypeComboBox, &QComboBox::currentIndexChanged, this, [this]() { auto* stack = mDocument->undoStack(); - stack->beginMacro(formatLabel("Set Type")); + stack->beginMacro(formatHistoryLabel("Set Type")); setEmitterProperty( "Set Type", diff --git a/src/editor/emitterWidget/colorPropertiesWidget.cpp b/src/editor/inspector/colorPropertiesWidget.cpp similarity index 98% rename from src/editor/emitterWidget/colorPropertiesWidget.cpp rename to src/editor/inspector/colorPropertiesWidget.cpp index fe7a00e..a8dd953 100644 --- a/src/editor/emitterWidget/colorPropertiesWidget.cpp +++ b/src/editor/inspector/colorPropertiesWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/colorPropertiesWidget.h" +#include "editor/inspector/colorPropertiesWidget.h" #include #include @@ -11,7 +11,7 @@ namespace PtclEditor { ColorPropertiesWidget::ColorPropertiesWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { // Color Behavior Type auto colorBehaviorLayout = new QHBoxLayout; mColorBehavior.addItem("Constant", QVariant::fromValue(Behavior::Constant)); @@ -305,7 +305,7 @@ void ColorPropertiesWidget::handleBehaviorChanged(s32 index) { break; } - mDocument->undoStack()->beginMacro(formatLabel(label)); + mDocument->undoStack()->beginMacro(formatHistoryLabel(label)); setEmitterProperty( "Set Color Random", diff --git a/src/editor/emitterWidget/combinerPropertiesWidget.cpp b/src/editor/inspector/combinerPropertiesWidget.cpp similarity index 96% rename from src/editor/emitterWidget/combinerPropertiesWidget.cpp rename to src/editor/inspector/combinerPropertiesWidget.cpp index 09d353a..55fc0fc 100644 --- a/src/editor/emitterWidget/combinerPropertiesWidget.cpp +++ b/src/editor/inspector/combinerPropertiesWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/combinerPropertiesWidget.h" +#include "editor/inspector/combinerPropertiesWidget.h" #include @@ -9,7 +9,7 @@ namespace PtclEditor { CombinerPropertiesWidget::CombinerPropertiesWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); diff --git a/src/editor/emitterWidget/emissionPropertiesWidget.cpp b/src/editor/inspector/emissionPropertiesWidget.cpp similarity index 97% rename from src/editor/emitterWidget/emissionPropertiesWidget.cpp rename to src/editor/inspector/emissionPropertiesWidget.cpp index 608c97e..980f1e3 100644 --- a/src/editor/emitterWidget/emissionPropertiesWidget.cpp +++ b/src/editor/inspector/emissionPropertiesWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/emissionPropertiesWidget.h" +#include "editor/inspector/emissionPropertiesWidget.h" #include #include @@ -11,7 +11,7 @@ namespace PtclEditor { EmissionPropertiesWidget::EmissionPropertiesWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { auto* mainLayout = new QGridLayout(this); diff --git a/src/editor/emitterWidget/fluctuationEditorWidget.cpp b/src/editor/inspector/fluctuationEditorWidget.cpp similarity index 97% rename from src/editor/emitterWidget/fluctuationEditorWidget.cpp rename to src/editor/inspector/fluctuationEditorWidget.cpp index adf0bd0..074f42e 100644 --- a/src/editor/emitterWidget/fluctuationEditorWidget.cpp +++ b/src/editor/inspector/fluctuationEditorWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/fluctuationEditorWidget.h" +#include "editor/inspector/fluctuationEditorWidget.h" #include @@ -9,7 +9,7 @@ namespace PtclEditor { FluctuationEditorWidget::FluctuationEditorWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { // TODO: Determine better ranges? mScaleSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); mFreqSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); diff --git a/src/editor/emitterWidget/gravityPropertiesWidget.cpp b/src/editor/inspector/gravityPropertiesWidget.cpp similarity index 94% rename from src/editor/emitterWidget/gravityPropertiesWidget.cpp rename to src/editor/inspector/gravityPropertiesWidget.cpp index a2e02c8..6e7241e 100644 --- a/src/editor/emitterWidget/gravityPropertiesWidget.cpp +++ b/src/editor/inspector/gravityPropertiesWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/gravityPropertiesWidget.h" +#include "editor/inspector/gravityPropertiesWidget.h" #include @@ -10,7 +10,7 @@ namespace PtclEditor { GravityPropertiesWidget::GravityPropertiesWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); diff --git a/src/editor/emitterWidget/inspectorPanel.cpp b/src/editor/inspector/inspectorPanel.cpp similarity index 89% rename from src/editor/emitterWidget/inspectorPanel.cpp rename to src/editor/inspector/inspectorPanel.cpp index 494360a..f8fe6e6 100644 --- a/src/editor/emitterWidget/inspectorPanel.cpp +++ b/src/editor/inspector/inspectorPanel.cpp @@ -1,22 +1,22 @@ -#include "editor/emitterWidget/inspectorPanel.h" - -#include "editor/emitterWidget/alphaPropertiesWidget.h" -#include "editor/emitterWidget/basicPropertiesWidget.h" -#include "editor/emitterWidget/colorPropertiesWidget.h" -#include "editor/emitterWidget/combinerPropertiesWidget.h" -#include "editor/emitterWidget/emissionPropertiesWidget.h" -#include "editor/emitterWidget/fluctuationEditorWidget.h" -#include "editor/emitterWidget/gravityPropertiesWidget.h" -#include "editor/emitterWidget/lifespanPropertiesWidget.h" -#include "editor/emitterWidget/rotationPropertiesWidget.h" -#include "editor/emitterWidget/scalePropertiesWidget.h" -#include "editor/emitterWidget/stripeEditorWidget.h" -#include "editor/emitterWidget/terminationPropertiesWidget.h" -#include "editor/emitterWidget/texturePropertiesWidget.h" -#include "editor/emitterWidget/transformPropertiesWidget.h" -#include "editor/emitterWidget/velocityPropertiesWidget.h" -#include "editor/emitterWidget/volumePropertiesWidget.h" +#include "editor/inspector/inspectorPanel.h" + +#include "editor/inspector/alphaPropertiesWidget.h" +#include "editor/inspector/basicPropertiesWidget.h" +#include "editor/inspector/colorPropertiesWidget.h" +#include "editor/inspector/combinerPropertiesWidget.h" +#include "editor/inspector/emissionPropertiesWidget.h" +#include "editor/inspector/fluctuationEditorWidget.h" +#include "editor/inspector/gravityPropertiesWidget.h" +#include "editor/inspector/lifespanPropertiesWidget.h" +#include "editor/inspector/rotationPropertiesWidget.h" +#include "editor/inspector/scalePropertiesWidget.h" +#include "editor/inspector/stripeEditorWidget.h" +#include "editor/inspector/terminationPropertiesWidget.h" +#include "editor/inspector/texturePropertiesWidget.h" +#include "editor/inspector/transformPropertiesWidget.h" +#include "editor/inspector/velocityPropertiesWidget.h" +#include "editor/inspector/volumePropertiesWidget.h" #include diff --git a/src/editor/emitterWidget/emitterWidgetBase.cpp b/src/editor/inspector/inspectorWidgetBase.cpp similarity index 75% rename from src/editor/emitterWidget/emitterWidgetBase.cpp rename to src/editor/inspector/inspectorWidgetBase.cpp index deea4e3..01c9add 100644 --- a/src/editor/emitterWidget/emitterWidgetBase.cpp +++ b/src/editor/inspector/inspectorWidgetBase.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/emitterWidgetBase.h" +#include "editor/inspector/inspectorWidgetBase.h" #include @@ -9,10 +9,10 @@ namespace PtclEditor { // ========================================================================== // -EmitterWidgetBase::EmitterWidgetBase(QWidget* parent) : +InspectorWidgetBase::InspectorWidgetBase(QWidget* parent) : QWidget{parent} {} -void EmitterWidgetBase::setDocument(Ptcl::Document* document) { +void InspectorWidgetBase::setDocument(Ptcl::Document* document) { if (mDocument) { mDocument->disconnect(this); } @@ -20,11 +20,11 @@ void EmitterWidgetBase::setDocument(Ptcl::Document* document) { mDocument = document; if (mDocument) { - connect(mDocument, &Ptcl::Document::emitterChanged, this, &EmitterWidgetBase::onEmitterChanged); + connect(mDocument, &Ptcl::Document::emitterChanged, this, &InspectorWidgetBase::onEmitterChanged); } } -void EmitterWidgetBase::setSelection(Ptcl::Selection* selection) { +void InspectorWidgetBase::setSelection(Ptcl::Selection* selection) { if (mSelection) { mSelection->disconnect(this); } @@ -47,7 +47,7 @@ void EmitterWidgetBase::setSelection(Ptcl::Selection* selection) { } } -QString EmitterWidgetBase::formatLabel(const QString& label) const { +QString InspectorWidgetBase::formatHistoryLabel(const QString& label) const { return QString("Set %1, Emitter %2 - %3") .arg(mSelection->emitterSetIndex()) .arg(mSelection->emitterIndex()) @@ -55,7 +55,7 @@ QString EmitterWidgetBase::formatLabel(const QString& label) const { } -void EmitterWidgetBase::onEmitterChanged(s32 setIndex, s32 emitterIndex) { +void InspectorWidgetBase::onEmitterChanged(s32 setIndex, s32 emitterIndex) { if (!mEmitter) { return; } diff --git a/src/editor/emitterWidget/lifespanPropertiesWidget.cpp b/src/editor/inspector/lifespanPropertiesWidget.cpp similarity index 96% rename from src/editor/emitterWidget/lifespanPropertiesWidget.cpp rename to src/editor/inspector/lifespanPropertiesWidget.cpp index efd8f64..94c40e3 100644 --- a/src/editor/emitterWidget/lifespanPropertiesWidget.cpp +++ b/src/editor/inspector/lifespanPropertiesWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/lifespanPropertiesWidget.h" +#include "editor/inspector/lifespanPropertiesWidget.h" #include @@ -10,7 +10,7 @@ namespace PtclEditor { LifespanPropertiesWidget::LifespanPropertiesWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); diff --git a/src/editor/emitterWidget/rotationPropertiesWidget.cpp b/src/editor/inspector/rotationPropertiesWidget.cpp similarity index 98% rename from src/editor/emitterWidget/rotationPropertiesWidget.cpp rename to src/editor/inspector/rotationPropertiesWidget.cpp index fa4d09e..15f6e74 100644 --- a/src/editor/emitterWidget/rotationPropertiesWidget.cpp +++ b/src/editor/inspector/rotationPropertiesWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/rotationPropertiesWidget.h" +#include "editor/inspector/rotationPropertiesWidget.h" #include "math/util.h" @@ -12,7 +12,7 @@ namespace PtclEditor { RotationPropertiesWidget::RotationPropertiesWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); diff --git a/src/editor/emitterWidget/scalePropertiesWidget.cpp b/src/editor/inspector/scalePropertiesWidget.cpp similarity index 98% rename from src/editor/emitterWidget/scalePropertiesWidget.cpp rename to src/editor/inspector/scalePropertiesWidget.cpp index 5101d4b..c48c4d9 100644 --- a/src/editor/emitterWidget/scalePropertiesWidget.cpp +++ b/src/editor/inspector/scalePropertiesWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/scalePropertiesWidget.h" +#include "editor/inspector/scalePropertiesWidget.h" #include @@ -10,7 +10,7 @@ namespace PtclEditor { ScalePropertiesWidget::ScalePropertiesWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { // TODO: Move these somewhere else static constexpr QColor sColorAxisX = { 238, 51, 79 }; diff --git a/src/editor/emitterWidget/stripeEditorWidget.cpp b/src/editor/inspector/stripeEditorWidget.cpp similarity index 98% rename from src/editor/emitterWidget/stripeEditorWidget.cpp rename to src/editor/inspector/stripeEditorWidget.cpp index 4860f49..58f14a6 100644 --- a/src/editor/emitterWidget/stripeEditorWidget.cpp +++ b/src/editor/inspector/stripeEditorWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/stripeEditorWidget.h" +#include "editor/inspector/stripeEditorWidget.h" #include @@ -9,7 +9,7 @@ namespace PtclEditor { StripeEditorWidget::StripeEditorWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { mTypeComboBox.addItem("Billboard Stripe", QVariant::fromValue(Ptcl::StripeType::Billboard)); mTypeComboBox.addItem("Emitter Maxtrix", QVariant::fromValue(Ptcl::StripeType::EmitterMatrix)); diff --git a/src/editor/emitterWidget/terminationPropertiesWidget.cpp b/src/editor/inspector/terminationPropertiesWidget.cpp similarity index 94% rename from src/editor/emitterWidget/terminationPropertiesWidget.cpp rename to src/editor/inspector/terminationPropertiesWidget.cpp index 7e2c079..e13d83e 100644 --- a/src/editor/emitterWidget/terminationPropertiesWidget.cpp +++ b/src/editor/inspector/terminationPropertiesWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/terminationPropertiesWidget.h" +#include "editor/inspector/terminationPropertiesWidget.h" #include @@ -10,7 +10,7 @@ namespace PtclEditor { TerminationPropertiesWidget::TerminationPropertiesWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); diff --git a/src/editor/emitterWidget/texturePropertiesWidget.cpp b/src/editor/inspector/texturePropertiesWidget.cpp similarity index 99% rename from src/editor/emitterWidget/texturePropertiesWidget.cpp rename to src/editor/inspector/texturePropertiesWidget.cpp index a164a2f..8153c81 100644 --- a/src/editor/emitterWidget/texturePropertiesWidget.cpp +++ b/src/editor/inspector/texturePropertiesWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/texturePropertiesWidget.h" +#include "editor/inspector/texturePropertiesWidget.h" #include "editor/textureSelectDialog.h" @@ -18,7 +18,7 @@ namespace PtclEditor { TexturePropertiesWidget::TexturePropertiesWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { // Texture Preview mTexturePreview.setThumbnailSize(QSize(64, 64)); diff --git a/src/editor/emitterWidget/transformPropertiesWidget.cpp b/src/editor/inspector/transformPropertiesWidget.cpp similarity index 95% rename from src/editor/emitterWidget/transformPropertiesWidget.cpp rename to src/editor/inspector/transformPropertiesWidget.cpp index 2f1fdde..ae26dbd 100644 --- a/src/editor/emitterWidget/transformPropertiesWidget.cpp +++ b/src/editor/inspector/transformPropertiesWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/transformPropertiesWidget.h" +#include "editor/inspector/transformPropertiesWidget.h" #include @@ -10,7 +10,7 @@ namespace PtclEditor { TransformPropertiesWidget::TransformPropertiesWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); diff --git a/src/editor/emitterWidget/velocityPropertiesWidget.cpp b/src/editor/inspector/velocityPropertiesWidget.cpp similarity index 97% rename from src/editor/emitterWidget/velocityPropertiesWidget.cpp rename to src/editor/inspector/velocityPropertiesWidget.cpp index 863fc78..0e16cf7 100644 --- a/src/editor/emitterWidget/velocityPropertiesWidget.cpp +++ b/src/editor/inspector/velocityPropertiesWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/velocityPropertiesWidget.h" +#include "editor/inspector/velocityPropertiesWidget.h" #include @@ -10,7 +10,7 @@ namespace PtclEditor { VelocityPropertiesWidget::VelocityPropertiesWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); diff --git a/src/editor/emitterWidget/volumePropertiesWidget.cpp b/src/editor/inspector/volumePropertiesWidget.cpp similarity index 98% rename from src/editor/emitterWidget/volumePropertiesWidget.cpp rename to src/editor/inspector/volumePropertiesWidget.cpp index cd7df8c..2120098 100644 --- a/src/editor/emitterWidget/volumePropertiesWidget.cpp +++ b/src/editor/inspector/volumePropertiesWidget.cpp @@ -1,4 +1,4 @@ -#include "editor/emitterWidget/volumePropertiesWidget.h" +#include "editor/inspector/volumePropertiesWidget.h" #include "math/util.h" @@ -12,7 +12,7 @@ namespace PtclEditor { VolumePropertiesWidget::VolumePropertiesWidget(QWidget* parent) : - EmitterWidgetBase{parent} { + InspectorWidgetBase{parent} { setupUi(); setupConnections(); From a232b96a505e5747ff42519c18c74b9ceea21350 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Tue, 17 Mar 2026 16:00:10 +0000 Subject: [PATCH 22/35] refactor: rename PropertyWidgets -> Inspector --- CMakeLists.txt | 64 +++---- ...ropertiesWidget.h => alphaAnimInspector.h} | 4 +- ...lorPropertiesWidget.h => colorInspector.h} | 4 +- ...PropertiesWidget.h => combinerInspector.h} | 4 +- ...PropertiesWidget.h => emissionInspector.h} | 4 +- ...nEditorWidget.h => fluctuationInspector.h} | 4 +- ...tiesWidget.h => generalEmitterInspector.h} | 4 +- ...yPropertiesWidget.h => gravityInspector.h} | 4 +- include/editor/inspector/inspectorPanel.h | 64 +++---- ...PropertiesWidget.h => lifespanInspector.h} | 4 +- ...PropertiesWidget.h => rotationInspector.h} | 4 +- ...ropertiesWidget.h => scaleAnimInspector.h} | 4 +- ...stripeEditorWidget.h => stripeInspector.h} | 4 +- ...pertiesWidget.h => terminationInspector.h} | 4 +- ...ePropertiesWidget.h => textureInspector.h} | 4 +- ...ropertiesWidget.h => transformInspector.h} | 4 +- ...PropertiesWidget.h => velocityInspector.h} | 4 +- ...mePropertiesWidget.h => volumeInspector.h} | 4 +- ...rtiesWidget.cpp => alphaAnimInspector.cpp} | 8 +- ...ropertiesWidget.cpp => colorInspector.cpp} | 16 +- ...ertiesWidget.cpp => combinerInspector.cpp} | 8 +- ...ertiesWidget.cpp => emissionInspector.cpp} | 8 +- ...torWidget.cpp => fluctuationInspector.cpp} | 8 +- ...Widget.cpp => generalEmitterInspector.cpp} | 20 +-- ...pertiesWidget.cpp => gravityInspector.cpp} | 8 +- src/editor/inspector/inspectorPanel.cpp | 162 +++++++++--------- ...ertiesWidget.cpp => lifespanInspector.cpp} | 8 +- ...ertiesWidget.cpp => rotationInspector.cpp} | 10 +- ...rtiesWidget.cpp => scaleAnimInspector.cpp} | 12 +- ...peEditorWidget.cpp => stripeInspector.cpp} | 8 +- ...iesWidget.cpp => terminationInspector.cpp} | 8 +- ...pertiesWidget.cpp => textureInspector.cpp} | 24 +-- ...rtiesWidget.cpp => transformInspector.cpp} | 8 +- ...ertiesWidget.cpp => velocityInspector.cpp} | 8 +- ...opertiesWidget.cpp => volumeInspector.cpp} | 12 +- 35 files changed, 264 insertions(+), 264 deletions(-) rename include/editor/inspector/{alphaPropertiesWidget.h => alphaAnimInspector.h} (80%) rename include/editor/inspector/{colorPropertiesWidget.h => colorInspector.h} (93%) rename include/editor/inspector/{combinerPropertiesWidget.h => combinerInspector.h} (85%) rename include/editor/inspector/{emissionPropertiesWidget.h => emissionInspector.h} (85%) rename include/editor/inspector/{fluctuationEditorWidget.h => fluctuationInspector.h} (84%) rename include/editor/inspector/{basicPropertiesWidget.h => generalEmitterInspector.h} (93%) rename include/editor/inspector/{gravityPropertiesWidget.h => gravityInspector.h} (81%) rename include/editor/inspector/{lifespanPropertiesWidget.h => lifespanInspector.h} (83%) rename include/editor/inspector/{rotationPropertiesWidget.h => rotationInspector.h} (86%) rename include/editor/inspector/{scalePropertiesWidget.h => scaleAnimInspector.h} (84%) rename include/editor/inspector/{stripeEditorWidget.h => stripeInspector.h} (87%) rename include/editor/inspector/{terminationPropertiesWidget.h => terminationInspector.h} (79%) rename include/editor/inspector/{texturePropertiesWidget.h => textureInspector.h} (93%) rename include/editor/inspector/{transformPropertiesWidget.h => transformInspector.h} (81%) rename include/editor/inspector/{velocityPropertiesWidget.h => velocityInspector.h} (85%) rename include/editor/inspector/{volumePropertiesWidget.h => volumeInspector.h} (90%) rename src/editor/inspector/{alphaPropertiesWidget.cpp => alphaAnimInspector.cpp} (92%) rename src/editor/inspector/{colorPropertiesWidget.cpp => colorInspector.cpp} (96%) rename src/editor/inspector/{combinerPropertiesWidget.cpp => combinerInspector.cpp} (92%) rename src/editor/inspector/{emissionPropertiesWidget.cpp => emissionInspector.cpp} (94%) rename src/editor/inspector/{fluctuationEditorWidget.cpp => fluctuationInspector.cpp} (94%) rename src/editor/inspector/{basicPropertiesWidget.cpp => generalEmitterInspector.cpp} (92%) rename src/editor/inspector/{gravityPropertiesWidget.cpp => gravityInspector.cpp} (86%) rename src/editor/inspector/{lifespanPropertiesWidget.cpp => lifespanInspector.cpp} (90%) rename src/editor/inspector/{rotationPropertiesWidget.cpp => rotationInspector.cpp} (95%) rename src/editor/inspector/{scalePropertiesWidget.cpp => scaleAnimInspector.cpp} (92%) rename src/editor/inspector/{stripeEditorWidget.cpp => stripeInspector.cpp} (96%) rename src/editor/inspector/{terminationPropertiesWidget.cpp => terminationInspector.cpp} (85%) rename src/editor/inspector/{texturePropertiesWidget.cpp => textureInspector.cpp} (95%) rename src/editor/inspector/{transformPropertiesWidget.cpp => transformInspector.cpp} (88%) rename src/editor/inspector/{velocityPropertiesWidget.cpp => velocityInspector.cpp} (94%) rename src/editor/inspector/{volumePropertiesWidget.cpp => volumeInspector.cpp} (95%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 12392e2..d41bde2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,24 +48,24 @@ set(PROJECT_SOURCES src/editor/childEditor/scalePropertiesWidget.cpp src/editor/childEditor/texturePropertiesWidget.cpp src/editor/childEditor/velocityPropertiesWidget.cpp - src/editor/inspector/alphaPropertiesWidget.cpp - src/editor/inspector/basicPropertiesWidget.cpp - src/editor/inspector/colorPropertiesWidget.cpp - src/editor/inspector/combinerPropertiesWidget.cpp - src/editor/inspector/emissionPropertiesWidget.cpp + src/editor/inspector/alphaAnimInspector.cpp + src/editor/inspector/generalEmitterInspector.cpp + src/editor/inspector/colorInspector.cpp + src/editor/inspector/combinerInspector.cpp + src/editor/inspector/emissionInspector.cpp src/editor/inspector/inspectorPanel.cpp src/editor/inspector/inspectorWidgetBase.cpp - src/editor/inspector/fluctuationEditorWidget.cpp - src/editor/inspector/gravityPropertiesWidget.cpp - src/editor/inspector/lifespanPropertiesWidget.cpp - src/editor/inspector/rotationPropertiesWidget.cpp - src/editor/inspector/scalePropertiesWidget.cpp - src/editor/inspector/stripeEditorWidget.cpp - src/editor/inspector/terminationPropertiesWidget.cpp - src/editor/inspector/texturePropertiesWidget.cpp - src/editor/inspector/transformPropertiesWidget.cpp - src/editor/inspector/velocityPropertiesWidget.cpp - src/editor/inspector/volumePropertiesWidget.cpp + src/editor/inspector/fluctuationInspector.cpp + src/editor/inspector/gravityInspector.cpp + src/editor/inspector/lifespanInspector.cpp + src/editor/inspector/rotationInspector.cpp + src/editor/inspector/scaleAnimInspector.cpp + src/editor/inspector/stripeInspector.cpp + src/editor/inspector/terminationInspector.cpp + src/editor/inspector/textureInspector.cpp + src/editor/inspector/transformInspector.cpp + src/editor/inspector/velocityInspector.cpp + src/editor/inspector/volumeInspector.cpp src/editor/fieldEditor/collisionDataWidget.cpp src/editor/fieldEditor/convergenceDataWidget.cpp src/editor/fieldEditor/fieldEditorWidget.cpp @@ -120,24 +120,24 @@ set(PROJECT_HEADERS include/editor/childEditor/scalePropertiesWidget.h include/editor/childEditor/texturePropertiesWidget.h include/editor/childEditor/velocityPropertiesWidget.h - include/editor/inspector/alphaPropertiesWidget.h - include/editor/inspector/basicPropertiesWidget.h - include/editor/inspector/colorPropertiesWidget.h - include/editor/inspector/combinerPropertiesWidget.h - include/editor/inspector/emissionPropertiesWidget.h + include/editor/inspector/alphaAnimInspector.h + include/editor/inspector/generalEmitterInspector.h + include/editor/inspector/colorInspector.h + include/editor/inspector/combinerInspector.h + include/editor/inspector/emissionInspector.h include/editor/inspector/inspectorPanel.h include/editor/inspector/inspectorWidgetBase.h - include/editor/inspector/fluctuationEditorWidget.h - include/editor/inspector/gravityPropertiesWidget.h - include/editor/inspector/lifespanPropertiesWidget.h - include/editor/inspector/rotationPropertiesWidget.h - include/editor/inspector/scalePropertiesWidget.h - include/editor/inspector/stripeEditorWidget.h - include/editor/inspector/terminationPropertiesWidget.h - include/editor/inspector/texturePropertiesWidget.h - include/editor/inspector/transformPropertiesWidget.h - include/editor/inspector/velocityPropertiesWidget.h - include/editor/inspector/volumePropertiesWidget.h + include/editor/inspector/fluctuationInspector.h + include/editor/inspector/gravityInspector.h + include/editor/inspector/lifespanInspector.h + include/editor/inspector/rotationInspector.h + include/editor/inspector/scaleAnimInspector.h + include/editor/inspector/stripeInspector.h + include/editor/inspector/terminationInspector.h + include/editor/inspector/textureInspector.h + include/editor/inspector/transformInspector.h + include/editor/inspector/velocityInspector.h + include/editor/inspector/volumeInspector.h include/editor/fieldEditor/collisionDataWidget.h include/editor/fieldEditor/convergenceDataWidget.h include/editor/fieldEditor/fieldEditorWidget.h diff --git a/include/editor/inspector/alphaPropertiesWidget.h b/include/editor/inspector/alphaAnimInspector.h similarity index 80% rename from include/editor/inspector/alphaPropertiesWidget.h rename to include/editor/inspector/alphaAnimInspector.h index 5cc495f..0d8bac1 100644 --- a/include/editor/inspector/alphaPropertiesWidget.h +++ b/include/editor/inspector/alphaAnimInspector.h @@ -12,10 +12,10 @@ namespace PtclEditor { // ========================================================================== // -class AlphaPropertiesWidget final : public InspectorWidgetBase { +class AlphaAnimInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit AlphaPropertiesWidget(QWidget* parent = nullptr); + explicit AlphaAnimInspector(QWidget* parent = nullptr); private: void populateProperties() final; diff --git a/include/editor/inspector/colorPropertiesWidget.h b/include/editor/inspector/colorInspector.h similarity index 93% rename from include/editor/inspector/colorPropertiesWidget.h rename to include/editor/inspector/colorInspector.h index c2bc6ff..75b0e62 100644 --- a/include/editor/inspector/colorPropertiesWidget.h +++ b/include/editor/inspector/colorInspector.h @@ -34,10 +34,10 @@ static s32 behaviorToIndex(Behavior behavior) { // ========================================================================== // -class ColorPropertiesWidget final : public InspectorWidgetBase { +class ColorInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit ColorPropertiesWidget(QWidget* parent = nullptr); + explicit ColorInspector(QWidget* parent = nullptr); private slots: void updateColorSection(ColorGradientEditor::HandleType handleType); diff --git a/include/editor/inspector/combinerPropertiesWidget.h b/include/editor/inspector/combinerInspector.h similarity index 85% rename from include/editor/inspector/combinerPropertiesWidget.h rename to include/editor/inspector/combinerInspector.h index cbb8cf4..b614720 100644 --- a/include/editor/inspector/combinerPropertiesWidget.h +++ b/include/editor/inspector/combinerInspector.h @@ -14,10 +14,10 @@ namespace PtclEditor { // ========================================================================== // -class CombinerPropertiesWidget final : public InspectorWidgetBase { +class CombinerInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit CombinerPropertiesWidget(QWidget* parent = nullptr); + explicit CombinerInspector(QWidget* parent = nullptr); private: void populateProperties() final; diff --git a/include/editor/inspector/emissionPropertiesWidget.h b/include/editor/inspector/emissionInspector.h similarity index 85% rename from include/editor/inspector/emissionPropertiesWidget.h rename to include/editor/inspector/emissionInspector.h index 8d635e9..337d8a2 100644 --- a/include/editor/inspector/emissionPropertiesWidget.h +++ b/include/editor/inspector/emissionInspector.h @@ -14,10 +14,10 @@ namespace PtclEditor { // ========================================================================== // -class EmissionPropertiesWidget final : public InspectorWidgetBase { +class EmissionInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit EmissionPropertiesWidget(QWidget* parent = nullptr); + explicit EmissionInspector(QWidget* parent = nullptr); private: void populateProperties() final; diff --git a/include/editor/inspector/fluctuationEditorWidget.h b/include/editor/inspector/fluctuationInspector.h similarity index 84% rename from include/editor/inspector/fluctuationEditorWidget.h rename to include/editor/inspector/fluctuationInspector.h index a5e5155..e5af2bf 100644 --- a/include/editor/inspector/fluctuationEditorWidget.h +++ b/include/editor/inspector/fluctuationInspector.h @@ -14,10 +14,10 @@ namespace PtclEditor { // ========================================================================== // -class FluctuationEditorWidget final : public InspectorWidgetBase { +class FluctuationInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit FluctuationEditorWidget(QWidget* parent = nullptr); + explicit FluctuationInspector(QWidget* parent = nullptr); private: void populateProperties() final; diff --git a/include/editor/inspector/basicPropertiesWidget.h b/include/editor/inspector/generalEmitterInspector.h similarity index 93% rename from include/editor/inspector/basicPropertiesWidget.h rename to include/editor/inspector/generalEmitterInspector.h index 8ed17ac..f253539 100644 --- a/include/editor/inspector/basicPropertiesWidget.h +++ b/include/editor/inspector/generalEmitterInspector.h @@ -16,7 +16,7 @@ namespace PtclEditor { // ========================================================================== // -class BasicPropertiesWidget final : public InspectorWidgetBase { +class GeneralEmitterInspector final : public InspectorWidgetBase { Q_OBJECT private: static constexpr std::array sBillboardTypes{ @@ -40,7 +40,7 @@ class BasicPropertiesWidget final : public InspectorWidgetBase { }; public: - explicit BasicPropertiesWidget(QWidget* parent = nullptr); + explicit GeneralEmitterInspector(QWidget* parent = nullptr); private: void populateProperties() final; diff --git a/include/editor/inspector/gravityPropertiesWidget.h b/include/editor/inspector/gravityInspector.h similarity index 81% rename from include/editor/inspector/gravityPropertiesWidget.h rename to include/editor/inspector/gravityInspector.h index 9167c72..38753f8 100644 --- a/include/editor/inspector/gravityPropertiesWidget.h +++ b/include/editor/inspector/gravityInspector.h @@ -13,10 +13,10 @@ namespace PtclEditor { // ========================================================================== // -class GravityPropertiesWidget final : public InspectorWidgetBase { +class GravityInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit GravityPropertiesWidget(QWidget* parent = nullptr); + explicit GravityInspector(QWidget* parent = nullptr); private: void populateProperties() final; diff --git a/include/editor/inspector/inspectorPanel.h b/include/editor/inspector/inspectorPanel.h index 3f3e401..576dab7 100644 --- a/include/editor/inspector/inspectorPanel.h +++ b/include/editor/inspector/inspectorPanel.h @@ -20,22 +20,22 @@ namespace PtclEditor { -class BasicPropertiesWidget; -class GravityPropertiesWidget; -class TransformPropertiesWidget; -class LifespanPropertiesWidget; -class ScalePropertiesWidget; -class TerminationPropertiesWidget; -class EmissionPropertiesWidget; -class VelocityPropertiesWidget; -class VolumePropertiesWidget; -class RotationPropertiesWidget; -class AlphaPropertiesWidget; -class CombinerPropertiesWidget; -class ColorPropertiesWidget; -class TexturePropertiesWidget; -class FluctuationEditorWidget; -class StripeEditorWidget; +class GeneralEmitterInspector; +class GravityInspector; +class TransformInspector; +class LifespanInspector; +class ScaleAnimInspector; +class TerminationInspector; +class EmissionInspector; +class VelocityInspector; +class VolumeInspector; +class RotationInspector; +class AlphaAnimInspector; +class CombinerInspector; +class ColorInspector; +class TextureInspector; +class FluctuationInspector; +class StripeInspector; // ========================================================================== // @@ -75,22 +75,22 @@ class InspectorPanel final : public QWidget { const Ptcl::TextureList* mTextureList{nullptr}; - BasicPropertiesWidget* mBasicProperties{nullptr}; - GravityPropertiesWidget* mGravityProperties{nullptr}; - TransformPropertiesWidget* mTransformProperties{nullptr}; - LifespanPropertiesWidget* mLifespanProperties{nullptr}; - TerminationPropertiesWidget* mTerminationProperties{nullptr}; - EmissionPropertiesWidget* mEmissionProperties{nullptr}; - VelocityPropertiesWidget* mVelocityProperties{nullptr}; - VolumePropertiesWidget* mVolumeProperties{nullptr}; - ColorPropertiesWidget* mColorProperties{nullptr}; - AlphaPropertiesWidget* mAlphaProperties{nullptr}; - RotationPropertiesWidget* mRotationProperties{nullptr}; - ScalePropertiesWidget* mScaleProperties{nullptr}; - TexturePropertiesWidget* mTextureProperties{nullptr}; - CombinerPropertiesWidget* mCombinerProperties{nullptr}; - StripeEditorWidget* mStripeEditorWidget{nullptr}; - FluctuationEditorWidget* mFluctuationEditorWidget{nullptr}; + GeneralEmitterInspector* mGeneralInspector{nullptr}; + GravityInspector* mGravityInspector{nullptr}; + TransformInspector* mTransformInspector{nullptr}; + LifespanInspector* mLifespanInspector{nullptr}; + TerminationInspector* mTerminationInspector{nullptr}; + EmissionInspector* mEmissionInspector{nullptr}; + VelocityInspector* mVelocityInspector{nullptr}; + VolumeInspector* mVolumeInspector{nullptr}; + ColorInspector* mColorInspector{nullptr}; + AlphaAnimInspector* mAlphaAnimInspector{nullptr}; + RotationInspector* mRotationInspector{nullptr}; + ScaleAnimInspector* mScaleAnimInspector{nullptr}; + TextureInspector* mTextureInspector{nullptr}; + CombinerInspector* mCombinerInspector{nullptr}; + StripeInspector* mStripeInspector{nullptr}; + FluctuationInspector* mFluctuationInspector{nullptr}; QTabWidget* mTabWidget{nullptr}; diff --git a/include/editor/inspector/lifespanPropertiesWidget.h b/include/editor/inspector/lifespanInspector.h similarity index 83% rename from include/editor/inspector/lifespanPropertiesWidget.h rename to include/editor/inspector/lifespanInspector.h index b9041cb..33e81e4 100644 --- a/include/editor/inspector/lifespanPropertiesWidget.h +++ b/include/editor/inspector/lifespanInspector.h @@ -14,10 +14,10 @@ namespace PtclEditor { // ========================================================================== // -class LifespanPropertiesWidget final : public InspectorWidgetBase { +class LifespanInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit LifespanPropertiesWidget(QWidget* parent = nullptr); + explicit LifespanInspector(QWidget* parent = nullptr); private: void populateProperties() final; diff --git a/include/editor/inspector/rotationPropertiesWidget.h b/include/editor/inspector/rotationInspector.h similarity index 86% rename from include/editor/inspector/rotationPropertiesWidget.h rename to include/editor/inspector/rotationInspector.h index 49cab26..81e7eea 100644 --- a/include/editor/inspector/rotationPropertiesWidget.h +++ b/include/editor/inspector/rotationInspector.h @@ -15,10 +15,10 @@ namespace PtclEditor { // ========================================================================== // -class RotationPropertiesWidget final : public InspectorWidgetBase { +class RotationInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit RotationPropertiesWidget(QWidget* parent = nullptr); + explicit RotationInspector(QWidget* parent = nullptr); private: void populateProperties() final; diff --git a/include/editor/inspector/scalePropertiesWidget.h b/include/editor/inspector/scaleAnimInspector.h similarity index 84% rename from include/editor/inspector/scalePropertiesWidget.h rename to include/editor/inspector/scaleAnimInspector.h index a1f57df..fb003a4 100644 --- a/include/editor/inspector/scalePropertiesWidget.h +++ b/include/editor/inspector/scaleAnimInspector.h @@ -12,10 +12,10 @@ namespace PtclEditor { // ========================================================================== // -class ScalePropertiesWidget final : public InspectorWidgetBase { +class ScaleAnimInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit ScalePropertiesWidget(QWidget* parent = nullptr); + explicit ScaleAnimInspector(QWidget* parent = nullptr); private: void populateProperties() final; diff --git a/include/editor/inspector/stripeEditorWidget.h b/include/editor/inspector/stripeInspector.h similarity index 87% rename from include/editor/inspector/stripeEditorWidget.h rename to include/editor/inspector/stripeInspector.h index 913f4e9..0b498d1 100644 --- a/include/editor/inspector/stripeEditorWidget.h +++ b/include/editor/inspector/stripeInspector.h @@ -16,10 +16,10 @@ namespace PtclEditor { // ========================================================================== // -class StripeEditorWidget final : public InspectorWidgetBase { +class StripeInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit StripeEditorWidget(QWidget* parent = nullptr); + explicit StripeInspector(QWidget* parent = nullptr); private: void populateProperties() final; diff --git a/include/editor/inspector/terminationPropertiesWidget.h b/include/editor/inspector/terminationInspector.h similarity index 79% rename from include/editor/inspector/terminationPropertiesWidget.h rename to include/editor/inspector/terminationInspector.h index ce282a6..84cebad 100644 --- a/include/editor/inspector/terminationPropertiesWidget.h +++ b/include/editor/inspector/terminationInspector.h @@ -14,10 +14,10 @@ namespace PtclEditor { // ========================================================================== // -class TerminationPropertiesWidget final : public InspectorWidgetBase { +class TerminationInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit TerminationPropertiesWidget(QWidget* parent = nullptr); + explicit TerminationInspector(QWidget* parent = nullptr); private: void populateProperties() final; diff --git a/include/editor/inspector/texturePropertiesWidget.h b/include/editor/inspector/textureInspector.h similarity index 93% rename from include/editor/inspector/texturePropertiesWidget.h rename to include/editor/inspector/textureInspector.h index a757253..c657bf5 100644 --- a/include/editor/inspector/texturePropertiesWidget.h +++ b/include/editor/inspector/textureInspector.h @@ -17,7 +17,7 @@ namespace PtclEditor { // ========================================================================== // -class TexturePropertiesWidget final : public InspectorWidgetBase { +class TextureInspector final : public InspectorWidgetBase { Q_OBJECT public: enum class AnimMode { @@ -34,7 +34,7 @@ class TexturePropertiesWidget final : public InspectorWidgetBase { } public: - explicit TexturePropertiesWidget(QWidget* parent = nullptr); + explicit TextureInspector(QWidget* parent = nullptr); private slots: void changeTexture(); diff --git a/include/editor/inspector/transformPropertiesWidget.h b/include/editor/inspector/transformInspector.h similarity index 81% rename from include/editor/inspector/transformPropertiesWidget.h rename to include/editor/inspector/transformInspector.h index 067adc6..fba5492 100644 --- a/include/editor/inspector/transformPropertiesWidget.h +++ b/include/editor/inspector/transformInspector.h @@ -10,10 +10,10 @@ namespace PtclEditor { // ========================================================================== // -class TransformPropertiesWidget final : public InspectorWidgetBase { +class TransformInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit TransformPropertiesWidget(QWidget* parent = nullptr); + explicit TransformInspector(QWidget* parent = nullptr); private: void populateProperties() final; diff --git a/include/editor/inspector/velocityPropertiesWidget.h b/include/editor/inspector/velocityInspector.h similarity index 85% rename from include/editor/inspector/velocityPropertiesWidget.h rename to include/editor/inspector/velocityInspector.h index f16077c..50ac40a 100644 --- a/include/editor/inspector/velocityPropertiesWidget.h +++ b/include/editor/inspector/velocityInspector.h @@ -14,10 +14,10 @@ namespace PtclEditor { // ========================================================================== // -class VelocityPropertiesWidget final : public InspectorWidgetBase { +class VelocityInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit VelocityPropertiesWidget(QWidget* parent = nullptr); + explicit VelocityInspector(QWidget* parent = nullptr); private: void populateProperties() final; diff --git a/include/editor/inspector/volumePropertiesWidget.h b/include/editor/inspector/volumeInspector.h similarity index 90% rename from include/editor/inspector/volumePropertiesWidget.h rename to include/editor/inspector/volumeInspector.h index 3e56df6..a1698b5 100644 --- a/include/editor/inspector/volumePropertiesWidget.h +++ b/include/editor/inspector/volumeInspector.h @@ -15,10 +15,10 @@ namespace PtclEditor { // ========================================================================== // -class VolumePropertiesWidget final : public InspectorWidgetBase { +class VolumeInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit VolumePropertiesWidget(QWidget* parent = nullptr); + explicit VolumeInspector(QWidget* parent = nullptr); private: void populateProperties() final; diff --git a/src/editor/inspector/alphaPropertiesWidget.cpp b/src/editor/inspector/alphaAnimInspector.cpp similarity index 92% rename from src/editor/inspector/alphaPropertiesWidget.cpp rename to src/editor/inspector/alphaAnimInspector.cpp index 22a225f..6828bbf 100644 --- a/src/editor/inspector/alphaPropertiesWidget.cpp +++ b/src/editor/inspector/alphaAnimInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/alphaPropertiesWidget.h" +#include "editor/inspector/alphaAnimInspector.h" #include @@ -9,7 +9,7 @@ namespace PtclEditor { // ========================================================================== // -AlphaPropertiesWidget::AlphaPropertiesWidget(QWidget* parent) : +AlphaAnimInspector::AlphaAnimInspector(QWidget* parent) : InspectorWidgetBase{parent} { mGraphA.setLineColor(QColor{20, 40, 60}); @@ -25,7 +25,7 @@ AlphaPropertiesWidget::AlphaPropertiesWidget(QWidget* parent) : }); } -void AlphaPropertiesWidget::updateAnimPoint(s32 pointIndex, const AnimGraph::GraphPoint& point) { +void AlphaAnimInspector::updateAnimPoint(s32 pointIndex, const AnimGraph::GraphPoint& point) { auto anim = mEmitter->alphaAnim(); const f32 oldP0 = anim.initAlpha; @@ -84,7 +84,7 @@ void AlphaPropertiesWidget::updateAnimPoint(s32 pointIndex, const AnimGraph::Gra ); } -void AlphaPropertiesWidget::populateProperties() { +void AlphaAnimInspector::populateProperties() { QSignalBlocker blocker(mGraphA); const auto& anim = mEmitter->alphaAnim(); diff --git a/src/editor/inspector/colorPropertiesWidget.cpp b/src/editor/inspector/colorInspector.cpp similarity index 96% rename from src/editor/inspector/colorPropertiesWidget.cpp rename to src/editor/inspector/colorInspector.cpp index a8dd953..35a866f 100644 --- a/src/editor/inspector/colorPropertiesWidget.cpp +++ b/src/editor/inspector/colorInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/colorPropertiesWidget.h" +#include "editor/inspector/colorInspector.h" #include #include @@ -10,7 +10,7 @@ namespace PtclEditor { // ========================================================================== // -ColorPropertiesWidget::ColorPropertiesWidget(QWidget* parent) : +ColorInspector::ColorInspector(QWidget* parent) : InspectorWidgetBase{parent} { // Color Behavior Type auto colorBehaviorLayout = new QHBoxLayout; @@ -76,12 +76,12 @@ ColorPropertiesWidget::ColorPropertiesWidget(QWidget* parent) : setupConnections(); } -void ColorPropertiesWidget::setupConnections() { +void ColorInspector::setupConnections() { // Behavior Type - connect(&mColorBehavior, &QComboBox::currentIndexChanged, this, &ColorPropertiesWidget::handleBehaviorChanged); + connect(&mColorBehavior, &QComboBox::currentIndexChanged, this, &ColorInspector::handleBehaviorChanged); // Color Sections - connect(&mColorSections, &ColorGradientEditor::handleMoved, this, &ColorPropertiesWidget::updateColorSection); + connect(&mColorSections, &ColorGradientEditor::handleMoved, this, &ColorInspector::updateColorSection); // Color Repeat connect(&mColorNumRepeatSpinBox, &SizedSpinBoxBase::valueChanged, this, [this](s32 value) { @@ -210,7 +210,7 @@ void ColorPropertiesWidget::setupConnections() { }); } -void ColorPropertiesWidget::populateProperties() { +void ColorInspector::populateProperties() { QSignalBlocker b1(mColorSections); QSignalBlocker b2(mColorNumRepeatSpinBox); QSignalBlocker b3(mColorCalcTypeSpinBox); @@ -280,7 +280,7 @@ void ColorPropertiesWidget::populateProperties() { mColorBehavior.setCurrentIndex(behaviorToIndex(behavior)); } -void ColorPropertiesWidget::handleBehaviorChanged(s32 index) { +void ColorInspector::handleBehaviorChanged(s32 index) { auto behavior = behaviorFromIndex(index); bool isColorRandom; @@ -326,7 +326,7 @@ void ColorPropertiesWidget::handleBehaviorChanged(s32 index) { mDocument->undoStack()->endMacro(); } -void ColorPropertiesWidget::updateColorSection(ColorGradientEditor::HandleType handleType) { +void ColorInspector::updateColorSection(ColorGradientEditor::HandleType handleType) { switch (handleType) { case ColorGradientEditor::HandleType::InCompletedHandle: { setEmitterProperty( diff --git a/src/editor/inspector/combinerPropertiesWidget.cpp b/src/editor/inspector/combinerInspector.cpp similarity index 92% rename from src/editor/inspector/combinerPropertiesWidget.cpp rename to src/editor/inspector/combinerInspector.cpp index 55fc0fc..5511e41 100644 --- a/src/editor/inspector/combinerPropertiesWidget.cpp +++ b/src/editor/inspector/combinerInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/combinerPropertiesWidget.h" +#include "editor/inspector/combinerInspector.h" #include @@ -8,7 +8,7 @@ namespace PtclEditor { // ========================================================================== // -CombinerPropertiesWidget::CombinerPropertiesWidget(QWidget* parent) : +CombinerInspector::CombinerInspector(QWidget* parent) : InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); @@ -24,7 +24,7 @@ CombinerPropertiesWidget::CombinerPropertiesWidget(QWidget* parent) : setupConnections(); } -void CombinerPropertiesWidget::setupConnections() { +void CombinerInspector::setupConnections() { connect(&mFogCheckBox, &QCheckBox::clicked, this, [this](bool checked) { setEmitterProperty( "Set Fog Enabled", @@ -69,7 +69,7 @@ void CombinerPropertiesWidget::setupConnections() { }); } -void CombinerPropertiesWidget::populateProperties() { +void CombinerInspector::populateProperties() { QSignalBlocker b1(mBlendFuncComboBox); QSignalBlocker b2(mDepthFuncComboBox); QSignalBlocker b3(mCombinerFuncComboBox); diff --git a/src/editor/inspector/emissionPropertiesWidget.cpp b/src/editor/inspector/emissionInspector.cpp similarity index 94% rename from src/editor/inspector/emissionPropertiesWidget.cpp rename to src/editor/inspector/emissionInspector.cpp index 980f1e3..efd6576 100644 --- a/src/editor/inspector/emissionPropertiesWidget.cpp +++ b/src/editor/inspector/emissionInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/emissionPropertiesWidget.h" +#include "editor/inspector/emissionInspector.h" #include #include @@ -10,7 +10,7 @@ namespace PtclEditor { // ========================================================================== // -EmissionPropertiesWidget::EmissionPropertiesWidget(QWidget* parent) : +EmissionInspector::EmissionInspector(QWidget* parent) : InspectorWidgetBase{parent} { auto* mainLayout = new QGridLayout(this); @@ -43,7 +43,7 @@ EmissionPropertiesWidget::EmissionPropertiesWidget(QWidget* parent) : setupConnections(); } -void EmissionPropertiesWidget::setupConnections() { +void EmissionInspector::setupConnections() { connect(&mStartFrameSpinBox, &QSpinBox::valueChanged, this, [this](u64 value) { setEmitterProperty( "Set Emission Start Frame", @@ -109,7 +109,7 @@ void EmissionPropertiesWidget::setupConnections() { }); } -void EmissionPropertiesWidget::populateProperties() { +void EmissionInspector::populateProperties() { QSignalBlocker b1(mStartFrameSpinBox); QSignalBlocker b2(mEndFrameSpinBox); QSignalBlocker b3(mLifeStepSpinBox); diff --git a/src/editor/inspector/fluctuationEditorWidget.cpp b/src/editor/inspector/fluctuationInspector.cpp similarity index 94% rename from src/editor/inspector/fluctuationEditorWidget.cpp rename to src/editor/inspector/fluctuationInspector.cpp index 074f42e..f9088e4 100644 --- a/src/editor/inspector/fluctuationEditorWidget.cpp +++ b/src/editor/inspector/fluctuationInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/fluctuationEditorWidget.h" +#include "editor/inspector/fluctuationInspector.h" #include @@ -8,7 +8,7 @@ namespace PtclEditor { // ========================================================================== // -FluctuationEditorWidget::FluctuationEditorWidget(QWidget* parent) : +FluctuationInspector::FluctuationInspector(QWidget* parent) : InspectorWidgetBase{parent} { // TODO: Determine better ranges? mScaleSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); @@ -35,7 +35,7 @@ FluctuationEditorWidget::FluctuationEditorWidget(QWidget* parent) : setupConnections(); } -void FluctuationEditorWidget::setupConnections() { +void FluctuationInspector::setupConnections() { // Enabled connect(&mEnabledCheckBox, &QCheckBox::clicked, this, [this](bool checked) { setEmitterProperty( @@ -103,7 +103,7 @@ void FluctuationEditorWidget::setupConnections() { }); } -void FluctuationEditorWidget::populateProperties() { +void FluctuationInspector::populateProperties() { QSignalBlocker b1(mScaleSpinBox); QSignalBlocker b2(mFreqSpinBox); QSignalBlocker b3(mPhaseRndCheckBox); diff --git a/src/editor/inspector/basicPropertiesWidget.cpp b/src/editor/inspector/generalEmitterInspector.cpp similarity index 92% rename from src/editor/inspector/basicPropertiesWidget.cpp rename to src/editor/inspector/generalEmitterInspector.cpp index 1168b5a..3312527 100644 --- a/src/editor/inspector/basicPropertiesWidget.cpp +++ b/src/editor/inspector/generalEmitterInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/basicPropertiesWidget.h" +#include "editor/inspector/generalEmitterInspector.h" #include "util/nameValidator.h" #include @@ -9,7 +9,7 @@ namespace PtclEditor { // ========================================================================== // -BasicPropertiesWidget::BasicPropertiesWidget(QWidget* parent) : +GeneralEmitterInspector::GeneralEmitterInspector(QWidget* parent) : InspectorWidgetBase{parent} { for (Ptcl::BillboardType type : sBillboardTypes) { @@ -57,7 +57,7 @@ BasicPropertiesWidget::BasicPropertiesWidget(QWidget* parent) : setupConnections(); } -void BasicPropertiesWidget::setupConnections() { +void GeneralEmitterInspector::setupConnections() { // Emitter Name connect(&mNameLineEdit, &QLineEdit::textChanged, this, [this](const QString& text) { setEmitterProperty( @@ -181,7 +181,7 @@ void BasicPropertiesWidget::setupConnections() { }); } -void BasicPropertiesWidget::setBillboardType(Ptcl::BillboardType type, const QString& label, const QString& key) { +void GeneralEmitterInspector::setBillboardType(Ptcl::BillboardType type, const QString& label, const QString& key) { setEmitterProperty( label, key, @@ -191,7 +191,7 @@ void BasicPropertiesWidget::setBillboardType(Ptcl::BillboardType type, const QSt ); } -bool BasicPropertiesWidget::isBillboardAllowedForShape(Ptcl::BillboardType billboard, ShapeType shape) { +bool GeneralEmitterInspector::isBillboardAllowedForShape(Ptcl::BillboardType billboard, ShapeType shape) { switch (shape) { case ShapeType::Particle: return billboard != Ptcl::BillboardType::Stripe && @@ -206,7 +206,7 @@ bool BasicPropertiesWidget::isBillboardAllowedForShape(Ptcl::BillboardType billb return false; } -BasicPropertiesWidget::ShapeType BasicPropertiesWidget::shapeFromBillboard(Ptcl::BillboardType type) { +GeneralEmitterInspector::ShapeType GeneralEmitterInspector::shapeFromBillboard(Ptcl::BillboardType type) { if (type == Ptcl::BillboardType::Stripe || type == Ptcl::BillboardType::ComplexStripe) { return ShapeType::Stripe; } @@ -218,7 +218,7 @@ BasicPropertiesWidget::ShapeType BasicPropertiesWidget::shapeFromBillboard(Ptcl: return ShapeType::Particle; } -void BasicPropertiesWidget::populateProperties() { +void GeneralEmitterInspector::populateProperties() { QSignalBlocker b1(mNameLineEdit); QSignalBlocker b2(mTypeComboBox); QSignalBlocker b3(mRandomSeedMode); @@ -254,7 +254,7 @@ void BasicPropertiesWidget::populateProperties() { updateShapeRowVisibility(); } -void BasicPropertiesWidget::updateShapeRowVisibility() { +void GeneralEmitterInspector::updateShapeRowVisibility() { if (mEmitter->type() == Ptcl::EmitterType::Simple) { mMainLayout->setRowVisible(&mShapeComboBox, false); } else { @@ -262,13 +262,13 @@ void BasicPropertiesWidget::updateShapeRowVisibility() { } } -void BasicPropertiesWidget::updateBillboardRowVisibility() { +void GeneralEmitterInspector::updateBillboardRowVisibility() { const auto shape = mShapeComboBox.currentData().value(); mMainLayout->setRowVisible(&mBillboardComboBox, shape == ShapeType::Particle); mMainLayout->setRowVisible(&mStripeTypeComboBox, shape == ShapeType::Stripe); } -void BasicPropertiesWidget::syncBillboardSelection() { +void GeneralEmitterInspector::syncBillboardSelection() { QSignalBlocker b1(mBillboardComboBox); QSignalBlocker b2(mStripeTypeComboBox); diff --git a/src/editor/inspector/gravityPropertiesWidget.cpp b/src/editor/inspector/gravityInspector.cpp similarity index 86% rename from src/editor/inspector/gravityPropertiesWidget.cpp rename to src/editor/inspector/gravityInspector.cpp index 6e7241e..57cd43e 100644 --- a/src/editor/inspector/gravityPropertiesWidget.cpp +++ b/src/editor/inspector/gravityInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/gravityPropertiesWidget.h" +#include "editor/inspector/gravityInspector.h" #include @@ -9,7 +9,7 @@ namespace PtclEditor { // ========================================================================== // -GravityPropertiesWidget::GravityPropertiesWidget(QWidget* parent) : +GravityInspector::GravityInspector(QWidget* parent) : InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); @@ -22,7 +22,7 @@ GravityPropertiesWidget::GravityPropertiesWidget(QWidget* parent) : setupConnections(); } -void GravityPropertiesWidget::setupConnections() { +void GravityInspector::setupConnections() { // Is Directional connect(&mIsDirectionalCheckBox, &QCheckBox::clicked, this, [this](bool checked) { setEmitterProperty( @@ -47,7 +47,7 @@ void GravityPropertiesWidget::setupConnections() { }); } -void GravityPropertiesWidget::populateProperties() { +void GravityInspector::populateProperties() { QSignalBlocker b1(mIsDirectionalCheckBox); QSignalBlocker b2(mGravitySpinBox); diff --git a/src/editor/inspector/inspectorPanel.cpp b/src/editor/inspector/inspectorPanel.cpp index f8fe6e6..2557bcb 100644 --- a/src/editor/inspector/inspectorPanel.cpp +++ b/src/editor/inspector/inspectorPanel.cpp @@ -1,22 +1,22 @@ #include "editor/inspector/inspectorPanel.h" -#include "editor/inspector/alphaPropertiesWidget.h" -#include "editor/inspector/basicPropertiesWidget.h" -#include "editor/inspector/colorPropertiesWidget.h" -#include "editor/inspector/combinerPropertiesWidget.h" -#include "editor/inspector/emissionPropertiesWidget.h" -#include "editor/inspector/fluctuationEditorWidget.h" -#include "editor/inspector/gravityPropertiesWidget.h" -#include "editor/inspector/lifespanPropertiesWidget.h" -#include "editor/inspector/rotationPropertiesWidget.h" -#include "editor/inspector/scalePropertiesWidget.h" -#include "editor/inspector/stripeEditorWidget.h" -#include "editor/inspector/terminationPropertiesWidget.h" -#include "editor/inspector/texturePropertiesWidget.h" -#include "editor/inspector/transformPropertiesWidget.h" -#include "editor/inspector/velocityPropertiesWidget.h" -#include "editor/inspector/volumePropertiesWidget.h" +#include "editor/inspector/alphaAnimInspector.h" +#include "editor/inspector/generalEmitterInspector.h" +#include "editor/inspector/colorInspector.h" +#include "editor/inspector/combinerInspector.h" +#include "editor/inspector/emissionInspector.h" +#include "editor/inspector/fluctuationInspector.h" +#include "editor/inspector/gravityInspector.h" +#include "editor/inspector/lifespanInspector.h" +#include "editor/inspector/rotationInspector.h" +#include "editor/inspector/scaleAnimInspector.h" +#include "editor/inspector/stripeInspector.h" +#include "editor/inspector/terminationInspector.h" +#include "editor/inspector/textureInspector.h" +#include "editor/inspector/transformInspector.h" +#include "editor/inspector/velocityInspector.h" +#include "editor/inspector/volumeInspector.h" #include @@ -29,24 +29,24 @@ namespace PtclEditor { InspectorPanel::InspectorPanel(QWidget* parent) : QWidget{parent} { - mBasicProperties = new BasicPropertiesWidget(this); - mGravityProperties = new GravityPropertiesWidget(this); - mTransformProperties = new TransformPropertiesWidget(this); - mLifespanProperties = new LifespanPropertiesWidget(this); - mTerminationProperties = new TerminationPropertiesWidget(this); - mEmissionProperties = new EmissionPropertiesWidget(this); - mVelocityProperties = new VelocityPropertiesWidget(this); - mVolumeProperties = new VolumePropertiesWidget(this); - mColorProperties = new ColorPropertiesWidget(this); - mAlphaProperties = new AlphaPropertiesWidget(this); - mRotationProperties = new RotationPropertiesWidget(this); - mScaleProperties = new ScalePropertiesWidget(this); - mTextureProperties = new TexturePropertiesWidget(this); - mCombinerProperties = new CombinerPropertiesWidget(this); - mStripeEditorWidget = new StripeEditorWidget(this); + mGeneralInspector = new GeneralEmitterInspector(this); + mGravityInspector = new GravityInspector(this); + mTransformInspector = new TransformInspector(this); + mLifespanInspector = new LifespanInspector(this); + mTerminationInspector = new TerminationInspector(this); + mEmissionInspector = new EmissionInspector(this); + mVelocityInspector = new VelocityInspector(this); + mVolumeInspector = new VolumeInspector(this); + mColorInspector = new ColorInspector(this); + mAlphaAnimInspector = new AlphaAnimInspector(this); + mRotationInspector = new RotationInspector(this); + mScaleAnimInspector = new ScaleAnimInspector(this); + mTextureInspector = new TextureInspector(this); + mCombinerInspector = new CombinerInspector(this); + mStripeInspector = new StripeInspector(this); mChildEditorWidget = new ChildEditorWidget(this); - mFluctuationEditorWidget = new FluctuationEditorWidget(this); + mFluctuationInspector = new FluctuationInspector(this); mFieldEditorWidget = new FieldEditorWidget(this); mTabWidget = new QTabWidget(this); @@ -102,25 +102,25 @@ QWidget* InspectorPanel::wrapInScroll(QWidget* widget) { } void InspectorPanel::buildEmitterTabs() { - mTabWidget->addTab(wrapInScroll(mBasicProperties), "General"); + mTabWidget->addTab(wrapInScroll(mGeneralInspector), "General"); if (mEmitter->hasStripeData()) { - mTabWidget->addTab(wrapInScroll(mStripeEditorWidget), "Stripe"); + mTabWidget->addTab(wrapInScroll(mStripeInspector), "Stripe"); } - mTabWidget->addTab(wrapInScroll(mLifespanProperties), "Life"); - mTabWidget->addTab(wrapInScroll(mTerminationProperties), "Termination"); - mTabWidget->addTab(wrapInScroll(mGravityProperties), "Gravity"); - mTabWidget->addTab(wrapInScroll(mEmissionProperties), "Emission"); - mTabWidget->addTab(wrapInScroll(mVolumeProperties), "Volume"); - mTabWidget->addTab(wrapInScroll(mVelocityProperties), "Velocity"); - mTabWidget->addTab(wrapInScroll(mTextureProperties), "Texture"); - mTabWidget->addTab(wrapInScroll(mColorProperties), "Color"); - mTabWidget->addTab(wrapInScroll(mCombinerProperties), "Combiner"); - mTabWidget->addTab(wrapInScroll(mAlphaProperties), "Alpha"); - mTabWidget->addTab(wrapInScroll(mTransformProperties), "Transform"); - mTabWidget->addTab(wrapInScroll(mRotationProperties), "Rotation"); - mTabWidget->addTab(wrapInScroll(mScaleProperties), "Scale"); + mTabWidget->addTab(wrapInScroll(mLifespanInspector), "Life"); + mTabWidget->addTab(wrapInScroll(mTerminationInspector), "Termination"); + mTabWidget->addTab(wrapInScroll(mGravityInspector), "Gravity"); + mTabWidget->addTab(wrapInScroll(mEmissionInspector), "Emission"); + mTabWidget->addTab(wrapInScroll(mVolumeInspector), "Volume"); + mTabWidget->addTab(wrapInScroll(mVelocityInspector), "Velocity"); + mTabWidget->addTab(wrapInScroll(mTextureInspector), "Texture"); + mTabWidget->addTab(wrapInScroll(mColorInspector), "Color"); + mTabWidget->addTab(wrapInScroll(mCombinerInspector), "Combiner"); + mTabWidget->addTab(wrapInScroll(mAlphaAnimInspector), "Alpha"); + mTabWidget->addTab(wrapInScroll(mTransformInspector), "Transform"); + mTabWidget->addTab(wrapInScroll(mRotationInspector), "Rotation"); + mTabWidget->addTab(wrapInScroll(mScaleAnimInspector), "Scale"); } void InspectorPanel::buildChildTabs() { @@ -129,7 +129,7 @@ void InspectorPanel::buildChildTabs() { } void InspectorPanel::buildFluxTabs() { - mTabWidget->addTab(wrapInScroll(mFluctuationEditorWidget), "Fluctuation"); + mTabWidget->addTab(wrapInScroll(mFluctuationInspector), "Fluctuation"); } void InspectorPanel::buildFieldTabs() { @@ -162,22 +162,22 @@ void InspectorPanel::setDocument(Ptcl::Document* document) { mDocument = document; - mBasicProperties->setDocument(document); - mGravityProperties->setDocument(document); - mTransformProperties->setDocument(document); - mLifespanProperties->setDocument(document); - mScaleProperties->setDocument(document); - mTerminationProperties->setDocument(document); - mEmissionProperties->setDocument(document); - mVelocityProperties->setDocument(document); - mVolumeProperties->setDocument(document); - mRotationProperties->setDocument(document); - mAlphaProperties->setDocument(document); - mCombinerProperties->setDocument(document); - mColorProperties->setDocument(document); - mTextureProperties->setDocument(document); - mFluctuationEditorWidget->setDocument(document); - mStripeEditorWidget->setDocument(document); + mGeneralInspector->setDocument(document); + mGravityInspector->setDocument(document); + mTransformInspector->setDocument(document); + mLifespanInspector->setDocument(document); + mScaleAnimInspector->setDocument(document); + mTerminationInspector->setDocument(document); + mEmissionInspector->setDocument(document); + mVelocityInspector->setDocument(document); + mVolumeInspector->setDocument(document); + mRotationInspector->setDocument(document); + mAlphaAnimInspector->setDocument(document); + mCombinerInspector->setDocument(document); + mColorInspector->setDocument(document); + mTextureInspector->setDocument(document); + mFluctuationInspector->setDocument(document); + mStripeInspector->setDocument(document); if (mDocument) { connect(mDocument, &Ptcl::Document::emitterChanged, this, [this](s32 setIndex, s32 emitterIndex) { @@ -202,22 +202,22 @@ void InspectorPanel::setSelection(Ptcl::Selection* selection) { mSelection = selection; - mBasicProperties->setSelection(selection); - mGravityProperties->setSelection(selection); - mTransformProperties->setSelection(selection); - mLifespanProperties->setSelection(selection); - mScaleProperties->setSelection(selection); - mTerminationProperties->setSelection(selection); - mEmissionProperties->setSelection(selection); - mVelocityProperties->setSelection(selection); - mVolumeProperties->setSelection(selection); - mRotationProperties->setSelection(selection); - mAlphaProperties->setSelection(selection); - mCombinerProperties->setSelection(selection); - mColorProperties->setSelection(selection); - mTextureProperties->setSelection(selection); - mFluctuationEditorWidget->setSelection(selection); - mStripeEditorWidget->setSelection(selection); + mGeneralInspector->setSelection(selection); + mGravityInspector->setSelection(selection); + mTransformInspector->setSelection(selection); + mLifespanInspector->setSelection(selection); + mScaleAnimInspector->setSelection(selection); + mTerminationInspector->setSelection(selection); + mEmissionInspector->setSelection(selection); + mVelocityInspector->setSelection(selection); + mVolumeInspector->setSelection(selection); + mRotationInspector->setSelection(selection); + mAlphaAnimInspector->setSelection(selection); + mCombinerInspector->setSelection(selection); + mColorInspector->setSelection(selection); + mTextureInspector->setSelection(selection); + mFluctuationInspector->setSelection(selection); + mStripeInspector->setSelection(selection); if (mSelection) { connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { @@ -243,7 +243,7 @@ void InspectorPanel::setSelection(Ptcl::Selection* selection) { void InspectorPanel::populateProperties() { QSignalBlocker b15(mChildEditorWidget); QSignalBlocker b17(mFieldEditorWidget); - QSignalBlocker b18(mStripeEditorWidget); + QSignalBlocker b18(mStripeInspector); mChildEditorWidget->setChildData(&mEmitter->childData(), mEmitter->complexProperties().childFlags); mChildEditorWidget->setParentColor0(mEmitter->primaryColor()); diff --git a/src/editor/inspector/lifespanPropertiesWidget.cpp b/src/editor/inspector/lifespanInspector.cpp similarity index 90% rename from src/editor/inspector/lifespanPropertiesWidget.cpp rename to src/editor/inspector/lifespanInspector.cpp index 94c40e3..a0d1655 100644 --- a/src/editor/inspector/lifespanPropertiesWidget.cpp +++ b/src/editor/inspector/lifespanInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/lifespanPropertiesWidget.h" +#include "editor/inspector/lifespanInspector.h" #include @@ -9,7 +9,7 @@ namespace PtclEditor { // ========================================================================== // -LifespanPropertiesWidget::LifespanPropertiesWidget(QWidget* parent) : +LifespanInspector::LifespanInspector(QWidget* parent) : InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); @@ -30,7 +30,7 @@ LifespanPropertiesWidget::LifespanPropertiesWidget(QWidget* parent) : setupConnections(); } -void LifespanPropertiesWidget::setupConnections() { +void LifespanInspector::setupConnections() { connect(&mLifeSpanSpinBox, &QSpinBox::valueChanged, this, [this](u64 value) { setEmitterProperty( "Set LifeSpan", @@ -64,7 +64,7 @@ void LifespanPropertiesWidget::setupConnections() { }); } -void LifespanPropertiesWidget::populateProperties() { +void LifespanInspector::populateProperties() { QSignalBlocker b1(mInfiniteLifeCheckBox); QSignalBlocker b2(mLifeSpanSpinBox); QSignalBlocker b3(mLifeSpanRndSpinBox); diff --git a/src/editor/inspector/rotationPropertiesWidget.cpp b/src/editor/inspector/rotationInspector.cpp similarity index 95% rename from src/editor/inspector/rotationPropertiesWidget.cpp rename to src/editor/inspector/rotationInspector.cpp index 15f6e74..3a747f6 100644 --- a/src/editor/inspector/rotationPropertiesWidget.cpp +++ b/src/editor/inspector/rotationInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/rotationPropertiesWidget.h" +#include "editor/inspector/rotationInspector.h" #include "math/util.h" @@ -11,7 +11,7 @@ namespace PtclEditor { // ========================================================================== // -RotationPropertiesWidget::RotationPropertiesWidget(QWidget* parent) : +RotationInspector::RotationInspector(QWidget* parent) : InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); @@ -27,7 +27,7 @@ RotationPropertiesWidget::RotationPropertiesWidget(QWidget* parent) : setupConnections(); } -void RotationPropertiesWidget::setupConnections() { +void RotationInspector::setupConnections() { connect(&mRotTypeSpinBox, &QComboBox::currentIndexChanged, this, [this]() { const auto type = mRotTypeSpinBox.currentEnum(); @@ -111,7 +111,7 @@ void RotationPropertiesWidget::setupConnections() { } -void RotationPropertiesWidget::populateProperties() { +void RotationInspector::populateProperties() { QSignalBlocker b1(mRotTypeSpinBox); QSignalBlocker b2(mInitRotSpinBox); QSignalBlocker b3(mInitRotRandSpinBox); @@ -137,7 +137,7 @@ void RotationPropertiesWidget::populateProperties() { updateAxis(); } -void RotationPropertiesWidget::updateAxis() { +void RotationInspector::updateAxis() { using Axis = VectorSpinBoxBase::Axis; switch (mEmitter->rotationType()) { diff --git a/src/editor/inspector/scalePropertiesWidget.cpp b/src/editor/inspector/scaleAnimInspector.cpp similarity index 92% rename from src/editor/inspector/scalePropertiesWidget.cpp rename to src/editor/inspector/scaleAnimInspector.cpp index c48c4d9..8d627af 100644 --- a/src/editor/inspector/scalePropertiesWidget.cpp +++ b/src/editor/inspector/scaleAnimInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/scalePropertiesWidget.h" +#include "editor/inspector/scaleAnimInspector.h" #include @@ -9,7 +9,7 @@ namespace PtclEditor { // ========================================================================== // -ScalePropertiesWidget::ScalePropertiesWidget(QWidget* parent) : +ScaleAnimInspector::ScaleAnimInspector(QWidget* parent) : InspectorWidgetBase{parent} { // TODO: Move these somewhere else @@ -31,7 +31,7 @@ ScalePropertiesWidget::ScalePropertiesWidget(QWidget* parent) : setupConnections(); } -void ScalePropertiesWidget::setupConnections() { +void ScaleAnimInspector::setupConnections() { connect(&mGraphX, &AnimGraph::pointEdited, this, [this](s32 pointIndex, const AnimGraph::GraphPoint& point) { updateAnimPoint(pointIndex, point, &Math::Vector2f::getX); }); @@ -51,7 +51,7 @@ void ScalePropertiesWidget::setupConnections() { }); } -void ScalePropertiesWidget::updateAnimPoint(s32 pointIndex, const AnimGraph::GraphPoint& point, f32 (Math::Vector2f::*get)() const) { +void ScaleAnimInspector::updateAnimPoint(s32 pointIndex, const AnimGraph::GraphPoint& point, f32 (Math::Vector2f::*get)() const) { auto anim = mEmitter->scaleAnim(); auto set = [&](Math::Vector2f& v, f32 val) { @@ -135,7 +135,7 @@ void ScalePropertiesWidget::updateAnimPoint(s32 pointIndex, const AnimGraph::Gra updateGraphs(); } -void ScalePropertiesWidget::updateGraphs() { +void ScaleAnimInspector::updateGraphs() { const auto& anim = mEmitter->scaleAnim(); auto updateGraph = [&](AnimGraph& graph, f32 (Math::Vector2f::*get)() const) { @@ -164,7 +164,7 @@ void ScalePropertiesWidget::updateGraphs() { updateGraph(mGraphY, &Math::Vector2f::getY); } -void ScalePropertiesWidget::populateProperties() { +void ScaleAnimInspector::populateProperties() { updateGraphs(); QSignalBlocker blockerRand(mRandSpinbox); diff --git a/src/editor/inspector/stripeEditorWidget.cpp b/src/editor/inspector/stripeInspector.cpp similarity index 96% rename from src/editor/inspector/stripeEditorWidget.cpp rename to src/editor/inspector/stripeInspector.cpp index 58f14a6..c21d0ac 100644 --- a/src/editor/inspector/stripeEditorWidget.cpp +++ b/src/editor/inspector/stripeInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/stripeEditorWidget.h" +#include "editor/inspector/stripeInspector.h" #include @@ -8,7 +8,7 @@ namespace PtclEditor { // ========================================================================== // -StripeEditorWidget::StripeEditorWidget(QWidget* parent) : +StripeInspector::StripeInspector(QWidget* parent) : InspectorWidgetBase{parent} { mTypeComboBox.addItem("Billboard Stripe", QVariant::fromValue(Ptcl::StripeType::Billboard)); @@ -36,7 +36,7 @@ StripeEditorWidget::StripeEditorWidget(QWidget* parent) : setupConnections(); } -void StripeEditorWidget::setupConnections() { +void StripeInspector::setupConnections() { // Type connect(&mTypeComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { Q_UNUSED(index); @@ -129,7 +129,7 @@ void StripeEditorWidget::setupConnections() { }); } -void StripeEditorWidget::populateProperties() { +void StripeInspector::populateProperties() { QSignalBlocker b1(mTypeComboBox); QSignalBlocker b2(mNumHistSpinBox); QSignalBlocker b3(mStartAlphaSpinBox); diff --git a/src/editor/inspector/terminationPropertiesWidget.cpp b/src/editor/inspector/terminationInspector.cpp similarity index 85% rename from src/editor/inspector/terminationPropertiesWidget.cpp rename to src/editor/inspector/terminationInspector.cpp index e13d83e..14dd8d6 100644 --- a/src/editor/inspector/terminationPropertiesWidget.cpp +++ b/src/editor/inspector/terminationInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/terminationPropertiesWidget.h" +#include "editor/inspector/terminationInspector.h" #include @@ -9,7 +9,7 @@ namespace PtclEditor { // ========================================================================== // -TerminationPropertiesWidget::TerminationPropertiesWidget(QWidget* parent) : +TerminationInspector::TerminationInspector(QWidget* parent) : InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); @@ -22,7 +22,7 @@ TerminationPropertiesWidget::TerminationPropertiesWidget(QWidget* parent) : setupConnections(); } -void TerminationPropertiesWidget::setupConnections() { +void TerminationInspector::setupConnections() { connect(&mIsStopEmitCheckBox, &QCheckBox::checkStateChanged, this, [this](bool checked) { setEmitterProperty( "Set Stop Emission", @@ -44,7 +44,7 @@ void TerminationPropertiesWidget::setupConnections() { }); } -void TerminationPropertiesWidget::populateProperties() { +void TerminationInspector::populateProperties() { QSignalBlocker b1(mIsStopEmitCheckBox); QSignalBlocker b2(mAlphaAddInSpinBox); diff --git a/src/editor/inspector/texturePropertiesWidget.cpp b/src/editor/inspector/textureInspector.cpp similarity index 95% rename from src/editor/inspector/texturePropertiesWidget.cpp rename to src/editor/inspector/textureInspector.cpp index 8153c81..b8eb9d8 100644 --- a/src/editor/inspector/texturePropertiesWidget.cpp +++ b/src/editor/inspector/textureInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/texturePropertiesWidget.h" +#include "editor/inspector/textureInspector.h" #include "editor/textureSelectDialog.h" @@ -17,7 +17,7 @@ namespace PtclEditor { // ========================================================================== // -TexturePropertiesWidget::TexturePropertiesWidget(QWidget* parent) : +TextureInspector::TextureInspector(QWidget* parent) : InspectorWidgetBase{parent} { // Texture Preview @@ -102,8 +102,8 @@ TexturePropertiesWidget::TexturePropertiesWidget(QWidget* parent) : setupConnections(); } -void TexturePropertiesWidget::setupConnections() { - connect(&mTexturePreview, &ThumbnailWidget::clicked, this, &TexturePropertiesWidget::changeTexture); +void TextureInspector::setupConnections() { + connect(&mTexturePreview, &ThumbnailWidget::clicked, this, &TextureInspector::changeTexture); // Wrap T connect(&mWrapTComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { @@ -301,7 +301,7 @@ void TexturePropertiesWidget::setupConnections() { }); } -void TexturePropertiesWidget::populateProperties() { +void TextureInspector::populateProperties() { QSignalBlocker b1(mWrapTComboBox); QSignalBlocker b2(mWrapSComboBox); QSignalBlocker b3(mMagFilterComboBox); @@ -364,7 +364,7 @@ void TexturePropertiesWidget::populateProperties() { updateTexPatTblColumns(); } -void TexturePropertiesWidget::updateTexPatTblColumns() { +void TextureInspector::updateTexPatTblColumns() { const QPalette& palette = mTexPatTbl.palette(); for (s32 i = 0; i < 16; ++i) { @@ -393,7 +393,7 @@ void TexturePropertiesWidget::updateTexPatTblColumns() { } } -void TexturePropertiesWidget::changeTexture() { +void TextureInspector::changeTexture() { const auto textureList = mDocument->textures(); TextureSelectDialog dialog(textureList, this); @@ -412,11 +412,11 @@ void TexturePropertiesWidget::changeTexture() { } } -s32 TexturePropertiesWidget::maxFrameCount() const { +s32 TextureInspector::maxFrameCount() const { return mEmitter->numTextureDivisionX() * mEmitter->numTextureDivisionY(); } -std::optional TexturePropertiesWidget::calcFrameUVOffset(s32 frame) const { +std::optional TextureInspector::calcFrameUVOffset(s32 frame) const { const auto divX = mEmitter->numTextureDivisionX(); const auto divY = mEmitter->numTextureDivisionY(); @@ -441,7 +441,7 @@ std::optional TexturePropertiesWidget::calcFrameUVOffset(s32 fra return uvOffset; } -QImage TexturePropertiesWidget::getFrameTexture(s32 frame) const { +QImage TextureInspector::getFrameTexture(s32 frame) const { if (!mEmitter->textureHandle().isValid()) { return {}; } @@ -469,7 +469,7 @@ QImage TexturePropertiesWidget::getFrameTexture(s32 frame) const { return rect.isValid() ? src.copy(rect) : QImage{}; } -QImage TexturePropertiesWidget::applyUVRepetition(const QImage& image, f32 repeatX, f32 repeatY) const { +QImage TextureInspector::applyUVRepetition(const QImage& image, f32 repeatX, f32 repeatY) const { if (image.isNull()) { return {}; } @@ -504,7 +504,7 @@ QImage TexturePropertiesWidget::applyUVRepetition(const QImage& image, f32 repea return out; } -QPixmap TexturePropertiesWidget::createFramePreview(s32 frame) const { +QPixmap TextureInspector::createFramePreview(s32 frame) const { QImage base = getFrameTexture(frame); if (base.isNull()) { return {}; diff --git a/src/editor/inspector/transformPropertiesWidget.cpp b/src/editor/inspector/transformInspector.cpp similarity index 88% rename from src/editor/inspector/transformPropertiesWidget.cpp rename to src/editor/inspector/transformInspector.cpp index ae26dbd..8aef2d2 100644 --- a/src/editor/inspector/transformPropertiesWidget.cpp +++ b/src/editor/inspector/transformInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/transformPropertiesWidget.h" +#include "editor/inspector/transformInspector.h" #include @@ -9,7 +9,7 @@ namespace PtclEditor { // ========================================================================== // -TransformPropertiesWidget::TransformPropertiesWidget(QWidget* parent) : +TransformInspector::TransformInspector(QWidget* parent) : InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); @@ -21,7 +21,7 @@ TransformPropertiesWidget::TransformPropertiesWidget(QWidget* parent) : setupConnections(); } -void TransformPropertiesWidget::setupConnections() { +void TransformInspector::setupConnections() { connect(&mTranslationSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { const auto translation = mTranslationSpinBox.getVector(); setEmitterProperty( @@ -56,7 +56,7 @@ void TransformPropertiesWidget::setupConnections() { }); } -void TransformPropertiesWidget::populateProperties() { +void TransformInspector::populateProperties() { QSignalBlocker b1(mTranslationSpinBox); QSignalBlocker b2(mRotationSpinBox); QSignalBlocker b3(mScaleSpinBox); diff --git a/src/editor/inspector/velocityPropertiesWidget.cpp b/src/editor/inspector/velocityInspector.cpp similarity index 94% rename from src/editor/inspector/velocityPropertiesWidget.cpp rename to src/editor/inspector/velocityInspector.cpp index 0e16cf7..19ed428 100644 --- a/src/editor/inspector/velocityPropertiesWidget.cpp +++ b/src/editor/inspector/velocityInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/velocityPropertiesWidget.h" +#include "editor/inspector/velocityInspector.h" #include @@ -9,7 +9,7 @@ namespace PtclEditor { // ========================================================================== // -VelocityPropertiesWidget::VelocityPropertiesWidget(QWidget* parent) : +VelocityInspector::VelocityInspector(QWidget* parent) : InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); @@ -29,7 +29,7 @@ VelocityPropertiesWidget::VelocityPropertiesWidget(QWidget* parent) : setupConnections(); } -void VelocityPropertiesWidget::setupConnections() { +void VelocityInspector::setupConnections() { connect(&mFigureVelSpinbox, &QDoubleSpinBox::valueChanged, this, [this](double value) { setEmitterProperty( "Set Figure Velocity", @@ -95,7 +95,7 @@ void VelocityPropertiesWidget::setupConnections() { }); } -void VelocityPropertiesWidget::populateProperties() { +void VelocityInspector::populateProperties() { QSignalBlocker b1(mFigureVelSpinbox); QSignalBlocker b2(mVelDirSpinbox); QSignalBlocker b3(mInitVelSpinbox); diff --git a/src/editor/inspector/volumePropertiesWidget.cpp b/src/editor/inspector/volumeInspector.cpp similarity index 95% rename from src/editor/inspector/volumePropertiesWidget.cpp rename to src/editor/inspector/volumeInspector.cpp index 2120098..3e97ea6 100644 --- a/src/editor/inspector/volumePropertiesWidget.cpp +++ b/src/editor/inspector/volumeInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/inspector/volumePropertiesWidget.h" +#include "editor/inspector/volumeInspector.h" #include "math/util.h" @@ -11,14 +11,14 @@ namespace PtclEditor { // ========================================================================== // -VolumePropertiesWidget::VolumePropertiesWidget(QWidget* parent) : +VolumeInspector::VolumeInspector(QWidget* parent) : InspectorWidgetBase{parent} { setupUi(); setupConnections(); } -void VolumePropertiesWidget::setupUi() { +void VolumeInspector::setupUi() { mRadiusSpinBox.setOrientation(Qt::Vertical); mSweepStartSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); @@ -106,7 +106,7 @@ void VolumePropertiesWidget::setupUi() { } } -void VolumePropertiesWidget::setupConnections() { +void VolumeInspector::setupConnections() { connect(&mTypeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, [this]() { const auto type = mTypeComboBox.currentEnum(); @@ -179,7 +179,7 @@ void VolumePropertiesWidget::setupConnections() { }); } -void VolumePropertiesWidget::populateProperties() { +void VolumeInspector::populateProperties() { QSignalBlocker b1(mVolumeTblIndexComboBox); QSignalBlocker b2(mTypeComboBox); QSignalBlocker b3(mRadiusSpinBox); @@ -202,7 +202,7 @@ void VolumePropertiesWidget::populateProperties() { updateFieldVisibility(volumeType); } -void VolumePropertiesWidget::updateFieldVisibility(Ptcl::VolumeType type) { +void VolumeInspector::updateFieldVisibility(Ptcl::VolumeType type) { for (const auto& field : mFields) { bool visible = field.isVisible(type); QWidget* widget = field.widget; From 08be2ac1b6f775644e2da4dd6289a617a94c318e Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Tue, 17 Mar 2026 20:38:20 +0000 Subject: [PATCH 23/35] feat(ui): implmennt undo/redo for fieldData and directly integrate with inspector --- CMakeLists.txt | 29 ++- .../editor/fieldEditor/fieldEditorWidget.h | 55 ----- .../field/fieldCollisionInspector.h} | 15 +- .../field/fieldConvergenceInspector.h} | 15 +- .../field/fieldMagnetInspector.h} | 15 +- .../field/fieldPosAddInspector.h} | 15 +- .../field/fieldRandomInspector.h} | 15 +- .../field/fieldSpinInspector.h} | 15 +- include/editor/inspector/inspectorPanel.h | 61 ++++- include/ptcl/ptclBinary.h | 13 +- include/ptcl/ptclEmitter.h | 147 +++++++++++- include/ptcl/ptclFieldData.h | 101 --------- src/editor/fieldEditor/fieldEditorWidget.cpp | 175 --------------- src/editor/fieldEditor/magnetDataWidget.cpp | 95 -------- .../field/fieldCollisionInspector.cpp} | 68 ++++-- .../field/fieldConvergenceInspector.cpp} | 45 ++-- .../inspector/field/fieldMagnetInspector.cpp | 126 +++++++++++ .../field/fieldPosAddInspector.cpp} | 36 +-- .../field/fieldRandomInspector.cpp} | 44 ++-- .../field/fieldSpinInspector.cpp} | 48 ++-- src/editor/inspector/inspectorPanel.cpp | 211 ++++++++++++------ src/editor/ptclListWidget.cpp | 2 +- src/ptcl/ptcl.cpp | 28 ++- src/ptcl/ptclBinary.cpp | 43 ++-- src/ptcl/ptclEmitter.cpp | 68 ++++-- src/ptcl/ptclFieldData.cpp | 102 --------- 26 files changed, 749 insertions(+), 838 deletions(-) delete mode 100644 include/editor/fieldEditor/fieldEditorWidget.h rename include/editor/{fieldEditor/collisionDataWidget.h => inspector/field/fieldCollisionInspector.h} (60%) rename include/editor/{fieldEditor/convergenceDataWidget.h => inspector/field/fieldConvergenceInspector.h} (58%) rename include/editor/{fieldEditor/magnetDataWidget.h => inspector/field/fieldMagnetInspector.h} (61%) rename include/editor/{fieldEditor/posAddDataWidget.h => inspector/field/fieldPosAddInspector.h} (56%) rename include/editor/{fieldEditor/randomDataWidget.h => inspector/field/fieldRandomInspector.h} (57%) rename include/editor/{fieldEditor/spinDataWidget.h => inspector/field/fieldSpinInspector.h} (58%) delete mode 100644 include/ptcl/ptclFieldData.h delete mode 100644 src/editor/fieldEditor/fieldEditorWidget.cpp delete mode 100644 src/editor/fieldEditor/magnetDataWidget.cpp rename src/editor/{fieldEditor/collisionDataWidget.cpp => inspector/field/fieldCollisionInspector.cpp} (50%) rename src/editor/{fieldEditor/convergenceDataWidget.cpp => inspector/field/fieldConvergenceInspector.cpp} (50%) create mode 100644 src/editor/inspector/field/fieldMagnetInspector.cpp rename src/editor/{fieldEditor/posAddDataWidget.cpp => inspector/field/fieldPosAddInspector.cpp} (56%) rename src/editor/{fieldEditor/randomDataWidget.cpp => inspector/field/fieldRandomInspector.cpp} (55%) rename src/editor/{fieldEditor/spinDataWidget.cpp => inspector/field/fieldSpinInspector.cpp} (53%) delete mode 100644 src/ptcl/ptclFieldData.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d41bde2..1066858 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,13 +66,12 @@ set(PROJECT_SOURCES src/editor/inspector/transformInspector.cpp src/editor/inspector/velocityInspector.cpp src/editor/inspector/volumeInspector.cpp - src/editor/fieldEditor/collisionDataWidget.cpp - src/editor/fieldEditor/convergenceDataWidget.cpp - src/editor/fieldEditor/fieldEditorWidget.cpp - src/editor/fieldEditor/magnetDataWidget.cpp - src/editor/fieldEditor/posAddDataWidget.cpp - src/editor/fieldEditor/randomDataWidget.cpp - src/editor/fieldEditor/spinDataWidget.cpp + src/editor/inspector/field/fieldCollisionInspector.cpp + src/editor/inspector/field/fieldConvergenceInspector.cpp + src/editor/inspector/field/fieldMagnetInspector.cpp + src/editor/inspector/field/fieldPosAddInspector.cpp + src/editor/inspector/field/fieldRandomInspector.cpp + src/editor/inspector/field/fieldSpinInspector.cpp src/etc1/rg_etc1.cpp src/ptcl/ptcl.cpp src/ptcl/ptclBinary.cpp @@ -80,7 +79,6 @@ set(PROJECT_SOURCES src/ptcl/ptclDocument.cpp src/ptcl/ptclEmitter.cpp src/ptcl/ptclEmitterSet.cpp - src/ptcl/ptclFieldData.cpp src/ptcl/ptclSeed.cpp src/ptcl/ptclTexture.cpp src/util/imageUtil.cpp @@ -138,13 +136,12 @@ set(PROJECT_HEADERS include/editor/inspector/transformInspector.h include/editor/inspector/velocityInspector.h include/editor/inspector/volumeInspector.h - include/editor/fieldEditor/collisionDataWidget.h - include/editor/fieldEditor/convergenceDataWidget.h - include/editor/fieldEditor/fieldEditorWidget.h - include/editor/fieldEditor/magnetDataWidget.h - include/editor/fieldEditor/posAddDataWidget.h - include/editor/fieldEditor/randomDataWidget.h - include/editor/fieldEditor/spinDataWidget.h + include/editor/inspector/field/fieldCollisionInspector.h + include/editor/inspector/field/fieldConvergenceInspector.h + include/editor/inspector/field/fieldMagnetInspector.h + include/editor/inspector/field/fieldPosAddInspector.h + include/editor/inspector/field/fieldRandomInspector.h + include/editor/inspector/field/fieldSpinInspector.h include/etc1/rg_etc1.h include/math/matrix.h include/math/util.h @@ -157,7 +154,7 @@ set(PROJECT_HEADERS include/ptcl/ptclEmitter.h include/ptcl/ptclEmitterSet.h include/ptcl/ptclEnum.h - include/ptcl/ptclFieldData.h + include/ptcl/ptclSeed.h include/ptcl/ptclTexture.h include/util/bitflagUtil.h diff --git a/include/editor/fieldEditor/fieldEditorWidget.h b/include/editor/fieldEditor/fieldEditorWidget.h deleted file mode 100644 index 91f4a8e..0000000 --- a/include/editor/fieldEditor/fieldEditorWidget.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "ptcl/ptclFieldData.h" -#include "util/bitflagUtil.h" - -#include -#include - -namespace PtclEditor { - - -// ========================================================================== // - - -class FieldEditorWidget final : public QWidget { - Q_OBJECT -public: - explicit FieldEditorWidget(QWidget* parent = nullptr); - - void setData(Ptcl::FieldData* fieldData, const BitFlag& fieldFlag); - -signals: - void flagsUpdated(const BitFlag& fieldFlag); - -private: - void setupLayout(QVBoxLayout* mainLayout); - void setupConnections(); - void setCombinerPropertiesSrc(); - -private: - class RandomDataWidget; - class MagnetDataWidget; - class SpinDataWidget; - class CollisionDataWidget; - class ConvergenceDataWidget; - class PosAddDataWidget; - -private: - Ptcl::FieldData* mDataPtr{nullptr}; - BitFlag mFieldFlag{}; - - QWidget* mSectionsContainer{}; - RandomDataWidget* mRandomDataWidget{nullptr}; - MagnetDataWidget* mMagnetDataWidget{nullptr}; - SpinDataWidget* mSpinDataWidget{nullptr}; - CollisionDataWidget* mCollisionDataWidget{nullptr}; - ConvergenceDataWidget* mConvergenceDataWidget{nullptr}; - PosAddDataWidget* mPosAddDataWidget{nullptr}; -}; - - -// ========================================================================== // - - -} // namespace PtclEditor diff --git a/include/editor/fieldEditor/collisionDataWidget.h b/include/editor/inspector/field/fieldCollisionInspector.h similarity index 60% rename from include/editor/fieldEditor/collisionDataWidget.h rename to include/editor/inspector/field/fieldCollisionInspector.h index 0cb60be..3467e8f 100644 --- a/include/editor/fieldEditor/collisionDataWidget.h +++ b/include/editor/inspector/field/fieldCollisionInspector.h @@ -1,7 +1,7 @@ #pragma once #include "editor/components/enumComboBox.h" -#include "editor/fieldEditor/fieldEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -14,23 +14,16 @@ namespace PtclEditor { // ========================================================================== // -class FieldEditorWidget::CollisionDataWidget final : public QWidget { +class FieldCollisionInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit CollisionDataWidget(QWidget* parent = nullptr); - - void setData(const Ptcl::FieldData::FieldCollisionData& data, bool isEnabled); - -signals: - void dataUpdated(const Ptcl::FieldData::FieldCollisionData& data); - void isEnabledUpdated(bool isEnabled); + explicit FieldCollisionInspector(QWidget* parent = nullptr); private: + void populateProperties() final; void setupConnections(); private: - Ptcl::FieldData::FieldCollisionData mData{}; - QWidget* mControlsWidget{nullptr}; EnumComboBox mCollisionTypeSpinBox{}; QCheckBox mIsWorldCheckBox{}; diff --git a/include/editor/fieldEditor/convergenceDataWidget.h b/include/editor/inspector/field/fieldConvergenceInspector.h similarity index 58% rename from include/editor/fieldEditor/convergenceDataWidget.h rename to include/editor/inspector/field/fieldConvergenceInspector.h index 099db12..c1e9228 100644 --- a/include/editor/fieldEditor/convergenceDataWidget.h +++ b/include/editor/inspector/field/fieldConvergenceInspector.h @@ -2,7 +2,7 @@ #include "editor/components/enumComboBox.h" #include "editor/components/vectorSpinBox.h" -#include "editor/fieldEditor/fieldEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -15,23 +15,16 @@ namespace PtclEditor { // ========================================================================== // -class FieldEditorWidget::ConvergenceDataWidget final : public QWidget { +class FieldConvergenceInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit ConvergenceDataWidget(QWidget* parent = nullptr); - - void setData(const Ptcl::FieldData::FieldConvergenceData& data, bool isEnabled); - -signals: - void dataUpdated(const Ptcl::FieldData::FieldConvergenceData& data); - void isEnabledUpdated(bool isEnabled); + explicit FieldConvergenceInspector(QWidget* parent = nullptr); private: + void populateProperties() final; void setupConnections(); private: - Ptcl::FieldData::FieldConvergenceData mData{}; - QWidget* mControlsWidget{nullptr}; EnumComboBox mTypeSpinBox{}; VectorSpinBox mPosSpinBox{}; diff --git a/include/editor/fieldEditor/magnetDataWidget.h b/include/editor/inspector/field/fieldMagnetInspector.h similarity index 61% rename from include/editor/fieldEditor/magnetDataWidget.h rename to include/editor/inspector/field/fieldMagnetInspector.h index 070f323..21b2fdc 100644 --- a/include/editor/fieldEditor/magnetDataWidget.h +++ b/include/editor/inspector/field/fieldMagnetInspector.h @@ -1,7 +1,7 @@ #pragma once #include "editor/components/vectorSpinBox.h" -#include "editor/fieldEditor/fieldEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -14,23 +14,16 @@ namespace PtclEditor { // ========================================================================== // -class FieldEditorWidget::MagnetDataWidget final : public QWidget { +class FieldMagnetInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit MagnetDataWidget(QWidget* parent = nullptr); - - void setData(const Ptcl::FieldData::FieldMagnetData& data, bool isEnabled); - -signals: - void dataUpdated(const Ptcl::FieldData::FieldMagnetData& data); - void isEnabledUpdated(bool isEnabled); + explicit FieldMagnetInspector(QWidget* parent = nullptr); private: + void populateProperties() final; void setupConnections(); private: - Ptcl::FieldData::FieldMagnetData mData{}; - QWidget* mControlsWidget{nullptr}; QDoubleSpinBox mMagnetPowerSpinBox{}; VectorSpinBox mMagnetPosSpinBox{}; diff --git a/include/editor/fieldEditor/posAddDataWidget.h b/include/editor/inspector/field/fieldPosAddInspector.h similarity index 56% rename from include/editor/fieldEditor/posAddDataWidget.h rename to include/editor/inspector/field/fieldPosAddInspector.h index a69fa25..7d27ce6 100644 --- a/include/editor/fieldEditor/posAddDataWidget.h +++ b/include/editor/inspector/field/fieldPosAddInspector.h @@ -1,7 +1,7 @@ #pragma once #include "editor/components/vectorSpinBox.h" -#include "editor/fieldEditor/fieldEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -14,23 +14,16 @@ namespace PtclEditor { // ========================================================================== // -class FieldEditorWidget::PosAddDataWidget final : public QWidget { +class FieldPosAddInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit PosAddDataWidget(QWidget* parent = nullptr); - - void setData(const Ptcl::FieldData::FieldPosAddData& data, bool isEnabled); - -signals: - void dataUpdated(const Ptcl::FieldData::FieldPosAddData& data); - void isEnabledUpdated(bool isEnabled); + explicit FieldPosAddInspector(QWidget* parent = nullptr); private: + void populateProperties() final; void setupConnections(); private: - Ptcl::FieldData::FieldPosAddData mData{}; - QWidget* mControlsWidget{nullptr}; VectorSpinBox mPosSpinBox{}; QCheckBox mEnabledCheckBox{}; diff --git a/include/editor/fieldEditor/randomDataWidget.h b/include/editor/inspector/field/fieldRandomInspector.h similarity index 57% rename from include/editor/fieldEditor/randomDataWidget.h rename to include/editor/inspector/field/fieldRandomInspector.h index a047a0a..bc58486 100644 --- a/include/editor/fieldEditor/randomDataWidget.h +++ b/include/editor/inspector/field/fieldRandomInspector.h @@ -1,7 +1,7 @@ #pragma once #include "editor/components/vectorSpinBox.h" -#include "editor/fieldEditor/fieldEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -14,23 +14,16 @@ namespace PtclEditor { // ========================================================================== // -class FieldEditorWidget::RandomDataWidget final : public QWidget { +class FieldRandomInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit RandomDataWidget(QWidget* parent = nullptr); - - void setData(const Ptcl::FieldData::FieldRandomData& data, bool isEnabled); - -signals: - void dataUpdated(const Ptcl::FieldData::FieldRandomData& data); - void isEnabledUpdated(bool isEnabled); + explicit FieldRandomInspector(QWidget* parent = nullptr); private: + void populateProperties() final; void setupConnections(); private: - Ptcl::FieldData::FieldRandomData mData{}; - QWidget* mControlsWidget{nullptr}; QSpinBox mRandomBlankSpinBox{}; VectorSpinBox mRandomVelAddSpinBox{}; diff --git a/include/editor/fieldEditor/spinDataWidget.h b/include/editor/inspector/field/fieldSpinInspector.h similarity index 58% rename from include/editor/fieldEditor/spinDataWidget.h rename to include/editor/inspector/field/fieldSpinInspector.h index a09a0e0..f1580b0 100644 --- a/include/editor/fieldEditor/spinDataWidget.h +++ b/include/editor/inspector/field/fieldSpinInspector.h @@ -1,7 +1,7 @@ #pragma once #include "editor/components/enumComboBox.h" -#include "editor/fieldEditor/fieldEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -14,23 +14,16 @@ namespace PtclEditor { // ========================================================================== // -class FieldEditorWidget::SpinDataWidget final : public QWidget { +class FieldSpinInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit SpinDataWidget(QWidget* parent = nullptr); - - void setData(const Ptcl::FieldData::FieldSpinData& data, bool isEnabled); - -signals: - void dataUpdated(const Ptcl::FieldData::FieldSpinData& data); - void isEnabledUpdated(bool isEnabled); + explicit FieldSpinInspector(QWidget* parent = nullptr); private: + void populateProperties() final; void setupConnections(); private: - Ptcl::FieldData::FieldSpinData mData{}; - QWidget* mControlsWidget{nullptr}; QDoubleSpinBox mSpinRotateSpinBox{}; EnumComboBox mSpinAxisSpinBox{}; diff --git a/include/editor/inspector/inspectorPanel.h b/include/editor/inspector/inspectorPanel.h index 576dab7..73ab454 100644 --- a/include/editor/inspector/inspectorPanel.h +++ b/include/editor/inspector/inspectorPanel.h @@ -2,7 +2,6 @@ #include "editor/childEditor/childEditorWidget.h" #include "editor/components/collapsibleWidget.h" -#include "editor/fieldEditor/fieldEditorWidget.h" #include "ptcl/ptclDocument.h" #include "ptcl/ptclEmitter.h" @@ -37,12 +36,46 @@ class TextureInspector; class FluctuationInspector; class StripeInspector; +class FieldCollisionInspector; +class FieldConvergenceInspector; +class FieldMagnetInspector; +class FieldPosAddInspector; +class FieldRandomInspector; +class FieldSpinInspector; + // ========================================================================== // class InspectorPanel final : public QWidget { Q_OBJECT +private: + enum class TabId { + General, + Gravity, + Transform, + Lifespan, + Termination, + Emission, + Velocity, + Volume, + Color, + AlphaAnim, + Rotation, + ScaleAnim, + Texture, + Combiner, + Stripe, + Fluctuation, + FieldCollision, + FieldConvergence, + FieldMagnet, + FieldPosAdd, + FieldRandom, + FieldSpin, + Child + }; + public: explicit InspectorPanel(QWidget* parent = nullptr); @@ -60,13 +93,9 @@ class InspectorPanel final : public QWidget { void setupConnections(); void populateProperties(); - void rebuildTabs(); - void buildEmitterTabs(); - void buildChildTabs(); - void buildFluxTabs(); - void buildFieldTabs(); - - QWidget* wrapInScroll(QWidget* widget); + s32 addTab(QWidget* widget, const QString& label, TabId id); + void buildTabs(); + void updateTabVisibility(); private: Ptcl::Document* mDocument{nullptr}; @@ -92,12 +121,20 @@ class InspectorPanel final : public QWidget { StripeInspector* mStripeInspector{nullptr}; FluctuationInspector* mFluctuationInspector{nullptr}; - QTabWidget* mTabWidget{nullptr}; - ChildEditorWidget* mChildEditorWidget{nullptr}; - FieldEditorWidget* mFieldEditorWidget{nullptr}; - CollapsibleWidget* mStripeSection{nullptr}; + FieldCollisionInspector* mFieldCollisionInspector{nullptr}; + FieldConvergenceInspector* mFieldConvergenceInspector{nullptr}; + FieldMagnetInspector* mFieldMagnetInspector{nullptr}; + FieldPosAddInspector* mFieldPosAddInspector{nullptr}; + FieldRandomInspector* mFieldRandomInspector{nullptr}; + FieldSpinInspector* mFieldSpinInspector{nullptr}; + + QTabWidget* mTabWidget{nullptr}; + QHash mTabIndex{}; + + Ptcl::EmitterType mLastEmitterType{}; + bool mLastEmitterHasStripe{}; }; diff --git a/include/ptcl/ptclBinary.h b/include/ptcl/ptclBinary.h index 5928cd1..66b57fe 100644 --- a/include/ptcl/ptclBinary.h +++ b/include/ptcl/ptclBinary.h @@ -1,7 +1,6 @@ #pragma once #include "ptcl/ptclEnum.h" -#include "ptcl/ptclFieldData.h" #include "typedefs.h" #include "util/bitflagUtil.h" #include "math/matrix.h" @@ -406,7 +405,7 @@ struct alignas(4) BinFieldRandomData { binVec3f fieldRandomVelAdd; // 0x04 BinFieldRandomData() = default; - BinFieldRandomData(const Ptcl::FieldData::FieldRandomData& fieldRandomData); + BinFieldRandomData(const Ptcl::Emitter& emitterData); friend QDataStream& operator>>(QDataStream& in, BinFieldRandomData& item); friend QDataStream& operator<<(QDataStream& out, const BinFieldRandomData& item); @@ -424,7 +423,7 @@ struct alignas(4) BinFieldMagnetData { BitFlag fieldMagnetFlag; // 0x10 BinFieldMagnetData() = default; - BinFieldMagnetData(const Ptcl::FieldData::FieldMagnetData& fieldMagnetData); + BinFieldMagnetData(const Ptcl::Emitter& emitterData); friend QDataStream& operator>>(QDataStream& in, BinFieldMagnetData& item); friend QDataStream& operator<<(QDataStream& out, const BinFieldMagnetData& item); @@ -441,7 +440,7 @@ struct alignas(4) BinFieldSpinData { FieldSpinAxis fieldSpinAxis; // 0x04 BinFieldSpinData() = default; - BinFieldSpinData(const Ptcl::FieldData::FieldSpinData& fieldSpinData); + BinFieldSpinData(const Ptcl::Emitter& emitterData); friend QDataStream& operator>>(QDataStream& in, BinFieldSpinData& item); friend QDataStream& operator<<(QDataStream& out, const BinFieldSpinData& item); @@ -461,7 +460,7 @@ struct alignas(4) BinFieldCollisionData { f32 fieldCollisionCoef; // 0x08 BinFieldCollisionData() = default; - BinFieldCollisionData(const Ptcl::FieldData::FieldCollisionData& fieldCollisionData); + BinFieldCollisionData(const Ptcl::Emitter& emitterData); friend QDataStream& operator>>(QDataStream& in, BinFieldCollisionData& item); friend QDataStream& operator<<(QDataStream& out, const BinFieldCollisionData& item); @@ -479,7 +478,7 @@ struct alignas(4) BinFieldConvergenceData { binVec3f fieldConvergencePos; // 0x04 BinFieldConvergenceData() = default; - BinFieldConvergenceData(const Ptcl::FieldData::FieldConvergenceData& fieldConvergenceData); + BinFieldConvergenceData(const Ptcl::Emitter& emitterData); friend QDataStream& operator>>(QDataStream& in, BinFieldConvergenceData& item); friend QDataStream& operator<<(QDataStream& out, const BinFieldConvergenceData& item); @@ -496,7 +495,7 @@ struct alignas(4) BinFieldPosAddData { binVec3f fieldPosAdd; // 0x00 BinFieldPosAddData() = default; - BinFieldPosAddData(const Ptcl::FieldData::FieldPosAddData& fieldPosAddData); + BinFieldPosAddData(const Ptcl::Emitter& emitterData); friend QDataStream& operator>>(QDataStream& in, BinFieldPosAddData& item); friend QDataStream& operator<<(QDataStream& out, const BinFieldPosAddData& item); diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index 3b2b01e..aabb6d9 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -6,7 +6,6 @@ #include "ptcl/ptclBinary.h" #include "ptcl/ptclChildData.h" #include "ptcl/ptclEnum.h" -#include "ptcl/ptclFieldData.h" #include "ptcl/ptclSeed.h" #include "ptcl/ptclTexture.h" @@ -56,7 +55,6 @@ class Emitter { ChildFlag::IsFollow, ChildFlag::Unk80 }; - BitFlag fieldFlags{}; }; public: @@ -434,6 +432,106 @@ class Emitter { bool isStripeEmitterCoord() const { return mStripeFlags.isSet(Ptcl::StripeFlag::EmitterCoord); } void setStripeEmitterCoord(bool emitterCoord) { mStripeFlags.set(Ptcl::StripeFlag::EmitterCoord, emitterCoord); } + // ----- Field Properties ----- \\ + + bool isFieldEnabled() const { return mFieldFlags.isSet(FieldFlag::Enabled); } + + const BitFlag& fieldFlags() const { return mFieldFlags; } + void setFieldFlags(const BitFlag& fieldFlags) { mFieldFlags = fieldFlags; } + + // Field Random + + void initFieldRandom(const BinFieldRandomData& randomData); + + bool isFieldRandomEnabled() const { return mFieldFlags.isSet(FieldFlag::Random); } + void setFieldRandomEnabled(bool enabled) { mFieldFlags.set(FieldFlag::Random, enabled); } + + s32 fieldRandomBlank() const { return mFieldRandom.randomBlank; } + void setFieldRandomBlank(s32 blank) { mFieldRandom.randomBlank = blank; } + + const Math::Vector3f& fieldRandomVelAdd() const { return mFieldRandom.randomVelAdd; } + void setFieldRandomVelAdd(const Math::Vector3f& velAdd) { mFieldRandom.randomVelAdd = velAdd; } + + // Field Magnet + + + void initFieldMagnet(const BinFieldMagnetData& magnetData); + + bool isFieldMagnetEnabled() const { return mFieldFlags.isSet(FieldFlag::Magnet); } + void setFieldMagnetEnabled(bool enabled) { mFieldFlags.set(FieldFlag::Magnet, enabled); } + + f32 fieldMagnetPower() const { return mFieldMagnet.magnetPower; } + void setFieldMagnetPower(f32 power) { mFieldMagnet.magnetPower = power; } + + const Math::Vector3f& fieldMagnetPos() const { return mFieldMagnet.magnetPos; } + void setFieldMagnetPos(const Math::Vector3f& pos) { mFieldMagnet.magnetPos = pos; } + + const BitFlag& fieldMagnetFlag() const { return mFieldMagnet.magnetFlag; } + + bool isFieldMagnetAxisTargetX() const { return mFieldMagnet.magnetFlag.isSet(FieldMagnetFlag::AxisTargetX); } + void setFieldMagnetAxisTargetX(bool enabled) { mFieldMagnet.magnetFlag.set(FieldMagnetFlag::AxisTargetX, enabled); } + + bool isFieldMagnetAxisTargetY() const { return mFieldMagnet.magnetFlag.isSet(FieldMagnetFlag::AxisTargetY); } + void setFieldMagnetAxisTargetY(bool enabled) { mFieldMagnet.magnetFlag.set(FieldMagnetFlag::AxisTargetY, enabled); } + + bool isFieldMagnetAxisTargetZ() const { return mFieldMagnet.magnetFlag.isSet(FieldMagnetFlag::AxisTargetZ); } + void setFieldMagnetAxisTargetZ(bool enabled) { mFieldMagnet.magnetFlag.set(FieldMagnetFlag::AxisTargetZ, enabled); } + + // Field Spin + + void initFieldSpin(const BinFieldSpinData& spinData); + + bool isFieldSpinEnabled() const { return mFieldFlags.isSet(FieldFlag::Spin); } + void setFieldSpinEnabled(bool enabled) { mFieldFlags.set(FieldFlag::Spin, enabled); } + + s32 fieldSpinRotate() const { return mFieldSpin.spinRotate; } + void setFieldSpinRotate(s32 rotate) { mFieldSpin.spinRotate = rotate; } + + FieldSpinAxis fieldSpinAxis() const { return mFieldSpin.spinAxis; } + void setFieldSpinAxis(FieldSpinAxis axis) { mFieldSpin.spinAxis = axis; } + + // Field Collision + + void initFieldCollision(const BinFieldCollisionData& collisionData); + + bool isFieldCollisionEnabled() const { return mFieldFlags.isSet(FieldFlag::Collision); } + void setFieldCollisionEnabled(bool enabled) { mFieldFlags.set(FieldFlag::Collision, enabled); } + + FieldCollisionType fieldCollisionType() const { return mFieldCollision.collisionType; } + void setFieldCollisionType(FieldCollisionType type) { mFieldCollision.collisionType = type; } + + bool fieldCollisionIsWorld() const { return mFieldCollision.collisionIsWorld; } + void setFieldCollisionIsWorld(bool isWorld) { mFieldCollision.collisionIsWorld = isWorld; } + + f32 fieldCollisionCoord() const { return mFieldCollision.collisionCoord; } + void setFieldCollisionCoord(f32 coord) { mFieldCollision.collisionCoord = coord; } + + f32 fieldCollisionCoef() const { return mFieldCollision.collisionCoef; } + void setFieldCollisionCoef(f32 coef) { mFieldCollision.collisionCoef = coef; } + + // Field Convergence + + void initFieldConvergence(const BinFieldConvergenceData& convergenceData); + + bool isFieldConvergenceEnabled() const { return mFieldFlags.isSet(FieldFlag::Convergence); } + void setFieldConvergenceEnabled(bool enabled) { mFieldFlags.set(FieldFlag::Convergence, enabled); } + + FieldConvergenceType fieldConvergenceType() const { return mFieldConvergence.convergenceType; } + void setFieldConvergenceType(FieldConvergenceType type) { mFieldConvergence.convergenceType = type; } + + const Math::Vector3f& fieldConvergencePos() const { return mFieldConvergence.convergencePos; } + void setFieldConvergencePos(const Math::Vector3f& pos) { mFieldConvergence.convergencePos = pos; } + + // Field PosAdd + + void initFieldPosAdd(const BinFieldPosAddData& posAddData); + + bool isFieldPosAddEnabled() const { return mFieldFlags.isSet(FieldFlag::PosAdd); } + void setFieldPosAddEnabled(bool enabled) { mFieldFlags.set(FieldFlag::PosAdd, enabled); } + + const Math::Vector3f& fieldPosAddPosition() const { return mFieldPosAdd.posAdd; } + void setFieldPosAddPosition(const Math::Vector3f& pos) { mFieldPosAdd.posAdd = pos; } + BitFlag& flags(); const BitFlag& flags() const; @@ -441,15 +539,10 @@ class Emitter { void setComplexProperties(const ComplexProperties& complexProperties); void setChildFlags(const BitFlag& childFlags); - void setFieldFlags(const BitFlag& fieldFlags); const ChildData& childData() const; ChildData& childData(); - const FieldData& fieldData() const; - FieldData& fieldData(); - void setFieldData(const FieldData& fieldData); - void initFromBinary(const BinCommonEmitterData& emitterData); void initComplexFromBinary(const BinComplexEmitterData& emitterData); @@ -586,8 +679,46 @@ class Emitter { f32 mStripeDirInterpolate{1.0f}; BitFlag mStripeFlags{}; + // Field Properties + struct { + s32 randomBlank{1}; + Math::Vector3f randomVelAdd{0.0f, 0.0f, 0.0f}; + } mFieldRandom; + + struct { + f32 magnetPower{0.0f}; + Math::Vector3f magnetPos{0.0f, 0.0f, 0.0f}; + BitFlag magnetFlag{ + FieldMagnetFlag::AxisTargetX, + FieldMagnetFlag::AxisTargetY, + FieldMagnetFlag::AxisTargetZ + }; + } mFieldMagnet; + + struct { + s32 spinRotate{0}; + FieldSpinAxis spinAxis{FieldSpinAxis::AxisX}; + } mFieldSpin; + + struct { + FieldCollisionType collisionType{FieldCollisionType::Die}; + bool collisionIsWorld{false}; + f32 collisionCoord{0.0f}; + f32 collisionCoef{0.0f}; + } mFieldCollision; + + struct { + FieldConvergenceType convergenceType{FieldConvergenceType::AssignedPos}; + Math::Vector3f convergencePos{0.0f, 0.0f, 0.0f}; + } mFieldConvergence; + + struct { + Math::Vector3f posAdd{0.0f, 0.0f, 0.0f}; + } mFieldPosAdd; + + BitFlag mFieldFlags{}; + ChildData mChildData{}; - FieldData mFieldData{}; }; diff --git a/include/ptcl/ptclFieldData.h b/include/ptcl/ptclFieldData.h deleted file mode 100644 index 893f4a6..0000000 --- a/include/ptcl/ptclFieldData.h +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once - -#include "typedefs.h" -#include "util/bitflagUtil.h" -#include "ptcl/ptclEnum.h" -#include "math/vector.h" - - -namespace Ptcl { - - -struct BinFieldRandomData; -struct BinFieldMagnetData; -struct BinFieldSpinData; -struct BinFieldCollisionData; -struct BinFieldConvergenceData; -struct BinFieldPosAddData; - - -// ========================================================================== // - - -class FieldData { -public: - struct FieldRandomData { - s32 randomBlank{1}; - Math::Vector3f randomVelAdd{0.0f, 0.0f, 0.0f}; - }; - - struct FieldMagnetData { - f32 magnetPower{0.0f}; - Math::Vector3f magnetPos{0.0f, 0.0f, 0.0f}; - BitFlag magnetFlag{ - FieldMagnetFlag::AxisTargetX, - FieldMagnetFlag::AxisTargetY, - FieldMagnetFlag::AxisTargetZ - }; - }; - - struct FieldSpinData { - s32 spinRotate{0}; - FieldSpinAxis spinAxis{FieldSpinAxis::AxisX}; - }; - - struct FieldCollisionData { - FieldCollisionType collisionType{FieldCollisionType::Die}; - bool collisionIsWorld{false}; - f32 collisionCoord{0.0f}; - f32 collisionCoef{0.0f}; - }; - - struct FieldConvergenceData { - FieldConvergenceType convergenceType{FieldConvergenceType::AssignedPos}; - Math::Vector3f convergencePos{0.0f, 0.0f, 0.0f}; - }; - - struct FieldPosAddData { - Math::Vector3f posAdd{0.0f, 0.0f, 0.0f}; - }; - -public: - FieldData() = default; - - const FieldRandomData& randomData() const; - void setRandomData(const FieldRandomData& randomData); - void initRandomData(const BinFieldRandomData& randomData); - - const FieldMagnetData& magnetData() const; - void setMagnetData(const FieldMagnetData& magnetData); - void initMagnetData(const BinFieldMagnetData& magnetData); - - const FieldSpinData& spinData() const; - void setSpinData(const FieldSpinData& spinData); - void initSpinData(const BinFieldSpinData& spinData); - - const FieldCollisionData& collisionData() const; - void setCollisionData(const FieldCollisionData& collisionData); - void initCollisionData(const BinFieldCollisionData& collisionData); - - const FieldConvergenceData& convergenceData() const; - void setConvergenceData(const FieldConvergenceData& convergenceData); - void initConvergenceData(const BinFieldConvergenceData& convergenceData); - - const FieldPosAddData& posAddData() const; - void setPosAddData(const FieldPosAddData& posAddData); - void initPosAddData(const BinFieldPosAddData& posAddData); - -private: - FieldRandomData mRandomData; - FieldMagnetData mMagnetData; - FieldSpinData mSpinData; - FieldCollisionData mCollisionData; - FieldConvergenceData mConvergenceData; - FieldPosAddData mPosAddData; -}; - - -// ========================================================================== // - - -} // namespace Ptcl diff --git a/src/editor/fieldEditor/fieldEditorWidget.cpp b/src/editor/fieldEditor/fieldEditorWidget.cpp deleted file mode 100644 index 5e6d845..0000000 --- a/src/editor/fieldEditor/fieldEditorWidget.cpp +++ /dev/null @@ -1,175 +0,0 @@ -#include "editor/components/collapsibleWidget.h" -#include "editor/fieldEditor/collisionDataWidget.h" -#include "editor/fieldEditor/convergenceDataWidget.h" -#include "editor/fieldEditor/fieldEditorWidget.h" -#include "editor/fieldEditor/magnetDataWidget.h" -#include "editor/fieldEditor/posAddDataWidget.h" -#include "editor/fieldEditor/randomDataWidget.h" -#include "editor/fieldEditor/spinDataWidget.h" - -#include - - -namespace PtclEditor { - - -// ========================================================================== // - - -FieldEditorWidget::FieldEditorWidget(QWidget* parent) : - QWidget{parent} { - - mRandomDataWidget = new RandomDataWidget(this); - mMagnetDataWidget = new MagnetDataWidget(this); - mSpinDataWidget = new SpinDataWidget(this); - mCollisionDataWidget = new CollisionDataWidget(this); - mConvergenceDataWidget = new ConvergenceDataWidget(this); - mPosAddDataWidget = new PosAddDataWidget(this); - - // Standard Widget - auto* standardWidget = new QWidget(this); - auto* mainLayout = new QVBoxLayout(standardWidget); - standardWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); - standardWidget->setMinimumWidth(400); - - auto* scrollArea = new QScrollArea(this); - scrollArea->setWidget(standardWidget); - scrollArea->setWidgetResizable(true); - scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - scrollArea->setFrameShape(QFrame::NoFrame); - - auto* outerLayout = new QVBoxLayout(this); - outerLayout->addWidget(scrollArea); - outerLayout->setContentsMargins(0, 0, 0, 0); - setLayout(outerLayout); - - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - setupLayout(mainLayout); - setupConnections(); -} - -void FieldEditorWidget::setupLayout(QVBoxLayout* mainLayout) { - // Section Containers - mSectionsContainer = new QWidget(this); - auto* sectionsLayout = new QVBoxLayout(mSectionsContainer); - sectionsLayout->setContentsMargins(0, 0, 0, 0); - - auto addSection = [sectionsLayout](const QString& title, QWidget* widget) { - auto* section = new CollapsibleWidget(title); - section->setContent(widget); - sectionsLayout->addWidget(section); - }; - - addSection("Randomness", mRandomDataWidget); - addSection("Magnetic Force", mMagnetDataWidget); - addSection("Spin Force", mSpinDataWidget); - addSection("Collision Plane", mCollisionDataWidget); - addSection("Convergence", mConvergenceDataWidget); - addSection("Add to Position", mPosAddDataWidget); - - sectionsLayout->addStretch(); - - mainLayout->addWidget(mSectionsContainer); - mainLayout->addStretch(); -} - -void FieldEditorWidget::setupConnections() { - // Randomness - connect(mRandomDataWidget, &RandomDataWidget::dataUpdated, this, [this](const Ptcl::FieldData::FieldRandomData& data) { - if (!mDataPtr) { return; } - mDataPtr->setRandomData(data); - }); - - connect(mRandomDataWidget, &RandomDataWidget::isEnabledUpdated, this, [this](bool isEnabled) { - if (!mDataPtr) { return; } - mFieldFlag.set(Ptcl::FieldFlag::Random, isEnabled); - emit flagsUpdated(mFieldFlag); - }); - - // Magnet - connect(mMagnetDataWidget, &MagnetDataWidget::dataUpdated, this, [this](const Ptcl::FieldData::FieldMagnetData& data) { - if (!mDataPtr) { return; } - mDataPtr->setMagnetData(data); - }); - - connect(mMagnetDataWidget, &MagnetDataWidget::isEnabledUpdated, this, [this](bool isEnabled) { - if (!mDataPtr) { return; } - mFieldFlag.set(Ptcl::FieldFlag::Magnet, isEnabled); - emit flagsUpdated(mFieldFlag); - }); - - // Spin - connect(mSpinDataWidget, &SpinDataWidget::dataUpdated, this, [this](const Ptcl::FieldData::FieldSpinData& data) { - if (!mDataPtr) { return; } - mDataPtr->setSpinData(data); - }); - - connect(mSpinDataWidget, &SpinDataWidget::isEnabledUpdated, this, [this](bool isEnabled) { - if (!mDataPtr) { return; } - mFieldFlag.set(Ptcl::FieldFlag::Spin, isEnabled); - emit flagsUpdated(mFieldFlag); - }); - - // Collision - connect(mCollisionDataWidget, &CollisionDataWidget::dataUpdated, this, [this](const Ptcl::FieldData::FieldCollisionData& data) { - if (!mDataPtr) { return; } - mDataPtr->setCollisionData(data); - }); - - connect(mCollisionDataWidget, &CollisionDataWidget::isEnabledUpdated, this, [this](bool isEnabled) { - if (!mDataPtr) { return; } - mFieldFlag.set(Ptcl::FieldFlag::Collision, isEnabled); - emit flagsUpdated(mFieldFlag); - }); - - // Convergence - connect(mConvergenceDataWidget, &ConvergenceDataWidget::dataUpdated, this, [this](const Ptcl::FieldData::FieldConvergenceData& data) { - if (!mDataPtr) { return; } - mDataPtr->setConvergenceData(data); - }); - - connect(mConvergenceDataWidget, &ConvergenceDataWidget::isEnabledUpdated, this, [this](bool isEnabled) { - if (!mDataPtr) { return; } - mFieldFlag.set(Ptcl::FieldFlag::Convergence, isEnabled); - emit flagsUpdated(mFieldFlag); - }); - - // PosAdd - connect(mPosAddDataWidget, &PosAddDataWidget::dataUpdated, this, [this](const Ptcl::FieldData::FieldPosAddData& data) { - if (!mDataPtr) { return; } - mDataPtr->setPosAddData(data); - }); - - connect(mPosAddDataWidget, &PosAddDataWidget::isEnabledUpdated, this, [this](bool isEnabled) { - if (!mDataPtr) { return; } - mFieldFlag.set(Ptcl::FieldFlag::PosAdd, isEnabled); - emit flagsUpdated(mFieldFlag); - }); -} - -void FieldEditorWidget::setData(Ptcl::FieldData* fieldData, const BitFlag& fieldFlag) { - QSignalBlocker b1(mRandomDataWidget); - QSignalBlocker b2(mMagnetDataWidget); - QSignalBlocker b3(mSpinDataWidget); - QSignalBlocker b4(mCollisionDataWidget); - QSignalBlocker b5(mConvergenceDataWidget); - QSignalBlocker b6(mPosAddDataWidget); - - mDataPtr = fieldData; - mFieldFlag = fieldFlag; - - mRandomDataWidget->setData(mDataPtr->randomData(), mFieldFlag.isSet(Ptcl::FieldFlag::Random)); - mMagnetDataWidget->setData(mDataPtr->magnetData(), mFieldFlag.isSet(Ptcl::FieldFlag::Magnet)); - mSpinDataWidget->setData(mDataPtr->spinData(), mFieldFlag.isSet(Ptcl::FieldFlag::Spin)); - mCollisionDataWidget->setData(mDataPtr->collisionData(), mFieldFlag.isSet(Ptcl::FieldFlag::Collision)); - mConvergenceDataWidget->setData(mDataPtr->convergenceData(), mFieldFlag.isSet(Ptcl::FieldFlag::Convergence)); - mPosAddDataWidget->setData(mDataPtr->posAddData(), mFieldFlag.isSet(Ptcl::FieldFlag::PosAdd)); -} - - -// ========================================================================== // - - -} // namespace PtclEditor diff --git a/src/editor/fieldEditor/magnetDataWidget.cpp b/src/editor/fieldEditor/magnetDataWidget.cpp deleted file mode 100644 index 5980b20..0000000 --- a/src/editor/fieldEditor/magnetDataWidget.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "editor/fieldEditor/magnetDataWidget.h" - -#include - -namespace PtclEditor { - - -// ========================================================================== // - - -FieldEditorWidget::MagnetDataWidget::MagnetDataWidget(QWidget* parent) : - QWidget{parent} { - - // TODO: Better ranges? - mMagnetPowerSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); - mMagnetPosSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); - mEnabledCheckBox.setText("Enabled"); - - mControlsWidget = new QWidget(this); - auto* controlsLayout = new QFormLayout(mControlsWidget); - controlsLayout->addRow("Magnet Power:", &mMagnetPowerSpinBox); - controlsLayout->addRow("Magnet Position:", &mMagnetPosSpinBox); - controlsLayout->addRow("Target X-Axis:", &mAxisXCheckBox); - controlsLayout->addRow("Target Y-Axis:", &mAxisYCheckBox); - controlsLayout->addRow("Target Z-Axis:", &mAxisZCheckBox); - - auto* mainLayout = new QFormLayout(this); - mainLayout->addRow("Magnetic Force:", &mEnabledCheckBox); - mainLayout->addRow(mControlsWidget); - - setupConnections(); -} - -void FieldEditorWidget::MagnetDataWidget::setupConnections() { - // Is Enabled - connect(&mEnabledCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - emit isEnabledUpdated(checked); - mControlsWidget->setEnabled(checked); - }); - - // Magnet Power - connect(&mMagnetPowerSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mData.magnetPower = static_cast(value); - emit dataUpdated(mData); - }); - - // Magnet pos - connect(&mMagnetPosSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mData.magnetPos = mMagnetPosSpinBox.getVector(); - emit dataUpdated(mData); - }); - - // Axis X - connect(&mAxisXCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - mData.magnetFlag.set(Ptcl::FieldMagnetFlag::AxisTargetX, checked); - emit dataUpdated(mData); - }); - - // Axis Y - connect(&mAxisYCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - mData.magnetFlag.set(Ptcl::FieldMagnetFlag::AxisTargetY, checked); - emit dataUpdated(mData); - }); - - // Axis Z - connect(&mAxisZCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - mData.magnetFlag.set(Ptcl::FieldMagnetFlag::AxisTargetZ, checked); - emit dataUpdated(mData); - }); -} - -void FieldEditorWidget::MagnetDataWidget::setData(const Ptcl::FieldData::FieldMagnetData& data, bool isEnabled) { - QSignalBlocker b1(mMagnetPowerSpinBox); - QSignalBlocker b2(mMagnetPosSpinBox); - QSignalBlocker b3(mAxisXCheckBox); - QSignalBlocker b4(mAxisYCheckBox); - QSignalBlocker b5(mAxisZCheckBox); - QSignalBlocker b6(mEnabledCheckBox); - - mData = data; - - mMagnetPowerSpinBox.setValue(mData.magnetPower); - mMagnetPosSpinBox.setVector(mData.magnetPos); - mAxisXCheckBox.setChecked(mData.magnetFlag.isSet(Ptcl::FieldMagnetFlag::AxisTargetX)); - mAxisYCheckBox.setChecked(mData.magnetFlag.isSet(Ptcl::FieldMagnetFlag::AxisTargetY)); - mAxisZCheckBox.setChecked(mData.magnetFlag.isSet(Ptcl::FieldMagnetFlag::AxisTargetZ)); - mEnabledCheckBox.setChecked(isEnabled); - mControlsWidget->setEnabled(isEnabled); -} - - -// ========================================================================== // - - -} // namespace PtclEditor diff --git a/src/editor/fieldEditor/collisionDataWidget.cpp b/src/editor/inspector/field/fieldCollisionInspector.cpp similarity index 50% rename from src/editor/fieldEditor/collisionDataWidget.cpp rename to src/editor/inspector/field/fieldCollisionInspector.cpp index 001e8da..73cae0b 100644 --- a/src/editor/fieldEditor/collisionDataWidget.cpp +++ b/src/editor/inspector/field/fieldCollisionInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/fieldEditor/collisionDataWidget.h" +#include "editor/inspector/field/fieldCollisionInspector.h" #include @@ -9,8 +9,8 @@ namespace PtclEditor { // ========================================================================== // -FieldEditorWidget::CollisionDataWidget::CollisionDataWidget(QWidget* parent) : - QWidget{parent} { +FieldCollisionInspector::FieldCollisionInspector(QWidget* parent) : + InspectorWidgetBase{parent} { // TODO: Better ranges? mIsWorldCheckBox.setText("Collision in world coordinates"); @@ -32,51 +32,77 @@ FieldEditorWidget::CollisionDataWidget::CollisionDataWidget(QWidget* parent) : setupConnections(); } -void FieldEditorWidget::CollisionDataWidget::setupConnections() { +void FieldCollisionInspector::setupConnections() { // Is Enabled connect(&mEnabledCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - emit isEnabledUpdated(checked); - mControlsWidget->setEnabled(checked); + setEmitterProperty( + "Toggle Field Collision", + "ToggleFieldCollision", + &Ptcl::Emitter::isFieldCollisionEnabled, + &Ptcl::Emitter::setFieldCollisionEnabled, + checked + ); }); // CollisionType connect(&mCollisionTypeSpinBox, &QComboBox::currentIndexChanged, this, [this]() { - mData.collisionType = mCollisionTypeSpinBox.currentEnum(); - emit dataUpdated(mData); + const auto type = mCollisionTypeSpinBox.currentEnum(); + setEmitterProperty( + "Set Field Collision Type", + "SetFieldCollisionType", + &Ptcl::Emitter::fieldCollisionType, + &Ptcl::Emitter::setFieldCollisionType, + type + ); }); // Is World connect(&mIsWorldCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - mData.collisionIsWorld = checked; - emit dataUpdated(mData); + setEmitterProperty( + "Toggle Field Collision WorldCoords", + "ToggleFieldCollisionInWorld", + &Ptcl::Emitter::fieldCollisionIsWorld, + &Ptcl::Emitter::setFieldCollisionIsWorld, + checked + ); }); // Coef - connect(&mCoefSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mData.collisionCoef = static_cast(value); - emit dataUpdated(mData); + connect(&mCoefSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { + setEmitterProperty( + "Set Field Collision Bounce Rate", + "SetFieldCollisionCoef", + &Ptcl::Emitter::fieldCollisionCoef, + &Ptcl::Emitter::setFieldCollisionCoef, + static_cast(value) + ); }); // Coord connect(&mCoordSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mData.collisionCoord = static_cast(value); - emit dataUpdated(mData); + setEmitterProperty( + "Set Field Collision Plane Coord", + "SetFieldCollisionCoord", + &Ptcl::Emitter::fieldCollisionCoord, + &Ptcl::Emitter::setFieldCollisionCoord, + static_cast(value) + ); }); } -void FieldEditorWidget::CollisionDataWidget::setData(const Ptcl::FieldData::FieldCollisionData& data, bool isEnabled) { +void FieldCollisionInspector::populateProperties() { QSignalBlocker b1(mCollisionTypeSpinBox); QSignalBlocker b2(mIsWorldCheckBox); QSignalBlocker b3(mCoefSpinBox); QSignalBlocker b4(mCoordSpinBox); QSignalBlocker b5(mEnabledCheckBox); - mData = data; + mCollisionTypeSpinBox.setCurrentEnum(mEmitter->fieldCollisionType()); + mIsWorldCheckBox.setChecked(mEmitter->fieldCollisionIsWorld()); + mCoefSpinBox.setValue(mEmitter->fieldCollisionCoef()); + mCoordSpinBox.setValue(mEmitter->fieldCollisionCoord()); - mCollisionTypeSpinBox.setCurrentEnum(mData.collisionType); - mIsWorldCheckBox.setChecked(mData.collisionIsWorld); - mCoefSpinBox.setValue(mData.collisionCoef); - mCoordSpinBox.setValue(mData.collisionCoord); + const bool isEnabled = mEmitter->isFieldCollisionEnabled(); mEnabledCheckBox.setChecked(isEnabled); mControlsWidget->setEnabled(isEnabled); } diff --git a/src/editor/fieldEditor/convergenceDataWidget.cpp b/src/editor/inspector/field/fieldConvergenceInspector.cpp similarity index 50% rename from src/editor/fieldEditor/convergenceDataWidget.cpp rename to src/editor/inspector/field/fieldConvergenceInspector.cpp index 7890a03..7e2d29e 100644 --- a/src/editor/fieldEditor/convergenceDataWidget.cpp +++ b/src/editor/inspector/field/fieldConvergenceInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/fieldEditor/convergenceDataWidget.h" +#include "editor/inspector/field/fieldConvergenceInspector.h" #include @@ -9,8 +9,8 @@ namespace PtclEditor { // ========================================================================== // -FieldEditorWidget::ConvergenceDataWidget::ConvergenceDataWidget(QWidget* parent) : - QWidget{parent} { +FieldConvergenceInspector::FieldConvergenceInspector(QWidget* parent) : + InspectorWidgetBase{parent} { // TODO: Better ranges? mPosSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); @@ -28,35 +28,52 @@ FieldEditorWidget::ConvergenceDataWidget::ConvergenceDataWidget(QWidget* parent) setupConnections(); } -void FieldEditorWidget::ConvergenceDataWidget::setupConnections() { +void FieldConvergenceInspector::setupConnections() { // Is Enabled connect(&mEnabledCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - emit isEnabledUpdated(checked); - mControlsWidget->setEnabled(checked); + setEmitterProperty( + "Toggle Field Convergence", + "ToggleFieldConvergence", + &Ptcl::Emitter::isFieldConvergenceEnabled, + &Ptcl::Emitter::setFieldConvergenceEnabled, + checked + ); }); // Type connect(&mTypeSpinBox, &QComboBox::currentIndexChanged, this, [this]() { - mData.convergenceType = mTypeSpinBox.currentEnum(); - emit dataUpdated(mData); + const auto type = mTypeSpinBox.currentEnum(); + setEmitterProperty( + "Set Field Convergence Type", + "SetFieldConvergenceType", + &Ptcl::Emitter::fieldConvergenceType, + &Ptcl::Emitter::setFieldConvergenceType, + type + ); }); // Position connect(&mPosSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mData.convergencePos = mPosSpinBox.getVector(); - emit dataUpdated(mData); + const auto pos = mPosSpinBox.getVector(); + setEmitterProperty( + "Set Field Convergence Pos", + "SetFieldConvergencePos", + &Ptcl::Emitter::fieldConvergencePos, + &Ptcl::Emitter::setFieldConvergencePos, + pos + ); }); } -void FieldEditorWidget::ConvergenceDataWidget::setData(const Ptcl::FieldData::FieldConvergenceData& data, bool isEnabled) { +void FieldConvergenceInspector::populateProperties() { QSignalBlocker b1(mTypeSpinBox); QSignalBlocker b2(mPosSpinBox); QSignalBlocker b3(mEnabledCheckBox); - mData = data; + mTypeSpinBox.setCurrentEnum(mEmitter->fieldConvergenceType()); + mPosSpinBox.setVector(mEmitter->fieldConvergencePos()); - mTypeSpinBox.setCurrentEnum(mData.convergenceType); - mPosSpinBox.setVector(mData.convergencePos); + const bool isEnabled = mEmitter->isFieldConvergenceEnabled(); mEnabledCheckBox.setChecked(isEnabled); mControlsWidget->setEnabled(isEnabled); } diff --git a/src/editor/inspector/field/fieldMagnetInspector.cpp b/src/editor/inspector/field/fieldMagnetInspector.cpp new file mode 100644 index 0000000..2bae27d --- /dev/null +++ b/src/editor/inspector/field/fieldMagnetInspector.cpp @@ -0,0 +1,126 @@ +#include "editor/inspector/field/fieldMagnetInspector.h" + +#include + +namespace PtclEditor { + + +// ========================================================================== // + + +FieldMagnetInspector::FieldMagnetInspector(QWidget* parent) : + InspectorWidgetBase{parent} { + + // TODO: Better ranges? + mMagnetPowerSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); + mMagnetPosSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); + mEnabledCheckBox.setText("Enabled"); + + mControlsWidget = new QWidget(this); + auto* controlsLayout = new QFormLayout(mControlsWidget); + controlsLayout->addRow("Magnet Power:", &mMagnetPowerSpinBox); + controlsLayout->addRow("Magnet Position:", &mMagnetPosSpinBox); + controlsLayout->addRow("Target X-Axis:", &mAxisXCheckBox); + controlsLayout->addRow("Target Y-Axis:", &mAxisYCheckBox); + controlsLayout->addRow("Target Z-Axis:", &mAxisZCheckBox); + + auto* mainLayout = new QFormLayout(this); + mainLayout->addRow("Magnetic Force:", &mEnabledCheckBox); + mainLayout->addRow(mControlsWidget); + + setupConnections(); +} + +void FieldMagnetInspector::setupConnections() { + // Is Enabled + connect(&mEnabledCheckBox, &QCheckBox::clicked, this, [this](bool checked) { + setEmitterProperty( + "Toggle Field Magnet", + "ToggleFieldMagnet", + &Ptcl::Emitter::isFieldMagnetEnabled, + &Ptcl::Emitter::setFieldMagnetEnabled, + checked + ); + }); + + // Magnet Power + connect(&mMagnetPowerSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { + setEmitterProperty( + "Set Field Magnet Power", + "SetFieldMagnetPower", + &Ptcl::Emitter::fieldMagnetPower, + &Ptcl::Emitter::setFieldMagnetPower, + static_cast(value) + ); + }); + + // Magnet pos + connect(&mMagnetPosSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { + const auto pos = mMagnetPosSpinBox.getVector(); + setEmitterProperty( + "Set Field Magnet Pos", + "SetFieldMagnetPos", + &Ptcl::Emitter::fieldMagnetPos, + &Ptcl::Emitter::setFieldMagnetPos, + pos + ); + }); + + // Axis X + connect(&mAxisXCheckBox, &QCheckBox::clicked, this, [this](bool checked) { + setEmitterProperty( + "Toggle Field Magnet X-Target", + "ToggleFieldMagnetTargetX", + &Ptcl::Emitter::isFieldMagnetAxisTargetX, + &Ptcl::Emitter::setFieldMagnetAxisTargetX, + checked + ); + }); + + // Axis Y + connect(&mAxisYCheckBox, &QCheckBox::clicked, this, [this](bool checked) { + setEmitterProperty( + "Toggle Field Magnet Y-Target", + "ToggleFieldMagnetTargetY", + &Ptcl::Emitter::isFieldMagnetAxisTargetY, + &Ptcl::Emitter::setFieldMagnetAxisTargetY, + checked + ); + }); + + // Axis Z + connect(&mAxisZCheckBox, &QCheckBox::clicked, this, [this](bool checked) { + setEmitterProperty( + "Toggle Field Magnet Z-Target", + "ToggleFieldMagnetTargetZ", + &Ptcl::Emitter::isFieldMagnetAxisTargetZ, + &Ptcl::Emitter::setFieldMagnetAxisTargetZ, + checked + ); + }); +} + +void FieldMagnetInspector::populateProperties() { + QSignalBlocker b1(mMagnetPowerSpinBox); + QSignalBlocker b2(mMagnetPosSpinBox); + QSignalBlocker b3(mAxisXCheckBox); + QSignalBlocker b4(mAxisYCheckBox); + QSignalBlocker b5(mAxisZCheckBox); + QSignalBlocker b6(mEnabledCheckBox); + + mMagnetPowerSpinBox.setValue(mEmitter->fieldMagnetPower()); + mMagnetPosSpinBox.setVector(mEmitter->fieldMagnetPos()); + mAxisXCheckBox.setChecked(mEmitter->isFieldMagnetAxisTargetX()); + mAxisYCheckBox.setChecked(mEmitter->isFieldMagnetAxisTargetY()); + mAxisZCheckBox.setChecked(mEmitter->isFieldMagnetAxisTargetZ()); + + const bool isEnabled = mEmitter->isFieldMagnetEnabled(); + mEnabledCheckBox.setChecked(isEnabled); + mControlsWidget->setEnabled(isEnabled); +} + + +// ========================================================================== // + + +} // namespace PtclEditor diff --git a/src/editor/fieldEditor/posAddDataWidget.cpp b/src/editor/inspector/field/fieldPosAddInspector.cpp similarity index 56% rename from src/editor/fieldEditor/posAddDataWidget.cpp rename to src/editor/inspector/field/fieldPosAddInspector.cpp index 1fa4296..898d164 100644 --- a/src/editor/fieldEditor/posAddDataWidget.cpp +++ b/src/editor/inspector/field/fieldPosAddInspector.cpp @@ -1,17 +1,16 @@ -#include "editor/fieldEditor/posAddDataWidget.h" - -#include "math/util.h" +#include "editor/inspector/field/fieldPosAddInspector.h" #include + namespace PtclEditor { // ========================================================================== // -FieldEditorWidget::PosAddDataWidget::PosAddDataWidget(QWidget* parent) : - QWidget{parent} { +FieldPosAddInspector::FieldPosAddInspector(QWidget* parent) : + InspectorWidgetBase{parent} { // TODO: Set better limits? mPosSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); @@ -28,27 +27,38 @@ FieldEditorWidget::PosAddDataWidget::PosAddDataWidget(QWidget* parent) : setupConnections(); } -void FieldEditorWidget::PosAddDataWidget::setupConnections() { +void FieldPosAddInspector::setupConnections() { // Is Enabled connect(&mEnabledCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - emit isEnabledUpdated(checked); - mControlsWidget->setEnabled(checked); + setEmitterProperty( + "Toggle Field PosAdd", + "ToggleFieldPosAdd", + &Ptcl::Emitter::isFieldPosAddEnabled, + &Ptcl::Emitter::setFieldPosAddEnabled, + checked + ); }); // Pos Add connect(&mPosSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mData.posAdd = mPosSpinBox.getVector(); - emit dataUpdated(mData); + const auto pos = mPosSpinBox.getVector(); + setEmitterProperty( + "Set Field PosAdd Position", + "SetFieldPosAdd", + &Ptcl::Emitter::fieldPosAddPosition, + &Ptcl::Emitter::setFieldPosAddPosition, + pos + ); }); } -void FieldEditorWidget::PosAddDataWidget::setData(const Ptcl::FieldData::FieldPosAddData& data, bool isEnabled) { +void FieldPosAddInspector::populateProperties() { QSignalBlocker b1(mPosSpinBox); QSignalBlocker b2(mEnabledCheckBox); - mData = data; + mPosSpinBox.setVector(mEmitter->fieldPosAddPosition()); - mPosSpinBox.setVector(mData.posAdd); + const bool isEnabled = mEmitter->isFieldPosAddEnabled(); mEnabledCheckBox.setChecked(isEnabled); mControlsWidget->setEnabled(isEnabled); } diff --git a/src/editor/fieldEditor/randomDataWidget.cpp b/src/editor/inspector/field/fieldRandomInspector.cpp similarity index 55% rename from src/editor/fieldEditor/randomDataWidget.cpp rename to src/editor/inspector/field/fieldRandomInspector.cpp index a53a081..9a7629b 100644 --- a/src/editor/fieldEditor/randomDataWidget.cpp +++ b/src/editor/inspector/field/fieldRandomInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/fieldEditor/randomDataWidget.h" +#include "editor/inspector/field/fieldRandomInspector.h" #include @@ -8,8 +8,8 @@ namespace PtclEditor { // ========================================================================== // -FieldEditorWidget::RandomDataWidget::RandomDataWidget(QWidget* parent) : - QWidget{parent} { +FieldRandomInspector::FieldRandomInspector(QWidget* parent) : + InspectorWidgetBase{parent} { // TODO: Better ranges? mRandomVelAddSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); @@ -28,35 +28,51 @@ FieldEditorWidget::RandomDataWidget::RandomDataWidget(QWidget* parent) : setupConnections(); } -void FieldEditorWidget::RandomDataWidget::setupConnections() { +void FieldRandomInspector::setupConnections() { // Is Enabled connect(&mEnabledCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - emit isEnabledUpdated(checked); - mControlsWidget->setEnabled(checked); + setEmitterProperty( + "Toggle Field Random", + "ToggleFieldRandom", + &Ptcl::Emitter::isFieldRandomEnabled, + &Ptcl::Emitter::setFieldRandomEnabled, + checked + ); }); // Random Vel Add connect(&mRandomVelAddSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mData.randomVelAdd = mRandomVelAddSpinBox.getVector(); - emit dataUpdated(mData); + const auto velocity = mRandomVelAddSpinBox.getVector(); + setEmitterProperty( + "Set Field Random Velocity", + "SetFieldRandomVelAdd", + &Ptcl::Emitter::fieldRandomVelAdd, + &Ptcl::Emitter::setFieldRandomVelAdd, + velocity + ); }); // Random Blank connect(&mRandomBlankSpinBox, &QSpinBox::valueChanged, this, [this](s32 value) { - mData.randomBlank = value; - emit dataUpdated(mData); + setEmitterProperty( + "Set Field Random Blank", + "SetFieldRandomBlank", + &Ptcl::Emitter::fieldRandomBlank, + &Ptcl::Emitter::setFieldRandomBlank, + value + ); }); } -void FieldEditorWidget::RandomDataWidget::setData(const Ptcl::FieldData::FieldRandomData& data, bool isEnabled) { +void FieldRandomInspector::populateProperties() { QSignalBlocker b1(mRandomBlankSpinBox); QSignalBlocker b2(mRandomVelAddSpinBox); QSignalBlocker b3(mEnabledCheckBox); - mData = data; + mRandomBlankSpinBox.setValue(mEmitter->fieldRandomBlank()); + mRandomVelAddSpinBox.setVector(mEmitter->fieldRandomVelAdd()); - mRandomBlankSpinBox.setValue(mData.randomBlank); - mRandomVelAddSpinBox.setVector(mData.randomVelAdd); + const bool isEnabled = mEmitter->isFieldRandomEnabled(); mEnabledCheckBox.setChecked(isEnabled); mControlsWidget->setEnabled(isEnabled); } diff --git a/src/editor/fieldEditor/spinDataWidget.cpp b/src/editor/inspector/field/fieldSpinInspector.cpp similarity index 53% rename from src/editor/fieldEditor/spinDataWidget.cpp rename to src/editor/inspector/field/fieldSpinInspector.cpp index 0e82596..499c0bd 100644 --- a/src/editor/fieldEditor/spinDataWidget.cpp +++ b/src/editor/inspector/field/fieldSpinInspector.cpp @@ -1,17 +1,16 @@ -#include "editor/fieldEditor/spinDataWidget.h" - -#include "math/util.h" +#include "editor/inspector/field/fieldSpinInspector.h" #include + namespace PtclEditor { // ========================================================================== // -FieldEditorWidget::SpinDataWidget::SpinDataWidget(QWidget* parent) : - QWidget{parent} { +FieldSpinInspector::FieldSpinInspector(QWidget* parent) : + InspectorWidgetBase{parent} { mSpinRotateSpinBox.setRange(-180.0f, 180.0f); mEnabledCheckBox.setText("Enabled"); @@ -28,35 +27,52 @@ FieldEditorWidget::SpinDataWidget::SpinDataWidget(QWidget* parent) : setupConnections(); } -void FieldEditorWidget::SpinDataWidget::setupConnections() { +void FieldSpinInspector::setupConnections() { // Is Enabled connect(&mEnabledCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - emit isEnabledUpdated(checked); - mControlsWidget->setEnabled(checked); + setEmitterProperty( + "Toggle Field Spin", + "ToggleFieldSpin", + &Ptcl::Emitter::isFieldSpinEnabled, + &Ptcl::Emitter::setFieldSpinEnabled, + checked + ); }); // Spin Rotate connect(&mSpinRotateSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mData.spinRotate = Math::Util::deg2idx(static_cast(value)); - emit dataUpdated(mData); + const f32 rotate = Math::Util::deg2idx(static_cast(value)); + setEmitterProperty( + "Set Field Spin Rotation", + "SetFieldSpinRotation", + &Ptcl::Emitter::fieldSpinRotate, + &Ptcl::Emitter::setFieldSpinRotate, + rotate + ); }); // Spin Axis connect(&mSpinAxisSpinBox, &QComboBox::currentIndexChanged, this, [this]() { - mData.spinAxis = mSpinAxisSpinBox.currentEnum(); - emit dataUpdated(mData); + const auto axis = mSpinAxisSpinBox.currentEnum(); + setEmitterProperty( + "Set Field Spin Axis", + "SetFieldSpinAxis", + &Ptcl::Emitter::fieldSpinAxis, + &Ptcl::Emitter::setFieldSpinAxis, + axis + ); }); } -void FieldEditorWidget::SpinDataWidget::setData(const Ptcl::FieldData::FieldSpinData& data, bool isEnabled) { +void FieldSpinInspector::populateProperties() { QSignalBlocker b1(mSpinRotateSpinBox); QSignalBlocker b2(mSpinAxisSpinBox); QSignalBlocker b3(mEnabledCheckBox); - mData = data; + mSpinRotateSpinBox.setValue(Math::Util::to180(Math::Util::idx2deg(mEmitter->fieldSpinRotate()))); + mSpinAxisSpinBox.setCurrentEnum(mEmitter->fieldSpinAxis()); - mSpinRotateSpinBox.setValue(Math::Util::to180(Math::Util::idx2deg(mData.spinRotate))); - mSpinAxisSpinBox.setCurrentEnum(mData.spinAxis); + const bool isEnabled = mEmitter->isFieldSpinEnabled(); mEnabledCheckBox.setChecked(isEnabled); mControlsWidget->setEnabled(isEnabled); } diff --git a/src/editor/inspector/inspectorPanel.cpp b/src/editor/inspector/inspectorPanel.cpp index 2557bcb..f590ac3 100644 --- a/src/editor/inspector/inspectorPanel.cpp +++ b/src/editor/inspector/inspectorPanel.cpp @@ -18,6 +18,13 @@ #include "editor/inspector/velocityInspector.h" #include "editor/inspector/volumeInspector.h" +#include "editor/inspector/field/fieldCollisionInspector.h" +#include "editor/inspector/field/fieldConvergenceInspector.h" +#include "editor/inspector/field/fieldMagnetInspector.h" +#include "editor/inspector/field/fieldPosAddInspector.h" +#include "editor/inspector/field/fieldRandomInspector.h" +#include "editor/inspector/field/fieldSpinInspector.h" + #include namespace PtclEditor { @@ -45,9 +52,15 @@ InspectorPanel::InspectorPanel(QWidget* parent) : mCombinerInspector = new CombinerInspector(this); mStripeInspector = new StripeInspector(this); + mFieldCollisionInspector = new FieldCollisionInspector(this); + mFieldConvergenceInspector = new FieldConvergenceInspector(this); + mFieldMagnetInspector = new FieldMagnetInspector(this); + mFieldPosAddInspector = new FieldPosAddInspector(this); + mFieldRandomInspector = new FieldRandomInspector(this); + mFieldSpinInspector = new FieldSpinInspector(this); + mChildEditorWidget = new ChildEditorWidget(this); mFluctuationInspector = new FluctuationInspector(this); - mFieldEditorWidget = new FieldEditorWidget(this); mTabWidget = new QTabWidget(this); mTabWidget->setTabPosition(QTabWidget::West); @@ -58,83 +71,134 @@ InspectorPanel::InspectorPanel(QWidget* parent) : setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + buildTabs(); setupConnections(); } -void InspectorPanel::rebuildTabs() { +s32 InspectorPanel::addTab(QWidget* widget, const QString& label, TabId id) { + s32 idx = mTabWidget->addTab(widget, label); + mTabWidget->tabBar()->setTabData(idx, QVariant::fromValue(id)); + mTabIndex[id] = idx; + return idx; +} + +void InspectorPanel::buildTabs() { + // Emitter + addTab(mGeneralInspector, "General", TabId::General); + addTab(mStripeInspector, "Stripe", TabId::Stripe); + addTab(mLifespanInspector, "Life", TabId::Lifespan); + addTab(mTerminationInspector, "Termination", TabId::Termination); + addTab(mGravityInspector, "Gravity", TabId::Gravity); + addTab(mEmissionInspector, "Emission", TabId::Emission); + addTab(mVolumeInspector, "Volume", TabId::Volume); + addTab(mVelocityInspector, "Velocity", TabId::Velocity); + addTab(mTextureInspector, "Texture", TabId::Texture); + addTab(mColorInspector, "Color", TabId::Color); + addTab(mCombinerInspector, "Combiner", TabId::Combiner); + addTab(mAlphaAnimInspector, "Alpha", TabId::AlphaAnim); + addTab(mTransformInspector, "Transform", TabId::Transform); + addTab(mRotationInspector, "Rotation", TabId::Rotation); + addTab(mScaleAnimInspector, "Scale", TabId::ScaleAnim); + + // Fluctuation + addTab(mFluctuationInspector, "Fluctuation",TabId::Fluctuation); + + // Field + addTab(mFieldCollisionInspector, "Collision", TabId::FieldCollision); + addTab(mFieldConvergenceInspector, "Convergence", TabId::FieldConvergence); + addTab(mFieldMagnetInspector, "Magnet", TabId::FieldMagnet); + addTab(mFieldPosAddInspector, "Pos Add", TabId::FieldPosAdd); + addTab(mFieldRandomInspector, "Random", TabId::FieldRandom); + addTab(mFieldSpinInspector, "Spin", TabId::FieldSpin); + + // Child + addTab(mChildEditorWidget, "Child", TabId::Child); + // TODO +} + +void InspectorPanel::updateTabVisibility() { if (!mSelection) { return; } - mTabWidget->clear(); + TabId currentId{}; + bool hasCurrent = false; + + s32 currentIndex = mTabWidget->currentIndex(); + if (currentIndex >= 0) { + QVariant data = mTabWidget->tabBar()->tabData(currentIndex); + if (data.isValid()) { + currentId = data.value(); + hasCurrent = true; + } + } + + const auto type = mSelection->type(); - switch (mSelection->type()) { + auto setVisible = [this](TabId id, bool visible) { + if (!mTabIndex.contains(id)) { + return; + } + mTabWidget->setTabVisible(mTabIndex[id], visible); + }; + + for (auto [id, _] : mTabIndex.asKeyValueRange()) { + setVisible(id, false); + } + + switch (type) { case Ptcl::Selection::Type::Emitter: - buildEmitterTabs(); + setVisible(TabId::General, true); + setVisible(TabId::Gravity, true); + setVisible(TabId::Transform, true); + setVisible(TabId::Lifespan, true); + setVisible(TabId::Termination, true); + setVisible(TabId::Emission, true); + setVisible(TabId::Velocity, true); + setVisible(TabId::Volume, true); + setVisible(TabId::Color, true); + setVisible(TabId::AlphaAnim, true); + setVisible(TabId::Rotation, true); + setVisible(TabId::ScaleAnim, true); + setVisible(TabId::Texture, true); + setVisible(TabId::Combiner, true); + + if (mEmitter && mEmitter->hasStripeData()) { + setVisible(TabId::Stripe, true); + } break; case Ptcl::Selection::Type::EmitterChild: - buildChildTabs(); + setVisible(TabId::Child, true); break; case Ptcl::Selection::Type::EmitterFlux: - buildFluxTabs(); + setVisible(TabId::Fluctuation, true); break; case Ptcl::Selection::Type::EmitterField: - buildFieldTabs(); + setVisible(TabId::FieldCollision, true); + setVisible(TabId::FieldConvergence, true); + setVisible(TabId::FieldMagnet, true); + setVisible(TabId::FieldPosAdd, true); + setVisible(TabId::FieldRandom, true); + setVisible(TabId::FieldSpin, true); break; default: break; } -} - -QWidget* InspectorPanel::wrapInScroll(QWidget* widget) { - auto* container = new QWidget; - auto* layout = new QVBoxLayout(container); - layout->addWidget(widget); - layout->addStretch(); - auto* scroll = new QScrollArea; - scroll->setWidget(container); - scroll->setWidgetResizable(true); - scroll->setFrameShape(QFrame::NoFrame); - scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - return scroll; -} - -void InspectorPanel::buildEmitterTabs() { - mTabWidget->addTab(wrapInScroll(mGeneralInspector), "General"); - - if (mEmitter->hasStripeData()) { - mTabWidget->addTab(wrapInScroll(mStripeInspector), "Stripe"); + if (hasCurrent && mTabIndex.contains(currentId)) { + s32 index = mTabIndex[currentId]; + if (mTabWidget->isTabVisible(index)) { + mTabWidget->setCurrentIndex(index); + return; + } } - mTabWidget->addTab(wrapInScroll(mLifespanInspector), "Life"); - mTabWidget->addTab(wrapInScroll(mTerminationInspector), "Termination"); - mTabWidget->addTab(wrapInScroll(mGravityInspector), "Gravity"); - mTabWidget->addTab(wrapInScroll(mEmissionInspector), "Emission"); - mTabWidget->addTab(wrapInScroll(mVolumeInspector), "Volume"); - mTabWidget->addTab(wrapInScroll(mVelocityInspector), "Velocity"); - mTabWidget->addTab(wrapInScroll(mTextureInspector), "Texture"); - mTabWidget->addTab(wrapInScroll(mColorInspector), "Color"); - mTabWidget->addTab(wrapInScroll(mCombinerInspector), "Combiner"); - mTabWidget->addTab(wrapInScroll(mAlphaAnimInspector), "Alpha"); - mTabWidget->addTab(wrapInScroll(mTransformInspector), "Transform"); - mTabWidget->addTab(wrapInScroll(mRotationInspector), "Rotation"); - mTabWidget->addTab(wrapInScroll(mScaleAnimInspector), "Scale"); -} - -void InspectorPanel::buildChildTabs() { - mTabWidget->addTab(wrapInScroll(mChildEditorWidget), "Child"); - // TODO -} - -void InspectorPanel::buildFluxTabs() { - mTabWidget->addTab(wrapInScroll(mFluctuationInspector), "Fluctuation"); -} - -void InspectorPanel::buildFieldTabs() { - mTabWidget->addTab(wrapInScroll(mFieldEditorWidget), "Field"); - // TODO + for (s32 i = 0; i < mTabWidget->count(); ++i) { + if (mTabWidget->isTabVisible(i)) { + mTabWidget->setCurrentIndex(i); + break; + } + } } void InspectorPanel::setupConnections() { @@ -145,14 +209,6 @@ void InspectorPanel::setupConnections() { emit complexFlagsChanged(); emit propertiesChanged(); }); - - // Field Editor Widget - connect(mFieldEditorWidget, &FieldEditorWidget::flagsUpdated, this, [this](const BitFlag& fieldFlags) { - if (!mEmitter) { return; } - mEmitter->setFieldFlags(fieldFlags); - emit complexFlagsChanged(); - emit propertiesChanged(); - }); } void InspectorPanel::setDocument(Ptcl::Document* document) { @@ -179,6 +235,13 @@ void InspectorPanel::setDocument(Ptcl::Document* document) { mFluctuationInspector->setDocument(document); mStripeInspector->setDocument(document); + mFieldCollisionInspector->setDocument(document); + mFieldConvergenceInspector->setDocument(document); + mFieldMagnetInspector->setDocument(document); + mFieldPosAddInspector->setDocument(document); + mFieldRandomInspector->setDocument(document); + mFieldSpinInspector->setDocument(document); + if (mDocument) { connect(mDocument, &Ptcl::Document::emitterChanged, this, [this](s32 setIndex, s32 emitterIndex) { if (!mEmitter) { @@ -190,7 +253,13 @@ void InspectorPanel::setDocument(Ptcl::Document* document) { } populateProperties(); - rebuildTabs(); + + if (mLastEmitterType != mEmitter->type() || mLastEmitterHasStripe != mEmitter->hasStripeData()) { + updateTabVisibility(); + + mLastEmitterType = mEmitter->type(); + mLastEmitterHasStripe = mEmitter->hasStripeData(); + } }); } } @@ -219,6 +288,13 @@ void InspectorPanel::setSelection(Ptcl::Selection* selection) { mFluctuationInspector->setSelection(selection); mStripeInspector->setSelection(selection); + mFieldCollisionInspector->setSelection(selection); + mFieldConvergenceInspector->setSelection(selection); + mFieldMagnetInspector->setSelection(selection); + mFieldPosAddInspector->setSelection(selection); + mFieldRandomInspector->setSelection(selection); + mFieldSpinInspector->setSelection(selection); + if (mSelection) { connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -229,7 +305,7 @@ void InspectorPanel::setSelection(Ptcl::Selection* selection) { mEmitter = mDocument->emitter(setIndex, emitterIndex); - rebuildTabs(); + updateTabVisibility(); // TODO: Have child widgets handle this themselves mChildEditorWidget->setTextureList(mTextureList); @@ -242,13 +318,10 @@ void InspectorPanel::setSelection(Ptcl::Selection* selection) { void InspectorPanel::populateProperties() { QSignalBlocker b15(mChildEditorWidget); - QSignalBlocker b17(mFieldEditorWidget); QSignalBlocker b18(mStripeInspector); mChildEditorWidget->setChildData(&mEmitter->childData(), mEmitter->complexProperties().childFlags); mChildEditorWidget->setParentColor0(mEmitter->primaryColor()); - - mFieldEditorWidget->setData(&mEmitter->fieldData(), mEmitter->complexProperties().fieldFlags); } diff --git a/src/editor/ptclListWidget.cpp b/src/editor/ptclListWidget.cpp index 61ed274..119f146 100644 --- a/src/editor/ptclListWidget.cpp +++ b/src/editor/ptclListWidget.cpp @@ -412,7 +412,7 @@ void PtclList::addComplexNodes(QStandardItem* emitterItem, s32 setIndex, s32 emi "Field", setIndex, emitterIndex, - props.fieldFlags.isSet(Ptcl::FieldFlag::Enabled) + emitter->isFieldEnabled() ); } diff --git a/src/ptcl/ptcl.cpp b/src/ptcl/ptcl.cpp index 66a66e8..6296793 100644 --- a/src/ptcl/ptcl.cpp +++ b/src/ptcl/ptcl.cpp @@ -156,38 +156,36 @@ void PtclBinaryReader::readComplexData(Emitter& emitter, const BinCommonEmitterD } // FieldData - Ptcl::FieldData fieldData; if (complex.fieldFlag.isSet(FieldFlag::Random)) { BinFieldRandomData randomData{}; mStream >> randomData; - fieldData.initRandomData(randomData); + emitter.initFieldRandom(randomData); } if (complex.fieldFlag.isSet(FieldFlag::Magnet)) { BinFieldMagnetData magnetData{}; mStream >> magnetData; - fieldData.initMagnetData(magnetData); + emitter.initFieldMagnet(magnetData); } if (complex.fieldFlag.isSet(FieldFlag::Spin)) { BinFieldSpinData spinData{}; mStream >> spinData; - fieldData.initSpinData(spinData); + emitter.initFieldSpin(spinData); } if (complex.fieldFlag.isSet(FieldFlag::Collision)) { BinFieldCollisionData collisionData{}; mStream >> collisionData; - fieldData.initCollisionData(collisionData); + emitter.initFieldCollision(collisionData); } if (complex.fieldFlag.isSet(FieldFlag::Convergence)) { BinFieldConvergenceData convergenceData{}; mStream >> convergenceData; - fieldData.initConvergenceData(convergenceData); + emitter.initFieldConvergence(convergenceData); } if (complex.fieldFlag.isSet(FieldFlag::PosAdd)) { BinFieldPosAddData posAddData{}; mStream >> posAddData; - fieldData.initPosAddData(posAddData); + emitter.initFieldPosAdd(posAddData); } - emitter.setFieldData(fieldData); // FluctuationData if (complex.fluctuationFlag.isSet(FluctuationFlag::Enabled)) { @@ -348,7 +346,7 @@ void PtclBinaryWriter::writeComplexEmitter(const Emitter& emitter) { // Field emitterData.fieldDataOffset = emitterDataSize; - const auto& fieldFlags = emitter.complexProperties().fieldFlags; + const auto& fieldFlags = emitter.fieldFlags(); const bool hasRandom = fieldFlags.isSet(FieldFlag::Random); const bool hasMagnet = fieldFlags.isSet(FieldFlag::Magnet); @@ -387,12 +385,12 @@ void PtclBinaryWriter::writeComplexEmitter(const Emitter& emitter) { // Sub-Blocks if (hasChild) { mEmitterData.emplace_back(childData); } - if (hasRandom) { mEmitterData.emplace_back(BinFieldRandomData{emitter.fieldData().randomData()}); } - if (hasMagnet) { mEmitterData.emplace_back(BinFieldMagnetData{emitter.fieldData().magnetData()}); } - if (hasSpin) { mEmitterData.emplace_back(BinFieldSpinData{emitter.fieldData().spinData()}); } - if (hasCollision) { mEmitterData.emplace_back(BinFieldCollisionData{emitter.fieldData().collisionData()}); } - if (hasConvergence) { mEmitterData.emplace_back(BinFieldConvergenceData{emitter.fieldData().convergenceData()}); } - if (hasPosAdd) { mEmitterData.emplace_back(BinFieldPosAddData{emitter.fieldData().posAddData()}); } + if (hasRandom) { mEmitterData.emplace_back(BinFieldRandomData{emitter}); } + if (hasMagnet) { mEmitterData.emplace_back(BinFieldMagnetData{emitter}); } + if (hasSpin) { mEmitterData.emplace_back(BinFieldSpinData{emitter}); } + if (hasCollision) { mEmitterData.emplace_back(BinFieldCollisionData{emitter}); } + if (hasConvergence) { mEmitterData.emplace_back(BinFieldConvergenceData{emitter}); } + if (hasPosAdd) { mEmitterData.emplace_back(BinFieldPosAddData{emitter}); } if (hasFluctuation) { mEmitterData.emplace_back(BinFluctuationData{emitter}); } if (hasStripe) { mEmitterData.emplace_back(BinStripeData{emitter}); } } diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index 811e66a..8ee7f15 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -1,7 +1,6 @@ #include "ptcl/ptclBinary.h" #include "ptcl/ptclEmitter.h" #include "util/printUtil.h" -#include "ptcl/ptclFieldData.h" namespace Ptcl { @@ -653,7 +652,7 @@ void BinCommonEmitterData::printData(u32 indentationLevel) { BinComplexEmitterData::BinComplexEmitterData(const Ptcl::Emitter& emitter) : BinCommonEmitterData{emitter} { childFlag = emitter.complexProperties().childFlags; - fieldFlag = emitter.complexProperties().fieldFlags; + fieldFlag = emitter.fieldFlags(); fluctuationFlag = emitter.fluctuationFlags(); stripeFlag = emitter.stripeFlags(); @@ -852,9 +851,9 @@ QDataStream& operator<<(QDataStream& out, const BinChildData& item) { // ========================================================================== // -BinFieldRandomData::BinFieldRandomData(const Ptcl::FieldData::FieldRandomData& fieldRandomData) { - fieldRandomBlank = fieldRandomData.randomBlank; - fieldRandomVelAdd = fieldRandomData.randomVelAdd; +BinFieldRandomData::BinFieldRandomData(const Ptcl::Emitter& emitterData) { + fieldRandomBlank = emitterData.fieldRandomBlank(); + fieldRandomVelAdd = emitterData.fieldRandomVelAdd(); } QDataStream& operator>>(QDataStream& in, BinFieldRandomData& item) { @@ -873,10 +872,10 @@ QDataStream& operator<<(QDataStream& out, const BinFieldRandomData& item) { // ========================================================================== // -BinFieldMagnetData::BinFieldMagnetData(const Ptcl::FieldData::FieldMagnetData& fieldMagnetData) { - fieldMagnetPower = fieldMagnetData.magnetPower; - fieldMagnetPos = fieldMagnetData.magnetPos; - fieldMagnetFlag = fieldMagnetData.magnetFlag; +BinFieldMagnetData::BinFieldMagnetData(const Ptcl::Emitter& emitterData) { + fieldMagnetPower = emitterData.fieldMagnetPower(); + fieldMagnetPos = emitterData.fieldMagnetPos(); + fieldMagnetFlag = emitterData.fieldMagnetFlag(); } QDataStream& operator>>(QDataStream& in, BinFieldMagnetData& item) { @@ -897,9 +896,9 @@ QDataStream& operator<<(QDataStream& out, const BinFieldMagnetData& item) { // ========================================================================== // -BinFieldSpinData::BinFieldSpinData(const Ptcl::FieldData::FieldSpinData& fieldSpinData) { - fieldSpinRotate = fieldSpinData.spinRotate; - fieldSpinAxis = fieldSpinData.spinAxis; +BinFieldSpinData::BinFieldSpinData(const Ptcl::Emitter& emitterData) { + fieldSpinRotate = emitterData.fieldSpinRotate(); + fieldSpinAxis = emitterData.fieldSpinAxis(); } QDataStream& operator>>(QDataStream& in, BinFieldSpinData& item) { @@ -918,11 +917,11 @@ QDataStream& operator<<(QDataStream& out, const BinFieldSpinData& item) { // ========================================================================== // -BinFieldCollisionData::BinFieldCollisionData(const Ptcl::FieldData::FieldCollisionData& fieldCollisionData) { - fieldCollisionType = fieldCollisionData.collisionType; - fieldCollisionIsWorld = fieldCollisionData.collisionIsWorld; - fieldCollisionCoord = fieldCollisionData.collisionCoord; - fieldCollisionCoef = fieldCollisionData.collisionCoef; +BinFieldCollisionData::BinFieldCollisionData(const Ptcl::Emitter& emitterData) { + fieldCollisionType = emitterData.fieldCollisionType(); + fieldCollisionIsWorld = emitterData.fieldCollisionIsWorld(); + fieldCollisionCoord = emitterData.fieldCollisionCoord(); + fieldCollisionCoef = emitterData.fieldCollisionCoef(); } QDataStream& operator>>(QDataStream& in, BinFieldCollisionData& item) { @@ -945,9 +944,9 @@ QDataStream& operator<<(QDataStream& out, const BinFieldCollisionData& item) { // ========================================================================== // -BinFieldConvergenceData::BinFieldConvergenceData(const Ptcl::FieldData::FieldConvergenceData& fieldConvergenceData) { - fieldConvergenceType = fieldConvergenceData.convergenceType; - fieldConvergencePos = fieldConvergenceData.convergencePos; +BinFieldConvergenceData::BinFieldConvergenceData(const Ptcl::Emitter& emitterData) { + fieldConvergenceType = emitterData.fieldConvergenceType(); + fieldConvergencePos = emitterData.fieldConvergencePos(); } QDataStream& operator>>(QDataStream& in, BinFieldConvergenceData& item) { @@ -966,8 +965,8 @@ QDataStream& operator<<(QDataStream& out, const BinFieldConvergenceData& item) { // ========================================================================== // -BinFieldPosAddData::BinFieldPosAddData(const Ptcl::FieldData::FieldPosAddData& fieldPosAddData) { - fieldPosAdd = fieldPosAddData.posAdd; +BinFieldPosAddData::BinFieldPosAddData(const Ptcl::Emitter& emitterData) { + fieldPosAdd = emitterData.fieldPosAddPosition(); } QDataStream& operator>>(QDataStream& in, BinFieldPosAddData& item) { diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index ef74da4..cc276ce 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -124,7 +124,15 @@ std::unique_ptr Emitter::clone() const { newEmitter->mStripeHistoryStep = mStripeHistoryStep; newEmitter->mStripeDirInterpolate = mStripeDirInterpolate; - newEmitter->mFieldData = mFieldData; + // Field Properties + newEmitter->mFieldFlags = mFieldFlags; + newEmitter->mFieldRandom = mFieldRandom; + newEmitter->mFieldMagnet = mFieldMagnet; + newEmitter->mFieldSpin = mFieldSpin; + newEmitter->mFieldCollision = mFieldCollision; + newEmitter->mFieldConvergence = mFieldConvergence; + newEmitter->mFieldPosAdd = mFieldPosAdd; + return newEmitter; } @@ -148,10 +156,6 @@ void Emitter::setChildFlags(const BitFlag& childFlags) { mComplexProperties.childFlags = childFlags; } -void Emitter::setFieldFlags(const BitFlag& fieldFlags) { - mComplexProperties.fieldFlags = fieldFlags; -} - const ChildData& Emitter::childData() const { return mChildData; } @@ -160,22 +164,54 @@ ChildData& Emitter::childData() { return mChildData; } -void Emitter::initFluctuationData(const BinFluctuationData& fluctuationData) { - mFluctuationScale = fluctuationData.fluctuationScale; - mFluctuationFreq = fluctuationData.fluctuationFreq; - mFluctuationPhaseRnd = static_cast(fluctuationData.fluctuationPhaseRnd); +void Emitter::initFieldRandom(const BinFieldRandomData& randomData) { + mFieldRandom = { + .randomBlank = randomData.fieldRandomBlank, + .randomVelAdd = Math::Vector3f(randomData.fieldRandomVelAdd.x, randomData.fieldRandomVelAdd.y, randomData.fieldRandomVelAdd.z) + }; } -const FieldData& Emitter::fieldData() const { - return mFieldData; +void Emitter::initFieldMagnet(const BinFieldMagnetData& magnetData) { + mFieldMagnet = { + .magnetPower = magnetData.fieldMagnetPower, + .magnetPos = Math::Vector3f(magnetData.fieldMagnetPos.x, magnetData.fieldMagnetPos.y, magnetData.fieldMagnetPos.z), + .magnetFlag = magnetData.fieldMagnetFlag, + }; } -FieldData& Emitter::fieldData() { - return mFieldData; +void Emitter::initFieldSpin(const BinFieldSpinData& spinData) { + mFieldSpin = { + .spinRotate = spinData.fieldSpinRotate, + .spinAxis = spinData.fieldSpinAxis + }; } -void Emitter::setFieldData(const FieldData& fieldData) { - mFieldData = fieldData; +void Emitter::initFieldCollision(const BinFieldCollisionData& collisionData) { + mFieldCollision = { + .collisionType = collisionData.fieldCollisionType, + .collisionIsWorld = collisionData.fieldCollisionIsWorld > 0, + .collisionCoord = collisionData.fieldCollisionCoord, + .collisionCoef = collisionData.fieldCollisionCoef + }; +} + +void Emitter::initFieldConvergence(const BinFieldConvergenceData& convergenceData) { + mFieldConvergence = { + .convergenceType = convergenceData.fieldConvergenceType, + .convergencePos = Math::Vector3f(convergenceData.fieldConvergencePos.x, convergenceData.fieldConvergencePos.y, convergenceData.fieldConvergencePos.z) + }; +} + +void Emitter::initFieldPosAdd(const BinFieldPosAddData& posAddData) { + mFieldPosAdd = { + .posAdd = Math::Vector3f(posAddData.fieldPosAdd.x, posAddData.fieldPosAdd.y, posAddData.fieldPosAdd.z) + }; +} + +void Emitter::initFluctuationData(const BinFluctuationData& fluctuationData) { + mFluctuationScale = fluctuationData.fluctuationScale; + mFluctuationFreq = fluctuationData.fluctuationFreq; + mFluctuationPhaseRnd = static_cast(fluctuationData.fluctuationPhaseRnd); } void Emitter::initStripeData(const BinStripeData& stripeData) { @@ -307,9 +343,9 @@ void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { void Emitter::initComplexFromBinary(const BinComplexEmitterData& emitterData) { mComplexProperties = { .childFlags = emitterData.childFlag, - .fieldFlags = emitterData.fieldFlag, }; + mFieldFlags = emitterData.fieldFlag, mFluctuationFlags = emitterData.fluctuationFlag; mStripeFlags = emitterData.stripeFlag; } diff --git a/src/ptcl/ptclFieldData.cpp b/src/ptcl/ptclFieldData.cpp deleted file mode 100644 index bc47b2a..0000000 --- a/src/ptcl/ptclFieldData.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include "ptcl/ptclFieldData.h" -#include "ptcl/ptclBinary.h" - -namespace Ptcl { - -const FieldData::FieldRandomData& FieldData::randomData() const { - return mRandomData; -} - -void FieldData::setRandomData(const FieldRandomData& randomData) { - mRandomData = randomData; -} - -void FieldData::initRandomData(const BinFieldRandomData& randomData) { - mRandomData = { - .randomBlank = randomData.fieldRandomBlank, - .randomVelAdd = Math::Vector3f(randomData.fieldRandomVelAdd.x, randomData.fieldRandomVelAdd.y, randomData.fieldRandomVelAdd.z) - }; -} - -const FieldData::FieldMagnetData& FieldData::magnetData() const { - return mMagnetData; -} - -void FieldData::setMagnetData(const FieldMagnetData& magnetData) { - mMagnetData = magnetData; -} - -void FieldData::initMagnetData(const BinFieldMagnetData& magnetData) { - mMagnetData = { - .magnetPower = magnetData.fieldMagnetPower, - .magnetPos = Math::Vector3f(magnetData.fieldMagnetPos.x, magnetData.fieldMagnetPos.y, magnetData.fieldMagnetPos.z), - .magnetFlag = magnetData.fieldMagnetFlag, - }; -} - -const FieldData::FieldSpinData& FieldData::spinData() const { - return mSpinData; -} - -void FieldData::setSpinData(const FieldSpinData& spinData) { - mSpinData = spinData; -} - -void FieldData::initSpinData(const BinFieldSpinData& spinData) { - mSpinData = { - .spinRotate = spinData.fieldSpinRotate, - .spinAxis = spinData.fieldSpinAxis - }; -} - -const FieldData::FieldCollisionData& FieldData::collisionData() const { - return mCollisionData; -} - -void FieldData::setCollisionData(const FieldCollisionData& collisionData) { - mCollisionData = collisionData; -} - -void FieldData::initCollisionData(const BinFieldCollisionData& collisionData) { - mCollisionData = { - .collisionType = collisionData.fieldCollisionType, - .collisionIsWorld = collisionData.fieldCollisionIsWorld > 0, - .collisionCoord = collisionData.fieldCollisionCoord, - .collisionCoef = collisionData.fieldCollisionCoef - }; -} - -const FieldData::FieldConvergenceData& FieldData::convergenceData() const { - return mConvergenceData; -} - -void FieldData::setConvergenceData(const FieldConvergenceData& convergenceData) { - mConvergenceData = convergenceData; -} - -void FieldData::initConvergenceData(const BinFieldConvergenceData& convergenceData) { - mConvergenceData = { - .convergenceType = convergenceData.fieldConvergenceType, - .convergencePos = Math::Vector3f(convergenceData.fieldConvergencePos.x, convergenceData.fieldConvergencePos.y, convergenceData.fieldConvergencePos.z) - }; -} - -const FieldData::FieldPosAddData& FieldData::posAddData() const { - return mPosAddData; -} - -void FieldData::setPosAddData(const FieldPosAddData& posAddData) { - mPosAddData = posAddData; -} - -void FieldData::initPosAddData(const BinFieldPosAddData& posAddData) { - mPosAddData = { - .posAdd = Math::Vector3f(posAddData.fieldPosAdd.x, posAddData.fieldPosAdd.y, posAddData.fieldPosAdd.z) - }; -} - - -// ========================================================================== // - - -} // namespace Ptcl From 0768e655f4d806afb32e6631fc9ff58cf80d0135 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Wed, 18 Mar 2026 19:23:07 +0000 Subject: [PATCH 24/35] refactor: rework inspector to use multiple tab widgets --- include/editor/inspector/inspectorPanel.h | 45 +----- src/editor/inspector/inspectorPanel.cpp | 177 +++++++++------------- 2 files changed, 74 insertions(+), 148 deletions(-) diff --git a/include/editor/inspector/inspectorPanel.h b/include/editor/inspector/inspectorPanel.h index 73ab454..b031560 100644 --- a/include/editor/inspector/inspectorPanel.h +++ b/include/editor/inspector/inspectorPanel.h @@ -1,11 +1,9 @@ #pragma once #include "editor/childEditor/childEditorWidget.h" -#include "editor/components/collapsibleWidget.h" #include "ptcl/ptclDocument.h" #include "ptcl/ptclEmitter.h" -#include "ptcl/ptcl.h" #include #include @@ -49,33 +47,6 @@ class FieldSpinInspector; class InspectorPanel final : public QWidget { Q_OBJECT -private: - enum class TabId { - General, - Gravity, - Transform, - Lifespan, - Termination, - Emission, - Velocity, - Volume, - Color, - AlphaAnim, - Rotation, - ScaleAnim, - Texture, - Combiner, - Stripe, - Fluctuation, - FieldCollision, - FieldConvergence, - FieldMagnet, - FieldPosAdd, - FieldRandom, - FieldSpin, - Child - }; - public: explicit InspectorPanel(QWidget* parent = nullptr); @@ -83,9 +54,6 @@ class InspectorPanel final : public QWidget { void setSelection(Ptcl::Selection* selection); signals: - void nameUpdated(const QString& name); - void emitterTypeChanged(); - void emitterNameChanged(); void complexFlagsChanged(); void propertiesChanged(); @@ -93,7 +61,6 @@ class InspectorPanel final : public QWidget { void setupConnections(); void populateProperties(); - s32 addTab(QWidget* widget, const QString& label, TabId id); void buildTabs(); void updateTabVisibility(); @@ -102,8 +69,6 @@ class InspectorPanel final : public QWidget { const Ptcl::Selection* mSelection{nullptr}; Ptcl::Emitter* mEmitter{nullptr}; - const Ptcl::TextureList* mTextureList{nullptr}; - GeneralEmitterInspector* mGeneralInspector{nullptr}; GravityInspector* mGravityInspector{nullptr}; TransformInspector* mTransformInspector{nullptr}; @@ -130,11 +95,13 @@ class InspectorPanel final : public QWidget { FieldRandomInspector* mFieldRandomInspector{nullptr}; FieldSpinInspector* mFieldSpinInspector{nullptr}; - QTabWidget* mTabWidget{nullptr}; - QHash mTabIndex{}; + QStackedWidget* mTabStack{nullptr}; + QTabWidget* mEmitterTabs{nullptr}; + QTabWidget* mChildTabs{nullptr}; + QTabWidget* mFieldTabs{nullptr}; + QTabWidget* mFluxTabs{nullptr}; - Ptcl::EmitterType mLastEmitterType{}; - bool mLastEmitterHasStripe{}; + Ptcl::Selection::Type mLastSelectionType{}; }; diff --git a/src/editor/inspector/inspectorPanel.cpp b/src/editor/inspector/inspectorPanel.cpp index f590ac3..a9e2c25 100644 --- a/src/editor/inspector/inspectorPanel.cpp +++ b/src/editor/inspector/inspectorPanel.cpp @@ -62,11 +62,26 @@ InspectorPanel::InspectorPanel(QWidget* parent) : mChildEditorWidget = new ChildEditorWidget(this); mFluctuationInspector = new FluctuationInspector(this); - mTabWidget = new QTabWidget(this); - mTabWidget->setTabPosition(QTabWidget::West); + mTabStack = new QStackedWidget(this); + + mEmitterTabs = new QTabWidget(this); + mEmitterTabs->setTabPosition(QTabWidget::West); + mTabStack->addWidget(mEmitterTabs); + + mChildTabs = new QTabWidget(this); + mChildTabs->setTabPosition(QTabWidget::West); + mTabStack->addWidget(mChildTabs); + + mFieldTabs = new QTabWidget(this); + mFieldTabs->setTabPosition(QTabWidget::West); + mTabStack->addWidget(mFieldTabs); + + mFluxTabs = new QTabWidget(this); + mFluxTabs->setTabPosition(QTabWidget::West); + mTabStack->addWidget(mFluxTabs); auto* layout = new QVBoxLayout(this); - layout->addWidget(mTabWidget); + layout->addWidget(mTabStack); layout->setContentsMargins(0, 0, 0, 0); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -75,44 +90,48 @@ InspectorPanel::InspectorPanel(QWidget* parent) : setupConnections(); } -s32 InspectorPanel::addTab(QWidget* widget, const QString& label, TabId id) { - s32 idx = mTabWidget->addTab(widget, label); - mTabWidget->tabBar()->setTabData(idx, QVariant::fromValue(id)); - mTabIndex[id] = idx; - return idx; -} - void InspectorPanel::buildTabs() { + // This is dumb, but fixes some issues with some tabs being incorrectly sized + // Individual inspector widgets should probably be adjusted instead so this can be removed + auto wrapInScroll = [this](QWidget* content) { + auto* scroll = new QScrollArea; + scroll->setWidget(content); + scroll->setWidgetResizable(true); + scroll->setFrameShape(QFrame::NoFrame); + scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + return scroll; + }; + // Emitter - addTab(mGeneralInspector, "General", TabId::General); - addTab(mStripeInspector, "Stripe", TabId::Stripe); - addTab(mLifespanInspector, "Life", TabId::Lifespan); - addTab(mTerminationInspector, "Termination", TabId::Termination); - addTab(mGravityInspector, "Gravity", TabId::Gravity); - addTab(mEmissionInspector, "Emission", TabId::Emission); - addTab(mVolumeInspector, "Volume", TabId::Volume); - addTab(mVelocityInspector, "Velocity", TabId::Velocity); - addTab(mTextureInspector, "Texture", TabId::Texture); - addTab(mColorInspector, "Color", TabId::Color); - addTab(mCombinerInspector, "Combiner", TabId::Combiner); - addTab(mAlphaAnimInspector, "Alpha", TabId::AlphaAnim); - addTab(mTransformInspector, "Transform", TabId::Transform); - addTab(mRotationInspector, "Rotation", TabId::Rotation); - addTab(mScaleAnimInspector, "Scale", TabId::ScaleAnim); + mEmitterTabs->addTab(wrapInScroll(mGeneralInspector), "General"); + mEmitterTabs->addTab(mStripeInspector, "Stripe"); + mEmitterTabs->addTab(wrapInScroll(mLifespanInspector), "Life"); + mEmitterTabs->addTab(wrapInScroll(mTerminationInspector), "Termination"); + mEmitterTabs->addTab(wrapInScroll(mGravityInspector), "Gravity"); + mEmitterTabs->addTab(wrapInScroll(mEmissionInspector), "Emission"); + mEmitterTabs->addTab(wrapInScroll(mVolumeInspector), "Volume"); + mEmitterTabs->addTab(wrapInScroll(mVelocityInspector), "Velocity"); + mEmitterTabs->addTab(wrapInScroll(mTextureInspector), "Texture"); + mEmitterTabs->addTab(wrapInScroll(mColorInspector), "Color"); + mEmitterTabs->addTab(wrapInScroll(mCombinerInspector), "Combiner"); + mEmitterTabs->addTab(wrapInScroll(mAlphaAnimInspector), "Alpha"); + mEmitterTabs->addTab(wrapInScroll(mTransformInspector), "Transform"); + mEmitterTabs->addTab(wrapInScroll(mRotationInspector), "Rotation"); + mEmitterTabs->addTab(wrapInScroll(mScaleAnimInspector), "Scale"); // Fluctuation - addTab(mFluctuationInspector, "Fluctuation",TabId::Fluctuation); + mFluxTabs->addTab(mFluctuationInspector, "Fluctuation"); // Field - addTab(mFieldCollisionInspector, "Collision", TabId::FieldCollision); - addTab(mFieldConvergenceInspector, "Convergence", TabId::FieldConvergence); - addTab(mFieldMagnetInspector, "Magnet", TabId::FieldMagnet); - addTab(mFieldPosAddInspector, "Pos Add", TabId::FieldPosAdd); - addTab(mFieldRandomInspector, "Random", TabId::FieldRandom); - addTab(mFieldSpinInspector, "Spin", TabId::FieldSpin); + mFieldTabs->addTab(mFieldCollisionInspector, "Collision"); + mFieldTabs->addTab(mFieldConvergenceInspector, "Convergence"); + mFieldTabs->addTab(mFieldMagnetInspector, "Magnet"); + mFieldTabs->addTab(mFieldPosAddInspector, "Pos Add"); + mFieldTabs->addTab(mFieldRandomInspector, "Random"); + mFieldTabs->addTab(mFieldSpinInspector, "Spin"); // Child - addTab(mChildEditorWidget, "Child", TabId::Child); + mChildTabs->addTab(mChildEditorWidget, "Child"); // TODO } @@ -121,84 +140,24 @@ void InspectorPanel::updateTabVisibility() { return; } - TabId currentId{}; - bool hasCurrent = false; - - s32 currentIndex = mTabWidget->currentIndex(); - if (currentIndex >= 0) { - QVariant data = mTabWidget->tabBar()->tabData(currentIndex); - if (data.isValid()) { - currentId = data.value(); - hasCurrent = true; - } - } - const auto type = mSelection->type(); - auto setVisible = [this](TabId id, bool visible) { - if (!mTabIndex.contains(id)) { - return; - } - mTabWidget->setTabVisible(mTabIndex[id], visible); - }; - - for (auto [id, _] : mTabIndex.asKeyValueRange()) { - setVisible(id, false); - } - switch (type) { case Ptcl::Selection::Type::Emitter: - setVisible(TabId::General, true); - setVisible(TabId::Gravity, true); - setVisible(TabId::Transform, true); - setVisible(TabId::Lifespan, true); - setVisible(TabId::Termination, true); - setVisible(TabId::Emission, true); - setVisible(TabId::Velocity, true); - setVisible(TabId::Volume, true); - setVisible(TabId::Color, true); - setVisible(TabId::AlphaAnim, true); - setVisible(TabId::Rotation, true); - setVisible(TabId::ScaleAnim, true); - setVisible(TabId::Texture, true); - setVisible(TabId::Combiner, true); - - if (mEmitter && mEmitter->hasStripeData()) { - setVisible(TabId::Stripe, true); - } + mTabStack->setCurrentWidget(mEmitterTabs); break; case Ptcl::Selection::Type::EmitterChild: - setVisible(TabId::Child, true); + mTabStack->setCurrentWidget(mChildTabs); break; case Ptcl::Selection::Type::EmitterFlux: - setVisible(TabId::Fluctuation, true); + mTabStack->setCurrentWidget(mFluxTabs); break; case Ptcl::Selection::Type::EmitterField: - setVisible(TabId::FieldCollision, true); - setVisible(TabId::FieldConvergence, true); - setVisible(TabId::FieldMagnet, true); - setVisible(TabId::FieldPosAdd, true); - setVisible(TabId::FieldRandom, true); - setVisible(TabId::FieldSpin, true); + mTabStack->setCurrentWidget(mFieldTabs); break; default: break; } - - if (hasCurrent && mTabIndex.contains(currentId)) { - s32 index = mTabIndex[currentId]; - if (mTabWidget->isTabVisible(index)) { - mTabWidget->setCurrentIndex(index); - return; - } - } - - for (s32 i = 0; i < mTabWidget->count(); ++i) { - if (mTabWidget->isTabVisible(i)) { - mTabWidget->setCurrentIndex(i); - break; - } - } } void InspectorPanel::setupConnections() { @@ -253,13 +212,6 @@ void InspectorPanel::setDocument(Ptcl::Document* document) { } populateProperties(); - - if (mLastEmitterType != mEmitter->type() || mLastEmitterHasStripe != mEmitter->hasStripeData()) { - updateTabVisibility(); - - mLastEmitterType = mEmitter->type(); - mLastEmitterHasStripe = mEmitter->hasStripeData(); - } }); } } @@ -305,10 +257,8 @@ void InspectorPanel::setSelection(Ptcl::Selection* selection) { mEmitter = mDocument->emitter(setIndex, emitterIndex); - updateTabVisibility(); - // TODO: Have child widgets handle this themselves - mChildEditorWidget->setTextureList(mTextureList); + mChildEditorWidget->setTextureList(nullptr); setEnabled(true); populateProperties(); @@ -317,11 +267,20 @@ void InspectorPanel::setSelection(Ptcl::Selection* selection) { } void InspectorPanel::populateProperties() { - QSignalBlocker b15(mChildEditorWidget); - QSignalBlocker b18(mStripeInspector); + QSignalBlocker b1(mChildEditorWidget); mChildEditorWidget->setChildData(&mEmitter->childData(), mEmitter->complexProperties().childFlags); mChildEditorWidget->setParentColor0(mEmitter->primaryColor()); + + if (mLastSelectionType != mSelection->type()) { + mLastSelectionType = mSelection->type(); + updateTabVisibility(); + } + + // TODO - Stripe should be combined some other tab, this shouldn't be handled by the inspector + const s32 stripeIdx = mEmitterTabs->indexOf(mStripeInspector); + const bool hasStripe = mEmitter->hasStripeData(); + mEmitterTabs->setTabVisible(stripeIdx, hasStripe); } From 020a25d654bf696f4ed995119cea05a6afc325ee Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Thu, 19 Mar 2026 06:08:22 +0000 Subject: [PATCH 25/35] feat(ui): implmennt undo/redo for childData and directly integrate with inspector --- CMakeLists.txt | 39 ++- .../editor/childEditor/childEditorWidget.h | 72 ----- .../child/childAlphaInspector.h} | 15 +- .../child/childColorInspector.h} | 15 +- .../child/childCombinerInspector.h} | 19 +- .../child/childEmissionInspector.h} | 16 +- .../child/childGeneralInspector.h} | 20 +- .../child/childRotationInspector.h} | 15 +- .../child/childScaleInspector.h} | 15 +- .../child/childTextureInspector.h} | 19 +- .../child/childVelocityInspector.h} | 15 +- include/editor/inspector/inspectorPanel.h | 33 +- include/ptcl/ptclBinary.h | 2 +- include/ptcl/ptclEmitter.h | 264 ++++++++++++++-- src/editor/childEditor/childEditorWidget.cpp | 285 ------------------ .../childEditor/colorPropertiesWidget.cpp | 81 ----- .../childEditor/combinerPropertiesWidget.cpp | 65 ---- .../childEditor/scalePropertiesWidget.cpp | 83 ----- .../childEditor/velocityPropertiesWidget.cpp | 105 ------- .../child/childAlphaInspector.cpp} | 77 +++-- .../inspector/child/childColorInspector.cpp | 96 ++++++ .../child/childCombinerInspector.cpp | 78 +++++ .../child/childEmissionInspector.cpp} | 71 +++-- .../child/childGeneralInspector.cpp} | 80 +++-- .../child/childRotationInspector.cpp} | 99 ++++-- .../inspector/child/childScaleInspector.cpp | 110 +++++++ .../child/childTextureInspector.cpp} | 118 +++++--- .../child/childVelocityInspector.cpp | 139 +++++++++ src/editor/inspector/inspectorPanel.cpp | 85 ++++-- src/editor/inspector/lifespanInspector.cpp | 2 +- src/editor/mainWindow.cpp | 8 - src/editor/ptclListWidget.cpp | 3 +- src/ptcl/ptcl.cpp | 8 +- src/ptcl/ptclBinary.cpp | 86 +++--- src/ptcl/ptclEmitter.cpp | 79 +++-- 35 files changed, 1198 insertions(+), 1119 deletions(-) delete mode 100644 include/editor/childEditor/childEditorWidget.h rename include/editor/{childEditor/alphaPropertiesWidget.h => inspector/child/childAlphaInspector.h} (57%) rename include/editor/{childEditor/colorPropertiesWidget.h => inspector/child/childColorInspector.h} (52%) rename include/editor/{childEditor/combinerPropertiesWidget.h => inspector/child/childCombinerInspector.h} (52%) rename include/editor/{childEditor/emissionPropertiesWidget.h => inspector/child/childEmissionInspector.h} (57%) rename include/editor/{childEditor/basicPropertiesWidget.h => inspector/child/childGeneralInspector.h} (58%) rename include/editor/{childEditor/rotationPropertiesWidget.h => inspector/child/childRotationInspector.h} (63%) rename include/editor/{childEditor/scalePropertiesWidget.h => inspector/child/childScaleInspector.h} (58%) rename include/editor/{childEditor/texturePropertiesWidget.h => inspector/child/childTextureInspector.h} (54%) rename include/editor/{childEditor/velocityPropertiesWidget.h => inspector/child/childVelocityInspector.h} (60%) delete mode 100644 src/editor/childEditor/childEditorWidget.cpp delete mode 100644 src/editor/childEditor/colorPropertiesWidget.cpp delete mode 100644 src/editor/childEditor/combinerPropertiesWidget.cpp delete mode 100644 src/editor/childEditor/scalePropertiesWidget.cpp delete mode 100644 src/editor/childEditor/velocityPropertiesWidget.cpp rename src/editor/{childEditor/alphaPropertiesWidget.cpp => inspector/child/childAlphaInspector.cpp} (50%) create mode 100644 src/editor/inspector/child/childColorInspector.cpp create mode 100644 src/editor/inspector/child/childCombinerInspector.cpp rename src/editor/{childEditor/emissionPropertiesWidget.cpp => inspector/child/childEmissionInspector.cpp} (58%) rename src/editor/{childEditor/basicPropertiesWidget.cpp => inspector/child/childGeneralInspector.cpp} (52%) rename src/editor/{childEditor/rotationPropertiesWidget.cpp => inspector/child/childRotationInspector.cpp} (57%) create mode 100644 src/editor/inspector/child/childScaleInspector.cpp rename src/editor/{childEditor/texturePropertiesWidget.cpp => inspector/child/childTextureInspector.cpp} (50%) create mode 100644 src/editor/inspector/child/childVelocityInspector.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1066858..9cd03ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,16 +38,6 @@ set(PROJECT_SOURCES src/editor/components/loadingSpinner.cpp src/editor/components/rgbaColorWidget.cpp src/editor/components/thumbnailWidget.cpp - src/editor/childEditor/alphaPropertiesWidget.cpp - src/editor/childEditor/basicPropertiesWidget.cpp - src/editor/childEditor/childEditorWidget.cpp - src/editor/childEditor/colorPropertiesWidget.cpp - src/editor/childEditor/combinerPropertiesWidget.cpp - src/editor/childEditor/emissionPropertiesWidget.cpp - src/editor/childEditor/rotationPropertiesWidget.cpp - src/editor/childEditor/scalePropertiesWidget.cpp - src/editor/childEditor/texturePropertiesWidget.cpp - src/editor/childEditor/velocityPropertiesWidget.cpp src/editor/inspector/alphaAnimInspector.cpp src/editor/inspector/generalEmitterInspector.cpp src/editor/inspector/colorInspector.cpp @@ -66,6 +56,15 @@ set(PROJECT_SOURCES src/editor/inspector/transformInspector.cpp src/editor/inspector/velocityInspector.cpp src/editor/inspector/volumeInspector.cpp + src/editor/inspector/child/childAlphaInspector.cpp + src/editor/inspector/child/childColorInspector.cpp + src/editor/inspector/child/childCombinerInspector.cpp + src/editor/inspector/child/childEmissionInspector.cpp + src/editor/inspector/child/childGeneralInspector.cpp + src/editor/inspector/child/childRotationInspector.cpp + src/editor/inspector/child/childScaleInspector.cpp + src/editor/inspector/child/childTextureInspector.cpp + src/editor/inspector/child/childVelocityInspector.cpp src/editor/inspector/field/fieldCollisionInspector.cpp src/editor/inspector/field/fieldConvergenceInspector.cpp src/editor/inspector/field/fieldMagnetInspector.cpp @@ -108,16 +107,6 @@ set(PROJECT_HEADERS include/editor/components/sizedSpinBox.h include/editor/components/thumbnailWidget.h include/editor/components/vectorSpinBox.h - include/editor/childEditor/alphaPropertiesWidget.h - include/editor/childEditor/basicPropertiesWidget.h - include/editor/childEditor/childEditorWidget.h - include/editor/childEditor/colorPropertiesWidget.h - include/editor/childEditor/combinerPropertiesWidget.h - include/editor/childEditor/emissionPropertiesWidget.h - include/editor/childEditor/rotationPropertiesWidget.h - include/editor/childEditor/scalePropertiesWidget.h - include/editor/childEditor/texturePropertiesWidget.h - include/editor/childEditor/velocityPropertiesWidget.h include/editor/inspector/alphaAnimInspector.h include/editor/inspector/generalEmitterInspector.h include/editor/inspector/colorInspector.h @@ -136,6 +125,15 @@ set(PROJECT_HEADERS include/editor/inspector/transformInspector.h include/editor/inspector/velocityInspector.h include/editor/inspector/volumeInspector.h + include/editor/inspector/child/childAlphaInspector.h + include/editor/inspector/child/childColorInspector.h + include/editor/inspector/child/childEmissionInspector.h + include/editor/inspector/child/childGeneralInspector.h + include/editor/inspector/child/childCombinerInspector.h + include/editor/inspector/child/childRotationInspector.h + include/editor/inspector/child/childScaleInspector.h + include/editor/inspector/child/childTextureInspector.h + include/editor/inspector/child/childVelocityInspector.h include/editor/inspector/field/fieldCollisionInspector.h include/editor/inspector/field/fieldConvergenceInspector.h include/editor/inspector/field/fieldMagnetInspector.h @@ -154,7 +152,6 @@ set(PROJECT_HEADERS include/ptcl/ptclEmitter.h include/ptcl/ptclEmitterSet.h include/ptcl/ptclEnum.h - include/ptcl/ptclSeed.h include/ptcl/ptclTexture.h include/util/bitflagUtil.h diff --git a/include/editor/childEditor/childEditorWidget.h b/include/editor/childEditor/childEditorWidget.h deleted file mode 100644 index 945a7e0..0000000 --- a/include/editor/childEditor/childEditorWidget.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include "ptcl/ptcl.h" -#include "ptcl/ptclChildData.h" - -#include -#include -#include -#include -#include - -namespace PtclEditor { - - -// ========================================================================== // - - -class ChildEditorWidget final : public QWidget { - Q_OBJECT -public: - explicit ChildEditorWidget(QWidget* parent = nullptr); - - void setChildData(Ptcl::ChildData* childData, const BitFlag& childFlag); - void setTextureList(const Ptcl::TextureList* textureList); - void setParentColor0(const Ptcl::binColor4f& parentColor0); - - void clear(); - -signals: - void flagsUpdated(const BitFlag& childFlag); - -private: - void setupLayout(QVBoxLayout* mainLayout); - void setupConnections(); - void setCombinerPropertiesSrc(); - -private: - class BasicPropertiesWidget; - class EmissionPropertiesWidget; - class VelocityPropertiesWidget; - class RotationPropertiesWidget; - class ScalePropertiesWidget; - class TexturePropertiesWidget; - class ColorPropertiesWidget; - class AlphaPropertiesWidget; - class CombinerPropertiesWidget; - -private: - Ptcl::ChildData* mDataPtr{nullptr}; - BitFlag mChildFlag{}; - - const Ptcl::TextureList* mTextureList{nullptr}; - Ptcl::binColor4f mParentColor0{}; - - QCheckBox mEnabledCheckbox{}; - QWidget* mSectionsContainer{}; - BasicPropertiesWidget* mBasicProperties{nullptr}; - EmissionPropertiesWidget* mEmissionProperties{nullptr}; - VelocityPropertiesWidget* mVelocityProperties{nullptr}; - RotationPropertiesWidget* mRotationProperties{nullptr}; - ScalePropertiesWidget* mScaleProperties{nullptr}; - TexturePropertiesWidget* mTextureProperties{nullptr}; - ColorPropertiesWidget* mColorProperties{nullptr}; - AlphaPropertiesWidget* mAlphaProperties{nullptr}; - CombinerPropertiesWidget* mCombinerProperties{nullptr}; -}; - - -// ========================================================================== // - - -} // namespace PtclEditor diff --git a/include/editor/childEditor/alphaPropertiesWidget.h b/include/editor/inspector/child/childAlphaInspector.h similarity index 57% rename from include/editor/childEditor/alphaPropertiesWidget.h rename to include/editor/inspector/child/childAlphaInspector.h index 484f21a..ee31afc 100644 --- a/include/editor/childEditor/alphaPropertiesWidget.h +++ b/include/editor/inspector/child/childAlphaInspector.h @@ -1,6 +1,6 @@ #pragma once -#include "editor/childEditor/childEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -14,23 +14,16 @@ namespace PtclEditor { // ========================================================================== // -class ChildEditorWidget::AlphaPropertiesWidget final : public QWidget { +class ChildAlphaInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit AlphaPropertiesWidget(QWidget* parent = nullptr); - - void setProperties(const Ptcl::ChildData::AlphaProperties& properties, bool inheritAlpha); - -signals: - void propertiesUpdated(const Ptcl::ChildData::AlphaProperties& properties); - void inheritAlphaUpdated(bool inherit); + explicit ChildAlphaInspector(QWidget* parent = nullptr); private: + void populateProperties() final; void setupConnections(); private: - Ptcl::ChildData::AlphaProperties mProps{}; - QDoubleSpinBox mAlphaSpinBox{}; QDoubleSpinBox mAlphaTargetSpinBox{}; QDoubleSpinBox mAlphaInitSpinBox{}; diff --git a/include/editor/childEditor/colorPropertiesWidget.h b/include/editor/inspector/child/childColorInspector.h similarity index 52% rename from include/editor/childEditor/colorPropertiesWidget.h rename to include/editor/inspector/child/childColorInspector.h index 231d2a2..d6db69a 100644 --- a/include/editor/childEditor/colorPropertiesWidget.h +++ b/include/editor/inspector/child/childColorInspector.h @@ -1,7 +1,7 @@ #pragma once #include "editor/components/rgbaColorWidget.h" -#include "editor/childEditor/childEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -13,23 +13,16 @@ namespace PtclEditor { // ========================================================================== // -class ChildEditorWidget::ColorPropertiesWidget final : public QWidget { +class ChildColorInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit ColorPropertiesWidget(QWidget* parent = nullptr); - - void setProperties(const Ptcl::ChildData::ColorProperties& properties, bool inheritColor); - -signals: - void propertiesUpdated(const Ptcl::ChildData::ColorProperties& properties); - void inheritColorUpdated(bool inherit); + explicit ChildColorInspector(QWidget* parent = nullptr); private: + void populateProperties() final; void setupConnections(); private: - Ptcl::ChildData::ColorProperties mProps{}; - RGBAColorWidget mColor0Widget{}; RGBAColorWidget mColor1Widget{}; QCheckBox mInheritColorCheckBox{}; diff --git a/include/editor/childEditor/combinerPropertiesWidget.h b/include/editor/inspector/child/childCombinerInspector.h similarity index 52% rename from include/editor/childEditor/combinerPropertiesWidget.h rename to include/editor/inspector/child/childCombinerInspector.h index 8377a13..607fa2e 100644 --- a/include/editor/childEditor/combinerPropertiesWidget.h +++ b/include/editor/inspector/child/childCombinerInspector.h @@ -2,32 +2,27 @@ #include "editor/components/combinerPreviewWidget.h" #include "editor/components/enumComboBox.h" -#include "editor/childEditor/childEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include + namespace PtclEditor { // ========================================================================== // -class ChildEditorWidget::CombinerPropertiesWidget final : public QWidget { +class ChildCombinerInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit CombinerPropertiesWidget(QWidget* parent = nullptr); - - void setProperties(const Ptcl::ChildData::CombinerProperties& properties); - void setCombinerSrc(const Ptcl::TextureHandle* texture, const Ptcl::binColor3f* constant, const Ptcl::binColor4f* primary); - - void updateCombinerPreview(); - -signals: - void propertiesUpdated(const Ptcl::ChildData::CombinerProperties& properties); + explicit ChildCombinerInspector(QWidget* parent = nullptr); private: - Ptcl::ChildData::CombinerProperties mProps{}; + void populateProperties() final; + void setupConnections(); +private: EnumComboBox mBlendFuncComboBox{}; EnumComboBox mDepthFuncComboBox{}; EnumComboBox mCombinerFuncComboBox{}; diff --git a/include/editor/childEditor/emissionPropertiesWidget.h b/include/editor/inspector/child/childEmissionInspector.h similarity index 57% rename from include/editor/childEditor/emissionPropertiesWidget.h rename to include/editor/inspector/child/childEmissionInspector.h index 5eaf39e..b5f17b0 100644 --- a/include/editor/childEditor/emissionPropertiesWidget.h +++ b/include/editor/inspector/child/childEmissionInspector.h @@ -1,9 +1,9 @@ #pragma once -#include "editor/childEditor/childEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include -#include +#include #include @@ -13,22 +13,16 @@ namespace PtclEditor { // ========================================================================== // -class ChildEditorWidget::EmissionPropertiesWidget final : public QWidget { +class ChildEmissionInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit EmissionPropertiesWidget(QWidget* parent = nullptr); - - void setProperties(const Ptcl::ChildData::EmissionProperties& properties); - -signals: - void propertiesUpdated(const Ptcl::ChildData::EmissionProperties& properties); + explicit ChildEmissionInspector(QWidget* parent = nullptr); private: + void populateProperties() final; void setupConnections(); private: - Ptcl::ChildData::EmissionProperties mProps{}; - QSpinBox mEmitRateSpinBox{}; QSpinBox mEmitTimingSpinBox{}; QSpinBox mLifeSpinBox{}; diff --git a/include/editor/childEditor/basicPropertiesWidget.h b/include/editor/inspector/child/childGeneralInspector.h similarity index 58% rename from include/editor/childEditor/basicPropertiesWidget.h rename to include/editor/inspector/child/childGeneralInspector.h index e910c8c..8744cbd 100644 --- a/include/editor/childEditor/basicPropertiesWidget.h +++ b/include/editor/inspector/child/childGeneralInspector.h @@ -1,6 +1,6 @@ #pragma once -#include "editor/childEditor/childEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -13,23 +13,14 @@ namespace PtclEditor { // ========================================================================== // -class ChildEditorWidget::BasicPropertiesWidget final : public QWidget { +class ChildGeneralInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit BasicPropertiesWidget(QWidget* parent = nullptr); - - void setProperties(const Ptcl::ChildData::BasicProperties& properties, bool isFollow, bool isParentField, bool isPreDraw); - -signals: - void propertiesUpdated(const Ptcl::ChildData::BasicProperties& properties); - void isFollowUpdated(bool isFollow); - void isParentFieldUpdated(bool isField); - void isPolygonUpdated(bool isPolygon); - void isPreDrawUpdated(bool isPreDraw); + explicit ChildGeneralInspector(QWidget* parent = nullptr); private: + void populateProperties() final; void setupConnections(); - void populateBillboardType(); private: static constexpr std::array sChildBillboardTypes{ @@ -47,10 +38,9 @@ class ChildEditorWidget::BasicPropertiesWidget final : public QWidget { }; private: - Ptcl::ChildData::BasicProperties mProps{}; - QComboBox mBillboardComboBox{}; QComboBox mDrawOrderComboBox{}; + QCheckBox mEnabledCheckBox{}; QCheckBox mFollowCheckBox{}; QCheckBox mParentFieldCheckBox{}; }; diff --git a/include/editor/childEditor/rotationPropertiesWidget.h b/include/editor/inspector/child/childRotationInspector.h similarity index 63% rename from include/editor/childEditor/rotationPropertiesWidget.h rename to include/editor/inspector/child/childRotationInspector.h index 38a1be7..3b25a73 100644 --- a/include/editor/childEditor/rotationPropertiesWidget.h +++ b/include/editor/inspector/child/childRotationInspector.h @@ -2,7 +2,7 @@ #include "editor/components/enumComboBox.h" #include "editor/components/vectorSpinBox.h" -#include "editor/childEditor/childEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -15,24 +15,17 @@ namespace PtclEditor { // ========================================================================== // -class ChildEditorWidget::RotationPropertiesWidget final : public QWidget { +class ChildRotationInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit RotationPropertiesWidget(QWidget* parent = nullptr); - - void setProperties(const Ptcl::ChildData::RotationProperties& properties, bool inheritRotation); - -signals: - void propertiesUpdated(const Ptcl::ChildData::RotationProperties& properties); - void inheritRotationUpdated(bool inherit); + explicit ChildRotationInspector(QWidget* parent = nullptr); private: + void populateProperties() final; void setupConnections(); void updateAxis(); private: - Ptcl::ChildData::RotationProperties mProps{}; - EnumComboBox mRotTypeSpinBox{}; VectorSpinBox mInitRotSpinBox{}; VectorSpinBox mInitRotRandSpinBox{}; diff --git a/include/editor/childEditor/scalePropertiesWidget.h b/include/editor/inspector/child/childScaleInspector.h similarity index 58% rename from include/editor/childEditor/scalePropertiesWidget.h rename to include/editor/inspector/child/childScaleInspector.h index 7cb83f9..9e61e74 100644 --- a/include/editor/childEditor/scalePropertiesWidget.h +++ b/include/editor/inspector/child/childScaleInspector.h @@ -1,7 +1,7 @@ #pragma once #include "editor/components/vectorSpinBox.h" -#include "editor/childEditor/childEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -15,23 +15,16 @@ namespace PtclEditor { // ========================================================================== // -class ChildEditorWidget::ScalePropertiesWidget final : public QWidget { +class ChildScaleInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit ScalePropertiesWidget(QWidget* parent = nullptr); - - void setProperties(const Ptcl::ChildData::ScaleProperties& properties, bool inheritScale); - -signals: - void propertiesUpdated(const Ptcl::ChildData::ScaleProperties& properties); - void inheritScaleUpdated(bool inherit); + explicit ChildScaleInspector(QWidget* parent = nullptr); private: + void populateProperties() final; void setupConnections(); private: - Ptcl::ChildData::ScaleProperties mProps{}; - VectorSpinBox mScaleSpinBox{}; VectorSpinBox mScaleTargetSpinBox{}; QDoubleSpinBox mInheritRateSpinBox{}; diff --git a/include/editor/childEditor/texturePropertiesWidget.h b/include/editor/inspector/child/childTextureInspector.h similarity index 54% rename from include/editor/childEditor/texturePropertiesWidget.h rename to include/editor/inspector/child/childTextureInspector.h index b84ba85..7390993 100644 --- a/include/editor/childEditor/texturePropertiesWidget.h +++ b/include/editor/inspector/child/childTextureInspector.h @@ -3,7 +3,7 @@ #include "editor/components/enumComboBox.h" #include "editor/components/thumbnailWidget.h" #include "editor/components/vectorSpinBox.h" -#include "editor/childEditor/childEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include @@ -14,30 +14,19 @@ namespace PtclEditor { // ========================================================================== // -class ChildEditorWidget::TexturePropertiesWidget final : public QWidget { +class ChildTextureInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit TexturePropertiesWidget(QWidget* parent = nullptr); - - void setProperties(const Ptcl::ChildData::TextureProperties& properties, const std::shared_ptr& texture); - void setTextureList(const Ptcl::TextureList* textureList); - -signals: - void propertiesUpdated(const Ptcl::ChildData::TextureProperties& properties); - void textureUpdated(const std::shared_ptr& oldTexture, const std::shared_ptr& newTexture); + explicit ChildTextureInspector(QWidget* parent = nullptr); private slots: void changeTexture(); private: + void populateProperties() final; void setupConnections(); private: - Ptcl::ChildData::TextureProperties mProps{}; - std::shared_ptr mTexture{}; - - const Ptcl::TextureList* mTextureList{nullptr}; - ThumbnailWidget mTexturePreview{}; EnumComboBox mWrapTComboBox{}; diff --git a/include/editor/childEditor/velocityPropertiesWidget.h b/include/editor/inspector/child/childVelocityInspector.h similarity index 60% rename from include/editor/childEditor/velocityPropertiesWidget.h rename to include/editor/inspector/child/childVelocityInspector.h index b10c1f1..3c593dc 100644 --- a/include/editor/childEditor/velocityPropertiesWidget.h +++ b/include/editor/inspector/child/childVelocityInspector.h @@ -1,7 +1,7 @@ #pragma once #include "editor/components/vectorSpinBox.h" -#include "editor/childEditor/childEditorWidget.h" +#include "editor/inspector/inspectorWidgetBase.h" #include #include @@ -14,23 +14,16 @@ namespace PtclEditor { // ========================================================================== // -class ChildEditorWidget::VelocityPropertiesWidget final : public QWidget { +class ChildVelocityInspector final : public InspectorWidgetBase { Q_OBJECT public: - explicit VelocityPropertiesWidget(QWidget* parent = nullptr); - - void setProperties(const Ptcl::ChildData::VelocityProperties& properties, bool inheritVelocity); - -signals: - void propertiesUpdated(const Ptcl::ChildData::VelocityProperties& properties); - void inheritVelUpdated(bool inherit); + explicit ChildVelocityInspector(QWidget* parent = nullptr); private: + void populateProperties() final; void setupConnections(); private: - Ptcl::ChildData::VelocityProperties mProps{}; - VectorSpinBox mRandVelSpinBox{}; VectorSpinBox mGravitySpinBox{}; QDoubleSpinBox mVelInheritSpinBox{}; diff --git a/include/editor/inspector/inspectorPanel.h b/include/editor/inspector/inspectorPanel.h index b031560..26e6023 100644 --- a/include/editor/inspector/inspectorPanel.h +++ b/include/editor/inspector/inspectorPanel.h @@ -1,7 +1,5 @@ #pragma once -#include "editor/childEditor/childEditorWidget.h" - #include "ptcl/ptclDocument.h" #include "ptcl/ptclEmitter.h" @@ -11,6 +9,7 @@ #include #include #include +#include #include #include @@ -31,9 +30,10 @@ class AlphaAnimInspector; class CombinerInspector; class ColorInspector; class TextureInspector; -class FluctuationInspector; class StripeInspector; +class FluctuationInspector; + class FieldCollisionInspector; class FieldConvergenceInspector; class FieldMagnetInspector; @@ -41,6 +41,16 @@ class FieldPosAddInspector; class FieldRandomInspector; class FieldSpinInspector; +class ChildAlphaInspector; +class ChildColorInspector; +class ChildCombinerInspector; +class ChildEmissionInspector; +class ChildGeneralInspector; +class ChildRotationInspector; +class ChildScaleInspector; +class ChildTextureInspector; +class ChildVelocityInspector; + // ========================================================================== // @@ -53,10 +63,6 @@ class InspectorPanel final : public QWidget { void setDocument(Ptcl::Document* document); void setSelection(Ptcl::Selection* selection); -signals: - void complexFlagsChanged(); - void propertiesChanged(); - private: void setupConnections(); void populateProperties(); @@ -84,9 +90,8 @@ class InspectorPanel final : public QWidget { TextureInspector* mTextureInspector{nullptr}; CombinerInspector* mCombinerInspector{nullptr}; StripeInspector* mStripeInspector{nullptr}; - FluctuationInspector* mFluctuationInspector{nullptr}; - ChildEditorWidget* mChildEditorWidget{nullptr}; + FluctuationInspector* mFluctuationInspector{nullptr}; FieldCollisionInspector* mFieldCollisionInspector{nullptr}; FieldConvergenceInspector* mFieldConvergenceInspector{nullptr}; @@ -95,6 +100,16 @@ class InspectorPanel final : public QWidget { FieldRandomInspector* mFieldRandomInspector{nullptr}; FieldSpinInspector* mFieldSpinInspector{nullptr}; + ChildAlphaInspector* mChildAlphaInspector{nullptr}; + ChildColorInspector* mChildColorInspector{nullptr}; + ChildCombinerInspector* mChildCombinerInspector{nullptr}; + ChildEmissionInspector* mChildEmissionInspector{nullptr}; + ChildGeneralInspector* mChildGeneralInspector{nullptr}; + ChildRotationInspector* mChildRotationInspector{nullptr}; + ChildScaleInspector* mChildScaleInspector{nullptr}; + ChildTextureInspector* mChildTextureInspector{nullptr}; + ChildVelocityInspector* mChildVelocityInspector{nullptr}; + QStackedWidget* mTabStack{nullptr}; QTabWidget* mEmitterTabs{nullptr}; QTabWidget* mChildTabs{nullptr}; diff --git a/include/ptcl/ptclBinary.h b/include/ptcl/ptclBinary.h index 66b57fe..c9b31de 100644 --- a/include/ptcl/ptclBinary.h +++ b/include/ptcl/ptclBinary.h @@ -387,7 +387,7 @@ struct alignas(4) BinChildData { f32 childAirResist; // 0xE8 BinChildData() = default; - BinChildData(const Ptcl::ChildData& childData); + BinChildData(const Ptcl::Emitter& emitterData); friend QDataStream& operator>>(QDataStream& in, BinChildData& item); friend QDataStream& operator<<(QDataStream& out, const BinChildData& item); diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index aabb6d9..b13ebc5 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -4,7 +4,6 @@ #include "math/util.h" #include "ptcl/ptclBinary.h" -#include "ptcl/ptclChildData.h" #include "ptcl/ptclEnum.h" #include "ptcl/ptclSeed.h" #include "ptcl/ptclTexture.h" @@ -47,16 +46,6 @@ class Emitter { bool operator==(const AlphaAnim&) const = default; }; - struct ComplexProperties { - BitFlag childFlags{ - ChildFlag::AlphaInherit, - ChildFlag::ScaleInherit, - ChildFlag::RotateInherit, - ChildFlag::IsFollow, - ChildFlag::Unk80 - }; - }; - public: Emitter() = default; @@ -65,6 +54,12 @@ class Emitter { std::unique_ptr clone() const; + BitFlag& flags(); + const BitFlag& flags() const; + + void initFromBinary(const BinCommonEmitterData& emitterData); + void initComplexFromBinary(const BinComplexEmitterData& emitterData); + // ----- Basic Properties ----- \\ EmitterType type() const { return mType; } @@ -454,7 +449,6 @@ class Emitter { // Field Magnet - void initFieldMagnet(const BinFieldMagnetData& magnetData); bool isFieldMagnetEnabled() const { return mFieldFlags.isSet(FieldFlag::Magnet); } @@ -532,19 +526,178 @@ class Emitter { const Math::Vector3f& fieldPosAddPosition() const { return mFieldPosAdd.posAdd; } void setFieldPosAddPosition(const Math::Vector3f& pos) { mFieldPosAdd.posAdd = pos; } - BitFlag& flags(); - const BitFlag& flags() const; + // ----- Child Properties ----- \\ - const ComplexProperties& complexProperties() const; - void setComplexProperties(const ComplexProperties& complexProperties); + void initChild(const BinChildData& childData); - void setChildFlags(const BitFlag& childFlags); + const BitFlag& childFlags() const { return mChildFlags; } + void setChildFlags(const BitFlag& childFlags) { mChildFlags = childFlags; } - const ChildData& childData() const; - ChildData& childData(); + bool isChildEnabled() const { return mChildFlags.isSet(ChildFlag::Enabled); } + void setChildEnabled(bool inherit) { mChildFlags.set(ChildFlag::Enabled, inherit); } - void initFromBinary(const BinCommonEmitterData& emitterData); - void initComplexFromBinary(const BinComplexEmitterData& emitterData); + bool isChildInheritAlpha() const { return mChildFlags.isSet(ChildFlag::AlphaInherit); } + void setChildInheritAlpha(bool inherit) { mChildFlags.set(ChildFlag::AlphaInherit, inherit); } + + bool isChildFollow() const { return mChildFlags.isSet(ChildFlag::IsFollow); } + void setChildFollow(bool follow) { mChildFlags.set(ChildFlag::IsFollow, follow); } + + bool isChildParentField() const { return mChildFlags.isSet(ChildFlag::ParentField); } + void setChildParentField(bool isField) { mChildFlags.set(ChildFlag::ParentField, isField); } + + bool isChildPreDraw() const { return mChildFlags.isSet(ChildFlag::PreChildDraw); } + void setChildPreDraw(bool preDraw) { mChildFlags.set(ChildFlag::PreChildDraw, preDraw); } + + bool isChildInheritParentColor() const { return mChildFlags.isSet(ChildFlag::Color0Inherit); } + void setChildInheritParentColor(bool inherit) { mChildFlags.set(ChildFlag::Color0Inherit, inherit); } + + bool isChildInheritRotation() const { return mChildFlags.isSet(ChildFlag::RotateInherit); } + void setChildInheritRotation(bool inherit) { mChildFlags.set(ChildFlag::RotateInherit, inherit); } + + bool isChildInheritScale() const { return mChildFlags.isSet(ChildFlag::ScaleInherit); } + void setChildInheritScale(bool inherit) { mChildFlags.set(ChildFlag::ScaleInherit, inherit); } + + bool isChildInheritVelocity() const { return mChildFlags.isSet(ChildFlag::VelInherit); } + void setChildInheritVelocity(bool inherit) { mChildFlags.set(ChildFlag::VelInherit, inherit); } + + // Child Basic + + BillboardType childBillboardType() const { return mChild.billboardType; } + void setChildBillboardType(BillboardType type) { + mChild.billboardType = type; + + const bool isPolygon = (type == Ptcl::BillboardType::PolygonXY || type == Ptcl::BillboardType::PolygonXZ); + mChildFlags.set(ChildFlag::IsPolygon, isPolygon); + } + + // Child Emission + + s32 childEmitRate() const { return mChild.emitRate; } + void setChildEmitRate(s32 rate) { mChild.emitRate = rate; } + + s32 childEmitTiming() const { return mChild.emitTiming; } + void setChildEmitTiming(s32 timing) { mChild.emitTiming = timing; } + + s32 childLife() const { return mChild.life; } + void setChildLife(s32 life) { mChild.life = life; } + + s32 childEmitStep() const { return mChild.emitStep; } + void setChildEmitStep(s32 step) { mChild.emitStep = step; } + + // Child Velocity + + const Math::Vector3f& childRandVelocity() const { return mChild.randVel; } + void setChildRandVelocity(const Math::Vector3f& vel) { mChild.randVel = vel; } + + const Math::Vector3f& childGravity() const { return mChild.gravity; } + void setChildGravity(const Math::Vector3f& gravity) { mChild.gravity = gravity; } + + f32 childVelocityInheritRate() const { return mChild.velInheritRate; } + void setChildVelocityInheritRate(f32 rate) { mChild.velInheritRate = rate; } + + f32 childInitalPositionRand() const { return mChild.initPosRand; } + void setChildInitialPositionRand(f32 rand) { mChild.initPosRand = rand; } + + f32 childFigureVelocity() const { return mChild.figurVel; } + void setChildFigureVelocity(f32 vel) { mChild.figurVel = vel; } + + f32 childAirResistance() const { return mChild.airResist; } + void setChildAirResistance(f32 resist) { mChild.airResist = resist; } + + // Child Rotation + + RotType childRotationType() const { return mRotType; } + void setChildRotationType(RotType type) { mRotType = type; } + + const Math::Vector3i& childInitialRotation() const { return mChild.initRot; } + void setChildInitialRotation(const Math::Vector3i& rotation) { mChild.initRot = rotation; } + + const Math::Vector3i& childInitialRotationRandom() const { return mChild.initRotRand; } + void setChildInitialRotationRandom(const Math::Vector3i& rotation) { mChild.initRotRand = rotation; } + + const Math::Vector3i& childRotationVelocity() const { return mChild.rotVel; } + void setChildRotationVelocity(const Math::Vector3i& velocity) { mChild.rotVel = velocity; } + + const Math::Vector3i& childRotationVelocityRandom() const { return mChild.rotVelRand; } + void setChildRotationVelocityRandom(const Math::Vector3i& random) { mChild.rotVelRand = random; } + + const Math::Vector2f& childRotationBasis() const { return mChild.rotBasis; } + void setChildRotationBasis(const Math::Vector2f& basis) { mChild.rotBasis = basis; } + + // Child Scale + + const Math::Vector2f& childScale() const { return mChild.scale; } + void setChildScale(const Math::Vector2f& scale) { mChild.scale = scale; } + + const Math::Vector2f& childScaleTarget() const { return mChild.scaleTarget; } + void setChildScaleTarget(const Math::Vector2f& scale) { mChild.scaleTarget = scale; } + + f32 childScaleInheritRate() const { return mChild.scaleInheritRate; } + void setChildScaleInheritRate(f32 rate) { mChild.scaleInheritRate = rate; } + + s32 childScaleStartFrame() const { return mChild.scaleStartFrame; } + void setChildScaleStartFrame(s32 frame) { mChild.scaleStartFrame = frame; } + + // Child Texture + + TextureWrap childTextureWrapT() const { return mTextureWrapT; } + void setChildTextureWrapT(TextureWrap wrap) { mTextureWrapT = wrap; } + + TextureWrap childTextureWrapS() const { return mChild.textureWrapS; } + void setChildTextureWrapS(TextureWrap wrap) { mChild.textureWrapS = wrap; } + + TextureFilter childTextureMagFilter() const { return mChild.textureMagFilter; } + void setChildTextureMagFilter(TextureFilter filter) { mChild.textureMagFilter = filter; } + + TextureFilter childTextureMinFilter() const { return mChild.textureMinFilter; } + void setChildTextureMinFilter(TextureFilter filter) { mChild.textureMinFilter = filter; } + + TextureMipFilter childTextureMipFilter() const { return mChild.textureMipFilter; } + void setChildTextureMipFilter(TextureMipFilter filter) { mChild.textureMipFilter = filter; } + + const Math::Vector2f& childTextureUVScale() const { return mChild.texUVScale; } + void setChildTextureUVScale(const Math::Vector2f& scale) { mChild.texUVScale = scale; } + + const TextureHandle& childTextureHandle() const { return mChild.textureHandle; } + + std::shared_ptr childTexture() const { return mChild.textureHandle.get(); } + void setChildTexture(const std::shared_ptr& texture) { mChild.textureHandle.set(texture); } + + // Child Color + + const binColor4f& childPrimaryColor() const { return mChild.color0; } + void setChildPrimaryColor(const binColor4f& color) { mChild.color0 = color; } + + const binColor3f& childSecondaryColor() const { return mChild.color1; } + void setChildSecondaryColor(const binColor3f& color) { mChild.color1 = color; } + + // Child Alpha + + f32 childAlpha() const { return mChild.alpha; } + void setChildAlpha(f32 alpha) { mChild.alpha = alpha; } + + f32 childAlphaTarget() const { return mChild.alphaTarget; } + void setChildAlphaTarget(f32 alpha) { mChild.alphaTarget = alpha; } + + f32 childAlphaInit() const { return mChild.alphaInit; } + void setChildAlphaInit(f32 alpha) { mChild.alphaInit = alpha; } + + s32 childAlphaStartFrame() const { return mChild.alphaStartFrame; } + void setChildAlphaStartFrame(s32 frame) { mChild.alphaStartFrame = frame; } + + s32 childAlphaBaseFrame() const { return mChild.alphaBaseFrame; } + void setChildAlphaBaseFrame(s32 frame) { mChild.alphaBaseFrame = frame; } + + // Child Combiner + + BlendFuncType childBlendFunc() const { return mChild.blendFunc; } + void setChildBlendFunc(BlendFuncType type) { mChild.blendFunc = type; } + + DepthFuncType childDepthFunc() const { return mChild.depthFunc; } + void setChildDepthFunc(DepthFuncType type) { mChild.depthFunc = type; } + + ColorCombinerFuncType childCombinerFunc() const { return mChild.combinerFunc; } + void setChildCombinerFunc(ColorCombinerFuncType type) { mChild.combinerFunc = type; } private: BitFlag mFlag{}; @@ -658,8 +811,6 @@ class Emitter { // ----- Complex Properties ----- \\ - ComplexProperties mComplexProperties{}; - // Fluctuation Properties f32 mFluctuationScale{1.0f}; f32 mFluctuationFreq{20.0f}; @@ -718,7 +869,72 @@ class Emitter { BitFlag mFieldFlags{}; - ChildData mChildData{}; + // Child Propertiies + struct { + // Basic Properties + BillboardType billboardType{BillboardType::Billboard}; + + // Emission Properties + s32 emitRate{1}; + s32 emitTiming{0}; + s32 life{10}; + s32 emitStep{1}; + + // Velocity Properties + Math::Vector3f randVel{0.0f, 0.0f, 0.0f}; + Math::Vector3f gravity{0.0f, -1.0f, 0.0f}; + f32 velInheritRate{1.0f}; + f32 initPosRand{0.0f}; + f32 figurVel{0.1f}; + f32 airResist{1.0f}; + + // Rotation Properties + RotType rotType{RotType::None}; + Math::Vector3i initRot{0, 0, 0}; + Math::Vector3i initRotRand{0, 0, 0}; + Math::Vector3i rotVel{0, 0, 0}; + Math::Vector3i rotVelRand{0, 0, 0}; + Math::Vector2f rotBasis{0.0f, 0.0f}; + + // Scale properties + Math::Vector2f scale{1.0f, 1.0f}; + Math::Vector2f scaleTarget{1.0f, 1.0f}; + f32 scaleInheritRate{1.0f}; + s32 scaleStartFrame{0}; + + // Texture Properties + TextureWrap textureWrapT{TextureWrap::ClampToEdge}; + TextureWrap textureWrapS{TextureWrap::ClampToEdge}; + TextureFilter textureMagFilter{TextureFilter::Nearest}; + TextureFilter textureMinFilter{TextureFilter::Nearest}; + TextureMipFilter textureMipFilter{TextureMipFilter::None}; + Math::Vector2f texUVScale{1.0f, 1.0f}; + TextureHandle textureHandle{}; + + // Color Properties + binColor4f color0{1.0f, 1.0f, 1.0f, 1.0f}; + binColor3f color1{255.0f, 255.0f, 255.0f}; + + // Alpha Properties + f32 alpha{1.0f}; + f32 alphaTarget{1.0f}; + f32 alphaInit{1.0f}; + s32 alphaStartFrame{1}; + s32 alphaBaseFrame{1}; + + // Combiner Properties + BlendFuncType blendFunc{BlendFuncType::Translucent}; + DepthFuncType depthFunc{DepthFuncType::Unk0}; + ColorCombinerFuncType combinerFunc{ColorCombinerFuncType::CombinerConfig0}; + } mChild; + + BitFlag mChildFlags{ + ChildFlag::AlphaInherit, + ChildFlag::ScaleInherit, + ChildFlag::RotateInherit, + ChildFlag::IsFollow, + ChildFlag::Unk80 + }; }; diff --git a/src/editor/childEditor/childEditorWidget.cpp b/src/editor/childEditor/childEditorWidget.cpp deleted file mode 100644 index 76621fe..0000000 --- a/src/editor/childEditor/childEditorWidget.cpp +++ /dev/null @@ -1,285 +0,0 @@ -#include "editor/components/collapsibleWidget.h" -#include "editor/childEditor/childEditorWidget.h" -#include "editor/childEditor/combinerPropertiesWidget.h" -#include "editor/childEditor/alphaPropertiesWidget.h" -#include "editor/childEditor/basicPropertiesWidget.h" -#include "editor/childEditor/colorPropertiesWidget.h" -#include "editor/childEditor/emissionPropertiesWidget.h" -#include "editor/childEditor/rotationPropertiesWidget.h" -#include "editor/childEditor/scalePropertiesWidget.h" -#include "editor/childEditor/texturePropertiesWidget.h" -#include "editor/childEditor/velocityPropertiesWidget.h" - -#include - - -namespace PtclEditor { - - -// ========================================================================== // - - -ChildEditorWidget::ChildEditorWidget(QWidget* parent) : - QWidget{parent} { - - mBasicProperties = new BasicPropertiesWidget(this); - mEmissionProperties = new EmissionPropertiesWidget(this); - mVelocityProperties = new VelocityPropertiesWidget(this); - mRotationProperties = new RotationPropertiesWidget(this); - mScaleProperties = new ScalePropertiesWidget(this); - mTextureProperties = new TexturePropertiesWidget(this); - mColorProperties = new ColorPropertiesWidget(this); - mAlphaProperties = new AlphaPropertiesWidget(this); - mCombinerProperties = new CombinerPropertiesWidget(this); - - // Standard Widget - auto* standardWidget = new QWidget(this); - auto* mainLayout = new QVBoxLayout(standardWidget); - standardWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); - standardWidget->setMinimumWidth(400); - - auto* scrollArea = new QScrollArea(this); - scrollArea->setWidget(standardWidget); - scrollArea->setWidgetResizable(true); - scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - scrollArea->setFrameShape(QFrame::NoFrame); - - auto* outerLayout = new QVBoxLayout(this); - outerLayout->addWidget(scrollArea); - outerLayout->setContentsMargins(0, 0, 0, 0); - setLayout(outerLayout); - - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - setupLayout(mainLayout); - setupConnections(); -} - -void ChildEditorWidget::setupLayout(QVBoxLayout* mainLayout) { - // ChildData row - auto* childDataRow = new QHBoxLayout(); - auto* childDataLabel = new QLabel("Child Emitter:"); - mEnabledCheckbox.setText("Enabled"); - - childDataRow->addWidget(childDataLabel); - childDataRow->addWidget(&mEnabledCheckbox); - childDataRow->addStretch(); - - mainLayout->addLayout(childDataRow); - - // Section Containers - mSectionsContainer = new QWidget(this); - auto* sectionsLayout = new QVBoxLayout(mSectionsContainer); - sectionsLayout->setContentsMargins(0, 0, 0, 0); - - auto addSection = [sectionsLayout](const QString& title, QWidget* widget) { - auto* section = new CollapsibleWidget(title); - section->setContent(widget); - sectionsLayout->addWidget(section); - }; - - addSection("Basic properties", mBasicProperties); - addSection("Emission properties", mEmissionProperties); - addSection("Velocity properties", mVelocityProperties); - addSection("Rotation properties", mRotationProperties); - addSection("Scale properties", mScaleProperties); - addSection("Texture Properties", mTextureProperties); - addSection("Color Properties", mColorProperties); - addSection("Alpha Properties", mAlphaProperties); - addSection("Combiner Properties", mCombinerProperties); - - sectionsLayout->addStretch(); - - mainLayout->addWidget(mSectionsContainer); - mainLayout->addStretch(); -} - -void ChildEditorWidget::setupConnections() { - // Enabled - connect(&mEnabledCheckbox, &QCheckBox::clicked, this, [this](bool checked) { - if (!mDataPtr) { return; } - - mSectionsContainer->setEnabled(checked); - mChildFlag.set(Ptcl::ChildFlag::Enabled, checked); - emit flagsUpdated(mChildFlag); - }); - - // Basic Properties - connect(mBasicProperties, &BasicPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::ChildData::BasicProperties& properties) { - if (!mDataPtr) { return; } - mDataPtr->setBasicProperties(properties); - }); - - connect(mBasicProperties, &BasicPropertiesWidget::isFollowUpdated, this, [this](bool follow) { - if (!mDataPtr) { return; } - mChildFlag.set(Ptcl::ChildFlag::IsFollow, follow); - emit flagsUpdated(mChildFlag); - }); - - connect(mBasicProperties, &BasicPropertiesWidget::isParentFieldUpdated, this, [this](bool field) { - if (!mDataPtr) { return; } - mChildFlag.set(Ptcl::ChildFlag::ParentField, field); - emit flagsUpdated(mChildFlag); - }); - - connect(mBasicProperties, &BasicPropertiesWidget::isPolygonUpdated, this, [this](bool polygon) { - if (!mDataPtr) { return; } - mChildFlag.set(Ptcl::ChildFlag::IsPolygon, polygon); - emit flagsUpdated(mChildFlag); - }); - - connect(mBasicProperties, &BasicPropertiesWidget::isPreDrawUpdated, this, [this](bool preDraw) { - if (!mDataPtr) { return; } - mChildFlag.set(Ptcl::ChildFlag::PreChildDraw, preDraw); - emit flagsUpdated(mChildFlag); - }); - - // Emission Properties - connect(mEmissionProperties, &EmissionPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::ChildData::EmissionProperties& properties) { - if (!mDataPtr) { return; } - mDataPtr->setEmissionProperties(properties); - }); - - // Velocity Properties - connect(mVelocityProperties, &VelocityPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::ChildData::VelocityProperties& properties) { - if (!mDataPtr) { return; } - mDataPtr->setVelocityProperties(properties); - }); - - connect(mVelocityProperties, &VelocityPropertiesWidget::inheritVelUpdated, this, [this](bool inherit) { - if (!mDataPtr) { return; } - mChildFlag.set(Ptcl::ChildFlag::VelInherit, inherit); - emit flagsUpdated(mChildFlag); - }); - - // Rotation Properties - connect(mRotationProperties, &RotationPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::ChildData::RotationProperties& properties) { - if (!mDataPtr) { return; } - mDataPtr->setRotationProperties(properties); - }); - - connect(mRotationProperties, &RotationPropertiesWidget::inheritRotationUpdated, this, [this](bool inherit) { - if (!mDataPtr) { return; } - mChildFlag.set(Ptcl::ChildFlag::RotateInherit, inherit); - emit flagsUpdated(mChildFlag); - }); - - // Scale Properties - connect(mScaleProperties, &ScalePropertiesWidget::propertiesUpdated, this, [this](const Ptcl::ChildData::ScaleProperties& properties) { - if (!mDataPtr) { return; } - mDataPtr->setScaleProperties(properties); - }); - - connect(mScaleProperties, &ScalePropertiesWidget::inheritScaleUpdated, this, [this](bool inherit) { - if (!mDataPtr) { return; } - mChildFlag.set(Ptcl::ChildFlag::ScaleInherit, inherit); - emit flagsUpdated(mChildFlag); - }); - - // Texture Properties - connect(mTextureProperties, &TexturePropertiesWidget::propertiesUpdated, this, [this](const Ptcl::ChildData::TextureProperties& properties) { - if (!mDataPtr) { return; } - mDataPtr->setTextureProperties(properties); - }); - - connect(mTextureProperties, &TexturePropertiesWidget::textureUpdated, this, [this](const std::shared_ptr& oldTexture, const std::shared_ptr& newTexture) { - if (!mDataPtr) { return; } - mDataPtr->setTexture(newTexture); - mCombinerProperties->updateCombinerPreview(); - }); - - // Color Properties - connect(mColorProperties, &ColorPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::ChildData::ColorProperties& properties) { - if (!mDataPtr) { return; } - mDataPtr->setColorProperties(properties); - mCombinerProperties->updateCombinerPreview(); - }); - - connect(mColorProperties, &ColorPropertiesWidget::inheritColorUpdated, this, [this](bool inherit) { - if (!mDataPtr) { return; } - mChildFlag.set(Ptcl::ChildFlag::Color0Inherit, inherit); - emit flagsUpdated(mChildFlag); - setCombinerPropertiesSrc(); - }); - - // Alpha Properties - connect(mAlphaProperties, &AlphaPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::ChildData::AlphaProperties& properties) { - if (!mDataPtr) { return; } - mDataPtr->setAlphaProperties(properties); - }); - - connect(mAlphaProperties, &AlphaPropertiesWidget::inheritAlphaUpdated, this, [this](bool inherit) { - if (!mDataPtr) { return; } - mChildFlag.set(Ptcl::ChildFlag::AlphaInherit, inherit); - emit flagsUpdated(mChildFlag); - }); - - // Combiner Properties - connect(mCombinerProperties, &CombinerPropertiesWidget::propertiesUpdated, this, [this](const Ptcl::ChildData::CombinerProperties& properties) { - if (!mDataPtr) { return; } - mDataPtr->setCombinerProperties(properties); - }); -} - -void ChildEditorWidget::setChildData(Ptcl::ChildData* childData, const BitFlag& childFlag) { - QSignalBlocker b1(mBasicProperties); - QSignalBlocker b2(mEmissionProperties); - QSignalBlocker b3(mVelocityProperties); - QSignalBlocker b4(mRotationProperties); - QSignalBlocker b5(mScaleProperties); - QSignalBlocker b6(mTextureProperties); - QSignalBlocker b7(mColorProperties); - QSignalBlocker b8(mAlphaProperties); - QSignalBlocker b9(mCombinerProperties); - - mDataPtr = childData; - mChildFlag = childFlag; - - mBasicProperties->setProperties(mDataPtr->basicProperties(), mChildFlag.isSet(Ptcl::ChildFlag::IsFollow), mChildFlag.isSet(Ptcl::ChildFlag::ParentField), mChildFlag.isSet(Ptcl::ChildFlag::PreChildDraw)); - mEmissionProperties->setProperties(mDataPtr->emissionProperties()); - mVelocityProperties->setProperties(mDataPtr->velocityProperties(), mChildFlag.isSet(Ptcl::ChildFlag::VelInherit)); - mRotationProperties->setProperties(mDataPtr->rotationProperties(), mChildFlag.isSet(Ptcl::ChildFlag::RotateInherit)); - mScaleProperties->setProperties(mDataPtr->scaleProperties(), mChildFlag.isSet(Ptcl::ChildFlag::ScaleInherit)); - mTextureProperties->setProperties(mDataPtr->textureProperties(), mDataPtr->textureHandle().get()); - mColorProperties->setProperties(mDataPtr->colorProperties(), mChildFlag.isSet(Ptcl::ChildFlag::Color0Inherit)); - mAlphaProperties->setProperties(mDataPtr->alphaProperties(), mChildFlag.isSet(Ptcl::ChildFlag::AlphaInherit)); - mCombinerProperties->setProperties(mDataPtr->combinerProperties()); - setCombinerPropertiesSrc(); - - const bool isEnabled = mChildFlag.isSet(Ptcl::ChildFlag::Enabled); - mEnabledCheckbox.setChecked(isEnabled); - mSectionsContainer->setEnabled(isEnabled); - - setEnabled(true); -} - -void ChildEditorWidget::setTextureList(const Ptcl::TextureList* textureList) { - mTextureList = textureList; - mTextureProperties->setTextureList(textureList); -} - -void ChildEditorWidget::setParentColor0(const Ptcl::binColor4f& parentColor0) { - mParentColor0 = parentColor0; - mCombinerProperties->updateCombinerPreview(); -}; - -void ChildEditorWidget::setCombinerPropertiesSrc() { - if (mChildFlag.isSet(Ptcl::ChildFlag::Color0Inherit)) { - mCombinerProperties->setCombinerSrc(&mDataPtr->textureHandle(), &mDataPtr->colorProperties().color1, &mParentColor0); - } else { - mCombinerProperties->setCombinerSrc(&mDataPtr->textureHandle(), &mDataPtr->colorProperties().color1, &mDataPtr->colorProperties().color0); - } - mCombinerProperties->updateCombinerPreview(); -} - -void ChildEditorWidget::clear() { - setEnabled(false); - mDataPtr = nullptr; -} - - -// ========================================================================== // - - -} // namespace PtclEditor diff --git a/src/editor/childEditor/colorPropertiesWidget.cpp b/src/editor/childEditor/colorPropertiesWidget.cpp deleted file mode 100644 index 05ef0a5..0000000 --- a/src/editor/childEditor/colorPropertiesWidget.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "editor/childEditor/colorPropertiesWidget.h" - -#include - - -namespace PtclEditor { - - -// ========================================================================== // - - -ChildEditorWidget::ColorPropertiesWidget::ColorPropertiesWidget(QWidget* parent) : - QWidget{parent} { - - auto* mainLayout = new QFormLayout(this); - - mInheritColorCheckBox.setText("Inherit Parent Color 0"); - mColor1Widget.enableAlpha(false); - - mainLayout->addRow("Color:", &mInheritColorCheckBox); - mainLayout->addRow("Color 0:", &mColor0Widget); - mainLayout->addRow("Color 1:", &mColor1Widget); - - setLayout(mainLayout); - setupConnections(); -} - -void ChildEditorWidget::ColorPropertiesWidget::setupConnections() { - // Inherit Color - connect(&mInheritColorCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - QSignalBlocker b1(mColor0Widget); - mColor0Widget.setDisabled(checked); - emit inheritColorUpdated(checked); - }); - - // Color 0 - connect(&mColor0Widget, &RGBAColorWidget::colorChanged, this, [this]() { - mProps.color0 = mColor0Widget.color(); - emit propertiesUpdated(mProps); - }); - - // Color 1 - connect(&mColor1Widget, &RGBAColorWidget::colorChanged, this, [this]() { - const auto& color = mColor1Widget.color(); - - Ptcl::binColor3f newColor{ - color.r * 255.0f, - color.g * 255.0f, - color.b * 255.0f - }; - - mProps.color1 = newColor; - emit propertiesUpdated(mProps); - }); -} - -void ChildEditorWidget::ColorPropertiesWidget::setProperties(const Ptcl::ChildData::ColorProperties& properties, bool inheritColor) { - QSignalBlocker b1(mColor0Widget); - QSignalBlocker b2(mColor1Widget); - QSignalBlocker b3(mInheritColorCheckBox); - - mProps = properties; - - mColor0Widget.setColor(mProps.color0); - mColor0Widget.setDisabled(inheritColor); - - Ptcl::binColor4f color1{ - std::clamp(mProps.color1.r / 255.0f, 0.0f, 1.0f), - std::clamp(mProps.color1.g / 255.0f, 0.0f, 1.0f), - std::clamp(mProps.color1.b / 255.0f, 0.0f, 1.0f), - 1.0f - }; - mColor1Widget.setColor(color1); - mInheritColorCheckBox.setChecked(inheritColor); -} - - -// ========================================================================== // - - -} // namespace PtclEditor diff --git a/src/editor/childEditor/combinerPropertiesWidget.cpp b/src/editor/childEditor/combinerPropertiesWidget.cpp deleted file mode 100644 index d17acc5..0000000 --- a/src/editor/childEditor/combinerPropertiesWidget.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "editor/childEditor/combinerPropertiesWidget.h" - -#include - - -namespace PtclEditor { - - -// ========================================================================== // - - -ChildEditorWidget::CombinerPropertiesWidget::CombinerPropertiesWidget(QWidget* parent) : - QWidget{parent} { - - auto* mainLayout = new QFormLayout(this); - - mainLayout->addRow("Blend Function:", &mBlendFuncComboBox); - mainLayout->addRow("Depth Function:", &mDepthFuncComboBox); - mainLayout->addRow("Combiner Function:", &mCombinerFuncComboBox); - mainLayout->addWidget(&mCombinerPreview); - - connect(&mBlendFuncComboBox, &QComboBox::currentIndexChanged, this, [this]() { - mProps.blendFunc = mBlendFuncComboBox.currentEnum(); - emit propertiesUpdated(mProps); - }); - - connect(&mDepthFuncComboBox, &QComboBox::currentIndexChanged, this, [this]() { - mProps.depthFunc = mDepthFuncComboBox.currentEnum(); - emit propertiesUpdated(mProps); - }); - - connect(&mCombinerFuncComboBox, &QComboBox::currentIndexChanged, this, [this]() { - auto value = mCombinerFuncComboBox.currentEnum(); - mProps.combinerFunc = value; - mCombinerPreview.setConfig(static_cast(value)); - emit propertiesUpdated(mProps); - }); -} - -void ChildEditorWidget::CombinerPropertiesWidget::setProperties(const Ptcl::ChildData::CombinerProperties& properties) { - QSignalBlocker b1(mBlendFuncComboBox); - QSignalBlocker b2(mDepthFuncComboBox); - QSignalBlocker b3(mCombinerFuncComboBox); - - mProps = properties; - - mBlendFuncComboBox.setCurrentEnum(mProps.blendFunc); - mDepthFuncComboBox.setCurrentEnum(mProps.depthFunc); - mCombinerFuncComboBox.setCurrentEnum(mProps.combinerFunc); - mCombinerPreview.setConfig(static_cast(mProps.combinerFunc)); -} - -void ChildEditorWidget::CombinerPropertiesWidget::setCombinerSrc(const Ptcl::TextureHandle* texture, const Ptcl::binColor3f* constant, const Ptcl::binColor4f* primary) { - mCombinerPreview.setCombinerSrc(texture, constant, primary); -} - -void ChildEditorWidget::CombinerPropertiesWidget::updateCombinerPreview() { - mCombinerPreview.updateStages(); -} - - -// ========================================================================== // - - -} // namespace PtclEditor diff --git a/src/editor/childEditor/scalePropertiesWidget.cpp b/src/editor/childEditor/scalePropertiesWidget.cpp deleted file mode 100644 index f35aee5..0000000 --- a/src/editor/childEditor/scalePropertiesWidget.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "editor/childEditor/scalePropertiesWidget.h" - -#include - -namespace PtclEditor { - - -// ========================================================================== // - - -ChildEditorWidget::ScalePropertiesWidget::ScalePropertiesWidget(QWidget* parent) : - QWidget{parent} { - - auto* mainLayout = new QFormLayout(this); - - mInheritScaleCheckBox.setText("Inherit Parent Scale"); - mInheritRateSpinBox.setRange(0.0f, 1.0f); - mInheritRateSpinBox.setSingleStep(0.1f); - mScaleSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); - mScaleTargetSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); - mStartFrameSpinBox.setRange(0, std::numeric_limits::max()); - - mainLayout->addRow("Scale:", &mInheritScaleCheckBox); - mainLayout->addRow("Inherit Rate:", &mInheritRateSpinBox); - mainLayout->addRow("Initial Scale:", &mScaleSpinBox); - mainLayout->addRow("Target Scale:", &mScaleTargetSpinBox); - mainLayout->addRow("Start Frame:", &mStartFrameSpinBox); - - setupConnections(); -} - -void ChildEditorWidget::ScalePropertiesWidget::setupConnections() { - // Inherit Scale - connect(&mInheritScaleCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - emit inheritScaleUpdated(checked); - }); - - // Scale - connect(&mScaleSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mProps.scale = mScaleSpinBox.getVector(); - emit propertiesUpdated(mProps); - }); - - // Scale Target - connect(&mScaleTargetSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mProps.scaleTarget = mScaleTargetSpinBox.getVector(); - emit propertiesUpdated(mProps); - }); - - // Inherit Rate - connect(&mInheritRateSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mProps.scaleInheritRate = static_cast(value); - emit propertiesUpdated(mProps); - }); - - // Inherit Rate - connect(&mStartFrameSpinBox, &QSpinBox::valueChanged, this, [this](s32 value) { - mProps.scaleStartFrame = value; - emit propertiesUpdated(mProps); - }); -} - -void ChildEditorWidget::ScalePropertiesWidget::setProperties(const Ptcl::ChildData::ScaleProperties& properties, bool inheritScale) { - QSignalBlocker b1(mScaleSpinBox); - QSignalBlocker b2(mScaleTargetSpinBox); - QSignalBlocker b3(mInheritRateSpinBox); - QSignalBlocker b4(mStartFrameSpinBox); - QSignalBlocker b5(mInheritScaleCheckBox); - - mProps = properties; - - mScaleSpinBox.setVector(mProps.scale); - mScaleTargetSpinBox.setVector(mProps.scaleTarget); - mInheritRateSpinBox.setValue(mProps.scaleInheritRate); - mStartFrameSpinBox.setValue(mProps.scaleStartFrame); - mInheritScaleCheckBox.setChecked(inheritScale); -} - - -// ========================================================================== // - - -} // namespace PtclEditor diff --git a/src/editor/childEditor/velocityPropertiesWidget.cpp b/src/editor/childEditor/velocityPropertiesWidget.cpp deleted file mode 100644 index 7e2563b..0000000 --- a/src/editor/childEditor/velocityPropertiesWidget.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include "editor/childEditor/velocityPropertiesWidget.h" - -#include - -namespace PtclEditor { - - -// ========================================================================== // - - -ChildEditorWidget::VelocityPropertiesWidget::VelocityPropertiesWidget(QWidget* parent) : - QWidget{parent} { - - // TODO: Determine better ranges? - mRandVelSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); - mGravitySpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); - mVelInheritSpinBox.setRange(0.0f, 1.0f); - mInitPosRandSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); - mFigureVelSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); - mAirResistSpinBox.setRange(0.0f, 1.0f); - - auto* mainLayout = new QFormLayout(this); - - mainLayout->addRow("Random Velocity:", &mRandVelSpinBox); - mainLayout->addRow("Gravity:", &mGravitySpinBox); - mainLayout->addRow("Inherit Parent Velocity:", &mInheritVelCheckBox); - mainLayout->addRow("Velocity Inherit Rate:", &mVelInheritSpinBox); - mainLayout->addRow("Inital Position Random:", &mInitPosRandSpinBox); - mainLayout->addRow("Figure Velocity:", &mFigureVelSpinBox); - mainLayout->addRow("Air Resistance:", &mAirResistSpinBox); - - setupConnections(); -} - -void ChildEditorWidget::VelocityPropertiesWidget::setupConnections() { - // Rand Velocity - connect(&mRandVelSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mProps.randVel = mRandVelSpinBox.getVector(); - emit propertiesUpdated(mProps); - }); - - // Gravity - connect(&mGravitySpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mProps.gravity = mGravitySpinBox.getVector(); - emit propertiesUpdated(mProps); - }); - - // Inherit Velocity - connect(&mInheritVelCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - QSignalBlocker b1(mVelInheritSpinBox); - mVelInheritSpinBox.setEnabled(checked); - emit inheritVelUpdated(checked); - }); - - // Inherit Rate - connect(&mVelInheritSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mProps.velInheritRate = static_cast(value); - emit propertiesUpdated(mProps); - }); - - // Init Pos Rand - connect(&mInitPosRandSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mProps.initPosRand = static_cast(value); - emit propertiesUpdated(mProps); - }); - - // Figure Velocity - connect(&mFigureVelSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mProps.figurVel = static_cast(value); - emit propertiesUpdated(mProps); - }); - - // Air Resistance - connect(&mAirResistSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mProps.airResist = static_cast(value); - emit propertiesUpdated(mProps); - }); -} - -void ChildEditorWidget::VelocityPropertiesWidget::setProperties(const Ptcl::ChildData::VelocityProperties& properties, bool inheritVelocity) { - QSignalBlocker b1(mRandVelSpinBox); - QSignalBlocker b2(mGravitySpinBox); - QSignalBlocker b3(mVelInheritSpinBox); - QSignalBlocker b4(mInitPosRandSpinBox); - QSignalBlocker b5(mFigureVelSpinBox); - QSignalBlocker b6(mAirResistSpinBox); - QSignalBlocker b7(mInheritVelCheckBox); - - mProps = properties; - - mRandVelSpinBox.setVector(mProps.randVel); - mGravitySpinBox.setVector(mProps.gravity); - mVelInheritSpinBox.setValue(mProps.velInheritRate); - mVelInheritSpinBox.setEnabled(inheritVelocity); - mInitPosRandSpinBox.setValue(mProps.initPosRand); - mFigureVelSpinBox.setValue(mProps.figurVel); - mAirResistSpinBox.setValue(mProps.airResist); - mInheritVelCheckBox.setChecked(inheritVelocity); -} - - -// ========================================================================== // - - -} // namespace PtclEditor diff --git a/src/editor/childEditor/alphaPropertiesWidget.cpp b/src/editor/inspector/child/childAlphaInspector.cpp similarity index 50% rename from src/editor/childEditor/alphaPropertiesWidget.cpp rename to src/editor/inspector/child/childAlphaInspector.cpp index 91a5118..6b732d1 100644 --- a/src/editor/childEditor/alphaPropertiesWidget.cpp +++ b/src/editor/inspector/child/childAlphaInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/childEditor/alphaPropertiesWidget.h" +#include "editor/inspector/child/childAlphaInspector.h" #include @@ -9,8 +9,8 @@ namespace PtclEditor { // ========================================================================== // -ChildEditorWidget::AlphaPropertiesWidget::AlphaPropertiesWidget(QWidget* parent) : - QWidget{parent} { +ChildAlphaInspector::ChildAlphaInspector(QWidget* parent) : + InspectorWidgetBase{parent} { mAlphaSpinBox.setRange(0.0f, 1.0f); mAlphaSpinBox.setSingleStep(0.1f); @@ -38,44 +38,75 @@ ChildEditorWidget::AlphaPropertiesWidget::AlphaPropertiesWidget(QWidget* parent) setupConnections(); } -void ChildEditorWidget::AlphaPropertiesWidget::setupConnections() { +void ChildAlphaInspector::setupConnections() { // Inherit Alpha connect(&mInheritAlphaCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - emit inheritAlphaUpdated(checked); + setEmitterProperty( + "Toggle Child Inherit Alpha", + "ToggleChildInheritAlpha", + &Ptcl::Emitter::isChildInheritAlpha, + &Ptcl::Emitter::setChildInheritAlpha, + checked + ); }); // Alpha connect(&mAlphaSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mProps.alpha = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Child Alpha", + "SetChildAlpha", + &Ptcl::Emitter::childAlpha, + &Ptcl::Emitter::setChildAlpha, + static_cast(value) + ); }); // Alpha Target connect(&mAlphaTargetSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mProps.alphaTarget = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Child Alpha Target", + "SetChildAlphaTarget", + &Ptcl::Emitter::childAlphaTarget, + &Ptcl::Emitter::setChildAlphaTarget, + static_cast(value) + ); }); // Alpha Init connect(&mAlphaInitSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { - mProps.alphaInit = static_cast(value); - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Child Alpha Init", + "SetChildAlphaInit", + &Ptcl::Emitter::childAlphaInit, + &Ptcl::Emitter::setChildAlphaInit, + static_cast(value) + ); }); // Start Frame connect(&mStartFrameSpinBox, &QSpinBox::valueChanged, this, [this](s32 value) { - mProps.alphaStartFrame = value; - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Child Alpha Start Frame", + "SetChildAlphaStartFrame", + &Ptcl::Emitter::childAlphaStartFrame, + &Ptcl::Emitter::setChildAlphaStartFrame, + value + ); }); // Base Frame connect(&mBaseFrameSpinBox, &QSpinBox::valueChanged, this, [this](s32 value) { - mProps.alphaBaseFrame = value; - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Child Alpha Base Frame", + "SetChildAlphaBaseFrame", + &Ptcl::Emitter::childAlphaBaseFrame, + &Ptcl::Emitter::setChildAlphaBaseFrame, + value + ); }); } -void ChildEditorWidget::AlphaPropertiesWidget::setProperties(const Ptcl::ChildData::AlphaProperties& properties, bool inheritAlpha) { +void ChildAlphaInspector::populateProperties() { QSignalBlocker b1(mAlphaSpinBox); QSignalBlocker b2(mAlphaTargetSpinBox); QSignalBlocker b3(mAlphaInitSpinBox); @@ -83,15 +114,13 @@ void ChildEditorWidget::AlphaPropertiesWidget::setProperties(const Ptcl::ChildDa QSignalBlocker b5(mBaseFrameSpinBox); QSignalBlocker b6(mInheritAlphaCheckBox); - mProps = properties; + mAlphaSpinBox.setValue(mEmitter->childAlpha()); + mAlphaTargetSpinBox.setValue(mEmitter->childAlphaTarget()); + mAlphaInitSpinBox.setValue(mEmitter->childAlphaInit()); + mStartFrameSpinBox.setValue(mEmitter->childAlphaStartFrame()); + mBaseFrameSpinBox.setValue(mEmitter->childAlphaBaseFrame()); - mAlphaSpinBox.setValue(mProps.alpha); - mAlphaTargetSpinBox.setValue(mProps.alphaTarget); - mAlphaInitSpinBox.setValue(mProps.alphaInit); - mStartFrameSpinBox.setValue(mProps.alphaStartFrame); - mBaseFrameSpinBox.setValue(mProps.alphaBaseFrame); - - mInheritAlphaCheckBox.setChecked(inheritAlpha); + mInheritAlphaCheckBox.setChecked(mEmitter->isChildInheritAlpha()); } diff --git a/src/editor/inspector/child/childColorInspector.cpp b/src/editor/inspector/child/childColorInspector.cpp new file mode 100644 index 0000000..dee0bdc --- /dev/null +++ b/src/editor/inspector/child/childColorInspector.cpp @@ -0,0 +1,96 @@ +#include "editor/inspector/child/childColorInspector.h" + +#include + + +namespace PtclEditor { + + +// ========================================================================== // + + +ChildColorInspector::ChildColorInspector(QWidget* parent) : + InspectorWidgetBase{parent} { + + auto* mainLayout = new QFormLayout(this); + + mInheritColorCheckBox.setText("Inherit Parent Primary Color"); + mColor1Widget.enableAlpha(false); + + mainLayout->addRow("Color:", &mInheritColorCheckBox); + mainLayout->addRow("Primary Color:", &mColor0Widget); + mainLayout->addRow("Secondary Color:", &mColor1Widget); + + setLayout(mainLayout); + setupConnections(); +} + +void ChildColorInspector::setupConnections() { + // Inherit Color + connect(&mInheritColorCheckBox, &QCheckBox::clicked, this, [this](bool checked) { + setEmitterProperty( + "Toggle Child Inherit Parent Color", + "ToggleChildInheritColor", + &Ptcl::Emitter::isChildInheritParentColor, + &Ptcl::Emitter::setChildInheritParentColor, + checked + ); + }); + + // Color 0 + connect(&mColor0Widget, &RGBAColorWidget::colorChanged, this, [this]() { + const auto newColor = mColor0Widget.color(); + setEmitterProperty( + "Set Child Primary Color", + "SetChildPrimaryColor", + &Ptcl::Emitter::childPrimaryColor, + &Ptcl::Emitter::setChildPrimaryColor, + newColor + ); + }); + + // Color 1 + connect(&mColor1Widget, &RGBAColorWidget::colorChanged, this, [this]() { + const auto color = mColor1Widget.color(); + + Ptcl::binColor3f newColor{ + color.r * 255.0f, + color.g * 255.0f, + color.b * 255.0f + }; + + setEmitterProperty( + "Set Child Secondary Color", + "SetChildSecondaryColor", + &Ptcl::Emitter::childSecondaryColor, + &Ptcl::Emitter::setChildSecondaryColor, + newColor + ); + }); +} + +void ChildColorInspector::populateProperties() { + QSignalBlocker b1(mColor0Widget); + QSignalBlocker b2(mColor1Widget); + QSignalBlocker b3(mInheritColorCheckBox); + + const bool inheritParentColor = mEmitter->isChildInheritParentColor(); + + mColor0Widget.setColor(mEmitter->childPrimaryColor()); + mColor0Widget.setDisabled(inheritParentColor); + + Ptcl::binColor4f color1{ + std::clamp(mEmitter->childSecondaryColor().r / 255.0f, 0.0f, 1.0f), + std::clamp(mEmitter->childSecondaryColor().g / 255.0f, 0.0f, 1.0f), + std::clamp(mEmitter->childSecondaryColor().b / 255.0f, 0.0f, 1.0f), + 1.0f + }; + mColor1Widget.setColor(color1); + mInheritColorCheckBox.setChecked(inheritParentColor); +} + + +// ========================================================================== // + + +} // namespace PtclEditor diff --git a/src/editor/inspector/child/childCombinerInspector.cpp b/src/editor/inspector/child/childCombinerInspector.cpp new file mode 100644 index 0000000..efbabdb --- /dev/null +++ b/src/editor/inspector/child/childCombinerInspector.cpp @@ -0,0 +1,78 @@ +#include "editor/inspector/child/childCombinerInspector.h" + +#include + + +namespace PtclEditor { + + +// ========================================================================== // + + +ChildCombinerInspector::ChildCombinerInspector(QWidget* parent) : + InspectorWidgetBase{parent} { + + auto* mainLayout = new QFormLayout(this); + + mainLayout->addRow("Blend Function:", &mBlendFuncComboBox); + mainLayout->addRow("Depth Function:", &mDepthFuncComboBox); + mainLayout->addRow("Combiner Function:", &mCombinerFuncComboBox); + mainLayout->addWidget(&mCombinerPreview); + + setupConnections(); +} + +void ChildCombinerInspector::setupConnections() { + connect(&mBlendFuncComboBox, &QComboBox::currentIndexChanged, this, [this]() { + const auto func = mBlendFuncComboBox.currentEnum(); + setEmitterProperty( + "Set Child Blend Function", + "SetChildBlendFunc", + &Ptcl::Emitter::childBlendFunc, + &Ptcl::Emitter::setChildBlendFunc, + func + ); + }); + + connect(&mDepthFuncComboBox, &QComboBox::currentIndexChanged, this, [this]() { + const auto func = mDepthFuncComboBox.currentEnum(); + setEmitterProperty( + "Set Child Depth Function", + "SetChildDepthFunc", + &Ptcl::Emitter::childDepthFunc, + &Ptcl::Emitter::setChildDepthFunc, + func + ); + }); + + connect(&mCombinerFuncComboBox, &QComboBox::currentIndexChanged, this, [this]() { + const auto func = mCombinerFuncComboBox.currentEnum(); + setEmitterProperty( + "Set Child Combiner Function", + "SetChildCombinerFunc", + &Ptcl::Emitter::childCombinerFunc, + &Ptcl::Emitter::setChildCombinerFunc, + func + ); + }); +} + +void ChildCombinerInspector::populateProperties() { + QSignalBlocker b1(mBlendFuncComboBox); + QSignalBlocker b2(mDepthFuncComboBox); + QSignalBlocker b3(mCombinerFuncComboBox); + + mBlendFuncComboBox.setCurrentEnum(mEmitter->childBlendFunc()); + mDepthFuncComboBox.setCurrentEnum(mEmitter->childDepthFunc()); + mCombinerFuncComboBox.setCurrentEnum(mEmitter->childCombinerFunc()); + mCombinerPreview.setConfig(static_cast(mEmitter->childCombinerFunc())); + + mCombinerPreview.setCombinerSrc(&mEmitter->childTextureHandle(), &mEmitter->childSecondaryColor(), &mEmitter->childPrimaryColor()); + mCombinerPreview.updateStages(); +} + + +// ========================================================================== // + + +} // namespace PtclEditor diff --git a/src/editor/childEditor/emissionPropertiesWidget.cpp b/src/editor/inspector/child/childEmissionInspector.cpp similarity index 58% rename from src/editor/childEditor/emissionPropertiesWidget.cpp rename to src/editor/inspector/child/childEmissionInspector.cpp index cc92752..015afe0 100644 --- a/src/editor/childEditor/emissionPropertiesWidget.cpp +++ b/src/editor/inspector/child/childEmissionInspector.cpp @@ -1,15 +1,16 @@ -#include "editor/childEditor/emissionPropertiesWidget.h" +#include "editor/inspector/child/childEmissionInspector.h" #include + namespace PtclEditor { // ========================================================================== // -ChildEditorWidget::EmissionPropertiesWidget::EmissionPropertiesWidget(QWidget* parent) : - QWidget{parent} { +ChildEmissionInspector::ChildEmissionInspector(QWidget* parent) : + InspectorWidgetBase{parent} { // Emission Rate mEmitRateSpinBox.setRange(0, std::numeric_limits::max()); @@ -47,55 +48,75 @@ ChildEditorWidget::EmissionPropertiesWidget::EmissionPropertiesWidget(QWidget* p setupConnections(); } -void ChildEditorWidget::EmissionPropertiesWidget::setupConnections() { +void ChildEmissionInspector::setupConnections() { // Emission Rate connect(&mEmitRateSpinBox, &QSpinBox::valueChanged, this, [this](s32 value) { - mProps.emitRate = value; - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Child Emission Rate", + "SetChildEmissionRate", + &Ptcl::Emitter::childEmitRate, + &Ptcl::Emitter::setChildEmitRate, + value + ); }); // Emission Timing connect(&mEmitTimingSpinBox, &QSpinBox::valueChanged, this, [this](s32 value) { - mProps.emitTiming = value; - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Child Emission Start Time", + "SetChildEmissionStartTime", + &Ptcl::Emitter::childEmitTiming, + &Ptcl::Emitter::setChildEmitTiming, + value + ); }); // Lifespan connect(&mLifeSpinBox, &QSpinBox::valueChanged, this, [this](s32 value) { - mProps.life = value; - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Child Lifespan", + "SetChildLifespan", + &Ptcl::Emitter::childLife, + &Ptcl::Emitter::setChildLife, + value + ); }); // Infinite Life connect(&mInfiniteLifeCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - QSignalBlocker b1(mLifeSpinBox); - - mProps.life = checked ? sLifeInfinite : 100; - mLifeSpinBox.setValue(mProps.life); - mLifeSpinBox.setEnabled(!checked); - emit propertiesUpdated(mProps); + const s32 newLife = checked ? sLifeInfinite : 100; + setEmitterProperty( + "Toggle Child Infinite Life", + "ToggleChildInfiniteLife", + &Ptcl::Emitter::childLife, + &Ptcl::Emitter::setChildLife, + newLife + ); }); // Emission Step connect(&mEmitStepSpinBox, &QSpinBox::valueChanged, this, [this](s32 value) { - mProps.emitStep = value; - emit propertiesUpdated(mProps); + setEmitterProperty( + "Toggle Child Emission Interval", + "ToggleChildEmitStep", + &Ptcl::Emitter::childEmitStep, + &Ptcl::Emitter::setChildEmitStep, + value + ); }); } -void ChildEditorWidget::EmissionPropertiesWidget::setProperties(const Ptcl::ChildData::EmissionProperties& properties) { +void ChildEmissionInspector::populateProperties() { QSignalBlocker b1(mEmitRateSpinBox); QSignalBlocker b2(mEmitTimingSpinBox); QSignalBlocker b3(mLifeSpinBox); QSignalBlocker b4(mEmitStepSpinBox); - mProps = properties; - - mEmitRateSpinBox.setValue(mProps.emitRate); - mEmitTimingSpinBox.setValue(mProps.emitTiming); - mEmitStepSpinBox.setValue(mProps.emitStep); + mEmitRateSpinBox.setValue(mEmitter->childEmitRate()); + mEmitTimingSpinBox.setValue(mEmitter->childEmitTiming()); + mEmitStepSpinBox.setValue(mEmitter->childEmitStep()); - const s32 lifeSpan = mProps.life; + const s32 lifeSpan = mEmitter->childLife(); const bool infiniteLife = (lifeSpan == sLifeInfinite); mLifeSpinBox.setValue(lifeSpan); mLifeSpinBox.setDisabled(infiniteLife); diff --git a/src/editor/childEditor/basicPropertiesWidget.cpp b/src/editor/inspector/child/childGeneralInspector.cpp similarity index 52% rename from src/editor/childEditor/basicPropertiesWidget.cpp rename to src/editor/inspector/child/childGeneralInspector.cpp index 6802618..da9e0d5 100644 --- a/src/editor/childEditor/basicPropertiesWidget.cpp +++ b/src/editor/inspector/child/childGeneralInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/childEditor/basicPropertiesWidget.h" +#include "editor/inspector/child/childGeneralInspector.h" #include @@ -8,13 +8,14 @@ namespace PtclEditor { // ========================================================================== // -ChildEditorWidget::BasicPropertiesWidget::BasicPropertiesWidget(QWidget* parent) : - QWidget{parent} { +ChildGeneralInspector::ChildGeneralInspector(QWidget* parent) : + InspectorWidgetBase{parent} { for (Ptcl::BillboardType type : sChildBillboardTypes) { mBillboardComboBox.addItem(Ptcl::toString(type), QVariant::fromValue(type)); } + mEnabledCheckBox.setText("Enable Child Emitter"); mFollowCheckBox.setText("Follow Parent Emitter"); mParentFieldCheckBox.setText("Apply Parent's Field"); @@ -23,6 +24,7 @@ ChildEditorWidget::BasicPropertiesWidget::BasicPropertiesWidget(QWidget* parent) auto* mainLayout = new QFormLayout(this); + mainLayout->addRow("Child:", &mEnabledCheckBox); mainLayout->addRow("Billboard Type:", &mBillboardComboBox); mainLayout->addRow("Follow Type:", &mFollowCheckBox); mainLayout->addRow("Field Type:", &mParentFieldCheckBox); @@ -31,63 +33,85 @@ ChildEditorWidget::BasicPropertiesWidget::BasicPropertiesWidget(QWidget* parent) setupConnections(); } -void ChildEditorWidget::BasicPropertiesWidget::setupConnections() { +void ChildGeneralInspector::setupConnections() { + // Is Enabled + connect(&mEnabledCheckBox, &QCheckBox::clicked, this, [this](bool checked) { + setEmitterProperty( + "Toggle Child", + "ToggleChild", + &Ptcl::Emitter::isChildEnabled, + &Ptcl::Emitter::setChildEnabled, + checked + ); + }); + // Is Follow connect(&mFollowCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - emit isFollowUpdated(checked); + setEmitterProperty( + "Toggle Child Follow Parent", + "ToggleChildFollowParent", + &Ptcl::Emitter::isChildFollow, + &Ptcl::Emitter::setChildFollow, + checked + ); }); // Is Parent Field connect(&mParentFieldCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - emit isParentFieldUpdated(checked); + setEmitterProperty( + "Toggle Child Apply Parent Field", + "ToggleChildParentField", + &Ptcl::Emitter::isChildParentField, + &Ptcl::Emitter::setChildParentField, + checked + ); }); // Billboard Type connect(&mBillboardComboBox, &QComboBox::currentIndexChanged, this, [this]() { const auto type = mBillboardComboBox.currentData().value(); - - // TODO: - // mProps.isVelLook = (type == Ptcl::BillboardType::VelLook || type == Ptcl::BillboardType::VelLookPolygon); - // mProps.isEmitterBillboardMtx = mProps.isVelLook; - - mProps.billboardType = type; - - const bool isPolygon = (type == Ptcl::BillboardType::PolygonXY || type == Ptcl::BillboardType::PolygonXZ); - emit isPolygonUpdated(isPolygon); - - emit propertiesUpdated(mProps); + setEmitterProperty( + "Set Child Billboard Type", + "SetChildBillboardType", + &Ptcl::Emitter::childBillboardType, + &Ptcl::Emitter::setChildBillboardType, + type + ); }); // Draw Order connect(&mDrawOrderComboBox, &QComboBox::currentIndexChanged, this, [this]() { const auto order = mDrawOrderComboBox.currentData().value(); const bool isPreDraw = (order == DrawOrder::BelowParent); - emit isPreDrawUpdated(isPreDraw); + setEmitterProperty( + "Set Child Draw Order", + "SetChildDrawOrder", + &Ptcl::Emitter::isChildPreDraw, + &Ptcl::Emitter::setChildPreDraw, + isPreDraw + ); }); } -void ChildEditorWidget::BasicPropertiesWidget::setProperties(const Ptcl::ChildData::BasicProperties& properties, bool isFollow, bool isParentField, bool isPreDraw) { +void ChildGeneralInspector::populateProperties() { QSignalBlocker b1(mBillboardComboBox); QSignalBlocker b2(mFollowCheckBox); QSignalBlocker b3(mParentFieldCheckBox); QSignalBlocker b4(mDrawOrderComboBox); - mProps = properties; - - const s32 billboardIndex = mBillboardComboBox.findData(QVariant::fromValue(mProps.billboardType)); + const s32 billboardIndex = mBillboardComboBox.findData(QVariant::fromValue(mEmitter->childBillboardType())); mBillboardComboBox.setCurrentIndex(billboardIndex); - mFollowCheckBox.setChecked(isFollow); - mParentFieldCheckBox.setChecked(isParentField); + mEnabledCheckBox.setChecked(mEmitter->isChildEnabled()); + + mFollowCheckBox.setChecked(mEmitter->isChildFollow()); + mParentFieldCheckBox.setChecked(mEmitter->isChildParentField()); - const auto drawOrder = isPreDraw ? DrawOrder::BelowParent : DrawOrder::AboveParent; + const auto drawOrder = mEmitter->isChildPreDraw() ? DrawOrder::BelowParent : DrawOrder::AboveParent; const s32 drawIndex = mDrawOrderComboBox.findData(QVariant::fromValue(drawOrder)); mDrawOrderComboBox.setCurrentIndex(drawIndex); } -void ChildEditorWidget::BasicPropertiesWidget::populateBillboardType() { -} - // ========================================================================== // diff --git a/src/editor/childEditor/rotationPropertiesWidget.cpp b/src/editor/inspector/child/childRotationInspector.cpp similarity index 57% rename from src/editor/childEditor/rotationPropertiesWidget.cpp rename to src/editor/inspector/child/childRotationInspector.cpp index 6283f61..00fe15a 100644 --- a/src/editor/childEditor/rotationPropertiesWidget.cpp +++ b/src/editor/inspector/child/childRotationInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/childEditor/rotationPropertiesWidget.h" +#include "editor/inspector/child/childRotationInspector.h" #include "math/util.h" @@ -11,8 +11,8 @@ namespace PtclEditor { // ========================================================================== // -ChildEditorWidget::RotationPropertiesWidget::RotationPropertiesWidget(QWidget* parent) : - QWidget{parent} { +ChildRotationInspector::ChildRotationInspector(QWidget* parent) : + InspectorWidgetBase{parent} { auto* mainLayout = new QFormLayout(this); @@ -29,7 +29,7 @@ ChildEditorWidget::RotationPropertiesWidget::RotationPropertiesWidget(QWidget* p setupConnections(); } -void ChildEditorWidget::RotationPropertiesWidget::setupConnections() { +void ChildRotationInspector::setupConnections() { auto deg2idxVec = [](const Math::Vector3f& v) { Math::Vector3i result { Math::Util::deg2idx(v.getX()), @@ -41,48 +41,89 @@ void ChildEditorWidget::RotationPropertiesWidget::setupConnections() { // Inherit Rotation connect(&mInheritRotCheckBox, &QCheckBox::clicked, this, [this](bool checked) { - emit inheritRotationUpdated(checked); + setEmitterProperty( + "Toggle Child Inherit Rotation", + "ToggleChildInheritRot", + &Ptcl::Emitter::isChildInheritRotation, + &Ptcl::Emitter::setChildInheritRotation, + checked + ); }); // RotType connect(&mRotTypeSpinBox, &QComboBox::currentIndexChanged, this, [this]() { - mProps.rotType = mRotTypeSpinBox.currentEnum(); - updateAxis(); - emit propertiesUpdated(mProps); + const auto type = mRotTypeSpinBox.currentEnum(); + setEmitterProperty( + "Set Child Rotation Type", + "SetChildRotationType", + &Ptcl::Emitter::childRotationType, + &Ptcl::Emitter::setChildRotationType, + type + ); }); // Initial Rotation connect(&mInitRotSpinBox, &VectorSpinBoxBase::valueChanged, this, [this, deg2idxVec]() { - mProps.initRot = deg2idxVec(mInitRotSpinBox.getVector()); - emit propertiesUpdated(mProps); + const auto rot = deg2idxVec(mInitRotSpinBox.getVector()); + setEmitterProperty( + "Set Child Initial Rotation", + "SetChildInitRotation", + &Ptcl::Emitter::childInitialRotation, + &Ptcl::Emitter::setChildInitialRotation, + rot + ); }); // Initial Rotation Rand connect(&mInitRotRandSpinBox, &VectorSpinBoxBase::valueChanged, this, [this, deg2idxVec]() { - mProps.initRotRand = deg2idxVec(mInitRotRandSpinBox.getVector()); - emit propertiesUpdated(mProps); + const auto rot = deg2idxVec(mInitRotRandSpinBox.getVector()); + setEmitterProperty( + "Set Child Initial Rotation Random", + "SetChildInitRotationRand", + &Ptcl::Emitter::childInitialRotationRandom, + &Ptcl::Emitter::setChildInitialRotationRandom, + rot + ); }); // Rotation Speed connect(&mRotVelSpinBox, &VectorSpinBoxBase::valueChanged, this, [this, deg2idxVec]() { - mProps.rotVel = deg2idxVec(mRotVelSpinBox.getVector()); - emit propertiesUpdated(mProps); + const auto speed = deg2idxVec(mRotVelSpinBox.getVector()); + setEmitterProperty( + "Set Child Rotation Speed", + "SetChildRotVel", + &Ptcl::Emitter::childRotationVelocity, + &Ptcl::Emitter::setChildRotationVelocity, + speed + ); }); // Rotation Speed Rand connect(&mRotVelRandSpinBox, &VectorSpinBoxBase::valueChanged, this, [this, deg2idxVec]() { - mProps.rotVelRand = deg2idxVec(mRotVelRandSpinBox.getVector()); - emit propertiesUpdated(mProps); + const auto vel = deg2idxVec(mRotVelRandSpinBox.getVector()); + setEmitterProperty( + "Set Child Rotation Speed Rand", + "SetChildRotVelRand", + &Ptcl::Emitter::childRotationVelocityRandom, + &Ptcl::Emitter::setChildRotationVelocityRandom, + vel + ); }); // Rotation Pivot connect(&mRotBasisSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mProps.rotBasis = mRotBasisSpinBox.getVector(); - emit propertiesUpdated(mProps); + const auto pivot = mRotBasisSpinBox.getVector(); + setEmitterProperty( + "Set Child Rotation Pivot", + "SetChildRotBasis", + &Ptcl::Emitter::childRotationBasis, + &Ptcl::Emitter::setChildRotationBasis, + pivot + ); }); } -void ChildEditorWidget::RotationPropertiesWidget::setProperties(const Ptcl::ChildData::RotationProperties& properties, bool inheritRotation) { +void ChildRotationInspector::populateProperties() { QSignalBlocker b1(mRotTypeSpinBox); QSignalBlocker b2(mInitRotSpinBox); QSignalBlocker b3(mInitRotRandSpinBox); @@ -91,8 +132,6 @@ void ChildEditorWidget::RotationPropertiesWidget::setProperties(const Ptcl::Chil QSignalBlocker b6(mRotBasisSpinBox); QSignalBlocker b7(mInheritRotCheckBox); - mProps = properties; - auto idx2degVec = [](const Math::Vector3i& v) { return Math::Vector3f { Math::Util::to180(Math::Util::idx2deg(v.getX())), @@ -101,21 +140,21 @@ void ChildEditorWidget::RotationPropertiesWidget::setProperties(const Ptcl::Chil }; }; - mRotTypeSpinBox.setCurrentEnum(mProps.rotType); - mInitRotSpinBox.setVector(idx2degVec(mProps.initRot)); - mInitRotRandSpinBox.setVector(idx2degVec(mProps.initRotRand)); - mRotVelSpinBox.setVector(idx2degVec(mProps.rotVel)); - mRotVelRandSpinBox.setVector(idx2degVec(mProps.rotVelRand)); - mRotBasisSpinBox.setVector(mProps.rotBasis); - mInheritRotCheckBox.setChecked(inheritRotation); + mRotTypeSpinBox.setCurrentEnum(mEmitter->childRotationType()); + mInitRotSpinBox.setVector(idx2degVec(mEmitter->childInitialRotation())); + mInitRotRandSpinBox.setVector(idx2degVec(mEmitter->childInitialRotationRandom())); + mRotVelSpinBox.setVector(idx2degVec(mEmitter->childRotationVelocity())); + mRotVelRandSpinBox.setVector(idx2degVec(mEmitter->childRotationVelocityRandom())); + mRotBasisSpinBox.setVector(mEmitter->childRotationBasis()); + mInheritRotCheckBox.setChecked(mEmitter->isChildInheritRotation()); updateAxis(); } -void ChildEditorWidget::RotationPropertiesWidget::updateAxis() { +void ChildRotationInspector::updateAxis() { using Axis = VectorSpinBoxBase::Axis; - switch (mProps.rotType) { + switch (mEmitter->childRotationType()) { case Ptcl::RotType::None: mInitRotSpinBox.setEnabledAxis(Axis::None); mInitRotRandSpinBox.setEnabledAxis(Axis::None); diff --git a/src/editor/inspector/child/childScaleInspector.cpp b/src/editor/inspector/child/childScaleInspector.cpp new file mode 100644 index 0000000..b685871 --- /dev/null +++ b/src/editor/inspector/child/childScaleInspector.cpp @@ -0,0 +1,110 @@ +#include "editor/inspector/child/childScaleInspector.h" + +#include + + +namespace PtclEditor { + + +// ========================================================================== // + + +ChildScaleInspector::ChildScaleInspector(QWidget* parent) : + InspectorWidgetBase{parent} { + + auto* mainLayout = new QFormLayout(this); + + mInheritScaleCheckBox.setText("Inherit Parent Scale"); + mInheritRateSpinBox.setRange(0.0f, 1.0f); + mInheritRateSpinBox.setSingleStep(0.1f); + mScaleSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); + mScaleTargetSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); + mStartFrameSpinBox.setRange(0, std::numeric_limits::max()); + + mainLayout->addRow("Scale:", &mInheritScaleCheckBox); + mainLayout->addRow("Inherit Rate:", &mInheritRateSpinBox); + mainLayout->addRow("Initial Scale:", &mScaleSpinBox); + mainLayout->addRow("Target Scale:", &mScaleTargetSpinBox); + mainLayout->addRow("Start Frame:", &mStartFrameSpinBox); + + setupConnections(); +} + +void ChildScaleInspector::setupConnections() { + // Inherit Scale + connect(&mInheritScaleCheckBox, &QCheckBox::clicked, this, [this](bool checked) { + setEmitterProperty( + "Toggle Child Inherit Scale", + "ToggleChildInheritScale", + &Ptcl::Emitter::isChildInheritScale, + &Ptcl::Emitter::setChildInheritScale, + checked + ); + }); + + // Scale + connect(&mScaleSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { + const auto scale = mScaleSpinBox.getVector(); + setEmitterProperty( + "Set Child Scale", + "SetChildScale", + &Ptcl::Emitter::childScale, + &Ptcl::Emitter::setChildScale, + scale + ); + }); + + // Scale Target + connect(&mScaleTargetSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { + const auto target = mScaleTargetSpinBox.getVector(); + setEmitterProperty( + "Set Child Target Scale", + "SetChildScaleTarget", + &Ptcl::Emitter::childScaleTarget, + &Ptcl::Emitter::setChildScaleTarget, + target + ); + }); + + // Inherit Rate + connect(&mInheritRateSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { + setEmitterProperty( + "Set Child Scale Inherit Rate", + "SetChildScaleInheritRate", + &Ptcl::Emitter::childScaleInheritRate, + &Ptcl::Emitter::setChildScaleInheritRate, + static_cast(value) + ); + }); + + // Start Frame + connect(&mStartFrameSpinBox, &QSpinBox::valueChanged, this, [this](s32 value) { + setEmitterProperty( + "Set Child Scale Start Frame", + "SetChildScaleStartFrame", + &Ptcl::Emitter::childScaleStartFrame, + &Ptcl::Emitter::setChildScaleStartFrame, + value + ); + }); +} + +void ChildScaleInspector::populateProperties() { + QSignalBlocker b1(mScaleSpinBox); + QSignalBlocker b2(mScaleTargetSpinBox); + QSignalBlocker b3(mInheritRateSpinBox); + QSignalBlocker b4(mStartFrameSpinBox); + QSignalBlocker b5(mInheritScaleCheckBox); + + mScaleSpinBox.setVector(mEmitter->childScale()); + mScaleTargetSpinBox.setVector(mEmitter->childScaleTarget()); + mInheritRateSpinBox.setValue(mEmitter->childScaleInheritRate()); + mStartFrameSpinBox.setValue(mEmitter->childScaleStartFrame()); + mInheritScaleCheckBox.setChecked(mEmitter->isChildInheritScale()); +} + + +// ========================================================================== // + + +} // namespace PtclEditor diff --git a/src/editor/childEditor/texturePropertiesWidget.cpp b/src/editor/inspector/child/childTextureInspector.cpp similarity index 50% rename from src/editor/childEditor/texturePropertiesWidget.cpp rename to src/editor/inspector/child/childTextureInspector.cpp index a03d0ca..546c57a 100644 --- a/src/editor/childEditor/texturePropertiesWidget.cpp +++ b/src/editor/inspector/child/childTextureInspector.cpp @@ -1,4 +1,4 @@ -#include "editor/childEditor/texturePropertiesWidget.h" +#include "editor/inspector/child/childTextureInspector.h" #include "editor/textureSelectDialog.h" @@ -17,8 +17,8 @@ namespace PtclEditor { // ========================================================================== // -ChildEditorWidget::TexturePropertiesWidget::TexturePropertiesWidget(QWidget* parent) : - QWidget{parent} { +ChildTextureInspector::ChildTextureInspector(QWidget* parent) : + InspectorWidgetBase{parent} { mUVScaleSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); @@ -53,53 +53,89 @@ ChildEditorWidget::TexturePropertiesWidget::TexturePropertiesWidget(QWidget* par setupConnections(); } -void ChildEditorWidget::TexturePropertiesWidget::setupConnections() { +void ChildTextureInspector::setupConnections() { // Wrap T connect(&mWrapTComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { Q_UNUSED(index); - mProps.textureWrapT = mWrapTComboBox.currentEnum(); - emit propertiesUpdated(mProps); + const auto wrap = mWrapTComboBox.currentEnum(); + setEmitterProperty( + "Set Child Texture Wrap U", + "SetChildTexWrapU", + &Ptcl::Emitter::childTextureWrapT, + &Ptcl::Emitter::setChildTextureWrapT, + wrap + ); }); // Wrap S connect(&mWrapSComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { Q_UNUSED(index); - mProps.textureWrapS = mWrapSComboBox.currentEnum(); - emit propertiesUpdated(mProps); + const auto wrap = mWrapSComboBox.currentEnum(); + setEmitterProperty( + "Set Child Texture Wrap V", + "SetChildTexWrapV", + &Ptcl::Emitter::childTextureWrapS, + &Ptcl::Emitter::setChildTextureWrapS, + wrap + ); }); // Mag Filter connect(&mMagFilterComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { Q_UNUSED(index); - mProps.textureMagFilter = mMagFilterComboBox.currentEnum(); - emit propertiesUpdated(mProps); + const auto filter = mMagFilterComboBox.currentEnum(); + setEmitterProperty( + "Set Child Texture Mag Filter", + "SetChildTexMagFilter", + &Ptcl::Emitter::childTextureMagFilter, + &Ptcl::Emitter::setChildTextureMagFilter, + filter + ); }); // Min Filter connect(&mMinFilterComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { Q_UNUSED(index); - mProps.textureMinFilter = mMinFilterComboBox.currentEnum(); - emit propertiesUpdated(mProps); + const auto filter = mMinFilterComboBox.currentEnum(); + setEmitterProperty( + "Set Child Texture Min Filter", + "SetChildTexMinFilter", + &Ptcl::Emitter::childTextureMinFilter, + &Ptcl::Emitter::setChildTextureMinFilter, + filter + ); }); // Mipmap Filter connect(&mMipFilterComboBox, &QComboBox::currentIndexChanged, this, [this](s32 index) { Q_UNUSED(index); - mProps.textureMipFilter = mMipFilterComboBox.currentEnum(); - emit propertiesUpdated(mProps); + const auto filter = mMipFilterComboBox.currentEnum(); + setEmitterProperty( + "Set Child Texture Mip Filter", + "SetChildTexMipFilter", + &Ptcl::Emitter::childTextureMipFilter, + &Ptcl::Emitter::setChildTextureMipFilter, + filter + ); }); // UV Scale connect(&mUVScaleSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { - mProps.texUVScale = mUVScaleSpinBox.getVector(); - emit propertiesUpdated(mProps); + const auto scale = mUVScaleSpinBox.getVector(); + setEmitterProperty( + "Set Child Texture UV Scale", + "SetChildTexUVScale", + &Ptcl::Emitter::childTextureUVScale, + &Ptcl::Emitter::setChildTextureUVScale, + scale + ); }); // Texture Preview - connect(&mTexturePreview, &ThumbnailWidget::clicked, this, &TexturePropertiesWidget::changeTexture); + connect(&mTexturePreview, &ThumbnailWidget::clicked, this, &ChildTextureInspector::changeTexture); } -void ChildEditorWidget::TexturePropertiesWidget::setProperties(const Ptcl::ChildData::TextureProperties& properties, const std::shared_ptr& texture) { +void ChildTextureInspector::populateProperties() { QSignalBlocker b1(mWrapTComboBox); QSignalBlocker b2(mWrapSComboBox); QSignalBlocker b3(mMagFilterComboBox); @@ -107,39 +143,33 @@ void ChildEditorWidget::TexturePropertiesWidget::setProperties(const Ptcl::Child QSignalBlocker b5(mMipFilterComboBox); QSignalBlocker b6(mUVScaleSpinBox); - mProps = properties; - mTexture = texture; - - if (mTexture) { - mTexturePreview.setPixmap(QPixmap::fromImage(mTexture->textureData())); + if (mEmitter->childTextureHandle().isValid()) { + mTexturePreview.setPixmap(QPixmap::fromImage(mEmitter->childTexture()->textureData())); } - mWrapTComboBox.setCurrentEnum(mProps.textureWrapT); - mWrapSComboBox.setCurrentEnum(mProps.textureWrapS); - mMagFilterComboBox.setCurrentEnum(mProps.textureMagFilter); - mMinFilterComboBox.setCurrentEnum(mProps.textureMinFilter); - mMipFilterComboBox.setCurrentEnum(mProps.textureMipFilter); - mUVScaleSpinBox.setVector(mProps.texUVScale); -} - -void ChildEditorWidget::TexturePropertiesWidget::setTextureList(const Ptcl::TextureList* textureList) { - mTextureList = textureList; + mWrapTComboBox.setCurrentEnum(mEmitter->childTextureWrapT()); + mWrapSComboBox.setCurrentEnum(mEmitter->childTextureWrapS()); + mMagFilterComboBox.setCurrentEnum(mEmitter->childTextureMagFilter()); + mMinFilterComboBox.setCurrentEnum(mEmitter->childTextureMinFilter()); + mMipFilterComboBox.setCurrentEnum(mEmitter->childTextureMipFilter()); + mUVScaleSpinBox.setVector(mEmitter->childTextureUVScale()); } -void ChildEditorWidget::TexturePropertiesWidget::changeTexture() { - if (!mTextureList) { - return; - } - - auto oldTexture = mTexture; +void ChildTextureInspector::changeTexture() { + const auto textureList = mDocument->textures(); - TextureSelectDialog dialog(*mTextureList, this); + TextureSelectDialog dialog(textureList, this); if (dialog.exec() == QDialog::Accepted) { s32 selectedInded = dialog.selectedIndex(); - if (selectedInded >= 0 && static_cast(selectedInded) < mTextureList->size()) { - mTexture = mTextureList->at(selectedInded); - mTexturePreview.setPixmap(QPixmap::fromImage(mTexture->textureData())); - emit textureUpdated(oldTexture, mTexture); + if (selectedInded >= 0 && static_cast(selectedInded) < textureList.size()) { + const auto& texture = textureList.at(selectedInded); + setEmitterProperty( + "Set Child Texture", + "SetChildTexture", + &Ptcl::Emitter::childTexture, + &Ptcl::Emitter::setChildTexture, + texture + ); } } } diff --git a/src/editor/inspector/child/childVelocityInspector.cpp b/src/editor/inspector/child/childVelocityInspector.cpp new file mode 100644 index 0000000..3f0c9d9 --- /dev/null +++ b/src/editor/inspector/child/childVelocityInspector.cpp @@ -0,0 +1,139 @@ +#include "editor/inspector/child/childVelocityInspector.h" + +#include + +namespace PtclEditor { + + +// ========================================================================== // + + +ChildVelocityInspector::ChildVelocityInspector(QWidget* parent) : + InspectorWidgetBase{parent} { + + // TODO: Determine better ranges? + mRandVelSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); + mGravitySpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); + mVelInheritSpinBox.setRange(0.0f, 1.0f); + mInitPosRandSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); + mFigureVelSpinBox.setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); + mAirResistSpinBox.setRange(0.0f, 1.0f); + + auto* mainLayout = new QFormLayout(this); + + mainLayout->addRow("Random Velocity:", &mRandVelSpinBox); + mainLayout->addRow("Gravity:", &mGravitySpinBox); + mainLayout->addRow("Inherit Parent Velocity:", &mInheritVelCheckBox); + mainLayout->addRow("Velocity Inherit Rate:", &mVelInheritSpinBox); + mainLayout->addRow("Inital Position Random:", &mInitPosRandSpinBox); + mainLayout->addRow("Figure Velocity:", &mFigureVelSpinBox); + mainLayout->addRow("Air Resistance:", &mAirResistSpinBox); + + setupConnections(); +} + +void ChildVelocityInspector::setupConnections() { + // Rand Velocity + connect(&mRandVelSpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { + const auto vel = mRandVelSpinBox.getVector(); + setEmitterProperty( + "Set Child Random Velocity", + "SetChildRandVel", + &Ptcl::Emitter::childRandVelocity, + &Ptcl::Emitter::setChildRandVelocity, + vel + ); + }); + + // Gravity + connect(&mGravitySpinBox, &VectorSpinBoxBase::valueChanged, this, [this]() { + const auto gravity = mGravitySpinBox.getVector(); + setEmitterProperty( + "Set Child Gravity", + "SetChildGravity", + &Ptcl::Emitter::childGravity, + &Ptcl::Emitter::setChildGravity, + gravity + ); + }); + + // Inherit Velocity + connect(&mInheritVelCheckBox, &QCheckBox::clicked, this, [this](bool checked) { + setEmitterProperty( + "Toggle Child Inherit Velocity", + "ToggleChildInheritVel", + &Ptcl::Emitter::isChildInheritVelocity, + &Ptcl::Emitter::setChildInheritVelocity, + checked + ); + }); + + // Inherit Rate + connect(&mVelInheritSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { + setEmitterProperty( + "Set Child Velocity Inherit Rate", + "SetChildVelInheritRate", + &Ptcl::Emitter::childVelocityInheritRate, + &Ptcl::Emitter::setChildVelocityInheritRate, + static_cast(value) + ); + }); + + // Init Pos Rand + connect(&mInitPosRandSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { + setEmitterProperty( + "Set Child Initial Position Random", + "SetChildInitPosRand", + &Ptcl::Emitter::childInitalPositionRand, + &Ptcl::Emitter::setChildInitialPositionRand, + static_cast(value) + ); + }); + + // Figure Velocity + connect(&mFigureVelSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { + setEmitterProperty( + "Set Child Figure Velocity", + "SetChildFigureVel", + &Ptcl::Emitter::childFigureVelocity, + &Ptcl::Emitter::setChildFigureVelocity, + static_cast(value) + ); + }); + + // Air Resistance + connect(&mAirResistSpinBox, &QDoubleSpinBox::valueChanged, this, [this](double value) { + setEmitterProperty( + "Set Child Air Resistance", + "SetChildAirResit", + &Ptcl::Emitter::childAirResistance, + &Ptcl::Emitter::setChildAirResistance, + static_cast(value) + ); + }); +} + +void ChildVelocityInspector::populateProperties() { + QSignalBlocker b1(mRandVelSpinBox); + QSignalBlocker b2(mGravitySpinBox); + QSignalBlocker b3(mVelInheritSpinBox); + QSignalBlocker b4(mInitPosRandSpinBox); + QSignalBlocker b5(mFigureVelSpinBox); + QSignalBlocker b6(mAirResistSpinBox); + QSignalBlocker b7(mInheritVelCheckBox); + + mRandVelSpinBox.setVector(mEmitter->childRandVelocity()); + mGravitySpinBox.setVector(mEmitter->childGravity()); + mVelInheritSpinBox.setValue(mEmitter->childVelocityInheritRate()); + mVelInheritSpinBox.setEnabled(mEmitter->isChildInheritVelocity()); + mInitPosRandSpinBox.setValue(mEmitter->childInitalPositionRand()); + mFigureVelSpinBox.setValue(mEmitter->childFigureVelocity()); + mAirResistSpinBox.setValue(mEmitter->childAirResistance()); + mInheritVelCheckBox.setChecked(mEmitter->isChildInheritVelocity()); +} + + +// ========================================================================== // + + +} // namespace PtclEditor diff --git a/src/editor/inspector/inspectorPanel.cpp b/src/editor/inspector/inspectorPanel.cpp index a9e2c25..248182e 100644 --- a/src/editor/inspector/inspectorPanel.cpp +++ b/src/editor/inspector/inspectorPanel.cpp @@ -6,7 +6,6 @@ #include "editor/inspector/colorInspector.h" #include "editor/inspector/combinerInspector.h" #include "editor/inspector/emissionInspector.h" -#include "editor/inspector/fluctuationInspector.h" #include "editor/inspector/gravityInspector.h" #include "editor/inspector/lifespanInspector.h" #include "editor/inspector/rotationInspector.h" @@ -18,6 +17,18 @@ #include "editor/inspector/velocityInspector.h" #include "editor/inspector/volumeInspector.h" +#include "editor/inspector/fluctuationInspector.h" + +#include "editor/inspector/child/childAlphaInspector.h" +#include "editor/inspector/child/childColorInspector.h" +#include "editor/inspector/child/childCombinerInspector.h" +#include "editor/inspector/child/childEmissionInspector.h" +#include "editor/inspector/child/childGeneralInspector.h" +#include "editor/inspector/child/childRotationInspector.h" +#include "editor/inspector/child/childScaleInspector.h" +#include "editor/inspector/child/childTextureInspector.h" +#include "editor/inspector/child/childVelocityInspector.h" + #include "editor/inspector/field/fieldCollisionInspector.h" #include "editor/inspector/field/fieldConvergenceInspector.h" #include "editor/inspector/field/fieldMagnetInspector.h" @@ -27,6 +38,7 @@ #include + namespace PtclEditor { @@ -59,7 +71,16 @@ InspectorPanel::InspectorPanel(QWidget* parent) : mFieldRandomInspector = new FieldRandomInspector(this); mFieldSpinInspector = new FieldSpinInspector(this); - mChildEditorWidget = new ChildEditorWidget(this); + mChildAlphaInspector = new ChildAlphaInspector(this); + mChildColorInspector = new ChildColorInspector(this); + mChildCombinerInspector = new ChildCombinerInspector(this); + mChildEmissionInspector = new ChildEmissionInspector(this); + mChildGeneralInspector = new ChildGeneralInspector(this); + mChildRotationInspector = new ChildRotationInspector(this); + mChildScaleInspector = new ChildScaleInspector(this); + mChildTextureInspector = new ChildTextureInspector(this); + mChildVelocityInspector = new ChildVelocityInspector(this); + mFluctuationInspector = new FluctuationInspector(this); mTabStack = new QStackedWidget(this); @@ -87,7 +108,6 @@ InspectorPanel::InspectorPanel(QWidget* parent) : setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); buildTabs(); - setupConnections(); } void InspectorPanel::buildTabs() { @@ -131,8 +151,15 @@ void InspectorPanel::buildTabs() { mFieldTabs->addTab(mFieldSpinInspector, "Spin"); // Child - mChildTabs->addTab(mChildEditorWidget, "Child"); - // TODO + mChildTabs->addTab(mChildGeneralInspector, "General"); + mChildTabs->addTab(mChildColorInspector, "Color"); + mChildTabs->addTab(mChildAlphaInspector, "Alpha"); + mChildTabs->addTab(mChildCombinerInspector, "Combiner"); + mChildTabs->addTab(mChildEmissionInspector, "Emission"); + mChildTabs->addTab(mChildRotationInspector, "Rotation"); + mChildTabs->addTab(mChildScaleInspector, "Scale"); + mChildTabs->addTab(mChildTextureInspector, "Texture"); + mChildTabs->addTab(mChildVelocityInspector, "Velocity"); } void InspectorPanel::updateTabVisibility() { @@ -160,16 +187,6 @@ void InspectorPanel::updateTabVisibility() { } } -void InspectorPanel::setupConnections() { - // Child Editor Widget - connect(mChildEditorWidget, &ChildEditorWidget::flagsUpdated, this, [this](const BitFlag& childFlags) { - if (!mEmitter) { return; } - mEmitter->setChildFlags(childFlags); - emit complexFlagsChanged(); - emit propertiesChanged(); - }); -} - void InspectorPanel::setDocument(Ptcl::Document* document) { if (mDocument) { mDocument->disconnect(this); @@ -191,9 +208,10 @@ void InspectorPanel::setDocument(Ptcl::Document* document) { mCombinerInspector->setDocument(document); mColorInspector->setDocument(document); mTextureInspector->setDocument(document); - mFluctuationInspector->setDocument(document); mStripeInspector->setDocument(document); + mFluctuationInspector->setDocument(document); + mFieldCollisionInspector->setDocument(document); mFieldConvergenceInspector->setDocument(document); mFieldMagnetInspector->setDocument(document); @@ -201,13 +219,19 @@ void InspectorPanel::setDocument(Ptcl::Document* document) { mFieldRandomInspector->setDocument(document); mFieldSpinInspector->setDocument(document); + mChildAlphaInspector->setDocument(document); + mChildColorInspector->setDocument(document); + mChildCombinerInspector->setDocument(document); + mChildEmissionInspector->setDocument(document); + mChildGeneralInspector->setDocument(document); + mChildRotationInspector->setDocument(document); + mChildScaleInspector->setDocument(document); + mChildTextureInspector->setDocument(document); + mChildVelocityInspector->setDocument(document); + if (mDocument) { connect(mDocument, &Ptcl::Document::emitterChanged, this, [this](s32 setIndex, s32 emitterIndex) { - if (!mEmitter) { - return; - } - - if (setIndex != mSelection->emitterSetIndex() || emitterIndex != mSelection->emitterIndex()) { + if (!mEmitter || setIndex != mSelection->emitterSetIndex() || emitterIndex != mSelection->emitterIndex()) { return; } @@ -247,6 +271,16 @@ void InspectorPanel::setSelection(Ptcl::Selection* selection) { mFieldRandomInspector->setSelection(selection); mFieldSpinInspector->setSelection(selection); + mChildAlphaInspector->setSelection(selection); + mChildColorInspector->setSelection(selection); + mChildCombinerInspector->setSelection(selection); + mChildEmissionInspector->setSelection(selection); + mChildGeneralInspector->setSelection(selection); + mChildRotationInspector->setSelection(selection); + mChildScaleInspector->setSelection(selection); + mChildTextureInspector->setSelection(selection); + mChildVelocityInspector->setSelection(selection); + if (mSelection) { connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { @@ -256,10 +290,6 @@ void InspectorPanel::setSelection(Ptcl::Selection* selection) { } mEmitter = mDocument->emitter(setIndex, emitterIndex); - - // TODO: Have child widgets handle this themselves - mChildEditorWidget->setTextureList(nullptr); - setEnabled(true); populateProperties(); }); @@ -267,11 +297,6 @@ void InspectorPanel::setSelection(Ptcl::Selection* selection) { } void InspectorPanel::populateProperties() { - QSignalBlocker b1(mChildEditorWidget); - - mChildEditorWidget->setChildData(&mEmitter->childData(), mEmitter->complexProperties().childFlags); - mChildEditorWidget->setParentColor0(mEmitter->primaryColor()); - if (mLastSelectionType != mSelection->type()) { mLastSelectionType = mSelection->type(); updateTabVisibility(); diff --git a/src/editor/inspector/lifespanInspector.cpp b/src/editor/inspector/lifespanInspector.cpp index a0d1655..b25e06e 100644 --- a/src/editor/inspector/lifespanInspector.cpp +++ b/src/editor/inspector/lifespanInspector.cpp @@ -33,7 +33,7 @@ LifespanInspector::LifespanInspector(QWidget* parent) : void LifespanInspector::setupConnections() { connect(&mLifeSpanSpinBox, &QSpinBox::valueChanged, this, [this](u64 value) { setEmitterProperty( - "Set LifeSpan", + "Set Lifespan", "SetLifeSpan", &Ptcl::Emitter::ptclLife, &Ptcl::Emitter::setPtclLife, diff --git a/src/editor/mainWindow.cpp b/src/editor/mainWindow.cpp index 8f9f1b1..fc18ed3 100644 --- a/src/editor/mainWindow.cpp +++ b/src/editor/mainWindow.cpp @@ -159,14 +159,6 @@ void MainWindow::setupConnections() { setDirty(true); }); - connect(&mInspector, &InspectorPanel::complexFlagsChanged, this, [this]() { - mPtclList.updateEmitter(mSelection.emitterSetIndex(), mSelection.emitterIndex()); - }); - - connect(&mInspector, &InspectorPanel::propertiesChanged, this, [this]() { - setDirty(true); - }); - // EmitterSet Widget connect(&mEmitterSetWidget, &EmitterSetWidget::emitterSetNamedChanged, this, [this]() { mPtclList.updateEmitterSetName(mSelection.emitterSetIndex()); diff --git a/src/editor/ptclListWidget.cpp b/src/editor/ptclListWidget.cpp index 119f146..365caa1 100644 --- a/src/editor/ptclListWidget.cpp +++ b/src/editor/ptclListWidget.cpp @@ -383,7 +383,6 @@ void PtclList::selectionChanged(const QItemSelection& selection) { void PtclList::addComplexNodes(QStandardItem* emitterItem, s32 setIndex, s32 emitterIndex) { const auto& emitter = mDocument->emitter(setIndex, emitterIndex); - const auto& props = emitter->complexProperties(); // ChildData ensureComplexNode( @@ -392,7 +391,7 @@ void PtclList::addComplexNodes(QStandardItem* emitterItem, s32 setIndex, s32 emi "ChildData", setIndex, emitterIndex, - props.childFlags.isSet(Ptcl::ChildFlag::Enabled) + emitter->isChildEnabled() ); // Fluctuation diff --git a/src/ptcl/ptcl.cpp b/src/ptcl/ptcl.cpp index 6296793..9d514b3 100644 --- a/src/ptcl/ptcl.cpp +++ b/src/ptcl/ptcl.cpp @@ -142,9 +142,9 @@ void PtclBinaryReader::readComplexData(Emitter& emitter, const BinCommonEmitterD if (complex.childFlag.isSet(ChildFlag::Enabled)) { BinChildData childData{}; mStream >> childData; - emitter.childData().initFromBinary(childData); + emitter.initChild(childData); - emitter.childData().setTexture( + emitter.setChildTexture( loadTexture( childData.childTexturePos, childData.childTextureSize, @@ -332,12 +332,12 @@ void PtclBinaryWriter::writeComplexEmitter(const Emitter& emitter) { // Child const bool hasChild = emitterData.childFlag.isSet(ChildFlag::Enabled); - BinChildData childData(emitter.childData()); + BinChildData childData(emitter); emitterData.childDataOffset = emitterDataSize; if (hasChild) { emitterDataSize += sizeof(BinChildData); - childData.childTexturePos = appendTexture(emitter.childData().textureHandle(), childData.childTextureSize); + childData.childTexturePos = appendTexture(emitter.childTextureHandle(), childData.childTextureSize); } // Main Texture diff --git a/src/ptcl/ptclBinary.cpp b/src/ptcl/ptclBinary.cpp index 8ee7f15..34f0bfd 100644 --- a/src/ptcl/ptclBinary.cpp +++ b/src/ptcl/ptclBinary.cpp @@ -651,7 +651,7 @@ void BinCommonEmitterData::printData(u32 indentationLevel) { BinComplexEmitterData::BinComplexEmitterData(const Ptcl::Emitter& emitter) : BinCommonEmitterData{emitter} { - childFlag = emitter.complexProperties().childFlags; + childFlag = emitter.childFlags(); fieldFlag = emitter.fieldFlags(); fluctuationFlag = emitter.fluctuationFlags(); stripeFlag = emitter.stripeFlags(); @@ -708,29 +708,29 @@ void BinComplexEmitterData::printData(u32 indentationLevel) { // ========================================================================== // -BinChildData::BinChildData(const Ptcl::ChildData& childData) { - childEmitRate = childData.emissionProperties().emitRate; - childEmitTiming = childData.emissionProperties().emitTiming; - childLife = childData.emissionProperties().life; - childEmitStep = childData.emissionProperties().emitStep; - childVelInheritRate = childData.velocityProperties().velInheritRate; - childFigurVel = childData.velocityProperties().figurVel; - childRandVel = childData.velocityProperties().randVel; - childInitPosRand = childData.velocityProperties().initPosRand; +BinChildData::BinChildData(const Ptcl::Emitter& emitterData) { + childEmitRate = emitterData.childEmitRate(); + childEmitTiming = emitterData.childEmitTiming(); + childLife = emitterData.childLife(); + childEmitStep = emitterData.childEmitStep(); + childVelInheritRate = emitterData.childVelocityInheritRate(); + childFigurVel = emitterData.childFigureVelocity(); + childRandVel = emitterData.childRandVelocity(); + childInitPosRand = emitterData.childInitalPositionRand(); - childBlendType = childData.combinerProperties().blendFunc; - childBillboardType = childData.basicProperties().billboardType; - childDepthType = childData.combinerProperties().depthFunc; + childBlendType = emitterData.childBlendFunc(); + childBillboardType = emitterData.childBillboardType(); + childDepthType = emitterData.childDepthFunc(); - if (childData.textureHandle().isValid()) { + if (emitterData.childTextureHandle().isValid()) { childTextureRes = { - .width = static_cast(childData.textureHandle()->textureData().width()), - .height = static_cast(childData.textureHandle()->textureData().height()), - .format = childData.textureHandle()->textureFormat(), - .wrapT = childData.textureProperties().textureWrapT, - .wrapS = childData.textureProperties().textureWrapS, - .magFilter = childData.textureProperties().textureMagFilter, - .minMipFilter = static_cast((static_cast(childData.textureProperties().textureMinFilter) & 0x1) | ((static_cast(childData.textureProperties().textureMipFilter) & 0x3) << 1)), + .width = static_cast(emitterData.childTextureHandle()->textureData().width()), + .height = static_cast(emitterData.childTextureHandle()->textureData().height()), + .format = emitterData.childTextureHandle()->textureFormat(), + .wrapT = emitterData.childTextureWrapT(), + .wrapS = emitterData.childTextureWrapS(), + .magFilter = emitterData.childTextureMagFilter(), + .minMipFilter = static_cast((static_cast(emitterData.childTextureMinFilter()) & 0x1) | ((static_cast(emitterData.childTextureMipFilter()) & 0x3) << 1)), }; } else { childTextureRes = {}; @@ -740,28 +740,28 @@ BinChildData::BinChildData(const Ptcl::ChildData& childData) { childTexturePos = 0; // To be assigned after construction... childTextureHandlePtr = 0; - childColor0 = childData.colorProperties().color0; - childColor1 = childData.colorProperties().color1; - childAlpha = childData.alphaProperties().alpha; - childAlphaTarget = childData.alphaProperties().alphaTarget; - childAlphaInit = childData.alphaProperties().alphaInit; - childScaleInheritRate = childData.scaleProperties().scaleInheritRate; - childScale = childData.scaleProperties().scale; - childRotType = childData.rotationProperties().rotType; - childInitRot = childData.rotationProperties().initRot; - childInitRotRand = childData.rotationProperties().initRotRand; - childRotVel = childData.rotationProperties().rotVel; - childRotVelRand = childData.rotationProperties().rotVelRand; - childRotBasis = childData.rotationProperties().rotBasis; - childGravity = childData.velocityProperties().gravity; - childAlphaStartFrame = childData.alphaProperties().alphaStartFrame; - childAlphaBaseFrame = childData.alphaProperties().alphaBaseFrame; - childScaleStartFrame = childData.scaleProperties().scaleStartFrame; - childScaleTarget = childData.scaleProperties().scaleTarget; - childTexUScale = childData.textureProperties().texUVScale.getX(); - childTexVScale = childData.textureProperties().texUVScale.getY(); - childCombinerType = childData.combinerProperties().combinerFunc; - childAirResist = childData.velocityProperties().airResist; + childColor0 = emitterData.childPrimaryColor(); + childColor1 = emitterData.childSecondaryColor(); + childAlpha = emitterData.childAlpha(); + childAlphaTarget = emitterData.childAlphaTarget(); + childAlphaInit = emitterData.childAlphaInit(); + childScaleInheritRate = emitterData.childScaleInheritRate(); + childScale = emitterData.childScale(); + childRotType = emitterData.childRotationType(); + childInitRot = emitterData.childInitialRotation(); + childInitRotRand = emitterData.childInitialRotationRandom(); + childRotVel = emitterData.childRotationVelocity(); + childRotVelRand = emitterData.childRotationVelocityRandom(); + childRotBasis = emitterData.childRotationBasis(); + childGravity = emitterData.childGravity(); + childAlphaStartFrame = emitterData.childAlphaStartFrame(); + childAlphaBaseFrame = emitterData.childAlphaBaseFrame(); + childScaleStartFrame = emitterData.childScaleStartFrame(); + childScaleTarget = emitterData.childScaleTarget(); + childTexUScale = emitterData.childTextureUVScale().getX(); + childTexVScale = emitterData.childTextureUVScale().getY(); + childCombinerType = emitterData.childCombinerFunc(); + childAirResist = emitterData.childAirResistance(); } diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index cc276ce..d1f44fd 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -105,8 +105,8 @@ std::unique_ptr Emitter::clone() const { newEmitter->mIsTexturePatternAnim = mIsTexturePatternAnim; newEmitter->mTextureHandle = mTextureHandle.clone(); - newEmitter->mComplexProperties = mComplexProperties; - newEmitter->mChildData = std::move(*mChildData.clone()); + newEmitter->mChildFlags = mChildFlags; + newEmitter->mChild = mChild; // TODO - texture handle clone? // Fluctuation Properties newEmitter->mFluctuationFlags = mFluctuationFlags; @@ -144,26 +144,6 @@ const BitFlag& Emitter::flags() const { return mFlag; } -const Emitter::ComplexProperties& Emitter::complexProperties() const { - return mComplexProperties; -} - -void Emitter::setComplexProperties(const ComplexProperties& complexProperties) { - mComplexProperties = complexProperties; -} - -void Emitter::setChildFlags(const BitFlag& childFlags) { - mComplexProperties.childFlags = childFlags; -} - -const ChildData& Emitter::childData() const { - return mChildData; -} - -ChildData& Emitter::childData() { - return mChildData; -} - void Emitter::initFieldRandom(const BinFieldRandomData& randomData) { mFieldRandom = { .randomBlank = randomData.fieldRandomBlank, @@ -224,6 +204,56 @@ void Emitter::initStripeData(const BinStripeData& stripeData) { mStripeDirInterpolate = stripeData.stripeDirInterpolate; } +void Emitter::initChild(const BinChildData& childData) { + mChild = { + .billboardType = childData.childBillboardType, + + .emitRate = childData.childEmitRate, + .emitTiming = childData.childEmitTiming, + .life = childData.childLife, + .emitStep = childData.childEmitStep, + + .randVel = { childData.childRandVel.x, childData.childRandVel.y, childData.childRandVel.z }, + .gravity = { childData.childGravity.x, childData.childGravity.y, childData.childGravity.z }, + .velInheritRate = childData.childVelInheritRate, + .initPosRand = childData.childInitPosRand, + .figurVel = childData.childFigurVel, + .airResist = childData.childAirResist, + + .rotType = childData.childRotType, + .initRot = { childData.childInitRot.x, childData.childInitRot.y, childData.childInitRot.z }, + .initRotRand = { childData.childInitRotRand.x, childData.childInitRotRand.y, childData.childInitRotRand.z }, + .rotVel = { childData.childRotVel.x, childData.childRotVel.y, childData.childRotVel.z }, + .rotVelRand = { childData.childRotVelRand.x, childData.childRotVelRand.y, childData.childRotVelRand.z }, + .rotBasis = { childData.childRotBasis.x, childData.childRotBasis.y }, + + .scale = { childData.childScale.x, childData.childScale.y }, + .scaleTarget = { childData.childScaleTarget.x, childData.childScaleTarget.y }, + .scaleInheritRate = childData.childScaleInheritRate, + .scaleStartFrame = childData.childScaleStartFrame, + + .textureWrapT = childData.childTextureRes.wrapT, + .textureWrapS = childData.childTextureRes.wrapS, + .textureMagFilter = childData.childTextureRes.magFilter, + .textureMinFilter = static_cast(childData.childTextureRes.minMipFilter & 0x1), + .textureMipFilter = static_cast((childData.childTextureRes.minMipFilter >> 1) & 0x3), + .texUVScale = { childData.childTexUScale, childData.childTexVScale }, + + .color0 = childData.childColor0, + .color1 = childData.childColor1, + + .alpha = childData.childAlpha, + .alphaTarget = childData.childAlphaTarget, + .alphaInit = childData.childAlphaInit, + .alphaStartFrame = childData.childAlphaStartFrame, + .alphaBaseFrame = childData.childAlphaBaseFrame, + + .blendFunc = childData.childBlendType, + .depthFunc = childData.childDepthType, + .combinerFunc = childData.childCombinerType + }; +} + bool Emitter::hasStripeData() const { const auto bType = mBillboardType; const bool isBillboardStripe = bType == BillboardType::Stripe || bType == BillboardType::ComplexStripe; @@ -341,10 +371,7 @@ void Emitter::initFromBinary(const BinCommonEmitterData& emitterData) { } void Emitter::initComplexFromBinary(const BinComplexEmitterData& emitterData) { - mComplexProperties = { - .childFlags = emitterData.childFlag, - }; - + mChildFlags = emitterData.childFlag, mFieldFlags = emitterData.fieldFlag, mFluctuationFlags = emitterData.fluctuationFlag; mStripeFlags = emitterData.stripeFlag; From 61c42c624a2e33848528db9dc6e61a3938e8dea7 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Thu, 19 Mar 2026 22:36:47 +0000 Subject: [PATCH 26/35] feat(ui): implmennt undo/redo for project name and directly integrate with inspector --- CMakeLists.txt | 2 - include/editor/inspector/inspectorPanel.h | 9 +- .../editor/inspector/inspectorWidgetBase.h | 2 +- include/editor/mainWindow.h | 2 - include/ptcl/ptclChildData.h | 142 -------------- include/ptcl/ptclCommand.h | 111 +++++++++-- include/ptcl/ptclDocument.h | 37 +++- src/editor/inspector/inspectorPanel.cpp | 25 ++- src/editor/mainWindow.cpp | 23 +-- src/editor/ptclListWidget.cpp | 12 +- src/ptcl/ptclChildData.cpp | 180 ------------------ src/ptcl/ptclDocument.cpp | 5 + src/ptcl/ptclEmitter.cpp | 4 +- src/ptcl/ptclTexture.cpp | 4 - 14 files changed, 170 insertions(+), 388 deletions(-) delete mode 100644 include/ptcl/ptclChildData.h delete mode 100644 src/ptcl/ptclChildData.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cd03ce..3b37b81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,7 +74,6 @@ set(PROJECT_SOURCES src/etc1/rg_etc1.cpp src/ptcl/ptcl.cpp src/ptcl/ptclBinary.cpp - src/ptcl/ptclChildData.cpp src/ptcl/ptclDocument.cpp src/ptcl/ptclEmitter.cpp src/ptcl/ptclEmitterSet.cpp @@ -146,7 +145,6 @@ set(PROJECT_HEADERS include/math/vector.h include/ptcl/ptcl.h include/ptcl/ptclBinary.h - include/ptcl/ptclChildData.h include/ptcl/ptclCommand.h include/ptcl/ptclDocument.h include/ptcl/ptclEmitter.h diff --git a/include/editor/inspector/inspectorPanel.h b/include/editor/inspector/inspectorPanel.h index 26e6023..0c0e82b 100644 --- a/include/editor/inspector/inspectorPanel.h +++ b/include/editor/inspector/inspectorPanel.h @@ -3,12 +3,7 @@ #include "ptcl/ptclDocument.h" #include "ptcl/ptclEmitter.h" -#include -#include -#include -#include #include -#include #include #include #include @@ -73,7 +68,9 @@ class InspectorPanel final : public QWidget { private: Ptcl::Document* mDocument{nullptr}; const Ptcl::Selection* mSelection{nullptr}; - Ptcl::Emitter* mEmitter{nullptr}; + const Ptcl::Emitter* mEmitter{nullptr}; + + QLineEdit* mProjNameLineEdit{nullptr}; GeneralEmitterInspector* mGeneralInspector{nullptr}; GravityInspector* mGravityInspector{nullptr}; diff --git a/include/editor/inspector/inspectorWidgetBase.h b/include/editor/inspector/inspectorWidgetBase.h index b0e4c91..c9e8277 100644 --- a/include/editor/inspector/inspectorWidgetBase.h +++ b/include/editor/inspector/inspectorWidgetBase.h @@ -41,7 +41,7 @@ protected slots: protected: Ptcl::Document* mDocument{nullptr}; const Ptcl::Selection* mSelection{nullptr}; - Ptcl::Emitter* mEmitter{nullptr}; + const Ptcl::Emitter* mEmitter{nullptr}; }; diff --git a/include/editor/mainWindow.h b/include/editor/mainWindow.h index b9f64f4..a765967 100644 --- a/include/editor/mainWindow.h +++ b/include/editor/mainWindow.h @@ -87,8 +87,6 @@ private slots: QSplitter* mTopSplitter{nullptr}; QSplitter* mBottomSplitter{nullptr}; - QLineEdit mProjNameLineEdit{}; - QStackedWidget* mPropertiesStack{nullptr}; QGroupBox mPropertiesGroup{}; diff --git a/include/ptcl/ptclChildData.h b/include/ptcl/ptclChildData.h deleted file mode 100644 index 674396d..0000000 --- a/include/ptcl/ptclChildData.h +++ /dev/null @@ -1,142 +0,0 @@ -#pragma once - -#include "math/vector.h" - -#include "ptcl/ptclBinary.h" -#include "ptcl/ptclEnum.h" -#include "ptcl/ptclTexture.h" - -#include "typedefs.h" - - -namespace Ptcl { - - -// ========================================================================== // - - -class ChildData { - -public: - struct BasicProperties { - BillboardType billboardType{BillboardType::Billboard}; - }; - - struct EmissionProperties { - s32 emitRate{1}; - s32 emitTiming{0}; - s32 life{10}; - s32 emitStep{1}; - }; - - struct VelocityProperties { - Math::Vector3f randVel{0.0f, 0.0f, 0.0f}; - Math::Vector3f gravity{0.0f, -1.0f, 0.0f}; - f32 velInheritRate{1.0f}; - f32 initPosRand{0.0f}; - f32 figurVel{0.1f}; - f32 airResist{1.0f}; - }; - - struct RotationProperties { - RotType rotType{RotType::None}; - Math::Vector3i initRot{0, 0, 0}; - Math::Vector3i initRotRand{0, 0, 0}; - Math::Vector3i rotVel{0, 0, 0}; - Math::Vector3i rotVelRand{0, 0, 0}; - Math::Vector2f rotBasis{0.0f, 0.0f}; - }; - - struct ScaleProperties { - Math::Vector2f scale{1.0f, 1.0f}; - Math::Vector2f scaleTarget{1.0f, 1.0f}; - f32 scaleInheritRate{1.0f}; - s32 scaleStartFrame{0}; - }; - - struct TextureProperties { - TextureWrap textureWrapT{TextureWrap::ClampToEdge}; - TextureWrap textureWrapS{TextureWrap::ClampToEdge}; - TextureFilter textureMagFilter{TextureFilter::Nearest}; - TextureFilter textureMinFilter{TextureFilter::Nearest}; - TextureMipFilter textureMipFilter{TextureMipFilter::None}; - Math::Vector2f texUVScale{1.0f, 1.0f}; - }; - - struct ColorProperties { - binColor4f color0{1.0f, 1.0f, 1.0f, 1.0f}; - binColor3f color1{255.0f, 255.0f, 255.0f}; - }; - - struct AlphaProperties { - f32 alpha{1.0f}; - f32 alphaTarget{1.0f}; - f32 alphaInit{1.0f}; - s32 alphaStartFrame{1}; - s32 alphaBaseFrame{1}; - }; - - struct CombinerProperties { - BlendFuncType blendFunc{BlendFuncType::Translucent}; - DepthFuncType depthFunc{DepthFuncType::Unk0}; - ColorCombinerFuncType combinerFunc{ColorCombinerFuncType::CombinerConfig0}; - }; - -public: - ChildData() = default; - ChildData(const ChildData&) = delete; - - std::unique_ptr clone() const; - - TextureHandle& textureHandle(); - const TextureHandle& textureHandle() const; - void setTexture(const std::shared_ptr& texture); - - const BasicProperties &basicProperties() const; - void setBasicProperties(const BasicProperties &basicProperties); - - const EmissionProperties& emissionProperties() const; - void setEmissionProperties(const EmissionProperties& emissionProperties); - - const VelocityProperties& velocityProperties() const; - void setVelocityProperties(const VelocityProperties& velocityProperties); - - const TextureProperties& textureProperties() const; - void setTextureProperties(const TextureProperties& textureProperties); - - const ColorProperties& colorProperties() const; - void setColorProperties(const ColorProperties& colorProperties); - - const AlphaProperties& alphaProperties() const; - void setAlphaProperties(const AlphaProperties& alphaProperties); - - const CombinerProperties& combinerProperties() const; - void setCombinerProperties(const CombinerProperties& combinerProperties); - - const RotationProperties& rotationProperties() const; - void setRotationProperties(const RotationProperties& rotationProperties); - - const ScaleProperties& scaleProperties() const; - void setScaleProperties(const ScaleProperties& scaleProperties); - - void initFromBinary(const BinChildData& childData); - -private: - BasicProperties mBasicProperties{}; - EmissionProperties mEmissionProperties{}; - VelocityProperties mVelocityProperties{}; - TextureProperties mTextureProperties{}; - ColorProperties mColorProperties{}; - AlphaProperties mAlphaProperties{}; - CombinerProperties mCombinerProperties{}; - RotationProperties mRotationProperties{}; - ScaleProperties mScaleProperties{}; - - TextureHandle mTextureHandle{}; -}; - - -// ========================================================================== // - - -} // namespace Ptcl diff --git a/include/ptcl/ptclCommand.h b/include/ptcl/ptclCommand.h index 3654a09..1128f70 100644 --- a/include/ptcl/ptclCommand.h +++ b/include/ptcl/ptclCommand.h @@ -5,6 +5,7 @@ #include +#include namespace Ptcl { @@ -13,14 +14,47 @@ namespace Ptcl { // ========================================================================== // +class DocumentCommandBase : public QUndoCommand { +public: + explicit DocumentCommandBase(Document* document, QString label, QUndoCommand* parent = nullptr) : + QUndoCommand{std::move(label), parent}, mDocument{document} {} + +protected: + Document* document() const { return mDocument; } + Emitter* emitter(s32 setIndex, s32 emitterIndex) { return mDocument->emitterMutable(setIndex, emitterIndex); } + EmitterSet* emitterSet(s32 setIndex) { return mDocument->emitterSetMutable(setIndex); } + + void setProjectName(const QString& newName) { mDocument->dataMutable().setName(newName); } + + void notifyEmitterChanged(s32 setIndex, s32 emitterIndex) { mDocument->notifyEmitterChanged(setIndex, emitterIndex); } + void notifyEmitterSetChanged(s32 setIndex) { mDocument->notifyEmitterSetChanged(setIndex); } + void notifyProjectChanged() { mDocument->notifyProjectChanged(); } + +private: + Document* mDocument; +}; + + +// ========================================================================== // + + template -class SetEmitterPropertyCommand final : public QUndoCommand { +class SetEmitterPropertyCommand final : public DocumentCommandBase { public: using Getter = std::function; using Setter = std::function; - SetEmitterPropertyCommand(Document* document, s32 setIndex, s32 emitterIndex, QString label, QString key, Getter getter, Setter setter, const T& newValue, QUndoCommand* parent = nullptr) : - QUndoCommand{std::move(label), parent}, mDocument{document}, mSetIndex{setIndex}, mEmitterIndex{emitterIndex}, mPropertyKey{std::move(key)}, mGetter{std::move(getter)}, mSetter{std::move(setter)}, mNewValue{newValue} { + SetEmitterPropertyCommand(Document* document, s32 setIndex, s32 emitterIndex, QString label, + QString key, Getter getter, Setter setter, const T& newValue, QUndoCommand* parent = nullptr) : + DocumentCommandBase{document, std::move(label), parent}, + mSetIndex{setIndex}, + mEmitterIndex{emitterIndex}, + mPropertyKey{std::move(key)}, + mGetter{std::move(getter)}, + mSetter{std::move(setter)}, + mNewValue{newValue}, + mId{static_cast(qHash(mPropertyKey))} + { auto& emitter = getEmitter(); mOldValue = mGetter(emitter); @@ -30,16 +64,17 @@ class SetEmitterPropertyCommand final : public QUndoCommand { } s32 id() const override { - return qHash(mPropertyKey); + return mId; } bool mergeWith(const QUndoCommand* other) override { - auto otherCmd = dynamic_cast(other); - if (!otherCmd) { + if (other->id() != id()) { return false; } - if (mDocument != otherCmd->mDocument || mSetIndex != otherCmd->mSetIndex || + auto otherCmd = static_cast(other); + + if (document() != otherCmd->document() || mSetIndex != otherCmd->mSetIndex || mEmitterIndex != otherCmd->mEmitterIndex || mPropertyKey != otherCmd->mPropertyKey) { return false; } @@ -52,11 +87,10 @@ class SetEmitterPropertyCommand final : public QUndoCommand { void redo() override { apply(mNewValue); } private: - Emitter& getEmitter() const; + Emitter& getEmitter(); void apply(const T& value); private: - Document* mDocument{}; s32 mSetIndex{}; s32 mEmitterIndex{}; @@ -67,6 +101,8 @@ class SetEmitterPropertyCommand final : public QUndoCommand { T mOldValue{}; T mNewValue{}; + + const s32 mId{}; }; @@ -74,18 +110,69 @@ class SetEmitterPropertyCommand final : public QUndoCommand { template -Emitter& SetEmitterPropertyCommand::getEmitter() const { - return *mDocument->emitter(mSetIndex, mEmitterIndex); +Emitter& SetEmitterPropertyCommand::getEmitter() { + return *emitter(mSetIndex, mEmitterIndex); } template void SetEmitterPropertyCommand::apply(const T& value) { auto& emitter = getEmitter(); mSetter(emitter, value); - mDocument->notifyEmitterChanged(mSetIndex, mEmitterIndex); + notifyEmitterChanged(mSetIndex, mEmitterIndex); } +// ========================================================================== // + + +class RenameProjectNameCommand final : public DocumentCommandBase { +public: + RenameProjectNameCommand(Document* document, QString newName, QUndoCommand* parent = nullptr) : + DocumentCommandBase{document, std::move("Rename Project"), parent}, mNewName{std::move(newName)} { + mOldName = document->projectName(); + + if (mOldName == mNewName) { + setObsolete(true); + } + } + + s32 id() const override { + return mId; + } + + bool mergeWith(const QUndoCommand* other) override { + if (other->id() != id()) { + return false; + } + + auto otherCmd = static_cast(other); + + if (document() != otherCmd->document()) { + return false; + } + + mNewName = otherCmd->mNewName; + return true; + } + + void undo() override { apply(mOldName); } + void redo() override { apply(mNewName); } + +private: + void apply(const QString& value) { + setProjectName(value); + notifyProjectChanged(); + }; + +private: + QString mOldName{}; + QString mNewName{}; + + const s32 mId{static_cast(qHash("RenameProject"))}; +}; + + + // ========================================================================== // diff --git a/include/ptcl/ptclDocument.h b/include/ptcl/ptclDocument.h index 18d66ec..9d5988b 100644 --- a/include/ptcl/ptclDocument.h +++ b/include/ptcl/ptclDocument.h @@ -46,6 +46,8 @@ class Selection final : public QObject { // ========================================================================== // +class DocumentCommandBase; + template class SetEmitterPropertyCommand; @@ -58,21 +60,25 @@ class Document final : public QObject { public: explicit Document(QObject* parent = nullptr); - bool load(const QString& filePath); - bool save(const QString& filePath); - + // TODO: Remove this + EmitterSet* emitterSet(s32 index) { return mData.emitterSet(index); } + // TODO: Remove this + TextureList& textures() { return mData.textures(); } + // TODO: Remove this PtclRes& data() { return mData; } - const PtclRes& data() const { return mData; } - EmitterSet* emitterSet(s32 index) { return mData.emitterSet(index); } - const EmitterSet* emitterSet(s32 index) const { return mData.emitterSet(index); } + bool load(const QString& filePath); + bool save(const QString& filePath); - Emitter* emitter(s32 setIndex, s32 emitterIndex) { return mData.emitter(setIndex, emitterIndex); } const Emitter* emitter(s32 setIndex, s32 emitterIndex) const { return mData.emitter(setIndex, emitterIndex); } + const EmitterSet* emitterSet(s32 index) const { return mData.emitterSet(index); } - TextureList& textures() { return mData.textures(); } + const EmitterSetList& emitterSets() const { return mData.getEmitterSets(); } const TextureList& textures() const { return mData.textures(); } + const QString& projectName() const { return mData.name(); } + void setProjectName(const QString& name); + bool isModified() const { return mModified; } QString filePath() const { return mFilePath; } @@ -83,10 +89,25 @@ class Document final : public QObject { mUndoStack.push(new SetEmitterPropertyCommand(this, setIndex, emitterIndex, label, key, getter, setter, value)); } + s32 emitterCount(s32 setIndex) const { return mData.emitterCount(setIndex); } + s32 emitterSetCount() const { return mData.emitterSetCount(); } + +private: + TextureList& texturesMutable() { return mData.textures(); } + Emitter* emitterMutable(s32 setIndex, s32 emitterIndex) { return mData.emitter(setIndex, emitterIndex); } + EmitterSet* emitterSetMutable(s32 index) { return mData.emitterSet(index); } + PtclRes& dataMutable() { return mData; } + void notifyEmitterChanged(s32 setIndex, s32 emitterIndex) { emit emitterChanged(setIndex, emitterIndex); } + void notifyEmitterSetChanged(s32 setIndex) { emit emitterSetChanged(setIndex); } + void notifyProjectChanged() { emit projectChanged(); } + + friend class DocumentCommandBase; signals: void emitterChanged(s32 setIndex, s32 emitterIndex); + void emitterSetChanged(s32 setIndex); + void projectChanged(); private: PtclRes mData{}; diff --git a/src/editor/inspector/inspectorPanel.cpp b/src/editor/inspector/inspectorPanel.cpp index 248182e..38e82b2 100644 --- a/src/editor/inspector/inspectorPanel.cpp +++ b/src/editor/inspector/inspectorPanel.cpp @@ -35,10 +35,10 @@ #include "editor/inspector/field/fieldPosAddInspector.h" #include "editor/inspector/field/fieldRandomInspector.h" #include "editor/inspector/field/fieldSpinInspector.h" +#include "util/nameValidator.h" #include - namespace PtclEditor { @@ -48,6 +48,14 @@ namespace PtclEditor { InspectorPanel::InspectorPanel(QWidget* parent) : QWidget{parent} { + mProjNameLineEdit = new QLineEdit(this); + mProjNameLineEdit->setPlaceholderText("PTCLProject"); + mProjNameLineEdit->setValidator(new EmitterNameValidator(mProjNameLineEdit)); + + connect(mProjNameLineEdit, &QLineEdit::textChanged, this, [this](const QString& text) { + mDocument->setProjectName(text); + }); + mGeneralInspector = new GeneralEmitterInspector(this); mGravityInspector = new GravityInspector(this); mTransformInspector = new TransformInspector(this); @@ -83,6 +91,11 @@ InspectorPanel::InspectorPanel(QWidget* parent) : mFluctuationInspector = new FluctuationInspector(this); + // Project Properties + auto* projectPropertiesLayout = new QHBoxLayout; + projectPropertiesLayout->addWidget(new QLabel("Project Name")); + projectPropertiesLayout->addWidget(mProjNameLineEdit); + mTabStack = new QStackedWidget(this); mEmitterTabs = new QTabWidget(this); @@ -102,6 +115,7 @@ InspectorPanel::InspectorPanel(QWidget* parent) : mTabStack->addWidget(mFluxTabs); auto* layout = new QVBoxLayout(this); + layout->addLayout(projectPropertiesLayout); layout->addWidget(mTabStack); layout->setContentsMargins(0, 0, 0, 0); @@ -230,6 +244,9 @@ void InspectorPanel::setDocument(Ptcl::Document* document) { mChildVelocityInspector->setDocument(document); if (mDocument) { + QSignalBlocker b1(mProjNameLineEdit); + mProjNameLineEdit->setText(document->projectName()); + connect(mDocument, &Ptcl::Document::emitterChanged, this, [this](s32 setIndex, s32 emitterIndex) { if (!mEmitter || setIndex != mSelection->emitterSetIndex() || emitterIndex != mSelection->emitterIndex()) { return; @@ -237,6 +254,12 @@ void InspectorPanel::setDocument(Ptcl::Document* document) { populateProperties(); }); + + connect(mDocument, &Ptcl::Document::projectChanged, this, [this]() { + QSignalBlocker b1(mProjNameLineEdit); + mProjNameLineEdit->setText(mDocument->projectName()); + }); + } } diff --git a/src/editor/mainWindow.cpp b/src/editor/mainWindow.cpp index fc18ed3..7fc5db5 100644 --- a/src/editor/mainWindow.cpp +++ b/src/editor/mainWindow.cpp @@ -44,21 +44,11 @@ void MainWindow::setupUi() { emitterGroupLayout->setContentsMargins(0, 0, 0, 0); emitterGroupLayout->addWidget(mPropertiesStack); - // Project Name - mProjNameLineEdit.setPlaceholderText("PTCLProject"); - mProjNameLineEdit.setValidator(new EmitterNameValidator(&mProjNameLineEdit)); - mProjNameLineEdit.setEnabled(false); - - auto* projectSettingsLayout = new QFormLayout; - projectSettingsLayout->setContentsMargins(0, 0, 0, 0); - projectSettingsLayout->addRow("Project Name:", &mProjNameLineEdit); - auto* propertiesContainer = new QWidget(this); auto* propertiesLayout = new QVBoxLayout(propertiesContainer); propertiesLayout->setContentsMargins(0, 0, 0, 0); propertiesLayout->setSpacing(4); - propertiesLayout->addLayout(projectSettingsLayout); propertiesLayout->addWidget(&mPropertiesGroup); // Top Splitter @@ -116,12 +106,6 @@ void MainWindow::setupUi() { } void MainWindow::setupConnections() { - // Proj Name - connect(&mProjNameLineEdit, &QLineEdit::textChanged, this, [this](const QString& text) { - mDocument->data().setName(text); - setDirty(true); - }); - connect(&mSelection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { if (!mDocument) { return; @@ -407,7 +391,6 @@ void MainWindow::loadPtclRes(const QString& path) { mEmitterSetWidget.setDocument(nullptr); mTextureWidget.setDocument(nullptr); - mProjNameLineEdit.setEnabled(false); mSaveAsAction.setEnabled(false); mDocument = std::make_unique(); @@ -429,17 +412,13 @@ void MainWindow::loadPtclRes(const QString& path) { mEmitterSetWidget.setDocument(mDocument.get()); mTextureWidget.setDocument(mDocument.get()); - QSignalBlocker b1(mProjNameLineEdit); - mProjNameLineEdit.setText(mDocument->data().name()); - - if (mDocument->data().emitterSetCount() != 0) { + if (mDocument->emitterSetCount() != 0) { mSelection.set(0, 0, Ptcl::Selection::Type::EmitterSet); } updateStatusBar(); updateWindowTitle(); - mProjNameLineEdit.setEnabled(true); mSaveAsAction.setEnabled(true); } diff --git a/src/editor/ptclListWidget.cpp b/src/editor/ptclListWidget.cpp index 365caa1..be8ce86 100644 --- a/src/editor/ptclListWidget.cpp +++ b/src/editor/ptclListWidget.cpp @@ -282,7 +282,7 @@ void PtclList::populateList() { } mListModel.clear(); - const auto& sets = mDocument->data().getEmitterSets(); + const auto& sets = mDocument->emitterSets(); for (s32 setIndex = 0; setIndex < sets.size(); ++setIndex) { insertEmitterSetNode(setIndex); } @@ -573,7 +573,7 @@ void PtclList::addEmitterSet() { return; } - const s32 setIndex = mDocument->data().emitterSetCount(); + const s32 setIndex = mDocument->emitterSetCount(); mDocument->data().addNewEmitterSet(); insertEmitterSetNode(setIndex); @@ -741,11 +741,11 @@ void PtclList::copyItem() { if (type == NodeType::EmitterSet) { const s32 setIndex = item->data(sRoleSetIdx).toInt(); - mClipboardSet = mDocument->data().emitterSet(setIndex)->clone(); + mClipboardSet = mDocument->emitterSet(setIndex)->clone(); } else if (type == NodeType::Emitter) { const s32 setIndex = item->parent()->data(sRoleSetIdx).toInt(); const s32 emitterIndex = item->data(sRoleEmitterIdx).toInt(); - mClipboardEmitter = mDocument->data().emitter(setIndex, emitterIndex)->clone(); + mClipboardEmitter = mDocument->emitter(setIndex, emitterIndex)->clone(); } updateToolbarForSelection(item); @@ -766,13 +766,13 @@ void PtclList::pasteItem() { auto& newSet = mDocument->data().appendEmitterSet(mClipboardSet); mClipboardSet = newSet->clone(); - const s32 setIndex = mDocument->data().emitterSetCount() - 1; + const s32 setIndex = mDocument->emitterSetCount() - 1; insertEmitterSetNode(setIndex); mSelection->set(setIndex, 0, Ptcl::Selection::Type::EmitterSet); } else if (mClipboardEmitter && type == NodeType::Emitter) { const auto& setItem = item->parent(); const s32 setIndex = item->data(sRoleSetIdx).toInt(); - auto set = mDocument->data().emitterSet(setIndex); + auto set = mDocument->emitterSet(setIndex); auto& newEmitter = set->appendEmitter(mClipboardEmitter); mClipboardEmitter = newEmitter->clone(); diff --git a/src/ptcl/ptclChildData.cpp b/src/ptcl/ptclChildData.cpp deleted file mode 100644 index 6a0af1c..0000000 --- a/src/ptcl/ptclChildData.cpp +++ /dev/null @@ -1,180 +0,0 @@ -#include "ptcl/ptclChildData.h" - - -namespace Ptcl { - - -// ========================================================================== // - -std::unique_ptr ChildData::clone() const { - auto newData = std::make_unique(); - - newData->mBasicProperties = mBasicProperties; - newData->mEmissionProperties = mEmissionProperties; - newData->mVelocityProperties = mVelocityProperties; - newData->mTextureProperties = mTextureProperties; - newData->mColorProperties = mColorProperties; - newData->mAlphaProperties = mAlphaProperties; - newData->mCombinerProperties = mCombinerProperties; - newData->mRotationProperties = mRotationProperties; - newData->mScaleProperties = mScaleProperties; - newData->mTextureHandle = mTextureHandle.clone(); - return newData; -} - -TextureHandle& ChildData::textureHandle() { - return mTextureHandle; -} - -const TextureHandle& ChildData::textureHandle() const { - return mTextureHandle; -} - -void ChildData::setTexture(const std::shared_ptr& texture) { - mTextureHandle.set(texture); -} - -const ChildData::BasicProperties& ChildData::basicProperties() const { - return mBasicProperties; -} - -void ChildData::setBasicProperties(const BasicProperties& basicProperties) { - mBasicProperties = basicProperties; -} - -const ChildData::EmissionProperties& ChildData::emissionProperties() const { - return mEmissionProperties; -} - -void ChildData::setEmissionProperties(const EmissionProperties& emissionProperties) { - mEmissionProperties = emissionProperties; -} - -const ChildData::VelocityProperties& ChildData::velocityProperties() const { - return mVelocityProperties; -} - -void ChildData::setVelocityProperties(const VelocityProperties& velocityProperties) { - mVelocityProperties = velocityProperties; -} - -const ChildData::TextureProperties& ChildData::textureProperties() const { - return mTextureProperties; -} - -void ChildData::setTextureProperties(const TextureProperties& textureProperties) { - mTextureProperties = textureProperties; -} - -const ChildData::ColorProperties& ChildData::colorProperties() const { - return mColorProperties; -} - -void ChildData::setColorProperties(const ColorProperties& colorProperties) { - mColorProperties = colorProperties; -} - -const ChildData::AlphaProperties& ChildData::alphaProperties() const { - return mAlphaProperties; -} - -void ChildData::setAlphaProperties(const AlphaProperties& alphaProperties) { - mAlphaProperties = alphaProperties; -} - -const ChildData::CombinerProperties& ChildData::combinerProperties() const { - return mCombinerProperties; -} - -void ChildData::setCombinerProperties(const CombinerProperties& combinerProperties) { - mCombinerProperties = combinerProperties; -} - -const ChildData::RotationProperties& ChildData::rotationProperties() const { - return mRotationProperties; -} - -void ChildData::setRotationProperties(const RotationProperties& rotationProperties) { - mRotationProperties = rotationProperties; -} - -const ChildData::ScaleProperties& ChildData::scaleProperties() const { - return mScaleProperties; -} - -void ChildData::setScaleProperties(const ScaleProperties& scaleProperties) { - mScaleProperties = scaleProperties; -} - -void ChildData::initFromBinary(const BinChildData& childData) { - - mBasicProperties = { - .billboardType = childData.childBillboardType - }; - - mEmissionProperties = { - .emitRate = childData.childEmitRate, - .emitTiming = childData.childEmitTiming, - .life = childData.childLife, - .emitStep = childData.childEmitStep - }; - - mVelocityProperties = { - .randVel = { childData.childRandVel.x, childData.childRandVel.y, childData.childRandVel.z }, - .gravity = { childData.childGravity.x, childData.childGravity.y, childData.childGravity.z }, - .velInheritRate = childData.childVelInheritRate, - .initPosRand = childData.childInitPosRand, - .figurVel = childData.childFigurVel, - .airResist = childData.childAirResist - }; - - mRotationProperties = { - .rotType = childData.childRotType, - .initRot = { childData.childInitRot.x, childData.childInitRot.y, childData.childInitRot.z }, - .initRotRand = { childData.childInitRotRand.x, childData.childInitRotRand.y, childData.childInitRotRand.z }, - .rotVel = { childData.childRotVel.x, childData.childRotVel.y, childData.childRotVel.z }, - .rotVelRand = { childData.childRotVelRand.x, childData.childRotVelRand.y, childData.childRotVelRand.z }, - .rotBasis = { childData.childRotBasis.x, childData.childRotBasis.y } - }; - - mScaleProperties = { - .scale = { childData.childScale.x, childData.childScale.y }, - .scaleTarget = { childData.childScaleTarget.x, childData.childScaleTarget.y }, - .scaleInheritRate = childData.childScaleInheritRate, - .scaleStartFrame = childData.childScaleStartFrame - }; - - mTextureProperties = { - .textureWrapT = childData.childTextureRes.wrapT, - .textureWrapS = childData.childTextureRes.wrapS, - .textureMagFilter = childData.childTextureRes.magFilter, - .textureMinFilter = static_cast(childData.childTextureRes.minMipFilter & 0x1), - .textureMipFilter = static_cast((childData.childTextureRes.minMipFilter >> 1) & 0x3), - .texUVScale = { childData.childTexUScale, childData.childTexVScale } - }; - - mColorProperties = { - .color0 = childData.childColor0, - .color1 = childData.childColor1 - }; - - mAlphaProperties = { - .alpha = childData.childAlpha, - .alphaTarget = childData.childAlphaTarget, - .alphaInit = childData.childAlphaInit, - .alphaStartFrame = childData.childAlphaStartFrame, - .alphaBaseFrame = childData.childAlphaBaseFrame - }; - - mCombinerProperties = { - .blendFunc = childData.childBlendType, - .depthFunc = childData.childDepthType, - .combinerFunc = childData.childCombinerType - }; -} - - -// ========================================================================== // - - -} // namespace ptcl diff --git a/src/ptcl/ptclDocument.cpp b/src/ptcl/ptclDocument.cpp index 6b5a967..b5f8560 100644 --- a/src/ptcl/ptclDocument.cpp +++ b/src/ptcl/ptclDocument.cpp @@ -1,3 +1,4 @@ +#include "ptcl/ptclCommand.h" #include "ptcl/ptclDocument.h" @@ -36,6 +37,10 @@ bool Document::save(const QString& filePath) { return mData.save(filePath); } +void Document::setProjectName(const QString& name) { + mUndoStack.push(new RenameProjectNameCommand(this, name)); +} + // ========================================================================== // diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index d1f44fd..00e60af 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -103,10 +103,10 @@ std::unique_ptr Emitter::clone() const { newEmitter->mTexturePatternFrequency = mTexturePatternFrequency; newEmitter->mTexturePatternTblUse = mTexturePatternTblUse; newEmitter->mIsTexturePatternAnim = mIsTexturePatternAnim; - newEmitter->mTextureHandle = mTextureHandle.clone(); + newEmitter->mTextureHandle = mTextureHandle; newEmitter->mChildFlags = mChildFlags; - newEmitter->mChild = mChild; // TODO - texture handle clone? + newEmitter->mChild = mChild; // Fluctuation Properties newEmitter->mFluctuationFlags = mFluctuationFlags; diff --git a/src/ptcl/ptclTexture.cpp b/src/ptcl/ptclTexture.cpp index 1145c55..c3c8531 100644 --- a/src/ptcl/ptclTexture.cpp +++ b/src/ptcl/ptclTexture.cpp @@ -151,10 +151,6 @@ TextureHandle& TextureHandle::operator=(TextureHandle&& other) noexcept { return *this; } -TextureHandle TextureHandle::clone() const { - return *this; -} - void TextureHandle::invalidate() { mTexturePtr = nullptr; } From 70f1c586157acdfe976acbdff497e88fcfcf78f6 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Fri, 20 Mar 2026 00:15:30 +0000 Subject: [PATCH 27/35] feat(ui): implement undo/redo for EmitterSet properties and directly integrate with inspector --- CMakeLists.txt | 8 +- .../emitterSetInspector.h} | 12 +- include/editor/inspector/inspectorPanel.h | 5 + include/editor/mainWindow.h | 20 --- include/ptcl/ptclCommand.h | 90 ++++++++++++- include/ptcl/ptclDocument.h | 7 + src/editor/emitterSetWidget.cpp | 89 ------------ src/editor/inspector/emitterSetInspector.cpp | 127 ++++++++++++++++++ src/editor/inspector/inspectorPanel.cpp | 18 +++ src/editor/mainWindow.cpp | 120 +---------------- 10 files changed, 253 insertions(+), 243 deletions(-) rename include/editor/{emitterSetWidget.h => inspector/emitterSetInspector.h} (74%) delete mode 100644 src/editor/emitterSetWidget.cpp create mode 100644 src/editor/inspector/emitterSetInspector.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b37b81..f57aab6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,6 @@ endif() set(PROJECT_SOURCES src/main.cpp src/editor/mainWindow.cpp - src/editor/emitterSetWidget.cpp src/editor/ptclListWidget.cpp src/editor/textureListWidget.cpp src/editor/textureImportDialog.cpp @@ -39,10 +38,11 @@ set(PROJECT_SOURCES src/editor/components/rgbaColorWidget.cpp src/editor/components/thumbnailWidget.cpp src/editor/inspector/alphaAnimInspector.cpp - src/editor/inspector/generalEmitterInspector.cpp src/editor/inspector/colorInspector.cpp src/editor/inspector/combinerInspector.cpp src/editor/inspector/emissionInspector.cpp + src/editor/inspector/emitterSetInspector.cpp + src/editor/inspector/generalEmitterInspector.cpp src/editor/inspector/inspectorPanel.cpp src/editor/inspector/inspectorWidgetBase.cpp src/editor/inspector/fluctuationInspector.cpp @@ -88,7 +88,6 @@ set(PROJECT_SOURCES set(PROJECT_HEADERS include/typedefs.h include/editor/mainWindow.h - include/editor/emitterSetWidget.h include/editor/ptclListWidget.h include/editor/textureImportDialog.h include/editor/textureListWidget.h @@ -107,10 +106,11 @@ set(PROJECT_HEADERS include/editor/components/thumbnailWidget.h include/editor/components/vectorSpinBox.h include/editor/inspector/alphaAnimInspector.h - include/editor/inspector/generalEmitterInspector.h include/editor/inspector/colorInspector.h include/editor/inspector/combinerInspector.h + include/editor/inspector/emitterSetInspector.h include/editor/inspector/emissionInspector.h + include/editor/inspector/generalEmitterInspector.h include/editor/inspector/inspectorPanel.h include/editor/inspector/inspectorWidgetBase.h include/editor/inspector/fluctuationInspector.h diff --git a/include/editor/emitterSetWidget.h b/include/editor/inspector/emitterSetInspector.h similarity index 74% rename from include/editor/emitterSetWidget.h rename to include/editor/inspector/emitterSetInspector.h index 8f4f6a6..b33535b 100644 --- a/include/editor/emitterSetWidget.h +++ b/include/editor/inspector/emitterSetInspector.h @@ -1,6 +1,6 @@ #pragma once -#include "components/sizedSpinBox.h" +#include "editor/components/sizedSpinBox.h" #include "ptcl/ptclDocument.h" #include "ptcl/ptclEmitterSet.h" @@ -15,18 +15,14 @@ namespace PtclEditor { // ========================================================================== // -class EmitterSetWidget final : public QWidget { +class EmitterSetInspector final : public QWidget { Q_OBJECT public: - explicit EmitterSetWidget(QWidget* parent = nullptr); + explicit EmitterSetInspector(QWidget* parent = nullptr); void setDocument(Ptcl::Document* document); void setSelection(Ptcl::Selection* selection); -signals: - void emitterSetNamedChanged(); - void propertiesChanged(); - private: void populateProperties(); void setupConnections(); @@ -34,7 +30,7 @@ class EmitterSetWidget final : public QWidget { private: Ptcl::Document* mDocument{nullptr}; const Ptcl::Selection* mSelection{nullptr}; - Ptcl::EmitterSet* mEmitterSet{nullptr}; + const Ptcl::EmitterSet* mEmitterSet{nullptr}; QLineEdit mNameLineEdit{}; SizedSpinBox mUserDataSpinBox{}; diff --git a/include/editor/inspector/inspectorPanel.h b/include/editor/inspector/inspectorPanel.h index 0c0e82b..2fe70e3 100644 --- a/include/editor/inspector/inspectorPanel.h +++ b/include/editor/inspector/inspectorPanel.h @@ -11,6 +11,8 @@ namespace PtclEditor { +class EmitterSetInspector; + class GeneralEmitterInspector; class GravityInspector; class TransformInspector; @@ -72,6 +74,8 @@ class InspectorPanel final : public QWidget { QLineEdit* mProjNameLineEdit{nullptr}; + EmitterSetInspector* mEmitterSetInspector{nullptr}; + GeneralEmitterInspector* mGeneralInspector{nullptr}; GravityInspector* mGravityInspector{nullptr}; TransformInspector* mTransformInspector{nullptr}; @@ -108,6 +112,7 @@ class InspectorPanel final : public QWidget { ChildVelocityInspector* mChildVelocityInspector{nullptr}; QStackedWidget* mTabStack{nullptr}; + QTabWidget* mEmitterSetTabs{nullptr}; QTabWidget* mEmitterTabs{nullptr}; QTabWidget* mChildTabs{nullptr}; QTabWidget* mFieldTabs{nullptr}; diff --git a/include/editor/mainWindow.h b/include/editor/mainWindow.h index a765967..fc17467 100644 --- a/include/editor/mainWindow.h +++ b/include/editor/mainWindow.h @@ -1,7 +1,6 @@ #pragma once #include "ptcl/ptclDocument.h" -#include "editor/emitterSetWidget.h" #include "editor/inspector/inspectorPanel.h" #include "editor/ptclListWidget.h" #include "editor/textureListWidget.h" @@ -26,16 +25,6 @@ namespace PtclEditor { class MainWindow final : public QMainWindow { Q_OBJECT - -private: - enum class PropertiesView { - EmitterSet, - Emitter, - EmitterChild, - EmitterFlux, - EmitterField - }; - public: MainWindow(QWidget* parent = nullptr); @@ -59,9 +48,6 @@ private slots: void setupConnections(); void setupMenus(); - void setPropertiesView(PropertiesView view); - void updatePropertiesStatus(); - void updateWindowTitle(); void updateStatusBar(); void setDirty(bool dirty); @@ -87,18 +73,12 @@ private slots: QSplitter* mTopSplitter{nullptr}; QSplitter* mBottomSplitter{nullptr}; - QStackedWidget* mPropertiesStack{nullptr}; - QGroupBox mPropertiesGroup{}; - QUndoView mUndoView{}; PtclEditor::PtclList mPtclList{}; PtclEditor::InspectorPanel mInspector{}; - PtclEditor::EmitterSetWidget mEmitterSetWidget{}; PtclEditor::TextureListWidget mTextureWidget{}; - PropertiesView mCurPropertiesView{PropertiesView::EmitterSet}; - QLabel* mStatusLabel{nullptr}; bool mHasUnsavedChanges{false}; }; diff --git a/include/ptcl/ptclCommand.h b/include/ptcl/ptclCommand.h index 1128f70..9e08686 100644 --- a/include/ptcl/ptclCommand.h +++ b/include/ptcl/ptclCommand.h @@ -105,7 +105,6 @@ class SetEmitterPropertyCommand final : public DocumentCommandBase { const s32 mId{}; }; - // ========================================================================== // @@ -116,8 +115,8 @@ Emitter& SetEmitterPropertyCommand::getEmitter() { template void SetEmitterPropertyCommand::apply(const T& value) { - auto& emitter = getEmitter(); - mSetter(emitter, value); + auto& emitterSet = getEmitter(); + mSetter(emitterSet, value); notifyEmitterChanged(mSetIndex, mEmitterIndex); } @@ -125,6 +124,91 @@ void SetEmitterPropertyCommand::apply(const T& value) { // ========================================================================== // +template +class SetEmitterSetPropertyCommand final : public DocumentCommandBase { +public: + using Getter = std::function; + using Setter = std::function; + + SetEmitterSetPropertyCommand(Document* document, s32 setIndex, QString label, + QString key, Getter getter, Setter setter, const T& newValue, QUndoCommand* parent = nullptr) : + DocumentCommandBase{document, std::move(label), parent}, + mSetIndex{setIndex}, + mPropertyKey{std::move(key)}, + mGetter{std::move(getter)}, + mSetter{std::move(setter)}, + mNewValue{newValue}, + mId{static_cast(qHash(mPropertyKey))} + { + auto& emitterSet = getEmitterSet(); + mOldValue = mGetter(emitterSet); + + if (mOldValue == mNewValue) { + setObsolete(true); + } + } + + s32 id() const override { + return mId; + } + + bool mergeWith(const QUndoCommand* other) override { + if (other->id() != id()) { + return false; + } + + auto otherCmd = static_cast(other); + + if (document() != otherCmd->document() || mSetIndex != otherCmd->mSetIndex || + mPropertyKey != otherCmd->mPropertyKey) { + return false; + } + + mNewValue = otherCmd->mNewValue; + return true; + } + + void undo() override { apply(mOldValue); } + void redo() override { apply(mNewValue); } + +private: + EmitterSet& getEmitterSet(); + void apply(const T& value); + +private: + s32 mSetIndex{}; + + QString mPropertyKey{}; + + Getter mGetter{}; + Setter mSetter{}; + + T mOldValue{}; + T mNewValue{}; + + const s32 mId{}; +}; + + +// ========================================================================== // + + +template +EmitterSet& SetEmitterSetPropertyCommand::getEmitterSet() { + return *emitterSet(mSetIndex); +} + +template +void SetEmitterSetPropertyCommand::apply(const T& value) { + auto& emitterSet = getEmitterSet(); + mSetter(emitterSet, value); + notifyEmitterSetChanged(mSetIndex); +} + + +// ========================================================================== // + + class RenameProjectNameCommand final : public DocumentCommandBase { public: RenameProjectNameCommand(Document* document, QString newName, QUndoCommand* parent = nullptr) : diff --git a/include/ptcl/ptclDocument.h b/include/ptcl/ptclDocument.h index 9d5988b..dc631bb 100644 --- a/include/ptcl/ptclDocument.h +++ b/include/ptcl/ptclDocument.h @@ -51,6 +51,8 @@ class DocumentCommandBase; template class SetEmitterPropertyCommand; +template +class SetEmitterSetPropertyCommand; // ========================================================================== // @@ -89,6 +91,11 @@ class Document final : public QObject { mUndoStack.push(new SetEmitterPropertyCommand(this, setIndex, emitterIndex, label, key, getter, setter, value)); } + template + void setEmitterSetProperty(s32 setIndex, QString label, QString key, Getter getter, Setter setter, const T& value) { + mUndoStack.push(new SetEmitterSetPropertyCommand(this, setIndex, label, key, getter, setter, value)); + } + s32 emitterCount(s32 setIndex) const { return mData.emitterCount(setIndex); } s32 emitterSetCount() const { return mData.emitterSetCount(); } diff --git a/src/editor/emitterSetWidget.cpp b/src/editor/emitterSetWidget.cpp deleted file mode 100644 index 32f8568..0000000 --- a/src/editor/emitterSetWidget.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "editor/emitterSetWidget.h" -#include "util/nameValidator.h" - -#include - - -namespace PtclEditor { - - -// ========================================================================== // - - -EmitterSetWidget::EmitterSetWidget(QWidget* parent) : - QWidget{parent} { - mNameLineEdit.setPlaceholderText("EmitterSetName"); - mNameLineEdit.setValidator(new EmitterNameValidator(&mNameLineEdit)); - // TODO: Check these - mUserDataSpinBox.setRange(std::numeric_limits::min(), std::numeric_limits::max()); - mLastUpdateSpinBox.setRange(std::numeric_limits::min(), std::numeric_limits::max()); - - auto* mainLayout = new QFormLayout(this); - mainLayout->addRow("EmitterSet Name:", &mNameLineEdit); - mainLayout->addRow("UserData:", &mUserDataSpinBox); - mainLayout->addRow("LastUpdate:", &mLastUpdateSpinBox); - - setupConnections(); -} - -void EmitterSetWidget::setupConnections() { - // Name Edit - connect(&mNameLineEdit, &QLineEdit::textEdited, this, [this](const QString& text) { - if (!mEmitterSet) { - return; - } - mEmitterSet->setName(text); - emit emitterSetNamedChanged(); - emit propertiesChanged(); - }); - - // User Data - connect(&mUserDataSpinBox, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { - mEmitterSet->setUserData(static_cast(value)); - emit propertiesChanged(); - }); - - // Last Update - connect(&mLastUpdateSpinBox, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { - mEmitterSet->setLastUpdateDate(static_cast(value)); - emit propertiesChanged(); - }); -} - -void EmitterSetWidget::setDocument(Ptcl::Document* document) { - mDocument = document; -} - -void EmitterSetWidget::setSelection(Ptcl::Selection* selection) { - mSelection = selection; - - connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex) { - Q_UNUSED(emitterIndex); - - if (!mDocument) { - mEmitterSet = nullptr; - setEnabled(false); - return; - } - - mEmitterSet = mDocument->emitterSet(setIndex); - setEnabled(true); - populateProperties(); - }); -} - -void EmitterSetWidget::populateProperties() { - blockSignals(true); - - mNameLineEdit.setText(mEmitterSet->name()); - mUserDataSpinBox.setValue(mEmitterSet->userData()); - mLastUpdateSpinBox.setValue(mEmitterSet->lastUpdateDate()); - - blockSignals(false); -} - - -// ========================================================================== // - - -} // namespace PtclEditor diff --git a/src/editor/inspector/emitterSetInspector.cpp b/src/editor/inspector/emitterSetInspector.cpp new file mode 100644 index 0000000..baff759 --- /dev/null +++ b/src/editor/inspector/emitterSetInspector.cpp @@ -0,0 +1,127 @@ +#include "editor/inspector/emitterSetInspector.h" +#include "util/nameValidator.h" + +#include + + +namespace PtclEditor { + + +// ========================================================================== // + + +EmitterSetInspector::EmitterSetInspector(QWidget* parent) : + QWidget{parent} { + mNameLineEdit.setPlaceholderText("EmitterSetName"); + mNameLineEdit.setValidator(new EmitterNameValidator(&mNameLineEdit)); + // TODO: Check these + mUserDataSpinBox.setRange(std::numeric_limits::min(), std::numeric_limits::max()); + mLastUpdateSpinBox.setRange(std::numeric_limits::min(), std::numeric_limits::max()); + + auto* mainLayout = new QFormLayout(this); + mainLayout->addRow("EmitterSet Name:", &mNameLineEdit); + mainLayout->addRow("UserData:", &mUserDataSpinBox); + mainLayout->addRow("LastUpdate:", &mLastUpdateSpinBox); + + setupConnections(); +} + +void EmitterSetInspector::setupConnections() { + // Name Edit + connect(&mNameLineEdit, &QLineEdit::textEdited, this, [this](const QString& text) { + mDocument->setEmitterSetProperty( + mSelection->emitterSetIndex(), + "Set EmitterSet Name", + "SetEmitterSetName", + &Ptcl::EmitterSet::name, + &Ptcl::EmitterSet::setName, + text + ); + }); + + // User Data + connect(&mUserDataSpinBox, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { + mDocument->setEmitterSetProperty( + mSelection->emitterSetIndex(), + "Set EmitterSet UserData", + "SetEmitterSetUserData", + &Ptcl::EmitterSet::userData, + &Ptcl::EmitterSet::setUserData, + static_cast(value) + ); + }); + + // Last Update + connect(&mLastUpdateSpinBox, &SizedSpinBoxBase::valueChanged, this, [this](u64 value) { + mDocument->setEmitterSetProperty( + mSelection->emitterSetIndex(), + "Set EmitterSet LastUpdate", + "SetEmitterSetLastUpdate", + &Ptcl::EmitterSet::lastUpdateDate, + &Ptcl::EmitterSet::setLastUpdateDate, + static_cast(value) + ); + }); +} + +void EmitterSetInspector::setDocument(Ptcl::Document* document) { + if (mDocument) { + mDocument->disconnect(this); + } + + mDocument = document; + + if (mDocument) { + connect(mDocument, &Ptcl::Document::emitterSetChanged, this, [this](s32 setIndex) { + if (!mEmitterSet) { + return; + } + + if (setIndex != mSelection->emitterSetIndex()) { + return; + } + + populateProperties(); + }); + } +} + +void EmitterSetInspector::setSelection(Ptcl::Selection* selection) { + if (mSelection) { + mSelection->disconnect(this); + } + + mSelection = selection; + + if (mSelection) { + connect(selection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex) { + Q_UNUSED(emitterIndex); + + if (!mDocument) { + mEmitterSet = nullptr; + setEnabled(false); + return; + } + + mEmitterSet = mDocument->emitterSet(setIndex); + setEnabled(true); + populateProperties(); + }); + } +} + +void EmitterSetInspector::populateProperties() { + QSignalBlocker b1(mNameLineEdit); + QSignalBlocker b2(mUserDataSpinBox); + QSignalBlocker b3(mLastUpdateSpinBox); + + mNameLineEdit.setText(mEmitterSet->name()); + mUserDataSpinBox.setValue(mEmitterSet->userData()); + mLastUpdateSpinBox.setValue(mEmitterSet->lastUpdateDate()); +} + + +// ========================================================================== // + + +} // namespace PtclEditor diff --git a/src/editor/inspector/inspectorPanel.cpp b/src/editor/inspector/inspectorPanel.cpp index 38e82b2..38b8cbf 100644 --- a/src/editor/inspector/inspectorPanel.cpp +++ b/src/editor/inspector/inspectorPanel.cpp @@ -1,6 +1,8 @@ #include "editor/inspector/inspectorPanel.h" +#include "editor/inspector/emitterSetInspector.h" + #include "editor/inspector/alphaAnimInspector.h" #include "editor/inspector/generalEmitterInspector.h" #include "editor/inspector/colorInspector.h" @@ -56,6 +58,8 @@ InspectorPanel::InspectorPanel(QWidget* parent) : mDocument->setProjectName(text); }); + mEmitterSetInspector = new EmitterSetInspector(this); + mGeneralInspector = new GeneralEmitterInspector(this); mGravityInspector = new GravityInspector(this); mTransformInspector = new TransformInspector(this); @@ -98,6 +102,10 @@ InspectorPanel::InspectorPanel(QWidget* parent) : mTabStack = new QStackedWidget(this); + mEmitterSetTabs = new QTabWidget(this); + mEmitterSetTabs->setTabPosition(QTabWidget::West); + mTabStack->addWidget(mEmitterSetTabs); + mEmitterTabs = new QTabWidget(this); mEmitterTabs->setTabPosition(QTabWidget::West); mTabStack->addWidget(mEmitterTabs); @@ -136,6 +144,9 @@ void InspectorPanel::buildTabs() { return scroll; }; + // EmitterSet + mEmitterSetTabs->addTab(mEmitterSetInspector, "EmitterSet"); + // Emitter mEmitterTabs->addTab(wrapInScroll(mGeneralInspector), "General"); mEmitterTabs->addTab(mStripeInspector, "Stripe"); @@ -184,6 +195,9 @@ void InspectorPanel::updateTabVisibility() { const auto type = mSelection->type(); switch (type) { + case Ptcl::Selection::Type::EmitterSet: + mTabStack->setCurrentWidget(mEmitterSetTabs); + break; case Ptcl::Selection::Type::Emitter: mTabStack->setCurrentWidget(mEmitterTabs); break; @@ -208,6 +222,8 @@ void InspectorPanel::setDocument(Ptcl::Document* document) { mDocument = document; + mEmitterSetInspector->setDocument(document); + mGeneralInspector->setDocument(document); mGravityInspector->setDocument(document); mTransformInspector->setDocument(document); @@ -270,6 +286,8 @@ void InspectorPanel::setSelection(Ptcl::Selection* selection) { mSelection = selection; + mEmitterSetInspector->setSelection(selection); + mGeneralInspector->setSelection(selection); mGravityInspector->setSelection(selection); mTransformInspector->setSelection(selection); diff --git a/src/editor/mainWindow.cpp b/src/editor/mainWindow.cpp index 7fc5db5..1ee1f14 100644 --- a/src/editor/mainWindow.cpp +++ b/src/editor/mainWindow.cpp @@ -33,28 +33,10 @@ void MainWindow::setupUi() { // MainWindow setAcceptDrops(true); - // Properties - mPropertiesStack = new QStackedWidget(this); - mPropertiesStack->addWidget(&mEmitterSetWidget); - mPropertiesStack->addWidget(&mInspector); - mPropertiesStack->setCurrentIndex(0); - - mPropertiesGroup.setFlat(false); - auto* emitterGroupLayout = new QVBoxLayout(&mPropertiesGroup); - emitterGroupLayout->setContentsMargins(0, 0, 0, 0); - emitterGroupLayout->addWidget(mPropertiesStack); - - auto* propertiesContainer = new QWidget(this); - auto* propertiesLayout = new QVBoxLayout(propertiesContainer); - propertiesLayout->setContentsMargins(0, 0, 0, 0); - propertiesLayout->setSpacing(4); - - propertiesLayout->addWidget(&mPropertiesGroup); - // Top Splitter mTopSplitter = new QSplitter(Qt::Horizontal, this); mTopSplitter->addWidget(&mPtclList); - mTopSplitter->addWidget(propertiesContainer); + mTopSplitter->addWidget(&mInspector); mTopSplitter->setStretchFactor(0, 0); mTopSplitter->setStretchFactor(1, 1); @@ -80,10 +62,6 @@ void MainWindow::setupUi() { mInspector.setEnabled(false); mInspector.setSelection(&mSelection); - // EmitterSet Widget - mEmitterSetWidget.setEnabled(false); - mEmitterSetWidget.setSelection(&mSelection); - // Texture Widget mTextureWidget.setEnabled(false); @@ -106,35 +84,6 @@ void MainWindow::setupUi() { } void MainWindow::setupConnections() { - connect(&mSelection, &Ptcl::Selection::selectionChanged, this, [this](s32 setIndex, s32 emitterIndex, Ptcl::Selection::Type type) { - if (!mDocument) { - return; - } - - switch (type) { - case Ptcl::Selection::Type::EmitterSet: - setPropertiesView(PropertiesView::EmitterSet); - break; - case Ptcl::Selection::Type::Emitter: - setPropertiesView(PropertiesView::Emitter); - break; - case Ptcl::Selection::Type::EmitterChild: - setPropertiesView(PropertiesView::EmitterChild); - break; - case Ptcl::Selection::Type::EmitterFlux: - setPropertiesView(PropertiesView::EmitterFlux); - break; - case Ptcl::Selection::Type::EmitterField: - setPropertiesView(PropertiesView::EmitterField); - break; - case Ptcl::Selection::Type::None: - // TODO - break; - } - - updatePropertiesStatus(); - }); - connect(&mPtclList, &PtclList::itemAdded, this, [this]() { setDirty(true); }); @@ -142,16 +91,6 @@ void MainWindow::setupConnections() { connect(&mPtclList, &PtclList::itemRemoved, this, [this]() { setDirty(true); }); - - // EmitterSet Widget - connect(&mEmitterSetWidget, &EmitterSetWidget::emitterSetNamedChanged, this, [this]() { - mPtclList.updateEmitterSetName(mSelection.emitterSetIndex()); - updatePropertiesStatus(); - }); - - connect(&mEmitterSetWidget, &EmitterSetWidget::propertiesChanged, this, [this]() { - setDirty(true); - }); } void MainWindow::setupMenus() { @@ -388,7 +327,6 @@ void MainWindow::updateRecentFileList() { void MainWindow::loadPtclRes(const QString& path) { mPtclList.setDocument(nullptr); mInspector.setDocument(nullptr); - mEmitterSetWidget.setDocument(nullptr); mTextureWidget.setDocument(nullptr); mSaveAsAction.setEnabled(false); @@ -409,7 +347,6 @@ void MainWindow::loadPtclRes(const QString& path) { mPtclList.setDocument(mDocument.get()); mInspector.setDocument(mDocument.get()); - mEmitterSetWidget.setDocument(mDocument.get()); mTextureWidget.setDocument(mDocument.get()); if (mDocument->emitterSetCount() != 0) { @@ -422,61 +359,6 @@ void MainWindow::loadPtclRes(const QString& path) { mSaveAsAction.setEnabled(true); } -void MainWindow::setPropertiesView(PropertiesView view) { - if (view == mCurPropertiesView) { - return; - } - - mCurPropertiesView = view; - - switch (mCurPropertiesView) { - case PropertiesView::EmitterSet: - mPropertiesStack->setCurrentWidget(&mEmitterSetWidget); - break; - case PropertiesView::Emitter: - case PropertiesView::EmitterChild: - case PropertiesView::EmitterFlux: - case PropertiesView::EmitterField: - mPropertiesStack->setCurrentWidget(&mInspector); - break; - } -} - -void MainWindow::updatePropertiesStatus() { - mPropertiesGroup.setTitle({}); - - if (!mDocument) { - return; - } - - const auto& emitterSet = mDocument->emitterSet(mSelection.emitterSetIndex()); - const auto& emitter = mDocument->emitter(mSelection.emitterSetIndex(), mSelection.emitterIndex()); - - QString title = emitterSet->name(); - - if (mCurPropertiesView != PropertiesView::EmitterSet) { - title += QStringLiteral(" > %1").arg(emitter->name()); - } - - switch (mCurPropertiesView) { - case PropertiesView::EmitterChild: - title += QStringLiteral(" > Child Properties:"); - break; - case PropertiesView::EmitterFlux: - title += QStringLiteral(" > Fluctuation Properties:"); - break; - case PropertiesView::EmitterField: - title += QStringLiteral(" > Field Properties:"); - break; - case PropertiesView::Emitter: - case PropertiesView::EmitterSet: - title += QStringLiteral(" Properties:"); - break; - } - - mPropertiesGroup.setTitle(title); -} - void MainWindow::updateWindowTitle() { QString title = "PTCLTool"; if (mDocument && !mDocument->filePath().isEmpty()) { From 2c560dc7ba5e880a80cf11c6526cdc6191f32d9e Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Fri, 20 Mar 2026 01:59:37 +0000 Subject: [PATCH 28/35] feat(ui): implement undo/redo for adding/removing emitters --- include/ptcl/ptcl.h | 1 + include/ptcl/ptclCommand.h | 87 +++++++++++++++++++++++++++++++++++ include/ptcl/ptclDocument.h | 10 ++++ src/editor/ptclListWidget.cpp | 30 ++++++++++-- src/ptcl/ptcl.cpp | 4 ++ src/ptcl/ptclDocument.cpp | 8 ++++ 6 files changed, 135 insertions(+), 5 deletions(-) diff --git a/include/ptcl/ptcl.h b/include/ptcl/ptcl.h index 1ea0f81..51c3550 100644 --- a/include/ptcl/ptcl.h +++ b/include/ptcl/ptcl.h @@ -128,6 +128,7 @@ class PtclRes { const QString& name() const; void setName(const QString& name); + EmitterSetList& getEmitterSets(); const EmitterSetList& getEmitterSets() const; EmitterSet* emitterSet(s32 index); diff --git a/include/ptcl/ptclCommand.h b/include/ptcl/ptclCommand.h index 9e08686..5bda0c5 100644 --- a/include/ptcl/ptclCommand.h +++ b/include/ptcl/ptclCommand.h @@ -24,11 +24,15 @@ class DocumentCommandBase : public QUndoCommand { Emitter* emitter(s32 setIndex, s32 emitterIndex) { return mDocument->emitterMutable(setIndex, emitterIndex); } EmitterSet* emitterSet(s32 setIndex) { return mDocument->emitterSetMutable(setIndex); } + EmitterList& emitterList(s32 setIndex) { return mDocument->emitterSetMutable(setIndex)->emitters(); } + void setProjectName(const QString& newName) { mDocument->dataMutable().setName(newName); } void notifyEmitterChanged(s32 setIndex, s32 emitterIndex) { mDocument->notifyEmitterChanged(setIndex, emitterIndex); } void notifyEmitterSetChanged(s32 setIndex) { mDocument->notifyEmitterSetChanged(setIndex); } void notifyProjectChanged() { mDocument->notifyProjectChanged(); } + void notifyEmitterAdded(s32 setIndex, s32 emitterIndex) { mDocument->notifyEmitterAdded(setIndex, emitterIndex); } + void notifyEmitterRemoved(s32 setIndex, s32 emitterIndex) { mDocument->notifyEmitterRemoved(setIndex, emitterIndex); } private: Document* mDocument; @@ -256,8 +260,91 @@ class RenameProjectNameCommand final : public DocumentCommandBase { }; +// ========================================================================== // + + +class AddEmitterCommand final : public DocumentCommandBase { +public: + AddEmitterCommand(Document* doc, s32 setIndex, std::unique_ptr emitter = nullptr, QUndoCommand* parent = nullptr) : + DocumentCommandBase{doc, std::move("Add Emitter"), parent}, mSetIndex{setIndex} { + + if (emitter) { + mNewEmitter = std::move(emitter); + } else { + auto newEmitterPtr = std::make_unique(); + newEmitterPtr->setName("New_Emitter_" + QString::number(document()->emitterCount(mSetIndex))); + mNewEmitter = std::move(newEmitterPtr); + } + + } + + s32 id() const override { + return mId; + } + + void undo() override { + auto* set = emitterSet(mSetIndex); + mNewEmitter = std::move(set->emitters()[mEmitterIndex]); + set->removeEmitter(mEmitterIndex); + notifyEmitterRemoved(mSetIndex, mEmitterIndex); + } + + void redo() override { + auto* set = emitterSet(mSetIndex); + set->emitters().push_back(std::move(mNewEmitter)); + mEmitterIndex = set->emitterCount() - 1; + notifyEmitterAdded(mSetIndex, mEmitterIndex); + } + +private: + s32 mSetIndex{}; + s32 mEmitterIndex{0}; + std::unique_ptr mNewEmitter{}; + + const s32 mId{static_cast(qHash("AddEmitter"))}; +}; + // ========================================================================== // +class RemoveEmitterCommand final : public DocumentCommandBase { +public: + RemoveEmitterCommand(Document* doc, s32 setIndex, s32 emitterIndex, QUndoCommand* parent = nullptr) : + DocumentCommandBase{doc, std::move("Remove Emitter"), parent}, mSetIndex{setIndex}, mEmitterIndex{emitterIndex} { + + auto* set = emitterSet(mSetIndex); + if (mEmitterIndex < set->emitterCount()) { + mRemovedEmitter = std::move(set->emitters()[mEmitterIndex]); + } + } + + s32 id() const override { + return mId; + } + + void undo() override { + auto* set = emitterSet(mSetIndex); + set->emitters().insert(set->emitters().begin() + mEmitterIndex, std::move(mRemovedEmitter)); + notifyEmitterAdded(mSetIndex, mEmitterIndex); + } + + void redo() override { + auto* set = emitterSet(mSetIndex); + mRemovedEmitter = std::move(set->emitters()[mEmitterIndex]); + set->removeEmitter(mEmitterIndex); + notifyEmitterRemoved(mSetIndex, mEmitterIndex); + } + +private: + s32 mSetIndex{}; + s32 mEmitterIndex{0}; + std::unique_ptr mRemovedEmitter{}; + + const s32 mId{static_cast(qHash("RemoveEmitter"))}; +}; + + +// ========================================================================== // + } // namespace Ptcl diff --git a/include/ptcl/ptclDocument.h b/include/ptcl/ptclDocument.h index dc631bb..1c20adc 100644 --- a/include/ptcl/ptclDocument.h +++ b/include/ptcl/ptclDocument.h @@ -99,15 +99,22 @@ class Document final : public QObject { s32 emitterCount(s32 setIndex) const { return mData.emitterCount(setIndex); } s32 emitterSetCount() const { return mData.emitterSetCount(); } + void addEmitter(s32 setIndex, std::unique_ptr emitter = nullptr); + void removeEmitter(s32 setIndex, s32 emitterIndex); + private: TextureList& texturesMutable() { return mData.textures(); } Emitter* emitterMutable(s32 setIndex, s32 emitterIndex) { return mData.emitter(setIndex, emitterIndex); } EmitterSet* emitterSetMutable(s32 index) { return mData.emitterSet(index); } + EmitterSetList& emitterSetsMutable() { return mData.getEmitterSets(); } + PtclRes& dataMutable() { return mData; } void notifyEmitterChanged(s32 setIndex, s32 emitterIndex) { emit emitterChanged(setIndex, emitterIndex); } void notifyEmitterSetChanged(s32 setIndex) { emit emitterSetChanged(setIndex); } void notifyProjectChanged() { emit projectChanged(); } + void notifyEmitterAdded(s32 setIndex, s32 emitterIndex) { emit emitterAdded(setIndex, emitterIndex); } + void notifyEmitterRemoved(s32 setIndex, s32 emitterIndex) { emit emitterRemoved(setIndex, emitterIndex); } friend class DocumentCommandBase; @@ -116,6 +123,9 @@ class Document final : public QObject { void emitterSetChanged(s32 setIndex); void projectChanged(); + void emitterAdded(s32 setIndex, s32 emitterIndex); + void emitterRemoved(s32 setIndex, s32 emitterIndex); + private: PtclRes mData{}; QString mFilePath{}; diff --git a/src/editor/ptclListWidget.cpp b/src/editor/ptclListWidget.cpp index be8ce86..811afe6 100644 --- a/src/editor/ptclListWidget.cpp +++ b/src/editor/ptclListWidget.cpp @@ -224,8 +224,8 @@ void PtclList::setupFilterMenu() { } void PtclList::setDocument(Ptcl::Document* document) { - if (mDocument == document) { - return; + if (mDocument) { + mDocument->disconnect(this); } mDocument = document; @@ -236,6 +236,26 @@ void PtclList::setDocument(Ptcl::Document* document) { return; } + connect(mDocument, &Ptcl::Document::emitterAdded, this, [this](s32 setIndex, s32 emitterIndex) { + QStandardItem* setItem = mListModel.item(setIndex); + if (!setItem) { + return; + } + + insertEmitterNode(setItem, setIndex, emitterIndex); + reindexEmitters(setItem, setIndex); + }); + + connect(mDocument, &Ptcl::Document::emitterRemoved, this, [this](s32 setIndex, s32 emitterIndex) { + QStandardItem* setItem = mListModel.item(setIndex); + if (!setItem) { + return; + } + + setItem->removeRow(emitterIndex); + reindexEmitters(setItem, setIndex); + }); + mListModel.clear(); populateList(); setEnabled(true); @@ -601,9 +621,8 @@ void PtclList::addEmitter() { const auto& emitterSet = mDocument->emitterSet(setIndex); const s32 emitterIndex = emitterSet->emitterCount(); - emitterSet->addNewEmitter(); + mDocument->addEmitter(setIndex); - insertEmitterNode(setItem, setIndex, emitterIndex); mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::Emitter); expandSourceIndex(mListModel.indexFromItem(setItem)); emit itemAdded(); @@ -645,7 +664,8 @@ void PtclList::removeEmitter(QStandardItem* setItem, QStandardItem* emitterItem) return; } - emitterSet->removeEmitter(emitterIndex); + mDocument->removeEmitter(setIndex, emitterIndex); + setItem->removeRow(emitterIndex); reindexEmitters(setItem, setIndex); diff --git a/src/ptcl/ptcl.cpp b/src/ptcl/ptcl.cpp index 9d514b3..b07ab7a 100644 --- a/src/ptcl/ptcl.cpp +++ b/src/ptcl/ptcl.cpp @@ -480,6 +480,10 @@ void PtclRes::setName(const QString& name) { mName = name; } +EmitterSetList& PtclRes::getEmitterSets() { + return mEmitterSets; +} + const EmitterSetList& PtclRes::getEmitterSets() const { return mEmitterSets; } diff --git a/src/ptcl/ptclDocument.cpp b/src/ptcl/ptclDocument.cpp index b5f8560..6996ee0 100644 --- a/src/ptcl/ptclDocument.cpp +++ b/src/ptcl/ptclDocument.cpp @@ -41,6 +41,14 @@ void Document::setProjectName(const QString& name) { mUndoStack.push(new RenameProjectNameCommand(this, name)); } +void Document::addEmitter(s32 setIndex, std::unique_ptr emitter) { + mUndoStack.push(new AddEmitterCommand(this, setIndex, std::move(emitter))); +} + +void Document::removeEmitter(s32 setIndex, s32 emitterIndex) { + mUndoStack.push(new RemoveEmitterCommand(this, setIndex, emitterIndex)); +} + // ========================================================================== // From 98e5fead77002cc8e5885c5cef8dd348bdc71c4d Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Fri, 20 Mar 2026 02:41:12 +0000 Subject: [PATCH 29/35] refactor: rework undo/redo for adding/removing emitters --- include/ptcl/ptclCommand.h | 17 +++++------------ include/ptcl/ptclEmitterSet.h | 5 ++--- src/editor/ptclListWidget.cpp | 3 --- src/ptcl/ptcl.cpp | 2 +- src/ptcl/ptclEmitterSet.cpp | 16 +++++++--------- 5 files changed, 15 insertions(+), 28 deletions(-) diff --git a/include/ptcl/ptclCommand.h b/include/ptcl/ptclCommand.h index 5bda0c5..a61ec8d 100644 --- a/include/ptcl/ptclCommand.h +++ b/include/ptcl/ptclCommand.h @@ -284,15 +284,14 @@ class AddEmitterCommand final : public DocumentCommandBase { void undo() override { auto* set = emitterSet(mSetIndex); - mNewEmitter = std::move(set->emitters()[mEmitterIndex]); - set->removeEmitter(mEmitterIndex); + mNewEmitter = set->removeEmitter(mEmitterIndex); notifyEmitterRemoved(mSetIndex, mEmitterIndex); } void redo() override { auto* set = emitterSet(mSetIndex); - set->emitters().push_back(std::move(mNewEmitter)); - mEmitterIndex = set->emitterCount() - 1; + mEmitterIndex = set->emitterCount(); + set->insertEmitter(mEmitterIndex, std::move(mNewEmitter)); notifyEmitterAdded(mSetIndex, mEmitterIndex); } @@ -312,11 +311,6 @@ class RemoveEmitterCommand final : public DocumentCommandBase { public: RemoveEmitterCommand(Document* doc, s32 setIndex, s32 emitterIndex, QUndoCommand* parent = nullptr) : DocumentCommandBase{doc, std::move("Remove Emitter"), parent}, mSetIndex{setIndex}, mEmitterIndex{emitterIndex} { - - auto* set = emitterSet(mSetIndex); - if (mEmitterIndex < set->emitterCount()) { - mRemovedEmitter = std::move(set->emitters()[mEmitterIndex]); - } } s32 id() const override { @@ -325,14 +319,13 @@ class RemoveEmitterCommand final : public DocumentCommandBase { void undo() override { auto* set = emitterSet(mSetIndex); - set->emitters().insert(set->emitters().begin() + mEmitterIndex, std::move(mRemovedEmitter)); + set->insertEmitter(mEmitterIndex, std::move(mRemovedEmitter)); notifyEmitterAdded(mSetIndex, mEmitterIndex); } void redo() override { auto* set = emitterSet(mSetIndex); - mRemovedEmitter = std::move(set->emitters()[mEmitterIndex]); - set->removeEmitter(mEmitterIndex); + mRemovedEmitter = set->removeEmitter(mEmitterIndex); notifyEmitterRemoved(mSetIndex, mEmitterIndex); } diff --git a/include/ptcl/ptclEmitterSet.h b/include/ptcl/ptclEmitterSet.h index aeb926f..1f4d9f1 100644 --- a/include/ptcl/ptclEmitterSet.h +++ b/include/ptcl/ptclEmitterSet.h @@ -38,11 +38,10 @@ class EmitterSet u32 userData() const; void setUserData(u32 data); - void addNewEmitter(); - void removeEmitter(s32 emitterIndex); + void insertEmitter(s32 emitterIndex, std::unique_ptr emitter); + std::unique_ptr removeEmitter(s32 emitterIndex); std::unique_ptr clone() const; - const std::unique_ptr& appendEmitter(std::unique_ptr& newEmitter); private: diff --git a/src/editor/ptclListWidget.cpp b/src/editor/ptclListWidget.cpp index 811afe6..a967411 100644 --- a/src/editor/ptclListWidget.cpp +++ b/src/editor/ptclListWidget.cpp @@ -666,9 +666,6 @@ void PtclList::removeEmitter(QStandardItem* setItem, QStandardItem* emitterItem) mDocument->removeEmitter(setIndex, emitterIndex); - setItem->removeRow(emitterIndex); - reindexEmitters(setItem, setIndex); - const s32 remainingCount = setItem->rowCount(); if (remainingCount > 0) { const s32 nextIndex = std::min(emitterIndex, remainingCount - 1); diff --git a/src/ptcl/ptcl.cpp b/src/ptcl/ptcl.cpp index b07ab7a..4dfc342 100644 --- a/src/ptcl/ptcl.cpp +++ b/src/ptcl/ptcl.cpp @@ -541,7 +541,7 @@ TextureList& PtclRes::textures() { void PtclRes::addNewEmitterSet() { auto newSet = std::make_unique(); newSet->setName("New_EmitterSet_" + QString::number(emitterSetCount())); - newSet->addNewEmitter(); + newSet->addEmitter(); mEmitterSets.push_back(std::move(newSet)); } diff --git a/src/ptcl/ptclEmitterSet.cpp b/src/ptcl/ptclEmitterSet.cpp index 1758cb0..0982c83 100644 --- a/src/ptcl/ptclEmitterSet.cpp +++ b/src/ptcl/ptclEmitterSet.cpp @@ -41,18 +41,16 @@ void EmitterSet::setUserData(u32 data) { mUserData = data; } -void EmitterSet::addNewEmitter() { - auto newEmitter = std::make_unique(); - newEmitter->setName("New_Emitter_" + QString::number(emitterCount())); - mEmitters.push_back(std::move(newEmitter)); +void EmitterSet::insertEmitter(s32 emitterIndex, std::unique_ptr emitter) { + mEmitters.insert(mEmitters.begin() + emitterIndex, std::move(emitter)); } -void EmitterSet::removeEmitter(s32 emitterIndex) { - if (emitterIndex >= mEmitters.size()) { - return; - } +std::unique_ptr EmitterSet::removeEmitter(s32 emitterIndex) { + auto it = mEmitters.begin() + emitterIndex; - mEmitters.erase(mEmitters.begin() + emitterIndex); + std::unique_ptr removed = std::move(*it); + mEmitters.erase(it); + return removed; } std::unique_ptr EmitterSet::clone() const { From ca08a3e92af29dfbe85f9dc2f77582d78f464831 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Fri, 20 Mar 2026 21:48:19 +0000 Subject: [PATCH 30/35] feat(ui): implement undo/redo for adding/removing emitterSets --- include/ptcl/ptcl.h | 4 +- include/ptcl/ptclCommand.h | 93 ++++++++++++++++++++++++++++++++++- include/ptcl/ptclDocument.h | 10 ++++ include/ptcl/ptclEmitter.h | 1 + include/ptcl/ptclEmitterSet.h | 9 ++-- src/editor/ptclListWidget.cpp | 18 ++++--- src/ptcl/ptcl.cpp | 17 +++---- src/ptcl/ptclDocument.cpp | 7 +++ src/ptcl/ptclEmitter.cpp | 3 ++ src/ptcl/ptclEmitterSet.cpp | 6 +++ 10 files changed, 144 insertions(+), 24 deletions(-) diff --git a/include/ptcl/ptcl.h b/include/ptcl/ptcl.h index 51c3550..168c335 100644 --- a/include/ptcl/ptcl.h +++ b/include/ptcl/ptcl.h @@ -140,8 +140,8 @@ class PtclRes { const TextureList& textures() const; TextureList& textures(); - void addNewEmitterSet(); - void removeEmitterSet(s32 setIndex); + void insertEmitterSet(s32 setIndex, std::unique_ptr emitterSet); + std::unique_ptr removeEmitterSet(s32 setIndex); s32 emitterSetCount() const; s32 emitterCount(s32 setIndex) const; diff --git a/include/ptcl/ptclCommand.h b/include/ptcl/ptclCommand.h index a61ec8d..d1e79c4 100644 --- a/include/ptcl/ptclCommand.h +++ b/include/ptcl/ptclCommand.h @@ -21,6 +21,8 @@ class DocumentCommandBase : public QUndoCommand { protected: Document* document() const { return mDocument; } + PtclRes& resource() { return mDocument->dataMutable(); } + Emitter* emitter(s32 setIndex, s32 emitterIndex) { return mDocument->emitterMutable(setIndex, emitterIndex); } EmitterSet* emitterSet(s32 setIndex) { return mDocument->emitterSetMutable(setIndex); } @@ -31,9 +33,13 @@ class DocumentCommandBase : public QUndoCommand { void notifyEmitterChanged(s32 setIndex, s32 emitterIndex) { mDocument->notifyEmitterChanged(setIndex, emitterIndex); } void notifyEmitterSetChanged(s32 setIndex) { mDocument->notifyEmitterSetChanged(setIndex); } void notifyProjectChanged() { mDocument->notifyProjectChanged(); } + void notifyEmitterAdded(s32 setIndex, s32 emitterIndex) { mDocument->notifyEmitterAdded(setIndex, emitterIndex); } void notifyEmitterRemoved(s32 setIndex, s32 emitterIndex) { mDocument->notifyEmitterRemoved(setIndex, emitterIndex); } + void notifyEmitterSetAdded(s32 setIndex) { mDocument->notifyEmitterSetAdded(setIndex); } + void notifyEmitterSetRemoved(s32 setIndex) { mDocument->notifyEmitterSetRemoved(setIndex); } + private: Document* mDocument; }; @@ -271,8 +277,7 @@ class AddEmitterCommand final : public DocumentCommandBase { if (emitter) { mNewEmitter = std::move(emitter); } else { - auto newEmitterPtr = std::make_unique(); - newEmitterPtr->setName("New_Emitter_" + QString::number(document()->emitterCount(mSetIndex))); + auto newEmitterPtr = std::make_unique("New_Emitter_" + QString::number(document()->emitterCount(mSetIndex))); mNewEmitter = std::move(newEmitterPtr); } @@ -340,4 +345,88 @@ class RemoveEmitterCommand final : public DocumentCommandBase { // ========================================================================== // + +class AddEmitterSetCommand final : public DocumentCommandBase { +public: + AddEmitterSetCommand(Document* doc, std::unique_ptr emitterSet = nullptr, QUndoCommand* parent = nullptr) : + DocumentCommandBase{doc, std::move("Add EmitterSet"), parent} { + + if (emitterSet) { + mNewEmitterSet = std::move(emitterSet); + } else { + auto newEmitterSetPtr = std::make_unique("New_EmitterSet_" + QString::number(document()->emitterSetCount())); + mNewEmitterSet = std::move(newEmitterSetPtr); + } + } + + s32 id() const override { + return mId; + } + + void undo() override { + auto removed = resource().removeEmitterSet(mSetIndex); + if (!removed) { + Q_ASSERT(false && "removeEmitterSet returned null"); + return; + } + + mNewEmitterSet = std::move(removed); + notifyEmitterSetRemoved(mSetIndex); + + } + + void redo() override { + mSetIndex = resource().emitterSetCount(); + resource().insertEmitterSet(mSetIndex, std::move(mNewEmitterSet)); + notifyEmitterSetAdded(mSetIndex); + } + +private: + s32 mSetIndex{0}; + std::unique_ptr mNewEmitterSet{}; + + const s32 mId{static_cast(qHash("AddEmitterSet"))}; +}; + + +// ========================================================================== // + + +class RemoveEmitterSetCommand final : public DocumentCommandBase { +public: + RemoveEmitterSetCommand(Document* doc, s32 setIndex, QUndoCommand* parent = nullptr) : + DocumentCommandBase{doc, std::move("Remove EmitterSet"), parent}, mSetIndex{setIndex} { + } + + s32 id() const override { + return mId; + } + + void undo() override { + resource().insertEmitterSet(mSetIndex, std::move(mRemovedEmitterSet)); + notifyEmitterSetAdded(mSetIndex); + } + + void redo() override { + auto removed = resource().removeEmitterSet(mSetIndex); + if (!removed) { + Q_ASSERT(false && "removeEmitterSet returned null"); + return; + } + + mRemovedEmitterSet = std::move(removed); + notifyEmitterSetRemoved(mSetIndex); + } + +private: + s32 mSetIndex{0}; + std::unique_ptr mRemovedEmitterSet{}; + + const s32 mId{static_cast(qHash("RemoveEmitterSet"))}; +}; + + +// ========================================================================== // + + } // namespace Ptcl diff --git a/include/ptcl/ptclDocument.h b/include/ptcl/ptclDocument.h index 1c20adc..698c68c 100644 --- a/include/ptcl/ptclDocument.h +++ b/include/ptcl/ptclDocument.h @@ -102,6 +102,9 @@ class Document final : public QObject { void addEmitter(s32 setIndex, std::unique_ptr emitter = nullptr); void removeEmitter(s32 setIndex, s32 emitterIndex); + void addEmitterSet(std::unique_ptr emitterSet = nullptr); + void removeEmitterSet(s32 setIndex); + private: TextureList& texturesMutable() { return mData.textures(); } Emitter* emitterMutable(s32 setIndex, s32 emitterIndex) { return mData.emitter(setIndex, emitterIndex); } @@ -113,9 +116,13 @@ class Document final : public QObject { void notifyEmitterChanged(s32 setIndex, s32 emitterIndex) { emit emitterChanged(setIndex, emitterIndex); } void notifyEmitterSetChanged(s32 setIndex) { emit emitterSetChanged(setIndex); } void notifyProjectChanged() { emit projectChanged(); } + void notifyEmitterAdded(s32 setIndex, s32 emitterIndex) { emit emitterAdded(setIndex, emitterIndex); } void notifyEmitterRemoved(s32 setIndex, s32 emitterIndex) { emit emitterRemoved(setIndex, emitterIndex); } + void notifyEmitterSetAdded(s32 setIndex) { emit emitterSetAdded(setIndex); } + void notifyEmitterSetRemoved(s32 setIndex) { emit emitterSetRemoved(setIndex); } + friend class DocumentCommandBase; signals: @@ -126,6 +133,9 @@ class Document final : public QObject { void emitterAdded(s32 setIndex, s32 emitterIndex); void emitterRemoved(s32 setIndex, s32 emitterIndex); + void emitterSetAdded(s32 setIndex); + void emitterSetRemoved(s32 setIndex); + private: PtclRes mData{}; QString mFilePath{}; diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index b13ebc5..c6fb4f3 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -48,6 +48,7 @@ class Emitter { public: Emitter() = default; + Emitter(QString name); Emitter(const Emitter&) = delete; Emitter& operator=(const Emitter&) = delete; diff --git a/include/ptcl/ptclEmitterSet.h b/include/ptcl/ptclEmitterSet.h index 1f4d9f1..bc02bbb 100644 --- a/include/ptcl/ptclEmitterSet.h +++ b/include/ptcl/ptclEmitterSet.h @@ -23,6 +23,7 @@ class EmitterSet { public: EmitterSet() = default; + EmitterSet(QString emitterName); EmitterSet(const BinEmitterSet& binEmitterSet); EmitterList& emitters(); @@ -45,11 +46,11 @@ class EmitterSet const std::unique_ptr& appendEmitter(std::unique_ptr& newEmitter); private: - QString mName; - EmitterList mEmitters; + QString mName{}; + EmitterList mEmitters{}; - u32 mUserData; - u32 mLastUpdateDate; // TODO: check this + u32 mUserData{0}; + u32 mLastUpdateDate{0}; // TODO: check this }; diff --git a/src/editor/ptclListWidget.cpp b/src/editor/ptclListWidget.cpp index a967411..60c4c1f 100644 --- a/src/editor/ptclListWidget.cpp +++ b/src/editor/ptclListWidget.cpp @@ -256,6 +256,16 @@ void PtclList::setDocument(Ptcl::Document* document) { reindexEmitters(setItem, setIndex); }); + connect(mDocument, &Ptcl::Document::emitterSetAdded, this, [this](s32 setIndex) { + insertEmitterSetNode(setIndex); + reindexEmitterSets(); + }); + + connect(mDocument, &Ptcl::Document::emitterSetRemoved, this, [this](s32 setIndex) { + mListModel.removeRow(setIndex); + reindexEmitterSets(); + }); + mListModel.clear(); populateList(); setEnabled(true); @@ -594,9 +604,8 @@ void PtclList::addEmitterSet() { } const s32 setIndex = mDocument->emitterSetCount(); - mDocument->data().addNewEmitterSet(); + mDocument->addEmitterSet(); - insertEmitterSetNode(setIndex); mSelection->set(setIndex, 0, Ptcl::Selection::Type::EmitterSet); expandSourceIndex(mListModel.index(setIndex, 0)); emit itemAdded(); @@ -622,7 +631,6 @@ void PtclList::addEmitter() { const s32 emitterIndex = emitterSet->emitterCount(); mDocument->addEmitter(setIndex); - mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::Emitter); expandSourceIndex(mListModel.indexFromItem(setItem)); emit itemAdded(); @@ -686,9 +694,7 @@ void PtclList::removeEmitterSet(QStandardItem* setItem) { return; } - mDocument->data().removeEmitterSet(setIndex); - mListModel.removeRow(setIndex); - reindexEmitterSets(); + mDocument->removeEmitterSet(setIndex); const s32 remainingCount = mListModel.rowCount(); diff --git a/src/ptcl/ptcl.cpp b/src/ptcl/ptcl.cpp index 4dfc342..44eff11 100644 --- a/src/ptcl/ptcl.cpp +++ b/src/ptcl/ptcl.cpp @@ -538,19 +538,16 @@ TextureList& PtclRes::textures() { return mTextures; } -void PtclRes::addNewEmitterSet() { - auto newSet = std::make_unique(); - newSet->setName("New_EmitterSet_" + QString::number(emitterSetCount())); - newSet->addEmitter(); - mEmitterSets.push_back(std::move(newSet)); +void PtclRes::insertEmitterSet(s32 setIndex, std::unique_ptr emitterSet) { + mEmitterSets.insert(mEmitterSets.begin() + setIndex, std::move(emitterSet)); } -void PtclRes::removeEmitterSet(s32 setIndex) { - if (setIndex >= mEmitterSets.size()) { - return; - } +std::unique_ptr PtclRes::removeEmitterSet(s32 setIndex) { + auto it = mEmitterSets.begin() + setIndex; - mEmitterSets.erase(mEmitterSets.begin() + setIndex); + std::unique_ptr removed = std::move(*it); + mEmitterSets.erase(it); + return removed; } const std::unique_ptr& PtclRes::appendEmitterSet(std::unique_ptr& newSet) { diff --git a/src/ptcl/ptclDocument.cpp b/src/ptcl/ptclDocument.cpp index 6996ee0..ed1954c 100644 --- a/src/ptcl/ptclDocument.cpp +++ b/src/ptcl/ptclDocument.cpp @@ -49,6 +49,13 @@ void Document::removeEmitter(s32 setIndex, s32 emitterIndex) { mUndoStack.push(new RemoveEmitterCommand(this, setIndex, emitterIndex)); } +void Document::addEmitterSet(std::unique_ptr emitterSet) { + mUndoStack.push(new AddEmitterSetCommand(this, std::move(emitterSet))); +} + +void Document::removeEmitterSet(s32 setIndex) { + mUndoStack.push(new RemoveEmitterSetCommand(this, setIndex)); +} // ========================================================================== // diff --git a/src/ptcl/ptclEmitter.cpp b/src/ptcl/ptclEmitter.cpp index 00e60af..043a4d9 100644 --- a/src/ptcl/ptclEmitter.cpp +++ b/src/ptcl/ptclEmitter.cpp @@ -6,6 +6,9 @@ namespace Ptcl { // ========================================================================== // +Emitter::Emitter(QString name) : + mName{std::move(name)} {} + std::unique_ptr Emitter::clone() const { auto newEmitter = std::make_unique(); diff --git a/src/ptcl/ptclEmitterSet.cpp b/src/ptcl/ptclEmitterSet.cpp index 0982c83..3ec77fa 100644 --- a/src/ptcl/ptclEmitterSet.cpp +++ b/src/ptcl/ptclEmitterSet.cpp @@ -6,6 +6,12 @@ namespace Ptcl { // ========================================================================== // +EmitterSet::EmitterSet(QString name) : + mName{std::move(name)} { + auto emitter = std::make_unique("New_Emitter_0"); + insertEmitter(0, std::move(emitter)); +} + EmitterSet::EmitterSet(const BinEmitterSet& binEmitterSet) {} From 97f967cf58e933477bac8325969a6422e5dae5f1 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Fri, 20 Mar 2026 22:21:08 +0000 Subject: [PATCH 31/35] fix: prevent possible invalid state when removing emitters/emitterSets --- include/editor/ptclListWidget.h | 4 +- src/editor/ptclListWidget.cpp | 113 +++++++++++++++++--------------- 2 files changed, 62 insertions(+), 55 deletions(-) diff --git a/include/editor/ptclListWidget.h b/include/editor/ptclListWidget.h index 89c4a55..8135fde 100644 --- a/include/editor/ptclListWidget.h +++ b/include/editor/ptclListWidget.h @@ -83,7 +83,6 @@ class PtclList : public QWidget { void itemRemoved(); private slots: - void selectionChanged(const QItemSelection& selection); void filterList(const QString& text); private: @@ -111,6 +110,9 @@ private slots: void reindexEmitters(QStandardItem* setItem, s32 setIndex); void reindexEmitterSets(); + void selectNearestValidEmitter(s32 setIndex, s32 preferredEmitter); + void selectNearestValidEmitterSet(s32 preferredSet); + void expandSourceIndex(const QModelIndex& sourceIndex); void copyItem(); diff --git a/src/editor/ptclListWidget.cpp b/src/editor/ptclListWidget.cpp index 60c4c1f..889339f 100644 --- a/src/editor/ptclListWidget.cpp +++ b/src/editor/ptclListWidget.cpp @@ -172,7 +172,41 @@ PtclList::PtclList(QWidget* parent) : // Tree View mTreeView.setModel(&mProxyModel); mTreeView.setHeaderHidden(true); - connect(mTreeView.selectionModel(), &QItemSelectionModel::selectionChanged, this, &PtclList::selectionChanged); + connect(&mTreeView, &QTreeView::clicked, this, [this](const QModelIndex& proxyIndex) { + const QModelIndex sourceIndex = mProxyModel.mapToSource(proxyIndex); + QStandardItem* item = mListModel.itemFromIndex(sourceIndex); + + if (!item || !mSelection || !mDocument) { + return; + } + + updateToolbarForSelection(item); + + const auto type = static_cast(item->data(sRoleNodeType).toUInt()); + + const s32 setIndex = item->data(sRoleSetIdx).toInt(); + const s32 emitterIndex = item->data(sRoleEmitterIdx).toInt(); + + switch (type) { + case NodeType::EmitterSet: + mSelection->set(setIndex, 0, Ptcl::Selection::Type::EmitterSet); + break; + case NodeType::Emitter: + mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::Emitter); + break; + case NodeType::ChildData: + mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::EmitterChild); + break; + case NodeType::Fluctuation: + mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::EmitterFlux); + break; + case NodeType::Field: + mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::EmitterField); + break; + default: + break; + } + }); // Search Layout auto* searchLayout = new QHBoxLayout; @@ -296,7 +330,6 @@ void PtclList::setSelection(Ptcl::Selection* selection) { } auto* selectionModel = mTreeView.selectionModel(); - selectionModel->clearSelection(); selectionModel->setCurrentIndex(proxyIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); mTreeView.scrollTo(proxyIndex); }); @@ -361,54 +394,35 @@ void PtclList::filterList(const QString& text) { mProxyModel.setFilterFixedString(text); } -void PtclList::selectionChanged(const QItemSelection& selection) { - if (selection.indexes().isEmpty()) { +void PtclList::selectNearestValidEmitter(s32 setIndex, s32 prefferedEmitter) { + if (!mDocument || !mSelection) { return; } - QModelIndex proxyIndex = selection.indexes().first(); - QModelIndex sourceIndex = mProxyModel.mapToSource(proxyIndex); - QStandardItem* item = mListModel.itemFromIndex(sourceIndex); - - updateToolbarForSelection(item); + const auto& set = mDocument->emitterSet(setIndex); + const s32 count = set ? set->emitterCount() : 0; - if (!item) { + if (count <= 0) { + mSelection->set(setIndex, 0, Ptcl::Selection::Type::EmitterSet); return; } - auto type = static_cast(item->data(sRoleNodeType).toUInt()); + const s32 clamped = std::clamp(prefferedEmitter, 0, count - 1); + mSelection->set(setIndex, clamped, Ptcl::Selection::Type::Emitter); +} - switch (type) { - case NodeType::EmitterSet: { - const s32 setIndex = sourceIndex.row(); - mSelection->set(setIndex, 0, Ptcl::Selection::Type::EmitterSet); - break; - } - case NodeType::Emitter: { - const s32 emitterIndex = sourceIndex.row(); - const s32 setIndex = sourceIndex.parent().row(); - mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::Emitter); - break; - } - case NodeType::ChildData: { - const s32 emitterIndex = sourceIndex.parent().row(); - const s32 setIndex = sourceIndex.parent().parent().row(); - mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::EmitterChild); - break; - } - case NodeType::Fluctuation: { - const s32 emitterIndex = sourceIndex.parent().row(); - const s32 setIndex = sourceIndex.parent().parent().row(); - mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::EmitterFlux); - break; - } - case NodeType::Field: { - const s32 emitterIndex = sourceIndex.parent().row(); - const s32 setIndex = sourceIndex.parent().parent().row(); - mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::EmitterField); - break; - } +void PtclList::selectNearestValidEmitterSet(s32 prefferedEmitterSet) { + if (!mDocument || !mSelection) { + return; + } + const s32 count = mDocument->emitterSetCount(); + + if (count <= 0) { + return; } + + const s32 clamped = std::clamp(prefferedEmitterSet, 0, count - 1); + mSelection->set(clamped, 0, Ptcl::Selection::Type::EmitterSet); } void PtclList::addComplexNodes(QStandardItem* emitterItem, s32 setIndex, s32 emitterIndex) { @@ -672,13 +686,9 @@ void PtclList::removeEmitter(QStandardItem* setItem, QStandardItem* emitterItem) return; } + const s32 nextPreferred = emitterIndex; mDocument->removeEmitter(setIndex, emitterIndex); - - const s32 remainingCount = setItem->rowCount(); - if (remainingCount > 0) { - const s32 nextIndex = std::min(emitterIndex, remainingCount - 1); - mSelection->set(setIndex, nextIndex, Ptcl::Selection::Type::Emitter); - } + selectNearestValidEmitter(setIndex, nextPreferred); } void PtclList::removeEmitterSet(QStandardItem* setItem) { @@ -694,14 +704,9 @@ void PtclList::removeEmitterSet(QStandardItem* setItem) { return; } + const s32 nextPreferred = setIndex; mDocument->removeEmitterSet(setIndex); - - const s32 remainingCount = mListModel.rowCount(); - - if (remainingCount > 0) { - const s32 nextIndex = std::min(setIndex, remainingCount - 1); - mSelection->set(nextIndex, 0, Ptcl::Selection::Type::EmitterSet); - } + selectNearestValidEmitterSet(nextPreferred); } void PtclList::reindexEmitters(QStandardItem* setItem, s32 setIndex) { From 522354385989a6f65c9b6776733a0ec36db2de3c Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Fri, 20 Mar 2026 22:41:51 +0000 Subject: [PATCH 32/35] feat(ui): implement undo/redo for copy/paste --- include/ptcl/ptclCommand.h | 8 ++++---- include/ptcl/ptclDocument.h | 8 ++------ src/editor/ptclListWidget.cpp | 19 +++++++------------ src/ptcl/ptclDocument.cpp | 8 ++++---- 4 files changed, 17 insertions(+), 26 deletions(-) diff --git a/include/ptcl/ptclCommand.h b/include/ptcl/ptclCommand.h index d1e79c4..129e38f 100644 --- a/include/ptcl/ptclCommand.h +++ b/include/ptcl/ptclCommand.h @@ -271,8 +271,8 @@ class RenameProjectNameCommand final : public DocumentCommandBase { class AddEmitterCommand final : public DocumentCommandBase { public: - AddEmitterCommand(Document* doc, s32 setIndex, std::unique_ptr emitter = nullptr, QUndoCommand* parent = nullptr) : - DocumentCommandBase{doc, std::move("Add Emitter"), parent}, mSetIndex{setIndex} { + AddEmitterCommand(Document* doc, s32 setIndex, QString label, std::unique_ptr emitter = nullptr, QUndoCommand* parent = nullptr) : + DocumentCommandBase{doc, std::move(label), parent}, mSetIndex{setIndex} { if (emitter) { mNewEmitter = std::move(emitter); @@ -348,8 +348,8 @@ class RemoveEmitterCommand final : public DocumentCommandBase { class AddEmitterSetCommand final : public DocumentCommandBase { public: - AddEmitterSetCommand(Document* doc, std::unique_ptr emitterSet = nullptr, QUndoCommand* parent = nullptr) : - DocumentCommandBase{doc, std::move("Add EmitterSet"), parent} { + AddEmitterSetCommand(Document* doc, QString label, std::unique_ptr emitterSet = nullptr, QUndoCommand* parent = nullptr) : + DocumentCommandBase{doc, std::move(label), parent} { if (emitterSet) { mNewEmitterSet = std::move(emitterSet); diff --git a/include/ptcl/ptclDocument.h b/include/ptcl/ptclDocument.h index 698c68c..7ade9cd 100644 --- a/include/ptcl/ptclDocument.h +++ b/include/ptcl/ptclDocument.h @@ -62,12 +62,8 @@ class Document final : public QObject { public: explicit Document(QObject* parent = nullptr); - // TODO: Remove this - EmitterSet* emitterSet(s32 index) { return mData.emitterSet(index); } // TODO: Remove this TextureList& textures() { return mData.textures(); } - // TODO: Remove this - PtclRes& data() { return mData; } bool load(const QString& filePath); bool save(const QString& filePath); @@ -99,10 +95,10 @@ class Document final : public QObject { s32 emitterCount(s32 setIndex) const { return mData.emitterCount(setIndex); } s32 emitterSetCount() const { return mData.emitterSetCount(); } - void addEmitter(s32 setIndex, std::unique_ptr emitter = nullptr); + void addEmitter(QString label, s32 setIndex, std::unique_ptr emitter = nullptr); void removeEmitter(s32 setIndex, s32 emitterIndex); - void addEmitterSet(std::unique_ptr emitterSet = nullptr); + void addEmitterSet(QString label, std::unique_ptr emitterSet = nullptr); void removeEmitterSet(s32 setIndex); private: diff --git a/src/editor/ptclListWidget.cpp b/src/editor/ptclListWidget.cpp index 889339f..99fb5ef 100644 --- a/src/editor/ptclListWidget.cpp +++ b/src/editor/ptclListWidget.cpp @@ -365,7 +365,7 @@ void PtclList::insertEmitterSetNode(s32 setIndex) { setItem->setIcon(QIcon(":/res/icons/emitterset.png")); // Emitters - for (s32 emitterIndex = 0; emitterIndex < set->emitters().size(); ++emitterIndex) { + for (s32 emitterIndex = 0; emitterIndex < mDocument->emitterCount(setIndex); ++emitterIndex) { insertEmitterNode(setItem, setIndex, emitterIndex); } mListModel.appendRow(setItem); @@ -618,7 +618,7 @@ void PtclList::addEmitterSet() { } const s32 setIndex = mDocument->emitterSetCount(); - mDocument->addEmitterSet(); + mDocument->addEmitterSet("Add New EmitterSet"); mSelection->set(setIndex, 0, Ptcl::Selection::Type::EmitterSet); expandSourceIndex(mListModel.index(setIndex, 0)); @@ -644,7 +644,7 @@ void PtclList::addEmitter() { const auto& emitterSet = mDocument->emitterSet(setIndex); const s32 emitterIndex = emitterSet->emitterCount(); - mDocument->addEmitter(setIndex); + mDocument->addEmitter("Add New Emitter", setIndex); mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::Emitter); expandSourceIndex(mListModel.indexFromItem(setItem)); emit itemAdded(); @@ -784,30 +784,25 @@ void PtclList::pasteItem() { QModelIndex sourceIndex = mProxyModel.mapToSource(proxyModel); QStandardItem* item = mListModel.itemFromIndex(sourceIndex); - if (!item) { + if (!item || !mDocument || !mSelection) { return; } const auto type = static_cast(item->data(sRoleNodeType).toUInt()); - if (mClipboardSet && type == NodeType::EmitterSet) { - auto& newSet = mDocument->data().appendEmitterSet(mClipboardSet); - mClipboardSet = newSet->clone(); + if (mClipboardSet && type == NodeType::EmitterSet) { + mDocument->addEmitterSet("Paste EmitterSet", mClipboardSet->clone()); const s32 setIndex = mDocument->emitterSetCount() - 1; - insertEmitterSetNode(setIndex); mSelection->set(setIndex, 0, Ptcl::Selection::Type::EmitterSet); } else if (mClipboardEmitter && type == NodeType::Emitter) { const auto& setItem = item->parent(); const s32 setIndex = item->data(sRoleSetIdx).toInt(); auto set = mDocument->emitterSet(setIndex); - auto& newEmitter = set->appendEmitter(mClipboardEmitter); - mClipboardEmitter = newEmitter->clone(); + mDocument->addEmitter("Paste Emitter", setIndex, mClipboardEmitter->clone()); const s32 emitterIndex = set->emitterCount() - 1; - insertEmitterNode(setItem, setIndex, emitterIndex); - mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::Emitter); } } diff --git a/src/ptcl/ptclDocument.cpp b/src/ptcl/ptclDocument.cpp index ed1954c..89f75ce 100644 --- a/src/ptcl/ptclDocument.cpp +++ b/src/ptcl/ptclDocument.cpp @@ -41,16 +41,16 @@ void Document::setProjectName(const QString& name) { mUndoStack.push(new RenameProjectNameCommand(this, name)); } -void Document::addEmitter(s32 setIndex, std::unique_ptr emitter) { - mUndoStack.push(new AddEmitterCommand(this, setIndex, std::move(emitter))); +void Document::addEmitter(QString label, s32 setIndex, std::unique_ptr emitter) { + mUndoStack.push(new AddEmitterCommand(this, setIndex, label, std::move(emitter))); } void Document::removeEmitter(s32 setIndex, s32 emitterIndex) { mUndoStack.push(new RemoveEmitterCommand(this, setIndex, emitterIndex)); } -void Document::addEmitterSet(std::unique_ptr emitterSet) { - mUndoStack.push(new AddEmitterSetCommand(this, std::move(emitterSet))); +void Document::addEmitterSet(QString label, std::unique_ptr emitterSet) { + mUndoStack.push(new AddEmitterSetCommand(this, label, std::move(emitterSet))); } void Document::removeEmitterSet(s32 setIndex) { From 23fd0fa0dddf9c0d4e283166243ae116429c22e4 Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Sat, 21 Mar 2026 00:05:53 +0000 Subject: [PATCH 33/35] refactor: use undoStack to determine if document is dirty --- include/editor/mainWindow.h | 3 --- include/editor/ptclListWidget.h | 4 ---- include/ptcl/ptclDocument.h | 4 +--- src/editor/mainWindow.cpp | 35 ++++++++++----------------------- src/editor/ptclListWidget.cpp | 3 --- src/ptcl/ptclDocument.cpp | 12 +++++++---- 6 files changed, 19 insertions(+), 42 deletions(-) diff --git a/include/editor/mainWindow.h b/include/editor/mainWindow.h index fc17467..d999aaa 100644 --- a/include/editor/mainWindow.h +++ b/include/editor/mainWindow.h @@ -45,12 +45,10 @@ private slots: void loadPtclRes(const QString& path); void setupUi(); - void setupConnections(); void setupMenus(); void updateWindowTitle(); void updateStatusBar(); - void setDirty(bool dirty); void bindUndoStack(); @@ -80,7 +78,6 @@ private slots: PtclEditor::TextureListWidget mTextureWidget{}; QLabel* mStatusLabel{nullptr}; - bool mHasUnsavedChanges{false}; }; diff --git a/include/editor/ptclListWidget.h b/include/editor/ptclListWidget.h index 8135fde..13525da 100644 --- a/include/editor/ptclListWidget.h +++ b/include/editor/ptclListWidget.h @@ -78,10 +78,6 @@ class PtclList : public QWidget { void updateEmitterSetName(s32 setIndex); void refresh(); -signals: - void itemAdded(); - void itemRemoved(); - private slots: void filterList(const QString& text); diff --git a/include/ptcl/ptclDocument.h b/include/ptcl/ptclDocument.h index 7ade9cd..d7d258f 100644 --- a/include/ptcl/ptclDocument.h +++ b/include/ptcl/ptclDocument.h @@ -77,7 +77,7 @@ class Document final : public QObject { const QString& projectName() const { return mData.name(); } void setProjectName(const QString& name); - bool isModified() const { return mModified; } + bool isDirty() const { return !mUndoStack.isClean(); } QString filePath() const { return mFilePath; } QUndoStack* undoStack() { return &mUndoStack; } @@ -136,8 +136,6 @@ class Document final : public QObject { PtclRes mData{}; QString mFilePath{}; QUndoStack mUndoStack{}; - - bool mModified{false}; }; diff --git a/src/editor/mainWindow.cpp b/src/editor/mainWindow.cpp index 1ee1f14..6db2158 100644 --- a/src/editor/mainWindow.cpp +++ b/src/editor/mainWindow.cpp @@ -1,5 +1,4 @@ #include "editor/mainWindow.h" -#include "util/nameValidator.h" #include "util/settingsUtil.h" #include @@ -25,7 +24,6 @@ namespace PtclEditor { MainWindow::MainWindow(QWidget* parent) : QMainWindow{parent} { setupUi(); - setupConnections(); updateRecentFileList(); } @@ -83,16 +81,6 @@ void MainWindow::setupUi() { updateWindowTitle(); } -void MainWindow::setupConnections() { - connect(&mPtclList, &PtclList::itemAdded, this, [this]() { - setDirty(true); - }); - - connect(&mPtclList, &PtclList::itemRemoved, this, [this]() { - setDirty(true); - }); -} - void MainWindow::setupMenus() { // Open File mOpenAction.setText("Open File"); @@ -156,7 +144,7 @@ void MainWindow::setupMenus() { } void MainWindow::closeEvent(QCloseEvent* event) { - if (!mDocument || !mHasUnsavedChanges) { + if (!mDocument || !mDocument->isDirty()) { auto& settings = SettingsUtil::SettingsMgr::instance(); settings.setWindowGeometry(saveGeometry()); settings.setWindowState(saveState()); @@ -246,7 +234,6 @@ void MainWindow::saveFile() { mDocument->save(mDocument->filePath()); - setDirty(false); statusBar()->showMessage("File Saved", 2000); SettingsUtil::SettingsMgr::instance().addRecentFile(mDocument->filePath()); @@ -280,7 +267,6 @@ void MainWindow::saveFileAs() { mDocument->save(filePath); - setDirty(false); statusBar()->showMessage("File Saved", 2000); mDocument->filePath() = filePath; @@ -339,7 +325,6 @@ void MainWindow::loadPtclRes(const QString& path) { } bindUndoStack(); - setDirty(false); SettingsUtil::SettingsMgr::instance().addRecentFile(path); SettingsUtil::SettingsMgr::instance().setLastOpenPath(QFileInfo(path).absolutePath()); @@ -364,7 +349,7 @@ void MainWindow::updateWindowTitle() { if (mDocument && !mDocument->filePath().isEmpty()) { title += " - " + QFileInfo(mDocument->filePath()).fileName(); } - if (mHasUnsavedChanges) { + if (mDocument && mDocument->isDirty()) { title += " *"; } setWindowTitle(title); @@ -377,20 +362,13 @@ void MainWindow::updateStatusBar() { if (!mDocument) { mStatusLabel->setText("No file loaded"); - } else if (mHasUnsavedChanges) { + } else if (mDocument->isDirty()) { mStatusLabel->setText("Unsaved changes"); } else { mStatusLabel->setText("All changes saved"); } } -void MainWindow::setDirty(bool dirty) { - mHasUnsavedChanges = dirty; - mSaveAction.setEnabled(dirty); - updateStatusBar(); - updateWindowTitle(); -} - void MainWindow::bindUndoStack() { if (!mDocument) { mUndoAction->setEnabled(false); @@ -417,6 +395,13 @@ void MainWindow::bindUndoStack() { mRedoAction->setText(text.isEmpty() ? "Redo" : "Redo " + text); }); + connect(stack, &QUndoStack::cleanChanged, this, [this](bool clean) { + const bool dirty = !clean; + mSaveAction.setEnabled(dirty); + updateStatusBar(); + updateWindowTitle(); + }); + mUndoAction->setEnabled(stack->canUndo()); mRedoAction->setEnabled(stack->canRedo()); diff --git a/src/editor/ptclListWidget.cpp b/src/editor/ptclListWidget.cpp index 99fb5ef..43d26cf 100644 --- a/src/editor/ptclListWidget.cpp +++ b/src/editor/ptclListWidget.cpp @@ -622,7 +622,6 @@ void PtclList::addEmitterSet() { mSelection->set(setIndex, 0, Ptcl::Selection::Type::EmitterSet); expandSourceIndex(mListModel.index(setIndex, 0)); - emit itemAdded(); } void PtclList::addEmitter() { @@ -647,7 +646,6 @@ void PtclList::addEmitter() { mDocument->addEmitter("Add New Emitter", setIndex); mSelection->set(setIndex, emitterIndex, Ptcl::Selection::Type::Emitter); expandSourceIndex(mListModel.indexFromItem(setItem)); - emit itemAdded(); } void PtclList::removeItem() { @@ -666,7 +664,6 @@ void PtclList::removeItem() { } else if (type == NodeType::EmitterSet) { removeEmitterSet(item); } - emit itemRemoved(); } diff --git a/src/ptcl/ptclDocument.cpp b/src/ptcl/ptclDocument.cpp index 89f75ce..410109a 100644 --- a/src/ptcl/ptclDocument.cpp +++ b/src/ptcl/ptclDocument.cpp @@ -1,6 +1,8 @@ #include "ptcl/ptclCommand.h" #include "ptcl/ptclDocument.h" +#include + namespace Ptcl { @@ -27,13 +29,14 @@ Document::Document(QObject* parent) : bool Document::load(const QString& filePath) { mFilePath = filePath; - mModified = false; + mUndoStack.clear(); + mUndoStack.setClean(); return mData.load(filePath); } bool Document::save(const QString& filePath) { mFilePath = filePath; - mModified = false; + mUndoStack.setClean(); return mData.save(filePath); } @@ -42,7 +45,7 @@ void Document::setProjectName(const QString& name) { } void Document::addEmitter(QString label, s32 setIndex, std::unique_ptr emitter) { - mUndoStack.push(new AddEmitterCommand(this, setIndex, label, std::move(emitter))); + mUndoStack.push(new AddEmitterCommand(this, setIndex, std::move(label), std::move(emitter))); } void Document::removeEmitter(s32 setIndex, s32 emitterIndex) { @@ -50,13 +53,14 @@ void Document::removeEmitter(s32 setIndex, s32 emitterIndex) { } void Document::addEmitterSet(QString label, std::unique_ptr emitterSet) { - mUndoStack.push(new AddEmitterSetCommand(this, label, std::move(emitterSet))); + mUndoStack.push(new AddEmitterSetCommand(this, std::move(label), std::move(emitterSet))); } void Document::removeEmitterSet(s32 setIndex) { mUndoStack.push(new RemoveEmitterSetCommand(this, setIndex)); } + // ========================================================================== // From 2163b3c65d59122c319cf5f56bcc6aa864dccf7f Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Sat, 21 Mar 2026 03:32:30 +0000 Subject: [PATCH 34/35] feat(ui): implement undo/redo for texture insertion/replacement --- include/editor/textureListWidget.h | 18 ++- include/ptcl/ptcl.h | 11 +- include/ptcl/ptclCommand.h | 111 ++++++++++++++++++ include/ptcl/ptclDocument.h | 15 ++- include/ptcl/ptclEmitter.h | 8 +- include/ptcl/ptclTexture.h | 17 ++- .../inspector/child/childTextureInspector.cpp | 4 +- src/editor/inspector/textureInspector.cpp | 4 +- src/editor/textureListWidget.cpp | 109 +++++++++++++---- src/ptcl/ptcl.cpp | 30 ++++- src/ptcl/ptclDocument.cpp | 12 ++ src/ptcl/ptclTexture.cpp | 40 +++---- 12 files changed, 295 insertions(+), 84 deletions(-) diff --git a/include/editor/textureListWidget.h b/include/editor/textureListWidget.h index 1eb5659..0cf6235 100644 --- a/include/editor/textureListWidget.h +++ b/include/editor/textureListWidget.h @@ -51,16 +51,19 @@ class TextureListModel final : public QAbstractListModel { public: explicit TextureListModel(QObject* parent = nullptr); - void setTextures(Ptcl::TextureList* textures); + void setTextures(const Ptcl::TextureList* textures); s32 rowCount(const QModelIndex& parent = {}) const final; QVariant data(const QModelIndex& index, s32 role) const final; + void onTextureAdded(s32 index); + void onTextureRemoved(s32 index); + private: void emitRowChangedFor(Ptcl::Texture* texture); private: - Ptcl::TextureList* mTextures{nullptr}; + const Ptcl::TextureList* mTextures{nullptr}; }; @@ -71,18 +74,21 @@ class TextureDetailsPanel final : public QWidget { public: explicit TextureDetailsPanel(QWidget* parent = nullptr); - void setTexture(Ptcl::Texture* texture); + void setTexture(const QModelIndex& index, Ptcl::Texture* texture); signals: void exportRequested(Ptcl::Texture* texture); - void replaceRequested(Ptcl::Texture* texture); + void replaceRequested(const QModelIndex& index); + void deleteRequested(const QModelIndex& index); private: Ptcl::Texture* mTexturePtr{nullptr}; + QModelIndex mIndex; ThumbnailWidget mThumbnailWidget{}; QPushButton mExportButton{}; QPushButton mReplaceButton{}; + QPushButton mDeleteButton{}; }; @@ -100,7 +106,8 @@ private slots: void exportAll(); void importTexture(); void exportTexture(Ptcl::Texture* texture); - void replaceTexture(Ptcl::Texture* texture); + void replaceTexture(const QModelIndex& index); + void deleteTexture(const QModelIndex& index); private: void setupToolbar(); @@ -111,7 +118,6 @@ private slots: private: Ptcl::Document* mDocument{nullptr}; - Ptcl::TextureList* mTexturesPtr{nullptr}; QToolBar mToolbar{}; QAction* mActionExportAll{nullptr}; diff --git a/include/ptcl/ptcl.h b/include/ptcl/ptcl.h index 168c335..ab7a847 100644 --- a/include/ptcl/ptcl.h +++ b/include/ptcl/ptcl.h @@ -14,7 +14,7 @@ namespace Ptcl { // ========================================================================== // -using TextureList = std::vector>; +using TextureList = std::vector>; using EmitterSetList = std::vector>; @@ -31,7 +31,7 @@ class PtclBinaryReader { TextureList takeTextures(); private: - std::shared_ptr loadTexture(u32 texturePos, u32 size, u32 width, u32 height, TextureFormat format); + Texture* loadTexture(u32 texturePos, u32 size, u32 width, u32 height, TextureFormat format); std::unique_ptr readEmitterSet(s32 index); void readComplexData(Emitter& emitter, const BinCommonEmitterData& common); @@ -137,15 +137,20 @@ class PtclRes { Emitter* emitter(s32 setIndex, s32 emitterIndex); const Emitter* emitter(s32 setIndex, s32 emitterIndex) const; - const TextureList& textures() const; TextureList& textures(); + const TextureList& textures() const; void insertEmitterSet(s32 setIndex, std::unique_ptr emitterSet); std::unique_ptr removeEmitterSet(s32 setIndex); + void insertTexture(s32 index, std::unique_ptr texture); + void swapTexture(s32 index, std::unique_ptr& texture); + std::unique_ptr removeTexture(s32 index); + s32 emitterSetCount() const; s32 emitterCount(s32 setIndex) const; u32 totalEmitterCount() const; + s32 textureCount() const; const std::unique_ptr& appendEmitterSet(std::unique_ptr& newSet); diff --git a/include/ptcl/ptclCommand.h b/include/ptcl/ptclCommand.h index 129e38f..b2b7074 100644 --- a/include/ptcl/ptclCommand.h +++ b/include/ptcl/ptclCommand.h @@ -32,6 +32,7 @@ class DocumentCommandBase : public QUndoCommand { void notifyEmitterChanged(s32 setIndex, s32 emitterIndex) { mDocument->notifyEmitterChanged(setIndex, emitterIndex); } void notifyEmitterSetChanged(s32 setIndex) { mDocument->notifyEmitterSetChanged(setIndex); } + void notifyTextureChanged(s32 index) { mDocument->notifyTextureChanged(index); } void notifyProjectChanged() { mDocument->notifyProjectChanged(); } void notifyEmitterAdded(s32 setIndex, s32 emitterIndex) { mDocument->notifyEmitterAdded(setIndex, emitterIndex); } @@ -40,6 +41,9 @@ class DocumentCommandBase : public QUndoCommand { void notifyEmitterSetAdded(s32 setIndex) { mDocument->notifyEmitterSetAdded(setIndex); } void notifyEmitterSetRemoved(s32 setIndex) { mDocument->notifyEmitterSetRemoved(setIndex); } + void notifyTextureAdded(s32 index) { mDocument->notifyTextureAdded(index); } + void notifyTextureRemoved(s32 index) { mDocument->notifyTextureRemoved(index); } + private: Document* mDocument; }; @@ -429,4 +433,111 @@ class RemoveEmitterSetCommand final : public DocumentCommandBase { // ========================================================================== // +class AddTextureCommand final : public DocumentCommandBase { +public: + AddTextureCommand(Document* doc, std::unique_ptr texture, QUndoCommand* parent = nullptr) : + DocumentCommandBase{doc, std::move("Add Texture"), parent}, mNewTexture{std::move(texture)} { + } + + s32 id() const override { + return mId; + } + + void undo() override { + auto removed = resource().removeTexture(mIndex); + if (!removed) { + Q_ASSERT(false && "removeTexture returned null"); + return; + } + + mNewTexture = std::move(removed); + notifyTextureRemoved(mIndex); + + } + + void redo() override { + mIndex = resource().textureCount(); + resource().insertTexture(mIndex, std::move(mNewTexture)); + notifyTextureAdded(mIndex); + } + +private: + s32 mIndex{0}; + std::unique_ptr mNewTexture{}; + + const s32 mId{static_cast(qHash("AddTexture"))}; +}; + + +// ========================================================================== // + + +class ReplaceTextureCommand final : public DocumentCommandBase { +public: + ReplaceTextureCommand(Document* doc, s32 index, std::unique_ptr texture, QUndoCommand* parent = nullptr) : + DocumentCommandBase{doc, std::move("Replace Texture"), parent}, mIndex{index}, mTexture{std::move(texture)} { + } + + s32 id() const override { + return mId; + } + + void undo() override { + resource().swapTexture(mIndex, mTexture); + notifyTextureChanged(mIndex); + + } + + void redo() override { + resource().swapTexture(mIndex, mTexture); + notifyTextureChanged(mIndex); + } + +private: + s32 mIndex{0}; + std::unique_ptr mTexture{}; + + const s32 mId{static_cast(qHash("ReplaceTexture"))}; +}; + + +// ========================================================================== // + + +class RemoveTextureCommand final : public DocumentCommandBase { +public: + RemoveTextureCommand(Document* doc, s32 index, QUndoCommand* parent = nullptr) : + DocumentCommandBase{doc, std::move("Remove Texture"), parent}, mIndex{index} { + } + + s32 id() const override { + return mId; + } + + void undo() override { + resource().insertTexture(mIndex, std::move(mRemovedTexture)); + notifyTextureAdded(mIndex); + } + + void redo() override { + auto removed = resource().removeTexture(mIndex); + if (!removed) { + Q_ASSERT(false && "removeTexture returned null"); + return; + } + + mRemovedTexture = std::move(removed); + notifyTextureRemoved(mIndex); + } + +private: + s32 mIndex{0}; + std::unique_ptr mRemovedTexture{}; + + const s32 mId{static_cast(qHash("RemoveTexture"))}; +}; + + +// ========================================================================== // + } // namespace Ptcl diff --git a/include/ptcl/ptclDocument.h b/include/ptcl/ptclDocument.h index d7d258f..8f5fd14 100644 --- a/include/ptcl/ptclDocument.h +++ b/include/ptcl/ptclDocument.h @@ -62,9 +62,6 @@ class Document final : public QObject { public: explicit Document(QObject* parent = nullptr); - // TODO: Remove this - TextureList& textures() { return mData.textures(); } - bool load(const QString& filePath); bool save(const QString& filePath); @@ -101,6 +98,10 @@ class Document final : public QObject { void addEmitterSet(QString label, std::unique_ptr emitterSet = nullptr); void removeEmitterSet(s32 setIndex); + void addTexture(std::unique_ptr texture); + void removeTexture(s32 index); + void replaceTexture(s32 index, std::unique_ptr texture); + private: TextureList& texturesMutable() { return mData.textures(); } Emitter* emitterMutable(s32 setIndex, s32 emitterIndex) { return mData.emitter(setIndex, emitterIndex); } @@ -111,6 +112,7 @@ class Document final : public QObject { void notifyEmitterChanged(s32 setIndex, s32 emitterIndex) { emit emitterChanged(setIndex, emitterIndex); } void notifyEmitterSetChanged(s32 setIndex) { emit emitterSetChanged(setIndex); } + void notifyTextureChanged(s32 index) { emit textureChanged(index); } void notifyProjectChanged() { emit projectChanged(); } void notifyEmitterAdded(s32 setIndex, s32 emitterIndex) { emit emitterAdded(setIndex, emitterIndex); } @@ -119,6 +121,9 @@ class Document final : public QObject { void notifyEmitterSetAdded(s32 setIndex) { emit emitterSetAdded(setIndex); } void notifyEmitterSetRemoved(s32 setIndex) { emit emitterSetRemoved(setIndex); } + void notifyTextureAdded(s32 index) { emit textureAdded(index); } + void notifyTextureRemoved(s32 index) { emit textureRemoved(index); } + friend class DocumentCommandBase; signals: @@ -132,6 +137,10 @@ class Document final : public QObject { void emitterSetAdded(s32 setIndex); void emitterSetRemoved(s32 setIndex); + void textureAdded(s32 index); + void textureRemoved(s32 index); + void textureChanged(s32 index); + private: PtclRes mData{}; QString mFilePath{}; diff --git a/include/ptcl/ptclEmitter.h b/include/ptcl/ptclEmitter.h index c6fb4f3..dbd2352 100644 --- a/include/ptcl/ptclEmitter.h +++ b/include/ptcl/ptclEmitter.h @@ -367,8 +367,8 @@ class Emitter { const TextureHandle& textureHandle() const { return mTextureHandle; } - std::shared_ptr texture() const { return mTextureHandle.get(); } - void setTexture(const std::shared_ptr& texture) { mTextureHandle.set(texture); } + Texture* texture() const { return mTextureHandle.get(); } + void setTexture(Texture* texture) { mTextureHandle.set(texture); } // ----- Fluctuation Properties ----- \\ @@ -661,8 +661,8 @@ class Emitter { const TextureHandle& childTextureHandle() const { return mChild.textureHandle; } - std::shared_ptr childTexture() const { return mChild.textureHandle.get(); } - void setChildTexture(const std::shared_ptr& texture) { mChild.textureHandle.set(texture); } + Texture* childTexture() const { return mChild.textureHandle.get(); } + void setChildTexture(Texture* texture) { mChild.textureHandle.set(texture); } // Child Color diff --git a/include/ptcl/ptclTexture.h b/include/ptcl/ptclTexture.h index a03e4f2..bbc7489 100644 --- a/include/ptcl/ptclTexture.h +++ b/include/ptcl/ptclTexture.h @@ -31,10 +31,9 @@ class Texture { bool isPlaceholder() const; - void replaceTexture(std::vector* encodedData, s32 width, s32 height, TextureFormat format); - void replaceTexture(const Texture& other); + void swapTexture(Texture& other); - static const std::shared_ptr& placeholder(); + static Texture* placeholder(); void setUserCountCallback(UserCountCallback callback); @@ -68,7 +67,7 @@ class Texture { class TextureHandle { public: - TextureHandle(std::shared_ptr texture = nullptr); + TextureHandle(Texture* texture = nullptr); TextureHandle(const TextureHandle& other); TextureHandle(TextureHandle&& other) noexcept; @@ -82,18 +81,18 @@ class TextureHandle { void invalidate(); bool isValid() const; - std::shared_ptr get() const; - void set(const std::shared_ptr& texture); + Texture* get() const; + void set(Texture* texture); - TextureHandle& operator=(const std::shared_ptr& texture); - std::shared_ptr operator->() const; + TextureHandle& operator=(Texture* texture); + Texture* operator->() const; private: void incrementCount(); void decrementCount(); private: - std::shared_ptr mTexturePtr; + Texture* mTexturePtr; }; diff --git a/src/editor/inspector/child/childTextureInspector.cpp b/src/editor/inspector/child/childTextureInspector.cpp index 546c57a..24bcef3 100644 --- a/src/editor/inspector/child/childTextureInspector.cpp +++ b/src/editor/inspector/child/childTextureInspector.cpp @@ -156,13 +156,13 @@ void ChildTextureInspector::populateProperties() { } void ChildTextureInspector::changeTexture() { - const auto textureList = mDocument->textures(); + const auto& textureList = mDocument->textures(); TextureSelectDialog dialog(textureList, this); if (dialog.exec() == QDialog::Accepted) { s32 selectedInded = dialog.selectedIndex(); if (selectedInded >= 0 && static_cast(selectedInded) < textureList.size()) { - const auto& texture = textureList.at(selectedInded); + const auto texture = textureList.at(selectedInded).get(); setEmitterProperty( "Set Child Texture", "SetChildTexture", diff --git a/src/editor/inspector/textureInspector.cpp b/src/editor/inspector/textureInspector.cpp index b8eb9d8..c75accf 100644 --- a/src/editor/inspector/textureInspector.cpp +++ b/src/editor/inspector/textureInspector.cpp @@ -394,13 +394,13 @@ void TextureInspector::updateTexPatTblColumns() { } void TextureInspector::changeTexture() { - const auto textureList = mDocument->textures(); + const auto& textureList = mDocument->textures(); TextureSelectDialog dialog(textureList, this); if (dialog.exec() == QDialog::Accepted) { s32 selectedInded = dialog.selectedIndex(); if (selectedInded >= 0 && static_cast(selectedInded) < textureList.size()) { - const auto& texture = textureList.at(selectedInded); + const auto texture = textureList.at(selectedInded).get(); setEmitterProperty( "Set Texture", "SetEmitterTexture", diff --git a/src/editor/textureListWidget.cpp b/src/editor/textureListWidget.cpp index 2bdfa45..25b73d3 100644 --- a/src/editor/textureListWidget.cpp +++ b/src/editor/textureListWidget.cpp @@ -83,7 +83,7 @@ QSize TextureItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QM TextureListModel::TextureListModel(QObject* parent) : QAbstractListModel{parent} {} -void TextureListModel::setTextures(Ptcl::TextureList* textures) { +void TextureListModel::setTextures(const Ptcl::TextureList* textures) { beginResetModel(); mTextures = textures; @@ -153,14 +153,39 @@ void TextureListModel::emitRowChangedFor(Ptcl::Texture* texture) { } } +void TextureListModel::onTextureAdded(s32 index) { + if (!mTextures) { + return; + } + + beginInsertRows(QModelIndex(), index, index); + + auto* texture = (*mTextures)[index].get(); + texture->setUserCountCallback([this, texture]() { + emitRowChangedFor(texture); + }); + + endInsertRows(); +} + +void TextureListModel::onTextureRemoved(s32 index) { + if (!mTextures) { + return; + } + + beginRemoveRows(QModelIndex(), index, index); + endRemoveRows(); +} + // ========================================================================== // TextureDetailsPanel::TextureDetailsPanel(QWidget* parent) : QWidget{parent} { mThumbnailWidget.setThumbnailSize({256, 256}); - mExportButton.setText("Export Texture"); - mReplaceButton.setText("Replace Texture"); + mExportButton.setText("Export"); + mReplaceButton.setText("Replace"); + mDeleteButton.setText("Delete"); auto* mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(0, 0, 0, 0); @@ -170,6 +195,7 @@ TextureDetailsPanel::TextureDetailsPanel(QWidget* parent) : auto* buttonLayout = new QHBoxLayout; buttonLayout->addWidget(&mExportButton); buttonLayout->addWidget(&mReplaceButton); + buttonLayout->addWidget(&mDeleteButton); mainLayout->addLayout(buttonLayout); mainLayout->addStretch(1); @@ -182,16 +208,24 @@ TextureDetailsPanel::TextureDetailsPanel(QWidget* parent) : connect(&mReplaceButton, &QPushButton::clicked, this, [this](bool checked) { Q_UNUSED(checked); - if (mTexturePtr) { - emit replaceRequested(mTexturePtr); + if (mIndex.isValid()) { + emit replaceRequested(mIndex); + } + }); + + connect(&mDeleteButton, &QPushButton::clicked, this, [this](bool checked) { + Q_UNUSED(checked); + if (mIndex.isValid()) { + emit deleteRequested(mIndex); } }); setEnabled(false); } -void TextureDetailsPanel::setTexture(Ptcl::Texture* texture) { +void TextureDetailsPanel::setTexture(const QModelIndex& index, Ptcl::Texture* texture) { mTexturePtr = texture; + mIndex = index; if (!mTexturePtr) { setEnabled(false); @@ -217,6 +251,7 @@ TextureListWidget::TextureListWidget(QWidget *parent) : connect(&mDetailsPanel, &TextureDetailsPanel::exportRequested, this, &TextureListWidget::exportTexture); connect(&mDetailsPanel, &TextureDetailsPanel::replaceRequested, this, &TextureListWidget::replaceTexture); + connect(&mDetailsPanel, &TextureDetailsPanel::deleteRequested, this, &TextureListWidget::deleteTexture); } void TextureListWidget::setupToolbar() { @@ -257,8 +292,11 @@ void TextureListWidget::setupContextMenu() { menu.addAction("Export", this, [this, texture] { exportTexture(texture); }); - menu.addAction("Replace", this, [this, texture] { - replaceTexture(texture); + menu.addAction("Replace", this, [this, index] { + replaceTexture(index); + }); + menu.addAction("Delete", this, [this, index] { + deleteTexture(index); }); menu.exec(mView.viewport()->mapToGlobal(pos)); @@ -284,15 +322,18 @@ void TextureListWidget::setupSelectionHandling() { if (current.isValid()) { texture = static_cast(current.data(TextureListModel::Roles::TexturePtrRole).value()); } - mDetailsPanel.setTexture(texture); + mDetailsPanel.setTexture(current, texture); }); } void TextureListWidget::setDocument(Ptcl::Document* document) { + if (mDocument) { + mDocument->disconnect(this); + } + mDocument = document; if (!mDocument) { - mTexturesPtr = nullptr; mModel.setTextures(nullptr); mActionExportAll->setEnabled(false); @@ -302,8 +343,20 @@ void TextureListWidget::setDocument(Ptcl::Document* document) { return; } - mTexturesPtr = &mDocument->textures(); - mModel.setTextures(mTexturesPtr); + connect(mDocument, &Ptcl::Document::textureChanged, this, [this](s32 index) { + QModelIndex idx = mModel.index(index); + emit mModel.dataChanged(idx, idx); + }); + + connect(mDocument, &Ptcl::Document::textureAdded, this, [this](s32 index) { + mModel.onTextureAdded(index); + }); + + connect(mDocument, &Ptcl::Document::textureRemoved, this, [this](s32 index) { + mModel.onTextureRemoved(index); + }); + + mModel.setTextures(&mDocument->textures()); mActionExportAll->setEnabled(true); mActionImportTexture->setEnabled(true); @@ -312,7 +365,7 @@ void TextureListWidget::setDocument(Ptcl::Document* document) { } void TextureListWidget::exportAll() { - if (!mTexturesPtr) { + if (!mDocument) { return; } @@ -332,8 +385,9 @@ void TextureListWidget::exportAll() { return; } - for (s32 idx = 0; idx < mTexturesPtr->size(); ++idx) { - const auto& texture = (*mTexturesPtr)[idx]; + const auto& textures = mDocument->textures(); + for (s32 idx = 0; idx < textures.size(); ++idx) { + const auto& texture = textures[idx]; texture->textureData().save(QString("%1/tex_%2.png").arg(dirPath).arg(idx)); } @@ -341,7 +395,7 @@ void TextureListWidget::exportAll() { } void TextureListWidget::importTexture() { - if (!mTexturesPtr) { + if (!mDocument) { return; } @@ -365,8 +419,7 @@ void TextureListWidget::importTexture() { dialog.setFilePath(filePath); if (dialog.exec() == QDialog::Accepted) { - mTexturesPtr->push_back(std::move(dialog.getTexture())); - mModel.setTextures(mTexturesPtr); + mDocument->addTexture(dialog.getTexture()); } SettingsUtil::SettingsMgr::instance().setLastImportPath(QFileInfo(filePath).absolutePath()); @@ -402,11 +455,13 @@ void TextureListWidget::exportTexture(Ptcl::Texture* texture) { SettingsUtil::SettingsMgr::instance().setLastExportPath(QFileInfo(filePath).absolutePath()); } -void TextureListWidget::replaceTexture(Ptcl::Texture* texture) { - if (!texture) { +void TextureListWidget::replaceTexture(const QModelIndex& index) { + if (!mDocument || !index.isValid()) { return; } + const s32 textureIndex = index.row(); + QString basePath = SettingsUtil::SettingsMgr::instance().lastImportPath(); if (basePath.isEmpty()) { QString lastOpenPath = SettingsUtil::SettingsMgr::instance().lastOpenPath(); @@ -427,14 +482,20 @@ void TextureListWidget::replaceTexture(Ptcl::Texture* texture) { dialog.setFilePath(filePath); if (dialog.exec() == QDialog::Accepted) { - texture->replaceTexture(*dialog.getTexture()); - mModel.setTextures(mTexturesPtr); - mDetailsPanel.setTexture(texture); + auto newTexture = dialog.getTexture(); + mDocument->replaceTexture(textureIndex, std::move(newTexture)); } SettingsUtil::SettingsMgr::instance().setLastImportPath(QFileInfo(filePath).absolutePath()); +} - texture->textureData().save(filePath); +void TextureListWidget::deleteTexture(const QModelIndex& index) { + if (!mDocument || !index.isValid()) { + return; + } + + const s32 textureIndex = index.row(); + mDocument->removeTexture(textureIndex); } diff --git a/src/ptcl/ptcl.cpp b/src/ptcl/ptcl.cpp index 44eff11..809657b 100644 --- a/src/ptcl/ptcl.cpp +++ b/src/ptcl/ptcl.cpp @@ -52,12 +52,12 @@ EmitterSetList PtclBinaryReader::readEmitterSets() { return std::move(setList); } -std::shared_ptr PtclBinaryReader::loadTexture(u32 texturePos, u32 size, u32 width, u32 height, TextureFormat format) { +Texture* PtclBinaryReader::loadTexture(u32 texturePos, u32 size, u32 width, u32 height, TextureFormat format) { const u32 offset = mTextureTblPos + texturePos; auto it = mTextureOffsetMap.find(offset); if (it != mTextureOffsetMap.end()) { - return mTextures[it->second]; + return mTextures[it->second].get(); } mFile.seek(offset); @@ -69,13 +69,13 @@ std::shared_ptr PtclBinaryReader::loadTexture(u32 texturePos, u32 size, qWarning() << "Expected to read" << size << "bytes, got" << bytesRead << "bytes."; } - auto texture = std::make_shared(&textureData, width, height, format); + auto texture = std::make_unique(&textureData, width, height, format); const u32 idx = static_cast(mTextures.size()); - mTextures.push_back(texture); + mTextures.push_back(std::move(texture)); mTextureOffsetMap.emplace(offset, idx); - return texture; + return mTextures.back().get(); } std::unique_ptr PtclBinaryReader::readEmitterSet(s32 index) { @@ -464,6 +464,10 @@ u32 PtclRes::totalEmitterCount() const { return count; } +s32 PtclRes::textureCount() const { + return mTextures.size(); +} + bool PtclRes::save(const QString& filePath) { PtclBinaryWriter writer(filePath); writer.write(*this); @@ -550,6 +554,22 @@ std::unique_ptr PtclRes::removeEmitterSet(s32 setIndex) { return removed; } +void PtclRes::insertTexture(s32 index, std::unique_ptr texture) { + mTextures.insert(mTextures.begin() + index, std::move(texture)); +} + +void PtclRes::swapTexture(s32 index, std::unique_ptr& texture) { + mTextures[index]->swapTexture(*texture); +} + +std::unique_ptr PtclRes::removeTexture(s32 index) { + auto it = mTextures.begin() + index; + + std::unique_ptr removed = std::move(*it); + mTextures.erase(it); + return removed; +} + const std::unique_ptr& PtclRes::appendEmitterSet(std::unique_ptr& newSet) { mEmitterSets.push_back(std::move(newSet)); return mEmitterSets.at(emitterSetCount() - 1); diff --git a/src/ptcl/ptclDocument.cpp b/src/ptcl/ptclDocument.cpp index 410109a..0b53efa 100644 --- a/src/ptcl/ptclDocument.cpp +++ b/src/ptcl/ptclDocument.cpp @@ -60,6 +60,18 @@ void Document::removeEmitterSet(s32 setIndex) { mUndoStack.push(new RemoveEmitterSetCommand(this, setIndex)); } +void Document::addTexture(std::unique_ptr texture) { + mUndoStack.push(new AddTextureCommand(this, std::move(texture))); +} + +void Document::removeTexture(s32 index) { + mUndoStack.push(new RemoveTextureCommand(this, index)); +} + +void Document::replaceTexture(s32 index, std::unique_ptr texture) { + mUndoStack.push(new ReplaceTextureCommand(this, index, std::move(texture))); +} + // ========================================================================== // diff --git a/src/ptcl/ptclTexture.cpp b/src/ptcl/ptclTexture.cpp index c3c8531..436cf7f 100644 --- a/src/ptcl/ptclTexture.cpp +++ b/src/ptcl/ptclTexture.cpp @@ -1,7 +1,6 @@ #include "ptcl/ptclTexture.h" #include "util/imageUtil.h" -#include #include @@ -44,24 +43,14 @@ bool Texture::isPlaceholder() const { return mIsPlaceholder; } -void Texture::replaceTexture(std::vector* encodedData, s32 width, s32 height, TextureFormat format) { - mEncodedData = std::move(*encodedData); - mTextureFormat = format; - mDecodedTexture = ImageUtil::picaTextureToQImage(mEncodedData, width, height, format); - - if (mDecodedTexture.isNull()) { - throw std::runtime_error("Failed to decode texture from encoded data."); - } -} - -void Texture::replaceTexture(const Texture& other) { - mTextureFormat = other.mTextureFormat; - mEncodedData = other.mEncodedData; - mDecodedTexture = other.mDecodedTexture; +void Texture::swapTexture(Texture& other) { + std::swap(mTextureFormat, other.mTextureFormat); + std::swap(mEncodedData, other.mEncodedData); + std::swap(mDecodedTexture, other.mDecodedTexture); } -const std::shared_ptr& Texture::placeholder() { - static std::shared_ptr sPlaceholder = [] { +Texture* Texture::placeholder() { + static Texture sPlaceholder = [] { constexpr s32 width = 8; constexpr s32 height = 8; constexpr s32 tile = width / 2; @@ -80,12 +69,11 @@ const std::shared_ptr& Texture::placeholder() { } auto encoded = ImageUtil::QImageToPicaTexture(img, TextureFormat::ETC1); - auto tex = std::make_shared(&encoded, width, height, TextureFormat::ETC1); - - tex->mIsPlaceholder = true; + Texture tex(&encoded, width, height, TextureFormat::ETC1); + tex.mIsPlaceholder = true; return tex; }(); - return sPlaceholder; + return &sPlaceholder; } void Texture::setUserCountCallback(Texture::UserCountCallback callback) { @@ -114,7 +102,7 @@ void Texture::doUserCountCallback() const { // ========================================================================== // -TextureHandle::TextureHandle(std::shared_ptr texture) : +TextureHandle::TextureHandle(Texture* texture) : mTexturePtr(std::move(texture)) { incrementCount(); } @@ -159,11 +147,11 @@ bool TextureHandle::isValid() const { return mTexturePtr != nullptr; } -std::shared_ptr TextureHandle::get() const { +Texture* TextureHandle::get() const { return mTexturePtr ? mTexturePtr : Texture::placeholder(); } -void TextureHandle::set(const std::shared_ptr& texture) { +void TextureHandle::set(Texture* texture) { if (mTexturePtr != texture) { decrementCount(); mTexturePtr = texture; @@ -171,12 +159,12 @@ void TextureHandle::set(const std::shared_ptr& texture) { } } -TextureHandle& TextureHandle::operator=(const std::shared_ptr& texture) { +TextureHandle& TextureHandle::operator=(Texture* texture) { set(std::move(texture)); return *this; } -std::shared_ptr TextureHandle::operator->() const { +Texture* TextureHandle::operator->() const { return get(); } From 0ed4578e47f0bac686daa512b70c19d9fa06df5e Mon Sep 17 00:00:00 2001 From: ExplosBlue Date: Sat, 21 Mar 2026 05:28:02 +0000 Subject: [PATCH 35/35] fix: prevent invalid document pointers being accessed on shutdown --- include/editor/mainWindow.h | 1 + include/ptcl/ptcl.h | 7 +++++-- include/ptcl/ptclEmitterSet.h | 1 - src/editor/inspector/textureInspector.cpp | 10 ++++------ src/editor/mainWindow.cpp | 6 ++++++ src/ptcl/ptcl.cpp | 5 ----- src/ptcl/ptclEmitterSet.cpp | 5 ----- 7 files changed, 16 insertions(+), 19 deletions(-) diff --git a/include/editor/mainWindow.h b/include/editor/mainWindow.h index d999aaa..9050532 100644 --- a/include/editor/mainWindow.h +++ b/include/editor/mainWindow.h @@ -27,6 +27,7 @@ class MainWindow final : public QMainWindow { Q_OBJECT public: MainWindow(QWidget* parent = nullptr); + ~MainWindow() final; protected: void closeEvent(QCloseEvent* event) final; diff --git a/include/ptcl/ptcl.h b/include/ptcl/ptcl.h index ab7a847..79f105c 100644 --- a/include/ptcl/ptcl.h +++ b/include/ptcl/ptcl.h @@ -119,6 +119,11 @@ class PtclRes { public: PtclRes() = default; + ~PtclRes() { + mEmitterSets.clear(); + mTextures.clear(); + } + PtclRes(const PtclRes&) = delete; PtclRes& operator=(const PtclRes&) = delete; @@ -152,8 +157,6 @@ class PtclRes { u32 totalEmitterCount() const; s32 textureCount() const; - const std::unique_ptr& appendEmitterSet(std::unique_ptr& newSet); - private: QString mName; EmitterSetList mEmitterSets; diff --git a/include/ptcl/ptclEmitterSet.h b/include/ptcl/ptclEmitterSet.h index bc02bbb..45544e5 100644 --- a/include/ptcl/ptclEmitterSet.h +++ b/include/ptcl/ptclEmitterSet.h @@ -43,7 +43,6 @@ class EmitterSet std::unique_ptr removeEmitter(s32 emitterIndex); std::unique_ptr clone() const; - const std::unique_ptr& appendEmitter(std::unique_ptr& newEmitter); private: QString mName{}; diff --git a/src/editor/inspector/textureInspector.cpp b/src/editor/inspector/textureInspector.cpp index c75accf..6be00ad 100644 --- a/src/editor/inspector/textureInspector.cpp +++ b/src/editor/inspector/textureInspector.cpp @@ -318,9 +318,7 @@ void TextureInspector::populateProperties() { QSignalBlocker b14(mTexRepetitionsY); QSignalBlocker b15(mAnimModeComboBox); - if (mEmitter->textureHandle().isValid()) { - mTexturePreview.setPixmap(QPixmap::fromImage(mEmitter->textureHandle()->textureData())); - } + mTexturePreview.setPixmap(QPixmap::fromImage(mEmitter->textureHandle()->textureData())); mWrapTComboBox.setCurrentEnum(mEmitter->textureWrapT()); mWrapSComboBox.setCurrentEnum(mEmitter->textureWrapS()); @@ -398,9 +396,9 @@ void TextureInspector::changeTexture() { TextureSelectDialog dialog(textureList, this); if (dialog.exec() == QDialog::Accepted) { - s32 selectedInded = dialog.selectedIndex(); - if (selectedInded >= 0 && static_cast(selectedInded) < textureList.size()) { - const auto texture = textureList.at(selectedInded).get(); + s32 selectedIndex = dialog.selectedIndex(); + if (selectedIndex >= 0 && static_cast(selectedIndex) < textureList.size()) { + const auto texture = textureList.at(selectedIndex).get(); setEmitterProperty( "Set Texture", "SetEmitterTexture", diff --git a/src/editor/mainWindow.cpp b/src/editor/mainWindow.cpp index 6db2158..55d742b 100644 --- a/src/editor/mainWindow.cpp +++ b/src/editor/mainWindow.cpp @@ -81,6 +81,12 @@ void MainWindow::setupUi() { updateWindowTitle(); } +MainWindow::~MainWindow() { + mPtclList.setDocument(nullptr); + mInspector.setDocument(nullptr); + mTextureWidget.setDocument(nullptr); +} + void MainWindow::setupMenus() { // Open File mOpenAction.setText("Open File"); diff --git a/src/ptcl/ptcl.cpp b/src/ptcl/ptcl.cpp index 809657b..69171c9 100644 --- a/src/ptcl/ptcl.cpp +++ b/src/ptcl/ptcl.cpp @@ -570,11 +570,6 @@ std::unique_ptr PtclRes::removeTexture(s32 index) { return removed; } -const std::unique_ptr& PtclRes::appendEmitterSet(std::unique_ptr& newSet) { - mEmitterSets.push_back(std::move(newSet)); - return mEmitterSets.at(emitterSetCount() - 1); -} - // ========================================================================== // diff --git a/src/ptcl/ptclEmitterSet.cpp b/src/ptcl/ptclEmitterSet.cpp index 3ec77fa..4f5265b 100644 --- a/src/ptcl/ptclEmitterSet.cpp +++ b/src/ptcl/ptclEmitterSet.cpp @@ -74,11 +74,6 @@ std::unique_ptr EmitterSet::clone() const { return newSet; } -const std::unique_ptr& EmitterSet::appendEmitter(std::unique_ptr& newSet) { - mEmitters.push_back(std::move(newSet)); - return mEmitters.at(emitterCount() - 1); -} - // ========================================================================== //