mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-17 23:39:18 +02:00
Merge branch 'nodecloner' into sim
This commit is contained in:
102
AnimModel.cpp
102
AnimModel.cpp
@@ -25,11 +25,10 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "Logs.h"
|
||||
#include "renderer.h"
|
||||
|
||||
TAnimContainer *TAnimModel::acAnimList = NULL;
|
||||
std::list<std::weak_ptr<TAnimContainer>> 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<TAnimContainer> 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<TAnimContainer>();
|
||||
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<TAnimContainer> 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);
|
||||
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)
|
||||
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<std::uintptr_t>( 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,7 +572,7 @@ void TAnimModel::LightSet(int const n, float const v)
|
||||
return; // przekroczony zakres
|
||||
}
|
||||
lsLights[ n ] = v;
|
||||
};
|
||||
}
|
||||
|
||||
std::optional<std::tuple<float, float, std::optional<glm::vec3>> > TAnimModel::LightGet(const int n)
|
||||
{
|
||||
@@ -611,13 +594,16 @@ std::optional<std::tuple<float, float, std::optional<glm::vec3>> > TAnimModel::L
|
||||
|
||||
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<TAnimContainer> ptr)
|
||||
{
|
||||
std::shared_ptr<TAnimContainer> 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
|
||||
|
||||
22
AnimModel.h
22
AnimModel.h
@@ -45,7 +45,7 @@ class TAnimVocaloidFrame
|
||||
|
||||
class basic_event;
|
||||
|
||||
class TAnimContainer
|
||||
class TAnimContainer : std::enable_shared_from_this<TAnimContainer>
|
||||
{ // 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<float4x4> 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<TAnimContainer> AddContainer(std::string const &Name);
|
||||
std::shared_ptr<TAnimContainer> GetContainer(std::string const &Name = "");
|
||||
void LightSet( int const n, float const v );
|
||||
std::optional<std::tuple<float, float, std::optional<glm::vec3> > > LightGet( int const n );
|
||||
int TerrainCount();
|
||||
@@ -142,7 +137,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<std::shared_ptr<TAnimContainer>> m_animlist;
|
||||
|
||||
// lista animacji z eventem, które muszą być przeliczane również bez wyświetlania
|
||||
static std::list<std::weak_ptr<TAnimContainer>> acAnimList;
|
||||
|
||||
private:
|
||||
// methods
|
||||
@@ -159,7 +157,7 @@ private:
|
||||
void export_as_text_( std::ostream &Output ) const;
|
||||
|
||||
// members
|
||||
TAnimContainer *pRoot { nullptr }; // pojemniki sterujące, tylko dla aniomowanych submodeli
|
||||
std::shared_ptr<TAnimContainer> pRoot; // pojemniki sterujące, tylko dla aniomowanych submodeli
|
||||
TModel3d *pModel { nullptr };
|
||||
glm::vec3 vAngle; // bazowe obroty egzemplarza względem osi
|
||||
material_data m_materialdata;
|
||||
|
||||
14
Event.cpp
14
Event.cpp
@@ -1291,8 +1291,8 @@ animation_event::init() {
|
||||
for( auto &target : m_targets ) {
|
||||
auto *targetmodel { static_cast<TAnimModel *>( std::get<scene::basic_node *>( 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<TAnimContainer> 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
|
||||
|
||||
2
Event.h
2
Event.h
@@ -390,7 +390,7 @@ private:
|
||||
int m_animationtype{ 0 };
|
||||
std::array<double, 4> m_animationparams{ 0.0 };
|
||||
std::string m_animationsubmodel;
|
||||
std::vector<TAnimContainer *> m_animationcontainers;
|
||||
std::list<std::weak_ptr<TAnimContainer>> m_animationcontainers;
|
||||
std::string m_animationfilename;
|
||||
std::size_t m_animationfilesize{ 0 };
|
||||
char *m_animationfiledata{ nullptr };
|
||||
|
||||
@@ -1060,8 +1060,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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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<float4x4> 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
|
||||
|
||||
11
Names.h
11
Names.h
@@ -69,6 +69,17 @@ public:
|
||||
lookup->second :
|
||||
-1 );
|
||||
}
|
||||
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 {
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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() {
|
||||
@@ -179,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<TAnimModel*>(m_node);
|
||||
if (!model)
|
||||
break;
|
||||
|
||||
m_node = nullptr;
|
||||
m_dragging = false;
|
||||
Application.set_cursor( GLFW_CURSOR_NORMAL );
|
||||
static_cast<editor_ui*>( m_userinterface.get() )->set_node(nullptr);
|
||||
|
||||
simulation::Region->erase(model);
|
||||
simulation::Instances.purge(model);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
@@ -229,19 +246,75 @@ 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<editor_ui*>( m_userinterface.get() )->set_node( m_node );
|
||||
});
|
||||
m_dragging = true;
|
||||
// left button press
|
||||
auto const mode = static_cast<editor_ui*>( m_userinterface.get() )->mode();
|
||||
|
||||
m_node = nullptr;
|
||||
|
||||
GfxRenderer.pick_node([this, mode](scene::basic_node *node)
|
||||
{
|
||||
editor_ui *ui = static_cast<editor_ui*>( 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->mark_dirty();
|
||||
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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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<std::string>(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<std::string>(desc));
|
||||
}
|
||||
|
||||
const std::string *nodebank_panel::get_active_template() {
|
||||
return m_selectedtemplate.get();
|
||||
}
|
||||
|
||||
@@ -33,3 +33,23 @@ private:
|
||||
std::string m_groupprefix;
|
||||
std::vector<text_line> m_grouplines;
|
||||
};
|
||||
|
||||
class nodebank_panel : public ui_panel {
|
||||
std::vector<std::shared_ptr<std::string>> m_nodebank;
|
||||
std::shared_ptr<std::string> 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();
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
@@ -31,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 );
|
||||
}
|
||||
|
||||
@@ -743,7 +743,7 @@ basic_node::export_as_text( std::ostream &Output ) const {
|
||||
<< ' ' << ( m_rangesquaredmax < std::numeric_limits<double>::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 );
|
||||
}
|
||||
|
||||
13
scenenode.h
13
scenenode.h
@@ -333,6 +333,8 @@ public:
|
||||
location( glm::dvec3 const Location );
|
||||
glm::dvec3 const &
|
||||
location() const;
|
||||
glm::dvec3 &
|
||||
location();
|
||||
float const &
|
||||
radius();
|
||||
void
|
||||
@@ -343,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
|
||||
@@ -352,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
|
||||
@@ -383,6 +390,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 ) {
|
||||
|
||||
@@ -145,24 +145,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<TMemCell *>( 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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,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
|
||||
|
||||
@@ -175,6 +175,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() {
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -956,7 +956,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\"" );
|
||||
@@ -965,66 +965,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
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -40,7 +40,10 @@ public:
|
||||
deserialize_continue(std::shared_ptr<deserializer_state> 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 );
|
||||
|
||||
private:
|
||||
// methods
|
||||
@@ -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 );
|
||||
void init_time();
|
||||
@@ -78,6 +80,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
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user