mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-10 13:31:49 +00:00
Merge branch 'main' of https://github.com/Xertis/VoxelEngine-Cpp
This commit is contained in:
commit
d4e5bc99a0
61 changed files with 1209 additions and 534 deletions
|
|
@ -95,6 +95,23 @@ Example:
|
||||||
0. A rectangular area with a width, height and length of 0.4 m, centered at 0.0.
|
0. A rectangular area with a width, height and length of 0.4 m, centered at 0.0.
|
||||||
1. Radial area with a radius of 1.6 m.
|
1. Radial area with a radius of 1.6 m.
|
||||||
|
|
||||||
|
### *solid*
|
||||||
|
|
||||||
|
Makes the entity's hitbox an obstacle. When enabling this property for `dynamic` body, a mass must be specified.
|
||||||
|
|
||||||
|
### *mass*
|
||||||
|
|
||||||
|
Defines the entity's mass. Kilograms are used as the default.
|
||||||
|
For `kinematic` bodies, an infinite mass is used instead.
|
||||||
|
|
||||||
|
### *elasticity*
|
||||||
|
|
||||||
|
Defines the entity's elasticity (affects the bounce on collision).
|
||||||
|
|
||||||
|
### *step-height*
|
||||||
|
|
||||||
|
Defines the maximum height of an obstacle an entity can step onto without jumping.
|
||||||
|
|
||||||
## View
|
## View
|
||||||
|
|
||||||
### *skeleton-name*
|
### *skeleton-name*
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,17 @@ A library for managing audio/visual weather effects.
|
||||||
|
|
||||||
Weather settings:
|
Weather settings:
|
||||||
|
|
||||||
| Property | Description | Default |
|
| Property | Description | Default |
|
||||||
| ------------ | ------------------------------ | ------- |
|
| ------------- | -------------------------------- | ----------------- |
|
||||||
| fall | Precipitation (see table 2) | {} |
|
| fall | Precipitation (see table 2) | {} |
|
||||||
| clouds | Cloudiness [0.0, 1.0] | 0.0 |
|
| clouds | Cloudiness [0.0, 1.0] | 0.0 |
|
||||||
| fog_opacity | Maximum fog density [0.0, 1.0] | 0.0 |
|
| fog_opacity | Maximum fog density [0.0, 1.0] | 0.0 |
|
||||||
| fog_dencity | Fog density | 1.0 |
|
| fog_dencity | Fog density | 1.0 |
|
||||||
| fog_curve | Fog curve | 1.0 |
|
| fog_curve | Fog curve | 1.0 |
|
||||||
| thunder_rate | Thunder rate [0.0, 1.0] | 0.0 |
|
| thunder_rate | Thunder rate [0.0, 1.0] | 0.0 |
|
||||||
|
| sky_tint | Sky color multiplier | {1, 1, 1} |
|
||||||
|
| clouds_tint | Cloud color multiplier | {1, 1, 1} |
|
||||||
|
| min_sky_light | Minimum sky-emitted light (moon) | {0.2, 0.25, 0.33} |
|
||||||
|
|
||||||
Precipitation:
|
Precipitation:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,14 +78,14 @@ Returns the id of all base packages (non-removeable)
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
pack.get_info(packid: str) -> {
|
pack.get_info(packid: str) -> {
|
||||||
id: str,
|
id: str,
|
||||||
title: str,
|
title: str,
|
||||||
creator: str,
|
creator: str,
|
||||||
description: str,
|
description: str,
|
||||||
version: str,
|
version: str,
|
||||||
path: str,
|
path: str,
|
||||||
icon: str, -- not available in headless mode
|
icon: str, -- not available in headless mode
|
||||||
dependencies: optional strings array
|
dependencies: optional strings array
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -95,6 +95,7 @@ Returns information about the pack (not necessarily installed).
|
||||||
- `!` - required
|
- `!` - required
|
||||||
- `?` - optional
|
- `?` - optional
|
||||||
- `~` - weak
|
- `~` - weak
|
||||||
|
|
||||||
for example `!teal`
|
for example `!teal`
|
||||||
|
|
||||||
To obtain information about multiple packs, use table of ids to avoid re-scanning:one
|
To obtain information about multiple packs, use table of ids to avoid re-scanning:one
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,21 @@ body:set_crouching(enabled: bool)
|
||||||
body:get_body_type() -> str
|
body:get_body_type() -> str
|
||||||
-- Sets the physical body type
|
-- Sets the physical body type
|
||||||
body:set_body_type(type: str)
|
body:set_body_type(type: str)
|
||||||
|
|
||||||
|
-- Returns the body material (same as for blocks)
|
||||||
|
body:get_material() -> str
|
||||||
|
-- Sets the body material
|
||||||
|
body:set_material(material: str)
|
||||||
|
|
||||||
|
-- Returns the body's mass
|
||||||
|
body:get_mass() -> number
|
||||||
|
-- Sets the body's mass
|
||||||
|
body:set_mass(mass: number)
|
||||||
|
|
||||||
|
-- Returns the body's elasticity
|
||||||
|
body:get_elasticity() -> number
|
||||||
|
-- Sets the body's elasticity
|
||||||
|
body:set_elasticity(elasticity: number)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Skeleton
|
### Skeleton
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ A panel that controls the size and position of two elements. Allows the user to
|
||||||
|
|
||||||
Inner text is a button text.
|
Inner text is a button text.
|
||||||
|
|
||||||
|
- `font` - font name
|
||||||
- `text-align` - inner text alignment (*left/center/right*). Type: string.
|
- `text-align` - inner text alignment (*left/center/right*). Type: string.
|
||||||
|
|
||||||
## *checkbox*
|
## *checkbox*
|
||||||
|
|
@ -108,6 +109,7 @@ Inner text is a button text.
|
||||||
|
|
||||||
## *label*
|
## *label*
|
||||||
|
|
||||||
|
- `font` - font name
|
||||||
- `text-align` - text alignment (*left/center/right*). Type: string.
|
- `text-align` - text alignment (*left/center/right*). Type: string.
|
||||||
- `valign` - vertical text alignment: top/center/bottom.
|
- `valign` - vertical text alignment: top/center/bottom.
|
||||||
- `supplier` - text supplier (called every frame).
|
- `supplier` - text supplier (called every frame).
|
||||||
|
|
@ -131,6 +133,7 @@ Inner text is a button text.
|
||||||
|
|
||||||
Inner text - initially entered text
|
Inner text - initially entered text
|
||||||
|
|
||||||
|
- `font` - font name
|
||||||
- `placeholder` - placeholder text (used if the text field is empty)
|
- `placeholder` - placeholder text (used if the text field is empty)
|
||||||
- `hint` - text displayed if the text field is empty (not sent to consumer, sub-consumer and validator).
|
- `hint` - text displayed if the text field is empty (not sent to consumer, sub-consumer and validator).
|
||||||
- `supplier` - text supplier (called every frame)
|
- `supplier` - text supplier (called every frame)
|
||||||
|
|
@ -190,6 +193,14 @@ Example of list description:
|
||||||
- `selected` - initially selected value. Default: "".
|
- `selected` - initially selected value. Default: "".
|
||||||
- `onselect` - function to which the user-selected value is passed
|
- `onselect` - function to which the user-selected value is passed
|
||||||
|
|
||||||
|
## *bindbox*
|
||||||
|
|
||||||
|
An element for displaying and editing key or mouse button bindings.
|
||||||
|
|
||||||
|
- `font` - font name. Type: string.
|
||||||
|
- `binding` - binding name. Type: string.
|
||||||
|
- `padding` - padding between text and element borders. Type: number. Default: 6
|
||||||
|
|
||||||
# Inventory elements
|
# Inventory elements
|
||||||
|
|
||||||
## *inventory*
|
## *inventory*
|
||||||
|
|
@ -213,9 +224,6 @@ Element must be in direct sub-element of *inventory*.
|
||||||
## *slots-grid*
|
## *slots-grid*
|
||||||
|
|
||||||
- `start-index` - inventory slot index of the first slot. Type: integer
|
- `start-index` - inventory slot index of the first slot. Type: integer
|
||||||
- `rows` - number of grid rows (unnecessary if *cols* and *count* specified). Type: integer
|
|
||||||
- `cols` - number of grid columns (unnecessary if *rows* and *count* specified). Type: integer
|
|
||||||
- `count` - total number of slots in grid (unnecessary if *rows* and *cols* specified). Type: integer
|
|
||||||
- `interval` - visual slots interval. Type: number
|
- `interval` - visual slots interval. Type: number
|
||||||
- `padding` - grid padding (not slots interval). Type: number. (*deprecated*)
|
- `padding` - grid padding (not slots interval). Type: number. (*deprecated*)
|
||||||
- `sharefunc` - Lua event called on <btn>LMB</btn> + <btn>Shift</btn>. Inventory id and slot index passed as arguments.
|
- `sharefunc` - Lua event called on <btn>LMB</btn> + <btn>Shift</btn>. Inventory id and slot index passed as arguments.
|
||||||
|
|
@ -223,3 +231,8 @@ Element must be in direct sub-element of *inventory*.
|
||||||
- `onrightclick` - Lua event called on <btn>RMB</btn> click. Inventory id and slot index passed as arguments.
|
- `onrightclick` - Lua event called on <btn>RMB</btn> click. Inventory id and slot index passed as arguments.
|
||||||
- `taking` - the ability to take an item from a slot.
|
- `taking` - the ability to take an item from a slot.
|
||||||
- `placing` - the ability to put an item in a slot.
|
- `placing` - the ability to put an item in a slot.
|
||||||
|
|
||||||
|
Slots configuration (just specify two attributes):
|
||||||
|
- `rows` - number of rows. Type: integer
|
||||||
|
- `cols` - number of columns. Type: integer
|
||||||
|
- `count` - total number of slots. Type: integer
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,23 @@
|
||||||
0. Прямоугольная область шириной, высотой и длиной в 0.4 м. с центром в 0.0.
|
0. Прямоугольная область шириной, высотой и длиной в 0.4 м. с центром в 0.0.
|
||||||
1. Радиальная область с радиусом 1.6 м.
|
1. Радиальная область с радиусом 1.6 м.
|
||||||
|
|
||||||
|
### Осязаемость - *solid*
|
||||||
|
|
||||||
|
Делает хитбокс сущности осязаемым препятствием. Вместе с включением данного свойства у `dynamic` тел следует указать массу (mass).
|
||||||
|
|
||||||
|
### Масса - *mass*
|
||||||
|
|
||||||
|
Определяет массу сущности. За стандарт приняты килограммы.
|
||||||
|
В случае `kinematic` тела вместо указанной используется бесконечная масса.
|
||||||
|
|
||||||
|
### Эластичность - *elasticity*
|
||||||
|
|
||||||
|
Определяет упругость сущности.
|
||||||
|
|
||||||
|
### Высота шага - *step-height*
|
||||||
|
|
||||||
|
Определяет максимальную высоту препятствия, на которое сущность может шагнуть без прыжка.
|
||||||
|
|
||||||
## Вид
|
## Вид
|
||||||
|
|
||||||
### Имя скелета - *skeleton-name*
|
### Имя скелета - *skeleton-name*
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,17 @@
|
||||||
|
|
||||||
Настройки погоды:
|
Настройки погоды:
|
||||||
|
|
||||||
| Свойство | Описание | По-умолчанию |
|
| Свойство | Описание | По-умолчанию |
|
||||||
| ------------ | ---------------------------------------- | ------------ |
|
| ------------- | ------------------------------------------ | ----------------- |
|
||||||
| fall | Осадки (см. таблица 2) | {} |
|
| fall | Осадки (см. таблица 2) | {} |
|
||||||
| clouds | Облачность [0.0, 1.0] | 0.0 |
|
| clouds | Облачность [0.0, 1.0] | 0.0 |
|
||||||
| fog_opacity | Максимальная плотность тумана [0.0, 1.0] | 0.0 |
|
| fog_opacity | Максимальная плотность тумана [0.0, 1.0] | 0.0 |
|
||||||
| fog_dencity | Плотность тумана | 1.0 |
|
| fog_dencity | Плотность тумана | 1.0 |
|
||||||
| fog_curve | Кривая тумана | 1.0 |
|
| fog_curve | Кривая тумана | 1.0 |
|
||||||
| thunder_rate | Частота грома [0.0, 1.0] | 0.0 |
|
| thunder_rate | Частота грома [0.0, 1.0] | 0.0 |
|
||||||
|
| sky_tint | Множитель цвета неба | {1, 1, 1} |
|
||||||
|
| clouds_tint | Множитель цвета облаков | {1, 1, 1} |
|
||||||
|
| min_sky_light | Минимальный свет излучаемый небом (лунный) | {0.2, 0.25, 0.33} |
|
||||||
|
|
||||||
Осадки:
|
Осадки:
|
||||||
|
|
||||||
|
|
@ -50,4 +53,4 @@ gfx.weather.get_fall_intensity() -> number
|
||||||
|
|
||||||
-- Проверяет, происходит ли в данный момент переключение погоды
|
-- Проверяет, происходит ли в данный момент переключение погоды
|
||||||
gfx.weather.is_transition() -> boolean
|
gfx.weather.is_transition() -> boolean
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -114,15 +114,29 @@ mat4.decompose(m: matrix)
|
||||||
} или nil
|
} или nil
|
||||||
```
|
```
|
||||||
|
|
||||||
## Отслеживание точки *mat4.look_at(...)*
|
## Отслеживание точки - *mat4.look_at(...)*
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- cоздает матрицу вида с точки 'eye' на точку 'center', где вектор 'up' определяет верх.
|
-- cоздаёт матрицу вида с точки 'eye' на точку 'center', где вектор 'up' определяет верх.
|
||||||
mat4.look_at(eye: vec3, center: vec3, up: vec3)
|
mat4.look_at(eye: vec3, center: vec3, up: vec3)
|
||||||
-- записывает матрицу вида в dst
|
-- записывает матрицу вида в dst
|
||||||
mat4.look_at(eye: vec3, center: vec3, up: vec3, dst: matrix)
|
mat4.look_at(eye: vec3, center: vec3, up: vec3, dst: matrix)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Расчёт матрицы перспективы - *mat4.perspective*
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- возвращает расчитанную матрицу перспективы.
|
||||||
|
-- параметр fov принимает значение вертикального поля зрения в градусах.
|
||||||
|
-- параметр ratio задаёт отношение сторон (сторона X к стороне Y, пример: для монитора 800:600 отношение сторон равно 4 к 3, или 4 / 3).
|
||||||
|
-- параметр near задаёт расстояние от камеры до плоскости Near.
|
||||||
|
-- параметр far задаёт расстояние от камеры до плоскости Far.
|
||||||
|
mat4.perspective(fov: number, ratio: number, near: number, far: number) -> mat4
|
||||||
|
|
||||||
|
-- расчитывает матрицу перспективы на основе переданных значений и записывает её в переменную dst.
|
||||||
|
mat4.perspective(fov: number, ratio: number, near: number, far: number, dst: mat4) -> nil
|
||||||
|
```
|
||||||
|
|
||||||
## Перевод в строку - *mat4.tostring(...)*
|
## Перевод в строку - *mat4.tostring(...)*
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ pack.get_info(packid: string) -> {
|
||||||
- `!` - required
|
- `!` - required
|
||||||
- `?` - optional
|
- `?` - optional
|
||||||
- `~` - weak
|
- `~` - weak
|
||||||
|
|
||||||
например `!teal`
|
например `!teal`
|
||||||
|
|
||||||
Для получения информации о нескольких паках используйте таблицу id, чтобы не
|
Для получения информации о нескольких паках используйте таблицу id, чтобы не
|
||||||
|
|
@ -101,4 +102,4 @@ pack.assemble(packids: table<string>) -> table<string>
|
||||||
pack.request_writeable(packid: string, callback: function(string))
|
pack.request_writeable(packid: string, callback: function(string))
|
||||||
```
|
```
|
||||||
|
|
||||||
Запрашивает у пользователя право на модификацию пака. При подтвержении новая точка входа будет передана в callback.
|
Запрашивает у пользователя право на модификацию пака. При подтвержении новая точка входа будет передана в callback.
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,34 @@
|
||||||
|
|
||||||
Библиотека для работы с кватернионами.
|
Библиотека для работы с кватернионами.
|
||||||
|
|
||||||
## Кватернион из матрицы - *mat4.from_quat(...)*
|
## Кватернион из матрицы - *quat.from_mat4(...)*
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- создает кватернион на основе матрицы вращения
|
-- создаёт кватернион на основе матрицы вращения
|
||||||
quat.from_mat4(m: matrix) -> quat
|
quat.from_mat4(m: matrix) -> quat
|
||||||
|
|
||||||
-- записывает кватернион по матрице вращения в dst
|
-- записывает кватернион по матрице вращения в dst
|
||||||
quat.from_mat4(m: matrix, dst: quat)
|
quat.from_mat4(m: matrix, dst: quat)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Кватернион из углов Эйлера - *quat.from_euler(...)*
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- создаёт кватернион на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll)
|
||||||
|
-- (значения углов строго в градусах)
|
||||||
|
quat.from_euler(euler: vec3) -> quat
|
||||||
|
|
||||||
|
-- записывает кватернион в dst на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll)
|
||||||
|
-- (значения углов строго в градусах)
|
||||||
|
quat.from_euler(euler: vec3, dst: quat)
|
||||||
|
```
|
||||||
|
|
||||||
## Сферическая линейная интерполяция - *quat.slerp(...)*
|
## Сферическая линейная интерполяция - *quat.slerp(...)*
|
||||||
|
|
||||||
Интерполяция всегда выполняется по короткому пути, а вращение выполняется с постоянной скоростью.
|
Интерполяция всегда выполняется по короткому пути, а вращение выполняется с постоянной скоростью.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- создает кватернион как интерполяцию между a и b,
|
-- создаёт кватернион как интерполяцию между a и b,
|
||||||
-- где t - фактор интерполяции
|
-- где t - фактор интерполяции
|
||||||
quat.slerp(a: quat, b: quat, t: number) -> quat
|
quat.slerp(a: quat, b: quat, t: number) -> quat
|
||||||
|
|
||||||
|
|
@ -29,6 +41,6 @@ quat.slerp(a: quat, b: quat, t: number, dst: quat)
|
||||||
## Перевод в строку - *quat.tostring(...)*
|
## Перевод в строку - *quat.tostring(...)*
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- возвращает строку представляющую содержимое кватерниона
|
-- возвращает строку, представляющую содержимое кватерниона
|
||||||
quat.tostring(q: quat) -> string
|
quat.tostring(q: quat) -> string
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,21 @@ body:set_crouching(enabled: bool)
|
||||||
body:get_body_type() -> str
|
body:get_body_type() -> str
|
||||||
-- Устанавливает тип физического тела
|
-- Устанавливает тип физического тела
|
||||||
body:set_body_type(type: str)
|
body:set_body_type(type: str)
|
||||||
|
|
||||||
|
-- Возвращает материал тела (то же, что и у блоков)
|
||||||
|
body:get_material() -> str
|
||||||
|
-- Устанавливает материал тела
|
||||||
|
body:set_material(material: str)
|
||||||
|
|
||||||
|
-- Возвращает массу тела
|
||||||
|
body:get_mass() -> number
|
||||||
|
-- Устанавливает массу тела
|
||||||
|
body:set_mass(mass: number)
|
||||||
|
|
||||||
|
-- Возвращает упругость тела
|
||||||
|
body:get_elasticity() -> number
|
||||||
|
-- Устанавливает упругость тела
|
||||||
|
body:set_elasticity(elasticity: number)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Skeleton
|
### Skeleton
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,18 @@ io_stream:write_line(string)
|
||||||
io_stream:read_fully(
|
io_stream:read_fully(
|
||||||
[опционально] useTable: boolean
|
[опционально] useTable: boolean
|
||||||
) -> Bytearray | table<number> | table<string> | string
|
) -> Bytearray | table<number> | table<string> | string
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Устанавливает позицию в потоке
|
||||||
|
Режимы:
|
||||||
|
b - Задаёт позицию относительно начало файла
|
||||||
|
c - Задаёт позицию относительно текущей позиции
|
||||||
|
e - Задаёт позицию относительно конца файла
|
||||||
|
--]]
|
||||||
|
io_stream:seek(
|
||||||
|
mode: string
|
||||||
|
offset: number
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Методы Buffered-режима
|
## Методы Buffered-режима
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@
|
||||||
|
|
||||||
Внутренний текст - текст кнопки.
|
Внутренний текст - текст кнопки.
|
||||||
|
|
||||||
|
- `font` - имя шрифта
|
||||||
- `text-align` - выравнивание текста ("left", "center" или "right"). Тип: строка.
|
- `text-align` - выравнивание текста ("left", "center" или "right"). Тип: строка.
|
||||||
|
|
||||||
## Флажок - *checkbox*
|
## Флажок - *checkbox*
|
||||||
|
|
@ -110,6 +111,7 @@
|
||||||
|
|
||||||
## Метка - *label*
|
## Метка - *label*
|
||||||
|
|
||||||
|
- `font` - имя шрифта
|
||||||
- `text-align` - выравнивание текста ("left", "center" или "right"). Тип: строка.
|
- `text-align` - выравнивание текста ("left", "center" или "right"). Тип: строка.
|
||||||
- `valign` - вертикальное выравнивание текста: top/center/bottom
|
- `valign` - вертикальное выравнивание текста: top/center/bottom
|
||||||
- `supplier` - поставщик текста (вызывается каждый кадр)
|
- `supplier` - поставщик текста (вызывается каждый кадр)
|
||||||
|
|
@ -132,6 +134,7 @@
|
||||||
|
|
||||||
Внутренний текст - изначально введенный текст
|
Внутренний текст - изначально введенный текст
|
||||||
|
|
||||||
|
- `font` - имя шрифта
|
||||||
- `placeholder` - текст подстановки (используется если текстовое поле пусто)
|
- `placeholder` - текст подстановки (используется если текстовое поле пусто)
|
||||||
- `hint` - текст, отображаемый, если текстовое поле пусто (не отправляется в consumer, sub-consumer и validator).
|
- `hint` - текст, отображаемый, если текстовое поле пусто (не отправляется в consumer, sub-consumer и validator).
|
||||||
- `supplier` - поставщик текста (вызывается каждый кадр)
|
- `supplier` - поставщик текста (вызывается каждый кадр)
|
||||||
|
|
@ -191,6 +194,14 @@
|
||||||
- `selected` - изначально выбранное значение. По-умолчанию: "".
|
- `selected` - изначально выбранное значение. По-умолчанию: "".
|
||||||
- `onselect` - функция, в которую передаётся выбранное пользователем значение
|
- `onselect` - функция, в которую передаётся выбранное пользователем значение
|
||||||
|
|
||||||
|
## Привязка ввода - *bindbox*
|
||||||
|
|
||||||
|
Элемент для отображения и изменения привязки клавиш или кнопок мыши.
|
||||||
|
|
||||||
|
- `font` - имя шрифта. Тип: строка.
|
||||||
|
- `binding` - имя привязки. Тип: строка.
|
||||||
|
- `padding` - отступ между текстом и границами элемента. Тип: число. По-умолчанию: 6
|
||||||
|
|
||||||
# Элементы инвентаря
|
# Элементы инвентаря
|
||||||
|
|
||||||
## Инвентарь - *inventory*
|
## Инвентарь - *inventory*
|
||||||
|
|
@ -214,10 +225,8 @@
|
||||||
## Сетка слотов - *slots-grid*
|
## Сетка слотов - *slots-grid*
|
||||||
|
|
||||||
Элемент должен находиться внутри `inventory` элемента, без посредников.
|
Элемент должен находиться внутри `inventory` элемента, без посредников.
|
||||||
|
|
||||||
- `start-index` - индекс первого слота
|
- `start-index` - индекс первого слота
|
||||||
- `rows` - число рядов (не указывается, если указано cols).
|
|
||||||
- `cols` - число столбцов (не указывается, если указано rows).
|
|
||||||
- `count` - общее число слотов (не указывается, если указаны rows и cols).
|
|
||||||
- `interval` - интервал между слотами. Тип: число.
|
- `interval` - интервал между слотами. Тип: число.
|
||||||
- `padding` - отступ вокруг решетки слотов. Тип: число. (*атрибут будет удален*)
|
- `padding` - отступ вокруг решетки слотов. Тип: число. (*атрибут будет удален*)
|
||||||
- `sharefunc` - lua событие вызываемое при использовании ЛКМ + Shift. Передается id инвентаря и индекс слота
|
- `sharefunc` - lua событие вызываемое при использовании ЛКМ + Shift. Передается id инвентаря и индекс слота
|
||||||
|
|
@ -225,3 +234,8 @@
|
||||||
- `onrightclick` - lua событие вызываемое при использовании ПКМ. Передается id инвентаря и индекс слота
|
- `onrightclick` - lua событие вызываемое при использовании ПКМ. Передается id инвентаря и индекс слота
|
||||||
- `taking` - возможность взять предмет из слота.
|
- `taking` - возможность взять предмет из слота.
|
||||||
- `placing` - возможность положить предмет в слот.
|
- `placing` - возможность положить предмет в слот.
|
||||||
|
|
||||||
|
Конфигурация слотов (достаточно указать два атрибута):
|
||||||
|
- `rows` - число рядов. Тип: число.
|
||||||
|
- `cols` - число столбцов. Тип: число.
|
||||||
|
- `count` - общее число слотов. Тип: число.
|
||||||
|
|
|
||||||
|
|
@ -3,5 +3,6 @@
|
||||||
"base:falling_block"
|
"base:falling_block"
|
||||||
],
|
],
|
||||||
"skeleton-name": "base:block",
|
"skeleton-name": "base:block",
|
||||||
"hitbox": [0.98, 0.98, 0.98]
|
"hitbox": [0.98, 0.98, 0.98],
|
||||||
|
"solid": true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ do -- setup visuals
|
||||||
0, 0, 0, 1
|
0, 0, 0, 1
|
||||||
}
|
}
|
||||||
rig:set_matrix(0, matrix)
|
rig:set_matrix(0, matrix)
|
||||||
|
body:set_material(block.material(id))
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_grounded()
|
function on_grounded()
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ local Rigidbody = {__index={
|
||||||
set_crouching=function(self, b) return __rigidbody.set_crouching(self.eid, b) end,
|
set_crouching=function(self, b) return __rigidbody.set_crouching(self.eid, b) end,
|
||||||
get_body_type=function(self) return __rigidbody.get_body_type(self.eid) end,
|
get_body_type=function(self) return __rigidbody.get_body_type(self.eid) end,
|
||||||
set_body_type=function(self, s) return __rigidbody.set_body_type(self.eid, s) end,
|
set_body_type=function(self, s) return __rigidbody.set_body_type(self.eid, s) end,
|
||||||
|
get_material=function(self) return __rigidbody.get_material(self.eid) end,
|
||||||
|
set_material=function(self, s) return __rigidbody.set_material(self.eid, s) end,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
local function new_Rigidbody(eid)
|
local function new_Rigidbody(eid)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ local io_stream = require "core:io_stream"
|
||||||
local lib = {
|
local lib = {
|
||||||
read = file.__read_descriptor,
|
read = file.__read_descriptor,
|
||||||
write = file.__write_descriptor,
|
write = file.__write_descriptor,
|
||||||
|
seek = file.__seek_descriptor,
|
||||||
flush = file.__flush_descriptor,
|
flush = file.__flush_descriptor,
|
||||||
is_alive = file.__has_descriptor,
|
is_alive = file.__has_descriptor,
|
||||||
close = file.__close_descriptor
|
close = file.__close_descriptor
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,10 @@ function lib.write(fd, bytearray)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function lib.seek(fd, mode, offset)
|
||||||
|
error("cannot seek the named pipe")
|
||||||
|
end
|
||||||
|
|
||||||
function lib.flush(fd)
|
function lib.flush(fd)
|
||||||
-- no flush on unix
|
-- no flush on unix
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ function lib.read(handle, len)
|
||||||
|
|
||||||
if not has_data then
|
if not has_data then
|
||||||
return out
|
return out
|
||||||
elseif hasData == -1 then
|
elseif has_data == -1 then
|
||||||
error("failed to read from named pipe: "..tostring(C.GetLastError()))
|
error("failed to read from named pipe: "..tostring(C.GetLastError()))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -84,8 +84,8 @@ end
|
||||||
|
|
||||||
function lib.write(handle, bytearray)
|
function lib.write(handle, bytearray)
|
||||||
local len = #bytearray
|
local len = #bytearray
|
||||||
|
|
||||||
local buffer = FFI.new("uint8_t[?]", len)
|
local buffer = FFI.new("uint8_t[?]", len)
|
||||||
|
|
||||||
for i = 1, len do
|
for i = 1, len do
|
||||||
buffer[i-1] = bytearray[i]
|
buffer[i-1] = bytearray[i]
|
||||||
end
|
end
|
||||||
|
|
@ -97,6 +97,10 @@ function lib.write(handle, bytearray)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function lib.seek(handle, mode, offset)
|
||||||
|
error("cannot seek the named pipe")
|
||||||
|
end
|
||||||
|
|
||||||
function lib.flush(handle)
|
function lib.flush(handle)
|
||||||
C.FlushFileBuffers(handle)
|
C.FlushFileBuffers(handle)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -369,6 +369,10 @@ function io_stream:write(arg, ...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function io_stream:seek(mode, offset)
|
||||||
|
self.ioLib.seek(self.descriptor, mode, offset)
|
||||||
|
end
|
||||||
|
|
||||||
function io_stream:is_alive()
|
function io_stream:is_alive()
|
||||||
return self.ioLib.is_alive(self.descriptor)
|
return self.ioLib.is_alive(self.descriptor)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ end
|
||||||
|
|
||||||
local prepared = false
|
local prepared = false
|
||||||
body:set_enabled(false)
|
body:set_enabled(false)
|
||||||
|
mob:set_flight(true)
|
||||||
|
|
||||||
function on_physics_update(delta)
|
function on_physics_update(delta)
|
||||||
if not prepared then
|
if not prepared then
|
||||||
|
|
|
||||||
|
|
@ -344,9 +344,10 @@ static void perform_box(const xmlelement& root, ModelBuilder& builder) {
|
||||||
static void perform_bone(const xmlelement& root, ModelBuilder& builder, Context& ctx) {
|
static void perform_bone(const xmlelement& root, ModelBuilder& builder, Context& ctx) {
|
||||||
std::string name = root.attr("name", "").getText();
|
std::string name = root.attr("name", "").getText();
|
||||||
|
|
||||||
|
glm::vec3 movement {};
|
||||||
glm::mat4 tsf(1.0f);
|
glm::mat4 tsf(1.0f);
|
||||||
if (root.has("move")) {
|
if (root.has("move")) {
|
||||||
tsf = glm::translate(tsf, root.attr("move").asVec3());
|
movement = root.attr("move").asVec3();
|
||||||
}
|
}
|
||||||
if (root.has("rotate")) {
|
if (root.has("rotate")) {
|
||||||
auto text = root.attr("rotate").getText();
|
auto text = root.attr("rotate").getText();
|
||||||
|
|
@ -371,7 +372,7 @@ static void perform_bone(const xmlelement& root, ModelBuilder& builder, Context&
|
||||||
}
|
}
|
||||||
builder.pop();
|
builder.pop();
|
||||||
} else {
|
} else {
|
||||||
glm::vec3 origin = builder.getTransform() * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
glm::vec3 origin = builder.getTransform() * glm::vec4(movement, 1.0f);
|
||||||
size_t boneIndex = ctx.boneIndex++;
|
size_t boneIndex = ctx.boneIndex++;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Bone>> bones;
|
std::vector<std::unique_ptr<Bone>> bones;
|
||||||
|
|
|
||||||
|
|
@ -88,5 +88,8 @@ template<> void ContentUnitLoader<EntityDef>::loadUnit(
|
||||||
|
|
||||||
root.at("skeleton-name").get(def.skeletonName);
|
root.at("skeleton-name").get(def.skeletonName);
|
||||||
root.at("blocking").get(def.blocking);
|
root.at("blocking").get(def.blocking);
|
||||||
|
root.at("solid").get(def.solid);
|
||||||
|
root.at("mass").get(def.mass);
|
||||||
|
root.at("elasticity").get(def.elasticity);
|
||||||
root.at("step-height").get(def.stepHeight);
|
root.at("step-height").get(def.stepHeight);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ AssetsManagement::AssetsManagement(Engine& engine)
|
||||||
|
|
||||||
AssetsManagement::~AssetsManagement() {
|
AssetsManagement::~AssetsManagement() {
|
||||||
finishBackgroundLoader();
|
finishBackgroundLoader();
|
||||||
|
assets.reset();
|
||||||
|
assetsVault.clearKeepedObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Assets* AssetsManagement::getStorage() const {
|
const Assets* AssetsManagement::getStorage() const {
|
||||||
|
|
|
||||||
|
|
@ -10,17 +10,19 @@
|
||||||
#include "logic/LevelController.hpp"
|
#include "logic/LevelController.hpp"
|
||||||
#include "logic/PlayerController.hpp"
|
#include "logic/PlayerController.hpp"
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
|
#include "physics/Hitbox.hpp"
|
||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
|
#include "voxels/Chunks.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "engine/Engine.hpp"
|
#include "engine/Engine.hpp"
|
||||||
|
|
||||||
LevelFrontend::LevelFrontend(
|
LevelFrontend::LevelFrontend(
|
||||||
Engine& engine,
|
Engine& engine,
|
||||||
Player* currentPlayer,
|
PlayerController& playerController,
|
||||||
LevelController* controller,
|
LevelController& controller,
|
||||||
const EngineSettings& settings
|
const EngineSettings& settings
|
||||||
)
|
)
|
||||||
: level(*controller->getLevel()),
|
: level(*controller.getLevel()),
|
||||||
controller(controller),
|
controller(controller),
|
||||||
assets(*engine.getAssets()),
|
assets(*engine.getAssets()),
|
||||||
contentCache(std::make_unique<ContentGfxCache>(
|
contentCache(std::make_unique<ContentGfxCache>(
|
||||||
|
|
@ -36,45 +38,79 @@ LevelFrontend::LevelFrontend(
|
||||||
"block-previews"
|
"block-previews"
|
||||||
);
|
);
|
||||||
|
|
||||||
auto& rassets = assets;
|
auto& currentPlayer = playerController.getPlayer();
|
||||||
controller->getBlocksController()->listenBlockInteraction(
|
auto& assets = this->assets;
|
||||||
[currentPlayer, controller, &rassets](auto player, const auto& pos, const auto& def, BlockInteraction type) {
|
auto& level = this->level;
|
||||||
const auto& level = *controller->getLevel();
|
|
||||||
|
playerController.setFootstepCallback(
|
||||||
|
[&level, ¤tPlayer, &assets](const Hitbox& hitbox) {
|
||||||
|
const BlockMaterial* material = nullptr;
|
||||||
|
if (hitbox.groundMaterial.empty()) {
|
||||||
|
const auto& pos = hitbox.position;
|
||||||
|
const auto& half = hitbox.getHalfSize();
|
||||||
|
|
||||||
|
auto& blockIndices = level.content.getIndices()->blocks;
|
||||||
|
for (int offsetZ = -1; offsetZ <= 1; offsetZ++) {
|
||||||
|
for (int offsetX = -1; offsetX <= 1; offsetX++) {
|
||||||
|
int x = std::floor(pos.x + half.x * offsetX);
|
||||||
|
int y = std::floor(pos.y - half.y * 1.1f);
|
||||||
|
int z = std::floor(pos.z + half.z * offsetZ);
|
||||||
|
auto vox = currentPlayer.chunks->get(x, y, z);
|
||||||
|
if (vox) {
|
||||||
|
auto& def = blockIndices.require(vox->id);
|
||||||
|
if (!def.obstacle) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
material = level.content.findBlockMaterial(def.material);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
material = level.content.findBlockMaterial(hitbox.groundMaterial);
|
||||||
|
}
|
||||||
|
if (material == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sound = assets.get<audio::Sound>(material->stepsSound);
|
||||||
|
glm::vec3 pos {};
|
||||||
|
auto soundsCamera = currentPlayer.currentCamera.get();
|
||||||
|
if (currentPlayer.isCurrentCameraBuiltin()) {
|
||||||
|
soundsCamera = currentPlayer.fpCamera.get();
|
||||||
|
}
|
||||||
|
bool relative = soundsCamera == currentPlayer.fpCamera.get();
|
||||||
|
if (!relative) {
|
||||||
|
pos = currentPlayer.getPosition();
|
||||||
|
}
|
||||||
|
audio::play(
|
||||||
|
sound,
|
||||||
|
pos,
|
||||||
|
relative,
|
||||||
|
0.333f,
|
||||||
|
1.0f + (rand() % 6 - 3) * 0.05f,
|
||||||
|
false,
|
||||||
|
audio::PRIORITY_LOW,
|
||||||
|
audio::get_channel_index("regular")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
controller.getBlocksController()->listenBlockInteraction(
|
||||||
|
[&level, &assets]
|
||||||
|
(auto player, const auto& pos, const auto& def, BlockInteraction type) {
|
||||||
auto material = level.content.findBlockMaterial(def.material);
|
auto material = level.content.findBlockMaterial(def.material);
|
||||||
if (material == nullptr) {
|
if (material == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == BlockInteraction::step) {
|
if (type != BlockInteraction::step) {
|
||||||
auto sound = rassets.get<audio::Sound>(material->stepsSound);
|
|
||||||
glm::vec3 pos {};
|
|
||||||
auto soundsCamera = currentPlayer->currentCamera.get();
|
|
||||||
if (currentPlayer->isCurrentCameraBuiltin()) {
|
|
||||||
soundsCamera = currentPlayer->fpCamera.get();
|
|
||||||
}
|
|
||||||
bool relative = player == currentPlayer &&
|
|
||||||
soundsCamera == currentPlayer->fpCamera.get();
|
|
||||||
if (!relative) {
|
|
||||||
pos = player->getPosition();
|
|
||||||
}
|
|
||||||
audio::play(
|
|
||||||
sound,
|
|
||||||
pos,
|
|
||||||
relative,
|
|
||||||
0.333f,
|
|
||||||
1.0f + (rand() % 6 - 3) * 0.05f,
|
|
||||||
false,
|
|
||||||
audio::PRIORITY_LOW,
|
|
||||||
audio::get_channel_index("regular")
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
audio::Sound* sound = nullptr;
|
audio::Sound* sound = nullptr;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BlockInteraction::placing:
|
case BlockInteraction::placing:
|
||||||
sound = rassets.get<audio::Sound>(material->placeSound);
|
sound = assets.get<audio::Sound>(material->placeSound);
|
||||||
break;
|
break;
|
||||||
case BlockInteraction::destruction:
|
case BlockInteraction::destruction:
|
||||||
sound = rassets.get<audio::Sound>(material->breakSound);
|
sound = assets.get<audio::Sound>(material->breakSound);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -108,6 +144,6 @@ const ContentGfxCache& LevelFrontend::getContentGfxCache() const {
|
||||||
return *contentCache;
|
return *contentCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelController* LevelFrontend::getController() const {
|
LevelController& LevelFrontend::getController() const {
|
||||||
return controller;
|
return controller;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,24 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class Level;
|
|
||||||
class Assets;
|
class Assets;
|
||||||
class Player;
|
|
||||||
class Engine;
|
|
||||||
class ContentGfxCache;
|
class ContentGfxCache;
|
||||||
|
class Engine;
|
||||||
|
class Level;
|
||||||
class LevelController;
|
class LevelController;
|
||||||
|
class PlayerController;
|
||||||
struct EngineSettings;
|
struct EngineSettings;
|
||||||
|
|
||||||
class LevelFrontend {
|
class LevelFrontend {
|
||||||
Level& level;
|
Level& level;
|
||||||
LevelController* controller;
|
LevelController& controller;
|
||||||
Assets& assets;
|
Assets& assets;
|
||||||
std::unique_ptr<ContentGfxCache> contentCache;
|
std::unique_ptr<ContentGfxCache> contentCache;
|
||||||
public:
|
public:
|
||||||
LevelFrontend(
|
LevelFrontend(
|
||||||
Engine& engine,
|
Engine& engine,
|
||||||
Player* currentPlayer,
|
PlayerController& currentPlayer,
|
||||||
LevelController* controller,
|
LevelController& controller,
|
||||||
const EngineSettings& settings
|
const EngineSettings& settings
|
||||||
);
|
);
|
||||||
~LevelFrontend();
|
~LevelFrontend();
|
||||||
|
|
@ -27,5 +27,5 @@ public:
|
||||||
Level& getLevel();
|
Level& getLevel();
|
||||||
const ContentGfxCache& getContentGfxCache() const;
|
const ContentGfxCache& getContentGfxCache() const;
|
||||||
ContentGfxCache& getContentGfxCache();
|
ContentGfxCache& getContentGfxCache();
|
||||||
LevelController* getController() const;
|
LevelController& getController() const;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,7 @@ void Hud::updateWorldGenDebug() {
|
||||||
const auto& chunks = *player.chunks;
|
const auto& chunks = *player.chunks;
|
||||||
uint padding = engine.getSettings().chunks.padding.get();
|
uint padding = engine.getSettings().chunks.padding.get();
|
||||||
auto generator =
|
auto generator =
|
||||||
frontend.getController()->getChunksController()->getGenerator();
|
frontend.getController().getChunksController()->getGenerator();
|
||||||
auto debugInfo = generator->createDebugInfo();
|
auto debugInfo = generator->createDebugInfo();
|
||||||
|
|
||||||
int width = debugImgWorldGen->getWidth();
|
int width = debugImgWorldGen->getWidth();
|
||||||
|
|
@ -294,7 +294,7 @@ void Hud::updateWorldGenDebug() {
|
||||||
|
|
||||||
bool isInLoadingZone =
|
bool isInLoadingZone =
|
||||||
frontend.getController()
|
frontend.getController()
|
||||||
->getChunksController()
|
.getChunksController()
|
||||||
->isInLoadingZone(player, padding, ax + ox, az + oz);
|
->isInLoadingZone(player, padding, ax + ox, az + oz);
|
||||||
|
|
||||||
data[(flippedZ * width + x) * 4 + 1] =
|
data[(flippedZ * width + x) * 4 + 1] =
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ LevelScreen::LevelScreen(
|
||||||
);
|
);
|
||||||
|
|
||||||
frontend = std::make_unique<LevelFrontend>(
|
frontend = std::make_unique<LevelFrontend>(
|
||||||
engine, player, controller.get(), settings
|
engine, *playerController, *controller, settings
|
||||||
);
|
);
|
||||||
renderer = std::make_unique<WorldRenderer>(
|
renderer = std::make_unique<WorldRenderer>(
|
||||||
engine, *frontend, *player
|
engine, *frontend, *player
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
class Engine;
|
class Engine;
|
||||||
class World;
|
class World;
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,9 @@ namespace {
|
||||||
const float CROUCH_SHIFT_Y = -0.2f;
|
const float CROUCH_SHIFT_Y = -0.2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
CameraControl::CameraControl(Player& player, const CameraSettings& settings)
|
CameraControl::CameraControl(
|
||||||
|
Player& player, const CameraSettings& settings
|
||||||
|
)
|
||||||
: player(player),
|
: player(player),
|
||||||
camera(player.fpCamera),
|
camera(player.fpCamera),
|
||||||
settings(settings),
|
settings(settings),
|
||||||
|
|
@ -195,18 +197,30 @@ void CameraControl::update(
|
||||||
|
|
||||||
refreshPosition();
|
refreshPosition();
|
||||||
|
|
||||||
|
auto castRay = [](Player& player, Camera& camera, const glm::vec3& front)
|
||||||
|
-> glm::vec3 {
|
||||||
|
auto blockEnd = player.chunks->rayCastToObstacle(camera.position, front, 3.0f);
|
||||||
|
auto entityRay = player.getLevel().entities->rayCast(
|
||||||
|
camera.position, front, 3.0f, player.getEntity(), true
|
||||||
|
);
|
||||||
|
if (entityRay.has_value() &&
|
||||||
|
entityRay->distance < glm::distance(camera.position, blockEnd)) {
|
||||||
|
return (camera.position + front * entityRay->distance);
|
||||||
|
} else {
|
||||||
|
return blockEnd;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
camera->updateVectors();
|
camera->updateVectors();
|
||||||
if (player.currentCamera == spCamera) {
|
if (player.currentCamera == spCamera) {
|
||||||
spCamera->position =
|
spCamera->position =
|
||||||
chunks.rayCastToObstacle(camera->position, camera->front, 3.0f) -
|
castRay(player, *camera, camera->front) - 0.4f * camera->front;
|
||||||
0.4f * camera->front;
|
|
||||||
spCamera->dir = -camera->dir;
|
spCamera->dir = -camera->dir;
|
||||||
spCamera->front = -camera->front;
|
spCamera->front = -camera->front;
|
||||||
spCamera->right = -camera->right;
|
spCamera->right = -camera->right;
|
||||||
} else if (player.currentCamera == tpCamera) {
|
} else if (player.currentCamera == tpCamera) {
|
||||||
tpCamera->position =
|
tpCamera->position = castRay(player, *camera, camera->front * -1.0f) +
|
||||||
chunks.rayCastToObstacle(camera->position, -camera->front, 3.0f) +
|
0.4f * camera->front;
|
||||||
0.4f * camera->front;
|
|
||||||
tpCamera->dir = camera->dir;
|
tpCamera->dir = camera->dir;
|
||||||
tpCamera->front = camera->front;
|
tpCamera->front = camera->front;
|
||||||
tpCamera->right = camera->right;
|
tpCamera->right = camera->right;
|
||||||
|
|
@ -229,26 +243,8 @@ PlayerController::PlayerController(
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerController::onFootstep(const Hitbox& hitbox) {
|
void PlayerController::onFootstep(const Hitbox& hitbox) {
|
||||||
auto pos = hitbox.position;
|
if (footstepCallback) {
|
||||||
auto half = hitbox.halfsize;
|
footstepCallback(hitbox);
|
||||||
|
|
||||||
for (int offsetZ = -1; offsetZ <= 1; offsetZ++) {
|
|
||||||
for (int offsetX = -1; offsetX <= 1; offsetX++) {
|
|
||||||
int x = std::floor(pos.x + half.x * offsetX);
|
|
||||||
int y = std::floor(pos.y - half.y * 1.1f);
|
|
||||||
int z = std::floor(pos.z + half.z * offsetZ);
|
|
||||||
auto vox = player.chunks->get(x, y, z);
|
|
||||||
if (vox) {
|
|
||||||
auto& def = level.content.getIndices()->blocks.require(vox->id);
|
|
||||||
if (!def.obstacle) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
blocksController.onBlockInteraction(
|
|
||||||
&player, glm::ivec3(x, y, z), def, BlockInteraction::step
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -557,3 +553,7 @@ void PlayerController::updateInteraction(const Input& inputEvents, float delta)
|
||||||
Player& PlayerController::getPlayer() {
|
Player& PlayerController::getPlayer() {
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayerController::setFootstepCallback(FootstepCallback&& callback) {
|
||||||
|
footstepCallback = std::move(callback);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ struct Hitbox;
|
||||||
struct CameraSettings;
|
struct CameraSettings;
|
||||||
struct EngineSettings;
|
struct EngineSettings;
|
||||||
|
|
||||||
|
using FootstepCallback = std::function<void(const Hitbox&)>;
|
||||||
|
|
||||||
class CameraControl {
|
class CameraControl {
|
||||||
Player& player;
|
Player& player;
|
||||||
std::shared_ptr<Camera> camera;
|
std::shared_ptr<Camera> camera;
|
||||||
|
|
@ -57,6 +59,7 @@ class PlayerController {
|
||||||
CameraControl camControl;
|
CameraControl camControl;
|
||||||
BlocksController& blocksController;
|
BlocksController& blocksController;
|
||||||
float interactionTimer = 0.0f;
|
float interactionTimer = 0.0f;
|
||||||
|
FootstepCallback footstepCallback;
|
||||||
|
|
||||||
void updateKeyboard(const Input& inputEvents);
|
void updateKeyboard(const Input& inputEvents);
|
||||||
void resetKeyboard();
|
void resetKeyboard();
|
||||||
|
|
@ -89,5 +92,8 @@ public:
|
||||||
void postUpdate(
|
void postUpdate(
|
||||||
float delta, int windowHeight, const Input* inputEvents, bool pause
|
float delta, int windowHeight, const Input* inputEvents, bool pause
|
||||||
);
|
);
|
||||||
|
|
||||||
Player& getPlayer();
|
Player& getPlayer();
|
||||||
|
|
||||||
|
void setFootstepCallback(FootstepCallback&& callback);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,48 @@ static int l_set_linear_damping(lua::State* L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_set_material(lua::State* L) {
|
||||||
|
if (auto entity = get_entity(L, 1)) {
|
||||||
|
entity->getRigidbody().hitbox.material = lua::require_string(L, 2);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_get_material(lua::State* L) {
|
||||||
|
if (auto entity = get_entity(L, 1)) {
|
||||||
|
return lua::pushstring(L, entity->getRigidbody().hitbox.material);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_get_mass(lua::State* L) {
|
||||||
|
if (auto entity = get_entity(L, 1)) {
|
||||||
|
return lua::pushnumber(L, entity->getRigidbody().mass);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_set_mass(lua::State* L) {
|
||||||
|
if (auto entity = get_entity(L, 1)) {
|
||||||
|
entity->getRigidbody().mass = lua::tonumber(L, 2);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_get_elasticity(lua::State* L) {
|
||||||
|
if (auto entity = get_entity(L, 1)) {
|
||||||
|
return lua::pushnumber(L, entity->getRigidbody().elasticity);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_set_elasticity(lua::State* L) {
|
||||||
|
if (auto entity = get_entity(L, 1)) {
|
||||||
|
entity->getRigidbody().elasticity = lua::tonumber(L, 2);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const luaL_Reg rigidbodylib[] = {
|
const luaL_Reg rigidbodylib[] = {
|
||||||
{"is_enabled", lua::wrap<l_is_enabled>},
|
{"is_enabled", lua::wrap<l_is_enabled>},
|
||||||
{"set_enabled", lua::wrap<l_set_enabled>},
|
{"set_enabled", lua::wrap<l_set_enabled>},
|
||||||
|
|
@ -169,5 +211,11 @@ const luaL_Reg rigidbodylib[] = {
|
||||||
{"set_crouching", lua::wrap<l_set_crouching>},
|
{"set_crouching", lua::wrap<l_set_crouching>},
|
||||||
{"get_body_type", lua::wrap<l_get_body_type>},
|
{"get_body_type", lua::wrap<l_get_body_type>},
|
||||||
{"set_body_type", lua::wrap<l_set_body_type>},
|
{"set_body_type", lua::wrap<l_set_body_type>},
|
||||||
|
{"set_material", lua::wrap<l_set_material>},
|
||||||
|
{"get_material", lua::wrap<l_get_material>},
|
||||||
|
{"get_mass", lua::wrap<l_get_mass>},
|
||||||
|
{"set_mass", lua::wrap<l_set_mass>},
|
||||||
|
{"get_elasticity", lua::wrap<l_get_elasticity>},
|
||||||
|
{"set_elasticity", lua::wrap<l_set_elasticity>},
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ static int l_set_pos(lua::State* L) {
|
||||||
auto vec = lua::tovec3(L, 2);
|
auto vec = lua::tovec3(L, 2);
|
||||||
check_valid(vec);
|
check_valid(vec);
|
||||||
entity->getTransform().setPos(vec);
|
entity->getTransform().setPos(vec);
|
||||||
entity->getRigidbody().hitbox.position = vec;
|
entity->getRigidbody().hitbox.setPos(vec);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,22 @@
|
||||||
|
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
|
|
||||||
static inline const Block* get_block_def(lua::State* L) {
|
static const Content& require_content() {
|
||||||
auto indices = content->getIndices();
|
if (content == nullptr) {
|
||||||
|
throw std::runtime_error("content is not initialized");
|
||||||
|
}
|
||||||
|
return *content;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Level& require_level() {
|
||||||
|
if (level == nullptr) {
|
||||||
|
throw std::runtime_error("level is not initialized");
|
||||||
|
}
|
||||||
|
return *level;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Block* get_block_def(lua::State* L) {
|
||||||
|
auto indices = require_content().getIndices();
|
||||||
auto id = lua::tointeger(L, 1);
|
auto id = lua::tointeger(L, 1);
|
||||||
return indices->blocks.get(id);
|
return indices->blocks.get(id);
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +59,7 @@ static int l_is_solid_at(lua::State* L) {
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
return lua::pushboolean(
|
return lua::pushboolean(
|
||||||
L, blocks_agent::is_solid_at(*level->chunks, x, y, z)
|
L, blocks_agent::is_solid_at(*require_level().chunks, x, y, z)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,7 +69,7 @@ static int l_count(lua::State* L) {
|
||||||
|
|
||||||
static int l_index(lua::State* L) {
|
static int l_index(lua::State* L) {
|
||||||
auto name = lua::require_string(L, 1);
|
auto name = lua::require_string(L, 1);
|
||||||
return lua::pushinteger(L, content->blocks.require(name).rt.id);
|
return lua::pushinteger(L, require_content().blocks.require(name).rt.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_is_extended(lua::State* L) {
|
static int l_is_extended(lua::State* L) {
|
||||||
|
|
@ -76,7 +90,7 @@ static int l_is_segment(lua::State* L) {
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
const auto& vox = blocks_agent::require(*level->chunks, x, y, z);
|
const auto& vox = blocks_agent::require(*require_level().chunks, x, y, z);
|
||||||
return lua::pushboolean(L, vox.state.segment);
|
return lua::pushboolean(L, vox.state.segment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,10 +98,11 @@ static int l_seek_origin(lua::State* L) {
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
const auto& vox = blocks_agent::require(*level->chunks, x, y, z);
|
const auto& level = require_level();
|
||||||
|
const auto& vox = blocks_agent::require(*level.chunks, x, y, z);
|
||||||
auto& def = indices->blocks.require(vox.id);
|
auto& def = indices->blocks.require(vox.id);
|
||||||
return lua::pushivec_stack(
|
return lua::pushivec_stack(
|
||||||
L, blocks_agent::seek_origin(*level->chunks, {x, y, z}, def, vox.state)
|
L, blocks_agent::seek_origin(*level.chunks, {x, y, z}, def, vox.state)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,10 +113,12 @@ static int l_set(lua::State* L) {
|
||||||
auto id = lua::tointeger(L, 4);
|
auto id = lua::tointeger(L, 4);
|
||||||
auto state = lua::tointeger(L, 5);
|
auto state = lua::tointeger(L, 5);
|
||||||
bool noupdate = lua::toboolean(L, 6);
|
bool noupdate = lua::toboolean(L, 6);
|
||||||
if (static_cast<size_t>(id) >= indices->blocks.count()) {
|
auto& level = require_level();
|
||||||
|
auto& indices = require_content().getIndices()->blocks;
|
||||||
|
if (static_cast<size_t>(id) >= indices.count()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!blocks_agent::set(*level->chunks, x, y, z, id, int2blockstate(state))) {
|
if (!blocks_agent::set(*level.chunks, x, y, z, id, int2blockstate(state))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,7 +140,7 @@ static int l_get(lua::State* L) {
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
auto vox = blocks_agent::get(*require_level().chunks, x, y, z);
|
||||||
int id = vox == nullptr ? -1 : vox->id;
|
int id = vox == nullptr ? -1 : vox->id;
|
||||||
return lua::pushinteger(L, id);
|
return lua::pushinteger(L, id);
|
||||||
}
|
}
|
||||||
|
|
@ -138,8 +155,9 @@ template<int n>
|
||||||
static int get_axis(lua::State* L) {
|
static int get_axis(lua::State* L) {
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
|
auto& level = require_level();
|
||||||
if (lua::gettop(L) == 2) {
|
if (lua::gettop(L) == 2) {
|
||||||
const auto& def = level->content.getIndices()->blocks.require(x);
|
const auto& def = level.content.getIndices()->blocks.require(x);
|
||||||
return get_axis<n>(L, def, y);
|
return get_axis<n>(L, def, y);
|
||||||
}
|
}
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
|
|
@ -147,11 +165,11 @@ static int get_axis(lua::State* L) {
|
||||||
glm::ivec3 defAxis {};
|
glm::ivec3 defAxis {};
|
||||||
defAxis[n] = 1;
|
defAxis[n] = 1;
|
||||||
|
|
||||||
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
auto vox = blocks_agent::get(*level.chunks, x, y, z);
|
||||||
if (vox == nullptr) {
|
if (vox == nullptr) {
|
||||||
return lua::pushivec_stack(L, defAxis);
|
return lua::pushivec_stack(L, defAxis);
|
||||||
}
|
}
|
||||||
const auto& def = level->content.getIndices()->blocks.require(vox->id);
|
const auto& def = level.content.getIndices()->blocks.require(vox->id);
|
||||||
if (!def.rotatable) {
|
if (!def.rotatable) {
|
||||||
return lua::pushivec_stack(L, defAxis);
|
return lua::pushivec_stack(L, defAxis);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -175,7 +193,7 @@ static int l_get_rotation(lua::State* L) {
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
auto vox = blocks_agent::get(*require_level().chunks, x, y, z);
|
||||||
int rotation = vox == nullptr ? 0 : vox->state.rotation;
|
int rotation = vox == nullptr ? 0 : vox->state.rotation;
|
||||||
return lua::pushinteger(L, rotation);
|
return lua::pushinteger(L, rotation);
|
||||||
}
|
}
|
||||||
|
|
@ -185,7 +203,7 @@ static int l_set_rotation(lua::State* L) {
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
auto value = lua::tointeger(L, 4);
|
auto value = lua::tointeger(L, 4);
|
||||||
blocks_agent::set_rotation(*level->chunks, x, y, z, value);
|
blocks_agent::set_rotation(*require_level().chunks, x, y, z, value);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,7 +211,7 @@ static int l_get_states(lua::State* L) {
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
auto vox = blocks_agent::get(*require_level().chunks, x, y, z);
|
||||||
int states = vox == nullptr ? 0 : blockstate2int(vox->state);
|
int states = vox == nullptr ? 0 : blockstate2int(vox->state);
|
||||||
return lua::pushinteger(L, states);
|
return lua::pushinteger(L, states);
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +226,7 @@ static int l_set_states(lua::State* L) {
|
||||||
}
|
}
|
||||||
int cx = floordiv<CHUNK_W>(x);
|
int cx = floordiv<CHUNK_W>(x);
|
||||||
int cz = floordiv<CHUNK_D>(z);
|
int cz = floordiv<CHUNK_D>(z);
|
||||||
auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz);
|
auto chunk = blocks_agent::get_chunk(*require_level().chunks, cx, cz);
|
||||||
if (chunk == nullptr) {
|
if (chunk == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -227,16 +245,17 @@ static int l_get_user_bits(lua::State* L) {
|
||||||
auto offset = lua::tointeger(L, 4) + VOXEL_USER_BITS_OFFSET;
|
auto offset = lua::tointeger(L, 4) + VOXEL_USER_BITS_OFFSET;
|
||||||
auto bits = lua::tointeger(L, 5);
|
auto bits = lua::tointeger(L, 5);
|
||||||
|
|
||||||
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
auto& level = require_level();
|
||||||
|
auto vox = blocks_agent::get(*level.chunks, x, y, z);
|
||||||
if (vox == nullptr) {
|
if (vox == nullptr) {
|
||||||
return lua::pushinteger(L, 0);
|
return lua::pushinteger(L, 0);
|
||||||
}
|
}
|
||||||
const auto& def = content->getIndices()->blocks.require(vox->id);
|
const auto& def = level.content.getIndices()->blocks.require(vox->id);
|
||||||
if (def.rt.extended) {
|
if (def.rt.extended) {
|
||||||
auto origin = blocks_agent::seek_origin(
|
auto origin = blocks_agent::seek_origin(
|
||||||
*level->chunks, {x, y, z}, def, vox->state
|
*level.chunks, {x, y, z}, def, vox->state
|
||||||
);
|
);
|
||||||
vox = blocks_agent::get(*level->chunks, origin.x, origin.y, origin.z);
|
vox = blocks_agent::get(*level.chunks, origin.x, origin.y, origin.z);
|
||||||
if (vox == nullptr) {
|
if (vox == nullptr) {
|
||||||
return lua::pushinteger(L, 0);
|
return lua::pushinteger(L, 0);
|
||||||
}
|
}
|
||||||
|
|
@ -246,7 +265,9 @@ static int l_get_user_bits(lua::State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_get_variant(lua::State* L) {
|
static int l_get_variant(lua::State* L) {
|
||||||
auto& chunks = *level->chunks;
|
auto& level = require_level();
|
||||||
|
auto& chunks = *level.chunks;
|
||||||
|
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
|
|
@ -255,7 +276,7 @@ static int l_get_variant(lua::State* L) {
|
||||||
if (vox == nullptr) {
|
if (vox == nullptr) {
|
||||||
return lua::pushinteger(L, 0);
|
return lua::pushinteger(L, 0);
|
||||||
}
|
}
|
||||||
const auto& def = content->getIndices()->blocks.require(vox->id);
|
const auto& def = level.content.getIndices()->blocks.require(vox->id);
|
||||||
if (def.variants == nullptr) {
|
if (def.variants == nullptr) {
|
||||||
return lua::pushinteger(L, 0);
|
return lua::pushinteger(L, 0);
|
||||||
}
|
}
|
||||||
|
|
@ -272,7 +293,9 @@ static int l_get_variant(lua::State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_set_user_bits(lua::State* L) {
|
static int l_set_user_bits(lua::State* L) {
|
||||||
auto& chunks = *level->chunks;
|
auto& level = require_level();
|
||||||
|
auto& chunks = *level.chunks;
|
||||||
|
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
|
|
@ -291,7 +314,7 @@ static int l_set_user_bits(lua::State* L) {
|
||||||
int lx = x - cx * CHUNK_W;
|
int lx = x - cx * CHUNK_W;
|
||||||
int lz = z - cz * CHUNK_D;
|
int lz = z - cz * CHUNK_D;
|
||||||
auto vox = &chunk->voxels[vox_index(lx, y, lz)];
|
auto vox = &chunk->voxels[vox_index(lx, y, lz)];
|
||||||
const auto& def = content->getIndices()->blocks.require(vox->id);
|
const auto& def = level.content.getIndices()->blocks.require(vox->id);
|
||||||
if (def.rt.extended) {
|
if (def.rt.extended) {
|
||||||
auto origin = blocks_agent::seek_origin(chunks, {x, y, z}, def, vox->state);
|
auto origin = blocks_agent::seek_origin(chunks, {x, y, z}, def, vox->state);
|
||||||
vox = blocks_agent::get(chunks, origin.x, origin.y, origin.z);
|
vox = blocks_agent::get(chunks, origin.x, origin.y, origin.z);
|
||||||
|
|
@ -313,7 +336,9 @@ static int l_set_user_bits(lua::State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_set_variant(lua::State* L) {
|
static int l_set_variant(lua::State* L) {
|
||||||
auto& chunks = *level->chunks;
|
auto& level = require_level();
|
||||||
|
auto& chunks = *level.chunks;
|
||||||
|
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
|
|
@ -327,7 +352,7 @@ static int l_set_variant(lua::State* L) {
|
||||||
int lx = x - cx * CHUNK_W;
|
int lx = x - cx * CHUNK_W;
|
||||||
int lz = z - cz * CHUNK_D;
|
int lz = z - cz * CHUNK_D;
|
||||||
auto vox = &chunk->voxels[vox_index(lx, y, lz)];
|
auto vox = &chunk->voxels[vox_index(lx, y, lz)];
|
||||||
const auto& def = content->getIndices()->blocks.require(vox->id);
|
const auto& def = level.content.getIndices()->blocks.require(vox->id);
|
||||||
|
|
||||||
if (def.variants == nullptr) {
|
if (def.variants == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -358,11 +383,12 @@ static int l_set_variant(lua::State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_is_replaceable_at(lua::State* L) {
|
static int l_is_replaceable_at(lua::State* L) {
|
||||||
|
auto& level = require_level();
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
return lua::pushboolean(
|
return lua::pushboolean(
|
||||||
L, blocks_agent::is_replaceable_at(*level->chunks, x, y, z)
|
L, blocks_agent::is_replaceable_at(*level.chunks, x, y, z)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -373,20 +399,28 @@ static int l_caption(lua::State* L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static lua::Integer get_variant_index(lua::State* L, const Block* const block, int argumentIndex) {
|
static lua::Integer get_variant_index(
|
||||||
const auto variantIndex = lua::gettop(L) >= argumentIndex ? lua::tointeger(L, argumentIndex) : 0;
|
lua::State* L, const Block& block, int argumentIndex
|
||||||
const size_t variantsSize = block->variants->variants.size();
|
) {
|
||||||
|
const auto variantIndex =
|
||||||
|
lua::isnumber(L, argumentIndex) ? lua::tointeger(L, argumentIndex) : 0;
|
||||||
|
const size_t variantsSize = block.variants->variants.size();
|
||||||
if (variantIndex < 0 || variantIndex >= variantsSize) {
|
if (variantIndex < 0 || variantIndex >= variantsSize) {
|
||||||
throw std::out_of_range(
|
throw std::out_of_range(
|
||||||
"variant index out of bounds [0, " + std::to_string(variantsSize - 1) + "]");
|
"variant index out of bounds [0, " +
|
||||||
|
std::to_string(variantsSize - 1) + "]"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return variantIndex;
|
return variantIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_get_textures(lua::State* L) {
|
static int l_get_textures(lua::State* L) {
|
||||||
if (auto def = get_block_def(L)) {
|
if (auto def = get_block_def(L)) {
|
||||||
const auto& textureFaces = (def->variants ? def->variants->variants[get_variant_index(L, def, 2)].textureFaces :
|
const auto& textureFaces =
|
||||||
def->defaults.textureFaces);
|
(def->variants
|
||||||
|
? def->variants->variants[get_variant_index(L, *def, 2)]
|
||||||
|
.textureFaces
|
||||||
|
: def->defaults.textureFaces);
|
||||||
lua::createtable(L, 6, 0);
|
lua::createtable(L, 6, 0);
|
||||||
for (size_t i = 0; i < 6; i++) {
|
for (size_t i = 0; i < 6; i++) {
|
||||||
lua::pushstring(L, textureFaces[i]);
|
lua::pushstring(L, textureFaces[i]);
|
||||||
|
|
@ -397,11 +431,13 @@ static int l_get_textures(lua::State* L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int l_model_name(lua::State* L) {
|
static int l_model_name(lua::State* L) {
|
||||||
if (auto def = get_block_def(L)) {
|
if (auto def = get_block_def(L)) {
|
||||||
const auto& modelName = (def->variants ? def->variants->variants[get_variant_index(L, def, 2)].model.name :
|
const auto& modelName =
|
||||||
def->defaults.model.name);
|
(def->variants
|
||||||
|
? def->variants->variants[get_variant_index(L, *def, 2)]
|
||||||
|
.model.name
|
||||||
|
: def->defaults.model.name);
|
||||||
if (modelName.empty()) {
|
if (modelName.empty()) {
|
||||||
return lua::pushlstring(L, def->name + ".model");
|
return lua::pushlstring(L, def->name + ".model");
|
||||||
}
|
}
|
||||||
|
|
@ -412,8 +448,11 @@ static int l_model_name(lua::State* L) {
|
||||||
|
|
||||||
static int l_get_model(lua::State* L) {
|
static int l_get_model(lua::State* L) {
|
||||||
if (auto def = get_block_def(L)) {
|
if (auto def = get_block_def(L)) {
|
||||||
const BlockModelType modelType = (def->variants ? def->variants->variants[get_variant_index(L, def, 2)].model.type :
|
const BlockModelType modelType =
|
||||||
def->defaults.model.type);
|
(def->variants
|
||||||
|
? def->variants->variants[get_variant_index(L, *def, 2)]
|
||||||
|
.model.type
|
||||||
|
: def->defaults.model.type);
|
||||||
return lua::pushlstring(L, BlockModelTypeMeta.getName(modelType));
|
return lua::pushlstring(L, BlockModelTypeMeta.getName(modelType));
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -422,7 +461,8 @@ static int l_get_model(lua::State* L) {
|
||||||
static int l_get_hitbox(lua::State* L) {
|
static int l_get_hitbox(lua::State* L) {
|
||||||
if (auto def = get_block_def(L)) {
|
if (auto def = get_block_def(L)) {
|
||||||
size_t rotation = lua::tointeger(L, 2);
|
size_t rotation = lua::tointeger(L, 2);
|
||||||
const size_t hitboxIndex = static_cast<size_t>(lua::gettop(L) >= 3 ? lua::tointeger(L, 3) : 0);
|
const size_t hitboxIndex =
|
||||||
|
static_cast<size_t>(lua::isnumber(L, 3) ? lua::tointeger(L, 3) : 0);
|
||||||
if (def->rotatable) {
|
if (def->rotatable) {
|
||||||
rotation %= def->rotations.MAX_COUNT;
|
rotation %= def->rotations.MAX_COUNT;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -456,25 +496,28 @@ static int l_get_picking_item(lua::State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_place(lua::State* L) {
|
static int l_place(lua::State* L) {
|
||||||
|
auto& level = require_level();
|
||||||
|
auto& indices = *level.content.getIndices();
|
||||||
|
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
auto id = lua::tointeger(L, 4);
|
auto id = lua::tointeger(L, 4);
|
||||||
auto state = lua::tointeger(L, 5);
|
auto state = lua::tointeger(L, 5);
|
||||||
auto playerid = lua::gettop(L) >= 6 ? lua::tointeger(L, 6) : -1;
|
auto playerid = lua::gettop(L) >= 6 ? lua::tointeger(L, 6) : -1;
|
||||||
if (static_cast<size_t>(id) >= indices->blocks.count()) {
|
if (static_cast<size_t>(id) >= indices.blocks.count()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!blocks_agent::get(*level->chunks, x, y, z)) {
|
if (!blocks_agent::get(*level.chunks, x, y, z)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const auto def = level->content.getIndices()->blocks.get(id);
|
const auto def = indices.blocks.get(id);
|
||||||
if (def == nullptr) {
|
if (def == nullptr) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"there is no block with index " + std::to_string(id)
|
"there is no block with index " + std::to_string(id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
auto player = level->players->get(playerid);
|
auto player = level.players->get(playerid);
|
||||||
controller->getBlocksController()->placeBlock(
|
controller->getBlocksController()->placeBlock(
|
||||||
player, *def, int2blockstate(state), x, y, z
|
player, *def, int2blockstate(state), x, y, z
|
||||||
);
|
);
|
||||||
|
|
@ -482,21 +525,26 @@ static int l_place(lua::State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_destruct(lua::State* L) {
|
static int l_destruct(lua::State* L) {
|
||||||
|
auto& level = require_level();
|
||||||
|
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
auto playerid = lua::gettop(L) >= 4 ? lua::tointeger(L, 4) : -1;
|
auto playerid = lua::isnumber(L, 4) ? lua::tointeger(L, 4) : -1;
|
||||||
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
|
||||||
|
auto vox = blocks_agent::get(*level.chunks, x, y, z);
|
||||||
if (vox == nullptr) {
|
if (vox == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
auto& def = level->content.getIndices()->blocks.require(vox->id);
|
auto& def = level.content.getIndices()->blocks.require(vox->id);
|
||||||
auto player = level->players->get(playerid);
|
auto player = level.players->get(playerid);
|
||||||
controller->getBlocksController()->breakBlock(player, def, x, y, z);
|
controller->getBlocksController()->breakBlock(player, def, x, y, z);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_raycast(lua::State* L) {
|
static int l_raycast(lua::State* L) {
|
||||||
|
auto& level = require_level();
|
||||||
|
|
||||||
auto start = lua::tovec<3>(L, 1);
|
auto start = lua::tovec<3>(L, 1);
|
||||||
auto dir = lua::tovec<3>(L, 2);
|
auto dir = lua::tovec<3>(L, 2);
|
||||||
auto maxDistance = lua::tonumber(L, 3);
|
auto maxDistance = lua::tonumber(L, 3);
|
||||||
|
|
@ -526,7 +574,7 @@ static int l_raycast(lua::State* L) {
|
||||||
glm::ivec3 normal;
|
glm::ivec3 normal;
|
||||||
glm::ivec3 iend;
|
glm::ivec3 iend;
|
||||||
if (auto voxel = blocks_agent::raycast(
|
if (auto voxel = blocks_agent::raycast(
|
||||||
*level->chunks,
|
*level.chunks,
|
||||||
start,
|
start,
|
||||||
dir,
|
dir,
|
||||||
maxDistance,
|
maxDistance,
|
||||||
|
|
@ -620,6 +668,8 @@ static int get_field(
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_get_field(lua::State* L) {
|
static int l_get_field(lua::State* L) {
|
||||||
|
auto& level = require_level();
|
||||||
|
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
|
|
@ -628,9 +678,10 @@ static int l_get_field(lua::State* L) {
|
||||||
if (lua::gettop(L) >= 5) {
|
if (lua::gettop(L) >= 5) {
|
||||||
index = lua::tointeger(L, 5);
|
index = lua::tointeger(L, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cx = floordiv(x, CHUNK_W);
|
auto cx = floordiv(x, CHUNK_W);
|
||||||
auto cz = floordiv(z, CHUNK_D);
|
auto cz = floordiv(z, CHUNK_D);
|
||||||
auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz);
|
auto chunk = blocks_agent::get_chunk(*level.chunks, cx, cz);
|
||||||
if (chunk == nullptr || y < 0 || y >= CHUNK_H) {
|
if (chunk == nullptr || y < 0 || y >= CHUNK_H) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -689,6 +740,8 @@ static int set_field(
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_set_field(lua::State* L) {
|
static int l_set_field(lua::State* L) {
|
||||||
|
auto& level = require_level();
|
||||||
|
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
|
|
@ -698,11 +751,12 @@ static int l_set_field(lua::State* L) {
|
||||||
if (lua::gettop(L) >= 6) {
|
if (lua::gettop(L) >= 6) {
|
||||||
index = lua::tointeger(L, 6);
|
index = lua::tointeger(L, 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cx = floordiv(x, CHUNK_W);
|
auto cx = floordiv(x, CHUNK_W);
|
||||||
auto cz = floordiv(z, CHUNK_D);
|
auto cz = floordiv(z, CHUNK_D);
|
||||||
auto lx = x - cx * CHUNK_W;
|
auto lx = x - cx * CHUNK_W;
|
||||||
auto lz = z - cz * CHUNK_W;
|
auto lz = z - cz * CHUNK_W;
|
||||||
auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz);
|
auto chunk = blocks_agent::get_chunk(*level.chunks, cx, cz);
|
||||||
if (chunk == nullptr || y < 0 || y >= CHUNK_H) {
|
if (chunk == nullptr || y < 0 || y >= CHUNK_H) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,28 +7,42 @@
|
||||||
|
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
|
|
||||||
|
static Level& require_level() {
|
||||||
|
if (level == nullptr) {
|
||||||
|
throw std::runtime_error("level is not initialized");
|
||||||
|
}
|
||||||
|
return *level;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Content& require_content() {
|
||||||
|
if (content == nullptr) {
|
||||||
|
throw std::runtime_error("content is not initialized");
|
||||||
|
}
|
||||||
|
return *content;
|
||||||
|
}
|
||||||
|
|
||||||
template <int (*getterfunc)(lua::State*, const Camera&)>
|
template <int (*getterfunc)(lua::State*, const Camera&)>
|
||||||
static int l_camera_getter(lua::State* L) {
|
static int l_camera_getter(lua::State* L) {
|
||||||
size_t index = static_cast<size_t>(lua::tointeger(L, 1));
|
size_t index = static_cast<size_t>(lua::tointeger(L, 1));
|
||||||
return getterfunc(L, *level->cameras.at(index));
|
return getterfunc(L, *require_level().cameras.at(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <void (*setterfunc)(lua::State*, Camera&, int)>
|
template <void (*setterfunc)(lua::State*, Camera&, int)>
|
||||||
static int l_camera_setter(lua::State* L) {
|
static int l_camera_setter(lua::State* L) {
|
||||||
size_t index = static_cast<size_t>(lua::tointeger(L, 1));
|
size_t index = static_cast<size_t>(lua::tointeger(L, 1));
|
||||||
setterfunc(L, *level->cameras.at(index), 2);
|
setterfunc(L, *require_level().cameras.at(index), 2);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_index(lua::State* L) {
|
static int l_index(lua::State* L) {
|
||||||
auto name = lua::require_string(L, 1);
|
auto name = lua::require_string(L, 1);
|
||||||
auto& indices = content->getIndices(ResourceType::CAMERA);
|
auto& indices = require_content().getIndices(ResourceType::CAMERA);
|
||||||
return lua::pushinteger(L, indices.indexOf(name));
|
return lua::pushinteger(L, indices.indexOf(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_name(lua::State* L) {
|
static int l_name(lua::State* L) {
|
||||||
size_t index = static_cast<size_t>(lua::tointeger(L, 1));
|
size_t index = static_cast<size_t>(lua::tointeger(L, 1));
|
||||||
auto& indices = content->getIndices(ResourceType::CAMERA);
|
auto& indices = require_content().getIndices(ResourceType::CAMERA);
|
||||||
return lua::pushstring(L, indices.getName(index));
|
return lua::pushstring(L, indices.getName(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,7 +103,7 @@ static int getter_up(lua::State* L, const Camera& camera) {
|
||||||
|
|
||||||
static int l_look_at(lua::State* L) {
|
static int l_look_at(lua::State* L) {
|
||||||
size_t index = static_cast<size_t>(lua::tointeger(L, 1));
|
size_t index = static_cast<size_t>(lua::tointeger(L, 1));
|
||||||
auto& camera = *level->cameras.at(index);
|
auto& camera = *require_level().cameras.at(index);
|
||||||
auto center = lua::tovec<3>(L, 2);
|
auto center = lua::tovec<3>(L, 2);
|
||||||
auto matrix = glm::inverse(
|
auto matrix = glm::inverse(
|
||||||
glm::lookAt(glm::vec3(), center - camera.position, glm::vec3(0, 1, 0))
|
glm::lookAt(glm::vec3(), center - camera.position, glm::vec3(0, 1, 0))
|
||||||
|
|
|
||||||
|
|
@ -347,6 +347,41 @@ static int l_write_descriptor(lua::State* L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_seek_descriptor(lua::State* L) {
|
||||||
|
int descriptor = lua::tointeger(L, 1);
|
||||||
|
|
||||||
|
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
|
||||||
|
throw std::runtime_error("unknown descriptor");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string mode = lua::require_string(L, 2);
|
||||||
|
std::ios_base::seekdir dir;
|
||||||
|
|
||||||
|
switch (mode[0]) {
|
||||||
|
case 'b':
|
||||||
|
dir = std::ios_base::beg;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
dir = std::ios_base::cur;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
dir = std::ios_base::end;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("invalid seek mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* stream = scripting::descriptors_manager::get_output(descriptor);
|
||||||
|
|
||||||
|
stream->seekp(lua::tointeger(L, 3), dir);
|
||||||
|
|
||||||
|
if (!stream->good()) {
|
||||||
|
throw std::runtime_error("failed to seek stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int l_flush_descriptor(lua::State* L) {
|
static int l_flush_descriptor(lua::State* L) {
|
||||||
int descriptor = lua::tointeger(L, 1);
|
int descriptor = lua::tointeger(L, 1);
|
||||||
|
|
||||||
|
|
@ -406,9 +441,9 @@ const luaL_Reg filelib[] = {
|
||||||
{"__has_descriptor", lua::wrap<l_has_descriptor>},
|
{"__has_descriptor", lua::wrap<l_has_descriptor>},
|
||||||
{"__read_descriptor", lua::wrap<l_read_descriptor>},
|
{"__read_descriptor", lua::wrap<l_read_descriptor>},
|
||||||
{"__write_descriptor", lua::wrap<l_write_descriptor>},
|
{"__write_descriptor", lua::wrap<l_write_descriptor>},
|
||||||
|
{"__seek_descriptor", lua::wrap<l_seek_descriptor>},
|
||||||
{"__flush_descriptor", lua::wrap<l_flush_descriptor>},
|
{"__flush_descriptor", lua::wrap<l_flush_descriptor>},
|
||||||
{"__close_descriptor", lua::wrap<l_close_descriptor>},
|
{"__close_descriptor", lua::wrap<l_close_descriptor>},
|
||||||
{"__close_all_descriptors", lua::wrap<l_close_all_descriptors>},
|
{"__close_all_descriptors", lua::wrap<l_close_all_descriptors>},
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,28 @@
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
Level& require_level() {
|
||||||
|
if (level == nullptr) {
|
||||||
|
throw std::runtime_error("world is not open");
|
||||||
|
}
|
||||||
|
return *level;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Content& require_content() {
|
||||||
|
if (content == nullptr) {
|
||||||
|
throw std::runtime_error("content is not initialized");
|
||||||
|
}
|
||||||
|
return *content;
|
||||||
|
}
|
||||||
|
|
||||||
void validate_itemid(itemid_t id) {
|
void validate_itemid(itemid_t id) {
|
||||||
if (id >= indices->items.count()) {
|
if (id >= require_content().getIndices()->items.count()) {
|
||||||
throw std::runtime_error("invalid item id");
|
throw std::runtime_error("invalid item id");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Inventory& get_inventory(int64_t id) {
|
Inventory& get_inventory(int64_t id) {
|
||||||
auto inv = level->inventories->get(id);
|
auto inv = require_level().inventories->get(id);
|
||||||
if (inv == nullptr) {
|
if (inv == nullptr) {
|
||||||
throw std::runtime_error("inventory not found: " + std::to_string(id));
|
throw std::runtime_error("inventory not found: " + std::to_string(id));
|
||||||
}
|
}
|
||||||
|
|
@ -23,7 +37,7 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
Inventory& get_inventory(int64_t id, int arg) {
|
Inventory& get_inventory(int64_t id, int arg) {
|
||||||
auto inv = level->inventories->get(id);
|
auto inv = require_level().inventories->get(id);
|
||||||
if (inv == nullptr) {
|
if (inv == nullptr) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"inventory not found: " + std::to_string(id) + " argument " +
|
"inventory not found: " + std::to_string(id) + " argument " +
|
||||||
|
|
@ -137,7 +151,7 @@ static int l_unbind_block(lua::State* L) {
|
||||||
|
|
||||||
static int l_create(lua::State* L) {
|
static int l_create(lua::State* L) {
|
||||||
auto invsize = lua::tointeger(L, 1);
|
auto invsize = lua::tointeger(L, 1);
|
||||||
auto inv = level->inventories->create(invsize);
|
auto inv = require_level().inventories->create(invsize);
|
||||||
if (inv == nullptr) {
|
if (inv == nullptr) {
|
||||||
return lua::pushinteger(L, 0);
|
return lua::pushinteger(L, 0);
|
||||||
}
|
}
|
||||||
|
|
@ -146,13 +160,13 @@ static int l_create(lua::State* L) {
|
||||||
|
|
||||||
static int l_remove(lua::State* L) {
|
static int l_remove(lua::State* L) {
|
||||||
auto invid = lua::tointeger(L, 1);
|
auto invid = lua::tointeger(L, 1);
|
||||||
level->inventories->remove(invid);
|
require_level().inventories->remove(invid);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_clone(lua::State* L) {
|
static int l_clone(lua::State* L) {
|
||||||
auto id = lua::tointeger(L, 1);
|
auto id = lua::tointeger(L, 1);
|
||||||
auto clone = level->inventories->clone(id);
|
auto clone = require_level().inventories->clone(id);
|
||||||
if (clone == nullptr) {
|
if (clone == nullptr) {
|
||||||
return lua::pushinteger(L, 0);
|
return lua::pushinteger(L, 0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
|
|
||||||
static const ItemDef* get_item_def(lua::State* L, int idx) {
|
static const ItemDef* get_item_def(lua::State* L, int idx) {
|
||||||
|
if (content == nullptr) {
|
||||||
|
throw std::runtime_error("content is not initialized");
|
||||||
|
}
|
||||||
auto indices = content->getIndices();
|
auto indices = content->getIndices();
|
||||||
auto id = lua::tointeger(L, idx);
|
auto id = lua::tointeger(L, idx);
|
||||||
return indices->items.get(id);
|
return indices->items.get(id);
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,20 @@ static int l_look_at(lua::State* L) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_perspective(lua::State *L) {
|
||||||
|
uint argc = lua::check_argc(L, 4, 5);
|
||||||
|
|
||||||
|
double fov = glm::radians(lua::tonumber(L, 1));
|
||||||
|
double ratio = lua::tonumber(L, 2);
|
||||||
|
double near = lua::tonumber(L, 3);
|
||||||
|
double far = lua::tonumber(L, 4);
|
||||||
|
|
||||||
|
glm::mat4 mat = glm::perspective(fov, ratio, near, far);
|
||||||
|
|
||||||
|
if (argc == 5) return lua::setmat4(L, 5, mat);
|
||||||
|
else return lua::pushmat4(L, mat);
|
||||||
|
}
|
||||||
|
|
||||||
static int l_from_quat(lua::State* L) {
|
static int l_from_quat(lua::State* L) {
|
||||||
uint argc = lua::check_argc(L, 1, 2);
|
uint argc = lua::check_argc(L, 1, 2);
|
||||||
auto quat = lua::toquat(L, 1);
|
auto quat = lua::toquat(L, 1);
|
||||||
|
|
@ -280,6 +294,7 @@ const luaL_Reg mat4lib[] = {
|
||||||
{"determinant", lua::wrap<l_determinant>},
|
{"determinant", lua::wrap<l_determinant>},
|
||||||
{"decompose", lua::wrap<l_decompose>},
|
{"decompose", lua::wrap<l_decompose>},
|
||||||
{"look_at", lua::wrap<l_look_at>},
|
{"look_at", lua::wrap<l_look_at>},
|
||||||
|
{"perspective", lua::wrap<l_perspective>},
|
||||||
{"from_quat", lua::wrap<l_from_quat>},
|
{"from_quat", lua::wrap<l_from_quat>},
|
||||||
{"tostring", lua::wrap<l_tostring>},
|
{"tostring", lua::wrap<l_tostring>},
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,13 @@
|
||||||
|
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
|
|
||||||
|
static Level& require_level() {
|
||||||
|
if (level == nullptr) {
|
||||||
|
throw std::runtime_error("world is not loaded");
|
||||||
|
}
|
||||||
|
return *level;
|
||||||
|
}
|
||||||
|
|
||||||
inline Player* get_player(lua::State* L, int idx) {
|
inline Player* get_player(lua::State* L, int idx) {
|
||||||
if (!lua::isnumber(L, idx)) {
|
if (!lua::isnumber(L, idx)) {
|
||||||
if (engine->isHeadless()) {
|
if (engine->isHeadless()) {
|
||||||
|
|
@ -22,7 +29,7 @@ inline Player* get_player(lua::State* L, int idx) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return level->players->get(lua::tointeger(L, idx));
|
return require_level().players->get(lua::tointeger(L, idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_get_pos(lua::State* L) {
|
static int l_get_pos(lua::State* L) {
|
||||||
|
|
@ -304,15 +311,18 @@ static int l_create(lua::State* L) {
|
||||||
if (lua::gettop(L) >= 2) {
|
if (lua::gettop(L) >= 2) {
|
||||||
playerId = lua::tointeger(L, 2);
|
playerId = lua::tointeger(L, 2);
|
||||||
}
|
}
|
||||||
auto player = level->players->create(playerId);
|
auto& level = require_level();
|
||||||
|
auto player = level.players->create(playerId);
|
||||||
player->setName(lua::require_string(L, 1));
|
player->setName(lua::require_string(L, 1));
|
||||||
return lua::pushinteger(L, player->getId());
|
return lua::pushinteger(L, player->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_delete(lua::State* L) {
|
static int l_delete(lua::State* L) {
|
||||||
auto id = lua::tointeger(L, 1);
|
auto id = lua::tointeger(L, 1);
|
||||||
level->players->suspend(id);
|
|
||||||
level->players->remove(id);
|
auto& level = require_level();
|
||||||
|
level.players->suspend(id);
|
||||||
|
level.players->remove(id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -324,19 +334,21 @@ static int l_is_suspended(lua::State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_set_suspended(lua::State* L) {
|
static int l_set_suspended(lua::State* L) {
|
||||||
|
auto& level = require_level();
|
||||||
if (lua::toboolean(L, 2)) {
|
if (lua::toboolean(L, 2)) {
|
||||||
level->players->suspend(lua::tointeger(L, 1));
|
level.players->suspend(lua::tointeger(L, 1));
|
||||||
} else {
|
} else {
|
||||||
level->players->resume(lua::tointeger(L, 1));
|
level.players->resume(lua::tointeger(L, 1));
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_get_all_in_radius(lua::State* L) {
|
static int l_get_all_in_radius(lua::State* L) {
|
||||||
|
auto& level = require_level();
|
||||||
auto center = lua::tovec3(L, 1);
|
auto center = lua::tovec3(L, 1);
|
||||||
auto radius = static_cast<float>(lua::tonumber(L, 2));
|
auto radius = static_cast<float>(lua::tonumber(L, 2));
|
||||||
|
|
||||||
auto players = level->players->getAllInRadius(center, radius);
|
auto players = level.players->getAllInRadius(center, radius);
|
||||||
lua::createtable(L, players.size(), 0);
|
lua::createtable(L, players.size(), 0);
|
||||||
for (size_t i = 0; i < players.size(); i++) {
|
for (size_t i = 0; i < players.size(); i++) {
|
||||||
lua::pushinteger(L, players[i]->getId());
|
lua::pushinteger(L, players[i]->getId());
|
||||||
|
|
@ -346,7 +358,7 @@ static int l_get_all_in_radius(lua::State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_get_all(lua::State* L) {
|
static int l_get_all(lua::State* L) {
|
||||||
auto players = level->players->getAll();
|
auto players = require_level().players->getAll();
|
||||||
lua::createtable(L, players.size(), 0);
|
lua::createtable(L, players.size(), 0);
|
||||||
for (size_t i = 0; i < players.size(); i++) {
|
for (size_t i = 0; i < players.size(); i++) {
|
||||||
lua::pushinteger(L, players[i]->getId());
|
lua::pushinteger(L, players[i]->getId());
|
||||||
|
|
@ -356,8 +368,9 @@ static int l_get_all(lua::State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_get_nearest(lua::State* L) {
|
static int l_get_nearest(lua::State* L) {
|
||||||
|
auto& level = require_level();
|
||||||
auto position = lua::tovec3(L, 1);
|
auto position = lua::tovec3(L, 1);
|
||||||
if (auto player = level->players->getNearest(position)) {
|
if (auto player = level.players->getNearest(position)) {
|
||||||
lua::pushinteger(L, player->getId());
|
lua::pushinteger(L, player->getId());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,17 @@ static int l_tostring(lua::State* L) {
|
||||||
return lua::pushstring(L, ss.str());
|
return lua::pushstring(L, ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_from_euler(lua::State *L) {
|
||||||
|
uint argc = lua::check_argc(L, 1, 2);
|
||||||
|
glm::vec3 euler = glm::radians(lua::tovec3(L, 1));
|
||||||
|
|
||||||
|
if (argc == 2) return lua::setquat(L, 2, glm::quat(euler));
|
||||||
|
return lua::pushquat(L, glm::quat(euler));
|
||||||
|
}
|
||||||
|
|
||||||
const luaL_Reg quatlib[] = {
|
const luaL_Reg quatlib[] = {
|
||||||
{"from_mat4", lua::wrap<l_from_mat4>},
|
{"from_mat4", lua::wrap<l_from_mat4>},
|
||||||
|
{"from_euler", lua::wrap<l_from_euler>},
|
||||||
{"slerp", lua::wrap<l_slerp>},
|
{"slerp", lua::wrap<l_slerp>},
|
||||||
{"tostring", lua::wrap<l_tostring>},
|
{"tostring", lua::wrap<l_tostring>},
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,15 @@
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
static WorldInfo& require_world_info() {
|
static Level& require_level() {
|
||||||
if (level == nullptr) {
|
if (level == nullptr) {
|
||||||
throw std::runtime_error("no world open");
|
throw std::runtime_error("world is not open");
|
||||||
}
|
}
|
||||||
return level->getWorld()->getInfo();
|
return *level;
|
||||||
|
}
|
||||||
|
|
||||||
|
static WorldInfo& require_world_info() {
|
||||||
|
return require_level().getWorld()->getInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_is_open(lua::State* L) {
|
static int l_is_open(lua::State* L) {
|
||||||
|
|
|
||||||
|
|
@ -38,15 +38,23 @@ struct AABB {
|
||||||
return AABB(a + pos, b + pos);
|
return AABB(a + pos, b + pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AABB operator+(const glm::vec3& offset) const {
|
||||||
|
return translated(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
AABB operator-(const glm::vec3& offset) const {
|
||||||
|
return translated(-offset);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief Multiply AABB size from center
|
/// @brief Multiply AABB size from center
|
||||||
inline void scale(const glm::vec3 mul) {
|
inline void scale(const glm::vec3& mul) {
|
||||||
glm::vec3 center = (a + b) * 0.5f;
|
glm::vec3 center = (a + b) * 0.5f;
|
||||||
a = (a - center) * mul + center;
|
a = (a - center) * mul + center;
|
||||||
b = (b - center) * mul + center;
|
b = (b - center) * mul + center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Multiply AABB size from given origin
|
/// @brief Multiply AABB size from given origin
|
||||||
inline void scale(const glm::vec3 mul, const glm::vec3 orig) {
|
inline void scale(const glm::vec3& mul, const glm::vec3& orig) {
|
||||||
glm::vec3 beg = min();
|
glm::vec3 beg = min();
|
||||||
glm::vec3 end = max();
|
glm::vec3 end = max();
|
||||||
glm::vec3 center = glm::mix(beg, end, orig);
|
glm::vec3 center = glm::mix(beg, end, orig);
|
||||||
|
|
@ -91,14 +99,15 @@ struct AABB {
|
||||||
addPoint(matrix * glm::vec4(pb.x, pa.y, pb.z, 1.0f));
|
addPoint(matrix * glm::vec4(pb.x, pa.y, pb.z, 1.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool intersect(const AABB& aabb) {
|
inline bool intersects(const AABB& aabb) const {
|
||||||
return (
|
return (
|
||||||
a.x <= aabb.b.x && b.x >= aabb.a.x && a.y <= aabb.b.y &&
|
a.x <= aabb.b.x && b.x >= aabb.a.x &&
|
||||||
b.y >= aabb.a.y && a.z <= aabb.b.z && b.z >= aabb.a.z
|
a.y <= aabb.b.y && b.y >= aabb.a.y &&
|
||||||
|
a.z <= aabb.b.z && b.z >= aabb.a.z
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool intersect(const AABB& aabb, float margin) {
|
inline bool intersects(const AABB& aabb, float margin) const {
|
||||||
return (
|
return (
|
||||||
a.x <= aabb.b.x + margin && b.x >= aabb.a.x - margin &&
|
a.x <= aabb.b.x + margin && b.x >= aabb.a.x - margin &&
|
||||||
a.y <= aabb.b.y + margin && b.y >= aabb.a.y - margin &&
|
a.y <= aabb.b.y + margin && b.y >= aabb.a.y - margin &&
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,30 @@
|
||||||
#define VC_ENABLE_REFLECTION
|
#define VC_ENABLE_REFLECTION
|
||||||
#include "Entities.hpp"
|
#include "Entities.hpp"
|
||||||
|
|
||||||
#include <entt/entity/registry.hpp>
|
|
||||||
#include <glm/ext/matrix_transform.hpp>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "assets/Assets.hpp"
|
#include "assets/Assets.hpp"
|
||||||
#include "content/Content.hpp"
|
#include "content/Content.hpp"
|
||||||
#include "data/dv_util.hpp"
|
#include "data/dv_util.hpp"
|
||||||
#include "debug/Logger.hpp"
|
#include "debug/Logger.hpp"
|
||||||
#include "engine/Engine.hpp"
|
#include "engine/Engine.hpp"
|
||||||
|
#include "Entity.hpp"
|
||||||
|
#include "EntityDef.hpp"
|
||||||
|
#include "graphics/commons/Model.hpp"
|
||||||
#include "graphics/core/DrawContext.hpp"
|
#include "graphics/core/DrawContext.hpp"
|
||||||
#include "graphics/core/LineBatch.hpp"
|
#include "graphics/core/LineBatch.hpp"
|
||||||
#include "graphics/commons/Model.hpp"
|
|
||||||
#include "graphics/render/ModelBatch.hpp"
|
#include "graphics/render/ModelBatch.hpp"
|
||||||
#include "logic/scripting/scripting.hpp"
|
#include "logic/scripting/scripting.hpp"
|
||||||
#include "maths/FrustumCulling.hpp"
|
#include "maths/FrustumCulling.hpp"
|
||||||
#include "maths/rays.hpp"
|
#include "maths/rays.hpp"
|
||||||
#include "EntityDef.hpp"
|
#include "maths/util.hpp"
|
||||||
#include "Entity.hpp"
|
|
||||||
#include "rigging.hpp"
|
|
||||||
#include "physics/PhysicsSolver.hpp"
|
#include "physics/PhysicsSolver.hpp"
|
||||||
|
#include "rigging.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
|
|
||||||
|
#include <entt/entity/registry.hpp>
|
||||||
|
#include <glm/ext/matrix_transform.hpp>
|
||||||
|
#include <limits>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
static debug::Logger logger("entities");
|
static debug::Logger logger("entities");
|
||||||
|
|
||||||
Entities::Entities(Level& level)
|
Entities::Entities(Level& level)
|
||||||
|
|
@ -79,7 +81,7 @@ entityid_t Entities::spawn(
|
||||||
entities[id] = entity;
|
entities[id] = entity;
|
||||||
uids[entity] = id;
|
uids[entity] = id;
|
||||||
|
|
||||||
registry->emplace<EntityId>(entity, static_cast<entityid_t>(id), def);
|
registry->emplace<EntityId>(entity, id, def);
|
||||||
const auto& tsf = registry->emplace<Transform>(
|
const auto& tsf = registry->emplace<Transform>(
|
||||||
entity,
|
entity,
|
||||||
position,
|
position,
|
||||||
|
|
@ -91,7 +93,7 @@ entityid_t Entities::spawn(
|
||||||
auto& body = registry->emplace<Rigidbody>(
|
auto& body = registry->emplace<Rigidbody>(
|
||||||
entity,
|
entity,
|
||||||
true,
|
true,
|
||||||
Hitbox {def.bodyType, position, def.hitbox * 0.5f},
|
Hitbox {id, def.bodyType, position, def.hitbox * 0.5f},
|
||||||
std::vector<Sensor> {}
|
std::vector<Sensor> {}
|
||||||
);
|
);
|
||||||
body.initialize(def, id, *this);
|
body.initialize(def, id, *this);
|
||||||
|
|
@ -161,7 +163,11 @@ void Entities::loadEntity(const dv::value& map, Entity entity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Entities::RaycastResult> Entities::rayCast(
|
std::optional<Entities::RaycastResult> Entities::rayCast(
|
||||||
glm::vec3 start, glm::vec3 dir, float maxDistance, entityid_t ignore
|
glm::vec3 start,
|
||||||
|
glm::vec3 dir,
|
||||||
|
float maxDistance,
|
||||||
|
entityid_t ignore,
|
||||||
|
bool solidOnly
|
||||||
) {
|
) {
|
||||||
Ray ray(start, dir);
|
Ray ray(start, dir);
|
||||||
auto view = registry->view<EntityId, Transform, Rigidbody>();
|
auto view = registry->view<EntityId, Transform, Rigidbody>();
|
||||||
|
|
@ -170,7 +176,7 @@ std::optional<Entities::RaycastResult> Entities::rayCast(
|
||||||
glm::ivec3 foundNormal;
|
glm::ivec3 foundNormal;
|
||||||
|
|
||||||
for (auto [entity, eid, transform, body] : view.each()) {
|
for (auto [entity, eid, transform, body] : view.each()) {
|
||||||
if (eid.uid == ignore || !body.enabled) {
|
if (eid.uid == ignore || !body.enabled || (solidOnly && !eid.def.solid)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto& hitbox = body.hitbox;
|
auto& hitbox = body.hitbox;
|
||||||
|
|
@ -278,13 +284,18 @@ void Entities::updateSensors(
|
||||||
}
|
}
|
||||||
|
|
||||||
void Entities::preparePhysics(float delta) {
|
void Entities::preparePhysics(float delta) {
|
||||||
|
auto& physics = *level.physics;
|
||||||
|
auto& hitboxes = physics.getHitboxesWriteable();
|
||||||
|
auto& solidHitboxes = physics.getSolidHitboxesWriteable();
|
||||||
|
|
||||||
if (sensorsTickClock.update(delta)) {
|
if (sensorsTickClock.update(delta)) {
|
||||||
auto part = sensorsTickClock.getPart();
|
auto part = sensorsTickClock.getPart();
|
||||||
auto parts = sensorsTickClock.getParts();
|
auto parts = sensorsTickClock.getParts();
|
||||||
|
|
||||||
|
auto& sensors = physics.getSensorsWriteable();
|
||||||
|
sensors.clear();
|
||||||
|
|
||||||
auto view = registry->view<EntityId, Transform, Rigidbody>();
|
auto view = registry->view<EntityId, Transform, Rigidbody>();
|
||||||
auto physics = level.physics.get();
|
|
||||||
std::vector<Sensor*> sensors;
|
|
||||||
for (auto [entity, eid, transform, rigidbody] : view.each()) {
|
for (auto [entity, eid, transform, rigidbody] : view.each()) {
|
||||||
if (!rigidbody.enabled) {
|
if (!rigidbody.enabled) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -294,7 +305,25 @@ void Entities::preparePhysics(float delta) {
|
||||||
}
|
}
|
||||||
updateSensors(rigidbody, transform, sensors);
|
updateSensors(rigidbody, transform, sensors);
|
||||||
}
|
}
|
||||||
physics->setSensors(std::move(sensors));
|
}
|
||||||
|
|
||||||
|
hitboxes.clear();
|
||||||
|
solidHitboxes.clear();
|
||||||
|
|
||||||
|
auto view = registry->view<EntityId, Rigidbody>();
|
||||||
|
for (auto [entity, eid, rigidbody] : view.each()) {
|
||||||
|
if (eid.destroyFlag || !rigidbody.enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rigidbody.hitbox.mass = eid.def.bodyType == BodyType::DYNAMIC
|
||||||
|
? rigidbody.mass
|
||||||
|
: std::numeric_limits<float>::infinity();
|
||||||
|
rigidbody.hitbox.elasticity = rigidbody.elasticity;
|
||||||
|
hitboxes.emplace_back(&rigidbody.hitbox);
|
||||||
|
if (!eid.def.solid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
solidHitboxes.emplace_back(&rigidbody.hitbox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -303,29 +332,31 @@ void Entities::updatePhysics(float delta) {
|
||||||
|
|
||||||
auto view = registry->view<EntityId, Transform, Rigidbody>();
|
auto view = registry->view<EntityId, Transform, Rigidbody>();
|
||||||
auto physics = level.physics.get();
|
auto physics = level.physics.get();
|
||||||
|
|
||||||
|
int substeps = std::max<int>(std::min<int>(delta * 1000, 200), 8);
|
||||||
|
physics->step(*level.chunks, delta, substeps);
|
||||||
|
|
||||||
for (auto [entity, eid, transform, rigidbody] : view.each()) {
|
for (auto [entity, eid, transform, rigidbody] : view.each()) {
|
||||||
if (!rigidbody.enabled || rigidbody.hitbox.type == BodyType::STATIC) {
|
if (!rigidbody.enabled ||
|
||||||
|
rigidbody.hitbox.type == BodyType::STATIC) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto& hitbox = rigidbody.hitbox;
|
auto& hitbox = rigidbody.hitbox;
|
||||||
auto prevVel = hitbox.velocity;
|
|
||||||
bool grounded = hitbox.grounded;
|
|
||||||
|
|
||||||
float vel = glm::length(prevVel);
|
|
||||||
int substeps = static_cast<int>(delta * vel * 20);
|
|
||||||
substeps = std::min(100, std::max(2, substeps));
|
|
||||||
physics->step(*level.chunks, hitbox, delta, substeps, eid.uid);
|
|
||||||
hitbox.friction = glm::abs(hitbox.gravityScale <= 1e-7f)
|
|
||||||
? 8.0f
|
|
||||||
: (!grounded ? 2.0f : 10.0f);
|
|
||||||
hitbox.scale = transform.size;
|
hitbox.scale = transform.size;
|
||||||
transform.setPos(hitbox.position);
|
if (util::is_nan_or_inf(hitbox.position)) {
|
||||||
if (hitbox.grounded && !grounded) {
|
logger.error()
|
||||||
|
<< "physics simulation produced nan or inf (entity "
|
||||||
|
<< eid.def.name << "#" << eid.uid << ")";
|
||||||
|
hitbox.position = transform.pos;
|
||||||
|
} else {
|
||||||
|
transform.setPos(hitbox.position);
|
||||||
|
}
|
||||||
|
if (hitbox.grounded && !hitbox.prevGrounded) {
|
||||||
scripting::on_entity_grounded(
|
scripting::on_entity_grounded(
|
||||||
*get(eid.uid), glm::length(prevVel - hitbox.velocity)
|
*get(eid.uid), glm::length(hitbox.prevVelocity - hitbox.velocity)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!hitbox.grounded && grounded) {
|
if (!hitbox.grounded && hitbox.prevGrounded) {
|
||||||
scripting::on_entity_fall(*get(eid.uid));
|
scripting::on_entity_fall(*get(eid.uid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -450,7 +481,7 @@ void Entities::render(
|
||||||
bool Entities::hasBlockingInside(AABB aabb) {
|
bool Entities::hasBlockingInside(AABB aabb) {
|
||||||
auto view = registry->view<EntityId, Rigidbody>();
|
auto view = registry->view<EntityId, Rigidbody>();
|
||||||
for (auto [entity, eid, body] : view.each()) {
|
for (auto [entity, eid, body] : view.each()) {
|
||||||
if (eid.def.blocking && aabb.intersect(body.hitbox.getAABB(), -0.05f)) {
|
if (eid.def.blocking && aabb.intersects(body.hitbox.getAABB(), -0.05f)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,12 +88,14 @@ public:
|
||||||
/// @param dir Ray direction normalized vector
|
/// @param dir Ray direction normalized vector
|
||||||
/// @param maxDistance Max ray length
|
/// @param maxDistance Max ray length
|
||||||
/// @param ignore Ignored entity ID
|
/// @param ignore Ignored entity ID
|
||||||
|
/// @param solidOnly If true, only entities with solid hitboxes will be checked
|
||||||
/// @return An optional structure containing entity, normal and distance
|
/// @return An optional structure containing entity, normal and distance
|
||||||
std::optional<RaycastResult> rayCast(
|
std::optional<RaycastResult> rayCast(
|
||||||
glm::vec3 start,
|
glm::vec3 start,
|
||||||
glm::vec3 dir,
|
glm::vec3 dir,
|
||||||
float maxDistance,
|
float maxDistance,
|
||||||
entityid_t ignore = -1
|
entityid_t ignore = -1,
|
||||||
|
bool solidOnly = false
|
||||||
);
|
);
|
||||||
|
|
||||||
void loadEntities(dv::value map);
|
void loadEntities(dv::value map);
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,9 @@ void EntityDef::cloneTo(EntityDef& dst) {
|
||||||
dst.radialSensors = radialSensors;
|
dst.radialSensors = radialSensors;
|
||||||
dst.skeletonName = skeletonName;
|
dst.skeletonName = skeletonName;
|
||||||
dst.blocking = blocking;
|
dst.blocking = blocking;
|
||||||
|
dst.solid = solid;
|
||||||
|
dst.mass = mass;
|
||||||
|
dst.elasticity = elasticity;
|
||||||
|
dst.stepHeight = stepHeight;
|
||||||
dst.save = save;
|
dst.save = save;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,15 @@ struct EntityDef {
|
||||||
/// @brief Does entity prevent blocks setup
|
/// @brief Does entity prevent blocks setup
|
||||||
bool blocking = true;
|
bool blocking = true;
|
||||||
|
|
||||||
|
/// @brief Is the entity solid (blocks other entities)
|
||||||
|
bool solid = false;
|
||||||
|
|
||||||
|
/// @brief Mass for physics calculations
|
||||||
|
float mass = 1.0f;
|
||||||
|
|
||||||
|
/// @brief Elasticity for physics calculations
|
||||||
|
float elasticity = 0.0f;
|
||||||
|
|
||||||
/// @brief Max obstacle height that does not require jumping
|
/// @brief Max obstacle height that does not require jumping
|
||||||
float stepHeight = 0.5f;
|
float stepHeight = 0.5f;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ void Player::teleport(glm::vec3 position) {
|
||||||
this->position = position;
|
this->position = position;
|
||||||
|
|
||||||
if (auto entity = level.entities->get(eid)) {
|
if (auto entity = level.entities->get(eid)) {
|
||||||
entity->getRigidbody().hitbox.position = position;
|
entity->getRigidbody().hitbox.setPos(position);
|
||||||
entity->getTransform().setPos(position);
|
entity->getTransform().setPos(position);
|
||||||
entity->setInterpolatedPosition(position);
|
entity->setInterpolatedPosition(position);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ dv::value Rigidbody::serialize(bool saveVelocity, bool saveBodySettings) const {
|
||||||
if (hitbox.crouching) {
|
if (hitbox.crouching) {
|
||||||
bodymap["crouch"] = hitbox.crouching;
|
bodymap["crouch"] = hitbox.crouching;
|
||||||
}
|
}
|
||||||
|
bodymap["mass"] = mass;
|
||||||
|
bodymap["elasticity"] = elasticity;
|
||||||
}
|
}
|
||||||
return bodymap;
|
return bodymap;
|
||||||
}
|
}
|
||||||
|
|
@ -33,6 +35,8 @@ void Rigidbody::deserialize(const dv::value& root) {
|
||||||
BodyTypeMeta.getItem(bodyTypeName, hitbox.type);
|
BodyTypeMeta.getItem(bodyTypeName, hitbox.type);
|
||||||
root["crouch"].asBoolean(hitbox.crouching);
|
root["crouch"].asBoolean(hitbox.crouching);
|
||||||
root["damping"].asNumber(hitbox.linearDamping);
|
root["damping"].asNumber(hitbox.linearDamping);
|
||||||
|
root["mass"].asNumber(mass);
|
||||||
|
root["elasticity"].asNumber(elasticity);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <void (*callback)(const Entity&, size_t, entityid_t)>
|
template <void (*callback)(const Entity&, size_t, entityid_t)>
|
||||||
|
|
@ -49,6 +53,8 @@ static sensorcallback create_sensor_callback(Entities& entities) {
|
||||||
void Rigidbody::initialize(
|
void Rigidbody::initialize(
|
||||||
const EntityDef& def, entityid_t id, Entities& entities
|
const EntityDef& def, entityid_t id, Entities& entities
|
||||||
) {
|
) {
|
||||||
|
mass = def.mass;
|
||||||
|
elasticity = def.elasticity;
|
||||||
sensors.resize(def.radialSensors.size() + def.boxSensors.size());
|
sensors.resize(def.radialSensors.size() + def.boxSensors.size());
|
||||||
for (auto& [i, box] : def.boxSensors) {
|
for (auto& [i, box] : def.boxSensors) {
|
||||||
SensorParams params {};
|
SensorParams params {};
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ struct Rigidbody {
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
Hitbox hitbox;
|
Hitbox hitbox;
|
||||||
std::vector<Sensor> sensors;
|
std::vector<Sensor> sensors;
|
||||||
|
float mass;
|
||||||
|
float elasticity;
|
||||||
|
|
||||||
dv::value serialize(bool saveVelocity, bool saveBodySettings) const;
|
dv::value serialize(bool saveVelocity, bool saveBodySettings) const;
|
||||||
void deserialize(const dv::value& root);
|
void deserialize(const dv::value& root);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
#include "Hitbox.hpp"
|
#include "Hitbox.hpp"
|
||||||
|
|
||||||
#include <stdexcept>
|
Hitbox::Hitbox(
|
||||||
|
entityid_t entity, BodyType type, glm::vec3 position, glm::vec3 halfsize
|
||||||
Hitbox::Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize)
|
)
|
||||||
: type(type),
|
: entity(entity),
|
||||||
position(position),
|
type(type),
|
||||||
halfsize(halfsize),
|
position(position),
|
||||||
velocity(0.0f,0.0f,0.0f)
|
halfsize(halfsize),
|
||||||
{}
|
velocity(0.0f, 0.0f, 0.0f),
|
||||||
|
prevPosition(position) {
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
|
||||||
#include "maths/aabb.hpp"
|
#include "maths/aabb.hpp"
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
#include "util/EnumMetadata.hpp"
|
#include "util/EnumMetadata.hpp"
|
||||||
|
|
@ -8,6 +10,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
enum class SensorType {
|
enum class SensorType {
|
||||||
AABB,
|
AABB,
|
||||||
|
|
@ -48,6 +51,7 @@ VC_ENUM_METADATA(BodyType)
|
||||||
VC_ENUM_END
|
VC_ENUM_END
|
||||||
|
|
||||||
struct Hitbox {
|
struct Hitbox {
|
||||||
|
entityid_t entity;
|
||||||
BodyType type;
|
BodyType type;
|
||||||
glm::vec3 position;
|
glm::vec3 position;
|
||||||
glm::vec3 halfsize;
|
glm::vec3 halfsize;
|
||||||
|
|
@ -60,14 +64,34 @@ struct Hitbox {
|
||||||
float gravityScale = 1.0f;
|
float gravityScale = 1.0f;
|
||||||
bool crouching = false;
|
bool crouching = false;
|
||||||
float stepHeight = 0.5f;
|
float stepHeight = 0.5f;
|
||||||
|
float mass = 1.0f;
|
||||||
|
float elasticity = 0.0f;
|
||||||
|
std::string material;
|
||||||
|
std::string groundMaterial;
|
||||||
|
glm::vec3 groundVelocity {};
|
||||||
|
|
||||||
|
glm::vec3 prevPosition {};
|
||||||
|
glm::vec3 prevVelocity {};
|
||||||
|
bool prevGrounded = false;
|
||||||
|
|
||||||
Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize);
|
static inline constexpr float TELEPORT_THRESOLD_SQR = 0.5f;
|
||||||
|
|
||||||
|
Hitbox(
|
||||||
|
entityid_t entity, BodyType type, glm::vec3 position, glm::vec3 halfsize
|
||||||
|
);
|
||||||
|
|
||||||
AABB getAABB() const {
|
AABB getAABB() const {
|
||||||
return AABB(position-halfsize, position+halfsize);
|
return AABB(position - halfsize, position + halfsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 getHalfSize() const {
|
glm::vec3 getHalfSize() const {
|
||||||
return halfsize * scale;
|
return halfsize * scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setPos(const glm::vec3& vec) {
|
||||||
|
position = vec;
|
||||||
|
if (glm::distance2(position, prevPosition) >= TELEPORT_THRESOLD_SQR) {
|
||||||
|
prevPosition = vec;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,197 +5,133 @@
|
||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
#include "voxels/GlobalChunks.hpp"
|
#include "voxels/GlobalChunks.hpp"
|
||||||
#include "voxels/voxel.hpp"
|
#include "voxels/voxel.hpp"
|
||||||
|
#include "objects/Entities.hpp"
|
||||||
|
#include "debug/Logger.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#define GLM_ENABLE_EXPERIMENTAL
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
#include <glm/gtx/norm.hpp>
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
inline const float E = 0.03f;
|
inline constexpr float E = 0.03f;
|
||||||
inline const float MAX_FIX = 0.1f;
|
inline constexpr float MAX_FIX = 0.1f;
|
||||||
|
|
||||||
PhysicsSolver::PhysicsSolver(glm::vec3 gravity) : gravity(gravity) {}
|
static debug::Logger logger("physics-solver");
|
||||||
|
|
||||||
void PhysicsSolver::step(
|
PhysicsSolver::PhysicsSolver(const GlobalChunks& chunks, glm::vec3 gravity)
|
||||||
const GlobalChunks& chunks,
|
: chunks(chunks), gravity(std::move(gravity)) {
|
||||||
Hitbox& hitbox,
|
}
|
||||||
float delta,
|
|
||||||
uint substeps,
|
static glm::vec3 calc_collsion_velocity_result(
|
||||||
entityid_t entity
|
const Hitbox& a, const Hitbox& b
|
||||||
) {
|
) {
|
||||||
float dt = delta / static_cast<float>(substeps);
|
const auto& vA = a.velocity;
|
||||||
float linearDamping = hitbox.linearDamping * hitbox.friction;
|
const auto& vB = b.velocity;
|
||||||
float s = 2.0f/BLOCK_AABB_GRID;
|
if (glm::isinf(a.mass)) {
|
||||||
|
return vA;
|
||||||
auto half = hitbox.getHalfSize();
|
|
||||||
glm::vec3& pos = hitbox.position;
|
|
||||||
glm::vec3& vel = hitbox.velocity;
|
|
||||||
float gravityScale = hitbox.gravityScale;
|
|
||||||
|
|
||||||
bool prevGrounded = hitbox.grounded;
|
|
||||||
hitbox.grounded = false;
|
|
||||||
for (uint i = 0; i < substeps; i++) {
|
|
||||||
float px = pos.x;
|
|
||||||
float py = pos.y;
|
|
||||||
float pz = pos.z;
|
|
||||||
|
|
||||||
vel += gravity * dt * gravityScale;
|
|
||||||
if (hitbox.type == BodyType::DYNAMIC) {
|
|
||||||
colisionCalc(chunks, hitbox, vel, pos, half,
|
|
||||||
(prevGrounded && gravityScale > 0.0f) ? hitbox.stepHeight : 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += vel * dt + gravity * gravityScale * dt * dt * 0.5f;
|
|
||||||
if (hitbox.grounded && pos.y < py) {
|
|
||||||
pos.y = py;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hitbox.crouching && hitbox.grounded){
|
|
||||||
float y = (pos.y-half.y-E);
|
|
||||||
hitbox.grounded = false;
|
|
||||||
for (int ix = 0; ix <= (half.x-E)*2/s; ix++) {
|
|
||||||
float x = (px-half.x+E) + ix * s;
|
|
||||||
for (int iz = 0; iz <= (half.z-E)*2/s; iz++){
|
|
||||||
float z = (pos.z-half.z+E) + iz * s;
|
|
||||||
if (chunks.isObstacleAt(x,y,z)){
|
|
||||||
hitbox.grounded = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hitbox.grounded) {
|
|
||||||
pos.z = pz;
|
|
||||||
vel.z = 0.0f;
|
|
||||||
}
|
|
||||||
hitbox.grounded = false;
|
|
||||||
for (int ix = 0; ix <= (half.x-E)*2/s; ix++) {
|
|
||||||
float x = (pos.x-half.x+E) + ix * s;
|
|
||||||
for (int iz = 0; iz <= (half.z-E)*2/s; iz++){
|
|
||||||
float z = (pz-half.z+E) + iz * s;
|
|
||||||
if (chunks.isObstacleAt(x,y,z)){
|
|
||||||
hitbox.grounded = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hitbox.grounded) {
|
|
||||||
pos.x = px;
|
|
||||||
vel.x = 0.0f;
|
|
||||||
}
|
|
||||||
hitbox.grounded = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
vel.x /= 1.0f + delta * linearDamping;
|
if (glm::isinf(b.mass)) {
|
||||||
vel.z /= 1.0f + delta * linearDamping;
|
return vB - a.elasticity * (vA - vB);
|
||||||
if (hitbox.verticalDamping > 0.0f) {
|
|
||||||
vel.y /= 1.0f + delta * linearDamping * hitbox.verticalDamping;
|
|
||||||
}
|
|
||||||
|
|
||||||
AABB aabb;
|
|
||||||
aabb.a = hitbox.position - hitbox.getHalfSize();
|
|
||||||
aabb.b = hitbox.position + hitbox.getHalfSize();
|
|
||||||
for (size_t i = 0; i < sensors.size(); i++) {
|
|
||||||
auto& sensor = *sensors[i];
|
|
||||||
if (sensor.entity == entity) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool triggered = false;
|
|
||||||
switch (sensor.type) {
|
|
||||||
case SensorType::AABB:
|
|
||||||
triggered = aabb.intersect(sensor.calculated.aabb);
|
|
||||||
break;
|
|
||||||
case SensorType::RADIUS:
|
|
||||||
triggered = glm::distance2(
|
|
||||||
hitbox.position, glm::vec3(sensor.calculated.radial))
|
|
||||||
< sensor.calculated.radial.w;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (triggered) {
|
|
||||||
if (sensor.prevEntered.find(entity) == sensor.prevEntered.end()) {
|
|
||||||
sensor.enterCallback(sensor.entity, sensor.index, entity);
|
|
||||||
}
|
|
||||||
sensor.nextEntered.insert(entity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const auto& mA = a.mass;
|
||||||
|
const auto& mB = b.mass;
|
||||||
|
return (mA * vA + mB * vB + a.elasticity * mB * (vB - vA)) / (mA + mB);
|
||||||
}
|
}
|
||||||
|
|
||||||
static float calc_step_height(
|
static float calc_step_height(
|
||||||
const GlobalChunks& chunks,
|
const GlobalChunks& chunks,
|
||||||
const glm::vec3& pos,
|
const glm::vec3& pos,
|
||||||
const glm::vec3& half,
|
const glm::vec3& half,
|
||||||
float stepHeight,
|
float stepHeight
|
||||||
float s
|
|
||||||
) {
|
) {
|
||||||
if (stepHeight > 0.0f) {
|
AABB aabb(-half, +half);
|
||||||
for (int ix = 0; ix <= (half.x-E)*2/s; ix++) {
|
aabb.scale(glm::vec3(1.0f - E * 2, 1.0f, 1.0f - E * 2));
|
||||||
float x = (pos.x-half.x+E) + ix * s;
|
aabb = aabb + pos + glm::vec3(0.0f, stepHeight, 0.0f);
|
||||||
for (int iz = 0; iz <= (half.z-E)*2/s; iz++) {
|
if (stepHeight <= 0.0f) {
|
||||||
float z = (pos.z-half.z+E) + iz * s;
|
return stepHeight;
|
||||||
if (chunks.isObstacleAt(x, pos.y+half.y+stepHeight, z)) {
|
}
|
||||||
return 0.0f;
|
for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) {
|
||||||
}
|
float x = (pos.x - half.x) + ix;
|
||||||
|
for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++) {
|
||||||
|
float z = (pos.z - half.z) + iz;
|
||||||
|
if (chunks.isObstacleAt(x, pos.y + half.y + stepHeight, z, aabb)) {
|
||||||
|
return 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return stepHeight;
|
return stepHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int nx, int ny, int nz>
|
// todo: reduce code duplication
|
||||||
static bool calc_collision_neg(
|
|
||||||
|
template <int nx, int ny, int nz, int sign>
|
||||||
|
static void calc_collision(
|
||||||
|
Hitbox& hitbox,
|
||||||
const GlobalChunks& chunks,
|
const GlobalChunks& chunks,
|
||||||
glm::vec3& pos,
|
const std::vector<Hitbox*>& solidHitboxes,
|
||||||
glm::vec3& vel,
|
|
||||||
const glm::vec3& half,
|
const glm::vec3& half,
|
||||||
float stepHeight,
|
float stepHeight
|
||||||
float s
|
|
||||||
) {
|
) {
|
||||||
if (vel[nx] >= 0.0f) {
|
auto& pos = hitbox.position;
|
||||||
return false;
|
auto& vel = hitbox.velocity;
|
||||||
}
|
|
||||||
glm::vec3 offset(0.0f, stepHeight, 0.0f);
|
glm::vec3 offset(0.0f, stepHeight + E, 0.0f);
|
||||||
for (int iy = 0; iy <= std::max<int>(1, ((half-offset*0.5f)[ny]-E)*2/s); iy++) {
|
for (auto box : solidHitboxes) {
|
||||||
glm::vec3 coord;
|
if (glm::distance2(box->position, pos) < E) {
|
||||||
coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy * s;
|
continue;
|
||||||
for (int iz = 0; iz <= (half[nz]-E)*2/s; iz++){
|
}
|
||||||
coord[nz] = (pos[nz]-half[nz]+E) + iz * s;
|
auto boxhalf = box->getHalfSize();
|
||||||
coord[nx] = (pos[nx]-half[nx]-E);
|
auto aabb = AABB(pos - half, pos + half);
|
||||||
if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z)) {
|
glm::vec3 scale(1.0f);
|
||||||
vel[nx] = 0.0f;
|
scale[nz] = 1.0f - E * 8.0f;
|
||||||
float newx = std::floor(coord[nx]) + half[nx] + aabb->max()[nx] + E;
|
scale[ny] = 1.0f - E * 2.0f;
|
||||||
if (newx - pos[nx] <= E) {
|
aabb.scale(scale);
|
||||||
pos[nx] = newx;
|
aabb = aabb + offset;
|
||||||
}
|
aabb.b.y -= stepHeight + E * 2;
|
||||||
return true;
|
|
||||||
|
if ((box->position[nx] - pos[nx]) * sign <= 0.0f || !box->getAABB().intersects(aabb)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float newnegx = box->position[nx] - boxhalf[nx] - half[nx];
|
||||||
|
float newposx = box->position[nx] + boxhalf[nx] + half[nx];
|
||||||
|
float newx;
|
||||||
|
if ((glm::abs(newnegx - pos[nx]) - glm::abs(newposx - pos[nx]))*sign < 0.0f) {
|
||||||
|
newx = sign > 0 ? newnegx : newposx;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((pos[nx] - newx) * sign > 0.0f && glm::abs(pos[nx] - newx) < MAX_FIX) {
|
||||||
|
auto velA = calc_collsion_velocity_result(hitbox, *box);
|
||||||
|
auto velB = calc_collsion_velocity_result(*box, hitbox);
|
||||||
|
if ((vel[nx] - box->velocity[nx]) * sign > 0.0f) {
|
||||||
|
vel[nx] = velA[nx];
|
||||||
|
box->velocity[nx] = velB[nx];
|
||||||
}
|
}
|
||||||
|
pos[nx] = newx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
if (vel[nx] * sign <= 0.0f && hitbox.groundVelocity[nx] * sign <= 0.0f) {
|
||||||
}
|
|
||||||
|
|
||||||
template <int nx, int ny, int nz>
|
|
||||||
static void calc_collision_pos(
|
|
||||||
const GlobalChunks& chunks,
|
|
||||||
glm::vec3& pos,
|
|
||||||
glm::vec3& vel,
|
|
||||||
const glm::vec3& half,
|
|
||||||
float stepHeight,
|
|
||||||
float s
|
|
||||||
) {
|
|
||||||
if (vel[nx] <= 0.0f) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
glm::vec3 offset(0.0f, stepHeight, 0.0f);
|
for (int iy = 0; iy <= glm::ceil(((half - offset * 0.5f)[ny] - E) * 2); iy++) {
|
||||||
for (int iy = 0; iy <= std::max<int>(1, ((half-offset*0.5f)[ny]-E)*2/s); iy++) {
|
|
||||||
glm::vec3 coord;
|
glm::vec3 coord;
|
||||||
coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy * s;
|
coord[ny] = ((pos + offset)[ny] - half[ny] + E) + iy;
|
||||||
for (int iz = 0; iz <= (half[nz]-E)*2/s; iz++) {
|
for (int iz = 0; iz <= glm::ceil((half[nz] - E) * 2); iz++) {
|
||||||
coord[nz] = (pos[nz]-half[nz]+E) + iz * s;
|
coord[nz] = (pos[nz] - half[nz] + E) + iz;
|
||||||
coord[nx] = (pos[nx]+half[nx]+E);
|
coord[nx] = pos[nx] + (half[nx] + E * 2) * sign;
|
||||||
if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z)) {
|
|
||||||
vel[nx] = 0.0f;
|
auto aabb = AABB(pos - half, pos + half);
|
||||||
float newx = std::floor(coord[nx]) - half[nx] + aabb->min()[nx] - E;
|
glm::vec3 scale(1.0f);
|
||||||
if (newx - pos[nx] <= E) {
|
scale[nz] = 1.0f - E * 8.0f;
|
||||||
|
aabb.scale(scale);
|
||||||
|
aabb = aabb + offset;
|
||||||
|
aabb.b.y -= stepHeight + E * 2;
|
||||||
|
|
||||||
|
if (const auto obstacle = chunks.isObstacleAt(coord.x, coord.y, coord.z, aabb)) {
|
||||||
|
float newx = std::floor(coord[nx]) - half[nx] * sign +
|
||||||
|
(sign > 0 ? obstacle->min() : obstacle->max())[nx];
|
||||||
|
if ((pos[nx] - newx) * sign > 0.0f && glm::abs(pos[nx] - newx) < MAX_FIX) {
|
||||||
|
vel[nx] = -hitbox.elasticity * vel[nx];
|
||||||
pos[nx] = newx;
|
pos[nx] = newx;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
@ -204,93 +140,314 @@ static void calc_collision_pos(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSolver::colisionCalc(
|
bool PhysicsSolver::calcCollisionNegY(
|
||||||
const GlobalChunks& chunks,
|
Hitbox& hitbox, const glm::vec3& half, float dt
|
||||||
Hitbox& hitbox,
|
|
||||||
glm::vec3& vel,
|
|
||||||
glm::vec3& pos,
|
|
||||||
const glm::vec3 half,
|
|
||||||
float stepHeight
|
|
||||||
) {
|
) {
|
||||||
// step size (smaller - more accurate, but slower) // TODO: GET RID OF THIS
|
auto& pos = hitbox.position;
|
||||||
float s = 2.0f/BLOCK_AABB_GRID;
|
auto& vel = hitbox.velocity;
|
||||||
|
|
||||||
stepHeight = calc_step_height(chunks, pos, half, stepHeight, s);
|
for (auto box : solidHitboxes) {
|
||||||
|
if (glm::distance2(box->position, pos) < E) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto aabb = AABB(pos - half, pos + half);
|
||||||
|
glm::vec3 scale(1.0f);
|
||||||
|
scale.x = 1.0f - E * 4.0f;
|
||||||
|
scale.z = 1.0f - E * 4.0f;
|
||||||
|
aabb.scale(scale);
|
||||||
|
|
||||||
const AABB* aabb;
|
auto boxhalf = box->getHalfSize();
|
||||||
|
if (box->position.y < pos.y && box->getAABB().intersects(aabb)) {
|
||||||
calc_collision_neg<0, 1, 2>(chunks, pos, vel, half, stepHeight, s);
|
float newy = box->position.y + boxhalf.y + half.y;
|
||||||
calc_collision_pos<0, 1, 2>(chunks, pos, vel, half, stepHeight, s);
|
if (pos.y < newy && glm::abs(pos.y - newy) < boxhalf.y) {
|
||||||
|
pos.y = newy;
|
||||||
|
}
|
||||||
|
|
||||||
calc_collision_neg<2, 1, 0>(chunks, pos, vel, half, stepHeight, s);
|
auto velA = calc_collsion_velocity_result(hitbox, *box);
|
||||||
calc_collision_pos<2, 1, 0>(chunks, pos, vel, half, stepHeight, s);
|
auto velB = calc_collsion_velocity_result(*box, hitbox);
|
||||||
|
|
||||||
if (calc_collision_neg<1, 0, 2>(chunks, pos, vel, half, 0.0f, s)) {
|
hitbox.groundVelocity = box->position - box->prevPosition;
|
||||||
hitbox.grounded = true;
|
if (vel.y < hitbox.groundVelocity.y / dt) {
|
||||||
}
|
vel.y = velA.y;
|
||||||
|
box->velocity.y = velB.y;
|
||||||
if (stepHeight > 0.0 && vel.y <= 0.0f){
|
if (hitbox.groundMaterial.empty() && !box->material.empty()) {
|
||||||
for (int ix = 0; ix <= (half.x-E)*2/s; ix++) {
|
hitbox.groundMaterial = box->material;
|
||||||
float x = (pos.x-half.x+E) + ix * s;
|
|
||||||
for (int iz = 0; iz <= (half.z-E)*2/s; iz++) {
|
|
||||||
float z = (pos.z-half.z+E) + iz * s;
|
|
||||||
float y = (pos.y-half.y+E);
|
|
||||||
if ((aabb = chunks.isObstacleAt(x,y,z))){
|
|
||||||
vel.y = 0.0f;
|
|
||||||
float newy = std::floor(y) + aabb->max().y + half.y;
|
|
||||||
if (std::abs(newy-pos.y) <= MAX_FIX+stepHeight) {
|
|
||||||
pos.y = newy;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (vel.y >= 0.0f) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hitbox.groundVelocity = {};
|
||||||
|
for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) {
|
||||||
|
glm::vec3 coord;
|
||||||
|
coord.x = (pos.x - half.x + E) + ix;
|
||||||
|
for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++) {
|
||||||
|
coord.z = (pos.z - half.z + E) + iz;
|
||||||
|
coord.y = pos.y - half.y - E * 2;
|
||||||
|
|
||||||
|
auto aabb = AABB(pos - half, pos + half);
|
||||||
|
glm::vec3 scale(1.0f);
|
||||||
|
scale.x = 1.0f - E * 4.0f;
|
||||||
|
scale.z = 1.0f - E * 4.0f;
|
||||||
|
aabb.scale(scale);
|
||||||
|
|
||||||
|
if (const auto obstacle =
|
||||||
|
chunks.isObstacleAt(coord.x, coord.y, coord.z, aabb)) {
|
||||||
|
float newy = std::floor(coord.y) + half.y + obstacle->max().y;
|
||||||
|
|
||||||
|
if (newy >= pos.y) {
|
||||||
|
vel.y = -hitbox.elasticity * vel.y;
|
||||||
|
pos.y = newy;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsSolver::calcCollisions(
|
||||||
|
Hitbox& hitbox,
|
||||||
|
glm::vec3& vel,
|
||||||
|
glm::vec3& pos,
|
||||||
|
const glm::vec3& half,
|
||||||
|
float stepHeight,
|
||||||
|
float dt
|
||||||
|
) {
|
||||||
|
stepHeight = calc_step_height(chunks, pos, half, stepHeight);
|
||||||
|
|
||||||
|
auto prevPos = pos;
|
||||||
|
|
||||||
|
calc_collision<0, 1, 2, -1>(hitbox, chunks, solidHitboxes, half, stepHeight);
|
||||||
|
calc_collision<0, 1, 2, 1>(hitbox, chunks, solidHitboxes, half, stepHeight);
|
||||||
|
|
||||||
|
float xpos = pos.x;
|
||||||
|
pos.x = prevPos.x;
|
||||||
|
|
||||||
|
calc_collision<2, 1, 0, -1>(hitbox, chunks, solidHitboxes, half, stepHeight);
|
||||||
|
calc_collision<2, 1, 0, 1>(hitbox, chunks, solidHitboxes, half, stepHeight);
|
||||||
|
pos.x = xpos;
|
||||||
|
|
||||||
|
if (calcCollisionNegY(hitbox, half, dt)) {
|
||||||
|
hitbox.grounded = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (vel.y > 0.0f){
|
if (vel.y > 0.0f){
|
||||||
for (int ix = 0; ix <= (half.x-E)*2/s; ix++) {
|
AABB boxAABB = AABB(-half, +half);
|
||||||
float x = (pos.x-half.x+E) + ix * s;
|
boxAABB.scale(glm::vec3(1.0f - E * 4, 1.0f + E * 2, 1.0f - E * 4));
|
||||||
for (int iz = 0; iz <= (half.z-E)*2/s; iz++) {
|
boxAABB = boxAABB.translated(pos);
|
||||||
float z = (pos.z-half.z+E) + iz * s;
|
|
||||||
float y = (pos.y+half.y+E);
|
for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) {
|
||||||
if ((aabb = chunks.isObstacleAt(x,y,z))){
|
float x = (pos.x - half.x + E) + ix;
|
||||||
vel.y = 0.0f;
|
for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++) {
|
||||||
|
float z = (pos.z - half.z + E) + iz;
|
||||||
|
float y = (pos.y + half.y + E) + 0.5f;
|
||||||
|
if (auto aabb = chunks.isObstacleAt(x, y, z, boxAABB)) {
|
||||||
float newy = std::floor(y) - half.y + aabb->min().y - E;
|
float newy = std::floor(y) - half.y + aabb->min().y - E;
|
||||||
if (std::abs(newy-pos.y) <= MAX_FIX) {
|
if (pos.y >= newy) {
|
||||||
|
vel.y = -hitbox.elasticity * vel.y;
|
||||||
pos.y = newy;
|
pos.y = newy;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (auto box : solidHitboxes) {
|
||||||
|
if (glm::distance2(box->position, pos) < E) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto boxhalf = box->getHalfSize();
|
||||||
|
if (box->position.y > pos.y && box->getAABB().intersects(boxAABB)) {
|
||||||
|
float newy = box->position.y - boxhalf.y - half.y;
|
||||||
|
if (pos.y > newy && glm::abs(pos.y - newy) < 0.5f) {
|
||||||
|
pos.y = newy;
|
||||||
|
}
|
||||||
|
auto velA = calc_collsion_velocity_result(hitbox, *box);
|
||||||
|
auto velB = calc_collsion_velocity_result(*box, hitbox);
|
||||||
|
if (vel.y > hitbox.groundVelocity.y / dt) {
|
||||||
|
vel.y = velA.y;
|
||||||
|
box->velocity.y = velB.y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// step on
|
||||||
|
if (stepHeight > 0.0 && vel.y <= 0.0f) {
|
||||||
|
AABB boxAABB = AABB(pos - half, pos + half);
|
||||||
|
boxAABB.scale(glm::vec3(1.0f - E * 2, 1.0f - E * 2, 1.0f - E * 2));
|
||||||
|
for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) {
|
||||||
|
float x = (pos.x - half.x) + ix;
|
||||||
|
for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++) {
|
||||||
|
float z = (pos.z - half.z) + iz;
|
||||||
|
float y = (pos.y - half.y + E) + E;
|
||||||
|
if (auto aabb = chunks.isObstacleAt(x, y, z, boxAABB)) {
|
||||||
|
if (vel.y < 0.0f) {
|
||||||
|
vel.y = 0.0f;
|
||||||
|
}
|
||||||
|
float newy = std::floor(y) + aabb->max().y + half.y;
|
||||||
|
if (std::abs(newy - pos.y) <= stepHeight) {
|
||||||
|
pos.y = newy;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto box : solidHitboxes) {
|
||||||
|
if (glm::distance2(box->position, pos) < E) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto boxhalf = box->getHalfSize();
|
||||||
|
if (box->getAABB().intersects(boxAABB)) {
|
||||||
|
vel.y = 0.0f;
|
||||||
|
float newy = box->position.y + boxhalf.y + half.y;
|
||||||
|
if (std::abs(newy - pos.y) <= stepHeight + E * 4) {
|
||||||
|
pos.y = newy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PhysicsSolver::isBlockInside(int x, int y, int z, Hitbox* hitbox) {
|
void PhysicsSolver::calcSubstep(
|
||||||
const glm::vec3& pos = hitbox->position;
|
Hitbox& hitbox, glm::vec3& vel, glm::vec3& pos, float dt
|
||||||
auto half = hitbox->getHalfSize();
|
) {
|
||||||
return x >= floor(pos.x-half.x) && x <= floor(pos.x+half.x) &&
|
auto initpos = pos;
|
||||||
z >= floor(pos.z-half.z) && z <= floor(pos.z+half.z) &&
|
auto half = hitbox.getHalfSize();
|
||||||
y >= floor(pos.y-half.y) && y <= floor(pos.y+half.y);
|
float gravityScale = hitbox.gravityScale;
|
||||||
|
|
||||||
|
if (hitbox.type == BodyType::DYNAMIC) {
|
||||||
|
calcCollisions(
|
||||||
|
hitbox,
|
||||||
|
vel,
|
||||||
|
pos,
|
||||||
|
half,
|
||||||
|
(hitbox.prevGrounded && gravityScale > 0.0f) ? hitbox.stepHeight
|
||||||
|
: 0.0f,
|
||||||
|
dt
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
vel += gravity * dt * gravityScale;
|
||||||
|
pos += vel * dt + gravity * gravityScale * dt * dt * 0.5f;
|
||||||
|
|
||||||
|
// crouching
|
||||||
|
if (!hitbox.crouching || !hitbox.grounded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float y = (pos.y - half.y - E);
|
||||||
|
for (int axis = 0; axis <= 2; axis += 2) {
|
||||||
|
hitbox.grounded = false;
|
||||||
|
|
||||||
|
auto checkPos = pos;
|
||||||
|
checkPos.x = axis != 0 ? initpos.x : pos.x;
|
||||||
|
checkPos.z = axis == 0 ? initpos.z : pos.z;
|
||||||
|
|
||||||
|
AABB boxAABB(checkPos - half, checkPos + half);
|
||||||
|
boxAABB.scale(glm::vec3(1.0f - E * 4, 1.5f, 1.0f - E * 4));
|
||||||
|
for (int ix = 0; ix <= glm::ceil((half.x - E) * 2); ix++) {
|
||||||
|
float x = (pos.x - half.x + E) + ix;
|
||||||
|
for (int iz = 0; iz <= glm::ceil((half.z - E) * 2); iz++){
|
||||||
|
float z = (pos.z - half.z + E) + iz;
|
||||||
|
if (chunks.isObstacleAt(x, y, z, boxAABB)){
|
||||||
|
hitbox.grounded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto box : solidHitboxes) {
|
||||||
|
if (glm::distance2(box->position, pos) < E) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (box->position.y < pos.y && box->getAABB().intersects(boxAABB)) {
|
||||||
|
hitbox.grounded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hitbox.grounded) {
|
||||||
|
pos[axis] = initpos[axis];
|
||||||
|
vel[axis] = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hitbox.grounded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PhysicsSolver::isBlockInside(int x, int y, int z, Block* def, blockstate state, Hitbox* hitbox) {
|
void PhysicsSolver::step(
|
||||||
const float e = 0.001f; // inaccuracy
|
const GlobalChunks& chunks, float delta, uint substeps
|
||||||
const glm::vec3& pos = hitbox->position;
|
) {
|
||||||
auto half = hitbox->getHalfSize();
|
for (auto hitbox : hitboxes) {
|
||||||
const auto& boxes = def->rotatable
|
hitbox->groundMaterial.clear();
|
||||||
? def->rt.hitboxes[state.rotation]
|
hitbox->prevGrounded = hitbox->grounded;
|
||||||
: def->hitboxes;
|
hitbox->grounded = false;
|
||||||
for (const auto& block_hitbox : boxes) {
|
hitbox->prevVelocity = hitbox->velocity;
|
||||||
glm::vec3 min = block_hitbox.min();
|
}
|
||||||
glm::vec3 max = block_hitbox.max();
|
|
||||||
if (min.x < pos.x+half.x-x-e && max.x > pos.x-half.x-x+e &&
|
float dt = delta / static_cast<float>(substeps);
|
||||||
min.z < pos.z+half.z-z-e && max.z > pos.z-half.z-z+e &&
|
for (uint i = 0; i < substeps; i++) {
|
||||||
min.y < pos.y+half.y-y-e && max.y > pos.y-half.y-y+e)
|
for (auto hitbox : hitboxes) {
|
||||||
return true;
|
glm::vec3& pos = hitbox->position;
|
||||||
|
hitbox->prevPosition = hitbox->position;
|
||||||
|
calcSubstep(*hitbox, hitbox->velocity, pos, dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto hitbox : hitboxes) {
|
||||||
|
float linearDamping = hitbox->linearDamping * hitbox->friction;
|
||||||
|
|
||||||
|
glm::vec3& vel = hitbox->velocity;
|
||||||
|
auto diff = hitbox->groundVelocity / dt - vel;
|
||||||
|
vel.x += diff.x * delta * linearDamping;
|
||||||
|
vel.z += diff.z * delta * linearDamping;
|
||||||
|
|
||||||
|
if (hitbox->verticalDamping > 0.0f) {
|
||||||
|
vel.y /= 1.0f + delta * linearDamping * hitbox->verticalDamping;
|
||||||
|
}
|
||||||
|
if (!hitbox->grounded) {
|
||||||
|
hitbox->groundVelocity = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSensors(*hitbox);
|
||||||
|
hitbox->friction = glm::abs(hitbox->gravityScale <= 1e-7f)
|
||||||
|
? 8.0f
|
||||||
|
: (!hitbox->prevGrounded ? 2.0f : 10.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsSolver::updateSensors(Hitbox& hitbox) {
|
||||||
|
auto aabb = hitbox.getAABB();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sensors.size(); i++) {
|
||||||
|
auto& sensor = *sensors[i];
|
||||||
|
if (sensor.entity == hitbox.entity) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bool triggered = false;
|
||||||
|
switch (sensor.type) {
|
||||||
|
case SensorType::AABB:
|
||||||
|
triggered = aabb.intersects(sensor.calculated.aabb);
|
||||||
|
break;
|
||||||
|
case SensorType::RADIUS:
|
||||||
|
triggered = glm::distance2(
|
||||||
|
hitbox.position, glm::vec3(sensor.calculated.radial))
|
||||||
|
< sensor.calculated.radial.w;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!triggered) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (sensor.prevEntered.find(hitbox.entity) == sensor.prevEntered.end()) {
|
||||||
|
sensor.enterCallback(sensor.entity, sensor.index, hitbox.entity);
|
||||||
|
}
|
||||||
|
sensor.nextEntered.insert(hitbox.entity);
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSolver::removeSensor(Sensor* sensor) {
|
void PhysicsSolver::removeSensor(Sensor* sensor) {
|
||||||
sensors.erase(std::remove(sensors.begin(), sensors.end(), sensor), sensors.end());
|
sensors.erase(
|
||||||
|
std::remove(sensors.begin(), sensors.end(), sensor), sensors.end()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,35 +9,48 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
class Block;
|
class Block;
|
||||||
|
class Entities;
|
||||||
class GlobalChunks;
|
class GlobalChunks;
|
||||||
struct Sensor;
|
struct Sensor;
|
||||||
|
|
||||||
class PhysicsSolver {
|
class PhysicsSolver {
|
||||||
glm::vec3 gravity;
|
|
||||||
std::vector<Sensor*> sensors;
|
|
||||||
public:
|
public:
|
||||||
PhysicsSolver(glm::vec3 gravity);
|
PhysicsSolver(const GlobalChunks& chunks, glm::vec3 gravity);
|
||||||
void step(
|
|
||||||
const GlobalChunks& chunks,
|
|
||||||
Hitbox& hitbox,
|
|
||||||
float delta,
|
|
||||||
uint substeps,
|
|
||||||
entityid_t entity
|
|
||||||
);
|
|
||||||
void colisionCalc(
|
|
||||||
const GlobalChunks& chunks,
|
|
||||||
Hitbox& hitbox,
|
|
||||||
glm::vec3& vel,
|
|
||||||
glm::vec3& pos,
|
|
||||||
const glm::vec3 half,
|
|
||||||
float stepHeight
|
|
||||||
);
|
|
||||||
bool isBlockInside(int x, int y, int z, Hitbox* hitbox);
|
|
||||||
bool isBlockInside(int x, int y, int z, Block* def, blockstate state, Hitbox* hitbox);
|
|
||||||
|
|
||||||
void setSensors(std::vector<Sensor*> sensors) {
|
void step(const GlobalChunks& chunks, float delta, uint substeps);
|
||||||
this->sensors = std::move(sensors);
|
|
||||||
|
auto& getSensorsWriteable() {
|
||||||
|
return sensors;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& getSolidHitboxesWriteable() {
|
||||||
|
return solidHitboxes;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& getHitboxesWriteable() {
|
||||||
|
return hitboxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeSensor(Sensor* sensor);
|
void removeSensor(Sensor* sensor);
|
||||||
|
private:
|
||||||
|
const GlobalChunks& chunks;
|
||||||
|
glm::vec3 gravity;
|
||||||
|
std::vector<Sensor*> sensors;
|
||||||
|
std::vector<Hitbox*> solidHitboxes;
|
||||||
|
std::vector<Hitbox*> hitboxes;
|
||||||
|
|
||||||
|
void calcCollisions(
|
||||||
|
Hitbox& hitbox,
|
||||||
|
glm::vec3& vel,
|
||||||
|
glm::vec3& pos,
|
||||||
|
const glm::vec3& half,
|
||||||
|
float stepHeight,
|
||||||
|
float dt
|
||||||
|
);
|
||||||
|
|
||||||
|
void calcSubstep(Hitbox& hitbox, glm::vec3& vel, glm::vec3& pos, float dt);
|
||||||
|
|
||||||
|
bool calcCollisionNegY(Hitbox& hitbox, const glm::vec3& half, float dt);
|
||||||
|
|
||||||
|
void updateSensors(Hitbox& hitbox);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
#include "Chunks.hpp"
|
#include "Chunks.hpp"
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "data/StructLayout.hpp"
|
#include "data/StructLayout.hpp"
|
||||||
#include "coders/byte_utils.hpp"
|
#include "coders/byte_utils.hpp"
|
||||||
#include "content/Content.hpp"
|
#include "content/Content.hpp"
|
||||||
|
|
@ -20,6 +14,11 @@
|
||||||
#include "VoxelsVolume.hpp"
|
#include "VoxelsVolume.hpp"
|
||||||
#include "blocks_agent.hpp"
|
#include "blocks_agent.hpp"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
Chunks::Chunks(
|
Chunks::Chunks(
|
||||||
int32_t w,
|
int32_t w,
|
||||||
int32_t d,
|
int32_t d,
|
||||||
|
|
@ -67,20 +66,21 @@ const AABB* Chunks::isObstacleAt(float x, float y, float z) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto& def = indices.blocks.require(v->id);
|
const auto& def = indices.blocks.require(v->id);
|
||||||
if (def.obstacle) {
|
if (!def.obstacle) {
|
||||||
glm::ivec3 offset {};
|
return nullptr;
|
||||||
if (v->state.segment) {
|
}
|
||||||
glm::ivec3 point(ix, iy, iz);
|
glm::ivec3 offset {};
|
||||||
offset = seekOrigin(point, def, v->state) - point;
|
if (v->state.segment) {
|
||||||
}
|
glm::ivec3 point(ix, iy, iz);
|
||||||
const auto& boxes =
|
offset = seekOrigin(point, def, v->state) - point;
|
||||||
def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes;
|
}
|
||||||
for (const auto& hitbox : boxes) {
|
const auto& boxes =
|
||||||
if (hitbox.contains(
|
def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes;
|
||||||
{x - ix - offset.x, y - iy - offset.y, z - iz - offset.z}
|
for (const auto& hitbox : boxes) {
|
||||||
)) {
|
if (hitbox.contains(
|
||||||
return &hitbox;
|
{x - ix - offset.x, y - iy - offset.y, z - iz - offset.z}
|
||||||
}
|
)) {
|
||||||
|
return &hitbox;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <memory>
|
|
||||||
#include <set>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
#include "voxel.hpp"
|
#include "voxel.hpp"
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
#include "util/AreaMap2D.hpp"
|
#include "util/AreaMap2D.hpp"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class VoxelRenderer;
|
class VoxelRenderer;
|
||||||
|
|
||||||
struct AABB;
|
struct AABB;
|
||||||
|
|
|
||||||
|
|
@ -214,6 +214,6 @@ void GlobalChunks::putChunk(std::shared_ptr<Chunk> chunk) {
|
||||||
chunksMap[keyfrom(chunk->x, chunk->z)] = std::move(chunk);
|
chunksMap[keyfrom(chunk->x, chunk->z)] = std::move(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
const AABB* GlobalChunks::isObstacleAt(float x, float y, float z) const {
|
std::optional<AABB> GlobalChunks::isObstacleAt(float x, float y, float z, const AABB& aabb) const {
|
||||||
return blocks_agent::is_obstacle_at(*this, x, y, z);
|
return blocks_agent::is_obstacle_at(*this, x, y, z, aabb);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#define GLM_ENABLE_EXPERIMENTAL
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
|
@ -57,7 +58,7 @@ public:
|
||||||
|
|
||||||
void putChunk(std::shared_ptr<Chunk> chunk);
|
void putChunk(std::shared_ptr<Chunk> chunk);
|
||||||
|
|
||||||
const AABB* isObstacleAt(float x, float y, float z) const;
|
std::optional<AABB> isObstacleAt(float x, float y, float z, const AABB& aabb) const;
|
||||||
|
|
||||||
inline Chunk* getChunk(int cx, int cz) const {
|
inline Chunk* getChunk(int cx, int cz) const {
|
||||||
const auto& found = chunksMap.find(keyfrom(cx, cz));
|
const auto& found = chunksMap.find(keyfrom(cx, cz));
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
#include "voxel.hpp"
|
#include "voxel.hpp"
|
||||||
#include "VoxelsVolume.hpp"
|
#include "VoxelsVolume.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
@ -468,8 +467,8 @@ void get_voxels(
|
||||||
);
|
);
|
||||||
|
|
||||||
template <class Storage>
|
template <class Storage>
|
||||||
inline const AABB* is_obstacle_at(
|
inline std::optional<AABB> is_obstacle_at(
|
||||||
const Storage& chunks, float x, float y, float z
|
const Storage& chunks, float x, float y, float z, const AABB& aabb
|
||||||
) {
|
) {
|
||||||
int ix = std::floor(x);
|
int ix = std::floor(x);
|
||||||
int iy = std::floor(y);
|
int iy = std::floor(y);
|
||||||
|
|
@ -477,30 +476,34 @@ inline const AABB* is_obstacle_at(
|
||||||
voxel* v = get(chunks, ix, iy, iz);
|
voxel* v = get(chunks, ix, iy, iz);
|
||||||
if (v == nullptr) {
|
if (v == nullptr) {
|
||||||
if (iy >= CHUNK_H) {
|
if (iy >= CHUNK_H) {
|
||||||
return nullptr;
|
return std::nullopt;
|
||||||
} else {
|
} else {
|
||||||
static const AABB empty;
|
return AABB();
|
||||||
return ∅
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto& def = chunks.getContentIndices().blocks.require(v->id);
|
const auto& def = chunks.getContentIndices().blocks.require(v->id);
|
||||||
if (def.obstacle) {
|
if (!def.obstacle) {
|
||||||
glm::ivec3 offset {};
|
return std::nullopt;
|
||||||
if (v->state.segment) {
|
}
|
||||||
glm::ivec3 point(ix, iy, iz);
|
glm::ivec3 offset {};
|
||||||
offset = seek_origin(chunks, point, def, v->state) - point;
|
if (v->state.segment) {
|
||||||
}
|
glm::ivec3 point(ix, iy, iz);
|
||||||
const auto& boxes =
|
offset = seek_origin(chunks, point, def, v->state) - point;
|
||||||
def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes;
|
}
|
||||||
for (const auto& hitbox : boxes) {
|
const auto& boxes =
|
||||||
if (hitbox.contains(
|
def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes;
|
||||||
{x - ix - offset.x, y - iy - offset.y, z - iz - offset.z}
|
|
||||||
)) {
|
for (const auto& hitbox : boxes) {
|
||||||
return &hitbox;
|
if (hitbox.intersects(aabb - glm::ivec3(ix, iy, iz))) {
|
||||||
}
|
return hitbox + offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Storage>
|
||||||
|
inline std::optional<AABB> is_obstacle_at(const Storage& chunks, float x, float y, float z) {
|
||||||
|
return is_obstacle_at(chunks, x, y, z, AABB({x, y, z}, {x + 1, y + 1, z + 1}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Check block grounding
|
/// @brief Check block grounding
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ Level::Level(
|
||||||
: world(std::move(worldPtr)),
|
: world(std::move(worldPtr)),
|
||||||
content(content),
|
content(content),
|
||||||
chunks(std::make_unique<GlobalChunks>(*this)),
|
chunks(std::make_unique<GlobalChunks>(*this)),
|
||||||
physics(std::make_unique<PhysicsSolver>(glm::vec3(0, -22.6f, 0))),
|
physics(std::make_unique<PhysicsSolver>(*chunks, 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)),
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ void WorldGenerator::placeStructure(
|
||||||
}
|
}
|
||||||
auto& otherPrototype = *found->second;
|
auto& otherPrototype = *found->second;
|
||||||
auto chunkAABB = gen_chunk_aabb(chunkX + lcx, chunkZ + lcz);
|
auto chunkAABB = gen_chunk_aabb(chunkX + lcx, chunkZ + lcz);
|
||||||
if (chunkAABB.intersect(aabb)) {
|
if (chunkAABB.intersects(aabb)) {
|
||||||
otherPrototype.placements.emplace_back(
|
otherPrototype.placements.emplace_back(
|
||||||
priority,
|
priority,
|
||||||
StructurePlacement {
|
StructurePlacement {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue