diff --git a/doc/en/entity-properties.md b/doc/en/entity-properties.md
index a6530191f..bb12dcd86 100644
--- a/doc/en/entity-properties.md
+++ b/doc/en/entity-properties.md
@@ -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*
diff --git a/doc/en/scripting/builtins/libgfx-weather.md b/doc/en/scripting/builtins/libgfx-weather.md
index 62d8ec33b..669ebc5ea 100644
--- a/doc/en/scripting/builtins/libgfx-weather.md
+++ b/doc/en/scripting/builtins/libgfx-weather.md
@@ -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:
diff --git a/doc/en/scripting/builtins/libpack.md b/doc/en/scripting/builtins/libpack.md
index 2daab1269..651a030d2 100644
--- a/doc/en/scripting/builtins/libpack.md
+++ b/doc/en/scripting/builtins/libpack.md
@@ -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
diff --git a/doc/en/scripting/ecs.md b/doc/en/scripting/ecs.md
index 7da294c22..e0c6b69c6 100644
--- a/doc/en/scripting/ecs.md
+++ b/doc/en/scripting/ecs.md
@@ -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
diff --git a/doc/en/xml-ui-layouts.md b/doc/en/xml-ui-layouts.md
index 97b36d1f6..0bd1b5353 100644
--- a/doc/en/xml-ui-layouts.md
+++ b/doc/en/xml-ui-layouts.md
@@ -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 LMB + Shift. 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 RMB 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
diff --git a/doc/ru/entity-properties.md b/doc/ru/entity-properties.md
index 3489e3aae..50b3b1499 100644
--- a/doc/ru/entity-properties.md
+++ b/doc/ru/entity-properties.md
@@ -95,6 +95,23 @@
0. Прямоугольная область шириной, высотой и длиной в 0.4 м. с центром в 0.0.
1. Радиальная область с радиусом 1.6 м.
+### Осязаемость - *solid*
+
+Делает хитбокс сущности осязаемым препятствием. Вместе с включением данного свойства у `dynamic` тел следует указать массу (mass).
+
+### Масса - *mass*
+
+Определяет массу сущности. За стандарт приняты килограммы.
+В случае `kinematic` тела вместо указанной используется бесконечная масса.
+
+### Эластичность - *elasticity*
+
+Определяет упругость сущности.
+
+### Высота шага - *step-height*
+
+Определяет максимальную высоту препятствия, на которое сущность может шагнуть без прыжка.
+
## Вид
### Имя скелета - *skeleton-name*
diff --git a/doc/ru/scripting/builtins/libgfx-weather.md b/doc/ru/scripting/builtins/libgfx-weather.md
index add94550b..b51bd58e1 100644
--- a/doc/ru/scripting/builtins/libgfx-weather.md
+++ b/doc/ru/scripting/builtins/libgfx-weather.md
@@ -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
-```
\ No newline at end of file
+```
diff --git a/doc/ru/scripting/builtins/libmat4.md b/doc/ru/scripting/builtins/libmat4.md
index 1531798a4..1b44df1ee 100644
--- a/doc/ru/scripting/builtins/libmat4.md
+++ b/doc/ru/scripting/builtins/libmat4.md
@@ -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
diff --git a/doc/ru/scripting/builtins/libpack.md b/doc/ru/scripting/builtins/libpack.md
index 0c4aac0c1..925cad7f8 100644
--- a/doc/ru/scripting/builtins/libpack.md
+++ b/doc/ru/scripting/builtins/libpack.md
@@ -82,6 +82,7 @@ pack.get_info(packid: string) -> {
- `!` - required
- `?` - optional
- `~` - weak
+
например `!teal`
Для получения информации о нескольких паках используйте таблицу id, чтобы не
@@ -101,4 +102,4 @@ pack.assemble(packids: table) -> table
pack.request_writeable(packid: string, callback: function(string))
```
-Запрашивает у пользователя право на модификацию пака. При подтвержении новая точка входа будет передана в callback.
\ No newline at end of file
+Запрашивает у пользователя право на модификацию пака. При подтвержении новая точка входа будет передана в callback.
diff --git a/doc/ru/scripting/builtins/libquat.md b/doc/ru/scripting/builtins/libquat.md
index 5a6495658..e991de252 100644
--- a/doc/ru/scripting/builtins/libquat.md
+++ b/doc/ru/scripting/builtins/libquat.md
@@ -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
```
diff --git a/doc/ru/scripting/ecs.md b/doc/ru/scripting/ecs.md
index 6c1b041d7..50735913a 100644
--- a/doc/ru/scripting/ecs.md
+++ b/doc/ru/scripting/ecs.md
@@ -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
diff --git a/doc/ru/scripting/io_stream.md b/doc/ru/scripting/io_stream.md
index 283e0c48b..cc3403eda 100644
--- a/doc/ru/scripting/io_stream.md
+++ b/doc/ru/scripting/io_stream.md
@@ -137,6 +137,18 @@ io_stream:write_line(string)
io_stream:read_fully(
[опционально] useTable: boolean
) -> Bytearray | table | table | string
+
+--[[
+Устанавливает позицию в потоке
+Режимы:
+ b - Задаёт позицию относительно начало файла
+ c - Задаёт позицию относительно текущей позиции
+ e - Задаёт позицию относительно конца файла
+--]]
+io_stream:seek(
+ mode: string
+ offset: number
+)
```
## Методы Buffered-режима
diff --git a/doc/ru/xml-ui-layouts.md b/doc/ru/xml-ui-layouts.md
index aeb669b3f..2ca6da1ce 100644
--- a/doc/ru/xml-ui-layouts.md
+++ b/doc/ru/xml-ui-layouts.md
@@ -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` - общее число слотов. Тип: число.
diff --git a/res/content/base/entities/falling_block.json b/res/content/base/entities/falling_block.json
index cf5146fd9..b466c2a11 100644
--- a/res/content/base/entities/falling_block.json
+++ b/res/content/base/entities/falling_block.json
@@ -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
}
diff --git a/res/content/base/scripts/components/falling_block.lua b/res/content/base/scripts/components/falling_block.lua
index 483e86aee..b6bbe4656 100644
--- a/res/content/base/scripts/components/falling_block.lua
+++ b/res/content/base/scripts/components/falling_block.lua
@@ -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()
diff --git a/res/modules/internal/stdcomp.lua b/res/modules/internal/stdcomp.lua
index 7cedd1887..d85c11963 100644
--- a/res/modules/internal/stdcomp.lua
+++ b/res/modules/internal/stdcomp.lua
@@ -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)
diff --git a/res/modules/internal/stream_providers/file.lua b/res/modules/internal/stream_providers/file.lua
index 80bc8c5bb..ec81a42c0 100644
--- a/res/modules/internal/stream_providers/file.lua
+++ b/res/modules/internal/stream_providers/file.lua
@@ -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
diff --git a/res/modules/internal/stream_providers/named_pipe_unix.lua b/res/modules/internal/stream_providers/named_pipe_unix.lua
index 1843dabaa..8b924f47a 100644
--- a/res/modules/internal/stream_providers/named_pipe_unix.lua
+++ b/res/modules/internal/stream_providers/named_pipe_unix.lua
@@ -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
diff --git a/res/modules/internal/stream_providers/named_pipe_windows.lua b/res/modules/internal/stream_providers/named_pipe_windows.lua
index 83691aa5a..3d4b31af1 100644
--- a/res/modules/internal/stream_providers/named_pipe_windows.lua
+++ b/res/modules/internal/stream_providers/named_pipe_windows.lua
@@ -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
diff --git a/res/modules/io_stream.lua b/res/modules/io_stream.lua
index 15ba85d12..b608a22a9 100644
--- a/res/modules/io_stream.lua
+++ b/res/modules/io_stream.lua
@@ -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
diff --git a/res/scripts/components/player.lua b/res/scripts/components/player.lua
index 66fdda825..840bce743 100644
--- a/res/scripts/components/player.lua
+++ b/res/scripts/components/player.lua
@@ -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
diff --git a/src/coders/vcm.cpp b/src/coders/vcm.cpp
index 0f408131d..738d49c09 100644
--- a/src/coders/vcm.cpp
+++ b/src/coders/vcm.cpp
@@ -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> bones;
diff --git a/src/content/loading/EntityLoader.cpp b/src/content/loading/EntityLoader.cpp
index 12e5313a7..91967ea89 100644
--- a/src/content/loading/EntityLoader.cpp
+++ b/src/content/loading/EntityLoader.cpp
@@ -88,5 +88,8 @@ template<> void ContentUnitLoader::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);
}
diff --git a/src/engine/AssetsManagement.cpp b/src/engine/AssetsManagement.cpp
index 1ca1935c9..13df11220 100644
--- a/src/engine/AssetsManagement.cpp
+++ b/src/engine/AssetsManagement.cpp
@@ -18,6 +18,8 @@ AssetsManagement::AssetsManagement(Engine& engine)
AssetsManagement::~AssetsManagement() {
finishBackgroundLoader();
+ assets.reset();
+ assetsVault.clearKeepedObjects();
}
const Assets* AssetsManagement::getStorage() const {
diff --git a/src/frontend/LevelFrontend.cpp b/src/frontend/LevelFrontend.cpp
index 9a8553da6..d70f09ba2 100644
--- a/src/frontend/LevelFrontend.cpp
+++ b/src/frontend/LevelFrontend.cpp
@@ -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(
@@ -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(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(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(material->placeSound);
+ sound = assets.get(material->placeSound);
break;
case BlockInteraction::destruction:
- sound = rassets.get(material->breakSound);
+ sound = assets.get(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;
}
diff --git a/src/frontend/LevelFrontend.hpp b/src/frontend/LevelFrontend.hpp
index cc4f996b2..451f88675 100644
--- a/src/frontend/LevelFrontend.hpp
+++ b/src/frontend/LevelFrontend.hpp
@@ -2,24 +2,24 @@
#include
-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 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;
};
diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp
index fee9ff192..4366d8c6e 100644
--- a/src/frontend/hud.cpp
+++ b/src/frontend/hud.cpp
@@ -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] =
diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp
index 13618d33a..737742afd 100644
--- a/src/frontend/screens/LevelScreen.cpp
+++ b/src/frontend/screens/LevelScreen.cpp
@@ -68,7 +68,7 @@ LevelScreen::LevelScreen(
);
frontend = std::make_unique(
- engine, player, controller.get(), settings
+ engine, *playerController, *controller, settings
);
renderer = std::make_unique(
engine, *frontend, *player
diff --git a/src/logic/EngineController.hpp b/src/logic/EngineController.hpp
index a46973cf3..baba7ba9e 100644
--- a/src/logic/EngineController.hpp
+++ b/src/logic/EngineController.hpp
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
class Engine;
class World;
diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp
index e707d1637..663ff1fa1 100644
--- a/src/logic/PlayerController.cpp
+++ b/src/logic/PlayerController.cpp
@@ -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);
+}
diff --git a/src/logic/PlayerController.hpp b/src/logic/PlayerController.hpp
index e730ff37c..5ec8e4b50 100644
--- a/src/logic/PlayerController.hpp
+++ b/src/logic/PlayerController.hpp
@@ -18,6 +18,8 @@ struct Hitbox;
struct CameraSettings;
struct EngineSettings;
+using FootstepCallback = std::function;
+
class CameraControl {
Player& player;
std::shared_ptr 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);
};
diff --git a/src/logic/scripting/lua/libs/lib__rigidbody.cpp b/src/logic/scripting/lua/libs/lib__rigidbody.cpp
index f32e9d986..52234f91e 100644
--- a/src/logic/scripting/lua/libs/lib__rigidbody.cpp
+++ b/src/logic/scripting/lua/libs/lib__rigidbody.cpp
@@ -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},
{"set_enabled", lua::wrap},
@@ -169,5 +211,11 @@ const luaL_Reg rigidbodylib[] = {
{"set_crouching", lua::wrap},
{"get_body_type", lua::wrap},
{"set_body_type", lua::wrap},
+ {"set_material", lua::wrap},
+ {"get_material", lua::wrap},
+ {"get_mass", lua::wrap},
+ {"set_mass", lua::wrap},
+ {"get_elasticity", lua::wrap},
+ {"set_elasticity", lua::wrap},
{nullptr, nullptr}
};
diff --git a/src/logic/scripting/lua/libs/lib__transform.cpp b/src/logic/scripting/lua/libs/lib__transform.cpp
index 7cf7bed62..16662daaa 100644
--- a/src/logic/scripting/lua/libs/lib__transform.cpp
+++ b/src/logic/scripting/lua/libs/lib__transform.cpp
@@ -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;
}
diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp
index 2fa2b1281..f2a615dc0 100644
--- a/src/logic/scripting/lua/libs/libblock.cpp
+++ b/src/logic/scripting/lua/libs/libblock.cpp
@@ -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(id) >= indices->blocks.count()) {
+ auto& level = require_level();
+ auto& indices = require_content().getIndices()->blocks;
+ if (static_cast(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
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(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(x);
int cz = floordiv(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(lua::gettop(L) >= 3 ? lua::tointeger(L, 3) : 0);
+ const size_t hitboxIndex =
+ static_cast(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(id) >= indices->blocks.count()) {
+ if (static_cast(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;
}
diff --git a/src/logic/scripting/lua/libs/libcamera.cpp b/src/logic/scripting/lua/libs/libcamera.cpp
index 6ec6ef96c..c45d2740a 100644
--- a/src/logic/scripting/lua/libs/libcamera.cpp
+++ b/src/logic/scripting/lua/libs/libcamera.cpp
@@ -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
static int l_camera_getter(lua::State* L) {
size_t index = static_cast(lua::tointeger(L, 1));
- return getterfunc(L, *level->cameras.at(index));
+ return getterfunc(L, *require_level().cameras.at(index));
}
template
static int l_camera_setter(lua::State* L) {
size_t index = static_cast(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(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(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))
diff --git a/src/logic/scripting/lua/libs/libfile.cpp b/src/logic/scripting/lua/libs/libfile.cpp
index 687008152..26248379c 100644
--- a/src/logic/scripting/lua/libs/libfile.cpp
+++ b/src/logic/scripting/lua/libs/libfile.cpp
@@ -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},
{"__read_descriptor", lua::wrap},
{"__write_descriptor", lua::wrap},
+ {"__seek_descriptor", lua::wrap},
{"__flush_descriptor", lua::wrap},
{"__close_descriptor", lua::wrap},
{"__close_all_descriptors", lua::wrap},
{nullptr, nullptr}
};
-
diff --git a/src/logic/scripting/lua/libs/libinventory.cpp b/src/logic/scripting/lua/libs/libinventory.cpp
index 7f43ae88b..3ec9d2ca3 100644
--- a/src/logic/scripting/lua/libs/libinventory.cpp
+++ b/src/logic/scripting/lua/libs/libinventory.cpp
@@ -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);
}
diff --git a/src/logic/scripting/lua/libs/libitem.cpp b/src/logic/scripting/lua/libs/libitem.cpp
index 3a0f77ec7..4c7ba3983 100644
--- a/src/logic/scripting/lua/libs/libitem.cpp
+++ b/src/logic/scripting/lua/libs/libitem.cpp
@@ -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);
diff --git a/src/logic/scripting/lua/libs/libmat4.cpp b/src/logic/scripting/lua/libs/libmat4.cpp
index 081d99b5e..59f15e661 100644
--- a/src/logic/scripting/lua/libs/libmat4.cpp
+++ b/src/logic/scripting/lua/libs/libmat4.cpp
@@ -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},
{"decompose", lua::wrap},
{"look_at", lua::wrap},
+ {"perspective", lua::wrap},
{"from_quat", lua::wrap},
{"tostring", lua::wrap},
{nullptr, nullptr}
diff --git a/src/logic/scripting/lua/libs/libplayer.cpp b/src/logic/scripting/lua/libs/libplayer.cpp
index 9f634bddb..a978e5c22 100644
--- a/src/logic/scripting/lua/libs/libplayer.cpp
+++ b/src/logic/scripting/lua/libs/libplayer.cpp
@@ -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(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;
}
diff --git a/src/logic/scripting/lua/libs/libquat.cpp b/src/logic/scripting/lua/libs/libquat.cpp
index 2316360a1..53757cb36 100644
--- a/src/logic/scripting/lua/libs/libquat.cpp
+++ b/src/logic/scripting/lua/libs/libquat.cpp
@@ -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},
+ {"from_euler", lua::wrap},
{"slerp", lua::wrap},
{"tostring", lua::wrap},
{nullptr, nullptr}
diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp
index 30a619788..c2d6be43c 100644
--- a/src/logic/scripting/lua/libs/libworld.cpp
+++ b/src/logic/scripting/lua/libs/libworld.cpp
@@ -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) {
diff --git a/src/maths/aabb.hpp b/src/maths/aabb.hpp
index 471441951..ca19a81fe 100644
--- a/src/maths/aabb.hpp
+++ b/src/maths/aabb.hpp
@@ -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 &&
diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp
index cbb507c89..82e293522 100644
--- a/src/objects/Entities.cpp
+++ b/src/objects/Entities.cpp
@@ -1,28 +1,30 @@
#define VC_ENABLE_REFLECTION
#include "Entities.hpp"
-#include
-#include
-#include
-
#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
+#include
+#include
+#include
+
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(entity, static_cast(id), def);
+ registry->emplace(entity, id, def);
const auto& tsf = registry->emplace(
entity,
position,
@@ -91,7 +93,7 @@ entityid_t Entities::spawn(
auto& body = registry->emplace(
entity,
true,
- Hitbox {def.bodyType, position, def.hitbox * 0.5f},
+ Hitbox {id, def.bodyType, position, def.hitbox * 0.5f},
std::vector {}
);
body.initialize(def, id, *this);
@@ -161,7 +163,11 @@ void Entities::loadEntity(const dv::value& map, Entity entity) {
}
std::optional 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();
@@ -170,7 +176,7 @@ std::optional 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();
- auto physics = level.physics.get();
- std::vector 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();
+ 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::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();
auto physics = level.physics.get();
+
+ int substeps = std::max(std::min(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(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();
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;
}
}
diff --git a/src/objects/Entities.hpp b/src/objects/Entities.hpp
index 804ce17f4..33786118a 100644
--- a/src/objects/Entities.hpp
+++ b/src/objects/Entities.hpp
@@ -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 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);
diff --git a/src/objects/EntityDef.cpp b/src/objects/EntityDef.cpp
index 4cc858710..ae1f87c68 100644
--- a/src/objects/EntityDef.cpp
+++ b/src/objects/EntityDef.cpp
@@ -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;
}
diff --git a/src/objects/EntityDef.hpp b/src/objects/EntityDef.hpp
index 3752f3063..195d07a17 100644
--- a/src/objects/EntityDef.hpp
+++ b/src/objects/EntityDef.hpp
@@ -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;
diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp
index a63341d49..b77a07fdd 100644
--- a/src/objects/Player.cpp
+++ b/src/objects/Player.cpp
@@ -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);
}
diff --git a/src/objects/Rigidbody.cpp b/src/objects/Rigidbody.cpp
index 9e2edba4a..2c885f8e0 100644
--- a/src/objects/Rigidbody.cpp
+++ b/src/objects/Rigidbody.cpp
@@ -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
@@ -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 {};
diff --git a/src/objects/Rigidbody.hpp b/src/objects/Rigidbody.hpp
index 91790d8a2..bc2a85f90 100644
--- a/src/objects/Rigidbody.hpp
+++ b/src/objects/Rigidbody.hpp
@@ -13,6 +13,8 @@ struct Rigidbody {
bool enabled = true;
Hitbox hitbox;
std::vector sensors;
+ float mass;
+ float elasticity;
dv::value serialize(bool saveVelocity, bool saveBodySettings) const;
void deserialize(const dv::value& root);
diff --git a/src/physics/Hitbox.cpp b/src/physics/Hitbox.cpp
index adf078418..f49e0c2b8 100644
--- a/src/physics/Hitbox.cpp
+++ b/src/physics/Hitbox.cpp
@@ -1,10 +1,12 @@
#include "Hitbox.hpp"
-#include
-
-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) {
+}
diff --git a/src/physics/Hitbox.hpp b/src/physics/Hitbox.hpp
index 909a48451..ecf41faa3 100644
--- a/src/physics/Hitbox.hpp
+++ b/src/physics/Hitbox.hpp
@@ -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
#include
#include
+#include
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;
+ }
+ }
};
diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp
index e20bafacd..92780fe5e 100644
--- a/src/physics/PhysicsSolver.cpp
+++ b/src/physics/PhysicsSolver.cpp
@@ -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
#include
#define GLM_ENABLE_EXPERIMENTAL
#include
-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(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
-static bool calc_collision_neg(
+// todo: reduce code duplication
+
+template
+static void calc_collision(
+ Hitbox& hitbox,
const GlobalChunks& chunks,
- glm::vec3& pos,
- glm::vec3& vel,
+ const std::vector& 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(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
-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(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(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()
+ );
}
diff --git a/src/physics/PhysicsSolver.hpp b/src/physics/PhysicsSolver.hpp
index b7810cefd..e616696ca 100644
--- a/src/physics/PhysicsSolver.hpp
+++ b/src/physics/PhysicsSolver.hpp
@@ -9,35 +9,48 @@
#include
class Block;
+class Entities;
class GlobalChunks;
struct Sensor;
class PhysicsSolver {
- glm::vec3 gravity;
- std::vector 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 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 sensors;
+ std::vector solidHitboxes;
+ std::vector 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);
};
diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp
index dc3f6153f..2a85b38bd 100644
--- a/src/voxels/Chunks.cpp
+++ b/src/voxels/Chunks.cpp
@@ -1,11 +1,5 @@
#include "Chunks.hpp"
-#include
-
-#include
-#include
-#include
-
#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
+#include
+#include
+#include
+
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;
diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp
index 4fe24f289..9fdebff1c 100644
--- a/src/voxels/Chunks.hpp
+++ b/src/voxels/Chunks.hpp
@@ -1,17 +1,16 @@
#pragma once
-#include
-
-#include
-#include
-#include
-#include
-
#include "typedefs.hpp"
#include "voxel.hpp"
#include "constants.hpp"
#include "util/AreaMap2D.hpp"
+#include
+#include
+#include
+#include
+#include
+
class VoxelRenderer;
struct AABB;
diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp
index 032ae17e2..d24d8b26d 100644
--- a/src/voxels/GlobalChunks.cpp
+++ b/src/voxels/GlobalChunks.cpp
@@ -214,6 +214,6 @@ void GlobalChunks::putChunk(std::shared_ptr 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 GlobalChunks::isObstacleAt(float x, float y, float z, const AABB& aabb) const {
+ return blocks_agent::is_obstacle_at(*this, x, y, z, aabb);
}
diff --git a/src/voxels/GlobalChunks.hpp b/src/voxels/GlobalChunks.hpp
index 7120658c2..de62cbc58 100644
--- a/src/voxels/GlobalChunks.hpp
+++ b/src/voxels/GlobalChunks.hpp
@@ -1,6 +1,7 @@
#pragma once
#include
+#include
#include
#define GLM_ENABLE_EXPERIMENTAL
@@ -57,7 +58,7 @@ public:
void putChunk(std::shared_ptr chunk);
- const AABB* isObstacleAt(float x, float y, float z) const;
+ std::optional 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));
diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp
index 0742a7293..a86b2e25b 100644
--- a/src/voxels/blocks_agent.hpp
+++ b/src/voxels/blocks_agent.hpp
@@ -13,7 +13,6 @@
#include "voxel.hpp"
#include "VoxelsVolume.hpp"
-#include
#include
#include
#include
@@ -468,8 +467,8 @@ void get_voxels(
);
template
-inline const AABB* is_obstacle_at(
- const Storage& chunks, float x, float y, float z
+inline std::optional 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
+inline std::optional 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
diff --git a/src/world/Level.cpp b/src/world/Level.cpp
index 2a1583bd7..e6d9000e1 100644
--- a/src/world/Level.cpp
+++ b/src/world/Level.cpp
@@ -26,7 +26,7 @@ Level::Level(
: world(std::move(worldPtr)),
content(content),
chunks(std::make_unique(*this)),
- physics(std::make_unique(glm::vec3(0, -22.6f, 0))),
+ physics(std::make_unique(*chunks, glm::vec3(0, -22.6f, 0))),
events(std::make_unique()),
entities(std::make_unique(*this)),
players(std::make_unique(*this)),
diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp
index 1f9a2241d..260a28e3c 100644
--- a/src/world/generator/WorldGenerator.cpp
+++ b/src/world/generator/WorldGenerator.cpp
@@ -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 {