commit
5de8ae5f61
@ -20,6 +20,22 @@ Example:
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can pass values in ARGS from the entity configuration.
|
||||||
|
They will be passed both when creating a new entity and when loading a saved one.
|
||||||
|
The `args` list is used for this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "base:drop",
|
||||||
|
"args": {
|
||||||
|
"item": "base:stone.item",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
The components code should be in `scripts/components`.
|
The components code should be in `scripts/components`.
|
||||||
|
|
||||||
## Physics
|
## Physics
|
||||||
|
|||||||
@ -32,6 +32,7 @@ Subsections:
|
|||||||
- [mat4](scripting/builtins/libmat4.md)
|
- [mat4](scripting/builtins/libmat4.md)
|
||||||
- [network](scripting/builtins/libnetwork.md)
|
- [network](scripting/builtins/libnetwork.md)
|
||||||
- [pack](scripting/builtins/libpack.md)
|
- [pack](scripting/builtins/libpack.md)
|
||||||
|
- [pathfinding](scripting/builtins/libpathfinding.md)
|
||||||
- [player](scripting/builtins/libplayer.md)
|
- [player](scripting/builtins/libplayer.md)
|
||||||
- [quat](scripting/builtins/libquat.md)
|
- [quat](scripting/builtins/libquat.md)
|
||||||
- [rules](scripting/builtins/librules.md)
|
- [rules](scripting/builtins/librules.md)
|
||||||
|
|||||||
66
doc/en/scripting/builtins/libpathfinding.md
Normal file
66
doc/en/scripting/builtins/libpathfinding.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# *pathfinding* library
|
||||||
|
|
||||||
|
The *pathfinding* library provides functions for working with the pathfinding system in the game world. It allows you to create and manage agents finding routes between points in the world.
|
||||||
|
|
||||||
|
When used in entity logic, the `core:pathfinding` component should be used.
|
||||||
|
|
||||||
|
## `core:pathfinding` component
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local pf = entity:get_component("core:pathfinding")
|
||||||
|
|
||||||
|
--- ...
|
||||||
|
local x = ...
|
||||||
|
local y = ...
|
||||||
|
local z = ...
|
||||||
|
|
||||||
|
--- Set the target for the agent
|
||||||
|
pf.set_target({x, y, z})
|
||||||
|
|
||||||
|
--- Get the current target of the agent
|
||||||
|
local target = pf.get_target() --> vec3 or nil
|
||||||
|
--- ...
|
||||||
|
|
||||||
|
--- Get the current route of the agent
|
||||||
|
local route = pf.get_route() --> table<vec3> or nil
|
||||||
|
--- ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Library functions
|
||||||
|
|
||||||
|
```lua
|
||||||
|
--- Create a new agent. Returns the ID of the created agent
|
||||||
|
local agent = pathfinding.create_agent() --> int
|
||||||
|
|
||||||
|
--- Delete an agent by ID. Returns true if the agent existed, otherwise false
|
||||||
|
pathfinding.remove_agent(agent: int) --> bool
|
||||||
|
|
||||||
|
--- Set the agent state (enabled/disabled)
|
||||||
|
pathfinding.set_enabled(agent: int, enabled: bool)
|
||||||
|
|
||||||
|
--- Check the agent state. Returns true if the agent is enabled, otherwise false
|
||||||
|
pathfinding.is_enabled(agent: int) --> bool
|
||||||
|
|
||||||
|
--- Create a route based on the given points. Returns an array of route points
|
||||||
|
pathfinding.make_route(start: vec3, target: vec3) --> table<vec3>
|
||||||
|
|
||||||
|
--- Asynchronously create a route based on the given points.
|
||||||
|
--- This function allows to perform pathfinding in the background without blocking the main thread of execution
|
||||||
|
pathfinding.make_route_async(agent: int, start: vec3, target: vec3)
|
||||||
|
|
||||||
|
--- Get the route that the agent has already found. Used to get the route after an asynchronous search.
|
||||||
|
--- If the search has not yet completed, returns nil. If the route is not found, returns an empty table.
|
||||||
|
pathfinding.pull_route(agent: int) --> table<vec3> or nil
|
||||||
|
|
||||||
|
--- Set the maximum number of visited blocks for the agent. Used to limit the amount of work of the pathfinding algorithm.
|
||||||
|
pathfinding.set_max_visited(agent: int, max_visited: int)
|
||||||
|
|
||||||
|
--- Adding an avoided blocks tag
|
||||||
|
pathfinding.avoid_tag(
|
||||||
|
agent: int,
|
||||||
|
-- tag for avoided blocks
|
||||||
|
tag: string, [optional],
|
||||||
|
-- cost of crossing a block
|
||||||
|
cost: int = 10
|
||||||
|
)
|
||||||
|
```
|
||||||
@ -100,6 +100,13 @@ vecn.length(a: vector)
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Distance - *vecn.distance(...)*
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- returns the distance between two vectors
|
||||||
|
vecn.distance(a: vector, b: vector)
|
||||||
|
```
|
||||||
|
|
||||||
#### Absolute value - *vecn.abs(...)*
|
#### Absolute value - *vecn.abs(...)*
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
@ -136,6 +143,16 @@ vecn.pow(v: vector, exponent: number, dst: vector)
|
|||||||
vecn.dot(a: vector, b: vector)
|
vecn.dot(a: vector, b: vector)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Mixing - *vecn.mix(...)*
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- returns vector a * (1.0 - t) + b * t
|
||||||
|
vecn.mix(a: vector, b: vector, t: number)
|
||||||
|
|
||||||
|
-- writes to dst vector a * (1.0 - t) + b * t
|
||||||
|
vecn.mix(a: vector, b: vector, t: number, dst: vector)
|
||||||
|
```
|
||||||
|
|
||||||
#### Convert to string - *vecn.tostring(...)*
|
#### Convert to string - *vecn.tostring(...)*
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> Returns only if the content is a vector
|
> Returns only if the content is a vector
|
||||||
@ -160,6 +177,12 @@ vec2.angle(v: vec2)
|
|||||||
|
|
||||||
-- returns the direction angle of the vector {x, y} in degrees [0, 360]
|
-- returns the direction angle of the vector {x, y} in degrees [0, 360]
|
||||||
vec2.angle(x: number, y: number)
|
vec2.angle(x: number, y: number)
|
||||||
|
|
||||||
|
-- returns the vector rotated by an angle in degrees counterclockwise
|
||||||
|
vec2.rotate(v: vec2, angle: number) -> vec2
|
||||||
|
|
||||||
|
-- writes the vector rotated by an angle in degrees counterclockwise to dst
|
||||||
|
vec2.rotate(v: vec2, angle: number, dst: vec2) -> vec2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -188,6 +211,10 @@ print("mul: " .. vec3.tostring(result_mul)) -- {10, 40, 80}
|
|||||||
local result_mul_scal = vec3.mul(v1_3d, scal)
|
local result_mul_scal = vec3.mul(v1_3d, scal)
|
||||||
print("mul_scal: " .. vec3.tostring(result_mul_scal)) -- {6, 12, 12}
|
print("mul_scal: " .. vec3.tostring(result_mul_scal)) -- {6, 12, 12}
|
||||||
|
|
||||||
|
-- calculating distance between vectors
|
||||||
|
local result_distance = vec3.distance(v1_3d, v2_3d)
|
||||||
|
print("distance: " .. result_distance) -- 43
|
||||||
|
|
||||||
-- vector normalization
|
-- vector normalization
|
||||||
local result_norm = vec3.normalize(v1_3d)
|
local result_norm = vec3.normalize(v1_3d)
|
||||||
print("norm: " .. vec3.tostring(result_norm)) -- {0.333, 0.667, 0.667}
|
print("norm: " .. vec3.tostring(result_norm)) -- {0.333, 0.667, 0.667}
|
||||||
@ -211,3 +238,7 @@ print("pow: " .. vec3.tostring(result_pow)) -- {1, 4, 4}
|
|||||||
-- scalar product of vectors
|
-- scalar product of vectors
|
||||||
local result_dot = vec3.dot(v1_3d, v2_3d)
|
local result_dot = vec3.dot(v1_3d, v2_3d)
|
||||||
print("dot: " ..result_dot) -- 250
|
print("dot: " ..result_dot) -- 250
|
||||||
|
|
||||||
|
-- mixing vectors
|
||||||
|
local result_mix = vec3.mix(v1_3d, v2_3d, 0.25)
|
||||||
|
print("mix: " .. vec3.tostring(result_mix)) -- {3.25, 6.5, 11.5}
|
||||||
|
|||||||
@ -26,6 +26,8 @@ entity:get_uid() -> int
|
|||||||
entity:get_component(name: str) -> component or nil
|
entity:get_component(name: str) -> component or nil
|
||||||
-- Checks for the presence of a component by name
|
-- Checks for the presence of a component by name
|
||||||
entity:has_component(name: str) -> bool
|
entity:has_component(name: str) -> bool
|
||||||
|
-- Retrieves a component by name. Throws an exception if it does not exist
|
||||||
|
entity:require_component(name: str) -> component
|
||||||
|
|
||||||
-- Enables/disables the component
|
-- Enables/disables the component
|
||||||
entity:set_enabled(name: str, enable: bool)
|
entity:set_enabled(name: str, enable: bool)
|
||||||
@ -93,10 +95,12 @@ body:get_linear_damping() -> number
|
|||||||
-- Sets the linear velocity attenuation multiplier
|
-- Sets the linear velocity attenuation multiplier
|
||||||
body:set_linear_damping(value: number)
|
body:set_linear_damping(value: number)
|
||||||
|
|
||||||
-- Checks if vertical velocity attenuation is enabled
|
-- Checks if vertical damping is enabled
|
||||||
body:is_vdamping() -> bool
|
body:is_vdamping() -> bool
|
||||||
-- Enables/disables vertical velocity attenuation
|
-- Returns the vertical damping multiplier
|
||||||
body:set_vdamping(enabled: bool)
|
body:get_vdamping() -> number
|
||||||
|
-- Enables/disables vertical damping / sets vertical damping multiplier
|
||||||
|
body:set_vdamping(enabled: bool | number)
|
||||||
|
|
||||||
-- Checks if the entity is on the ground
|
-- Checks if the entity is on the ground
|
||||||
body:is_grounded() -> bool
|
body:is_grounded() -> bool
|
||||||
@ -188,6 +192,12 @@ function on_update(tps: int)
|
|||||||
|
|
||||||
Called every entities tick (currently 20 times per second).
|
Called every entities tick (currently 20 times per second).
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function on_physics_update(delta: number)
|
||||||
|
```
|
||||||
|
|
||||||
|
Called after each physics step
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function on_render(delta: number)
|
function on_render(delta: number)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -20,6 +20,22 @@
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Из конфигурации сущности можно передавать значения в ARGS.
|
||||||
|
Они будут передаваться как при создании новой сущности, так и при загрузке сохранённой.
|
||||||
|
Для этого используется список `args`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "base:drop",
|
||||||
|
"args": {
|
||||||
|
"item": "base:stone.item",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
Код компонентов должен находиться в `scripts/components`.
|
Код компонентов должен находиться в `scripts/components`.
|
||||||
|
|
||||||
## Физика
|
## Физика
|
||||||
|
|||||||
@ -32,6 +32,7 @@
|
|||||||
- [mat4](scripting/builtins/libmat4.md)
|
- [mat4](scripting/builtins/libmat4.md)
|
||||||
- [network](scripting/builtins/libnetwork.md)
|
- [network](scripting/builtins/libnetwork.md)
|
||||||
- [pack](scripting/builtins/libpack.md)
|
- [pack](scripting/builtins/libpack.md)
|
||||||
|
- [pathfinding](scripting/builtins/libpathfinding.md)
|
||||||
- [player](scripting/builtins/libplayer.md)
|
- [player](scripting/builtins/libplayer.md)
|
||||||
- [quat](scripting/builtins/libquat.md)
|
- [quat](scripting/builtins/libquat.md)
|
||||||
- [rules](scripting/builtins/librules.md)
|
- [rules](scripting/builtins/librules.md)
|
||||||
|
|||||||
66
doc/ru/scripting/builtins/libpathfinding.md
Normal file
66
doc/ru/scripting/builtins/libpathfinding.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# Библиотека *pathfinding*
|
||||||
|
|
||||||
|
Библиотека *pathfinding* предоставляет функции для работы с системой поиска пути в игровом мире. Она позволяет создавать и управлять агентами, которые могут находить маршруты между точками в мире.
|
||||||
|
|
||||||
|
При использовании в логике сущностей следует использовать компонент `core:pathfinding`.
|
||||||
|
|
||||||
|
## Компонент `core:pathfinding`
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local pf = entity:get_component("core:pathfinding")
|
||||||
|
|
||||||
|
--- ...
|
||||||
|
local x = ...
|
||||||
|
local y = ...
|
||||||
|
local z = ...
|
||||||
|
|
||||||
|
--- Установка цели для агента
|
||||||
|
pf.set_target({x, y, z})
|
||||||
|
|
||||||
|
--- Получение текущей цели агента
|
||||||
|
local target = pf.get_target() --> vec3 или nil
|
||||||
|
--- ...
|
||||||
|
|
||||||
|
--- Получение текущего маршрута агента
|
||||||
|
local route = pf.get_route() --> table<vec3> или nil
|
||||||
|
--- ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Функции библиотеки
|
||||||
|
|
||||||
|
```lua
|
||||||
|
--- Создание нового агента. Возвращает идентификатор созданного агента
|
||||||
|
local agent = pathfinding.create_agent() --> int
|
||||||
|
|
||||||
|
--- Удаление агента по идентификатору. Возвращает true, если агент существовал, иначе false
|
||||||
|
pathfinding.remove_agent(agent: int) --> bool
|
||||||
|
|
||||||
|
--- Установка состояния агента (включен/выключен)
|
||||||
|
pathfinding.set_enabled(agent: int, enabled: bool)
|
||||||
|
|
||||||
|
--- Проверка состояния агента. Возвращает true, если агент включен, иначе false
|
||||||
|
pathfinding.is_enabled(agent: int) --> bool
|
||||||
|
|
||||||
|
--- Создание маршрута на основе заданных точек. Возвращает массив точек маршрута
|
||||||
|
pathfinding.make_route(start: vec3, target: vec3) --> table<vec3>
|
||||||
|
|
||||||
|
--- Асинхронное создание маршрута на основе заданных точек.
|
||||||
|
--- Функция позволяет выполнять поиск пути в фоновом режиме, не блокируя основной поток выполнения
|
||||||
|
pathfinding.make_route_async(agent: int, start: vec3, target: vec3)
|
||||||
|
|
||||||
|
--- Получение маршрута, который агент уже нашел. Используется для получения маршрута после асинхронного поиска.
|
||||||
|
--- Если поиск ещё не завершён, возвращает nil. Если маршрут не найден, возвращает пустую таблицу.
|
||||||
|
pathfinding.pull_route(agent: int) --> table<vec3> или nil
|
||||||
|
|
||||||
|
--- Установка максимального количества посещенных блоков для агента. Используется для ограничения объема работы алгоритма поиска пути.
|
||||||
|
pathfinding.set_max_visited(agent: int, max_visited: int)
|
||||||
|
|
||||||
|
--- Добавление тега избегаемых блоков
|
||||||
|
pathfinding.avoid_tag(
|
||||||
|
agent: int,
|
||||||
|
-- тег избегаемых блоков
|
||||||
|
tag: string, [опционально],
|
||||||
|
-- стоимость пересечения блока
|
||||||
|
cost: int = 10
|
||||||
|
)
|
||||||
|
```
|
||||||
@ -100,6 +100,13 @@ vecn.length(a: vector)
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Дистанция - *vecn.distance(...)*
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- возвращает расстояние между двумя векторами
|
||||||
|
vecn.distance(a: vector, b: vector)
|
||||||
|
```
|
||||||
|
|
||||||
#### Абсолютное значение - *vecn.abs(...)*
|
#### Абсолютное значение - *vecn.abs(...)*
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
@ -136,6 +143,16 @@ vecn.pow(v: vector, exponent: number, dst: vector)
|
|||||||
vecn.dot(a: vector, b: vector)
|
vecn.dot(a: vector, b: vector)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Смешивание - *vecn.mix(...)*
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- возвращает вектор a * (1.0 - t) + b * t
|
||||||
|
vecn.mix(a: vector, b: vector, t: number)
|
||||||
|
|
||||||
|
-- записывает в dst вектор a * (1.0 - t) + b * t
|
||||||
|
vecn.mix(a: vector, b: vector, t: number, dst: vector)
|
||||||
|
```
|
||||||
|
|
||||||
#### Перевод в строку - *vecn.tostring(...)*
|
#### Перевод в строку - *vecn.tostring(...)*
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> Возвращает только тогда, когда содержимым является вектор
|
> Возвращает только тогда, когда содержимым является вектор
|
||||||
@ -160,6 +177,12 @@ vec2.angle(v: vec2)
|
|||||||
|
|
||||||
-- возвращает угол направления вектора {x, y} в градусах [0, 360]
|
-- возвращает угол направления вектора {x, y} в градусах [0, 360]
|
||||||
vec2.angle(x: number, y: number)
|
vec2.angle(x: number, y: number)
|
||||||
|
|
||||||
|
-- возвращает повернутый вектор на угол в градусах против часовой стрелки
|
||||||
|
vec2.rotate(v: vec2, angle: number) -> vec2
|
||||||
|
|
||||||
|
-- записывает повернутый вектор на угол в градусах против часовой стрелки в dst
|
||||||
|
vec2.rotate(v: vec2, angle: number, dst: vec2) -> vec2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -192,6 +215,10 @@ print("mul_scal: " .. vec3.tostring(result_mul_scal)) -- {6, 12, 12}
|
|||||||
local result_norm = vec3.normalize(v1_3d)
|
local result_norm = vec3.normalize(v1_3d)
|
||||||
print("norm: " .. vec3.tostring(result_norm)) -- {0.333, 0.667, 0.667}
|
print("norm: " .. vec3.tostring(result_norm)) -- {0.333, 0.667, 0.667}
|
||||||
|
|
||||||
|
-- дистанция между векторами
|
||||||
|
local result_distance = vec3.distance(v1_3d, v2_3d)
|
||||||
|
print("distance: " .. result_distance) -- 43
|
||||||
|
|
||||||
-- длина вектора
|
-- длина вектора
|
||||||
local result_len = vec3.length(v1_3d)
|
local result_len = vec3.length(v1_3d)
|
||||||
print("len: " .. result_len) -- 3
|
print("len: " .. result_len) -- 3
|
||||||
@ -211,4 +238,9 @@ print("pow: " .. vec3.tostring(result_pow)) -- {1, 4, 4}
|
|||||||
-- скалярное произведение векторов
|
-- скалярное произведение векторов
|
||||||
local result_dot = vec3.dot(v1_3d, v2_3d)
|
local result_dot = vec3.dot(v1_3d, v2_3d)
|
||||||
print("dot: " .. result_dot) -- 250
|
print("dot: " .. result_dot) -- 250
|
||||||
|
|
||||||
|
-- смешивание векторов
|
||||||
|
local result_mix = vec3.mix(v1_3d, v2_3d, 0.25)
|
||||||
|
print("mix: " .. vec3.tostring(result_mix)) -- {3.25, 6.5, 11.5}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@ -26,6 +26,8 @@ entity:get_uid() -> int
|
|||||||
entity:get_component(name: str) -> компонент или nil
|
entity:get_component(name: str) -> компонент или nil
|
||||||
-- Проверяет наличие компонента по имени
|
-- Проверяет наличие компонента по имени
|
||||||
entity:has_component(name: str) -> bool
|
entity:has_component(name: str) -> bool
|
||||||
|
-- Запрашивает компонент по имени. Бросает исключение при отсутствии
|
||||||
|
entity:require_component(name: str) -> компонент
|
||||||
|
|
||||||
-- Включает/выключает компонент по имени
|
-- Включает/выключает компонент по имени
|
||||||
entity:set_enabled(name: str, enable: bool)
|
entity:set_enabled(name: str, enable: bool)
|
||||||
@ -95,8 +97,10 @@ body:set_linear_damping(value: number)
|
|||||||
|
|
||||||
-- Проверяет, включено ли вертикальное затухание скорости
|
-- Проверяет, включено ли вертикальное затухание скорости
|
||||||
body:is_vdamping() -> bool
|
body:is_vdamping() -> bool
|
||||||
-- Включает/выключает вертикальное затухание скорости
|
-- Возвращает множитель вертикального затухания скорости
|
||||||
body:set_vdamping(enabled: bool)
|
body:get_vdamping() -> number
|
||||||
|
-- Включает/выключает вертикальное затухание скорости / устанавливает значение множителя
|
||||||
|
body:set_vdamping(enabled: bool | number)
|
||||||
|
|
||||||
-- Проверяет, находится ли сущность на земле (приземлена)
|
-- Проверяет, находится ли сущность на земле (приземлена)
|
||||||
body:is_grounded() -> bool
|
body:is_grounded() -> bool
|
||||||
@ -188,6 +192,12 @@ function on_update(tps: int)
|
|||||||
|
|
||||||
Вызывается каждый такт сущностей (на данный момент - 20 раз в секунду).
|
Вызывается каждый такт сущностей (на данный момент - 20 раз в секунду).
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function on_physics_update(delta: number)
|
||||||
|
```
|
||||||
|
|
||||||
|
Вызывается после каждого шага физики
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function on_render(delta: number)
|
function on_render(delta: number)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -8,5 +8,5 @@
|
|||||||
"selectable": false,
|
"selectable": false,
|
||||||
"replaceable": true,
|
"replaceable": true,
|
||||||
"translucent": true,
|
"translucent": true,
|
||||||
"tags": ["base:liquid"]
|
"tags": ["core:liquid"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,13 @@
|
|||||||
{
|
{
|
||||||
"components": [
|
"components": [
|
||||||
"base:drop"
|
{
|
||||||
|
"name": "base:drop",
|
||||||
|
"args": {
|
||||||
|
"item": "base:stone.item",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
],
|
],
|
||||||
"hitbox": [0.4, 0.25, 0.4],
|
"hitbox": [0.4, 0.25, 0.4],
|
||||||
"sensors": [
|
"sensors": [
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
{
|
{
|
||||||
"components": [
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "core:mob",
|
||||||
|
"args": {
|
||||||
|
"jump_force": 8.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"core:player",
|
||||||
"base:player_animator"
|
"base:player_animator"
|
||||||
],
|
],
|
||||||
"hitbox": [0.6, 1.8, 0.6]
|
"hitbox": [0.6, 1.8, 0.6]
|
||||||
|
|||||||
@ -8,6 +8,9 @@ timer = 0.3
|
|||||||
|
|
||||||
local def_index = entity:def_index()
|
local def_index = entity:def_index()
|
||||||
dropitem = ARGS
|
dropitem = ARGS
|
||||||
|
if dropitem.item then
|
||||||
|
dropitem.id = item.index(dropitem.item)
|
||||||
|
end
|
||||||
if dropitem then
|
if dropitem then
|
||||||
timer = dropitem.pickup_delay or timer
|
timer = dropitem.pickup_delay or timer
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
local tsf = entity.transform
|
local tsf = entity.transform
|
||||||
local body = entity.rigidbody
|
local body = entity.rigidbody
|
||||||
local rig = entity.skeleton
|
local rig = entity.skeleton
|
||||||
|
local mob = entity:require_component("core:mob")
|
||||||
|
|
||||||
local itemid = 0
|
local itemid = 0
|
||||||
local headIndex = rig:index("head")
|
|
||||||
local itemIndex = rig:index("item")
|
local itemIndex = rig:index("item")
|
||||||
local bodyIndex = rig:index("body")
|
|
||||||
|
|
||||||
local function refresh_model(id)
|
local function refresh_model(id)
|
||||||
itemid = id
|
itemid = id
|
||||||
@ -18,10 +17,11 @@ function on_render()
|
|||||||
if pid == -1 then
|
if pid == -1 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local rx, ry, rz = player.get_rot(pid, pid ~= hud.get_player())
|
local rx, _, _ = player.get_rot(pid, pid ~= hud.get_player())
|
||||||
rig:set_matrix(headIndex, mat4.rotate({1, 0, 0}, ry))
|
|
||||||
rig:set_matrix(bodyIndex, mat4.rotate({0, 1, 0}, rx))
|
local dir = vec2.rotate({0, -1}, -rx)
|
||||||
|
mob.set_dir({dir[1], 0, dir[2]})
|
||||||
|
|
||||||
local invid, slotid = player.get_inventory(pid)
|
local invid, slotid = player.get_inventory(pid)
|
||||||
local id, _ = inventory.get(invid, slotid)
|
local id, _ = inventory.get(invid, slotid)
|
||||||
|
|||||||
@ -110,6 +110,21 @@ function vec3.dot(a, b)
|
|||||||
return a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
|
return a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function vec3.mix(a, b, t, dest)
|
||||||
|
if dest then
|
||||||
|
dest[1] = a[1] * (1.0 - t) + b[1] * t
|
||||||
|
dest[2] = a[2] * (1.0 - t) + b[2] * t
|
||||||
|
dest[3] = a[3] * (1.0 - t) + b[3] * t
|
||||||
|
return dest
|
||||||
|
else
|
||||||
|
return {
|
||||||
|
a[1] * (1.0 - t) + b[1] * t,
|
||||||
|
a[2] * (1.0 - t) + b[2] * t,
|
||||||
|
a[3] * (1.0 - t) + b[3] * t,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- =================================================== --
|
-- =================================================== --
|
||||||
-- ====================== vec2 ======================= --
|
-- ====================== vec2 ======================= --
|
||||||
-- =================================================== --
|
-- =================================================== --
|
||||||
@ -210,3 +225,16 @@ end
|
|||||||
function vec2.dot(a, b)
|
function vec2.dot(a, b)
|
||||||
return a[1] * b[1] + a[2] * b[2]
|
return a[1] * b[1] + a[2] * b[2]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function vec2.mix(a, b, t, dest)
|
||||||
|
if dest then
|
||||||
|
dest[1] = a[1] * (1.0 - t) + b[1] * t
|
||||||
|
dest[2] = a[2] * (1.0 - t) + b[2] * t
|
||||||
|
return dest
|
||||||
|
else
|
||||||
|
return {
|
||||||
|
a[1] * (1.0 - t) + b[1] * t,
|
||||||
|
a[2] * (1.0 - t) + b[2] * t,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
@ -25,6 +25,7 @@ local Rigidbody = {__index={
|
|||||||
get_linear_damping=function(self) return __rigidbody.get_linear_damping(self.eid) end,
|
get_linear_damping=function(self) return __rigidbody.get_linear_damping(self.eid) end,
|
||||||
set_linear_damping=function(self, f) return __rigidbody.set_linear_damping(self.eid, f) end,
|
set_linear_damping=function(self, f) return __rigidbody.set_linear_damping(self.eid, f) end,
|
||||||
is_vdamping=function(self) return __rigidbody.is_vdamping(self.eid) end,
|
is_vdamping=function(self) return __rigidbody.is_vdamping(self.eid) end,
|
||||||
|
get_vdamping=function(self) return __rigidbody.get_vdamping(self.eid) end,
|
||||||
set_vdamping=function(self, b) return __rigidbody.set_vdamping(self.eid, b) end,
|
set_vdamping=function(self, b) return __rigidbody.set_vdamping(self.eid, b) end,
|
||||||
is_grounded=function(self) return __rigidbody.is_grounded(self.eid) end,
|
is_grounded=function(self) return __rigidbody.is_grounded(self.eid) end,
|
||||||
is_crouching=function(self) return __rigidbody.is_crouching(self.eid) end,
|
is_crouching=function(self) return __rigidbody.is_crouching(self.eid) end,
|
||||||
@ -63,6 +64,13 @@ local Entity = {__index={
|
|||||||
get_skeleton=function(self) return entities.get_skeleton(self.eid) end,
|
get_skeleton=function(self) return entities.get_skeleton(self.eid) end,
|
||||||
set_skeleton=function(self, s) return entities.set_skeleton(self.eid, s) end,
|
set_skeleton=function(self, s) return entities.set_skeleton(self.eid, s) end,
|
||||||
get_component=function(self, name) return self.components[name] end,
|
get_component=function(self, name) return self.components[name] end,
|
||||||
|
require_component=function(self, name)
|
||||||
|
local component = self.components[name]
|
||||||
|
if not component then
|
||||||
|
error(("entity has no required component '%s'"):format(name))
|
||||||
|
end
|
||||||
|
return component
|
||||||
|
end,
|
||||||
has_component=function(self, name) return self.components[name] ~= nil end,
|
has_component=function(self, name) return self.components[name] ~= nil end,
|
||||||
get_uid=function(self) return self.eid end,
|
get_uid=function(self) return self.eid end,
|
||||||
def_index=function(self) return entities.get_def(self.eid) end,
|
def_index=function(self) return entities.get_def(self.eid) end,
|
||||||
@ -125,6 +133,19 @@ return {
|
|||||||
::continue::
|
::continue::
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
physics_update = function(delta)
|
||||||
|
for uid, entity in pairs(entities) do
|
||||||
|
for _, component in pairs(entity.components) do
|
||||||
|
local callback = component.on_physics_update
|
||||||
|
if not component.__disabled and callback then
|
||||||
|
local result, err = pcall(callback, delta)
|
||||||
|
if err then
|
||||||
|
debug.error(err)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
render = function(delta)
|
render = function(delta)
|
||||||
for _,entity in pairs(entities) do
|
for _,entity in pairs(entities) do
|
||||||
for _, component in pairs(entity.components) do
|
for _, component in pairs(entity.components) do
|
||||||
|
|||||||
@ -11,6 +11,9 @@ local Schedule = {
|
|||||||
self._next_interval = id + 1
|
self._next_interval = id + 1
|
||||||
return id
|
return id
|
||||||
end,
|
end,
|
||||||
|
set_timeout = function(self, ms, callback)
|
||||||
|
self:set_interval(ms, callback, 1)
|
||||||
|
end,
|
||||||
tick = function(self, dt)
|
tick = function(self, dt)
|
||||||
local timer = self._timer + dt
|
local timer = self._timer + dt
|
||||||
for id, interval in pairs(self._intervals) do
|
for id, interval in pairs(self._intervals) do
|
||||||
|
|||||||
177
res/scripts/components/mob.lua
Normal file
177
res/scripts/components/mob.lua
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
local body = entity.rigidbody
|
||||||
|
local tsf = entity.transform
|
||||||
|
local rig = entity.skeleton
|
||||||
|
|
||||||
|
local props = {}
|
||||||
|
|
||||||
|
local function def_prop(name, def_value)
|
||||||
|
props[name] = SAVED_DATA[name] or ARGS[name] or def_value
|
||||||
|
this["get_"..name] = function() return props[name] end
|
||||||
|
this["set_"..name] = function(value)
|
||||||
|
props[name] = value
|
||||||
|
if math.abs(value - def_value) < 1e-7 then
|
||||||
|
SAVED_DATA[name] = nil
|
||||||
|
else
|
||||||
|
SAVED_DATA[name] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def_prop("jump_force", 0.0)
|
||||||
|
def_prop("air_damping", 1.0)
|
||||||
|
def_prop("ground_damping", 1.0)
|
||||||
|
def_prop("movement_speed", 3.0)
|
||||||
|
def_prop("run_speed_mul", 1.5)
|
||||||
|
def_prop("crouch_speed_mul", 0.35)
|
||||||
|
def_prop("flight_speed_mul", 4.0)
|
||||||
|
def_prop("gravity_scale", 1.0)
|
||||||
|
|
||||||
|
local function normalize_angle(angle)
|
||||||
|
while angle > 180 do
|
||||||
|
angle = angle - 360
|
||||||
|
end
|
||||||
|
while angle <= -180 do
|
||||||
|
angle = angle + 360
|
||||||
|
end
|
||||||
|
return angle
|
||||||
|
end
|
||||||
|
|
||||||
|
local function angle_delta(a, b)
|
||||||
|
return normalize_angle(a - b)
|
||||||
|
end
|
||||||
|
|
||||||
|
local dir = mat4.mul(tsf:get_rot(), {0, 0, -1})
|
||||||
|
local flight = false
|
||||||
|
|
||||||
|
function jump(multiplier)
|
||||||
|
local vel = body:get_vel()
|
||||||
|
body:set_vel(
|
||||||
|
vec3.add(vel, {0, props.jump_force * (multiplier or 1.0), 0}, vel))
|
||||||
|
end
|
||||||
|
|
||||||
|
function move_vertical(speed, vel)
|
||||||
|
vel = vel or body:get_vel()
|
||||||
|
vel[2] = vel[2] * 0.2 + props.movement_speed * speed * 0.8
|
||||||
|
body:set_vel(vel)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function move_horizontal(speed, dir, vel)
|
||||||
|
vel = vel or body:get_vel()
|
||||||
|
if vec2.length(dir) > 0.0 then
|
||||||
|
vec2.normalize(dir, dir)
|
||||||
|
|
||||||
|
local magnitude = vec2.length({vel[1], vel[3]})
|
||||||
|
|
||||||
|
if magnitude <= 1e-4 or (magnitude < speed or vec2.dot(
|
||||||
|
{vel[1] / magnitude, vel[3] / magnitude}, dir) < 0.9)
|
||||||
|
then
|
||||||
|
vel[1] = vel[1] * 0.2 + dir[1] * speed * 0.8
|
||||||
|
vel[3] = vel[3] * 0.2 + dir[2] * speed * 0.8
|
||||||
|
end
|
||||||
|
magnitude = vec3.length({vel[1], 0, vel[3]})
|
||||||
|
if vec2.dot({vel[1] / magnitude, vel[3] / magnitude}, dir) > 0.5 then
|
||||||
|
vel[1] = vel[1] / magnitude * speed
|
||||||
|
vel[3] = vel[3] / magnitude * speed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
body:set_vel(vel)
|
||||||
|
end
|
||||||
|
|
||||||
|
function go(dir, speed_multiplier, sprint, crouch, vel)
|
||||||
|
local speed = props.movement_speed * speed_multiplier
|
||||||
|
if flight then
|
||||||
|
speed = speed * props.flight_speed_mul
|
||||||
|
end
|
||||||
|
if sprint then
|
||||||
|
speed = speed * props.run_speed_mul
|
||||||
|
elseif crouch then
|
||||||
|
speed = speed * props.crouch_speed_mul
|
||||||
|
end
|
||||||
|
move_horizontal(speed, dir, vel)
|
||||||
|
end
|
||||||
|
|
||||||
|
local headIndex = rig:index("head")
|
||||||
|
|
||||||
|
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)
|
||||||
|
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.2
|
||||||
|
dir[3] = dir[3] * 0.8 + viewdir[3] * 0.2
|
||||||
|
end
|
||||||
|
|
||||||
|
if not headIndex then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local headrot = mat4.idt()
|
||||||
|
local curdir = mat4.mul(mat4.mul(tsf:get_rot(),
|
||||||
|
rig:get_matrix(headIndex)), {0, 0, -1})
|
||||||
|
|
||||||
|
vec3.mix(curdir, viewdir, 0.2, viewdir)
|
||||||
|
|
||||||
|
headrot = mat4.inverse(mat4.look_at({0,0,0}, viewdir, {0, 1, 0}))
|
||||||
|
headrot = mat4.mul(mat4.inverse(tsf:get_rot()), headrot)
|
||||||
|
rig:set_matrix(headIndex, headrot)
|
||||||
|
end
|
||||||
|
|
||||||
|
function follow_waypoints(pathfinding)
|
||||||
|
pathfinding = pathfinding or entity:require_component("core:pathfinding")
|
||||||
|
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(
|
||||||
|
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)
|
||||||
|
if upper and body:is_grounded() then
|
||||||
|
jump(1.0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function set_dir(new_dir)
|
||||||
|
dir = new_dir
|
||||||
|
end
|
||||||
|
|
||||||
|
function is_flight() return flight end
|
||||||
|
|
||||||
|
function set_flight(flag) flight = flag end
|
||||||
|
|
||||||
|
local prev_angle = (vec2.angle({dir[3], dir[1]})) % 360
|
||||||
|
|
||||||
|
function on_physics_update(delta)
|
||||||
|
local grounded = body:is_grounded()
|
||||||
|
body:set_vdamping(flight)
|
||||||
|
body:set_gravity_scale({0, flight and 0.0 or props.gravity_scale, 0})
|
||||||
|
body:set_linear_damping(
|
||||||
|
(flight or not grounded) and props.air_damping or props.ground_damping
|
||||||
|
)
|
||||||
|
|
||||||
|
local new_angle = (vec2.angle({dir[3], dir[1]})) % 360
|
||||||
|
local angle = prev_angle
|
||||||
|
|
||||||
|
local adelta = angle_delta(
|
||||||
|
normalize_angle(new_angle),
|
||||||
|
normalize_angle(prev_angle)
|
||||||
|
)
|
||||||
|
local rotate_speed = entity:get_player() == -1 and 200 or 400
|
||||||
|
|
||||||
|
if math.abs(adelta) > 5 then
|
||||||
|
angle = angle + delta * rotate_speed * (adelta > 0 and 1 or -1)
|
||||||
|
end
|
||||||
|
|
||||||
|
tsf:set_rot(mat4.rotate({0, 1, 0}, angle + 180))
|
||||||
|
prev_angle = angle
|
||||||
|
end
|
||||||
71
res/scripts/components/pathfinding.lua
Normal file
71
res/scripts/components/pathfinding.lua
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
local target
|
||||||
|
local route
|
||||||
|
local started
|
||||||
|
|
||||||
|
local tsf = entity.transform
|
||||||
|
local body = entity.rigidbody
|
||||||
|
|
||||||
|
agent = pathfinding.create_agent()
|
||||||
|
pathfinding.set_max_visited(agent, 1e3)
|
||||||
|
pathfinding.avoid_tag(agent, "core:liquid", 8)
|
||||||
|
|
||||||
|
function set_target(new_target)
|
||||||
|
target = new_target
|
||||||
|
end
|
||||||
|
|
||||||
|
function set_jump_height(height)
|
||||||
|
pathfinding.set_jump_height(agent, height)
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_target()
|
||||||
|
return target
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_route()
|
||||||
|
return route
|
||||||
|
end
|
||||||
|
|
||||||
|
function next_waypoint()
|
||||||
|
if not route or #route == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local waypoint = route[#route]
|
||||||
|
local pos = tsf:get_pos()
|
||||||
|
local dst = vec2.length({
|
||||||
|
math.floor(waypoint[1] - math.floor(pos[1])),
|
||||||
|
math.floor(waypoint[3] - math.floor(pos[3]))
|
||||||
|
})
|
||||||
|
if dst < 1.0 then
|
||||||
|
table.remove(route, #route)
|
||||||
|
end
|
||||||
|
return route[#route]
|
||||||
|
end
|
||||||
|
|
||||||
|
local refresh_internal = 100
|
||||||
|
local frameid = math.random(0, refresh_internal)
|
||||||
|
|
||||||
|
function set_refresh_interval(interval)
|
||||||
|
refresh_internal = interval
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_update()
|
||||||
|
if not started then
|
||||||
|
frameid = frameid + 1
|
||||||
|
if body:is_grounded() then
|
||||||
|
if target and (frameid % refresh_internal == 1 or not route) then
|
||||||
|
pathfinding.make_route_async(agent, tsf:get_pos(), target)
|
||||||
|
started = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local new_route = pathfinding.pull_route(agent)
|
||||||
|
if new_route then
|
||||||
|
route = new_route
|
||||||
|
started = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_despawn()
|
||||||
|
pathfinding.remove_agent(agent)
|
||||||
|
end
|
||||||
62
res/scripts/components/player.lua
Normal file
62
res/scripts/components/player.lua
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
local tsf = entity.transform
|
||||||
|
local body = entity.rigidbody
|
||||||
|
local mob = entity:require_component("core:mob")
|
||||||
|
|
||||||
|
local cheat_speed_mul = 5.0
|
||||||
|
|
||||||
|
local function process_player_inputs(pid, delta)
|
||||||
|
if not hud or hud.is_inventory_open() or menu.page ~= "" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local cam = cameras.get("core:first-person")
|
||||||
|
local front = cam:get_front()
|
||||||
|
local right = cam:get_right()
|
||||||
|
front[2] = 0.0
|
||||||
|
vec3.normalize(front, front)
|
||||||
|
|
||||||
|
local isjump = input.is_active('movement.jump')
|
||||||
|
local issprint = input.is_active('movement.sprint')
|
||||||
|
local iscrouch = input.is_active('movement.crouch')
|
||||||
|
local isforward = input.is_active('movement.forward')
|
||||||
|
local ischeat = input.is_active('movement.cheat')
|
||||||
|
local isback = input.is_active('movement.back')
|
||||||
|
local isleft = input.is_active('movement.left')
|
||||||
|
local isright = input.is_active('movement.right')
|
||||||
|
mob.set_flight(player.is_flight(pid))
|
||||||
|
body:set_body_type(player.is_noclip(pid) and "kinematic" or "dynamic")
|
||||||
|
body:set_crouching(iscrouch)
|
||||||
|
|
||||||
|
local vel = body:get_vel()
|
||||||
|
local speed = ischeat and cheat_speed_mul or 1.0
|
||||||
|
|
||||||
|
local dir = {0, 0, 0}
|
||||||
|
|
||||||
|
if isforward then vec3.add(dir, front, dir) end
|
||||||
|
if isback then vec3.sub(dir, front, dir) end
|
||||||
|
if isright then vec3.add(dir, right, dir) end
|
||||||
|
if isleft then vec3.sub(dir, right, dir) end
|
||||||
|
|
||||||
|
if vec3.length(dir) > 0.0 then
|
||||||
|
mob.go({dir[1], dir[3]}, speed, issprint, iscrouch, vel)
|
||||||
|
end
|
||||||
|
|
||||||
|
if mob.is_flight() then
|
||||||
|
if isjump then
|
||||||
|
mob.move_vertical(speed * 4)
|
||||||
|
elseif iscrouch then
|
||||||
|
mob.move_vertical(-speed * 4)
|
||||||
|
end
|
||||||
|
elseif body:is_grounded() and isjump then
|
||||||
|
mob.jump()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_physics_update(delta)
|
||||||
|
local pid = entity:get_player()
|
||||||
|
if pid ~= -1 then
|
||||||
|
local pos = tsf:get_pos()
|
||||||
|
local cam = cameras.get("core:first-person")
|
||||||
|
process_player_inputs(pid, delta)
|
||||||
|
mob.look_at(vec3.add(pos, cam:get_front()))
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -22,6 +22,39 @@ local function configure_SSAO()
|
|||||||
-- for test purposes
|
-- for test purposes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function update_hand()
|
||||||
|
local skeleton = gfx.skeletons
|
||||||
|
local pid = hud.get_player()
|
||||||
|
local invid, slot = player.get_inventory(pid)
|
||||||
|
local itemid = inventory.get(invid, slot)
|
||||||
|
|
||||||
|
local cam = cameras.get("core:first-person")
|
||||||
|
local bone = skeleton.index("hand", "item")
|
||||||
|
|
||||||
|
local offset = vec3.mul(vec3.sub(cam:get_pos(), {player.get_pos(pid)}), -1)
|
||||||
|
|
||||||
|
local rotation = cam:get_rot()
|
||||||
|
|
||||||
|
local angle = player.get_rot(pid) - 90
|
||||||
|
local cos = math.cos(angle / (180 / math.pi))
|
||||||
|
local sin = math.sin(angle / (180 / math.pi))
|
||||||
|
|
||||||
|
local newX = offset[1] * cos - offset[3] * sin
|
||||||
|
local newZ = offset[1] * sin + offset[3] * cos
|
||||||
|
|
||||||
|
offset[1] = newX
|
||||||
|
offset[3] = newZ
|
||||||
|
|
||||||
|
local mat = mat4.translate(mat4.idt(), {0.06, 0.035, -0.1})
|
||||||
|
mat4.scale(mat, {0.1, 0.1, 0.1}, mat)
|
||||||
|
mat4.mul(rotation, mat, mat)
|
||||||
|
mat4.rotate(mat, {0, 1, 0}, -90, mat)
|
||||||
|
mat4.translate(mat, offset, mat)
|
||||||
|
|
||||||
|
skeleton.set_matrix("hand", bone, mat)
|
||||||
|
skeleton.set_model("hand", bone, item.model_name(itemid))
|
||||||
|
end
|
||||||
|
|
||||||
function on_hud_open()
|
function on_hud_open()
|
||||||
input.add_callback("player.pick", function ()
|
input.add_callback("player.pick", function ()
|
||||||
if hud.is_paused() or hud.is_inventory_open() then
|
if hud.is_paused() or hud.is_inventory_open() then
|
||||||
@ -63,7 +96,7 @@ function on_hud_open()
|
|||||||
player.set_noclip(pid, true)
|
player.set_noclip(pid, true)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
input.add_callback("player.flight", function ()
|
input.add_callback("player.flight", function ()
|
||||||
if hud.is_paused() or hud.is_inventory_open() then
|
if hud.is_paused() or hud.is_inventory_open() then
|
||||||
return
|
return
|
||||||
@ -81,39 +114,8 @@ function on_hud_open()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
configure_SSAO()
|
configure_SSAO()
|
||||||
end
|
|
||||||
|
|
||||||
local function update_hand()
|
hud.default_hand_controller = update_hand
|
||||||
local skeleton = gfx.skeletons
|
|
||||||
local pid = hud.get_player()
|
|
||||||
local invid, slot = player.get_inventory(pid)
|
|
||||||
local itemid = inventory.get(invid, slot)
|
|
||||||
|
|
||||||
local cam = cameras.get("core:first-person")
|
|
||||||
local bone = skeleton.index("hand", "item")
|
|
||||||
|
|
||||||
local offset = vec3.mul(vec3.sub(cam:get_pos(), {player.get_pos(pid)}), -1)
|
|
||||||
|
|
||||||
local rotation = cam:get_rot()
|
|
||||||
|
|
||||||
local angle = player.get_rot() - 90
|
|
||||||
local cos = math.cos(angle / (180 / math.pi))
|
|
||||||
local sin = math.sin(angle / (180 / math.pi))
|
|
||||||
|
|
||||||
local newX = offset[1] * cos - offset[3] * sin
|
|
||||||
local newZ = offset[1] * sin + offset[3] * cos
|
|
||||||
|
|
||||||
offset[1] = newX
|
|
||||||
offset[3] = newZ
|
|
||||||
|
|
||||||
local mat = mat4.translate(mat4.idt(), {0.06, 0.035, -0.1})
|
|
||||||
mat4.scale(mat, {0.1, 0.1, 0.1}, mat)
|
|
||||||
mat4.mul(rotation, mat, mat)
|
|
||||||
mat4.rotate(mat, {0, 1, 0}, -90, mat)
|
|
||||||
mat4.translate(mat, offset, mat)
|
|
||||||
|
|
||||||
skeleton.set_matrix("hand", bone, mat)
|
|
||||||
skeleton.set_model("hand", bone, item.model_name(itemid))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_hud_render()
|
function on_hud_render()
|
||||||
|
|||||||
@ -157,6 +157,16 @@ console.add_command(
|
|||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
console.add_command(
|
||||||
|
"entity.spawn name:str x:num~pos.x y:num~pos.y z:num~pos.z",
|
||||||
|
"Spawn entity with default parameters",
|
||||||
|
function(args, kwargs)
|
||||||
|
local eid = entities.spawn(args[1], {args[2], args[3], args[4]})
|
||||||
|
return string.format("spawned %s at %s, %s, %s", unpack(args))
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
console.add_command(
|
console.add_command(
|
||||||
"entity.despawn entity:sel=$entity.selected",
|
"entity.despawn entity:sel=$entity.selected",
|
||||||
"Despawn entity",
|
"Despawn entity",
|
||||||
|
|||||||
@ -429,6 +429,8 @@ function __vc_on_hud_open()
|
|||||||
hud.open_permanent("core:ingame_chat")
|
hud.open_permanent("core:ingame_chat")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local Schedule = require "core:schedule"
|
||||||
|
|
||||||
local ScheduleGroup_mt = {
|
local ScheduleGroup_mt = {
|
||||||
__index = {
|
__index = {
|
||||||
publish = function(self, schedule)
|
publish = function(self, schedule)
|
||||||
@ -440,10 +442,11 @@ local ScheduleGroup_mt = {
|
|||||||
for id, schedule in pairs(self._schedules) do
|
for id, schedule in pairs(self._schedules) do
|
||||||
schedule:tick(dt)
|
schedule:tick(dt)
|
||||||
end
|
end
|
||||||
|
self.common:tick(dt)
|
||||||
end,
|
end,
|
||||||
remove = function(self, id)
|
remove = function(self, id)
|
||||||
self._schedules[id] = nil
|
self._schedules[id] = nil
|
||||||
end
|
end,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,6 +454,7 @@ local function ScheduleGroup()
|
|||||||
return setmetatable({
|
return setmetatable({
|
||||||
_next_schedule = 1,
|
_next_schedule = 1,
|
||||||
_schedules = {},
|
_schedules = {},
|
||||||
|
common = Schedule()
|
||||||
}, ScheduleGroup_mt)
|
}, ScheduleGroup_mt)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -71,8 +71,8 @@ static auto process_program(const ResPaths& paths, const std::string& filename)
|
|||||||
|
|
||||||
auto& preprocessor = *Shader::preprocessor;
|
auto& preprocessor = *Shader::preprocessor;
|
||||||
|
|
||||||
auto vertex = preprocessor.process(vertexFile, vertexSource);
|
auto vertex = preprocessor.process(vertexFile, vertexSource, false, {});
|
||||||
auto fragment = preprocessor.process(fragmentFile, fragmentSource);
|
auto fragment = preprocessor.process(fragmentFile, fragmentSource, false, {});
|
||||||
return std::make_pair(vertex, fragment);
|
return std::make_pair(vertex, fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ assetload::postfunc assetload::posteffect(
|
|||||||
|
|
||||||
auto& preprocessor = *Shader::preprocessor;
|
auto& preprocessor = *Shader::preprocessor;
|
||||||
preprocessor.addHeader(
|
preprocessor.addHeader(
|
||||||
"__effect__", preprocessor.process(effectFile, effectSource, true)
|
"__effect__", preprocessor.process(effectFile, effectSource, true, {})
|
||||||
);
|
);
|
||||||
|
|
||||||
auto [vertex, fragment] = process_program(paths, SHADERS_FOLDER + "/effect");
|
auto [vertex, fragment] = process_program(paths, SHADERS_FOLDER + "/effect");
|
||||||
|
|||||||
@ -22,6 +22,10 @@ void GLSLExtension::setPaths(const ResPaths* paths) {
|
|||||||
this->paths = paths;
|
this->paths = paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLSLExtension::setTraceOutput(bool enabled) {
|
||||||
|
this->traceOutput = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
void GLSLExtension::loadHeader(const std::string& name) {
|
void GLSLExtension::loadHeader(const std::string& name) {
|
||||||
if (paths == nullptr) {
|
if (paths == nullptr) {
|
||||||
return;
|
return;
|
||||||
@ -29,7 +33,7 @@ void GLSLExtension::loadHeader(const std::string& name) {
|
|||||||
io::path file = paths->find("shaders/lib/" + name + ".glsl");
|
io::path file = paths->find("shaders/lib/" + name + ".glsl");
|
||||||
std::string source = io::read_string(file);
|
std::string source = io::read_string(file);
|
||||||
addHeader(name, {});
|
addHeader(name, {});
|
||||||
addHeader(name, process(file, source, true));
|
addHeader(name, process(file, source, true, {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLSLExtension::addHeader(const std::string& name, ProcessingResult header) {
|
void GLSLExtension::addHeader(const std::string& name, ProcessingResult header) {
|
||||||
@ -123,13 +127,22 @@ static Value default_value_for(Type type) {
|
|||||||
|
|
||||||
class GLSLParser : public BasicParser<char> {
|
class GLSLParser : public BasicParser<char> {
|
||||||
public:
|
public:
|
||||||
GLSLParser(GLSLExtension& glsl, std::string_view file, std::string_view source, bool header)
|
GLSLParser(
|
||||||
|
GLSLExtension& glsl,
|
||||||
|
std::string_view file,
|
||||||
|
std::string_view source,
|
||||||
|
bool header,
|
||||||
|
const std::vector<std::string>& defines
|
||||||
|
)
|
||||||
: BasicParser(file, source), glsl(glsl) {
|
: BasicParser(file, source), glsl(glsl) {
|
||||||
if (!header) {
|
if (!header) {
|
||||||
ss << "#version " << GLSLExtension::VERSION << '\n';
|
ss << "#version " << GLSLExtension::VERSION << '\n';
|
||||||
}
|
for (auto& entry : defines) {
|
||||||
for (auto& entry : glsl.getDefines()) {
|
ss << "#define " << entry << '\n';
|
||||||
ss << "#define " << entry.first << " " << entry.second << '\n';
|
}
|
||||||
|
for (auto& entry : defines) {
|
||||||
|
ss << "#define " << entry << '\n';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
uint linenum = 1;
|
uint linenum = 1;
|
||||||
source_line(ss, linenum);
|
source_line(ss, linenum);
|
||||||
@ -289,10 +302,34 @@ private:
|
|||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void trace_output(
|
||||||
|
const io::path& file,
|
||||||
|
const std::string& source,
|
||||||
|
const GLSLExtension::ProcessingResult& result
|
||||||
|
) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "export:trace/" << file.name();
|
||||||
|
io::path outfile = ss.str();
|
||||||
|
try {
|
||||||
|
io::create_directories(outfile.parent());
|
||||||
|
io::write_string(outfile, result.code);
|
||||||
|
} catch (const std::runtime_error& err) {
|
||||||
|
logger.error() << "error on saving GLSLExtension::preprocess output ("
|
||||||
|
<< outfile.string() << "): " << err.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GLSLExtension::ProcessingResult GLSLExtension::process(
|
GLSLExtension::ProcessingResult GLSLExtension::process(
|
||||||
const io::path& file, const std::string& source, bool header
|
const io::path& file,
|
||||||
|
const std::string& source,
|
||||||
|
bool header,
|
||||||
|
const std::vector<std::string>& defines
|
||||||
) {
|
) {
|
||||||
std::string filename = file.string();
|
std::string filename = file.string();
|
||||||
GLSLParser parser(*this, filename, source, header);
|
GLSLParser parser(*this, filename, source, header, defines);
|
||||||
return parser.process();
|
auto result = parser.process();
|
||||||
|
if (traceOutput) {
|
||||||
|
trace_output(file, source, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "io/io.hpp"
|
#include "io/io.hpp"
|
||||||
|
#include "data/setting.hpp"
|
||||||
#include "graphics/core/PostEffect.hpp"
|
#include "graphics/core/PostEffect.hpp"
|
||||||
|
|
||||||
class ResPaths;
|
class ResPaths;
|
||||||
@ -19,6 +20,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
void setPaths(const ResPaths* paths);
|
void setPaths(const ResPaths* paths);
|
||||||
|
void setTraceOutput(bool enabled);
|
||||||
|
|
||||||
void define(const std::string& name, std::string value);
|
void define(const std::string& name, std::string value);
|
||||||
void undefine(const std::string& name);
|
void undefine(const std::string& name);
|
||||||
@ -37,7 +39,8 @@ public:
|
|||||||
ProcessingResult process(
|
ProcessingResult process(
|
||||||
const io::path& file,
|
const io::path& file,
|
||||||
const std::string& source,
|
const std::string& source,
|
||||||
bool header = false
|
bool header,
|
||||||
|
const std::vector<std::string>& defines
|
||||||
);
|
);
|
||||||
|
|
||||||
static inline std::string VERSION = "330 core";
|
static inline std::string VERSION = "330 core";
|
||||||
@ -46,4 +49,5 @@ private:
|
|||||||
std::unordered_map<std::string, std::string> defines;
|
std::unordered_map<std::string, std::string> defines;
|
||||||
|
|
||||||
const ResPaths* paths = nullptr;
|
const ResPaths* paths = nullptr;
|
||||||
|
bool traceOutput = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -30,7 +30,18 @@ template<> void ContentUnitLoader<EntityDef>::loadUnit(
|
|||||||
|
|
||||||
if (auto found = root.at("components")) {
|
if (auto found = root.at("components")) {
|
||||||
for (const auto& elem : *found) {
|
for (const auto& elem : *found) {
|
||||||
def.components.emplace_back(elem.asString());
|
std::string name;
|
||||||
|
dv::value params;
|
||||||
|
if (elem.isObject()) {
|
||||||
|
name = elem["name"].asString();
|
||||||
|
if (elem.has("args")) {
|
||||||
|
params = elem["args"];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
name = elem.asString();
|
||||||
|
}
|
||||||
|
def.components.push_back(ComponentInstance {
|
||||||
|
std::move(name), std::move(params)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (auto found = root.at("hitbox")) {
|
if (auto found = root.at("hitbox")) {
|
||||||
|
|||||||
@ -46,12 +46,12 @@ namespace dv {
|
|||||||
if (!map.has(key)) {
|
if (!map.has(key)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto& list = map[key];
|
const auto& srcList = map[key];
|
||||||
for (size_t i = 0; i < n; i++) {
|
for (size_t i = 0; i < n; i++) {
|
||||||
if constexpr (std::is_floating_point<T>()) {
|
if constexpr (std::is_floating_point<T>()) {
|
||||||
vec[i] = list[i].asNumber();
|
vec[i] = srcList[i].asNumber();
|
||||||
} else {
|
} else {
|
||||||
vec[i] = list[i].asInteger();
|
vec[i] = srcList[i].asInteger();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -143,6 +143,12 @@ void Engine::initializeClient() {
|
|||||||
},
|
},
|
||||||
true
|
true
|
||||||
));
|
));
|
||||||
|
keepAlive(settings.debug.doTraceShaders.observe(
|
||||||
|
[](bool value) {
|
||||||
|
Shader::preprocessor->setTraceOutput(value);
|
||||||
|
},
|
||||||
|
true
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::initialize(CoreParameters coreParameters) {
|
void Engine::initialize(CoreParameters coreParameters) {
|
||||||
|
|||||||
@ -12,12 +12,14 @@
|
|||||||
#include "graphics/render/WorldRenderer.hpp"
|
#include "graphics/render/WorldRenderer.hpp"
|
||||||
#include "graphics/render/ParticlesRenderer.hpp"
|
#include "graphics/render/ParticlesRenderer.hpp"
|
||||||
#include "graphics/render/ChunksRenderer.hpp"
|
#include "graphics/render/ChunksRenderer.hpp"
|
||||||
|
#include "graphics/render/DebugLinesRenderer.hpp"
|
||||||
#include "logic/scripting/scripting.hpp"
|
#include "logic/scripting/scripting.hpp"
|
||||||
#include "network/Network.hpp"
|
#include "network/Network.hpp"
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
#include "objects/Players.hpp"
|
#include "objects/Players.hpp"
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
#include "objects/EntityDef.hpp"
|
#include "objects/EntityDef.hpp"
|
||||||
|
#include "objects/Entity.hpp"
|
||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
@ -44,6 +46,7 @@ static std::shared_ptr<Label> create_label(GUI& gui, wstringsupplier supplier) {
|
|||||||
// TODO: move to xml
|
// TODO: move to xml
|
||||||
// TODO: move to xml finally
|
// TODO: move to xml finally
|
||||||
// TODO: move to xml finally
|
// TODO: move to xml finally
|
||||||
|
// TODO: move to xml finally
|
||||||
std::shared_ptr<UINode> create_debug_panel(
|
std::shared_ptr<UINode> create_debug_panel(
|
||||||
Engine& engine,
|
Engine& engine,
|
||||||
Level& level,
|
Level& level,
|
||||||
@ -260,6 +263,18 @@ std::shared_ptr<UINode> create_debug_panel(
|
|||||||
});
|
});
|
||||||
panel->add(checkbox);
|
panel->add(checkbox);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
auto checkbox = std::make_shared<FullCheckBox>(
|
||||||
|
gui, L"Show Paths", glm::vec2(400, 24)
|
||||||
|
);
|
||||||
|
checkbox->setSupplier([=]() {
|
||||||
|
return DebugLinesRenderer::showPaths;
|
||||||
|
});
|
||||||
|
checkbox->setConsumer([=](bool checked) {
|
||||||
|
DebugLinesRenderer::showPaths = checked;
|
||||||
|
});
|
||||||
|
panel->add(checkbox);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
auto checkbox = std::make_shared<FullCheckBox>(
|
auto checkbox = std::make_shared<FullCheckBox>(
|
||||||
gui, L"Show Generator Minimap", glm::vec2(400, 24)
|
gui, L"Show Generator Minimap", glm::vec2(400, 24)
|
||||||
|
|||||||
@ -176,7 +176,7 @@ void LevelScreen::saveWorldPreview() {
|
|||||||
static_cast<uint>(previewSize)}
|
static_cast<uint>(previewSize)}
|
||||||
);
|
);
|
||||||
|
|
||||||
renderer->draw(ctx, camera, false, true, 0.0f, *postProcessing);
|
renderer->renderFrame(ctx, camera, false, true, 0.0f, *postProcessing);
|
||||||
auto image = postProcessing->toImage();
|
auto image = postProcessing->toImage();
|
||||||
image->flipY();
|
image->flipY();
|
||||||
imageio::write("world:preview.png", image.get());
|
imageio::write("world:preview.png", image.get());
|
||||||
@ -263,7 +263,7 @@ void LevelScreen::draw(float delta) {
|
|||||||
if (!hud->isPause()) {
|
if (!hud->isPause()) {
|
||||||
scripting::on_entities_render(engine.getTime().getDelta());
|
scripting::on_entities_render(engine.getTime().getDelta());
|
||||||
}
|
}
|
||||||
renderer->draw(
|
renderer->renderFrame(
|
||||||
ctx, *camera, hudVisible, hud->isPause(), delta, *postProcessing
|
ctx, *camera, hudVisible, hud->isPause(), delta, *postProcessing
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#include "ImageData.hpp"
|
#include "ImageData.hpp"
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@ -187,6 +188,41 @@ void ImageData::drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<uint channels>
|
||||||
|
static void draw_rect(ImageData& image, int dstX, int dstY, int width, int height, const glm::ivec4& color) {
|
||||||
|
ubyte* data = image.getData();
|
||||||
|
int imageWidth = image.getWidth();
|
||||||
|
int imageHeight = image.getHeight();
|
||||||
|
|
||||||
|
int x1 = glm::min(glm::max(dstX, 0), imageWidth - 1);
|
||||||
|
int y1 = glm::min(glm::max(dstY, 0), imageHeight - 1);
|
||||||
|
|
||||||
|
int x2 = glm::min(glm::max(dstX + width, 0), imageWidth - 1);
|
||||||
|
int y2 = glm::min(glm::max(dstY + height, 0), imageHeight - 1);
|
||||||
|
|
||||||
|
for (int y = y1; y <= y2; y++) {
|
||||||
|
for (int x = x1; x <= x2; x++) {
|
||||||
|
int index = (y * imageWidth + x) * channels;
|
||||||
|
for (int i = 0; i < channels; i++) {
|
||||||
|
data[index + i] = color[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageData::drawRect(int x, int y, int width, int height, const glm::ivec4& color) {
|
||||||
|
switch (format) {
|
||||||
|
case ImageFormat::rgb888:
|
||||||
|
draw_rect<3>(*this, x, y, width, height, color);
|
||||||
|
break;
|
||||||
|
case ImageFormat::rgba8888:
|
||||||
|
draw_rect<4>(*this, x, y, width, height, color);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ImageData::blitRGB_on_RGBA(const ImageData& image, int x, int y) {
|
void ImageData::blitRGB_on_RGBA(const ImageData& image, int x, int y) {
|
||||||
ubyte* source = image.getData();
|
ubyte* source = image.getData();
|
||||||
uint srcwidth = image.getWidth();
|
uint srcwidth = image.getWidth();
|
||||||
|
|||||||
@ -28,6 +28,7 @@ public:
|
|||||||
void flipY();
|
void flipY();
|
||||||
|
|
||||||
void drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color);
|
void drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color);
|
||||||
|
void drawRect(int x, int y, int width, int height, const glm::ivec4& color);
|
||||||
void blit(const ImageData& image, int x, int y);
|
void blit(const ImageData& image, int x, int y);
|
||||||
void extrude(int x, int y, int w, int h);
|
void extrude(int x, int y, int w, int h);
|
||||||
void fixAlphaColor();
|
void fixAlphaColor();
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
@ -138,15 +137,21 @@ glshader compile_shader(GLenum type, const GLchar* source, const std::string& fi
|
|||||||
}
|
}
|
||||||
|
|
||||||
static GLuint compile_program(
|
static GLuint compile_program(
|
||||||
const Shader::Source& vertexSource, const Shader::Source& fragmentSource
|
const Shader::Source& vertexSource,
|
||||||
|
const Shader::Source& fragmentSource,
|
||||||
|
const std::vector<std::string>& defines
|
||||||
) {
|
) {
|
||||||
auto& preprocessor = *Shader::preprocessor;
|
auto& preprocessor = *Shader::preprocessor;
|
||||||
|
|
||||||
auto vertexCode = std::move(
|
auto vertexCode = std::move(
|
||||||
preprocessor.process(vertexSource.file, vertexSource.code).code
|
preprocessor
|
||||||
|
.process(vertexSource.file, vertexSource.code, false, defines)
|
||||||
|
.code
|
||||||
);
|
);
|
||||||
auto fragmentCode = std::move(
|
auto fragmentCode = std::move(
|
||||||
preprocessor.process(fragmentSource.file, fragmentSource.code).code
|
preprocessor
|
||||||
|
.process(fragmentSource.file, fragmentSource.code, false, defines)
|
||||||
|
.code
|
||||||
);
|
);
|
||||||
|
|
||||||
const GLchar* vCode = vertexCode.c_str();
|
const GLchar* vCode = vertexCode.c_str();
|
||||||
@ -176,8 +181,8 @@ static GLuint compile_program(
|
|||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::recompile() {
|
void Shader::recompile(const std::vector<std::string>& defines) {
|
||||||
GLuint newProgram = compile_program(vertexSource, fragmentSource);
|
GLuint newProgram = compile_program(vertexSource, fragmentSource, defines);
|
||||||
glDeleteProgram(id);
|
glDeleteProgram(id);
|
||||||
id = newProgram;
|
id = newProgram;
|
||||||
uniformLocations.clear();
|
uniformLocations.clear();
|
||||||
@ -188,7 +193,7 @@ std::unique_ptr<Shader> Shader::create(
|
|||||||
Source&& vertexSource, Source&& fragmentSource
|
Source&& vertexSource, Source&& fragmentSource
|
||||||
) {
|
) {
|
||||||
return std::make_unique<Shader>(
|
return std::make_unique<Shader>(
|
||||||
compile_program(vertexSource, fragmentSource),
|
compile_program(vertexSource, fragmentSource, {}),
|
||||||
std::move(vertexSource),
|
std::move(vertexSource),
|
||||||
std::move(fragmentSource)
|
std::move(fragmentSource)
|
||||||
);
|
);
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ public:
|
|||||||
void uniform4v(const std::string& name, int length, const float* v);
|
void uniform4v(const std::string& name, int length, const float* v);
|
||||||
|
|
||||||
/// @brief Re-preprocess source code and re-compile shader program
|
/// @brief Re-preprocess source code and re-compile shader program
|
||||||
void recompile();
|
void recompile(const std::vector<std::string>& defines);
|
||||||
|
|
||||||
/// @brief Create shader program using vertex and fragment shaders source.
|
/// @brief Create shader program using vertex and fragment shaders source.
|
||||||
/// @return linked shader program containing vertex and fragment shaders
|
/// @return linked shader program containing vertex and fragment shaders
|
||||||
|
|||||||
@ -1,47 +0,0 @@
|
|||||||
#include "ShadowMap.hpp"
|
|
||||||
|
|
||||||
#include <GL/glew.h>
|
|
||||||
|
|
||||||
ShadowMap::ShadowMap(int resolution) : resolution(resolution) {
|
|
||||||
glGenTextures(1, &depthMap);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, depthMap);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
|
|
||||||
resolution, resolution, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
|
||||||
float border[4] {1.0f, 1.0f, 1.0f, 1.0f};
|
|
||||||
glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_BORDER_COLOR, border);
|
|
||||||
|
|
||||||
glGenFramebuffers(1, &fbo);
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
|
|
||||||
glDrawBuffer(GL_NONE);
|
|
||||||
glReadBuffer(GL_NONE);
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ShadowMap::~ShadowMap() {
|
|
||||||
glDeleteFramebuffers(1, &fbo);
|
|
||||||
glDeleteTextures(1, &depthMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowMap::bind() {
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
||||||
glClear(GL_DEPTH_BUFFER_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowMap::unbind() {
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint ShadowMap::getDepthMap() const {
|
|
||||||
return depthMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ShadowMap::getResolution() const {
|
|
||||||
return resolution;
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "typedefs.hpp"
|
|
||||||
|
|
||||||
class ShadowMap {
|
|
||||||
public:
|
|
||||||
ShadowMap(int resolution);
|
|
||||||
~ShadowMap();
|
|
||||||
|
|
||||||
void bind();
|
|
||||||
void unbind();
|
|
||||||
uint getDepthMap() const;
|
|
||||||
int getResolution() const;
|
|
||||||
private:
|
|
||||||
uint fbo;
|
|
||||||
uint depthMap;
|
|
||||||
int resolution;
|
|
||||||
};
|
|
||||||
199
src/graphics/core/Shadows.cpp
Normal file
199
src/graphics/core/Shadows.cpp
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
#include "Shadows.hpp"
|
||||||
|
|
||||||
|
#include <GL/glew.h>
|
||||||
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
|
#include "assets/Assets.hpp"
|
||||||
|
#include "graphics/core/DrawContext.hpp"
|
||||||
|
#include "graphics/core/Shader.hpp"
|
||||||
|
#include "graphics/core/commons.hpp"
|
||||||
|
#include "world/Level.hpp"
|
||||||
|
#include "world/Weather.hpp"
|
||||||
|
#include "world/World.hpp"
|
||||||
|
|
||||||
|
using namespace advanced_pipeline;
|
||||||
|
|
||||||
|
inline constexpr int MIN_SHADOW_MAP_RES = 512;
|
||||||
|
inline constexpr GLenum TEXTURE_MAIN = GL_TEXTURE0;
|
||||||
|
|
||||||
|
class ShadowMap {
|
||||||
|
public:
|
||||||
|
ShadowMap(int resolution) : resolution(resolution) {
|
||||||
|
glGenTextures(1, &depthMap);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, depthMap);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
|
||||||
|
resolution, resolution, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
|
||||||
|
glTexParameteri(
|
||||||
|
GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE
|
||||||
|
);
|
||||||
|
float border[4] {1.0f, 1.0f, 1.0f, 1.0f};
|
||||||
|
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
|
||||||
|
|
||||||
|
glGenFramebuffers(1, &fbo);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
|
glFramebufferTexture2D(
|
||||||
|
GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0
|
||||||
|
);
|
||||||
|
glDrawBuffer(GL_NONE);
|
||||||
|
glReadBuffer(GL_NONE);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
~ShadowMap() {
|
||||||
|
glDeleteFramebuffers(1, &fbo);
|
||||||
|
glDeleteTextures(1, &depthMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind(){
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unbind() {
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
uint getDepthMap() const {
|
||||||
|
return depthMap;
|
||||||
|
}
|
||||||
|
int getResolution() const {
|
||||||
|
return resolution;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
uint fbo;
|
||||||
|
uint depthMap;
|
||||||
|
int resolution;
|
||||||
|
};
|
||||||
|
|
||||||
|
Shadows::Shadows(const Level& level) : level(level) {}
|
||||||
|
|
||||||
|
Shadows::~Shadows() = default;
|
||||||
|
|
||||||
|
void Shadows::setQuality(int quality) {
|
||||||
|
int resolution = MIN_SHADOW_MAP_RES << quality;
|
||||||
|
if (quality > 0 && !shadows) {
|
||||||
|
shadowMap = std::make_unique<ShadowMap>(resolution);
|
||||||
|
wideShadowMap = std::make_unique<ShadowMap>(resolution);
|
||||||
|
shadows = true;
|
||||||
|
} else if (quality == 0 && shadows) {
|
||||||
|
shadowMap.reset();
|
||||||
|
wideShadowMap.reset();
|
||||||
|
shadows = false;
|
||||||
|
}
|
||||||
|
if (shadows && shadowMap->getResolution() != resolution) {
|
||||||
|
shadowMap = std::make_unique<ShadowMap>(resolution);
|
||||||
|
wideShadowMap = std::make_unique<ShadowMap>(resolution);
|
||||||
|
}
|
||||||
|
this->quality = quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shadows::setup(Shader& shader, const Weather& weather) {
|
||||||
|
if (shadows) {
|
||||||
|
const auto& worldInfo = level.getWorld()->getInfo();
|
||||||
|
float cloudsIntensity = glm::max(worldInfo.fog, weather.clouds());
|
||||||
|
shader.uniform1i("u_screen", 0);
|
||||||
|
shader.uniformMatrix("u_shadowsMatrix[0]", shadowCamera.getProjView());
|
||||||
|
shader.uniformMatrix("u_shadowsMatrix[1]", wideShadowCamera.getProjView());
|
||||||
|
shader.uniform3f("u_sunDir", shadowCamera.front);
|
||||||
|
shader.uniform1i("u_shadowsRes", shadowMap->getResolution());
|
||||||
|
shader.uniform1f("u_shadowsOpacity", 1.0f - cloudsIntensity); // TODO: make it configurable
|
||||||
|
shader.uniform1f("u_shadowsSoftness", 1.0f + cloudsIntensity * 4); // TODO: make it configurable
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS0);
|
||||||
|
shader.uniform1i("u_shadows[0]", TARGET_SHADOWS0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, shadowMap->getDepthMap());
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS1);
|
||||||
|
shader.uniform1i("u_shadows[1]", TARGET_SHADOWS1);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, wideShadowMap->getDepthMap());
|
||||||
|
|
||||||
|
glActiveTexture(TEXTURE_MAIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shadows::refresh(const Camera& camera, const DrawContext& pctx, std::function<void(Camera&)> renderShadowPass) {
|
||||||
|
static int frameid = 0;
|
||||||
|
if (shadows) {
|
||||||
|
if (frameid % 2 == 0) {
|
||||||
|
generateShadowsMap(camera, pctx, *shadowMap, shadowCamera, 1.0f, renderShadowPass);
|
||||||
|
} else {
|
||||||
|
generateShadowsMap(camera, pctx, *wideShadowMap, wideShadowCamera, 3.0f, renderShadowPass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frameid++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shadows::generateShadowsMap(
|
||||||
|
const Camera& camera,
|
||||||
|
const DrawContext& pctx,
|
||||||
|
ShadowMap& shadowMap,
|
||||||
|
Camera& shadowCamera,
|
||||||
|
float scale,
|
||||||
|
std::function<void(Camera&)> renderShadowPass
|
||||||
|
) {
|
||||||
|
auto world = level.getWorld();
|
||||||
|
const auto& worldInfo = world->getInfo();
|
||||||
|
|
||||||
|
int resolution = shadowMap.getResolution();
|
||||||
|
float shadowMapScale = 0.32f / (1 << glm::max(0, quality)) * scale;
|
||||||
|
float shadowMapSize = resolution * shadowMapScale;
|
||||||
|
|
||||||
|
glm::vec3 basePos = glm::floor(camera.position / 4.0f) * 4.0f;
|
||||||
|
glm::vec3 prevPos = shadowCamera.position;
|
||||||
|
shadowCamera = Camera(
|
||||||
|
glm::distance2(prevPos, basePos) > 25.0f ? basePos : prevPos,
|
||||||
|
shadowMapSize
|
||||||
|
);
|
||||||
|
shadowCamera.near = 0.1f;
|
||||||
|
shadowCamera.far = 1000.0f;
|
||||||
|
shadowCamera.perspective = false;
|
||||||
|
shadowCamera.setAspectRatio(1.0f);
|
||||||
|
|
||||||
|
float t = worldInfo.daytime - 0.25f;
|
||||||
|
if (t < 0.0f) {
|
||||||
|
t += 1.0f;
|
||||||
|
}
|
||||||
|
t = fmod(t, 0.5f);
|
||||||
|
|
||||||
|
float sunCycleStep = 1.0f / 500.0f;
|
||||||
|
float sunAngle = glm::radians(
|
||||||
|
90.0f -
|
||||||
|
((static_cast<int>(t / sunCycleStep)) * sunCycleStep + 0.25f) * 360.0f
|
||||||
|
);
|
||||||
|
float sunAltitude = glm::pi<float>() * 0.25f;
|
||||||
|
shadowCamera.rotate(
|
||||||
|
-glm::cos(sunAngle + glm::pi<float>() * 0.5f) * sunAltitude,
|
||||||
|
sunAngle - glm::pi<float>() * 0.5f,
|
||||||
|
glm::radians(0.0f)
|
||||||
|
);
|
||||||
|
|
||||||
|
shadowCamera.position -= shadowCamera.front * 500.0f;
|
||||||
|
shadowCamera.position += shadowCamera.up * 0.0f;
|
||||||
|
shadowCamera.position += camera.front * 0.0f;
|
||||||
|
|
||||||
|
auto view = shadowCamera.getView();
|
||||||
|
|
||||||
|
auto currentPos = shadowCamera.position;
|
||||||
|
auto topRight = shadowCamera.right + shadowCamera.up;
|
||||||
|
auto min = view * glm::vec4(currentPos - topRight * shadowMapSize * 0.5f, 1.0f);
|
||||||
|
auto max = view * glm::vec4(currentPos + topRight * shadowMapSize * 0.5f, 1.0f);
|
||||||
|
|
||||||
|
shadowCamera.setProjection(glm::ortho(min.x, max.x, min.y, max.y, 0.1f, 1000.0f));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto sctx = pctx.sub();
|
||||||
|
sctx.setDepthTest(true);
|
||||||
|
sctx.setCullFace(true);
|
||||||
|
sctx.setViewport({resolution, resolution});
|
||||||
|
shadowMap.bind();
|
||||||
|
if (renderShadowPass) {
|
||||||
|
renderShadowPass(shadowCamera);
|
||||||
|
}
|
||||||
|
shadowMap.unbind();
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/graphics/core/Shadows.hpp
Normal file
46
src/graphics/core/Shadows.hpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "typedefs.hpp"
|
||||||
|
#include "window/Camera.hpp"
|
||||||
|
|
||||||
|
class Shader;
|
||||||
|
class Level;
|
||||||
|
class Assets;
|
||||||
|
struct Weather;
|
||||||
|
class DrawContext;
|
||||||
|
struct EngineSettings;
|
||||||
|
class ShadowMap;
|
||||||
|
|
||||||
|
class Shadows {
|
||||||
|
public:
|
||||||
|
Shadows(const Level& level);
|
||||||
|
~Shadows();
|
||||||
|
|
||||||
|
void setup(Shader& shader, const Weather& weather);
|
||||||
|
void setQuality(int quality);
|
||||||
|
void refresh(
|
||||||
|
const Camera& camera,
|
||||||
|
const DrawContext& pctx,
|
||||||
|
std::function<void(Camera&)> renderShadowPass
|
||||||
|
);
|
||||||
|
private:
|
||||||
|
const Level& level;
|
||||||
|
bool shadows = false;
|
||||||
|
Camera shadowCamera;
|
||||||
|
Camera wideShadowCamera;
|
||||||
|
std::unique_ptr<ShadowMap> shadowMap;
|
||||||
|
std::unique_ptr<ShadowMap> wideShadowMap;
|
||||||
|
int quality = 0;
|
||||||
|
|
||||||
|
void generateShadowsMap(
|
||||||
|
const Camera& camera,
|
||||||
|
const DrawContext& pctx,
|
||||||
|
ShadowMap& shadowMap,
|
||||||
|
Camera& shadowCamera,
|
||||||
|
float scale,
|
||||||
|
std::function<void(Camera&)> renderShadowPass
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -187,7 +187,7 @@ const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk(
|
|||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunksRenderer::drawChunksShadowsPass(
|
void ChunksRenderer::drawShadowsPass(
|
||||||
const Camera& camera, Shader& shader, const Camera& playerCamera
|
const Camera& camera, Shader& shader, const Camera& playerCamera
|
||||||
) {
|
) {
|
||||||
Frustum frustum;
|
Frustum frustum;
|
||||||
|
|||||||
@ -73,7 +73,7 @@ public:
|
|||||||
const std::shared_ptr<Chunk>& chunk, bool important
|
const std::shared_ptr<Chunk>& chunk, bool important
|
||||||
);
|
);
|
||||||
|
|
||||||
void drawChunksShadowsPass(
|
void drawShadowsPass(
|
||||||
const Camera& camera, Shader& shader, const Camera& playerCamera
|
const Camera& camera, Shader& shader, const Camera& playerCamera
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,51 @@
|
|||||||
#include "GuidesRenderer.hpp"
|
#include "DebugLinesRenderer.hpp"
|
||||||
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
|
||||||
|
|
||||||
#include "graphics/core/Shader.hpp"
|
#include "graphics/core/Shader.hpp"
|
||||||
|
#include "window/Camera.hpp"
|
||||||
#include "graphics/core/LineBatch.hpp"
|
#include "graphics/core/LineBatch.hpp"
|
||||||
#include "graphics/core/DrawContext.hpp"
|
#include "graphics/core/DrawContext.hpp"
|
||||||
|
#include "graphics/render/LinesRenderer.hpp"
|
||||||
|
#include "world/Level.hpp"
|
||||||
|
#include "voxels/Chunk.hpp"
|
||||||
|
#include "voxels/Pathfinding.hpp"
|
||||||
#include "maths/voxmaths.hpp"
|
#include "maths/voxmaths.hpp"
|
||||||
#include "window/Camera.hpp"
|
|
||||||
#include "constants.hpp"
|
|
||||||
|
|
||||||
void GuidesRenderer::drawBorders(
|
bool DebugLinesRenderer::showPaths = false;
|
||||||
|
|
||||||
|
static void draw_route(
|
||||||
|
LinesRenderer& lines, const voxels::Agent& agent
|
||||||
|
) {
|
||||||
|
const auto& route = agent.route;
|
||||||
|
if (!route.found)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 1; i < route.nodes.size(); i++) {
|
||||||
|
const auto& a = route.nodes.at(i - 1);
|
||||||
|
const auto& b = route.nodes.at(i);
|
||||||
|
|
||||||
|
if (i == 1) {
|
||||||
|
lines.pushLine(
|
||||||
|
glm::vec3(a.pos) + glm::vec3(0.5f),
|
||||||
|
glm::vec3(a.pos) + glm::vec3(0.5f, 1.0f, 0.5f),
|
||||||
|
glm::vec4(1, 1, 1, 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.pushLine(
|
||||||
|
glm::vec3(a.pos) + glm::vec3(0.5f),
|
||||||
|
glm::vec3(b.pos) + glm::vec3(0.5f),
|
||||||
|
glm::vec4(1, 0, 1, 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
lines.pushLine(
|
||||||
|
glm::vec3(b.pos) + glm::vec3(0.5f),
|
||||||
|
glm::vec3(b.pos) + glm::vec3(0.5f, 1.0f, 0.5f),
|
||||||
|
glm::vec4(1, 1, 1, 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugLinesRenderer::drawBorders(
|
||||||
LineBatch& batch, int sx, int sy, int sz, int ex, int ey, int ez
|
LineBatch& batch, int sx, int sy, int sz, int ex, int ey, int ez
|
||||||
) {
|
) {
|
||||||
int ww = ex - sx;
|
int ww = ex - sx;
|
||||||
@ -37,7 +73,7 @@ void GuidesRenderer::drawBorders(
|
|||||||
batch.flush();
|
batch.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuidesRenderer::drawCoordSystem(
|
void DebugLinesRenderer::drawCoordSystem(
|
||||||
LineBatch& batch, const DrawContext& pctx, float length
|
LineBatch& batch, const DrawContext& pctx, float length
|
||||||
) {
|
) {
|
||||||
auto ctx = pctx.sub();
|
auto ctx = pctx.sub();
|
||||||
@ -55,14 +91,22 @@ void GuidesRenderer::drawCoordSystem(
|
|||||||
batch.line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 1.f);
|
batch.line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 1.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuidesRenderer::renderDebugLines(
|
|
||||||
const DrawContext& pctx,
|
void DebugLinesRenderer::render(
|
||||||
|
DrawContext& pctx,
|
||||||
const Camera& camera,
|
const Camera& camera,
|
||||||
LineBatch& batch,
|
LinesRenderer& renderer,
|
||||||
|
LineBatch& linesBatch,
|
||||||
Shader& linesShader,
|
Shader& linesShader,
|
||||||
bool showChunkBorders
|
bool showChunkBorders
|
||||||
) {
|
) {
|
||||||
DrawContext ctx = pctx.sub(&batch);
|
// In-world lines
|
||||||
|
if (showPaths) {
|
||||||
|
for (const auto& [_, agent] : level.pathfinding->getAgents()) {
|
||||||
|
draw_route(renderer, agent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DrawContext ctx = pctx.sub(&linesBatch);
|
||||||
const auto& viewport = ctx.getViewport();
|
const auto& viewport = ctx.getViewport();
|
||||||
|
|
||||||
ctx.setDepthTest(true);
|
ctx.setDepthTest(true);
|
||||||
@ -78,7 +122,7 @@ void GuidesRenderer::renderDebugLines(
|
|||||||
int cz = floordiv(static_cast<int>(coord.z), CHUNK_D);
|
int cz = floordiv(static_cast<int>(coord.z), CHUNK_D);
|
||||||
|
|
||||||
drawBorders(
|
drawBorders(
|
||||||
batch,
|
linesBatch,
|
||||||
cx * CHUNK_W,
|
cx * CHUNK_W,
|
||||||
0,
|
0,
|
||||||
cz * CHUNK_D,
|
cz * CHUNK_D,
|
||||||
@ -103,5 +147,5 @@ void GuidesRenderer::renderDebugLines(
|
|||||||
) * model *
|
) * model *
|
||||||
glm::inverse(camera.rotation)
|
glm::inverse(camera.rotation)
|
||||||
);
|
);
|
||||||
drawCoordSystem(batch, ctx, length);
|
drawCoordSystem(linesBatch, ctx, length);
|
||||||
}
|
}
|
||||||
41
src/graphics/render/DebugLinesRenderer.hpp
Normal file
41
src/graphics/render/DebugLinesRenderer.hpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class DrawContext;
|
||||||
|
class Camera;
|
||||||
|
class LineBatch;
|
||||||
|
class LinesRenderer;
|
||||||
|
class Shader;
|
||||||
|
class Level;
|
||||||
|
|
||||||
|
class DebugLinesRenderer {
|
||||||
|
public:
|
||||||
|
static bool showPaths;
|
||||||
|
|
||||||
|
DebugLinesRenderer(const Level& level)
|
||||||
|
: level(level) {};
|
||||||
|
|
||||||
|
/// @brief Render debug lines in the world
|
||||||
|
/// @param ctx Draw context
|
||||||
|
/// @param camera Camera used for rendering
|
||||||
|
/// @param renderer Lines renderer used for rendering lines
|
||||||
|
/// @param linesShader Shader used for rendering lines
|
||||||
|
/// @param showChunkBorders Whether to show chunk borders
|
||||||
|
void render(
|
||||||
|
DrawContext& ctx,
|
||||||
|
const Camera& camera,
|
||||||
|
LinesRenderer& renderer,
|
||||||
|
LineBatch& linesBatch,
|
||||||
|
Shader& linesShader,
|
||||||
|
bool showChunkBorders
|
||||||
|
);
|
||||||
|
private:
|
||||||
|
const Level& level;
|
||||||
|
|
||||||
|
void drawBorders(
|
||||||
|
LineBatch& batch, int sx, int sy, int sz, int ex, int ey, int ez
|
||||||
|
);
|
||||||
|
void drawCoordSystem(
|
||||||
|
LineBatch& batch, const DrawContext& pctx, float length
|
||||||
|
);
|
||||||
|
|
||||||
|
};
|
||||||
@ -15,6 +15,7 @@
|
|||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
#include "objects/Players.hpp"
|
#include "objects/Players.hpp"
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
|
#include "objects/Entity.hpp"
|
||||||
#include "logic/LevelController.hpp"
|
#include "logic/LevelController.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
#include "engine/Engine.hpp"
|
#include "engine/Engine.hpp"
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include "window/Camera.hpp"
|
#include "window/Camera.hpp"
|
||||||
#include "graphics/core/Texture.hpp"
|
#include "graphics/core/Texture.hpp"
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
|
#include "objects/Entity.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
|
|
||||||
Emitter::Emitter(
|
Emitter::Emitter(
|
||||||
|
|||||||
@ -1,28 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
class LineBatch;
|
|
||||||
class DrawContext;
|
|
||||||
class Camera;
|
|
||||||
class Shader;
|
|
||||||
|
|
||||||
class GuidesRenderer {
|
|
||||||
public:
|
|
||||||
void drawBorders(
|
|
||||||
LineBatch& batch, int sx, int sy, int sz, int ex, int ey, int ez
|
|
||||||
);
|
|
||||||
void drawCoordSystem(
|
|
||||||
LineBatch& batch, const DrawContext& pctx, float length
|
|
||||||
);
|
|
||||||
|
|
||||||
/// @brief Render all debug lines (chunks borders, coord system guides)
|
|
||||||
/// @param context graphics context
|
|
||||||
/// @param camera active camera
|
|
||||||
/// @param linesShader shader used
|
|
||||||
void renderDebugLines(
|
|
||||||
const DrawContext& context,
|
|
||||||
const Camera& camera,
|
|
||||||
LineBatch& batch,
|
|
||||||
Shader& linesShader,
|
|
||||||
bool showChunkBorders
|
|
||||||
);
|
|
||||||
};
|
|
||||||
14
src/graphics/render/LinesRenderer.cpp
Normal file
14
src/graphics/render/LinesRenderer.cpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#include "LinesRenderer.hpp"
|
||||||
|
|
||||||
|
#include "graphics/core/LineBatch.hpp"
|
||||||
|
|
||||||
|
void LinesRenderer::draw(LineBatch& batch) {
|
||||||
|
for (const auto& line : queue) {
|
||||||
|
batch.line(line.a, line.b, line.color);
|
||||||
|
}
|
||||||
|
queue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinesRenderer::pushLine(const glm::vec3& a, const glm::vec3& b, const glm::vec4& color) {
|
||||||
|
queue.push_back({a, b, color});
|
||||||
|
}
|
||||||
22
src/graphics/render/LinesRenderer.hpp
Normal file
22
src/graphics/render/LinesRenderer.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
#include <glm/vec4.hpp>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class LineBatch;
|
||||||
|
|
||||||
|
class LinesRenderer {
|
||||||
|
public:
|
||||||
|
struct Line {
|
||||||
|
glm::vec3 a;
|
||||||
|
glm::vec3 b;
|
||||||
|
glm::vec4 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
void draw(LineBatch& batch);
|
||||||
|
|
||||||
|
void pushLine(const glm::vec3& a, const glm::vec3& b, const glm::vec4& color);
|
||||||
|
private:
|
||||||
|
std::vector<Line> queue;
|
||||||
|
};
|
||||||
@ -27,6 +27,7 @@
|
|||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
#include "voxels/Chunk.hpp"
|
#include "voxels/Chunk.hpp"
|
||||||
#include "voxels/Chunks.hpp"
|
#include "voxels/Chunks.hpp"
|
||||||
|
#include "voxels/Pathfinding.hpp"
|
||||||
#include "window/Window.hpp"
|
#include "window/Window.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "world/LevelEvents.hpp"
|
#include "world/LevelEvents.hpp"
|
||||||
@ -42,7 +43,7 @@
|
|||||||
#include "graphics/core/Shader.hpp"
|
#include "graphics/core/Shader.hpp"
|
||||||
#include "graphics/core/Texture.hpp"
|
#include "graphics/core/Texture.hpp"
|
||||||
#include "graphics/core/Font.hpp"
|
#include "graphics/core/Font.hpp"
|
||||||
#include "graphics/core/ShadowMap.hpp"
|
#include "graphics/core/Shadows.hpp"
|
||||||
#include "graphics/core/GBuffer.hpp"
|
#include "graphics/core/GBuffer.hpp"
|
||||||
#include "BlockWrapsRenderer.hpp"
|
#include "BlockWrapsRenderer.hpp"
|
||||||
#include "ParticlesRenderer.hpp"
|
#include "ParticlesRenderer.hpp"
|
||||||
@ -51,7 +52,8 @@
|
|||||||
#include "NamedSkeletons.hpp"
|
#include "NamedSkeletons.hpp"
|
||||||
#include "TextsRenderer.hpp"
|
#include "TextsRenderer.hpp"
|
||||||
#include "ChunksRenderer.hpp"
|
#include "ChunksRenderer.hpp"
|
||||||
#include "GuidesRenderer.hpp"
|
#include "LinesRenderer.hpp"
|
||||||
|
#include "DebugLinesRenderer.hpp"
|
||||||
#include "ModelBatch.hpp"
|
#include "ModelBatch.hpp"
|
||||||
#include "Skybox.hpp"
|
#include "Skybox.hpp"
|
||||||
#include "Emitter.hpp"
|
#include "Emitter.hpp"
|
||||||
@ -61,8 +63,6 @@ using namespace advanced_pipeline;
|
|||||||
|
|
||||||
inline constexpr size_t BATCH3D_CAPACITY = 4096;
|
inline constexpr size_t BATCH3D_CAPACITY = 4096;
|
||||||
inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000;
|
inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000;
|
||||||
inline constexpr GLenum TEXTURE_MAIN = GL_TEXTURE0;
|
|
||||||
inline constexpr int MIN_SHADOW_MAP_RES = 512;
|
|
||||||
|
|
||||||
bool WorldRenderer::showChunkBorders = false;
|
bool WorldRenderer::showChunkBorders = false;
|
||||||
bool WorldRenderer::showEntitiesDebug = false;
|
bool WorldRenderer::showEntitiesDebug = false;
|
||||||
@ -80,8 +80,7 @@ WorldRenderer::WorldRenderer(
|
|||||||
modelBatch(std::make_unique<ModelBatch>(
|
modelBatch(std::make_unique<ModelBatch>(
|
||||||
MODEL_BATCH_CAPACITY, assets, *player.chunks, engine.getSettings()
|
MODEL_BATCH_CAPACITY, assets, *player.chunks, engine.getSettings()
|
||||||
)),
|
)),
|
||||||
guides(std::make_unique<GuidesRenderer>()),
|
chunksRenderer(std::make_unique<ChunksRenderer>(
|
||||||
chunks(std::make_unique<ChunksRenderer>(
|
|
||||||
&level,
|
&level,
|
||||||
*player.chunks,
|
*player.chunks,
|
||||||
assets,
|
assets,
|
||||||
@ -102,7 +101,7 @@ WorldRenderer::WorldRenderer(
|
|||||||
auto& settings = engine.getSettings();
|
auto& settings = engine.getSettings();
|
||||||
level.events->listen(
|
level.events->listen(
|
||||||
LevelEventType::CHUNK_HIDDEN,
|
LevelEventType::CHUNK_HIDDEN,
|
||||||
[this](LevelEventType, Chunk* chunk) { chunks->unload(chunk); }
|
[this](LevelEventType, Chunk* chunk) { chunksRenderer->unload(chunk); }
|
||||||
);
|
);
|
||||||
auto assets = engine.getAssets();
|
auto assets = engine.getAssets();
|
||||||
skybox = std::make_unique<Skybox>(
|
skybox = std::make_unique<Skybox>(
|
||||||
@ -118,10 +117,26 @@ WorldRenderer::WorldRenderer(
|
|||||||
hands = std::make_unique<HandsRenderer>(
|
hands = std::make_unique<HandsRenderer>(
|
||||||
*assets, *modelBatch, skeletons->createSkeleton("hand", &skeletonConfig)
|
*assets, *modelBatch, skeletons->createSkeleton("hand", &skeletonConfig)
|
||||||
);
|
);
|
||||||
|
lines = std::make_unique<LinesRenderer>();
|
||||||
|
shadowMapping = std::make_unique<Shadows>(level);
|
||||||
|
debugLines = std::make_unique<DebugLinesRenderer>(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
WorldRenderer::~WorldRenderer() = default;
|
WorldRenderer::~WorldRenderer() = default;
|
||||||
|
|
||||||
|
static void setup_weather(Shader& shader, const Weather& weather) {
|
||||||
|
shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity());
|
||||||
|
shader.uniform1f("u_weatherFogDencity", weather.fogDencity());
|
||||||
|
shader.uniform1f("u_weatherFogCurve", weather.fogCurve());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setup_camera(Shader& shader, const Camera& camera) {
|
||||||
|
shader.uniformMatrix("u_model", glm::mat4(1.0f));
|
||||||
|
shader.uniformMatrix("u_proj", camera.getProjection());
|
||||||
|
shader.uniformMatrix("u_view", camera.getView());
|
||||||
|
shader.uniform3f("u_cameraPos", camera.position);
|
||||||
|
}
|
||||||
|
|
||||||
void WorldRenderer::setupWorldShader(
|
void WorldRenderer::setupWorldShader(
|
||||||
Shader& shader,
|
Shader& shader,
|
||||||
const Camera& camera,
|
const Camera& camera,
|
||||||
@ -129,45 +144,20 @@ void WorldRenderer::setupWorldShader(
|
|||||||
float fogFactor
|
float fogFactor
|
||||||
) {
|
) {
|
||||||
shader.use();
|
shader.use();
|
||||||
shader.uniformMatrix("u_model", glm::mat4(1.0f));
|
|
||||||
shader.uniformMatrix("u_proj", camera.getProjection());
|
setup_camera(shader, camera);
|
||||||
shader.uniformMatrix("u_view", camera.getView());
|
setup_weather(shader, weather);
|
||||||
|
shadowMapping->setup(shader, weather);
|
||||||
|
|
||||||
shader.uniform1f("u_timer", timer);
|
shader.uniform1f("u_timer", timer);
|
||||||
shader.uniform1f("u_gamma", settings.graphics.gamma.get());
|
shader.uniform1f("u_gamma", settings.graphics.gamma.get());
|
||||||
shader.uniform1f("u_fogFactor", fogFactor);
|
shader.uniform1f("u_fogFactor", fogFactor);
|
||||||
shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
|
shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
|
||||||
shader.uniform1i("u_debugLights", lightsDebug);
|
shader.uniform1i("u_debugLights", lightsDebug);
|
||||||
shader.uniform1i("u_debugNormals", false);
|
shader.uniform1i("u_debugNormals", false);
|
||||||
shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity());
|
|
||||||
shader.uniform1f("u_weatherFogDencity", weather.fogDencity());
|
|
||||||
shader.uniform1f("u_weatherFogCurve", weather.fogCurve());
|
|
||||||
shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime);
|
shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime);
|
||||||
shader.uniform2f("u_lightDir", skybox->getLightDir());
|
shader.uniform2f("u_lightDir", skybox->getLightDir());
|
||||||
shader.uniform3f("u_cameraPos", camera.position);
|
shader.uniform1i("u_skybox", TARGET_SKYBOX);
|
||||||
shader.uniform1i("u_skybox", 1);
|
|
||||||
shader.uniform1i("u_enableShadows", shadows);
|
|
||||||
|
|
||||||
if (shadows) {
|
|
||||||
const auto& worldInfo = level.getWorld()->getInfo();
|
|
||||||
float cloudsIntensity = glm::max(worldInfo.fog, weather.clouds());
|
|
||||||
shader.uniform1i("u_screen", 0);
|
|
||||||
shader.uniformMatrix("u_shadowsMatrix[0]", shadowCamera.getProjView());
|
|
||||||
shader.uniformMatrix("u_shadowsMatrix[1]", wideShadowCamera.getProjView());
|
|
||||||
shader.uniform3f("u_sunDir", shadowCamera.front);
|
|
||||||
shader.uniform1i("u_shadowsRes", shadowMap->getResolution());
|
|
||||||
shader.uniform1f("u_shadowsOpacity", 1.0f - cloudsIntensity); // TODO: make it configurable
|
|
||||||
shader.uniform1f("u_shadowsSoftness", 1.0f + cloudsIntensity * 4); // TODO: make it configurable
|
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS0);
|
|
||||||
shader.uniform1i("u_shadows[0]", TARGET_SHADOWS0);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, shadowMap->getDepthMap());
|
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS1);
|
|
||||||
shader.uniform1i("u_shadows[1]", TARGET_SHADOWS1);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, wideShadowMap->getDepthMap());
|
|
||||||
|
|
||||||
glActiveTexture(TEXTURE_MAIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto indices = level.content.getIndices();
|
auto indices = level.content.getIndices();
|
||||||
// Light emission when an emissive item is chosen
|
// Light emission when an emissive item is chosen
|
||||||
@ -186,7 +176,7 @@ void WorldRenderer::setupWorldShader(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::renderLevel(
|
void WorldRenderer::renderOpaque(
|
||||||
const DrawContext& ctx,
|
const DrawContext& ctx,
|
||||||
const Camera& camera,
|
const Camera& camera,
|
||||||
const EngineSettings& settings,
|
const EngineSettings& settings,
|
||||||
@ -215,7 +205,9 @@ void WorldRenderer::renderLevel(
|
|||||||
*modelBatch,
|
*modelBatch,
|
||||||
culling ? frustumCulling.get() : nullptr,
|
culling ? frustumCulling.get() : nullptr,
|
||||||
delta,
|
delta,
|
||||||
pause
|
pause,
|
||||||
|
player.currentCamera.get() == player.fpCamera.get() ? player.getEntity()
|
||||||
|
: 0
|
||||||
);
|
);
|
||||||
modelBatch->render();
|
modelBatch->render();
|
||||||
particles->render(camera, delta * !pause);
|
particles->render(camera, delta * !pause);
|
||||||
@ -225,7 +217,7 @@ void WorldRenderer::renderLevel(
|
|||||||
|
|
||||||
setupWorldShader(shader, camera, settings, fogFactor);
|
setupWorldShader(shader, camera, settings, fogFactor);
|
||||||
|
|
||||||
chunks->drawChunks(camera, shader);
|
chunksRenderer->drawChunks(camera, shader);
|
||||||
blockWraps->draw(ctx, player);
|
blockWraps->draw(ctx, player);
|
||||||
|
|
||||||
if (hudVisible) {
|
if (hudVisible) {
|
||||||
@ -284,79 +276,7 @@ void WorldRenderer::renderLines(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::generateShadowsMap(
|
void WorldRenderer::renderFrame(
|
||||||
const Camera& camera,
|
|
||||||
const DrawContext& pctx,
|
|
||||||
ShadowMap& shadowMap,
|
|
||||||
Camera& shadowCamera,
|
|
||||||
float scale
|
|
||||||
) {
|
|
||||||
auto& shadowsShader = assets.require<Shader>("shadows");
|
|
||||||
|
|
||||||
auto world = level.getWorld();
|
|
||||||
const auto& worldInfo = world->getInfo();
|
|
||||||
|
|
||||||
const auto& settings = engine.getSettings();
|
|
||||||
int resolution = shadowMap.getResolution();
|
|
||||||
int quality = settings.graphics.shadowsQuality.get();
|
|
||||||
float shadowMapScale = 0.32f / (1 << glm::max(0, quality)) * scale;
|
|
||||||
float shadowMapSize = resolution * shadowMapScale;
|
|
||||||
|
|
||||||
glm::vec3 basePos = glm::floor(camera.position / 4.0f) * 4.0f;
|
|
||||||
glm::vec3 prevPos = shadowCamera.position;
|
|
||||||
shadowCamera = Camera(
|
|
||||||
glm::distance2(prevPos, basePos) > 25.0f ? basePos : prevPos,
|
|
||||||
shadowMapSize
|
|
||||||
);
|
|
||||||
shadowCamera.near = 0.1f;
|
|
||||||
shadowCamera.far = 1000.0f;
|
|
||||||
shadowCamera.perspective = false;
|
|
||||||
shadowCamera.setAspectRatio(1.0f);
|
|
||||||
|
|
||||||
float t = worldInfo.daytime - 0.25f;
|
|
||||||
if (t < 0.0f) {
|
|
||||||
t += 1.0f;
|
|
||||||
}
|
|
||||||
t = fmod(t, 0.5f);
|
|
||||||
|
|
||||||
float sunCycleStep = 1.0f / 500.0f;
|
|
||||||
float sunAngle = glm::radians(
|
|
||||||
90.0f -
|
|
||||||
((static_cast<int>(t / sunCycleStep)) * sunCycleStep + 0.25f) * 360.0f
|
|
||||||
);
|
|
||||||
float sunAltitude = glm::pi<float>() * 0.25f;
|
|
||||||
shadowCamera.rotate(
|
|
||||||
-glm::cos(sunAngle + glm::pi<float>() * 0.5f) * sunAltitude,
|
|
||||||
sunAngle - glm::pi<float>() * 0.5f,
|
|
||||||
glm::radians(0.0f)
|
|
||||||
);
|
|
||||||
|
|
||||||
shadowCamera.position -= shadowCamera.front * 500.0f;
|
|
||||||
shadowCamera.position += shadowCamera.up * 0.0f;
|
|
||||||
shadowCamera.position += camera.front * 0.0f;
|
|
||||||
|
|
||||||
auto view = shadowCamera.getView();
|
|
||||||
|
|
||||||
auto currentPos = shadowCamera.position;
|
|
||||||
auto topRight = shadowCamera.right + shadowCamera.up;
|
|
||||||
auto min = view * glm::vec4(currentPos - topRight * shadowMapSize * 0.5f, 1.0f);
|
|
||||||
auto max = view * glm::vec4(currentPos + topRight * shadowMapSize * 0.5f, 1.0f);
|
|
||||||
|
|
||||||
shadowCamera.setProjection(glm::ortho(min.x, max.x, min.y, max.y, 0.1f, 1000.0f));
|
|
||||||
|
|
||||||
{
|
|
||||||
auto sctx = pctx.sub();
|
|
||||||
sctx.setDepthTest(true);
|
|
||||||
sctx.setCullFace(true);
|
|
||||||
sctx.setViewport({resolution, resolution});
|
|
||||||
shadowMap.bind();
|
|
||||||
setupWorldShader(shadowsShader, shadowCamera, settings, 0.0f);
|
|
||||||
chunks->drawChunksShadowsPass(shadowCamera, shadowsShader, camera);
|
|
||||||
shadowMap.unbind();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WorldRenderer::draw(
|
|
||||||
const DrawContext& pctx,
|
const DrawContext& pctx,
|
||||||
Camera& camera,
|
Camera& camera,
|
||||||
bool hudVisible,
|
bool hudVisible,
|
||||||
@ -365,6 +285,9 @@ void WorldRenderer::draw(
|
|||||||
PostProcessing& postProcessing
|
PostProcessing& postProcessing
|
||||||
) {
|
) {
|
||||||
// TODO: REFACTOR WHOLE RENDER ENGINE
|
// TODO: REFACTOR WHOLE RENDER ENGINE
|
||||||
|
|
||||||
|
auto projView = camera.getProjView();
|
||||||
|
|
||||||
float delta = uiDelta * !pause;
|
float delta = uiDelta * !pause;
|
||||||
timer += delta;
|
timer += delta;
|
||||||
weather.update(delta);
|
weather.update(delta);
|
||||||
@ -380,22 +303,17 @@ void WorldRenderer::draw(
|
|||||||
auto& deferredShader = assets.require<PostEffect>("deferred_lighting").getShader();
|
auto& deferredShader = assets.require<PostEffect>("deferred_lighting").getShader();
|
||||||
const auto& settings = engine.getSettings();
|
const auto& settings = engine.getSettings();
|
||||||
|
|
||||||
|
Shader* affectedShaders[] {
|
||||||
|
&mainShader, &entityShader, &translucentShader, &deferredShader
|
||||||
|
};
|
||||||
|
|
||||||
gbufferPipeline = settings.graphics.advancedRender.get();
|
gbufferPipeline = settings.graphics.advancedRender.get();
|
||||||
int shadowsQuality = settings.graphics.shadowsQuality.get() * gbufferPipeline;
|
int shadowsQuality = settings.graphics.shadowsQuality.get() * gbufferPipeline;
|
||||||
int resolution = MIN_SHADOW_MAP_RES << shadowsQuality;
|
shadowMapping->setQuality(shadowsQuality);
|
||||||
if (shadowsQuality > 0 && !shadows) {
|
|
||||||
shadowMap = std::make_unique<ShadowMap>(resolution);
|
|
||||||
wideShadowMap = std::make_unique<ShadowMap>(resolution);
|
|
||||||
shadows = true;
|
|
||||||
} else if (shadowsQuality == 0 && shadows) {
|
|
||||||
shadowMap.reset();
|
|
||||||
wideShadowMap.reset();
|
|
||||||
shadows = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompileTimeShaderSettings currentSettings {
|
CompileTimeShaderSettings currentSettings {
|
||||||
gbufferPipeline,
|
gbufferPipeline,
|
||||||
shadows,
|
shadowsQuality != 0,
|
||||||
settings.graphics.ssao.get() && gbufferPipeline
|
settings.graphics.ssao.get() && gbufferPipeline
|
||||||
};
|
};
|
||||||
if (
|
if (
|
||||||
@ -403,19 +321,15 @@ void WorldRenderer::draw(
|
|||||||
prevCTShaderSettings.shadows != currentSettings.shadows ||
|
prevCTShaderSettings.shadows != currentSettings.shadows ||
|
||||||
prevCTShaderSettings.ssao != currentSettings.ssao
|
prevCTShaderSettings.ssao != currentSettings.ssao
|
||||||
) {
|
) {
|
||||||
Shader::preprocessor->setDefined("ENABLE_SHADOWS", currentSettings.shadows);
|
std::vector<std::string> defines;
|
||||||
Shader::preprocessor->setDefined("ENABLE_SSAO", currentSettings.ssao);
|
if (currentSettings.shadows) defines.emplace_back("ENABLE_SHADOWS");
|
||||||
Shader::preprocessor->setDefined("ADVANCED_RENDER", currentSettings.advancedRender);
|
if (currentSettings.ssao) defines.emplace_back("ENABLE_SSAO");
|
||||||
mainShader.recompile();
|
if (currentSettings.advancedRender) defines.emplace_back("ADVANCED_RENDER");
|
||||||
entityShader.recompile();
|
|
||||||
deferredShader.recompile();
|
|
||||||
translucentShader.recompile();
|
|
||||||
prevCTShaderSettings = currentSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shadows && shadowMap->getResolution() != resolution) {
|
for (auto shader : affectedShaders) {
|
||||||
shadowMap = std::make_unique<ShadowMap>(resolution);
|
shader->recompile(defines);
|
||||||
wideShadowMap = std::make_unique<ShadowMap>(resolution);
|
}
|
||||||
|
prevCTShaderSettings = currentSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& worldInfo = world->getInfo();
|
const auto& worldInfo = world->getInfo();
|
||||||
@ -426,38 +340,24 @@ void WorldRenderer::draw(
|
|||||||
|
|
||||||
skybox->refresh(pctx, worldInfo.daytime, mie, 4);
|
skybox->refresh(pctx, worldInfo.daytime, mie, 4);
|
||||||
|
|
||||||
chunks->update();
|
chunksRenderer->update();
|
||||||
|
|
||||||
static int frameid = 0;
|
shadowMapping->refresh(camera, pctx, [this, &camera](Camera& shadowCamera) {
|
||||||
if (shadows) {
|
auto& shader = assets.require<Shader>("shadows");
|
||||||
if (frameid % 2 == 0) {
|
setupWorldShader(shader, shadowCamera, engine.getSettings(), 0.0f);
|
||||||
generateShadowsMap(camera, pctx, *shadowMap, shadowCamera, 1.0f);
|
chunksRenderer->drawShadowsPass(shadowCamera, shader, camera);
|
||||||
} else {
|
});
|
||||||
generateShadowsMap(camera, pctx, *wideShadowMap, wideShadowCamera, 3.0f);
|
{
|
||||||
}
|
|
||||||
}
|
|
||||||
frameid++;
|
|
||||||
|
|
||||||
auto& linesShader = assets.require<Shader>("lines");
|
|
||||||
/* World render scope with diegetic HUD included */ {
|
|
||||||
DrawContext wctx = pctx.sub();
|
DrawContext wctx = pctx.sub();
|
||||||
postProcessing.use(wctx, gbufferPipeline);
|
postProcessing.use(wctx, gbufferPipeline);
|
||||||
|
|
||||||
display::clearDepth();
|
display::clearDepth();
|
||||||
|
|
||||||
/* Actually world render with depth buffer on */ {
|
/* Main opaque pass (GBuffer pass) */ {
|
||||||
DrawContext ctx = wctx.sub();
|
DrawContext ctx = wctx.sub();
|
||||||
ctx.setDepthTest(true);
|
ctx.setDepthTest(true);
|
||||||
ctx.setCullFace(true);
|
ctx.setCullFace(true);
|
||||||
renderLevel(ctx, camera, settings, uiDelta, pause, hudVisible);
|
renderOpaque(ctx, camera, settings, uiDelta, pause, hudVisible);
|
||||||
// Debug lines
|
|
||||||
if (hudVisible) {
|
|
||||||
if (debug) {
|
|
||||||
guides->renderDebugLines(
|
|
||||||
ctx, camera, *lineBatch, linesShader, showChunkBorders
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
texts->render(pctx, camera, settings, hudVisible, true);
|
texts->render(pctx, camera, settings, hudVisible, true);
|
||||||
}
|
}
|
||||||
@ -478,19 +378,33 @@ void WorldRenderer::draw(
|
|||||||
} else {
|
} else {
|
||||||
postProcessing.getFramebuffer()->bind();
|
postProcessing.getFramebuffer()->bind();
|
||||||
}
|
}
|
||||||
// Drawing background sky plane
|
|
||||||
|
// Background sky plane
|
||||||
skybox->draw(ctx, camera, assets, worldInfo.daytime, clouds);
|
skybox->draw(ctx, camera, assets, worldInfo.daytime, clouds);
|
||||||
|
|
||||||
|
auto& linesShader = assets.require<Shader>("lines");
|
||||||
|
linesShader.use();
|
||||||
|
if (debug && hudVisible) {
|
||||||
|
debugLines->render(
|
||||||
|
ctx, camera, *lines, *lineBatch, linesShader, showChunkBorders
|
||||||
|
);
|
||||||
|
}
|
||||||
|
linesShader.uniformMatrix("u_projview", projView);
|
||||||
|
lines->draw(*lineBatch);
|
||||||
|
lineBatch->flush();
|
||||||
|
|
||||||
|
// Translucent blocks
|
||||||
{
|
{
|
||||||
auto sctx = ctx.sub();
|
auto sctx = ctx.sub();
|
||||||
sctx.setCullFace(true);
|
sctx.setCullFace(true);
|
||||||
skybox->bind();
|
skybox->bind();
|
||||||
translucentShader.use();
|
translucentShader.use();
|
||||||
setupWorldShader(translucentShader, camera, settings, fogFactor);
|
setupWorldShader(translucentShader, camera, settings, fogFactor);
|
||||||
chunks->drawSortedMeshes(camera, translucentShader);
|
chunksRenderer->drawSortedMeshes(camera, translucentShader);
|
||||||
skybox->unbind();
|
skybox->unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Weather effects
|
||||||
entityShader.use();
|
entityShader.use();
|
||||||
setupWorldShader(entityShader, camera, settings, fogFactor);
|
setupWorldShader(entityShader, camera, settings, fogFactor);
|
||||||
|
|
||||||
@ -582,7 +496,7 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::clear() {
|
void WorldRenderer::clear() {
|
||||||
chunks->clear();
|
chunksRenderer->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::setDebug(bool flag) {
|
void WorldRenderer::setDebug(bool flag) {
|
||||||
|
|||||||
@ -22,7 +22,7 @@ class BlockWrapsRenderer;
|
|||||||
class PrecipitationRenderer;
|
class PrecipitationRenderer;
|
||||||
class HandsRenderer;
|
class HandsRenderer;
|
||||||
class NamedSkeletons;
|
class NamedSkeletons;
|
||||||
class GuidesRenderer;
|
class LinesRenderer;
|
||||||
class TextsRenderer;
|
class TextsRenderer;
|
||||||
class Shader;
|
class Shader;
|
||||||
class Frustum;
|
class Frustum;
|
||||||
@ -33,8 +33,9 @@ class PostProcessing;
|
|||||||
class DrawContext;
|
class DrawContext;
|
||||||
class ModelBatch;
|
class ModelBatch;
|
||||||
class Assets;
|
class Assets;
|
||||||
class ShadowMap;
|
class Shadows;
|
||||||
class GBuffer;
|
class GBuffer;
|
||||||
|
class DebugLinesRenderer;
|
||||||
struct EngineSettings;
|
struct EngineSettings;
|
||||||
|
|
||||||
struct CompileTimeShaderSettings {
|
struct CompileTimeShaderSettings {
|
||||||
@ -52,21 +53,17 @@ class WorldRenderer {
|
|||||||
std::unique_ptr<LineBatch> lineBatch;
|
std::unique_ptr<LineBatch> lineBatch;
|
||||||
std::unique_ptr<Batch3D> batch3d;
|
std::unique_ptr<Batch3D> batch3d;
|
||||||
std::unique_ptr<ModelBatch> modelBatch;
|
std::unique_ptr<ModelBatch> modelBatch;
|
||||||
std::unique_ptr<GuidesRenderer> guides;
|
std::unique_ptr<ChunksRenderer> chunksRenderer;
|
||||||
std::unique_ptr<ChunksRenderer> chunks;
|
|
||||||
std::unique_ptr<HandsRenderer> hands;
|
std::unique_ptr<HandsRenderer> hands;
|
||||||
std::unique_ptr<Skybox> skybox;
|
std::unique_ptr<Skybox> skybox;
|
||||||
std::unique_ptr<ShadowMap> shadowMap;
|
std::unique_ptr<Shadows> shadowMapping;
|
||||||
std::unique_ptr<ShadowMap> wideShadowMap;
|
std::unique_ptr<DebugLinesRenderer> debugLines;
|
||||||
Weather weather {};
|
Weather weather {};
|
||||||
Camera shadowCamera;
|
|
||||||
Camera wideShadowCamera;
|
|
||||||
|
|
||||||
float timer = 0.0f;
|
float timer = 0.0f;
|
||||||
bool debug = false;
|
bool debug = false;
|
||||||
bool lightsDebug = false;
|
bool lightsDebug = false;
|
||||||
bool gbufferPipeline = false;
|
bool gbufferPipeline = false;
|
||||||
bool shadows = false;
|
|
||||||
|
|
||||||
CompileTimeShaderSettings prevCTShaderSettings {};
|
CompileTimeShaderSettings prevCTShaderSettings {};
|
||||||
|
|
||||||
@ -89,12 +86,17 @@ class WorldRenderer {
|
|||||||
float fogFactor
|
float fogFactor
|
||||||
);
|
);
|
||||||
|
|
||||||
void generateShadowsMap(
|
/// @brief Render opaque pass
|
||||||
const Camera& camera,
|
/// @param context graphics context
|
||||||
const DrawContext& pctx,
|
/// @param camera active camera
|
||||||
ShadowMap& shadowMap,
|
/// @param settings engine settings
|
||||||
Camera& shadowCamera,
|
void renderOpaque(
|
||||||
float scale
|
const DrawContext& context,
|
||||||
|
const Camera& camera,
|
||||||
|
const EngineSettings& settings,
|
||||||
|
float delta,
|
||||||
|
bool pause,
|
||||||
|
bool hudVisible
|
||||||
);
|
);
|
||||||
public:
|
public:
|
||||||
std::unique_ptr<ParticlesRenderer> particles;
|
std::unique_ptr<ParticlesRenderer> particles;
|
||||||
@ -102,6 +104,7 @@ public:
|
|||||||
std::unique_ptr<BlockWrapsRenderer> blockWraps;
|
std::unique_ptr<BlockWrapsRenderer> blockWraps;
|
||||||
std::unique_ptr<PrecipitationRenderer> precipitation;
|
std::unique_ptr<PrecipitationRenderer> precipitation;
|
||||||
std::unique_ptr<NamedSkeletons> skeletons;
|
std::unique_ptr<NamedSkeletons> skeletons;
|
||||||
|
std::unique_ptr<LinesRenderer> lines;
|
||||||
|
|
||||||
static bool showChunkBorders;
|
static bool showChunkBorders;
|
||||||
static bool showEntitiesDebug;
|
static bool showEntitiesDebug;
|
||||||
@ -109,7 +112,7 @@ public:
|
|||||||
WorldRenderer(Engine& engine, LevelFrontend& frontend, Player& player);
|
WorldRenderer(Engine& engine, LevelFrontend& frontend, Player& player);
|
||||||
~WorldRenderer();
|
~WorldRenderer();
|
||||||
|
|
||||||
void draw(
|
void renderFrame(
|
||||||
const DrawContext& context,
|
const DrawContext& context,
|
||||||
Camera& camera,
|
Camera& camera,
|
||||||
bool hudVisible,
|
bool hudVisible,
|
||||||
@ -118,19 +121,6 @@ public:
|
|||||||
PostProcessing& postProcessing
|
PostProcessing& postProcessing
|
||||||
);
|
);
|
||||||
|
|
||||||
/// @brief Render level without diegetic interface
|
|
||||||
/// @param context graphics context
|
|
||||||
/// @param camera active camera
|
|
||||||
/// @param settings engine settings
|
|
||||||
void renderLevel(
|
|
||||||
const DrawContext& context,
|
|
||||||
const Camera& camera,
|
|
||||||
const EngineSettings& settings,
|
|
||||||
float delta,
|
|
||||||
bool pause,
|
|
||||||
bool hudVisible
|
|
||||||
);
|
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
void setDebug(bool flag);
|
void setDebug(bool flag);
|
||||||
|
|||||||
@ -83,9 +83,13 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) {
|
|||||||
builder.add("language", &settings.ui.language);
|
builder.add("language", &settings.ui.language);
|
||||||
builder.add("world-preview-size", &settings.ui.worldPreviewSize);
|
builder.add("world-preview-size", &settings.ui.worldPreviewSize);
|
||||||
|
|
||||||
|
builder.section("pathfinding");
|
||||||
|
builder.add("steps-per-async-agent", &settings.pathfinding.stepsPerAsyncAgent);
|
||||||
|
|
||||||
builder.section("debug");
|
builder.section("debug");
|
||||||
builder.add("generator-test-mode", &settings.debug.generatorTestMode);
|
builder.add("generator-test-mode", &settings.debug.generatorTestMode);
|
||||||
builder.add("do-write-lights", &settings.debug.doWriteLights);
|
builder.add("do-write-lights", &settings.debug.doWriteLights);
|
||||||
|
builder.add("do-trace-shaders", &settings.debug.doTraceShaders);
|
||||||
builder.add("enable-experimental", &settings.debug.enableExperimental);
|
builder.add("enable-experimental", &settings.debug.enableExperimental);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
#include "voxels/Chunks.hpp"
|
#include "voxels/Chunks.hpp"
|
||||||
|
#include "voxels/Pathfinding.hpp"
|
||||||
#include "scripting/scripting.hpp"
|
#include "scripting/scripting.hpp"
|
||||||
#include "lighting/Lighting.hpp"
|
#include "lighting/Lighting.hpp"
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
@ -69,6 +70,9 @@ LevelController::LevelController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LevelController::update(float delta, bool pause) {
|
void LevelController::update(float delta, bool pause) {
|
||||||
|
level->pathfinding->performAllAsync(
|
||||||
|
settings.pathfinding.stepsPerAsyncAgent.get()
|
||||||
|
);
|
||||||
for (const auto& [_, player] : *level->players) {
|
for (const auto& [_, player] : *level->players) {
|
||||||
if (player->isSuspended()) {
|
if (player->isSuspended()) {
|
||||||
continue;
|
continue;
|
||||||
@ -91,7 +95,6 @@ void LevelController::update(float delta, bool pause) {
|
|||||||
if (!pause) {
|
if (!pause) {
|
||||||
// update all objects that needed
|
// update all objects that needed
|
||||||
blocks->update(delta, settings.chunks.padding.get());
|
blocks->update(delta, settings.chunks.padding.get());
|
||||||
level->entities->updatePhysics(delta);
|
|
||||||
level->entities->update(delta);
|
level->entities->update(delta);
|
||||||
for (const auto& [_, player] : *level->players) {
|
for (const auto& [_, player] : *level->players) {
|
||||||
if (player->isSuspended()) {
|
if (player->isSuspended()) {
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
#include "items/ItemStack.hpp"
|
#include "items/ItemStack.hpp"
|
||||||
#include "lighting/Lighting.hpp"
|
#include "lighting/Lighting.hpp"
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
|
#include "objects/Entity.hpp"
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
#include "objects/Players.hpp"
|
#include "objects/Players.hpp"
|
||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
@ -270,7 +271,6 @@ void PlayerController::update(float delta, const Input* inputEvents) {
|
|||||||
} else {
|
} else {
|
||||||
resetKeyboard();
|
resetKeyboard();
|
||||||
}
|
}
|
||||||
updatePlayer(delta);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerController::postUpdate(
|
void PlayerController::postUpdate(
|
||||||
@ -309,20 +309,7 @@ void PlayerController::updateKeyboard(const Input& inputEvents) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PlayerController::resetKeyboard() {
|
void PlayerController::resetKeyboard() {
|
||||||
input.zoom = false;
|
input = {};
|
||||||
input.moveForward = false;
|
|
||||||
input.moveBack = false;
|
|
||||||
input.moveLeft = false;
|
|
||||||
input.moveRight = false;
|
|
||||||
input.sprint = false;
|
|
||||||
input.shift = false;
|
|
||||||
input.cheat = false;
|
|
||||||
input.jump = false;
|
|
||||||
input.delta = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlayerController::updatePlayer(float delta) {
|
|
||||||
player.updateInput(input, delta);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int determine_rotation(
|
static int determine_rotation(
|
||||||
@ -338,7 +325,8 @@ static int determine_rotation(
|
|||||||
if (norm.z > 0.0f) return BLOCK_DIR_NORTH;
|
if (norm.z > 0.0f) return BLOCK_DIR_NORTH;
|
||||||
if (norm.z < 0.0f) return BLOCK_DIR_SOUTH;
|
if (norm.z < 0.0f) return BLOCK_DIR_SOUTH;
|
||||||
} else if (name == "pane" || name == "stairs") {
|
} else if (name == "pane" || name == "stairs") {
|
||||||
int verticalBit = (name == "stairs" && (norm.y - camDir.y * 0.5f) < 0.0) ? 4 : 0;
|
int verticalBit =
|
||||||
|
(name == "stairs" && (norm.y - camDir.y * 0.5f) < 0.0) ? 4 : 0;
|
||||||
if (abs(camDir.x) > abs(camDir.z)) {
|
if (abs(camDir.x) > abs(camDir.z)) {
|
||||||
if (camDir.x > 0.0f) return BLOCK_DIR_EAST | verticalBit;
|
if (camDir.x > 0.0f) return BLOCK_DIR_EAST | verticalBit;
|
||||||
if (camDir.x < 0.0f) return BLOCK_DIR_WEST | verticalBit;
|
if (camDir.x < 0.0f) return BLOCK_DIR_WEST | verticalBit;
|
||||||
|
|||||||
@ -60,7 +60,6 @@ class PlayerController {
|
|||||||
|
|
||||||
void updateKeyboard(const Input& inputEvents);
|
void updateKeyboard(const Input& inputEvents);
|
||||||
void resetKeyboard();
|
void resetKeyboard();
|
||||||
void updatePlayer(float delta);
|
|
||||||
void updateEntityInteraction(entityid_t eid, bool lclick, bool rclick);
|
void updateEntityInteraction(entityid_t eid, bool lclick, bool rclick);
|
||||||
void updateInteraction(const Input& inputEvents, float delta);
|
void updateInteraction(const Input& inputEvents, float delta);
|
||||||
|
|
||||||
|
|||||||
@ -38,6 +38,7 @@ extern const luaL_Reg mat4lib[];
|
|||||||
extern const luaL_Reg networklib[];
|
extern const luaL_Reg networklib[];
|
||||||
extern const luaL_Reg packlib[];
|
extern const luaL_Reg packlib[];
|
||||||
extern const luaL_Reg particleslib[]; // gfx.particles
|
extern const luaL_Reg particleslib[]; // gfx.particles
|
||||||
|
extern const luaL_Reg pathfindinglib[];
|
||||||
extern const luaL_Reg playerlib[];
|
extern const luaL_Reg playerlib[];
|
||||||
extern const luaL_Reg posteffectslib[]; // gfx.posteffects
|
extern const luaL_Reg posteffectslib[]; // gfx.posteffects
|
||||||
extern const luaL_Reg quatlib[];
|
extern const luaL_Reg quatlib[];
|
||||||
|
|||||||
@ -62,6 +62,15 @@ static int l_set_gravity_scale(lua::State* L) {
|
|||||||
static int l_is_vdamping(lua::State* L) {
|
static int l_is_vdamping(lua::State* L) {
|
||||||
if (auto entity = get_entity(L, 1)) {
|
if (auto entity = get_entity(L, 1)) {
|
||||||
return lua::pushboolean(
|
return lua::pushboolean(
|
||||||
|
L, entity->getRigidbody().hitbox.verticalDamping > 0.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_get_vdamping(lua::State* L) {
|
||||||
|
if (auto entity = get_entity(L, 1)) {
|
||||||
|
return lua::pushnumber(
|
||||||
L, entity->getRigidbody().hitbox.verticalDamping
|
L, entity->getRigidbody().hitbox.verticalDamping
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -70,7 +79,11 @@ static int l_is_vdamping(lua::State* L) {
|
|||||||
|
|
||||||
static int l_set_vdamping(lua::State* L) {
|
static int l_set_vdamping(lua::State* L) {
|
||||||
if (auto entity = get_entity(L, 1)) {
|
if (auto entity = get_entity(L, 1)) {
|
||||||
entity->getRigidbody().hitbox.verticalDamping = lua::toboolean(L, 2);
|
if (lua::isboolean(L, 2)) {
|
||||||
|
entity->getRigidbody().hitbox.verticalDamping = lua::toboolean(L, 2);
|
||||||
|
} else {
|
||||||
|
entity->getRigidbody().hitbox.verticalDamping = lua::tonumber(L, 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -144,6 +157,7 @@ const luaL_Reg rigidbodylib[] = {
|
|||||||
{"get_linear_damping", lua::wrap<l_get_linear_damping>},
|
{"get_linear_damping", lua::wrap<l_get_linear_damping>},
|
||||||
{"set_linear_damping", lua::wrap<l_set_linear_damping>},
|
{"set_linear_damping", lua::wrap<l_set_linear_damping>},
|
||||||
{"is_vdamping", lua::wrap<l_is_vdamping>},
|
{"is_vdamping", lua::wrap<l_is_vdamping>},
|
||||||
|
{"get_vdamping", lua::wrap<l_get_vdamping>},
|
||||||
{"set_vdamping", lua::wrap<l_set_vdamping>},
|
{"set_vdamping", lua::wrap<l_set_vdamping>},
|
||||||
{"is_grounded", lua::wrap<l_is_grounded>},
|
{"is_grounded", lua::wrap<l_is_grounded>},
|
||||||
{"is_crouching", lua::wrap<l_is_crouching>},
|
{"is_crouching", lua::wrap<l_is_crouching>},
|
||||||
|
|||||||
@ -91,7 +91,7 @@ static int l_set_texture(lua::State* L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int l_index(lua::State* L) {
|
static int l_index(lua::State* L) {
|
||||||
if (auto skeleton= get_skeleton(L)) {
|
if (auto skeleton = get_skeleton(L)) {
|
||||||
if (auto bone = skeleton->config->find(lua::require_string(L, 2))) {
|
if (auto bone = skeleton->config->find(lua::require_string(L, 2))) {
|
||||||
return lua::pushinteger(L, bone->getIndex());
|
return lua::pushinteger(L, bone->getIndex());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
#include "engine/Engine.hpp"
|
#include "engine/Engine.hpp"
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
#include "objects/EntityDef.hpp"
|
#include "objects/EntityDef.hpp"
|
||||||
|
#include "objects/Entity.hpp"
|
||||||
|
#include "objects/Rigidbody.hpp"
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
#include "objects/rigging.hpp"
|
#include "objects/rigging.hpp"
|
||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "frontend/hud.hpp"
|
#include "frontend/hud.hpp"
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
|
#include "objects/Entity.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "logic/LevelController.hpp"
|
#include "logic/LevelController.hpp"
|
||||||
#include "api_lua.hpp"
|
#include "api_lua.hpp"
|
||||||
|
|||||||
@ -31,6 +31,8 @@ static int l_mousecode(lua::State* L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int l_add_callback(lua::State* L) {
|
static int l_add_callback(lua::State* L) {
|
||||||
|
if (engine->isHeadless())
|
||||||
|
return 0;
|
||||||
std::string bindname = lua::require_string(L, 1);
|
std::string bindname = lua::require_string(L, 1);
|
||||||
size_t pos = bindname.find(':');
|
size_t pos = bindname.find(':');
|
||||||
|
|
||||||
@ -75,10 +77,14 @@ static int l_add_callback(lua::State* L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int l_get_mouse_pos(lua::State* L) {
|
static int l_get_mouse_pos(lua::State* L) {
|
||||||
|
if (engine->isHeadless())
|
||||||
|
return 0;
|
||||||
return lua::pushvec2(L, engine->getInput().getCursor().pos);
|
return lua::pushvec2(L, engine->getInput().getCursor().pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_get_bindings(lua::State* L) {
|
static int l_get_bindings(lua::State* L) {
|
||||||
|
if (engine->isHeadless())
|
||||||
|
return 0;
|
||||||
const auto& bindings = engine->getInput().getBindings().getAll();
|
const auto& bindings = engine->getInput().getBindings().getAll();
|
||||||
lua::createtable(L, bindings.size(), 0);
|
lua::createtable(L, bindings.size(), 0);
|
||||||
|
|
||||||
@ -92,18 +98,24 @@ static int l_get_bindings(lua::State* L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int l_get_binding_text(lua::State* L) {
|
static int l_get_binding_text(lua::State* L) {
|
||||||
|
if (engine->isHeadless())
|
||||||
|
return 0;
|
||||||
auto bindname = lua::require_string(L, 1);
|
auto bindname = lua::require_string(L, 1);
|
||||||
const auto& bind = engine->getInput().getBindings().require(bindname);
|
const auto& bind = engine->getInput().getBindings().require(bindname);
|
||||||
return lua::pushstring(L, bind.text());
|
return lua::pushstring(L, bind.text());
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_is_active(lua::State* L) {
|
static int l_is_active(lua::State* L) {
|
||||||
|
if (engine->isHeadless())
|
||||||
|
return 0;
|
||||||
auto bindname = lua::require_string(L, 1);
|
auto bindname = lua::require_string(L, 1);
|
||||||
auto& bind = engine->getInput().getBindings().require(bindname);
|
auto& bind = engine->getInput().getBindings().require(bindname);
|
||||||
return lua::pushboolean(L, bind.active());
|
return lua::pushboolean(L, bind.active());
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_is_pressed(lua::State* L) {
|
static int l_is_pressed(lua::State* L) {
|
||||||
|
if (engine->isHeadless())
|
||||||
|
return 0;
|
||||||
std::string code = lua::require_string(L, 1);
|
std::string code = lua::require_string(L, 1);
|
||||||
size_t sep = code.find(':');
|
size_t sep = code.find(':');
|
||||||
if (sep == std::string::npos) {
|
if (sep == std::string::npos) {
|
||||||
@ -136,6 +148,8 @@ static void reset_pack_bindings(const io::path& packFolder) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int l_reset_bindings(lua::State*) {
|
static int l_reset_bindings(lua::State*) {
|
||||||
|
if (engine->isHeadless())
|
||||||
|
return 0;
|
||||||
reset_pack_bindings("res:");
|
reset_pack_bindings("res:");
|
||||||
for (const auto& pack : content_control->getContentPacks()) {
|
for (const auto& pack : content_control->getContentPacks()) {
|
||||||
reset_pack_bindings(pack.folder);
|
reset_pack_bindings(pack.folder);
|
||||||
@ -144,6 +158,8 @@ static int l_reset_bindings(lua::State*) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int l_set_enabled(lua::State* L) {
|
static int l_set_enabled(lua::State* L) {
|
||||||
|
if (engine->isHeadless())
|
||||||
|
return 0;
|
||||||
std::string bindname = lua::require_string(L, 1);
|
std::string bindname = lua::require_string(L, 1);
|
||||||
bool enabled = lua::toboolean(L, 2);
|
bool enabled = lua::toboolean(L, 2);
|
||||||
engine->getInput().getBindings().require(bindname).enabled = enabled;
|
engine->getInput().getBindings().require(bindname).enabled = enabled;
|
||||||
@ -161,4 +177,5 @@ const luaL_Reg inputlib[] = {
|
|||||||
{"is_pressed", lua::wrap<l_is_pressed>},
|
{"is_pressed", lua::wrap<l_is_pressed>},
|
||||||
{"reset_bindings", lua::wrap<l_reset_bindings>},
|
{"reset_bindings", lua::wrap<l_reset_bindings>},
|
||||||
{"set_enabled", lua::wrap<l_set_enabled>},
|
{"set_enabled", lua::wrap<l_set_enabled>},
|
||||||
{NULL, NULL}};
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|||||||
130
src/logic/scripting/lua/libs/libpathfinding.cpp
Normal file
130
src/logic/scripting/lua/libs/libpathfinding.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include "api_lua.hpp"
|
||||||
|
|
||||||
|
#include "content/Content.hpp"
|
||||||
|
#include "voxels/Pathfinding.hpp"
|
||||||
|
#include "world/Level.hpp"
|
||||||
|
|
||||||
|
using namespace scripting;
|
||||||
|
|
||||||
|
static voxels::Agent* get_agent(lua::State* L) {
|
||||||
|
return level->pathfinding->getAgent(lua::tointeger(L, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_create_agent(lua::State* L) {
|
||||||
|
return lua::pushinteger(L, level->pathfinding->createAgent());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_remove_agent(lua::State* L) {
|
||||||
|
int id = lua::tointeger(L, 1);
|
||||||
|
return lua::pushboolean(L, level->pathfinding->removeAgent(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_set_enabled(lua::State* L) {
|
||||||
|
if (auto agent = get_agent(L)) {
|
||||||
|
agent->enabled = lua::toboolean(L, 2);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_is_enabled(lua::State* L) {
|
||||||
|
if (auto agent = get_agent(L)) {
|
||||||
|
return lua::pushboolean(L, agent->enabled);
|
||||||
|
}
|
||||||
|
return lua::pushboolean(L, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int push_route(lua::State* L, const voxels::Route& route) {
|
||||||
|
lua::createtable(L, route.nodes.size(), 1);
|
||||||
|
for (int i = 0; i < route.nodes.size(); i++) {
|
||||||
|
lua::pushvec3(L, route.nodes[i].pos);
|
||||||
|
lua::rawseti(L, i + 1);
|
||||||
|
}
|
||||||
|
lua::pushinteger(L, route.totalVisited);
|
||||||
|
lua::setfield(L, "total_visited");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_make_route(lua::State* L) {
|
||||||
|
if (auto agent = get_agent(L)) {
|
||||||
|
auto start = lua::tovec3(L, 2);
|
||||||
|
auto target = lua::tovec3(L, 3);
|
||||||
|
agent->state = {};
|
||||||
|
agent->start = glm::floor(start);
|
||||||
|
agent->target = target;
|
||||||
|
auto route = level->pathfinding->perform(*agent);
|
||||||
|
if (!route.found) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return push_route(L, route);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_make_route_async(lua::State* L) {
|
||||||
|
if (auto agent = get_agent(L)) {
|
||||||
|
auto start = lua::tovec3(L, 2);
|
||||||
|
auto target = lua::tovec3(L, 3);
|
||||||
|
agent->state = {};
|
||||||
|
agent->start = glm::floor(start);
|
||||||
|
agent->target = target;
|
||||||
|
level->pathfinding->perform(*agent, 0);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_pull_route(lua::State* L) {
|
||||||
|
if (auto agent = get_agent(L)) {
|
||||||
|
auto& route = agent->route;
|
||||||
|
if (!agent->state.finished) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!route.found && !agent->mayBeIncomplete) {
|
||||||
|
return lua::createtable(L, 0, 0);
|
||||||
|
}
|
||||||
|
return push_route(L, route);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_set_max_visited_blocks(lua::State* L) {
|
||||||
|
if (auto agent = get_agent(L)) {
|
||||||
|
agent->maxVisitedBlocks = lua::tointeger(L, 2);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_set_jump_height(lua::State* L) {
|
||||||
|
if (auto agent = get_agent(L)) {
|
||||||
|
agent->jumpHeight = lua::tointeger(L, 2);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_avoid_tag(lua::State* L) {
|
||||||
|
if (auto agent = get_agent(L)) {
|
||||||
|
int index =
|
||||||
|
content->getTagIndex(std::string(lua::require_lstring(L, 2)));
|
||||||
|
if (index != -1) {
|
||||||
|
int cost = lua::tonumber(L, 3);
|
||||||
|
if (cost == 0) {
|
||||||
|
cost = 10;
|
||||||
|
}
|
||||||
|
agent->avoidTags.insert({index, cost});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const luaL_Reg pathfindinglib[] = {
|
||||||
|
{"create_agent", lua::wrap<l_create_agent>},
|
||||||
|
{"remove_agent", lua::wrap<l_remove_agent>},
|
||||||
|
{"set_enabled", lua::wrap<l_set_enabled>},
|
||||||
|
{"is_enabled", lua::wrap<l_is_enabled>},
|
||||||
|
{"make_route", lua::wrap<l_make_route>},
|
||||||
|
{"make_route_async", lua::wrap<l_make_route_async>},
|
||||||
|
{"pull_route", lua::wrap<l_pull_route>},
|
||||||
|
{"set_max_visited", lua::wrap<l_set_max_visited_blocks>},
|
||||||
|
{"set_jump_height", lua::wrap<l_set_jump_height>},
|
||||||
|
{"avoid_tag", lua::wrap<l_avoid_tag>},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
@ -5,6 +5,7 @@
|
|||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
#include "objects/Players.hpp"
|
#include "objects/Players.hpp"
|
||||||
|
#include "objects/Entity.hpp"
|
||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
#include "window/Camera.hpp"
|
#include "window/Camera.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
|
|||||||
@ -15,10 +15,24 @@ inline T angle(glm::vec<2, T> vec) {
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <int n>
|
||||||
|
static int l_mix(lua::State* L) {
|
||||||
|
uint argc = lua::check_argc(L, 3, 4);
|
||||||
|
auto a = lua::tovec<n, number_t>(L, 1);
|
||||||
|
auto b = lua::tovec<n, number_t>(L, 2);
|
||||||
|
auto t = lua::tonumber(L, 3);
|
||||||
|
|
||||||
|
if (argc == 3) {
|
||||||
|
return lua::pushvec(L, a * (1.0 - t) + b * t);
|
||||||
|
} else {
|
||||||
|
return lua::setvec(L, 4, a * (1.0 - t) + b * t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <int n, template <class> class Op>
|
template <int n, template <class> class Op>
|
||||||
static int l_binop(lua::State* L) {
|
static int l_binop(lua::State* L) {
|
||||||
uint argc = lua::check_argc(L, 2, 3);
|
uint argc = lua::check_argc(L, 2, 3);
|
||||||
auto a = lua::tovec<n>(L, 1);
|
auto a = lua::tovec<n, number_t>(L, 1);
|
||||||
|
|
||||||
if (lua::isnumber(L, 2)) { // scalar second operand overload
|
if (lua::isnumber(L, 2)) { // scalar second operand overload
|
||||||
auto b = lua::tonumber(L, 2);
|
auto b = lua::tonumber(L, 2);
|
||||||
@ -31,10 +45,10 @@ static int l_binop(lua::State* L) {
|
|||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return lua::setvec(L, 3, op(a, glm::vec<n, float>(b)));
|
return lua::setvec(L, 3, op(a, glm::vec<n, number_t>(b)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto b = lua::tovec<n>(L, 2);
|
auto b = lua::tovec<n, number_t>(L, 2);
|
||||||
Op op;
|
Op op;
|
||||||
if (argc == 2) {
|
if (argc == 2) {
|
||||||
lua::createtable(L, n, 0);
|
lua::createtable(L, n, 0);
|
||||||
@ -49,10 +63,10 @@ static int l_binop(lua::State* L) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int n, glm::vec<n, float> (*func)(const glm::vec<n, float>&)>
|
template <int n, glm::vec<n, number_t> (*func)(const glm::vec<n, number_t>&)>
|
||||||
static int l_unaryop(lua::State* L) {
|
static int l_unaryop(lua::State* L) {
|
||||||
uint argc = lua::check_argc(L, 1, 2);
|
uint argc = lua::check_argc(L, 1, 2);
|
||||||
auto vec = func(lua::tovec<n>(L, 1));
|
auto vec = func(lua::tovec<n, number_t>(L, 1));
|
||||||
switch (argc) {
|
switch (argc) {
|
||||||
case 1:
|
case 1:
|
||||||
lua::createtable(L, n, 0);
|
lua::createtable(L, n, 0);
|
||||||
@ -67,17 +81,25 @@ static int l_unaryop(lua::State* L) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int n, float (*func)(const glm::vec<n, float>&)>
|
template <int n, number_t (*func)(const glm::vec<n, number_t>&)>
|
||||||
static int l_scalar_op(lua::State* L) {
|
static int l_scalar_op(lua::State* L) {
|
||||||
lua::check_argc(L, 1);
|
lua::check_argc(L, 1);
|
||||||
auto vec = lua::tovec<n>(L, 1);
|
auto vec = lua::tovec<n, number_t>(L, 1);
|
||||||
return lua::pushnumber(L, func(vec));
|
return lua::pushnumber(L, func(vec));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <int n>
|
||||||
|
static int l_distance(lua::State* L) {
|
||||||
|
lua::check_argc(L, 2);
|
||||||
|
auto a = lua::tovec<n, number_t>(L, 1);
|
||||||
|
auto b = lua::tovec<n, number_t>(L, 2);
|
||||||
|
return lua::pushnumber(L,glm::distance(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
template <int n>
|
template <int n>
|
||||||
static int l_pow(lua::State* L) {
|
static int l_pow(lua::State* L) {
|
||||||
uint argc = lua::check_argc(L, 2, 3);
|
uint argc = lua::check_argc(L, 2, 3);
|
||||||
auto a = lua::tovec<n>(L, 1);
|
auto a = lua::tovec<n, number_t>(L, 1);
|
||||||
|
|
||||||
if (lua::isnumber(L, 2)) {
|
if (lua::isnumber(L, 2)) {
|
||||||
auto b = lua::tonumber(L, 2);
|
auto b = lua::tonumber(L, 2);
|
||||||
@ -89,10 +111,10 @@ static int l_pow(lua::State* L) {
|
|||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return lua::setvec(L, 3, pow(a, glm::vec<n, float>(b)));
|
return lua::setvec(L, 3, pow(a, glm::vec<n, number_t>(b)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto b = lua::tovec<n>(L, 2);
|
auto b = lua::tovec<n, number_t>(L, 2);
|
||||||
if (argc == 2) {
|
if (argc == 2) {
|
||||||
lua::createtable(L, n, 0);
|
lua::createtable(L, n, 0);
|
||||||
for (uint i = 0; i < n; i++) {
|
for (uint i = 0; i < n; i++) {
|
||||||
@ -109,15 +131,15 @@ static int l_pow(lua::State* L) {
|
|||||||
template <int n>
|
template <int n>
|
||||||
static int l_dot(lua::State* L) {
|
static int l_dot(lua::State* L) {
|
||||||
lua::check_argc(L, 2);
|
lua::check_argc(L, 2);
|
||||||
const auto& a = lua::tovec<n>(L, 1);
|
auto a = lua::tovec<n, number_t>(L, 1);
|
||||||
const auto& b = lua::tovec<n>(L, 2);
|
auto b = lua::tovec<n, number_t>(L, 2);
|
||||||
return lua::pushnumber(L, glm::dot(a, b));
|
return lua::pushnumber(L, glm::dot(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int n>
|
template <int n>
|
||||||
static int l_inverse(lua::State* L) {
|
static int l_inverse(lua::State* L) {
|
||||||
uint argc = lua::check_argc(L, 1, 2);
|
uint argc = lua::check_argc(L, 1, 2);
|
||||||
auto vec = lua::tovec<n>(L, 1);
|
auto vec = lua::tovec<n, number_t>(L, 1);
|
||||||
switch (argc) {
|
switch (argc) {
|
||||||
case 1:
|
case 1:
|
||||||
lua::createtable(L, n, 0);
|
lua::createtable(L, n, 0);
|
||||||
@ -141,7 +163,7 @@ static int l_spherical_rand(lua::State* L) {
|
|||||||
return lua::setvec(
|
return lua::setvec(
|
||||||
L,
|
L,
|
||||||
2,
|
2,
|
||||||
glm::sphericalRand(static_cast<float>(lua::tonumber(L, 1)))
|
glm::sphericalRand(lua::tonumber(L, 1))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -161,10 +183,22 @@ static int l_vec2_angle(lua::State* L) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_vec2_rotate(lua::State* L) {
|
||||||
|
uint argc = lua::check_argc(L, 2, 3);
|
||||||
|
auto vec = lua::tovec<2, number_t>(L, 1);
|
||||||
|
auto angle = glm::radians(lua::tonumber(L, 2));
|
||||||
|
|
||||||
|
if (argc == 2) {
|
||||||
|
return lua::pushvec(L, glm::rotate(vec, angle));
|
||||||
|
} else {
|
||||||
|
return lua::setvec(L, 3, glm::rotate(vec, angle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <int n>
|
template <int n>
|
||||||
static int l_tostring(lua::State* L) {
|
static int l_tostring(lua::State* L) {
|
||||||
lua::check_argc(L, 1);
|
lua::check_argc(L, 1);
|
||||||
auto vec = lua::tovec<n>(L, 1);
|
auto vec = lua::tovec<n, number_t>(L, 1);
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "vec" << std::to_string(n) << "{";
|
ss << "vec" << std::to_string(n) << "{";
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
@ -182,6 +216,7 @@ const luaL_Reg vec2lib[] = {
|
|||||||
{"sub", lua::wrap<l_binop<2, std::minus>>},
|
{"sub", lua::wrap<l_binop<2, std::minus>>},
|
||||||
{"mul", lua::wrap<l_binop<2, std::multiplies>>},
|
{"mul", lua::wrap<l_binop<2, std::multiplies>>},
|
||||||
{"div", lua::wrap<l_binop<2, std::divides>>},
|
{"div", lua::wrap<l_binop<2, std::divides>>},
|
||||||
|
{"distance", lua::wrap<l_distance<2>>},
|
||||||
{"normalize", lua::wrap<l_unaryop<2, glm::normalize>>},
|
{"normalize", lua::wrap<l_unaryop<2, glm::normalize>>},
|
||||||
{"length", lua::wrap<l_scalar_op<2, glm::length>>},
|
{"length", lua::wrap<l_scalar_op<2, glm::length>>},
|
||||||
{"tostring", lua::wrap<l_tostring<2>>},
|
{"tostring", lua::wrap<l_tostring<2>>},
|
||||||
@ -191,6 +226,8 @@ const luaL_Reg vec2lib[] = {
|
|||||||
{"pow", lua::wrap<l_pow<2>>},
|
{"pow", lua::wrap<l_pow<2>>},
|
||||||
{"dot", lua::wrap<l_dot<2>>},
|
{"dot", lua::wrap<l_dot<2>>},
|
||||||
{"angle", lua::wrap<l_vec2_angle>},
|
{"angle", lua::wrap<l_vec2_angle>},
|
||||||
|
{"mix", lua::wrap<l_mix<2>>},
|
||||||
|
{"rotate", lua::wrap<l_vec2_rotate>},
|
||||||
{NULL, NULL}};
|
{NULL, NULL}};
|
||||||
|
|
||||||
const luaL_Reg vec3lib[] = {
|
const luaL_Reg vec3lib[] = {
|
||||||
@ -198,6 +235,7 @@ const luaL_Reg vec3lib[] = {
|
|||||||
{"sub", lua::wrap<l_binop<3, std::minus>>},
|
{"sub", lua::wrap<l_binop<3, std::minus>>},
|
||||||
{"mul", lua::wrap<l_binop<3, std::multiplies>>},
|
{"mul", lua::wrap<l_binop<3, std::multiplies>>},
|
||||||
{"div", lua::wrap<l_binop<3, std::divides>>},
|
{"div", lua::wrap<l_binop<3, std::divides>>},
|
||||||
|
{"distance", lua::wrap<l_distance<3>>},
|
||||||
{"normalize", lua::wrap<l_unaryop<3, glm::normalize>>},
|
{"normalize", lua::wrap<l_unaryop<3, glm::normalize>>},
|
||||||
{"length", lua::wrap<l_scalar_op<3, glm::length>>},
|
{"length", lua::wrap<l_scalar_op<3, glm::length>>},
|
||||||
{"tostring", lua::wrap<l_tostring<3>>},
|
{"tostring", lua::wrap<l_tostring<3>>},
|
||||||
@ -207,6 +245,7 @@ const luaL_Reg vec3lib[] = {
|
|||||||
{"pow", lua::wrap<l_pow<3>>},
|
{"pow", lua::wrap<l_pow<3>>},
|
||||||
{"dot", lua::wrap<l_dot<3>>},
|
{"dot", lua::wrap<l_dot<3>>},
|
||||||
{"spherical_rand", lua::wrap<l_spherical_rand>},
|
{"spherical_rand", lua::wrap<l_spherical_rand>},
|
||||||
|
{"mix", lua::wrap<l_mix<3>>},
|
||||||
{NULL, NULL}};
|
{NULL, NULL}};
|
||||||
|
|
||||||
const luaL_Reg vec4lib[] = {
|
const luaL_Reg vec4lib[] = {
|
||||||
@ -214,6 +253,7 @@ const luaL_Reg vec4lib[] = {
|
|||||||
{"sub", lua::wrap<l_binop<4, std::minus>>},
|
{"sub", lua::wrap<l_binop<4, std::minus>>},
|
||||||
{"mul", lua::wrap<l_binop<4, std::multiplies>>},
|
{"mul", lua::wrap<l_binop<4, std::multiplies>>},
|
||||||
{"div", lua::wrap<l_binop<4, std::divides>>},
|
{"div", lua::wrap<l_binop<4, std::divides>>},
|
||||||
|
{"distance", lua::wrap<l_distance<4>>},
|
||||||
{"normalize", lua::wrap<l_unaryop<4, glm::normalize>>},
|
{"normalize", lua::wrap<l_unaryop<4, glm::normalize>>},
|
||||||
{"length", lua::wrap<l_scalar_op<4, glm::length>>},
|
{"length", lua::wrap<l_scalar_op<4, glm::length>>},
|
||||||
{"tostring", lua::wrap<l_tostring<4>>},
|
{"tostring", lua::wrap<l_tostring<4>>},
|
||||||
@ -222,4 +262,5 @@ const luaL_Reg vec4lib[] = {
|
|||||||
{"inverse", lua::wrap<l_inverse<4>>},
|
{"inverse", lua::wrap<l_inverse<4>>},
|
||||||
{"pow", lua::wrap<l_pow<4>>},
|
{"pow", lua::wrap<l_pow<4>>},
|
||||||
{"dot", lua::wrap<l_dot<4>>},
|
{"dot", lua::wrap<l_dot<4>>},
|
||||||
|
{"mix", lua::wrap<l_mix<4>>},
|
||||||
{NULL, NULL}};
|
{NULL, NULL}};
|
||||||
|
|||||||
@ -72,6 +72,7 @@ static void create_libs(State* L, StateType stateType) {
|
|||||||
openlib(L, "input", inputlib);
|
openlib(L, "input", inputlib);
|
||||||
openlib(L, "inventory", inventorylib);
|
openlib(L, "inventory", inventorylib);
|
||||||
openlib(L, "network", networklib);
|
openlib(L, "network", networklib);
|
||||||
|
openlib(L, "pathfinding", pathfindinglib);
|
||||||
openlib(L, "player", playerlib);
|
openlib(L, "player", playerlib);
|
||||||
openlib(L, "time", timelib);
|
openlib(L, "time", timelib);
|
||||||
openlib(L, "world", worldlib);
|
openlib(L, "world", worldlib);
|
||||||
|
|||||||
@ -48,8 +48,8 @@ namespace lua {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int n>
|
template <int n, typename T = float>
|
||||||
inline int pushvec(lua::State* L, const glm::vec<n, float>& vec) {
|
inline int pushvec(lua::State* L, const glm::vec<n, T>& vec) {
|
||||||
createtable(L, n, 0);
|
createtable(L, n, 0);
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
pushnumber(L, vec[i]);
|
pushnumber(L, vec[i]);
|
||||||
@ -161,8 +161,8 @@ namespace lua {
|
|||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
template <int n>
|
template <int n, typename T = float>
|
||||||
inline int setvec(lua::State* L, int idx, glm::vec<n, float> vec) {
|
inline int setvec(lua::State* L, int idx, glm::vec<n, T> vec) {
|
||||||
pushvalue(L, idx);
|
pushvalue(L, idx);
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
pushnumber(L, vec[i]);
|
pushnumber(L, vec[i]);
|
||||||
@ -305,15 +305,15 @@ namespace lua {
|
|||||||
setglobal(L, name);
|
setglobal(L, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int n>
|
template <int n, typename T = float>
|
||||||
inline glm::vec<n, float> tovec(lua::State* L, int idx) {
|
inline glm::vec<n, T> tovec(lua::State* L, int idx) {
|
||||||
pushvalue(L, idx);
|
pushvalue(L, idx);
|
||||||
if (!istable(L, idx) || objlen(L, idx) < n) {
|
if (!istable(L, idx) || objlen(L, idx) < n) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"value must be an array of " + std::to_string(n) + " numbers"
|
"value must be an array of " + std::to_string(n) + " numbers"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
glm::vec<n, float> vec;
|
glm::vec<n, T> vec;
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
rawgeti(L, i + 1);
|
rawgeti(L, i + 1);
|
||||||
vec[i] = tonumber(L, -1);
|
vec[i] = tonumber(L, -1);
|
||||||
|
|||||||
@ -19,8 +19,6 @@
|
|||||||
#include "lua/lua_engine.hpp"
|
#include "lua/lua_engine.hpp"
|
||||||
#include "lua/lua_custom_types.hpp"
|
#include "lua/lua_custom_types.hpp"
|
||||||
#include "maths/Heightmap.hpp"
|
#include "maths/Heightmap.hpp"
|
||||||
#include "objects/Entities.hpp"
|
|
||||||
#include "objects/EntityDef.hpp"
|
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
#include "util/timeutil.hpp"
|
#include "util/timeutil.hpp"
|
||||||
@ -34,8 +32,6 @@ using namespace scripting;
|
|||||||
|
|
||||||
static debug::Logger logger("scripting");
|
static debug::Logger logger("scripting");
|
||||||
|
|
||||||
static inline const std::string STDCOMP = "stdcomp";
|
|
||||||
|
|
||||||
std::ostream* scripting::output_stream = &std::cout;
|
std::ostream* scripting::output_stream = &std::cout;
|
||||||
std::ostream* scripting::error_stream = &std::cerr;
|
std::ostream* scripting::error_stream = &std::cerr;
|
||||||
Engine* scripting::engine = nullptr;
|
Engine* scripting::engine = nullptr;
|
||||||
@ -234,36 +230,6 @@ std::unique_ptr<Process> scripting::start_coroutine(const io::path& script) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] static scriptenv create_component_environment(
|
|
||||||
const scriptenv& parent, int entityIdx, const std::string& name
|
|
||||||
) {
|
|
||||||
auto L = lua::get_main_state();
|
|
||||||
int id = lua::create_environment(L, *parent);
|
|
||||||
|
|
||||||
lua::pushvalue(L, entityIdx);
|
|
||||||
|
|
||||||
lua::pushenv(L, id);
|
|
||||||
|
|
||||||
lua::pushvalue(L, -1);
|
|
||||||
lua::setfield(L, "this");
|
|
||||||
|
|
||||||
lua::pushvalue(L, -2);
|
|
||||||
lua::setfield(L, "entity");
|
|
||||||
|
|
||||||
lua::pop(L);
|
|
||||||
if (lua::getfield(L, "components")) {
|
|
||||||
lua::pushenv(L, id);
|
|
||||||
lua::setfield(L, name);
|
|
||||||
lua::pop(L);
|
|
||||||
}
|
|
||||||
lua::pop(L);
|
|
||||||
|
|
||||||
return std::shared_ptr<int>(new int(id), [=](int* id) { //-V508
|
|
||||||
lua::remove_environment(L, *id);
|
|
||||||
delete id;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::process_post_runnables() {
|
void scripting::process_post_runnables() {
|
||||||
auto L = lua::get_main_state();
|
auto L = lua::get_main_state();
|
||||||
if (lua::getglobal(L, "__process_post_runnables")) {
|
if (lua::getglobal(L, "__process_post_runnables")) {
|
||||||
@ -599,244 +565,6 @@ bool scripting::on_item_break_block(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
dv::value scripting::get_component_value(
|
|
||||||
const scriptenv& env, const std::string& name
|
|
||||||
) {
|
|
||||||
auto L = lua::get_main_state();
|
|
||||||
lua::pushenv(L, *env);
|
|
||||||
if (lua::getfield(L, name)) {
|
|
||||||
return lua::tovalue(L, -1);
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_entity_spawn(
|
|
||||||
const EntityDef&,
|
|
||||||
entityid_t eid,
|
|
||||||
const std::vector<std::unique_ptr<UserComponent>>& components,
|
|
||||||
const dv::value& args,
|
|
||||||
const dv::value& saved
|
|
||||||
) {
|
|
||||||
auto L = lua::get_main_state();
|
|
||||||
lua::stackguard guard(L);
|
|
||||||
lua::requireglobal(L, STDCOMP);
|
|
||||||
if (lua::getfield(L, "new_Entity")) {
|
|
||||||
lua::pushinteger(L, eid);
|
|
||||||
lua::call(L, 1);
|
|
||||||
}
|
|
||||||
if (components.size() > 1) {
|
|
||||||
for (size_t i = 0; i < components.size() - 1; i++) {
|
|
||||||
lua::pushvalue(L, -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto& component : components) {
|
|
||||||
auto compenv = create_component_environment(
|
|
||||||
get_root_environment(), -1, component->name
|
|
||||||
);
|
|
||||||
lua::get_from(L, lua::CHUNKS_TABLE, component->name, true);
|
|
||||||
lua::pushenv(L, *compenv);
|
|
||||||
|
|
||||||
if (args != nullptr) {
|
|
||||||
std::string compfieldname = component->name;
|
|
||||||
util::replaceAll(compfieldname, ":", "__");
|
|
||||||
if (args.has(compfieldname)) {
|
|
||||||
lua::pushvalue(L, args[compfieldname]);
|
|
||||||
} else {
|
|
||||||
lua::createtable(L, 0, 0);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lua::createtable(L, 0, 0);
|
|
||||||
}
|
|
||||||
lua::setfield(L, "ARGS");
|
|
||||||
|
|
||||||
if (saved == nullptr) {
|
|
||||||
lua::createtable(L, 0, 0);
|
|
||||||
} else {
|
|
||||||
if (saved.has(component->name)) {
|
|
||||||
lua::pushvalue(L, saved[component->name]);
|
|
||||||
} else {
|
|
||||||
lua::createtable(L, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lua::setfield(L, "SAVED_DATA");
|
|
||||||
|
|
||||||
lua::setfenv(L);
|
|
||||||
lua::call_nothrow(L, 0, 0);
|
|
||||||
|
|
||||||
lua::pushenv(L, *compenv);
|
|
||||||
auto& funcsset = component->funcsset;
|
|
||||||
funcsset.on_grounded = lua::hasfield(L, "on_grounded");
|
|
||||||
funcsset.on_fall = lua::hasfield(L, "on_fall");
|
|
||||||
funcsset.on_despawn = lua::hasfield(L, "on_despawn");
|
|
||||||
funcsset.on_sensor_enter = lua::hasfield(L, "on_sensor_enter");
|
|
||||||
funcsset.on_sensor_exit = lua::hasfield(L, "on_sensor_exit");
|
|
||||||
funcsset.on_save = lua::hasfield(L, "on_save");
|
|
||||||
funcsset.on_aim_on = lua::hasfield(L, "on_aim_on");
|
|
||||||
funcsset.on_aim_off = lua::hasfield(L, "on_aim_off");
|
|
||||||
funcsset.on_attacked = lua::hasfield(L, "on_attacked");
|
|
||||||
funcsset.on_used = lua::hasfield(L, "on_used");
|
|
||||||
lua::pop(L, 2);
|
|
||||||
|
|
||||||
component->env = compenv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void process_entity_callback(
|
|
||||||
const scriptenv& env,
|
|
||||||
const std::string& name,
|
|
||||||
std::function<int(lua::State*)> args
|
|
||||||
) {
|
|
||||||
auto L = lua::get_main_state();
|
|
||||||
lua::pushenv(L, *env);
|
|
||||||
if (lua::hasfield(L, "__disabled")) {
|
|
||||||
lua::pop(L);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (lua::getfield(L, name)) {
|
|
||||||
if (args) {
|
|
||||||
lua::call_nothrow(L, args(L), 0);
|
|
||||||
} else {
|
|
||||||
lua::call_nothrow(L, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lua::pop(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void process_entity_callback(
|
|
||||||
const Entity& entity,
|
|
||||||
const std::string& name,
|
|
||||||
bool EntityFuncsSet::*flag,
|
|
||||||
std::function<int(lua::State*)> args
|
|
||||||
) {
|
|
||||||
const auto& script = entity.getScripting();
|
|
||||||
for (auto& component : script.components) {
|
|
||||||
if (component->funcsset.*flag) {
|
|
||||||
process_entity_callback(component->env, name, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_entity_despawn(const Entity& entity) {
|
|
||||||
process_entity_callback(
|
|
||||||
entity, "on_despawn", &EntityFuncsSet::on_despawn, nullptr
|
|
||||||
);
|
|
||||||
auto L = lua::get_main_state();
|
|
||||||
lua::get_from(L, "stdcomp", "remove_Entity", true);
|
|
||||||
lua::pushinteger(L, entity.getUID());
|
|
||||||
lua::call(L, 1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_entity_grounded(const Entity& entity, float force) {
|
|
||||||
process_entity_callback(
|
|
||||||
entity,
|
|
||||||
"on_grounded",
|
|
||||||
&EntityFuncsSet::on_grounded,
|
|
||||||
[force](auto L) { return lua::pushnumber(L, force); }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_entity_fall(const Entity& entity) {
|
|
||||||
process_entity_callback(
|
|
||||||
entity, "on_fall", &EntityFuncsSet::on_fall, nullptr
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_entity_save(const Entity& entity) {
|
|
||||||
process_entity_callback(
|
|
||||||
entity, "on_save", &EntityFuncsSet::on_save, nullptr
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_sensor_enter(
|
|
||||||
const Entity& entity, size_t index, entityid_t oid
|
|
||||||
) {
|
|
||||||
process_entity_callback(
|
|
||||||
entity,
|
|
||||||
"on_sensor_enter",
|
|
||||||
&EntityFuncsSet::on_sensor_enter,
|
|
||||||
[index, oid](auto L) {
|
|
||||||
lua::pushinteger(L, index);
|
|
||||||
lua::pushinteger(L, oid);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_sensor_exit(
|
|
||||||
const Entity& entity, size_t index, entityid_t oid
|
|
||||||
) {
|
|
||||||
process_entity_callback(
|
|
||||||
entity,
|
|
||||||
"on_sensor_exit",
|
|
||||||
&EntityFuncsSet::on_sensor_exit,
|
|
||||||
[index, oid](auto L) {
|
|
||||||
lua::pushinteger(L, index);
|
|
||||||
lua::pushinteger(L, oid);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_aim_on(const Entity& entity, Player* player) {
|
|
||||||
process_entity_callback(
|
|
||||||
entity,
|
|
||||||
"on_aim_on",
|
|
||||||
&EntityFuncsSet::on_aim_on,
|
|
||||||
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_aim_off(const Entity& entity, Player* player) {
|
|
||||||
process_entity_callback(
|
|
||||||
entity,
|
|
||||||
"on_aim_off",
|
|
||||||
&EntityFuncsSet::on_aim_off,
|
|
||||||
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_attacked(
|
|
||||||
const Entity& entity, Player* player, entityid_t attacker
|
|
||||||
) {
|
|
||||||
process_entity_callback(
|
|
||||||
entity,
|
|
||||||
"on_attacked",
|
|
||||||
&EntityFuncsSet::on_attacked,
|
|
||||||
[player, attacker](auto L) {
|
|
||||||
lua::pushinteger(L, attacker);
|
|
||||||
lua::pushinteger(L, player->getId());
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_entity_used(const Entity& entity, Player* player) {
|
|
||||||
process_entity_callback(
|
|
||||||
entity,
|
|
||||||
"on_used",
|
|
||||||
&EntityFuncsSet::on_used,
|
|
||||||
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_entities_update(int tps, int parts, int part) {
|
|
||||||
auto L = lua::get_main_state();
|
|
||||||
lua::get_from(L, STDCOMP, "update", true);
|
|
||||||
lua::pushinteger(L, tps);
|
|
||||||
lua::pushinteger(L, parts);
|
|
||||||
lua::pushinteger(L, part);
|
|
||||||
lua::call_nothrow(L, 3, 0);
|
|
||||||
lua::pop(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_entities_render(float delta) {
|
|
||||||
auto L = lua::get_main_state();
|
|
||||||
lua::get_from(L, STDCOMP, "render", true);
|
|
||||||
lua::pushnumber(L, delta);
|
|
||||||
lua::call_nothrow(L, 1, 0);
|
|
||||||
lua::pop(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_ui_open(
|
void scripting::on_ui_open(
|
||||||
UiDocument* layout, std::vector<dv::value> args
|
UiDocument* layout, std::vector<dv::value> args
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -139,6 +139,7 @@ namespace scripting {
|
|||||||
void on_entity_fall(const Entity& entity);
|
void on_entity_fall(const Entity& entity);
|
||||||
void on_entity_save(const Entity& entity);
|
void on_entity_save(const Entity& entity);
|
||||||
void on_entities_update(int tps, int parts, int part);
|
void on_entities_update(int tps, int parts, int part);
|
||||||
|
void on_entities_physics_update(float delta);
|
||||||
void on_entities_render(float delta);
|
void on_entities_render(float delta);
|
||||||
void on_sensor_enter(const Entity& entity, size_t index, entityid_t oid);
|
void on_sensor_enter(const Entity& entity, size_t index, entityid_t oid);
|
||||||
void on_sensor_exit(const Entity& entity, size_t index, entityid_t oid);
|
void on_sensor_exit(const Entity& entity, size_t index, entityid_t oid);
|
||||||
|
|||||||
296
src/logic/scripting/scripting_entities.cpp
Normal file
296
src/logic/scripting/scripting_entities.cpp
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
#include "scripting.hpp"
|
||||||
|
|
||||||
|
#include "lua/lua_engine.hpp"
|
||||||
|
#include "objects/Entities.hpp"
|
||||||
|
#include "objects/EntityDef.hpp"
|
||||||
|
#include "objects/Entity.hpp"
|
||||||
|
#include "objects/Player.hpp"
|
||||||
|
#include "util/stringutil.hpp"
|
||||||
|
|
||||||
|
using namespace scripting;
|
||||||
|
|
||||||
|
static inline const std::string STDCOMP = "stdcomp";
|
||||||
|
|
||||||
|
[[nodiscard]] static scriptenv create_component_environment(
|
||||||
|
const scriptenv& parent, int entityIdx, const std::string& name
|
||||||
|
) {
|
||||||
|
auto L = lua::get_main_state();
|
||||||
|
int id = lua::create_environment(L, *parent);
|
||||||
|
|
||||||
|
lua::pushvalue(L, entityIdx);
|
||||||
|
|
||||||
|
lua::pushenv(L, id);
|
||||||
|
|
||||||
|
lua::pushvalue(L, -1);
|
||||||
|
lua::setfield(L, "this");
|
||||||
|
|
||||||
|
lua::pushvalue(L, -2);
|
||||||
|
lua::setfield(L, "entity");
|
||||||
|
|
||||||
|
lua::pop(L);
|
||||||
|
if (lua::getfield(L, "components")) {
|
||||||
|
lua::pushenv(L, id);
|
||||||
|
lua::setfield(L, name);
|
||||||
|
lua::pop(L);
|
||||||
|
}
|
||||||
|
lua::pop(L);
|
||||||
|
|
||||||
|
return std::shared_ptr<int>(new int(id), [=](int* id) { //-V508
|
||||||
|
lua::remove_environment(L, *id);
|
||||||
|
delete id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dv::value scripting::get_component_value(
|
||||||
|
const scriptenv& env, const std::string& name
|
||||||
|
) {
|
||||||
|
auto L = lua::get_main_state();
|
||||||
|
lua::pushenv(L, *env);
|
||||||
|
if (lua::getfield(L, name)) {
|
||||||
|
return lua::tovalue(L, -1);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void create_component(
|
||||||
|
lua::State* L,
|
||||||
|
int entityIdx,
|
||||||
|
UserComponent& component,
|
||||||
|
const dv::value& args,
|
||||||
|
const dv::value& saved
|
||||||
|
) {
|
||||||
|
lua::pushvalue(L, entityIdx);
|
||||||
|
auto compenv = create_component_environment(
|
||||||
|
get_root_environment(), -1, component.name
|
||||||
|
);
|
||||||
|
lua::get_from(L, lua::CHUNKS_TABLE, component.name, true);
|
||||||
|
lua::pushenv(L, *compenv);
|
||||||
|
|
||||||
|
if (args != nullptr) {
|
||||||
|
std::string compfieldname = component.name;
|
||||||
|
util::replaceAll(compfieldname, ":", "__");
|
||||||
|
if (args.has(compfieldname)) {
|
||||||
|
lua::pushvalue(L, args[compfieldname]);
|
||||||
|
} else {
|
||||||
|
lua::createtable(L, 0, 0);
|
||||||
|
}
|
||||||
|
} else if (component.params != nullptr) {
|
||||||
|
lua::pushvalue(L, component.params);
|
||||||
|
} else {
|
||||||
|
lua::createtable(L, 0, 0);
|
||||||
|
}
|
||||||
|
lua::setfield(L, "ARGS");
|
||||||
|
|
||||||
|
if (saved == nullptr) {
|
||||||
|
lua::createtable(L, 0, 0);
|
||||||
|
} else {
|
||||||
|
if (saved.has(component.name)) {
|
||||||
|
lua::pushvalue(L, saved[component.name]);
|
||||||
|
} else {
|
||||||
|
lua::createtable(L, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua::setfield(L, "SAVED_DATA");
|
||||||
|
|
||||||
|
lua::setfenv(L);
|
||||||
|
lua::call_nothrow(L, 0, 0);
|
||||||
|
|
||||||
|
lua::pushenv(L, *compenv);
|
||||||
|
auto& funcsset = component.funcsset;
|
||||||
|
funcsset.on_grounded = lua::hasfield(L, "on_grounded");
|
||||||
|
funcsset.on_fall = lua::hasfield(L, "on_fall");
|
||||||
|
funcsset.on_despawn = lua::hasfield(L, "on_despawn");
|
||||||
|
funcsset.on_sensor_enter = lua::hasfield(L, "on_sensor_enter");
|
||||||
|
funcsset.on_sensor_exit = lua::hasfield(L, "on_sensor_exit");
|
||||||
|
funcsset.on_save = lua::hasfield(L, "on_save");
|
||||||
|
funcsset.on_aim_on = lua::hasfield(L, "on_aim_on");
|
||||||
|
funcsset.on_aim_off = lua::hasfield(L, "on_aim_off");
|
||||||
|
funcsset.on_attacked = lua::hasfield(L, "on_attacked");
|
||||||
|
funcsset.on_used = lua::hasfield(L, "on_used");
|
||||||
|
lua::pop(L, 2);
|
||||||
|
|
||||||
|
component.env = compenv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::on_entity_spawn(
|
||||||
|
const EntityDef&,
|
||||||
|
entityid_t eid,
|
||||||
|
const std::vector<std::unique_ptr<UserComponent>>& components,
|
||||||
|
const dv::value& args,
|
||||||
|
const dv::value& saved
|
||||||
|
) {
|
||||||
|
auto L = lua::get_main_state();
|
||||||
|
lua::stackguard guard(L);
|
||||||
|
lua::requireglobal(L, STDCOMP);
|
||||||
|
if (lua::getfield(L, "new_Entity")) {
|
||||||
|
lua::pushinteger(L, eid);
|
||||||
|
lua::call(L, 1);
|
||||||
|
}
|
||||||
|
for (auto& component : components) {
|
||||||
|
create_component(L, -1, *component, args, saved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_entity_callback(
|
||||||
|
const scriptenv& env,
|
||||||
|
const std::string& name,
|
||||||
|
std::function<int(lua::State*)> args
|
||||||
|
) {
|
||||||
|
auto L = lua::get_main_state();
|
||||||
|
lua::pushenv(L, *env);
|
||||||
|
if (lua::hasfield(L, "__disabled")) {
|
||||||
|
lua::pop(L);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (lua::getfield(L, name)) {
|
||||||
|
if (args) {
|
||||||
|
lua::call_nothrow(L, args(L), 0);
|
||||||
|
} else {
|
||||||
|
lua::call_nothrow(L, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua::pop(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_entity_callback(
|
||||||
|
const Entity& entity,
|
||||||
|
const std::string& name,
|
||||||
|
bool EntityFuncsSet::*flag,
|
||||||
|
std::function<int(lua::State*)> args
|
||||||
|
) {
|
||||||
|
const auto& script = entity.getScripting();
|
||||||
|
for (auto& component : script.components) {
|
||||||
|
if (component->funcsset.*flag) {
|
||||||
|
process_entity_callback(component->env, name, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::on_entity_despawn(const Entity& entity) {
|
||||||
|
process_entity_callback(
|
||||||
|
entity, "on_despawn", &EntityFuncsSet::on_despawn, nullptr
|
||||||
|
);
|
||||||
|
auto L = lua::get_main_state();
|
||||||
|
lua::get_from(L, "stdcomp", "remove_Entity", true);
|
||||||
|
lua::pushinteger(L, entity.getUID());
|
||||||
|
lua::call(L, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::on_entity_grounded(const Entity& entity, float force) {
|
||||||
|
process_entity_callback(
|
||||||
|
entity,
|
||||||
|
"on_grounded",
|
||||||
|
&EntityFuncsSet::on_grounded,
|
||||||
|
[force](auto L) { return lua::pushnumber(L, force); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::on_entity_fall(const Entity& entity) {
|
||||||
|
process_entity_callback(
|
||||||
|
entity, "on_fall", &EntityFuncsSet::on_fall, nullptr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::on_entity_save(const Entity& entity) {
|
||||||
|
process_entity_callback(
|
||||||
|
entity, "on_save", &EntityFuncsSet::on_save, nullptr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::on_sensor_enter(
|
||||||
|
const Entity& entity, size_t index, entityid_t oid
|
||||||
|
) {
|
||||||
|
process_entity_callback(
|
||||||
|
entity,
|
||||||
|
"on_sensor_enter",
|
||||||
|
&EntityFuncsSet::on_sensor_enter,
|
||||||
|
[index, oid](auto L) {
|
||||||
|
lua::pushinteger(L, index);
|
||||||
|
lua::pushinteger(L, oid);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::on_sensor_exit(
|
||||||
|
const Entity& entity, size_t index, entityid_t oid
|
||||||
|
) {
|
||||||
|
process_entity_callback(
|
||||||
|
entity,
|
||||||
|
"on_sensor_exit",
|
||||||
|
&EntityFuncsSet::on_sensor_exit,
|
||||||
|
[index, oid](auto L) {
|
||||||
|
lua::pushinteger(L, index);
|
||||||
|
lua::pushinteger(L, oid);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::on_aim_on(const Entity& entity, Player* player) {
|
||||||
|
process_entity_callback(
|
||||||
|
entity,
|
||||||
|
"on_aim_on",
|
||||||
|
&EntityFuncsSet::on_aim_on,
|
||||||
|
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::on_aim_off(const Entity& entity, Player* player) {
|
||||||
|
process_entity_callback(
|
||||||
|
entity,
|
||||||
|
"on_aim_off",
|
||||||
|
&EntityFuncsSet::on_aim_off,
|
||||||
|
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::on_attacked(
|
||||||
|
const Entity& entity, Player* player, entityid_t attacker
|
||||||
|
) {
|
||||||
|
process_entity_callback(
|
||||||
|
entity,
|
||||||
|
"on_attacked",
|
||||||
|
&EntityFuncsSet::on_attacked,
|
||||||
|
[player, attacker](auto L) {
|
||||||
|
lua::pushinteger(L, attacker);
|
||||||
|
lua::pushinteger(L, player->getId());
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::on_entity_used(const Entity& entity, Player* player) {
|
||||||
|
process_entity_callback(
|
||||||
|
entity,
|
||||||
|
"on_used",
|
||||||
|
&EntityFuncsSet::on_used,
|
||||||
|
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::on_entities_update(int tps, int parts, int part) {
|
||||||
|
auto L = lua::get_main_state();
|
||||||
|
lua::get_from(L, STDCOMP, "update", true);
|
||||||
|
lua::pushinteger(L, tps);
|
||||||
|
lua::pushinteger(L, parts);
|
||||||
|
lua::pushinteger(L, part);
|
||||||
|
lua::call_nothrow(L, 3, 0);
|
||||||
|
lua::pop(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::on_entities_physics_update(float delta) {
|
||||||
|
auto L = lua::get_main_state();
|
||||||
|
lua::get_from(L, STDCOMP, "physics_update", true);
|
||||||
|
lua::pushnumber(L, delta);
|
||||||
|
lua::call_nothrow(L, 1, 0);
|
||||||
|
lua::pop(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::on_entities_render(float delta) {
|
||||||
|
auto L = lua::get_main_state();
|
||||||
|
lua::get_from(L, STDCOMP, "render", true);
|
||||||
|
lua::pushnumber(L, delta);
|
||||||
|
lua::call_nothrow(L, 1, 0);
|
||||||
|
lua::pop(L);
|
||||||
|
}
|
||||||
@ -743,7 +743,6 @@ public:
|
|||||||
|
|
||||||
class SocketUdpServer : public UdpServer {
|
class SocketUdpServer : public UdpServer {
|
||||||
u64id_t id;
|
u64id_t id;
|
||||||
Network* network;
|
|
||||||
SOCKET descriptor;
|
SOCKET descriptor;
|
||||||
bool open = true;
|
bool open = true;
|
||||||
std::unique_ptr<std::thread> thread = nullptr;
|
std::unique_ptr<std::thread> thread = nullptr;
|
||||||
@ -752,13 +751,13 @@ class SocketUdpServer : public UdpServer {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
SocketUdpServer(u64id_t id, Network* network, SOCKET descriptor, int port)
|
SocketUdpServer(u64id_t id, Network* network, SOCKET descriptor, int port)
|
||||||
: id(id), network(network), descriptor(descriptor), port(port) {}
|
: id(id), descriptor(descriptor), port(port) {}
|
||||||
|
|
||||||
~SocketUdpServer() override {
|
~SocketUdpServer() override {
|
||||||
SocketUdpServer::close();
|
SocketUdpServer::close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void startListen(ServerDatagramCallback handler) {
|
void startListen(ServerDatagramCallback handler) override {
|
||||||
callback = std::move(handler);
|
callback = std::move(handler);
|
||||||
|
|
||||||
thread = std::make_unique<std::thread>([this]() {
|
thread = std::make_unique<std::thread>([this]() {
|
||||||
|
|||||||
@ -17,110 +17,25 @@
|
|||||||
#include "maths/FrustumCulling.hpp"
|
#include "maths/FrustumCulling.hpp"
|
||||||
#include "maths/rays.hpp"
|
#include "maths/rays.hpp"
|
||||||
#include "EntityDef.hpp"
|
#include "EntityDef.hpp"
|
||||||
|
#include "Entity.hpp"
|
||||||
#include "rigging.hpp"
|
#include "rigging.hpp"
|
||||||
#include "physics/Hitbox.hpp"
|
|
||||||
#include "physics/PhysicsSolver.hpp"
|
#include "physics/PhysicsSolver.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
|
|
||||||
static debug::Logger logger("entities");
|
static debug::Logger logger("entities");
|
||||||
|
|
||||||
static inline std::string COMP_TRANSFORM = "transform";
|
|
||||||
static inline std::string COMP_RIGIDBODY = "rigidbody";
|
|
||||||
static inline std::string COMP_SKELETON = "skeleton";
|
|
||||||
static inline std::string SAVED_DATA_VARNAME = "SAVED_DATA";
|
|
||||||
|
|
||||||
void Transform::refresh() {
|
|
||||||
combined = glm::mat4(1.0f);
|
|
||||||
combined = glm::translate(combined, pos);
|
|
||||||
combined = combined * glm::mat4(rot);
|
|
||||||
combined = glm::scale(combined, size);
|
|
||||||
displayPos = pos;
|
|
||||||
displaySize = size;
|
|
||||||
dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::setInterpolatedPosition(const glm::vec3& position) {
|
|
||||||
getSkeleton().interpolation.refresh(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 Entity::getInterpolatedPosition() const {
|
|
||||||
const auto& skeleton = getSkeleton();
|
|
||||||
if (skeleton.interpolation.isEnabled()) {
|
|
||||||
return skeleton.interpolation.getCurrent();
|
|
||||||
}
|
|
||||||
return getTransform().pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::destroy() {
|
|
||||||
if (isValid()) {
|
|
||||||
entities.despawn(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rigging::Skeleton& Entity::getSkeleton() const {
|
|
||||||
return registry.get<rigging::Skeleton>(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::setRig(const rigging::SkeletonConfig* rigConfig) {
|
|
||||||
auto& skeleton = registry.get<rigging::Skeleton>(entity);
|
|
||||||
skeleton.config = rigConfig;
|
|
||||||
skeleton.pose.matrices.resize(
|
|
||||||
rigConfig->getBones().size(), glm::mat4(1.0f)
|
|
||||||
);
|
|
||||||
skeleton.calculated.matrices.resize(
|
|
||||||
rigConfig->getBones().size(), glm::mat4(1.0f)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Entities::Entities(Level& level)
|
Entities::Entities(Level& level)
|
||||||
: level(level), sensorsTickClock(20, 3), updateTickClock(20, 3) {
|
: level(level),
|
||||||
|
sensorsTickClock(20, 3),
|
||||||
|
updateTickClock(20, 3) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <void (*callback)(const Entity&, size_t, entityid_t)>
|
std::optional<Entity> Entities::get(entityid_t id) {
|
||||||
static sensorcallback create_sensor_callback(Entities* entities) {
|
const auto& found = entities.find(id);
|
||||||
return [=](auto entityid, auto index, auto otherid) {
|
if (found != entities.end() && registry.valid(found->second)) {
|
||||||
if (auto entity = entities->get(entityid)) {
|
return Entity(*this, id, registry, found->second);
|
||||||
if (entity->isValid()) {
|
|
||||||
callback(*entity, index, otherid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static void initialize_body(
|
|
||||||
const EntityDef& def, Rigidbody& body, entityid_t id, Entities* entities
|
|
||||||
) {
|
|
||||||
body.sensors.resize(def.radialSensors.size() + def.boxSensors.size());
|
|
||||||
for (auto& [i, box] : def.boxSensors) {
|
|
||||||
SensorParams params {};
|
|
||||||
params.aabb = box;
|
|
||||||
body.sensors[i] = Sensor {
|
|
||||||
true,
|
|
||||||
SensorType::AABB,
|
|
||||||
i,
|
|
||||||
id,
|
|
||||||
params,
|
|
||||||
params,
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
create_sensor_callback<scripting::on_sensor_enter>(entities),
|
|
||||||
create_sensor_callback<scripting::on_sensor_exit>(entities)};
|
|
||||||
}
|
|
||||||
for (auto& [i, radius] : def.radialSensors) {
|
|
||||||
SensorParams params {};
|
|
||||||
params.radial = glm::vec4(radius);
|
|
||||||
body.sensors[i] = Sensor {
|
|
||||||
true,
|
|
||||||
SensorType::RADIUS,
|
|
||||||
i,
|
|
||||||
id,
|
|
||||||
params,
|
|
||||||
params,
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
create_sensor_callback<scripting::on_sensor_enter>(entities),
|
|
||||||
create_sensor_callback<scripting::on_sensor_exit>(entities)};
|
|
||||||
}
|
}
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
entityid_t Entities::spawn(
|
entityid_t Entities::spawn(
|
||||||
@ -168,14 +83,14 @@ entityid_t Entities::spawn(
|
|||||||
Hitbox {def.bodyType, position, def.hitbox * 0.5f},
|
Hitbox {def.bodyType, position, def.hitbox * 0.5f},
|
||||||
std::vector<Sensor> {}
|
std::vector<Sensor> {}
|
||||||
);
|
);
|
||||||
initialize_body(def, body, id, this);
|
body.initialize(def, id, *this);
|
||||||
|
|
||||||
auto& scripting = registry.emplace<ScriptComponents>(entity);
|
auto& scripting = registry.emplace<ScriptComponents>(entity);
|
||||||
registry.emplace<rigging::Skeleton>(entity, skeleton->instance());
|
registry.emplace<rigging::Skeleton>(entity, skeleton->instance());
|
||||||
|
|
||||||
for (auto& componentName : def.components) {
|
for (auto& instance : def.components) {
|
||||||
auto component = std::make_unique<UserComponent>(
|
auto component = std::make_unique<UserComponent>(
|
||||||
componentName, EntityFuncsSet {}, nullptr
|
instance.component, EntityFuncsSet {}, nullptr, instance.params
|
||||||
);
|
);
|
||||||
scripting.components.emplace_back(std::move(component));
|
scripting.components.emplace_back(std::move(component));
|
||||||
}
|
}
|
||||||
@ -186,7 +101,8 @@ entityid_t Entities::spawn(
|
|||||||
}
|
}
|
||||||
body.hitbox.position = tsf.pos;
|
body.hitbox.position = tsf.pos;
|
||||||
scripting::on_entity_spawn(
|
scripting::on_entity_spawn(
|
||||||
def, id, scripting.components, args, componentsMap);
|
def, id, scripting.components, args, componentsMap
|
||||||
|
);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,41 +129,18 @@ void Entities::loadEntity(const dv::value& map, Entity entity) {
|
|||||||
auto& skeleton = entity.getSkeleton();
|
auto& skeleton = entity.getSkeleton();
|
||||||
|
|
||||||
if (map.has(COMP_RIGIDBODY)) {
|
if (map.has(COMP_RIGIDBODY)) {
|
||||||
auto& bodymap = map[COMP_RIGIDBODY];
|
body.deserialize(map[COMP_RIGIDBODY]);
|
||||||
dv::get_vec(bodymap, "vel", body.hitbox.velocity);
|
|
||||||
std::string bodyTypeName;
|
|
||||||
map.at("type").get(bodyTypeName);
|
|
||||||
BodyTypeMeta.getItem(bodyTypeName, body.hitbox.type);
|
|
||||||
bodymap["crouch"].asBoolean(body.hitbox.crouching);
|
|
||||||
bodymap["damping"].asNumber(body.hitbox.linearDamping);
|
|
||||||
}
|
}
|
||||||
if (map.has(COMP_TRANSFORM)) {
|
if (map.has(COMP_TRANSFORM)) {
|
||||||
auto& tsfmap = map[COMP_TRANSFORM];
|
transform.deserialize(map[COMP_TRANSFORM]);
|
||||||
dv::get_vec(tsfmap, "pos", transform.pos);
|
|
||||||
dv::get_vec(tsfmap, "size", transform.size);
|
|
||||||
dv::get_mat(tsfmap, "rot", transform.rot);
|
|
||||||
}
|
}
|
||||||
std::string skeletonName = skeleton.config->getName();
|
std::string skeletonName = skeleton.config->getName();
|
||||||
map.at("skeleton").get(skeletonName);
|
map.at("skeleton").get(skeletonName);
|
||||||
if (skeletonName != skeleton.config->getName()) {
|
if (skeletonName != skeleton.config->getName()) {
|
||||||
skeleton.config = level.content.getSkeleton(skeletonName);
|
skeleton.config = level.content.getSkeleton(skeletonName);
|
||||||
}
|
}
|
||||||
if (auto found = map.at(COMP_SKELETON)) {
|
if (auto foundSkeleton = map.at(COMP_SKELETON)) {
|
||||||
auto& skeletonmap = *found;
|
skeleton.deserialize(*foundSkeleton);
|
||||||
if (auto found = skeletonmap.at("textures")) {
|
|
||||||
auto& texturesmap = *found;
|
|
||||||
for (auto& [slot, _] : texturesmap.asObject()) {
|
|
||||||
texturesmap.at(slot).get(skeleton.textures[slot]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (auto found = skeletonmap.at("pose")) {
|
|
||||||
auto& posearr = *found;
|
|
||||||
for (size_t i = 0;
|
|
||||||
i < std::min(skeleton.pose.matrices.size(), posearr.size());
|
|
||||||
i++) {
|
|
||||||
dv::get_mat(posearr[i], skeleton.pose.matrices[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,7 +177,7 @@ std::optional<Entities::RaycastResult> Entities::rayCast(
|
|||||||
|
|
||||||
void Entities::loadEntities(dv::value root) {
|
void Entities::loadEntities(dv::value root) {
|
||||||
clean();
|
clean();
|
||||||
auto& list = root["data"];
|
const auto& list = root["data"];
|
||||||
for (auto& map : list) {
|
for (auto& map : list) {
|
||||||
try {
|
try {
|
||||||
loadEntity(map);
|
loadEntity(map);
|
||||||
@ -298,74 +191,6 @@ void Entities::onSave(const Entity& entity) {
|
|||||||
scripting::on_entity_save(entity);
|
scripting::on_entity_save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
dv::value Entities::serialize(const Entity& entity) {
|
|
||||||
auto root = dv::object();
|
|
||||||
auto& eid = entity.getID();
|
|
||||||
auto& def = eid.def;
|
|
||||||
root["def"] = def.name;
|
|
||||||
root["uid"] = eid.uid;
|
|
||||||
{
|
|
||||||
auto& transform = entity.getTransform();
|
|
||||||
auto& tsfmap = root.object(COMP_TRANSFORM);
|
|
||||||
tsfmap["pos"] = dv::to_value(transform.pos);
|
|
||||||
if (transform.size != glm::vec3(1.0f)) {
|
|
||||||
tsfmap["size"] = dv::to_value(transform.size);
|
|
||||||
}
|
|
||||||
if (transform.rot != glm::mat3(1.0f)) {
|
|
||||||
tsfmap["rot"] = dv::to_value(transform.rot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto& rigidbody = entity.getRigidbody();
|
|
||||||
auto& hitbox = rigidbody.hitbox;
|
|
||||||
auto& bodymap = root.object(COMP_RIGIDBODY);
|
|
||||||
if (!rigidbody.enabled) {
|
|
||||||
bodymap["enabled"] = false;
|
|
||||||
}
|
|
||||||
if (def.save.body.velocity) {
|
|
||||||
bodymap["vel"] = dv::to_value(rigidbody.hitbox.velocity);
|
|
||||||
}
|
|
||||||
if (def.save.body.settings) {
|
|
||||||
bodymap["damping"] = rigidbody.hitbox.linearDamping;
|
|
||||||
if (hitbox.type != def.bodyType) {
|
|
||||||
bodymap["type"] = BodyTypeMeta.getNameString(hitbox.type);
|
|
||||||
}
|
|
||||||
if (hitbox.crouching) {
|
|
||||||
bodymap["crouch"] = hitbox.crouching;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto& skeleton = entity.getSkeleton();
|
|
||||||
if (skeleton.config->getName() != def.skeletonName) {
|
|
||||||
root["skeleton"] = skeleton.config->getName();
|
|
||||||
}
|
|
||||||
if (def.save.skeleton.pose || def.save.skeleton.textures) {
|
|
||||||
auto& skeletonmap = root.object(COMP_SKELETON);
|
|
||||||
if (def.save.skeleton.textures) {
|
|
||||||
auto& map = skeletonmap.object("textures");
|
|
||||||
for (auto& [slot, texture] : skeleton.textures) {
|
|
||||||
map[slot] = texture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (def.save.skeleton.pose) {
|
|
||||||
auto& list = skeletonmap.list("pose");
|
|
||||||
for (auto& mat : skeleton.pose.matrices) {
|
|
||||||
list.add(dv::to_value(mat));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto& scripts = entity.getScripting();
|
|
||||||
if (!scripts.components.empty()) {
|
|
||||||
auto& compsMap = root.object("comps");
|
|
||||||
for (auto& comp : scripts.components) {
|
|
||||||
auto data =
|
|
||||||
scripting::get_component_value(comp->env, SAVED_DATA_VARNAME);
|
|
||||||
compsMap[comp->name] = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
dv::value Entities::serialize(const std::vector<Entity>& entities) {
|
dv::value Entities::serialize(const std::vector<Entity>& entities) {
|
||||||
auto list = dv::list();
|
auto list = dv::list();
|
||||||
for (auto& entity : entities) {
|
for (auto& entity : entities) {
|
||||||
@ -375,7 +200,7 @@ dv::value Entities::serialize(const std::vector<Entity>& entities) {
|
|||||||
}
|
}
|
||||||
level.entities->onSave(entity);
|
level.entities->onSave(entity);
|
||||||
if (!eid.destroyFlag) {
|
if (!eid.destroyFlag) {
|
||||||
list.add(level.entities->serialize(entity));
|
list.add(entity.serialize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
@ -474,7 +299,9 @@ void Entities::updatePhysics(float delta) {
|
|||||||
int substeps = static_cast<int>(delta * vel * 20);
|
int substeps = static_cast<int>(delta * vel * 20);
|
||||||
substeps = std::min(100, std::max(2, substeps));
|
substeps = std::min(100, std::max(2, substeps));
|
||||||
physics->step(*level.chunks, hitbox, delta, substeps, eid.uid);
|
physics->step(*level.chunks, hitbox, delta, substeps, eid.uid);
|
||||||
hitbox.linearDamping = hitbox.grounded * 24;
|
hitbox.friction = glm::abs(hitbox.gravityScale <= 1e-7f)
|
||||||
|
? 8.0f
|
||||||
|
: (!grounded ? 2.0f : 10.0f);
|
||||||
transform.setPos(hitbox.position);
|
transform.setPos(hitbox.position);
|
||||||
if (hitbox.grounded && !grounded) {
|
if (hitbox.grounded && !grounded) {
|
||||||
scripting::on_entity_grounded(
|
scripting::on_entity_grounded(
|
||||||
@ -495,6 +322,8 @@ void Entities::update(float delta) {
|
|||||||
updateTickClock.getPart()
|
updateTickClock.getPart()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
updatePhysics(delta);
|
||||||
|
scripting::on_entities_physics_update(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void debug_render_skeleton(
|
static void debug_render_skeleton(
|
||||||
@ -505,13 +334,10 @@ static void debug_render_skeleton(
|
|||||||
size_t pindex = bone->getIndex();
|
size_t pindex = bone->getIndex();
|
||||||
for (auto& sub : bone->getSubnodes()) {
|
for (auto& sub : bone->getSubnodes()) {
|
||||||
size_t sindex = sub->getIndex();
|
size_t sindex = sub->getIndex();
|
||||||
|
const auto& matrices = skeleton.calculated.matrices;
|
||||||
batch.line(
|
batch.line(
|
||||||
glm::vec3(
|
glm::vec3(matrices[pindex] * glm::vec4(0, 0, 0, 1)),
|
||||||
skeleton.calculated.matrices[pindex] * glm::vec4(0, 0, 0, 1)
|
glm::vec3(matrices[sindex] * glm::vec4(0, 0, 0, 1)),
|
||||||
),
|
|
||||||
glm::vec3(
|
|
||||||
skeleton.calculated.matrices[sindex] * glm::vec4(0, 0, 0, 1)
|
|
||||||
),
|
|
||||||
glm::vec4(0, 0.5f, 0, 1)
|
glm::vec4(0, 0.5f, 0, 1)
|
||||||
);
|
);
|
||||||
debug_render_skeleton(batch, sub.get(), skeleton);
|
debug_render_skeleton(batch, sub.get(), skeleton);
|
||||||
@ -568,10 +394,14 @@ void Entities::render(
|
|||||||
ModelBatch& batch,
|
ModelBatch& batch,
|
||||||
const Frustum* frustum,
|
const Frustum* frustum,
|
||||||
float delta,
|
float delta,
|
||||||
bool pause
|
bool pause,
|
||||||
|
entityid_t fpsEntity
|
||||||
) {
|
) {
|
||||||
auto view = registry.view<Transform, rigging::Skeleton>();
|
auto view = registry.view<EntityId, Transform, rigging::Skeleton>();
|
||||||
for (auto [entity, transform, skeleton] : view.each()) {
|
for (auto [entity, eid, transform, skeleton] : view.each()) {
|
||||||
|
if (eid.uid == fpsEntity) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (transform.dirty) {
|
if (transform.dirty) {
|
||||||
transform.refresh();
|
transform.refresh();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,101 +5,21 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "data/dv.hpp"
|
|
||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
|
#include "Transform.hpp"
|
||||||
|
#include "Rigidbody.hpp"
|
||||||
|
#include "ScriptComponents.hpp"
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
#include "util/Clock.hpp"
|
#include "util/Clock.hpp"
|
||||||
#define GLM_ENABLE_EXPERIMENTAL
|
|
||||||
#include <entt/entity/registry.hpp>
|
|
||||||
#include <glm/gtx/norm.hpp>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
struct EntityFuncsSet {
|
#include <entt/entity/registry.hpp>
|
||||||
bool init;
|
#include <unordered_map>
|
||||||
bool on_despawn;
|
|
||||||
bool on_grounded;
|
|
||||||
bool on_fall;
|
|
||||||
bool on_sensor_enter;
|
|
||||||
bool on_sensor_exit;
|
|
||||||
bool on_save;
|
|
||||||
bool on_aim_on;
|
|
||||||
bool on_aim_off;
|
|
||||||
bool on_attacked;
|
|
||||||
bool on_used;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct EntityDef;
|
struct EntityDef;
|
||||||
|
|
||||||
struct EntityId {
|
|
||||||
entityid_t uid;
|
|
||||||
const EntityDef& def;
|
|
||||||
bool destroyFlag = false;
|
|
||||||
int64_t player = -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Transform {
|
|
||||||
static inline constexpr float EPSILON = 0.0000001f;
|
|
||||||
glm::vec3 pos;
|
|
||||||
glm::vec3 size;
|
|
||||||
glm::mat3 rot;
|
|
||||||
glm::mat4 combined;
|
|
||||||
bool dirty = true;
|
|
||||||
|
|
||||||
glm::vec3 displayPos;
|
|
||||||
glm::vec3 displaySize;
|
|
||||||
|
|
||||||
void refresh();
|
|
||||||
|
|
||||||
inline void setRot(glm::mat3 m) {
|
|
||||||
rot = m;
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void setSize(glm::vec3 v) {
|
|
||||||
if (glm::distance2(displaySize, v) >= EPSILON) {
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
size = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void setPos(glm::vec3 v) {
|
|
||||||
if (glm::distance2(displayPos, v) >= EPSILON) {
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
pos = v;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Rigidbody {
|
|
||||||
bool enabled = true;
|
|
||||||
Hitbox hitbox;
|
|
||||||
std::vector<Sensor> sensors;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UserComponent {
|
|
||||||
std::string name;
|
|
||||||
EntityFuncsSet funcsset;
|
|
||||||
scriptenv env;
|
|
||||||
|
|
||||||
UserComponent(
|
|
||||||
const std::string& name, EntityFuncsSet funcsset, scriptenv env
|
|
||||||
)
|
|
||||||
: name(name), funcsset(funcsset), env(env) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ScriptComponents {
|
|
||||||
std::vector<std::unique_ptr<UserComponent>> components;
|
|
||||||
|
|
||||||
ScriptComponents() = default;
|
|
||||||
|
|
||||||
ScriptComponents(ScriptComponents&& other)
|
|
||||||
: components(std::move(other.components)) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Level;
|
class Level;
|
||||||
class Assets;
|
class Assets;
|
||||||
|
class Entity;
|
||||||
class LineBatch;
|
class LineBatch;
|
||||||
class ModelBatch;
|
class ModelBatch;
|
||||||
class Frustum;
|
class Frustum;
|
||||||
@ -111,72 +31,6 @@ namespace rigging {
|
|||||||
class SkeletonConfig;
|
class SkeletonConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Entity {
|
|
||||||
Entities& entities;
|
|
||||||
entityid_t id;
|
|
||||||
entt::registry& registry;
|
|
||||||
const entt::entity entity;
|
|
||||||
public:
|
|
||||||
Entity(
|
|
||||||
Entities& entities,
|
|
||||||
entityid_t id,
|
|
||||||
entt::registry& registry,
|
|
||||||
const entt::entity entity
|
|
||||||
)
|
|
||||||
: entities(entities), id(id), registry(registry), entity(entity) {
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityId& getID() const {
|
|
||||||
return registry.get<EntityId>(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isValid() const {
|
|
||||||
return registry.valid(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
const EntityDef& getDef() const {
|
|
||||||
return registry.get<EntityId>(entity).def;
|
|
||||||
}
|
|
||||||
|
|
||||||
Transform& getTransform() const {
|
|
||||||
return registry.get<Transform>(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
Rigidbody& getRigidbody() const {
|
|
||||||
return registry.get<Rigidbody>(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptComponents& getScripting() const {
|
|
||||||
return registry.get<ScriptComponents>(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
rigging::Skeleton& getSkeleton() const;
|
|
||||||
|
|
||||||
void setRig(const rigging::SkeletonConfig* rigConfig);
|
|
||||||
|
|
||||||
entityid_t getUID() const {
|
|
||||||
return registry.get<EntityId>(entity).uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
entt::entity getHandler() const {
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t getPlayer() const {
|
|
||||||
return registry.get<EntityId>(entity).player;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPlayer(int64_t id) {
|
|
||||||
registry.get<EntityId>(entity).player = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setInterpolatedPosition(const glm::vec3& position);
|
|
||||||
|
|
||||||
glm::vec3 getInterpolatedPosition() const;
|
|
||||||
|
|
||||||
void destroy();
|
|
||||||
};
|
|
||||||
|
|
||||||
class Entities {
|
class Entities {
|
||||||
entt::registry registry;
|
entt::registry registry;
|
||||||
Level& level;
|
Level& level;
|
||||||
@ -211,7 +65,8 @@ public:
|
|||||||
ModelBatch& batch,
|
ModelBatch& batch,
|
||||||
const Frustum* frustum,
|
const Frustum* frustum,
|
||||||
float delta,
|
float delta,
|
||||||
bool pause
|
bool pause,
|
||||||
|
entityid_t fpsEntity
|
||||||
);
|
);
|
||||||
|
|
||||||
entityid_t spawn(
|
entityid_t spawn(
|
||||||
@ -222,13 +77,7 @@ public:
|
|||||||
entityid_t uid = 0
|
entityid_t uid = 0
|
||||||
);
|
);
|
||||||
|
|
||||||
std::optional<Entity> get(entityid_t id) {
|
std::optional<Entity> get(entityid_t id);
|
||||||
const auto& found = entities.find(id);
|
|
||||||
if (found != entities.end() && registry.valid(found->second)) {
|
|
||||||
return Entity(*this, id, registry, found->second);
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Entities raycast. No blocks check included, use combined with
|
/// @brief Entities raycast. No blocks check included, use combined with
|
||||||
/// Chunks.rayCast
|
/// Chunks.rayCast
|
||||||
@ -253,7 +102,6 @@ public:
|
|||||||
std::vector<Entity> getAllInRadius(glm::vec3 center, float radius);
|
std::vector<Entity> getAllInRadius(glm::vec3 center, float radius);
|
||||||
void despawn(entityid_t id);
|
void despawn(entityid_t id);
|
||||||
void despawn(std::vector<Entity> entities);
|
void despawn(std::vector<Entity> entities);
|
||||||
dv::value serialize(const Entity& entity);
|
|
||||||
dv::value serialize(const std::vector<Entity>& entities);
|
dv::value serialize(const std::vector<Entity>& entities);
|
||||||
|
|
||||||
void setNextID(entityid_t id) {
|
void setNextID(entityid_t id) {
|
||||||
|
|||||||
119
src/objects/Entity.cpp
Normal file
119
src/objects/Entity.cpp
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
#include "Entity.hpp"
|
||||||
|
|
||||||
|
#include "Transform.hpp"
|
||||||
|
#include "Rigidbody.hpp"
|
||||||
|
#include "ScriptComponents.hpp"
|
||||||
|
#include "Entities.hpp"
|
||||||
|
#include "EntityDef.hpp"
|
||||||
|
#include "rigging.hpp"
|
||||||
|
#include "logic/scripting/scripting.hpp"
|
||||||
|
|
||||||
|
#include <entt/entt.hpp>
|
||||||
|
|
||||||
|
static inline std::string SAVED_DATA_VARNAME = "SAVED_DATA";
|
||||||
|
|
||||||
|
void Entity::setInterpolatedPosition(const glm::vec3& position) {
|
||||||
|
getSkeleton().interpolation.refresh(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 Entity::getInterpolatedPosition() const {
|
||||||
|
const auto& skeleton = getSkeleton();
|
||||||
|
if (skeleton.interpolation.isEnabled()) {
|
||||||
|
return skeleton.interpolation.getCurrent();
|
||||||
|
}
|
||||||
|
return getTransform().pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entity::destroy() {
|
||||||
|
if (isValid()) {
|
||||||
|
entities.despawn(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rigging::Skeleton& Entity::getSkeleton() const {
|
||||||
|
return registry.get<rigging::Skeleton>(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entity::setRig(const rigging::SkeletonConfig* rigConfig) {
|
||||||
|
auto& skeleton = registry.get<rigging::Skeleton>(entity);
|
||||||
|
skeleton.config = rigConfig;
|
||||||
|
skeleton.pose.matrices.resize(
|
||||||
|
rigConfig->getBones().size(), glm::mat4(1.0f)
|
||||||
|
);
|
||||||
|
skeleton.calculated.matrices.resize(
|
||||||
|
rigConfig->getBones().size(), glm::mat4(1.0f)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
dv::value Entity::serialize() const {
|
||||||
|
const auto& eid = getID();
|
||||||
|
const auto& def = eid.def;
|
||||||
|
const auto& transform = getTransform();
|
||||||
|
const auto& rigidbody = getRigidbody();
|
||||||
|
const auto& skeleton = getSkeleton();
|
||||||
|
const auto& scripts = getScripting();
|
||||||
|
|
||||||
|
auto root = dv::object();
|
||||||
|
root["def"] = def.name;
|
||||||
|
root["uid"] = eid.uid;
|
||||||
|
|
||||||
|
root[COMP_TRANSFORM] = transform.serialize();
|
||||||
|
root[COMP_RIGIDBODY] =
|
||||||
|
rigidbody.serialize(def.save.body.velocity, def.save.body.settings);
|
||||||
|
|
||||||
|
if (skeleton.config->getName() != def.skeletonName) {
|
||||||
|
root["skeleton"] = skeleton.config->getName();
|
||||||
|
}
|
||||||
|
if (def.save.skeleton.pose || def.save.skeleton.textures) {
|
||||||
|
root[COMP_SKELETON] = skeleton.serialize(
|
||||||
|
def.save.skeleton.pose, def.save.skeleton.textures
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!scripts.components.empty()) {
|
||||||
|
auto& compsMap = root.object("comps");
|
||||||
|
for (auto& comp : scripts.components) {
|
||||||
|
auto data =
|
||||||
|
scripting::get_component_value(comp->env, SAVED_DATA_VARNAME);
|
||||||
|
compsMap[comp->name] = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityId& Entity::getID() const {
|
||||||
|
return registry.get<EntityId>(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Entity::isValid() const {
|
||||||
|
return registry.valid(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform& Entity::getTransform() const {
|
||||||
|
return registry.get<Transform>(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ScriptComponents& Entity::getScripting() const {
|
||||||
|
return registry.get<ScriptComponents>(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
const EntityDef& Entity::getDef() const {
|
||||||
|
return registry.get<EntityId>(entity).def;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rigidbody& Entity::getRigidbody() const {
|
||||||
|
return registry.get<Rigidbody>(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
entityid_t Entity::getUID() const {
|
||||||
|
return registry.get<EntityId>(entity).uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t Entity::getPlayer() const {
|
||||||
|
return registry.get<EntityId>(entity).player;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entity::setPlayer(int64_t id) {
|
||||||
|
registry.get<EntityId>(entity).player = id;
|
||||||
|
}
|
||||||
|
|
||||||
80
src/objects/Entity.hpp
Normal file
80
src/objects/Entity.hpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "typedefs.hpp"
|
||||||
|
#include "data/dv_fwd.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <entt/fwd.hpp>
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
|
||||||
|
class Entities;
|
||||||
|
struct EntityDef;
|
||||||
|
struct Transform;
|
||||||
|
struct Rigidbody;
|
||||||
|
struct ScriptComponents;
|
||||||
|
|
||||||
|
inline std::string COMP_TRANSFORM = "transform";
|
||||||
|
inline std::string COMP_RIGIDBODY = "rigidbody";
|
||||||
|
inline std::string COMP_SKELETON = "skeleton";
|
||||||
|
|
||||||
|
namespace rigging {
|
||||||
|
struct Skeleton;
|
||||||
|
class SkeletonConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EntityId {
|
||||||
|
entityid_t uid;
|
||||||
|
const EntityDef& def;
|
||||||
|
bool destroyFlag = false;
|
||||||
|
int64_t player = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Entity {
|
||||||
|
Entities& entities;
|
||||||
|
entityid_t id;
|
||||||
|
entt::registry& registry;
|
||||||
|
const entt::entity entity;
|
||||||
|
public:
|
||||||
|
Entity(
|
||||||
|
Entities& entities,
|
||||||
|
entityid_t id,
|
||||||
|
entt::registry& registry,
|
||||||
|
const entt::entity entity
|
||||||
|
)
|
||||||
|
: entities(entities), id(id), registry(registry), entity(entity) {
|
||||||
|
}
|
||||||
|
|
||||||
|
dv::value serialize() const;
|
||||||
|
|
||||||
|
EntityId& getID() const;
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
|
const EntityDef& getDef() const;
|
||||||
|
|
||||||
|
Transform& getTransform() const;
|
||||||
|
|
||||||
|
Rigidbody& getRigidbody() const;
|
||||||
|
|
||||||
|
ScriptComponents& getScripting() const;
|
||||||
|
|
||||||
|
rigging::Skeleton& getSkeleton() const;
|
||||||
|
|
||||||
|
void setRig(const rigging::SkeletonConfig* rigConfig);
|
||||||
|
|
||||||
|
entityid_t getUID() const;
|
||||||
|
|
||||||
|
int64_t getPlayer() const;
|
||||||
|
|
||||||
|
void setPlayer(int64_t id);
|
||||||
|
|
||||||
|
void setInterpolatedPosition(const glm::vec3& position);
|
||||||
|
|
||||||
|
glm::vec3 getInterpolatedPosition() const;
|
||||||
|
|
||||||
|
entt::entity getHandler() const {
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy();
|
||||||
|
};
|
||||||
@ -5,6 +5,7 @@
|
|||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
|
#include "data/dv.hpp"
|
||||||
#include "maths/aabb.hpp"
|
#include "maths/aabb.hpp"
|
||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
|
|
||||||
@ -12,12 +13,17 @@ namespace rigging {
|
|||||||
class SkeletonConfig;
|
class SkeletonConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ComponentInstance {
|
||||||
|
std::string component;
|
||||||
|
dv::value params;
|
||||||
|
};
|
||||||
|
|
||||||
struct EntityDef {
|
struct EntityDef {
|
||||||
/// @brief Entity string id (with prefix included)
|
/// @brief Entity string id (with prefix included)
|
||||||
std::string const name;
|
std::string const name;
|
||||||
|
|
||||||
/// @brief Component IDs
|
/// @brief Component instances
|
||||||
std::vector<std::string> components;
|
std::vector<ComponentInstance> components;
|
||||||
|
|
||||||
/// @brief Physic body type
|
/// @brief Physic body type
|
||||||
BodyType bodyType = BodyType::DYNAMIC;
|
BodyType bodyType = BodyType::DYNAMIC;
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include "content/ContentReport.hpp"
|
#include "content/ContentReport.hpp"
|
||||||
#include "items/Inventory.hpp"
|
#include "items/Inventory.hpp"
|
||||||
#include "Entities.hpp"
|
#include "Entities.hpp"
|
||||||
|
#include "Entity.hpp"
|
||||||
#include "rigging.hpp"
|
#include "rigging.hpp"
|
||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
#include "physics/PhysicsSolver.hpp"
|
#include "physics/PhysicsSolver.hpp"
|
||||||
@ -20,13 +21,6 @@
|
|||||||
|
|
||||||
static debug::Logger logger("player");
|
static debug::Logger logger("player");
|
||||||
|
|
||||||
constexpr float CROUCH_SPEED_MUL = 0.35f;
|
|
||||||
constexpr float RUN_SPEED_MUL = 1.5f;
|
|
||||||
constexpr float PLAYER_GROUND_DAMPING = 10.0f;
|
|
||||||
constexpr float PLAYER_AIR_DAMPING = 8.0f;
|
|
||||||
constexpr float FLIGHT_SPEED_MUL = 4.0f;
|
|
||||||
constexpr float CHEAT_SPEED_MUL = 5.0f;
|
|
||||||
constexpr float JUMP_FORCE = 8.0f;
|
|
||||||
constexpr int SPAWN_ATTEMPTS_PER_UPDATE = 64;
|
constexpr int SPAWN_ATTEMPTS_PER_UPDATE = 64;
|
||||||
|
|
||||||
Player::Player(
|
Player::Player(
|
||||||
@ -81,17 +75,6 @@ void Player::updateEntity() {
|
|||||||
"will be respawned";
|
"will be respawned";
|
||||||
eid = ENTITY_AUTO;
|
eid = ENTITY_AUTO;
|
||||||
}
|
}
|
||||||
auto hitbox = getHitbox();
|
|
||||||
if (hitbox == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
hitbox->linearDamping = PLAYER_GROUND_DAMPING;
|
|
||||||
hitbox->verticalDamping = flight;
|
|
||||||
hitbox->gravityScale = flight ? 0.0f : 1.0f;
|
|
||||||
if (flight || !hitbox->grounded) {
|
|
||||||
hitbox->linearDamping = PLAYER_AIR_DAMPING;
|
|
||||||
}
|
|
||||||
hitbox->type = noclip ? BodyType::KINEMATIC : BodyType::DYNAMIC;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Hitbox* Player::getHitbox() {
|
Hitbox* Player::getHitbox() {
|
||||||
@ -101,57 +84,6 @@ Hitbox* Player::getHitbox() {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::updateInput(PlayerInput& input, float delta) {
|
|
||||||
auto hitbox = getHitbox();
|
|
||||||
if (hitbox == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bool crouch = input.shift && hitbox->grounded && !input.sprint;
|
|
||||||
float speed = this->speed;
|
|
||||||
if (flight) {
|
|
||||||
speed *= FLIGHT_SPEED_MUL;
|
|
||||||
}
|
|
||||||
if (input.cheat) {
|
|
||||||
speed *= CHEAT_SPEED_MUL;
|
|
||||||
}
|
|
||||||
|
|
||||||
hitbox->crouching = crouch;
|
|
||||||
if (crouch) {
|
|
||||||
speed *= CROUCH_SPEED_MUL;
|
|
||||||
} else if (input.sprint) {
|
|
||||||
speed *= RUN_SPEED_MUL;
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 dir(0, 0, 0);
|
|
||||||
if (input.moveForward) {
|
|
||||||
dir += fpCamera->dir;
|
|
||||||
}
|
|
||||||
if (input.moveBack) {
|
|
||||||
dir -= fpCamera->dir;
|
|
||||||
}
|
|
||||||
if (input.moveRight) {
|
|
||||||
dir += fpCamera->right;
|
|
||||||
}
|
|
||||||
if (input.moveLeft) {
|
|
||||||
dir -= fpCamera->right;
|
|
||||||
}
|
|
||||||
if (glm::length(dir) > 0.0f) {
|
|
||||||
dir = glm::normalize(dir);
|
|
||||||
hitbox->velocity += dir * speed * delta * 9.0f;
|
|
||||||
}
|
|
||||||
if (flight) {
|
|
||||||
if (input.jump) {
|
|
||||||
hitbox->velocity.y += speed * delta * 9;
|
|
||||||
}
|
|
||||||
if (input.shift) {
|
|
||||||
hitbox->velocity.y -= speed * delta * 9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (input.jump && hitbox->grounded) {
|
|
||||||
hitbox->velocity.y = JUMP_FORCE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::updateSelectedEntity() {
|
void Player::updateSelectedEntity() {
|
||||||
selectedEid = selection.entity;
|
selectedEid = selection.entity;
|
||||||
}
|
}
|
||||||
@ -172,10 +104,6 @@ void Player::postUpdate() {
|
|||||||
attemptToFindSpawnpoint();
|
attemptToFindSpawnpoint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: ERASE & FORGET
|
|
||||||
auto& skeleton = entity->getSkeleton();
|
|
||||||
skeleton.visible = currentCamera != fpCamera;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::teleport(glm::vec3 position) {
|
void Player::teleport(glm::vec3 position) {
|
||||||
|
|||||||
@ -82,7 +82,6 @@ public:
|
|||||||
|
|
||||||
void teleport(glm::vec3 position);
|
void teleport(glm::vec3 position);
|
||||||
void updateEntity();
|
void updateEntity();
|
||||||
void updateInput(PlayerInput& input, float delta);
|
|
||||||
void updateSelectedEntity();
|
void updateSelectedEntity();
|
||||||
void postUpdate();
|
void postUpdate();
|
||||||
|
|
||||||
|
|||||||
83
src/objects/Rigidbody.cpp
Normal file
83
src/objects/Rigidbody.cpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#define VC_ENABLE_REFLECTION
|
||||||
|
|
||||||
|
#include "Rigidbody.hpp"
|
||||||
|
|
||||||
|
#include "EntityDef.hpp"
|
||||||
|
#include "Entities.hpp"
|
||||||
|
#include "Entity.hpp"
|
||||||
|
#include "data/dv_util.hpp"
|
||||||
|
#include "logic/scripting/scripting.hpp"
|
||||||
|
|
||||||
|
dv::value Rigidbody::serialize(bool saveVelocity, bool saveBodySettings) const {
|
||||||
|
auto bodymap = dv::object();
|
||||||
|
if (!enabled) {
|
||||||
|
bodymap["enabled"] = false;
|
||||||
|
}
|
||||||
|
if (saveVelocity) {
|
||||||
|
bodymap["vel"] = dv::to_value(hitbox.velocity);
|
||||||
|
}
|
||||||
|
if (saveBodySettings) {
|
||||||
|
bodymap["damping"] = hitbox.linearDamping;
|
||||||
|
bodymap["type"] = BodyTypeMeta.getNameString(hitbox.type);
|
||||||
|
if (hitbox.crouching) {
|
||||||
|
bodymap["crouch"] = hitbox.crouching;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bodymap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rigidbody::deserialize(const dv::value& root) {
|
||||||
|
dv::get_vec(root, "vel", hitbox.velocity);
|
||||||
|
std::string bodyTypeName;
|
||||||
|
root.at("type").get(bodyTypeName);
|
||||||
|
BodyTypeMeta.getItem(bodyTypeName, hitbox.type);
|
||||||
|
root["crouch"].asBoolean(hitbox.crouching);
|
||||||
|
root["damping"].asNumber(hitbox.linearDamping);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <void (*callback)(const Entity&, size_t, entityid_t)>
|
||||||
|
static sensorcallback create_sensor_callback(Entities& entities) {
|
||||||
|
return [&entities](auto entityid, auto index, auto otherid) {
|
||||||
|
if (auto entity = entities.get(entityid)) {
|
||||||
|
if (entity->isValid()) {
|
||||||
|
callback(*entity, index, otherid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rigidbody::initialize(
|
||||||
|
const EntityDef& def, entityid_t id, Entities& entities
|
||||||
|
) {
|
||||||
|
sensors.resize(def.radialSensors.size() + def.boxSensors.size());
|
||||||
|
for (auto& [i, box] : def.boxSensors) {
|
||||||
|
SensorParams params {};
|
||||||
|
params.aabb = box;
|
||||||
|
sensors[i] = Sensor {
|
||||||
|
true,
|
||||||
|
SensorType::AABB,
|
||||||
|
i,
|
||||||
|
id,
|
||||||
|
params,
|
||||||
|
params,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
create_sensor_callback<scripting::on_sensor_enter>(entities),
|
||||||
|
create_sensor_callback<scripting::on_sensor_exit>(entities)};
|
||||||
|
}
|
||||||
|
for (auto& [i, radius] : def.radialSensors) {
|
||||||
|
SensorParams params {};
|
||||||
|
params.radial = glm::vec4(radius);
|
||||||
|
sensors[i] = Sensor {
|
||||||
|
true,
|
||||||
|
SensorType::RADIUS,
|
||||||
|
i,
|
||||||
|
id,
|
||||||
|
params,
|
||||||
|
params,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
create_sensor_callback<scripting::on_sensor_enter>(entities),
|
||||||
|
create_sensor_callback<scripting::on_sensor_exit>(entities)};
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/objects/Rigidbody.hpp
Normal file
23
src/objects/Rigidbody.hpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "data/dv_fwd.hpp"
|
||||||
|
#include "physics/Hitbox.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <entt/fwd.hpp>
|
||||||
|
|
||||||
|
class Entities;
|
||||||
|
struct EntityDef;
|
||||||
|
|
||||||
|
struct Rigidbody {
|
||||||
|
bool enabled = true;
|
||||||
|
Hitbox hitbox;
|
||||||
|
std::vector<Sensor> sensors;
|
||||||
|
|
||||||
|
dv::value serialize(bool saveVelocity, bool saveBodySettings) const;
|
||||||
|
void deserialize(const dv::value& root);
|
||||||
|
|
||||||
|
void initialize(
|
||||||
|
const EntityDef& def, entityid_t id, Entities& entities
|
||||||
|
);
|
||||||
|
};
|
||||||
49
src/objects/ScriptComponents.hpp
Normal file
49
src/objects/ScriptComponents.hpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "typedefs.hpp"
|
||||||
|
#include "data/dv.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct EntityFuncsSet {
|
||||||
|
bool init;
|
||||||
|
bool on_despawn;
|
||||||
|
bool on_grounded;
|
||||||
|
bool on_fall;
|
||||||
|
bool on_sensor_enter;
|
||||||
|
bool on_sensor_exit;
|
||||||
|
bool on_save;
|
||||||
|
bool on_aim_on;
|
||||||
|
bool on_aim_off;
|
||||||
|
bool on_attacked;
|
||||||
|
bool on_used;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UserComponent {
|
||||||
|
std::string name;
|
||||||
|
EntityFuncsSet funcsset;
|
||||||
|
scriptenv env;
|
||||||
|
dv::value params;
|
||||||
|
|
||||||
|
UserComponent(
|
||||||
|
const std::string& name,
|
||||||
|
EntityFuncsSet funcsset,
|
||||||
|
scriptenv env,
|
||||||
|
dv::value params
|
||||||
|
)
|
||||||
|
: name(name),
|
||||||
|
funcsset(funcsset),
|
||||||
|
env(std::move(env)),
|
||||||
|
params(std::move(params)) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScriptComponents {
|
||||||
|
std::vector<std::unique_ptr<UserComponent>> components;
|
||||||
|
|
||||||
|
ScriptComponents() = default;
|
||||||
|
|
||||||
|
ScriptComponents(ScriptComponents&& other)
|
||||||
|
: components(std::move(other.components)) {
|
||||||
|
}
|
||||||
|
};
|
||||||
31
src/objects/Transform.cpp
Normal file
31
src/objects/Transform.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include "Transform.hpp"
|
||||||
|
|
||||||
|
#include "data/dv_util.hpp"
|
||||||
|
|
||||||
|
void Transform::refresh() {
|
||||||
|
combined = glm::mat4(1.0f);
|
||||||
|
combined = glm::translate(combined, pos);
|
||||||
|
combined = combined * glm::mat4(rot);
|
||||||
|
combined = glm::scale(combined, size);
|
||||||
|
displayPos = pos;
|
||||||
|
displaySize = size;
|
||||||
|
dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dv::value Transform::serialize() const {
|
||||||
|
auto tsfmap = dv::object();
|
||||||
|
tsfmap["pos"] = dv::to_value(pos);
|
||||||
|
if (size != glm::vec3(1.0f)) {
|
||||||
|
tsfmap["size"] = dv::to_value(size);
|
||||||
|
}
|
||||||
|
if (rot != glm::mat3(1.0f)) {
|
||||||
|
tsfmap["rot"] = dv::to_value(rot);
|
||||||
|
}
|
||||||
|
return tsfmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transform::deserialize(const dv::value& root) {
|
||||||
|
dv::get_vec(root, "pos", pos);
|
||||||
|
dv::get_vec(root, "size", size);
|
||||||
|
dv::get_mat(root, "rot", rot);
|
||||||
|
}
|
||||||
44
src/objects/Transform.hpp
Normal file
44
src/objects/Transform.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
#include <glm/gtx/norm.hpp>
|
||||||
|
#include <data/dv_fwd.hpp>
|
||||||
|
|
||||||
|
struct Transform {
|
||||||
|
static inline constexpr float EPSILON = 1e-7f;
|
||||||
|
glm::vec3 pos;
|
||||||
|
glm::vec3 size;
|
||||||
|
glm::mat3 rot;
|
||||||
|
glm::mat4 combined;
|
||||||
|
bool dirty = true;
|
||||||
|
|
||||||
|
glm::vec3 displayPos;
|
||||||
|
glm::vec3 displaySize;
|
||||||
|
|
||||||
|
dv::value serialize() const;
|
||||||
|
void deserialize(const dv::value& root);
|
||||||
|
|
||||||
|
void refresh();
|
||||||
|
|
||||||
|
inline void setRot(glm::mat3 m) {
|
||||||
|
rot = m;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setSize(glm::vec3 v) {
|
||||||
|
if (glm::distance2(displaySize, v) >= EPSILON) {
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
size = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setPos(glm::vec3 v) {
|
||||||
|
if (glm::distance2(displayPos, v) >= EPSILON) {
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
pos = v;
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -23,7 +23,7 @@ Bone::Bone(
|
|||||||
std::string name,
|
std::string name,
|
||||||
std::string model,
|
std::string model,
|
||||||
std::vector<std::unique_ptr<Bone>> bones,
|
std::vector<std::unique_ptr<Bone>> bones,
|
||||||
glm::vec3 offset
|
const glm::vec3& offset
|
||||||
)
|
)
|
||||||
: index(index),
|
: index(index),
|
||||||
name(std::move(name)),
|
name(std::move(name)),
|
||||||
@ -53,6 +53,39 @@ Skeleton::Skeleton(const SkeletonConfig* config)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dv::value Skeleton::serialize(bool saveTextures, bool savePose) const {
|
||||||
|
auto root = dv::object();
|
||||||
|
if (saveTextures) {
|
||||||
|
auto& map = root.object("textures");
|
||||||
|
for (auto& [slot, texture] : textures) {
|
||||||
|
map[slot] = texture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (savePose) {
|
||||||
|
auto& list = root.list("pose");
|
||||||
|
for (auto& mat : pose.matrices) {
|
||||||
|
list.add(dv::to_value(mat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skeleton::deserialize(const dv::value& root) {
|
||||||
|
if (auto found = root.at("textures")) {
|
||||||
|
auto& texturesmap = *found;
|
||||||
|
for (auto& [slot, _] : texturesmap.asObject()) {
|
||||||
|
texturesmap.at(slot).get(textures[slot]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (auto found = root.at("pose")) {
|
||||||
|
auto& posearr = *found;
|
||||||
|
auto& matrices = pose.matrices;
|
||||||
|
for (size_t i = 0; i < std::min(matrices.size(), posearr.size()); i++) {
|
||||||
|
dv::get_mat(posearr[i], pose.matrices[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void get_all_nodes(std::vector<Bone*>& nodes, Bone* node) {
|
static void get_all_nodes(std::vector<Bone*>& nodes, Bone* node) {
|
||||||
nodes[node->getIndex()] = node;
|
nodes[node->getIndex()] = node;
|
||||||
for (auto& subnode : node->getSubnodes()) {
|
for (auto& subnode : node->getSubnodes()) {
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
|
#include "data/dv_fwd.hpp"
|
||||||
#include "util/Interpolation.hpp"
|
#include "util/Interpolation.hpp"
|
||||||
|
|
||||||
class Assets;
|
class Assets;
|
||||||
@ -50,7 +51,7 @@ namespace rigging {
|
|||||||
std::string name,
|
std::string name,
|
||||||
std::string model,
|
std::string model,
|
||||||
std::vector<std::unique_ptr<Bone>> bones,
|
std::vector<std::unique_ptr<Bone>> bones,
|
||||||
glm::vec3 offset
|
const glm::vec3& offset
|
||||||
);
|
);
|
||||||
|
|
||||||
void setModel(const std::string& name);
|
void setModel(const std::string& name);
|
||||||
@ -89,6 +90,9 @@ namespace rigging {
|
|||||||
util::VecInterpolation<3, float> interpolation {false};
|
util::VecInterpolation<3, float> interpolation {false};
|
||||||
|
|
||||||
Skeleton(const SkeletonConfig* config);
|
Skeleton(const SkeletonConfig* config);
|
||||||
|
|
||||||
|
dv::value serialize(bool saveTextures, bool savePose) const;
|
||||||
|
void deserialize(const dv::value& root);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SkeletonConfig {
|
class SkeletonConfig {
|
||||||
|
|||||||
@ -6,6 +6,5 @@ Hitbox::Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize)
|
|||||||
: type(type),
|
: type(type),
|
||||||
position(position),
|
position(position),
|
||||||
halfsize(halfsize),
|
halfsize(halfsize),
|
||||||
velocity(0.0f,0.0f,0.0f),
|
velocity(0.0f,0.0f,0.0f)
|
||||||
linearDamping(0.1f)
|
|
||||||
{}
|
{}
|
||||||
|
|||||||
@ -52,8 +52,9 @@ struct Hitbox {
|
|||||||
glm::vec3 position;
|
glm::vec3 position;
|
||||||
glm::vec3 halfsize;
|
glm::vec3 halfsize;
|
||||||
glm::vec3 velocity;
|
glm::vec3 velocity;
|
||||||
float linearDamping;
|
float linearDamping = 0.5;
|
||||||
bool verticalDamping = false;
|
float friction = 1.0f;
|
||||||
|
float verticalDamping = 1.0f;
|
||||||
bool grounded = false;
|
bool grounded = false;
|
||||||
float gravityScale = 1.0f;
|
float gravityScale = 1.0f;
|
||||||
bool crouching = false;
|
bool crouching = false;
|
||||||
|
|||||||
@ -25,7 +25,7 @@ void PhysicsSolver::step(
|
|||||||
entityid_t entity
|
entityid_t entity
|
||||||
) {
|
) {
|
||||||
float dt = delta / static_cast<float>(substeps);
|
float dt = delta / static_cast<float>(substeps);
|
||||||
float linearDamping = hitbox.linearDamping;
|
float linearDamping = hitbox.linearDamping * hitbox.friction;
|
||||||
float s = 2.0f/BLOCK_AABB_GRID;
|
float s = 2.0f/BLOCK_AABB_GRID;
|
||||||
|
|
||||||
const glm::vec3& half = hitbox.halfsize;
|
const glm::vec3& half = hitbox.halfsize;
|
||||||
@ -45,11 +45,6 @@ void PhysicsSolver::step(
|
|||||||
colisionCalc(chunks, hitbox, vel, pos, half,
|
colisionCalc(chunks, hitbox, vel, pos, half,
|
||||||
(prevGrounded && gravityScale > 0.0f) ? 0.5f : 0.0f);
|
(prevGrounded && gravityScale > 0.0f) ? 0.5f : 0.0f);
|
||||||
}
|
}
|
||||||
vel.x *= glm::max(0.0f, 1.0f - dt * linearDamping);
|
|
||||||
if (hitbox.verticalDamping) {
|
|
||||||
vel.y *= glm::max(0.0f, 1.0f - dt * linearDamping);
|
|
||||||
}
|
|
||||||
vel.z *= glm::max(0.0f, 1.0f - dt * linearDamping);
|
|
||||||
|
|
||||||
pos += vel * dt + gravity * gravityScale * dt * dt * 0.5f;
|
pos += vel * dt + gravity * gravityScale * dt * dt * 0.5f;
|
||||||
if (hitbox.grounded && pos.y < py) {
|
if (hitbox.grounded && pos.y < py) {
|
||||||
@ -89,6 +84,12 @@ void PhysicsSolver::step(
|
|||||||
hitbox.grounded = true;
|
hitbox.grounded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
vel.x /= 1.0f + delta * linearDamping;
|
||||||
|
vel.z /= 1.0f + delta * linearDamping;
|
||||||
|
if (hitbox.verticalDamping > 0.0f) {
|
||||||
|
vel.y /= 1.0f + delta * linearDamping * hitbox.verticalDamping;
|
||||||
|
}
|
||||||
|
|
||||||
AABB aabb;
|
AABB aabb;
|
||||||
aabb.a = hitbox.position - hitbox.halfsize;
|
aabb.a = hitbox.position - hitbox.halfsize;
|
||||||
aabb.b = hitbox.position + hitbox.halfsize;
|
aabb.b = hitbox.position + hitbox.halfsize;
|
||||||
|
|||||||
@ -85,11 +85,18 @@ struct GraphicsSettings {
|
|||||||
IntegerSetting denseRenderDistance {56, 0, 10'000};
|
IntegerSetting denseRenderDistance {56, 0, 10'000};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PathfindingSettings {
|
||||||
|
/// @brief Max visited blocks by an agent per async tick
|
||||||
|
IntegerSetting stepsPerAsyncAgent {128, 1, 2048};
|
||||||
|
};
|
||||||
|
|
||||||
struct DebugSettings {
|
struct DebugSettings {
|
||||||
/// @brief Turns off chunks saving/loading
|
/// @brief Turns off chunks saving/loading
|
||||||
FlagSetting generatorTestMode {false};
|
FlagSetting generatorTestMode {false};
|
||||||
/// @brief Write lights cache
|
/// @brief Write lights cache
|
||||||
FlagSetting doWriteLights {true};
|
FlagSetting doWriteLights {true};
|
||||||
|
/// @brief Write preprocessed shaders code to user:export
|
||||||
|
FlagSetting doTraceShaders {false};
|
||||||
/// @brief Enable experimental optimizations and features
|
/// @brief Enable experimental optimizations and features
|
||||||
FlagSetting enableExperimental {false};
|
FlagSetting enableExperimental {false};
|
||||||
};
|
};
|
||||||
@ -111,4 +118,5 @@ struct EngineSettings {
|
|||||||
DebugSettings debug;
|
DebugSettings debug;
|
||||||
UiSettings ui;
|
UiSettings ui;
|
||||||
NetworkSettings network;
|
NetworkSettings network;
|
||||||
|
PathfindingSettings pathfinding;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,7 +10,7 @@ Clock::Clock(int tickRate, int tickParts)
|
|||||||
|
|
||||||
bool Clock::update(float delta) {
|
bool Clock::update(float delta) {
|
||||||
tickTimer += delta;
|
tickTimer += delta;
|
||||||
float delay = 1.0f / float(tickRate);
|
float delay = 1.0f / static_cast<float>(tickRate);
|
||||||
if (tickTimer > delay || tickPartsUndone) {
|
if (tickTimer > delay || tickPartsUndone) {
|
||||||
if (tickPartsUndone) {
|
if (tickPartsUndone) {
|
||||||
tickPartsUndone--;
|
tickPartsUndone--;
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
#include "lighting/Lightmap.hpp"
|
#include "lighting/Lightmap.hpp"
|
||||||
#include "maths/voxmaths.hpp"
|
#include "maths/voxmaths.hpp"
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
|
#include "objects/Entity.hpp"
|
||||||
#include "voxels/blocks_agent.hpp"
|
#include "voxels/blocks_agent.hpp"
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
#include "world/LevelEvents.hpp"
|
#include "world/LevelEvents.hpp"
|
||||||
|
|||||||
269
src/voxels/Pathfinding.cpp
Normal file
269
src/voxels/Pathfinding.cpp
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
#include "Pathfinding.hpp"
|
||||||
|
|
||||||
|
#include "content/Content.hpp"
|
||||||
|
#include "voxels/Chunk.hpp"
|
||||||
|
#include "voxels/GlobalChunks.hpp"
|
||||||
|
#include "voxels/blocks_agent.hpp"
|
||||||
|
#include "world/Level.hpp"
|
||||||
|
|
||||||
|
inline constexpr float SQRT2 = 1.4142135623730951f; // sqrt(2)
|
||||||
|
|
||||||
|
using namespace voxels;
|
||||||
|
|
||||||
|
static float heuristic(const glm::ivec3& a, const glm::ivec3& b) {
|
||||||
|
return glm::distance(glm::vec3(a), glm::vec3(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
Pathfinding::Pathfinding(const Level& level)
|
||||||
|
: level(level),
|
||||||
|
chunks(*level.chunks),
|
||||||
|
blockDefs(level.content.getIndices()->blocks) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_passability(
|
||||||
|
const Agent& agent,
|
||||||
|
const GlobalChunks& chunks,
|
||||||
|
const Node& node,
|
||||||
|
const glm::ivec2& offset,
|
||||||
|
bool diagonal
|
||||||
|
) {
|
||||||
|
if (!diagonal) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto a = node.pos + glm::ivec3(offset.x, 0, 0);
|
||||||
|
auto b = node.pos + glm::ivec3(0, 0, offset.y);
|
||||||
|
|
||||||
|
for (int i = 0; i < agent.height; i++) {
|
||||||
|
if (blocks_agent::is_obstacle_at(chunks, a.x, a.y + i, a.z))
|
||||||
|
return false;
|
||||||
|
if (blocks_agent::is_obstacle_at(chunks, b.x, b.y + i, b.z))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void restore_route(
|
||||||
|
Route& route,
|
||||||
|
const glm::ivec3& lastPos,
|
||||||
|
const std::unordered_map<glm::ivec3, Node>& parents
|
||||||
|
) {
|
||||||
|
auto pos = lastPos;
|
||||||
|
while (true) {
|
||||||
|
const auto& found = parents.find(pos);
|
||||||
|
if (found == parents.end()) {
|
||||||
|
route.nodes.push_back({pos});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
route.nodes.push_back({pos});
|
||||||
|
pos = found->second.pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Pathfinding::createAgent() {
|
||||||
|
int id = nextAgent++;
|
||||||
|
agents[id] = Agent();
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pathfinding::removeAgent(int id) {
|
||||||
|
auto found = agents.find(id);
|
||||||
|
if (found != agents.end()) {
|
||||||
|
agents.erase(found);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pathfinding::performAllAsync(int stepsPerAgent) {
|
||||||
|
for (auto& [_, agent] : agents) {
|
||||||
|
if (agent.state.finished) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
perform(agent, stepsPerAgent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Route finish_route(Agent& agent, State&& state) {
|
||||||
|
Route route {};
|
||||||
|
restore_route(route, state.nearest, state.parents);
|
||||||
|
route.totalVisited = state.blocked.size();
|
||||||
|
route.nodes.push_back({agent.start});
|
||||||
|
route.found = true;
|
||||||
|
state.finished = true;
|
||||||
|
agent.state = std::move(state);
|
||||||
|
agent.route = route;
|
||||||
|
return route;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Passability {
|
||||||
|
NON_PASSABLE = -1,
|
||||||
|
OBSTACLE = 0,
|
||||||
|
PASSABLE = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
Route Pathfinding::perform(Agent& agent, int maxVisited) {
|
||||||
|
using namespace blocks_agent;
|
||||||
|
|
||||||
|
State state = std::move(agent.state);
|
||||||
|
if (state.queue.empty()) {
|
||||||
|
state.queue.push(
|
||||||
|
{agent.start, {}, 0, heuristic(agent.start, agent.target)}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& chunks = *level.chunks;
|
||||||
|
int height = std::max(agent.height, 1);
|
||||||
|
|
||||||
|
if (state.nearest == glm::ivec3(0)) {
|
||||||
|
state.nearest = agent.start;
|
||||||
|
state.minHScore = heuristic(agent.start, agent.target);
|
||||||
|
}
|
||||||
|
int visited = -1;
|
||||||
|
|
||||||
|
while (!state.queue.empty()) {
|
||||||
|
if (state.blocked.size() == agent.maxVisitedBlocks) {
|
||||||
|
if (agent.mayBeIncomplete) {
|
||||||
|
return finish_route(agent, std::move(state));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
visited++;
|
||||||
|
if (visited == maxVisited) {
|
||||||
|
state.finished = false;
|
||||||
|
agent.state = std::move(state);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto node = state.queue.top();
|
||||||
|
state.queue.pop();
|
||||||
|
|
||||||
|
if (node.pos.x == agent.target.x &&
|
||||||
|
glm::abs((node.pos.y - agent.target.y) / height) == 0 &&
|
||||||
|
node.pos.z == agent.target.z) {
|
||||||
|
return finish_route(agent, std::move(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
state.blocked.emplace(node.pos);
|
||||||
|
glm::ivec2 neighbors[8] {
|
||||||
|
{0, 1},
|
||||||
|
{1, 0},
|
||||||
|
{0, -1},
|
||||||
|
{-1, 0},
|
||||||
|
{-1, -1},
|
||||||
|
{1, -1},
|
||||||
|
{1, 1},
|
||||||
|
{-1, 1},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < sizeof(neighbors) / sizeof(glm::ivec2); i++) {
|
||||||
|
auto offset = neighbors[i];
|
||||||
|
auto pos = node.pos;
|
||||||
|
|
||||||
|
float cost = glm::abs(node.pos.y - pos.y) * 10;
|
||||||
|
int surface = getSurfaceAt(
|
||||||
|
agent, pos + glm::ivec3(offset.x, 0, offset.y), 1, cost
|
||||||
|
);
|
||||||
|
|
||||||
|
if (surface == NON_PASSABLE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pos.y = surface;
|
||||||
|
auto point = pos + glm::ivec3(offset.x, 0, offset.y);
|
||||||
|
if (state.blocked.find(point) != state.blocked.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_obstacle_at(
|
||||||
|
chunks, pos.x, pos.y + agent.jumpHeight, pos.z
|
||||||
|
)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!check_passability(agent, chunks, node, offset, i >= 4)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float sum = glm::abs(offset.x) + glm::abs(offset.y);
|
||||||
|
float gScore = node.gScore + sum + cost;
|
||||||
|
const auto& found = state.parents.find(point);
|
||||||
|
if (found == state.parents.end()) {
|
||||||
|
float hScore = heuristic(point, agent.target);
|
||||||
|
if (hScore < state.minHScore) {
|
||||||
|
state.minHScore = hScore;
|
||||||
|
state.nearest = point;
|
||||||
|
}
|
||||||
|
float fScore = gScore * 0.75f + hScore;
|
||||||
|
Node nNode {point, node.pos, gScore, fScore};
|
||||||
|
state.parents[point] = node;
|
||||||
|
state.queue.push(nNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.finished = true;
|
||||||
|
agent.state = std::move(state);
|
||||||
|
return finish_route(agent, std::move(agent.state));
|
||||||
|
}
|
||||||
|
|
||||||
|
Agent* Pathfinding::getAgent(int id) {
|
||||||
|
const auto& found = agents.find(id);
|
||||||
|
if (found != agents.end()) {
|
||||||
|
return &found->second;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::unordered_map<int, Agent>& Pathfinding::getAgents() const {
|
||||||
|
return agents;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Pathfinding::checkPoint(const Agent& agent, int x, int y, int z, int& cost) {
|
||||||
|
auto vox = blocks_agent::get(chunks, x, y, z);
|
||||||
|
if (vox == nullptr) {
|
||||||
|
return OBSTACLE;
|
||||||
|
}
|
||||||
|
const auto& def = blockDefs.require(vox->id);
|
||||||
|
if (def.obstacle) {
|
||||||
|
return OBSTACLE;
|
||||||
|
}
|
||||||
|
for (const auto& pair : agent.avoidTags) {
|
||||||
|
if (def.rt.tags.find(pair.first) != def.rt.tags.end()) {
|
||||||
|
cost = pair.second;
|
||||||
|
return NON_PASSABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PASSABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Pathfinding::getSurfaceAt(
|
||||||
|
const Agent& agent, const glm::ivec3& pos, int maxDelta, float& cost
|
||||||
|
) {
|
||||||
|
using namespace blocks_agent;
|
||||||
|
|
||||||
|
int status;
|
||||||
|
int surface = pos.y;
|
||||||
|
int ncost = 0;
|
||||||
|
if ((status = checkPoint(agent, pos.x, surface, pos.z, ncost)) == OBSTACLE) {
|
||||||
|
if ((status = checkPoint(agent, pos.x, surface + 1, pos.z, ncost)) == OBSTACLE) {
|
||||||
|
return NON_PASSABLE;
|
||||||
|
} else if (status == NON_PASSABLE) {
|
||||||
|
cost += 5;
|
||||||
|
}
|
||||||
|
cost += ncost;
|
||||||
|
return surface + 1;
|
||||||
|
} else {
|
||||||
|
if (status == NON_PASSABLE) {
|
||||||
|
cost += 5;
|
||||||
|
}
|
||||||
|
if ((status = checkPoint(agent, pos.x, surface - 1, pos.z, ncost)) == OBSTACLE) {
|
||||||
|
cost += ncost;
|
||||||
|
return surface;
|
||||||
|
} else if (status == NON_PASSABLE) {
|
||||||
|
cost += 5;
|
||||||
|
}
|
||||||
|
if ((status = checkPoint(agent, pos.x, surface - 2, pos.z, ncost)) == OBSTACLE) {
|
||||||
|
cost += ncost;
|
||||||
|
return surface - 1;
|
||||||
|
}
|
||||||
|
return NON_PASSABLE;
|
||||||
|
}
|
||||||
|
return NON_PASSABLE;
|
||||||
|
}
|
||||||
95
src/voxels/Pathfinding.hpp
Normal file
95
src/voxels/Pathfinding.hpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
#include <glm/gtx/hash.hpp>
|
||||||
|
#include <glm/vec2.hpp>
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Block;
|
||||||
|
class Level;
|
||||||
|
class GlobalChunks;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class ContentUnitIndices;
|
||||||
|
|
||||||
|
namespace voxels {
|
||||||
|
struct RouteNode {
|
||||||
|
glm::ivec3 pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Route {
|
||||||
|
bool found;
|
||||||
|
std::vector<RouteNode> nodes;
|
||||||
|
int totalVisited;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Node {
|
||||||
|
glm::ivec3 pos;
|
||||||
|
glm::ivec3 parent;
|
||||||
|
float gScore;
|
||||||
|
float fScore;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NodeLess {
|
||||||
|
bool operator()(const Node& l, const Node& r) const {
|
||||||
|
return l.fScore > r.fScore;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
std::priority_queue<Node, std::vector<Node>, NodeLess> queue;
|
||||||
|
std::unordered_set<glm::ivec3> blocked;
|
||||||
|
std::unordered_map<glm::ivec3, Node> parents;
|
||||||
|
glm::ivec3 nearest;
|
||||||
|
float minHScore;
|
||||||
|
bool finished = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Agent {
|
||||||
|
bool enabled = false;
|
||||||
|
bool mayBeIncomplete = true;
|
||||||
|
int height = 2;
|
||||||
|
int jumpHeight = 1;
|
||||||
|
int maxVisitedBlocks = 1e3;
|
||||||
|
glm::ivec3 start;
|
||||||
|
glm::ivec3 target;
|
||||||
|
Route route;
|
||||||
|
State state {};
|
||||||
|
std::set<std::pair<int, int>> avoidTags;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Pathfinding {
|
||||||
|
public:
|
||||||
|
Pathfinding(const Level& level);
|
||||||
|
|
||||||
|
int createAgent();
|
||||||
|
|
||||||
|
bool removeAgent(int id);
|
||||||
|
|
||||||
|
void performAllAsync(int stepsPerAgent);
|
||||||
|
|
||||||
|
Route perform(Agent& agent, int maxVisited = -1);
|
||||||
|
|
||||||
|
Agent* getAgent(int id);
|
||||||
|
|
||||||
|
const std::unordered_map<int, Agent>& getAgents() const;
|
||||||
|
private:
|
||||||
|
const Level& level;
|
||||||
|
const GlobalChunks& chunks;
|
||||||
|
const ContentUnitIndices<Block>& blockDefs;
|
||||||
|
std::unordered_map<int, Agent> agents;
|
||||||
|
int nextAgent = 1;
|
||||||
|
|
||||||
|
int getSurfaceAt(
|
||||||
|
const Agent& agent, const glm::ivec3& pos, int maxDelta, float& cost
|
||||||
|
);
|
||||||
|
|
||||||
|
int checkPoint(const Agent& agent, int x, int y, int z, int& cost);
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@
|
|||||||
#include "items/Inventories.hpp"
|
#include "items/Inventories.hpp"
|
||||||
#include "items/Inventory.hpp"
|
#include "items/Inventory.hpp"
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
|
#include "objects/Entity.hpp"
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
#include "objects/Players.hpp"
|
#include "objects/Players.hpp"
|
||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
@ -12,6 +13,7 @@
|
|||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
#include "voxels/Chunk.hpp"
|
#include "voxels/Chunk.hpp"
|
||||||
#include "voxels/GlobalChunks.hpp"
|
#include "voxels/GlobalChunks.hpp"
|
||||||
|
#include "voxels/Pathfinding.hpp"
|
||||||
#include "window/Camera.hpp"
|
#include "window/Camera.hpp"
|
||||||
#include "LevelEvents.hpp"
|
#include "LevelEvents.hpp"
|
||||||
#include "World.hpp"
|
#include "World.hpp"
|
||||||
@ -27,7 +29,8 @@ Level::Level(
|
|||||||
physics(std::make_unique<PhysicsSolver>(glm::vec3(0, -22.6f, 0))),
|
physics(std::make_unique<PhysicsSolver>(glm::vec3(0, -22.6f, 0))),
|
||||||
events(std::make_unique<LevelEvents>()),
|
events(std::make_unique<LevelEvents>()),
|
||||||
entities(std::make_unique<Entities>(*this)),
|
entities(std::make_unique<Entities>(*this)),
|
||||||
players(std::make_unique<Players>(*this)) {
|
players(std::make_unique<Players>(*this)),
|
||||||
|
pathfinding(std::make_unique<voxels::Pathfinding>(*this)) {
|
||||||
const auto& worldInfo = world->getInfo();
|
const auto& worldInfo = world->getInfo();
|
||||||
auto& cameraIndices = content.getIndices(ResourceType::CAMERA);
|
auto& cameraIndices = content.getIndices(ResourceType::CAMERA);
|
||||||
for (size_t i = 0; i < cameraIndices.size(); i++) {
|
for (size_t i = 0; i < cameraIndices.size(); i++) {
|
||||||
|
|||||||
@ -18,6 +18,10 @@ class Camera;
|
|||||||
class Players;
|
class Players;
|
||||||
struct EngineSettings;
|
struct EngineSettings;
|
||||||
|
|
||||||
|
namespace voxels {
|
||||||
|
class Pathfinding;
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief A level, contains chunks and objects
|
/// @brief A level, contains chunks and objects
|
||||||
class Level {
|
class Level {
|
||||||
std::unique_ptr<World> world;
|
std::unique_ptr<World> world;
|
||||||
@ -30,6 +34,7 @@ public:
|
|||||||
std::unique_ptr<LevelEvents> events;
|
std::unique_ptr<LevelEvents> events;
|
||||||
std::unique_ptr<Entities> entities;
|
std::unique_ptr<Entities> entities;
|
||||||
std::unique_ptr<Players> players;
|
std::unique_ptr<Players> players;
|
||||||
|
std::unique_ptr<voxels::Pathfinding> pathfinding;
|
||||||
std::vector<std::shared_ptr<Camera>> cameras; // move somewhere?
|
std::vector<std::shared_ptr<Camera>> cameras; // move somewhere?
|
||||||
|
|
||||||
Level(
|
Level(
|
||||||
|
|||||||
@ -11,7 +11,7 @@ TEST(GLSLExtension, processing) {
|
|||||||
"float sum(float a, float b) {\n"
|
"float sum(float a, float b) {\n"
|
||||||
" return a + b;\n"
|
" return a + b;\n"
|
||||||
"}\n",
|
"}\n",
|
||||||
true
|
true, {}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
@ -27,7 +27,7 @@ TEST(GLSLExtension, processing) {
|
|||||||
" vec4 color = texture(u_screen, v_uv);\n"
|
" vec4 color = texture(u_screen, v_uv);\n"
|
||||||
" return mix(color, 1.0 - color, p_intensity);\n"
|
" return mix(color, 1.0 - color, p_intensity);\n"
|
||||||
"}\n",
|
"}\n",
|
||||||
false);
|
false, {});
|
||||||
std::cout << processed.code << std::endl;
|
std::cout << processed.code << std::endl;
|
||||||
} catch (const parsing_error& err) {
|
} catch (const parsing_error& err) {
|
||||||
std::cerr << err.errorLog() << std::endl;
|
std::cerr << err.errorLog() << std::endl;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user