From 6b36992119898d770c18b02dd431f074562d2525 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 26 Sep 2026 23:09:40 +0300 Subject: [PATCH 01/21] make VCA animation line intensity multiplier implicit --- res/modules/internal/animation_codegen.lua | 17 ++++++++++++++--- src/engine/Time.hpp | 3 ++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/res/modules/internal/animation_codegen.lua b/res/modules/internal/animation_codegen.lua index dc6a29f0e..97deab9df 100644 --- a/res/modules/internal/animation_codegen.lua +++ b/res/modules/internal/animation_codegen.lua @@ -15,7 +15,13 @@ local exclude_patters = { } -- TODO: replace with actual expression -> lua translator -local function process_expression(src, memoised) +local function process_expression(src, memoised, ismul) + if ismul then + src = string.format("(%s) * intensity + (1.0 - intensity)", src) + else + src = string.format("(%s) * intensity", src) + end + for i, pattern in ipairs(exclude_patters) do if src:find(pattern) then debug.print(exclude_patters) @@ -127,6 +133,11 @@ for _, name in ipairs(math_funcs) do env[name] = math[name] end +is_multiplier = { + [animation.CH_SCALE] = true, + [animation.CH_ZOOM] = true, +} + local function codegen_track(raw_track, lineset, memoised, keysets, use_tsf) local lines = lineset.lines local code = "" @@ -137,7 +148,7 @@ local function codegen_track(raw_track, lineset, memoised, keysets, use_tsf) for i, line in ipairs(lines) do if line.expression then code = code .. "\n local l" .. i .. " = (" .. - process_expression(line.expression, memoised) .. ")" + process_expression(line.expression, memoised, is_multiplier[line.channel]) .. ")" elseif line.keys then local target_keysets = keysets[lineset.target_name] if not target_keysets then @@ -290,7 +301,7 @@ function internals.compile_animation_track(raw_track, track_name) code = memoised_code .. "\n" .. code end - local src = "return function(target, t, m)\n" + local src = "return function(target, t, intensity, m)\n m = m or 1\n" .. code .. "\nend" if animation.TRACE_CODEGEN then diff --git a/src/engine/Time.hpp b/src/engine/Time.hpp index 9f914b1fc..b335db313 100644 --- a/src/engine/Time.hpp +++ b/src/engine/Time.hpp @@ -6,12 +6,13 @@ class Time { uint64_t frame = 0; double lastTime = 0.0; double delta = 0.0; + double timeMultiplier = 1.0; public: Time() {} void update(double currentTime) { frame++; - delta = currentTime - lastTime; + delta = (currentTime - lastTime) * timeMultiplier; lastTime = currentTime; } From c3ca6be0565204a4b1d4d4b05d05adea2c2b97b3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 26 Sep 2026 23:13:54 +0300 Subject: [PATCH 02/21] add missing local --- res/modules/internal/animation_codegen.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/modules/internal/animation_codegen.lua b/res/modules/internal/animation_codegen.lua index 97deab9df..50ae8a5a0 100644 --- a/res/modules/internal/animation_codegen.lua +++ b/res/modules/internal/animation_codegen.lua @@ -133,7 +133,7 @@ for _, name in ipairs(math_funcs) do env[name] = math[name] end -is_multiplier = { +local is_multiplier = { [animation.CH_SCALE] = true, [animation.CH_ZOOM] = true, } From 0151fdfbf7922bf8040f5d42fef32d8dde6a35f5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 27 Sep 2026 02:14:36 +0300 Subject: [PATCH 03/21] implement actual animation blending in core:animator --- res/modules/animation.lua | 3 +++ res/modules/internal/animation_codegen.lua | 9 +++++---- res/scripts/components/animator.lua | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/res/modules/animation.lua b/res/modules/animation.lua index 411623364..1e19822a4 100644 --- a/res/modules/animation.lua +++ b/res/modules/animation.lua @@ -97,6 +97,9 @@ local PlayingTrack = { } function this.play(name, target) + if target and target.reset_pose then + target:reset_pose() + end local track = setmetatable({ name = name, target = target, diff --git a/res/modules/internal/animation_codegen.lua b/res/modules/internal/animation_codegen.lua index 50ae8a5a0..a8fabab8e 100644 --- a/res/modules/internal/animation_codegen.lua +++ b/res/modules/internal/animation_codegen.lua @@ -194,7 +194,6 @@ local function codegen_track(raw_track, lineset, memoised, keysets, use_tsf) return code end - code = code .. "\n mat4.idt(dst)" if translation[1] or translation[2] or translation[3] then code = code .. "\n mat4.translate(dst, {" .. (translation[1] and ("l" .. translation[1]) or '0').. ", " .. @@ -226,7 +225,6 @@ end local function codegen_rig_target(raw_track, context) local code = "\n if target.set_matrix and target.index then\n" - code = code .. " local dst = DST\n" for bone, lineset in pairs(raw_track.linesets) do if lineset.target_type ~= "bone" and lineset.target_type ~= "texture" then goto continue @@ -234,10 +232,13 @@ local function codegen_rig_target(raw_track, context) local lineset_code = codegen_track( raw_track, lineset, context.memoised, context.keysets, true) - code = code .. "\n do" .. lineset_code .. "\n end\n" + code = code + .. string.format("\n local bone_index = target:index(%s)" + .. "\n local dst = target:get_matrix(bone_index)", string.escape(bone)) + .. "\n do" .. lineset_code .. "\n end\n" if lineset.target_type == "bone" then code = code .. - " target:set_matrix(target:index(" .. string.escape(bone) .. "), dst)\n" + " target:set_matrix(bone_index, dst)\n" end ::continue:: end diff --git a/res/scripts/components/animator.lua b/res/scripts/components/animator.lua index 611ffa6f8..341f7cf11 100644 --- a/res/scripts/components/animator.lua +++ b/res/scripts/components/animator.lua @@ -38,6 +38,7 @@ function set_state(name, fade_time) end local function update(delta) + rig:reset_pose() for _, state in pairs(states) do state.timer = state.timer + delta @@ -51,7 +52,7 @@ local function update(delta) end state.track = state.track or animation.get_track(state.track_id) - if state.track and intensity > 0.0 then + if state.track then state.track.func( rig, state.timer % math.min(MAX_DURATION, state.track.duration), From 64ba1dad4e8e9b214fd11cfd7c4391ed4bffb677 Mon Sep 17 00:00:00 2001 From: boolean-false Date: Sun, 27 Sep 2026 17:33:55 +0700 Subject: [PATCH 04/21] fix glyph positions --- src/coders/vector_fonts.cpp | 17 ++++++++++------- src/graphics/core/Font.cpp | 27 +++++++++++++-------------- src/graphics/core/Font.hpp | 1 + 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/coders/vector_fonts.cpp b/src/coders/vector_fonts.cpp index a2b6b55d0..9ebb3c8d8 100644 --- a/src/coders/vector_fonts.cpp +++ b/src/coders/vector_fonts.cpp @@ -122,10 +122,16 @@ namespace { std::vector& glyphs ) { int size = canvas.getWidth() / 16; + + const size_t end = (pageid + 1) * 256; + if (glyphs.size() < end) { + glyphs.resize(end); + } + for (int c = 0; c < 256; c++) { int codepoint = pageid << 8 | c; if (!renderGlyph(codepoint, bitmapDst)) { - glyphs.push_back(Glyph {0, size / 2}); + glyphs[codepoint] = Glyph {0, size / 2}; continue; } @@ -133,13 +139,10 @@ namespace { Glyph glyph { face->glyph->bitmap_top - size, - static_cast(face->glyph->advance.x >> 6) + static_cast(face->glyph->advance.x >> 6), + face->glyph->bitmap_left }; - if (codepoint < glyphs.size()) { - glyphs[codepoint] = std::move(glyph); - } else { - glyphs.push_back(std::move(glyph)); - } + glyphs[codepoint] = glyph; } canvas.flipY(); } diff --git a/src/graphics/core/Font.cpp b/src/graphics/core/Font.cpp index 4843387e4..e94881adc 100644 --- a/src/graphics/core/Font.cpp +++ b/src/graphics/core/Font.cpp @@ -176,7 +176,7 @@ static inline void draw_text( int y = 0; bool hasLines = false; - float baseAdvance = glm::length(right); + const float cellAdvance = interval * font.getLineHeight(); do { for (size_t i = 0; i < text.length(); i++) { @@ -193,12 +193,12 @@ static inline void draw_text( continue; } int yOffset = 0; - float advance = baseAdvance; + int xOffset = 0; + float advance = 1.0f; if (auto glyph = font.getGlyph(c)) { yOffset = glyph->yOffset; - advance = glyph->xAdvance / - static_cast(font.getLineHeight()) * 2.0f * - baseAdvance; + xOffset = glyph->xOffset; + advance = glyph->xAdvance / cellAdvance; } uint charpage = c >> 8; if (charpage == page){ @@ -207,7 +207,7 @@ static inline void draw_text( batch, pos, glm::vec2( - x, + x + xOffset / cellAdvance, y - yOffset * (is3d ? -1 : 1) / static_cast(font.getLineHeight()) ), @@ -221,7 +221,7 @@ static inline void draw_text( else if (charpage > page && charpage < next){ next = charpage; } - x += advance / baseAdvance; + x += advance; } page = next; next = MAX_CODEPAGES; @@ -329,23 +329,22 @@ const Glyph* Font::getGlyph(int codepoint) { if (codepoint < 0) { return nullptr; } - if (codepoint < glyphs.size()) { + const int codepage = codepoint >> 8; + if (codepoint < glyphs.size() && + (!fontFile.has_value() || + (codepage < pages.size() && pages[codepage]))) { return &glyphs.at(codepoint); } if (!this->fontFile.has_value() || this->fontFile->expired()) { return nullptr; } - int codepage = codepoint >> 8; if (codepage >= 1024) { return nullptr; } - if (glyphs.size() < (codepage << 8)) { - glyphs.resize(codepage << 8); - } auto fontFile = this->fontFile->lock(); if (pages.size() <= codepage) { - pages.resize(codepage); + pages.resize(codepage + 1); } - pages.push_back(fontFile->renderPage(codepage, glyphs, lineHeight)); + pages[codepage] = fontFile->renderPage(codepage, glyphs, lineHeight); return &glyphs.at(codepoint); } diff --git a/src/graphics/core/Font.hpp b/src/graphics/core/Font.hpp index 1f0e3ce8c..19e77ef83 100644 --- a/src/graphics/core/Font.hpp +++ b/src/graphics/core/Font.hpp @@ -26,6 +26,7 @@ namespace vector_fonts { struct Glyph { int yOffset; int xAdvance; + int xOffset = 0; }; class Font { From 61af506f4c9990bf39ef8e8803df56daae25d451 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 27 Sep 2026 16:14:14 +0300 Subject: [PATCH 05/21] fix xml.tostring --- dev/tests/parsing.lua | 33 +++++++++++++++++++++++++ src/logic/scripting/lua/libs/libxml.cpp | 9 ++++++- test/coders/xml.cpp | 6 +++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 dev/tests/parsing.lua diff --git a/dev/tests/parsing.lua b/dev/tests/parsing.lua new file mode 100644 index 000000000..3b1a4840d --- /dev/null +++ b/dev/tests/parsing.lua @@ -0,0 +1,33 @@ +local src = "test" +local parsed = xml.parse(src) +asserts.equals(src, xml.tostring(parsed, false)) + +local src = "{\"test\": \"test\", \"sub\": {\"test\": \"test\"}}" +local parsed = json.parse(src) +asserts.equals("test", parsed.test) +asserts.equals("test", parsed.sub.test) + +-- toml +local src = [[ +[section] +key = "value" +# comment +key2 = "other value" +number = 42 +pi = 3.14159 +enabled = true +items = ["a", "b", "c"] +table = { x = 1, y = 2 } +]] +local parsed = toml.parse(src) + +asserts.equals("value", parsed.section.key) +asserts.equals("other value", parsed.section.key2) +asserts.equals(42, parsed.section.number) +asserts.equals(3.14159, parsed.section.pi) +asserts.equals(true, parsed.section.enabled) +asserts.equals("a", parsed.section.items[1]) +asserts.equals("b", parsed.section.items[2]) +asserts.equals("c", parsed.section.items[3]) +asserts.equals(1, parsed.section.table.x) +asserts.equals(2, parsed.section.table.y) diff --git a/src/logic/scripting/lua/libs/libxml.cpp b/src/logic/scripting/lua/libs/libxml.cpp index 4aa11f3dd..575a849cb 100644 --- a/src/logic/scripting/lua/libs/libxml.cpp +++ b/src/logic/scripting/lua/libs/libxml.cpp @@ -30,7 +30,7 @@ static int push_xml(lua::State* L, const xml::xmlelement& elem) { } static std::unique_ptr toxml(lua::State* L) { - lua::getfield(L, TAG_ATTR); + lua::requirefield(L, TAG_ATTR); auto tag = lua::require_lstring(L, -1); lua::pop(L); @@ -39,6 +39,13 @@ static std::unique_ptr toxml(lua::State* L) { for (int i = 0; i < length; i++) { lua::rawgeti(L, i + 1); + if (lua::isstring(L, -1)) { + auto textElement = std::make_unique("#"); + textElement->set("#", std::string(lua::require_lstring(L, -1))); + elem->add(std::move(textElement)); + lua::pop(L); + continue; + } elem->add(toxml(L)); lua::pop(L); } diff --git a/test/coders/xml.cpp b/test/coders/xml.cpp index 1357a4492..a394d40a6 100644 --- a/test/coders/xml.cpp +++ b/test/coders/xml.cpp @@ -22,3 +22,9 @@ TEST(XML, VCM) { throw err; } } + +TEST(XML, TWO_WAYS) { + auto src = "test"; + auto document = xml::parse("", src); + ASSERT_EQ(src, xml::stringify(*document->getRoot(), false)); +} From 4549d4892403b6ebf320bb526df3d3fc9a8bcc22 Mon Sep 17 00:00:00 2001 From: nogitsune-youkai Date: Sun, 27 Sep 2026 19:47:52 +0300 Subject: [PATCH 06/21] Fix: Make clear and pop_back noexcept compliant --- src/util/stack_vector.hpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/util/stack_vector.hpp b/src/util/stack_vector.hpp index a58777845..e6565a811 100644 --- a/src/util/stack_vector.hpp +++ b/src/util/stack_vector.hpp @@ -69,18 +69,16 @@ namespace util { ++size_; } - void pop_back() { + void pop_back() noexcept { if (size_ > 0) { data_.ptr()[size_ - 1].~T(); --size_; - } else { - throw std::underflow_error("stack vector is empty"); - } + } } - void clear() { + void clear() noexcept { for (int i = 0; i < size_; ++i) { - data_.ptr()[i].~T(); + data_.ptr()[i].~T(); } size_ = 0; } From 771c72324cfffcb29b8727348e8f0db62f09eaee Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 27 Sep 2026 23:13:45 +0300 Subject: [PATCH 07/21] update advanced shading --- res/shaders/effects/deferred_lighting.glsl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/res/shaders/effects/deferred_lighting.glsl b/res/shaders/effects/deferred_lighting.glsl index 55e267bf3..8df2dd0d7 100644 --- a/res/shaders/effects/deferred_lighting.glsl +++ b/res/shaders/effects/deferred_lighting.glsl @@ -26,8 +26,12 @@ vec4 effect() { float emission = texture(u_emission, v_uv).r; + float rg = 1.0; #ifdef ENABLE_SHADOWS - light *= calc_shadow(modelpos, normal, length(pos)); + float shadow_mul = calc_shadow(modelpos, normal, length(pos)); + light *= shadow_mul; + rg *= min(1.0, shadow_mul + emission); + light *= 2.0; #endif light = max(light, emission); @@ -40,5 +44,5 @@ vec4 effect() { mix(1.0, CLOUDS_FOG_FACTOR_MUL, emission), mix(1.0, CLOUDS_FOG_CURVE_MUL, emission) ); - return vec4(mix(texture(u_screen, v_uv).rgb * mix(1.0, light, 1.0), fogColor, fog), 1.0); + return vec4(mix(texture(u_screen, v_uv).rgb * mix(1.0, light, 1.0) * vec3(rg, 0.5 + rg * 0.5, 1.0), fogColor, fog), 1.0); } From 2b8d97a2cacd6e37999d5230f5cb5aed4b446f3c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 27 Sep 2026 23:21:45 +0300 Subject: [PATCH 08/21] feat: color temperature compensation --- res/shaders/effects/deferred_lighting.glsl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/shaders/effects/deferred_lighting.glsl b/res/shaders/effects/deferred_lighting.glsl index 8df2dd0d7..274ceae56 100644 --- a/res/shaders/effects/deferred_lighting.glsl +++ b/res/shaders/effects/deferred_lighting.glsl @@ -3,6 +3,7 @@ #define CLOUDS_FOG_FACTOR_MUL 0.3f #define CLOUDS_FOG_CURVE_MUL 0.4f +#define COLOR_TEMPERATURE_COMPENSATION 1.12f vec4 effect() { vec4 pos = texture(u_position, v_uv); @@ -30,7 +31,7 @@ vec4 effect() { #ifdef ENABLE_SHADOWS float shadow_mul = calc_shadow(modelpos, normal, length(pos)); light *= shadow_mul; - rg *= min(1.0, shadow_mul + emission); + rg *= min(1.0, shadow_mul + emission) + COLOR_TEMPERATURE_COMPENSATION; light *= 2.0; #endif From 8726a95a9bbf15ef211420b5b0934c2ca4ff867e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 27 Sep 2026 23:23:21 +0300 Subject: [PATCH 09/21] fix --- res/shaders/effects/deferred_lighting.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/shaders/effects/deferred_lighting.glsl b/res/shaders/effects/deferred_lighting.glsl index 274ceae56..ebcf138aa 100644 --- a/res/shaders/effects/deferred_lighting.glsl +++ b/res/shaders/effects/deferred_lighting.glsl @@ -31,7 +31,7 @@ vec4 effect() { #ifdef ENABLE_SHADOWS float shadow_mul = calc_shadow(modelpos, normal, length(pos)); light *= shadow_mul; - rg *= min(1.0, shadow_mul + emission) + COLOR_TEMPERATURE_COMPENSATION; + rg *= min(1.0, shadow_mul + emission) * COLOR_TEMPERATURE_COMPENSATION; light *= 2.0; #endif From 611852c273b3cc20315b2e04a9b3d955693aa892 Mon Sep 17 00:00:00 2001 From: nogitsune-youkai Date: Mon, 28 Sep 2026 00:38:38 +0300 Subject: [PATCH 10/21] Refactor: Use size_type for sizes and indices --- src/util/stack_vector.hpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/util/stack_vector.hpp b/src/util/stack_vector.hpp index e6565a811..87749396b 100644 --- a/src/util/stack_vector.hpp +++ b/src/util/stack_vector.hpp @@ -1,9 +1,12 @@ #pragma once +#include +#include #include +#include namespace util { - template + template class stack_vector { struct buffer { alignas(alignof(T)) char data[sizeof(T) * capacity]; @@ -16,18 +19,19 @@ namespace util { } }; public: + using size_type = std::size_t; stack_vector() : size_(0) {} stack_vector(const stack_vector& other) : size_(other.size_) { - for (int i = 0; i < size_; ++i) { + for (size_type i = 0; i < size_; ++i) { new (&data_.ptr()[i]) T(other.data_.ptr()[i]); } } stack_vector(stack_vector&& other) noexcept : size_(other.size_) { - for (int i = 0; i < size_; ++i) { + for (size_type i = 0; i < size_; ++i) { new (&data_.ptr()[i]) T(std::move(other.data_.ptr()[i])); } other.size_ = 0; @@ -77,29 +81,29 @@ namespace util { } void clear() noexcept { - for (int i = 0; i < size_; ++i) { + for (size_type i = 0; i < size_; ++i) { data_.ptr()[i].~T(); } size_ = 0; } - T& operator[](int index) { + T& operator[](size_type index) { return data_.ptr()[index]; } - const T& operator[](int index) const { + const T& operator[](size_type index) const { return data_.ptr()[index]; } - T& at(int index) { - if (index < 0 || index >= size_) { + T& at(size_type index) { + if (index >= size_) { throw std::out_of_range("index out of range"); } return data_.ptr()[index]; } - const T& at(int index) const { - if (index < 0 || index >= size_) { + const T& at(size_type index) const { + if (index >= size_) { throw std::out_of_range("index out of range"); } return data_.ptr()[index]; @@ -121,7 +125,7 @@ namespace util { return data_.ptr()[size_ - 1]; } - int size() const { + size_type size() const { return size_; } @@ -150,6 +154,6 @@ namespace util { } private: buffer data_; - int size_; + size_type size_; }; } From ffba11e87fd1268208def134fa32412b075b8154 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 28 Sep 2026 01:40:48 +0300 Subject: [PATCH 11/21] update clouds shader --- res/shaders/clouds.glslv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/shaders/clouds.glslv b/res/shaders/clouds.glslv index 0b9c17774..e1576f633 100644 --- a/res/shaders/clouds.glslv +++ b/res/shaders/clouds.glslv @@ -22,7 +22,7 @@ void main() { vec3 pos3d = a_modelpos.xyz - u_cameraPos; - a_realnormal = v_normal.xyz * 2.0 - 1.0; + a_realnormal = vec3(0.0, 1.0, 0.0); a_normal = calc_screen_normal(a_realnormal); a_torchLight = vec4(calc_torch_light( From 19a952a3bec85a26365a544c031acac1e8b265bf Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 28 Sep 2026 01:47:23 +0300 Subject: [PATCH 12/21] update shadows.glsl --- res/shaders/lib/shadows.glsl | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/res/shaders/lib/shadows.glsl b/res/shaders/lib/shadows.glsl index 658e9a699..a686683f2 100644 --- a/res/shaders/lib/shadows.glsl +++ b/res/shaders/lib/shadows.glsl @@ -25,18 +25,14 @@ float calc_shadow( projCoords.z -= 0.00001 / u_shadowsRes + bias; float shadow = 0.0; - if (dot(realnormal, u_sunDir) < 0.0) { - // 3x3 kernel - for (int y = -1; y <= 1; y++) { - for (int x = -1; x <= 1; x++) { - vec3 offset = vec3(x, y, -(abs(x) + abs(y)) * 0.8) * step * 1.0 * u_shadowsSoftness; - shadow += texture(shadowsMap, projCoords + offset); - } + // 3x3 kernel + for (int y = -1; y <= 1; y++) { + for (int x = -1; x <= 1; x++) { + vec3 offset = vec3(x, y, -(abs(x) + abs(y)) * 0.8) * step * 1.0 * u_shadowsSoftness; + shadow += texture(shadowsMap, projCoords + offset); } - shadow /= 9.0; - } else { - shadow = 0.0; } + shadow /= 9.0; return shadow; } From 3378001b9bff52120725234afe2e625798871625 Mon Sep 17 00:00:00 2001 From: nogitsune-youkai Date: Mon, 28 Sep 2026 22:02:08 +0300 Subject: [PATCH 13/21] Fix: Add asserts to prevent undefined behavior on empty containers --- src/util/stack_vector.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/util/stack_vector.hpp b/src/util/stack_vector.hpp index 87749396b..3c506f160 100644 --- a/src/util/stack_vector.hpp +++ b/src/util/stack_vector.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -74,6 +75,7 @@ namespace util { } void pop_back() noexcept { + assert(!empty() && "pop_back() called on empty stack_vector"); if (size_ > 0) { data_.ptr()[size_ - 1].~T(); --size_; @@ -110,18 +112,22 @@ namespace util { } T& front() { + assert(!empty() && "front() called on empty stack_vector"); return data_.ptr()[0]; } T& back() { + assert(!empty() && "back() called on empty stack_vector"); return data_.ptr()[size_ - 1]; } const T& front() const { + assert(!empty() && "front() called on empty stack_vector"); return data_.ptr()[0]; } const T& back() const { + assert(!empty() && "back() called on empty stack_vector"); return data_.ptr()[size_ - 1]; } From ca68ddc03aad65bb365e6263bf68485b1d85e245 Mon Sep 17 00:00:00 2001 From: nogitsune-youkai Date: Tue, 29 Sep 2026 01:20:54 +0300 Subject: [PATCH 14/21] Improve Windows presets --- CMakePresets.json | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 508719628..86a6fd075 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -2,19 +2,36 @@ "version": 6, "configurePresets": [ { - "name": "default-vs-msvc-windows", + "name": "windows-msvc-base", + "hidden": true, "condition": { "type": "equals", "rhs": "${hostSystemName}", "lhs": "Windows" }, - "generator": "Visual Studio 17 2022", "binaryDir": "${sourceDir}/build", "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "VOXELENGINE_BUILD_TESTS": "ON" } }, + { + "name": "default-vs-msvc-windows", + "displayName": "Visual Studio (auto: latest installed)", + "inherits": "windows-msvc-base" + }, + { + "name": "vs2022", + "displayName": "Visual Studio 2022", + "inherits": "windows-msvc-base", + "generator": "Visual Studio 17 2022" + }, + { + "name": "vs2026", + "displayName": "Visual Studio 2026", + "inherits": "windows-msvc-base", + "generator": "Visual Studio 18 2026" + }, { "name": "default-ninja-mingw-windows", "condition": { From 7211e99bc87b3ed69c690fca0a3cd64fc49b8fd1 Mon Sep 17 00:00:00 2001 From: boolean-false Date: Tue, 29 Sep 2026 20:31:14 +0700 Subject: [PATCH 15/21] fix text width and caret positioning in text boxes --- src/graphics/core/Font.cpp | 46 ++++++++++++++++++---------- src/graphics/core/Font.hpp | 10 ++++-- src/graphics/ui/elements/TextBox.cpp | 38 ++++++++++++++++------- 3 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/graphics/core/Font.cpp b/src/graphics/core/Font.cpp index e94881adc..fde3e0c17 100644 --- a/src/graphics/core/Font.cpp +++ b/src/graphics/core/Font.cpp @@ -29,6 +29,10 @@ Font::Font( if (this->fontFile.has_value()) { if (auto fontPtr = this->fontFile->lock()) { monospace = fontPtr->isMonospace(); + if (monospace && this->glyphs.size() > '0' && + this->glyphs['0'].xAdvance > 0) { + glyphInterval = this->glyphs['0'].xAdvance; + } } } } @@ -62,21 +66,13 @@ bool Font::isPrintableChar(uint codepoint) const { int FontMetrics::calcWidth(std::wstring_view text, size_t offset, size_t length) const { auto font = this->font.has_value() ? this->font->lock() : nullptr; - if (font == nullptr || font->isMonospace()) { - return std::min(text.length() - offset, length) * _glyphInterval; + if (font != nullptr) { + return font->calcWidth(text, offset, length); } - int totalWidth = 0; - for (int i = offset; i < offset + length && i < text.length(); i++) { - auto codepoint = text[i]; - if (!font->isPrintableChar(codepoint)) { - totalWidth += _glyphInterval; - } else if (auto glyph = font->getGlyph(codepoint)) { - totalWidth += glyph->xAdvance; - } else { - totalWidth += _glyphInterval; - } + if (offset >= text.length()) { + return 0; } - return totalWidth; + return std::min(text.length() - offset, length) * _glyphInterval; } int Font::calcWidth(std::wstring_view text, size_t length) const { @@ -84,7 +80,24 @@ int Font::calcWidth(std::wstring_view text, size_t length) const { } int Font::calcWidth(std::wstring_view text, size_t offset, size_t length) const { - return std::min(text.length()-offset, length) * glyphInterval; + if (offset >= text.length()) { + return 0; + } + size_t end = offset + std::min(text.length() - offset, length); + int width = 0; + for (size_t i = offset; i < end; i++) { + width += getAdvance(text[i]); + } + return width; +} + +int Font::getAdvance(uint codepoint) const { + if (isPrintableChar(codepoint)) { + if (auto glyph = getGlyph(codepoint)) { + return glyph->xAdvance; + } + } + return glyphInterval; } static inline void draw_glyph( @@ -194,11 +207,10 @@ static inline void draw_text( } int yOffset = 0; int xOffset = 0; - float advance = 1.0f; + float advance = font.getAdvance(c) / cellAdvance; if (auto glyph = font.getGlyph(c)) { yOffset = glyph->yOffset; xOffset = glyph->xOffset; - advance = glyph->xAdvance / cellAdvance; } uint charpage = c >> 8; if (charpage == page){ @@ -325,7 +337,7 @@ std::unique_ptr Font::createBitmapFont( return std::make_unique(std::move(textures), std::move(glyphs), res, 4); } -const Glyph* Font::getGlyph(int codepoint) { +const Glyph* Font::getGlyph(int codepoint) const { if (codepoint < 0) { return nullptr; } diff --git a/src/graphics/core/Font.hpp b/src/graphics/core/Font.hpp index 19e77ef83..b20b5e530 100644 --- a/src/graphics/core/Font.hpp +++ b/src/graphics/core/Font.hpp @@ -57,6 +57,10 @@ public: /// @return pixel width of the substring int calcWidth(std::wstring_view text, size_t offset, size_t length) const; + /// @brief Get the horizontal advance of a character in pixels + /// @param codepoint character unicode codepoint + int getAdvance(uint codepoint) const; + /// @brief Check if character is visible (non-whitespace) /// @param codepoint character unicode codepoint bool isPrintableChar(uint codepoint) const; @@ -87,7 +91,7 @@ public: return {std::nullopt, lineHeight, yoffset, glyphInterval}; } - const Glyph* getGlyph(int codepoint); + const Glyph* getGlyph(int codepoint) const; static std::unique_ptr createBitmapFont( std::vector> pages @@ -97,7 +101,7 @@ private: int yoffset; int glyphInterval; bool monospace = true; - std::vector> pages; - std::vector glyphs; + mutable std::vector> pages; + mutable std::vector glyphs; std::optional> fontFile; }; diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index d60b09142..2f6a20301 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -255,9 +255,9 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) { // drawing caret here if (isFocused() && editable && static_cast((time - caretLastMove) * 2) % 2 == 0) { uint line = label->getLineByTextIndex(caret); - auto linestart = label->getTextLineOffset(line); - uint lcaret = caret - label->getTextLineOffset(line); - int width = rawTextCache.metrics.calcWidth(input.substr(linestart), 0, lcaret); + size_t lineStart = label->getTextLineOffset(line); + size_t lineCaret = caret - lineStart; + int width = rawTextCache.metrics.calcWidth(input, lineStart, lineCaret); batch->rect( lcoord.x + width, @@ -275,11 +275,13 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) { uint endLine = label->getLineByTextIndex(selectionEnd); batch->setColor(glm::vec4(0.8f, 0.9f, 1.0f, 0.25f)); + size_t startLineOffset = label->getTextLineOffset(startLine); + size_t endLineOffset = label->getTextLineOffset(endLine); int start = rawTextCache.metrics.calcWidth( - labelText, 0, selectionStart - label->getTextLineOffset(startLine) + labelText, startLineOffset, selectionStart - startLineOffset ); int end = rawTextCache.metrics.calcWidth( - labelText, 0, selectionEnd - label->getTextLineOffset(endLine) + labelText, endLineOffset, selectionEnd - endLineOffset ); int lineY = label->getLineYOffset(startLine); @@ -352,6 +354,11 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) { } void TextBox::drawBackground(const DrawContext& pctx, const Assets& assets) { + if (lineNumbersLabel->getFontName() != label->getFontName()) { + lineNumbersLabel->setFontName(label->getFontName()); + } + lineNumbersLabel->setLineInterval(label->getLineInterval()); + auto font = assets.getShared(label->getFontName()); if (font != nullptr) { rawTextCache.prepare(font, font->getMetrics(), label->getSize().x); @@ -681,13 +688,19 @@ int TextBox::calcIndexAt(int x, int y) const { uint line = label->getLineByYOffset(y - lcoord.y); line = std::min(line, label->getLinesNumber() - 1); size_t lineLength = getLineLength(line); + size_t lineStart = label->getTextLineOffset(line); uint offset = 0; - while (lcoord.x + rawTextCache.metrics.calcWidth(labelText, 0, offset) < x && - offset < lineLength - 1) { + while (offset < lineLength - 1) { + int width = rawTextCache.metrics.calcWidth( + labelText, lineStart, offset + ); + if (lcoord.x + width >= x) { + break; + } offset++; } return std::min( - offset + label->getTextLineOffset(line), labelText.length() + offset + lineStart, labelText.length() ); } @@ -1224,9 +1237,12 @@ void TextBox::setCaret(size_t position) { offset -= getSize().y; scrolled(-glm::ceil(offset / static_cast(scrollStep) + 0.5f)); } - int lcaret = caret - rawTextCache.getTextLineOffset(line); - int realoffset = rawTextCache.metrics.calcWidth(labelText, 0, lcaret) - - static_cast(textOffset); + size_t lineStart = rawTextCache.getTextLineOffset(line); + size_t lineCaret = caret - lineStart; + int caretWidth = rawTextCache.metrics.calcWidth( + labelText, lineStart, lineCaret + ); + int realoffset = caretWidth - static_cast(textOffset); if (realoffset - width > 0) { setTextOffset(textOffset + realoffset - width); From ea63d29221903a948ed84b2c982e5a99e296037e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 29 Sep 2026 18:39:46 +0300 Subject: [PATCH 16/21] fix custom curves --- res/modules/internal/animation_codegen.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/res/modules/internal/animation_codegen.lua b/res/modules/internal/animation_codegen.lua index a8fabab8e..004cdc4a3 100644 --- a/res/modules/internal/animation_codegen.lua +++ b/res/modules/internal/animation_codegen.lua @@ -15,11 +15,13 @@ local exclude_patters = { } -- TODO: replace with actual expression -> lua translator -local function process_expression(src, memoised, ismul) - if ismul then - src = string.format("(%s) * intensity + (1.0 - intensity)", src) - else - src = string.format("(%s) * intensity", src) +local function process_expression(src, memoised, ismul, iscurve) + if not iscurve then + if ismul then + src = string.format("(%s) * intensity + (1.0 - intensity)", src) + else + src = string.format("(%s) * intensity", src) + end end for i, pattern in ipairs(exclude_patters) do @@ -284,7 +286,7 @@ function internals.compile_animation_track(raw_track, track_name) for name, curve in pairs(raw_track.curves) do context.curves[name] = load(string.format( "return function(kl, kr, t) return %s end", - process_expression(curve.func, context.memoised) + process_expression(curve.func, context.memoised, false, true) ), "", "t", env)() end From d3de449b7b0f1c777dd5cf714967d9d976461a70 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 29 Sep 2026 18:41:02 +0300 Subject: [PATCH 17/21] fix absolute bezier keyframes --- res/modules/animation.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/res/modules/animation.lua b/res/modules/animation.lua index 1e19822a4..d08b1bc18 100644 --- a/res/modules/animation.lua +++ b/res/modules/animation.lua @@ -21,8 +21,10 @@ local function bezier_interpolation(k0, k1, t) local u = t for i=1,8 do - local x = bezier(k0.frame, k0.rx, k1.lx, k1.frame, u) - local dx = bezier_derivative(k0.frame, k0.rx, k1.lx, k1.frame, u) + local x = bezier( + k0.frame, k0.rx + k0.frame, k1.lx + k1.frame, k1.frame, u) + local dx = bezier_derivative( + k0.frame, k0.rx + k0.frame, k1.lx + k1.frame, k1.frame, u) if math.abs(dx) < 1e-8 then break From dd577dd08059b2a4fa32bf1d1505916fa47d3ada Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 29 Sep 2026 18:44:19 +0300 Subject: [PATCH 18/21] cleanup --- res/modules/internal/animation_codegen.lua | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/res/modules/internal/animation_codegen.lua b/res/modules/internal/animation_codegen.lua index 004cdc4a3..0b96dc15b 100644 --- a/res/modules/internal/animation_codegen.lua +++ b/res/modules/internal/animation_codegen.lua @@ -15,18 +15,15 @@ local exclude_patters = { } -- TODO: replace with actual expression -> lua translator -local function process_expression(src, memoised, ismul, iscurve) - if not iscurve then - if ismul then - src = string.format("(%s) * intensity + (1.0 - intensity)", src) - else - src = string.format("(%s) * intensity", src) - end +local function process_expression(src, memoised, mode) + if mode == 'mul' then + src = string.format("(%s) * intensity + (1.0 - intensity)", src) + elseif mode == "add" then + src = string.format("(%s) * intensity", src) end for i, pattern in ipairs(exclude_patters) do if src:find(pattern) then - debug.print(exclude_patters) error("invalid syntax "..string.escape(src)) end end @@ -150,7 +147,8 @@ local function codegen_track(raw_track, lineset, memoised, keysets, use_tsf) for i, line in ipairs(lines) do if line.expression then code = code .. "\n local l" .. i .. " = (" .. - process_expression(line.expression, memoised, is_multiplier[line.channel]) .. ")" + process_expression(line.expression, memoised, + is_multiplier[line.channel] and "mul" or "add") .. ")" elseif line.keys then local target_keysets = keysets[lineset.target_name] if not target_keysets then @@ -286,7 +284,7 @@ function internals.compile_animation_track(raw_track, track_name) for name, curve in pairs(raw_track.curves) do context.curves[name] = load(string.format( "return function(kl, kr, t) return %s end", - process_expression(curve.func, context.memoised, false, true) + process_expression(curve.func, context.memoised, "curve") ), "", "t", env)() end From e4eae0416e2971be99fb4af2379e070bf50eace0 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 29 Sep 2026 18:59:28 +0300 Subject: [PATCH 19/21] fix the --- res/modules/animation.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/modules/animation.lua b/res/modules/animation.lua index d08b1bc18..817b1611f 100644 --- a/res/modules/animation.lua +++ b/res/modules/animation.lua @@ -36,7 +36,7 @@ local function bezier_interpolation(k0, k1, t) if u > 1 then u = 1 end end - return bezier(k0.value, k0.ry, k1.ly, k1.value, u) + return bezier(k0.value, k0.ry + k0.value, k1.ly + k1.value, k1.value, u) end local this = { From d062c28376d3d00eb7ea52472d95f7d89666a479 Mon Sep 17 00:00:00 2001 From: nogitsune-youkai Date: Fri, 2 Oct 2026 17:23:49 +0300 Subject: [PATCH 20/21] Fix: correct luajit lookup on Windows (#1006) * Fix: correct luajit paths * fixed comment * removed unnecessary comments --- src/CMakeLists.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bbe8a9763..802c06e1a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,23 +27,23 @@ endif() if(CMAKE_SYSTEM_NAME STREQUAL "Windows") find_package(vorbis REQUIRED) + + set(LUAJIT_PACKAGE_DIR "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}") if(VCPKG_TARGET_TRIPLET MATCHES "static") add_library(luajit STATIC IMPORTED) set_target_properties( luajit PROPERTIES IMPORTED_LOCATION - "$ENV{VCPKG_ROOT}/packages/luajit_${VCPKG_TARGET_TRIPLET}/lib/libluajit-5.1.a" + "${LUAJIT_PACKAGE_DIR}/lib/libluajit-5.1.a" INTERFACE_INCLUDE_DIRECTORIES - "$ENV{VCPKG_ROOT}/packages/luajit_${VCPKG_TARGET_TRIPLET}/include/" + "${LUAJIT_PACKAGE_DIR}/include/" ) else() - set(LUAJIT_PACKAGE_DIR "$ENV{VCPKG_ROOT}/packages/luajit_${VCPKG_TARGET_TRIPLET}") - - if(EXISTS "$ENV{VCPKG_ROOT}/packages/luajit_${VCPKG_TARGET_TRIPLET}/include/luajit-2.1") - set(LUAJIT_INCLUDE_DIR "$ENV{VCPKG_ROOT}/packages/luajit_${VCPKG_TARGET_TRIPLET}/include/luajit-2.1") + if(EXISTS "${LUAJIT_PACKAGE_DIR}/include/luajit-2.1") + set(LUAJIT_INCLUDE_DIR "${LUAJIT_PACKAGE_DIR}/include/luajit-2.1") else() - set(LUAJIT_INCLUDE_DIR "$ENV{VCPKG_ROOT}/packages/luajit_${VCPKG_TARGET_TRIPLET}/include/luajit") + set(LUAJIT_INCLUDE_DIR "${LUAJIT_PACKAGE_DIR}/include/luajit") endif() add_library(luajit SHARED IMPORTED) @@ -51,9 +51,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Windows") luajit PROPERTIES IMPORTED_LOCATION - "$ENV{VCPKG_ROOT}/packages/luajit_${VCPKG_TARGET_TRIPLET}/bin/lua51.dll" + "${LUAJIT_PACKAGE_DIR}/bin/lua51.dll" IMPORTED_IMPLIB - "$ENV{VCPKG_ROOT}/packages/luajit_${VCPKG_TARGET_TRIPLET}/lib/lua51.lib" + "${LUAJIT_PACKAGE_DIR}/lib/lua51.lib" INTERFACE_INCLUDE_DIRECTORIES "${LUAJIT_INCLUDE_DIR}" ) endif() From ef4207aed092bee8a0f1a658fadcae84d1324d51 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 2 Oct 2026 17:44:48 +0300 Subject: [PATCH 21/21] fixes --- res/modules/animation.lua | 3 +++ res/modules/internal/animation_codegen.lua | 4 ++-- res/scripts/components/animator.lua | 6 +++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/res/modules/animation.lua b/res/modules/animation.lua index 817b1611f..0c7bb7741 100644 --- a/res/modules/animation.lua +++ b/res/modules/animation.lua @@ -135,6 +135,9 @@ function internals.on_animation_frame() if track_info.__timer > track.duration then table.remove(playing_tracks, i) else + if track_info.target.reset_pose then + track_info.target:reset_pose() + end track.func(track_info.target, track_info.__timer) end end diff --git a/res/modules/internal/animation_codegen.lua b/res/modules/internal/animation_codegen.lua index 0b96dc15b..525240f66 100644 --- a/res/modules/internal/animation_codegen.lua +++ b/res/modules/internal/animation_codegen.lua @@ -247,7 +247,7 @@ end local function codegen_object_target(raw_track, context) local code = "\n if target.set_pos then\n" - code = code .. " local dst = DST\n" + code = code .. " local dst = mat4.idt()\n" local lineset = raw_track.linesets[""] if not lineset then return "" @@ -302,7 +302,7 @@ function internals.compile_animation_track(raw_track, track_name) code = memoised_code .. "\n" .. code end - local src = "return function(target, t, intensity, m)\n m = m or 1\n" + local src = "return function(target, t, intensity, m)\n m = m or 1\n intensity = intensity or 1.0\n" .. code .. "\nend" if animation.TRACE_CODEGEN then diff --git a/res/scripts/components/animator.lua b/res/scripts/components/animator.lua index 341f7cf11..e5f959df7 100644 --- a/res/scripts/components/animator.lua +++ b/res/scripts/components/animator.lua @@ -12,7 +12,7 @@ function create_state(name, track_id, max_intencity) fade_time = 0.0, fade_timer = 0.0, fade_in = false, - max_intencity = max_intencity, + max_intencity = max_intencity or 1.0, } end @@ -25,14 +25,14 @@ function set_state(name, fade_time) end if current_state then current_state.fade_in = false - current_state.fade_time = fade_time + current_state.fade_time = fade_time or 0.0 current_state.fade_timer = 0.0 end current_state = state if state then state.timer = 0.0 state.fade_in = true - state.fade_time = fade_time + state.fade_time = fade_time or 0.0 state.fade_timer = 0.0 end end