mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-04 18:41:51 +00:00
Merge branch 'main' into release-0.32
This commit is contained in:
commit
d6583e0334
17 changed files with 240 additions and 111 deletions
|
|
@ -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": {
|
||||
|
|
|
|||
33
dev/tests/parsing.lua
Normal file
33
dev/tests/parsing.lua
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
local src = "<test><sub>test</sub></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)
|
||||
|
|
@ -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
|
||||
|
|
@ -34,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 = {
|
||||
|
|
@ -97,6 +99,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,
|
||||
|
|
@ -130,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
|
||||
|
|
|
|||
|
|
@ -15,10 +15,15 @@ local exclude_patters = {
|
|||
}
|
||||
|
||||
-- TODO: replace with actual expression -> lua translator
|
||||
local function process_expression(src, memoised)
|
||||
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
|
||||
|
|
@ -127,6 +132,11 @@ for _, name in ipairs(math_funcs) do
|
|||
env[name] = math[name]
|
||||
end
|
||||
|
||||
local 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 +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) .. ")"
|
||||
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
|
||||
|
|
@ -183,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').. ", " ..
|
||||
|
|
@ -215,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
|
||||
|
|
@ -223,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
|
||||
|
|
@ -235,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 ""
|
||||
|
|
@ -272,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)
|
||||
process_expression(curve.func, context.memoised, "curve")
|
||||
), "<curve>", "t", env)()
|
||||
end
|
||||
|
||||
|
|
@ -290,7 +302,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 intensity = intensity or 1.0\n"
|
||||
.. code .. "\nend"
|
||||
|
||||
if animation.TRACE_CODEGEN then
|
||||
|
|
|
|||
|
|
@ -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,19 +25,20 @@ 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
|
||||
|
||||
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),
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -26,8 +27,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) * COLOR_TEMPERATURE_COMPENSATION;
|
||||
light *= 2.0;
|
||||
#endif
|
||||
|
||||
light = max(light, emission);
|
||||
|
|
@ -40,5 +45,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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -122,10 +122,16 @@ namespace {
|
|||
std::vector<Glyph>& 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<int>(face->glyph->advance.x >> 6)
|
||||
static_cast<int>(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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
@ -176,7 +189,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 +206,11 @@ static inline void draw_text(
|
|||
continue;
|
||||
}
|
||||
int yOffset = 0;
|
||||
float advance = baseAdvance;
|
||||
int xOffset = 0;
|
||||
float advance = font.getAdvance(c) / cellAdvance;
|
||||
if (auto glyph = font.getGlyph(c)) {
|
||||
yOffset = glyph->yOffset;
|
||||
advance = glyph->xAdvance /
|
||||
static_cast<float>(font.getLineHeight()) * 2.0f *
|
||||
baseAdvance;
|
||||
xOffset = glyph->xOffset;
|
||||
}
|
||||
uint charpage = c >> 8;
|
||||
if (charpage == page){
|
||||
|
|
@ -207,7 +219,7 @@ static inline void draw_text(
|
|||
batch,
|
||||
pos,
|
||||
glm::vec2(
|
||||
x,
|
||||
x + xOffset / cellAdvance,
|
||||
y - yOffset * (is3d ? -1 : 1) /
|
||||
static_cast<float>(font.getLineHeight())
|
||||
),
|
||||
|
|
@ -221,7 +233,7 @@ static inline void draw_text(
|
|||
else if (charpage > page && charpage < next){
|
||||
next = charpage;
|
||||
}
|
||||
x += advance / baseAdvance;
|
||||
x += advance;
|
||||
}
|
||||
page = next;
|
||||
next = MAX_CODEPAGES;
|
||||
|
|
@ -325,27 +337,26 @@ std::unique_ptr<Font> Font::createBitmapFont(
|
|||
return std::make_unique<Font>(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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ namespace vector_fonts {
|
|||
struct Glyph {
|
||||
int yOffset;
|
||||
int xAdvance;
|
||||
int xOffset = 0;
|
||||
};
|
||||
|
||||
class Font {
|
||||
|
|
@ -56,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;
|
||||
|
|
@ -86,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<Font> createBitmapFont(
|
||||
std::vector<std::unique_ptr<ImageData>> pages
|
||||
|
|
@ -96,7 +101,7 @@ private:
|
|||
int yoffset;
|
||||
int glyphInterval;
|
||||
bool monospace = true;
|
||||
std::vector<std::unique_ptr<Texture>> pages;
|
||||
std::vector<Glyph> glyphs;
|
||||
mutable std::vector<std::unique_ptr<Texture>> pages;
|
||||
mutable std::vector<Glyph> glyphs;
|
||||
std::optional<std::weak_ptr<vector_fonts::FontFile>> fontFile;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -255,9 +255,9 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
|
|||
// drawing caret here
|
||||
if (isFocused() && editable && static_cast<int>((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<Font>(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<double>(scrollStep) + 0.5f));
|
||||
}
|
||||
int lcaret = caret - rawTextCache.getTextLineOffset(line);
|
||||
int realoffset = rawTextCache.metrics.calcWidth(labelText, 0, lcaret) -
|
||||
static_cast<int>(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<int>(textOffset);
|
||||
|
||||
if (realoffset - width > 0) {
|
||||
setTextOffset(textOffset + realoffset - width);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ static int push_xml(lua::State* L, const xml::xmlelement& elem) {
|
|||
}
|
||||
|
||||
static std::unique_ptr<xml::xmlelement> 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<xml::xmlelement> 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<xml::xmlelement>("#");
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <initializer_list>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace util {
|
||||
template<typename T, int capacity>
|
||||
template<typename T, std::size_t capacity>
|
||||
class stack_vector {
|
||||
struct buffer {
|
||||
alignas(alignof(T)) char data[sizeof(T) * capacity];
|
||||
|
|
@ -16,18 +20,19 @@ namespace util {
|
|||
}
|
||||
};
|
||||
public:
|
||||
using size_type = std::size_t;
|
||||
stack_vector() : size_(0) {}
|
||||
|
||||
stack_vector(const stack_vector<T, capacity>& 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<T, capacity>&& 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;
|
||||
|
|
@ -69,61 +74,64 @@ namespace util {
|
|||
++size_;
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
void pop_back() noexcept {
|
||||
assert(!empty() && "pop_back() called on empty stack_vector");
|
||||
if (size_ > 0) {
|
||||
data_.ptr()[size_ - 1].~T();
|
||||
--size_;
|
||||
} else {
|
||||
throw std::underflow_error("stack vector is empty");
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
for (int i = 0; i < size_; ++i) {
|
||||
void clear() noexcept {
|
||||
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];
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
int size() const {
|
||||
size_type size() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
|
|
@ -152,6 +160,6 @@ namespace util {
|
|||
}
|
||||
private:
|
||||
buffer data_;
|
||||
int size_;
|
||||
size_type size_;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,3 +22,9 @@ TEST(XML, VCM) {
|
|||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(XML, TWO_WAYS) {
|
||||
auto src = "<test><sub>test</sub></test>";
|
||||
auto document = xml::parse("<test>", src);
|
||||
ASSERT_EQ(src, xml::stringify(*document->getRoot(), false));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue