Fix media output format, fix link creation

This commit is contained in:
anthony@rabine.fr 2024-05-15 15:44:38 +02:00
parent be16a68e85
commit 016282064e
15 changed files with 197 additions and 150 deletions

View file

@ -10,6 +10,8 @@ public:
virtual ~IStoryProject() {}; virtual ~IStoryProject() {};
virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(const std::string &nodeId) = 0; virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(const std::string &nodeId) = 0;
virtual int OutputsCount(const std::string &nodeId) = 0; virtual int OutputsCount(const std::string &nodeId) = 0;
virtual std::string ImageExtension(const std::string &filename) const = 0;
virtual std::string SoundExtension(const std::string &filename) const = 0;
}; };

View file

@ -27,3 +27,4 @@ std::string Resource::SoundFormatToString(SoundFormat format)
} }
return text; return text;
} }

View file

@ -119,7 +119,6 @@ void StoryProject::ModelToJson(nlohmann::json &model)
{ {
nlohmann::json c(*cnx); nlohmann::json c(*cnx);
connections.push_back(c); connections.push_back(c);
// Connection cnx = LinkToModel(linkInfo->ed_link->InputId, linkInfo->ed_link->OutputId);
} }
model["connections"] = connections; model["connections"] = connections;
@ -275,6 +274,32 @@ int StoryProject::OutputsCount(const std::string &nodeId)
return count; return count;
} }
std::string StoryProject::ImageExtension(const std::string &filename) const
{
std::string ext = SysLib::GetFileExtension(filename);
if (m_imageFormat == Resource::ImageFormat::IMG_FORMAT_QOIF)
{
return "qoi";
}
return ext;
}
std::string StoryProject::SoundExtension(const std::string &filename) const
{
std::string ext = SysLib::GetFileExtension(filename);
if (m_soundFormat == Resource::SoundFormat::SND_FORMAT_QOAF)
{
return "qoa";
}
else if (m_soundFormat == Resource::SoundFormat::SND_FORMAT_WAV)
{
return "wav";
}
return ext;
}
std::list<std::shared_ptr<Connection>> StoryProject::GetNodeConnections(const std::string &nodeId) std::list<std::shared_ptr<Connection>> StoryProject::GetNodeConnections(const std::string &nodeId)
{ {
std::list<std::shared_ptr<Connection>> c; std::list<std::shared_ptr<Connection>> c;
@ -555,7 +580,7 @@ std::string StoryProject::BuildFullAssetsPath(const std::string &fileName) const
std::string StoryProject::FileToConstant(const std::string &FileName, const std::string &extension) std::string StoryProject::FileToConstant(const std::string &FileName, const std::string &extension)
{ {
std::string f = SysLib::RemoveFileExtension(FileName); std::string f = SysLib::RemoveFileExtension(FileName);
return "$" + f + " DC8 \"" + f + extension + "\", 8\r\n"; return "$" + f + " DC8 \"" + f + "." + extension + "\", 8\r\n";
} }

View file

@ -135,6 +135,8 @@ public:
virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(const std::string &nodeId) override; virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(const std::string &nodeId) override;
std::string FindFirstNode() const; std::string FindFirstNode() const;
virtual int OutputsCount(const std::string &nodeId) override; virtual int OutputsCount(const std::string &nodeId) override;
virtual std::string ImageExtension(const std::string &filename) const override;
virtual std::string SoundExtension(const std::string &filename) const override;
std::shared_ptr<BaseNode> CreateNode(const std::string& type); std::shared_ptr<BaseNode> CreateNode(const std::string& type);
void AddConnection(std::shared_ptr<Connection> c); void AddConnection(std::shared_ptr<Connection> c);

View file

@ -148,23 +148,23 @@ include_directories(${sdl3_mixer_SOURCE_DIR}/include)
# ========================================================================================================================= # =========================================================================================================================
# SDL3-Image # SDL3-Image
# ========================================================================================================================= # =========================================================================================================================
# FetchContent_Declare( FetchContent_Declare(
# SDL2_image sdl_image
# GIT_REPOSITORY https://github.com/libsdl-org/SDL_image.git GIT_REPOSITORY https://github.com/libsdl-org/SDL_image.git
# GIT_TAG origin/main GIT_TAG origin/main
# GIT_SHALLOW TRUE GIT_SHALLOW TRUE
# GIT_PROGRESS TRUE GIT_PROGRESS TRUE
# ) )
# # START ADDITION
# set(SDL2IMAGE_INSTALL OFF)
# set(BUILD_SHARED_LIBS FALSE)
# # END ADDITION
add_subdirectory(libs/SDL_image) set(SDL3IMAGE_INSTALL OFF)
include_directories(libs/SDL_image/include) set(BUILD_SHARED_LIBS FALSE)
FetchContent_MakeAvailable(sdl_image)
include_directories(${sdl_image_SOURCE_DIR}/include)
# FetchContent_MakeAvailable(SDL2_image) # =========================================================================================================================
# Project sources
# =========================================================================================================================
set(SRCS set(SRCS
src/main.cpp src/main.cpp
@ -332,11 +332,12 @@ target_compile_definitions(${STORY_EDITOR_PROJECT} PUBLIC cimg_display=0)
target_compile_definitions(${STORY_EDITOR_PROJECT} PUBLIC "$<$<CONFIG:DEBUG>:DEBUG>") target_compile_definitions(${STORY_EDITOR_PROJECT} PUBLIC "$<$<CONFIG:DEBUG>:DEBUG>")
target_link_directories(${STORY_EDITOR_PROJECT} PUBLIC ${sdl3_BINARY_DIR} ${curl_BINARY_DIR} ${CMAKE_BINARY_DIR}/libs/SDL_image) target_link_directories(${STORY_EDITOR_PROJECT} PUBLIC ${sdl3_BINARY_DIR} ${curl_BINARY_DIR})
# On est obligé de passer par une variable pour injecter # On est obligé de passer par une variable pour injecter
# certaines informations à CPACK # certaines informations à CPACK
set(SDL_BIN_DIR ${sdl3_BINARY_DIR}) set(SDL_BIN_DIR ${sdl3_BINARY_DIR})
set(SDL_IMAGE_BIN_DIR ${sdl_image_BINARY_DIR})
if(UNIX) if(UNIX)
target_link_libraries(${STORY_EDITOR_PROJECT} target_link_libraries(${STORY_EDITOR_PROJECT}
@ -389,6 +390,12 @@ if(WIN32)
set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/story-editor-logo.ico") set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/story-editor-logo.ico")
endif() endif()
if(LINUX)
install_files("." FILES "${SDL_BIN_DIR}/SDL3.so")
install_files("." FILES "${SDL_IMAGE_BIN_DIR}/SDL3_image.so")
endif()
if (APPLE) if (APPLE)
set(CPACK_GENERATOR "DragNDrop") set(CPACK_GENERATOR "DragNDrop")
set(MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/bundle.plist.in) set(MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/bundle.plist.in)

View file

@ -99,6 +99,8 @@ void EmulatorWindow::Draw()
ImGui::SameLine(); ImGui::SameLine();
ImGui::Text("VM state: %s", m_story.VmState().c_str());
WindowBase::EndDraw(); WindowBase::EndDraw();
} }

View file

@ -36,7 +36,6 @@ your use of the corresponding standard functions.
#include "IconsMaterialDesignIcons.h" #include "IconsMaterialDesignIcons.h"
#include "IconsFontAwesome5_c.h" #include "IconsFontAwesome5_c.h"
#include "qoi.h"
static void glfw_error_callback(int error, const char* description) static void glfw_error_callback(int error, const char* description)
@ -50,13 +49,6 @@ static SDL_Renderer* renderer{nullptr};
#include "stb_image.h" #include "stb_image.h"
static std::string GetFileExtension(const std::string &fileName)
{
if(fileName.find_last_of(".") != std::string::npos)
return fileName.substr(fileName.find_last_of(".")+1);
return "";
}
// Simple helper function to load an image into a OpenGL texture with common settings // Simple helper function to load an image into a OpenGL texture with common settings
bool LoadTextureFromFile(const char* filename, Gui::Image &img) bool LoadTextureFromFile(const char* filename, Gui::Image &img)

View file

@ -58,6 +58,7 @@ public:
virtual void Pause() = 0; virtual void Pause() = 0;
virtual void Next() = 0; virtual void Next() = 0;
virtual void Previous() = 0; virtual void Previous() = 0;
virtual std::string VmState() const = 0;
}; };
#endif // I_STORY_MANAGER_H #endif // I_STORY_MANAGER_H

View file

@ -93,16 +93,44 @@ void MainWindow::Pause()
void MainWindow::Next() void MainWindow::Next()
{ {
Log("End of audio track"); Log("Next button");
m_eventQueue.push({VmEventType::EvNextButton}); m_eventQueue.push({VmEventType::EvNextButton});
} }
void MainWindow::Previous() void MainWindow::Previous()
{ {
Log("End of audio track"); Log("Previous button");
m_eventQueue.push({VmEventType::EvPreviousButton}); m_eventQueue.push({VmEventType::EvPreviousButton});
} }
std::string MainWindow::VmState() const
{
std::string state = "Unknown";
switch (m_dbg.run_result)
{
case VM_READY:
state = "VM Ready";
break;
case VM_FINISHED:
state = "VM Finished";
break;
case VM_SKIPED:
state = "VM Skiped";
break;
case VM_WAIT_EVENT:
state = "VM Wait Event";
break;
case VM_OK:
state = "VM Ok";
break;
default:
state = "VM Error";
break;
}
return state;
}
void MainWindow::EndOfAudio() void MainWindow::EndOfAudio()
{ {
Log("End of audio track"); Log("End of audio track");
@ -659,6 +687,7 @@ void MainWindow::SaveProject()
{ {
nlohmann::json model; nlohmann::json model;
m_story->Save(m_resources); m_story->Save(m_resources);
Log("Project saved");
} }
void MainWindow::OpenProject(const std::string &uuid) void MainWindow::OpenProject(const std::string &uuid)
@ -858,8 +887,8 @@ void MainWindow::Loop()
// Rendering and event handling // Rendering and event handling
Uint64 frameTime = SDL_GetTicks() - frameStart; // Temps écoulé pour la frame Uint64 frameTime = SDL_GetTicks() - frameStart; // Temps écoulé pour la frame
if (frameTime < 16) { // Limite de 60 FPS if (frameTime < 32) { // 16 ms = 60 FPS
SDL_Delay(16 - frameTime); // Attendez pour compléter la frame SDL_Delay(32 - frameTime); // Attendez pour compléter la frame
} }

View file

@ -144,6 +144,7 @@ private:
virtual void Pause() override; virtual void Pause() override;
virtual void Next() override; virtual void Next() override;
virtual void Previous() override; virtual void Previous() override;
virtual std::string VmState() const override;
// From ILogger // From ILogger

View file

@ -14,19 +14,39 @@
#define STBI_NO_LINEAR #define STBI_NO_LINEAR
#include "stb_image.h" #include "stb_image.h"
#define QOI_IMPLEMENTATION
#undef QOI_NO_STDIO
#include "qoi.h"
#define DR_MP3_IMPLEMENTATION #define DR_MP3_IMPLEMENTATION
#include "dr_mp3.h" #include "dr_mp3.h"
#include "qoi.h"
static int qoi_encode_and_write(const char *filename, const void *data, const qoi_desc *desc) {
FILE *f = fopen(filename, "wb");
int size;
void *encoded;
if (!f) {
return 0;
}
encoded = qoi_encode(data, desc, &size);
if (!encoded) {
fclose(f);
return 0;
}
fwrite(encoded, 1, size, f);
fclose(f);
free(encoded);
return size;
}
MediaConverter::MediaConverter() MediaConverter::MediaConverter()
{ {
} }
int MediaConverter::ImageToQoi(const std::string &inputFileName, const std::string &outputFileName) int MediaConverter::ImageToQoi(const std::string &inputFileName, const std::string &outputFileName)
{ {
void *pixels = NULL; void *pixels = NULL;
@ -54,7 +74,8 @@ int MediaConverter::ImageToQoi(const std::string &inputFileName, const std::stri
desc.width = w; desc.width = w;
desc.height = h; desc.height = h;
int encoded = qoi_write(outputFileName.c_str(), pixels, &desc);
int encoded = qoi_encode_and_write(outputFileName.c_str(), pixels, &desc);
free(pixels); free(pixels);
if (!encoded) if (!encoded)
@ -241,101 +262,3 @@ int MediaConverter::OggToWav(const std::string &inputFileName, const std::string
return cSuccess; return cSuccess;
} }
/*
OGG TO WAV
#define STB_VORBIS_HEADER_ONLY
#include "stb_vorbis.c"
#include <stdio.h>
#include <stdlib.h>
// Function to read the entire contents of a file into memory
unsigned char* read_entire_file(const char* filename, int* length) {
FILE* f = fopen(filename, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
unsigned char* buffer = (unsigned char*)malloc(size);
if (!buffer) {
fclose(f);
return NULL;
}
fread(buffer, 1, size, f);
fclose(f);
if (length) *length = (int)size;
return buffer;
}
int main(int argc, char** argv) {
if (argc != 3) {
printf("Usage: %s input.ogg output.wav\n", argv[0]);
return 1;
}
const char* input_filename = argv[1];
const char* output_filename = argv[2];
int ogg_length;
unsigned char* ogg_data = read_entire_file(input_filename, &ogg_length);
if (!ogg_data) {
printf("Failed to read input file %s\n", input_filename);
return 1;
}
int channels, sample_rate;
short* samples = stb_vorbis_decode_memory(ogg_data, ogg_length, &channels, &sample_rate);
if (!samples) {
printf("Failed to decode OGG file %s\n", input_filename);
free(ogg_data);
return 1;
}
// Write WAV file header
FILE* wav_file = fopen(output_filename, "wb");
if (!wav_file) {
printf("Failed to create output file %s\n", output_filename);
free(ogg_data);
free(samples);
return 1;
}
fwrite("RIFF", 1, 4, wav_file);
int total_size = 36 + channels * (sample_rate * sizeof(short));
fwrite(&total_size, 4, 1, wav_file);
fwrite("WAVEfmt ", 1, 8, wav_file);
int format_size = 16;
fwrite(&format_size, 4, 1, wav_file);
short format_type = 1; // PCM
fwrite(&format_type, 2, 1, wav_file);
fwrite(&channels, 2, 1, wav_file);
fwrite(&sample_rate, 4, 1, wav_file);
int byte_rate = sample_rate * channels * sizeof(short);
fwrite(&byte_rate, 4, 1, wav_file);
short block_align = channels * sizeof(short);
fwrite(&block_align, 2, 1, wav_file);
short bits_per_sample = 16;
fwrite(&bits_per_sample, 2, 1, wav_file);
fwrite("data", 1, 4, wav_file);
int data_size = channels * (sample_rate * sizeof(short));
fwrite(&data_size, 4, 1, wav_file);
// Write sample data
fwrite(samples, sizeof(short), sample_rate * channels, wav_file);
fclose(wav_file);
free(ogg_data);
free(samples);
printf("Conversion complete. WAV file saved as %s\n", output_filename);
return 0;
}
*/

View file

@ -177,6 +177,8 @@ public:
atIndex = 0; atIndex = 0;
for (auto &i : m_node->Outputs) for (auto &i : m_node->Outputs)
{ {
// std::cout << "Output Pin id: " << (int)i.ID.Get() << std::endl;
if (i.ID == pinId) if (i.ID == pinId)
{ {
found = true; found = true;

View file

@ -167,29 +167,79 @@ void NodeEditorWindow::CreateLink(std::shared_ptr<Connection> model, ed::PinId i
m_links.push_back(conn); m_links.push_back(conn);
} }
// retourne 1 si c'est une sortie, 2 une entrée, 0 pas trouvé
int NodeEditorWindow::FindNodeAndPin(ed::PinId pinId, int &foundIndex, std::string &foundNodeId)
{
int success = 0;
for (const auto & n : m_nodes)
{
// std::cout << "---> Node: " << n->Base()->GetId() << std::endl;
if (n->HasOnputPinId(pinId, foundIndex))
{
foundNodeId = n->Base()->GetId();
success = 1;
break;
}
if (n->HasInputPinId(pinId, foundIndex))
{
foundNodeId = n->Base()->GetId();
success = 2;
break;
}
}
return success;
}
bool NodeEditorWindow::FillConnection(std::shared_ptr<Connection> c, ed::PinId pinId)
{
bool success = false;
std::string nodeId;
int nodeIndex;
int ret = FindNodeAndPin(pinId, nodeIndex, nodeId);
if (ret > 0)
{
if (ret == 1)
{
c->outNodeId = nodeId;
c->outPortIndex = nodeIndex;
}
else
{
c->inNodeId = nodeId;
c->inPortIndex = nodeIndex;
}
success = true;
}
return success;
}
/*
std::shared_ptr<Connection> NodeEditorWindow::LinkToModel(ed::PinId InputId, ed::PinId OutputId) std::shared_ptr<Connection> NodeEditorWindow::LinkToModel(ed::PinId InputId, ed::PinId OutputId)
{ {
auto c = std::make_shared<Connection>(); auto c = std::make_shared<Connection>();
int index; int foundIndex = -1;
for (const auto & n : m_nodes) for (const auto & n : m_nodes)
{ {
if (n->HasOnputPinId(OutputId, index)) // std::cout << "---> Node: " << n->Base()->GetId() << std::endl;
if (n->HasOnputPinId(OutputId, foundIndex))
{ {
c->outNodeId = n->Base()->GetId(); c->outNodeId = n->Base()->GetId();
c->outPortIndex = index; c->outPortIndex = foundIndex;
} }
if (n->HasInputPinId(InputId, index)) if (n->HasInputPinId(InputId, foundIndex))
{ {
c->inNodeId = n->Base()->GetId(); c->inNodeId = n->Base()->GetId();
c->inPortIndex = index; c->inPortIndex = foundIndex;
} }
} }
return c; return c;
} }*/
std::shared_ptr<BaseNodeWidget> NodeEditorWindow::GetSelectedNode() std::shared_ptr<BaseNodeWidget> NodeEditorWindow::GetSelectedNode()
@ -243,8 +293,8 @@ void NodeEditorWindow::Draw()
// Handle creation action, returns true if editor want to create new object (node or link) // Handle creation action, returns true if editor want to create new object (node or link)
if (ed::BeginCreate()) if (ed::BeginCreate())
{ {
ed::PinId inputPinId, outputPinId; ed::PinId startId, endId;
if (ed::QueryNewLink(&inputPinId, &outputPinId)) if (ed::QueryNewLink(&startId, &endId))
{ {
// QueryNewLink returns true if editor want to create new link between pins. // QueryNewLink returns true if editor want to create new link between pins.
// //
@ -258,18 +308,26 @@ void NodeEditorWindow::Draw()
// * input invalid, output valid - user started to drag new ling from output pin // * input invalid, output valid - user started to drag new ling from output pin
// * input valid, output valid - user dragged link over other pin, can be validated // * input valid, output valid - user dragged link over other pin, can be validated
if (inputPinId && outputPinId) // both are valid, let's accept link if (startId && endId) // both are valid, let's accept link
{ {
// ed::AcceptNewItem() return true when user release mouse button. // ed::AcceptNewItem() return true when user release mouse button.
if (ed::AcceptNewItem()) if (ed::AcceptNewItem())
{ {
auto c = LinkToModel(inputPinId, outputPinId); auto c = std::make_shared<Connection>();
m_story->AddConnection(c);
CreateLink(c, inputPinId, outputPinId); // On cherche à quel noeud appartien les pin (selon si le lien a été créé à partir d'une entrée ou d'une sortie)
if (FillConnection(c, startId))
{
if (FillConnection(c, endId))
{
m_story->AddConnection(c);
// Draw new link. CreateLink(c, startId, endId);
ed::Link(m_links.back()->ed_link->Id, inputPinId, outputPinId);
// Draw new link.
ed::Link(m_links.back()->ed_link->Id, startId, endId);
}
}
} }
// You may choose to reject connection between these nodes // You may choose to reject connection between these nodes

View file

@ -116,6 +116,8 @@ private:
ed::PinId GetInputPin(const std::string &modelNodeId, int pinIndex); ed::PinId GetInputPin(const std::string &modelNodeId, int pinIndex);
ed::PinId GetOutputPin(const std::string &modelNodeId, int pinIndex); ed::PinId GetOutputPin(const std::string &modelNodeId, int pinIndex);
void CreateLink(std::shared_ptr<Connection> model, ed::PinId inId, ed::PinId outId); void CreateLink(std::shared_ptr<Connection> model, ed::PinId inId, ed::PinId outId);
std::shared_ptr<Connection> LinkToModel(ed::PinId InputId, ed::PinId OutputId); // std::shared_ptr<Connection> LinkToModel(ed::PinId InputId, ed::PinId OutputId);
int FindNodeAndPin(ed::PinId pinId, int &foundIndex, std::string &foundNodeId);
bool FillConnection(std::shared_ptr<Connection> c, ed::PinId pinId);
}; };

View file

@ -24,11 +24,11 @@ std::string MediaNode::GenerateConstants(IStoryProject &story, int nb_out_conns)
if (image.size() > 0) if (image.size() > 0)
{ {
s = StoryProject::FileToConstant(image, ".qoi"); // FIXME: Generate the extension setup in user option of output format s = StoryProject::FileToConstant(image, story.ImageExtension(image));
} }
if (sound.size() > 0) if (sound.size() > 0)
{ {
s += StoryProject::FileToConstant(sound, ".wav"); // FIXME: Generate the extension setup in user option of output format s += StoryProject::FileToConstant(sound, story.SoundExtension(sound)); // FIXME: Generate the extension setup in user option of output format
} }