diff --git a/CMakeLists.txt b/CMakeLists.txt index 1912c49..29da5b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE) # Build as shared lib option(RN_ENABLE_SANITIZER "Set to compile with adress sanitizer (MSVC)") # set(RN_ENABLE_SANITIZER ON) option(RN_BUILD_TESTS "Set to compile the test executable") # set(RN_BUILD_TESTS ON) option(RN_BUILD_RELEASE "Set to build the release archives") # set(RN_BUILD_RELEASE ON) # You don't really need this -set(RN_VERSION "1.0.3") +set(RN_VERSION "1.0.4") # Compiler options for MSVC and GCC include(cmake/CompilerOptions.cmake) @@ -33,7 +33,7 @@ add_subdirectory(src/plugins) add_subdirectory(src/import) # Add all the dependencies to the executable -add_dependencies(raynodes BuiltIns QuestScript) +add_dependencies(raynodes BuiltIns QuestScript Logics) if (RN_BUILD_TESTS) enable_testing() diff --git a/cmake/LoadInternalLibs.cmake b/cmake/LoadInternalLibs.cmake index 816c7de..6ad038e 100644 --- a/cmake/LoadInternalLibs.cmake +++ b/cmake/LoadInternalLibs.cmake @@ -10,5 +10,4 @@ include_directories( add_subdirectory("${DEPENDENCIES_PATH}/raylib" raylib) - add_library(nativefiledialogs STATIC "${DEPENDENCIES_PATH}/tinyfiledialogs/tinyfiledialogs.c") \ No newline at end of file diff --git a/cmake/PluginDefinition.cmake b/cmake/PluginDefinition.cmake new file mode 100644 index 0000000..877c41e --- /dev/null +++ b/cmake/PluginDefinition.cmake @@ -0,0 +1,23 @@ +# This script expects PLUGIN_NAME and PLUGIN_FILES to be set before including it. + +if(NOT DEFINED PLUGIN_NAME) + message(FATAL_ERROR "PLUGIN_NAME is not defined") + message("Use set(PLUGIN_NAME MyPlugin) to set the name") +endif() + +if(NOT DEFINED PLUGIN_FILES) + message(FATAL_ERROR "PLUGIN_FILES is not defined") + message("Use set(PLUGIN_NAME MyPlugin) to set the name") + +endif() + +add_library(${PLUGIN_NAME} SHARED ${PLUGIN_FILES}) + +set_target_properties(${PLUGIN_NAME} PROPERTIES + PREFIX "" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins" +) + +target_include_directories(${PLUGIN_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src/raynodes) +target_link_libraries(${PLUGIN_NAME} PRIVATE editor) \ No newline at end of file diff --git a/src/external/cxstructs/include/cxutil/cxio.h b/src/external/cxstructs/include/cxutil/cxio.h index cdd538a..d2f6b37 100644 --- a/src/external/cxstructs/include/cxutil/cxio.h +++ b/src/external/cxstructs/include/cxutil/cxio.h @@ -21,18 +21,20 @@ #ifndef CXSTRUCTS_SRC_CXIO_H_ # define CXSTRUCTS_SRC_CXIO_H_ -#include "../cxconfig.h" +# include "../cxconfig.h" # include // Simple, readable and fast *symmetric* serialization structure with loading -// and saving. Each line is a concatenated list of values and separator SEPARATOR +// and saving. Each line is a concatenated list of values and a separator // 13|3.145|This is a string|56| +// I didnt find a way around hard coding the separator... + // Using the CXX23 std::print() is about 10% slower namespace cxstructs { - static constexpr int MAX_SECTION_SIZE = 16; +# define NEW_LINE_SUB '\036' //-----------SHARED-----------// namespace { @@ -77,9 +79,17 @@ inline void io_save_newline(FILE* file) { fputc('\n', file); } -// Writes a string value to file +// Writes a string value to file, replacing newlines with the SEPARATOR inline void io_save(FILE* file, const char* value) { - fprintf(file, "%s\037", value); + while (*value != '\0') { + if (*value == '\n') { + fputc(NEW_LINE_SUB, file); // Replace newline with SEPARATOR + } else { + fputc(*value, file); // Write the character as is + } + ++value; + } + fputc('\037', file); // Add SEPARATOR at the end } // Writes an integer or enum property to the file @@ -97,9 +107,13 @@ inline void io_save(FILE* file, const float value) { fprintf(file, "%.3f\037", value); } -// Writes three floats to the file +// Writes three floats to the file - separated by ";" inline void io_save(FILE* file, const float value, const float value2, const float value3) { - fprintf(file, "%.3f\037%.3f\037%.3f\037", value, value2, value3); + fprintf(file, "%.6f;%.6f;%.6f\037", value, value2, value3); +} +// Writes three floats to the file - separated by ";" +inline void io_save(FILE* file, const float value, const float value2) { + fprintf(file, "%.6f;%.6f\037", value, value2); } // Buffers the given SaveFunc to memory so the file is only written if it @@ -216,6 +230,7 @@ inline void io_load(FILE* file, std::string& s) { //s.reserve(reserve_amount); // Dont need to reserve - string shouldnt allocate below 15 characters char ch; while (fread(&ch, 1, 1, file) == 1 && ch != '\037') { + if (ch == NEW_LINE_SUB) [[unlikely]] { ch = '\n'; } s.push_back(ch); } while (ch != '\37' && fread(&ch, 1, 1, file) == 1) {} @@ -226,6 +241,7 @@ inline int io_load(FILE* file, char* buffer, size_t buffer_size) { int count = 0; char ch; while (count < buffer_size - 1 && fread(&ch, 1, 1, file) == 1 && ch != '\037') { + if (ch == NEW_LINE_SUB) [[unlikely]] { ch = '\n'; } buffer[count++] = ch; } buffer[count] = '\0'; @@ -251,181 +267,12 @@ inline void io_load(FILE* file, float& f) { // Directly load three floats from the file inline void io_load(FILE* file, float& f, float& f2, float& f3) { - fscanf(file, "%f\037%f\037%f\037", &f, &f2, &f3); -} -} // namespace cxstructs - -# ifdef CX_INCLUDE_TESTS -# include -namespace cxtests { -using namespace cxstructs; -using namespace std::chrono; -static void benchMark() { - FILE* file; - const char* filename = "hello.txt"; - constexpr int num = 1000; - int val = 5; - auto start_write = high_resolution_clock::now(); - fopen_s(&file, filename, "wb"); - if (file != nullptr) { - for (int i = 0; i < num; i++) { - for (int j = 0; j < num; j++) { - io_save(file, j); - } - io_save_newline(file); - } - fclose(file); - } - auto end_write = high_resolution_clock::now(); - auto start_read = high_resolution_clock::now(); - - fopen_s(&file, filename, "rb"); - if (file != nullptr) { - for (int i = 0; i < num; i++) { - for (int j = 0; j < num; j++) { - io_load(file, val); - } - } - fclose(file); - } - auto end_read = high_resolution_clock::now(); - auto duration_write = duration_cast(end_write - start_write).count(); - auto duration_read = duration_cast(end_read - start_read).count(); - printf("Write time: %lld ms\n", duration_write); - printf("Read time: %lld ms\n", duration_read); -} -void test_save_load_string() { - const char* test_filename = "test_string.txt"; - const char* original_string = "Hello, world!"; - char buffer[256]; - - // Save - FILE* file = std::fopen(test_filename, "wb"); - cxstructs::io_save(file, original_string); - cxstructs::io_save_newline(file); - std::fclose(file); - - // Load - file = std::fopen(test_filename, "rb"); - cxstructs::io_load(file, buffer, sizeof(buffer)); - std::fclose(file); - - // Assert - CX_ASSERT(std::strcmp(original_string, buffer) == 0, "String save/load failed"); -} -void test_save_load_int() { - const char* test_filename = "test_int.txt"; - const int original_int = 42; - int loaded_int; - - // Save - FILE* file = std::fopen(test_filename, "wb"); - cxstructs::io_save(file, original_int); - cxstructs::io_save_newline(file); - std::fclose(file); - - // Load - file = std::fopen(test_filename, "rb"); - cxstructs::io_load(file, loaded_int); - std::fclose(file); - - // Assert - CX_ASSERT(original_int == loaded_int, "Int save/load failed"); + fscanf(file, "%f;%f;%f\037", &f, &f2, &f3); } -void test_save_load_float() { - const char* test_filename = "test_float.txt"; - constexpr float original_float = 3.141; - float loaded_float; - - // Save - FILE* file; - fopen_s(&file, test_filename, "wb"); - cxstructs::io_save(file, original_float); - cxstructs::io_save_newline(file); - std::fclose(file); - - // Load - fopen_s(&file, test_filename, "rb"); - cxstructs::io_load(file, loaded_float); - std::fclose(file); - - // Assert - CX_ASSERT(original_float == loaded_float, "Float save/load failed"); -} -void delete_test_files() { - // List of test files to delete - const char* files[] = {"test_string.txt", "test_int.txt", "test_float.txt", "test_complex.txt", "hello.txt"}; - - // Iterate over the array and delete each file - for (const char* filename : files) { - if (std::remove(filename) != 0) { - perror("Error deleting file"); - } else { - printf("%s deleted successfully.\n", filename); - } - } +// Directly load three floats from the file +inline void io_load(FILE* file, float& f, float& f2) { + fscanf(file, "%f;%f\037", &f, &f2); } -void test_complex_save_load() { - const char* test_filename = "test_complex.txt"; - const char* original_str1 = "TestString1"; - const int original_int = 123; - const float original_float = 456.789f; - const char* original_str2 = "TestString2"; - - char buffer_str1[256]; - int loaded_int = -1; - float loaded_float = -1; - std::string buffer_str2; - std::string hello; - std::string bye; - std::string hello2; - - // Save complex data - FILE* file; - fopen_s(&file, test_filename, "wb"); - if (file) { - cxstructs::io_save(file, original_str1); - cxstructs::io_save(file, original_int); - cxstructs::io_save(file, original_float); - cxstructs::io_save(file, original_str2); - cxstructs::io_save_newline(file); - io_save(file, "hello"); - io_save(file, "bye"); - io_save(file, "hello"); - std::fclose(file); - } - - // Load complex data - file = std::fopen(test_filename, "rb"); - if (file) { - cxstructs::io_load(file, buffer_str1, sizeof(buffer_str1)); - cxstructs::io_load(file, loaded_int); - cxstructs::io_load(file, loaded_float); - cxstructs::io_load(file, buffer_str2); - io_load_newline(file); - io_load(file, hello); - io_load(file, bye); - io_load(file, hello2); - std::fclose(file); - } +} // namespace cxstructs - // Assert all loaded data matches original - CX_ASSERT(std::strcmp(original_str1, buffer_str1) == 0, "String1 save/load failed"); - CX_ASSERT(original_int == loaded_int, "Int save/load failed"); - CX_ASSERT(std::fabs(original_float - loaded_float) < 0.001, - "Float save/load failed"); // Allow for slight floating-point inaccuracies - CX_ASSERT(std::strcmp(original_str2, buffer_str2.c_str()) == 0, "String2 save/load failed"); - CX_ASSERT(hello == hello2, ""); - CX_ASSERT(bye == "bye", ""); -} -static void TEST_IO() { - benchMark(); - test_save_load_float(); - test_save_load_int(); - test_save_load_string(); - test_complex_save_load(); - delete_test_files(); -} -} // namespace cxtests -# endif #endif // CXSTRUCTS_SRC_CXIO_H_ \ No newline at end of file diff --git a/src/external/cxstructs/include/cxutil/cxstring.h b/src/external/cxstructs/include/cxutil/cxstring.h index a2d989a..286a4f2 100644 --- a/src/external/cxstructs/include/cxutil/cxstring.h +++ b/src/external/cxstructs/include/cxutil/cxstring.h @@ -130,7 +130,7 @@ inline int64_t str_parse_long(const char* str, const int radix = 10) { } // Parses the given string into a float on best effort basis inline float str_parse_float(const char* str) { - if (str == nullptr || *str == '\0') return 0.0; + if (str == nullptr || *str == '\0') return 0.0F; float result = 0.0; bool negative = false; @@ -159,10 +159,41 @@ inline float str_parse_float(const char* str) { return negative ? -result : result; } +// Parses the given string into a double on best effort basis +inline double str_parse_double(const char* str) { + if (str == nullptr || *str == '\0') return 0.0; + + double result = 0.0; + bool negative = false; + if (*str == '-') { + negative = true; + ++str; + } + + // Parse the integer part + while (*str && *str != '.') { + if (*str < '0' || *str > '9') break; + result = result * 10 + (*str - '0'); + ++str; + } + + // Parse the fractional part + if (*str == '.') { + ++str; + double factor = 0.1; + while (*str && *str >= '0' && *str <= '9') { + result += (*str - '0') * factor; + factor *= 0.1; + ++str; + } + } + + return negative ? -result : result; +} // Returns the levenshtein distance for the two given strings - stops when either string ends // This doesnt penalize long strings // Failure: returns -1 -template +template int str_sort_levenshtein_prefix(const char* s1, const char* s2) { const size_t len1 = str_len(s1), len2 = str_len(s2); const size_t limit = len1 < len2 ? len1 : len2; @@ -173,8 +204,10 @@ int str_sort_levenshtein_prefix(const char* s1, const char* s2) { unsigned int d[MAX_LEN + 1][MAX_LEN + 1] = {0}; - for (unsigned int i = 1; i <= limit; ++i) d[i][0] = i; - for (unsigned int i = 1; i <= len2; ++i) d[0][i] = i; + for (unsigned int i = 1; i <= limit; ++i) + d[i][0] = i; + for (unsigned int i = 1; i <= len2; ++i) + d[0][i] = i; for (unsigned int i = 1; i <= limit; ++i) { for (unsigned int j = 1; j <= len2; ++j) { @@ -189,7 +222,6 @@ int str_sort_levenshtein_prefix(const char* s1, const char* s2) { return d[limit][len2]; } - // string hash function constexpr auto str_hash_fnv1a_32(char const* s) noexcept -> uint32_t { uint32_t hash = 2166136261U; @@ -214,39 +246,16 @@ struct Fnv1aHash { struct StrEqual { bool operator()(const char* s1, const char* s2) const { return std::strcmp(s1, s2) == 0; } }; -} // namespace cxstructs -# ifdef CX_INCLUDE_TESTS -# include -# include -# include "../cxconfig.h" - -namespace cxtests { -static void TEST_STRING() { - std::unordered_map myMap; - auto* hey1 = "hey"; - auto* hey2 = "hey"; - auto s = std::string("hey"); - auto* hey3 = s.c_str(); - - myMap.insert({"hey", 1}); - - CX_ASSERT(myMap.at(hey1) == 1, ""); - CX_ASSERT(myMap.at(hey2) == 1, ""); - CX_ASSERT(myMap.at(hey3) == 1, ""); - CX_ASSERT(myMap.at("hey") == 1, ""); - CX_ASSERT(myMap.at("hey") == 1, ""); - - auto it = myMap.find(hey3); - CX_ASSERT(it != myMap.end(), ""); - it = myMap.find(hey1); - CX_ASSERT(it != myMap.end(), ""); - it = myMap.find(hey2); - CX_ASSERT(it != myMap.end(), ""); - it = myMap.find("hey"); - CX_ASSERT(it != myMap.end(), ""); +# if defined(_STRING_) || defined(_GLIBCXX_STRING) +// Adds the string representation of the given number to the string +inline void str_embed_num(std::string& s, float num) { + char buff[20]; + snprintf(buff, sizeof(buff), "%.6f", num); + s.append(buff); } -} // namespace cxtests # endif +} // namespace cxstructs + #endif //CXSTRUCTS_SRC_CXUTIL_CXSTRING_H_ \ No newline at end of file diff --git a/src/import/RnImport.h b/src/import/RnImport.h index a2d5599..2605e82 100644 --- a/src/import/RnImport.h +++ b/src/import/RnImport.h @@ -59,7 +59,7 @@ RnImport importRNFromMemory(char* fileData, uint32_t fileSize); #define COMPS_PER_NODE 6 // Max components per node #define SEPARATOR '\037' // Set this to the linesep used by cxio (default '\037' - Unit Separator) #define COMPONENT_SEPARATOR '\035' // The separator between component data (default '\035' - Group Separator) -#define LINE_FEED_SUB '\036' // Replaces new line ('\n') in the file ( default '\036' - Record Separator) +#define NEW_LINE_SUB '\036' // Replaces new line ('\n') in the file ( default '\036' - Record Separator) #define RN_MAX_NAME_LEN 16 // Max length of any component or node identifiers using ByteIndex = uint32_t; // Limits fileSize to 4GB @@ -361,7 +361,11 @@ auto NodeData::getData(char* fileData, const ComponentIndex id, const int index) while (*(start + count) != SEPARATOR && *(start + count) != '\0') { count++; } - return std::string{start, (size_t)count}; // Rely on RVO for efficient return + std::string result{start, (size_t)count}; + for (char& ch : result) { + if (ch == NEW_LINE_SUB) { ch = '\n'; } + } + return result; } else if constexpr (dt == STRING_VIEW) { return StringView{workPtr, (uint16_t)_str_count_chars_until(workPtr, SEPARATOR, RN_MAX_NAME_LEN)}; } else if constexpr (dt == INTEGER) { @@ -369,9 +373,13 @@ auto NodeData::getData(char* fileData, const ComponentIndex id, const int index) } else if constexpr (dt == FLOAT) { return std::strtod(workPtr, nullptr); } else if constexpr (dt == VECTOR_2) { - static_assert(dt == STRING, "Not yet supported"); + Vec2 vec2{}; + sscanf(workPtr, "%f;%f", &vec2.x, &vec2.y); + return vec2; } else if constexpr (dt == VECTOR_3) { - static_assert(dt == STRING, "Not yet supported"); + Vec3 vec3{}; + sscanf(workPtr, "%f;%f;%f", &vec3.x, &vec3.y, &vec3.z); + return vec3; } else if constexpr (dt == DATA) { static_assert(dt == STRING, "Wont be supported... How?"); } @@ -400,13 +408,22 @@ inline StringView NodeTemplate::getName(const char* fileData) const { inline char* StringView::getAllocatedString() const { auto* ret = new char[length + 1]; // +1 for the null terminator std::memcpy(ret, start, length); - ret[length] = '\0'; // Null terminate the string + ret[length] = '\0'; + + for (int i = 0; i < length; ++i) { + if (ret[i] == NEW_LINE_SUB) { ret[i] = '\n'; } + } + return ret; } inline std::string StringView::getString() const { if (start == nullptr || length == 0) [[unlikely]] { return {}; } - return {start, length}; + std::string result(start, length); + for (char& ch : result) { + if (ch == NEW_LINE_SUB) { ch = '\n'; } + } + return result; } constexpr uint32_t StringView::getHash() const { diff --git a/src/plugins/BuiltIns/CMakeLists.txt b/src/plugins/BuiltIns/CMakeLists.txt index 9014073..bed4f44 100644 --- a/src/plugins/BuiltIns/CMakeLists.txt +++ b/src/plugins/BuiltIns/CMakeLists.txt @@ -1,14 +1,3 @@ +set(PLUGIN_NAME BuiltIns) file(GLOB_RECURSE PLUGIN_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") - -add_library(BuiltIns SHARED ${PLUGIN_FILES}) - -set_target_properties(BuiltIns PROPERTIES - PREFIX "" - # Sets the output directory for the actual library files - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins" - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins" -) - - -target_include_directories(BuiltIns PRIVATE ${CMAKE_SOURCE_DIR}/src/raynodes) -target_link_libraries(BuiltIns PRIVATE editor) \ No newline at end of file +include(${CMAKE_SOURCE_DIR}/cmake/PluginDefinition.cmake) \ No newline at end of file diff --git a/src/plugins/BuiltIns/components/DisplayC.h b/src/plugins/BuiltIns/components/DisplayC.h index ffae215..b8012d3 100644 --- a/src/plugins/BuiltIns/components/DisplayC.h +++ b/src/plugins/BuiltIns/components/DisplayC.h @@ -44,13 +44,11 @@ struct DisplayC final : Component { DrawTextEx(ec.display.editorFont, txt, {x, y}, ec.display.fontSize, 1.0F, WHITE); } } - void update(EditorContext& ec, Node& /**/) override { outputs[0].setData(inputs[0].getData()); outputs[1].setData(inputs[1].getData()); outputs[2].setData(inputs[2].getData()); } - void onCreate(EditorContext& ec, Node& /**/) override { addPinInput(STRING); addPinInput(FLOAT); diff --git a/src/plugins/BuiltIns/components/Vec2C.h b/src/plugins/BuiltIns/components/Vec2C.h index eaf3e21..bcef05a 100644 --- a/src/plugins/BuiltIns/components/Vec2C.h +++ b/src/plugins/BuiltIns/components/Vec2C.h @@ -28,16 +28,15 @@ #include "ui/elements/TextField.h" template -class Vec2C final : public Component { +struct Vec2C final : Component { static constexpr int FLOAT_FIELDS = 2; TextField textFields[FLOAT_FIELDS]{}; - public: explicit Vec2C(const ComponentTemplate ct) : Component(ct, 200, 20) {} Component* clone() override { return new Vec2C(*this); } void draw(EditorContext& ec, Node& /**/) override { - auto bounds = getBounds(); + const auto bounds = getBounds(); constexpr std::array labels = {"X:", "Y:"}; const auto& font = ec.display.editorFont; @@ -110,14 +109,19 @@ class Vec2C final : public Component { } void save(FILE* file) override { - for (auto& f : textFields) { - cxstructs::io_save(file, f.buffer.c_str()); + float floats[FLOAT_FIELDS]; + for (int i = 0; i < FLOAT_FIELDS; ++i) { + floats[i] = cxstructs::str_parse_float(textFields[i].buffer.c_str()); } + cxstructs::io_save(file, floats[0], floats[1]); } void load(FILE* file) override { - for (auto& f : textFields) { - cxstructs::io_load(file, f.buffer); + // This is done to make the low level saving and loading more straightforward and independent of our handling + float floats[FLOAT_FIELDS]; + cxstructs::io_load(file, floats[0], floats[1]); + for (int i = 0; i < FLOAT_FIELDS; ++i) { + cxstructs::str_embed_num(textFields[i].buffer, floats[i]); } } }; diff --git a/src/plugins/BuiltIns/components/Vec3C.h b/src/plugins/BuiltIns/components/Vec3C.h index 1af64c7..63b02ab 100644 --- a/src/plugins/BuiltIns/components/Vec3C.h +++ b/src/plugins/BuiltIns/components/Vec3C.h @@ -28,16 +28,15 @@ #include "ui/elements/TextField.h" template -class Vec3C final : public Component { +struct Vec3C final : Component { static constexpr int FLOAT_FIELDS = 3; TextField textFields[FLOAT_FIELDS]{}; - public: explicit Vec3C(const ComponentTemplate ct) : Component(ct, 200, 20) {} Component* clone() override { return new Vec3C(*this); } void draw(EditorContext& ec, Node& /**/) override { - auto bounds = getBounds(); + const auto bounds = getBounds(); constexpr std::array labels = {"X:", "Y:", "Z:"}; const auto& font = ec.display.editorFont; @@ -111,14 +110,19 @@ class Vec3C final : public Component { } void save(FILE* file) override { - for (auto& f : textFields) { - cxstructs::io_save(file, f.buffer.c_str()); + float floats[FLOAT_FIELDS]; + for (int i = 0; i < FLOAT_FIELDS; ++i) { + floats[i] = cxstructs::str_parse_float(textFields[i].buffer.c_str()); } + cxstructs::io_save(file, floats[0], floats[1], floats[2]); } void load(FILE* file) override { - for (auto& f : textFields) { - cxstructs::io_load(file, f.buffer); + // This is done to make the low level saving and loading more straightforward and independent of our handling + float floats[FLOAT_FIELDS]; + cxstructs::io_load(file, floats[0], floats[1], floats[2]); + for (int i = 0; i < FLOAT_FIELDS; ++i) { + cxstructs::str_embed_num(textFields[i].buffer, floats[i]); } } }; diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 78492ad..072a555 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -1,19 +1,11 @@ -# Plugins +# IMPORTANT: Include a CMakeLists.txt with this in your plugin directory -# "CMakeLists.txt" template for plugins: -# -# add_library(MyPlugin SHARED MyPlugin.cpp) -# -#set_target_properties(MyPlugin PROPERTIES -# PREFIX "" -# # Sets the output directory for the actual library files -# RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins" -# LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins" -#) -# -# -#target_include_directories(MyPlugin PRIVATE ${CMAKE_SOURCE_DIR}/src/raynodes) -#target_link_libraries(MyPlugin PRIVATE editor) # Link against the editor (static) or possibly raylib (shared) +#set(PLUGIN_NAME MyPluginName) +#file(GLOB_RECURSE PLUGIN_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") +#include(${CMAKE_SOURCE_DIR}/cmake/PluginDefinition.cmake) + +# Add all the plugins add_subdirectory(BuiltIns) # Built in Types (raynodes STL...) -add_subdirectory(QuestScript) # MageQuest Quest Script \ No newline at end of file +add_subdirectory(QuestScript) # Quest Script +add_subdirectory(Logic) # Logic (circuits) \ No newline at end of file diff --git a/src/plugins/Logic/CMakeLists.txt b/src/plugins/Logic/CMakeLists.txt new file mode 100644 index 0000000..6c1479b --- /dev/null +++ b/src/plugins/Logic/CMakeLists.txt @@ -0,0 +1,3 @@ +set(PLUGIN_NAME Logics) +file(GLOB_RECURSE PLUGIN_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") +include(${CMAKE_SOURCE_DIR}/cmake/PluginDefinition.cmake) \ No newline at end of file diff --git a/src/plugins/Logic/Logics.cpp b/src/plugins/Logic/Logics.cpp new file mode 100644 index 0000000..67f0a29 --- /dev/null +++ b/src/plugins/Logic/Logics.cpp @@ -0,0 +1,9 @@ +#include "Logics.h" + +#include "components/ClockC.h" + +void Logics::registerComponents(ComponentRegister& cr) { + cr.registerComponent("L_Clock"); +} + +void Logics::registerNodes(NodeRegister& nr) {} \ No newline at end of file diff --git a/src/plugins/Logic/Logics.h b/src/plugins/Logic/Logics.h new file mode 100644 index 0000000..3fb359f --- /dev/null +++ b/src/plugins/Logic/Logics.h @@ -0,0 +1,17 @@ +#ifndef LOGIC_H +#define LOGIC_H + +#include "plugin/PluginInterface.h" + +// Accepting naming suggestions instead of logics + +struct Logics final : RaynodesPluginI { + void registerComponents(ComponentRegister& cr) override; + void registerNodes(NodeRegister& nr) override; +}; + +extern "C" EXPORT RaynodesPluginI* CreatePlugin() { + return new Logics(); +} + +#endif //LOGIC_H \ No newline at end of file diff --git a/src/plugins/Logic/components/ClockC.h b/src/plugins/Logic/components/ClockC.h new file mode 100644 index 0000000..73013bd --- /dev/null +++ b/src/plugins/Logic/components/ClockC.h @@ -0,0 +1,14 @@ +#ifndef CLOCKC_H +#define CLOCKC_H + +#include "application/EditorContext.h" + +struct ClockC final : Component { + int delayMillis; + explicit ClockC(const ComponentTemplate ct) : Component(ct, 200, 20) {} + Component* clone() override { return new ClockC(*this); } + void draw(EditorContext& ec, Node& parent) override {} + void update(EditorContext& ec, Node& parent) override {} +}; + +#endif //CLOCKC_H \ No newline at end of file diff --git a/src/plugins/QuestScript/CMakeLists.txt b/src/plugins/QuestScript/CMakeLists.txt index 72c5f27..6b401e0 100644 --- a/src/plugins/QuestScript/CMakeLists.txt +++ b/src/plugins/QuestScript/CMakeLists.txt @@ -1,13 +1,3 @@ +set(PLUGIN_NAME QuestScript) file(GLOB_RECURSE PLUGIN_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") - -add_library(QuestScript SHARED ${PLUGIN_FILES}) - -set_target_properties(QuestScript PROPERTIES - PREFIX "" - # Sets the output directory for the actual library files - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins" - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins" -) - -target_include_directories(QuestScript PRIVATE ${CMAKE_SOURCE_DIR}/src/raynodes) -target_link_libraries(QuestScript PRIVATE editor) \ No newline at end of file +include(${CMAKE_SOURCE_DIR}/cmake/PluginDefinition.cmake) \ No newline at end of file diff --git a/src/raynodes/application/context/ContextInfo.h b/src/raynodes/application/context/ContextInfo.h index 03d8dcd..026370a 100644 --- a/src/raynodes/application/context/ContextInfo.h +++ b/src/raynodes/application/context/ContextInfo.h @@ -33,7 +33,7 @@ struct EXPORT Info final { static constexpr auto userTemplates = "res/Templates.txt"; static constexpr int majorVersion = 1; static constexpr int minorVersion = 0; - static constexpr int patch = 3; + static constexpr int patch = 4; static const char* getVersion(EditorContext& ec); }; diff --git a/src/raynodes/application/context/impl/ContextPersist.cpp b/src/raynodes/application/context/impl/ContextPersist.cpp index 0c13001..276d986 100644 --- a/src/raynodes/application/context/impl/ContextPersist.cpp +++ b/src/raynodes/application/context/impl/ContextPersist.cpp @@ -119,9 +119,10 @@ void SaveEditorData(FILE* file, EditorContext& ec) { io_save(file, ec.display.camera.zoom); io_save_newline(file); } + void SaveTemplates(FILE* file, EditorContext& ec) { io_save_section(file, "Templates"); - io_save(file, (int)ec.templates.registeredNodes.size()); + io_save(file, static_cast(ec.templates.registeredNodes.size())); io_save_newline(file); for (const auto& nt : ec.templates.registeredNodes | std::views::values) { io_save(file, compIndices.add(nt.nTemplate.label)); // The arbitrary id of the template diff --git a/src/raynodes/ui/Window.h b/src/raynodes/ui/Window.h index cae6f46..b71562a 100644 --- a/src/raynodes/ui/Window.h +++ b/src/raynodes/ui/Window.h @@ -5,6 +5,9 @@ #include +#pragma warning(push) +#pragma warning(disable : 4100) // unreferenced formal parameter + class Window { inline static Vector2 dragOffset = {0.0f, 0.0f}; Rectangle bodyRect; @@ -36,12 +39,14 @@ class Window { protected: // Events methods - virtual void onOpen(EditorContext&ec) {} - virtual void onClose(EditorContext&ec) {} + virtual void onOpen(EditorContext& ec) {} + virtual void onClose(EditorContext& ec) {} virtual void drawContent(EditorContext& ec, const Rectangle& body) = 0; private: void update(EditorContext& ec); }; +#pragma warning(pop) + #endif //MAGEQUEST_SRC_UI_COMPONENTS_GAMEWINDOW_H_ \ No newline at end of file diff --git a/test/ImportTest.cpp b/test/ImportTest.cpp index 0b1c683..956e5f3 100644 --- a/test/ImportTest.cpp +++ b/test/ImportTest.cpp @@ -75,14 +75,22 @@ TEST_CASE("Test getComponentData", "[Import]") { REQUIRE(raynodes::StringView::Hash(staticStr) == view.getHash()); } + // Vec2 + { + auto val = rn.getComponentData(6, "Vec2"); + Vec2 test{1.11, 1.222}; + REQUIRE(val.x == test.x); + REQUIRE(val.y == test.y); + } + // Vec3 - /** - Vec3 test{1.1, 1.22, 1.333}; - auto val = rn.getComponentData(3, "Vector3"); - REQUIRE(val.x == test.x); - REQUIRE(val.y == test.y); - REQUIRE(val.z == test.z); - */ + { + auto val = rn.getComponentData(3, "Vec3"); + Vec3 test{1.11, 1.222, 1.3333}; + REQUIRE(val.x == test.x); + REQUIRE(val.y == test.y); + REQUIRE(val.z == test.z); + } } TEST_CASE("Test getConnectionOut", "[Import]") { TestUtil::SetupCWD(); @@ -246,7 +254,7 @@ TEST_CASE("Test completeness - static tests", "[Import]") { TestUtil::SetupCWD(); auto rn = raynodes::importRN("res/Test1.rn"); - REQUIRE(rn.nodeCnt == 6); + REQUIRE(rn.nodeCnt == 7); REQUIRE(rn.connCnt == 6); REQUIRE(rn.templates[9].isNodeName(rn.fileData, "Dialog Choice") == true); diff --git a/test/PersistTest.cpp b/test/PersistTest.cpp index 299885f..08be8de 100644 --- a/test/PersistTest.cpp +++ b/test/PersistTest.cpp @@ -38,7 +38,9 @@ TEST_CASE("Test correct file creation and count", "[Persist]") { ec.core.hasUnsavedChanges = true; ec.persist.saveProject(ec); - REQUIRE(ec.core.nodes.size() == testSize); + // Reset state + ec.core.resetEditor(ec); + REQUIRE(std::filesystem::exists(testPath) == true); ec.persist.importProject(ec); @@ -54,20 +56,39 @@ TEST_CASE("Teset correct data persistence", "[Persist]") { constexpr int testInt = 5; constexpr const char* testString = "Test"; + constexpr const char* testFloat = "3.141592"; // Create nodes { ec.core.createNode(ec, "Text", {})->getComponent>("Text")->textField.buffer = testString; ec.core.createNode(ec, "Int", {})->getComponent("Int")->selectedMode = testInt; + auto vec2 = ec.core.createNode(ec, "Vec2", {})->getComponent>("Vec2")->textFields; + vec2[0].buffer = testFloat, vec2[1].buffer = testFloat; + auto vec3 = ec.core.createNode(ec, "Vec3", {})->getComponent>("Vec3")->textFields; + vec3[0].buffer = testFloat, vec3[1].buffer = testFloat, vec3[2].buffer = testFloat; } // Persist cycle ec.core.hasUnsavedChanges = true; ec.persist.saveProject(ec); + + // Reset state + ec.core.resetEditor(ec); + ec.persist.importProject(ec); - REQUIRE(ec.core.getNode(NodeID(0))->getComponent>("Text")->textField.buffer == testString); - REQUIRE(ec.core.getNode(NodeID(1))->getComponent("Int")->selectedMode == testInt); + // Test values + { + REQUIRE(ec.core.getNode(NodeID(0))->getComponent>("Text")->textField.buffer == testString); + REQUIRE(ec.core.getNode(NodeID(1))->getComponent("Int")->selectedMode == testInt); + auto vec2 = ec.core.getNode(NodeID(2))->getComponent>("Vec2")->textFields; + REQUIRE(vec2[0].buffer == testFloat); + REQUIRE(vec2[1].buffer == testFloat); + auto vec3 = ec.core.getNode(NodeID(3))->getComponent>("Vec3")->textFields; + REQUIRE(vec3[0].buffer == testFloat); + REQUIRE(vec3[1].buffer == testFloat); + REQUIRE(vec3[2].buffer == testFloat); + } } TEST_CASE("Benchmark saving and loading", "[Persist]") { diff --git a/test/TestUtil.h b/test/TestUtil.h index edae989..61004d0 100644 --- a/test/TestUtil.h +++ b/test/TestUtil.h @@ -27,6 +27,8 @@ #include "plugin/RegisterInterface.h" #include "BuiltIns/components/TextFieldC.h" #include "BuiltIns/components/MathC.h" +#include "BuiltIns/components/Vec2C.h" +#include "BuiltIns/components/Vec3C.h" namespace { void registerNodes(EditorContext& ec) { @@ -35,8 +37,12 @@ void registerNodes(EditorContext& ec) { ComponentRegister cr{ec, pc}; cr.registerComponent>("Text"); cr.registerComponent("Int"); + cr.registerComponent>("Vec2"); + cr.registerComponent>("Vec3"); nr.registerNode("Text", {{"Text", "Text"}}); nr.registerNode("Int", {{"Int", "Int"}}); + nr.registerNode("Vec2", {{"Vec2", "Vec2"}}); + nr.registerNode("Vec3", {{"Vec3", "Vec3"}}); } } // namespace diff --git a/test/res/Test1.rn b/test/res/Test1.rn index 720eec4..5090149 100644 --- a/test/res/Test1.rn +++ b/test/res/Test1.rn @@ -1,32 +1,33 @@ ---EditorData-- -66-75.000-180.0001.0001 ---Templates-- -14 -0Vector2Vec2255 -1MathOpOperation255 -2DisplayDisplay255 -3StringToNumConverter255 -4Vector3Vec3255 -5SeparateXYSeparator255 -6SeparateXYZSeparator255 -7TextFieldText255 -8NumberFieldNumber255 -9Dialog ChoiceNPCTypeDisplayTextChoice1Choice2Choice3Choice4255 -10DialogNPCTypeDisplayText255 -11QuestHeaderNameDescriptionZoneLevel255 -12GOTO PlayerZoneTarget255 -13GOTO Player ProximityZoneTargetDistance255 ---Nodes-- -70Test1-694-201 -71Test2-327-539 -822.33-290-118 -431.11.221.333-52742 -1417515 -95DisplayC1C2C3C4313-524 ---Connections-- -0-101-10 -3-104-10 -0-105-10 -1-105-10 -201400 -201401 \ No newline at end of file +--EditorData-- +76-43.048-42.0951.050 +--Templates-- +14 +0Vector2Vec2255 +1MathOpOperation255 +2DisplayDisplay255 +3StringToNumConverter255 +4Vector3Vec3255 +5SeparateXYSeparator255 +6SeparateXYZSeparator255 +7TextFieldText255 +8NumberFieldNumber255 +9Dialog ChoiceNPCTypeDisplayTextChoice1Choice2Choice3Choice4255 +10DialogNPCTypeDisplayText255 +11QuestHeaderNameDescriptionZoneLevel255 +12GOTO PositionNPCTypeZoneTarget255 +13GOTO ProximityNPCTypeZoneTargetDistance255 +--Nodes-- +70Test1-694-201 +71Test2-327-539 +822.33-270-120 +431.110000;1.222000;1.333300-52742 +1417515 +95DisplayC1C2C3C4313-524 +061.110000;1.222000-242190 +--Connections-- +0-101-10 +3-104-10 +0-105-10 +1-105-10 +201400 +201401