From 5fe4e1213eb8bbbe8d69a89c60b03483efe5a3c5 Mon Sep 17 00:00:00 2001 From: milek7 Date: Sat, 23 Feb 2019 01:03:18 +0100 Subject: [PATCH 1/5] node cloner --- editormode.cpp | 82 +++++++++++++++++++++++++++++++------ editoruilayer.cpp | 26 ++++++++++++ editoruilayer.h | 10 +++++ editoruipanels.cpp | 47 +++++++++++++++++++++ editoruipanels.h | 20 +++++++++ sceneeditor.cpp | 11 ++++- scenenode.cpp | 2 +- scenenode.h | 8 ++++ simulation.cpp | 5 +++ simulation.h | 4 ++ simulationstateserializer.h | 6 ++- uilayer.cpp | 2 +- 12 files changed, 205 insertions(+), 18 deletions(-) diff --git a/editormode.cpp b/editormode.cpp index c43d3dfc..d99d4fa1 100644 --- a/editormode.cpp +++ b/editormode.cpp @@ -19,6 +19,7 @@ http://mozilla.org/MPL/2.0/. #include "Timer.h" #include "Console.h" #include "renderer.h" +#include "AnimModel.h" bool editor_mode::editormode_input::init() { @@ -229,19 +230,74 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods if( Button == GLFW_MOUSE_BUTTON_LEFT ) { if( Action == GLFW_PRESS ) { - // left button press - GfxRenderer.pick_node([this](scene::basic_node *node) - { - if (!m_dragging) - return; - m_node = node; - if( m_node ) - Application.set_cursor( GLFW_CURSOR_DISABLED ); - else - m_dragging = false; - dynamic_cast( m_userinterface.get() )->set_node( m_node ); - }); - m_dragging = true; + // left button press + auto const mode = dynamic_cast( m_userinterface.get() )->mode(); + + m_node = nullptr; + + GfxRenderer.pick_node([this, mode](scene::basic_node *node) + { + editor_ui *ui = dynamic_cast( m_userinterface.get() ); + + if (mode == nodebank_panel::MODIFY) { + if (!m_dragging) + return; + + m_node = node; + if( m_node ) + Application.set_cursor( GLFW_CURSOR_DISABLED ); + else + m_dragging = false; + ui->set_node( m_node ); + } + else if (mode == nodebank_panel::COPY) { + if (node && typeid(*node) == typeid(TAnimModel)) { + std::string as_text; + node->export_as_text(as_text); + + ui->add_node_template(as_text); + } + + m_dragging = false; + } + else if (mode == nodebank_panel::ADD) { + const std::string *src = ui->get_active_node_template(); + + if (!src) + return; + + cParser parser(*src); + parser.getTokens(); // "node" + parser.getTokens(2); // ranges + + scene::node_data nodedata; + parser >> nodedata.range_max >> nodedata.range_min; + + parser.getTokens(2); // name, type + nodedata.name = "editor_" + std::to_string(LocalRandom(0.0, 100000.0)); + nodedata.type = "model"; + + scene::scratch_data scratch; + + TAnimModel *cloned = simulation::State.deserialize_model(parser, scratch, nodedata); + + if (!cloned) + return; + + cloned->location(Camera.Pos + GfxRenderer.Mouse_Position()); + simulation::Instances.insert(cloned); + simulation::Region->insert(cloned); + + if (!m_dragging) + return; + + m_node = cloned; + Application.set_cursor( GLFW_CURSOR_DISABLED ); + ui->set_node( m_node ); + } + }); + + m_dragging = true; } else { // left button release diff --git a/editoruilayer.cpp b/editoruilayer.cpp index 0331271f..f7994064 100644 --- a/editoruilayer.cpp +++ b/editoruilayer.cpp @@ -18,6 +18,7 @@ editor_ui::editor_ui() { clear_panels(); // bind the panels with ui object. maybe not the best place for this but, eh push_back( &m_itempropertiespanel ); + push_back( &m_nodebankpanel ); } // potentially processes provided input key. returns: true if key was processed, false otherwise @@ -54,3 +55,28 @@ editor_ui::set_node( scene::basic_node * Node ) { m_node = Node; } + + +nodebank_panel::edit_mode +editor_ui::mode() { + return m_nodebankpanel.mode; +} + +void +editor_ui::add_node_template(const std::string &desc) { + m_nodebankpanel.add_template(desc); +} + +const std::string *editor_ui::get_active_node_template() { + return m_nodebankpanel.get_active_template(); +} + +void editor_ui::render_menu_contents() { + ui_layer::render_menu_contents(); + + if (ImGui::BeginMenu(locale::strings[locale::string::ui_mode_windows].c_str())) + { + ImGui::MenuItem(m_nodebankpanel.title.c_str(), nullptr, &m_nodebankpanel.is_open); + ImGui::EndMenu(); + } +} diff --git a/editoruilayer.h b/editoruilayer.h index 95b06169..a2a3f1ae 100644 --- a/editoruilayer.h +++ b/editoruilayer.h @@ -32,9 +32,19 @@ public: update() override; void set_node( scene::basic_node * Node ); + nodebank_panel::edit_mode + mode(); + void + add_node_template(const std::string &desc); + const std::string * + get_active_node_template(); + +protected: + void render_menu_contents(); private: // members itemproperties_panel m_itempropertiespanel { "Node Properties", true }; + nodebank_panel m_nodebankpanel; scene::basic_node * m_node { nullptr }; // currently bound scene node, if any }; diff --git a/editoruipanels.cpp b/editoruipanels.cpp index 517d36cc..4372cf78 100644 --- a/editoruipanels.cpp +++ b/editoruipanels.cpp @@ -281,3 +281,50 @@ itemproperties_panel::render_group() { return true; } + +nodebank_panel::nodebank_panel() : ui_panel("nodebank", true) { + size_min = { 100, 50 }; + size_max = { 1000, 1000 }; + title = "nodebank"; + + std::ifstream file; + file.open("nodebank.txt", std::ios_base::in | std::ios_base::binary); + + std::string line; + while (std::getline(file, line)) + if (line.size() > 2) + m_nodebank.push_back(std::make_unique(line)); +} + +void +nodebank_panel::render_contents() { + ImGui::RadioButton("modify", (int*)&mode, MODIFY); + ImGui::RadioButton("clone", (int*)&mode, COPY); + ImGui::RadioButton("add", (int*)&mode, ADD); + + ImGui::PushItemWidth(-1); + if (ImGui::ListBoxHeader("##nodebank", ImVec2(-1, -1))) + { + int i = 0; + for (auto const entry : m_nodebank) { + std::string label = *entry + "##" + std::to_string(i); + if (ImGui::Selectable(label.c_str(), entry == m_selectedtemplate)) + m_selectedtemplate = entry; + i++; + } + + ImGui::ListBoxFooter(); + } +} + +void nodebank_panel::add_template(const std::string &desc) { + std::ofstream file; + file.open("nodebank.txt", std::ios_base::out | std::ios_base::app | std::ios_base::binary); + file << desc; + + m_nodebank.push_back(std::make_unique(desc)); +} + +const std::string *nodebank_panel::get_active_template() { + return m_selectedtemplate.get(); +} diff --git a/editoruipanels.h b/editoruipanels.h index 9b2b723a..489292f0 100644 --- a/editoruipanels.h +++ b/editoruipanels.h @@ -33,3 +33,23 @@ private: std::string m_groupprefix; std::vector m_grouplines; }; + +class nodebank_panel : public ui_panel { + std::vector> m_nodebank; + std::shared_ptr m_selectedtemplate; + +public: + enum edit_mode { + MODIFY, + COPY, + ADD + }; + + edit_mode mode = MODIFY; + + nodebank_panel(); + + void render_contents() override; + void add_template(const std::string &desc); + const std::string* get_active_template(); +}; diff --git a/sceneeditor.cpp b/sceneeditor.cpp index c5843e48..99513935 100644 --- a/sceneeditor.cpp +++ b/sceneeditor.cpp @@ -23,7 +23,16 @@ namespace scene { void basic_editor::translate( scene::basic_node *Node, glm::dvec3 const &Location, bool const Snaptoground ) { - auto const initiallocation { Node->location() }; + auto &initiallocation { Node->location() }; + + // fixup NaNs + if (std::isnan(initiallocation.x)) + initiallocation.x = Location.x; + if (std::isnan(initiallocation.y)) + initiallocation.y = Location.y; + if (std::isnan(initiallocation.z)) + initiallocation.z = Location.z; + auto targetlocation { Location }; if( false == Snaptoground ) { targetlocation.y = initiallocation.y; diff --git a/scenenode.cpp b/scenenode.cpp index 23f8a8b4..1bb38c4d 100644 --- a/scenenode.cpp +++ b/scenenode.cpp @@ -743,7 +743,7 @@ basic_node::export_as_text( std::ostream &Output ) const { << ' ' << ( m_rangesquaredmax < std::numeric_limits::max() ? std::sqrt( m_rangesquaredmax ) : -1 ) << ' ' << std::sqrt( m_rangesquaredmin ) // name - << ' ' << m_name << ' '; + << ' ' << ((m_name.length() > 0) ? m_name : "none") << ' '; // template method implementation export_as_text_( Output ); } diff --git a/scenenode.h b/scenenode.h index cfd72c71..ba5a38c0 100644 --- a/scenenode.h +++ b/scenenode.h @@ -333,6 +333,8 @@ public: location( glm::dvec3 const Location ); glm::dvec3 const & location() const; + glm::dvec3 & + location(); float const & radius(); void @@ -383,6 +385,12 @@ basic_node::location() const { return m_area.center; } +inline +glm::dvec3 & +basic_node::location() { + return m_area.center; +} + inline void basic_node::visible( bool const Visible ) { diff --git a/simulation.cpp b/simulation.cpp index 1993963e..c5829d69 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -148,6 +148,11 @@ void state_manager::process_commands() { } } +TAnimModel * +state_manager::deserialize_model(cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata) { + return m_serializer.deserialize_model(Input, Scratchpad, Nodedata); +} + void state_manager::update_clocks() { diff --git a/simulation.h b/simulation.h index 2a36c5b4..358a126b 100644 --- a/simulation.h +++ b/simulation.h @@ -39,6 +39,10 @@ public: void process_commands(); + // temporary for editor + TAnimModel * + deserialize_model(cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata); + private: // members state_serializer m_serializer; diff --git a/simulationstateserializer.h b/simulationstateserializer.h index 6790b129..5a55bfe3 100644 --- a/simulationstateserializer.h +++ b/simulationstateserializer.h @@ -42,6 +42,9 @@ public: void export_as_text( std::string const &Scenariofile ) const; + // temporary public for editor + TAnimModel * deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); + private: // methods // restores class data from provided stream @@ -56,7 +59,7 @@ private: void deserialize_group( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_endgroup( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_light( cParser &Input, scene::scratch_data &Scratchpad ); - void deserialize_node( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_node( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_origin( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_endorigin( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_rotate( cParser &Input, scene::scratch_data &Scratchpad ); @@ -70,7 +73,6 @@ private: TTractionPowerSource * deserialize_tractionpowersource( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); TMemCell * deserialize_memorycell( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); TEventLauncher * deserialize_eventlauncher( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); - TAnimModel * deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); TDynamicObject * deserialize_dynamic( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); sound_source * deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); // skips content of stream until specified token diff --git a/uilayer.cpp b/uilayer.cpp index 52559fa4..67621c0f 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -39,7 +39,7 @@ void ui_panel::render() int flags = window_flags; if (flags == -1) flags = ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoCollapse | - ((size.x > 0 || size_min.x > 0) ? ImGuiWindowFlags_NoResize : 0); + ((size.x > 0) ? ImGuiWindowFlags_NoResize : 0); if (size.x > 0) ImGui::SetNextWindowSize(ImVec2(size.x, size.y)); From cbb61ce639684086d393ce70c011ef6f795e93a2 Mon Sep 17 00:00:00 2001 From: milek7 Date: Sat, 23 Feb 2019 14:44:21 +0100 Subject: [PATCH 2/5] make TAnimModel possible to delete --- AnimModel.cpp | 100 ++++++++++++++++++++++---------------------------- AnimModel.h | 22 +++++------ Event.cpp | 14 +++++-- Event.h | 2 +- Model3d.cpp | 4 +- Model3d.h | 2 +- Track.cpp | 4 +- 7 files changed, 69 insertions(+), 79 deletions(-) diff --git a/AnimModel.cpp b/AnimModel.cpp index a4f10bc4..bcc8f4bf 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -25,11 +25,10 @@ http://mozilla.org/MPL/2.0/. #include "Logs.h" #include "renderer.h" -TAnimContainer *TAnimModel::acAnimList = NULL; +std::list> TAnimModel::acAnimList; TAnimContainer::TAnimContainer() { - pNext = NULL; vRotateAngles = Math3D::vector3(0.0f, 0.0f, 0.0f); // aktualne kąty obrotu vDesiredAngles = Math3D::vector3(0.0f, 0.0f, 0.0f); // docelowe kąty obrotu fRotateSpeed = 0.0; @@ -39,15 +38,7 @@ TAnimContainer::TAnimContainer() fAngleSpeed = 0.0; pSubModel = NULL; iAnim = 0; // położenie początkowe - mAnim = NULL; // nie ma macierzy obrotu dla submodelu - evDone = NULL; // powiadamianie o zakończeniu animacji - acAnimNext = NULL; // na razie jest poza listą -} - -TAnimContainer::~TAnimContainer() -{ - SafeDelete(pNext); - delete mAnim; // AnimContainer jest właścicielem takich macierzy + evDone = NULL; // powiadamianie o zakończeniu animacji } bool TAnimContainer::Init(TSubModel *pNewSubModel) @@ -73,9 +64,8 @@ void TAnimContainer::SetRotateAnim( Math3D::vector3 vNewRotateAngles, double fNe // wyświetlania if (iAnim >= 0) { // jeśli nie jest jeszcze na liście animacyjnej - acAnimNext = TAnimModel::acAnimList; // pozostałe doklić sobie jako ogon - TAnimModel::acAnimList = this; // a wstawić się na początek - iAnim |= 0x80000000; // dodany do listy + TAnimModel::acAnimList.push_back(shared_from_this()); + iAnim |= 0x80000000; // dodany do listy } } } @@ -96,8 +86,7 @@ void TAnimContainer::SetTranslateAnim( Math3D::vector3 vNewTranslate, double fNe // wyświetlania if (iAnim >= 0) { // jeśli nie jest jeszcze na liście animacyjnej - acAnimNext = TAnimModel::acAnimList; // pozostałe doklić sobie jako ogon - TAnimModel::acAnimList = this; // a wstawić się na początek + TAnimModel::acAnimList.push_back(shared_from_this()); iAnim |= 0x80000000; // dodany do listy } } @@ -264,11 +253,6 @@ TAnimModel::TAnimModel( scene::node_data const &Nodedata ) : basic_node( Nodedat m_lightopacities.fill( 1.f ); } -TAnimModel::~TAnimModel() -{ - SafeDelete(pRoot); -} - bool TAnimModel::Init(std::string const &asName, std::string const &asReplacableTexture) { if( asReplacableTexture.substr( 0, 1 ) == "*" ) { @@ -383,32 +367,32 @@ bool TAnimModel::Load(cParser *parser, bool ter) return true; } -TAnimContainer * TAnimModel::AddContainer(std::string const &Name) +std::shared_ptr TAnimModel::AddContainer(std::string const &Name) { // dodanie sterowania submodelem dla egzemplarza if (!pModel) - return NULL; + return nullptr; TSubModel *tsb = pModel->GetFromName(Name); if (tsb) { - TAnimContainer *tmp = new TAnimContainer(); + auto tmp = std::make_shared(); tmp->Init(tsb); - tmp->pNext = pRoot; - pRoot = tmp; - return tmp; + m_animlist.push_back(tmp); + return tmp; } - return NULL; + return nullptr; } -TAnimContainer * TAnimModel::GetContainer(std::string const &Name) +std::shared_ptr TAnimModel::GetContainer(std::string const &Name) { // szukanie/dodanie sterowania submodelem dla egzemplarza if (true == Name.empty()) - return pRoot; // pobranie pierwszego (dla obrotnicy) - TAnimContainer *pCurrent; - for (pCurrent = pRoot; pCurrent != NULL; pCurrent = pCurrent->pNext) - // if (pCurrent->GetName()==pName) - if (Name == pCurrent->NameGet()) - return pCurrent; - return AddContainer(Name); + return m_animlist.front(); // pobranie pierwszego (dla obrotnicy) + + for (auto entry : m_animlist) { + if (entry->NameGet() == Name) + return entry; + } + + return AddContainer(Name); } // przeliczenie animacji - jednorazowo na klatkę @@ -485,13 +469,13 @@ void TAnimModel::RaAnimate( unsigned int const Framestamp ) { } // Ra 2F1I: to by można pomijać dla modeli bez animacji, których jest większość - TAnimContainer *pCurrent; - for (pCurrent = pRoot; pCurrent != nullptr; pCurrent = pCurrent->pNext) - if (!pCurrent->evDone) // jeśli jest bez eventu - pCurrent->UpdateModel(); // przeliczenie animacji każdego submodelu + for (auto entry : m_animlist) { + if (!entry->evDone) // jeśli jest bez eventu + entry->UpdateModel(); // przeliczenie animacji każdego submodelu + } m_framestamp = Framestamp; -}; +} void TAnimModel::RaPrepare() { // ustawia światła i animacje we wzorcu modelu przed renderowaniem egzemplarza @@ -555,12 +539,10 @@ void TAnimModel::RaPrepare() } TSubModel::iInstance = reinterpret_cast( this ); //żeby nie robić cudzych animacji TSubModel::pasText = &asText; // przekazanie tekstu do wyświetlacza (!!!! do przemyślenia) - TAnimContainer *pCurrent; - for (pCurrent = pRoot; pCurrent != NULL; pCurrent = pCurrent->pNext) - pCurrent->PrepareModel(); // ustawienie animacji egzemplarza dla każdego submodelu - // if () //tylko dla modeli z IK !!!! - // for (pCurrent=pRoot;pCurrent!=NULL;pCurrent=pCurrent->pNext) //albo osobny łańcuch - // pCurrent->UpdateModelIK(); //przeliczenie odwrotnej kinematyki + + for (auto entry : m_animlist) { + entry->PrepareModel(); + } } int TAnimModel::Flags() @@ -569,18 +551,19 @@ int TAnimModel::Flags() if( m_materialdata.replacable_skins[ 1 ] > 0 ) // jeśli ma wymienną teksturę 0 i |= (i & 0x01010001) * ((m_materialdata.textures_alpha & 1) ? 0x20 : 0x10); return i; -}; +} //--------------------------------------------------------------------------- int TAnimModel::TerrainCount() { // zliczanie kwadratów kilometrowych (główna linia po Next) do tworznia tablicy return pModel ? pModel->TerrainCount() : 0; -}; +} + TSubModel * TAnimModel::TerrainSquare(int n) { // pobieranie wskaźników do pierwszego submodelu return pModel ? pModel->TerrainSquare(n) : 0; -}; +} //--------------------------------------------------------------------------- void TAnimModel::LightSet(int const n, float const v) @@ -589,17 +572,20 @@ void TAnimModel::LightSet(int const n, float const v) return; // przekroczony zakres } lsLights[ n ] = v; -}; +} void TAnimModel::AnimUpdate(double dt) { // wykonanie zakolejkowanych animacji, nawet gdy modele nie są aktualnie wyświetlane - TAnimContainer *p = TAnimModel::acAnimList; - while( p ) { + acAnimList.remove_if([](std::weak_ptr ptr) + { + std::shared_ptr container = ptr.lock(); + if (!container) + return true; - p->UpdateModel(); - p = p->acAnimNext; // na razie bez usuwania z listy, bo głównie obrotnica na nią wchodzi - } -}; + container->UpdateModel(); // na razie bez usuwania z listy, bo głównie obrotnica na nią wchodzi + return false; + }); +} // radius() subclass details, calculates node's bounding radius float diff --git a/AnimModel.h b/AnimModel.h index 4c0a3cd1..16192fe1 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -45,7 +45,7 @@ class TAnimVocaloidFrame class basic_event; -class TAnimContainer +class TAnimContainer : std::enable_shared_from_this { // opakowanie submodelu, określające animację egzemplarza - obsługiwane jako lista friend TAnimModel; @@ -62,7 +62,7 @@ class TAnimContainer float fAngleCurrent; // parametr interpolacyjny: 0=start, 1=docelowy float fAngleSpeed; // zmiana parametru interpolacji w sekundach TSubModel *pSubModel; - float4x4 *mAnim; // macierz do animacji kwaternionowych + std::shared_ptr mAnim; // macierz do animacji kwaternionowych // dla kinematyki odwróconej używane są kwaterniony float fLength; // długość kości dla IK int iAnim; // animacja: +1-obrót Eulera, +2-przesuw, +4-obrót kwaternionem, +8-IK @@ -71,11 +71,8 @@ class TAnimContainer //+0x200: drugi stopień IK - dostosować do pozycji potomnego potomnego (wnuka) basic_event *evDone; // ewent wykonywany po zakończeniu animacji, np. zapór, obrotnicy public: - TAnimContainer *pNext; - TAnimContainer *acAnimNext; // lista animacji z eventem, które muszą być przeliczane również bez // wyświetlania - TAnimContainer(); - ~TAnimContainer(); + TAnimContainer(); bool Init(TSubModel *pNewSubModel); inline std::string NameGet() { @@ -112,14 +109,12 @@ class TAnimModel : public scene::basic_node { public: // constructors explicit TAnimModel( scene::node_data const &Nodedata ); -// destructor - ~TAnimModel(); // methods static void AnimUpdate( double dt ); bool Init(std::string const &asName, std::string const &asReplacableTexture); bool Load(cParser *parser, bool ter = false); - TAnimContainer * AddContainer(std::string const &Name); - TAnimContainer * GetContainer(std::string const &Name = ""); + std::shared_ptr AddContainer(std::string const &Name); + std::shared_ptr GetContainer(std::string const &Name = ""); void LightSet( int const n, float const v ); int TerrainCount(); TSubModel * TerrainSquare(int n); @@ -141,7 +136,10 @@ public: Angles() const { return vAngle; } // members - static TAnimContainer *acAnimList; // lista animacji z eventem, które muszą być przeliczane również bez wyświetlania + std::list> m_animlist; + + // lista animacji z eventem, które muszą być przeliczane również bez wyświetlania + static std::list> acAnimList; private: // methods @@ -158,7 +156,7 @@ private: void export_as_text_( std::ostream &Output ) const; // members - TAnimContainer *pRoot { nullptr }; // pojemniki sterujące, tylko dla aniomowanych submodeli + std::shared_ptr pRoot; // pojemniki sterujące, tylko dla aniomowanych submodeli TModel3d *pModel { nullptr }; glm::vec3 vAngle; // bazowe obroty egzemplarza względem osi material_data m_materialdata; diff --git a/Event.cpp b/Event.cpp index a2297eff..beee4d92 100644 --- a/Event.cpp +++ b/Event.cpp @@ -1291,8 +1291,8 @@ animation_event::init() { for( auto &target : m_targets ) { auto *targetmodel { static_cast( std::get( target ) ) }; if( targetmodel == nullptr ) { continue; } - auto *targetcontainer{ targetmodel->GetContainer( m_animationsubmodel ) }; - if( targetcontainer == nullptr ) { + auto targetcontainer{ targetmodel->GetContainer( m_animationsubmodel ) }; + if( !targetcontainer ) { m_ignored = true; ErrorLog( "Bad event: \"" + m_name + "\" (type: " + type() + ") can't find submodel " + m_animationsubmodel + " in model instance \"" + targetmodel->name() + "\"" ); break; @@ -1399,7 +1399,12 @@ animation_event::run_() { WriteLog( "Type: Animation" ); // animation modes target specific submodels - for( auto *targetcontainer : m_animationcontainers ) { + m_animationcontainers.remove_if([this](std::weak_ptr ptr) + { + auto targetcontainer = ptr.lock(); + if (!targetcontainer) + return true; + switch( m_animationtype ) { case 1: { // rotate targetcontainer->SetRotateAnim( @@ -1418,7 +1423,8 @@ animation_event::run_() { break; } } - } + return false; + }); } // export_as_text() subclass details diff --git a/Event.h b/Event.h index a6825159..a626edee 100644 --- a/Event.h +++ b/Event.h @@ -390,7 +390,7 @@ private: int m_animationtype{ 0 }; std::array m_animationparams{ 0.0 }; std::string m_animationsubmodel; - std::vector m_animationcontainers; + std::list> m_animationcontainers; std::string m_animationfilename; std::size_t m_animationfilesize{ 0 }; char *m_animationfiledata{ nullptr }; diff --git a/Model3d.cpp b/Model3d.cpp index 80c52e35..592e0ed3 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1036,8 +1036,8 @@ void TSubModel::RaAnimation(glm::mat4 &m, TAnimType a) } if (mAnimMatrix) // można by to dać np. do at_Translate { - m *= glm::make_mat4(mAnimMatrix->e); - mAnimMatrix = NULL; // jak animator będzie potrzebował, to ustawi ponownie + m *= glm::make_mat4(mAnimMatrix.get()->e); + mAnimMatrix.reset(); // jak animator będzie potrzebował, to ustawi ponownie } }; diff --git a/Model3d.h b/Model3d.h index 69a8cdd5..1a09118f 100644 --- a/Model3d.h +++ b/Model3d.h @@ -137,7 +137,7 @@ public: // chwilowo float m_boundingradius { 0 }; std::uintptr_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację TAnimType b_aAnim{ TAnimType::at_None }; // kody animacji oddzielnie, bo zerowane - float4x4 *mAnimMatrix{ nullptr }; // macierz do animacji kwaternionowych (należy do AnimContainer) + std::shared_ptr mAnimMatrix; // macierz do animacji kwaternionowych TSubModel **smLetter{ nullptr }; // wskaźnik na tablicę submdeli do generoania tekstu (docelowo zapisać do E3D) TSubModel *Parent{ nullptr }; // nadrzędny, np. do wymnażania macierzy int iVisible { 1 }; // roboczy stan widoczności diff --git a/Track.cpp b/Track.cpp index 0eb368dd..1219a95a 100644 --- a/Track.cpp +++ b/Track.cpp @@ -1101,7 +1101,7 @@ bool TTrack::InMovement() if (!SwitchExtension->CurrentIndex) return false; // 0=zablokowana się nie animuje // trzeba każdorazowo porównywać z kątem modelu - TAnimContainer *ac = ( + auto ac = ( SwitchExtension->pModel ? SwitchExtension->pModel->GetContainer() : nullptr ); @@ -1883,7 +1883,7 @@ TTrack * TTrack::RaAnimate() SwitchExtension->CurrentIndex) // 0=zablokowana się nie animuje { // trzeba każdorazowo porównywać z kątem modelu // //pobranie kąta z modelu - TAnimContainer *ac = ( + auto ac = ( SwitchExtension->pModel ? SwitchExtension->pModel->GetContainer() : // pobranie głównego submodelu nullptr ); From 37dc56a8d8b1af8a84b7d877a64b7c74a9ad89db Mon Sep 17 00:00:00 2001 From: milek7 Date: Sat, 23 Feb 2019 15:35:23 +0100 Subject: [PATCH 3/5] node delete in editor --- Names.h | 10 ++++++++++ editormode.cpp | 20 ++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Names.h b/Names.h index 67b64068..f7b10f64 100644 --- a/Names.h +++ b/Names.h @@ -62,6 +62,16 @@ public: m_itemmap.erase(lookup); } + void purge (Type_ *Item) + { + for (auto it = m_items.begin(); it != m_items.end(); it++) { + if (*it == Item) { + delete *it; + *it = nullptr; + return; + } + } + } // locates item with specified name. returns pointer to the item, or nullptr Type_ * find( std::string const &Name ) const { diff --git a/editormode.cpp b/editormode.cpp index d99d4fa1..71e14e96 100644 --- a/editormode.cpp +++ b/editormode.cpp @@ -180,6 +180,22 @@ editor_mode::on_key( int const Key, int const Scancode, int const Action, int co break; } + case GLFW_KEY_DELETE: { + TAnimModel *model = dynamic_cast(m_node); + if (!model) + break; + + m_node = nullptr; + m_dragging = false; + Application.set_cursor( GLFW_CURSOR_NORMAL ); + static_cast( m_userinterface.get() )->set_node(nullptr); + + simulation::Region->erase(model); + simulation::Instances.purge(model); + + break; + } + default: { break; } @@ -231,13 +247,13 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods if( Action == GLFW_PRESS ) { // left button press - auto const mode = dynamic_cast( m_userinterface.get() )->mode(); + auto const mode = static_cast( m_userinterface.get() )->mode(); m_node = nullptr; GfxRenderer.pick_node([this, mode](scene::basic_node *node) { - editor_ui *ui = dynamic_cast( m_userinterface.get() ); + editor_ui *ui = static_cast( m_userinterface.get() ); if (mode == nodebank_panel::MODIFY) { if (!m_dragging) From 41548af003615f3bed7c9eaae849b411a1e96127 Mon Sep 17 00:00:00 2001 From: milek7 Date: Sat, 23 Feb 2019 23:41:25 +0100 Subject: [PATCH 4/5] improvements --- editormode.cpp | 1 + sceneeditor.cpp | 1 + scenenode.h | 5 ++ scenenodegroups.cpp | 22 +++++-- scenenodegroups.h | 2 +- simulationstateserializer.cpp | 119 +++++++++++++++++++--------------- simulationstateserializer.h | 3 +- 7 files changed, 94 insertions(+), 59 deletions(-) diff --git a/editormode.cpp b/editormode.cpp index 71e14e96..ef9b897f 100644 --- a/editormode.cpp +++ b/editormode.cpp @@ -300,6 +300,7 @@ editor_mode::on_mouse_button( int const Button, int const Action, int const Mods if (!cloned) return; + cloned->mark_dirty(); cloned->location(Camera.Pos + GfxRenderer.Mouse_Position()); simulation::Instances.insert(cloned); simulation::Region->insert(cloned); diff --git a/sceneeditor.cpp b/sceneeditor.cpp index 99513935..6b730a83 100644 --- a/sceneeditor.cpp +++ b/sceneeditor.cpp @@ -40,6 +40,7 @@ basic_editor::translate( scene::basic_node *Node, glm::dvec3 const &Location, bo // NOTE: bit of a waste for single nodes, for the sake of less varied code down the road auto const translation { targetlocation - initiallocation }; + Node->mark_dirty(); if( Node->group() == null_handle ) { translate_node( Node, Node->location() + translation ); } diff --git a/scenenode.h b/scenenode.h index ba5a38c0..18da3158 100644 --- a/scenenode.h +++ b/scenenode.h @@ -345,6 +345,10 @@ public: group( scene::group_handle Group ); scene::group_handle group() const; + void + mark_dirty() { m_dirty = true; } + bool + dirty() const { return m_dirty; } protected: // members @@ -354,6 +358,7 @@ protected: double m_rangesquaredmax { 0.0 }; // visibility range, max bool m_visible { true }; // visibility flag std::string m_name; + bool m_dirty { false }; private: // methods diff --git a/scenenodegroups.cpp b/scenenodegroups.cpp index 58031caf..f45c5ecb 100644 --- a/scenenodegroups.cpp +++ b/scenenodegroups.cpp @@ -119,24 +119,38 @@ node_groups::insert( scene::group_handle const Group, basic_event *Event ) { // sends basic content of the class in legacy (text) format to provided stream void -node_groups::export_as_text( std::ostream &Output ) const { +node_groups::export_as_text( std::ostream &Output, bool Dirty ) const { for( auto const &group : m_groupmap ) { - - Output << "group\n"; + bool any = false; for( auto *node : group.second.nodes ) { + if (node->dirty() != Dirty) + continue; // HACK: auto-generated memory cells aren't exported, so we check for this // TODO: is_exportable as basic_node method if( ( typeid( *node ) == typeid( TMemCell ) ) && ( false == static_cast( node )->is_exportable ) ) { continue; } + + if (!any) + Output << "group\n"; + any = true; + node->export_as_text( Output ); } for( auto *event : group.second.events ) { + if (Dirty) + continue; + + if (!any) + Output << "group\n"; + any = true; + event->export_as_text( Output ); } - Output << "endgroup\n"; + if (any) + Output << "endgroup\n"; } } diff --git a/scenenodegroups.h b/scenenodegroups.h index d0815bae..9f040409 100644 --- a/scenenodegroups.h +++ b/scenenodegroups.h @@ -48,7 +48,7 @@ public: return m_groupmap[ Group ]; } // sends basic content of the class in legacy (text) format to provided stream void - export_as_text( std::ostream &Output ) const; + export_as_text(std::ostream &Output , bool Dirty) const; private: // types diff --git a/simulationstateserializer.cpp b/simulationstateserializer.cpp index a601893c..7294ab27 100644 --- a/simulationstateserializer.cpp +++ b/simulationstateserializer.cpp @@ -952,7 +952,7 @@ state_serializer::transform( glm::dvec3 Location, scene::scratch_data const &Scr // stores class data in specified file, in legacy (text) format void -state_serializer::export_as_text( std::string const &Scenariofile ) const { +state_serializer::export_as_text(std::string const &Scenariofile) const { if( Scenariofile == "$.scn" ) { ErrorLog( "Bad file: scenery export not supported for file \"$.scn\"" ); @@ -961,66 +961,79 @@ state_serializer::export_as_text( std::string const &Scenariofile ) const { WriteLog( "Scenery data export in progress..." ); } - auto filename { Scenariofile }; - while( filename[ 0 ] == '$' ) { + auto filename { Scenariofile }; + while( filename[ 0 ] == '$' ) { // trim leading $ char rainsted utility may add to the base name for modified .scn files - filename.erase( 0, 1 ); + filename.erase( 0, 1 ); } - erase_extension( filename ); - filename = Global.asCurrentSceneryPath + filename + "_export"; + erase_extension( filename ); + auto absfilename = Global.asCurrentSceneryPath + filename + "_export"; - std::ofstream scmfile { filename + ".scm" }; - // groups - scmfile << "// groups\n"; - scene::Groups.export_as_text( scmfile ); - // tracks - scmfile << "// paths\n"; - for( auto const *path : Paths.sequence() ) { - if( path->group() == null_handle ) { - path->export_as_text( scmfile ); - } - } - // traction - scmfile << "// traction\n"; - for( auto const *traction : Traction.sequence() ) { - if( traction->group() == null_handle ) { - traction->export_as_text( scmfile ); - } - } - // power grid - scmfile << "// traction power sources\n"; - for( auto const *powersource : Powergrid.sequence() ) { - if( powersource->group() == null_handle ) { - powersource->export_as_text( scmfile ); - } - } - // models - scmfile << "// instanced models\n"; - for( auto const *instance : Instances.sequence() ) { - if( instance->group() == null_handle ) { - instance->export_as_text( scmfile ); - } - } - // sounds - // NOTE: sounds currently aren't included in groups - scmfile << "// sounds\n"; - Region->export_as_text( scmfile ); + std::ofstream scmdirtyfile { absfilename + "_dirty.scm" }; + export_nodes_to_stream(scmdirtyfile, true); - std::ofstream ctrfile { filename + ".ctr" }; - // mem cells - ctrfile << "// memory cells\n"; - for( auto const *memorycell : Memory.sequence() ) { - if( ( true == memorycell->is_exportable ) - && ( memorycell->group() == null_handle ) ) { - memorycell->export_as_text( ctrfile ); - } - } - // events - Events.export_as_text( ctrfile ); + std::ofstream scmfile { absfilename + ".scm" }; + export_nodes_to_stream(scmfile, false); + + // sounds + // NOTE: sounds currently aren't included in groups + scmfile << "// sounds\n"; + Region->export_as_text( scmfile ); + + scmfile << "// modified objects\ninclude " << filename << "_export_dirty.scm\n"; + + std::ofstream ctrfile { absfilename + ".ctr" }; + // mem cells + ctrfile << "// memory cells\n"; + for( auto const *memorycell : Memory.sequence() ) { + if( ( true == memorycell->is_exportable ) + && ( memorycell->group() == null_handle ) ) { + memorycell->export_as_text( ctrfile ); + } + } + + // events + Events.export_as_text( ctrfile ); WriteLog( "Scenery data export done." ); } +void +state_serializer::export_nodes_to_stream(std::ostream &scmfile, bool Dirty) const { + // groups + scmfile << "// groups\n"; + scene::Groups.export_as_text( scmfile, Dirty ); + + // tracks + scmfile << "// paths\n"; + for( auto const *path : Paths.sequence() ) { + if( path->dirty() == Dirty && path->group() == null_handle ) { + path->export_as_text( scmfile ); + } + } + // traction + scmfile << "// traction\n"; + for( auto const *traction : Traction.sequence() ) { + if( traction->dirty() == Dirty && traction->group() == null_handle ) { + traction->export_as_text( scmfile ); + } + } + // power grid + scmfile << "// traction power sources\n"; + for( auto const *powersource : Powergrid.sequence() ) { + if( powersource->dirty() == Dirty && powersource->group() == null_handle ) { + powersource->export_as_text( scmfile ); + } + } + // models + scmfile << "// instanced models\n"; + for( auto const *instance : Instances.sequence() ) { + if( instance && instance->dirty() == Dirty && instance->group() == null_handle ) { + instance->export_as_text( scmfile ); + } + } +} + } // simulation //--------------------------------------------------------------------------- diff --git a/simulationstateserializer.h b/simulationstateserializer.h index 5a55bfe3..a73b858a 100644 --- a/simulationstateserializer.h +++ b/simulationstateserializer.h @@ -40,7 +40,7 @@ public: deserialize_continue(std::shared_ptr state); // stores class data in specified file, in legacy (text) format void - export_as_text( std::string const &Scenariofile ) const; + export_as_text(std::string const &Scenariofile) const; // temporary public for editor TAnimModel * deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata ); @@ -79,6 +79,7 @@ private: void skip_until( cParser &Input, std::string const &Token ); // transforms provided location by specifed rotation and offset glm::dvec3 transform( glm::dvec3 Location, scene::scratch_data const &Scratchpad ); + void export_nodes_to_stream(std::ostream &, bool Dirty) const; }; } // simulation From 22261c8e5b6e3185c0a8409b0ac127daa51998c0 Mon Sep 17 00:00:00 2001 From: milek7 Date: Sat, 2 Mar 2019 14:49:28 +0100 Subject: [PATCH 5/5] fix TAnimModel::GetContainer crash if animlist empty --- AnimModel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AnimModel.cpp b/AnimModel.cpp index bcc8f4bf..c55fc68a 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -384,8 +384,8 @@ std::shared_ptr TAnimModel::AddContainer(std::string const &Name std::shared_ptr TAnimModel::GetContainer(std::string const &Name) { // szukanie/dodanie sterowania submodelem dla egzemplarza - if (true == Name.empty()) - return m_animlist.front(); // pobranie pierwszego (dla obrotnicy) + if (true == Name.empty()) + return (!m_animlist.empty()) ? m_animlist.front() : nullptr; // pobranie pierwszego (dla obrotnicy) for (auto entry : m_animlist) { if (entry->NameGet() == Name)