mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-04 10:31:50 +00:00
commit
349e61a3f9
59 changed files with 1674 additions and 506 deletions
|
|
@ -3,11 +3,13 @@
|
|||
`preload.json` in content-pack folder is used for specifying additional assets should be loaded, like sounds.
|
||||
|
||||
The file contains following categories available:
|
||||
- animation
|
||||
- fonts
|
||||
- shaders
|
||||
- textures
|
||||
- sounds
|
||||
- models
|
||||
- shaders
|
||||
- skeletons
|
||||
- sounds
|
||||
- textures
|
||||
|
||||
> [!NOTE]
|
||||
> Sound loading with all variations following template:
|
||||
|
|
|
|||
|
|
@ -37,6 +37,16 @@ assets.parse_model(
|
|||
skeleton_name: string
|
||||
)
|
||||
|
||||
-- Parses and loads animation
|
||||
assets.parse_animation(
|
||||
-- Animation file format (vca)
|
||||
format: string,
|
||||
-- Animation file content
|
||||
content: string,
|
||||
-- Animation name after loading
|
||||
name: string
|
||||
)
|
||||
|
||||
-- Creates a Canvas from a loaded texture.
|
||||
assets.to_canvas(
|
||||
-- The name of the loaded texture.
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ entity:def_name() -> str
|
|||
-- Returns the name of the entity skeleton
|
||||
entity:get_skeleton() -> str
|
||||
-- Replaces the entity skeleton
|
||||
entity:set_skeleton(name: str)
|
||||
entity:set_skeleton(name: str | nil)
|
||||
|
||||
-- Returns the unique entity identifier
|
||||
entity:get_uid() -> int
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ xml.tostring(data: XML, multiline: bool = true) -> str
|
|||
-- Parses XML
|
||||
xml.parse(code: str) -> XML
|
||||
|
||||
-- Parses the VCD (VoxelCore Declaration) format used by the VCM format.
|
||||
-- Parses the VCD (VoxelCore Declaration) format used by VCM and VCA formats.
|
||||
-- root_tag - a root element with the specified tag will be implicitly created,
|
||||
-- containing all root elements
|
||||
xml.parse_vcd(code: str, root_tag: str = "root") -> XML
|
||||
|
|
|
|||
34
doc/ru/animation.md
Normal file
34
doc/ru/animation.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Анимация
|
||||
|
||||
Для того, чтобы загрузить дорожку анимации для скелета сущности ([см. риггинг](rigging.md)) или камеры ([см. камеры](scripting/builtins/libcameras.md)) её, как любой, незагружаемый по-умолчанию, следует указать в [preload.json пака](assets-preload.md) в секции animation.
|
||||
|
||||
На данный момент, движком поддерживается один формат описания анимации - [VCA](vca.md).
|
||||
|
||||
Общий API для работы с анимацией, как и другими продолжительными событиями, существует библиотека animation, доступная в виде глобальной переменной.
|
||||
|
||||
Самый простой способ анимировать что-то:
|
||||
|
||||
```lua
|
||||
animation.play("имя_анимации", цель)
|
||||
```
|
||||
|
||||
Где цель может быть камерой:
|
||||
|
||||
```lua
|
||||
local camera = cameras.get(cameras.index("пак:имя_камеры"))
|
||||
animation.play("имя_анимации", camera)
|
||||
```
|
||||
|
||||
Или скелет сущности:
|
||||
|
||||
```lua
|
||||
animation.play("имя_анимации", entity.skeleton)
|
||||
```
|
||||
|
||||
Но для скелетов, подразумевающих переключение между состояниями (те же мобы), лучше использовать [компонент](scripting/ecs.md), реализующий простую машину состояний для анимации - [core:animator](#компонент-coreanimator)
|
||||
|
||||
Более подробно функциональность библиотеки animation описана [в соответствующем разделе](scripting/builtins/libanimation.md)
|
||||
|
||||
## Компонент core:animator
|
||||
|
||||
Компонент находится в работе. Зайдите позже.
|
||||
|
|
@ -3,11 +3,13 @@
|
|||
Для загрузки ассетов, не загружаемых автоматически, такие как звуки, дополнительные текстуры, используется файл `preload.json`, создающийся в папке контент-пака.
|
||||
|
||||
Ассеты в файле разделяются на категории:
|
||||
- animation - анимация
|
||||
- fonts - шрифты
|
||||
- shaders - шейдеры
|
||||
- textures - текстуры
|
||||
- sounds - звуки
|
||||
- models - модели
|
||||
- shaders - шейдеры
|
||||
- skeletons - скелеты
|
||||
- sounds - звуки
|
||||
- textures - текстуры
|
||||
|
||||
> [!NOTE]
|
||||
> При загрузке звука подгружаются все его вариации, по шаблону:
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
## Разделы
|
||||
|
||||
- [XML разметка интерфейса](xml-ui-layouts.md)
|
||||
- [Анимация](animation.md)
|
||||
- [Аудио](audio.md)
|
||||
- [Движок генерации мира](world-generator.md)
|
||||
- [Консоль](console.md)
|
||||
|
|
@ -21,4 +22,5 @@
|
|||
- [Скриптинг](scripting.md)
|
||||
- [Стили текста](text-styles.md)
|
||||
- [Формат моделей VCM](vcm.md)
|
||||
- [Формат анимации VCA](vca.md)
|
||||
- [Частицы](particles.md)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
- [Свойства и методы UI элементов](scripting/ui.md)
|
||||
- [Сущности и компоненты](scripting/ecs.md)
|
||||
- [Библиотеки](#)
|
||||
- [animation](scripting/builtins/libanimation.md)
|
||||
- [app](scripting/builtins/libapp.md)
|
||||
- [assets](scripting/builtins/libassets.md)
|
||||
- [base64](scripting/builtins/libbase64.md)
|
||||
|
|
|
|||
21
doc/ru/scripting/builtins/libanimation.md
Normal file
21
doc/ru/scripting/builtins/libanimation.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Библиотека *animation*
|
||||
|
||||
Библиотека для работы с анимацией и решения некоторых сопутствующих задач.
|
||||
|
||||
```lua
|
||||
-- Воспроизводит анимацию на указанном объекте
|
||||
animation.play(
|
||||
-- Имя дорожки анимации
|
||||
name: string,
|
||||
-- Цель, что будет анимирована
|
||||
target: Skeleton | Camera
|
||||
)
|
||||
```
|
||||
|
||||
```lua
|
||||
-- Запускает корутину, обновляющуюся вне паузы и завершающуюся вместе с выходом из мира.
|
||||
animation.action(
|
||||
-- Целевая асинхронная функция
|
||||
func: function()
|
||||
)
|
||||
```
|
||||
|
|
@ -38,6 +38,16 @@ assets.parse_model(
|
|||
skeleton_name: string
|
||||
)
|
||||
|
||||
-- Парсит и загружает анимацию
|
||||
assets.parse_animation(
|
||||
-- Формат файла анимации (vca)
|
||||
format: string,
|
||||
-- Содержимое файла анимации
|
||||
content: string,
|
||||
-- Имя анимации после загрузки
|
||||
name: string
|
||||
)
|
||||
|
||||
-- Создаёт холст (Canvas) из загруженной текстуры
|
||||
assets.to_canvas(
|
||||
-- Имя загруженной текстуры.
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ entity:def_name() -> str
|
|||
-- Возращает имя скелета сущности
|
||||
entity:get_skeleton() -> str
|
||||
-- Заменяет скелет сущности
|
||||
entity:set_skeleton(name: str)
|
||||
entity:set_skeleton(name: str | nil)
|
||||
|
||||
-- Возращает уникальный идентификатор сущности
|
||||
entity:get_uid() -> int
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ xml.tostring(data: XML, multiline: bool = true) -> str
|
|||
-- Парсит XML
|
||||
xml.parse(code: str) -> XML
|
||||
|
||||
-- Парсит формат VCD (VoxelCore Declaration), используемый форматом VCM.
|
||||
-- Парсит формат VCD (VoxelCore Declaration), используемый форматами VCM и VCA.
|
||||
-- root_tag - будет неявно создан корневой элемент с указанным тегом,
|
||||
-- содержащий все корневые элементы
|
||||
xml.parse_vcd(code: str, root_tag: str | nil = nil) -> XML
|
||||
|
|
|
|||
107
doc/ru/vca.md
Normal file
107
doc/ru/vca.md
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# VCA
|
||||
|
||||
Текстовый формат анимации, использующий синтаксис [VCM](vcm.md).
|
||||
|
||||
VCA файл состоит из набора директив, порядок следования которых не влияет на порядок применение трансформаций.
|
||||
|
||||
Директивы применяются к переданной цели, которая может быть скелетом сущности и камерой.
|
||||
При использовании скелета, в директиве указывается имя кости через атрибут `bone`:
|
||||
|
||||
```vcd
|
||||
@move bone имя_кости ...
|
||||
```
|
||||
|
||||
Директивы отвечают за отдельные скалярные значения, такие как x, y, z и т.д., указываемые через атрибут `by`:
|
||||
|
||||
```vcd
|
||||
@move bone hand by y ...
|
||||
```
|
||||
|
||||
На данный момент доступны два вида описания кривых, дающих значение `f(t) = x` где t - время:
|
||||
- [кривые с ключевыми кадрами (curve)](#кривые-с-ключевыми-кадрами)
|
||||
- [кривые через выражения (func)](#кривые-через-выражения)
|
||||
|
||||
## Директивы анимации
|
||||
|
||||
- `@move` - смещение объекта/кости
|
||||
- `@rotate` - поворот объекта/кости
|
||||
- `@scale` - масштабирование объекта/кости (множитель)
|
||||
- `@zoom` - масштаб камеры (множитель)
|
||||
|
||||
## Мета-информация
|
||||
|
||||
Информация о анимации в файле настраивается через директиву `configure`:
|
||||
- `fps` - частота кадров, являющаяся делителем при рассчёте длительности
|
||||
- `frames` - число кадров анимации, являющееся делимым при рассчёте длительности
|
||||
- `duration` - явно указанная длительность в секундах (требуется указание fps при наличии ключевых кадров)
|
||||
- `rotation-order` - указывает порядок применения углов Эйлера при вращении (так как порядок директив не учитывается форматом). Примеры: XYZ, ZYX, YZX.
|
||||
|
||||
Мета-информация применяется глобально. Поведение не зависит от её положения в файле, но рекомендуется располагать её в начале.
|
||||
|
||||
## Кривые с ключевыми кадрами
|
||||
|
||||
Требуется через атрибут `curve` указать тип кривой:
|
||||
- `const` - значения не интерполируются, сменяясь в момент достижения ключевого кадра
|
||||
- `linear` - ломанная линия, используется линейная интерполяция
|
||||
- `bezier` - ключевые кадры кривой Безье с явно указанными левой и правой касательными (lx, ly, rx, ry)
|
||||
|
||||
Ключевые кадры описываются в последующем `{...}` блоке в виде упорядоченных `@key` директив:
|
||||
|
||||
```vcd
|
||||
@rotate by z curve linear {
|
||||
@key ...
|
||||
@key ...
|
||||
}
|
||||
```
|
||||
|
||||
Ключевой кадр должен содержать номер кадра `frame` и значенин `value`. Для `bezier` кадров также описываются касательные.
|
||||
|
||||
Пример для `curve bezier`:
|
||||
```vcd
|
||||
@key frame 138 value 53.00411 lx 127.067 ly 53.00411 rx 138.001 ry 53.00411
|
||||
```
|
||||
|
||||
## Кривые через выражения
|
||||
|
||||
Через атрибут описывается функция `f(t) = x` где t - время в секундах.
|
||||
|
||||
Пример: `func (sin(t * 2.5) + pi)`.
|
||||
|
||||
Операции доступные в выражениях:
|
||||
|
||||
- `a + b` - сложение
|
||||
- `a * b` - умножение
|
||||
- `a / b` - деление
|
||||
- `a % b` - деление по модулю
|
||||
- `a ^ b` - возведение в степень
|
||||
|
||||
Константы доступные в выражениях:
|
||||
|
||||
- `pi` число π
|
||||
- `e` число Эйлера
|
||||
|
||||
Функции доступные в выражениях:
|
||||
|
||||
- `sin(x)` - синус от x
|
||||
- `cos(x)` - косинус от x
|
||||
- `tan(x)` - тангенс от x
|
||||
- `noise(x, octaves)` - быстрый одномерный шум от x с указанным числом октав
|
||||
- `noise2d(x, y, octaves)` - двумерный шум от x,y с указанным числом октав
|
||||
- `sign(x)` - целое число, указывающее знак x (-1/0/1)
|
||||
- `rand(low, high)` - псевдослучайное дробное число в диапазоне от low до high
|
||||
- `round(x, places)` - округляет значение x до указанного количества знаков после запятой places (опционально).
|
||||
- `floor(x)` - округляет значение x до ближайшего меньшего целого
|
||||
- `ceil(x)` - округляет значение x до ближайшего большего целого
|
||||
- `exp(x)` - $e^{x}$
|
||||
- `min(x, ...)` - выбирает наименьшее значение из переданных аргументов
|
||||
- `max(x, ...)` - выбирает наибольшее значение из переданных аргументов
|
||||
- `sqrt(x)` - квадратный корень от x
|
||||
- `log(x)` - $ln({x})$
|
||||
- `log10(x)` - $log_{10}({x})$
|
||||
- `deg(x)` - конвертирует радианы в градусы
|
||||
- `rad(x)` - конвертирует градусы в радианы
|
||||
|
||||
|
||||
>[!WARNING]
|
||||
> Использование незадокументированных функций может иметь иметь любые незадокументированные последствия.
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# VC-DBG protocol v1
|
||||
# VC-DBG protocol v1.1
|
||||
|
||||
## Notes
|
||||
|
||||
|
|
@ -116,6 +116,15 @@ Then `value` is:
|
|||
}
|
||||
```
|
||||
|
||||
### File hot-reload signal
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "hot-reload",
|
||||
"file": "string"
|
||||
}
|
||||
```
|
||||
|
||||
## Server-to-client
|
||||
|
||||
### Response signals
|
||||
|
|
|
|||
7
res/devtools/syntax/vca.toml
Normal file
7
res/devtools/syntax/vca.toml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
language = "VCA"
|
||||
extensions = ["vca"]
|
||||
line-comment = "#"
|
||||
identifier-part-chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"
|
||||
keywords = [
|
||||
"bone", "by", "func", "curve", "fps", "duration", "frames", "rotation-order"
|
||||
]
|
||||
30
res/models/human.vcm
Normal file
30
res/models/human.vcm
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
@bone name 'body' {
|
||||
@box from (-0.2,0,-0.125) to (0.2,0.5,0.15)
|
||||
@bone name 'head' move (0,0.5,0) {
|
||||
@box from (-0.2,0,-0.2) to (0.2,0.4,0.2)
|
||||
}
|
||||
@bone name 'leg_left' move (-0.125, 0, 0) rotate (-0,0,0) {
|
||||
@box from (-0.075, -0.45, -0.125) to (0.125, 0.0, 0.15)
|
||||
@bone name 'leg_left_btm' move (0, -0.45, 0) rotate (0,0,0) {
|
||||
@box from (-0.075, -0.45, -0.125) to (0.125, 0.0, 0.15)
|
||||
}
|
||||
}
|
||||
@bone name 'leg_right' move (0.125, 0.0, 0) rotate (0,0,0) {
|
||||
@box from (-0.125, -0.45, -0.125) to (0.075, 0.0, 0.15)
|
||||
@bone name 'leg_right_btm' move (0, -0.45, 0) rotate (0,0,0) {
|
||||
@box from (-0.125, -0.45, -0.125) to (0.075, 0.0, 0.15)
|
||||
}
|
||||
}
|
||||
@bone name 'hand_left' move (-0.325, 0.5, 0) rotate (-0,0,0) {
|
||||
@box from (-0.075, -0.45, -0.125) to (0.125, 0.0, 0.15)
|
||||
@bone name 'hand_left_btm' move (0, -0.45, 0) rotate (-0,0,0) {
|
||||
@box from (-0.075, -0.45, -0.125) to (0.125, 0.0, 0.15)
|
||||
}
|
||||
}
|
||||
@bone name 'hand_right' move (0.325, 0.5, 0) rotate (0,0,0) {
|
||||
@box from (-0.125, -0.45, -0.125) to (0.075, 0.0, 0.15)
|
||||
@bone name 'hand_right_btm' move (0, -0.45, 0) rotate (-0,0,0) {
|
||||
@box from (-0.125, -0.45, -0.125) to (0.075, 0.0, 0.15)
|
||||
}
|
||||
}
|
||||
}
|
||||
378
res/modules/animation.lua
Normal file
378
res/modules/animation.lua
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
local internals = __vc_internals
|
||||
|
||||
local this = {
|
||||
CH_TRANSLATE = 1,
|
||||
CH_ROTATE = 2,
|
||||
CH_SCALE = 3,
|
||||
CH_ZOOM = 4,
|
||||
|
||||
INT_CONST = 1,
|
||||
INT_LINEAR = 2,
|
||||
INT_BEZIER = 3,
|
||||
|
||||
TRACE_CODEGEN = false,
|
||||
}
|
||||
|
||||
local INT_CONST = this.INT_CONST
|
||||
local INT_BEZIER = this.INT_BEZIER
|
||||
|
||||
local function bezier(a, b, c, d, u)
|
||||
local s = 1 - u
|
||||
return s * s * s * a +
|
||||
3 * s * s * u * b +
|
||||
3 * s * u * u * c +
|
||||
u * u * u * d
|
||||
end
|
||||
|
||||
local function bezier_derivative(a, b, c, d, u)
|
||||
local s = 1 - u
|
||||
return
|
||||
3 * s * s * (b - a) +
|
||||
6 * s * u * (c - b) +
|
||||
3 * u * u * (d - c)
|
||||
end
|
||||
|
||||
local function bezier_interpolation(k0, k1, t)
|
||||
local frame = k0.frame + t * (k1.frame - k0.frame)
|
||||
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)
|
||||
|
||||
if math.abs(dx) < 1e-8 then
|
||||
break
|
||||
end
|
||||
|
||||
u = u - (x - frame) / dx
|
||||
|
||||
if u < 0 then u = 0 end
|
||||
if u > 1 then u = 1 end
|
||||
end
|
||||
|
||||
return bezier(k0.value, k0.ry, k1.ly, k1.value, u)
|
||||
end
|
||||
|
||||
local patterns = {
|
||||
{name="sint", pattern="sin(t)"},
|
||||
{name="sint2", pattern="sin(t * 2)"},
|
||||
}
|
||||
local exclude_patters = {
|
||||
"end",
|
||||
(string.pattern_safe("'")),
|
||||
(string.pattern_safe('"')),
|
||||
(string.pattern_safe("--")),
|
||||
(string.pattern_safe("..")),
|
||||
}
|
||||
|
||||
-- TODO: replace with actual expression -> lua translator
|
||||
local function process_expression(src, memoised)
|
||||
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
|
||||
for i, pattern in ipairs(patterns) do
|
||||
local pattern_safe = string.pattern_safe(pattern.pattern)
|
||||
if src:find(pattern_safe) then
|
||||
memoised[pattern.name] = pattern.pattern
|
||||
src = src:gsub(pattern_safe, pattern.name)
|
||||
end
|
||||
end
|
||||
return src
|
||||
end
|
||||
|
||||
local function key_neighbors(keys, frame)
|
||||
local left = 1
|
||||
local right = #keys
|
||||
|
||||
while left <= right do
|
||||
local mid = math.floor((left + right) / 2)
|
||||
|
||||
if keys[mid].frame < frame then
|
||||
left = mid + 1
|
||||
elseif keys[mid].frame > frame then
|
||||
right = mid - 1
|
||||
else
|
||||
return mid, mid
|
||||
end
|
||||
end
|
||||
if left > #keys then
|
||||
left = #keys
|
||||
end
|
||||
return right, left
|
||||
end
|
||||
|
||||
local env = {
|
||||
mat4 = mat4,
|
||||
e = math.exp(1),
|
||||
X = {1, 0, 0},
|
||||
Y = {0, 1, 0},
|
||||
Z = {0, 0, 1},
|
||||
DST = mat4.idt(),
|
||||
value_at = function(keys, frame, interp)
|
||||
local left, right = key_neighbors(keys, frame)
|
||||
if left == right then
|
||||
return keys[left].value
|
||||
end
|
||||
left = keys[left]
|
||||
if interp == INT_CONST then
|
||||
return left.value
|
||||
end
|
||||
right = keys[right]
|
||||
if left == nil then
|
||||
return right.value
|
||||
end
|
||||
local t = (frame - left.frame) / (right.frame - left.frame)
|
||||
if interp == INT_BEZIER then
|
||||
return bezier_interpolation(left, right, t)
|
||||
end
|
||||
return left.value * (1.0 - t) + right.value * t
|
||||
end,
|
||||
set_matrix = function(target, matrix)
|
||||
local info = mat4.decompose(matrix)
|
||||
if info then
|
||||
local pos = info.translation
|
||||
local rot = info.rotation
|
||||
local scale = info.scale
|
||||
if target.set_pos then
|
||||
target:set_pos(pos)
|
||||
end
|
||||
if target.set_rot then
|
||||
target:set_rot(rot)
|
||||
end
|
||||
if target.set_scale then
|
||||
target:set_scale(scale)
|
||||
end
|
||||
end
|
||||
end,
|
||||
dump = debug.print
|
||||
}
|
||||
|
||||
local math_funcs = {
|
||||
"sqrt", "min", "max", "deg", "rad", "log", "log10", "floor", "ceil", "sin",
|
||||
"tan", "noise", "noise2", "sign", "round", "exp", "pi", "e"
|
||||
}
|
||||
for _, name in ipairs(math_funcs) do
|
||||
env[name] = math[name]
|
||||
end
|
||||
|
||||
local function codegen_track(raw_track, lineset, memoised, keysets, use_tsf)
|
||||
local lines = lineset.lines
|
||||
local code = ""
|
||||
local has_tsf = false
|
||||
local translation = {false, false, false}
|
||||
local rotation = {false, false, false}
|
||||
local scale = {false, false, false}
|
||||
for i, line in ipairs(lines) do
|
||||
if line.expression then
|
||||
code = code .. "\n local l" .. i .. " = (" ..
|
||||
process_expression(line.expression, memoised) .. ")"
|
||||
elseif line.keys then
|
||||
local target_keysets = keysets[lineset.target_name]
|
||||
if not target_keysets then
|
||||
target_keysets = {}
|
||||
keysets[lineset.target_name] = target_keysets
|
||||
end
|
||||
target_keysets[i] = line.keys
|
||||
code = code .. string.format(
|
||||
"\n local l%d = value_at(keysets['%s'][%d], t * %s, %s)",
|
||||
i, lineset.target_name, i, raw_track.fps, line.interp)
|
||||
end
|
||||
|
||||
if line.channel == this.CH_TRANSLATE then
|
||||
translation[line.axis] = i
|
||||
has_tsf = true
|
||||
elseif line.channel == this.CH_ROTATE then
|
||||
rotation[line.axis] = i
|
||||
has_tsf = true
|
||||
elseif line.channel == this.CH_SCALE then
|
||||
scale[line.axis] = i
|
||||
has_tsf = true
|
||||
elseif line.channel == this.CH_ZOOM then
|
||||
code = code .. "\n zoom = l" .. i
|
||||
end
|
||||
end
|
||||
|
||||
if not has_tsf or not use_tsf then
|
||||
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').. ", " ..
|
||||
(translation[2] and ("l" .. translation[2]) or '0').. ", " ..
|
||||
(translation[3] and ("l" .. translation[3]) or '0').. "}, dst)"
|
||||
end
|
||||
|
||||
local axis_names = {"X", "Y", "Z"}
|
||||
local axis_indices = {X=1, Y=2, Z=3}
|
||||
local rotation_order = raw_track.rotation_order
|
||||
for i=1,3 do
|
||||
local axis = axis_indices[rotation_order[i]]
|
||||
local var = rotation[axis]
|
||||
if var then
|
||||
code = code .. "\n mat4.rotate(dst, " .. axis_names[axis] ..
|
||||
", l" .. var .. ", dst)"
|
||||
end
|
||||
end
|
||||
|
||||
if scale[1] or scale[2] or scale[3] then
|
||||
code = code .. "\n mat4.scale(dst, {" ..
|
||||
(scale[1] and ("l" .. scale[1]) or '1').. ", " ..
|
||||
(scale[2] and ("l" .. scale[2]) or '1').. ", " ..
|
||||
(scale[3] and ("l" .. scale[3]) or '1').. "}, dst)"
|
||||
end
|
||||
|
||||
return code
|
||||
end
|
||||
|
||||
local function codegen_rig_target(raw_track, memoised, keysets)
|
||||
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" then
|
||||
goto continue
|
||||
end
|
||||
local lineset_code = codegen_track(
|
||||
raw_track, lineset, memoised, keysets, true)
|
||||
|
||||
code = code .. "\n do" .. lineset_code .. "\n end\n" ..
|
||||
" target:set_matrix(target:index(" .. string.escape(bone) .. "), dst)\n"
|
||||
::continue::
|
||||
end
|
||||
return code .. " end"
|
||||
end
|
||||
|
||||
local function codegen_object_target(raw_track, memoised, keysets)
|
||||
local code = "\n if target.set_pos then\n"
|
||||
code = code .. " local dst = DST\n"
|
||||
local lineset = raw_track.linesets[""]
|
||||
if not lineset then
|
||||
return ""
|
||||
end
|
||||
local lineset_code = codegen_track(
|
||||
raw_track, lineset, memoised, keysets, true)
|
||||
code = code .. "\n do" .. lineset_code .. "\n end\n"
|
||||
.. " set_matrix(target, dst)\n"
|
||||
return code .. " end"
|
||||
end
|
||||
|
||||
local function codegen_camera_target(raw_track, memoised, keysets)
|
||||
local code = "\n if target.set_zoom then\n"
|
||||
code = code .. " local zoom = 1.0\n"
|
||||
local lineset = raw_track.linesets[""]
|
||||
if not lineset then
|
||||
return ""
|
||||
end
|
||||
local lineset_code = codegen_track(
|
||||
raw_track, lineset, memoised, keysets, false)
|
||||
code = code .. "\n do" .. lineset_code .. "\n end\n"
|
||||
.. " target:set_zoom(zoom)\n"
|
||||
return code .. " end"
|
||||
end
|
||||
|
||||
function this.compile_track(raw_track, track_name)
|
||||
local code = ""
|
||||
local memoised = {}
|
||||
local keysets = {}
|
||||
|
||||
code = code .. codegen_rig_target(raw_track, memoised, keysets)
|
||||
code = code .. codegen_object_target(raw_track, memoised, keysets)
|
||||
code = code .. codegen_camera_target(raw_track, memoised, keysets)
|
||||
|
||||
local memoised_code = ""
|
||||
for name, expression in pairs(memoised) do
|
||||
memoised_code = memoised_code .. "\n local " .. name .. " = "
|
||||
.. expression
|
||||
end
|
||||
|
||||
if #memoised_code > 0 then
|
||||
code = memoised_code .. "\n" .. code
|
||||
end
|
||||
|
||||
local src = "return function(target, t, m)\n"
|
||||
.. code .. "\nend"
|
||||
|
||||
if this.TRACE_CODEGEN then
|
||||
debug.log("["..
|
||||
string.escape(track_name or "nil").." codegen trace]:\n"..src)
|
||||
end
|
||||
|
||||
local generator, err = load(
|
||||
src, "<expr>", "bt", table.extend({keysets = keysets}, env))
|
||||
if not generator then
|
||||
error(err)
|
||||
end
|
||||
return {
|
||||
duration = raw_track.duration,
|
||||
func = generator(),
|
||||
}
|
||||
end
|
||||
|
||||
local loaded_tracks = {}
|
||||
local backup_tracks = {}
|
||||
|
||||
function internals.store_animation(name, track)
|
||||
loaded_tracks[name] = track
|
||||
end
|
||||
|
||||
function this.get_track(identifier)
|
||||
return loaded_tracks[identifier]
|
||||
end
|
||||
|
||||
local running_actions = {}
|
||||
local playing_tracks = {}
|
||||
|
||||
function this.action(func)
|
||||
table.insert(running_actions, coroutine.create(func))
|
||||
end
|
||||
|
||||
function this.play(name, target)
|
||||
table.insert(playing_tracks, {name=name, target=target, timer=0.0})
|
||||
end
|
||||
|
||||
function internals.on_animation_frame()
|
||||
for i=1,#running_actions do
|
||||
local co = running_actions[i]
|
||||
local status, result = coroutine.resume(co)
|
||||
if not status then
|
||||
debug.error("error in animation action: "..result)
|
||||
table.remove(running_actions, i)
|
||||
elseif coroutine.status(co) == "dead" then
|
||||
table.remove(running_actions, i)
|
||||
end
|
||||
end
|
||||
local delta = time.delta()
|
||||
for i=1,#playing_tracks do
|
||||
local track_info = playing_tracks[i]
|
||||
local track = loaded_tracks[track_info.name]
|
||||
if not track then
|
||||
debug.error("animation track not found: "..track_info.name)
|
||||
table.remove(playing_tracks, i)
|
||||
else
|
||||
track_info.timer = track_info.timer + delta
|
||||
if track_info.timer > track.duration then
|
||||
table.remove(playing_tracks, i)
|
||||
else
|
||||
track.func(track_info.target, track_info.timer)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function internals.stop_animation_actions()
|
||||
running_actions = {}
|
||||
end
|
||||
|
||||
function internals.backup_and_clear_animation()
|
||||
loaded_tracks, backup_tracks = backup_tracks, {}
|
||||
end
|
||||
|
||||
function internals.restore_animation_backup()
|
||||
loaded_tracks, backup_tracks = backup_tracks, {}
|
||||
end
|
||||
|
||||
return this
|
||||
107
res/modules/internal/formats/vca.lua
Normal file
107
res/modules/internal/formats/vca.lua
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
local internals = __vc_internals
|
||||
|
||||
local DEFAULT_FPS = 60
|
||||
local INT_BEZIER = animation.INT_BEZIER
|
||||
|
||||
local action_to_channel = {
|
||||
move = animation.CH_TRANSLATE,
|
||||
rotate = animation.CH_ROTATE,
|
||||
scale = animation.CH_SCALE,
|
||||
zoom = animation.CH_ZOOM,
|
||||
}
|
||||
|
||||
local curve_to_interp = {
|
||||
const = animation.INT_CONST,
|
||||
linear = animation.INT_LINEAR,
|
||||
bezier = animation.INT_BEZIER,
|
||||
}
|
||||
|
||||
local function parse_configure(raw_track, node)
|
||||
if node.fps then
|
||||
raw_track.fps = node.fps
|
||||
end
|
||||
if node.frames then
|
||||
raw_track.duration = node.frames / (node.fps or DEFAULT_FPS)
|
||||
elseif node.duration then
|
||||
raw_track.duration = node.duration
|
||||
end
|
||||
raw_track.rotation_order = string.upper(node["rotation-order"]) or "XYZ"
|
||||
end
|
||||
|
||||
local function parse_curve(line, node)
|
||||
line.interp = curve_to_interp[node.curve]
|
||||
line.keys = {}
|
||||
for j, key_node in ipairs(node) do
|
||||
local keyframe = {
|
||||
frame = tonumber(key_node.frame),
|
||||
value = tonumber(key_node.value),
|
||||
}
|
||||
if line.interp == INT_BEZIER then
|
||||
keyframe.lx = tonumber(key_node.lx)
|
||||
keyframe.ly = tonumber(key_node.ly)
|
||||
keyframe.rx = tonumber(key_node.rx)
|
||||
keyframe.ry = tonumber(key_node.ry)
|
||||
end
|
||||
table.insert(line.keys, keyframe)
|
||||
end
|
||||
end
|
||||
|
||||
local function parse_track(root)
|
||||
local raw_track = {
|
||||
duration = math.huge,
|
||||
fps = DEFAULT_FPS,
|
||||
linesets = {},
|
||||
}
|
||||
local linesets = raw_track.linesets
|
||||
for i, node in ipairs(root) do
|
||||
if type(node) == "string" then
|
||||
goto continue
|
||||
end
|
||||
local tag = node['#']
|
||||
if tag == "configure" then
|
||||
parse_configure(raw_track, node)
|
||||
goto continue
|
||||
end
|
||||
|
||||
local target_type = nil
|
||||
if node.bone then
|
||||
target_type = "bone"
|
||||
end
|
||||
local target_name = node.bone or ""
|
||||
local lineset = linesets[target_name]
|
||||
if not lineset then
|
||||
lineset = {
|
||||
lines = {},
|
||||
target_type = target_type,
|
||||
target_name = target_name
|
||||
}
|
||||
linesets[target_name] = lineset
|
||||
end
|
||||
|
||||
local line = {
|
||||
axis = node.by and ("xyz"):find(node.by) or "",
|
||||
channel = action_to_channel[tag]
|
||||
}
|
||||
if node.func then
|
||||
line.expression = node.func
|
||||
elseif node.curve then
|
||||
parse_curve(line, node)
|
||||
else
|
||||
error("not implemented")
|
||||
end
|
||||
|
||||
table.insert(lineset.lines, line)
|
||||
::continue::
|
||||
end
|
||||
return raw_track
|
||||
end
|
||||
|
||||
local function load_vca(source, filepath)
|
||||
local raw_track = parse_track(xml.parse_vcd(source, "track"))
|
||||
return animation.compile_track(raw_track, filepath)
|
||||
end
|
||||
|
||||
function internals.load_vca_animation(filepath, source, identifier)
|
||||
local track = load_vca(source or file.read(filepath), filepath)
|
||||
internals.store_animation(identifier, track)
|
||||
end
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
local internals = __vc_internals
|
||||
|
||||
local updating_blocks = {}
|
||||
local present_queues = {}
|
||||
local REGISTER_BIT = 0x1
|
||||
|
|
@ -5,7 +7,7 @@ local UPDATING_BIT = 0x2
|
|||
local PRESENT_BIT = 0x4
|
||||
local REMOVED_BIT = 0x8
|
||||
|
||||
block.__perform_ticks = function(delta)
|
||||
function internals.tick_updating_blocks(delta)
|
||||
for id, entry in pairs(updating_blocks) do
|
||||
entry.timer = entry.timer + delta
|
||||
local steps = math.floor(entry.timer / entry.delta * #entry / 3)
|
||||
|
|
@ -63,7 +65,7 @@ end
|
|||
local block_pull_register_events = block.__pull_register_events
|
||||
block.__pull_register_events = nil
|
||||
|
||||
block.__process_register_events = function()
|
||||
function internals.process_block_register_events()
|
||||
local register_events = block_pull_register_events()
|
||||
if not register_events then
|
||||
return
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
local __app = __vc_app
|
||||
local app = __vc_app
|
||||
local __rules = rules
|
||||
local internals = __vc_internals
|
||||
|
||||
local function configure_SSAO()
|
||||
-- Temporary using slot to configure built-in SSAO effect
|
||||
|
|
@ -32,10 +33,10 @@ local function configure_SSAO()
|
|||
end
|
||||
events.on("core:setting.graphics.ssao.set", update_ssao_quality)
|
||||
|
||||
update_ssao_quality(__app.get_setting("graphics.ssao"))
|
||||
update_ssao_quality(app.get_setting("graphics.ssao"))
|
||||
end
|
||||
|
||||
local function __vc_on_hud_open()
|
||||
function internals.on_hud_open()
|
||||
local _hud_is_content_access = hud._is_content_access
|
||||
local _hud_set_content_access = hud._set_content_access
|
||||
local _hud_set_debug_cheats = hud._set_debug_cheats
|
||||
|
|
@ -130,7 +131,8 @@ local function ScheduleGroup()
|
|||
end
|
||||
|
||||
local RULES_FILE = "world:rules.toml"
|
||||
local function __vc_on_world_open()
|
||||
|
||||
function internals.on_world_open()
|
||||
time.schedules.world = ScheduleGroup()
|
||||
|
||||
if not file.exists(RULES_FILE) then
|
||||
|
|
@ -142,15 +144,15 @@ local function __vc_on_world_open()
|
|||
end
|
||||
end
|
||||
|
||||
local function __vc_on_world_tick(tps)
|
||||
function internals.on_world_tick(tps)
|
||||
time.schedules.world:tick(1.0 / tps)
|
||||
end
|
||||
|
||||
local function __vc_process_before_quit()
|
||||
block.__process_register_events()
|
||||
function internals.process_before_quit()
|
||||
internals.process_block_register_events()
|
||||
end
|
||||
|
||||
local function __vc_on_world_save()
|
||||
function internals.on_world_save()
|
||||
local rule_values = {}
|
||||
for name, rule in pairs(__rules.rules) do
|
||||
rule_values[name] = rule.value
|
||||
|
|
@ -165,7 +167,8 @@ file.__close_all_descriptors = nil
|
|||
gui_util.__reset_local = nil
|
||||
stdcomp.reset = nil
|
||||
|
||||
local function __vc_on_world_quit()
|
||||
function internals.on_world_quit()
|
||||
internals.stop_animation_actions()
|
||||
__rules.clear()
|
||||
__gui_util_reset_local()
|
||||
__stdcomp_reset()
|
||||
|
|
@ -176,8 +179,6 @@ local __post_runnables = {}
|
|||
local fn_audio_reset_fetch_buffer = audio.__reset_fetch_buffer
|
||||
audio.__reset_fetch_buffer = nil
|
||||
|
||||
local __vc_update_coroutines = _G.__vc_update_coroutines
|
||||
|
||||
local function __process_post_runnables()
|
||||
if #__post_runnables > 0 then
|
||||
for _, func in ipairs(__post_runnables) do
|
||||
|
|
@ -189,41 +190,38 @@ local function __process_post_runnables()
|
|||
__post_runnables = {}
|
||||
end
|
||||
|
||||
__vc_update_coroutines()
|
||||
internals.update_coroutines()
|
||||
|
||||
fn_audio_reset_fetch_buffer()
|
||||
debug.pull_events()
|
||||
network.__process_events()
|
||||
if not hud or not hud.is_paused() then
|
||||
block.__process_register_events()
|
||||
block.__perform_ticks(time.delta())
|
||||
internals.process_block_register_events()
|
||||
internals.tick_updating_blocks(time.delta())
|
||||
end
|
||||
end
|
||||
|
||||
local __vc__is_post_runnable = false
|
||||
local is_post_runnable = false
|
||||
|
||||
function __vc__process_post_runnables()
|
||||
__vc__is_post_runnable = true
|
||||
function internals.process_post_runnables()
|
||||
is_post_runnable = true
|
||||
local success, err = pcall(__process_post_runnables)
|
||||
if not success then
|
||||
debug.error("an error ocurred while processing post-runnables: ".. err)
|
||||
end
|
||||
__vc__is_post_runnable = false
|
||||
is_post_runnable = false
|
||||
end
|
||||
|
||||
function internals.is_post_runnable_context()
|
||||
return is_post_runnable
|
||||
end
|
||||
|
||||
function time.post_runnable(runnable)
|
||||
table.insert(__post_runnables, runnable)
|
||||
end
|
||||
|
||||
return {
|
||||
__vc_on_hud_open = __vc_on_hud_open,
|
||||
__vc_on_world_open = __vc_on_world_open,
|
||||
__vc_on_world_tick = __vc_on_world_tick,
|
||||
__vc_process_before_quit = __vc_process_before_quit,
|
||||
__vc_on_world_save = __vc_on_world_save,
|
||||
__vc_on_world_quit = __vc_on_world_quit,
|
||||
__vc__process_post_runnables = __vc__process_post_runnables,
|
||||
__vc_is_post_runnable_context = function()
|
||||
return __vc__is_post_runnable
|
||||
end
|
||||
}
|
||||
function internals.on_render()
|
||||
internals.on_animation_frame()
|
||||
end
|
||||
|
||||
return {}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
local sin = math.sin
|
||||
local cos = math.cos
|
||||
local sqrt = math.sqrt
|
||||
local rad = math.rad
|
||||
|
||||
-- =================================================== --
|
||||
-- ====================== vec3 ======================= --
|
||||
-- =================================================== --
|
||||
|
|
@ -238,3 +243,93 @@ function vec2.mix(a, b, t, dest)
|
|||
}
|
||||
end
|
||||
end
|
||||
|
||||
function mat4.idt(dst)
|
||||
if dst then
|
||||
for i=1,16 do
|
||||
dst[i] = (i % 5 == 1) and 1 or 0
|
||||
end
|
||||
return dst
|
||||
end
|
||||
return {
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1
|
||||
}
|
||||
end
|
||||
|
||||
function mat4.rotate(...)
|
||||
local argc = select("#", ...)
|
||||
|
||||
local matrix, axis, angle, dst
|
||||
|
||||
if argc == 2 then
|
||||
axis, angle = ...
|
||||
matrix = mat4.idt()
|
||||
elseif argc == 3 then
|
||||
matrix, axis, angle = ...
|
||||
elseif argc == 4 then
|
||||
matrix, axis, angle, dst = ...
|
||||
else
|
||||
error("invalid arguments number (2, 3 or 4 expected)")
|
||||
end
|
||||
|
||||
angle = rad(angle)
|
||||
|
||||
local x, y, z = axis[1], axis[2], axis[3]
|
||||
|
||||
local len = sqrt(x * x + y * y + z * z)
|
||||
if len == 0 then
|
||||
error("rotation axis has zero length")
|
||||
end
|
||||
|
||||
x = x / len
|
||||
y = y / len
|
||||
z = z / len
|
||||
|
||||
local c = cos(angle)
|
||||
local s = sin(angle)
|
||||
local t = 1 - c
|
||||
|
||||
local r00 = t * x * x + c
|
||||
local r01 = t * x * y - s * z
|
||||
local r02 = t * x * z + s * y
|
||||
|
||||
local r10 = t * x * y + s * z
|
||||
local r11 = t * y * y + c
|
||||
local r12 = t * y * z - s * x
|
||||
|
||||
local r20 = t * x * z - s * y
|
||||
local r21 = t * y * z + s * x
|
||||
local r22 = t * z * z + c
|
||||
|
||||
local out = dst or {}
|
||||
|
||||
local m1, m2, m3, m4 = matrix[1], matrix[2], matrix[3], matrix[4]
|
||||
local m5, m6, m7, m8 = matrix[5], matrix[6], matrix[7], matrix[8]
|
||||
local m9, m10, m11, m12 = matrix[9], matrix[10], matrix[11], matrix[12]
|
||||
local m13, m14, m15, m16 = matrix[13], matrix[14], matrix[15], matrix[16]
|
||||
|
||||
out[1] = m1 * r00 + m5 * r10 + m9 * r20
|
||||
out[2] = m2 * r00 + m6 * r10 + m10 * r20
|
||||
out[3] = m3 * r00 + m7 * r10 + m11 * r20
|
||||
out[4] = m4 * r00 + m8 * r10 + m12 * r20
|
||||
|
||||
out[5] = m1 * r01 + m5 * r11 + m9 * r21
|
||||
out[6] = m2 * r01 + m6 * r11 + m10 * r21
|
||||
out[7] = m3 * r01 + m7 * r11 + m11 * r21
|
||||
out[8] = m4 * r01 + m8 * r11 + m12 * r21
|
||||
|
||||
out[9] = m1 * r02 + m5 * r12 + m9 * r22
|
||||
out[10] = m2 * r02 + m6 * r12 + m10 * r22
|
||||
out[11] = m3 * r02 + m7 * r12 + m11 * r22
|
||||
out[12] = m4 * r02 + m8 * r12 + m12 * r22
|
||||
|
||||
out[13] = m13
|
||||
out[14] = m14
|
||||
out[15] = m15
|
||||
out[16] = m16
|
||||
|
||||
return out
|
||||
end
|
||||
|
|
|
|||
|
|
@ -50,8 +50,9 @@ end
|
|||
local Skeleton = {__index={
|
||||
get_model=function(self, i) return __skeleton.get_model(self.eid, i) end,
|
||||
set_model=function(self, i, s) return __skeleton.set_model(self.eid, i, s) end,
|
||||
get_matrix=function(self, i) return __skeleton.get_matrix(self.eid, i) end,
|
||||
get_matrix=function(self, i, ...) return __skeleton.get_matrix(self.eid, i, ...) end,
|
||||
set_matrix=function(self, i, m) return __skeleton.set_matrix(self.eid, i, m) end,
|
||||
reset_pose=function(self) return __skeleton.reset_pose(self.eid) end,
|
||||
get_texture=function(self, s) return __skeleton.get_texture(self.eid, s) end,
|
||||
set_texture=function(self, s, s2) return __skeleton.set_texture(self.eid, s, s2) end,
|
||||
index=function(self, s) return __skeleton.index(self.eid, s) end,
|
||||
|
|
@ -86,7 +87,7 @@ local Entity = {__index={
|
|||
def_name=function(self) return entities.def_name(entities.get_def(self.eid)) end,
|
||||
get_player=function(self) return entities.get_player(self.eid) end,
|
||||
set_enabled=function(self, name, flag)
|
||||
local comp = self.components[name]
|
||||
local comp = self.components[name]
|
||||
if comp then
|
||||
if flag then
|
||||
if comp.__disabled and comp.on_enable then
|
||||
|
|
@ -112,6 +113,7 @@ return {
|
|||
entity.rigidbody = new_Rigidbody(eid)
|
||||
entity.skeleton = new_Skeleton(eid)
|
||||
entity.components = {}
|
||||
entity.ordered_components = {}
|
||||
entities[eid] = entity;
|
||||
return entity
|
||||
end,
|
||||
|
|
@ -122,6 +124,7 @@ return {
|
|||
local entity = entities[eid]
|
||||
if entity then
|
||||
entity.components = nil
|
||||
entity.ordered_components = nil
|
||||
entities[eid] = nil;
|
||||
end
|
||||
end,
|
||||
|
|
@ -130,10 +133,10 @@ return {
|
|||
if uid % parts ~= part then
|
||||
goto continue
|
||||
end
|
||||
for _, component in pairs(entity.components) do
|
||||
for _, component in ipairs(entity.ordered_components) do
|
||||
local callback = rawget(component, "on_update")
|
||||
if not component.__disabled and callback then
|
||||
local result, err = pcall(callback, tps)
|
||||
local _, err = pcall(callback, tps)
|
||||
if err then
|
||||
debug.error(err)
|
||||
end
|
||||
|
|
@ -144,7 +147,7 @@ return {
|
|||
end,
|
||||
physics_update = function(delta)
|
||||
for uid, entity in pairs(entities) do
|
||||
for _, component in pairs(entity.components) do
|
||||
for _, component in ipairs(entity.ordered_components) do
|
||||
local callback = rawget(component, "on_physics_update")
|
||||
if not component.__disabled and callback then
|
||||
local result, err = pcall(callback, delta)
|
||||
|
|
@ -157,10 +160,10 @@ return {
|
|||
end,
|
||||
render = function(delta)
|
||||
for _,entity in pairs(entities) do
|
||||
for _, component in pairs(entity.components) do
|
||||
for _, component in ipairs(entity.ordered_components) do
|
||||
local callback = rawget(component, "on_render")
|
||||
if not component.__disabled and callback then
|
||||
local result, err = pcall(callback, delta)
|
||||
local _, err = pcall(callback, delta)
|
||||
if err then
|
||||
debug.error(err)
|
||||
end
|
||||
|
|
|
|||
66
res/scripts/components/animator.lua
Normal file
66
res/scripts/components/animator.lua
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
local ALMOST_HUGE = 1e9
|
||||
|
||||
local rig = entity.skeleton
|
||||
|
||||
local states = {}
|
||||
|
||||
function create_state(name, track_id, max_intencity)
|
||||
states[name] = {
|
||||
track = nil,
|
||||
track_id = track_id,
|
||||
timer = 0.0,
|
||||
fade_time = 0.0,
|
||||
fade_timer = 0.0,
|
||||
fade_in = false,
|
||||
max_intencity = max_intencity,
|
||||
}
|
||||
end
|
||||
|
||||
local current_state = nil
|
||||
|
||||
function set_state(name, fade_time)
|
||||
local state = states[name]
|
||||
if state == current_state then
|
||||
return
|
||||
end
|
||||
if current_state then
|
||||
current_state.fade_in = false
|
||||
current_state.fade_time = fade_time
|
||||
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_timer = 0.0
|
||||
end
|
||||
end
|
||||
|
||||
local function update(delta)
|
||||
for _, state in pairs(states) do
|
||||
state.timer = state.timer + delta
|
||||
|
||||
local intensity = state.fade_in and 1.0 or 0.0
|
||||
if state.fade_timer < state.fade_time then
|
||||
state.fade_timer = state.fade_timer + delta
|
||||
intensity = math.min(1.0, state.fade_timer / state.fade_time)
|
||||
if not state.fade_in then
|
||||
intensity = 1.0 - intensity
|
||||
end
|
||||
end
|
||||
|
||||
state.track = state.track or animation.get_track(state.track_id)
|
||||
if state.track and intensity > 0.0 then
|
||||
state.track.func(
|
||||
rig,
|
||||
state.timer % math.min(ALMOST_HUGE, state.track.duration),
|
||||
intensity * state.max_intencity
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function on_render()
|
||||
update(time.delta())
|
||||
end
|
||||
|
|
@ -40,7 +40,7 @@ local function angle_delta(a, b)
|
|||
return normalize_angle(a - b)
|
||||
end
|
||||
|
||||
local dir = mat4.mul(tsf:get_rot(), {0, 0, -1})
|
||||
local mob_dir = mat4.mul(tsf:get_rot(), {0, 0, -1})
|
||||
local flight = false
|
||||
|
||||
function jump(multiplier)
|
||||
|
|
@ -87,12 +87,12 @@ function look_at(point, change_dir)
|
|||
local pos = tsf:get_pos()
|
||||
local viewdir = vec3.normalize(vec3.sub(point, pos))
|
||||
|
||||
local dot = vec3.dot(viewdir, dir)
|
||||
local dot = vec3.dot(viewdir, mob_dir)
|
||||
if dot < 0.0 and not change_dir then
|
||||
viewdir = mat4.mul(tsf:get_rot(), {0, 0, -1})
|
||||
else
|
||||
dir[1] = dir[1] * 0.8 + viewdir[1] * 0.13
|
||||
dir[3] = dir[3] * 0.8 + viewdir[3] * 0.13
|
||||
mob_dir[1] = mob_dir[1] * 0.8 + viewdir[1] * 0.13
|
||||
mob_dir[3] = mob_dir[3] * 0.8 + viewdir[3] * 0.13
|
||||
end
|
||||
|
||||
if not headIndex then
|
||||
|
|
@ -112,33 +112,30 @@ end
|
|||
|
||||
function follow_waypoints(pathfinding)
|
||||
pathfinding = pathfinding or entity:require_component("core:pathfinding")
|
||||
local pos = tsf:get_pos()
|
||||
|
||||
local pos = tsf:get_pos()
|
||||
local waypoint = pathfinding.next_waypoint()
|
||||
if not waypoint then
|
||||
return
|
||||
end
|
||||
local speed = props.movement_speed
|
||||
local vel = body:get_vel()
|
||||
dir = vec3.sub(
|
||||
mob_dir = vec3.sub(
|
||||
vec3.add(waypoint, {0.5, 0, 0.5}),
|
||||
{pos[1], math.floor(pos[2]), pos[3]}
|
||||
)
|
||||
local upper = dir[2] > 0
|
||||
dir[2] = 0.0
|
||||
vec3.normalize(dir, dir)
|
||||
move_horizontal(speed, {dir[1], dir[3]}, vel)
|
||||
local upper = mob_dir[2] > 0
|
||||
mob_dir[2] = 0.0
|
||||
vec3.normalize(mob_dir, mob_dir)
|
||||
move_horizontal(speed, {mob_dir[1], mob_dir[3]}, vel)
|
||||
if upper and body:is_grounded() then
|
||||
jump(1.0)
|
||||
end
|
||||
end
|
||||
|
||||
function set_dir(new_dir)
|
||||
dir = new_dir
|
||||
end
|
||||
function set_dir(new_dir) mob_dir = new_dir end
|
||||
|
||||
function get_dir()
|
||||
return dir
|
||||
end
|
||||
function get_dir() return mob_dir end
|
||||
|
||||
function is_flight() return flight end
|
||||
|
||||
|
|
@ -147,7 +144,7 @@ function set_flight(flag)
|
|||
body:set_gravity_scale(flight and 0.0 or props.gravity_scale)
|
||||
end
|
||||
|
||||
local prev_angle = (vec2.angle({dir[3], dir[1]})) % 360
|
||||
local prev_angle = (vec2.angle({mob_dir[3], mob_dir[1]})) % 360
|
||||
|
||||
function on_physics_update(delta)
|
||||
local grounded = body:is_grounded()
|
||||
|
|
@ -156,7 +153,7 @@ function on_physics_update(delta)
|
|||
(flight or not grounded) and props.air_damping or props.ground_damping
|
||||
)
|
||||
|
||||
local new_angle = (vec2.angle({dir[3], dir[1]})) % 360
|
||||
local new_angle = (vec2.angle({mob_dir[3], mob_dir[1]})) % 360
|
||||
local angle = prev_angle
|
||||
|
||||
local adelta = angle_delta(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
local __app = __vc_app
|
||||
local internals = __vc_internals
|
||||
local enable_experimental = __app.get_setting("debug.enable-experimental")
|
||||
|
||||
------------------------------------------------
|
||||
|
|
@ -45,7 +46,7 @@ local function complete_app_lib(app)
|
|||
app.tick = __app_tick
|
||||
|
||||
local function call_in_app_script_co(func, ...)
|
||||
if __vc_is_post_runnable_context() then
|
||||
if internals.is_post_runnable_context() then
|
||||
func(...)
|
||||
return
|
||||
end
|
||||
|
|
@ -208,7 +209,7 @@ function start_coroutine(chunk, name)
|
|||
__vc_named_coroutines[name] = co
|
||||
end
|
||||
|
||||
function __vc_update_coroutines()
|
||||
function internals.update_coroutines()
|
||||
local dead = {}
|
||||
for name, co in pairs(__vc_named_coroutines) do
|
||||
local success, err = coroutine.resume(co)
|
||||
|
|
@ -332,6 +333,9 @@ end
|
|||
world.raycast = entities.__world_raycast
|
||||
entities.__world_raycast = nil
|
||||
|
||||
animation = require "core:animation"
|
||||
require "core:internal/formats/vca"
|
||||
|
||||
__vc_scripts_registry = require "core:internal/scripts_registry"
|
||||
|
||||
file.open = require "core:internal/stream_providers/file"
|
||||
|
|
@ -366,6 +370,7 @@ core.get_core_token = audio.input.__get_core_token
|
|||
|
||||
require "core:internal/console"
|
||||
require "core:internal/deprecated"
|
||||
require "core:internal/internal_events"
|
||||
|
||||
|
||||
------------------------------------------
|
||||
|
|
@ -395,5 +400,4 @@ __vc_app = nil
|
|||
__vc_internals = nil
|
||||
__vc_lock_internal_modules()
|
||||
__vc_lock_internal_modules = nil
|
||||
__vc_update_coroutines = nil
|
||||
__VC_SCRIPT_NAME = ""
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ using namespace rigging;
|
|||
|
||||
void ModelReference::refresh(const Assets& assets) {
|
||||
if (updateFlag) {
|
||||
model = assets.get<model::Model>(name);
|
||||
model = assets.getShared<model::Model>(name);
|
||||
updateFlag = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -29,24 +29,27 @@ Bone::Bone(
|
|||
name(std::move(name)),
|
||||
bones(std::move(bones)),
|
||||
offset(offset),
|
||||
model({model, nullptr, true}) {
|
||||
model({model, {}, true}) {
|
||||
}
|
||||
|
||||
void Bone::setModel(const std::string& name) {
|
||||
if (model.name == name) {
|
||||
return;
|
||||
}
|
||||
model = {name, nullptr, true};
|
||||
model = {name, {}, true};
|
||||
}
|
||||
|
||||
Skeleton::Skeleton(std::shared_ptr<const SkeletonConfig> config)
|
||||
: config(config),
|
||||
pose(config->getBones().size()),
|
||||
calculated(config->getBones().size()),
|
||||
flags(config->getBones().size()),
|
||||
pose(config ? config->getBones().size() : 0),
|
||||
calculated(config ? config->getBones().size() : 0),
|
||||
flags(config ? config->getBones().size() : 0),
|
||||
textures(),
|
||||
modelOverrides(config->getBones().size()),
|
||||
modelOverrides(config ? config->getBones().size() : 0),
|
||||
visible(true) {
|
||||
if (config == nullptr) {
|
||||
return;
|
||||
}
|
||||
const auto& bones = config->getBones();
|
||||
for (size_t i = 0; i < bones.size(); i++) {
|
||||
flags[i].visible = true;
|
||||
|
|
@ -89,19 +92,15 @@ void Skeleton::deserialize(const dv::value& root) {
|
|||
void Skeleton::setConfig(std::shared_ptr<const SkeletonConfig> rigConfig) {
|
||||
config = std::move(rigConfig);
|
||||
|
||||
const auto& bones = config->getBones();
|
||||
int bonesCount = config ? config->getBones().size() : 0;
|
||||
|
||||
pose.matrices.resize(bonesCount, glm::mat4(1.0f));
|
||||
calculated.matrices.resize(bonesCount, glm::mat4(1.0f));
|
||||
|
||||
pose.matrices.resize(
|
||||
bones.size(), glm::mat4(1.0f)
|
||||
);
|
||||
calculated.matrices.resize(
|
||||
bones.size(), glm::mat4(1.0f)
|
||||
);
|
||||
modelOverrides.resize(bonesCount);
|
||||
flags.resize(bonesCount);
|
||||
|
||||
modelOverrides.resize(bones.size());
|
||||
flags.resize(bones.size());
|
||||
|
||||
for (size_t i = 0; i < bones.size(); i++) {
|
||||
for (size_t i = 0; i < bonesCount; i++) {
|
||||
flags[i].visible = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -194,12 +193,14 @@ void SkeletonConfig::render(
|
|||
continue;
|
||||
}
|
||||
node->model.refresh(assets);
|
||||
auto model = node->model.model;
|
||||
auto model = node->model.model.lock().get(); // TODO: cache model pointer during frame
|
||||
auto& modelOverride = skeleton.modelOverrides.at(i);
|
||||
if (modelOverride.updateFlag) {
|
||||
modelOverride.refresh(assets);
|
||||
}
|
||||
model = modelOverride.model ? modelOverride.model : model;
|
||||
if (auto foundOverride = modelOverride.model.lock()) {
|
||||
model = foundOverride.get();
|
||||
}
|
||||
if (model) {
|
||||
batch.draw(
|
||||
skeleton.calculated.matrices[i],
|
||||
|
|
@ -207,7 +208,9 @@ void SkeletonConfig::render(
|
|||
model,
|
||||
&skeleton.textures
|
||||
);
|
||||
}
|
||||
} else if (!node->model.name.empty()) {
|
||||
node->model.updateFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -33,8 +33,8 @@ namespace rigging {
|
|||
|
||||
struct ModelReference {
|
||||
std::string name;
|
||||
model::Model* model;
|
||||
bool updateFlag;
|
||||
std::weak_ptr<model::Model> model;
|
||||
bool updateFlag = false;
|
||||
|
||||
void refresh(const Assets& assets);
|
||||
};
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
#include "Assets.hpp"
|
||||
#include "AssetsLoader.hpp"
|
||||
#include "io/path.hpp"
|
||||
|
||||
Assets::Assets(util::ObjectsKeeper* vault) : vault(vault) {
|
||||
Assets::Assets(util::ObjectsKeeper* vault) : vault(vault), assetsLoadInfo(std::make_unique<AssetsLoadInfo>()) {
|
||||
}
|
||||
|
||||
Assets::~Assets() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/stringutil.hpp"
|
||||
#include "util/ObjectsKeeper.hpp"
|
||||
#include "graphics/core/TextureAnimation.hpp"
|
||||
#include "io/fwd.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
|
@ -11,13 +16,12 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "util/stringutil.hpp"
|
||||
#include "util/ObjectsKeeper.hpp"
|
||||
#include "graphics/core/TextureAnimation.hpp"
|
||||
|
||||
class Assets;
|
||||
struct AssetsLoadInfo;
|
||||
class AssetsLoader;
|
||||
|
||||
enum class AssetType {
|
||||
ANIMATION,
|
||||
TEXTURE,
|
||||
SHADER,
|
||||
FONT,
|
||||
|
|
@ -31,12 +35,10 @@ enum class AssetType {
|
|||
|
||||
namespace assetload {
|
||||
/// @brief final work to do in the main thread
|
||||
using postfunc = std::function<void(Assets*)>;
|
||||
|
||||
using setupfunc = std::function<void(const Assets*)>;
|
||||
using postfunc = std::function<void(Assets&)>;
|
||||
|
||||
template <class T>
|
||||
void assets_setup(const Assets*);
|
||||
void assets_setup(const Assets&);
|
||||
|
||||
class error : public std::runtime_error {
|
||||
AssetType type;
|
||||
|
|
@ -62,16 +64,13 @@ namespace assetload {
|
|||
return reason;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class Assets {
|
||||
util::ObjectsKeeper* vault;
|
||||
std::vector<TextureAnimation> animations;
|
||||
|
||||
using assets_map = std::unordered_map<std::string, std::shared_ptr<void>>;
|
||||
std::unordered_map<std::type_index, assets_map> assets;
|
||||
std::vector<assetload::setupfunc> setupFuncs;
|
||||
public:
|
||||
using assets_map = std::unordered_map<std::string, std::shared_ptr<void>>;
|
||||
|
||||
Assets(util::ObjectsKeeper* vault);
|
||||
Assets(const Assets&) = delete;
|
||||
~Assets();
|
||||
|
|
@ -143,20 +142,20 @@ public:
|
|||
return &mapIter->second;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
for (auto& setupFunc : setupFuncs) {
|
||||
setupFunc(this);
|
||||
}
|
||||
AssetsLoadInfo& getLoadInfo() const {
|
||||
return *assetsLoadInfo;
|
||||
}
|
||||
private:
|
||||
util::ObjectsKeeper* vault;
|
||||
std::vector<TextureAnimation> animations;
|
||||
|
||||
void addSetupFunc(assetload::setupfunc setupfunc) {
|
||||
setupFuncs.push_back(setupfunc);
|
||||
}
|
||||
std::unordered_map<std::type_index, assets_map> assets;
|
||||
std::unique_ptr<AssetsLoadInfo> assetsLoadInfo;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
void assetload::assets_setup(const Assets* assets) {
|
||||
if (auto mapPtr = assets->getMap<T>()) {
|
||||
void assetload::assets_setup(const Assets& assets) {
|
||||
if (auto mapPtr = assets.getMap<T>()) {
|
||||
for (const auto& entry : **mapPtr) {
|
||||
static_cast<T*>(entry.second.get())->setup();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "AssetsLoader.hpp"
|
||||
|
||||
#include "animation/rigging.hpp"
|
||||
#include "assetload_funcs.hpp"
|
||||
#include "Assets.hpp"
|
||||
#include "coders/commons.hpp"
|
||||
|
|
@ -15,7 +16,6 @@
|
|||
#include "items/ItemDef.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
#include "objects/EntityDef.hpp"
|
||||
#include "objects/rigging.hpp"
|
||||
#include "util/ThreadPool.hpp"
|
||||
#include "voxels/Block.hpp"
|
||||
|
||||
|
|
@ -26,17 +26,23 @@ namespace fs = std::filesystem;
|
|||
|
||||
static debug::Logger logger("assets-loader");
|
||||
|
||||
AssetsLoader::AssetsLoader(Engine& engine, Assets& assets, const ResPaths& paths)
|
||||
: engine(engine), assets(assets), paths(paths) {
|
||||
addLoader(AssetType::SHADER, assetload::shader);
|
||||
addLoader(AssetType::TEXTURE, assetload::texture);
|
||||
addLoader(AssetType::FONT, assetload::font);
|
||||
AssetsLoader::AssetsLoader(
|
||||
Engine& engine, Assets& assets, const ResPaths& paths
|
||||
)
|
||||
: engine(engine),
|
||||
assets(assets),
|
||||
paths(paths),
|
||||
assetsLoadInfo(assets.getLoadInfo()) {
|
||||
addLoader(AssetType::ANIMATION, assetload::animation);
|
||||
addLoader(AssetType::ATLAS, assetload::atlas);
|
||||
addLoader(AssetType::FONT, assetload::font);
|
||||
addLoader(AssetType::LAYOUT, assetload::layout);
|
||||
addLoader(AssetType::SOUND, assetload::sound);
|
||||
addLoader(AssetType::MODEL, assetload::model);
|
||||
addLoader(AssetType::POST_EFFECT, assetload::posteffect);
|
||||
addLoader(AssetType::SHADER, assetload::shader);
|
||||
addLoader(AssetType::SKELETON, assetload::skeleton);
|
||||
addLoader(AssetType::SOUND, assetload::sound);
|
||||
addLoader(AssetType::TEXTURE, assetload::texture);
|
||||
}
|
||||
|
||||
void AssetsLoader::addLoader(AssetType tag, aloader_func func) {
|
||||
|
|
@ -50,11 +56,11 @@ void AssetsLoader::add(
|
|||
std::shared_ptr<AssetCfg> settings,
|
||||
bool overwrite
|
||||
) {
|
||||
if (!overwrite && enqueued.find({tag, alias}) != enqueued.end()){
|
||||
if (!overwrite && enqueued.find({alias, tag}) != enqueued.end()){
|
||||
return;
|
||||
}
|
||||
entries.push(aloader_entry {tag, filename, alias, std::move(settings)});
|
||||
enqueued.insert({tag, alias});
|
||||
enqueued.insert({alias, tag});
|
||||
}
|
||||
|
||||
bool AssetsLoader::hasNext() const {
|
||||
|
|
@ -72,15 +78,15 @@ aloader_func AssetsLoader::getLoader(AssetType tag) {
|
|||
}
|
||||
|
||||
void AssetsLoader::loadNext() {
|
||||
const aloader_entry& entry = entries.front();
|
||||
aloader_entry& entry = entries.front();
|
||||
logger.info() << "loading " << entry.filename << " as " << entry.alias;
|
||||
|
||||
std::string error {};
|
||||
try {
|
||||
aloader_func loader = getLoader(entry.tag);
|
||||
auto postfunc =
|
||||
loader(this, paths, entry.filename, entry.alias, entry.config);
|
||||
postfunc(&assets);
|
||||
loader(*this, paths, entry.filename, entry.alias, entry.config);
|
||||
postfunc(assets);
|
||||
} catch (const parsing_error& err) {
|
||||
error = err.errorLog();
|
||||
} catch (const std::runtime_error& err) {
|
||||
|
|
@ -93,6 +99,8 @@ void AssetsLoader::loadNext() {
|
|||
entries.pop();
|
||||
throw assetload::error(tag, std::move(filename), std::move(error));
|
||||
}
|
||||
assetsLoadInfo.processedEntries[{entry.alias, entry.tag}] =
|
||||
std::move(entry);
|
||||
entries.pop();
|
||||
}
|
||||
|
||||
|
|
@ -117,34 +125,28 @@ static void add_layouts(
|
|||
}
|
||||
}
|
||||
|
||||
void AssetsLoader::tryAddSound(const std::string& name) {
|
||||
if (name.empty()) {
|
||||
return;
|
||||
}
|
||||
std::string file = SOUNDS_FOLDER + "/" + name;
|
||||
add(AssetType::SOUND, file, name);
|
||||
}
|
||||
|
||||
static std::string assets_def_folder(AssetType tag) {
|
||||
switch (tag) {
|
||||
case AssetType::FONT:
|
||||
return FONTS_FOLDER;
|
||||
case AssetType::SHADER:
|
||||
return SHADERS_FOLDER;
|
||||
case AssetType::TEXTURE:
|
||||
return TEXTURES_FOLDER;
|
||||
case AssetType::ANIMATION:
|
||||
return ANIMATION_FOLDER;
|
||||
case AssetType::ATLAS:
|
||||
return TEXTURES_FOLDER;
|
||||
case AssetType::FONT:
|
||||
return FONTS_FOLDER;
|
||||
case AssetType::LAYOUT:
|
||||
return LAYOUTS_FOLDER;
|
||||
case AssetType::SOUND:
|
||||
return SOUNDS_FOLDER;
|
||||
case AssetType::MODEL:
|
||||
return MODELS_FOLDER;
|
||||
case AssetType::POST_EFFECT:
|
||||
return POST_EFFECTS_FOLDER;
|
||||
case AssetType::SHADER:
|
||||
return SHADERS_FOLDER;
|
||||
case AssetType::SKELETON:
|
||||
return SKELETONS_FOLDER;
|
||||
case AssetType::SOUND:
|
||||
return SOUNDS_FOLDER;
|
||||
case AssetType::TEXTURE:
|
||||
return TEXTURES_FOLDER;
|
||||
}
|
||||
return "<error>";
|
||||
}
|
||||
|
|
@ -160,7 +162,8 @@ void AssetsLoader::processPreload(
|
|||
}
|
||||
std::shared_ptr<AssetCfg> config = nullptr;
|
||||
map.at("path").get(path);
|
||||
logger.debug() << "processing preload " << util::quote(name) << " path: " << util::quote(path);
|
||||
logger.debug() << "processing preload " << util::quote(name)
|
||||
<< " path: " << util::quote(path);
|
||||
switch (tag) {
|
||||
case AssetType::SOUND: {
|
||||
bool keepPCM = false;
|
||||
|
|
@ -221,14 +224,15 @@ void AssetsLoader::processPreloadList(AssetType tag, const dv::value& list) {
|
|||
|
||||
void AssetsLoader::processPreloadConfig(const io::path& file) {
|
||||
auto root = io::read_json(file);
|
||||
processPreloadList(AssetType::ANIMATION, root["animation"]);
|
||||
processPreloadList(AssetType::ATLAS, root["atlases"]);
|
||||
processPreloadList(AssetType::FONT, root["fonts"]);
|
||||
processPreloadList(AssetType::SHADER, root["shaders"]);
|
||||
processPreloadList(AssetType::TEXTURE, root["textures"]);
|
||||
processPreloadList(AssetType::SOUND, root["sounds"]);
|
||||
processPreloadList(AssetType::MODEL, root["models"]);
|
||||
processPreloadList(AssetType::POST_EFFECT, root["post-effects"]);
|
||||
processPreloadList(AssetType::SHADER, root["shaders"]);
|
||||
processPreloadList(AssetType::SKELETON, root["skeletons"]);
|
||||
processPreloadList(AssetType::SOUND, root["sounds"]);
|
||||
processPreloadList(AssetType::TEXTURE, root["textures"]);
|
||||
// layouts are loaded automatically
|
||||
}
|
||||
|
||||
|
|
@ -263,65 +267,73 @@ static void add_variant(AssetsLoader& loader, const Variant& variant) {
|
|||
}
|
||||
}
|
||||
|
||||
void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
|
||||
loader.processPreloadConfigs(content);
|
||||
if (content) {
|
||||
for (auto& entry : content->getBlockMaterials()) {
|
||||
auto& material = *entry.second;
|
||||
loader.tryAddSound(material.stepsSound);
|
||||
loader.tryAddSound(material.placeSound);
|
||||
loader.tryAddSound(material.breakSound);
|
||||
loader.tryAddSound(material.hitSound);
|
||||
void AssetsLoader::addDefaults(const Content* content) {
|
||||
processPreloadConfigs(content);
|
||||
if (content == nullptr) {
|
||||
return;
|
||||
}
|
||||
auto tryAddSound = [this](const std::string& name){
|
||||
if (name.empty()) {
|
||||
return;
|
||||
}
|
||||
std::string file = SOUNDS_FOLDER + "/" + name;
|
||||
add(AssetType::SOUND, file, name);
|
||||
};
|
||||
for (auto& entry : content->getBlockMaterials()) {
|
||||
auto& material = *entry.second;
|
||||
tryAddSound(material.stepsSound);
|
||||
tryAddSound(material.placeSound);
|
||||
tryAddSound(material.breakSound);
|
||||
tryAddSound(material.hitSound);
|
||||
}
|
||||
|
||||
for (auto& entry : content->getPacks()) {
|
||||
auto pack = entry.second.get();
|
||||
auto& info = pack->getInfo();
|
||||
io::path folder = info.folder / "layouts";
|
||||
add_layouts(pack->getEnvironment(), info.id, folder, loader);
|
||||
}
|
||||
for (auto& entry : content->getPacks()) {
|
||||
auto pack = entry.second.get();
|
||||
auto& info = pack->getInfo();
|
||||
io::path folder = info.folder / "layouts";
|
||||
add_layouts(pack->getEnvironment(), info.id, folder, *this);
|
||||
}
|
||||
|
||||
for (const auto& entry : content->getPacks()) {
|
||||
io::path skeletonsDir = entry.first + ":skeletons";
|
||||
if (!io::is_directory(skeletonsDir)) {
|
||||
continue;
|
||||
}
|
||||
for (const auto& file : io::directory_iterator(skeletonsDir)) {
|
||||
loader.add(
|
||||
AssetType::SKELETON,
|
||||
(file.parent() / file.stem()).string(),
|
||||
entry.first + ":" + file.stem()
|
||||
);
|
||||
}
|
||||
for (const auto& entry : content->getPacks()) {
|
||||
io::path skeletonsDir = entry.first + ":skeletons";
|
||||
if (!io::is_directory(skeletonsDir)) {
|
||||
continue;
|
||||
}
|
||||
for (const auto& file : io::directory_iterator(skeletonsDir)) {
|
||||
add(
|
||||
AssetType::SKELETON,
|
||||
(file.parent() / file.stem()).string(),
|
||||
entry.first + ":" + file.stem()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [_, def] : content->blocks.getDefs()) {
|
||||
if (def->variants) {
|
||||
for (const auto& variant : def->variants->variants) {
|
||||
add_variant(loader, variant);
|
||||
}
|
||||
} else {
|
||||
add_variant(loader, def->defaults);
|
||||
for (const auto& [_, def] : content->blocks.getDefs()) {
|
||||
if (def->variants) {
|
||||
for (const auto& variant : def->variants->variants) {
|
||||
add_variant(*this, variant);
|
||||
}
|
||||
} else {
|
||||
add_variant(*this, def->defaults);
|
||||
}
|
||||
for (const auto& [_, def] : content->items.getDefs()) {
|
||||
if (def->modelName.find(':') == std::string::npos) {
|
||||
loader.add(
|
||||
AssetType::MODEL,
|
||||
MODELS_FOLDER + "/" + def->modelName,
|
||||
def->modelName
|
||||
);
|
||||
}
|
||||
}
|
||||
for (const auto& [_, def] : content->items.getDefs()) {
|
||||
if (def->modelName.find(':') == std::string::npos) {
|
||||
add(
|
||||
AssetType::MODEL,
|
||||
MODELS_FOLDER + "/" + def->modelName,
|
||||
def->modelName
|
||||
);
|
||||
}
|
||||
for (const auto& [_, def] : content->entities.getDefs()) {
|
||||
if (def->skeletonName.find(':') == std::string::npos) {
|
||||
// expecting a VCM with skeleton
|
||||
loader.add(
|
||||
AssetType::MODEL,
|
||||
MODELS_FOLDER + "/" + def->skeletonName,
|
||||
def->skeletonName
|
||||
);
|
||||
}
|
||||
}
|
||||
for (const auto& [_, def] : content->entities.getDefs()) {
|
||||
if (def->skeletonName.find(':') == std::string::npos) {
|
||||
// expecting a VCM with skeleton
|
||||
add(
|
||||
AssetType::MODEL,
|
||||
MODELS_FOLDER + "/" + def->skeletonName,
|
||||
def->skeletonName
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -361,17 +373,17 @@ const ResPaths& AssetsLoader::getPaths() const {
|
|||
}
|
||||
|
||||
class LoaderWorker : public util::Worker<aloader_entry, assetload::postfunc> {
|
||||
AssetsLoader* loader;
|
||||
AssetsLoader& loader;
|
||||
public:
|
||||
LoaderWorker(AssetsLoader* loader) : loader(loader) {
|
||||
LoaderWorker(AssetsLoader& loader) : loader(loader) {
|
||||
}
|
||||
|
||||
assetload::postfunc operator()(const aloader_entry& entry
|
||||
) override {
|
||||
aloader_func loadfunc = loader->getLoader(entry.tag);
|
||||
aloader_func loadfunc = loader.getLoader(entry.tag);
|
||||
return loadfunc(
|
||||
loader,
|
||||
loader->getPaths(),
|
||||
loader.getPaths(),
|
||||
entry.filename,
|
||||
entry.alias,
|
||||
entry.config
|
||||
|
|
@ -383,8 +395,8 @@ std::shared_ptr<Task> AssetsLoader::startTask(runnable onDone, int maxWorkers) {
|
|||
auto pool =
|
||||
std::make_shared<util::ThreadPool<aloader_entry, assetload::postfunc>>(
|
||||
"assets-loader-pool",
|
||||
[=]() { return std::make_unique<LoaderWorker>(this); },
|
||||
[this](const assetload::postfunc& func) { func(&assets); },
|
||||
[=]() { return std::make_unique<LoaderWorker>(*this); },
|
||||
[this](const assetload::postfunc& func) { func(assets); },
|
||||
maxWorkers
|
||||
);
|
||||
pool->setOnComplete(std::move(onDone));
|
||||
|
|
@ -404,3 +416,22 @@ std::shared_ptr<Task> AssetsLoader::startTask(runnable onDone, int maxWorkers) {
|
|||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
void AssetsLoader::attachToFile(const io::path& file, AssetFullId assetId) {
|
||||
assetsLoadInfo.referencedAssets.insert({file, std::move(assetId)});
|
||||
}
|
||||
|
||||
int AssetsLoader::addReload(const io::path& path) {
|
||||
int added = 0;
|
||||
|
||||
auto range = assetsLoadInfo.referencedAssets.equal_range(path);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
const auto& recipe = assetsLoadInfo.processedEntries.find(it->second);
|
||||
if (recipe != assetsLoadInfo.processedEntries.end()) {
|
||||
entries.push(recipe->second);
|
||||
added++;
|
||||
}
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "delegates.hpp"
|
||||
#include "interfaces/Task.hpp"
|
||||
#include "typedefs.hpp"
|
||||
#include "Assets.hpp"
|
||||
#include "data/dv.hpp"
|
||||
#include "io/fwd.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
|
@ -8,13 +15,6 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "delegates.hpp"
|
||||
#include "interfaces/Task.hpp"
|
||||
#include "typedefs.hpp"
|
||||
#include "Assets.hpp"
|
||||
#include "data/dv.hpp"
|
||||
#include "io/fwd.hpp"
|
||||
|
||||
class ResPaths;
|
||||
class AssetsLoader;
|
||||
class Content;
|
||||
|
|
@ -72,7 +72,12 @@ struct ModelCfg : AssetCfg {
|
|||
|
||||
using aloader_func = std::function<
|
||||
assetload::
|
||||
postfunc(AssetsLoader*, const ResPaths&, const std::string&, const std::string&, std::shared_ptr<AssetCfg>)>;
|
||||
postfunc(
|
||||
AssetsLoader&,
|
||||
const ResPaths&,
|
||||
const std::string&,
|
||||
const std::string&,
|
||||
std::shared_ptr<AssetCfg>)>;
|
||||
|
||||
struct aloader_entry {
|
||||
AssetType tag;
|
||||
|
|
@ -81,24 +86,18 @@ struct aloader_entry {
|
|||
std::shared_ptr<AssetCfg> config;
|
||||
};
|
||||
|
||||
using AssetFullId = std::pair<std::string, AssetType>;
|
||||
|
||||
struct AssetsLoadInfo {
|
||||
std::map<AssetFullId, aloader_entry> processedEntries;
|
||||
std::multimap<io::path, AssetFullId> referencedAssets;
|
||||
};
|
||||
|
||||
class AssetsLoader {
|
||||
Engine& engine;
|
||||
Assets& assets;
|
||||
std::map<AssetType, aloader_func> loaders;
|
||||
std::queue<aloader_entry> entries;
|
||||
std::set<std::pair<AssetType, std::string>> enqueued;
|
||||
const ResPaths& paths;
|
||||
|
||||
void tryAddSound(const std::string& name);
|
||||
|
||||
void processPreload(
|
||||
AssetType tag, const std::string& name, const dv::value& map
|
||||
);
|
||||
void processPreloadList(AssetType tag, const dv::value& list);
|
||||
void processPreloadConfig(const io::path& file);
|
||||
void processPreloadConfigs(const Content* content);
|
||||
public:
|
||||
AssetsLoader(Engine& engine, Assets& assets, const ResPaths& paths);
|
||||
AssetsLoader(const AssetsLoader&) = delete;
|
||||
|
||||
void addLoader(AssetType tag, aloader_func func);
|
||||
|
||||
/// @brief Enqueue asset load
|
||||
|
|
@ -125,9 +124,8 @@ public:
|
|||
aloader_func getLoader(AssetType tag);
|
||||
|
||||
/// @brief Enqueue core and content assets
|
||||
/// @param loader target loader
|
||||
/// @param content engine content
|
||||
static void addDefaults(AssetsLoader& loader, const Content* content);
|
||||
void addDefaults(const Content* content);
|
||||
|
||||
static bool loadExternalTexture(
|
||||
AssetsLoader& loader,
|
||||
|
|
@ -135,6 +133,25 @@ public:
|
|||
const std::vector<io::path>& alternatives
|
||||
);
|
||||
|
||||
int addReload(const io::path& path);
|
||||
void attachToFile(const io::path& file, AssetFullId assetId);
|
||||
|
||||
Assets& getAssets();
|
||||
Engine& getEngine();
|
||||
private:
|
||||
Engine& engine;
|
||||
Assets& assets;
|
||||
std::map<AssetType, aloader_func> loaders;
|
||||
std::queue<aloader_entry> entries;
|
||||
std::set<AssetFullId> enqueued;
|
||||
|
||||
const ResPaths& paths;
|
||||
AssetsLoadInfo& assetsLoadInfo;
|
||||
|
||||
void processPreload(
|
||||
AssetType tag, const std::string& name, const dv::value& map
|
||||
);
|
||||
void processPreloadList(AssetType tag, const dv::value& list);
|
||||
void processPreloadConfig(const io::path& file);
|
||||
void processPreloadConfigs(const Content* content);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
#include "assetload_funcs.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "animation/rigging.hpp"
|
||||
#include "Assets.hpp"
|
||||
#include "AssetsLoader.hpp"
|
||||
#include "audio/audio.hpp"
|
||||
#include "coders/GLSLExtension.hpp"
|
||||
#include "coders/commons.hpp"
|
||||
#include "coders/GLSLExtension.hpp"
|
||||
#include "coders/imageio.hpp"
|
||||
#include "coders/json.hpp"
|
||||
#include "coders/obj.hpp"
|
||||
|
|
@ -17,26 +15,28 @@
|
|||
#include "constants.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "engine/EnginePaths.hpp"
|
||||
#include "io/io.hpp"
|
||||
#include "frontend/UiDocument.hpp"
|
||||
#include "graphics/commons/Model.hpp"
|
||||
#include "graphics/core/Atlas.hpp"
|
||||
#include "graphics/core/Font.hpp"
|
||||
#include "graphics/core/ImageData.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "graphics/core/TextureAnimation.hpp"
|
||||
#include "graphics/commons/Model.hpp"
|
||||
#include "objects/rigging.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
#include "io/io.hpp"
|
||||
#include "util/stringutil.hpp"
|
||||
#include "Assets.hpp"
|
||||
#include "AssetsLoader.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
|
||||
static debug::Logger logger("assetload-funcs");
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
static bool load_animation(
|
||||
Assets* assets,
|
||||
static bool load_animated_texture(
|
||||
Assets& assets,
|
||||
const ResPaths& paths,
|
||||
const std::string& atlasName,
|
||||
const std::string& directory,
|
||||
|
|
@ -44,22 +44,39 @@ static bool load_animation(
|
|||
Atlas* dstAtlas
|
||||
);
|
||||
|
||||
assetload::postfunc assetload::texture(
|
||||
AssetsLoader*,
|
||||
assetload::postfunc assetload::animation(
|
||||
AssetsLoader& loader,
|
||||
const ResPaths& paths,
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>&
|
||||
) {
|
||||
auto actualFile = paths.find(filename + ".png");
|
||||
auto path = paths.find(filename + ".vca");
|
||||
loader.attachToFile(path, {name, AssetType::ANIMATION});
|
||||
|
||||
if (io::is_regular_file(path)) {
|
||||
scripting::load_vca_animation(path, nullptr, name);
|
||||
}
|
||||
return [](auto&) {};
|
||||
}
|
||||
|
||||
assetload::postfunc assetload::texture(
|
||||
AssetsLoader& loader,
|
||||
const ResPaths& paths,
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>&
|
||||
) {
|
||||
auto file = paths.find(filename + ".png");
|
||||
loader.attachToFile(file, {name, AssetType::TEXTURE});
|
||||
try {
|
||||
std::shared_ptr<ImageData> image(imageio::read(actualFile));
|
||||
return [name, image, actualFile](auto assets) {
|
||||
assets->store(Texture::from(image.get()), name);
|
||||
std::shared_ptr<ImageData> image(imageio::read(file));
|
||||
return [name, image, file](auto& assets) {
|
||||
assets.store(Texture::from(image.get()), name);
|
||||
};
|
||||
} catch (const std::runtime_error& err) {
|
||||
logger.error() << actualFile.string() << ": " << err.what();
|
||||
return [](auto) {};
|
||||
logger.error() << file.string() << ": " << err.what();
|
||||
return [](auto&) {};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +103,7 @@ static auto read_program(const ResPaths& paths, const std::string& filename) {
|
|||
}
|
||||
|
||||
assetload::postfunc assetload::shader(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
|
|
@ -99,8 +116,8 @@ assetload::postfunc assetload::shader(
|
|||
io::path vertexFile = paths.find(filename + ".glslv");
|
||||
io::path fragmentFile = paths.find(filename + ".glslf");
|
||||
|
||||
return [=](auto assets) {
|
||||
assets->store(
|
||||
return [=](auto& assets) {
|
||||
assets.store(
|
||||
Shader::create(
|
||||
{vertexFile.string(), vertex},
|
||||
{fragmentFile.string(), fragment}
|
||||
|
|
@ -111,13 +128,15 @@ assetload::postfunc assetload::shader(
|
|||
}
|
||||
|
||||
assetload::postfunc assetload::posteffect(
|
||||
AssetsLoader*,
|
||||
AssetsLoader& loader,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
) {
|
||||
io::path effectFile = paths.find(file + ".glsl");
|
||||
loader.attachToFile(effectFile, {name, AssetType::POST_EFFECT});
|
||||
|
||||
std::string effectSource = io::read_string(effectFile);
|
||||
|
||||
auto& preprocessor = *Shader::preprocessor;
|
||||
|
|
@ -131,7 +150,7 @@ assetload::postfunc assetload::posteffect(
|
|||
std::string vertexSource = std::move(vertex.code);
|
||||
std::string fragmentSource = std::move(fragment.code);
|
||||
|
||||
return [=](auto assets) {
|
||||
return [=](auto& assets) {
|
||||
auto program = Shader::create(
|
||||
{effectFile.string(), vertexSource},
|
||||
{effectFile.string(), fragmentSource}
|
||||
|
|
@ -140,7 +159,7 @@ assetload::postfunc assetload::posteffect(
|
|||
if (settings) {
|
||||
advanced = dynamic_cast<const PostEffectCfg*>(settings.get())->advanced;
|
||||
}
|
||||
assets->store(
|
||||
assets.store(
|
||||
std::make_shared<PostEffect>(advanced, std::move(program), params),
|
||||
name
|
||||
);
|
||||
|
|
@ -160,7 +179,7 @@ static bool append_atlas(AtlasBuilder& atlas, const io::path& file) {
|
|||
}
|
||||
|
||||
assetload::postfunc assetload::atlas(
|
||||
AssetsLoader* loader,
|
||||
AssetsLoader& loader,
|
||||
const ResPaths& paths,
|
||||
const std::string& directory,
|
||||
const std::string& name,
|
||||
|
|
@ -171,13 +190,13 @@ assetload::postfunc assetload::atlas(
|
|||
for (const auto& file : paths.listdir(directory)) {
|
||||
if (!imageio::is_read_supported(file.extension()))
|
||||
continue;
|
||||
loader->add(
|
||||
loader.add(
|
||||
AssetType::TEXTURE,
|
||||
directory + "/" + file.stem(),
|
||||
name + "/" + file.stem()
|
||||
);
|
||||
}
|
||||
return [](auto){};
|
||||
return [](auto&){};
|
||||
}
|
||||
AtlasBuilder builder;
|
||||
for (const auto& file : paths.listdir(directory)) {
|
||||
|
|
@ -186,17 +205,17 @@ assetload::postfunc assetload::atlas(
|
|||
}
|
||||
std::set<std::string> names = builder.getNames();
|
||||
Atlas* atlas = builder.build(ATLAS_EXTRUSION, false).release();
|
||||
return [=](auto assets) {
|
||||
return [=](Assets& assets) {
|
||||
atlas->prepare();
|
||||
assets->store(std::unique_ptr<Atlas>(atlas), name);
|
||||
assets.store(std::unique_ptr<Atlas>(atlas), name);
|
||||
for (const auto& file : names) {
|
||||
load_animation(assets, paths, name, directory, file, atlas);
|
||||
load_animated_texture(assets, paths, name, directory, file, atlas);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
assetload::postfunc assetload::font(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
|
|
@ -204,17 +223,17 @@ assetload::postfunc assetload::font(
|
|||
) {
|
||||
auto cfg = std::dynamic_pointer_cast<FontCfg>(config);
|
||||
auto ext = fs::path(filename).extension().string();
|
||||
if (ext == ".ttf" || ext == ".otf") {
|
||||
if (!ext.empty()) {
|
||||
logger.info() << "loading vector font " << util::quote(filename);
|
||||
return [=](Assets* assets) {
|
||||
return [=](Assets& assets) {
|
||||
using FontFile = vector_fonts::FontFile;
|
||||
FontFile* fontFile;
|
||||
if ((fontFile = assets->get<FontFile>(filename)) == nullptr) {
|
||||
if ((fontFile = assets.get<FontFile>(filename)) == nullptr) {
|
||||
auto fontFilePtr = vector_fonts::load_font(paths.find(filename).string());
|
||||
fontFile = fontFilePtr.get();
|
||||
assets->store(fontFilePtr, filename);
|
||||
assets.store(fontFilePtr, filename);
|
||||
}
|
||||
assets->store(fontFile->createInstance(cfg ? cfg->size : 16), name);
|
||||
assets.store(fontFile->createInstance(cfg ? cfg->size : 16), name);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -230,24 +249,24 @@ assetload::postfunc assetload::font(
|
|||
pages->push_back(nullptr);
|
||||
}
|
||||
}
|
||||
return [=](auto assets) {
|
||||
assets->store(Font::createBitmapFont(std::move(*pages)), name);
|
||||
return [=](auto& assets) {
|
||||
assets.store(Font::createBitmapFont(std::move(*pages)), name);
|
||||
};
|
||||
}
|
||||
|
||||
assetload::postfunc assetload::layout(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths&,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& config
|
||||
) {
|
||||
return [=](auto assets) {
|
||||
return [=](auto& assets) {
|
||||
try {
|
||||
auto cfg = std::dynamic_pointer_cast<LayoutCfg>(config);
|
||||
size_t pos = name.find(':');
|
||||
auto prefix = name.substr(0, pos);
|
||||
assets->store(
|
||||
assets.store(
|
||||
UiDocument::read(
|
||||
*cfg->gui,
|
||||
cfg->env,
|
||||
|
|
@ -265,7 +284,7 @@ assetload::postfunc assetload::layout(
|
|||
};
|
||||
}
|
||||
assetload::postfunc assetload::sound(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
|
|
@ -307,81 +326,162 @@ assetload::postfunc assetload::sound(
|
|||
}
|
||||
|
||||
auto sound = baseSound.release();
|
||||
return [=](auto assets) {
|
||||
assets->store(std::unique_ptr<audio::Sound>(sound), name);
|
||||
return [=](auto& assets) {
|
||||
assets.store(std::unique_ptr<audio::Sound>(sound), name);
|
||||
};
|
||||
}
|
||||
|
||||
static void request_textures(AssetsLoader* loader, const model::Model& model) {
|
||||
static void request_textures(AssetsLoader& loader, const model::Model& model) {
|
||||
for (auto& mesh : model.meshes) {
|
||||
if (mesh.texture.find('$') == std::string::npos &&
|
||||
mesh.texture.find(':') == std::string::npos) {
|
||||
auto filename = TEXTURES_FOLDER + "/" + mesh.texture;
|
||||
loader->add(
|
||||
loader.add(
|
||||
AssetType::TEXTURE, filename, mesh.texture, nullptr
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static assetload::postfunc load_vec3_model(
|
||||
const io::path& file,
|
||||
AssetsLoader& loader,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<ModelCfg>& config
|
||||
) {
|
||||
loader.attachToFile(file, {name, AssetType::MODEL});
|
||||
auto bytes = io::read_bytes_buffer(file);
|
||||
auto modelVEC3 = std::make_shared<vec3::File>(vec3::load(file.string(), bytes));
|
||||
return [&loader, name, file, config, modelVEC3 = std::move(modelVEC3)](
|
||||
Assets& assets
|
||||
) {
|
||||
loader.attachToFile(file, {name, AssetType::MODEL});
|
||||
if (config && config->squashed) {
|
||||
model::Model fullModel;
|
||||
for (auto& entry : modelVEC3->models) {
|
||||
auto& vec3model = entry.second;
|
||||
auto& model = vec3model.model;
|
||||
model.translate(vec3model.origin);
|
||||
fullModel.merge(std::move(model));
|
||||
}
|
||||
request_textures(loader, fullModel);
|
||||
assets.store(
|
||||
std::make_unique<model::Model>(fullModel),
|
||||
name
|
||||
);
|
||||
logger.info() << "store model " << util::quote(name);
|
||||
return;
|
||||
}
|
||||
for (auto& [modelName, model] : modelVEC3->models) {
|
||||
request_textures(loader, model.model);
|
||||
std::string fullName = name;
|
||||
if (name != modelName) {
|
||||
fullName += "." + modelName;
|
||||
}
|
||||
assets.store(
|
||||
std::make_unique<model::Model>(model.model),
|
||||
fullName
|
||||
);
|
||||
logger.info() << "store model " << util::quote(modelName)
|
||||
<< " as " << util::quote(fullName);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static assetload::postfunc load_obj_model(
|
||||
const io::path& file,
|
||||
AssetsLoader& loader,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<ModelCfg>&
|
||||
) {
|
||||
loader.attachToFile(file, {name, AssetType::MODEL});
|
||||
auto text = io::read_string(file);
|
||||
try {
|
||||
auto model = obj::parse(file.string(), text).release();
|
||||
return [=, &loader](Assets& assets) {
|
||||
request_textures(loader, *model);
|
||||
assets.store(std::unique_ptr<model::Model>(model), name);
|
||||
};
|
||||
} catch (const parsing_error& err) {
|
||||
logger.error() << "error on loading model " << name << ": "
|
||||
<< err.errorLog();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
static assetload::postfunc load_vcm_model(
|
||||
const io::path& file,
|
||||
AssetsLoader& loader,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<ModelCfg>& cfg
|
||||
) {
|
||||
loader.attachToFile(file, {name, AssetType::MODEL});
|
||||
auto text = io::read_string(file);
|
||||
try {
|
||||
auto vcmModel = vcm::parse(file.string(), text, file.extension() == ".xml");
|
||||
|
||||
assert(vcmModel.parts.size() > 0);
|
||||
|
||||
if (vcmModel.parts.size() == 1 || (cfg && cfg->squashed)) {
|
||||
auto modelPtr =
|
||||
std::make_unique<model::Model>(std::move(vcmModel.squash()))
|
||||
.release();
|
||||
return [=, &loader](Assets& assets) {
|
||||
auto model = std::unique_ptr<model::Model>(modelPtr);
|
||||
request_textures(loader, *model);
|
||||
assets.store(std::move(model), name);
|
||||
logger.info() << "store model " << util::quote(name);
|
||||
};
|
||||
}
|
||||
auto vcmModelPtr =
|
||||
std::make_unique<vcm::VcmModel>(std::move(vcmModel)).release();
|
||||
return [=, &loader](Assets& assets) {
|
||||
auto vcmModel = std::unique_ptr<vcm::VcmModel>(vcmModelPtr);
|
||||
for (auto& [partName, model] : vcmModel->parts) {
|
||||
auto fullName = name + "." + partName;
|
||||
logger.info()
|
||||
<< "store model part " << util::quote(partName)
|
||||
<< " as " << util::quote(fullName);
|
||||
request_textures(loader, model);
|
||||
assets.store(
|
||||
std::make_unique<model::Model>(std::move(model)),
|
||||
fullName
|
||||
);
|
||||
}
|
||||
for (auto& bone : vcmModel->skeleton->getBones()) {
|
||||
bone->setModel(name + "." + bone->model.name);
|
||||
}
|
||||
logger.info() << "store skeleton " << util::quote(name);
|
||||
assets.store<rigging::SkeletonConfig>(
|
||||
std::make_unique<rigging::SkeletonConfig>(
|
||||
std::move(*vcmModel->skeleton)
|
||||
),
|
||||
name
|
||||
);
|
||||
};
|
||||
} catch (const parsing_error& err) {
|
||||
logger.error() << "error on loading model " << name << ": "
|
||||
<< err.errorLog();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
assetload::postfunc assetload::model(
|
||||
AssetsLoader* loader,
|
||||
AssetsLoader& loader,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& config
|
||||
) {
|
||||
auto cfg = std::dynamic_pointer_cast<ModelCfg>(config);
|
||||
auto modelConfig = std::dynamic_pointer_cast<ModelCfg>(config);
|
||||
|
||||
auto path = paths.find(file + ".vec3");
|
||||
if (io::exists(path)) {
|
||||
auto bytes = io::read_bytes_buffer(path);
|
||||
auto modelVEC3 = std::make_shared<vec3::File>(vec3::load(path.string(), bytes));
|
||||
return [loader, name, cfg, modelVEC3=std::move(modelVEC3)](Assets* assets) {
|
||||
if (cfg && cfg->squashed) {
|
||||
model::Model fullModel;
|
||||
for (auto& entry : modelVEC3->models) {
|
||||
auto& vec3model = entry.second;
|
||||
auto& model = vec3model.model;
|
||||
model.translate(vec3model.origin);
|
||||
fullModel.merge(std::move(model));
|
||||
}
|
||||
request_textures(loader, fullModel);
|
||||
assets->store(
|
||||
std::make_unique<model::Model>(fullModel),
|
||||
name
|
||||
);
|
||||
logger.info() << "store model " << util::quote(name);
|
||||
return;
|
||||
}
|
||||
for (auto& [modelName, model] : modelVEC3->models) {
|
||||
request_textures(loader, model.model);
|
||||
std::string fullName = name;
|
||||
if (name != modelName) {
|
||||
fullName += "." + modelName;
|
||||
}
|
||||
assets->store(
|
||||
std::make_unique<model::Model>(model.model),
|
||||
fullName
|
||||
);
|
||||
logger.info() << "store model " << util::quote(modelName)
|
||||
<< " as " << util::quote(fullName);
|
||||
}
|
||||
};
|
||||
return load_vec3_model(path, loader, name, modelConfig);
|
||||
}
|
||||
path = paths.find(file + ".obj");
|
||||
if (io::exists(path)) {
|
||||
auto text = io::read_string(path);
|
||||
try {
|
||||
auto model = obj::parse(path.string(), text).release();
|
||||
return [=](Assets* assets) {
|
||||
request_textures(loader, *model);
|
||||
assets->store(std::unique_ptr<model::Model>(model), name);
|
||||
};
|
||||
} catch (const parsing_error& err) {
|
||||
std::cerr << err.errorLog() << std::endl;
|
||||
throw;
|
||||
}
|
||||
return load_obj_model(path, loader, name, modelConfig);
|
||||
}
|
||||
|
||||
std::array<std::string, 2> extensions {
|
||||
|
|
@ -401,60 +501,17 @@ assetload::postfunc assetload::model(
|
|||
throw std::runtime_error("could not to find model " + util::quote(file));
|
||||
}
|
||||
|
||||
auto text = io::read_string(path);
|
||||
try {
|
||||
auto vcmModel = vcm::parse(path.string(), text, path.extension() == ".xml");
|
||||
|
||||
assert(vcmModel.parts.size() > 0);
|
||||
|
||||
if (vcmModel.parts.size() == 1 || (cfg && cfg->squashed)) {
|
||||
auto modelPtr = std::make_unique<model::Model>(std::move(vcmModel.squash())).release();
|
||||
return [=](Assets* assets) {
|
||||
auto model = std::unique_ptr<model::Model>(modelPtr);
|
||||
request_textures(loader, *model);
|
||||
assets->store(std::move(model), name);
|
||||
logger.info() << "store model " << util::quote(name);
|
||||
};
|
||||
} else {
|
||||
auto vcmModelPtr = std::make_unique<vcm::VcmModel>(std::move(vcmModel)).release();
|
||||
return [=](Assets* assets) {
|
||||
auto vcmModel = std::unique_ptr<vcm::VcmModel>(vcmModelPtr);
|
||||
for (auto& [partName, model] : vcmModel->parts) {
|
||||
auto fullName = name + "." + partName;
|
||||
logger.info()
|
||||
<< "store model part " << util::quote(partName)
|
||||
<< " as " << util::quote(fullName);
|
||||
assets->store(
|
||||
std::make_unique<model::Model>(std::move(model)),
|
||||
fullName
|
||||
);
|
||||
}
|
||||
for (auto& bone : vcmModel->skeleton->getBones()) {
|
||||
bone->setModel(name + "." + bone->model.name);
|
||||
}
|
||||
logger.info() << "store skeleton " << util::quote(name);
|
||||
assets->store<rigging::SkeletonConfig>(
|
||||
std::make_unique<rigging::SkeletonConfig>(
|
||||
std::move(*vcmModel->skeleton)
|
||||
),
|
||||
name
|
||||
);
|
||||
};
|
||||
}
|
||||
} catch (const parsing_error& err) {
|
||||
std::cerr << err.errorLog() << std::endl;
|
||||
throw;
|
||||
}
|
||||
return load_vcm_model(path, loader, name, modelConfig);
|
||||
}
|
||||
|
||||
assetload::postfunc assetload::skeleton(
|
||||
AssetsLoader* loader,
|
||||
AssetsLoader& loader,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
) {
|
||||
return [=](auto assets) {
|
||||
return [&loader, &paths, file, name](auto& assets) {
|
||||
auto path = paths.find(file + ".json");
|
||||
std::string text = io::read_string(path);
|
||||
auto skeleton = rigging::SkeletonConfig::parse(text, file, name);
|
||||
|
|
@ -465,12 +522,12 @@ assetload::postfunc assetload::skeleton(
|
|||
model = model.substr(0, pos);
|
||||
}
|
||||
if (!model.empty()) {
|
||||
loader->add(
|
||||
loader.add(
|
||||
AssetType::MODEL, MODELS_FOLDER + "/" + model, model
|
||||
);
|
||||
}
|
||||
}
|
||||
assets->store(std::move(skeleton), name);
|
||||
assets.store(std::move(skeleton), name);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -552,8 +609,8 @@ inline bool contains(
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool load_animation(
|
||||
Assets* assets,
|
||||
static bool load_animated_texture(
|
||||
Assets& assets,
|
||||
const ResPaths& paths,
|
||||
const std::string& atlasName,
|
||||
const std::string& directory,
|
||||
|
|
@ -576,8 +633,7 @@ static bool load_animation(
|
|||
read_anim_file(animFile, frameList);
|
||||
}
|
||||
for (const auto& file : paths.listdir(animsDir + "/" + name)) {
|
||||
if (!frameList.empty() &&
|
||||
!contains(frameList, file.stem())) {
|
||||
if (!frameList.empty() && !contains(frameList, file.stem())) {
|
||||
continue;
|
||||
}
|
||||
if (!append_atlas(builder, file)) continue;
|
||||
|
|
@ -591,10 +647,10 @@ static bool load_animation(
|
|||
auto animation = create_animation(
|
||||
srcAtlas.get(), dstAtlas, name, builder.getNames(), frameList
|
||||
);
|
||||
assets->store(
|
||||
assets.store(
|
||||
std::move(srcAtlas), atlasName + "/" + name + "_animation"
|
||||
);
|
||||
assets->store(animation);
|
||||
assets.store(animation);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "Assets.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "Assets.hpp"
|
||||
|
||||
class ResPaths;
|
||||
class Assets;
|
||||
class AssetsLoader;
|
||||
|
|
@ -13,57 +13,64 @@ struct AssetCfg;
|
|||
|
||||
/// @brief see AssetsLoader.h: aloader_func
|
||||
namespace assetload {
|
||||
postfunc animation(
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc texture(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc shader(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc atlas(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& directory,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc font(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc layout(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc sound(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc model(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc posteffect(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
|
|
@ -71,7 +78,7 @@ namespace assetload {
|
|||
);
|
||||
|
||||
postfunc skeleton(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
|
|
|
|||
|
|
@ -37,16 +37,22 @@ void Syntax::deserialize(const dv::value& src) {
|
|||
std::string multilineCommentEnd;
|
||||
std::string multilineStringStart;
|
||||
std::string multilineStringEnd;
|
||||
std::string identifierStartChars;
|
||||
std::string identifierPartChars;
|
||||
src.at("line-comment").get(lineComment);
|
||||
src.at("multiline-comment-start").get(multilineCommentStart);
|
||||
src.at("multiline-comment-end").get(multilineCommentEnd);
|
||||
src.at("multiline-string-start").get(multilineStringStart);
|
||||
src.at("multiline-string-end").get(multilineStringEnd);
|
||||
src.at("identifier-start-chars").get(identifierStartChars);
|
||||
src.at("identifier-part-chars").get(identifierPartChars);
|
||||
this->lineComment = util::str2wstr_utf8(lineComment);
|
||||
this->multilineCommentStart = util::str2wstr_utf8(multilineCommentStart);
|
||||
this->multilineCommentEnd = util::str2wstr_utf8(multilineCommentEnd);
|
||||
this->multilineStringStart = util::str2wstr_utf8(multilineStringStart);
|
||||
this->multilineStringEnd = util::str2wstr_utf8(multilineStringEnd);
|
||||
this->identifierStarts = util::str2wstr_utf8(identifierStartChars);
|
||||
this->identifierParts = util::str2wstr_utf8(identifierPartChars);
|
||||
|
||||
if (src.has("extensions")) {
|
||||
const auto& extsList = src["extensions"];
|
||||
|
|
@ -77,6 +83,8 @@ inline bool is_lua_operator_start(int c) {
|
|||
|| c == '.';
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
class Tokenizer : BasicParser<wchar_t> {
|
||||
const Syntax& syntax;
|
||||
std::vector<Token> tokens;
|
||||
|
|
@ -87,13 +95,28 @@ public:
|
|||
: BasicParser(file, source), syntax(syntax) {
|
||||
}
|
||||
|
||||
std::wstring parseLuaName() {
|
||||
char c = peek();
|
||||
if (!is_identifier_start(c)) {
|
||||
|
||||
bool isIdentifierStart(wchar_t c) {
|
||||
if (!syntax.identifierStarts.empty()) {
|
||||
return syntax.identifierStarts.find(c) != std::wstring::npos;
|
||||
}
|
||||
return is_common_identifier_start(c);
|
||||
}
|
||||
|
||||
bool isIdentifierPart(wchar_t c) {
|
||||
if (!syntax.identifierParts.empty()) {
|
||||
return syntax.identifierParts.find(c) != std::wstring::npos;
|
||||
}
|
||||
return is_common_identifier_part(c);
|
||||
}
|
||||
|
||||
std::wstring parseIdentifier() {
|
||||
wchar_t c = peek();
|
||||
if (!isIdentifierStart(c)) {
|
||||
throw error("identifier expected");
|
||||
}
|
||||
int start = pos;
|
||||
while (hasNext() && is_minimal_identifier_part(source[pos])) {
|
||||
while (hasNext() && isIdentifierPart(source[pos])) {
|
||||
pos++;
|
||||
}
|
||||
return std::wstring(source.substr(start, pos - start));
|
||||
|
|
@ -167,7 +190,7 @@ public:
|
|||
wchar_t c = peek();
|
||||
auto start = currentLocation();
|
||||
if (is_common_identifier_start(c)) {
|
||||
auto name = parseLuaName();
|
||||
auto name = parseIdentifier();
|
||||
TokenTag tag =
|
||||
(syntax.keywords.find(name) == syntax.keywords.end()
|
||||
? TokenTag::NAME
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ namespace devtools {
|
|||
std::wstring multilineCommentEnd;
|
||||
std::wstring multilineStringStart;
|
||||
std::wstring multilineStringEnd;
|
||||
std::wstring identifierStarts;
|
||||
std::wstring identifierParts;
|
||||
|
||||
dv::value serialize() const override;
|
||||
void deserialize(const dv::value& src) override;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "animation/rigging.hpp"
|
||||
#include "graphics/commons/Model.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "graphics/commons/Model.hpp"
|
||||
#include "objects/rigging.hpp"
|
||||
|
||||
namespace rigging {
|
||||
class SkeletonConfig;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,13 +75,14 @@ inline constexpr int DEFAULT_FONT_SIZE = 16;
|
|||
|
||||
inline constexpr int DEFAULT_PRE_RENDER_FONT_PAGES = 1; // must be at least 1
|
||||
|
||||
inline const std::string SHADERS_FOLDER = "shaders";
|
||||
inline const std::string TEXTURES_FOLDER = "textures";
|
||||
inline const std::string ANIMATION_FOLDER = "animation";
|
||||
inline const std::string FONTS_FOLDER = "fonts";
|
||||
inline const std::string LAYOUTS_FOLDER = "layouts";
|
||||
inline const std::string SOUNDS_FOLDER = "sounds";
|
||||
inline const std::string MODELS_FOLDER = "models";
|
||||
inline const std::string SKELETONS_FOLDER = "skeletons";
|
||||
inline const std::string POST_EFFECTS_FOLDER = "shaders/effects";
|
||||
inline const std::string SHADERS_FOLDER = "shaders";
|
||||
inline const std::string SKELETONS_FOLDER = "skeletons";
|
||||
inline const std::string SOUNDS_FOLDER = "sounds";
|
||||
inline const std::string TEXTURES_FOLDER = "textures";
|
||||
|
||||
inline const std::string FONT_DEFAULT = "normal";
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
#include "ContentControl.hpp"
|
||||
|
||||
#include "io/io.hpp"
|
||||
#include "engine/EnginePaths.hpp"
|
||||
#include "animation/rigging.hpp"
|
||||
#include "Content.hpp"
|
||||
#include "ContentPack.hpp"
|
||||
#include "ContentBuilder.hpp"
|
||||
#include "ContentLoader.hpp"
|
||||
#include "PacksManager.hpp"
|
||||
#include "objects/rigging.hpp"
|
||||
#include "devtools/Project.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
#include "ContentPack.hpp"
|
||||
#include "core_defs.hpp"
|
||||
#include "devtools/Project.hpp"
|
||||
#include "engine/EnginePaths.hpp"
|
||||
#include "io/io.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
#include "PacksManager.hpp"
|
||||
|
||||
static void load_configs(Input* input, const io::path& root) {
|
||||
auto configFolder = root / "config";
|
||||
|
|
|
|||
17
src/debug/debug.hpp
Normal file
17
src/debug/debug.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
template<int N, typename T>
|
||||
std::ostream& operator<<(std::ostream& out, const glm::vec<N, T>& vec) {
|
||||
out << "{";
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (i > 0) {
|
||||
out << ", ";
|
||||
}
|
||||
out << vec[i];
|
||||
}
|
||||
out << "}";
|
||||
return out;
|
||||
}
|
||||
|
|
@ -1,9 +1,13 @@
|
|||
#include "DebuggingServer.hpp"
|
||||
|
||||
#include "engine/Engine.hpp"
|
||||
#include "network/Network.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "assets/AssetsLoader.hpp"
|
||||
#include "coders/json.hpp"
|
||||
#include "data/dv.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "engine/AssetsManagement.hpp"
|
||||
#include "engine/Engine.hpp"
|
||||
#include "io/path.hpp"
|
||||
#include "network/Network.hpp"
|
||||
|
||||
using namespace devtools;
|
||||
|
||||
|
|
@ -260,6 +264,16 @@ bool DebuggingServer::performCommand(
|
|||
}
|
||||
});
|
||||
return true;
|
||||
} else if (type == "hot-reload") {
|
||||
if (!map.has("file")) {
|
||||
return false;
|
||||
}
|
||||
logger.info() << "hot-reload: " << map["file"].asString();
|
||||
int count = engine.getAssetsManagement().acquireBackgroundLoader().addReload(
|
||||
map["file"].asString()
|
||||
);
|
||||
logger.info() << "enqueued to reload: " << count;
|
||||
return false;
|
||||
} else {
|
||||
logger.error() << "unsupported command '" << type << "'";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/render/ModelsGenerator.hpp"
|
||||
#include "graphics/ui/GUI.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
|
||||
static debug::Logger logger("assets-management");
|
||||
|
||||
|
|
@ -49,35 +50,41 @@ AssetsLoader& AssetsManagement::acquireBackgroundLoader() {
|
|||
void AssetsManagement::loadAssets(Content* content) {
|
||||
finishBackgroundLoader();
|
||||
|
||||
scripting::on_assets_loading();
|
||||
|
||||
const auto& paths = engine.getPaths();
|
||||
logger.info() << "loading assets";
|
||||
Shader::preprocessor->setPaths(&paths.resPaths);
|
||||
|
||||
auto new_assets = std::make_unique<Assets>(
|
||||
auto newAssets = std::make_unique<Assets>(
|
||||
settings.system.preserveAssetsDuringFrame.get() ? &assetsVault : nullptr
|
||||
);
|
||||
AssetsLoader loader(engine, *new_assets, paths.resPaths);
|
||||
AssetsLoader::addDefaults(loader, content);
|
||||
AssetsLoader loader(engine, *newAssets, paths.resPaths);
|
||||
loader.addDefaults(content);
|
||||
|
||||
// no need
|
||||
// correct log messages order is more useful
|
||||
// todo: before setting to true, check if GLSLExtension thread safe
|
||||
bool threading = false; // look at three upper lines
|
||||
if (threading) {
|
||||
auto task = loader.startTask(
|
||||
[=]() {}, 0
|
||||
);
|
||||
task->waitForEnd();
|
||||
} else {
|
||||
while (loader.hasNext()) {
|
||||
loader.loadNext();
|
||||
try {
|
||||
// no need
|
||||
// correct log messages order is more useful
|
||||
// todo: before setting to true, check if GLSLExtension thread safe
|
||||
bool threading = false; // look at three upper lines
|
||||
if (threading) {
|
||||
auto task = loader.startTask(
|
||||
[=]() {}, 0
|
||||
);
|
||||
task->waitForEnd();
|
||||
} else {
|
||||
while (loader.hasNext()) {
|
||||
loader.loadNext();
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
scripting::revert_assets_loading();
|
||||
throw;
|
||||
}
|
||||
assets = std::move(new_assets);
|
||||
assets = std::move(newAssets);
|
||||
if (content) {
|
||||
ModelsGenerator::prepare(*content, *assets);
|
||||
}
|
||||
assets->setup();
|
||||
engine.getGUI().onAssetsLoad(assets.get());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ void Engine::initialize(CoreParameters coreParameters) {
|
|||
content = std::make_unique<ContentControl>(
|
||||
*project, *paths, input.get(), [this]() { onContentLoad(); }
|
||||
);
|
||||
scripting::initialize(this);
|
||||
scripting::initialize(*this);
|
||||
|
||||
if (!isHeadless()) {
|
||||
gui->getMenu()->setPageLoader(scripting::create_page_loader());
|
||||
|
|
|
|||
|
|
@ -190,5 +190,9 @@ public:
|
|||
return *appScripts;
|
||||
}
|
||||
|
||||
AssetsManagement& getAssetsManagement() const {
|
||||
return *assets;
|
||||
}
|
||||
|
||||
void detachDebugger();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
#include "HandsRenderer.hpp"
|
||||
|
||||
#include "animation/rigging.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "graphics/commons/Model.hpp"
|
||||
#include "ModelBatch.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
|
||||
#include <glm/ext.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include "ModelBatch.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "graphics/commons/Model.hpp"
|
||||
#include "objects/rigging.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
|
||||
using namespace rigging;
|
||||
|
||||
HandsRenderer::HandsRenderer(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#include "NamedSkeletons.hpp"
|
||||
|
||||
#include "objects/rigging.hpp"
|
||||
#include "animation/rigging.hpp"
|
||||
|
||||
using namespace rigging;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "WorldRenderer.hpp"
|
||||
|
||||
#include "animation/rigging.hpp"
|
||||
#include "BlockWrapsRenderer.hpp"
|
||||
#include "ChunksRenderer.hpp"
|
||||
#include "CloudsRenderer.hpp"
|
||||
|
|
@ -42,7 +43,6 @@
|
|||
#include "maths/voxmaths.hpp"
|
||||
#include "objects/Entities.hpp"
|
||||
#include "objects/Player.hpp"
|
||||
#include "objects/rigging.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "voxels/Block.hpp"
|
||||
#include "voxels/Chunk.hpp"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#include "objects/rigging.hpp"
|
||||
#include "libentity.hpp"
|
||||
|
||||
#include "animation/rigging.hpp"
|
||||
#include "engine/Engine.hpp"
|
||||
#include "graphics/render/WorldRenderer.hpp"
|
||||
#include "graphics/render/NamedSkeletons.hpp"
|
||||
|
|
@ -42,7 +42,7 @@ static int l_get_model(lua::State* L) {
|
|||
auto& rigConfig = *skeleton->config;
|
||||
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
|
||||
const auto& modelOverride = skeleton->modelOverrides[index];
|
||||
if (modelOverride.model) {
|
||||
if (modelOverride.model.expired()) {
|
||||
return lua::pushstring(L, modelOverride.name);
|
||||
}
|
||||
return lua::pushstring(L, rigConfig.getBones()[index]->model.name);
|
||||
|
|
@ -55,9 +55,9 @@ static int l_set_model(lua::State* L) {
|
|||
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
|
||||
auto& modelOverride = skeleton->modelOverrides[index];
|
||||
if (lua::isnoneornil(L, 3)) {
|
||||
modelOverride = {"", nullptr, true};
|
||||
modelOverride = {"", {}, true};
|
||||
} else {
|
||||
modelOverride = {lua::require_string(L, 3), nullptr, true};
|
||||
modelOverride = {lua::require_string(L, 3), {}, true};
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -66,19 +66,34 @@ static int l_set_model(lua::State* L) {
|
|||
static int l_get_matrix(lua::State* L) {
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
|
||||
return lua::pushmat4(L, skeleton->pose.matrices[index]);
|
||||
if (lua::istable(L, 3)) {
|
||||
lua::setmat4(L, 3, skeleton->pose.matrices[index]);
|
||||
} else {
|
||||
return lua::pushmat4(L, skeleton->pose.matrices[index]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_matrix(lua::State* L) {
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
if (lua::isnoneornil(L, 2)) {
|
||||
return 0;
|
||||
}
|
||||
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
|
||||
skeleton->pose.matrices[index] = lua::tomat4(L, 3);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_reset_pose(lua::State* L) {
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
auto& pose = skeleton->pose;
|
||||
std::fill(pose.matrices.begin(), pose.matrices.end(), glm::mat4(1.0f));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_texture(lua::State* L) {
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
const auto& found = skeleton->textures.find(lua::require_string(L, 2));
|
||||
|
|
@ -169,6 +184,7 @@ const luaL_Reg skeletonlib[] = {
|
|||
{"set_model", lua::wrap<l_set_model>},
|
||||
{"get_matrix", lua::wrap<l_get_matrix>},
|
||||
{"set_matrix", lua::wrap<l_set_matrix>},
|
||||
{"reset_pose", lua::wrap<l_reset_pose>},
|
||||
{"get_texture", lua::wrap<l_get_texture>},
|
||||
{"set_texture", lua::wrap<l_set_texture>},
|
||||
{"index", lua::wrap<l_index>},
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "graphics/commons/Model.hpp"
|
||||
#include "graphics/core/Atlas.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "io/io.hpp"
|
||||
#include "util/Buffer.hpp"
|
||||
|
||||
using namespace scripting;
|
||||
|
|
@ -66,6 +67,20 @@ static int l_load_texture(lua::State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l_parse_animation(lua::State* L) {
|
||||
auto format = lua::require_lstring(L, 1);
|
||||
auto string = lua::require_lstring(L, 2);
|
||||
std::string name = lua::require_string(L, 3);
|
||||
|
||||
if (format != "vca") {
|
||||
throw std::runtime_error(
|
||||
"unknown format " + util::quote(std::string(format))
|
||||
);
|
||||
}
|
||||
scripting::load_vca_animation(name, &string, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_parse_model(lua::State* L) {
|
||||
auto& assets = engine->requireAssets();
|
||||
|
||||
|
|
@ -161,6 +176,7 @@ static int l_to_canvas(lua::State* L) {
|
|||
const luaL_Reg assetslib[] = {
|
||||
{"request_texture", lua::wrap<l_request_texture>},
|
||||
{"load_texture", lua::wrap<l_load_texture>},
|
||||
{"parse_animation", lua::wrap<l_parse_animation>},
|
||||
{"parse_model", lua::wrap<l_parse_model>},
|
||||
{"to_canvas", lua::wrap<l_to_canvas>},
|
||||
{nullptr, nullptr}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
#include "libentity.hpp"
|
||||
|
||||
#include "animation/rigging.hpp"
|
||||
#include "assets/Assets.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "content/ContentPack.hpp"
|
||||
#include "engine/Engine.hpp"
|
||||
#include "engine/EnginePaths.hpp"
|
||||
#include "objects/Entities.hpp"
|
||||
#include "objects/EntityDef.hpp"
|
||||
#include "objects/Entity.hpp"
|
||||
#include "objects/Rigidbody.hpp"
|
||||
#include "objects/EntityDef.hpp"
|
||||
#include "objects/Player.hpp"
|
||||
#include "objects/rigging.hpp"
|
||||
#include "objects/Rigidbody.hpp"
|
||||
#include "physics/Hitbox.hpp"
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "voxels/Block.hpp"
|
||||
#include "voxels/blocks_agent.hpp"
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
|
||||
#include <set>
|
||||
|
|
@ -116,6 +116,10 @@ static int l_set_skeleton(lua::State* L) {
|
|||
return 0;
|
||||
}
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
if (lua::isnil(L, 2)) {
|
||||
entity->setRig(nullptr);
|
||||
return 0;
|
||||
}
|
||||
std::string skeletonName = lua::require_string(L, 2);
|
||||
auto rigConfig = assets->getShared<rigging::SkeletonConfig>(skeletonName);
|
||||
if (rigConfig == nullptr) {
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ static void create_libs(State* L, StateType stateType) {
|
|||
createtable(L, 0, 0);
|
||||
pushvalue(L, -1);
|
||||
setglobal(L, "__vc_internals");
|
||||
setregistry(L, "internals");
|
||||
setregistry(L, lua::INTERNALS_TABLE);
|
||||
|
||||
if (stateType == StateType::SCRIPT) {
|
||||
getregistry(L, "app");
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ namespace lua {
|
|||
inline std::string CHUNKS_TABLE = "$C"; // precompiled lua chunks
|
||||
inline std::string PACK_ENVS_TABLE = "$P";
|
||||
inline std::string ENVS_TABLE = "$E";
|
||||
inline std::string INTERNALS_TABLE = "$I";
|
||||
extern std::unordered_map<std::type_index, std::string> usertypeNames;
|
||||
int userdata_destructor(lua::State* L);
|
||||
|
||||
|
|
@ -635,6 +636,13 @@ namespace lua {
|
|||
int call(lua::State*, int argc, int nresults = -1);
|
||||
int call_nothrow(lua::State*, int argc, int nresults = 1);
|
||||
|
||||
inline void call_internal(lua::State* L, const std::string& name) {
|
||||
if (lua::get_from_registry(L, lua::INTERNALS_TABLE, name, true)) {
|
||||
lua::call_nothrow(L, 0, 0);
|
||||
pop(L);
|
||||
}
|
||||
}
|
||||
|
||||
inline int eval(
|
||||
lua::State* L,
|
||||
int env,
|
||||
|
|
|
|||
|
|
@ -65,14 +65,13 @@ int scripting::load_script(
|
|||
return lua::execute(lua::get_main_state(), env, src, fileName);
|
||||
}
|
||||
|
||||
void scripting::initialize(Engine* engine) {
|
||||
scripting::engine = engine;
|
||||
scripting::content_control = &engine->getContentControl();
|
||||
lua::initialize(engine->getPaths(), engine->getCoreParameters());
|
||||
void scripting::initialize(Engine& engine) {
|
||||
scripting::engine = &engine;
|
||||
scripting::content_control = &engine.getContentControl();
|
||||
lua::initialize(engine.getPaths(), engine.getCoreParameters());
|
||||
|
||||
load_script(io::path("stdlib.lua"), true);
|
||||
load_script(io::path("classes.lua"), true);
|
||||
load_script(io::path("internal_events.lua"), true);
|
||||
}
|
||||
|
||||
class LuaCoroutine : public Process {
|
||||
|
|
@ -244,9 +243,7 @@ std::unique_ptr<Process> scripting::start_app_script(const io::path& script) {
|
|||
|
||||
void scripting::process_post_runnables() {
|
||||
auto L = lua::get_main_state();
|
||||
if (lua::getglobal(L, "__vc__process_post_runnables")) {
|
||||
lua::call_nothrow(L, 0, 0);
|
||||
}
|
||||
lua::call_internal(L, "process_post_runnables");
|
||||
}
|
||||
|
||||
template <class T, typename IdType>
|
||||
|
|
@ -263,6 +260,14 @@ static int push_properties_tables(
|
|||
return 1;
|
||||
}
|
||||
|
||||
void scripting::on_assets_loading() {
|
||||
lua::call_internal(lua::get_main_state(), "backup_and_clear_animation");
|
||||
}
|
||||
|
||||
void scripting::revert_assets_loading() {
|
||||
lua::call_internal(lua::get_main_state(), "restore_animation_backup");
|
||||
}
|
||||
|
||||
void scripting::on_content_load(Content* content) {
|
||||
scripting::content = content;
|
||||
scripting::indices = content->getIndices();
|
||||
|
|
@ -291,13 +296,15 @@ void scripting::on_content_load(Content* content) {
|
|||
|
||||
lua::getregistry(L, "app");
|
||||
lua::setglobal(L, "__vc_app");
|
||||
lua::getregistry(L, "internals");
|
||||
lua::getregistry(L, lua::INTERNALS_TABLE);
|
||||
lua::setglobal(L, "__vc_internals");
|
||||
try {
|
||||
load_script("post_content.lua", true);
|
||||
} catch (const std::exception&) {
|
||||
lua::pushnil(L);
|
||||
lua::setglobal(L, "__vc_app");
|
||||
lua::pushnil(L);
|
||||
lua::setglobal(L, "__vc_internals");
|
||||
throw;
|
||||
}
|
||||
lua::pushnil(L);
|
||||
|
|
@ -319,9 +326,7 @@ void scripting::on_world_load(LevelController* controller) {
|
|||
scripting::controller = controller;
|
||||
|
||||
auto L = lua::get_main_state();
|
||||
if (lua::getglobal(L, "__vc_on_world_open")) {
|
||||
lua::call_nothrow(L, 0, 0);
|
||||
}
|
||||
lua::call_internal(L, "on_world_open");
|
||||
|
||||
for (auto& pack : content_control->getAllContentPacks()) {
|
||||
lua::emit_event(L, pack.id + ":.worldopen", [](auto L) {
|
||||
|
|
@ -334,9 +339,10 @@ void scripting::on_world_load(LevelController* controller) {
|
|||
|
||||
void scripting::on_world_tick(int tps) {
|
||||
auto L = lua::get_main_state();
|
||||
if (lua::getglobal(L, "__vc_on_world_tick")) {
|
||||
if (lua::get_from_registry(L, lua::INTERNALS_TABLE, "on_world_tick", true)) {
|
||||
lua::pushinteger(L, tps);
|
||||
lua::call_nothrow(L, 1, 0);
|
||||
lua::pop(L);
|
||||
}
|
||||
for (auto& pack : content_control->getAllContentPacks()) {
|
||||
lua::emit_event(L, pack.id + ":.worldtick");
|
||||
|
|
@ -348,16 +354,12 @@ void scripting::on_world_save() {
|
|||
for (auto& pack : content_control->getAllContentPacks()) {
|
||||
lua::emit_event(L, pack.id + ":.worldsave");
|
||||
}
|
||||
if (lua::getglobal(L, "__vc_on_world_save")) {
|
||||
lua::call_nothrow(L, 0, 0);
|
||||
}
|
||||
lua::call_internal(L, "on_world_save");
|
||||
}
|
||||
|
||||
void scripting::process_before_quit() {
|
||||
auto L = lua::get_main_state();
|
||||
if (lua::getglobal(L, "__vc_process_before_quit")) {
|
||||
lua::call_nothrow(L, 0, 0);
|
||||
}
|
||||
lua::call_internal(L, "process_before_quit");
|
||||
}
|
||||
|
||||
void scripting::on_world_quit() {
|
||||
|
|
@ -365,9 +367,7 @@ void scripting::on_world_quit() {
|
|||
for (auto& pack : content_control->getAllContentPacks()) {
|
||||
lua::emit_event(L, pack.id + ":.worldquit");
|
||||
}
|
||||
if (lua::getglobal(L, "__vc_on_world_quit")) {
|
||||
lua::call_nothrow(L, 0, 0);
|
||||
}
|
||||
lua::call_internal(L, "on_world_quit");
|
||||
scripting::level = nullptr;
|
||||
scripting::content = nullptr;
|
||||
scripting::indices = nullptr;
|
||||
|
|
@ -842,6 +842,27 @@ void scripting::load_layout_script(
|
|||
lua::pop(L);
|
||||
}
|
||||
|
||||
void scripting::load_vca_animation(
|
||||
const io::path& file,
|
||||
const std::string_view* content,
|
||||
const std::string& identifier
|
||||
) {
|
||||
auto L = lua::get_main_state();
|
||||
if (lua::get_from_registry(L, lua::INTERNALS_TABLE, "load_vca_animation", true)) {
|
||||
lua::pushlstring(L, file.string());
|
||||
if (content) {
|
||||
lua::pushlstring(L, *content);
|
||||
} else {
|
||||
lua::pushnil(L);
|
||||
}
|
||||
lua::pushlstring(L, identifier);
|
||||
if (lua::call(L, 3, 0)) {
|
||||
lua::pop(L);
|
||||
}
|
||||
lua::pop(L);
|
||||
}
|
||||
}
|
||||
|
||||
void scripting::close() {
|
||||
lua::finalize();
|
||||
content = nullptr;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,10 @@ namespace scripting {
|
|||
extern std::ostream* output_stream;
|
||||
extern std::ostream* error_stream;
|
||||
|
||||
void initialize(Engine* engine);
|
||||
void initialize(Engine& engine);
|
||||
|
||||
void on_assets_loading();
|
||||
void revert_assets_loading();
|
||||
|
||||
void on_content_load(Content* content);
|
||||
void on_content_reset();
|
||||
|
|
@ -251,6 +254,12 @@ namespace scripting {
|
|||
const std::string& fileName,
|
||||
UiDocScript& script
|
||||
);
|
||||
|
||||
void load_vca_animation(
|
||||
const io::path& file,
|
||||
const std::string_view* content,
|
||||
const std::string& identifier
|
||||
);
|
||||
|
||||
/// @brief Finalize lua state. Using scripting after will lead to Lua panic
|
||||
void close();
|
||||
|
|
|
|||
|
|
@ -37,6 +37,11 @@ static inline const std::string STDCOMP = "stdcomp";
|
|||
lua::setfield(L, name);
|
||||
lua::pop(L);
|
||||
}
|
||||
if (lua::getfield(L, "ordered_components")) {
|
||||
lua::pushenv(L, id);
|
||||
lua::rawseti(L, lua::objlen(L, -2) + 1);
|
||||
lua::pop(L);
|
||||
}
|
||||
lua::pop(L);
|
||||
|
||||
return std::shared_ptr<int>(new int(id), [=](int* id) { //-V508
|
||||
|
|
|
|||
|
|
@ -47,9 +47,7 @@ void scripting::on_frontend_init(
|
|||
|
||||
load_script("hud_classes.lua");
|
||||
|
||||
if (lua::getglobal(L, "__vc_on_hud_open")) {
|
||||
lua::call_nothrow(L, 0, 0);
|
||||
}
|
||||
lua::call_internal(L, "on_hud_open");
|
||||
|
||||
for (auto& pack : content_control->getAllContentPacks()) {
|
||||
lua::emit_event(
|
||||
|
|
@ -63,12 +61,13 @@ void scripting::on_frontend_init(
|
|||
}
|
||||
|
||||
void scripting::on_frontend_render() {
|
||||
auto L = lua::get_main_state();
|
||||
|
||||
lua::call_internal(L, "on_render");
|
||||
for (auto& pack : content_control->getAllContentPacks()) {
|
||||
lua::emit_event(
|
||||
lua::get_main_state(),
|
||||
pack.id + ":.hudrender",
|
||||
[](lua::State* L) { return 0; }
|
||||
);
|
||||
lua::emit_event(L, pack.id + ":.hudrender", [](lua::State* L) {
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#define VC_ENABLE_REFLECTION
|
||||
#include "Entities.hpp"
|
||||
|
||||
#include "animation/rigging.hpp"
|
||||
#include "assets/Assets.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "data/dv_util.hpp"
|
||||
|
|
@ -17,7 +18,6 @@
|
|||
#include "maths/rays.hpp"
|
||||
#include "maths/util.hpp"
|
||||
#include "physics/PhysicsSolver.hpp"
|
||||
#include "rigging.hpp"
|
||||
#include "world/Level.hpp"
|
||||
|
||||
#include <entt/entity/registry.hpp>
|
||||
|
|
@ -59,7 +59,11 @@ entityid_t Entities::spawn(
|
|||
if (assets) {
|
||||
skeleton = assets->get<rigging::SkeletonConfig>(def.skeletonName);
|
||||
if (skeleton == nullptr) {
|
||||
throw std::runtime_error("skeleton " + def.skeletonName + " not found");
|
||||
if (def.skeletonName == def.name) {
|
||||
logger.warning() << "skeleton " + def.skeletonName + " not found";
|
||||
} else {
|
||||
throw std::runtime_error("skeleton " + def.skeletonName + " not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
entityid_t id;
|
||||
|
|
@ -100,7 +104,9 @@ entityid_t Entities::spawn(
|
|||
|
||||
auto& scripting = registry->emplace<ScriptComponents>(entity);
|
||||
if (assets) {
|
||||
registry->emplace<rigging::Skeleton>(entity, skeleton->instance());
|
||||
registry->emplace<rigging::Skeleton>(
|
||||
entity, skeleton ? skeleton->instance() : rigging::Skeleton(nullptr)
|
||||
);
|
||||
}
|
||||
|
||||
for (auto& instance : def.components) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
#include "Entity.hpp"
|
||||
|
||||
#include "Transform.hpp"
|
||||
#include "Rigidbody.hpp"
|
||||
#include "ScriptComponents.hpp"
|
||||
#include "animation/rigging.hpp"
|
||||
#include "Entities.hpp"
|
||||
#include "EntityDef.hpp"
|
||||
#include "rigging.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
#include "Rigidbody.hpp"
|
||||
#include "ScriptComponents.hpp"
|
||||
#include "Transform.hpp"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,27 +1,28 @@
|
|||
#include "Player.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
|
||||
#include "animation/rigging.hpp"
|
||||
#include "content/ContentReport.hpp"
|
||||
#include "data/dv_util.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "Entities.hpp"
|
||||
#include "Entity.hpp"
|
||||
#include "items/Inventory.hpp"
|
||||
#include "physics/Hitbox.hpp"
|
||||
#include "physics/PhysicsSolver.hpp"
|
||||
#include "util/stringutil.hpp"
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
#include "world/generator/GeneratorDef.hpp"
|
||||
#include "world/Level.hpp"
|
||||
#include "world/World.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <utility>
|
||||
|
||||
#include "content/ContentReport.hpp"
|
||||
#include "items/Inventory.hpp"
|
||||
#include "Entities.hpp"
|
||||
#include "Entity.hpp"
|
||||
#include "rigging.hpp"
|
||||
#include "physics/Hitbox.hpp"
|
||||
#include "physics/PhysicsSolver.hpp"
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
#include "world/Level.hpp"
|
||||
#include "world/World.hpp"
|
||||
#include "world/generator/GeneratorDef.hpp"
|
||||
#include "data/dv_util.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "util/stringutil.hpp"
|
||||
|
||||
static debug::Logger logger("player");
|
||||
|
||||
constexpr int SPAWN_ATTEMPTS_PER_UPDATE = 64;
|
||||
|
|
|
|||
|
|
@ -103,6 +103,22 @@ namespace util {
|
|||
return data_.ptr()[index];
|
||||
}
|
||||
|
||||
T& front() {
|
||||
return data_.ptr()[0];
|
||||
}
|
||||
|
||||
T& back() {
|
||||
return data_.ptr()[size_ - 1];
|
||||
}
|
||||
|
||||
const T& front() const {
|
||||
return data_.ptr()[0];
|
||||
}
|
||||
|
||||
const T& back() const {
|
||||
return data_.ptr()[size_ - 1];
|
||||
}
|
||||
|
||||
int size() const {
|
||||
return size_;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue