Merge branch 'main' into release-0.32

This commit is contained in:
MihailRis 2026-09-24 18:12:25 +03:00
commit 64e458d7af
6 changed files with 68 additions and 7 deletions

View file

@ -27,6 +27,7 @@ Two kinds of curve definitions are currently available, each producing a value `
- `@rotate` - rotates an object/bone - `@rotate` - rotates an object/bone
- `@scale` - scales an object/bone (multiplier) - `@scale` - scales an object/bone (multiplier)
- `@zoom` - camera zoom (multiplier) - `@zoom` - camera zoom (multiplier)
- `@texture` - change of a dynamically assigned texture (see [skeleton:set_texture](scripting/ecs.md#skeleton))
## Metadata ## Metadata
@ -73,6 +74,18 @@ Example for `curve bezier`:
@key frame 138 value 53.00411 lx 127.067 ly 53.00411 rx 138.001 ry 53.00411 @key frame 138 value 53.00411 lx 127.067 ly 53.00411 rx 138.001 ry 53.00411
``` ```
For the `texture` directive, it is simpler. Example:
```vcd
@texture name $0 {
@key frame 0 value entities/tireman:face_0
@key frame 24 value entities/tireman:face_1
}
```
> Here, `entities/tireman` is the name of the texture atlas containing `face_*` textures.
> The texture does not necessarily have to be in an atlas.
## Expression curves ## Expression curves
The attribute describes a function `f(t) = x`, where t is time in seconds. The attribute describes a function `f(t) = x`, where t is time in seconds.

View file

@ -27,6 +27,7 @@ VCA файл состоит из набора директив, порядок
- `@rotate` - поворот объекта/кости - `@rotate` - поворот объекта/кости
- `@scale` - масштабирование объекта/кости (множитель) - `@scale` - масштабирование объекта/кости (множитель)
- `@zoom` - масштаб камеры (множитель) - `@zoom` - масштаб камеры (множитель)
- `@texture` - смена динамически-назначаемой текстуры (см. [skeleton:set_texture](scripting/ecs.md#skeleton))
## Мета-информация ## Мета-информация
@ -73,6 +74,18 @@ VCA файл состоит из набора директив, порядок
@key frame 138 value 53.00411 lx 127.067 ly 53.00411 rx 138.001 ry 53.00411 @key frame 138 value 53.00411 lx 127.067 ly 53.00411 rx 138.001 ry 53.00411
``` ```
Для директивы texture всё проще. Пример:
```vcd
@texture name $0 {
@key frame 0 value entities/tireman:face_0
@key frame 24 value entities/tireman:face_1
}
```
> entities/tireman здесь - имя текстурного атласа, в котором находятся face_\* текстуры.
> Текстура не обязательно должна находиться в атласе.
## Кривые через выражения ## Кривые через выражения
Через атрибут описывается функция `f(t) = x` где t - время в секундах. Через атрибут описывается функция `f(t) = x` где t - время в секундах.

View file

@ -42,6 +42,7 @@ local this = {
CH_ROTATE = 2, CH_ROTATE = 2,
CH_SCALE = 3, CH_SCALE = 3,
CH_ZOOM = 4, CH_ZOOM = 4,
CH_TEXTURE = 5,
INT_CONST = 1, INT_CONST = 1,
INT_LINEAR = 2, INT_LINEAR = 2,

View file

@ -60,6 +60,11 @@ local env = {
Y = {0, 1, 0}, Y = {0, 1, 0},
Z = {0, 0, 1}, Z = {0, 0, 1},
DST = mat4.idt(), DST = mat4.idt(),
string_at = function(keys, frame)
local left, _ = key_neighbors(keys, frame)
left = keys[left]
return left.value
end,
value_at = function(keys, frame, interp) value_at = function(keys, frame, interp)
local left, right = key_neighbors(keys, frame) local left, right = key_neighbors(keys, frame)
if left == right then if left == right then
@ -147,6 +152,10 @@ local function codegen_track(raw_track, lineset, memoised, keysets, use_tsf)
code = code .. string.format( code = code .. string.format(
"\n local l%d = value_at_custom(keysets['%s'][%d], t * %s, %s)", "\n local l%d = value_at_custom(keysets['%s'][%d], t * %s, %s)",
i, lineset.target_name, i, raw_track.fps, valueat) i, lineset.target_name, i, raw_track.fps, valueat)
elseif line.channel == animation.CH_TEXTURE then
code = code .. string.format(
"\n local l%d = string_at(keysets['%s'][%d], t * %s)",
i, lineset.target_name, i, raw_track.fps)
else else
code = code .. string.format( code = code .. string.format(
"\n local l%d = value_at(keysets['%s'][%d], t * %s, %s)", "\n local l%d = value_at(keysets['%s'][%d], t * %s, %s)",
@ -164,7 +173,9 @@ local function codegen_track(raw_track, lineset, memoised, keysets, use_tsf)
scale[line.axis] = i scale[line.axis] = i
has_tsf = true has_tsf = true
elseif line.channel == animation.CH_ZOOM then elseif line.channel == animation.CH_ZOOM then
code = code .. "\n zoom = l" .. i code = code .. "\n zoom = l" .. i
elseif line.channel == animation.CH_TEXTURE then
code = code .. string.format("\n target:set_texture(%s, %s)", lineset.target_name:escape(), "l"..i)
end end
end end
@ -206,14 +217,17 @@ local function codegen_rig_target(raw_track, context)
local code = "\n if target.set_matrix and target.index then\n" local code = "\n if target.set_matrix and target.index then\n"
code = code .. " local dst = DST\n" code = code .. " local dst = DST\n"
for bone, lineset in pairs(raw_track.linesets) do for bone, lineset in pairs(raw_track.linesets) do
if lineset.target_type ~= "bone" then if lineset.target_type ~= "bone" and lineset.target_type ~= "texture" then
goto continue goto continue
end end
local lineset_code = codegen_track( local lineset_code = codegen_track(
raw_track, lineset, context.memoised, context.keysets, true) raw_track, lineset, context.memoised, context.keysets, true)
code = code .. "\n do" .. lineset_code .. "\n end\n" .. code = code .. "\n do" .. lineset_code .. "\n end\n"
" target:set_matrix(target:index(" .. string.escape(bone) .. "), dst)\n" if lineset.target_type == "bone" then
code = code ..
" target:set_matrix(target:index(" .. string.escape(bone) .. "), dst)\n"
end
::continue:: ::continue::
end end
return code .. " end" return code .. " end"

View file

@ -7,6 +7,7 @@ local action_to_channel = {
rotate = animation.CH_ROTATE, rotate = animation.CH_ROTATE,
scale = animation.CH_SCALE, scale = animation.CH_SCALE,
zoom = animation.CH_ZOOM, zoom = animation.CH_ZOOM,
texture = animation.CH_TEXTURE,
} }
local curve_to_interp = { local curve_to_interp = {
@ -50,6 +51,17 @@ local function parse_curve(line, node)
end end
end end
local function parse_simple_frames(line, node)
line.keys = {}
for j, key_node in ipairs(node) do
local keyframe = {
frame = tonumber(key_node.frame),
value = key_node.value,
}
table.insert(line.keys, keyframe)
end
end
local function parse_track(root) local function parse_track(root)
local raw_track = { local raw_track = {
duration = math.huge, duration = math.huge,
@ -74,8 +86,10 @@ local function parse_track(root)
local target_type = nil local target_type = nil
if node.bone then if node.bone then
target_type = "bone" target_type = "bone"
elseif tag == "texture" then
target_type = "texture"
end end
local target_name = node.bone or "" local target_name = node.bone or node.name or ""
local lineset = linesets[target_name] local lineset = linesets[target_name]
if not lineset then if not lineset then
lineset = { lineset = {
@ -86,16 +100,20 @@ local function parse_track(root)
linesets[target_name] = lineset linesets[target_name] = lineset
end end
local channel = action_to_channel[tag]
if not channel then
error("unknown directive " .. tag:escape())
end
local line = { local line = {
axis = node.by and ("xyz"):find(node.by) or "", axis = node.by and ("xyz"):find(node.by) or "",
channel = action_to_channel[tag] channel = channel
} }
if node.func then if node.func then
line.expression = node.func line.expression = node.func
elseif node.curve then elseif node.curve then
parse_curve(line, node) parse_curve(line, node)
else else
error("not implemented") parse_simple_frames(line, node)
end end
table.insert(lineset.lines, line) table.insert(lineset.lines, line)

View file

@ -104,6 +104,8 @@ void CameraControl::updateMouse(PlayerInput& input, int windowHeight) {
glm::vec3 CameraControl::updateCameraShaking( glm::vec3 CameraControl::updateCameraShaking(
const Hitbox& hitbox, float delta const Hitbox& hitbox, float delta
) { ) {
delta = glm::min(delta, 0.1f);
glm::vec3 offset {}; glm::vec3 offset {};
const float k = CAM_SHAKE_DELTA_K; const float k = CAM_SHAKE_DELTA_K;
const float ov = CAM_SHAKE_OFFSET_Y; const float ov = CAM_SHAKE_OFFSET_Y;