save project (WIP), bugs in conection ids and missing image resources

This commit is contained in:
Anthony Rabine 2023-12-17 15:39:40 +01:00
parent 79445e1505
commit 308586d4b8
7 changed files with 273 additions and 12 deletions

View file

@ -47,7 +47,7 @@ void BaseNode::DeleteOutput()
m_node->Outputs.pop_back();
}
void BaseNode::SetPosition(int x, int y)
void BaseNode::SetPosition(float x, float y)
{
m_pos.x = x;
m_pos.y = y;
@ -93,3 +93,15 @@ void BaseNode::DrawPins()
ed::EndPin();
}
}
float BaseNode::GetX() const
{
auto pos = GetNodePosition(m_node->ID);
return pos.x;
}
float BaseNode::GetY() const
{
auto pos = GetNodePosition(m_node->ID);
return pos.y;
}

View file

@ -97,8 +97,8 @@ class BaseNode
public:
struct NodePosition
{
int x;
int y;
float x;
float y;
};
BaseNode(const std::string &title, IStoryProject &proj);
@ -108,13 +108,17 @@ public:
virtual void DrawProperties() = 0;
void SetPosition(int x, int y);
void SetPosition(float x, float y);
void FrameStart();
void FrameEnd();
void DrawPins();
float GetX() const;
float GetY() const;
uint32_t Inputs() const { return m_node->Inputs.size(); }
uint32_t Outputs() const { return m_node->Outputs.size(); }
void SetType(const std::string &type)
@ -134,9 +138,8 @@ public:
void seTitle(const std::string &title) { m_title = title; }
std::string getTitle() const { return m_title; }
virtual void FromJson(nlohmann::json &) {
// default implementation does nothing
}
virtual void FromJson(const nlohmann::json &) = 0;
virtual void ToJson(nlohmann::json &) = 0;
virtual nlohmann::json ToJson() const {
nlohmann::json j;
@ -179,6 +182,36 @@ public:
return id;
}
bool HasInputPinId(ed::PinId &pinId, int &atIndex) const
{
bool found = false;
atIndex = 0;
for (auto &i : m_node->Inputs)
{
if (i.ID == pinId)
{
found = true;
}
atIndex++;
}
return found;
}
bool HasOnputPinId(ed::PinId &pinId, int &atIndex) const
{
bool found = false;
atIndex = 0;
for (auto &i : m_node->Outputs)
{
if (i.ID == pinId)
{
found = true;
}
atIndex++;
}
return found;
}
void AddInput();
void AddOutputs(int num = 1);

View file

@ -78,6 +78,11 @@ void MainWindow::DrawMainMenuBar()
showOpenProject = true;
}
if (ImGui::MenuItem("Save project"))
{
SaveProject();
}
if (ImGui::MenuItem("Close project"))
{
CloseProject();
@ -138,9 +143,10 @@ void MainWindow::DrawMainMenuBar()
if (ImGui::BeginPopupModal("AboutPopup", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::Text("Story Editor V1");
ImGui::Text("Story Editor V2");
ImGui::Separator();
ImGui::TextColored(ImVec4(1.0f, 0.0f, 1.0f, 1.0f), "Platform");
ImGui::Text("http://www.openstoryteller.org");
// ImGui::Text("%s", SDL_GetPlatform());
// ImGui::Text("CPU cores: %d", SDL_GetCPUCount());
// ImGui::Text("RAM: %.2f GB", SDL_GetSystemRAM() / 1024.0f);
@ -550,7 +556,8 @@ void MainWindow::NewProjectPopup()
void MainWindow::SaveProject()
{
nlohmann::json model; // = m_model.Save();
nlohmann::json model;
m_nodeEditorWindow.Save(model);
m_project.Save(model, m_resources);
}

View file

@ -101,7 +101,7 @@ void MediaNode::Draw()
},
*/
void MediaNode::FromJson(nlohmann::json &j)
void MediaNode::FromJson(const nlohmann::json &j)
{
m_image.name = j["image"].get<std::string>();
@ -111,6 +111,12 @@ void MediaNode::FromJson(nlohmann::json &j)
m_soundPath = m_project.BuildFullAssetsPath(m_soundName);
}
void MediaNode::ToJson(nlohmann::json &j)
{
j["image"] = m_image.name;
j["sound"] = m_soundName;
}
void MediaNode::DrawProperties()
{
ImGui::AlignTextToFramePadding();

View file

@ -18,7 +18,8 @@ public:
void Draw() override;
virtual void FromJson(nlohmann::json &j) override;
virtual void FromJson(const nlohmann::json &j) override;
virtual void ToJson(nlohmann::json &j) override;
virtual void DrawProperties() override;

View file

@ -54,7 +54,7 @@ void NodeEditorWindow::LoadNode(const nlohmann::json &nodeJson)
n->SetId(restoredNodeId);
nlohmann::json posJson = nodeJson["position"];
n->SetOutputs(nodeJson["outPortCount"].get<int>());
n->SetPosition(posJson["x"].get<int>(), posJson["y"].get<int>());
n->SetPosition(posJson["x"].get<float>(), posJson["y"].get<float>());
n->FromJson(internalDataJson);
m_nodes[n->GetInternalId()] = n;
@ -146,6 +146,204 @@ void NodeEditorWindow::Load(const nlohmann::json &model)
}
}
void NodeEditorWindow::Save(nlohmann::json &model)
{
ed::SetCurrentEditor(m_context);
// Save nodes
nlohmann::json nodes = nlohmann::json::array();
for (const auto & n : m_nodes)
{
nlohmann::json node;
node["id"] = n.second->GetId();
node["type"] = n.second->GetType();
node["outPortCount"] = n.second->Outputs();
node["inPortCount"] = n.second->Inputs();
nlohmann::json position;
position["x"] = n.second->GetX();
position["y"] = n.second->GetY();
nlohmann::json internalData;
n.second->ToJson(internalData);
node["position"] = position;
node["internal-data"] = internalData;
}
model["nodes"] = nodes;
// Save links
nlohmann::json connections = nlohmann::json::array();
for (const auto& linkInfo : m_links)
{
nlohmann::json c;
int index;
for (const auto & n : m_nodes)
{
if (n.second->HasOnputPinId(linkInfo->OutputId, index))
{
c["outNodeId"] = n.second->GetId();
c["outPortIndex"] = index;
}
if (n.second->HasInputPinId(linkInfo->InputId, index))
{
c["inNodeId"] = n.second->GetId();
c["inPortIndex"] = index;
}
}
connections.push_back(c);
ed::Link(linkInfo->Id, linkInfo->OutputId, linkInfo->InputId);
}
model["connections"] = connections;
ed::SetCurrentEditor(nullptr);
}
/*
std::string NodeEditorWindow::ChoiceLabel() const
{
std::stringstream ss;
ss << "mediaChoice" << std::setw(4) << std::setfill('0') << GetId();
return ss.str();
}
std::string NodeEditorWindow::EntryLabel() const
{
std::stringstream ss;
ss << ".mediaEntry" << std::setw(4) << std::setfill('0') << getNodeId();
return ss.str();
}
std::string NodeEditorWindow::GenerateConstants()
{
std::string s;
std::string image = m_mediaData["image"].get<std::string>();
std::string sound = m_mediaData["sound"].get<std::string>();
if (image.size() > 0)
{
s = StoryProject::FileToConstant(image, ".qoi"); // FIXME: Generate the extension setup in user option of output format
}
if (sound.size() > 0)
{
s += StoryProject::FileToConstant(sound, ".wav"); // FIXME: Generate the extension setup in user option of output format
}
int nb_out_conns = ComputeOutputConnections();
if (nb_out_conns > 1)
{
// Generate choice table if needed (out ports > 1)
std::stringstream ss;
std::string label = ChoiceLabel();
ss << "$" << label
<< " DC32, "
<< nb_out_conns << ", ";
std::unordered_set<ConnectionId> conns = m_model.allConnectionIds(getNodeId());
int i = 0;
for (auto & c : conns)
{
std::stringstream ssChoice;
// On va chercher le label d'entrée du noeud connecté à l'autre bout
ss << m_model.GetNodeEntryLabel(c.inNodeId);
if (i < (nb_out_conns - 1))
{
ss << ", ";
}
else
{
ss << "\n";
}
i++;
}
s += ss.str();
}
return s;
}
std::string NodeEditorWindow::Build()
{
std::stringstream ss;
int nb_out_conns = ComputeOutputConnections();
ss << R"(; ---------------------------- )"
<< GetNodeTitle()
<< " Type: "
<< (nb_out_conns == 0 ? "End" : nb_out_conns == 1 ? "Transition" : "Choice")
<< "\n";
std::string image = StoryProject::RemoveFileExtension(m_mediaData["image"].get<std::string>());
std::string sound = StoryProject::RemoveFileExtension(m_mediaData["sound"].get<std::string>());
// Le label de ce noeud est généré de la façon suivante :
// "media" + Node ID + id du noeud parent. Si pas de noeud parent, alors rien
ss << EntryLabel() << ":\n";
if (image.size() > 0)
{
ss << "lcons r0, $" << image << "\n";
}
else
{
ss << "lcons r0, 0\n";
}
if (sound.size() > 0)
{
ss << "lcons r1, $" << sound << "\n";
}
else
{
ss << "lcons r1, 0\n";
}
// Call the media executor (image, sound)
ss << "syscall 1\n";
// Check output connections number
// == 0: end node : generate halt
// == 1: transition node : image + sound on demand, jump directly to the other node when OK
// > 1 : choice node : call the node choice manager
if (nb_out_conns == 0) // End node
{
ss << "halt\n";
}
else if (nb_out_conns == 1) // Transition node
{
std::unordered_set<ConnectionId> conns = m_model.allConnectionIds(getNodeId());
for (auto c : conns)
{
if (c.outNodeId == getNodeId())
{
// On place dans R0 le prochain noeud à exécuter en cas de OK
ss << "lcons r0, "
<< m_model.GetNodeEntryLabel(c.inNodeId) << "\n"
<< "ret\n";
}
}
}
else // Choice node
{
ss << "lcons r0, $" << ChoiceLabel() << "\n"
<< "jump .media ; no return possible, so a jump is enough";
}
return ss.str();
}
*/
std::shared_ptr<BaseNode> NodeEditorWindow::GetSelectedNode()
{
std::shared_ptr<BaseNode> selected;

View file

@ -46,8 +46,10 @@ public:
void Initialize();
void Clear();
void Load(const nlohmann::json &model);
void Save(nlohmann::json &model);
std::shared_ptr<BaseNode> GetSelectedNode();
std::string GenerateConstants();
private:
IStoryProject &m_project;
@ -102,5 +104,7 @@ private:
void LoadNode(const nlohmann::json &nodeJson);
ed::PinId GetInputPin(unsigned long modelNodeId, int pinIndex);
ed::PinId GetOutputPin(unsigned long modelNodeId, int pinIndex);
std::string ChoiceLabel() const;
std::string EntryLabel() const;
};