mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-04 10:31:50 +00:00
Merge branch 'MihailRis:main' into main
This commit is contained in:
commit
e049f4ae0c
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.
|
||||
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
|
||||
|
||||
### *skeleton-name*
|
||||
|
|
|
|||
|
|
@ -4,14 +4,17 @@ A library for managing audio/visual weather effects.
|
|||
|
||||
Weather settings:
|
||||
|
||||
| Property | Description | Default |
|
||||
| ------------ | ------------------------------ | ------- |
|
||||
| fall | Precipitation (see table 2) | {} |
|
||||
| clouds | Cloudiness [0.0, 1.0] | 0.0 |
|
||||
| fog_opacity | Maximum fog density [0.0, 1.0] | 0.0 |
|
||||
| fog_dencity | Fog density | 1.0 |
|
||||
| fog_curve | Fog curve | 1.0 |
|
||||
| thunder_rate | Thunder rate [0.0, 1.0] | 0.0 |
|
||||
| Property | Description | Default |
|
||||
| ------------- | -------------------------------- | ----------------- |
|
||||
| fall | Precipitation (see table 2) | {} |
|
||||
| clouds | Cloudiness [0.0, 1.0] | 0.0 |
|
||||
| fog_opacity | Maximum fog density [0.0, 1.0] | 0.0 |
|
||||
| fog_dencity | Fog density | 1.0 |
|
||||
| fog_curve | Fog curve | 1.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:
|
||||
|
||||
|
|
|
|||
|
|
@ -78,14 +78,14 @@ Returns the id of all base packages (non-removeable)
|
|||
|
||||
```lua
|
||||
pack.get_info(packid: str) -> {
|
||||
id: str,
|
||||
title: str,
|
||||
creator: str,
|
||||
description: str,
|
||||
version: str,
|
||||
path: str,
|
||||
icon: str, -- not available in headless mode
|
||||
dependencies: optional strings array
|
||||
id: str,
|
||||
title: str,
|
||||
creator: str,
|
||||
description: str,
|
||||
version: str,
|
||||
path: str,
|
||||
icon: str, -- not available in headless mode
|
||||
dependencies: optional strings array
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -95,6 +95,7 @@ Returns information about the pack (not necessarily installed).
|
|||
- `!` - required
|
||||
- `?` - optional
|
||||
- `~` - weak
|
||||
|
||||
for example `!teal`
|
||||
|
||||
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
|
||||
-- Sets the physical body type
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
- `font` - font name
|
||||
- `text-align` - inner text alignment (*left/center/right*). Type: string.
|
||||
|
||||
## *checkbox*
|
||||
|
|
@ -108,6 +109,7 @@ Inner text is a button text.
|
|||
|
||||
## *label*
|
||||
|
||||
- `font` - font name
|
||||
- `text-align` - text alignment (*left/center/right*). Type: string.
|
||||
- `valign` - vertical text alignment: top/center/bottom.
|
||||
- `supplier` - text supplier (called every frame).
|
||||
|
|
@ -131,6 +133,7 @@ Inner text is a button text.
|
|||
|
||||
Inner text - initially entered text
|
||||
|
||||
- `font` - font name
|
||||
- `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).
|
||||
- `supplier` - text supplier (called every frame)
|
||||
|
|
@ -190,6 +193,14 @@ Example of list description:
|
|||
- `selected` - initially selected value. Default: "".
|
||||
- `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*
|
||||
|
|
@ -213,9 +224,6 @@ Element must be in direct sub-element of *inventory*.
|
|||
## *slots-grid*
|
||||
|
||||
- `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
|
||||
- `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.
|
||||
|
|
@ -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.
|
||||
- `taking` - the ability to take an item from 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.
|
||||
1. Радиальная область с радиусом 1.6 м.
|
||||
|
||||
### Осязаемость - *solid*
|
||||
|
||||
Делает хитбокс сущности осязаемым препятствием. Вместе с включением данного свойства у `dynamic` тел следует указать массу (mass).
|
||||
|
||||
### Масса - *mass*
|
||||
|
||||
Определяет массу сущности. За стандарт приняты килограммы.
|
||||
В случае `kinematic` тела вместо указанной используется бесконечная масса.
|
||||
|
||||
### Эластичность - *elasticity*
|
||||
|
||||
Определяет упругость сущности.
|
||||
|
||||
### Высота шага - *step-height*
|
||||
|
||||
Определяет максимальную высоту препятствия, на которое сущность может шагнуть без прыжка.
|
||||
|
||||
## Вид
|
||||
|
||||
### Имя скелета - *skeleton-name*
|
||||
|
|
|
|||
|
|
@ -4,14 +4,17 @@
|
|||
|
||||
Настройки погоды:
|
||||
|
||||
| Свойство | Описание | По-умолчанию |
|
||||
| ------------ | ---------------------------------------- | ------------ |
|
||||
| fall | Осадки (см. таблица 2) | {} |
|
||||
| clouds | Облачность [0.0, 1.0] | 0.0 |
|
||||
| fog_opacity | Максимальная плотность тумана [0.0, 1.0] | 0.0 |
|
||||
| fog_dencity | Плотность тумана | 1.0 |
|
||||
| fog_curve | Кривая тумана | 1.0 |
|
||||
| thunder_rate | Частота грома [0.0, 1.0] | 0.0 |
|
||||
| Свойство | Описание | По-умолчанию |
|
||||
| ------------- | ------------------------------------------ | ----------------- |
|
||||
| fall | Осадки (см. таблица 2) | {} |
|
||||
| clouds | Облачность [0.0, 1.0] | 0.0 |
|
||||
| fog_opacity | Максимальная плотность тумана [0.0, 1.0] | 0.0 |
|
||||
| fog_dencity | Плотность тумана | 1.0 |
|
||||
| fog_curve | Кривая тумана | 1.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
|
||||
```
|
||||
```
|
||||
|
|
|
|||
|
|
@ -114,15 +114,29 @@ mat4.decompose(m: matrix)
|
|||
} или nil
|
||||
```
|
||||
|
||||
## Отслеживание точки *mat4.look_at(...)*
|
||||
## Отслеживание точки - *mat4.look_at(...)*
|
||||
|
||||
```lua
|
||||
-- cоздает матрицу вида с точки 'eye' на точку 'center', где вектор 'up' определяет верх.
|
||||
-- cоздаёт матрицу вида с точки 'eye' на точку 'center', где вектор 'up' определяет верх.
|
||||
mat4.look_at(eye: vec3, center: vec3, up: vec3)
|
||||
-- записывает матрицу вида в dst
|
||||
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(...)*
|
||||
|
||||
```lua
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ pack.get_info(packid: string) -> {
|
|||
- `!` - required
|
||||
- `?` - optional
|
||||
- `~` - weak
|
||||
|
||||
например `!teal`
|
||||
|
||||
Для получения информации о нескольких паках используйте таблицу id, чтобы не
|
||||
|
|
@ -101,4 +102,4 @@ pack.assemble(packids: table<string>) -> table<string>
|
|||
pack.request_writeable(packid: string, callback: function(string))
|
||||
```
|
||||
|
||||
Запрашивает у пользователя право на модификацию пака. При подтвержении новая точка входа будет передана в callback.
|
||||
Запрашивает у пользователя право на модификацию пака. При подтвержении новая точка входа будет передана в callback.
|
||||
|
|
|
|||
|
|
@ -2,22 +2,34 @@
|
|||
|
||||
Библиотека для работы с кватернионами.
|
||||
|
||||
## Кватернион из матрицы - *mat4.from_quat(...)*
|
||||
## Кватернион из матрицы - *quat.from_mat4(...)*
|
||||
|
||||
```lua
|
||||
-- создает кватернион на основе матрицы вращения
|
||||
-- создаёт кватернион на основе матрицы вращения
|
||||
quat.from_mat4(m: matrix) -> quat
|
||||
|
||||
-- записывает кватернион по матрице вращения в dst
|
||||
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(...)*
|
||||
|
||||
Интерполяция всегда выполняется по короткому пути, а вращение выполняется с постоянной скоростью.
|
||||
|
||||
```lua
|
||||
-- создает кватернион как интерполяцию между a и b,
|
||||
-- создаёт кватернион как интерполяцию между a и b,
|
||||
-- где t - фактор интерполяции
|
||||
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(...)*
|
||||
|
||||
```lua
|
||||
-- возвращает строку представляющую содержимое кватерниона
|
||||
-- возвращает строку, представляющую содержимое кватерниона
|
||||
quat.tostring(q: quat) -> string
|
||||
```
|
||||
|
|
|
|||
|
|
@ -114,6 +114,21 @@ body:set_crouching(enabled: bool)
|
|||
body:get_body_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
|
||||
|
|
|
|||
|
|
@ -137,6 +137,18 @@ io_stream:write_line(string)
|
|||
io_stream:read_fully(
|
||||
[опционально] useTable: boolean
|
||||
) -> Bytearray | table<number> | table<string> | string
|
||||
|
||||
--[[
|
||||
Устанавливает позицию в потоке
|
||||
Режимы:
|
||||
b - Задаёт позицию относительно начало файла
|
||||
c - Задаёт позицию относительно текущей позиции
|
||||
e - Задаёт позицию относительно конца файла
|
||||
--]]
|
||||
io_stream:seek(
|
||||
mode: string
|
||||
offset: number
|
||||
)
|
||||
```
|
||||
|
||||
## Методы Buffered-режима
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@
|
|||
|
||||
Внутренний текст - текст кнопки.
|
||||
|
||||
- `font` - имя шрифта
|
||||
- `text-align` - выравнивание текста ("left", "center" или "right"). Тип: строка.
|
||||
|
||||
## Флажок - *checkbox*
|
||||
|
|
@ -110,6 +111,7 @@
|
|||
|
||||
## Метка - *label*
|
||||
|
||||
- `font` - имя шрифта
|
||||
- `text-align` - выравнивание текста ("left", "center" или "right"). Тип: строка.
|
||||
- `valign` - вертикальное выравнивание текста: top/center/bottom
|
||||
- `supplier` - поставщик текста (вызывается каждый кадр)
|
||||
|
|
@ -132,6 +134,7 @@
|
|||
|
||||
Внутренний текст - изначально введенный текст
|
||||
|
||||
- `font` - имя шрифта
|
||||
- `placeholder` - текст подстановки (используется если текстовое поле пусто)
|
||||
- `hint` - текст, отображаемый, если текстовое поле пусто (не отправляется в consumer, sub-consumer и validator).
|
||||
- `supplier` - поставщик текста (вызывается каждый кадр)
|
||||
|
|
@ -191,6 +194,14 @@
|
|||
- `selected` - изначально выбранное значение. По-умолчанию: "".
|
||||
- `onselect` - функция, в которую передаётся выбранное пользователем значение
|
||||
|
||||
## Привязка ввода - *bindbox*
|
||||
|
||||
Элемент для отображения и изменения привязки клавиш или кнопок мыши.
|
||||
|
||||
- `font` - имя шрифта. Тип: строка.
|
||||
- `binding` - имя привязки. Тип: строка.
|
||||
- `padding` - отступ между текстом и границами элемента. Тип: число. По-умолчанию: 6
|
||||
|
||||
# Элементы инвентаря
|
||||
|
||||
## Инвентарь - *inventory*
|
||||
|
|
@ -214,10 +225,8 @@
|
|||
## Сетка слотов - *slots-grid*
|
||||
|
||||
Элемент должен находиться внутри `inventory` элемента, без посредников.
|
||||
|
||||
- `start-index` - индекс первого слота
|
||||
- `rows` - число рядов (не указывается, если указано cols).
|
||||
- `cols` - число столбцов (не указывается, если указано rows).
|
||||
- `count` - общее число слотов (не указывается, если указаны rows и cols).
|
||||
- `interval` - интервал между слотами. Тип: число.
|
||||
- `padding` - отступ вокруг решетки слотов. Тип: число. (*атрибут будет удален*)
|
||||
- `sharefunc` - lua событие вызываемое при использовании ЛКМ + Shift. Передается id инвентаря и индекс слота
|
||||
|
|
@ -225,3 +234,8 @@
|
|||
- `onrightclick` - lua событие вызываемое при использовании ПКМ. Передается id инвентаря и индекс слота
|
||||
- `taking` - возможность взять предмет из слота.
|
||||
- `placing` - возможность положить предмет в слот.
|
||||
|
||||
Конфигурация слотов (достаточно указать два атрибута):
|
||||
- `rows` - число рядов. Тип: число.
|
||||
- `cols` - число столбцов. Тип: число.
|
||||
- `count` - общее число слотов. Тип: число.
|
||||
|
|
|
|||
|
|
@ -3,5 +3,6 @@
|
|||
"base:falling_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
|
||||
}
|
||||
rig:set_matrix(0, matrix)
|
||||
body:set_material(block.material(id))
|
||||
end
|
||||
|
||||
function on_grounded()
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ local Rigidbody = {__index={
|
|||
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,
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ local io_stream = require "core:io_stream"
|
|||
local lib = {
|
||||
read = file.__read_descriptor,
|
||||
write = file.__write_descriptor,
|
||||
seek = file.__seek_descriptor,
|
||||
flush = file.__flush_descriptor,
|
||||
is_alive = file.__has_descriptor,
|
||||
close = file.__close_descriptor
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ function lib.write(fd, bytearray)
|
|||
end
|
||||
end
|
||||
|
||||
function lib.seek(fd, mode, offset)
|
||||
error("cannot seek the named pipe")
|
||||
end
|
||||
|
||||
function lib.flush(fd)
|
||||
-- no flush on unix
|
||||
end
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ function lib.read(handle, len)
|
|||
|
||||
if not has_data then
|
||||
return out
|
||||
elseif hasData == -1 then
|
||||
elseif has_data == -1 then
|
||||
error("failed to read from named pipe: "..tostring(C.GetLastError()))
|
||||
end
|
||||
|
||||
|
|
@ -84,8 +84,8 @@ end
|
|||
|
||||
function lib.write(handle, bytearray)
|
||||
local len = #bytearray
|
||||
|
||||
local buffer = FFI.new("uint8_t[?]", len)
|
||||
|
||||
for i = 1, len do
|
||||
buffer[i-1] = bytearray[i]
|
||||
end
|
||||
|
|
@ -97,6 +97,10 @@ function lib.write(handle, bytearray)
|
|||
end
|
||||
end
|
||||
|
||||
function lib.seek(handle, mode, offset)
|
||||
error("cannot seek the named pipe")
|
||||
end
|
||||
|
||||
function lib.flush(handle)
|
||||
C.FlushFileBuffers(handle)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -369,6 +369,10 @@ function io_stream:write(arg, ...)
|
|||
end
|
||||
end
|
||||
|
||||
function io_stream:seek(mode, offset)
|
||||
self.ioLib.seek(self.descriptor, mode, offset)
|
||||
end
|
||||
|
||||
function io_stream:is_alive()
|
||||
return self.ioLib.is_alive(self.descriptor)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ end
|
|||
|
||||
local prepared = false
|
||||
body:set_enabled(false)
|
||||
mob:set_flight(true)
|
||||
|
||||
function on_physics_update(delta)
|
||||
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) {
|
||||
std::string name = root.attr("name", "").getText();
|
||||
|
||||
glm::vec3 movement {};
|
||||
glm::mat4 tsf(1.0f);
|
||||
if (root.has("move")) {
|
||||
tsf = glm::translate(tsf, root.attr("move").asVec3());
|
||||
movement = root.attr("move").asVec3();
|
||||
}
|
||||
if (root.has("rotate")) {
|
||||
auto text = root.attr("rotate").getText();
|
||||
|
|
@ -371,7 +372,7 @@ static void perform_bone(const xmlelement& root, ModelBuilder& builder, Context&
|
|||
}
|
||||
builder.pop();
|
||||
} 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++;
|
||||
|
||||
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("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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ AssetsManagement::AssetsManagement(Engine& engine)
|
|||
|
||||
AssetsManagement::~AssetsManagement() {
|
||||
finishBackgroundLoader();
|
||||
assets.reset();
|
||||
assetsVault.clearKeepedObjects();
|
||||
}
|
||||
|
||||
const Assets* AssetsManagement::getStorage() const {
|
||||
|
|
|
|||
|
|
@ -10,17 +10,19 @@
|
|||
#include "logic/LevelController.hpp"
|
||||
#include "logic/PlayerController.hpp"
|
||||
#include "objects/Player.hpp"
|
||||
#include "physics/Hitbox.hpp"
|
||||
#include "voxels/Block.hpp"
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "world/Level.hpp"
|
||||
#include "engine/Engine.hpp"
|
||||
|
||||
LevelFrontend::LevelFrontend(
|
||||
Engine& engine,
|
||||
Player* currentPlayer,
|
||||
LevelController* controller,
|
||||
PlayerController& playerController,
|
||||
LevelController& controller,
|
||||
const EngineSettings& settings
|
||||
)
|
||||
: level(*controller->getLevel()),
|
||||
: level(*controller.getLevel()),
|
||||
controller(controller),
|
||||
assets(*engine.getAssets()),
|
||||
contentCache(std::make_unique<ContentGfxCache>(
|
||||
|
|
@ -36,45 +38,79 @@ LevelFrontend::LevelFrontend(
|
|||
"block-previews"
|
||||
);
|
||||
|
||||
auto& rassets = assets;
|
||||
controller->getBlocksController()->listenBlockInteraction(
|
||||
[currentPlayer, controller, &rassets](auto player, const auto& pos, const auto& def, BlockInteraction type) {
|
||||
const auto& level = *controller->getLevel();
|
||||
auto& currentPlayer = playerController.getPlayer();
|
||||
auto& assets = this->assets;
|
||||
auto& level = this->level;
|
||||
|
||||
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);
|
||||
if (material == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
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 {
|
||||
if (type != BlockInteraction::step) {
|
||||
audio::Sound* sound = nullptr;
|
||||
switch (type) {
|
||||
case BlockInteraction::placing:
|
||||
sound = rassets.get<audio::Sound>(material->placeSound);
|
||||
sound = assets.get<audio::Sound>(material->placeSound);
|
||||
break;
|
||||
case BlockInteraction::destruction:
|
||||
sound = rassets.get<audio::Sound>(material->breakSound);
|
||||
sound = assets.get<audio::Sound>(material->breakSound);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -108,6 +144,6 @@ const ContentGfxCache& LevelFrontend::getContentGfxCache() const {
|
|||
return *contentCache;
|
||||
}
|
||||
|
||||
LevelController* LevelFrontend::getController() const {
|
||||
LevelController& LevelFrontend::getController() const {
|
||||
return controller;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,24 +2,24 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
class Level;
|
||||
class Assets;
|
||||
class Player;
|
||||
class Engine;
|
||||
class ContentGfxCache;
|
||||
class Engine;
|
||||
class Level;
|
||||
class LevelController;
|
||||
class PlayerController;
|
||||
struct EngineSettings;
|
||||
|
||||
class LevelFrontend {
|
||||
Level& level;
|
||||
LevelController* controller;
|
||||
LevelController& controller;
|
||||
Assets& assets;
|
||||
std::unique_ptr<ContentGfxCache> contentCache;
|
||||
public:
|
||||
LevelFrontend(
|
||||
Engine& engine,
|
||||
Player* currentPlayer,
|
||||
LevelController* controller,
|
||||
PlayerController& currentPlayer,
|
||||
LevelController& controller,
|
||||
const EngineSettings& settings
|
||||
);
|
||||
~LevelFrontend();
|
||||
|
|
@ -27,5 +27,5 @@ public:
|
|||
Level& getLevel();
|
||||
const ContentGfxCache& getContentGfxCache() const;
|
||||
ContentGfxCache& getContentGfxCache();
|
||||
LevelController* getController() const;
|
||||
LevelController& getController() const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ void Hud::updateWorldGenDebug() {
|
|||
const auto& chunks = *player.chunks;
|
||||
uint padding = engine.getSettings().chunks.padding.get();
|
||||
auto generator =
|
||||
frontend.getController()->getChunksController()->getGenerator();
|
||||
frontend.getController().getChunksController()->getGenerator();
|
||||
auto debugInfo = generator->createDebugInfo();
|
||||
|
||||
int width = debugImgWorldGen->getWidth();
|
||||
|
|
@ -294,7 +294,7 @@ void Hud::updateWorldGenDebug() {
|
|||
|
||||
bool isInLoadingZone =
|
||||
frontend.getController()
|
||||
->getChunksController()
|
||||
.getChunksController()
|
||||
->isInLoadingZone(player, padding, ax + ox, az + oz);
|
||||
|
||||
data[(flippedZ * width + x) * 4 + 1] =
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ LevelScreen::LevelScreen(
|
|||
);
|
||||
|
||||
frontend = std::make_unique<LevelFrontend>(
|
||||
engine, player, controller.get(), settings
|
||||
engine, *playerController, *controller, settings
|
||||
);
|
||||
renderer = std::make_unique<WorldRenderer>(
|
||||
engine, *frontend, *player
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
class Engine;
|
||||
class World;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,9 @@ namespace {
|
|||
const float CROUCH_SHIFT_Y = -0.2f;
|
||||
}
|
||||
|
||||
CameraControl::CameraControl(Player& player, const CameraSettings& settings)
|
||||
CameraControl::CameraControl(
|
||||
Player& player, const CameraSettings& settings
|
||||
)
|
||||
: player(player),
|
||||
camera(player.fpCamera),
|
||||
settings(settings),
|
||||
|
|
@ -195,18 +197,30 @@ void CameraControl::update(
|
|||
|
||||
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();
|
||||
if (player.currentCamera == spCamera) {
|
||||
spCamera->position =
|
||||
chunks.rayCastToObstacle(camera->position, camera->front, 3.0f) -
|
||||
0.4f * camera->front;
|
||||
castRay(player, *camera, camera->front) - 0.4f * camera->front;
|
||||
spCamera->dir = -camera->dir;
|
||||
spCamera->front = -camera->front;
|
||||
spCamera->right = -camera->right;
|
||||
} else if (player.currentCamera == tpCamera) {
|
||||
tpCamera->position =
|
||||
chunks.rayCastToObstacle(camera->position, -camera->front, 3.0f) +
|
||||
0.4f * camera->front;
|
||||
tpCamera->position = castRay(player, *camera, camera->front * -1.0f) +
|
||||
0.4f * camera->front;
|
||||
tpCamera->dir = camera->dir;
|
||||
tpCamera->front = camera->front;
|
||||
tpCamera->right = camera->right;
|
||||
|
|
@ -229,26 +243,8 @@ PlayerController::PlayerController(
|
|||
}
|
||||
|
||||
void PlayerController::onFootstep(const Hitbox& hitbox) {
|
||||
auto pos = hitbox.position;
|
||||
auto half = hitbox.halfsize;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (footstepCallback) {
|
||||
footstepCallback(hitbox);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -557,3 +553,7 @@ void PlayerController::updateInteraction(const Input& inputEvents, float delta)
|
|||
Player& PlayerController::getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
void PlayerController::setFootstepCallback(FootstepCallback&& callback) {
|
||||
footstepCallback = std::move(callback);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ struct Hitbox;
|
|||
struct CameraSettings;
|
||||
struct EngineSettings;
|
||||
|
||||
using FootstepCallback = std::function<void(const Hitbox&)>;
|
||||
|
||||
class CameraControl {
|
||||
Player& player;
|
||||
std::shared_ptr<Camera> camera;
|
||||
|
|
@ -57,6 +59,7 @@ class PlayerController {
|
|||
CameraControl camControl;
|
||||
BlocksController& blocksController;
|
||||
float interactionTimer = 0.0f;
|
||||
FootstepCallback footstepCallback;
|
||||
|
||||
void updateKeyboard(const Input& inputEvents);
|
||||
void resetKeyboard();
|
||||
|
|
@ -89,5 +92,8 @@ public:
|
|||
void postUpdate(
|
||||
float delta, int windowHeight, const Input* inputEvents, bool pause
|
||||
);
|
||||
|
||||
Player& getPlayer();
|
||||
|
||||
void setFootstepCallback(FootstepCallback&& callback);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -150,6 +150,48 @@ static int l_set_linear_damping(lua::State* L) {
|
|||
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[] = {
|
||||
{"is_enabled", lua::wrap<l_is_enabled>},
|
||||
{"set_enabled", lua::wrap<l_set_enabled>},
|
||||
|
|
@ -169,5 +211,11 @@ const luaL_Reg rigidbodylib[] = {
|
|||
{"set_crouching", lua::wrap<l_set_crouching>},
|
||||
{"get_body_type", lua::wrap<l_get_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}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ static int l_set_pos(lua::State* L) {
|
|||
auto vec = lua::tovec3(L, 2);
|
||||
check_valid(vec);
|
||||
entity->getTransform().setPos(vec);
|
||||
entity->getRigidbody().hitbox.position = vec;
|
||||
entity->getRigidbody().hitbox.setPos(vec);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,22 @@
|
|||
|
||||
using namespace scripting;
|
||||
|
||||
static inline const Block* get_block_def(lua::State* L) {
|
||||
auto indices = content->getIndices();
|
||||
static const Content& require_content() {
|
||||
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);
|
||||
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 z = lua::tointeger(L, 3);
|
||||
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) {
|
||||
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) {
|
||||
|
|
@ -76,7 +90,7 @@ static int l_is_segment(lua::State* L) {
|
|||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -84,10 +98,11 @@ static int l_seek_origin(lua::State* L) {
|
|||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
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);
|
||||
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 state = lua::tointeger(L, 5);
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -123,7 +140,7 @@ static int l_get(lua::State* L) {
|
|||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
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;
|
||||
return lua::pushinteger(L, id);
|
||||
}
|
||||
|
|
@ -138,8 +155,9 @@ template<int n>
|
|||
static int get_axis(lua::State* L) {
|
||||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
auto& level = require_level();
|
||||
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);
|
||||
}
|
||||
auto z = lua::tointeger(L, 3);
|
||||
|
|
@ -147,11 +165,11 @@ static int get_axis(lua::State* L) {
|
|||
glm::ivec3 defAxis {};
|
||||
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) {
|
||||
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) {
|
||||
return lua::pushivec_stack(L, defAxis);
|
||||
} else {
|
||||
|
|
@ -175,7 +193,7 @@ static int l_get_rotation(lua::State* L) {
|
|||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
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;
|
||||
return lua::pushinteger(L, rotation);
|
||||
}
|
||||
|
|
@ -185,7 +203,7 @@ static int l_set_rotation(lua::State* L) {
|
|||
auto y = lua::tointeger(L, 2);
|
||||
auto z = lua::tointeger(L, 3);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -193,7 +211,7 @@ static int l_get_states(lua::State* L) {
|
|||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
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);
|
||||
return lua::pushinteger(L, states);
|
||||
}
|
||||
|
|
@ -208,7 +226,7 @@ static int l_set_states(lua::State* L) {
|
|||
}
|
||||
int cx = floordiv<CHUNK_W>(x);
|
||||
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) {
|
||||
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 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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
auto& chunks = *level->chunks;
|
||||
auto& level = require_level();
|
||||
auto& chunks = *level.chunks;
|
||||
|
||||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
auto z = lua::tointeger(L, 3);
|
||||
|
|
@ -255,7 +276,7 @@ static int l_get_variant(lua::State* L) {
|
|||
if (vox == nullptr) {
|
||||
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) {
|
||||
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) {
|
||||
auto& chunks = *level->chunks;
|
||||
auto& level = require_level();
|
||||
auto& chunks = *level.chunks;
|
||||
|
||||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
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 lz = z - cz * CHUNK_D;
|
||||
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) {
|
||||
auto origin = blocks_agent::seek_origin(chunks, {x, y, z}, def, vox->state);
|
||||
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) {
|
||||
auto& chunks = *level->chunks;
|
||||
auto& level = require_level();
|
||||
auto& chunks = *level.chunks;
|
||||
|
||||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
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 lz = z - cz * CHUNK_D;
|
||||
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) {
|
||||
return 0;
|
||||
|
|
@ -358,11 +383,12 @@ static int l_set_variant(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_is_replaceable_at(lua::State* L) {
|
||||
auto& level = require_level();
|
||||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
auto z = lua::tointeger(L, 3);
|
||||
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;
|
||||
}
|
||||
|
||||
static lua::Integer get_variant_index(lua::State* L, const Block* const block, int argumentIndex) {
|
||||
const auto variantIndex = lua::gettop(L) >= argumentIndex ? lua::tointeger(L, argumentIndex) : 0;
|
||||
const size_t variantsSize = block->variants->variants.size();
|
||||
static lua::Integer get_variant_index(
|
||||
lua::State* L, const Block& block, int argumentIndex
|
||||
) {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
static int l_get_textures(lua::State* L) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
const auto& textureFaces = (def->variants ? def->variants->variants[get_variant_index(L, def, 2)].textureFaces :
|
||||
def->defaults.textureFaces);
|
||||
const auto& textureFaces =
|
||||
(def->variants
|
||||
? def->variants->variants[get_variant_index(L, *def, 2)]
|
||||
.textureFaces
|
||||
: def->defaults.textureFaces);
|
||||
lua::createtable(L, 6, 0);
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
lua::pushstring(L, textureFaces[i]);
|
||||
|
|
@ -397,11 +431,13 @@ static int l_get_textures(lua::State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int l_model_name(lua::State* L) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
const auto& modelName = (def->variants ? def->variants->variants[get_variant_index(L, def, 2)].model.name :
|
||||
def->defaults.model.name);
|
||||
const auto& modelName =
|
||||
(def->variants
|
||||
? def->variants->variants[get_variant_index(L, *def, 2)]
|
||||
.model.name
|
||||
: def->defaults.model.name);
|
||||
if (modelName.empty()) {
|
||||
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) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
const BlockModelType modelType = (def->variants ? def->variants->variants[get_variant_index(L, def, 2)].model.type :
|
||||
def->defaults.model.type);
|
||||
const BlockModelType modelType =
|
||||
(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 0;
|
||||
|
|
@ -422,7 +461,8 @@ static int l_get_model(lua::State* L) {
|
|||
static int l_get_hitbox(lua::State* L) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
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) {
|
||||
rotation %= def->rotations.MAX_COUNT;
|
||||
} else {
|
||||
|
|
@ -456,25 +496,28 @@ static int l_get_picking_item(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 y = lua::tointeger(L, 2);
|
||||
auto z = lua::tointeger(L, 3);
|
||||
auto id = lua::tointeger(L, 4);
|
||||
auto state = lua::tointeger(L, 5);
|
||||
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;
|
||||
}
|
||||
if (!blocks_agent::get(*level->chunks, x, y, z)) {
|
||||
if (!blocks_agent::get(*level.chunks, x, y, z)) {
|
||||
return 0;
|
||||
}
|
||||
const auto def = level->content.getIndices()->blocks.get(id);
|
||||
const auto def = indices.blocks.get(id);
|
||||
if (def == nullptr) {
|
||||
throw std::runtime_error(
|
||||
"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(
|
||||
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) {
|
||||
auto& level = require_level();
|
||||
|
||||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
auto z = lua::tointeger(L, 3);
|
||||
auto playerid = lua::gettop(L) >= 4 ? lua::tointeger(L, 4) : -1;
|
||||
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
||||
auto playerid = lua::isnumber(L, 4) ? lua::tointeger(L, 4) : -1;
|
||||
|
||||
auto vox = blocks_agent::get(*level.chunks, x, y, z);
|
||||
if (vox == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
auto& def = level->content.getIndices()->blocks.require(vox->id);
|
||||
auto player = level->players->get(playerid);
|
||||
auto& def = level.content.getIndices()->blocks.require(vox->id);
|
||||
auto player = level.players->get(playerid);
|
||||
controller->getBlocksController()->breakBlock(player, def, x, y, z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_raycast(lua::State* L) {
|
||||
auto& level = require_level();
|
||||
|
||||
auto start = lua::tovec<3>(L, 1);
|
||||
auto dir = lua::tovec<3>(L, 2);
|
||||
auto maxDistance = lua::tonumber(L, 3);
|
||||
|
|
@ -526,7 +574,7 @@ static int l_raycast(lua::State* L) {
|
|||
glm::ivec3 normal;
|
||||
glm::ivec3 iend;
|
||||
if (auto voxel = blocks_agent::raycast(
|
||||
*level->chunks,
|
||||
*level.chunks,
|
||||
start,
|
||||
dir,
|
||||
maxDistance,
|
||||
|
|
@ -620,6 +668,8 @@ static int get_field(
|
|||
}
|
||||
|
||||
static int l_get_field(lua::State* L) {
|
||||
auto& level = require_level();
|
||||
|
||||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
auto z = lua::tointeger(L, 3);
|
||||
|
|
@ -628,9 +678,10 @@ static int l_get_field(lua::State* L) {
|
|||
if (lua::gettop(L) >= 5) {
|
||||
index = lua::tointeger(L, 5);
|
||||
}
|
||||
|
||||
auto cx = floordiv(x, CHUNK_W);
|
||||
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) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -689,6 +740,8 @@ static int set_field(
|
|||
}
|
||||
|
||||
static int l_set_field(lua::State* L) {
|
||||
auto& level = require_level();
|
||||
|
||||
auto x = lua::tointeger(L, 1);
|
||||
auto y = lua::tointeger(L, 2);
|
||||
auto z = lua::tointeger(L, 3);
|
||||
|
|
@ -698,11 +751,12 @@ static int l_set_field(lua::State* L) {
|
|||
if (lua::gettop(L) >= 6) {
|
||||
index = lua::tointeger(L, 6);
|
||||
}
|
||||
|
||||
auto cx = floordiv(x, CHUNK_W);
|
||||
auto cz = floordiv(z, CHUNK_D);
|
||||
auto lx = x - cx * 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) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,28 +7,42 @@
|
|||
|
||||
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&)>
|
||||
static int l_camera_getter(lua::State* L) {
|
||||
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)>
|
||||
static int l_camera_setter(lua::State* L) {
|
||||
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;
|
||||
}
|
||||
|
||||
static int l_index(lua::State* L) {
|
||||
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));
|
||||
}
|
||||
|
||||
static int l_name(lua::State* L) {
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
@ -89,7 +103,7 @@ static int getter_up(lua::State* L, const Camera& camera) {
|
|||
|
||||
static int l_look_at(lua::State* L) {
|
||||
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 matrix = glm::inverse(
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
int descriptor = lua::tointeger(L, 1);
|
||||
|
||||
|
|
@ -406,9 +441,9 @@ const luaL_Reg filelib[] = {
|
|||
{"__has_descriptor", lua::wrap<l_has_descriptor>},
|
||||
{"__read_descriptor", lua::wrap<l_read_descriptor>},
|
||||
{"__write_descriptor", lua::wrap<l_write_descriptor>},
|
||||
{"__seek_descriptor", lua::wrap<l_seek_descriptor>},
|
||||
{"__flush_descriptor", lua::wrap<l_flush_descriptor>},
|
||||
{"__close_descriptor", lua::wrap<l_close_descriptor>},
|
||||
{"__close_all_descriptors", lua::wrap<l_close_all_descriptors>},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,14 +8,28 @@
|
|||
using namespace scripting;
|
||||
|
||||
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) {
|
||||
if (id >= indices->items.count()) {
|
||||
if (id >= require_content().getIndices()->items.count()) {
|
||||
throw std::runtime_error("invalid item id");
|
||||
}
|
||||
}
|
||||
|
||||
Inventory& get_inventory(int64_t id) {
|
||||
auto inv = level->inventories->get(id);
|
||||
auto inv = require_level().inventories->get(id);
|
||||
if (inv == nullptr) {
|
||||
throw std::runtime_error("inventory not found: " + std::to_string(id));
|
||||
}
|
||||
|
|
@ -23,7 +37,7 @@ namespace {
|
|||
}
|
||||
|
||||
Inventory& get_inventory(int64_t id, int arg) {
|
||||
auto inv = level->inventories->get(id);
|
||||
auto inv = require_level().inventories->get(id);
|
||||
if (inv == nullptr) {
|
||||
throw std::runtime_error(
|
||||
"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) {
|
||||
auto invsize = lua::tointeger(L, 1);
|
||||
auto inv = level->inventories->create(invsize);
|
||||
auto inv = require_level().inventories->create(invsize);
|
||||
if (inv == nullptr) {
|
||||
return lua::pushinteger(L, 0);
|
||||
}
|
||||
|
|
@ -146,13 +160,13 @@ static int l_create(lua::State* L) {
|
|||
|
||||
static int l_remove(lua::State* L) {
|
||||
auto invid = lua::tointeger(L, 1);
|
||||
level->inventories->remove(invid);
|
||||
require_level().inventories->remove(invid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_clone(lua::State* L) {
|
||||
auto id = lua::tointeger(L, 1);
|
||||
auto clone = level->inventories->clone(id);
|
||||
auto clone = require_level().inventories->clone(id);
|
||||
if (clone == nullptr) {
|
||||
return lua::pushinteger(L, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
using namespace scripting;
|
||||
|
||||
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 id = lua::tointeger(L, idx);
|
||||
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) {
|
||||
uint argc = lua::check_argc(L, 1, 2);
|
||||
auto quat = lua::toquat(L, 1);
|
||||
|
|
@ -280,6 +294,7 @@ const luaL_Reg mat4lib[] = {
|
|||
{"determinant", lua::wrap<l_determinant>},
|
||||
{"decompose", lua::wrap<l_decompose>},
|
||||
{"look_at", lua::wrap<l_look_at>},
|
||||
{"perspective", lua::wrap<l_perspective>},
|
||||
{"from_quat", lua::wrap<l_from_quat>},
|
||||
{"tostring", lua::wrap<l_tostring>},
|
||||
{nullptr, nullptr}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,13 @@
|
|||
|
||||
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) {
|
||||
if (!lua::isnumber(L, idx)) {
|
||||
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) {
|
||||
|
|
@ -304,15 +311,18 @@ static int l_create(lua::State* L) {
|
|||
if (lua::gettop(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));
|
||||
return lua::pushinteger(L, player->getId());
|
||||
}
|
||||
|
||||
static int l_delete(lua::State* L) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -324,19 +334,21 @@ static int l_is_suspended(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_set_suspended(lua::State* L) {
|
||||
auto& level = require_level();
|
||||
if (lua::toboolean(L, 2)) {
|
||||
level->players->suspend(lua::tointeger(L, 1));
|
||||
level.players->suspend(lua::tointeger(L, 1));
|
||||
} else {
|
||||
level->players->resume(lua::tointeger(L, 1));
|
||||
level.players->resume(lua::tointeger(L, 1));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_all_in_radius(lua::State* L) {
|
||||
auto& level = require_level();
|
||||
auto center = lua::tovec3(L, 1);
|
||||
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);
|
||||
for (size_t i = 0; i < players.size(); i++) {
|
||||
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) {
|
||||
auto players = level->players->getAll();
|
||||
auto players = require_level().players->getAll();
|
||||
lua::createtable(L, players.size(), 0);
|
||||
for (size_t i = 0; i < players.size(); i++) {
|
||||
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) {
|
||||
auto& level = require_level();
|
||||
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());
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,8 +44,17 @@ static int l_tostring(lua::State* L) {
|
|||
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[] = {
|
||||
{"from_mat4", lua::wrap<l_from_mat4>},
|
||||
{"from_euler", lua::wrap<l_from_euler>},
|
||||
{"slerp", lua::wrap<l_slerp>},
|
||||
{"tostring", lua::wrap<l_tostring>},
|
||||
{nullptr, nullptr}
|
||||
|
|
|
|||
|
|
@ -25,11 +25,15 @@
|
|||
using namespace scripting;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
static WorldInfo& require_world_info() {
|
||||
static Level& require_level() {
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -38,15 +38,23 @@ struct AABB {
|
|||
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
|
||||
inline void scale(const glm::vec3 mul) {
|
||||
inline void scale(const glm::vec3& mul) {
|
||||
glm::vec3 center = (a + b) * 0.5f;
|
||||
a = (a - center) * mul + center;
|
||||
b = (b - center) * mul + center;
|
||||
}
|
||||
|
||||
/// @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 end = max();
|
||||
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));
|
||||
}
|
||||
|
||||
inline bool intersect(const AABB& aabb) {
|
||||
inline bool intersects(const AABB& aabb) const {
|
||||
return (
|
||||
a.x <= aabb.b.x && b.x >= aabb.a.x && a.y <= aabb.b.y &&
|
||||
b.y >= aabb.a.y && a.z <= aabb.b.z && b.z >= aabb.a.z
|
||||
a.x <= aabb.b.x && b.x >= aabb.a.x &&
|
||||
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 (
|
||||
a.x <= aabb.b.x + margin && b.x >= aabb.a.x - margin &&
|
||||
a.y <= aabb.b.y + margin && b.y >= aabb.a.y - margin &&
|
||||
|
|
|
|||
|
|
@ -1,28 +1,30 @@
|
|||
#define VC_ENABLE_REFLECTION
|
||||
#include "Entities.hpp"
|
||||
|
||||
#include <entt/entity/registry.hpp>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <sstream>
|
||||
|
||||
#include "assets/Assets.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "data/dv_util.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "engine/Engine.hpp"
|
||||
#include "Entity.hpp"
|
||||
#include "EntityDef.hpp"
|
||||
#include "graphics/commons/Model.hpp"
|
||||
#include "graphics/core/DrawContext.hpp"
|
||||
#include "graphics/core/LineBatch.hpp"
|
||||
#include "graphics/commons/Model.hpp"
|
||||
#include "graphics/render/ModelBatch.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
#include "maths/FrustumCulling.hpp"
|
||||
#include "maths/rays.hpp"
|
||||
#include "EntityDef.hpp"
|
||||
#include "Entity.hpp"
|
||||
#include "rigging.hpp"
|
||||
#include "maths/util.hpp"
|
||||
#include "physics/PhysicsSolver.hpp"
|
||||
#include "rigging.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");
|
||||
|
||||
Entities::Entities(Level& level)
|
||||
|
|
@ -79,7 +81,7 @@ entityid_t Entities::spawn(
|
|||
entities[id] = entity;
|
||||
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>(
|
||||
entity,
|
||||
position,
|
||||
|
|
@ -91,7 +93,7 @@ entityid_t Entities::spawn(
|
|||
auto& body = registry->emplace<Rigidbody>(
|
||||
entity,
|
||||
true,
|
||||
Hitbox {def.bodyType, position, def.hitbox * 0.5f},
|
||||
Hitbox {id, def.bodyType, position, def.hitbox * 0.5f},
|
||||
std::vector<Sensor> {}
|
||||
);
|
||||
body.initialize(def, id, *this);
|
||||
|
|
@ -161,7 +163,11 @@ void Entities::loadEntity(const dv::value& map, Entity entity) {
|
|||
}
|
||||
|
||||
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);
|
||||
auto view = registry->view<EntityId, Transform, Rigidbody>();
|
||||
|
|
@ -170,7 +176,7 @@ std::optional<Entities::RaycastResult> Entities::rayCast(
|
|||
glm::ivec3 foundNormal;
|
||||
|
||||
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;
|
||||
}
|
||||
auto& hitbox = body.hitbox;
|
||||
|
|
@ -278,13 +284,18 @@ void Entities::updateSensors(
|
|||
}
|
||||
|
||||
void Entities::preparePhysics(float delta) {
|
||||
auto& physics = *level.physics;
|
||||
auto& hitboxes = physics.getHitboxesWriteable();
|
||||
auto& solidHitboxes = physics.getSolidHitboxesWriteable();
|
||||
|
||||
if (sensorsTickClock.update(delta)) {
|
||||
auto part = sensorsTickClock.getPart();
|
||||
auto parts = sensorsTickClock.getParts();
|
||||
|
||||
auto& sensors = physics.getSensorsWriteable();
|
||||
sensors.clear();
|
||||
|
||||
auto view = registry->view<EntityId, Transform, Rigidbody>();
|
||||
auto physics = level.physics.get();
|
||||
std::vector<Sensor*> sensors;
|
||||
for (auto [entity, eid, transform, rigidbody] : view.each()) {
|
||||
if (!rigidbody.enabled) {
|
||||
continue;
|
||||
|
|
@ -294,7 +305,25 @@ void Entities::preparePhysics(float delta) {
|
|||
}
|
||||
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 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()) {
|
||||
if (!rigidbody.enabled || rigidbody.hitbox.type == BodyType::STATIC) {
|
||||
if (!rigidbody.enabled ||
|
||||
rigidbody.hitbox.type == BodyType::STATIC) {
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
transform.setPos(hitbox.position);
|
||||
if (hitbox.grounded && !grounded) {
|
||||
if (util::is_nan_or_inf(hitbox.position)) {
|
||||
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(
|
||||
*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));
|
||||
}
|
||||
}
|
||||
|
|
@ -450,7 +481,7 @@ void Entities::render(
|
|||
bool Entities::hasBlockingInside(AABB aabb) {
|
||||
auto view = registry->view<EntityId, Rigidbody>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,12 +88,14 @@ public:
|
|||
/// @param dir Ray direction normalized vector
|
||||
/// @param maxDistance Max ray length
|
||||
/// @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
|
||||
std::optional<RaycastResult> rayCast(
|
||||
glm::vec3 start,
|
||||
glm::vec3 dir,
|
||||
float maxDistance,
|
||||
entityid_t ignore = -1
|
||||
entityid_t ignore = -1,
|
||||
bool solidOnly = false
|
||||
);
|
||||
|
||||
void loadEntities(dv::value map);
|
||||
|
|
|
|||
|
|
@ -9,5 +9,9 @@ void EntityDef::cloneTo(EntityDef& dst) {
|
|||
dst.radialSensors = radialSensors;
|
||||
dst.skeletonName = skeletonName;
|
||||
dst.blocking = blocking;
|
||||
dst.solid = solid;
|
||||
dst.mass = mass;
|
||||
dst.elasticity = elasticity;
|
||||
dst.stepHeight = stepHeight;
|
||||
dst.save = save;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,15 @@ struct EntityDef {
|
|||
/// @brief Does entity prevent blocks setup
|
||||
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
|
||||
float stepHeight = 0.5f;
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ void Player::teleport(glm::vec3 position) {
|
|||
this->position = position;
|
||||
|
||||
if (auto entity = level.entities->get(eid)) {
|
||||
entity->getRigidbody().hitbox.position = position;
|
||||
entity->getRigidbody().hitbox.setPos(position);
|
||||
entity->getTransform().setPos(position);
|
||||
entity->setInterpolatedPosition(position);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ dv::value Rigidbody::serialize(bool saveVelocity, bool saveBodySettings) const {
|
|||
if (hitbox.crouching) {
|
||||
bodymap["crouch"] = hitbox.crouching;
|
||||
}
|
||||
bodymap["mass"] = mass;
|
||||
bodymap["elasticity"] = elasticity;
|
||||
}
|
||||
return bodymap;
|
||||
}
|
||||
|
|
@ -33,6 +35,8 @@ void Rigidbody::deserialize(const dv::value& root) {
|
|||
BodyTypeMeta.getItem(bodyTypeName, hitbox.type);
|
||||
root["crouch"].asBoolean(hitbox.crouching);
|
||||
root["damping"].asNumber(hitbox.linearDamping);
|
||||
root["mass"].asNumber(mass);
|
||||
root["elasticity"].asNumber(elasticity);
|
||||
}
|
||||
|
||||
template <void (*callback)(const Entity&, size_t, entityid_t)>
|
||||
|
|
@ -49,6 +53,8 @@ static sensorcallback create_sensor_callback(Entities& entities) {
|
|||
void Rigidbody::initialize(
|
||||
const EntityDef& def, entityid_t id, Entities& entities
|
||||
) {
|
||||
mass = def.mass;
|
||||
elasticity = def.elasticity;
|
||||
sensors.resize(def.radialSensors.size() + def.boxSensors.size());
|
||||
for (auto& [i, box] : def.boxSensors) {
|
||||
SensorParams params {};
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ struct Rigidbody {
|
|||
bool enabled = true;
|
||||
Hitbox hitbox;
|
||||
std::vector<Sensor> sensors;
|
||||
float mass;
|
||||
float elasticity;
|
||||
|
||||
dv::value serialize(bool saveVelocity, bool saveBodySettings) const;
|
||||
void deserialize(const dv::value& root);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
#include "Hitbox.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
Hitbox::Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize)
|
||||
: type(type),
|
||||
position(position),
|
||||
halfsize(halfsize),
|
||||
velocity(0.0f,0.0f,0.0f)
|
||||
{}
|
||||
Hitbox::Hitbox(
|
||||
entityid_t entity, BodyType type, glm::vec3 position, glm::vec3 halfsize
|
||||
)
|
||||
: entity(entity),
|
||||
type(type),
|
||||
position(position),
|
||||
halfsize(halfsize),
|
||||
velocity(0.0f, 0.0f, 0.0f),
|
||||
prevPosition(position) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
|
||||
#include "maths/aabb.hpp"
|
||||
#include "typedefs.hpp"
|
||||
#include "util/EnumMetadata.hpp"
|
||||
|
|
@ -8,6 +10,7 @@
|
|||
#include <string>
|
||||
#include <functional>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
enum class SensorType {
|
||||
AABB,
|
||||
|
|
@ -48,6 +51,7 @@ VC_ENUM_METADATA(BodyType)
|
|||
VC_ENUM_END
|
||||
|
||||
struct Hitbox {
|
||||
entityid_t entity;
|
||||
BodyType type;
|
||||
glm::vec3 position;
|
||||
glm::vec3 halfsize;
|
||||
|
|
@ -60,14 +64,34 @@ struct Hitbox {
|
|||
float gravityScale = 1.0f;
|
||||
bool crouching = false;
|
||||
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 {
|
||||
return AABB(position-halfsize, position+halfsize);
|
||||
return AABB(position - halfsize, position + halfsize);
|
||||
}
|
||||
|
||||
glm::vec3 getHalfSize() const {
|
||||
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/GlobalChunks.hpp"
|
||||
#include "voxels/voxel.hpp"
|
||||
#include "objects/Entities.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
inline const float E = 0.03f;
|
||||
inline const float MAX_FIX = 0.1f;
|
||||
inline constexpr float E = 0.03f;
|
||||
inline constexpr float MAX_FIX = 0.1f;
|
||||
|
||||
PhysicsSolver::PhysicsSolver(glm::vec3 gravity) : gravity(gravity) {}
|
||||
static debug::Logger logger("physics-solver");
|
||||
|
||||
void PhysicsSolver::step(
|
||||
const GlobalChunks& chunks,
|
||||
Hitbox& hitbox,
|
||||
float delta,
|
||||
uint substeps,
|
||||
entityid_t entity
|
||||
PhysicsSolver::PhysicsSolver(const GlobalChunks& chunks, glm::vec3 gravity)
|
||||
: chunks(chunks), gravity(std::move(gravity)) {
|
||||
}
|
||||
|
||||
static glm::vec3 calc_collsion_velocity_result(
|
||||
const Hitbox& a, const Hitbox& b
|
||||
) {
|
||||
float dt = delta / static_cast<float>(substeps);
|
||||
float linearDamping = hitbox.linearDamping * hitbox.friction;
|
||||
float s = 2.0f/BLOCK_AABB_GRID;
|
||||
|
||||
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;
|
||||
}
|
||||
const auto& vA = a.velocity;
|
||||
const auto& vB = b.velocity;
|
||||
if (glm::isinf(a.mass)) {
|
||||
return vA;
|
||||
}
|
||||
vel.x /= 1.0f + delta * linearDamping;
|
||||
vel.z /= 1.0f + delta * linearDamping;
|
||||
if (hitbox.verticalDamping > 0.0f) {
|
||||
vel.y /= 1.0f + delta * linearDamping * hitbox.verticalDamping;
|
||||
}
|
||||
|
||||
AABB aabb;
|
||||
aabb.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);
|
||||
}
|
||||
if (glm::isinf(b.mass)) {
|
||||
return vB - a.elasticity * (vA - vB);
|
||||
}
|
||||
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(
|
||||
const GlobalChunks& chunks,
|
||||
const glm::vec3& pos,
|
||||
const glm::vec3& half,
|
||||
float stepHeight,
|
||||
float s
|
||||
float stepHeight
|
||||
) {
|
||||
if (stepHeight > 0.0f) {
|
||||
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 = (pos.z-half.z+E) + iz * s;
|
||||
if (chunks.isObstacleAt(x, pos.y+half.y+stepHeight, z)) {
|
||||
return 0.0f;
|
||||
}
|
||||
AABB aabb(-half, +half);
|
||||
aabb.scale(glm::vec3(1.0f - E * 2, 1.0f, 1.0f - E * 2));
|
||||
aabb = aabb + pos + glm::vec3(0.0f, stepHeight, 0.0f);
|
||||
if (stepHeight <= 0.0f) {
|
||||
return stepHeight;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
template <int nx, int ny, int nz>
|
||||
static bool calc_collision_neg(
|
||||
// todo: reduce code duplication
|
||||
|
||||
template <int nx, int ny, int nz, int sign>
|
||||
static void calc_collision(
|
||||
Hitbox& hitbox,
|
||||
const GlobalChunks& chunks,
|
||||
glm::vec3& pos,
|
||||
glm::vec3& vel,
|
||||
const std::vector<Hitbox*>& solidHitboxes,
|
||||
const glm::vec3& half,
|
||||
float stepHeight,
|
||||
float s
|
||||
float stepHeight
|
||||
) {
|
||||
if (vel[nx] >= 0.0f) {
|
||||
return false;
|
||||
}
|
||||
glm::vec3 offset(0.0f, stepHeight, 0.0f);
|
||||
for (int iy = 0; iy <= std::max<int>(1, ((half-offset*0.5f)[ny]-E)*2/s); iy++) {
|
||||
glm::vec3 coord;
|
||||
coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy * s;
|
||||
for (int iz = 0; iz <= (half[nz]-E)*2/s; iz++){
|
||||
coord[nz] = (pos[nz]-half[nz]+E) + iz * s;
|
||||
coord[nx] = (pos[nx]-half[nx]-E);
|
||||
if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z)) {
|
||||
vel[nx] = 0.0f;
|
||||
float newx = std::floor(coord[nx]) + half[nx] + aabb->max()[nx] + E;
|
||||
if (newx - pos[nx] <= E) {
|
||||
pos[nx] = newx;
|
||||
}
|
||||
return true;
|
||||
auto& pos = hitbox.position;
|
||||
auto& vel = hitbox.velocity;
|
||||
|
||||
glm::vec3 offset(0.0f, stepHeight + E, 0.0f);
|
||||
for (auto box : solidHitboxes) {
|
||||
if (glm::distance2(box->position, pos) < E) {
|
||||
continue;
|
||||
}
|
||||
auto boxhalf = box->getHalfSize();
|
||||
auto aabb = AABB(pos - half, pos + half);
|
||||
glm::vec3 scale(1.0f);
|
||||
scale[nz] = 1.0f - E * 8.0f;
|
||||
scale[ny] = 1.0f - E * 2.0f;
|
||||
aabb.scale(scale);
|
||||
aabb = aabb + offset;
|
||||
aabb.b.y -= stepHeight + E * 2;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (vel[nx] * sign <= 0.0f && hitbox.groundVelocity[nx] * sign <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
glm::vec3 offset(0.0f, stepHeight, 0.0f);
|
||||
for (int iy = 0; iy <= std::max<int>(1, ((half-offset*0.5f)[ny]-E)*2/s); iy++) {
|
||||
for (int iy = 0; iy <= glm::ceil(((half - offset * 0.5f)[ny] - E) * 2); iy++) {
|
||||
glm::vec3 coord;
|
||||
coord[ny] = ((pos+offset)[ny]-half[ny]+E) + iy * s;
|
||||
for (int iz = 0; iz <= (half[nz]-E)*2/s; iz++) {
|
||||
coord[nz] = (pos[nz]-half[nz]+E) + iz * s;
|
||||
coord[nx] = (pos[nx]+half[nx]+E);
|
||||
if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z)) {
|
||||
vel[nx] = 0.0f;
|
||||
float newx = std::floor(coord[nx]) - half[nx] + aabb->min()[nx] - E;
|
||||
if (newx - pos[nx] <= E) {
|
||||
coord[ny] = ((pos + offset)[ny] - half[ny] + E) + iy;
|
||||
for (int iz = 0; iz <= glm::ceil((half[nz] - E) * 2); iz++) {
|
||||
coord[nz] = (pos[nz] - half[nz] + E) + iz;
|
||||
coord[nx] = pos[nx] + (half[nx] + E * 2) * sign;
|
||||
|
||||
auto aabb = AABB(pos - half, pos + half);
|
||||
glm::vec3 scale(1.0f);
|
||||
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;
|
||||
}
|
||||
return;
|
||||
|
|
@ -204,93 +140,314 @@ static void calc_collision_pos(
|
|||
}
|
||||
}
|
||||
|
||||
void PhysicsSolver::colisionCalc(
|
||||
const GlobalChunks& chunks,
|
||||
Hitbox& hitbox,
|
||||
glm::vec3& vel,
|
||||
glm::vec3& pos,
|
||||
const glm::vec3 half,
|
||||
float stepHeight
|
||||
bool PhysicsSolver::calcCollisionNegY(
|
||||
Hitbox& hitbox, const glm::vec3& half, float dt
|
||||
) {
|
||||
// step size (smaller - more accurate, but slower) // TODO: GET RID OF THIS
|
||||
float s = 2.0f/BLOCK_AABB_GRID;
|
||||
auto& pos = hitbox.position;
|
||||
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;
|
||||
|
||||
calc_collision_neg<0, 1, 2>(chunks, pos, vel, half, stepHeight, s);
|
||||
calc_collision_pos<0, 1, 2>(chunks, pos, vel, half, stepHeight, s);
|
||||
auto boxhalf = box->getHalfSize();
|
||||
if (box->position.y < pos.y && box->getAABB().intersects(aabb)) {
|
||||
float newy = box->position.y + boxhalf.y + half.y;
|
||||
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);
|
||||
calc_collision_pos<2, 1, 0>(chunks, pos, vel, half, stepHeight, s);
|
||||
auto velA = calc_collsion_velocity_result(hitbox, *box);
|
||||
auto velB = calc_collsion_velocity_result(*box, hitbox);
|
||||
|
||||
if (calc_collision_neg<1, 0, 2>(chunks, pos, vel, half, 0.0f, s)) {
|
||||
hitbox.grounded = true;
|
||||
}
|
||||
|
||||
if (stepHeight > 0.0 && vel.y <= 0.0f){
|
||||
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 = (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;
|
||||
hitbox.groundVelocity = box->position - box->prevPosition;
|
||||
if (vel.y < hitbox.groundVelocity.y / dt) {
|
||||
vel.y = velA.y;
|
||||
box->velocity.y = velB.y;
|
||||
if (hitbox.groundMaterial.empty() && !box->material.empty()) {
|
||||
hitbox.groundMaterial = box->material;
|
||||
}
|
||||
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){
|
||||
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 = (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;
|
||||
AABB boxAABB = AABB(-half, +half);
|
||||
boxAABB.scale(glm::vec3(1.0f - E * 4, 1.0f + E * 2, 1.0f - E * 4));
|
||||
boxAABB = boxAABB.translated(pos);
|
||||
|
||||
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;
|
||||
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;
|
||||
if (std::abs(newy-pos.y) <= MAX_FIX) {
|
||||
if (pos.y >= newy) {
|
||||
vel.y = -hitbox.elasticity * vel.y;
|
||||
pos.y = newy;
|
||||
}
|
||||
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) {
|
||||
const glm::vec3& pos = hitbox->position;
|
||||
auto half = hitbox->getHalfSize();
|
||||
return x >= floor(pos.x-half.x) && x <= floor(pos.x+half.x) &&
|
||||
z >= floor(pos.z-half.z) && z <= floor(pos.z+half.z) &&
|
||||
y >= floor(pos.y-half.y) && y <= floor(pos.y+half.y);
|
||||
void PhysicsSolver::calcSubstep(
|
||||
Hitbox& hitbox, glm::vec3& vel, glm::vec3& pos, float dt
|
||||
) {
|
||||
auto initpos = pos;
|
||||
auto half = hitbox.getHalfSize();
|
||||
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) {
|
||||
const float e = 0.001f; // inaccuracy
|
||||
const glm::vec3& pos = hitbox->position;
|
||||
auto half = hitbox->getHalfSize();
|
||||
const auto& boxes = def->rotatable
|
||||
? def->rt.hitboxes[state.rotation]
|
||||
: def->hitboxes;
|
||||
for (const auto& block_hitbox : boxes) {
|
||||
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 &&
|
||||
min.z < pos.z+half.z-z-e && max.z > pos.z-half.z-z+e &&
|
||||
min.y < pos.y+half.y-y-e && max.y > pos.y-half.y-y+e)
|
||||
return true;
|
||||
void PhysicsSolver::step(
|
||||
const GlobalChunks& chunks, float delta, uint substeps
|
||||
) {
|
||||
for (auto hitbox : hitboxes) {
|
||||
hitbox->groundMaterial.clear();
|
||||
hitbox->prevGrounded = hitbox->grounded;
|
||||
hitbox->grounded = false;
|
||||
hitbox->prevVelocity = hitbox->velocity;
|
||||
}
|
||||
|
||||
float dt = delta / static_cast<float>(substeps);
|
||||
for (uint i = 0; i < substeps; i++) {
|
||||
for (auto hitbox : hitboxes) {
|
||||
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) {
|
||||
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>
|
||||
|
||||
class Block;
|
||||
class Entities;
|
||||
class GlobalChunks;
|
||||
struct Sensor;
|
||||
|
||||
class PhysicsSolver {
|
||||
glm::vec3 gravity;
|
||||
std::vector<Sensor*> sensors;
|
||||
public:
|
||||
PhysicsSolver(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);
|
||||
PhysicsSolver(const GlobalChunks& chunks, glm::vec3 gravity);
|
||||
|
||||
void setSensors(std::vector<Sensor*> sensors) {
|
||||
this->sensors = std::move(sensors);
|
||||
void step(const GlobalChunks& chunks, float delta, uint substeps);
|
||||
|
||||
auto& getSensorsWriteable() {
|
||||
return sensors;
|
||||
}
|
||||
|
||||
auto& getSolidHitboxesWriteable() {
|
||||
return solidHitboxes;
|
||||
}
|
||||
|
||||
auto& getHitboxesWriteable() {
|
||||
return hitboxes;
|
||||
}
|
||||
|
||||
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 <math.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "data/StructLayout.hpp"
|
||||
#include "coders/byte_utils.hpp"
|
||||
#include "content/Content.hpp"
|
||||
|
|
@ -20,6 +14,11 @@
|
|||
#include "VoxelsVolume.hpp"
|
||||
#include "blocks_agent.hpp"
|
||||
|
||||
#include <math.h>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
Chunks::Chunks(
|
||||
int32_t w,
|
||||
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);
|
||||
if (def.obstacle) {
|
||||
glm::ivec3 offset {};
|
||||
if (v->state.segment) {
|
||||
glm::ivec3 point(ix, iy, iz);
|
||||
offset = seekOrigin(point, def, v->state) - point;
|
||||
}
|
||||
const auto& boxes =
|
||||
def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes;
|
||||
for (const auto& hitbox : boxes) {
|
||||
if (hitbox.contains(
|
||||
{x - ix - offset.x, y - iy - offset.y, z - iz - offset.z}
|
||||
)) {
|
||||
return &hitbox;
|
||||
}
|
||||
if (!def.obstacle) {
|
||||
return nullptr;
|
||||
}
|
||||
glm::ivec3 offset {};
|
||||
if (v->state.segment) {
|
||||
glm::ivec3 point(ix, iy, iz);
|
||||
offset = seekOrigin(point, def, v->state) - point;
|
||||
}
|
||||
const auto& boxes =
|
||||
def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes;
|
||||
for (const auto& hitbox : boxes) {
|
||||
if (hitbox.contains(
|
||||
{x - ix - offset.x, y - iy - offset.y, z - iz - offset.z}
|
||||
)) {
|
||||
return &hitbox;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "typedefs.hpp"
|
||||
#include "voxel.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "util/AreaMap2D.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
class VoxelRenderer;
|
||||
|
||||
struct AABB;
|
||||
|
|
|
|||
|
|
@ -214,6 +214,6 @@ void GlobalChunks::putChunk(std::shared_ptr<Chunk> chunk) {
|
|||
chunksMap[keyfrom(chunk->x, chunk->z)] = std::move(chunk);
|
||||
}
|
||||
|
||||
const AABB* GlobalChunks::isObstacleAt(float x, float y, float z) const {
|
||||
return blocks_agent::is_obstacle_at(*this, x, y, z);
|
||||
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, aabb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
|
|
@ -57,7 +58,7 @@ public:
|
|||
|
||||
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 {
|
||||
const auto& found = chunksMap.find(keyfrom(cx, cz));
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
#include "voxel.hpp"
|
||||
#include "VoxelsVolume.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <algorithm>
|
||||
#include <glm/glm.hpp>
|
||||
#include <set>
|
||||
|
|
@ -468,8 +467,8 @@ void get_voxels(
|
|||
);
|
||||
|
||||
template <class Storage>
|
||||
inline const AABB* is_obstacle_at(
|
||||
const Storage& chunks, float x, float y, float z
|
||||
inline std::optional<AABB> is_obstacle_at(
|
||||
const Storage& chunks, float x, float y, float z, const AABB& aabb
|
||||
) {
|
||||
int ix = std::floor(x);
|
||||
int iy = std::floor(y);
|
||||
|
|
@ -477,30 +476,34 @@ inline const AABB* is_obstacle_at(
|
|||
voxel* v = get(chunks, ix, iy, iz);
|
||||
if (v == nullptr) {
|
||||
if (iy >= CHUNK_H) {
|
||||
return nullptr;
|
||||
return std::nullopt;
|
||||
} else {
|
||||
static const AABB empty;
|
||||
return ∅
|
||||
return AABB();
|
||||
}
|
||||
}
|
||||
const auto& def = chunks.getContentIndices().blocks.require(v->id);
|
||||
if (def.obstacle) {
|
||||
glm::ivec3 offset {};
|
||||
if (v->state.segment) {
|
||||
glm::ivec3 point(ix, iy, iz);
|
||||
offset = seek_origin(chunks, point, def, v->state) - point;
|
||||
}
|
||||
const auto& boxes =
|
||||
def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes;
|
||||
for (const auto& hitbox : boxes) {
|
||||
if (hitbox.contains(
|
||||
{x - ix - offset.x, y - iy - offset.y, z - iz - offset.z}
|
||||
)) {
|
||||
return &hitbox;
|
||||
}
|
||||
if (!def.obstacle) {
|
||||
return std::nullopt;
|
||||
}
|
||||
glm::ivec3 offset {};
|
||||
if (v->state.segment) {
|
||||
glm::ivec3 point(ix, iy, iz);
|
||||
offset = seek_origin(chunks, point, def, v->state) - point;
|
||||
}
|
||||
const auto& boxes =
|
||||
def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes;
|
||||
|
||||
for (const auto& hitbox : boxes) {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ Level::Level(
|
|||
: world(std::move(worldPtr)),
|
||||
content(content),
|
||||
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>()),
|
||||
entities(std::make_unique<Entities>(*this)),
|
||||
players(std::make_unique<Players>(*this)),
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ void WorldGenerator::placeStructure(
|
|||
}
|
||||
auto& otherPrototype = *found->second;
|
||||
auto chunkAABB = gen_chunk_aabb(chunkX + lcx, chunkZ + lcz);
|
||||
if (chunkAABB.intersect(aabb)) {
|
||||
if (chunkAABB.intersects(aabb)) {
|
||||
otherPrototype.placements.emplace_back(
|
||||
priority,
|
||||
StructurePlacement {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue