Merge branch 'MihailRis:main' into main
This commit is contained in:
commit
52720e5bfe
@ -30,6 +30,7 @@ AppDir:
|
|||||||
- libogg0
|
- libogg0
|
||||||
- libvorbis0a
|
- libvorbis0a
|
||||||
- libvorbisfile3
|
- libvorbisfile3
|
||||||
|
- libluajit-5.1-2
|
||||||
exclude:
|
exclude:
|
||||||
- hicolor-icon-theme
|
- hicolor-icon-theme
|
||||||
- sound-theme-freedesktop
|
- sound-theme-freedesktop
|
||||||
|
|||||||
@ -60,7 +60,7 @@ Library **audio** contains available Audio API in Lua scripts.
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
audio.play_stream(
|
audio.play_stream(
|
||||||
-- audio file location
|
-- audio file location (without entry point, but with extension included)
|
||||||
name: string,
|
name: string,
|
||||||
-- audio source world position
|
-- audio source world position
|
||||||
x: number, y: number, z: number,
|
x: number, y: number, z: number,
|
||||||
@ -79,7 +79,7 @@ Plays streaming audio from the specified file at the specified world position. R
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
audio.play_stream_2d(
|
audio.play_stream_2d(
|
||||||
-- audio file location
|
-- audio file location (without entry point, but with extension included)
|
||||||
name: string,
|
name: string,
|
||||||
-- audio gain (0.0 - 1.0)
|
-- audio gain (0.0 - 1.0)
|
||||||
volume: number
|
volume: number
|
||||||
|
|||||||
@ -35,6 +35,16 @@ Block model type from list:
|
|||||||
- "X" - grass model (two crossed sprites)
|
- "X" - grass model (two crossed sprites)
|
||||||
- "aabb" - model based of block hitbox (complex hitbox will be combined into one). Examples: pipes, bulbs, panels.
|
- "aabb" - model based of block hitbox (complex hitbox will be combined into one). Examples: pipes, bulbs, panels.
|
||||||
|
|
||||||
|
### *model-name*
|
||||||
|
|
||||||
|
In addition to built-in model types, you can use your own, loaded from file.
|
||||||
|
|
||||||
|
The property specifies the model name without `entry_point:models/` nor extension.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> Textures (materials) used in the model must be in the `blocks` atlas and specified in the *atlas-texture* format:
|
||||||
|
> `blocks:texture_name`
|
||||||
|
|
||||||
### *draw-group*
|
### *draw-group*
|
||||||
|
|
||||||
Integer specifying number of block draw group (render order). Used for semi-transparent blocks.
|
Integer specifying number of block draw group (render order). Used for semi-transparent blocks.
|
||||||
@ -81,6 +91,13 @@ Turns off block model shading
|
|||||||
|
|
||||||
Determines the presence of the vertex AO effect. Turned-on by default.
|
Determines the presence of the vertex AO effect. Turned-on by default.
|
||||||
|
|
||||||
|
### *culling*
|
||||||
|
|
||||||
|
Face culling mode:
|
||||||
|
- **default** - normal face culling
|
||||||
|
- **optional** - face culling among blocks of the same rendering group can be disabled via the `graphics.dense-render` setting.
|
||||||
|
- **disabled** - face culling among blocks of the same rendering group disabled.
|
||||||
|
|
||||||
## Physics
|
## Physics
|
||||||
|
|
||||||
### *obstacle*
|
### *obstacle*
|
||||||
|
|||||||
@ -16,6 +16,10 @@ Particles are a table, all fields of which are optional.
|
|||||||
| acceleration | Particles acceleration. | {0, -16, 0} |
|
| acceleration | Particles acceleration. | {0, -16, 0} |
|
||||||
| explosion | Force of particles explosion on spawn. | {2, 2, 2} |
|
| explosion | Force of particles explosion on spawn. | {2, 2, 2} |
|
||||||
| size | Size of particles. | {0.1, 0.1, 0.1} |
|
| size | Size of particles. | {0.1, 0.1, 0.1} |
|
||||||
|
| size_spread | Maximum particle size spread over time. | 0.2 |
|
||||||
|
| angle_spread | Maximum initial rotation angle spread (0 to 1) | 0.0 |
|
||||||
|
| min_angular_vel | Minimum angular velocity (radians per sec). Non-negative. | 0.0 |
|
||||||
|
| max_angular_vel | Maximum angular velocity (radians per sec). Non-negative. | 0.0 |
|
||||||
| spawn_shape | Shape of particle spawn area. (ball/sphere/box) | ball |
|
| spawn_shape | Shape of particle spawn area. (ball/sphere/box) | ball |
|
||||||
| spawn_spread | Size of particle spawn area. | {0, 0, 0} |
|
| spawn_spread | Size of particle spawn area. | {0, 0, 0} |
|
||||||
| random_sub_uv | Size of random texture subregion (1 - entire texture will be used). | 1.0 |
|
| random_sub_uv | Size of random texture subregion (1 - entire texture will be used). | 1.0 |
|
||||||
|
|||||||
@ -15,7 +15,8 @@ entities.get(uid: int) -> table
|
|||||||
-- prefix - component pack id
|
-- prefix - component pack id
|
||||||
-- name - component name
|
-- name - component name
|
||||||
-- component prefix and name are separated with two underscores
|
-- component prefix and name are separated with two underscores
|
||||||
entities.spawn(name: str, pos: vec3, [optional] args: table)
|
-- Returns an entity object
|
||||||
|
entities.spawn(name: str, pos: vec3, [optional] args: table) -> table
|
||||||
|
|
||||||
-- Checks the existence of an entity by a unique identifier.
|
-- Checks the existence of an entity by a unique identifier.
|
||||||
entities.exists(uid: int) -> bool
|
entities.exists(uid: int) -> bool
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
* [Small structures placement](#small-structures-placement)
|
* [Small structures placement](#small-structures-placement)
|
||||||
* [Wide structures placement](#wide-structures-placement)
|
* [Wide structures placement](#wide-structures-placement)
|
||||||
- [Structural air](#structural-air)
|
- [Structural air](#structural-air)
|
||||||
|
- [Generator 'Demo' (base:demo)](#generator-demo-basedemo)
|
||||||
|
|
||||||
## Basic concepts
|
## Basic concepts
|
||||||
|
|
||||||
@ -52,6 +53,7 @@ The main properties described in the configuration file:
|
|||||||
- **biomes-bpd** - number of blocks per point of the biome selection parameter map. Default: 4.
|
- **biomes-bpd** - number of blocks per point of the biome selection parameter map. Default: 4.
|
||||||
- **heights-bpd** - number of blocks per point of the height map. Default: 4.
|
- **heights-bpd** - number of blocks per point of the height map. Default: 4.
|
||||||
- **wide-structs-chunks-radius** - maximum radius for placing 'wide' structures, measured in chunks.
|
- **wide-structs-chunks-radius** - maximum radius for placing 'wide' structures, measured in chunks.
|
||||||
|
- **heightmap-inputs** - an array of parameter map numbers that will be passed by the inputs table to the height map generation function.
|
||||||
|
|
||||||
## Global variables
|
## Global variables
|
||||||
|
|
||||||
@ -472,3 +474,24 @@ function place_structures_wide(
|
|||||||
`core:struct_air` - a block that should be used in chunks to mark empty space that should not be filled with blocks when generated in the world.
|
`core:struct_air` - a block that should be used in chunks to mark empty space that should not be filled with blocks when generated in the world.
|
||||||
|
|
||||||
<image src="../../res/textures/blocks/struct_air.png" width="128px" height="128px" style="image-rendering: pixelated">
|
<image src="../../res/textures/blocks/struct_air.png" width="128px" height="128px" style="image-rendering: pixelated">
|
||||||
|
|
||||||
|
# Generator 'Demo' (base:demo)
|
||||||
|
|
||||||
|
## Adding new ore
|
||||||
|
|
||||||
|
To add a new ore in your pack:
|
||||||
|
1. In the `generators` folder, create a `demo.files` folder (you don't need to create demo.toml).
|
||||||
|
|
||||||
|
2. In the created folder, create a fragments folder and place the ore fragment file in it.
|
||||||
|
3. In `demo.files`, create a structures.toml file:
|
||||||
|
```toml
|
||||||
|
fragment_name = {}
|
||||||
|
```
|
||||||
|
4. Also in `demo.files`, create an ores.json file:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{"struct": "fragment_name", "rarity": rarity}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
The higher the rarity value, the less ore generation chance.
|
||||||
|
You can rely on the rarity of coal ore: 4400.
|
||||||
|
|||||||
@ -61,7 +61,7 @@
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
audio.play_stream(
|
audio.play_stream(
|
||||||
-- путь к аудио-файлу
|
-- путь к аудио-файлу (без точки входа, но с указанием расширения)
|
||||||
name: string,
|
name: string,
|
||||||
-- позиция источника аудио в мире
|
-- позиция источника аудио в мире
|
||||||
x: number, y: number, z: number,
|
x: number, y: number, z: number,
|
||||||
@ -80,7 +80,7 @@ audio.play_stream(
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
audio.play_stream_2d(
|
audio.play_stream_2d(
|
||||||
-- путь к аудио-файлу
|
-- путь к аудио-файлу (без точки входа, но с указанием расширения)
|
||||||
name: string,
|
name: string,
|
||||||
-- громкость аудио (от 0.0 до 1.0)
|
-- громкость аудио (от 0.0 до 1.0)
|
||||||
volume: number
|
volume: number
|
||||||
|
|||||||
@ -35,6 +35,16 @@
|
|||||||
- "X" - модель травы (крест из двух спрайтов)
|
- "X" - модель травы (крест из двух спрайтов)
|
||||||
- "aabb" - модель, соответствующая хитбоксу блока (составной хитбокс будет объединен в один). Примеры: трубы, лампочки, панели.
|
- "aabb" - модель, соответствующая хитбоксу блока (составной хитбокс будет объединен в один). Примеры: трубы, лампочки, панели.
|
||||||
|
|
||||||
|
### Имя модели - *model-name*
|
||||||
|
|
||||||
|
Кроме встроенных типов моделей, можно использовать собственные, загружаемые из файлов.
|
||||||
|
|
||||||
|
В свойстве указывается имя модели без `точка_входа:models/` и расширения.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> Текстуры (материалы), использующиеся в модели, должны находиться в атласе `blocks` и указываться в соответствующем формате:
|
||||||
|
> `blocks:имя_текстуры`
|
||||||
|
|
||||||
### Группа отрисовки - *draw-group*
|
### Группа отрисовки - *draw-group*
|
||||||
|
|
||||||
Целое число определяющее номер группы отрисовки данного блока.
|
Целое число определяющее номер группы отрисовки данного блока.
|
||||||
@ -82,7 +92,6 @@
|
|||||||
|
|
||||||
При значении `true` блок не препятствует прохождению вертикального луча солнечного света.
|
При значении `true` блок не препятствует прохождению вертикального луча солнечного света.
|
||||||
|
|
||||||
|
|
||||||
### Без освещения - *shadeless*
|
### Без освещения - *shadeless*
|
||||||
|
|
||||||
Выключает освещение на модели блока.
|
Выключает освещение на модели блока.
|
||||||
@ -91,6 +100,13 @@
|
|||||||
|
|
||||||
Определяет наличие эффекта вершинного AO. Включен по-умолчанию.
|
Определяет наличие эффекта вершинного AO. Включен по-умолчанию.
|
||||||
|
|
||||||
|
### Отсечение - *culling*
|
||||||
|
|
||||||
|
Режим отсечения граней:
|
||||||
|
- **default** - обычное отсечение граней
|
||||||
|
- **optional** - отсечение граней среди блоков одной группы отрисовки можно отключить через настройку `graphics.dense-render` (Плотный рендер блоков).
|
||||||
|
- **disabled** - отсечение граней среди блоков одной группы отрисовки отключено.
|
||||||
|
|
||||||
## Физика
|
## Физика
|
||||||
|
|
||||||
### Препятствие - *obstacle*
|
### Препятствие - *obstacle*
|
||||||
|
|||||||
@ -18,6 +18,9 @@
|
|||||||
| explosion | Сила разлёта частиц при спавне. | {2, 2, 2} |
|
| explosion | Сила разлёта частиц при спавне. | {2, 2, 2} |
|
||||||
| size | Размер частиц. | {0.1, 0.1, 0.1} |
|
| size | Размер частиц. | {0.1, 0.1, 0.1} |
|
||||||
| size_spread | Максимальное отклонение времени размера частиц. | 0.2 |
|
| size_spread | Максимальное отклонение времени размера частиц. | 0.2 |
|
||||||
|
| angle_spread | Максимальное отклонение начального угла поворота (от 0 до 1) | 0.0 |
|
||||||
|
| min_angular_vel | Минимальная угловая скорость (радианы в сек.). Неотрицательное. | 0.0 |
|
||||||
|
| max_angular_vel | Максимальная угловая скорость (радианы в сек.). Неотрицательное. | 0.0 |
|
||||||
| spawn_shape | Форма области спавна частиц. (ball/sphere/box) | ball |
|
| spawn_shape | Форма области спавна частиц. (ball/sphere/box) | ball |
|
||||||
| spawn_spread | Размер области спавна частиц. | {0, 0, 0} |
|
| spawn_spread | Размер области спавна частиц. | {0, 0, 0} |
|
||||||
| random_sub_uv | Размер случайного подрегиона текстуры (1 - будет использована вся текстура). | 1.0 |
|
| random_sub_uv | Размер случайного подрегиона текстуры (1 - будет использована вся текстура). | 1.0 |
|
||||||
|
|||||||
@ -15,7 +15,8 @@ entities.get(uid: int) -> table
|
|||||||
-- префикс - id пака
|
-- префикс - id пака
|
||||||
-- имя - название компонента
|
-- имя - название компонента
|
||||||
-- префикс и имя компонента разделяются двумя подчеркиваниями
|
-- префикс и имя компонента разделяются двумя подчеркиваниями
|
||||||
entities.spawn(name: str, pos: vec3, [optional] args: table)
|
-- Возвращает обьект сущности
|
||||||
|
entities.spawn(name: str, pos: vec3, [optional] args: table) -> table
|
||||||
|
|
||||||
-- Проверяет наличие сущности по уникальному идентификатору.
|
-- Проверяет наличие сущности по уникальному идентификатору.
|
||||||
entities.exists(uid: int) -> bool
|
entities.exists(uid: int) -> bool
|
||||||
|
|||||||
@ -46,6 +46,13 @@ table.remove_value(t: table, x: object)
|
|||||||
|
|
||||||
Удаляет элемент **x** из **t**.
|
Удаляет элемент **x** из **t**.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
table.shuffle(t: table) -> table
|
||||||
|
```
|
||||||
|
|
||||||
|
Перемешивает значения в таблице.
|
||||||
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
table.tostring(t: table) -> string
|
table.tostring(t: table) -> string
|
||||||
```
|
```
|
||||||
@ -146,6 +153,18 @@ math.rand(low, high)
|
|||||||
|
|
||||||
Возвращает случайное дробное число в диапазоне от **low** до **high**.
|
Возвращает случайное дробное число в диапазоне от **low** до **high**.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
math.normalize(num: number, [опционально] conf: num) -> number
|
||||||
|
```
|
||||||
|
|
||||||
|
Возвращает нормализованное значение num относительно conf.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
math.round(num: number, [опционально] places: num) -> number
|
||||||
|
```
|
||||||
|
|
||||||
|
Возвращает округлённое значение num до указанного количества знаков после запятой places.
|
||||||
|
|
||||||
## Дополнительные глобальные функции
|
## Дополнительные глобальные функции
|
||||||
|
|
||||||
В этом же скрипте также определены и другие глобальные функции которые доступны для использования. Ниже их список
|
В этом же скрипте также определены и другие глобальные функции которые доступны для использования. Ниже их список
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
* [Расстановка малых структур](#расстановка-малых-структур)
|
* [Расстановка малых структур](#расстановка-малых-структур)
|
||||||
* [Расстановка 'широких' структур](#расстановка-широких-структур)
|
* [Расстановка 'широких' структур](#расстановка-широких-структур)
|
||||||
- [Структурный воздух](#структурный-воздух)
|
- [Структурный воздух](#структурный-воздух)
|
||||||
|
- [Генератор 'Demo' (base:demo)](#генератор-demo-basedemo)
|
||||||
|
|
||||||
## Основные понятия
|
## Основные понятия
|
||||||
|
|
||||||
@ -52,6 +53,7 @@
|
|||||||
- **biomes-bpd** - количество блоков на точку карты параметра выбора биомов. По-умолчанию: 4.
|
- **biomes-bpd** - количество блоков на точку карты параметра выбора биомов. По-умолчанию: 4.
|
||||||
- **heights-bpd** - количество блоков на точку карты высот. По-умолчанию: 4.
|
- **heights-bpd** - количество блоков на точку карты высот. По-умолчанию: 4.
|
||||||
- **wide-structs-chunks-radius** - масимальный радиус размещения 'широких' структур, измеряемый в чанках.
|
- **wide-structs-chunks-radius** - масимальный радиус размещения 'широких' структур, измеряемый в чанках.
|
||||||
|
- **heightmap-inputs** - массив номеров карт параметров, которые будут переданы таблицей inputs в функцию генерации карты высот.
|
||||||
|
|
||||||
## Глобальные переменные
|
## Глобальные переменные
|
||||||
|
|
||||||
@ -477,3 +479,23 @@ function place_structures_wide(
|
|||||||
`core:struct_air` - блок, которые следует использовать в фрагментах для обозначения пустого пространства, которое не должно заполняться блоками при генерации в мире.
|
`core:struct_air` - блок, которые следует использовать в фрагментах для обозначения пустого пространства, которое не должно заполняться блоками при генерации в мире.
|
||||||
|
|
||||||
<image src="../../res/textures/blocks/struct_air.png" width="128px" height="128px" style="image-rendering: pixelated">
|
<image src="../../res/textures/blocks/struct_air.png" width="128px" height="128px" style="image-rendering: pixelated">
|
||||||
|
|
||||||
|
# Генератор 'Demo' (base:demo)
|
||||||
|
|
||||||
|
## Добавление новой руды
|
||||||
|
|
||||||
|
Чтобы добавить новую руду из своего пака:
|
||||||
|
1. В папке `generators` создайте папку `demo.files` (demo.toml создавать не нужно).
|
||||||
|
2. В созданной папке создайте папку fragments и поместите в неё файл фрагмента руды.
|
||||||
|
3. В `demo.files` создайте файл structures.toml:
|
||||||
|
```toml
|
||||||
|
имя_фрагмента = {}
|
||||||
|
```
|
||||||
|
4. Также в `demo.files` создайте файл ores.json:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{"struct": "имя_фрагмента", "rarity": редкость}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
Чем выше значение редкости, тем меньше вероятность генерации руды.
|
||||||
|
Опираться можно на редкость угольной руды: 4400.
|
||||||
|
|||||||
@ -1,5 +1,26 @@
|
|||||||
{
|
{
|
||||||
"texture": "leaves",
|
"texture": "leaves",
|
||||||
"material": "base:grass",
|
"material": "base:grass",
|
||||||
|
"draw-group": 5,
|
||||||
|
"culling": "optional",
|
||||||
|
"particles": {
|
||||||
|
"lifetime": 4.0,
|
||||||
|
"spawn_interval": 1000.0,
|
||||||
|
"acceleration": [0, -0.1, 0],
|
||||||
|
"velocity": [0.2, -2.5, 0.3],
|
||||||
|
"explosion": [0, 0, 0],
|
||||||
|
"collision": false,
|
||||||
|
"size": [0.3, 0.3, 0.3],
|
||||||
|
"size_spread": 0.2,
|
||||||
|
"spawn_shape": "box",
|
||||||
|
"spawn_spread": [0.2, 0.2, 0.2],
|
||||||
|
"angle_spread": 1.0,
|
||||||
|
"min_angular_vel": 0.5,
|
||||||
|
"max_angular_vel": 2.0,
|
||||||
|
"lighting": true,
|
||||||
|
"frames": [
|
||||||
|
"particles:leaf_0"
|
||||||
|
]
|
||||||
|
},
|
||||||
"base:durability": 0.7
|
"base:durability": 0.7
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 899 B |
BIN
res/content/base/textures/blocks/leaves_opaque.png
Normal file
BIN
res/content/base/textures/blocks/leaves_opaque.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
BIN
res/content/base/textures/particles/leaf_0.png
Normal file
BIN
res/content/base/textures/particles/leaf_0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
@ -41,4 +41,5 @@ function on_open()
|
|||||||
create_setting("graphics.fog-curve", "Fog Curve", 0.1)
|
create_setting("graphics.fog-curve", "Fog Curve", 0.1)
|
||||||
create_setting("graphics.gamma", "Gamma", 0.05, "", "graphics.gamma.tooltip")
|
create_setting("graphics.gamma", "Gamma", 0.05, "", "graphics.gamma.tooltip")
|
||||||
create_checkbox("graphics.backlight", "Backlight", "graphics.backlight.tooltip")
|
create_checkbox("graphics.backlight", "Backlight", "graphics.backlight.tooltip")
|
||||||
|
create_checkbox("graphics.dense-render", "Dense blocks render", "graphics.dense-render.tooltip")
|
||||||
end
|
end
|
||||||
|
|||||||
@ -72,9 +72,14 @@ function data_buffer:put_byte(byte)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function data_buffer:put_bytes(bytes)
|
function data_buffer:put_bytes(bytes)
|
||||||
|
if type(self.bytes) == 'table' then
|
||||||
for i = 1, #bytes do
|
for i = 1, #bytes do
|
||||||
self:put_byte(bytes[i])
|
self:put_byte(bytes[i])
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
self.bytes:insert(self.pos, bytes)
|
||||||
|
self.pos = self.pos + #bytes
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function data_buffer:put_single(single)
|
function data_buffer:put_single(single)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
-- Check if given table is an array
|
-- Check if given table is an array
|
||||||
function is_array(x)
|
function is_array(x)
|
||||||
if #t > 0 then
|
if #x > 0 then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
for k, v in pairs(x) do
|
for k, v in pairs(x) do
|
||||||
@ -51,6 +51,19 @@ function math.rand(low, high)
|
|||||||
return low + (high - low) * math.random()
|
return low + (high - low) * math.random()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function math.normalize(num, conf)
|
||||||
|
conf = conf or 1
|
||||||
|
|
||||||
|
return (num / conf) % 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function math.round(num, places)
|
||||||
|
places = places or 0
|
||||||
|
|
||||||
|
local mult = 10 ^ places
|
||||||
|
return math.floor(num * mult + 0.5) / mult
|
||||||
|
end
|
||||||
|
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
function table.copy(t)
|
function table.copy(t)
|
||||||
@ -74,7 +87,7 @@ function table.deep_copy(t)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return copied
|
return setmetatable(copied, getmetatable(t))
|
||||||
end
|
end
|
||||||
|
|
||||||
function table.count_pairs(t)
|
function table.count_pairs(t)
|
||||||
@ -91,6 +104,15 @@ function table.random(t)
|
|||||||
return t[math.random(1, #t)]
|
return t[math.random(1, #t)]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function table.shuffle(t)
|
||||||
|
for i = #t, 2, -1 do
|
||||||
|
local j = math.random(i)
|
||||||
|
t[i], t[j] = t[j], t[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
local pattern_escape_replacements = {
|
local pattern_escape_replacements = {
|
||||||
|
|||||||
@ -17,7 +17,7 @@ vec3 pick_sky_color(samplerCube cubemap) {
|
|||||||
vec3 skyLightColor = texture(cubemap, vec3(0.4f, 0.0f, 0.4f)).rgb;
|
vec3 skyLightColor = texture(cubemap, vec3(0.4f, 0.0f, 0.4f)).rgb;
|
||||||
skyLightColor *= SKY_LIGHT_TINT;
|
skyLightColor *= SKY_LIGHT_TINT;
|
||||||
skyLightColor = min(vec3(1.0), skyLightColor*SKY_LIGHT_MUL);
|
skyLightColor = min(vec3(1.0), skyLightColor*SKY_LIGHT_MUL);
|
||||||
skyLightColor = max(MAX_SKY_LIGHT, skyLightColor);
|
skyLightColor = max(MIN_SKY_LIGHT, skyLightColor);
|
||||||
return skyLightColor;
|
return skyLightColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
// lighting
|
// lighting
|
||||||
#define SKY_LIGHT_MUL 2.9
|
#define SKY_LIGHT_MUL 2.9
|
||||||
#define SKY_LIGHT_TINT vec3(0.9, 0.8, 1.0)
|
#define SKY_LIGHT_TINT vec3(0.9, 0.8, 1.0)
|
||||||
#define MAX_SKY_LIGHT vec3(0.1, 0.11, 0.14)
|
#define MIN_SKY_LIGHT vec3(0.2, 0.25, 0.33)
|
||||||
|
|
||||||
// fog
|
// fog
|
||||||
#define FOG_POS_SCALE vec3(1.0, 0.2, 1.0)
|
#define FOG_POS_SCALE vec3(1.0, 0.2, 1.0)
|
||||||
|
|||||||
@ -16,6 +16,7 @@ devtools.traceback=Traceback (most recent call first)
|
|||||||
# Tooltips
|
# Tooltips
|
||||||
graphics.gamma.tooltip=Lighting brightness curve
|
graphics.gamma.tooltip=Lighting brightness curve
|
||||||
graphics.backlight.tooltip=Backlight to prevent total darkness
|
graphics.backlight.tooltip=Backlight to prevent total darkness
|
||||||
|
graphics.dense-render.tooltip=Enables transparency in blocks like leaves
|
||||||
|
|
||||||
# settings
|
# settings
|
||||||
settings.Controls Search Mode=Search by attached button name
|
settings.Controls Search Mode=Search by attached button name
|
||||||
|
|||||||
@ -28,6 +28,7 @@ pack.remove-confirm=Удалить весь поставляемый паком/
|
|||||||
# Подсказки
|
# Подсказки
|
||||||
graphics.gamma.tooltip=Кривая яркости освещения
|
graphics.gamma.tooltip=Кривая яркости освещения
|
||||||
graphics.backlight.tooltip=Подсветка, предотвращающая полную темноту
|
graphics.backlight.tooltip=Подсветка, предотвращающая полную темноту
|
||||||
|
graphics.dense-render.tooltip=Включает прозрачность блоков, таких как листья.
|
||||||
|
|
||||||
# Меню
|
# Меню
|
||||||
menu.Apply=Применить
|
menu.Apply=Применить
|
||||||
@ -67,6 +68,7 @@ world.delete-confirm=Удалить мир безвозвратно?
|
|||||||
# Настройки
|
# Настройки
|
||||||
settings.Ambient=Фон
|
settings.Ambient=Фон
|
||||||
settings.Backlight=Подсветка
|
settings.Backlight=Подсветка
|
||||||
|
settings.Dense blocks render=Плотный рендер блоков
|
||||||
settings.Camera Shaking=Тряска Камеры
|
settings.Camera Shaking=Тряска Камеры
|
||||||
settings.Camera Inertia=Инерция Камеры
|
settings.Camera Inertia=Инерция Камеры
|
||||||
settings.Camera FOV Effects=Эффекты поля зрения
|
settings.Camera FOV Effects=Эффекты поля зрения
|
||||||
|
|||||||
@ -220,11 +220,11 @@ void ContentLoader::loadBlock(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// block model
|
// block model
|
||||||
std::string modelTypeName;
|
std::string modelTypeName = to_string(def.model);
|
||||||
root.at("model").get(modelTypeName);
|
root.at("model").get(modelTypeName);
|
||||||
root.at("model-name").get(def.modelName);
|
root.at("model-name").get(def.modelName);
|
||||||
if (auto model = BlockModel_from(modelTypeName)) {
|
if (auto model = BlockModel_from(modelTypeName)) {
|
||||||
if (*model == BlockModel::custom) {
|
if (*model == BlockModel::custom && def.customModelRaw == nullptr) {
|
||||||
if (root.has("model-primitives")) {
|
if (root.has("model-primitives")) {
|
||||||
def.customModelRaw = root["model-primitives"];
|
def.customModelRaw = root["model-primitives"];
|
||||||
} else if (def.modelName.empty()) {
|
} else if (def.modelName.empty()) {
|
||||||
@ -239,14 +239,22 @@ void ContentLoader::loadBlock(
|
|||||||
}
|
}
|
||||||
def.model = *model;
|
def.model = *model;
|
||||||
} else if (!modelTypeName.empty()) {
|
} else if (!modelTypeName.empty()) {
|
||||||
logger.error() << "unknown model " << modelTypeName;
|
logger.error() << "unknown model: " << modelTypeName;
|
||||||
def.model = BlockModel::none;
|
def.model = BlockModel::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string cullingModeName = to_string(def.culling);
|
||||||
|
root.at("culling").get(cullingModeName);
|
||||||
|
if (auto mode = CullingMode_from(cullingModeName)) {
|
||||||
|
def.culling = *mode;
|
||||||
|
} else {
|
||||||
|
logger.error() << "unknown culling mode: " << cullingModeName;
|
||||||
|
}
|
||||||
|
|
||||||
root.at("material").get(def.material);
|
root.at("material").get(def.material);
|
||||||
|
|
||||||
// rotation profile
|
// rotation profile
|
||||||
std::string profile = "none";
|
std::string profile = def.rotations.name;
|
||||||
root.at("rotation").get(profile);
|
root.at("rotation").get(profile);
|
||||||
|
|
||||||
def.rotatable = profile != "none";
|
def.rotatable = profile != "none";
|
||||||
@ -285,8 +293,6 @@ void ContentLoader::loadBlock(
|
|||||||
);
|
);
|
||||||
aabb.b += aabb.a;
|
aabb.b += aabb.a;
|
||||||
def.hitboxes = {aabb};
|
def.hitboxes = {aabb};
|
||||||
} else {
|
|
||||||
def.hitboxes = {AABB()};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// block light emission [r, g, b] where r,g,b in range [0..15]
|
// block light emission [r, g, b] where r,g,b in range [0..15]
|
||||||
|
|||||||
@ -68,6 +68,7 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) {
|
|||||||
builder.section("graphics");
|
builder.section("graphics");
|
||||||
builder.add("fog-curve", &settings.graphics.fogCurve);
|
builder.add("fog-curve", &settings.graphics.fogCurve);
|
||||||
builder.add("backlight", &settings.graphics.backlight);
|
builder.add("backlight", &settings.graphics.backlight);
|
||||||
|
builder.add("dense-render", &settings.graphics.denseRender);
|
||||||
builder.add("gamma", &settings.graphics.gamma);
|
builder.add("gamma", &settings.graphics.gamma);
|
||||||
builder.add("frustum-culling", &settings.graphics.frustumCulling);
|
builder.add("frustum-culling", &settings.graphics.frustumCulling);
|
||||||
builder.add("skybox-resolution", &settings.graphics.skyboxResolution);
|
builder.add("skybox-resolution", &settings.graphics.skyboxResolution);
|
||||||
|
|||||||
@ -6,32 +6,39 @@
|
|||||||
#include "assets/Assets.hpp"
|
#include "assets/Assets.hpp"
|
||||||
#include "content/Content.hpp"
|
#include "content/Content.hpp"
|
||||||
#include "content/ContentPack.hpp"
|
#include "content/ContentPack.hpp"
|
||||||
#include "core_defs.hpp"
|
|
||||||
#include "graphics/core/Atlas.hpp"
|
#include "graphics/core/Atlas.hpp"
|
||||||
#include "maths/UVRegion.hpp"
|
#include "maths/UVRegion.hpp"
|
||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
|
#include "core_defs.hpp"
|
||||||
|
#include "settings.hpp"
|
||||||
|
|
||||||
ContentGfxCache::ContentGfxCache(const Content* content, const Assets& assets)
|
|
||||||
: content(content) {
|
|
||||||
auto indices = content->getIndices();
|
|
||||||
sideregions = std::make_unique<UVRegion[]>(indices->blocks.count() * 6);
|
|
||||||
const auto& atlas = assets.require<Atlas>("blocks");
|
|
||||||
|
|
||||||
const auto& blocks = indices->blocks.getIterable();
|
ContentGfxCache::ContentGfxCache(
|
||||||
for (blockid_t i = 0; i < blocks.size(); i++) {
|
const Content& content,
|
||||||
auto def = blocks[i];
|
const Assets& assets,
|
||||||
|
const GraphicsSettings& settings
|
||||||
|
)
|
||||||
|
: content(content), assets(assets), settings(settings) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContentGfxCache::refresh(const Block& def, const Atlas& atlas) {
|
||||||
for (uint side = 0; side < 6; side++) {
|
for (uint side = 0; side < 6; side++) {
|
||||||
const std::string& tex = def->textureFaces[side];
|
std::string tex = def.textureFaces[side];
|
||||||
|
if (def.culling == CullingMode::OPTIONAL &&
|
||||||
|
!settings.denseRender.get() && atlas.has(tex + "_opaque")) {
|
||||||
|
tex = tex + "_opaque";
|
||||||
|
}
|
||||||
if (atlas.has(tex)) {
|
if (atlas.has(tex)) {
|
||||||
sideregions[i * 6 + side] = atlas.get(tex);
|
sideregions[def.rt.id * 6 + side] = atlas.get(tex);
|
||||||
} else if (atlas.has(TEXTURE_NOTFOUND)) {
|
} else if (atlas.has(TEXTURE_NOTFOUND)) {
|
||||||
sideregions[i * 6 + side] = atlas.get(TEXTURE_NOTFOUND);
|
sideregions[def.rt.id * 6 + side] = atlas.get(TEXTURE_NOTFOUND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (def->model == BlockModel::custom) {
|
if (def.model == BlockModel::custom) {
|
||||||
auto model = assets.require<model::Model>(def->modelName);
|
auto model = assets.require<model::Model>(def.modelName);
|
||||||
// temporary dirty fix tbh
|
// temporary dirty fix tbh
|
||||||
if (def->modelName.find(':') == std::string::npos) {
|
if (def.modelName.find(':') == std::string::npos) {
|
||||||
for (auto& mesh : model.meshes) {
|
for (auto& mesh : model.meshes) {
|
||||||
size_t pos = mesh.texture.find(':');
|
size_t pos = mesh.texture.find(':');
|
||||||
if (pos == std::string::npos) {
|
if (pos == std::string::npos) {
|
||||||
@ -44,15 +51,26 @@ ContentGfxCache::ContentGfxCache(const Content* content, const Assets& assets)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
models[def->rt.id] = std::move(model);
|
models[def.rt.id] = std::move(model);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContentGfxCache::refresh() {
|
||||||
|
auto indices = content.getIndices();
|
||||||
|
sideregions = std::make_unique<UVRegion[]>(indices->blocks.count() * 6);
|
||||||
|
const auto& atlas = assets.require<Atlas>("blocks");
|
||||||
|
|
||||||
|
const auto& blocks = indices->blocks.getIterable();
|
||||||
|
for (blockid_t i = 0; i < blocks.size(); i++) {
|
||||||
|
auto def = blocks[i];
|
||||||
|
refresh(*def, atlas);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentGfxCache::~ContentGfxCache() = default;
|
ContentGfxCache::~ContentGfxCache() = default;
|
||||||
|
|
||||||
const Content* ContentGfxCache::getContent() const {
|
const Content* ContentGfxCache::getContent() const {
|
||||||
return content;
|
return &content;
|
||||||
}
|
}
|
||||||
|
|
||||||
const model::Model& ContentGfxCache::getModel(blockid_t id) const {
|
const model::Model& ContentGfxCache::getModel(blockid_t id) const {
|
||||||
|
|||||||
@ -10,19 +10,29 @@
|
|||||||
|
|
||||||
class Content;
|
class Content;
|
||||||
class Assets;
|
class Assets;
|
||||||
|
class Atlas;
|
||||||
|
class Block;
|
||||||
struct UVRegion;
|
struct UVRegion;
|
||||||
|
struct GraphicsSettings;
|
||||||
|
|
||||||
namespace model {
|
namespace model {
|
||||||
struct Model;
|
struct Model;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ContentGfxCache {
|
class ContentGfxCache {
|
||||||
const Content* content;
|
const Content& content;
|
||||||
|
const Assets& assets;
|
||||||
|
const GraphicsSettings& settings;
|
||||||
|
|
||||||
// array of block sides uv regions (6 per block)
|
// array of block sides uv regions (6 per block)
|
||||||
std::unique_ptr<UVRegion[]> sideregions;
|
std::unique_ptr<UVRegion[]> sideregions;
|
||||||
std::unordered_map<blockid_t, model::Model> models;
|
std::unordered_map<blockid_t, model::Model> models;
|
||||||
public:
|
public:
|
||||||
ContentGfxCache(const Content* content, const Assets& assets);
|
ContentGfxCache(
|
||||||
|
const Content& content,
|
||||||
|
const Assets& assets,
|
||||||
|
const GraphicsSettings& settings
|
||||||
|
);
|
||||||
~ContentGfxCache();
|
~ContentGfxCache();
|
||||||
|
|
||||||
inline const UVRegion& getRegion(blockid_t id, int side) const {
|
inline const UVRegion& getRegion(blockid_t id, int side) const {
|
||||||
@ -32,4 +42,8 @@ public:
|
|||||||
const model::Model& getModel(blockid_t id) const;
|
const model::Model& getModel(blockid_t id) const;
|
||||||
|
|
||||||
const Content* getContent() const;
|
const Content* getContent() const;
|
||||||
|
|
||||||
|
void refresh(const Block& block, const Atlas& atlas);
|
||||||
|
|
||||||
|
void refresh();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,12 +14,17 @@
|
|||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
|
|
||||||
LevelFrontend::LevelFrontend(
|
LevelFrontend::LevelFrontend(
|
||||||
Player* currentPlayer, LevelController* controller, Assets& assets
|
Player* currentPlayer,
|
||||||
) : level(*controller->getLevel()),
|
LevelController* controller,
|
||||||
|
Assets& assets,
|
||||||
|
const EngineSettings& settings
|
||||||
|
)
|
||||||
|
: level(*controller->getLevel()),
|
||||||
controller(controller),
|
controller(controller),
|
||||||
assets(assets),
|
assets(assets),
|
||||||
contentCache(std::make_unique<ContentGfxCache>(level.content, assets))
|
contentCache(std::make_unique<ContentGfxCache>(
|
||||||
{
|
*level.content, assets, settings.graphics
|
||||||
|
)) {
|
||||||
assets.store(
|
assets.store(
|
||||||
BlocksPreview::build(
|
BlocksPreview::build(
|
||||||
*contentCache, assets, *level.content->getIndices()
|
*contentCache, assets, *level.content->getIndices()
|
||||||
@ -98,6 +103,10 @@ const Assets& LevelFrontend::getAssets() const {
|
|||||||
return assets;
|
return assets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContentGfxCache& LevelFrontend::getContentGfxCache() {
|
||||||
|
return *contentCache;
|
||||||
|
}
|
||||||
|
|
||||||
const ContentGfxCache& LevelFrontend::getContentGfxCache() const {
|
const ContentGfxCache& LevelFrontend::getContentGfxCache() const {
|
||||||
return *contentCache;
|
return *contentCache;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ class Assets;
|
|||||||
class Player;
|
class Player;
|
||||||
class ContentGfxCache;
|
class ContentGfxCache;
|
||||||
class LevelController;
|
class LevelController;
|
||||||
|
struct EngineSettings;
|
||||||
|
|
||||||
class LevelFrontend {
|
class LevelFrontend {
|
||||||
Level& level;
|
Level& level;
|
||||||
@ -14,12 +15,18 @@ class LevelFrontend {
|
|||||||
const Assets& assets;
|
const Assets& assets;
|
||||||
std::unique_ptr<ContentGfxCache> contentCache;
|
std::unique_ptr<ContentGfxCache> contentCache;
|
||||||
public:
|
public:
|
||||||
LevelFrontend(Player* currentPlayer, LevelController* controller, Assets& assets);
|
LevelFrontend(
|
||||||
|
Player* currentPlayer,
|
||||||
|
LevelController* controller,
|
||||||
|
Assets& assets,
|
||||||
|
const EngineSettings& settings
|
||||||
|
);
|
||||||
~LevelFrontend();
|
~LevelFrontend();
|
||||||
|
|
||||||
Level& getLevel();
|
Level& getLevel();
|
||||||
const Level& getLevel() const;
|
const Level& getLevel() const;
|
||||||
const Assets& getAssets() const;
|
const Assets& getAssets() const;
|
||||||
const ContentGfxCache& getContentGfxCache() const;
|
const ContentGfxCache& getContentGfxCache() const;
|
||||||
|
ContentGfxCache& getContentGfxCache();
|
||||||
LevelController* getController() const;
|
LevelController* getController() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -47,6 +47,7 @@
|
|||||||
#include "window/Window.hpp"
|
#include "window/Window.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "world/World.hpp"
|
#include "world/World.hpp"
|
||||||
|
#include "debug/Logger.hpp"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -56,6 +57,8 @@
|
|||||||
|
|
||||||
using namespace gui;
|
using namespace gui;
|
||||||
|
|
||||||
|
static debug::Logger logger("hud");
|
||||||
|
|
||||||
bool Hud::showGeneratorMinimap = false;
|
bool Hud::showGeneratorMinimap = false;
|
||||||
|
|
||||||
// implemented in debug_panel.cpp
|
// implemented in debug_panel.cpp
|
||||||
@ -485,7 +488,32 @@ void Hud::openPermanent(UiDocument* doc) {
|
|||||||
add(HudElement(hud_element_mode::permanent, doc, doc->getRoot(), false));
|
add(HudElement(hud_element_mode::permanent, doc, doc->getRoot(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Hud::dropExchangeSlot() {
|
||||||
|
auto slotView = std::dynamic_pointer_cast<SlotView>(
|
||||||
|
gui->get(SlotView::EXCHANGE_SLOT_NAME)
|
||||||
|
);
|
||||||
|
if (slotView == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ItemStack& stack = slotView->getStack();
|
||||||
|
|
||||||
|
auto indices = frontend.getLevel().content->getIndices();
|
||||||
|
if (auto invView = std::dynamic_pointer_cast<InventoryView>(blockUI)) {
|
||||||
|
invView->getInventory()->move(stack, indices);
|
||||||
|
}
|
||||||
|
if (stack.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
player->getInventory()->move(stack, indices);
|
||||||
|
if (!stack.isEmpty()) {
|
||||||
|
logger.warning() << "discard item [" << stack.getItemId() << ":"
|
||||||
|
<< stack.getCount();
|
||||||
|
stack.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Hud::closeInventory() {
|
void Hud::closeInventory() {
|
||||||
|
dropExchangeSlot();
|
||||||
gui->remove(SlotView::EXCHANGE_SLOT_NAME);
|
gui->remove(SlotView::EXCHANGE_SLOT_NAME);
|
||||||
exchangeSlot = nullptr;
|
exchangeSlot = nullptr;
|
||||||
exchangeSlotInv = nullptr;
|
exchangeSlotInv = nullptr;
|
||||||
|
|||||||
@ -128,6 +128,9 @@ class Hud : public util::ObjectsKeeper {
|
|||||||
void updateHotbarControl();
|
void updateHotbarControl();
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
|
/// @brief Perform exchange slot removal when it's not empty.
|
||||||
|
void dropExchangeSlot();
|
||||||
|
|
||||||
void showExchangeSlot();
|
void showExchangeSlot();
|
||||||
void updateWorldGenDebugVisualization();
|
void updateWorldGenDebugVisualization();
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
#include "graphics/render/Decorator.hpp"
|
#include "graphics/render/Decorator.hpp"
|
||||||
#include "graphics/ui/elements/Menu.hpp"
|
#include "graphics/ui/elements/Menu.hpp"
|
||||||
#include "graphics/ui/GUI.hpp"
|
#include "graphics/ui/GUI.hpp"
|
||||||
|
#include "frontend/ContentGfxCache.hpp"
|
||||||
#include "logic/LevelController.hpp"
|
#include "logic/LevelController.hpp"
|
||||||
#include "logic/scripting/scripting_hud.hpp"
|
#include "logic/scripting/scripting_hud.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
@ -42,7 +43,7 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr<Level> levelPtr)
|
|||||||
|
|
||||||
controller = std::make_unique<LevelController>(engine, std::move(levelPtr));
|
controller = std::make_unique<LevelController>(engine, std::move(levelPtr));
|
||||||
frontend = std::make_unique<LevelFrontend>(
|
frontend = std::make_unique<LevelFrontend>(
|
||||||
controller->getPlayer(), controller.get(), assets
|
controller->getPlayer(), controller.get(), assets, settings
|
||||||
);
|
);
|
||||||
worldRenderer = std::make_unique<WorldRenderer>(
|
worldRenderer = std::make_unique<WorldRenderer>(
|
||||||
engine, *frontend, controller->getPlayer()
|
engine, *frontend, controller->getPlayer()
|
||||||
@ -57,6 +58,11 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr<Level> levelPtr)
|
|||||||
controller->getLevel()->chunks->saveAndClear();
|
controller->getLevel()->chunks->saveAndClear();
|
||||||
worldRenderer->clear();
|
worldRenderer->clear();
|
||||||
}));
|
}));
|
||||||
|
keepAlive(settings.graphics.denseRender.observe([=](bool) {
|
||||||
|
controller->getLevel()->chunks->saveAndClear();
|
||||||
|
worldRenderer->clear();
|
||||||
|
frontend->getContentGfxCache().refresh();
|
||||||
|
}));
|
||||||
keepAlive(settings.camera.fov.observe([=](double value) {
|
keepAlive(settings.camera.fov.observe([=](double value) {
|
||||||
controller->getPlayer()->fpCamera->setFov(glm::radians(value));
|
controller->getPlayer()->fpCamera->setFov(glm::radians(value));
|
||||||
}));
|
}));
|
||||||
|
|||||||
@ -25,7 +25,7 @@ GLTexture::GLTexture(const ubyte* data, uint width, uint height, ImageFormat ima
|
|||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,9 +8,6 @@
|
|||||||
#include "voxels/Chunks.hpp"
|
#include "voxels/Chunks.hpp"
|
||||||
#include "lighting/Lightmap.hpp"
|
#include "lighting/Lightmap.hpp"
|
||||||
#include "frontend/ContentGfxCache.hpp"
|
#include "frontend/ContentGfxCache.hpp"
|
||||||
#include "settings.hpp"
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
const glm::vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f);
|
const glm::vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f);
|
||||||
|
|
||||||
@ -342,41 +339,41 @@ void BlocksRenderer::blockCube(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ao) {
|
if (ao) {
|
||||||
if (isOpen(coord + Z, group)) {
|
if (isOpen(coord + Z, block)) {
|
||||||
faceAO(coord, X, Y, Z, texfaces[5], lights);
|
faceAO(coord, X, Y, Z, texfaces[5], lights);
|
||||||
}
|
}
|
||||||
if (isOpen(coord - Z, group)) {
|
if (isOpen(coord - Z, block)) {
|
||||||
faceAO(coord, -X, Y, -Z, texfaces[4], lights);
|
faceAO(coord, -X, Y, -Z, texfaces[4], lights);
|
||||||
}
|
}
|
||||||
if (isOpen(coord + Y, group)) {
|
if (isOpen(coord + Y, block)) {
|
||||||
faceAO(coord, X, -Z, Y, texfaces[3], lights);
|
faceAO(coord, X, -Z, Y, texfaces[3], lights);
|
||||||
}
|
}
|
||||||
if (isOpen(coord - Y, group)) {
|
if (isOpen(coord - Y, block)) {
|
||||||
faceAO(coord, X, Z, -Y, texfaces[2], lights);
|
faceAO(coord, X, Z, -Y, texfaces[2], lights);
|
||||||
}
|
}
|
||||||
if (isOpen(coord + X, group)) {
|
if (isOpen(coord + X, block)) {
|
||||||
faceAO(coord, -Z, Y, X, texfaces[1], lights);
|
faceAO(coord, -Z, Y, X, texfaces[1], lights);
|
||||||
}
|
}
|
||||||
if (isOpen(coord - X, group)) {
|
if (isOpen(coord - X, block)) {
|
||||||
faceAO(coord, Z, Y, -X, texfaces[0], lights);
|
faceAO(coord, Z, Y, -X, texfaces[0], lights);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isOpen(coord + Z, group)) {
|
if (isOpen(coord + Z, block)) {
|
||||||
face(coord, X, Y, Z, texfaces[5], pickLight(coord + Z), lights);
|
face(coord, X, Y, Z, texfaces[5], pickLight(coord + Z), lights);
|
||||||
}
|
}
|
||||||
if (isOpen(coord - Z, group)) {
|
if (isOpen(coord - Z, block)) {
|
||||||
face(coord, -X, Y, -Z, texfaces[4], pickLight(coord - Z), lights);
|
face(coord, -X, Y, -Z, texfaces[4], pickLight(coord - Z), lights);
|
||||||
}
|
}
|
||||||
if (isOpen(coord + Y, group)) {
|
if (isOpen(coord + Y, block)) {
|
||||||
face(coord, X, -Z, Y, texfaces[3], pickLight(coord + Y), lights);
|
face(coord, X, -Z, Y, texfaces[3], pickLight(coord + Y), lights);
|
||||||
}
|
}
|
||||||
if (isOpen(coord - Y, group)) {
|
if (isOpen(coord - Y, block)) {
|
||||||
face(coord, X, Z, -Y, texfaces[2], pickLight(coord - Y), lights);
|
face(coord, X, Z, -Y, texfaces[2], pickLight(coord - Y), lights);
|
||||||
}
|
}
|
||||||
if (isOpen(coord + X, group)) {
|
if (isOpen(coord + X, block)) {
|
||||||
face(coord, -Z, Y, X, texfaces[1], pickLight(coord + X), lights);
|
face(coord, -Z, Y, X, texfaces[1], pickLight(coord + X), lights);
|
||||||
}
|
}
|
||||||
if (isOpen(coord - X, group)) {
|
if (isOpen(coord - X, block)) {
|
||||||
face(coord, Z, Y, -X, texfaces[0], pickLight(coord - X), lights);
|
face(coord, Z, Y, -X, texfaces[0], pickLight(coord - X), lights);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
#include "graphics/core/MeshData.hpp"
|
#include "graphics/core/MeshData.hpp"
|
||||||
#include "maths/util.hpp"
|
#include "maths/util.hpp"
|
||||||
#include "commons.hpp"
|
#include "commons.hpp"
|
||||||
|
#include "settings.hpp"
|
||||||
|
|
||||||
class Content;
|
class Content;
|
||||||
class Mesh;
|
class Mesh;
|
||||||
@ -22,7 +23,6 @@ class Chunks;
|
|||||||
class VoxelsVolume;
|
class VoxelsVolume;
|
||||||
class Chunks;
|
class Chunks;
|
||||||
class ContentGfxCache;
|
class ContentGfxCache;
|
||||||
struct EngineSettings;
|
|
||||||
struct UVRegion;
|
struct UVRegion;
|
||||||
|
|
||||||
class BlocksRenderer {
|
class BlocksRenderer {
|
||||||
@ -118,7 +118,7 @@ class BlocksRenderer {
|
|||||||
bool isOpenForLight(int x, int y, int z) const;
|
bool isOpenForLight(int x, int y, int z) const;
|
||||||
|
|
||||||
// Does block allow to see other blocks sides (is it transparent)
|
// Does block allow to see other blocks sides (is it transparent)
|
||||||
inline bool isOpen(const glm::ivec3& pos, ubyte group) const {
|
inline bool isOpen(const glm::ivec3& pos, const Block& def) const {
|
||||||
auto id = voxelsBuffer->pickBlockId(
|
auto id = voxelsBuffer->pickBlockId(
|
||||||
chunk->x * CHUNK_W + pos.x, pos.y, chunk->z * CHUNK_D + pos.z
|
chunk->x * CHUNK_W + pos.x, pos.y, chunk->z * CHUNK_D + pos.z
|
||||||
);
|
);
|
||||||
@ -126,7 +126,13 @@ class BlocksRenderer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto& block = *blockDefsCache[id];
|
const auto& block = *blockDefsCache[id];
|
||||||
if ((block.drawGroup != group && block.lightPassing) || !block.rt.solid) {
|
if (((block.drawGroup != def.drawGroup) && block.drawGroup) || !block.rt.solid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ((def.culling == CullingMode::DISABLED ||
|
||||||
|
(def.culling == CullingMode::OPTIONAL &&
|
||||||
|
settings.graphics.denseRender.get())) &&
|
||||||
|
id == def.rt.id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return !id;
|
return !id;
|
||||||
|
|||||||
@ -99,9 +99,8 @@ void Decorator::update(
|
|||||||
|
|
||||||
void Decorator::update(float delta, const Camera& camera) {
|
void Decorator::update(float delta, const Camera& camera) {
|
||||||
glm::ivec3 pos = camera.position;
|
glm::ivec3 pos = camera.position;
|
||||||
pos -= glm::ivec3(UPDATE_AREA_DIAMETER / 2);
|
|
||||||
for (int i = 0; i < ITERATIONS; i++) {
|
for (int i = 0; i < ITERATIONS; i++) {
|
||||||
update(delta, pos, camera.position);
|
update(delta, pos - glm::ivec3(UPDATE_AREA_DIAMETER / 2), pos);
|
||||||
}
|
}
|
||||||
const auto& chunks = *level.chunks;
|
const auto& chunks = *level.chunks;
|
||||||
const auto& indices = *level.content->getIndices();
|
const auto& indices = *level.content->getIndices();
|
||||||
|
|||||||
@ -14,7 +14,7 @@ class Chunks;
|
|||||||
class Camera;
|
class Camera;
|
||||||
class Assets;
|
class Assets;
|
||||||
class Player;
|
class Player;
|
||||||
struct Block;
|
class Block;
|
||||||
class Engine;
|
class Engine;
|
||||||
class LevelController;
|
class LevelController;
|
||||||
class WorldRenderer;
|
class WorldRenderer;
|
||||||
|
|||||||
@ -19,12 +19,13 @@ Emitter::Emitter(
|
|||||||
)
|
)
|
||||||
: level(level),
|
: level(level),
|
||||||
origin(std::move(origin)),
|
origin(std::move(origin)),
|
||||||
prototype({this, 0, glm::vec3(), preset.velocity, preset.lifetime, region}),
|
prototype({this, 0, {}, preset.velocity, preset.lifetime, region}),
|
||||||
texture(texture),
|
texture(texture),
|
||||||
count(count),
|
count(count),
|
||||||
preset(std::move(preset)) {
|
preset(std::move(preset)) {
|
||||||
|
random.setSeed(reinterpret_cast<ptrdiff_t>(this));
|
||||||
this->prototype.emitter = this;
|
this->prototype.emitter = this;
|
||||||
timer = preset.spawnInterval;
|
timer = preset.spawnInterval * random.randFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Texture* Emitter::getTexture() const {
|
const Texture* Emitter::getTexture() const {
|
||||||
@ -76,6 +77,10 @@ void Emitter::update(
|
|||||||
count = std::max(0, count - skipped);
|
count = std::max(0, count - skipped);
|
||||||
timer -= skipped * spawnInterval;
|
timer -= skipped * spawnInterval;
|
||||||
}
|
}
|
||||||
|
if (count < 0) {
|
||||||
|
int skipped = timer / spawnInterval;
|
||||||
|
timer -= skipped * spawnInterval;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while (count && timer > spawnInterval) {
|
while (count && timer > spawnInterval) {
|
||||||
@ -83,6 +88,15 @@ void Emitter::update(
|
|||||||
Particle particle = prototype;
|
Particle particle = prototype;
|
||||||
particle.emitter = this;
|
particle.emitter = this;
|
||||||
particle.random = random.rand32();
|
particle.random = random.rand32();
|
||||||
|
if (glm::abs(preset.angleSpread) >= 0.005f) {
|
||||||
|
particle.angle =
|
||||||
|
random.randFloat() * preset.angleSpread * glm::pi<float>() * 2;
|
||||||
|
}
|
||||||
|
particle.angularVelocity =
|
||||||
|
(preset.minAngularVelocity +
|
||||||
|
random.randFloat() *
|
||||||
|
(preset.maxAngularVelocity - preset.minAngularVelocity)) *
|
||||||
|
((random.rand() % 2) * 2 - 1);
|
||||||
|
|
||||||
glm::vec3 spawnOffset = generate_coord(preset.spawnShape);
|
glm::vec3 spawnOffset = generate_coord(preset.spawnShape);
|
||||||
spawnOffset *= preset.spawnSpread;
|
spawnOffset *= preset.spawnSpread;
|
||||||
@ -103,6 +117,7 @@ void Emitter::update(
|
|||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
count--;
|
count--;
|
||||||
}
|
}
|
||||||
|
refCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,6 +129,10 @@ bool Emitter::isDead() const {
|
|||||||
return count == 0;
|
return count == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Emitter::isReferred() const {
|
||||||
|
return refCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
const EmitterOrigin& Emitter::getOrigin() const {
|
const EmitterOrigin& Emitter::getOrigin() const {
|
||||||
return origin;
|
return origin;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,10 @@ struct Particle {
|
|||||||
float lifetime;
|
float lifetime;
|
||||||
/// @brief UV region
|
/// @brief UV region
|
||||||
UVRegion region;
|
UVRegion region;
|
||||||
|
/// @brief Current rotation angle
|
||||||
|
float angle;
|
||||||
|
/// @brief Angular velocity
|
||||||
|
float angularVelocity;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Texture;
|
class Texture;
|
||||||
@ -39,7 +43,7 @@ class Emitter {
|
|||||||
EmitterOrigin origin;
|
EmitterOrigin origin;
|
||||||
/// @brief Particle prototype
|
/// @brief Particle prototype
|
||||||
Particle prototype;
|
Particle prototype;
|
||||||
/// @brief Particle
|
/// @brief Particle texture
|
||||||
const Texture* texture;
|
const Texture* texture;
|
||||||
/// @brief Number of particles should be spawned before emitter deactivation.
|
/// @brief Number of particles should be spawned before emitter deactivation.
|
||||||
/// -1 is infinite.
|
/// -1 is infinite.
|
||||||
@ -50,6 +54,9 @@ class Emitter {
|
|||||||
|
|
||||||
util::PseudoRandom random;
|
util::PseudoRandom random;
|
||||||
public:
|
public:
|
||||||
|
/// @brief Number of references (alive particles)
|
||||||
|
int refCount = 0;
|
||||||
|
/// @brief Particle settings
|
||||||
ParticlesPreset preset;
|
ParticlesPreset preset;
|
||||||
|
|
||||||
Emitter(
|
Emitter(
|
||||||
@ -82,6 +89,9 @@ public:
|
|||||||
/// @return true if the emitter has spawned all particles
|
/// @return true if the emitter has spawned all particles
|
||||||
bool isDead() const;
|
bool isDead() const;
|
||||||
|
|
||||||
|
/// @return true if there is at least one alive referring particle left
|
||||||
|
bool isReferred() const;
|
||||||
|
|
||||||
const EmitterOrigin& getOrigin() const;
|
const EmitterOrigin& getOrigin() const;
|
||||||
|
|
||||||
void setOrigin(const EmitterOrigin& origin);
|
void setOrigin(const EmitterOrigin& origin);
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
struct ItemDef;
|
struct ItemDef;
|
||||||
class Assets;
|
class Assets;
|
||||||
class Content;
|
class Content;
|
||||||
struct Block;
|
class Block;
|
||||||
|
|
||||||
class ModelsGenerator {
|
class ModelsGenerator {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -31,12 +31,14 @@ static inline void update_particle(
|
|||||||
const auto& preset = particle.emitter->preset;
|
const auto& preset = particle.emitter->preset;
|
||||||
auto& pos = particle.position;
|
auto& pos = particle.position;
|
||||||
auto& vel = particle.velocity;
|
auto& vel = particle.velocity;
|
||||||
|
auto& angle = particle.angle;
|
||||||
|
|
||||||
vel += delta * preset.acceleration;
|
vel += delta * preset.acceleration;
|
||||||
if (preset.collision && chunks.isObstacleAt(pos + vel * delta)) {
|
if (preset.collision && chunks.isObstacleAt(pos + vel * delta)) {
|
||||||
vel *= 0.0f;
|
vel *= 0.0f;
|
||||||
}
|
}
|
||||||
pos += vel * delta;
|
pos += vel * delta;
|
||||||
|
angle += particle.angularVelocity * delta;
|
||||||
particle.lifetime -= delta;
|
particle.lifetime -= delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +63,8 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
|||||||
auto iter = vec.begin();
|
auto iter = vec.begin();
|
||||||
while (iter != vec.end()) {
|
while (iter != vec.end()) {
|
||||||
auto& particle = *iter;
|
auto& particle = *iter;
|
||||||
auto& preset = particle.emitter->preset;
|
auto& emitter = *particle.emitter;
|
||||||
|
auto& preset = emitter.preset;
|
||||||
|
|
||||||
if (!preset.frames.empty()) {
|
if (!preset.frames.empty()) {
|
||||||
float time = preset.lifetime - particle.lifetime;
|
float time = preset.lifetime - particle.lifetime;
|
||||||
@ -82,19 +85,52 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
|||||||
}
|
}
|
||||||
update_particle(particle, delta, chunks);
|
update_particle(particle, delta, chunks);
|
||||||
|
|
||||||
|
float scale = 1.0f + ((particle.random ^ 2628172) % 1000) *
|
||||||
|
0.001f * preset.sizeSpread;
|
||||||
|
|
||||||
glm::vec4 light(1, 1, 1, 0);
|
glm::vec4 light(1, 1, 1, 0);
|
||||||
if (preset.lighting) {
|
if (preset.lighting) {
|
||||||
light = MainBatch::sampleLight(
|
light = MainBatch::sampleLight(
|
||||||
particle.position, chunks, backlight
|
particle.position,
|
||||||
|
chunks,
|
||||||
|
backlight
|
||||||
);
|
);
|
||||||
|
auto size = glm::max(glm::vec3(0.5f), preset.size * scale);
|
||||||
|
for (int x = -1; x <= 1; x++) {
|
||||||
|
for (int y = -1; y <= 1; y++) {
|
||||||
|
for (int z = -1; z <= 1; z++) {
|
||||||
|
light = glm::max(
|
||||||
|
light,
|
||||||
|
MainBatch::sampleLight(
|
||||||
|
particle.position -
|
||||||
|
size * glm::vec3(x, y, z),
|
||||||
|
chunks,
|
||||||
|
backlight
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
light *= 0.9f + (particle.random % 100) * 0.001f;
|
light *= 0.9f + (particle.random % 100) * 0.001f;
|
||||||
}
|
}
|
||||||
float scale = 1.0f + ((particle.random ^ 2628172) % 1000) *
|
|
||||||
0.001f * preset.sizeSpread;
|
|
||||||
|
glm::vec3 localRight = right;
|
||||||
|
glm::vec3 localUp = preset.globalUpVector ? glm::vec3(0, 1, 0) : up;
|
||||||
|
float angle = particle.angle;
|
||||||
|
if (glm::abs(angle) >= 0.005f) {
|
||||||
|
glm::vec3 rotatedRight(glm::cos(angle), -glm::sin(angle), 0.0f);
|
||||||
|
glm::vec3 rotatedUp(glm::sin(angle), glm::cos(angle), 0.0f);
|
||||||
|
|
||||||
|
localRight = right * rotatedRight.x + localUp * rotatedRight.y +
|
||||||
|
camera.front * rotatedRight.z;
|
||||||
|
localUp = right * rotatedUp.x + localUp * rotatedUp.y +
|
||||||
|
camera.front * rotatedUp.z;
|
||||||
|
}
|
||||||
batch->quad(
|
batch->quad(
|
||||||
particle.position,
|
particle.position,
|
||||||
right,
|
localRight,
|
||||||
preset.globalUpVector ? glm::vec3(0, 1, 0) : up,
|
localUp,
|
||||||
preset.size * scale,
|
preset.size * scale,
|
||||||
light,
|
light,
|
||||||
glm::vec3(1.0f),
|
glm::vec3(1.0f),
|
||||||
@ -102,6 +138,7 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
|||||||
);
|
);
|
||||||
if (particle.lifetime <= 0.0f) {
|
if (particle.lifetime <= 0.0f) {
|
||||||
iter = vec.erase(iter);
|
iter = vec.erase(iter);
|
||||||
|
emitter.refCount--;
|
||||||
} else {
|
} else {
|
||||||
iter++;
|
iter++;
|
||||||
}
|
}
|
||||||
@ -124,19 +161,15 @@ void ParticlesRenderer::render(const Camera& camera, float delta) {
|
|||||||
auto iter = emitters.begin();
|
auto iter = emitters.begin();
|
||||||
while (iter != emitters.end()) {
|
while (iter != emitters.end()) {
|
||||||
auto& emitter = *iter->second;
|
auto& emitter = *iter->second;
|
||||||
auto texture = emitter.getTexture();
|
if (emitter.isDead() && !emitter.isReferred()) {
|
||||||
const auto& found = particles.find(texture);
|
|
||||||
std::vector<Particle>* vec;
|
|
||||||
if (found == particles.end()) {
|
|
||||||
if (emitter.isDead()) {
|
|
||||||
// destruct Emitter only when there is no particles spawned by it
|
// destruct Emitter only when there is no particles spawned by it
|
||||||
iter = emitters.erase(iter);
|
iter = emitters.erase(iter);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
auto texture = emitter.getTexture();
|
||||||
|
const auto& found = particles.find(texture);
|
||||||
|
std::vector<Particle>* vec;
|
||||||
vec = &particles[texture];
|
vec = &particles[texture];
|
||||||
} else {
|
|
||||||
vec = &found->second;
|
|
||||||
}
|
|
||||||
emitter.update(delta, camera.position, *vec);
|
emitter.update(delta, camera.position, *vec);
|
||||||
iter++;
|
iter++;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -149,27 +149,6 @@ static int l_read_bytes(lua::State* L) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void read_bytes_from_table(
|
|
||||||
lua::State* L, int tableIndex, std::vector<ubyte>& bytes
|
|
||||||
) {
|
|
||||||
if (!lua::istable(L, tableIndex)) {
|
|
||||||
throw std::runtime_error("table expected");
|
|
||||||
} else {
|
|
||||||
size_t size = lua::objlen(L, tableIndex);
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
|
||||||
lua::rawgeti(L, i + 1, tableIndex);
|
|
||||||
const int byte = lua::tointeger(L, -1);
|
|
||||||
lua::pop(L);
|
|
||||||
if (byte < 0 || byte > 255) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
"invalid byte '" + std::to_string(byte) + "'"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
bytes.push_back(byte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int l_write_bytes(lua::State* L) {
|
static int l_write_bytes(lua::State* L) {
|
||||||
fs::path path = get_writeable_path(L);
|
fs::path path = get_writeable_path(L);
|
||||||
|
|
||||||
@ -181,7 +160,7 @@ static int l_write_bytes(lua::State* L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ubyte> bytes;
|
std::vector<ubyte> bytes;
|
||||||
read_bytes_from_table(L, 2, bytes);
|
lua::read_bytes_from_table(L, 2, bytes);
|
||||||
return lua::pushboolean(
|
return lua::pushboolean(
|
||||||
L, files::write_bytes(path, bytes.data(), bytes.size())
|
L, files::write_bytes(path, bytes.data(), bytes.size())
|
||||||
);
|
);
|
||||||
@ -223,7 +202,7 @@ static int l_list(lua::State* L) {
|
|||||||
static int l_gzip_compress(lua::State* L) {
|
static int l_gzip_compress(lua::State* L) {
|
||||||
std::vector<ubyte> bytes;
|
std::vector<ubyte> bytes;
|
||||||
|
|
||||||
read_bytes_from_table(L, 1, bytes);
|
lua::read_bytes_from_table(L, 1, bytes);
|
||||||
auto compressed_bytes = gzip::compress(bytes.data(), bytes.size());
|
auto compressed_bytes = gzip::compress(bytes.data(), bytes.size());
|
||||||
int newTable = lua::gettop(L);
|
int newTable = lua::gettop(L);
|
||||||
|
|
||||||
@ -237,7 +216,7 @@ static int l_gzip_compress(lua::State* L) {
|
|||||||
static int l_gzip_decompress(lua::State* L) {
|
static int l_gzip_decompress(lua::State* L) {
|
||||||
std::vector<ubyte> bytes;
|
std::vector<ubyte> bytes;
|
||||||
|
|
||||||
read_bytes_from_table(L, 1, bytes);
|
lua::read_bytes_from_table(L, 1, bytes);
|
||||||
auto decompressed_bytes = gzip::decompress(bytes.data(), bytes.size());
|
auto decompressed_bytes = gzip::decompress(bytes.data(), bytes.size());
|
||||||
int newTable = lua::gettop(L);
|
int newTable = lua::gettop(L);
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <typeindex>
|
#include <typeindex>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
|
#include <stdexcept>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "data/dv.hpp"
|
#include "data/dv.hpp"
|
||||||
@ -698,4 +699,25 @@ namespace lua {
|
|||||||
}
|
}
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void read_bytes_from_table(
|
||||||
|
lua::State* L, int tableIndex, std::vector<ubyte>& bytes
|
||||||
|
) {
|
||||||
|
if (!lua::istable(L, tableIndex)) {
|
||||||
|
throw std::runtime_error("table expected");
|
||||||
|
} else {
|
||||||
|
size_t size = lua::objlen(L, tableIndex);
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
lua::rawgeti(L, i + 1, tableIndex);
|
||||||
|
const int byte = lua::tointeger(L, -1);
|
||||||
|
lua::pop(L);
|
||||||
|
if (byte < 0 || byte > 255) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"invalid byte '" + std::to_string(byte) + "'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
bytes.push_back(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "util/listutil.hpp"
|
||||||
#include "../lua_util.hpp"
|
#include "../lua_util.hpp"
|
||||||
|
|
||||||
using namespace lua;
|
using namespace lua;
|
||||||
@ -18,8 +19,16 @@ LuaBytearray::~LuaBytearray() {
|
|||||||
|
|
||||||
static int l_append(lua::State* L) {
|
static int l_append(lua::State* L) {
|
||||||
if (auto buffer = touserdata<LuaBytearray>(L, 1)) {
|
if (auto buffer = touserdata<LuaBytearray>(L, 1)) {
|
||||||
|
if (lua::isnumber(L, 2)) {
|
||||||
auto value = tointeger(L, 2);
|
auto value = tointeger(L, 2);
|
||||||
buffer->data().push_back(static_cast<ubyte>(value));
|
buffer->data().push_back(static_cast<ubyte>(value));
|
||||||
|
} else if (lua::istable(L, 2)) {
|
||||||
|
lua::read_bytes_from_table(L, 2, buffer->data());
|
||||||
|
} else if (auto extension = lua::touserdata<LuaBytearray>(L, 2)) {
|
||||||
|
util::concat(buffer->data(), extension->data());
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("integer/table/Bytearray expected");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -34,8 +43,19 @@ static int l_insert(lua::State* L) {
|
|||||||
if (static_cast<size_t>(index) > data.size()) {
|
if (static_cast<size_t>(index) > data.size()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (lua::isnumber(L, 3)) {
|
||||||
auto value = tointeger(L, 3);
|
auto value = tointeger(L, 3);
|
||||||
data.insert(data.begin() + index, static_cast<ubyte>(value));
|
data.insert(data.begin() + index, static_cast<ubyte>(value));
|
||||||
|
} else if (lua::istable(L, 3)) {
|
||||||
|
std::vector<ubyte> temp;
|
||||||
|
lua::read_bytes_from_table(L, 3, temp);
|
||||||
|
data.insert(data.begin() + index, temp.begin(), temp.end());
|
||||||
|
} else if (auto extension = lua::touserdata<LuaBytearray>(L, 3)) {
|
||||||
|
const std::vector<ubyte>& src = extension->data();
|
||||||
|
data.insert(data.begin() + index, src.begin(), src.end());
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("integer/table/Bytearray expected");
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
30
src/maths/UVFace.hpp
Normal file
30
src/maths/UVFace.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#include "UVRegion.hpp"
|
||||||
|
|
||||||
|
struct UVFace {
|
||||||
|
std::array<glm::vec2, 4> points;
|
||||||
|
|
||||||
|
UVFace(const UVRegion& region) {
|
||||||
|
points[0] = {region.u1, region.v1};
|
||||||
|
points[1] = {region.u2, region.v1};
|
||||||
|
points[2] = {region.u2, region.v2};
|
||||||
|
points[3] = {region.u1, region.v2};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int n>
|
||||||
|
inline void rotate() {
|
||||||
|
int times = n % 4;
|
||||||
|
if (times < 0) {
|
||||||
|
times += 4;
|
||||||
|
}
|
||||||
|
std::array<glm::vec2, 4> temp = points;
|
||||||
|
points[0] = temp[times];
|
||||||
|
points[1] = temp[(times + 1) % 4];
|
||||||
|
points[2] = temp[(times + 2) % 4];
|
||||||
|
points[3] = temp[(times + 3) % 4];
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -9,6 +9,12 @@
|
|||||||
#include <glm/gtx/norm.hpp>
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
namespace util {
|
namespace util {
|
||||||
|
inline uint64_t shuffle_bits_step(uint64_t x, uint64_t m, unsigned shift) {
|
||||||
|
uint64_t t = ((x >> shift) ^ x) & m;
|
||||||
|
x = (x ^ t) ^ (t << shift);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr inline float EPSILON = 1e-6f;
|
constexpr inline float EPSILON = 1e-6f;
|
||||||
|
|
||||||
class PseudoRandom {
|
class PseudoRandom {
|
||||||
@ -57,17 +63,20 @@ namespace util {
|
|||||||
return randU64() / static_cast<double>(UINT64_MAX);
|
return randU64() / static_cast<double>(UINT64_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSeed(int number) {
|
|
||||||
seed = (static_cast<unsigned short>(number * 23729) ^
|
|
||||||
static_cast<unsigned short>(number + 16786));
|
|
||||||
rand();
|
|
||||||
}
|
|
||||||
void setSeed(int number1, int number2) {
|
void setSeed(int number1, int number2) {
|
||||||
seed = ((static_cast<unsigned short>(number1 * 23729) |
|
seed = ((static_cast<unsigned short>(number1 * 23729) |
|
||||||
static_cast<unsigned short>(number2 % 16786)) ^
|
static_cast<unsigned short>(number2 % 16786)) ^
|
||||||
static_cast<unsigned short>(number2 * number1));
|
static_cast<unsigned short>(number2 * number1));
|
||||||
rand();
|
rand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setSeed(long number) {
|
||||||
|
number = shuffle_bits_step(number, 0x2222222222222222ull, 1);
|
||||||
|
number = shuffle_bits_step(number, 0x0c0c0c0c0c0c0c0cull, 2);
|
||||||
|
number = shuffle_bits_step(number, 0x00f000f000f000f0ull, 4);
|
||||||
|
seed = number;
|
||||||
|
rand();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|||||||
@ -43,6 +43,9 @@ dv::value ParticlesPreset::serialize() const {
|
|||||||
root["explosion"] = dv::to_value(explosion);
|
root["explosion"] = dv::to_value(explosion);
|
||||||
root["size"] = dv::to_value(size);
|
root["size"] = dv::to_value(size);
|
||||||
root["size_spread"] = sizeSpread;
|
root["size_spread"] = sizeSpread;
|
||||||
|
root["angle_spread"] = angleSpread;
|
||||||
|
root["min_angular_vel"] = minAngularVelocity;
|
||||||
|
root["max_angular_vel"] = maxAngularVelocity;
|
||||||
root["spawn_spread"] = dv::to_value(size);
|
root["spawn_spread"] = dv::to_value(size);
|
||||||
root["spawn_shape"] = to_string(spawnShape);
|
root["spawn_shape"] = to_string(spawnShape);
|
||||||
root["random_sub_uv"] = randomSubUV;
|
root["random_sub_uv"] = randomSubUV;
|
||||||
@ -58,6 +61,9 @@ void ParticlesPreset::deserialize(const dv::value& src) {
|
|||||||
src.at("spawn_interval").get(spawnInterval);
|
src.at("spawn_interval").get(spawnInterval);
|
||||||
src.at("lifetime").get(lifetime);
|
src.at("lifetime").get(lifetime);
|
||||||
src.at("lifetime_spread").get(lifetimeSpread);
|
src.at("lifetime_spread").get(lifetimeSpread);
|
||||||
|
src.at("angle_spread").get(angleSpread);
|
||||||
|
src.at("min_angular_vel").get(minAngularVelocity);
|
||||||
|
src.at("max_angular_vel").get(maxAngularVelocity);
|
||||||
src.at("random_sub_uv").get(randomSubUV);
|
src.at("random_sub_uv").get(randomSubUV);
|
||||||
if (src.has("velocity")) {
|
if (src.has("velocity")) {
|
||||||
dv::get_vec(src["velocity"], velocity);
|
dv::get_vec(src["velocity"], velocity);
|
||||||
|
|||||||
@ -27,7 +27,7 @@ struct ParticlesPreset : public Serializable {
|
|||||||
/// @brief Use global up vector instead of camera-dependent one
|
/// @brief Use global up vector instead of camera-dependent one
|
||||||
bool globalUpVector = false;
|
bool globalUpVector = false;
|
||||||
/// @brief Max distance of actually spawning particles.
|
/// @brief Max distance of actually spawning particles.
|
||||||
float maxDistance = 16.0f;
|
float maxDistance = 32.0f;
|
||||||
/// @brief Particles spawn interval
|
/// @brief Particles spawn interval
|
||||||
float spawnInterval = 0.1f;
|
float spawnInterval = 0.1f;
|
||||||
/// @brief Particle life time
|
/// @brief Particle life time
|
||||||
@ -44,6 +44,12 @@ struct ParticlesPreset : public Serializable {
|
|||||||
glm::vec3 size {0.1f};
|
glm::vec3 size {0.1f};
|
||||||
/// @brief Particles size spread
|
/// @brief Particles size spread
|
||||||
float sizeSpread = 0.2f;
|
float sizeSpread = 0.2f;
|
||||||
|
/// @brief Random initial angle spread
|
||||||
|
float angleSpread = 0.0f;
|
||||||
|
/// @brief Minimum angular velocity
|
||||||
|
float minAngularVelocity = 0.0f;
|
||||||
|
/// @brief Maximum angular velocity
|
||||||
|
float maxAngularVelocity = 0.0f;
|
||||||
/// @brief Spawn spread shape
|
/// @brief Spawn spread shape
|
||||||
ParticleSpawnShape spawnShape = BALL;
|
ParticleSpawnShape spawnShape = BALL;
|
||||||
/// @brief Spawn spread
|
/// @brief Spawn spread
|
||||||
|
|||||||
@ -63,16 +63,22 @@ struct GraphicsSettings {
|
|||||||
NumberSetting gamma {1.0f, 0.4f, 1.0f};
|
NumberSetting gamma {1.0f, 0.4f, 1.0f};
|
||||||
/// @brief Enable blocks backlight to prevent complete darkness
|
/// @brief Enable blocks backlight to prevent complete darkness
|
||||||
FlagSetting backlight {true};
|
FlagSetting backlight {true};
|
||||||
|
/// @brief Disable culling with 'optional' mode
|
||||||
|
FlagSetting denseRender {true};
|
||||||
/// @brief Enable chunks frustum culling
|
/// @brief Enable chunks frustum culling
|
||||||
FlagSetting frustumCulling {true};
|
FlagSetting frustumCulling {true};
|
||||||
|
/// @brief Skybox texture face resolution
|
||||||
IntegerSetting skyboxResolution {64 + 32, 64, 128};
|
IntegerSetting skyboxResolution {64 + 32, 64, 128};
|
||||||
|
/// @brief Chunk renderer vertices buffer capacity
|
||||||
IntegerSetting chunkMaxVertices {200'000, 0, 4'000'000};
|
IntegerSetting chunkMaxVertices {200'000, 0, 4'000'000};
|
||||||
|
/// @brief Limit of chunk renderers count
|
||||||
IntegerSetting chunkMaxRenderers {6, -4, 32};
|
IntegerSetting chunkMaxRenderers {6, -4, 32};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DebugSettings {
|
struct DebugSettings {
|
||||||
/// @brief Turns off chunks saving/loading
|
/// @brief Turns off chunks saving/loading
|
||||||
FlagSetting generatorTestMode {false};
|
FlagSetting generatorTestMode {false};
|
||||||
|
/// @brief Write lights cache
|
||||||
FlagSetting doWriteLights {true};
|
FlagSetting doWriteLights {true};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -49,6 +49,30 @@ std::optional<BlockModel> BlockModel_from(std::string_view str) {
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string to_string(CullingMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case CullingMode::DEFAULT:
|
||||||
|
return "default";
|
||||||
|
case CullingMode::OPTIONAL:
|
||||||
|
return "optional";
|
||||||
|
case CullingMode::DISABLED:
|
||||||
|
return "disabled";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<CullingMode> CullingMode_from(std::string_view str) {
|
||||||
|
if (str == "default") {
|
||||||
|
return CullingMode::DEFAULT;
|
||||||
|
} else if (str == "optional") {
|
||||||
|
return CullingMode::OPTIONAL;
|
||||||
|
} else if (str == "disabled") {
|
||||||
|
return CullingMode::DISABLED;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
CoordSystem::CoordSystem(glm::ivec3 axisX, glm::ivec3 axisY, glm::ivec3 axisZ)
|
CoordSystem::CoordSystem(glm::ivec3 axisX, glm::ivec3 axisY, glm::ivec3 axisZ)
|
||||||
: axisX(axisX), axisY(axisY), axisZ(axisZ) {
|
: axisX(axisX), axisY(axisY), axisZ(axisZ) {
|
||||||
fix = glm::ivec3(0);
|
fix = glm::ivec3(0);
|
||||||
|
|||||||
@ -97,6 +97,15 @@ enum class BlockModel {
|
|||||||
std::string to_string(BlockModel model);
|
std::string to_string(BlockModel model);
|
||||||
std::optional<BlockModel> BlockModel_from(std::string_view str);
|
std::optional<BlockModel> BlockModel_from(std::string_view str);
|
||||||
|
|
||||||
|
enum class CullingMode {
|
||||||
|
DEFAULT,
|
||||||
|
OPTIONAL,
|
||||||
|
DISABLED,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string to_string(CullingMode mode);
|
||||||
|
std::optional<CullingMode> CullingMode_from(std::string_view str);
|
||||||
|
|
||||||
using BoxModel = AABB;
|
using BoxModel = AABB;
|
||||||
|
|
||||||
/// @brief Common kit of block properties applied to groups of blocks
|
/// @brief Common kit of block properties applied to groups of blocks
|
||||||
@ -142,6 +151,9 @@ public:
|
|||||||
|
|
||||||
std::string modelName = "";
|
std::string modelName = "";
|
||||||
|
|
||||||
|
/// @brief Culling mode
|
||||||
|
CullingMode culling = CullingMode::DEFAULT;
|
||||||
|
|
||||||
/// @brief Does the block passing lights into itself
|
/// @brief Does the block passing lights into itself
|
||||||
bool lightPassing = false;
|
bool lightPassing = false;
|
||||||
|
|
||||||
@ -181,7 +193,7 @@ public:
|
|||||||
bool translucent = false;
|
bool translucent = false;
|
||||||
|
|
||||||
/// @brief Set of block physical hitboxes
|
/// @brief Set of block physical hitboxes
|
||||||
std::vector<AABB> hitboxes;
|
std::vector<AABB> hitboxes {AABB()};
|
||||||
|
|
||||||
/// @brief Set of available block rotations (coord-systems)
|
/// @brief Set of available block rotations (coord-systems)
|
||||||
BlockRotProfile rotations = BlockRotProfile::NONE;
|
BlockRotProfile rotations = BlockRotProfile::NONE;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user