mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-07 01:15:14 +01:00
335 lines
8.3 KiB
C++
335 lines
8.3 KiB
C++
#include "media_node.h"
|
|
|
|
namespace ed = ax::NodeEditor;
|
|
#include "IconsMaterialDesignIcons.h"
|
|
#include "story_project.h"
|
|
|
|
MediaNode::MediaNode(const std::string &title, IStoryManager &proj)
|
|
: BaseNode(title, proj)
|
|
, m_story(proj)
|
|
{
|
|
Gui::LoadRawImage("fairy.png", m_image);
|
|
|
|
// Create defaut one input and one output
|
|
AddInput();
|
|
AddOutputs(1);
|
|
|
|
|
|
std::string widgetId = std::to_string(GetInternalId()); // Make widget unique by using the node ID
|
|
|
|
m_buttonUniqueName = "Play " ICON_MDI_PLAY "##id" + widgetId;
|
|
|
|
}
|
|
|
|
void MediaNode::Draw()
|
|
{
|
|
BaseNode::FrameStart();
|
|
|
|
|
|
static ImGuiTableFlags flags = ImGuiTableFlags_Borders |
|
|
ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_SizingFixedFit;
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(10.0f, 10.0f));
|
|
if (ImGui::BeginTable("table1", 1, flags))
|
|
{
|
|
ImGui::TableNextRow();
|
|
ImU32 bg_color = ImGui::GetColorU32(ImVec4(0.3f, 0.3f, 0.7f, 1.0f));
|
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, bg_color);
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGui::TextUnformatted("Media node");
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::PopStyleVar();
|
|
|
|
|
|
if (m_image.Valid())
|
|
{
|
|
ImGui::Image(m_image.texture, ImVec2(320, 240));
|
|
}
|
|
else
|
|
{
|
|
ImGui::Dummy(ImVec2(320, 240));
|
|
}
|
|
|
|
// Use AlignTextToFramePadding() to align text baseline to the baseline of framed elements
|
|
// (otherwise a Text+SameLine+Button sequence will have the text a little too high by default)
|
|
|
|
|
|
|
|
// Use AlignTextToFramePadding() to align text baseline to the baseline of framed elements
|
|
// (otherwise a Text+SameLine+Button sequence will have the text a little too high by default)
|
|
|
|
|
|
ImGui::AlignTextToFramePadding();
|
|
ImGui::Text("Outputs:");
|
|
ImGui::SameLine();
|
|
|
|
// Arrow buttons with Repeater
|
|
|
|
uint32_t counter = Outputs();
|
|
float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
|
|
ImGui::PushButtonRepeat(true);
|
|
std::string leftSingle = "##left" + std::to_string(GetId());
|
|
if (ImGui::ArrowButton(leftSingle.c_str(), ImGuiDir_Left)) { if (counter > 1) counter--; }
|
|
ImGui::SameLine(0.0f, spacing);
|
|
|
|
std::string rightSingle = "##right" + std::to_string(GetId());
|
|
if (ImGui::ArrowButton(rightSingle.c_str(), ImGuiDir_Right))
|
|
{
|
|
counter++;
|
|
}
|
|
ImGui::PopButtonRepeat();
|
|
ImGui::SameLine();
|
|
ImGui::Text("%d", counter);
|
|
|
|
SetOutputs(counter);
|
|
|
|
DrawPins();
|
|
|
|
BaseNode::FrameEnd();
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
"internal-data": {
|
|
"image": "fairy.png",
|
|
"sound": "la_fee_luminelle.mp3"
|
|
},
|
|
|
|
*/
|
|
void MediaNode::FromJson(const nlohmann::json &j)
|
|
{
|
|
SetImage(j["image"].get<std::string>());
|
|
SetSound(j["sound"].get<std::string>());
|
|
}
|
|
|
|
void MediaNode::ToJson(nlohmann::json &j)
|
|
{
|
|
j["image"] = m_image.name;
|
|
j["sound"] = m_soundName;
|
|
}
|
|
|
|
void MediaNode::DrawProperties()
|
|
{
|
|
ImGui::AlignTextToFramePadding();
|
|
ImGui::Text("Image");
|
|
ImGui::SameLine();
|
|
|
|
ImGui::Text("%s", m_image.name.c_str());
|
|
|
|
ImGui::SameLine();
|
|
|
|
static bool isImage = true;
|
|
if (ImGui::Button("Select...##image")) {
|
|
ImGui::OpenPopup("popup_button");
|
|
isImage = true;
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button(ICON_MDI_CLOSE_BOX_OUTLINE "##delimage")) {
|
|
SetImage("");
|
|
}
|
|
|
|
|
|
ImGui::AlignTextToFramePadding();
|
|
ImGui::Text("Sound");
|
|
ImGui::SameLine();
|
|
|
|
ImGui::Text("%s", m_soundName.c_str());
|
|
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::Button(m_buttonUniqueName.c_str()))
|
|
{
|
|
m_story.PlaySoundFile(m_soundPath);
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::Button("Select...##sound")) {
|
|
ImGui::OpenPopup("popup_button");
|
|
isImage = false;
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button(ICON_MDI_CLOSE_BOX_OUTLINE "##delsound")) {
|
|
SetSound("");
|
|
}
|
|
|
|
// This is the actual popup Gui drawing section.
|
|
if (ImGui::BeginPopup("popup_button")) {
|
|
ImGui::SeparatorText(isImage ? "Images" : "Sounds");
|
|
|
|
auto [filtreDebut, filtreFin] = isImage ? m_story.Images() : m_story.Sounds();
|
|
int n = 0;
|
|
for (auto it = filtreDebut; it != filtreFin; ++it, n++)
|
|
{
|
|
if (ImGui::Selectable((*it)->file.c_str()))
|
|
{
|
|
if (isImage)
|
|
{
|
|
SetImage((*it)->file);
|
|
}
|
|
else
|
|
{
|
|
SetSound((*it)->file);
|
|
}
|
|
}
|
|
}
|
|
|
|
ImGui::EndPopup(); // Note this does not do anything to the popup open/close state. It just terminates the content declaration.
|
|
}
|
|
|
|
}
|
|
|
|
void MediaNode::SetImage(const std::string &f)
|
|
{
|
|
m_image.name = f;
|
|
m_image.Load(m_story.BuildFullAssetsPath(f));
|
|
}
|
|
|
|
void MediaNode::SetSound(const std::string &f)
|
|
{
|
|
m_soundName = f;
|
|
m_soundPath = m_story.BuildFullAssetsPath(m_soundName);
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string MediaNode::ChoiceLabel() const
|
|
{
|
|
std::stringstream ss;
|
|
ss << "mediaChoice" << std::setw(4) << std::setfill('0') << GetId();
|
|
return ss.str();
|
|
}
|
|
|
|
std::string MediaNode::GetEntryLabel()
|
|
{
|
|
std::stringstream ss;
|
|
ss << ".mediaEntry" << std::setw(4) << std::setfill('0') << GetId();
|
|
return ss.str();
|
|
}
|
|
|
|
|
|
std::string MediaNode::GenerateConstants()
|
|
{
|
|
std::string s;
|
|
|
|
if (m_image.name.size() > 0)
|
|
{
|
|
s = StoryProject::FileToConstant(m_image.name, ".qoi"); // FIXME: Generate the extension setup in user option of output format
|
|
}
|
|
if (m_soundName.size() > 0)
|
|
{
|
|
s += StoryProject::FileToConstant(m_soundName, ".wav"); // FIXME: Generate the extension setup in user option of output format
|
|
}
|
|
|
|
int nb_out_conns = Outputs();
|
|
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::list<std::shared_ptr<Connection>> conns = m_story.GetNodeConnections(GetId());
|
|
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_story.GetNodeEntryLabel(c->inNodeId);
|
|
if (i < (nb_out_conns - 1))
|
|
{
|
|
ss << ", ";
|
|
}
|
|
else
|
|
{
|
|
ss << "\n";
|
|
}
|
|
i++;
|
|
}
|
|
|
|
s += ss.str();
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
std::string MediaNode::Build()
|
|
{
|
|
std::stringstream ss;
|
|
int nb_out_conns = Outputs();
|
|
|
|
ss << R"(; ---------------------------- )"
|
|
<< GetTitle()
|
|
<< " Type: "
|
|
<< (nb_out_conns == 0 ? "End" : nb_out_conns == 1 ? "Transition" : "Choice")
|
|
<< "\n";
|
|
std::string image = StoryProject::RemoveFileExtension(m_image.name);
|
|
std::string sound = StoryProject::RemoveFileExtension(m_soundName);
|
|
|
|
// 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 << GetEntryLabel() << ":\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) // it is a transition node
|
|
{
|
|
std::list<std::shared_ptr<Connection>> conns = m_story.GetNodeConnections(GetId());
|
|
|
|
|
|
for (auto c : conns)
|
|
{
|
|
if (c->outNodeId == GetId())
|
|
{
|
|
// On place dans R0 le prochain noeud à exécuter en cas de OK
|
|
ss << "lcons r0, "
|
|
<< m_story.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();
|
|
}
|
|
|