mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-14 07:21:51 +00:00
Merge branch 'MihailRis:main' into main
This commit is contained in:
commit
3e20166401
119 changed files with 1501 additions and 957 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -49,3 +49,6 @@ appimage-build/
|
||||||
# libs
|
# libs
|
||||||
/libs/
|
/libs/
|
||||||
/vcpkg_installed/
|
/vcpkg_installed/
|
||||||
|
|
||||||
|
# vcstudio
|
||||||
|
.vcstudio
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ Installing last version that supports C++17.
|
||||||
```sh
|
```sh
|
||||||
git clone --branch v3.16.0 https://github.com/skypjack/entt.git
|
git clone --branch v3.16.0 https://github.com/skypjack/entt.git
|
||||||
cd entt
|
cd entt
|
||||||
mkdir build && cd build
|
mkdir -p build && cd build
|
||||||
cmake -DCMAKE_BUILD_TYPE=Release -DENTT_INSTALL=ON ..
|
cmake -DCMAKE_BUILD_TYPE=Release -DENTT_INSTALL=ON ..
|
||||||
sudo make install
|
sudo make install
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ Rotation profile (set of available block rotations and behaviour of placing bloc
|
||||||
- "pipe" - wood logs, pipes, pillars
|
- "pipe" - wood logs, pipes, pillars
|
||||||
- "pane" - panels, doors, signs
|
- "pane" - panels, doors, signs
|
||||||
- "stairs" - "pane" + flipped variants
|
- "stairs" - "pane" + flipped variants
|
||||||
|
- "ladder" - like a "pipe", but on horizontal surfaces it is installed like "pane"
|
||||||
|
|
||||||
### *particles*
|
### *particles*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ Content-pack folder must contain file **package.json** with following contents:
|
||||||
{
|
{
|
||||||
"id": "pack_id",
|
"id": "pack_id",
|
||||||
"title": "pack name will be displayed in the content menu",
|
"title": "pack name will be displayed in the content menu",
|
||||||
"version": "content-pack version - major.minor",
|
"version": "content-pack version - major.minor.[patch])",
|
||||||
"creator": "content-pack creator",
|
"creator": "content-pack creator",
|
||||||
"description": "short description",
|
"description": "short description",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
|
@ -26,6 +26,7 @@ Dependency levels are indicated by prefixes in the name:
|
||||||
- '!' - required dependency
|
- '!' - required dependency
|
||||||
- '?' - optional dependency
|
- '?' - optional dependency
|
||||||
- '~' - weak dependency
|
- '~' - weak dependency
|
||||||
|
|
||||||
If prefix is not specified, '!' level will be used.
|
If prefix is not specified, '!' level will be used.
|
||||||
|
|
||||||
Example: '~randutil' - weak dependency 'randutil'.
|
Example: '~randutil' - weak dependency 'randutil'.
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ gui.confirm(
|
||||||
## Documents and templates
|
## Documents and templates
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- Loads a UI document with its script, returns the name of the document if successfully loaded.
|
-- Loads a UI document with its script. Returns document environment table
|
||||||
gui.load_document(
|
gui.load_document(
|
||||||
-- Path to the xml file of the page. Example: `core:layouts/pages/main.xml`
|
-- Path to the xml file of the page. Example: `core:layouts/pages/main.xml`
|
||||||
path: str,
|
path: str,
|
||||||
|
|
@ -147,7 +147,7 @@ gui.load_document(
|
||||||
name: str
|
name: str
|
||||||
-- Table of parameters passed to the on_open event
|
-- Table of parameters passed to the on_open event
|
||||||
args: table
|
args: table
|
||||||
) -> str
|
) -> table
|
||||||
|
|
||||||
-- Loads and processes layout template from file
|
-- Loads and processes layout template from file
|
||||||
gui.template(
|
gui.template(
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,10 @@ hud.get_block_inventory() -> int
|
||||||
-- Gives the ID of the second open inventory (block, virtual inventory...) or 0.
|
-- Gives the ID of the second open inventory (block, virtual inventory...) or 0.
|
||||||
hud.get_second_inventory() -> int
|
hud.get_second_inventory() -> int
|
||||||
|
|
||||||
|
-- Get the inventory ID of exchange item (grabbed item in inventory) or 0.
|
||||||
|
-- It has only one slot - 0
|
||||||
|
hud.get_exchange_inventory() -> int
|
||||||
|
|
||||||
-- Gives the ID of the player that the UI is bound to.
|
-- Gives the ID of the player that the UI is bound to.
|
||||||
hud.get_player() -> int
|
hud.get_player() -> int
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,36 @@
|
||||||
|
|
||||||
Quaternions manipulation library.
|
Quaternions manipulation library.
|
||||||
|
|
||||||
## Quaternion from matrix - *mat4.from_quat(...)*
|
Quaternions can be created as a table of 4 numbers:
|
||||||
|
`{w, x, y, z}`
|
||||||
|
|
||||||
|
## Quaternion from matrix - *quat.from_mat4(...)*
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- creates a quaternion based on the rotation matrix
|
-- creates a quaternion based on the rotation matrix
|
||||||
quat.from_mat4(m: matrix)
|
quat.from_mat4(m: matrix, [optional] dst: quat) -> quat
|
||||||
|
```
|
||||||
|
|
||||||
-- writes a quaternion from the rotation matrix to dst
|
## Quaternion of Euler angles - *quat.from_euler(...)*
|
||||||
quat.from_mat4(m: matrix, dst: quat)
|
|
||||||
|
```lua
|
||||||
|
-- creates quaternion from euler angles passed as XYZ (pitch, yaw, roll)
|
||||||
|
-- angles in degrees
|
||||||
|
quat.from_euler(euler: vec3, [optional] dst: quat) -> quat
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quaternion composition - quat.mul(...)
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- multiplies quaternions
|
||||||
|
quat.mul(a: quat, b: quat, [optional] dst: quat) -> quat
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3D vector rotation - quat.mul_vec3(...)
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- rotates vector by quaternion
|
||||||
|
quat.mul_vec3(a: quat, b: vec3, [optional] dst: vec3) -> vec3
|
||||||
```
|
```
|
||||||
|
|
||||||
## Spherical linear interpolation - *quat.slerp(...)*
|
## Spherical linear interpolation - *quat.slerp(...)*
|
||||||
|
|
@ -19,16 +41,12 @@ The interpolation always take the short path and the rotation is performed at co
|
||||||
```lua
|
```lua
|
||||||
-- creates a quaternion as an interpolation between a and b,
|
-- creates a quaternion as an interpolation between a and b,
|
||||||
-- where t is interpolation factor
|
-- where t is interpolation factor
|
||||||
quat.slerp(a: quat, b: quat, t: number)
|
quat.slerp(a: quat, b: quat, t: number, [optional] dst: quat) -> quat
|
||||||
|
|
||||||
-- writes a quaternion as an interpolation between a and b to dst,
|
|
||||||
-- where t is interpolation factor
|
|
||||||
quat.slerp(a: quat, b: quat, t: number, dst: quat)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Casting to string - *quat.tostring(...)*
|
## Casting to string - *quat.tostring(...)*
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- returns a string representing the contents of the quaternion
|
-- returns a string representing the contents of the quaternion
|
||||||
quat.tostring(q: quat)
|
quat.tostring(q: quat) -> string
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ Called on block broken by player
|
||||||
function on_replaced(x, y, z, playerid)
|
function on_replaced(x, y, z, playerid)
|
||||||
```
|
```
|
||||||
|
|
||||||
Called on block replaced with other by player
|
Called before a player replaces a block
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function on_interact(x, y, z, playerid) -> bool
|
function on_interact(x, y, z, playerid) -> bool
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ Properties that apply to all elements:
|
||||||
| contentOffset | vec2 | yes | *no* | element content offset |
|
| contentOffset | vec2 | yes | *no* | element content offset |
|
||||||
| cursor | string | yes | yes | cursor displayed on hover |
|
| cursor | string | yes | yes | cursor displayed on hover |
|
||||||
| parent | Element | yes | *no* | parent element or nil |
|
| parent | Element | yes | *no* | parent element or nil |
|
||||||
| zIndex | bool | yes | *no* | z-index value. In panel, it determines the order |
|
| zIndex | int | yes | *no* | z-index value. In panel, it determines the order |
|
||||||
|
|
||||||
Common element methods:
|
Common element methods:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -212,10 +212,27 @@ local map = Heightmap(width, height)
|
||||||
Operations apply to all height values.
|
Operations apply to all height values.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
-- Convert to absolute values
|
||||||
map:abs()
|
map:abs()
|
||||||
```
|
|
||||||
|
|
||||||
Casts height values to absolute.
|
-- Round down
|
||||||
|
map:floor()
|
||||||
|
|
||||||
|
-- Round up
|
||||||
|
map:ceil()
|
||||||
|
|
||||||
|
-- Round to the nearest integer
|
||||||
|
map:round()
|
||||||
|
|
||||||
|
-- Sine of height values (in radians)
|
||||||
|
map:sin()
|
||||||
|
|
||||||
|
-- Cosine of height values (in radians)
|
||||||
|
map:cos()
|
||||||
|
|
||||||
|
-- Tangent of height values (in radians)
|
||||||
|
map:tan()
|
||||||
|
```
|
||||||
|
|
||||||
### Binary Operations
|
### Binary Operations
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@ Examples:
|
||||||
- `position-func` - position supplier for an element (two numbers), called on every parent container size update or on element adding on a container. May be called before *on_hud_open*
|
- `position-func` - position supplier for an element (two numbers), called on every parent container size update or on element adding on a container. May be called before *on_hud_open*
|
||||||
- `size-func` - element size provider (two numbers), called when the size of the container in which the element is located changes, or when an element is added to the container. Can be called before on_hud_open is called.
|
- `size-func` - element size provider (two numbers), called when the size of the container in which the element is located changes, or when an element is added to the container. Can be called before on_hud_open is called.
|
||||||
- `onclick` - lua function called when an element is clicked.
|
- `onclick` - lua function called when an element is clicked.
|
||||||
|
- `onrightclick` - lua function called when the right mouse button is pressed on an element.
|
||||||
|
- `onmiddleclick` - lua function called when the middle mouse button is pressed on an element.
|
||||||
- `ondoubleclick` - lua function called when you double click on an element.
|
- `ondoubleclick` - lua function called when you double click on an element.
|
||||||
- `onfocus` - lua function called when focusing on an element.
|
- `onfocus` - lua function called when focusing on an element.
|
||||||
- `ondefocus` - lua function called when the element loses focus.
|
- `ondefocus` - lua function called when the element loses focus.
|
||||||
|
|
@ -192,6 +194,9 @@ Example of list description:
|
||||||
- `width` - minimum content width. Default: 100.
|
- `width` - minimum content width. Default: 100.
|
||||||
- `selected` - initially selected value. Default: "".
|
- `selected` - initially selected value. Default: "".
|
||||||
- `onselect` - function to which the user-selected value is passed
|
- `onselect` - function to which the user-selected value is passed
|
||||||
|
- `mode` - behaviour / display mode:
|
||||||
|
- `select` - default mode.
|
||||||
|
- `button` - button mode: the text is not replaced with the selected option; the element rendered as a regular button.
|
||||||
|
|
||||||
## *bindbox*
|
## *bindbox*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@
|
||||||
- "pipe" - профиль "труба". Примеры блоков: бревно, труба, лампочка
|
- "pipe" - профиль "труба". Примеры блоков: бревно, труба, лампочка
|
||||||
- "pane" - профиль "панель". Примеры блоков: панель, дверь, табличка
|
- "pane" - профиль "панель". Примеры блоков: панель, дверь, табличка
|
||||||
- "stairs" - профиль "ступеньки" ("pane" + перевернутые варианты)
|
- "stairs" - профиль "ступеньки" ("pane" + перевернутые варианты)
|
||||||
|
- "ladder" - профиль "лестница" (как "pipe", но на горизонтальных поверхностях как "pane")
|
||||||
|
|
||||||
### Испускаемые частицы - *particles*
|
### Испускаемые частицы - *particles*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
{
|
{
|
||||||
"id": "выбранное_имя_пака",
|
"id": "выбранное_имя_пака",
|
||||||
"title": "имя контент-пака для отображения в меню контента",
|
"title": "имя контент-пака для отображения в меню контента",
|
||||||
"version": "версия контент-пака в формате major.minor",
|
"version": "версия контент-пака в формате major.minor.[patch]",
|
||||||
"creator": "создатель контент-пака",
|
"creator": "создатель контент-пака",
|
||||||
"description": "краткое описание",
|
"description": "краткое описание",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
- '!' - обязательная зависимость
|
- '!' - обязательная зависимость
|
||||||
- '?' - опциональная зависимость
|
- '?' - опциональная зависимость
|
||||||
- '~' - слабая зависимость
|
- '~' - слабая зависимость
|
||||||
|
|
||||||
Отсутствие префикса интерпретируется как '!'.
|
Отсутствие префикса интерпретируется как '!'.
|
||||||
|
|
||||||
Пример: '~randutil' - слабая зависимость 'randutil'.
|
Пример: '~randutil' - слабая зависимость 'randutil'.
|
||||||
|
|
|
||||||
|
|
@ -140,12 +140,12 @@ gui.confirm(
|
||||||
## Документы и шаблоны
|
## Документы и шаблоны
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- Загружает UI документ и его скрипт. Возвращает имя документа.
|
-- Загружает UI документ и его скрипт. Возвращает пространство имён документа
|
||||||
gui.load_document(
|
gui.load_document(
|
||||||
path: string, -- путь к xml файлу, например: core:layouts/pages/main.xml
|
path: string, -- путь к xml файлу, например: core:layouts/pages/main.xml
|
||||||
name: string, -- id документа, например: core:pages/main
|
name: string, -- id документа, например: core:pages/main
|
||||||
args: table -- параметры для события on_open
|
args: table -- параметры для события on_open
|
||||||
) -> string
|
) -> table
|
||||||
|
|
||||||
-- Обрабатывает xml шаблон макета из файла
|
-- Обрабатывает xml шаблон макета из файла
|
||||||
gui.template(
|
gui.template(
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,11 @@ hud.get_block_inventory() -> int
|
||||||
-- Дает ID второго открытого инвентаря (блок, виртуальный инвентарь...) или 0.
|
-- Дает ID второго открытого инвентаря (блок, виртуальный инвентарь...) или 0.
|
||||||
hud.get_second_inventory() -> int
|
hud.get_second_inventory() -> int
|
||||||
|
|
||||||
|
-- Получить ID инвентаря, в котором находится предмет во время
|
||||||
|
-- его перемещения в инвентаре, или 0 если его не существует.
|
||||||
|
-- Имеет единственный слот - 0
|
||||||
|
hud.get_exchange_inventory() -> int
|
||||||
|
|
||||||
-- Дает ID игрока, к которому привязан пользовательский интерфейс.
|
-- Дает ID игрока, к которому привязан пользовательский интерфейс.
|
||||||
hud.get_player() -> int
|
hud.get_player() -> int
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
Библиотека для работы с кватернионами.
|
Библиотека для работы с кватернионами.
|
||||||
|
|
||||||
|
Кватернионы задаюся таблицей, состоящей из 4 компонентов:
|
||||||
|
`{w, x, y, z}`
|
||||||
|
|
||||||
## Кватернион из матрицы - *quat.from_mat4(...)*
|
## Кватернион из матрицы - *quat.from_mat4(...)*
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- создаёт кватернион на основе матрицы вращения
|
-- создаёт кватернион на основе матрицы вращения
|
||||||
quat.from_mat4(m: matrix) -> quat
|
quat.from_mat4(m: matrix, [опционально] dst: quat) -> quat
|
||||||
|
|
||||||
-- записывает кватернион по матрице вращения в dst
|
|
||||||
quat.from_mat4(m: matrix, dst: quat)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Кватернион из углов Эйлера - *quat.from_euler(...)*
|
## Кватернион из углов Эйлера - *quat.from_euler(...)*
|
||||||
|
|
@ -17,11 +17,21 @@ quat.from_mat4(m: matrix, dst: quat)
|
||||||
```lua
|
```lua
|
||||||
-- создаёт кватернион на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll)
|
-- создаёт кватернион на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll)
|
||||||
-- (значения углов строго в градусах)
|
-- (значения углов строго в градусах)
|
||||||
quat.from_euler(euler: vec3) -> quat
|
quat.from_euler(euler: vec3, [опционально] dst: quat) -> quat
|
||||||
|
```
|
||||||
|
|
||||||
-- записывает кватернион в dst на основе вектора, содержащего углы Эйлера в порядке XYZ (pitch, yaw, roll)
|
## Композиция кватернионов - quat.mul(...)
|
||||||
-- (значения углов строго в градусах)
|
|
||||||
quat.from_euler(euler: vec3, dst: quat)
|
```lua
|
||||||
|
-- Умножает два кватерниона (композиция поворотов)
|
||||||
|
quat.mul(a: quat, b: quat, [опционально] dst: quat) -> quat
|
||||||
|
```
|
||||||
|
|
||||||
|
## Поворот 3д вектора на кватернион - quat.mul_vec3(...)
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Поворачивает вектор на заданный кватернион
|
||||||
|
quat.mul_vec3(a: quat, b: vec3, [опционально] dst: vec3) -> vec3
|
||||||
```
|
```
|
||||||
|
|
||||||
## Сферическая линейная интерполяция - *quat.slerp(...)*
|
## Сферическая линейная интерполяция - *quat.slerp(...)*
|
||||||
|
|
@ -31,11 +41,7 @@ quat.from_euler(euler: vec3, dst: quat)
|
||||||
```lua
|
```lua
|
||||||
-- создаёт кватернион как интерполяцию между a и b,
|
-- создаёт кватернион как интерполяцию между a и b,
|
||||||
-- где t - фактор интерполяции
|
-- где t - фактор интерполяции
|
||||||
quat.slerp(a: quat, b: quat, t: number) -> quat
|
quat.slerp(a: quat, b: quat, t: number, [опционально] dst: quat) -> quat
|
||||||
|
|
||||||
-- записывает кватернион как интерполяцию между a и b в dst,
|
|
||||||
-- где t - фактор интерполяции
|
|
||||||
quat.slerp(a: quat, b: quat, t: number, dst: quat)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Перевод в строку - *quat.tostring(...)*
|
## Перевод в строку - *quat.tostring(...)*
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ function on_broken(x, y, z, playerid)
|
||||||
function on_replaced(x, y, z, playerid)
|
function on_replaced(x, y, z, playerid)
|
||||||
```
|
```
|
||||||
|
|
||||||
Вызывается после замены блока игроком
|
Вызывается перед заменой блока игроком
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function on_interact(x, y, z, playerid) -> bool
|
function on_interact(x, y, z, playerid) -> bool
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ document["worlds-panel"]:clear()
|
||||||
| contentOffset | vec2 | да | *нет* | смещение содержимого |
|
| contentOffset | vec2 | да | *нет* | смещение содержимого |
|
||||||
| cursor | string | да | да | курсор, отображаемый при наведении |
|
| cursor | string | да | да | курсор, отображаемый при наведении |
|
||||||
| parent | Element | да | *нет* | родительский элемент или nil |
|
| parent | Element | да | *нет* | родительский элемент или nil |
|
||||||
| zIndex | bool | да | *нет* | значение z-индекса. В panel определяет порядок |
|
| zIndex | int | да | *нет* | значение z-индекса. В panel определяет порядок |
|
||||||
|
|
||||||
Общие методы элементов:
|
Общие методы элементов:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -215,12 +215,28 @@ local map = Heightmap(ширина, высота)
|
||||||
Операции применяются ко всем значениям высоты.
|
Операции применяются ко всем значениям высоты.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
-- Приведение значений высот к абсолютным
|
||||||
map:abs()
|
map:abs()
|
||||||
|
|
||||||
|
-- Округление к меньшему целому
|
||||||
|
map:floor()
|
||||||
|
|
||||||
|
-- Округление к большему целому
|
||||||
|
map:ceil()
|
||||||
|
|
||||||
|
-- Округление к ближайшему целому
|
||||||
|
map:round()
|
||||||
|
|
||||||
|
-- Синус от значений высот (в радианах)
|
||||||
|
map:sin()
|
||||||
|
|
||||||
|
-- Косинус от значений высот (в радианах)
|
||||||
|
map:cos()
|
||||||
|
|
||||||
|
-- Тангенс от значений высот (в радианах)
|
||||||
|
map:tan()
|
||||||
```
|
```
|
||||||
|
|
||||||
Приводит значения высот к абсолютным.
|
|
||||||
|
|
||||||
|
|
||||||
### Бинарные операции
|
### Бинарные операции
|
||||||
|
|
||||||
Операции с применением второй карты или скаляра.
|
Операции с применением второй карты или скаляра.
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@
|
||||||
- `position-func` - поставщик позиции элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
|
- `position-func` - поставщик позиции элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
|
||||||
- `size-func` - поставщик размера элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
|
- `size-func` - поставщик размера элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
|
||||||
- `onclick` - lua функция вызываемая при нажатии на элемент.
|
- `onclick` - lua функция вызываемая при нажатии на элемент.
|
||||||
|
- `onrightclick` - lua функция вызываемая при нажатии правой кнопки мыши на элемент.
|
||||||
|
- `onmiddleclick` - lua функция вызываемая при нажатии средней кнопки мыши на элемент.
|
||||||
- `ondoubleclick` - lua функция вызываемая при двойном нажатии на элемент.
|
- `ondoubleclick` - lua функция вызываемая при двойном нажатии на элемент.
|
||||||
- `onfocus` - lua функция вызываемая при фокусировке на элемент.
|
- `onfocus` - lua функция вызываемая при фокусировке на элемент.
|
||||||
- `ondefocus` - lua функция вызываемая при потере фокуса элеметом.
|
- `ondefocus` - lua функция вызываемая при потере фокуса элеметом.
|
||||||
|
|
@ -193,6 +195,9 @@
|
||||||
- `width` - минимальная ширина содержимого. По-умолчанию: 100.
|
- `width` - минимальная ширина содержимого. По-умолчанию: 100.
|
||||||
- `selected` - изначально выбранное значение. По-умолчанию: "".
|
- `selected` - изначально выбранное значение. По-умолчанию: "".
|
||||||
- `onselect` - функция, в которую передаётся выбранное пользователем значение
|
- `onselect` - функция, в которую передаётся выбранное пользователем значение
|
||||||
|
- `mode` - режим работы:
|
||||||
|
- `select` - режим по-умолчанию.
|
||||||
|
- `button` - режим кнопки: текст не заменяется на выбранную опцию, а элемент выглядит как обычная кнопка.
|
||||||
|
|
||||||
## Привязка ввода - *bindbox*
|
## Привязка ввода - *bindbox*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,12 +86,27 @@ function generate_heightmap(x, y, w, h, s, inputs)
|
||||||
|
|
||||||
local rivermap = Heightmap(w, h)
|
local rivermap = Heightmap(w, h)
|
||||||
rivermap.noiseSeed = SEED
|
rivermap.noiseSeed = SEED
|
||||||
rivermap:noise({x+21, y+12}, 0.1*s, 4)
|
rivermap:noise({ x + 21, y + 12 }, 0.1 * s, 4)
|
||||||
rivermap:abs()
|
rivermap:pow(4)
|
||||||
rivermap:mul(2.0)
|
rivermap:mul(3500)
|
||||||
rivermap:pow(0.15)
|
rivermap:add(1)
|
||||||
rivermap:max(0.5)
|
rivermap:pow(-1)
|
||||||
map:mul(rivermap)
|
rivermap:mul(-1)
|
||||||
|
rivermap:add(1)
|
||||||
|
|
||||||
|
local rivermap_bottom = Heightmap(w, h)
|
||||||
|
rivermap_bottom:add(0.23)
|
||||||
|
rivermap_bottom:noise({ x, y }, 2 * s, 4, 0.005)
|
||||||
|
|
||||||
|
map:sub(rivermap_bottom)
|
||||||
|
|
||||||
|
local map_multiplied = Heightmap(w, h)
|
||||||
|
map_multiplied:add(map)
|
||||||
|
map_multiplied:mul(rivermap)
|
||||||
|
|
||||||
|
map:min(map_multiplied)
|
||||||
|
|
||||||
|
map:add(rivermap_bottom)
|
||||||
|
|
||||||
local desertmap = Heightmap(w, h)
|
local desertmap = Heightmap(w, h)
|
||||||
desertmap.noiseSeed = SEED
|
desertmap.noiseSeed = SEED
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,44 @@
|
||||||
local util = {}
|
local util = {}
|
||||||
|
|
||||||
|
local DROP_FORCE = 8
|
||||||
|
local DROP_INIT_VEL = {0, 3, 0}
|
||||||
|
|
||||||
|
---@param mode integer 0 - whole stack, 1 - single item, 2 - dupe whole stack
|
||||||
|
function util.drop_from_slot(invid, slot, mode)
|
||||||
|
if invid == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local pid = hud.get_player()
|
||||||
|
if mode == 2 and not player.is_infinite_items(pid) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local itemid, itemcount = inventory.get(invid, slot)
|
||||||
|
if itemid == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local drop_itemcount = 1
|
||||||
|
if mode == 0 then
|
||||||
|
drop_itemcount = itemcount
|
||||||
|
end
|
||||||
|
if mode == 2 then
|
||||||
|
drop_itemcount = item.stack_size(itemid)
|
||||||
|
else
|
||||||
|
inventory.set(invid, slot, itemid, itemcount - drop_itemcount)
|
||||||
|
end
|
||||||
|
|
||||||
|
local data = inventory.get_all_data(invid, slot)
|
||||||
|
local pvel = { player.get_vel(pid) }
|
||||||
|
local ppos = vec3.add({ player.get_pos(pid) }, { 0, 0.7, 0 })
|
||||||
|
local throw_force = vec3.mul(player.get_dir(pid), DROP_FORCE)
|
||||||
|
local drop = util.drop(ppos, itemid, drop_itemcount, data, 1.5)
|
||||||
|
if not drop then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local velocity = vec3.add(throw_force, vec3.add(pvel, DROP_INIT_VEL))
|
||||||
|
drop.rigidbody:set_vel(velocity)
|
||||||
|
end
|
||||||
|
|
||||||
function util.drop(ppos, itemid, count, data, pickup_delay)
|
function util.drop(ppos, itemid, count, data, pickup_delay)
|
||||||
if itemid == 0 or not itemid then
|
if itemid == 0 or not itemid then
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,21 @@
|
||||||
local base_util = require "util"
|
local base_util = require "util"
|
||||||
|
|
||||||
local DROP_FORCE = 8
|
|
||||||
local DROP_INIT_VEL = {0, 3, 0}
|
|
||||||
|
|
||||||
function on_hud_open()
|
function on_hud_open()
|
||||||
input.add_callback("player.drop", function ()
|
events.on("core:drop_outside_inventory", function(mode)
|
||||||
|
local invid = hud.get_exchange_inventory()
|
||||||
|
base_util.drop_from_slot(invid, 0, mode)
|
||||||
|
end)
|
||||||
|
input.add_callback("player.drop", function()
|
||||||
if hud.is_paused() or hud.is_inventory_open() then
|
if hud.is_paused() or hud.is_inventory_open() then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local pid = hud.get_player()
|
local pid = hud.get_player()
|
||||||
local invid, slot = player.get_inventory(pid)
|
local invid, slot = player.get_inventory(pid)
|
||||||
local itemid, itemcount = inventory.get(invid, slot)
|
local mode = 1
|
||||||
if itemid == 0 then
|
if input.is_pressed("key:left-ctrl") or input.is_pressed("key:right-ctrl") then
|
||||||
return
|
mode = 0
|
||||||
end
|
end
|
||||||
local data = inventory.get_all_data(invid, slot)
|
base_util.drop_from_slot(invid, slot, mode)
|
||||||
inventory.set(invid, slot, itemid, itemcount-1)
|
|
||||||
|
|
||||||
local pvel = {player.get_vel(pid)}
|
|
||||||
local ppos = vec3.add({player.get_pos(pid)}, {0, 0.7, 0})
|
|
||||||
local throw_force = vec3.mul(player.get_dir(pid), DROP_FORCE)
|
|
||||||
local drop = base_util.drop(ppos, itemid, 1, data, 1.5)
|
|
||||||
if not drop then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local velocity = vec3.add(throw_force, vec3.add(pvel, DROP_INIT_VEL))
|
|
||||||
drop.rigidbody:set_vel(velocity)
|
|
||||||
end)
|
end)
|
||||||
|
rules.create("do-loot-non-player", true)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,4 @@ function on_block_broken(id, x, y, z, playerid)
|
||||||
spawn_spread=vec3.mul(size, 0.4)
|
spawn_spread=vec3.mul(size, 0.4)
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
rules.create("do-loot-non-player", true)
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<inventory>
|
<inventory>
|
||||||
<slots-grid pos="0, 164" rows="1" count="10" sharefunc="inventory_share_func"/>
|
<slots-grid pos="0, 164" rows="1" count="10" sharefunc="share_func"/>
|
||||||
<slots-grid rows="3" start-index="10" count="30" sharefunc="inventory_share_func"/>
|
<slots-grid rows="3" start-index="10" count="30" sharefunc="share_func"/>
|
||||||
</inventory>
|
</inventory>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1 @@
|
||||||
function inventory_share_func(invid, slotid)
|
share_func = require("core:inventory_utils").share_default_func
|
||||||
local blockinv = hud.get_block_inventory()
|
|
||||||
if blockinv ~= 0 then
|
|
||||||
inventory.move(invid, slotid, blockinv)
|
|
||||||
elseif rules.get("allow-content-access") then
|
|
||||||
inventory.set(invid, slotid, 0, 0)
|
|
||||||
elseif slotid < 10 then
|
|
||||||
inventory.move_range(invid, slotid, invid, 10)
|
|
||||||
else
|
|
||||||
inventory.move_range(invid, slotid, invid, 0, 9)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<container id='pack_%{id}' onclick='%{callback}' size='0,80' color='#00000040' hover-color='#00000080' position-func="%{position_func}" z-index="%{index}">
|
<container id='pack_%{id}' onclick='%{callback}' size='0,80' color='#00000040' hover-color='#00000080' position-func="%{position_func}" z-index="%{index}">
|
||||||
<label color='#FFFFFF80' size='300,25' align='right' gravity='top-right'>
|
<label color='#FFFFFF80' size='340,25' align='right' gravity='top-right'>
|
||||||
[%{id_verbose}]
|
[%{id_verbose} %{version}]
|
||||||
</label>
|
</label>
|
||||||
<label pos='78,6'>%{title}</label>
|
<label pos='78,6'>%{title}</label>
|
||||||
<label if='%{creator}' color='#CCFFE5B2' size='300,20' align='right'
|
<label if='%{creator}' color='#CCFFE5B2' size='300,20' align='right'
|
||||||
|
|
|
||||||
|
|
@ -192,13 +192,15 @@ function gui.ask(text, on_yes, on_no, yes_text, no_text)
|
||||||
<container id='%s' color='#00000080' size-func='-1,-1' z-index='10'>
|
<container id='%s' color='#00000080' size-func='-1,-1' z-index='10'>
|
||||||
<panel color='#507090E0' size='600' padding='16'
|
<panel color='#507090E0' size='600' padding='16'
|
||||||
gravity='center-center' interval='4'>
|
gravity='center-center' interval='4'>
|
||||||
<label margin='4'>%s</label>
|
<label margin='4' markup='md'>%s</label>
|
||||||
<button onclick='DATA.on_yes()'>@%s</button>
|
<button onclick='DATA.on_yes()'>@%s</button>
|
||||||
<button onclick='DATA.on_no()'>@%s</button>
|
<button onclick='DATA.on_no()'>@%s</button>
|
||||||
</panel>
|
</panel>
|
||||||
</container>
|
</container>
|
||||||
]], id, string.escape_xml(text)), {on_yes=yes_callback, on_no=no_callback},
|
]], id,
|
||||||
string.escape_xml(yes_text), string.escape_xml(no_text))
|
string.escape_xml(text),
|
||||||
|
string.escape_xml(yes_text),
|
||||||
|
string.escape_xml(no_text)), {on_yes=yes_callback, on_no=no_callback})
|
||||||
input.add_callback("key:escape", no_callback, gui.root[id])
|
input.add_callback("key:escape", no_callback, gui.root[id])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ return {
|
||||||
goto continue
|
goto continue
|
||||||
end
|
end
|
||||||
for _, component in pairs(entity.components) do
|
for _, component in pairs(entity.components) do
|
||||||
local callback = component.on_update
|
local callback = rawget(component, "on_update")
|
||||||
if not component.__disabled and callback then
|
if not component.__disabled and callback then
|
||||||
local result, err = pcall(callback, tps)
|
local result, err = pcall(callback, tps)
|
||||||
if err then
|
if err then
|
||||||
|
|
@ -143,7 +143,7 @@ return {
|
||||||
physics_update = function(delta)
|
physics_update = function(delta)
|
||||||
for uid, entity in pairs(entities) do
|
for uid, entity in pairs(entities) do
|
||||||
for _, component in pairs(entity.components) do
|
for _, component in pairs(entity.components) do
|
||||||
local callback = component.on_physics_update
|
local callback = rawget(component, "on_physics_update")
|
||||||
if not component.__disabled and callback then
|
if not component.__disabled and callback then
|
||||||
local result, err = pcall(callback, delta)
|
local result, err = pcall(callback, delta)
|
||||||
if err then
|
if err then
|
||||||
|
|
@ -156,7 +156,7 @@ return {
|
||||||
render = function(delta)
|
render = function(delta)
|
||||||
for _,entity in pairs(entities) do
|
for _,entity in pairs(entities) do
|
||||||
for _, component in pairs(entity.components) do
|
for _, component in pairs(entity.components) do
|
||||||
local callback = component.on_render
|
local callback = rawget(component, "on_render")
|
||||||
if not component.__disabled and callback then
|
if not component.__disabled and callback then
|
||||||
local result, err = pcall(callback, delta)
|
local result, err = pcall(callback, delta)
|
||||||
if err then
|
if err then
|
||||||
|
|
|
||||||
21
res/modules/inventory_utils.lua
Normal file
21
res/modules/inventory_utils.lua
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.share_default_func(invid, slotid)
|
||||||
|
local blockinv = hud.get_block_inventory()
|
||||||
|
local playerinv = player.get_inventory(hud.get_player())
|
||||||
|
if blockinv ~= 0 then
|
||||||
|
if invid == blockinv then
|
||||||
|
inventory.move(blockinv, slotid, playerinv)
|
||||||
|
else
|
||||||
|
inventory.move(invid, slotid, blockinv)
|
||||||
|
end
|
||||||
|
elseif rules.get("allow-content-access") then
|
||||||
|
inventory.set(invid, slotid, 0, 0)
|
||||||
|
elseif slotid < 10 then
|
||||||
|
inventory.move_range(invid, slotid, invid, 10)
|
||||||
|
else
|
||||||
|
inventory.move_range(invid, slotid, invid, 0, 9)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
#include <shadows>
|
#include <shadows>
|
||||||
#include <fog>
|
#include <fog>
|
||||||
|
|
||||||
|
#define CLOUDS_FOG_FACTOR_MUL 0.3f
|
||||||
|
#define CLOUDS_FOG_CURVE_MUL 0.4f
|
||||||
|
|
||||||
vec4 effect() {
|
vec4 effect() {
|
||||||
vec4 pos = texture(u_position, v_uv);
|
vec4 pos = texture(u_position, v_uv);
|
||||||
float light = 1.0;
|
float light = 1.0;
|
||||||
|
|
@ -32,6 +35,10 @@ vec4 effect() {
|
||||||
light = pow(light, u_gamma);
|
light = pow(light, u_gamma);
|
||||||
|
|
||||||
vec3 fogColor = texture(u_skybox, dir).rgb;
|
vec3 fogColor = texture(u_skybox, dir).rgb;
|
||||||
float fog = calc_fog(length(u_view * vec4((modelpos.xyz - u_cameraPos) * FOG_POS_SCALE, 0.0)) / 256.0);
|
float fog = calc_fog(
|
||||||
|
length(u_view * vec4((modelpos.xyz - u_cameraPos) * FOG_POS_SCALE, 0.0)) / 256.0,
|
||||||
|
mix(1.0, CLOUDS_FOG_FACTOR_MUL, emission),
|
||||||
|
mix(1.0, CLOUDS_FOG_CURVE_MUL, emission)
|
||||||
|
);
|
||||||
return vec4(mix(texture(u_screen, v_uv).rgb * mix(1.0, light, 1.0), fogColor, fog), 1.0);
|
return vec4(mix(texture(u_screen, v_uv).rgb * mix(1.0, light, 1.0), fogColor, fog), 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,33 @@ uniform float u_fogCurve;
|
||||||
uniform bool u_alphaClip;
|
uniform bool u_alphaClip;
|
||||||
uniform vec3 u_sunDir;
|
uniform vec3 u_sunDir;
|
||||||
|
|
||||||
|
const float BAYER_MATRIX[64] = float[](
|
||||||
|
0.0, 32.0, 8.0, 40.0, 2.0, 34.0, 10.0, 42.0,
|
||||||
|
48.0, 16.0, 56.0, 24.0, 50.0, 18.0, 58.0, 26.0,
|
||||||
|
12.0, 44.0, 4.0, 36.0, 14.0, 46.0, 6.0, 38.0,
|
||||||
|
60.0, 28.0, 52.0, 20.0, 62.0, 30.0, 54.0, 22.0,
|
||||||
|
3.0, 35.0, 11.0, 43.0, 1.0, 33.0, 9.0, 41.0,
|
||||||
|
51.0, 19.0, 59.0, 27.0, 49.0, 17.0, 57.0, 25.0,
|
||||||
|
15.0, 47.0, 7.0, 39.0, 13.0, 45.0, 5.0, 37.0,
|
||||||
|
63.0, 31.0, 55.0, 23.0, 61.0, 29.0, 53.0, 21.0
|
||||||
|
);
|
||||||
|
|
||||||
|
float calc_bayer(vec2 pos){
|
||||||
|
ivec2 p = ivec2(mod(pos, 8.0));
|
||||||
|
return BAYER_MATRIX[p.y * 8 + p.x] / 64.0;
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 texColor = texture(u_texture0, a_texCoord);
|
vec4 texColor = texture(u_texture0, a_texCoord);
|
||||||
float alpha = a_color.a * texColor.a;
|
float alpha = a_color.a * texColor.a;
|
||||||
// anyway it's any alpha-test alternative required
|
// anyway it's any alpha-test alternative required
|
||||||
if (alpha < (u_alphaClip ? 0.5f : 0.15f)) {
|
if (u_alphaClip) {
|
||||||
|
float bayer = calc_bayer(gl_FragCoord.xy);
|
||||||
|
if (alpha - bayer < 0.01f) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
alpha = 1.0;
|
||||||
|
} else if (alpha < 0.05f) {
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
f_color = a_color * texColor;
|
f_color = a_color * texColor;
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,17 @@ uniform float u_weatherFogOpacity;
|
||||||
uniform float u_weatherFogDencity;
|
uniform float u_weatherFogDencity;
|
||||||
uniform float u_weatherFogCurve;
|
uniform float u_weatherFogCurve;
|
||||||
|
|
||||||
float calc_fog(float depth) {
|
float calc_fog(float depth, float fogFactor, float fogCurve) {
|
||||||
return min(
|
return min(
|
||||||
1.0,
|
1.0,
|
||||||
max(pow(depth * u_fogFactor, u_fogCurve),
|
max(pow(depth * u_fogFactor * fogFactor, u_fogCurve * fogCurve),
|
||||||
min(pow(depth * u_weatherFogDencity, u_weatherFogCurve),
|
min(pow(depth * u_weatherFogDencity, u_weatherFogCurve),
|
||||||
u_weatherFogOpacity))
|
u_weatherFogOpacity))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float calc_fog(float depth) {
|
||||||
|
return calc_fog(depth, 1.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // FOG_GLSL_
|
#endif // FOG_GLSL_
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,8 @@ template<> void ContentUnitLoader<Block>::loadUnit(
|
||||||
def.rotations = BlockRotProfile::PANE;
|
def.rotations = BlockRotProfile::PANE;
|
||||||
} else if (profile == BlockRotProfile::STAIRS_NAME) {
|
} else if (profile == BlockRotProfile::STAIRS_NAME) {
|
||||||
def.rotations = BlockRotProfile::STAIRS;
|
def.rotations = BlockRotProfile::STAIRS;
|
||||||
|
} else if (profile == BlockRotProfile::LADDER_NAME) {
|
||||||
|
def.rotations = BlockRotProfile::LADDER;
|
||||||
} else if (profile != "none") {
|
} else if (profile != "none") {
|
||||||
logger.error() << "unknown rotation profile " << profile;
|
logger.error() << "unknown rotation profile " << profile;
|
||||||
def.rotatable = false;
|
def.rotatable = false;
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ void ServerMainloop::run() {
|
||||||
delta = time.getDelta();
|
delta = time.getDelta();
|
||||||
}
|
}
|
||||||
if (controller) {
|
if (controller) {
|
||||||
controller->getLevel()->getWorld()->updateTimers(delta);
|
controller->getLevel()->getWorld().updateTimers(delta);
|
||||||
controller->update(glm::min(delta, 0.2), false);
|
controller->update(glm::min(delta, 0.2), false);
|
||||||
}
|
}
|
||||||
engine.applicationTick();
|
engine.applicationTick();
|
||||||
|
|
|
||||||
|
|
@ -147,3 +147,7 @@ const ContentGfxCache& LevelFrontend::getContentGfxCache() const {
|
||||||
LevelController& LevelFrontend::getController() const {
|
LevelController& LevelFrontend::getController() const {
|
||||||
return controller;
|
return controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Weather& LevelFrontend::getWeather() {
|
||||||
|
return weather;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "world/Weather.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class Assets;
|
class Assets;
|
||||||
|
|
@ -15,6 +17,7 @@ class LevelFrontend {
|
||||||
LevelController& controller;
|
LevelController& controller;
|
||||||
Assets& assets;
|
Assets& assets;
|
||||||
std::unique_ptr<ContentGfxCache> contentCache;
|
std::unique_ptr<ContentGfxCache> contentCache;
|
||||||
|
Weather weather {};
|
||||||
public:
|
public:
|
||||||
LevelFrontend(
|
LevelFrontend(
|
||||||
Engine& engine,
|
Engine& engine,
|
||||||
|
|
@ -28,4 +31,5 @@ public:
|
||||||
const ContentGfxCache& getContentGfxCache() const;
|
const ContentGfxCache& getContentGfxCache() const;
|
||||||
ContentGfxCache& getContentGfxCache();
|
ContentGfxCache& getContentGfxCache();
|
||||||
LevelController& getController() const;
|
LevelController& getController() const;
|
||||||
|
Weather& getWeather();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,7 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
panel->add(create_label(gui, [&]() {
|
panel->add(create_label(gui, [&]() {
|
||||||
return L"seed: " + std::to_wstring(level.getWorld()->getSeed());
|
return L"seed: " + std::to_wstring(level.getWorld().getSeed());
|
||||||
}));
|
}));
|
||||||
|
|
||||||
for (int ax = 0; ax < 3; ax++) {
|
for (int ax = 0; ax < 3; ax++) {
|
||||||
|
|
@ -294,7 +294,7 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||||
sub->add(box, glm::vec2(20, 0));
|
sub->add(box, glm::vec2(20, 0));
|
||||||
panel->add(sub);
|
panel->add(sub);
|
||||||
}
|
}
|
||||||
auto& worldInfo = level.getWorld()->getInfo();
|
auto& worldInfo = level.getWorld().getInfo();
|
||||||
panel->add(create_label(gui, [&]() {
|
panel->add(create_label(gui, [&]() {
|
||||||
int hour, minute, second;
|
int hour, minute, second;
|
||||||
timeutil::from_value(worldInfo.daytime, hour, minute, second);
|
timeutil::from_value(worldInfo.daytime, hour, minute, second);
|
||||||
|
|
@ -310,12 +310,6 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||||
bar->setConsumer([&](double val) { worldInfo.daytime = val; });
|
bar->setConsumer([&](double val) { worldInfo.daytime = val; });
|
||||||
panel->add(bar);
|
panel->add(bar);
|
||||||
}
|
}
|
||||||
if (allowDebugCheats) {
|
|
||||||
auto bar = std::make_shared<TrackBar>(gui, 0.0f, 1.0f, 0.0f, 0.005f, 8);
|
|
||||||
bar->setSupplier([&]() { return worldInfo.fog; });
|
|
||||||
bar->setConsumer([&](double val) { worldInfo.fog = val; });
|
|
||||||
panel->add(bar);
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
auto checkbox = std::make_shared<FullCheckBox>(
|
auto checkbox = std::make_shared<FullCheckBox>(
|
||||||
gui, L"Show Chunk Borders", glm::vec2(400, 24)
|
gui, L"Show Chunk Borders", glm::vec2(400, 24)
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ std::shared_ptr<UINode> HudElement::getNode() const {
|
||||||
std::shared_ptr<InventoryView> Hud::createContentAccess() {
|
std::shared_ptr<InventoryView> Hud::createContentAccess() {
|
||||||
auto& content = frontend.getLevel().content;
|
auto& content = frontend.getLevel().content;
|
||||||
auto& indices = *content.getIndices();
|
auto& indices = *content.getIndices();
|
||||||
|
auto& input = this->input;
|
||||||
auto inventory = player.getInventory();
|
auto inventory = player.getInventory();
|
||||||
|
|
||||||
size_t itemsCount = indices.items.count();
|
size_t itemsCount = indices.items.count();
|
||||||
|
|
@ -115,12 +116,19 @@ std::shared_ptr<InventoryView> Hud::createContentAccess() {
|
||||||
}
|
}
|
||||||
|
|
||||||
SlotLayout slotLayout(-1, glm::vec2(), false, true, nullptr,
|
SlotLayout slotLayout(-1, glm::vec2(), false, true, nullptr,
|
||||||
[inventory, &indices](uint, ItemStack& item) {
|
[inventory, &indices, &input](uint, ItemStack& item) {
|
||||||
auto copy = ItemStack(item);
|
auto copy = ItemStack(item);
|
||||||
|
if (input.pressed(Keycode::LEFT_CONTROL)) {
|
||||||
|
copy.maximizeCount(indices);
|
||||||
|
}
|
||||||
inventory->move(copy, indices);
|
inventory->move(copy, indices);
|
||||||
},
|
},
|
||||||
[this, inventory](uint, ItemStack& item) {
|
[this, inventory, &indices, &input](uint, ItemStack& item) {
|
||||||
inventory->getSlot(player.getChosenSlot()).set(item);
|
auto copy = ItemStack(item);
|
||||||
|
if (input.pressed(Keycode::LEFT_CONTROL)) {
|
||||||
|
copy.maximizeCount(indices);
|
||||||
|
}
|
||||||
|
inventory->getSlot(player.getChosenSlot()).set(copy);
|
||||||
});
|
});
|
||||||
|
|
||||||
InventoryBuilder builder(gui);
|
InventoryBuilder builder(gui);
|
||||||
|
|
@ -175,6 +183,15 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
|
||||||
"<container size='4000' color='#00000080' z-index='-1' visible='false'/>"
|
"<container size='4000' color='#00000080' z-index='-1' visible='false'/>"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
inventoryDropArea = guiutil::create(
|
||||||
|
gui,
|
||||||
|
R"(<container size='4000' color='#00000000' z-index='-1' visible='false'
|
||||||
|
onclick='events.emit(\"core:drop_outside_inventory\", 0)'
|
||||||
|
onrightclick='events.emit(\"core:drop_outside_inventory\", 1)'
|
||||||
|
onmiddleclick='events.emit(\"core:drop_outside_inventory\", 2)'
|
||||||
|
/>)"
|
||||||
|
);
|
||||||
|
|
||||||
uicamera = std::make_unique<Camera>(glm::vec3(), 1);
|
uicamera = std::make_unique<Camera>(glm::vec3(), 1);
|
||||||
uicamera->perspective = false;
|
uicamera->perspective = false;
|
||||||
uicamera->flipped = true;
|
uicamera->flipped = true;
|
||||||
|
|
@ -188,6 +205,7 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
|
||||||
|
|
||||||
gui.add(debugPanel);
|
gui.add(debugPanel);
|
||||||
gui.add(darkOverlay);
|
gui.add(darkOverlay);
|
||||||
|
gui.add(inventoryDropArea);
|
||||||
gui.add(hotbarView);
|
gui.add(hotbarView);
|
||||||
gui.add(contentAccessPanel);
|
gui.add(contentAccessPanel);
|
||||||
|
|
||||||
|
|
@ -213,6 +231,7 @@ Hud::~Hud() {
|
||||||
}
|
}
|
||||||
gui.remove(hotbarView);
|
gui.remove(hotbarView);
|
||||||
gui.remove(darkOverlay);
|
gui.remove(darkOverlay);
|
||||||
|
gui.remove(inventoryDropArea);
|
||||||
gui.remove(contentAccessPanel);
|
gui.remove(contentAccessPanel);
|
||||||
gui.remove(debugPanel);
|
gui.remove(debugPanel);
|
||||||
}
|
}
|
||||||
|
|
@ -358,6 +377,7 @@ void Hud::update(bool visible) {
|
||||||
contentAccess->setMinSize(glm::vec2(1, windowSize.y));
|
contentAccess->setMinSize(glm::vec2(1, windowSize.y));
|
||||||
hotbarView->setVisible(visible && !(secondUI && !inventoryView));
|
hotbarView->setVisible(visible && !(secondUI && !inventoryView));
|
||||||
darkOverlay->setVisible(isMenuOpen);
|
darkOverlay->setVisible(isMenuOpen);
|
||||||
|
inventoryDropArea->setVisible(inventoryOpen);
|
||||||
menu.setVisible(isMenuOpen);
|
menu.setVisible(isMenuOpen);
|
||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
|
|
@ -739,6 +759,10 @@ std::shared_ptr<Inventory> Hud::getSecondInventory() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Inventory> Hud::getExchangeInventory() {
|
||||||
|
return exchangeSlotInv;
|
||||||
|
}
|
||||||
|
|
||||||
bool Hud::isContentAccess() const {
|
bool Hud::isContentAccess() const {
|
||||||
return showContentPanel;
|
return showContentPanel;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,8 @@ class Hud : public util::ObjectsKeeper {
|
||||||
std::shared_ptr<gui::UINode> debugPanel;
|
std::shared_ptr<gui::UINode> debugPanel;
|
||||||
/// @brief Overlay used in pause mode
|
/// @brief Overlay used in pause mode
|
||||||
std::shared_ptr<gui::UINode> darkOverlay;
|
std::shared_ptr<gui::UINode> darkOverlay;
|
||||||
|
/// @brief Overlay used in inventory
|
||||||
|
std::shared_ptr<gui::UINode> inventoryDropArea;
|
||||||
/// @brief Inventories interaction agent (grabbed item)
|
/// @brief Inventories interaction agent (grabbed item)
|
||||||
std::shared_ptr<gui::SlotView> exchangeSlot;
|
std::shared_ptr<gui::SlotView> exchangeSlot;
|
||||||
/// @brief Exchange slot inventory (1 slot only)
|
/// @brief Exchange slot inventory (1 slot only)
|
||||||
|
|
@ -207,6 +209,8 @@ public:
|
||||||
|
|
||||||
std::shared_ptr<Inventory> getSecondInventory();
|
std::shared_ptr<Inventory> getSecondInventory();
|
||||||
|
|
||||||
|
std::shared_ptr<Inventory> getExchangeInventory();
|
||||||
|
|
||||||
bool isContentAccess() const;
|
bool isContentAccess() const;
|
||||||
|
|
||||||
void setContentAccess(bool flag);
|
void setContentAccess(bool flag);
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ LevelScreen::LevelScreen(
|
||||||
Engine& engine, std::unique_ptr<Level> levelPtr, int64_t localPlayer
|
Engine& engine, std::unique_ptr<Level> levelPtr, int64_t localPlayer
|
||||||
)
|
)
|
||||||
: Screen(engine),
|
: Screen(engine),
|
||||||
world(*levelPtr->getWorld()),
|
world(levelPtr->getWorld()),
|
||||||
postProcessing(std::make_unique<PostProcessing>(
|
postProcessing(std::make_unique<PostProcessing>(
|
||||||
levelPtr->content.getIndices(ResourceType::POST_EFFECT_SLOT).size()
|
levelPtr->content.getIndices(ResourceType::POST_EFFECT_SLOT).size()
|
||||||
)),
|
)),
|
||||||
|
|
@ -81,7 +81,7 @@ LevelScreen::LevelScreen(
|
||||||
|
|
||||||
auto resetChunks = [=](bool) {
|
auto resetChunks = [=](bool) {
|
||||||
player->chunks->saveAndClear();
|
player->chunks->saveAndClear();
|
||||||
renderer->clear();
|
renderer->resetCache();
|
||||||
};
|
};
|
||||||
keepAlive(settings.graphics.backlight.observe(resetChunks));
|
keepAlive(settings.graphics.backlight.observe(resetChunks));
|
||||||
keepAlive(settings.graphics.softLighting.observe(resetChunks));
|
keepAlive(settings.graphics.softLighting.observe(resetChunks));
|
||||||
|
|
@ -94,11 +94,11 @@ LevelScreen::LevelScreen(
|
||||||
}));
|
}));
|
||||||
keepAlive(input.addCallback(BIND_CHUNKS_RELOAD, [=]() {
|
keepAlive(input.addCallback(BIND_CHUNKS_RELOAD, [=]() {
|
||||||
player->chunks->saveAndClear();
|
player->chunks->saveAndClear();
|
||||||
renderer->clear();
|
renderer->resetCache();
|
||||||
return false;
|
return false;
|
||||||
}));
|
}));
|
||||||
controller->preQuitCallbacks.listen([this]() {
|
controller->preQuitCallbacks.listen([this]() {
|
||||||
if (!controller->getLevel()->getWorld()->isNameless()) {
|
if (!controller->getLevel()->getWorld().isNameless()) {
|
||||||
saveWorldPreview();
|
saveWorldPreview();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -109,7 +109,7 @@ LevelScreen::LevelScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelScreen::~LevelScreen() {
|
LevelScreen::~LevelScreen() {
|
||||||
if (!controller->getLevel()->getWorld()->isNameless()) {
|
if (!controller->getLevel()->getWorld().isNameless()) {
|
||||||
saveDecorations();
|
saveDecorations();
|
||||||
}
|
}
|
||||||
scripting::on_frontend_close();
|
scripting::on_frontend_close();
|
||||||
|
|
@ -153,7 +153,7 @@ void LevelScreen::loadDecorations() {
|
||||||
}
|
}
|
||||||
auto data = io::read_object(CLIENT_FILE);
|
auto data = io::read_object(CLIENT_FILE);
|
||||||
if (data.has("weather")) {
|
if (data.has("weather")) {
|
||||||
renderer->getWeather().deserialize(data["weather"]);
|
frontend->getWeather().deserialize(data["weather"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,7 +161,7 @@ void LevelScreen::saveDecorations() {
|
||||||
io::create_directory("world:client");
|
io::create_directory("world:client");
|
||||||
|
|
||||||
auto data = dv::object();
|
auto data = dv::object();
|
||||||
data["weather"] = renderer->getWeather().serialize();
|
data["weather"] = frontend->getWeather().serialize();
|
||||||
io::write_json(CLIENT_FILE, data, true);
|
io::write_json(CLIENT_FILE, data, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,7 +257,7 @@ void LevelScreen::update(float delta) {
|
||||||
|
|
||||||
hud->update(hudVisible);
|
hud->update(hudVisible);
|
||||||
|
|
||||||
const Weather& weather = renderer->getWeather();
|
const Weather& weather = frontend->getWeather();
|
||||||
const Player& player = playerController->getPlayer();
|
const Player& player = playerController->getPlayer();
|
||||||
const Camera& camera = *player.currentCamera;
|
const Camera& camera = *player.currentCamera;
|
||||||
decorator->update(paused ? 0.0f : delta, camera, weather);
|
decorator->update(paused ? 0.0f : delta, camera, weather);
|
||||||
|
|
|
||||||
|
|
@ -372,7 +372,7 @@ void Batch2D::sprite(
|
||||||
void Batch2D::flush() {
|
void Batch2D::flush() {
|
||||||
if (index == 0)
|
if (index == 0)
|
||||||
return;
|
return;
|
||||||
mesh->reload(buffer.get(), index);
|
mesh->reload(buffer.get(), index, true);
|
||||||
mesh->draw(gl::to_glenum(primitive));
|
mesh->draw(gl::to_glenum(primitive));
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -292,13 +292,13 @@ void Batch3D::point(const glm::vec3& coord, const glm::vec4& tint) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Batch3D::flush() {
|
void Batch3D::flush() {
|
||||||
mesh->reload(buffer.get(), index);
|
mesh->reload(buffer.get(), index, true);
|
||||||
mesh->draw();
|
mesh->draw();
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Batch3D::flushPoints() {
|
void Batch3D::flushPoints() {
|
||||||
mesh->reload(buffer.get(), index);
|
mesh->reload(buffer.get(), index, true);
|
||||||
mesh->draw(GL_POINTS);
|
mesh->draw(GL_POINTS);
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ void LineBatch::box(float x, float y, float z, float w, float h, float d,
|
||||||
void LineBatch::flush(){
|
void LineBatch::flush(){
|
||||||
if (index == 0)
|
if (index == 0)
|
||||||
return;
|
return;
|
||||||
mesh->reload(buffer.get(), index);
|
mesh->reload(buffer.get(), index, true);
|
||||||
mesh->draw(GL_LINES);
|
mesh->draw(GL_LINES);
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,12 +47,15 @@ public:
|
||||||
void reload(
|
void reload(
|
||||||
const VertexStructure* vertexBuffer,
|
const VertexStructure* vertexBuffer,
|
||||||
size_t vertexCount,
|
size_t vertexCount,
|
||||||
const std::vector<IndexBufferData>& indices
|
const std::vector<IndexBufferData>& indices,
|
||||||
|
bool streaming
|
||||||
);
|
);
|
||||||
|
|
||||||
void reload(const VertexStructure* vertexBuffer, size_t vertexCount) {
|
void reload(
|
||||||
|
const VertexStructure* vertexBuffer, size_t vertexCount, bool streaming
|
||||||
|
) {
|
||||||
static const std::vector<IndexBufferData> indices {};
|
static const std::vector<IndexBufferData> indices {};
|
||||||
reload(vertexBuffer, vertexCount, indices);
|
reload(vertexBuffer, vertexCount, indices, streaming);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Draw mesh with specified primitives type
|
/// @brief Draw mesh with specified primitives type
|
||||||
|
|
@ -61,6 +64,8 @@ public:
|
||||||
|
|
||||||
/// @brief Draw mesh as triangles
|
/// @brief Draw mesh as triangles
|
||||||
void draw() const;
|
void draw() const;
|
||||||
|
private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "graphics/core/Mesh.inl"
|
#include "graphics/core/Mesh.inl"
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,9 @@ Mesh<VertexStructure>::Mesh(
|
||||||
glGenVertexArrays(1, &vao);
|
glGenVertexArrays(1, &vao);
|
||||||
glGenBuffers(1, &vbo);
|
glGenBuffers(1, &vbo);
|
||||||
|
|
||||||
reload(vertexBuffer, vertices, std::move(indices));
|
|
||||||
|
|
||||||
glBindVertexArray(vao);
|
glBindVertexArray(vao);
|
||||||
|
reload(vertexBuffer, vertices, std::move(indices), false);
|
||||||
|
|
||||||
// attributes
|
// attributes
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (int i = 0; attrs[i].count; i++) {
|
for (int i = 0; attrs[i].count; i++) {
|
||||||
|
|
@ -82,20 +82,22 @@ template <typename VertexStructure>
|
||||||
void Mesh<VertexStructure>::reload(
|
void Mesh<VertexStructure>::reload(
|
||||||
const VertexStructure* vertexBuffer,
|
const VertexStructure* vertexBuffer,
|
||||||
size_t vertexCount,
|
size_t vertexCount,
|
||||||
const std::vector<IndexBufferData>& indices
|
const std::vector<IndexBufferData>& indices,
|
||||||
|
bool streaming
|
||||||
) {
|
) {
|
||||||
this->vertexCount = vertexCount;
|
this->vertexCount = vertexCount;
|
||||||
glBindVertexArray(vao);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
if (vertexBuffer != nullptr && vertexCount != 0) {
|
if (vertexBuffer != nullptr && vertexCount != 0) {
|
||||||
glBufferData(
|
glBufferData(
|
||||||
GL_ARRAY_BUFFER,
|
GL_ARRAY_BUFFER,
|
||||||
vertexCount * sizeof(VertexStructure),
|
vertexCount * sizeof(VertexStructure),
|
||||||
vertexBuffer,
|
vertexBuffer,
|
||||||
GL_STREAM_DRAW
|
streaming ? GL_STREAM_DRAW : GL_STATIC_DRAW
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
glBufferData(GL_ARRAY_BUFFER, 0, {}, GL_STREAM_DRAW);
|
glBufferData(
|
||||||
|
GL_ARRAY_BUFFER, 0, {}, streaming ? GL_STREAM_DRAW : GL_STATIC_DRAW
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < ibos.size(); i++) {
|
for (int i = 0; i < ibos.size(); i++) {
|
||||||
|
|
@ -116,7 +118,6 @@ void Mesh<VertexStructure>::reload(
|
||||||
GL_STATIC_DRAW
|
GL_STATIC_DRAW
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
glBindVertexArray(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename VertexStructure>
|
template <typename VertexStructure>
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,8 @@ void Shader::recompile(const std::vector<std::string>& defines) {
|
||||||
glDeleteProgram(id);
|
glDeleteProgram(id);
|
||||||
id = newProgram;
|
id = newProgram;
|
||||||
uniformLocations.clear();
|
uniformLocations.clear();
|
||||||
logger.info() << "shader " << id << " has been recompiled";
|
logger.debug() << "shader program (" << vertexSource.file << ", "
|
||||||
|
<< fragmentSource.file << ") has been recompiled";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Shader> Shader::create(
|
std::unique_ptr<Shader> Shader::create(
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,8 @@ void Shadows::setup(Shader& shader, const Weather& weather) {
|
||||||
if (!shadows) {
|
if (!shadows) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto& worldInfo = level.getWorld()->getInfo();
|
const auto& worldInfo = level.getWorld().getInfo();
|
||||||
float cloudsIntensity = glm::max(worldInfo.fog, weather.clouds());
|
float cloudsIntensity = weather.clouds();
|
||||||
float shadowsOpacity = 1.0f - cloudsIntensity;
|
float shadowsOpacity = 1.0f - cloudsIntensity;
|
||||||
shadowsOpacity *= glm::sqrt(glm::abs(
|
shadowsOpacity *= glm::sqrt(glm::abs(
|
||||||
glm::mod((worldInfo.daytime + 0.5f) * 2.0f, 1.0f) * 2.0f - 1.0f
|
glm::mod((worldInfo.daytime + 0.5f) * 2.0f, 1.0f) * 2.0f - 1.0f
|
||||||
|
|
@ -158,8 +158,8 @@ void Shadows::generateShadowsMap(
|
||||||
float scale,
|
float scale,
|
||||||
const std::function<void(Camera&)>& renderShadowPass
|
const std::function<void(Camera&)>& renderShadowPass
|
||||||
) {
|
) {
|
||||||
auto world = level.getWorld();
|
const auto& world = level.getWorld();
|
||||||
const auto& worldInfo = world->getInfo();
|
const auto& worldInfo = world.getInfo();
|
||||||
|
|
||||||
int resolution = shadowMap.getResolution();
|
int resolution = shadowMap.getResolution();
|
||||||
float shadowMapScale = 0.32f / (1 << glm::max(0, quality)) * scale;
|
float shadowMapScale = 0.32f / (1 << glm::max(0, quality)) * scale;
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,14 @@
|
||||||
#include "frontend/ContentGfxCache.hpp"
|
#include "frontend/ContentGfxCache.hpp"
|
||||||
|
|
||||||
const glm::vec3 BlocksRenderer::SUN_VECTOR(0.528265, 0.833149, -0.163704);
|
const glm::vec3 BlocksRenderer::SUN_VECTOR(0.528265, 0.833149, -0.163704);
|
||||||
const float DIRECTIONAL_LIGHT_FACTOR = 0.3f;
|
constexpr float DIRECTIONAL_LIGHT_FACTOR = 0.3f;
|
||||||
|
|
||||||
BlocksRenderer::BlocksRenderer(
|
BlocksRenderer::BlocksRenderer(
|
||||||
size_t capacity,
|
size_t capacity,
|
||||||
const Content& content,
|
const Block* const* blockDefs,
|
||||||
const ContentGfxCache& cache,
|
const ContentGfxCache& cache,
|
||||||
const EngineSettings& settings
|
const EngineSettings& settings
|
||||||
) : content(content),
|
) : vertexBuffer(std::make_unique<ChunkVertex[]>(capacity)),
|
||||||
vertexBuffer(std::make_unique<ChunkVertex[]>(capacity)),
|
|
||||||
indexBuffer(std::make_unique<uint32_t[]>(capacity)),
|
indexBuffer(std::make_unique<uint32_t[]>(capacity)),
|
||||||
denseIndexBuffer(std::make_unique<uint32_t[]>(capacity)),
|
denseIndexBuffer(std::make_unique<uint32_t[]>(capacity)),
|
||||||
vertexCount(0),
|
vertexCount(0),
|
||||||
|
|
@ -28,7 +27,7 @@ BlocksRenderer::BlocksRenderer(
|
||||||
cache(cache),
|
cache(cache),
|
||||||
settings(settings)
|
settings(settings)
|
||||||
{
|
{
|
||||||
blockDefsCache = content.getIndices()->blocks.getDefs();
|
blockDefsCache = blockDefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlocksRenderer::~BlocksRenderer() = default;
|
BlocksRenderer::~BlocksRenderer() = default;
|
||||||
|
|
@ -482,83 +481,78 @@ glm::vec4 BlocksRenderer::pickSoftLight(
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlocksRenderer::render(
|
void BlocksRenderer::render(
|
||||||
const voxel* voxels, const int beginEnds[256][2]
|
const voxel* voxels, int totalBegin, int totalEnd
|
||||||
) {
|
) {
|
||||||
bool denseRender = this->denseRender;
|
bool denseRender = this->denseRender;
|
||||||
bool densePass = this->densePass;
|
bool densePass = this->densePass;
|
||||||
bool enableAO = settings.graphics.softLighting.get();
|
bool enableAO = settings.graphics.softLighting.get();
|
||||||
for (const auto drawGroup : *content.drawGroups) {
|
|
||||||
int begin = beginEnds[drawGroup][0];
|
for (int i = totalBegin; i < totalEnd; i++) {
|
||||||
if (begin == 0) {
|
const voxel& vox = voxels[i];
|
||||||
|
blockid_t id = vox.id;
|
||||||
|
blockstate state = vox.state;
|
||||||
|
const auto& def = *blockDefsCache[id];
|
||||||
|
uint8_t variantId = def.getVariantIndex(state.userbits);
|
||||||
|
const auto& variant = def.getVariant(variantId);
|
||||||
|
if (id == 0 || state.segment) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int end = beginEnds[drawGroup][1];
|
if (denseRender != (variant.culling == CullingMode::OPTIONAL)) {
|
||||||
for (int i = begin-1; i <= end; i++) {
|
continue;
|
||||||
const voxel& vox = voxels[i];
|
}
|
||||||
blockid_t id = vox.id;
|
if (def.translucent) {
|
||||||
blockstate state = vox.state;
|
continue;
|
||||||
const auto& def = *blockDefsCache[id];
|
}
|
||||||
uint8_t variantId = def.getVariantIndex(state.userbits);
|
const UVRegion texfaces[6] {
|
||||||
const auto& variant = def.getVariant(variantId);
|
cache.getRegion(id, variantId, 0, densePass),
|
||||||
if (id == 0 || variant.drawGroup != drawGroup || state.segment) {
|
cache.getRegion(id, variantId, 1, densePass),
|
||||||
continue;
|
cache.getRegion(id, variantId, 2, densePass),
|
||||||
|
cache.getRegion(id, variantId, 3, densePass),
|
||||||
|
cache.getRegion(id, variantId, 4, densePass),
|
||||||
|
cache.getRegion(id, variantId, 5, densePass)
|
||||||
|
};
|
||||||
|
int x = i % CHUNK_W;
|
||||||
|
int y = i / (CHUNK_D * CHUNK_W);
|
||||||
|
int z = (i / CHUNK_D) % CHUNK_W;
|
||||||
|
|
||||||
|
switch (def.getModel(state.userbits).type) {
|
||||||
|
case BlockModelType::BLOCK:
|
||||||
|
blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless,
|
||||||
|
def.ambientOcclusion && enableAO);
|
||||||
|
break;
|
||||||
|
case BlockModelType::XSPRITE: {
|
||||||
|
if (!denseRender)
|
||||||
|
blockXSprite(x, y, z, glm::vec3(1.0f),
|
||||||
|
texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (denseRender != (variant.culling == CullingMode::OPTIONAL)) {
|
case BlockModelType::AABB: {
|
||||||
continue;
|
if (!denseRender)
|
||||||
|
blockAABB({x, y, z}, texfaces, &def, vox.state.rotation,
|
||||||
|
!def.shadeless, def.ambientOcclusion && enableAO);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (def.translucent) {
|
case BlockModelType::CUSTOM: {
|
||||||
continue;
|
blockCustomModel(
|
||||||
}
|
{x, y, z},
|
||||||
const UVRegion texfaces[6] {
|
def,
|
||||||
cache.getRegion(id, variantId, 0, densePass),
|
vox.state,
|
||||||
cache.getRegion(id, variantId, 1, densePass),
|
!def.shadeless,
|
||||||
cache.getRegion(id, variantId, 2, densePass),
|
def.ambientOcclusion && enableAO
|
||||||
cache.getRegion(id, variantId, 3, densePass),
|
);
|
||||||
cache.getRegion(id, variantId, 4, densePass),
|
break;
|
||||||
cache.getRegion(id, variantId, 5, densePass)
|
|
||||||
};
|
|
||||||
int x = i % CHUNK_W;
|
|
||||||
int y = i / (CHUNK_D * CHUNK_W);
|
|
||||||
int z = (i / CHUNK_D) % CHUNK_W;
|
|
||||||
switch (def.getModel(state.userbits).type) {
|
|
||||||
case BlockModelType::BLOCK:
|
|
||||||
blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless,
|
|
||||||
def.ambientOcclusion && enableAO);
|
|
||||||
break;
|
|
||||||
case BlockModelType::XSPRITE: {
|
|
||||||
if (!denseRender)
|
|
||||||
blockXSprite(x, y, z, glm::vec3(1.0f),
|
|
||||||
texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BlockModelType::AABB: {
|
|
||||||
if (!denseRender)
|
|
||||||
blockAABB({x, y, z}, texfaces, &def, vox.state.rotation,
|
|
||||||
!def.shadeless, def.ambientOcclusion && enableAO);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BlockModelType::CUSTOM: {
|
|
||||||
blockCustomModel(
|
|
||||||
{x, y, z},
|
|
||||||
def,
|
|
||||||
vox.state,
|
|
||||||
!def.shadeless,
|
|
||||||
def.ambientOcclusion && enableAO
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (overflow) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (overflow) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SortingMeshData BlocksRenderer::renderTranslucent(
|
SortingMeshData BlocksRenderer::renderTranslucent(
|
||||||
const voxel* voxels, int beginEnds[256][2]
|
const voxel* voxels, int totalBegin, int totalEnd
|
||||||
) {
|
) {
|
||||||
SortingMeshData sortingMesh {{}};
|
SortingMeshData sortingMesh {{}};
|
||||||
|
|
||||||
|
|
@ -568,106 +562,98 @@ SortingMeshData BlocksRenderer::renderTranslucent(
|
||||||
|
|
||||||
bool densePass = this->densePass;
|
bool densePass = this->densePass;
|
||||||
bool enableAO = settings.graphics.softLighting.get();
|
bool enableAO = settings.graphics.softLighting.get();
|
||||||
for (const auto drawGroup : *content.drawGroups) {
|
for (int i = totalBegin; i < totalEnd; i++) {
|
||||||
int begin = beginEnds[drawGroup][0];
|
const voxel& vox = voxels[i];
|
||||||
if (begin == 0) {
|
blockid_t id = vox.id;
|
||||||
|
blockstate state = vox.state;
|
||||||
|
const auto& def = *blockDefsCache[id];
|
||||||
|
uint8_t variantId = def.getVariantIndex(state.userbits);
|
||||||
|
if (id == 0 || state.segment) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int end = beginEnds[drawGroup][1];
|
if (!def.translucent) {
|
||||||
for (int i = begin-1; i <= end; i++) {
|
continue;
|
||||||
const voxel& vox = voxels[i];
|
|
||||||
blockid_t id = vox.id;
|
|
||||||
blockstate state = vox.state;
|
|
||||||
const auto& def = *blockDefsCache[id];
|
|
||||||
uint8_t variantId = def.getVariantIndex(state.userbits);
|
|
||||||
const auto& variant = def.getVariant(variantId);
|
|
||||||
if (id == 0 || variant.drawGroup != drawGroup || state.segment) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!def.translucent) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const UVRegion texfaces[6] {
|
|
||||||
cache.getRegion(id, variantId, 0, densePass),
|
|
||||||
cache.getRegion(id, variantId, 1, densePass),
|
|
||||||
cache.getRegion(id, variantId, 2, densePass),
|
|
||||||
cache.getRegion(id, variantId, 3, densePass),
|
|
||||||
cache.getRegion(id, variantId, 4, densePass),
|
|
||||||
cache.getRegion(id, variantId, 5, densePass)
|
|
||||||
};
|
|
||||||
int x = i % CHUNK_W;
|
|
||||||
int y = i / (CHUNK_D * CHUNK_W);
|
|
||||||
int z = (i / CHUNK_D) % CHUNK_W;
|
|
||||||
switch (def.getModel(state.userbits).type) {
|
|
||||||
case BlockModelType::BLOCK:
|
|
||||||
blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless,
|
|
||||||
def.ambientOcclusion && enableAO);
|
|
||||||
break;
|
|
||||||
case BlockModelType::XSPRITE: {
|
|
||||||
blockXSprite(x, y, z, glm::vec3(1.0f),
|
|
||||||
texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BlockModelType::AABB: {
|
|
||||||
blockAABB(
|
|
||||||
{x, y, z},
|
|
||||||
texfaces,
|
|
||||||
&def,
|
|
||||||
vox.state.rotation,
|
|
||||||
!def.shadeless,
|
|
||||||
def.ambientOcclusion && enableAO
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BlockModelType::CUSTOM: {
|
|
||||||
blockCustomModel(
|
|
||||||
{x, y, z},
|
|
||||||
def,
|
|
||||||
vox.state,
|
|
||||||
!def.shadeless,
|
|
||||||
def.ambientOcclusion && enableAO
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (vertexCount == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
SortingMeshEntry entry {
|
|
||||||
glm::vec3(
|
|
||||||
x + chunk->x * CHUNK_W + 0.5f,
|
|
||||||
y + 0.5f,
|
|
||||||
z + chunk->z * CHUNK_D + 0.5f
|
|
||||||
),
|
|
||||||
util::Buffer<ChunkVertex>(indexCount), 0};
|
|
||||||
|
|
||||||
totalSize += entry.vertexData.size();
|
|
||||||
|
|
||||||
for (int j = 0; j < indexCount; j++) {
|
|
||||||
std::memcpy(
|
|
||||||
entry.vertexData.data() + j,
|
|
||||||
vertexBuffer.get() + indexBuffer[j],
|
|
||||||
sizeof(ChunkVertex)
|
|
||||||
);
|
|
||||||
ChunkVertex& vertex = entry.vertexData[j];
|
|
||||||
|
|
||||||
if (!aabbInit) {
|
|
||||||
aabbInit = true;
|
|
||||||
aabb.a = aabb.b = vertex.position;
|
|
||||||
} else {
|
|
||||||
aabb.addPoint(vertex.position);
|
|
||||||
}
|
|
||||||
|
|
||||||
vertex.position.x += chunk->x * CHUNK_W + 0.5f;
|
|
||||||
vertex.position.y += 0.5f;
|
|
||||||
vertex.position.z += chunk->z * CHUNK_D + 0.5f;
|
|
||||||
}
|
|
||||||
sortingMesh.entries.push_back(std::move(entry));
|
|
||||||
vertexCount = 0;
|
|
||||||
vertexOffset = indexCount = 0;
|
|
||||||
}
|
}
|
||||||
|
const UVRegion texfaces[6] {
|
||||||
|
cache.getRegion(id, variantId, 0, densePass),
|
||||||
|
cache.getRegion(id, variantId, 1, densePass),
|
||||||
|
cache.getRegion(id, variantId, 2, densePass),
|
||||||
|
cache.getRegion(id, variantId, 3, densePass),
|
||||||
|
cache.getRegion(id, variantId, 4, densePass),
|
||||||
|
cache.getRegion(id, variantId, 5, densePass)
|
||||||
|
};
|
||||||
|
int x = i % CHUNK_W;
|
||||||
|
int y = i / (CHUNK_D * CHUNK_W);
|
||||||
|
int z = (i / CHUNK_D) % CHUNK_W;
|
||||||
|
switch (def.getModel(state.userbits).type) {
|
||||||
|
case BlockModelType::BLOCK:
|
||||||
|
blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless,
|
||||||
|
def.ambientOcclusion && enableAO);
|
||||||
|
break;
|
||||||
|
case BlockModelType::XSPRITE: {
|
||||||
|
blockXSprite(x, y, z, glm::vec3(1.0f),
|
||||||
|
texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BlockModelType::AABB: {
|
||||||
|
blockAABB(
|
||||||
|
{x, y, z},
|
||||||
|
texfaces,
|
||||||
|
&def,
|
||||||
|
vox.state.rotation,
|
||||||
|
!def.shadeless,
|
||||||
|
def.ambientOcclusion && enableAO
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BlockModelType::CUSTOM: {
|
||||||
|
blockCustomModel(
|
||||||
|
{x, y, z},
|
||||||
|
def,
|
||||||
|
vox.state,
|
||||||
|
!def.shadeless,
|
||||||
|
def.ambientOcclusion && enableAO
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (vertexCount == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SortingMeshEntry entry {
|
||||||
|
glm::vec3(
|
||||||
|
x + chunk->x * CHUNK_W + 0.5f,
|
||||||
|
y + 0.5f,
|
||||||
|
z + chunk->z * CHUNK_D + 0.5f
|
||||||
|
),
|
||||||
|
util::Buffer<ChunkVertex>(indexCount), 0};
|
||||||
|
|
||||||
|
totalSize += entry.vertexData.size();
|
||||||
|
|
||||||
|
for (int j = 0; j < indexCount; j++) {
|
||||||
|
std::memcpy(
|
||||||
|
entry.vertexData.data() + j,
|
||||||
|
vertexBuffer.get() + indexBuffer[j],
|
||||||
|
sizeof(ChunkVertex)
|
||||||
|
);
|
||||||
|
ChunkVertex& vertex = entry.vertexData[j];
|
||||||
|
|
||||||
|
if (!aabbInit) {
|
||||||
|
aabbInit = true;
|
||||||
|
aabb.a = aabb.b = vertex.position;
|
||||||
|
} else {
|
||||||
|
aabb.addPoint(vertex.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position.x += chunk->x * CHUNK_W + 0.5f;
|
||||||
|
vertex.position.y += 0.5f;
|
||||||
|
vertex.position.z += chunk->z * CHUNK_D + 0.5f;
|
||||||
|
}
|
||||||
|
sortingMesh.entries.push_back(std::move(entry));
|
||||||
|
vertexCount = 0;
|
||||||
|
vertexOffset = indexCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// additional powerful optimization
|
// additional powerful optimization
|
||||||
|
|
@ -709,19 +695,16 @@ void BlocksRenderer::build(
|
||||||
|
|
||||||
int totalBegin = chunk->bottom * (CHUNK_W * CHUNK_D);
|
int totalBegin = chunk->bottom * (CHUNK_W * CHUNK_D);
|
||||||
int totalEnd = chunk->top * (CHUNK_W * CHUNK_D);
|
int totalEnd = chunk->top * (CHUNK_W * CHUNK_D);
|
||||||
|
|
||||||
bool hasTranslucent = false;
|
bool hasTranslucent = false;
|
||||||
int beginEnds[256][2] {};
|
|
||||||
for (int i = totalBegin; i < totalEnd; i++) {
|
for (int i = totalBegin; i < totalEnd; i++) {
|
||||||
const voxel& vox = voxels[i];
|
const voxel& vox = voxels[i];
|
||||||
blockid_t id = vox.id;
|
blockid_t id = vox.id;
|
||||||
const auto& def = *blockDefsCache[id];
|
const auto& def = *blockDefsCache[id];
|
||||||
const auto& variant = def.getVariantByBits(vox.state.userbits);
|
if (def.translucent) {
|
||||||
hasTranslucent = def.translucent || hasTranslucent;
|
hasTranslucent = true;
|
||||||
|
break;
|
||||||
if (beginEnds[variant.drawGroup][0] == 0) {
|
|
||||||
beginEnds[variant.drawGroup][0] = i + 1;
|
|
||||||
}
|
}
|
||||||
beginEnds[variant.drawGroup][1] = i;
|
|
||||||
}
|
}
|
||||||
cancelled = false;
|
cancelled = false;
|
||||||
|
|
||||||
|
|
@ -733,7 +716,7 @@ void BlocksRenderer::build(
|
||||||
densePass = false;
|
densePass = false;
|
||||||
|
|
||||||
if (hasTranslucent) {
|
if (hasTranslucent) {
|
||||||
sortingMesh = renderTranslucent(voxels, beginEnds);
|
sortingMesh = renderTranslucent(voxels, totalBegin, totalEnd);
|
||||||
} else {
|
} else {
|
||||||
sortingMesh = {};
|
sortingMesh = {};
|
||||||
}
|
}
|
||||||
|
|
@ -746,13 +729,13 @@ void BlocksRenderer::build(
|
||||||
|
|
||||||
denseRender = false;
|
denseRender = false;
|
||||||
densePass = false;
|
densePass = false;
|
||||||
render(voxels, beginEnds);
|
render(voxels, totalBegin, totalEnd);
|
||||||
|
|
||||||
size_t endIndex = indexCount;
|
size_t endIndex = indexCount;
|
||||||
|
|
||||||
denseRender = true;
|
denseRender = true;
|
||||||
densePass = settings.graphics.denseRender.get();
|
densePass = settings.graphics.denseRender.get();
|
||||||
render(voxels, beginEnds);
|
render(voxels, totalBegin, totalEnd);
|
||||||
|
|
||||||
denseIndexCount = indexCount;
|
denseIndexCount = indexCount;
|
||||||
for (size_t i = 0; i < denseIndexCount; i++) {
|
for (size_t i = 0; i < denseIndexCount; i++) {
|
||||||
|
|
@ -761,7 +744,7 @@ void BlocksRenderer::build(
|
||||||
|
|
||||||
indexCount = endIndex;
|
indexCount = endIndex;
|
||||||
densePass = false;
|
densePass = false;
|
||||||
render(voxels, beginEnds);
|
render(voxels, totalBegin, totalEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChunkMeshData BlocksRenderer::createMesh() {
|
ChunkMeshData BlocksRenderer::createMesh() {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class BlocksRenderer final {
|
||||||
public:
|
public:
|
||||||
BlocksRenderer(
|
BlocksRenderer(
|
||||||
size_t capacity,
|
size_t capacity,
|
||||||
const Content& content,
|
const Block* const* blockDefs,
|
||||||
const ContentGfxCache& cache,
|
const ContentGfxCache& cache,
|
||||||
const EngineSettings& settings
|
const EngineSettings& settings
|
||||||
);
|
);
|
||||||
|
|
@ -44,7 +44,6 @@ public:
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
static const glm::vec3 SUN_VECTOR;
|
static const glm::vec3 SUN_VECTOR;
|
||||||
const Content& content;
|
|
||||||
std::unique_ptr<ChunkVertex[]> vertexBuffer;
|
std::unique_ptr<ChunkVertex[]> vertexBuffer;
|
||||||
std::unique_ptr<uint32_t[]> indexBuffer;
|
std::unique_ptr<uint32_t[]> indexBuffer;
|
||||||
std::unique_ptr<uint32_t[]> denseIndexBuffer;
|
std::unique_ptr<uint32_t[]> denseIndexBuffer;
|
||||||
|
|
@ -145,13 +144,16 @@ private:
|
||||||
);
|
);
|
||||||
|
|
||||||
// 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, const Block& def, const Variant& variant) const {
|
bool isOpen(const glm::ivec3& pos, const Block& def, const Variant& variant) const {
|
||||||
const auto& vox = voxelsBuffer->pickBlock(
|
const auto& vox = voxelsBuffer->pickBlock(
|
||||||
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
|
||||||
);
|
);
|
||||||
if (vox.id == BLOCK_VOID) {
|
if (vox.id == BLOCK_VOID) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (def.rt.id == vox.id && def.rt.solid && variant.culling == CullingMode::DEFAULT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const auto& block = *blockDefsCache[vox.id];
|
const auto& block = *blockDefsCache[vox.id];
|
||||||
const auto& blockVariant = block.getVariantByBits(vox.state.userbits);
|
const auto& blockVariant = block.getVariantByBits(vox.state.userbits);
|
||||||
uint8_t otherDrawGroup = blockVariant.drawGroup;
|
uint8_t otherDrawGroup = blockVariant.drawGroup;
|
||||||
|
|
@ -178,6 +180,6 @@ private:
|
||||||
float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up
|
float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
void render(const voxel* voxels, const int beginEnds[256][2]);
|
void render(const voxel* voxels, int totalBegin, int totalEnd);
|
||||||
SortingMeshData renderTranslucent(const voxel* voxels, int beginEnds[256][2]);
|
SortingMeshData renderTranslucent(const voxel* voxels, int totalBegin, int totalEnd);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,12 @@
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "window/Camera.hpp"
|
#include "window/Camera.hpp"
|
||||||
#include "maths/FrustumCulling.hpp"
|
#include "maths/FrustumCulling.hpp"
|
||||||
|
#include "maths/util.hpp"
|
||||||
#include "util/listutil.hpp"
|
#include "util/listutil.hpp"
|
||||||
#include "util/ObjectsPool.hpp"
|
#include "util/ObjectsPool.hpp"
|
||||||
|
#include "util/timeutil.hpp"
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
|
#include "content/Content.hpp"
|
||||||
|
|
||||||
static debug::Logger logger("chunks-render");
|
static debug::Logger logger("chunks-render");
|
||||||
|
|
||||||
|
|
@ -33,7 +36,7 @@ public:
|
||||||
settings.graphics.denseRender.get()
|
settings.graphics.denseRender.get()
|
||||||
? settings.graphics.chunkMaxVerticesDense.get()
|
? settings.graphics.chunkMaxVerticesDense.get()
|
||||||
: settings.graphics.chunkMaxVertices.get(),
|
: settings.graphics.chunkMaxVertices.get(),
|
||||||
level.content,
|
level.content.getIndices()->blocks.getDefs(),
|
||||||
cache,
|
cache,
|
||||||
settings
|
settings
|
||||||
) {
|
) {
|
||||||
|
|
@ -92,7 +95,7 @@ ChunksRenderer::ChunksRenderer(
|
||||||
threadPool.setStopOnFail(false);
|
threadPool.setStopOnFail(false);
|
||||||
renderer = std::make_unique<BlocksRenderer>(
|
renderer = std::make_unique<BlocksRenderer>(
|
||||||
settings.graphics.chunkMaxVertices.get(),
|
settings.graphics.chunkMaxVertices.get(),
|
||||||
level.content, cache, settings
|
level.content.getIndices()->blocks.getDefs(), cache, settings
|
||||||
);
|
);
|
||||||
logger.info() << "created " << threadPool.getWorkersCount() << " workers";
|
logger.info() << "created " << threadPool.getWorkersCount() << " workers";
|
||||||
logger.info() << "memory consumption is "
|
logger.info() << "memory consumption is "
|
||||||
|
|
@ -122,30 +125,30 @@ std::shared_ptr<VoxelsRenderVolume> ChunksRenderer::prepareVoxelsVolume(
|
||||||
return voxelsBuffer;
|
return voxelsBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChunkMesh* ChunksRenderer::render(
|
void ChunksRenderer::renderBlocking(const std::shared_ptr<Chunk>& chunk) {
|
||||||
const std::shared_ptr<Chunk>& chunk, bool important, bool lowPriority
|
glm::ivec2 key(chunk->x, chunk->z);
|
||||||
|
ChunkMesh mesh {};
|
||||||
|
auto voxelsBuffer = prepareVoxelsVolume(*chunk);
|
||||||
|
mesh = renderer->render(chunk.get(), *voxelsBuffer);
|
||||||
|
meshes[key] = std::move(mesh);
|
||||||
|
chunk->flags.modified = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChunksRenderer::render(
|
||||||
|
const std::shared_ptr<Chunk>& chunk, bool lowPriority
|
||||||
) {
|
) {
|
||||||
glm::ivec2 key(chunk->x, chunk->z);
|
glm::ivec2 key(chunk->x, chunk->z);
|
||||||
if (important) {
|
|
||||||
ChunkMesh mesh {};
|
|
||||||
auto voxelsBuffer = prepareVoxelsVolume(*chunk);
|
|
||||||
mesh = renderer->render(chunk.get(), *voxelsBuffer);
|
|
||||||
meshes[key] = std::move(mesh);
|
|
||||||
chunk->flags.modified = false;
|
|
||||||
return &meshes[key];
|
|
||||||
}
|
|
||||||
if (inwork.find(key) != inwork.end() ||
|
if (inwork.find(key) != inwork.end() ||
|
||||||
((inwork.size() >= threadPool.getWorkersCount() ||
|
((inwork.size() >= threadPool.getWorkersCount() ||
|
||||||
enqueuedInFrame >= MAX_CHUNKS_ENQUEUED_IN_FRAME) &&
|
enqueuedInFrame >= MAX_CHUNKS_ENQUEUED_IN_FRAME) &&
|
||||||
lowPriority)) {
|
lowPriority)) {
|
||||||
return nullptr;
|
return;
|
||||||
}
|
}
|
||||||
chunk->flags.modified = false;
|
chunk->flags.modified = false;
|
||||||
enqueuedInFrame++;
|
enqueuedInFrame++;
|
||||||
auto voxelsBuffer = prepareVoxelsVolume(*chunk);
|
auto voxelsBuffer = prepareVoxelsVolume(*chunk);
|
||||||
threadPool.enqueueJob({chunk, std::move(voxelsBuffer)});
|
threadPool.enqueueJob({chunk, std::move(voxelsBuffer)});
|
||||||
inwork[key] = true;
|
inwork[key] = true;
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunksRenderer::unload(const Chunk* chunk) {
|
void ChunksRenderer::unload(const Chunk* chunk) {
|
||||||
|
|
@ -161,76 +164,69 @@ void ChunksRenderer::clear() {
|
||||||
threadPool.clearQueue();
|
threadPool.clearQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChunkMesh* ChunksRenderer::getOrRender(
|
|
||||||
const std::shared_ptr<Chunk>& chunk, bool important, bool lowPriority
|
|
||||||
) {
|
|
||||||
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
|
||||||
if (found == meshes.end()) {
|
|
||||||
return render(chunk, important, lowPriority);
|
|
||||||
}
|
|
||||||
if (chunk->flags.modified && chunk->flags.lighted) {
|
|
||||||
render(chunk, important, lowPriority);
|
|
||||||
}
|
|
||||||
return &found->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChunksRenderer::update() {
|
void ChunksRenderer::update() {
|
||||||
threadPool.pullResults();
|
threadPool.pullResults();
|
||||||
enqueuedInFrame = 0;
|
enqueuedInFrame = 0;
|
||||||
}
|
|
||||||
|
|
||||||
const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk(
|
int width = chunks.getWidth();
|
||||||
size_t index, const Camera& camera, bool culling
|
int halfWidth = width / 2;
|
||||||
) {
|
int halfHeight = chunks.getHeight() / 2;
|
||||||
auto chunk = chunks.getChunks()[index];
|
int centerX = chunks.getOffsetX() + halfWidth;
|
||||||
if (chunk == nullptr) {
|
int centerY = chunks.getOffsetY() + halfHeight;
|
||||||
return nullptr;
|
|
||||||
}
|
meshBuildQueue.clear();
|
||||||
if (!chunk->flags.lighted) {
|
auto& chunksArray = chunks.getChunks();
|
||||||
const auto& found = meshes.find({chunk->x, chunk->z});
|
for (int index = 0; index < chunks.getVolume(); index++) {
|
||||||
if (found == meshes.end()) {
|
const auto& chunk = chunks.getChunks()[index];
|
||||||
return nullptr;
|
if (chunk == nullptr || !chunk->flags.lighted) {
|
||||||
} else {
|
continue;
|
||||||
return found->second.mesh.get();
|
}
|
||||||
|
int x = chunk->x;
|
||||||
|
int z = chunk->z;
|
||||||
|
if (chunk->flags.modified || meshes.find({x, z}) == meshes.end()) {
|
||||||
|
meshBuildQueue.emplace_back(x - centerX, z - centerY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
float distance = glm::distance(
|
std::sort(
|
||||||
camera.position,
|
meshBuildQueue.begin(),
|
||||||
glm::vec3(
|
meshBuildQueue.end(),
|
||||||
(chunk->x + 0.5f) * CHUNK_W,
|
[](const glm::ivec2& a, const glm::ivec2& b) {
|
||||||
camera.position.y,
|
return util::length2(a) < util::length2(b);
|
||||||
(chunk->z + 0.5f) * CHUNK_D
|
}
|
||||||
)
|
|
||||||
);
|
);
|
||||||
auto mesh = getOrRender(
|
|
||||||
chunk,
|
|
||||||
distance < CHUNK_W * 1.5f,
|
|
||||||
distance > CHUNK_W * settings.chunks.loadDistance.get() * 0.5
|
|
||||||
);
|
|
||||||
if (mesh == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (chunk->flags.dirtyHeights) {
|
|
||||||
chunk->updateHeights();
|
|
||||||
}
|
|
||||||
if (culling) {
|
|
||||||
const auto& meshAABB = mesh->meshAABB;
|
|
||||||
auto aabbMin = meshAABB.min();
|
|
||||||
auto aabbMax = meshAABB.max();
|
|
||||||
glm::vec3 min(
|
|
||||||
chunk->x * CHUNK_W + std::min(0.0f, aabbMin.x),
|
|
||||||
chunk->bottom,
|
|
||||||
chunk->z * CHUNK_D + std::min(0.0f, aabbMin.z)
|
|
||||||
);
|
|
||||||
glm::vec3 max(
|
|
||||||
chunk->x * CHUNK_W + aabbMax.x,
|
|
||||||
chunk->top,
|
|
||||||
chunk->z * CHUNK_D + aabbMax.z
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!frustum.isBoxVisible(min, max)) return nullptr;
|
int loadDistance = settings.chunks.loadDistance.get();
|
||||||
|
|
||||||
|
const int topN = 10;
|
||||||
|
int top = std::min<int>(meshBuildQueue.size(), topN);
|
||||||
|
for (int i = 0; i < top; i++) {
|
||||||
|
glm::ivec2 offset = meshBuildQueue[i];
|
||||||
|
size_t index =
|
||||||
|
(offset.y + halfHeight) * width + offset.x + halfWidth;
|
||||||
|
|
||||||
|
auto& chunk = chunksArray[index];
|
||||||
|
assert(chunk != nullptr);
|
||||||
|
|
||||||
|
float distance = glm::distance(
|
||||||
|
glm::vec3 (centerX, 0.0f, centerY),
|
||||||
|
glm::vec3(
|
||||||
|
(chunk->x + 0.5f) * CHUNK_W,
|
||||||
|
0,
|
||||||
|
(chunk->z + 0.5f) * CHUNK_D
|
||||||
|
)
|
||||||
|
);
|
||||||
|
bool important = distance < CHUNK_W * 1.5f;
|
||||||
|
bool lowPriority = distance > CHUNK_W * loadDistance * 0.5;
|
||||||
|
|
||||||
|
if (chunk->flags.dirtyHeights) {
|
||||||
|
chunk->updateHeights();
|
||||||
|
}
|
||||||
|
if (important) {
|
||||||
|
renderBlocking(chunk);
|
||||||
|
} else {
|
||||||
|
render(chunk, lowPriority);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return mesh->mesh.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunksRenderer::drawShadowsPass(
|
void ChunksRenderer::drawShadowsPass(
|
||||||
|
|
@ -317,16 +313,43 @@ void ChunksRenderer::drawChunks(
|
||||||
// TODO: minimize draw calls number
|
// TODO: minimize draw calls number
|
||||||
for (int i = indices.size()-1; i >= 0; i--) {
|
for (int i = indices.size()-1; i >= 0; i--) {
|
||||||
auto& chunk = chunks.getChunks()[indices[i].index];
|
auto& chunk = chunks.getChunks()[indices[i].index];
|
||||||
auto mesh = retrieveChunk(indices[i].index, camera, culling);
|
if (chunk == nullptr) {
|
||||||
if (mesh == nullptr) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||||
|
if (found == meshes.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto& mesh = found->second;
|
||||||
|
if (mesh.mesh == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (culling) {
|
||||||
|
const auto& meshAABB = mesh.meshAABB;
|
||||||
|
auto aabbMin = meshAABB.min();
|
||||||
|
auto aabbMax = meshAABB.max();
|
||||||
|
glm::vec3 min(
|
||||||
|
chunk->x * CHUNK_W + std::min(0.0f, aabbMin.x),
|
||||||
|
chunk->bottom,
|
||||||
|
chunk->z * CHUNK_D + std::min(0.0f, aabbMin.z)
|
||||||
|
);
|
||||||
|
glm::vec3 max(
|
||||||
|
chunk->x * CHUNK_W + aabbMax.x,
|
||||||
|
chunk->top,
|
||||||
|
chunk->z * CHUNK_D + aabbMax.z
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!frustum.isBoxVisible(min, max)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec3 coord(
|
glm::vec3 coord(
|
||||||
chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f
|
chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f
|
||||||
);
|
);
|
||||||
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
||||||
shader.uniformMatrix("u_model", model);
|
shader.uniformMatrix("u_model", model);
|
||||||
mesh->draw(GL_TRIANGLES, glm::distance2(camera.position * glm::vec3(1, 0, 1),
|
mesh.mesh->draw(GL_TRIANGLES, glm::distance2(camera.position * glm::vec3(1, 0, 1),
|
||||||
(coord + glm::vec3(CHUNK_W * 0.5f, 0.0f, CHUNK_D * 0.5f))) < denseDistance2);
|
(coord + glm::vec3(CHUNK_W * 0.5f, 0.0f, CHUNK_D * 0.5f))) < denseDistance2);
|
||||||
visibleChunks++;
|
visibleChunks++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
|
||||||
|
#include "util/ThreadPool.hpp"
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#define GLM_ENABLE_EXPERIMENTAL
|
|
||||||
#include <glm/gtx/hash.hpp>
|
#include <glm/gtx/hash.hpp>
|
||||||
|
|
||||||
#include "util/ThreadPool.hpp"
|
|
||||||
#include "commons.hpp"
|
|
||||||
|
|
||||||
template<typename VertexStructure> class Mesh;
|
template<typename VertexStructure> class Mesh;
|
||||||
class Chunk;
|
class Chunk;
|
||||||
class Level;
|
class Level;
|
||||||
|
|
@ -44,22 +45,6 @@ struct RendererJob {
|
||||||
};
|
};
|
||||||
|
|
||||||
class ChunksRenderer {
|
class ChunksRenderer {
|
||||||
const Chunks& chunks;
|
|
||||||
const Assets& assets;
|
|
||||||
const Frustum& frustum;
|
|
||||||
const EngineSettings& settings;
|
|
||||||
|
|
||||||
std::unique_ptr<BlocksRenderer> renderer;
|
|
||||||
std::unordered_map<glm::ivec2, ChunkMesh> meshes;
|
|
||||||
std::unordered_map<glm::ivec2, bool> inwork;
|
|
||||||
std::vector<ChunksSortEntry> indices;
|
|
||||||
util::ThreadPool<RendererJob, RendererResult> threadPool;
|
|
||||||
const Mesh<ChunkVertex>* retrieveChunk(
|
|
||||||
size_t index, const Camera& camera, bool culling
|
|
||||||
);
|
|
||||||
std::shared_ptr<VoxelsRenderVolume> prepareVoxelsVolume(const Chunk& chunk);
|
|
||||||
|
|
||||||
size_t enqueuedInFrame = 0;
|
|
||||||
public:
|
public:
|
||||||
ChunksRenderer(
|
ChunksRenderer(
|
||||||
const Level& level,
|
const Level& level,
|
||||||
|
|
@ -69,18 +54,11 @@ public:
|
||||||
const ContentGfxCache& cache,
|
const ContentGfxCache& cache,
|
||||||
const EngineSettings& settings
|
const EngineSettings& settings
|
||||||
);
|
);
|
||||||
virtual ~ChunksRenderer();
|
~ChunksRenderer();
|
||||||
|
|
||||||
const ChunkMesh* render(
|
|
||||||
const std::shared_ptr<Chunk>& chunk, bool important, bool lowPriority
|
|
||||||
);
|
|
||||||
void unload(const Chunk* chunk);
|
void unload(const Chunk* chunk);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
const ChunkMesh* getOrRender(
|
|
||||||
const std::shared_ptr<Chunk>& chunk, bool important, bool lowPriority
|
|
||||||
);
|
|
||||||
|
|
||||||
void drawShadowsPass(
|
void drawShadowsPass(
|
||||||
const Camera& camera, Shader& shader, const Camera& playerCamera
|
const Camera& camera, Shader& shader, const Camera& playerCamera
|
||||||
);
|
);
|
||||||
|
|
@ -92,4 +70,25 @@ public:
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
static size_t visibleChunks;
|
static size_t visibleChunks;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Chunks& chunks;
|
||||||
|
const Assets& assets;
|
||||||
|
const Frustum& frustum;
|
||||||
|
const EngineSettings& settings;
|
||||||
|
|
||||||
|
std::unique_ptr<BlocksRenderer> renderer;
|
||||||
|
std::unordered_map<glm::ivec2, ChunkMesh> meshes;
|
||||||
|
std::unordered_map<glm::ivec2, bool> inwork;
|
||||||
|
std::vector<ChunksSortEntry> indices;
|
||||||
|
util::ThreadPool<RendererJob, RendererResult> threadPool;
|
||||||
|
std::vector<glm::ivec2> meshBuildQueue;
|
||||||
|
|
||||||
|
size_t enqueuedInFrame = 0;
|
||||||
|
|
||||||
|
std::shared_ptr<VoxelsRenderVolume> prepareVoxelsVolume(const Chunk& chunk);
|
||||||
|
|
||||||
|
void render(const std::shared_ptr<Chunk>& chunk, bool lowPriority);
|
||||||
|
|
||||||
|
void renderBlocking(const std::shared_ptr<Chunk>& chunk);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -329,7 +329,7 @@ void CloudsRenderer::draw(
|
||||||
|
|
||||||
shader.uniform4f("u_tint", glm::vec4(weather.cloudsTint(), 1.0f));
|
shader.uniform4f("u_tint", glm::vec4(weather.cloudsTint(), 1.0f));
|
||||||
shader.uniform1f("u_fogFactor", fogFactor * 0.03f);
|
shader.uniform1f("u_fogFactor", fogFactor * 0.03f);
|
||||||
shader.uniform1f("u_fogCurve", 0.7f - 0.3f);
|
shader.uniform1f("u_fogCurve", 0.4f);
|
||||||
|
|
||||||
for (int i = 0; i < std::min<int>(quality, layers.size()); i++) {
|
for (int i = 0; i < std::min<int>(quality, layers.size()); i++) {
|
||||||
draw(layers[i], frustum, shader, camera, timer, i);
|
draw(layers[i], frustum, shader, camera, timer, i);
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ void DebugLinesRenderer::drawCoordSystem(
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugLinesRenderer::render(
|
void DebugLinesRenderer::render(
|
||||||
DrawContext& pctx,
|
const DrawContext& pctx,
|
||||||
const Camera& camera,
|
const Camera& camera,
|
||||||
LineBatch& linesBatch,
|
LineBatch& linesBatch,
|
||||||
Shader& linesShader,
|
Shader& linesShader,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ public:
|
||||||
/// @param linesShader Shader used for rendering lines
|
/// @param linesShader Shader used for rendering lines
|
||||||
/// @param showChunkBorders Whether to show chunk borders
|
/// @param showChunkBorders Whether to show chunk borders
|
||||||
void render(
|
void render(
|
||||||
DrawContext& ctx,
|
const DrawContext& ctx,
|
||||||
const Camera& camera,
|
const Camera& camera,
|
||||||
LineBatch& linesBatch,
|
LineBatch& linesBatch,
|
||||||
Shader& linesShader,
|
Shader& linesShader,
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
#include "voxels/blocks_agent.hpp"
|
#include "voxels/blocks_agent.hpp"
|
||||||
#include "window/Camera.hpp"
|
#include "window/Camera.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
|
#include "world/Weather.hpp"
|
||||||
#include "WorldRenderer.hpp"
|
#include "WorldRenderer.hpp"
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ void MainBatch::flush() {
|
||||||
texture = blank.get();
|
texture = blank.get();
|
||||||
}
|
}
|
||||||
texture->bind();
|
texture->bind();
|
||||||
mesh->reload(buffer.get(), index);
|
mesh->reload(buffer.get(), index, true);
|
||||||
mesh->draw();
|
mesh->draw();
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,9 @@
|
||||||
#include "window/Window.hpp"
|
#include "window/Window.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "world/LevelEvents.hpp"
|
#include "world/LevelEvents.hpp"
|
||||||
|
#include "world/Weather.hpp"
|
||||||
#include "world/World.hpp"
|
#include "world/World.hpp"
|
||||||
|
#include "debug/Logger.hpp"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
@ -62,9 +64,20 @@
|
||||||
inline constexpr size_t BATCH3D_CAPACITY = 4096;
|
inline constexpr size_t BATCH3D_CAPACITY = 4096;
|
||||||
inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000;
|
inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000;
|
||||||
|
|
||||||
|
static debug::Logger logger("world-renderer");
|
||||||
|
|
||||||
bool WorldRenderer::showChunkBorders = false;
|
bool WorldRenderer::showChunkBorders = false;
|
||||||
bool WorldRenderer::showEntitiesDebug = false;
|
bool WorldRenderer::showEntitiesDebug = false;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static ObserverHandler observe_setting(
|
||||||
|
ObservableSetting<T>& setting, bool& dirtySettings
|
||||||
|
) {
|
||||||
|
return setting.observe([&dirtySettings](auto const&) {
|
||||||
|
dirtySettings = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
WorldRenderer::WorldRenderer(
|
WorldRenderer::WorldRenderer(
|
||||||
Engine& engine, LevelFrontend& frontend, Player& player
|
Engine& engine, LevelFrontend& frontend, Player& player
|
||||||
)
|
)
|
||||||
|
|
@ -72,6 +85,7 @@ WorldRenderer::WorldRenderer(
|
||||||
level(frontend.getLevel()),
|
level(frontend.getLevel()),
|
||||||
player(player),
|
player(player),
|
||||||
assets(*engine.getAssets()),
|
assets(*engine.getAssets()),
|
||||||
|
weather(frontend.getWeather()),
|
||||||
frustumCulling(std::make_unique<Frustum>()),
|
frustumCulling(std::make_unique<Frustum>()),
|
||||||
lineBatch(std::make_unique<LineBatch>()),
|
lineBatch(std::make_unique<LineBatch>()),
|
||||||
batch3d(std::make_unique<Batch3D>(BATCH3D_CAPACITY)),
|
batch3d(std::make_unique<Batch3D>(BATCH3D_CAPACITY)),
|
||||||
|
|
@ -120,10 +134,41 @@ WorldRenderer::WorldRenderer(
|
||||||
shadowMapping = std::make_unique<Shadows>(level);
|
shadowMapping = std::make_unique<Shadows>(level);
|
||||||
debugLines = std::make_unique<DebugLinesRenderer>(level);
|
debugLines = std::make_unique<DebugLinesRenderer>(level);
|
||||||
cloudsRenderer = std::make_unique<CloudsRenderer>();
|
cloudsRenderer = std::make_unique<CloudsRenderer>();
|
||||||
|
|
||||||
|
keepAlive(observe_setting(settings.graphics.advancedRender, dirtySettings));
|
||||||
|
keepAlive(observe_setting(settings.graphics.shadowsQuality, dirtySettings));
|
||||||
|
keepAlive(observe_setting(settings.graphics.ssao, dirtySettings));
|
||||||
}
|
}
|
||||||
|
|
||||||
WorldRenderer::~WorldRenderer() = default;
|
WorldRenderer::~WorldRenderer() = default;
|
||||||
|
|
||||||
|
void WorldRenderer::refreshSettings() {
|
||||||
|
Shader* affectedShaders[] {
|
||||||
|
&assets.require<Shader>("main"),
|
||||||
|
&assets.require<Shader>("entity"),
|
||||||
|
&assets.require<Shader>("clouds"),
|
||||||
|
&assets.require<Shader>("translucent"),
|
||||||
|
&assets.require<PostEffect>("deferred_lighting").getShader(),
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto& graphics = engine.getSettings().graphics;
|
||||||
|
gbufferPipeline = graphics.advancedRender.get();
|
||||||
|
|
||||||
|
int shadowsQuality = graphics.shadowsQuality.get() * gbufferPipeline;
|
||||||
|
shadowMapping->setQuality(shadowsQuality);
|
||||||
|
|
||||||
|
std::vector<std::string> defines;
|
||||||
|
if (shadowsQuality != 0) defines.emplace_back("ENABLE_SHADOWS");
|
||||||
|
if (graphics.ssao.get()) defines.emplace_back("ENABLE_SSAO");
|
||||||
|
if (gbufferPipeline) defines.emplace_back("ADVANCED_RENDER");
|
||||||
|
|
||||||
|
logger.info() << "recompiling shaders due to settings change";
|
||||||
|
for (size_t i = 0; affectedShaders[i]; i++) {
|
||||||
|
affectedShaders[i]->recompile(defines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void setup_weather(Shader& shader, const Weather& weather) {
|
static void setup_weather(Shader& shader, const Weather& weather) {
|
||||||
shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity());
|
shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity());
|
||||||
shader.uniform1f("u_weatherFogDencity", weather.fogDencity());
|
shader.uniform1f("u_weatherFogDencity", weather.fogDencity());
|
||||||
|
|
@ -156,7 +201,7 @@ void WorldRenderer::setupWorldShader(
|
||||||
shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
|
shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
|
||||||
shader.uniform1i("u_debugLights", lightsDebug);
|
shader.uniform1i("u_debugLights", lightsDebug);
|
||||||
shader.uniform1i("u_debugNormals", false);
|
shader.uniform1i("u_debugNormals", false);
|
||||||
shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime);
|
shader.uniform1f("u_dayTime", level.getWorld().getInfo().daytime);
|
||||||
shader.uniform2f("u_lightDir", skybox->getLightDir());
|
shader.uniform2f("u_lightDir", skybox->getLightDir());
|
||||||
shader.uniform1i("u_skybox", advanced_pipeline::TARGET_SKYBOX);
|
shader.uniform1i("u_skybox", advanced_pipeline::TARGET_SKYBOX);
|
||||||
|
|
||||||
|
|
@ -188,8 +233,6 @@ void WorldRenderer::renderOpaque(
|
||||||
const EngineSettings& settings,
|
const EngineSettings& settings,
|
||||||
bool hudVisible
|
bool hudVisible
|
||||||
) {
|
) {
|
||||||
texts->render(ctx, camera, settings, hudVisible, false);
|
|
||||||
|
|
||||||
float fogFactor = calcFogFactor();
|
float fogFactor = calcFogFactor();
|
||||||
|
|
||||||
auto& entityShader = assets.require<Shader>("entity");
|
auto& entityShader = assets.require<Shader>("entity");
|
||||||
|
|
@ -212,12 +255,12 @@ void WorldRenderer::renderOpaque(
|
||||||
modelBatch->render();
|
modelBatch->render();
|
||||||
particles->render(camera);
|
particles->render(camera);
|
||||||
|
|
||||||
auto& shader = assets.require<Shader>("main");
|
auto& mainShader = assets.require<Shader>("main");
|
||||||
auto& cloudsShader = assets.require<Shader>("clouds");
|
auto& cloudsShader = assets.require<Shader>("clouds");
|
||||||
|
|
||||||
setupWorldShader(shader, camera, settings, fogFactor);
|
setupWorldShader(mainShader, camera, settings, fogFactor);
|
||||||
|
|
||||||
chunksRenderer->drawChunks(camera, shader);
|
chunksRenderer->drawChunks(camera, mainShader);
|
||||||
blockWraps->draw(ctx);
|
blockWraps->draw(ctx);
|
||||||
|
|
||||||
if (level.environment.sky.clouds) {
|
if (level.environment.sky.clouds) {
|
||||||
|
|
@ -232,7 +275,7 @@ void WorldRenderer::renderOpaque(
|
||||||
|
|
||||||
if (hudVisible) {
|
if (hudVisible) {
|
||||||
auto& linesShader = assets.require<Shader>("lines");
|
auto& linesShader = assets.require<Shader>("lines");
|
||||||
renderLines(camera, linesShader, ctx);
|
renderInWorldLines(camera, linesShader, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,7 +308,7 @@ void WorldRenderer::renderBlockSelection() {
|
||||||
lineBatch->flush();
|
lineBatch->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::renderLines(
|
void WorldRenderer::renderInWorldLines(
|
||||||
const Camera& camera, Shader& linesShader, const DrawContext& pctx
|
const Camera& camera, Shader& linesShader, const DrawContext& pctx
|
||||||
) {
|
) {
|
||||||
linesShader.use();
|
linesShader.use();
|
||||||
|
|
@ -283,193 +326,144 @@ void WorldRenderer::renderLines(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::refreshSettings(Shader** shaders) {
|
|
||||||
const auto& graphics = engine.getSettings().graphics;
|
|
||||||
gbufferPipeline = graphics.advancedRender.get();
|
|
||||||
|
|
||||||
int shadowsQuality = graphics.shadowsQuality.get() * gbufferPipeline;
|
|
||||||
shadowMapping->setQuality(shadowsQuality);
|
|
||||||
|
|
||||||
CompileTimeShaderSettings currentSettings {
|
|
||||||
gbufferPipeline,
|
|
||||||
shadowsQuality != 0,
|
|
||||||
graphics.ssao.get() && gbufferPipeline};
|
|
||||||
if (prevCTShaderSettings.advancedRender != currentSettings.advancedRender ||
|
|
||||||
prevCTShaderSettings.shadows != currentSettings.shadows ||
|
|
||||||
prevCTShaderSettings.ssao != currentSettings.ssao) {
|
|
||||||
std::vector<std::string> defines;
|
|
||||||
if (currentSettings.shadows) defines.emplace_back("ENABLE_SHADOWS");
|
|
||||||
if (currentSettings.ssao) defines.emplace_back("ENABLE_SSAO");
|
|
||||||
if (currentSettings.advancedRender)
|
|
||||||
defines.emplace_back("ADVANCED_RENDER");
|
|
||||||
|
|
||||||
for (size_t i = 0; shaders[i]; i++) {
|
|
||||||
shaders[i]->recompile(defines);
|
|
||||||
}
|
|
||||||
prevCTShaderSettings = currentSettings;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WorldRenderer::update(const Camera& camera, float delta) {
|
void WorldRenderer::update(const Camera& camera, float delta) {
|
||||||
timer += delta;
|
timer += delta;
|
||||||
|
chunksRenderer->update();
|
||||||
weather.update(delta);
|
weather.update(delta);
|
||||||
precipitation->update(delta);
|
precipitation->update(delta);
|
||||||
particles->update(camera, delta);
|
particles->update(camera, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WorldRenderer::renderFrameClassic(
|
||||||
|
const DrawContext& pctx,
|
||||||
|
Camera& camera,
|
||||||
|
bool hudVisible,
|
||||||
|
PostProcessing& postProcessing
|
||||||
|
) {
|
||||||
|
const auto& settings = engine.getSettings();
|
||||||
|
const auto& worldInfo = level.getWorld().getInfo();
|
||||||
|
|
||||||
|
DrawContext ctx = pctx.sub();
|
||||||
|
ctx.setDepthTest(true);
|
||||||
|
ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
||||||
|
ctx.setFramebuffer(postProcessing.getFramebuffer());
|
||||||
|
|
||||||
|
skybox->draw(
|
||||||
|
level.environment, ctx, camera, worldInfo.daytime, weather.clouds()
|
||||||
|
);
|
||||||
|
texts->render(ctx, camera, settings, hudVisible, false);
|
||||||
|
|
||||||
|
if (debug && hudVisible) {
|
||||||
|
renderDebugLines(ctx, camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawContext wctx = ctx.sub();
|
||||||
|
wctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
|
||||||
|
wctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
||||||
|
// Translucent blocks
|
||||||
|
{
|
||||||
|
const auto& settings = engine.getSettings();
|
||||||
|
auto& translucentShader = assets.require<Shader>("translucent");
|
||||||
|
auto sctx = wctx.sub();
|
||||||
|
sctx.setCullFace(true);
|
||||||
|
translucentShader.use();
|
||||||
|
setupWorldShader(translucentShader, camera, settings, calcFogFactor());
|
||||||
|
chunksRenderer->drawSortedMeshes(camera, translucentShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderWeatherEffects(camera);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldRenderer::renderFrameAdvanced(
|
||||||
|
const DrawContext& pctx,
|
||||||
|
Camera& camera,
|
||||||
|
bool hudVisible,
|
||||||
|
PostProcessing& postProcessing
|
||||||
|
) {
|
||||||
|
const auto& settings = engine.getSettings();
|
||||||
|
const auto& worldInfo = level.getWorld().getInfo();
|
||||||
|
float fogFactor = calcFogFactor();
|
||||||
|
|
||||||
|
shadowMapping->refresh(camera, pctx, [this, &camera](Camera& shadowCamera) {
|
||||||
|
auto& shader = assets.require<Shader>("shadows");
|
||||||
|
setupWorldShader(shader, shadowCamera, engine.getSettings(), 0.0f);
|
||||||
|
chunksRenderer->drawShadowsPass(shadowCamera, shader, camera);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto& deferredShader =
|
||||||
|
assets.require<PostEffect>("deferred_lighting").getShader();
|
||||||
|
deferredShader.use();
|
||||||
|
setupWorldShader(deferredShader, camera, settings, fogFactor);
|
||||||
|
postProcessing.renderDeferredShading(pctx, assets, timer, camera);
|
||||||
|
|
||||||
|
DrawContext ctx = pctx.sub();
|
||||||
|
ctx.setDepthTest(true);
|
||||||
|
ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
||||||
|
|
||||||
|
postProcessing.bindDepthBuffer();
|
||||||
|
skybox->draw(
|
||||||
|
level.environment, ctx, camera, worldInfo.daytime, weather.clouds()
|
||||||
|
);
|
||||||
|
texts->render(ctx, camera, settings, hudVisible, false);
|
||||||
|
if (debug && hudVisible) {
|
||||||
|
renderDebugLines(ctx, camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawContext wctx = ctx.sub();
|
||||||
|
wctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
|
||||||
|
wctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
||||||
|
// Translucent blocks
|
||||||
|
{
|
||||||
|
const auto& settings = engine.getSettings();
|
||||||
|
auto& translucentShader = assets.require<Shader>("translucent");
|
||||||
|
auto sctx = wctx.sub();
|
||||||
|
sctx.setCullFace(true);
|
||||||
|
translucentShader.use();
|
||||||
|
setupWorldShader(translucentShader, camera, settings, fogFactor);
|
||||||
|
chunksRenderer->drawSortedMeshes(camera, translucentShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderWeatherEffects(camera);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
void WorldRenderer::renderFrame(
|
void WorldRenderer::renderFrame(
|
||||||
const DrawContext& pctx,
|
const DrawContext& pctx,
|
||||||
Camera& camera,
|
Camera& camera,
|
||||||
bool hudVisible,
|
bool hudVisible,
|
||||||
PostProcessing& postProcessing
|
PostProcessing& postProcessing
|
||||||
) {
|
) {
|
||||||
auto projView = camera.getProjView();
|
|
||||||
auto world = level.getWorld();
|
|
||||||
|
|
||||||
const auto& vp = pctx.getViewport();
|
const auto& vp = pctx.getViewport();
|
||||||
camera.setAspectRatio(vp.x / static_cast<float>(vp.y));
|
camera.setAspectRatio(vp.x / static_cast<float>(vp.y));
|
||||||
|
if (dirtySettings) {
|
||||||
|
refreshSettings();
|
||||||
|
dirtySettings = false;
|
||||||
|
}
|
||||||
|
|
||||||
auto& mainShader = assets.require<Shader>("main");
|
const auto& worldInfo = level.getWorld().getInfo();
|
||||||
auto& entityShader = assets.require<Shader>("entity");
|
|
||||||
auto& cloudsShader = assets.require<Shader>("clouds");
|
|
||||||
auto& translucentShader = assets.require<Shader>("translucent");
|
|
||||||
auto& deferredShader =
|
|
||||||
assets.require<PostEffect>("deferred_lighting").getShader();
|
|
||||||
|
|
||||||
const auto& settings = engine.getSettings();
|
|
||||||
|
|
||||||
Shader* affectedShaders[] {
|
|
||||||
&mainShader,
|
|
||||||
&entityShader,
|
|
||||||
&cloudsShader,
|
|
||||||
&translucentShader,
|
|
||||||
&deferredShader,
|
|
||||||
nullptr};
|
|
||||||
|
|
||||||
refreshSettings(affectedShaders);
|
|
||||||
|
|
||||||
const auto& worldInfo = world->getInfo();
|
|
||||||
|
|
||||||
float clouds = weather.clouds();
|
float clouds = weather.clouds();
|
||||||
clouds = glm::max(worldInfo.fog, clouds);
|
|
||||||
float mie = 1.0f + glm::max(worldInfo.fog, clouds * 0.5f) * 2.0f;
|
|
||||||
|
|
||||||
float random = rand() / static_cast<float>(RAND_MAX);
|
float random = rand() / static_cast<float>(RAND_MAX);
|
||||||
skybox->refresh(
|
skybox->refresh(
|
||||||
level.environment,
|
level.environment,
|
||||||
pctx,
|
pctx,
|
||||||
worldInfo.daytime,
|
worldInfo.daytime,
|
||||||
mie,
|
1.0f + clouds,
|
||||||
weather.skyTint(),
|
weather.skyTint(),
|
||||||
weather.highlight * random,
|
weather.highlight * random,
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
renderOpaquePass(pctx, camera, hudVisible, postProcessing);
|
||||||
chunksRenderer->update();
|
|
||||||
|
|
||||||
shadowMapping->refresh(camera, pctx, [this, &camera](Camera& shadowCamera) {
|
|
||||||
auto& shader = assets.require<Shader>("shadows");
|
|
||||||
setupWorldShader(shader, shadowCamera, engine.getSettings(), 0.0f);
|
|
||||||
chunksRenderer->drawShadowsPass(shadowCamera, shader, camera);
|
|
||||||
});
|
|
||||||
{
|
|
||||||
DrawContext wctx = pctx.sub();
|
|
||||||
postProcessing.use(wctx, gbufferPipeline);
|
|
||||||
display::clearDepth();
|
|
||||||
|
|
||||||
/* Main opaque pass (GBuffer pass) */ {
|
|
||||||
DrawContext ctx = wctx.sub();
|
|
||||||
ctx.setDepthTest(true);
|
|
||||||
ctx.setCullFace(true);
|
|
||||||
ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
|
|
||||||
ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
|
||||||
renderOpaque(ctx, camera, settings, hudVisible);
|
|
||||||
}
|
|
||||||
texts->render(pctx, camera, settings, hudVisible, true);
|
|
||||||
}
|
|
||||||
float fogFactor = calcFogFactor();
|
|
||||||
if (gbufferPipeline) {
|
if (gbufferPipeline) {
|
||||||
deferredShader.use();
|
renderFrameAdvanced(pctx, camera, hudVisible, postProcessing);
|
||||||
setupWorldShader(deferredShader, camera, settings, fogFactor);
|
} else {
|
||||||
postProcessing.renderDeferredShading(pctx, assets, timer, camera);
|
renderFrameClassic(pctx, camera, hudVisible, postProcessing);
|
||||||
}
|
|
||||||
{
|
|
||||||
DrawContext ctx = pctx.sub();
|
|
||||||
ctx.setDepthTest(true);
|
|
||||||
ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
|
||||||
|
|
||||||
if (gbufferPipeline) {
|
|
||||||
postProcessing.bindDepthBuffer();
|
|
||||||
} else {
|
|
||||||
ctx.setFramebuffer(postProcessing.getFramebuffer());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Background sky plane
|
|
||||||
skybox->draw(
|
|
||||||
level.environment, ctx, camera, worldInfo.daytime, clouds
|
|
||||||
);
|
|
||||||
|
|
||||||
auto& linesShader = assets.require<Shader>("lines");
|
|
||||||
linesShader.use();
|
|
||||||
if (debug && hudVisible) {
|
|
||||||
debugLines->render(
|
|
||||||
ctx, camera, *lineBatch, linesShader, showChunkBorders
|
|
||||||
);
|
|
||||||
}
|
|
||||||
linesShader.uniformMatrix("u_projview", projView);
|
|
||||||
lineBatch->flush();
|
|
||||||
|
|
||||||
DrawContext wctx = ctx.sub();
|
|
||||||
wctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
|
|
||||||
wctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
|
||||||
// Translucent blocks
|
|
||||||
{
|
|
||||||
auto sctx = wctx.sub();
|
|
||||||
sctx.setCullFace(true);
|
|
||||||
translucentShader.use();
|
|
||||||
setupWorldShader(translucentShader, camera, settings, fogFactor);
|
|
||||||
chunksRenderer->drawSortedMeshes(camera, translucentShader);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Weather effects
|
|
||||||
entityShader.use();
|
|
||||||
setupWorldShader(entityShader, camera, settings, fogFactor);
|
|
||||||
|
|
||||||
std::array<const WeatherPreset*, 2> weatherInstances {
|
|
||||||
&weather.a, &weather.b};
|
|
||||||
for (const auto& weather : weatherInstances) {
|
|
||||||
float maxIntensity = weather->fall.maxIntensity;
|
|
||||||
float zero = weather->fall.minOpacity;
|
|
||||||
float one = weather->fall.maxOpacity;
|
|
||||||
float t = (weather->intensity * (one - zero)) * maxIntensity + zero;
|
|
||||||
entityShader.uniform1i("u_alphaClip", weather->fall.opaque);
|
|
||||||
entityShader.uniform1f(
|
|
||||||
"u_opacity", weather->fall.opaque ? t * t : t
|
|
||||||
);
|
|
||||||
if (weather->intensity > 1.e-3f && !weather->fall.texture.empty()) {
|
|
||||||
precipitation->render(camera, *weather);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
||||||
}
|
}
|
||||||
postProcessing.render(pctx, assets, timer, camera);
|
postProcessing.render(pctx, assets, timer, camera);
|
||||||
|
|
||||||
if (player.currentCamera == player.fpCamera) {
|
if (player.currentCamera == player.fpCamera) {
|
||||||
DrawContext ctx = pctx.sub();
|
renderHandsPass(pctx, camera);
|
||||||
ctx.setDepthTest(true);
|
|
||||||
ctx.setCullFace(true);
|
|
||||||
ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
|
|
||||||
ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
|
||||||
display::clearDepth();
|
|
||||||
|
|
||||||
// prepare modified HUD camera
|
|
||||||
Camera hudcam = camera;
|
|
||||||
hudcam.far = 10.0f;
|
|
||||||
hudcam.setFov(0.9f);
|
|
||||||
hudcam.position = {};
|
|
||||||
|
|
||||||
setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f);
|
|
||||||
hands->render(camera);
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto bctx = pctx.sub();
|
auto bctx = pctx.sub();
|
||||||
|
|
@ -479,6 +473,85 @@ void WorldRenderer::renderFrame(
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WorldRenderer::renderOpaquePass(
|
||||||
|
const DrawContext& pctx,
|
||||||
|
Camera& camera,
|
||||||
|
bool hudVisible,
|
||||||
|
PostProcessing& postProcessing
|
||||||
|
) {
|
||||||
|
const auto& settings = engine.getSettings();
|
||||||
|
|
||||||
|
DrawContext wctx = pctx.sub();
|
||||||
|
postProcessing.use(wctx, gbufferPipeline);
|
||||||
|
display::clearDepth();
|
||||||
|
|
||||||
|
/* Main opaque pass (GBuffer pass) */ {
|
||||||
|
DrawContext ctx = wctx.sub();
|
||||||
|
ctx.setDepthTest(true);
|
||||||
|
ctx.setCullFace(true);
|
||||||
|
ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
|
||||||
|
ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
||||||
|
renderOpaque(ctx, camera, settings, hudVisible);
|
||||||
|
|
||||||
|
ctx.setDepthMask(false);
|
||||||
|
texts->render(wctx, camera, settings, hudVisible, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldRenderer::renderWeatherEffects(Camera& camera) {
|
||||||
|
const auto& settings = engine.getSettings();
|
||||||
|
auto& entityShader = assets.require<Shader>("entity");
|
||||||
|
entityShader.use();
|
||||||
|
setupWorldShader(entityShader, camera, settings, calcFogFactor());
|
||||||
|
|
||||||
|
std::array<const WeatherPreset*, 2> weatherInstances {
|
||||||
|
&weather.a, &weather.b};
|
||||||
|
for (const auto& weather : weatherInstances) {
|
||||||
|
float maxIntensity = weather->fall.maxIntensity;
|
||||||
|
float zero = weather->fall.minOpacity;
|
||||||
|
float one = weather->fall.maxOpacity;
|
||||||
|
float t = (weather->intensity * (one - zero)) * maxIntensity + zero;
|
||||||
|
entityShader.uniform1i("u_alphaClip", weather->fall.opaque);
|
||||||
|
entityShader.uniform1f(
|
||||||
|
"u_opacity", weather->fall.opaque ? t * t : t
|
||||||
|
);
|
||||||
|
if (weather->intensity > 1.e-3f && !weather->fall.texture.empty()) {
|
||||||
|
precipitation->render(camera, *weather);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldRenderer::renderDebugLines(const DrawContext& ctx, Camera& camera) {
|
||||||
|
auto& linesShader = assets.require<Shader>("lines");
|
||||||
|
linesShader.use();
|
||||||
|
debugLines->render(
|
||||||
|
ctx, camera, *lineBatch, linesShader, showChunkBorders
|
||||||
|
);
|
||||||
|
linesShader.uniformMatrix("u_projview", camera.getProjView());
|
||||||
|
lineBatch->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldRenderer::renderHandsPass(const DrawContext& pctx, Camera& camera) {
|
||||||
|
auto& entityShader = assets.require<Shader>("entity");
|
||||||
|
|
||||||
|
DrawContext ctx = pctx.sub();
|
||||||
|
ctx.setDepthTest(true);
|
||||||
|
ctx.setCullFace(true);
|
||||||
|
ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
|
||||||
|
ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
||||||
|
display::clearDepth();
|
||||||
|
|
||||||
|
// prepare modified HUD camera
|
||||||
|
Camera hudcam = camera;
|
||||||
|
hudcam.far = 10.0f;
|
||||||
|
hudcam.setFov(0.9f);
|
||||||
|
hudcam.position = {};
|
||||||
|
|
||||||
|
setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f);
|
||||||
|
hands->render(camera);
|
||||||
|
}
|
||||||
|
|
||||||
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
||||||
int x = std::floor(player.currentCamera->position.x);
|
int x = std::floor(player.currentCamera->position.x);
|
||||||
int y = std::floor(player.currentCamera->position.y);
|
int y = std::floor(player.currentCamera->position.y);
|
||||||
|
|
@ -524,7 +597,7 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
||||||
batch3d->flush();
|
batch3d->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::clear() {
|
void WorldRenderer::resetCache() {
|
||||||
chunksRenderer->clear();
|
chunksRenderer->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,11 @@
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
|
|
||||||
#include "presets/WeatherPreset.hpp"
|
#include "presets/WeatherPreset.hpp"
|
||||||
#include "world/Weather.hpp"
|
|
||||||
#include "window/Camera.hpp"
|
#include "window/Camera.hpp"
|
||||||
|
#include "util/ObjectsKeeper.hpp"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class Assets;
|
class Assets;
|
||||||
|
|
@ -36,18 +35,38 @@ class Skybox;
|
||||||
class TextsRenderer;
|
class TextsRenderer;
|
||||||
class CloudsRenderer;
|
class CloudsRenderer;
|
||||||
struct EngineSettings;
|
struct EngineSettings;
|
||||||
|
struct Weather;
|
||||||
|
|
||||||
struct CompileTimeShaderSettings {
|
class WorldRenderer final : public util::ObjectsKeeper {
|
||||||
bool advancedRender = false;
|
public:
|
||||||
bool shadows = false;
|
static bool showChunkBorders;
|
||||||
bool ssao = false;
|
static bool showEntitiesDebug;
|
||||||
};
|
|
||||||
|
|
||||||
class WorldRenderer {
|
WorldRenderer(Engine& engine, LevelFrontend& frontend, Player& player);
|
||||||
|
~WorldRenderer();
|
||||||
|
|
||||||
|
void update(const Camera& camera, float delta);
|
||||||
|
|
||||||
|
void renderFrame(
|
||||||
|
const DrawContext& context,
|
||||||
|
Camera& camera,
|
||||||
|
bool hudVisible,
|
||||||
|
PostProcessing& postProcessing
|
||||||
|
);
|
||||||
|
|
||||||
|
void resetCache();
|
||||||
|
|
||||||
|
void setDebug(bool flag);
|
||||||
|
|
||||||
|
void toggleLightsDebug();
|
||||||
|
|
||||||
|
Weather& getWeather();
|
||||||
|
private:
|
||||||
Engine& engine;
|
Engine& engine;
|
||||||
const Level& level;
|
const Level& level;
|
||||||
Player& player;
|
Player& player;
|
||||||
const Assets& assets;
|
const Assets& assets;
|
||||||
|
Weather& weather;
|
||||||
std::unique_ptr<Frustum> frustumCulling;
|
std::unique_ptr<Frustum> frustumCulling;
|
||||||
std::unique_ptr<LineBatch> lineBatch;
|
std::unique_ptr<LineBatch> lineBatch;
|
||||||
std::unique_ptr<Batch3D> batch3d;
|
std::unique_ptr<Batch3D> batch3d;
|
||||||
|
|
@ -59,14 +78,12 @@ class WorldRenderer {
|
||||||
std::unique_ptr<DebugLinesRenderer> debugLines;
|
std::unique_ptr<DebugLinesRenderer> debugLines;
|
||||||
std::unique_ptr<PrecipitationRenderer> precipitation;
|
std::unique_ptr<PrecipitationRenderer> precipitation;
|
||||||
std::unique_ptr<CloudsRenderer> cloudsRenderer;
|
std::unique_ptr<CloudsRenderer> cloudsRenderer;
|
||||||
Weather weather {};
|
|
||||||
|
|
||||||
float timer = 0.0f;
|
float timer = 0.0f;
|
||||||
bool debug = false;
|
bool debug = false;
|
||||||
bool lightsDebug = false;
|
bool lightsDebug = false;
|
||||||
bool gbufferPipeline = false;
|
bool gbufferPipeline = false;
|
||||||
|
bool dirtySettings = true;
|
||||||
CompileTimeShaderSettings prevCTShaderSettings {};
|
|
||||||
|
|
||||||
/// @brief Render block selection lines
|
/// @brief Render block selection lines
|
||||||
void renderBlockSelection();
|
void renderBlockSelection();
|
||||||
|
|
@ -74,7 +91,7 @@ class WorldRenderer {
|
||||||
/// @brief Render lines (selection and debug)
|
/// @brief Render lines (selection and debug)
|
||||||
/// @param camera active camera
|
/// @param camera active camera
|
||||||
/// @param linesShader shader used
|
/// @param linesShader shader used
|
||||||
void renderLines(
|
void renderInWorldLines(
|
||||||
const Camera& camera, Shader& linesShader, const DrawContext& pctx
|
const Camera& camera, Shader& linesShader, const DrawContext& pctx
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -98,7 +115,34 @@ class WorldRenderer {
|
||||||
bool hudVisible
|
bool hudVisible
|
||||||
);
|
);
|
||||||
|
|
||||||
void refreshSettings(Shader** shaders);
|
void renderOpaquePass(
|
||||||
|
const DrawContext& context,
|
||||||
|
Camera& camera,
|
||||||
|
bool hudVisible,
|
||||||
|
PostProcessing& postProcessing
|
||||||
|
);
|
||||||
|
|
||||||
|
void renderWeatherEffects(Camera& camera);
|
||||||
|
|
||||||
|
void renderHandsPass(const DrawContext& pctx, Camera& camera);
|
||||||
|
|
||||||
|
void renderDebugLines(const DrawContext& context, Camera& camera);
|
||||||
|
|
||||||
|
void renderFrameClassic(
|
||||||
|
const DrawContext& context,
|
||||||
|
Camera& camera,
|
||||||
|
bool hudVisible,
|
||||||
|
PostProcessing& postProcessing
|
||||||
|
);
|
||||||
|
|
||||||
|
void renderFrameAdvanced(
|
||||||
|
const DrawContext& context,
|
||||||
|
Camera& camera,
|
||||||
|
bool hudVisible,
|
||||||
|
PostProcessing& postProcessing
|
||||||
|
);
|
||||||
|
|
||||||
|
void refreshSettings();
|
||||||
|
|
||||||
float calcFogFactor() const;
|
float calcFogFactor() const;
|
||||||
public:
|
public:
|
||||||
|
|
@ -106,27 +150,4 @@ public:
|
||||||
std::unique_ptr<TextsRenderer> texts;
|
std::unique_ptr<TextsRenderer> texts;
|
||||||
std::unique_ptr<BlockWrapsRenderer> blockWraps;
|
std::unique_ptr<BlockWrapsRenderer> blockWraps;
|
||||||
std::unique_ptr<NamedSkeletons> skeletons;
|
std::unique_ptr<NamedSkeletons> skeletons;
|
||||||
|
|
||||||
static bool showChunkBorders;
|
|
||||||
static bool showEntitiesDebug;
|
|
||||||
|
|
||||||
WorldRenderer(Engine& engine, LevelFrontend& frontend, Player& player);
|
|
||||||
~WorldRenderer();
|
|
||||||
|
|
||||||
void update(const Camera& camera, float delta);
|
|
||||||
|
|
||||||
void renderFrame(
|
|
||||||
const DrawContext& context,
|
|
||||||
Camera& camera,
|
|
||||||
bool hudVisible,
|
|
||||||
PostProcessing& postProcessing
|
|
||||||
);
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
void setDebug(bool flag);
|
|
||||||
|
|
||||||
void toggleLightsDebug();
|
|
||||||
|
|
||||||
Weather& getWeather();
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
#include "frontend/UiDocument.hpp"
|
#include "frontend/UiDocument.hpp"
|
||||||
#include "frontend/locale.hpp"
|
#include "frontend/locale.hpp"
|
||||||
#include "graphics/core/Batch2D.hpp"
|
#include "graphics/core/Batch2D.hpp"
|
||||||
#include "graphics/core/LineBatch.hpp"
|
|
||||||
#include "graphics/core/Shader.hpp"
|
#include "graphics/core/Shader.hpp"
|
||||||
#include "graphics/core/Font.hpp"
|
#include "graphics/core/Font.hpp"
|
||||||
#include "graphics/core/DrawContext.hpp"
|
#include "graphics/core/DrawContext.hpp"
|
||||||
|
|
@ -188,8 +187,8 @@ void GUI::actMouse(Frame& frame, float delta, const CursorState& cursor) {
|
||||||
doubleClicked = true;
|
doubleClicked = true;
|
||||||
} else {
|
} else {
|
||||||
pressed->click(cursorPos.x, cursorPos.y);
|
pressed->click(cursorPos.x, cursorPos.y);
|
||||||
|
doubleClickTimer = 0.0f;
|
||||||
}
|
}
|
||||||
doubleClickTimer = 0.0f;
|
|
||||||
if (focus && focus != pressed) {
|
if (focus && focus != pressed) {
|
||||||
focus->defocus();
|
focus->defocus();
|
||||||
}
|
}
|
||||||
|
|
@ -260,7 +259,7 @@ void GUI::act(float delta, const glm::uvec2& vp) {
|
||||||
hover = nullptr;
|
hover = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (focus) {
|
if (focus && focusedOnStart == focus.get()) {
|
||||||
actFocused();
|
actFocused();
|
||||||
}
|
}
|
||||||
if (focus && !focus->isFocused()) {
|
if (focus && !focus->isFocused()) {
|
||||||
|
|
@ -274,6 +273,7 @@ void GUI::postAct() {
|
||||||
postRunnables.pop();
|
postRunnables.pop();
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
focusedOnStart = focus.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GUI::draw(const DrawContext& pctx, Assets& assets) {
|
void GUI::draw(const DrawContext& pctx, Assets& assets) {
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ namespace gui {
|
||||||
std::shared_ptr<UINode> hover;
|
std::shared_ptr<UINode> hover;
|
||||||
std::shared_ptr<UINode> pressed;
|
std::shared_ptr<UINode> pressed;
|
||||||
std::shared_ptr<UINode> focus;
|
std::shared_ptr<UINode> focus;
|
||||||
|
const UINode* focusedOnStart = nullptr;
|
||||||
std::shared_ptr<UINode> tooltip;
|
std::shared_ptr<UINode> tooltip;
|
||||||
std::shared_ptr<UiDocument> rootDocument;
|
std::shared_ptr<UiDocument> rootDocument;
|
||||||
std::unique_ptr<FontStylesScheme> syntaxColorScheme;
|
std::unique_ptr<FontStylesScheme> syntaxColorScheme;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#include "Panel.hpp"
|
#include "Panel.hpp"
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
class CheckBox : public UINode {
|
class CheckBox final : public UINode {
|
||||||
protected:
|
protected:
|
||||||
glm::vec4 checkColor {1.0f, 1.0f, 1.0f, 0.4f};
|
glm::vec4 checkColor {1.0f, 1.0f, 1.0f, 0.4f};
|
||||||
boolsupplier supplier = nullptr;
|
boolsupplier supplier = nullptr;
|
||||||
|
|
@ -15,23 +15,22 @@ namespace gui {
|
||||||
public:
|
public:
|
||||||
explicit CheckBox(GUI& gui, bool checked = false);
|
explicit CheckBox(GUI& gui, bool checked = false);
|
||||||
|
|
||||||
virtual void draw(const DrawContext& pctx, const Assets& assets)
|
void draw(const DrawContext& pctx, const Assets& assets) override;
|
||||||
override;
|
|
||||||
|
|
||||||
virtual void mouseRelease(int x, int y) override;
|
void mouseRelease(int x, int y) override;
|
||||||
|
|
||||||
virtual void setSupplier(boolsupplier supplier);
|
void setSupplier(boolsupplier supplier);
|
||||||
virtual void setConsumer(boolconsumer consumer);
|
void setConsumer(boolconsumer consumer);
|
||||||
|
|
||||||
virtual CheckBox* setChecked(bool flag);
|
CheckBox* setChecked(bool flag);
|
||||||
|
|
||||||
virtual bool isChecked() const {
|
bool isChecked() const {
|
||||||
if (supplier) return supplier();
|
if (supplier) return supplier();
|
||||||
return checked;
|
return checked;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class FullCheckBox : public Panel {
|
class FullCheckBox final : public Panel {
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<CheckBox> checkbox;
|
std::shared_ptr<CheckBox> checkbox;
|
||||||
std::shared_ptr<Label> label;
|
std::shared_ptr<Label> label;
|
||||||
|
|
@ -43,28 +42,28 @@ namespace gui {
|
||||||
bool checked = false
|
bool checked = false
|
||||||
);
|
);
|
||||||
|
|
||||||
virtual void setSupplier(boolsupplier supplier) {
|
void setSupplier(boolsupplier supplier) {
|
||||||
checkbox->setSupplier(std::move(supplier));
|
checkbox->setSupplier(std::move(supplier));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void setConsumer(boolconsumer consumer) {
|
void setConsumer(boolconsumer consumer) {
|
||||||
checkbox->setConsumer(std::move(consumer));
|
checkbox->setConsumer(std::move(consumer));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void setChecked(bool flag) {
|
void setChecked(bool flag) {
|
||||||
checkbox->setChecked(flag);
|
checkbox->setChecked(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool isChecked() const {
|
bool isChecked() const {
|
||||||
return checkbox->isChecked();
|
return checkbox->isChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void setTooltip(const std::wstring& text) override {
|
void setTooltip(const std::wstring& text) override {
|
||||||
Panel::setTooltip(text);
|
Panel::setTooltip(text);
|
||||||
checkbox->setTooltip(text);
|
checkbox->setTooltip(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void setSize(const glm::vec2& new_size) override {
|
void setSize(const glm::vec2& new_size) override {
|
||||||
Panel::setSize(new_size);
|
Panel::setSize(new_size);
|
||||||
checkbox->setSize(glm::vec2(size.y, size.y));
|
checkbox->setSize(glm::vec2(size.y, size.y));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
#include "Container.hpp"
|
#include "Container.hpp"
|
||||||
|
|
||||||
#include "graphics/core/DrawContext.hpp"
|
|
||||||
#include "graphics/core/Batch2D.hpp"
|
|
||||||
#include "window/Window.hpp"
|
|
||||||
#include "../GUI.hpp"
|
#include "../GUI.hpp"
|
||||||
|
#include "graphics/core/Batch2D.hpp"
|
||||||
|
#include "graphics/core/DrawContext.hpp"
|
||||||
|
#include "window/Window.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
using namespace gui;
|
using namespace gui;
|
||||||
|
|
||||||
Container::Container(GUI& gui, glm::vec2 size) : UINode(gui, size) {
|
Container::Container(GUI& gui, glm::vec2 size) : UINode(gui, size) {
|
||||||
actualLength = size.y;
|
actualLengthY = size.y;
|
||||||
setColor(glm::vec4());
|
setColor(glm::vec4());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -26,15 +26,15 @@ std::shared_ptr<UINode> Container::getAt(const glm::vec2& pos) {
|
||||||
if (!isInside(pos)) {
|
if (!isInside(pos)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
int diff = (actualLength-size.y);
|
int diff = (actualLengthY - size.y);
|
||||||
if (scrollable && diff > 0 && pos.x > calcPos().x + getSize().x - scrollBarWidth) {
|
if (scrollable && diff > 0 &&
|
||||||
|
pos.x > calcPos().x + getSize().x - scrollBarWidth) {
|
||||||
return UINode::getAt(pos);
|
return UINode::getAt(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = nodes.size()-1; i >= 0; i--) {
|
for (int i = nodes.size() - 1; i >= 0; i--) {
|
||||||
auto& node = nodes[i];
|
auto& node = nodes[i];
|
||||||
if (!node->isVisible())
|
if (!node->isVisible()) continue;
|
||||||
continue;
|
|
||||||
auto hover = node->getAt(pos);
|
auto hover = node->getAt(pos);
|
||||||
if (hover != nullptr) {
|
if (hover != nullptr) {
|
||||||
return hover;
|
return hover;
|
||||||
|
|
@ -65,11 +65,12 @@ void Container::mouseMove(int x, int y) {
|
||||||
auto pos = calcPos();
|
auto pos = calcPos();
|
||||||
x -= pos.x;
|
x -= pos.x;
|
||||||
y -= pos.y;
|
y -= pos.y;
|
||||||
int diff = (actualLength-size.y);
|
int diff = (actualLengthY - size.y);
|
||||||
if (diff > 0) {
|
if (diff > 0) {
|
||||||
scroll -= (y - prevScrollY) / static_cast<float>(size.y) * actualLength;
|
scrollY -=
|
||||||
scroll = -glm::min(
|
(y - prevScrollY) / static_cast<float>(size.y) * actualLengthY;
|
||||||
glm::max(static_cast<float>(-scroll), 0.0f), actualLength - size.y
|
scrollY = -glm::min(
|
||||||
|
glm::max(static_cast<float>(-scrollY), 0.0f), actualLengthY - size.y
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
prevScrollY = y;
|
prevScrollY = y;
|
||||||
|
|
@ -101,23 +102,28 @@ void Container::act(float delta) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
intervalEvents.erase(std::remove_if(
|
intervalEvents.erase(
|
||||||
intervalEvents.begin(), intervalEvents.end(),
|
std::remove_if(
|
||||||
[](const IntervalEvent& event) {
|
intervalEvents.begin(),
|
||||||
return event.repeat == 0;
|
intervalEvents.end(),
|
||||||
}
|
[](const IntervalEvent& event) { return event.repeat == 0; }
|
||||||
), intervalEvents.end());
|
),
|
||||||
|
intervalEvents.end()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Container::scrolled(int value) {
|
void Container::scrolled(int value) {
|
||||||
int diff = (actualLength-getSize().y);
|
auto size = getSize();
|
||||||
|
bool isVertical = actualLengthY > size.y;
|
||||||
|
int diff = (isVertical ? actualLengthY - size.y : actualLengthX - size.x);
|
||||||
|
int& scroll = (isVertical ? scrollY : scrollX);
|
||||||
|
|
||||||
if (scroll < 0 && diff <= 0) {
|
if (scroll < 0 && diff <= 0) {
|
||||||
scroll = 0;
|
scroll = 0;
|
||||||
}
|
}
|
||||||
if (diff > 0 && scrollable) {
|
if (diff > 0 && scrollable) {
|
||||||
scroll += value * scrollStep;
|
scroll += value * scrollStep;
|
||||||
if (scroll > 0)
|
if (scroll > 0) scroll = 0;
|
||||||
scroll = 0;
|
|
||||||
if (-scroll > diff) {
|
if (-scroll > diff) {
|
||||||
scroll = -diff;
|
scroll = -diff;
|
||||||
}
|
}
|
||||||
|
|
@ -140,21 +146,25 @@ void Container::draw(const DrawContext& pctx, const Assets& assets) {
|
||||||
if (!nodes.empty()) {
|
if (!nodes.empty()) {
|
||||||
batch->flush();
|
batch->flush();
|
||||||
DrawContext ctx = pctx.sub();
|
DrawContext ctx = pctx.sub();
|
||||||
ctx.setScissors(glm::vec4(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y)));
|
ctx.setScissors(
|
||||||
|
glm::vec4(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y))
|
||||||
|
);
|
||||||
for (const auto& node : nodes) {
|
for (const auto& node : nodes) {
|
||||||
if (node->isVisible())
|
if (node->isVisible()) node->draw(pctx, assets);
|
||||||
node->draw(pctx, assets);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int diff = (actualLength-size.y);
|
int diff = (actualLengthY - size.y);
|
||||||
if (scrollable && diff > 0) {
|
if (scrollable && diff > 0) {
|
||||||
int h = glm::max(size.y / actualLength * size.y, scrollBarWidth / 2.0f);
|
int h = glm::max(
|
||||||
|
size.y / actualLengthY * size.y, scrollBarWidth / 2.0f
|
||||||
|
);
|
||||||
batch->untexture();
|
batch->untexture();
|
||||||
batch->setColor(glm::vec4(1, 1, 1, 0.3f));
|
batch->setColor(glm::vec4(1, 1, 1, 0.3f));
|
||||||
batch->rect(
|
batch->rect(
|
||||||
pos.x + size.x - scrollBarWidth,
|
pos.x + size.x - scrollBarWidth,
|
||||||
pos.y - scroll / static_cast<float>(diff) * (size.y - h),
|
pos.y - scrollY / static_cast<float>(diff) * (size.y - h),
|
||||||
scrollBarWidth, h
|
scrollBarWidth,
|
||||||
|
h
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
batch->flush();
|
batch->flush();
|
||||||
|
|
@ -163,8 +173,7 @@ void Container::draw(const DrawContext& pctx, const Assets& assets) {
|
||||||
|
|
||||||
void Container::drawBackground(const DrawContext& pctx, const Assets&) {
|
void Container::drawBackground(const DrawContext& pctx, const Assets&) {
|
||||||
glm::vec4 color = calcColor();
|
glm::vec4 color = calcColor();
|
||||||
if (color.a <= 0.001f)
|
if (color.a <= 0.001f) return;
|
||||||
return;
|
|
||||||
glm::vec2 pos = calcPos();
|
glm::vec2 pos = calcPos();
|
||||||
|
|
||||||
auto batch = pctx.getBatch2D();
|
auto batch = pctx.getBatch2D();
|
||||||
|
|
@ -194,11 +203,16 @@ void Container::add(const std::shared_ptr<UINode>& node, glm::vec2 pos) {
|
||||||
|
|
||||||
void Container::remove(UINode* selected) {
|
void Container::remove(UINode* selected) {
|
||||||
selected->setParent(nullptr);
|
selected->setParent(nullptr);
|
||||||
nodes.erase(std::remove_if(nodes.begin(), nodes.end(),
|
nodes.erase(
|
||||||
[selected](const std::shared_ptr<UINode>& node) {
|
std::remove_if(
|
||||||
return node.get() == selected;
|
nodes.begin(),
|
||||||
}
|
nodes.end(),
|
||||||
), nodes.end());
|
[selected](const std::shared_ptr<UINode>& node) {
|
||||||
|
return node.get() == selected;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
nodes.end()
|
||||||
|
);
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,13 +256,17 @@ void Container::setScrollStep(int step) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Container::refresh() {
|
void Container::refresh() {
|
||||||
std::stable_sort(nodes.begin(), nodes.end(), [](const auto& a, const auto& b) {
|
std::stable_sort(
|
||||||
return a->getZIndex() < b->getZIndex();
|
nodes.begin(),
|
||||||
});
|
nodes.end(),
|
||||||
|
[](const auto& a, const auto& b) {
|
||||||
|
return a->getZIndex() < b->getZIndex();
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Container::setScroll(int scroll) {
|
void Container::setScroll(int scroll) {
|
||||||
this->scroll = scroll;
|
this->scrollY = scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::shared_ptr<UINode>>& Container::getNodes() const {
|
const std::vector<std::shared_ptr<UINode>>& Container::getNodes() const {
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,12 @@ namespace gui {
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::shared_ptr<UINode>> nodes;
|
std::vector<std::shared_ptr<UINode>> nodes;
|
||||||
std::vector<IntervalEvent> intervalEvents;
|
std::vector<IntervalEvent> intervalEvents;
|
||||||
int scroll = 0;
|
int scrollY = 0;
|
||||||
|
int scrollX = 0;
|
||||||
int scrollStep = 40;
|
int scrollStep = 40;
|
||||||
int scrollBarWidth = 10;
|
int scrollBarWidth = 10;
|
||||||
int actualLength = 0;
|
int actualLengthY = 0;
|
||||||
|
int actualLengthX = 0;
|
||||||
bool scrollable = true;
|
bool scrollable = true;
|
||||||
|
|
||||||
bool isScrolling() const {
|
bool isScrolling() const {
|
||||||
|
|
@ -40,7 +42,7 @@ namespace gui {
|
||||||
virtual void setScrollable(bool flag);
|
virtual void setScrollable(bool flag);
|
||||||
void listenInterval(float interval, OnTimeOut callback, int repeat=-1);
|
void listenInterval(float interval, OnTimeOut callback, int repeat=-1);
|
||||||
virtual glm::vec2 getContentOffset() const override {
|
virtual glm::vec2 getContentOffset() const override {
|
||||||
return glm::vec2(0.0f, scroll);
|
return glm::vec2(scrollX, scrollY);
|
||||||
};
|
};
|
||||||
virtual void setSize(const glm::vec2& size) override;
|
virtual void setSize(const glm::vec2& size) override;
|
||||||
virtual int getScrollStep() const;
|
virtual int getScrollStep() const;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace gui {
|
||||||
class Frame final : public Container {
|
class Frame final : public Container {
|
||||||
public:
|
public:
|
||||||
Frame(GUI& gui, std::string id, std::string outputTexture);
|
Frame(GUI& gui, std::string id, std::string outputTexture);
|
||||||
virtual ~Frame();
|
~Frame();
|
||||||
|
|
||||||
void draw(const DrawContext& pctx, const Assets& assets) override;
|
void draw(const DrawContext& pctx, const Assets& assets) override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ void InlineFrame::setSrc(const std::string& src) {
|
||||||
this->src = src;
|
this->src = src;
|
||||||
if (document) {
|
if (document) {
|
||||||
scripting::on_ui_close(*document, nullptr);
|
scripting::on_ui_close(*document, nullptr);
|
||||||
|
clear();
|
||||||
document = nullptr;
|
document = nullptr;
|
||||||
root = nullptr;
|
root = nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,8 @@
|
||||||
#include "graphics/core/Batch2D.hpp"
|
#include "graphics/core/Batch2D.hpp"
|
||||||
#include "graphics/core/DrawContext.hpp"
|
#include "graphics/core/DrawContext.hpp"
|
||||||
#include "graphics/core/Font.hpp"
|
#include "graphics/core/Font.hpp"
|
||||||
#include "graphics/core/Shader.hpp"
|
|
||||||
#include "graphics/core/Texture.hpp"
|
|
||||||
#include "graphics/render/BlocksPreview.hpp"
|
#include "graphics/render/BlocksPreview.hpp"
|
||||||
#include "graphics/ui/GUI.hpp"
|
#include "graphics/ui/GUI.hpp"
|
||||||
#include "items/Inventories.hpp"
|
|
||||||
#include "items/Inventory.hpp"
|
#include "items/Inventory.hpp"
|
||||||
#include "items/ItemDef.hpp"
|
#include "items/ItemDef.hpp"
|
||||||
#include "logic/scripting/scripting.hpp"
|
#include "logic/scripting/scripting.hpp"
|
||||||
|
|
@ -369,28 +366,12 @@ void SlotView::performLeftClick(ItemStack& stack, ItemStack& grabbed) {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto indices = *content->getIndices();
|
||||||
if (!layout.itemSource && stack.accepts(grabbed) && layout.placing) {
|
if (!layout.itemSource && stack.accepts(grabbed) && layout.placing) {
|
||||||
action = InteractionAction::PUT;
|
action = InteractionAction::PUT;
|
||||||
stack.move(grabbed, *content->getIndices());
|
stack.move(grabbed, indices);
|
||||||
} else {
|
} else {
|
||||||
if (layout.itemSource) {
|
actIfCannotPut(stack, grabbed, action);
|
||||||
if (grabbed.isEmpty()) {
|
|
||||||
action = InteractionAction::TAKE;
|
|
||||||
grabbed.set(stack);
|
|
||||||
} else {
|
|
||||||
action = InteractionAction::PUT;
|
|
||||||
grabbed.clear();
|
|
||||||
}
|
|
||||||
} else if (grabbed.isEmpty()) {
|
|
||||||
if (layout.taking) {
|
|
||||||
action = InteractionAction::TAKE;
|
|
||||||
std::swap(grabbed, stack);
|
|
||||||
}
|
|
||||||
} else if (layout.taking && layout.placing) {
|
|
||||||
action = InteractionAction::PUT;
|
|
||||||
std::swap(grabbed, stack);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action != InteractionAction::UNDEFINED) {
|
if (action != InteractionAction::UNDEFINED) {
|
||||||
|
|
@ -403,6 +384,39 @@ void SlotView::performLeftClick(ItemStack& stack, ItemStack& grabbed) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SlotView::actIfCannotPut(ItemStack& stack, ItemStack& grabbed, InteractionAction& action) {
|
||||||
|
const auto& input = gui.getInput();
|
||||||
|
auto indices = *content->getIndices();
|
||||||
|
if (layout.itemSource) {
|
||||||
|
if (grabbed.isEmpty()) {
|
||||||
|
action = InteractionAction::TAKE;
|
||||||
|
grabbed.set(stack);
|
||||||
|
if (input.pressed(Keycode::LEFT_CONTROL)) {
|
||||||
|
grabbed.maximizeCount(*content->getIndices());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (grabbed.accepts(stack)) {
|
||||||
|
auto& def = indices.items.require(stack.getItemId());
|
||||||
|
if (grabbed.getCount() < def.stackSize) {
|
||||||
|
action = InteractionAction::TAKE;
|
||||||
|
grabbed.setCount(grabbed.getCount() + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
action = InteractionAction::PUT;
|
||||||
|
grabbed.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (grabbed.isEmpty()) {
|
||||||
|
if (layout.taking) {
|
||||||
|
action = InteractionAction::TAKE;
|
||||||
|
std::swap(grabbed, stack);
|
||||||
|
}
|
||||||
|
} else if (layout.taking && layout.placing) {
|
||||||
|
action = InteractionAction::PUT;
|
||||||
|
std::swap(grabbed, stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SlotView::performRightClick(ItemStack& stack, ItemStack& grabbed) {
|
void SlotView::performRightClick(ItemStack& stack, ItemStack& grabbed) {
|
||||||
if (layout.rightClick) {
|
if (layout.rightClick) {
|
||||||
layout.rightClick(inventoryId, stack);
|
layout.rightClick(inventoryId, stack);
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ namespace gui {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SlotView : public gui::UINode {
|
class SlotView final : public gui::UINode {
|
||||||
struct {
|
struct {
|
||||||
ItemStack stack {};
|
ItemStack stack {};
|
||||||
std::wstring countStr;
|
std::wstring countStr;
|
||||||
|
|
@ -89,14 +89,14 @@ namespace gui {
|
||||||
public:
|
public:
|
||||||
SlotView(GUI& gui, SlotLayout layout);
|
SlotView(GUI& gui, SlotLayout layout);
|
||||||
|
|
||||||
virtual void draw(const DrawContext& pctx, const Assets& assets) override;
|
void draw(const DrawContext& pctx, const Assets& assets) override;
|
||||||
|
|
||||||
void setHighlighted(bool flag);
|
void setHighlighted(bool flag);
|
||||||
bool isHighlighted() const;
|
bool isHighlighted() const;
|
||||||
|
|
||||||
virtual void clicked(Mousecode) override;
|
void clicked(Mousecode) override;
|
||||||
virtual void onFocus() override;
|
void onFocus() override;
|
||||||
virtual const std::wstring& getTooltip() const override;
|
const std::wstring& getTooltip() const override;
|
||||||
|
|
||||||
void bind(
|
void bind(
|
||||||
int64_t inventoryid,
|
int64_t inventoryid,
|
||||||
|
|
@ -123,6 +123,8 @@ namespace gui {
|
||||||
SHARE = 2,
|
SHARE = 2,
|
||||||
UNDEFINED,
|
UNDEFINED,
|
||||||
};
|
};
|
||||||
|
private:
|
||||||
|
void actIfCannotPut(ItemStack& stack, ItemStack& grabbed, InteractionAction& action);
|
||||||
};
|
};
|
||||||
|
|
||||||
class InventoryView final : public gui::Container {
|
class InventoryView final : public gui::Container {
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ namespace gui {
|
||||||
uint getLineByTextIndex(size_t index) const;
|
uint getLineByTextIndex(size_t index) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Label : public UINode {
|
class Label final : public UINode {
|
||||||
LabelCache cache;
|
LabelCache cache;
|
||||||
|
|
||||||
glm::vec2 calcSize();
|
glm::vec2 calcSize();
|
||||||
|
|
@ -69,64 +69,64 @@ namespace gui {
|
||||||
Label(GUI& gui, const std::string& text, std::string fontName="normal");
|
Label(GUI& gui, const std::string& text, std::string fontName="normal");
|
||||||
Label(GUI& gui, const std::wstring& text, std::string fontName="normal");
|
Label(GUI& gui, const std::wstring& text, std::string fontName="normal");
|
||||||
|
|
||||||
virtual ~Label();
|
~Label();
|
||||||
|
|
||||||
virtual void setText(std::wstring text);
|
void setText(std::wstring text);
|
||||||
const std::wstring& getText() const;
|
const std::wstring& getText() const;
|
||||||
|
|
||||||
virtual void setFontName(std::string name);
|
void setFontName(std::string name);
|
||||||
virtual const std::string& getFontName() const;
|
const std::string& getFontName() const;
|
||||||
|
|
||||||
/// @brief Set text vertical alignment (default value: center)
|
/// @brief Set text vertical alignment (default value: center)
|
||||||
/// @param align Align::top / Align::center / Align::bottom
|
/// @param align Align::top / Align::center / Align::bottom
|
||||||
virtual void setVerticalAlign(Align align);
|
void setVerticalAlign(Align align);
|
||||||
virtual Align getVerticalAlign() const;
|
Align getVerticalAlign() const;
|
||||||
|
|
||||||
/// @brief Get line height multiplier used for multiline labels
|
/// @brief Get line height multiplier used for multiline labels
|
||||||
/// (default value: 1.5)
|
/// (default value: 1.5)
|
||||||
virtual float getLineInterval() const;
|
float getLineInterval() const;
|
||||||
|
|
||||||
/// @brief Set line height multiplier used for multiline labels
|
/// @brief Set line height multiplier used for multiline labels
|
||||||
virtual void setLineInterval(float interval);
|
void setLineInterval(float interval);
|
||||||
|
|
||||||
/// @brief Get Y position of the text relative to label position
|
/// @brief Get Y position of the text relative to label position
|
||||||
/// @return Y offset
|
/// @return Y offset
|
||||||
virtual int getTextYOffset() const;
|
int getTextYOffset() const;
|
||||||
|
|
||||||
/// @brief Get Y position of the line relative to label position
|
/// @brief Get Y position of the line relative to label position
|
||||||
/// @param line target line index
|
/// @param line target line index
|
||||||
/// @return Y offset
|
/// @return Y offset
|
||||||
virtual int getLineYOffset(uint line) const;
|
int getLineYOffset(uint line) const;
|
||||||
|
|
||||||
/// @brief Get position of line start in the text
|
/// @brief Get position of line start in the text
|
||||||
/// @param line target line index
|
/// @param line target line index
|
||||||
/// @return position in the text [0..length]
|
/// @return position in the text [0..length]
|
||||||
virtual size_t getTextLineOffset(size_t line) const;
|
size_t getTextLineOffset(size_t line) const;
|
||||||
|
|
||||||
/// @brief Get line index by its Y offset relative to label position
|
/// @brief Get line index by its Y offset relative to label position
|
||||||
/// @param offset target Y offset
|
/// @param offset target Y offset
|
||||||
/// @return line index [0..+]
|
/// @return line index [0..+]
|
||||||
virtual uint getLineByYOffset(int offset) const;
|
uint getLineByYOffset(int offset) const;
|
||||||
virtual uint getLineByTextIndex(size_t index) const;
|
uint getLineByTextIndex(size_t index) const;
|
||||||
virtual uint getLinesNumber() const;
|
uint getLinesNumber() const;
|
||||||
virtual bool isFakeLine(size_t line) const;
|
bool isFakeLine(size_t line) const;
|
||||||
|
|
||||||
virtual void draw(const DrawContext& pctx, const Assets& assets) override;
|
void draw(const DrawContext& pctx, const Assets& assets) override;
|
||||||
|
|
||||||
virtual void textSupplier(wstringsupplier supplier);
|
void textSupplier(wstringsupplier supplier);
|
||||||
|
|
||||||
virtual void setAutoResize(bool flag);
|
void setAutoResize(bool flag);
|
||||||
virtual bool isAutoResize() const;
|
bool isAutoResize() const;
|
||||||
|
|
||||||
virtual void setMultiline(bool multiline);
|
void setMultiline(bool multiline);
|
||||||
virtual bool isMultiline() const;
|
bool isMultiline() const;
|
||||||
|
|
||||||
virtual void setTextWrapping(bool flag);
|
void setTextWrapping(bool flag);
|
||||||
virtual bool isTextWrapping() const;
|
bool isTextWrapping() const;
|
||||||
|
|
||||||
virtual void setMarkup(std::string_view lang);
|
void setMarkup(std::string_view lang);
|
||||||
virtual const std::string& getMarkup() const;
|
const std::string& getMarkup() const;
|
||||||
|
|
||||||
virtual void setStyles(std::unique_ptr<FontStylesScheme> styles);
|
void setStyles(std::unique_ptr<FontStylesScheme> styles);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ namespace gui {
|
||||||
|
|
||||||
using PageLoaderFunc = std::function<std::shared_ptr<UINode>(const std::string&)>;
|
using PageLoaderFunc = std::function<std::shared_ptr<UINode>(const std::string&)>;
|
||||||
|
|
||||||
class Menu : public Container {
|
class Menu final : public Container {
|
||||||
protected:
|
protected:
|
||||||
std::unordered_map<std::string, Page> pages;
|
std::unordered_map<std::string, Page> pages;
|
||||||
std::stack<Page> pageStack;
|
std::stack<Page> pageStack;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
#include "window/Window.hpp"
|
#include "window/Window.hpp"
|
||||||
#include "../GUI.hpp"
|
#include "../GUI.hpp"
|
||||||
|
|
||||||
|
// TODO: remove
|
||||||
// TODO: remove
|
// TODO: remove
|
||||||
#include <GL/glew.h>
|
#include <GL/glew.h>
|
||||||
|
|
||||||
|
|
@ -24,8 +25,7 @@ ModelViewer::ModelViewer(
|
||||||
: Container(gui, size),
|
: Container(gui, size),
|
||||||
modelName(modelName),
|
modelName(modelName),
|
||||||
camera(),
|
camera(),
|
||||||
batch(std::make_unique<Batch3D>(1024)),
|
batch(std::make_unique<Batch3D>(1024)) {
|
||||||
fbo(std::make_unique<Framebuffer>(size.x, size.y)) {
|
|
||||||
camera.perspective = true;
|
camera.perspective = true;
|
||||||
camera.position = glm::vec3(2, 2, 2);
|
camera.position = glm::vec3(2, 2, 2);
|
||||||
}
|
}
|
||||||
|
|
@ -116,6 +116,12 @@ void ModelViewer::draw(const DrawContext& pctx, const Assets& assets) {
|
||||||
}
|
}
|
||||||
auto& prevShader = Shader::getUsed();
|
auto& prevShader = Shader::getUsed();
|
||||||
|
|
||||||
|
if (!fbo) {
|
||||||
|
if (size.x <= 0 || size.y <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fbo = std::make_unique<Framebuffer>(size.x, size.y);
|
||||||
|
}
|
||||||
fbo->resize(size.x, size.y);
|
fbo->resize(size.x, size.y);
|
||||||
{
|
{
|
||||||
glDisable(GL_SCISSOR_TEST);
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
|
@ -154,6 +160,13 @@ void ModelViewer::draw(const DrawContext& pctx, const Assets& assets) {
|
||||||
Container::draw(pctx, assets);
|
Container::draw(pctx, assets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModelViewer::setVisible(bool flag) {
|
||||||
|
if (!flag && fbo) {
|
||||||
|
fbo.reset();
|
||||||
|
}
|
||||||
|
Container::setVisible(flag);
|
||||||
|
}
|
||||||
|
|
||||||
void ModelViewer::setCenter(const glm::vec3& center) {
|
void ModelViewer::setCenter(const glm::vec3& center) {
|
||||||
this->center = center;
|
this->center = center;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Container.hpp"
|
||||||
|
#include "window/Camera.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "Container.hpp"
|
|
||||||
#include "window/Camera.hpp"
|
|
||||||
|
|
||||||
class Batch3D;
|
class Batch3D;
|
||||||
class Framebuffer;
|
class Framebuffer;
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
class ModelViewer : public Container {
|
class ModelViewer final : public Container {
|
||||||
private:
|
private:
|
||||||
std::string modelName;
|
std::string modelName;
|
||||||
Camera camera;
|
Camera camera;
|
||||||
|
|
@ -22,7 +23,6 @@ namespace gui {
|
||||||
bool grabbing = false;
|
bool grabbing = false;
|
||||||
public:
|
public:
|
||||||
ModelViewer(GUI& gui, const glm::vec2& size, const std::string& modelName);
|
ModelViewer(GUI& gui, const glm::vec2& size, const std::string& modelName);
|
||||||
|
|
||||||
~ModelViewer();
|
~ModelViewer();
|
||||||
|
|
||||||
void setModel(const std::string& modelName);
|
void setModel(const std::string& modelName);
|
||||||
|
|
@ -33,6 +33,7 @@ namespace gui {
|
||||||
|
|
||||||
void act(float delta) override;
|
void act(float delta) override;
|
||||||
void draw(const DrawContext& pctx, const Assets& assets) override;
|
void draw(const DrawContext& pctx, const Assets& assets) override;
|
||||||
|
void setVisible(bool flag) override;
|
||||||
|
|
||||||
void setRotation(const glm::vec3& euler);
|
void setRotation(const glm::vec3& euler);
|
||||||
void setCenter(const glm::vec3& center);
|
void setCenter(const glm::vec3& center);
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,14 @@ glm::vec2 Panel::getContentSize() const {
|
||||||
void Panel::cropToContent() {
|
void Panel::cropToContent() {
|
||||||
if (maxLength > 0.0f) {
|
if (maxLength > 0.0f) {
|
||||||
setSize(glm::vec2(
|
setSize(glm::vec2(
|
||||||
getSize().x, glm::max(minLength, glm::min(maxLength, actualLength))
|
glm::max(minLength, glm::min(maxLength, actualLengthX)),
|
||||||
|
glm::max(minLength, glm::min(maxLength, actualLengthY))
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
setSize(glm::vec2(getSize().x, glm::max(minLength, actualLength)));
|
setSize(glm::vec2(
|
||||||
|
glm::max(minLength, actualLengthX),
|
||||||
|
glm::max(minLength, actualLengthY)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,21 +97,30 @@ void Panel::refresh() {
|
||||||
y += nodeSize.y + margin.w + interval;
|
y += nodeSize.y + margin.w + interval;
|
||||||
maxw = fmax(maxw, ex + nodeSize.x + margin.z + padding.z);
|
maxw = fmax(maxw, ex + nodeSize.x + margin.z + padding.z);
|
||||||
}
|
}
|
||||||
actualLength = y + padding.w;
|
actualLengthX = size.x;
|
||||||
|
actualLengthY = y + padding.w;
|
||||||
} else {
|
} else {
|
||||||
float maxh = size.y;
|
float maxh = size.y;
|
||||||
for (auto& node : nodes) {
|
for (auto& node : nodes) {
|
||||||
glm::vec2 nodesize = node->getSize();
|
|
||||||
const glm::vec4 margin = node->getMargin();
|
const glm::vec4 margin = node->getMargin();
|
||||||
x += margin.x;
|
x += margin.x;
|
||||||
node->setPos(glm::vec2(x, y + margin.y));
|
|
||||||
x += nodesize.x + margin.z + interval;
|
|
||||||
|
|
||||||
node->refresh();
|
float ey = y + margin.y;
|
||||||
maxh = fmax(
|
node->setPos(glm::vec2(x, ey));
|
||||||
maxh, y + margin.y + node->getSize().y + margin.w + padding.w
|
|
||||||
|
int height = glm::floor(
|
||||||
|
size.y - padding.y - padding.w - margin.y - margin.w
|
||||||
);
|
);
|
||||||
|
if (node->isResizing()) {
|
||||||
|
node->setMaxSize({node->getMaxSize().x, height});
|
||||||
|
node->setSize(glm::vec2(node->getSize().x, height));
|
||||||
|
}
|
||||||
|
node->refresh();
|
||||||
|
glm::vec2 nodesize = node->getSize();
|
||||||
|
x += nodesize.x + margin.z + interval;
|
||||||
|
maxh = fmax(maxh, ey + nodesize.y + margin.w + padding.w);
|
||||||
}
|
}
|
||||||
actualLength = size.y;
|
actualLengthY = size.y;
|
||||||
|
actualLengthX = x + padding.z;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ class Assets;
|
||||||
class DrawContext;
|
class DrawContext;
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
class Plotter : public UINode {
|
class Plotter final : public UINode {
|
||||||
std::unique_ptr<int[]> points;
|
std::unique_ptr<int[]> points;
|
||||||
float multiplier;
|
float multiplier;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,21 @@ SelectBox::SelectBox(
|
||||||
GUI& gui,
|
GUI& gui,
|
||||||
std::vector<Option>&& options,
|
std::vector<Option>&& options,
|
||||||
Option selected,
|
Option selected,
|
||||||
|
Mode mode,
|
||||||
int contentWidth,
|
int contentWidth,
|
||||||
const glm::vec4& padding
|
const glm::vec4& padding
|
||||||
)
|
)
|
||||||
: Button(gui, selected.text, padding, nullptr, glm::vec2(contentWidth, -1)),
|
: Button(gui, selected.text, padding, nullptr, glm::vec2(-1, -1)),
|
||||||
options(std::move(options)) {
|
options(std::move(options)), mode(mode) {
|
||||||
|
listenAction(UIAction::CLICK, [this, contentWidth](GUI& gui) {
|
||||||
listenAction(UIAction::CLICK, [this](GUI& gui) {
|
auto panel = std::make_shared<Panel>(
|
||||||
auto panel = std::make_shared<Panel>(gui, getSize());
|
gui,
|
||||||
|
getMode() == Mode::BUTTON ? glm::vec2 {contentWidth, contentWidth}
|
||||||
|
: getSize(),
|
||||||
|
glm::vec4 {2.0f},
|
||||||
|
0.0f
|
||||||
|
);
|
||||||
|
panel->setColor({});
|
||||||
panel->setPadding(glm::vec4(0, size.y, 0, 0));
|
panel->setPadding(glm::vec4(0, size.y, 0, 0));
|
||||||
panel->setPos(calcPos() + glm::vec2(0, 0));
|
panel->setPos(calcPos() + glm::vec2(0, 0));
|
||||||
for (const auto& option : this->options) {
|
for (const auto& option : this->options) {
|
||||||
|
|
@ -51,7 +58,9 @@ void SelectBox::listenChange(OnStringChange&& callback) {
|
||||||
|
|
||||||
void SelectBox::setSelected(const Option& selected) {
|
void SelectBox::setSelected(const Option& selected) {
|
||||||
this->selected = selected;
|
this->selected = selected;
|
||||||
this->label->setText(selected.text);
|
if (mode == Mode::SELECT) {
|
||||||
|
this->label->setText(selected.text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SelectBox::Option& SelectBox::getSelected() const {
|
const SelectBox::Option& SelectBox::getSelected() const {
|
||||||
|
|
@ -66,6 +75,14 @@ void SelectBox::setOptions(std::vector<Option>&& options) {
|
||||||
this->options = std::move(options);
|
this->options = std::move(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SelectBox::Mode SelectBox::getMode() const {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelectBox::setMode(SelectBox::Mode mode) {
|
||||||
|
this->mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
void SelectBox::drawBackground(const DrawContext& pctx, const Assets&) {
|
void SelectBox::drawBackground(const DrawContext& pctx, const Assets&) {
|
||||||
glm::vec2 pos = calcPos();
|
glm::vec2 pos = calcPos();
|
||||||
auto batch = pctx.getBatch2D();
|
auto batch = pctx.getBatch2D();
|
||||||
|
|
@ -74,6 +91,10 @@ void SelectBox::drawBackground(const DrawContext& pctx, const Assets&) {
|
||||||
batch->rect(pos.x, pos.y, size.x, size.y);
|
batch->rect(pos.x, pos.y, size.x, size.y);
|
||||||
batch->setColor({1.0f, 1.0f, 1.0f, 0.333f});
|
batch->setColor({1.0f, 1.0f, 1.0f, 0.333f});
|
||||||
|
|
||||||
|
if (mode != Mode::SELECT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int paddingRight = padding.w;
|
int paddingRight = padding.w;
|
||||||
int widthHalf = 8;
|
int widthHalf = 8;
|
||||||
int heightHalf = 4;
|
int heightHalf = 4;
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,21 @@ namespace gui {
|
||||||
std::string value;
|
std::string value;
|
||||||
std::wstring text;
|
std::wstring text;
|
||||||
};
|
};
|
||||||
|
enum class Mode {
|
||||||
|
SELECT,
|
||||||
|
BUTTON,
|
||||||
|
};
|
||||||
private:
|
private:
|
||||||
std::vector<Option> options;
|
std::vector<Option> options;
|
||||||
Option selected {};
|
Option selected {};
|
||||||
StringCallbacksSet changeCallbacks;
|
StringCallbacksSet changeCallbacks;
|
||||||
|
Mode mode;
|
||||||
public:
|
public:
|
||||||
SelectBox(
|
SelectBox(
|
||||||
GUI& gui,
|
GUI& gui,
|
||||||
std::vector<Option>&& elements,
|
std::vector<Option>&& elements,
|
||||||
Option selected,
|
Option selected,
|
||||||
|
Mode mode,
|
||||||
int contentWidth,
|
int contentWidth,
|
||||||
const glm::vec4& padding
|
const glm::vec4& padding
|
||||||
);
|
);
|
||||||
|
|
@ -34,8 +40,13 @@ namespace gui {
|
||||||
|
|
||||||
void setOptions(std::vector<Option>&& options);
|
void setOptions(std::vector<Option>&& options);
|
||||||
|
|
||||||
|
Mode getMode() const;
|
||||||
|
|
||||||
|
void setMode(Mode mode);
|
||||||
|
|
||||||
void drawBackground(const DrawContext& pctx, const Assets&) override;
|
void drawBackground(const DrawContext& pctx, const Assets&) override;
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<Label> getLabel() const;
|
std::shared_ptr<Label> getLabel() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -452,7 +452,7 @@ void TextBox::refreshLabel() {
|
||||||
uint height = label->getLinesNumber() * rawTextCache.metrics.lineHeight *
|
uint height = label->getLinesNumber() * rawTextCache.metrics.lineHeight *
|
||||||
label->getLineInterval();
|
label->getLineInterval();
|
||||||
label->setSize(glm::vec2(label->getSize().x, height));
|
label->setSize(glm::vec2(label->getSize().x, height));
|
||||||
actualLength = height;
|
actualLengthY = height;
|
||||||
} else {
|
} else {
|
||||||
setScrollable(false);
|
setScrollable(false);
|
||||||
}
|
}
|
||||||
|
|
@ -1133,10 +1133,10 @@ void TextBox::setOnControlCombination(key_handler handler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextBox::setFocusedColor(glm::vec4 color) {
|
void TextBox::setFocusedColor(glm::vec4 color) {
|
||||||
this->focusedColor = color;
|
this->focusedColor = std::move(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec4 TextBox::getFocusedColor() const {
|
const glm::vec4& TextBox::getFocusedColor() const {
|
||||||
return focusedColor;
|
return focusedColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1144,7 +1144,7 @@ void TextBox::setTextColor(glm::vec4 color) {
|
||||||
this->textColor = color;
|
this->textColor = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec4 TextBox::getTextColor() const {
|
const glm::vec4& TextBox::getTextColor() const {
|
||||||
return textColor;
|
return textColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1248,7 +1248,7 @@ void TextBox::setPadding(glm::vec4 padding) {
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec4 TextBox::getPadding() const {
|
const glm::vec4& TextBox::getPadding() const {
|
||||||
return padding;
|
return padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ class ActionsHistory;
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
class TextBoxHistorian;
|
class TextBoxHistorian;
|
||||||
class TextBox : public Container {
|
class TextBox final : public Container {
|
||||||
const Input& inputEvents;
|
const Input& inputEvents;
|
||||||
LabelCache rawTextCache;
|
LabelCache rawTextCache;
|
||||||
std::shared_ptr<ActionsHistory> history;
|
std::shared_ptr<ActionsHistory> history;
|
||||||
|
|
@ -100,124 +100,124 @@ namespace gui {
|
||||||
glm::vec4 padding=glm::vec4(4.0f)
|
glm::vec4 padding=glm::vec4(4.0f)
|
||||||
);
|
);
|
||||||
|
|
||||||
virtual ~TextBox();
|
~TextBox();
|
||||||
|
|
||||||
void paste(const std::wstring& text, bool history=true);
|
void paste(const std::wstring& text, bool history=true);
|
||||||
void erase(size_t start, size_t length);
|
void erase(size_t start, size_t length);
|
||||||
void resetSelection();
|
void resetSelection();
|
||||||
|
|
||||||
virtual void setTextSupplier(wstringsupplier supplier);
|
void setTextSupplier(wstringsupplier supplier);
|
||||||
|
|
||||||
/// @brief Consumer called on stop editing text (textbox defocus)
|
/// @brief Consumer called on stop editing text (textbox defocus)
|
||||||
/// @param consumer std::wstring consumer function
|
/// @param consumer std::wstring consumer function
|
||||||
virtual void setTextConsumer(wstringconsumer consumer);
|
void setTextConsumer(wstringconsumer consumer);
|
||||||
|
|
||||||
/// @brief Sub-consumer called while editing text
|
/// @brief Sub-consumer called while editing text
|
||||||
/// @param consumer std::wstring consumer function
|
/// @param consumer std::wstring consumer function
|
||||||
virtual void setTextSubConsumer(wstringconsumer consumer);
|
void setTextSubConsumer(wstringconsumer consumer);
|
||||||
|
|
||||||
/// @brief Text validator called while text editing and returns true if
|
/// @brief Text validator called while text editing and returns true if
|
||||||
/// text is valid
|
/// text is valid
|
||||||
/// @param validator std::wstring consumer returning boolean
|
/// @param validator std::wstring consumer returning boolean
|
||||||
virtual void setTextValidator(wstringchecker validator);
|
void setTextValidator(wstringchecker validator);
|
||||||
|
|
||||||
virtual void setOnControlCombination(key_handler handler);
|
void setOnControlCombination(key_handler handler);
|
||||||
|
|
||||||
virtual void setFocusedColor(glm::vec4 color);
|
void setFocusedColor(glm::vec4 color);
|
||||||
virtual glm::vec4 getFocusedColor() const;
|
const glm::vec4& getFocusedColor() const;
|
||||||
|
|
||||||
virtual void setTextColor(glm::vec4 color);
|
void setTextColor(glm::vec4 color);
|
||||||
virtual glm::vec4 getTextColor() const;
|
const glm::vec4& getTextColor() const;
|
||||||
|
|
||||||
/// @brief Set color of textbox marked by validator as invalid
|
/// @brief Set color of textbox marked by validator as invalid
|
||||||
virtual void setErrorColor(glm::vec4 color);
|
void setErrorColor(glm::vec4 color);
|
||||||
|
|
||||||
/// @brief Get color of textbox marked by validator as invalid
|
/// @brief Get color of textbox marked by validator as invalid
|
||||||
virtual glm::vec4 getErrorColor() const;
|
glm::vec4 getErrorColor() const;
|
||||||
|
|
||||||
/// @brief Get TextBox content text or placeholder if empty
|
/// @brief Get TextBox content text or placeholder if empty
|
||||||
virtual const std::wstring& getText() const;
|
const std::wstring& getText() const;
|
||||||
|
|
||||||
/// @brief Set TextBox content text
|
/// @brief Set TextBox content text
|
||||||
virtual void setText(const std::wstring &value);
|
void setText(const std::wstring &value);
|
||||||
|
|
||||||
/// @brief Get text placeholder
|
/// @brief Get text placeholder
|
||||||
virtual const std::wstring& getPlaceholder() const;
|
const std::wstring& getPlaceholder() const;
|
||||||
|
|
||||||
/// @brief Set text placeholder
|
/// @brief Set text placeholder
|
||||||
/// @param text will be used instead of empty
|
/// @param text will be used instead of empty
|
||||||
virtual void setPlaceholder(const std::wstring& text);
|
void setPlaceholder(const std::wstring& text);
|
||||||
|
|
||||||
/// @brief Get textbox hint
|
/// @brief Get textbox hint
|
||||||
virtual const std::wstring& getHint() const;
|
const std::wstring& getHint() const;
|
||||||
|
|
||||||
/// @brief Set textbox hint
|
/// @brief Set textbox hint
|
||||||
/// @param text will be shown instead of empty
|
/// @param text will be shown instead of empty
|
||||||
virtual void setHint(const std::wstring& text);
|
void setHint(const std::wstring& text);
|
||||||
|
|
||||||
/// @brief Get selected text
|
/// @brief Get selected text
|
||||||
virtual std::wstring getSelection() const;
|
std::wstring getSelection() const;
|
||||||
|
|
||||||
/// @brief Get current caret position in text
|
/// @brief Get current caret position in text
|
||||||
/// @return integer in range [0, text.length()]
|
/// @return integer in range [0, text.length()]
|
||||||
virtual size_t getCaret() const;
|
size_t getCaret() const;
|
||||||
|
|
||||||
/// @brief Set caret position in the text
|
/// @brief Set caret position in the text
|
||||||
/// @param position integer in range [0, text.length()]
|
/// @param position integer in range [0, text.length()]
|
||||||
virtual void setCaret(size_t position);
|
void setCaret(size_t position);
|
||||||
|
|
||||||
/// @brief Set caret position in the text
|
/// @brief Set caret position in the text
|
||||||
/// @param position integer in range [-text.length(), text.length()]
|
/// @param position integer in range [-text.length(), text.length()]
|
||||||
virtual void setCaret(ptrdiff_t position);
|
void setCaret(ptrdiff_t position);
|
||||||
|
|
||||||
/// @brief Select part of the text
|
/// @brief Select part of the text
|
||||||
/// @param start index of the first selected character
|
/// @param start index of the first selected character
|
||||||
/// @param end index of the last selected character + 1
|
/// @param end index of the last selected character + 1
|
||||||
virtual void select(int start, int end);
|
void select(int start, int end);
|
||||||
|
|
||||||
/// @brief Get number of line at specific position in text
|
/// @brief Get number of line at specific position in text
|
||||||
/// @param position target position
|
/// @param position target position
|
||||||
/// @return line number
|
/// @return line number
|
||||||
virtual uint getLineAt(size_t position) const;
|
uint getLineAt(size_t position) const;
|
||||||
|
|
||||||
/// @brief Get specific line text position
|
/// @brief Get specific line text position
|
||||||
/// @param line target line
|
/// @param line target line
|
||||||
/// @return line position in text
|
/// @return line position in text
|
||||||
virtual size_t getLinePos(uint line) const;
|
size_t getLinePos(uint line) const;
|
||||||
|
|
||||||
int calcIndexAt(int x, int y) const;
|
int calcIndexAt(int x, int y) const;
|
||||||
int getLineYOffset(int line) const;
|
int getLineYOffset(int line) const;
|
||||||
|
|
||||||
/// @brief Check text with validator set with setTextValidator
|
/// @brief Check text with validator set with setTextValidator
|
||||||
/// @return true if text is valid
|
/// @return true if text is valid
|
||||||
virtual bool validate();
|
bool validate();
|
||||||
|
|
||||||
virtual void setValid(bool valid);
|
void setValid(bool valid);
|
||||||
virtual bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
/// @brief Enable/disable multiline mode
|
/// @brief Enable/disable multiline mode
|
||||||
virtual void setMultiline(bool multiline);
|
void setMultiline(bool multiline);
|
||||||
|
|
||||||
/// @brief Check if multiline mode is enabled
|
/// @brief Check if multiline mode is enabled
|
||||||
virtual bool isMultiline() const;
|
bool isMultiline() const;
|
||||||
|
|
||||||
/// @brief Enable/disable text wrapping
|
/// @brief Enable/disable text wrapping
|
||||||
virtual void setTextWrapping(bool flag);
|
void setTextWrapping(bool flag);
|
||||||
|
|
||||||
/// @brief Check if text wrapping is enabled
|
/// @brief Check if text wrapping is enabled
|
||||||
virtual bool isTextWrapping() const;
|
bool isTextWrapping() const;
|
||||||
|
|
||||||
/// @brief Enable/disable text editing feature
|
/// @brief Enable/disable text editing feature
|
||||||
virtual void setEditable(bool editable);
|
void setEditable(bool editable);
|
||||||
|
|
||||||
/// @brief Check if text editing feature is enabled
|
/// @brief Check if text editing feature is enabled
|
||||||
virtual bool isEditable() const;
|
bool isEditable() const;
|
||||||
|
|
||||||
virtual bool isEdited() const;
|
bool isEdited() const;
|
||||||
virtual void setUnedited();
|
void setUnedited();
|
||||||
|
|
||||||
virtual void setPadding(glm::vec4 padding);
|
void setPadding(glm::vec4 padding);
|
||||||
glm::vec4 getPadding() const;
|
const glm::vec4& getPadding() const;
|
||||||
|
|
||||||
size_t getSelectionStart() const;
|
size_t getSelectionStart() const;
|
||||||
size_t getSelectionEnd() const;
|
size_t getSelectionEnd() const;
|
||||||
|
|
@ -226,34 +226,34 @@ namespace gui {
|
||||||
bool isKeepLineSelection() const;
|
bool isKeepLineSelection() const;
|
||||||
|
|
||||||
/// @brief Set runnable called on textbox focus
|
/// @brief Set runnable called on textbox focus
|
||||||
virtual void setOnEditStart(runnable oneditstart);
|
void setOnEditStart(runnable oneditstart);
|
||||||
|
|
||||||
virtual void setAutoResize(bool flag);
|
void setAutoResize(bool flag);
|
||||||
virtual bool isAutoResize() const;
|
bool isAutoResize() const;
|
||||||
|
|
||||||
virtual void setShowLineNumbers(bool flag);
|
void setShowLineNumbers(bool flag);
|
||||||
virtual bool isShowLineNumbers() const;
|
bool isShowLineNumbers() const;
|
||||||
|
|
||||||
virtual void reposition() override;
|
void reposition() override;
|
||||||
virtual void onFocus() override;
|
void onFocus() override;
|
||||||
virtual void refresh() override;
|
void refresh() override;
|
||||||
virtual void doubleClick(int x, int y) override;
|
void doubleClick(int x, int y) override;
|
||||||
virtual void click(int, int) override;
|
void click(int, int) override;
|
||||||
virtual void mouseMove(int x, int y) override;
|
void mouseMove(int x, int y) override;
|
||||||
virtual bool isFocuskeeper() const override {return true;}
|
bool isFocuskeeper() const override {return true;}
|
||||||
virtual void draw(const DrawContext& pctx, const Assets& assets) override;
|
void draw(const DrawContext& pctx, const Assets& assets) override;
|
||||||
virtual void drawBackground(const DrawContext& pctx, const Assets& assets) override;
|
void drawBackground(const DrawContext& pctx, const Assets& assets) override;
|
||||||
virtual void typed(unsigned int codepoint) override;
|
void typed(unsigned int codepoint) override;
|
||||||
virtual void keyPressed(Keycode key) override;
|
void keyPressed(Keycode key) override;
|
||||||
virtual std::shared_ptr<UINode> getAt(const glm::vec2& pos) override;
|
std::shared_ptr<UINode> getAt(const glm::vec2& pos) override;
|
||||||
virtual void setOnUpPressed(const runnable &callback);
|
void setOnUpPressed(const runnable& callback);
|
||||||
virtual void setOnDownPressed(const runnable &callback);
|
void setOnDownPressed(const runnable& callback);
|
||||||
|
|
||||||
virtual void setSyntax(std::string_view lang);
|
void setSyntax(std::string_view lang);
|
||||||
virtual const std::string& getSyntax() const;
|
const std::string& getSyntax() const;
|
||||||
|
|
||||||
virtual void setMarkup(std::string_view lang);
|
void setMarkup(std::string_view lang);
|
||||||
virtual const std::string& getMarkup() const;
|
const std::string& getMarkup() const;
|
||||||
|
|
||||||
std::shared_ptr<Label> getLabel() const;
|
std::shared_ptr<Label> getLabel() const;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ int TrackBar::getTrackWidth() const {
|
||||||
return trackWidth;
|
return trackWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec4 TrackBar::getTrackColor() const {
|
const glm::vec4& TrackBar::getTrackColor() const {
|
||||||
return trackColor;
|
return trackColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include "UINode.hpp"
|
#include "UINode.hpp"
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
class TrackBar : public UINode {
|
class TrackBar final : public UINode {
|
||||||
protected:
|
protected:
|
||||||
glm::vec4 trackColor {1.0f, 1.0f, 1.0f, 0.4f};
|
glm::vec4 trackColor {1.0f, 1.0f, 1.0f, 0.4f};
|
||||||
doublesupplier supplier = nullptr;
|
doublesupplier supplier = nullptr;
|
||||||
|
|
@ -24,29 +24,29 @@ namespace gui {
|
||||||
double step = 1.0,
|
double step = 1.0,
|
||||||
int trackWidth = 12
|
int trackWidth = 12
|
||||||
);
|
);
|
||||||
virtual void draw(const DrawContext& pctx, const Assets& assets) override;
|
void draw(const DrawContext& pctx, const Assets& assets) override;
|
||||||
|
|
||||||
virtual void setSupplier(doublesupplier);
|
void setSupplier(doublesupplier);
|
||||||
virtual void setConsumer(doubleconsumer);
|
void setConsumer(doubleconsumer);
|
||||||
virtual void setSubConsumer(doubleconsumer);
|
void setSubConsumer(doubleconsumer);
|
||||||
|
|
||||||
virtual void mouseMove(int x, int y) override;
|
void mouseMove(int x, int y) override;
|
||||||
virtual void mouseRelease(int x, int y) override;
|
void mouseRelease(int x, int y) override;
|
||||||
|
|
||||||
virtual double getValue() const;
|
double getValue() const;
|
||||||
virtual double getMin() const;
|
double getMin() const;
|
||||||
virtual double getMax() const;
|
double getMax() const;
|
||||||
virtual double getStep() const;
|
double getStep() const;
|
||||||
virtual int getTrackWidth() const;
|
int getTrackWidth() const;
|
||||||
virtual glm::vec4 getTrackColor() const;
|
const glm::vec4& getTrackColor() const;
|
||||||
virtual bool isChangeOnRelease() const;
|
bool isChangeOnRelease() const;
|
||||||
|
|
||||||
virtual void setValue(double);
|
void setValue(double);
|
||||||
virtual void setMin(double);
|
void setMin(double);
|
||||||
virtual void setMax(double);
|
void setMax(double);
|
||||||
virtual void setStep(double);
|
void setStep(double);
|
||||||
virtual void setTrackWidth(int);
|
void setTrackWidth(int);
|
||||||
virtual void setTrackColor(glm::vec4);
|
void setTrackColor(glm::vec4);
|
||||||
virtual void setChangeOnRelease(bool);
|
void setChangeOnRelease(bool);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,8 @@ void UINode::click(int, int) {
|
||||||
void UINode::clicked(Mousecode button) {
|
void UINode::clicked(Mousecode button) {
|
||||||
if (button == Mousecode::BUTTON_2) {
|
if (button == Mousecode::BUTTON_2) {
|
||||||
actions.notify(UIAction::RIGHT_CLICK, gui);
|
actions.notify(UIAction::RIGHT_CLICK, gui);
|
||||||
|
} else if (button == Mousecode::BUTTON_3) {
|
||||||
|
actions.notify(UIAction::MIDDLE_CLICK, gui);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ namespace gui {
|
||||||
FOCUS,
|
FOCUS,
|
||||||
DEFOCUS,
|
DEFOCUS,
|
||||||
RIGHT_CLICK,
|
RIGHT_CLICK,
|
||||||
|
MIDDLE_CLICK,
|
||||||
MOUSE_OVER,
|
MOUSE_OVER,
|
||||||
MOUSE_OUT,
|
MOUSE_OUT,
|
||||||
MOUSE_ENTER,
|
MOUSE_ENTER,
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,7 @@ static void read_uinode(
|
||||||
|
|
||||||
register_action(node, reader, element, "onclick", UIAction::CLICK);
|
register_action(node, reader, element, "onclick", UIAction::CLICK);
|
||||||
register_action(node, reader, element, "onrightclick", UIAction::RIGHT_CLICK);
|
register_action(node, reader, element, "onrightclick", UIAction::RIGHT_CLICK);
|
||||||
|
register_action(node, reader, element, "onmiddleclick", UIAction::MIDDLE_CLICK);
|
||||||
register_action(node, reader, element, "onfocus", UIAction::FOCUS);
|
register_action(node, reader, element, "onfocus", UIAction::FOCUS);
|
||||||
register_action(node, reader, element, "ondefocus", UIAction::DEFOCUS);
|
register_action(node, reader, element, "ondefocus", UIAction::DEFOCUS);
|
||||||
register_action(node, reader, element, "ondoubleclick", UIAction::DOUBLE_CLICK);
|
register_action(node, reader, element, "ondoubleclick", UIAction::DOUBLE_CLICK);
|
||||||
|
|
@ -458,8 +459,7 @@ static std::shared_ptr<UINode> read_select(
|
||||||
}
|
}
|
||||||
auto value = elem->attr("value").getText();
|
auto value = elem->attr("value").getText();
|
||||||
auto text = parse_inner_text(*elem, reader.getContext());
|
auto text = parse_inner_text(*elem, reader.getContext());
|
||||||
options.push_back(SelectBox::Option {std::move(value), std::move(text)}
|
options.push_back(SelectBox::Option {std::move(value), std::move(text)});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.has("selected")) {
|
if (element.has("selected")) {
|
||||||
|
|
@ -481,10 +481,19 @@ static std::shared_ptr<UINode> read_select(
|
||||||
selected.text = innerText;
|
selected.text = innerText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SelectBox::Mode mode = SelectBox::Mode::SELECT;
|
||||||
|
if (element.has("mode")) {
|
||||||
|
auto modeName = element.attr("mode").getText();
|
||||||
|
if (modeName == "button") {
|
||||||
|
mode = SelectBox::Mode::BUTTON;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto selectBox = std::make_shared<SelectBox>(
|
auto selectBox = std::make_shared<SelectBox>(
|
||||||
gui,
|
gui,
|
||||||
std::move(options),
|
std::move(options),
|
||||||
std::move(selected),
|
std::move(selected),
|
||||||
|
mode,
|
||||||
contentWidth,
|
contentWidth,
|
||||||
std::move(padding)
|
std::move(padding)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <iostream>
|
#include <iosfwd>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
#include "../path.hpp"
|
#include "../path.hpp"
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ Inventories::Inventories(Level& level) : level(level) {
|
||||||
Inventories::~Inventories() = default;
|
Inventories::~Inventories() = default;
|
||||||
|
|
||||||
std::shared_ptr<Inventory> Inventories::create(size_t size) {
|
std::shared_ptr<Inventory> Inventories::create(size_t size) {
|
||||||
int64_t id = level.getWorld()->getNextInventoryId();
|
int64_t id = level.getWorld().getNextInventoryId();
|
||||||
auto inv = std::make_shared<Inventory>(id, size);
|
auto inv = std::make_shared<Inventory>(id, size);
|
||||||
store(inv);
|
store(inv);
|
||||||
return inv;
|
return inv;
|
||||||
|
|
@ -52,7 +52,7 @@ std::shared_ptr<Inventory> Inventories::clone(int64_t id) {
|
||||||
auto original = get(id);
|
auto original = get(id);
|
||||||
if (original == nullptr) return nullptr;
|
if (original == nullptr) return nullptr;
|
||||||
auto clone = std::make_shared<Inventory>(*original);
|
auto clone = std::make_shared<Inventory>(*original);
|
||||||
clone->setId(level.getWorld()->getNextInventoryId());
|
clone->setId(level.getWorld().getNextInventoryId());
|
||||||
store(clone);
|
store(clone);
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,14 @@ void ItemStack::setCount(itemcount_t count) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ItemStack::maximizeCount(const ContentIndices& indices) {
|
||||||
|
auto& def = indices.items.require(getItemId());
|
||||||
|
this->count = def.stackSize;
|
||||||
|
if (count == 0) {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ItemStack::setField(std::string_view name, dv::value value) {
|
void ItemStack::setField(std::string_view name, dv::value value) {
|
||||||
if (fields == nullptr) {
|
if (fields == nullptr) {
|
||||||
if (value == nullptr) {
|
if (value == nullptr) {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ public:
|
||||||
void set(const ItemStack& item);
|
void set(const ItemStack& item);
|
||||||
void set(ItemStack&& item);
|
void set(ItemStack&& item);
|
||||||
void setCount(itemcount_t count);
|
void setCount(itemcount_t count);
|
||||||
|
void maximizeCount(const ContentIndices& indices);
|
||||||
|
|
||||||
/// @brief Set a field in the item stack data.
|
/// @brief Set a field in the item stack data.
|
||||||
void setField(std::string_view name, dv::value value);
|
void setField(std::string_view name, dv::value value);
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ ChunksController::ChunksController(Level& level)
|
||||||
generator(std::make_unique<WorldGenerator>(
|
generator(std::make_unique<WorldGenerator>(
|
||||||
level.content.generators.require(level.environment.generator),
|
level.content.generators.require(level.environment.generator),
|
||||||
level.content,
|
level.content,
|
||||||
level.getWorld()->getSeed()
|
level.getWorld().getSeed()
|
||||||
)) {}
|
)) {}
|
||||||
|
|
||||||
ChunksController::~ChunksController() = default;
|
ChunksController::~ChunksController() = default;
|
||||||
|
|
|
||||||
|
|
@ -283,7 +283,7 @@ void EngineController::createWorld(
|
||||||
|
|
||||||
// generator-based environments must die in 1.0
|
// generator-based environments must die in 1.0
|
||||||
bool genBasedEnv = false;
|
bool genBasedEnv = false;
|
||||||
if (auto genDef = generators.find(environment)) {
|
if (generators.find(environment)) {
|
||||||
genBasedEnv = true;
|
genBasedEnv = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -314,8 +314,8 @@ void EngineController::setLocalPlayer(int64_t player) {
|
||||||
localPlayer = player;
|
localPlayer = player;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EngineController::reopenWorld(World* world) {
|
void EngineController::reopenWorld(World& world) {
|
||||||
std::string name = world->wfile->getFolder().name();
|
std::string name = world.wfile->getFolder().name();
|
||||||
engine.onWorldClosed();
|
engine.onWorldClosed();
|
||||||
openWorld(name, true);
|
openWorld(name, true);
|
||||||
}
|
}
|
||||||
|
|
@ -384,16 +384,16 @@ void EngineController::reconfigPacks(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto level = controller->getLevel();
|
auto level = controller->getLevel();
|
||||||
auto world = level->getWorld();
|
auto& world = level->getWorld();
|
||||||
controller->processBeforeQuit();
|
controller->processBeforeQuit();
|
||||||
controller->saveWorld();
|
controller->saveWorld();
|
||||||
|
|
||||||
auto names = PacksManager::getNames(world->getPacks());
|
auto names = PacksManager::getNames(world.getPacks());
|
||||||
auto& manager = contentControl.scan();
|
auto& manager = contentControl.scan();
|
||||||
reconfig_packs_inside(manager, names, packsToAdd, packsToRemove);
|
reconfig_packs_inside(manager, names, packsToAdd, packsToRemove);
|
||||||
|
|
||||||
const auto& settings = engine.getSettings();
|
const auto& settings = engine.getSettings();
|
||||||
auto& wfile = *world->wfile;
|
auto& wfile = *world.wfile;
|
||||||
if (!settings.debug.generatorTestMode.get()) {
|
if (!settings.debug.generatorTestMode.get()) {
|
||||||
wfile.removeIndices(packsToRemove);
|
wfile.removeIndices(packsToRemove);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,5 +41,5 @@ public:
|
||||||
|
|
||||||
void setLocalPlayer(int64_t player);
|
void setLocalPlayer(int64_t player);
|
||||||
|
|
||||||
void reopenWorld(World* world);
|
void reopenWorld(World& world);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -140,16 +140,16 @@ void LevelController::processBeforeQuit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void LevelController::saveWorld() {
|
void LevelController::saveWorld() {
|
||||||
auto world = level->getWorld();
|
auto& world = level->getWorld();
|
||||||
if (world->isNameless()) {
|
if (world.isNameless()) {
|
||||||
logger.info() << "nameless world will not be saved";
|
logger.info() << "nameless world will not be saved";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.info() << "writing world '" << world->getName() << "'";
|
logger.info() << "writing world '" << world.getName() << "'";
|
||||||
world->wfile->createDirectories();
|
world.wfile->createDirectories();
|
||||||
scripting::on_world_save();
|
scripting::on_world_save();
|
||||||
level->onSave();
|
level->onSave();
|
||||||
level->getWorld()->write(*level);
|
level->getWorld().write(*level);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LevelController::onWorldQuit() {
|
void LevelController::onWorldQuit() {
|
||||||
|
|
|
||||||
|
|
@ -340,6 +340,19 @@ static int determine_rotation(
|
||||||
if (camDir.z > 0.0f) return BLOCK_DIR_SOUTH | verticalBit;
|
if (camDir.z > 0.0f) return BLOCK_DIR_SOUTH | verticalBit;
|
||||||
if (camDir.z < 0.0f) return BLOCK_DIR_NORTH | verticalBit;
|
if (camDir.z < 0.0f) return BLOCK_DIR_NORTH | verticalBit;
|
||||||
}
|
}
|
||||||
|
} else if (name == "ladder") {
|
||||||
|
if (norm.x < 0.0f) return BLOCK_DIR_EAST;
|
||||||
|
if (norm.x > 0.0f) return BLOCK_DIR_WEST;
|
||||||
|
if (norm.z > 0.0f) return BLOCK_DIR_NORTH;
|
||||||
|
if (norm.z < 0.0f) return BLOCK_DIR_SOUTH;
|
||||||
|
if (abs(camDir.x) > abs(camDir.z)) {
|
||||||
|
if (camDir.x > 0.0f) return BLOCK_DIR_EAST;
|
||||||
|
if (camDir.x < 0.0f) return BLOCK_DIR_WEST;
|
||||||
|
}
|
||||||
|
if (abs(camDir.x) < abs(camDir.z)) {
|
||||||
|
if (camDir.z > 0.0f) return BLOCK_DIR_SOUTH;
|
||||||
|
if (camDir.z < 0.0f) return BLOCK_DIR_NORTH;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ static int l_get_model(lua::State* L) {
|
||||||
auto& rigConfig = *skeleton->config;
|
auto& rigConfig = *skeleton->config;
|
||||||
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
|
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
|
||||||
const auto& modelOverride = skeleton->modelOverrides[index];
|
const auto& modelOverride = skeleton->modelOverrides[index];
|
||||||
if (!modelOverride.model) {
|
if (modelOverride.model) {
|
||||||
return lua::pushstring(L, modelOverride.name);
|
return lua::pushstring(L, modelOverride.name);
|
||||||
}
|
}
|
||||||
return lua::pushstring(L, rigConfig.getBones()[index]->model.name);
|
return lua::pushstring(L, rigConfig.getBones()[index]->model.name);
|
||||||
|
|
|
||||||
|
|
@ -1127,6 +1127,7 @@ static int l_gui_load_document(lua::State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto env = scripting::create_doc_environment(parentEnv, alias);
|
auto env = scripting::create_doc_environment(parentEnv, alias);
|
||||||
|
auto envId = *env;
|
||||||
// namespace extension
|
// namespace extension
|
||||||
if (lua::istable(L, 4)) {
|
if (lua::istable(L, 4)) {
|
||||||
if (lua::get_from(L, "table", "extend")) {
|
if (lua::get_from(L, "table", "extend")) {
|
||||||
|
|
@ -1148,7 +1149,7 @@ static int l_gui_load_document(lua::State* L) {
|
||||||
engine->requireAssets().store(std::move(documentPtr), alias);
|
engine->requireAssets().store(std::move(documentPtr), alias);
|
||||||
|
|
||||||
scripting::on_ui_open(*document, {args});
|
scripting::on_ui_open(*document, {args});
|
||||||
return 0;
|
return lua::pushenv(L, envId);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_set_syntax_styles(lua::State* L) {
|
static int l_set_syntax_styles(lua::State* L) {
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,15 @@ static int l_get_second_inventory(lua::State* L) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_get_exchange_inventory(lua::State* L) {
|
||||||
|
auto inventory = hud->getExchangeInventory();
|
||||||
|
if (inventory == nullptr) {
|
||||||
|
return lua::pushinteger(L, 0);
|
||||||
|
} else {
|
||||||
|
return lua::pushinteger(L, inventory->getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int l_get_player(lua::State* L) {
|
static int l_get_player(lua::State* L) {
|
||||||
auto player = hud->getPlayer();
|
auto player = hud->getPlayer();
|
||||||
return lua::pushinteger(L, player->getId());
|
return lua::pushinteger(L, player->getId());
|
||||||
|
|
@ -216,6 +225,7 @@ const luaL_Reg hudlib[] = {
|
||||||
{"show_overlay", wrap_hud<l_show_overlay>},
|
{"show_overlay", wrap_hud<l_show_overlay>},
|
||||||
{"get_block_inventory", wrap_hud<l_get_block_inventory>},
|
{"get_block_inventory", wrap_hud<l_get_block_inventory>},
|
||||||
{"get_second_inventory", wrap_hud<l_get_second_inventory>},
|
{"get_second_inventory", wrap_hud<l_get_second_inventory>},
|
||||||
|
{"get_exchange_inventory", wrap_hud<l_get_exchange_inventory>},
|
||||||
{"close", wrap_hud<l_close>},
|
{"close", wrap_hud<l_close>},
|
||||||
{"pause", wrap_hud<l_pause>},
|
{"pause", wrap_hud<l_pause>},
|
||||||
{"resume", wrap_hud<l_resume>},
|
{"resume", wrap_hud<l_resume>},
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include "items/Inventories.hpp"
|
#include "items/Inventories.hpp"
|
||||||
#include "items/ItemStack.hpp"
|
#include "items/ItemStack.hpp"
|
||||||
#include "logic/BlocksController.hpp"
|
#include "logic/BlocksController.hpp"
|
||||||
|
#include "logic/scripting/lua/lua_util.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "api_lua.hpp"
|
#include "api_lua.hpp"
|
||||||
|
|
||||||
|
|
@ -180,7 +181,7 @@ static int l_move(lua::State* L) {
|
||||||
validate_slotid(slotAid, invA);
|
validate_slotid(slotAid, invA);
|
||||||
|
|
||||||
auto invBid = lua::tointeger(L, 3);
|
auto invBid = lua::tointeger(L, 3);
|
||||||
auto slotBid = lua::isnil(L, 4) ? -1 : lua::tointeger(L, 4);
|
auto slotBid = lua::isnoneornil(L, 4) ? -1 : lua::tointeger(L, 4);
|
||||||
auto& invB = get_inventory(invBid, 3);
|
auto& invB = get_inventory(invBid, 3);
|
||||||
auto& slot = invA.getSlot(slotAid);
|
auto& slot = invA.getSlot(slotAid);
|
||||||
if (slotBid == -1) {
|
if (slotBid == -1) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
#include <iostream>
|
||||||
|
#include <glm/gtx/string_cast.hpp>
|
||||||
#include "api_lua.hpp"
|
#include "api_lua.hpp"
|
||||||
|
|
||||||
#define GLM_ENABLE_EXPERIMENTAL
|
|
||||||
#include <glm/ext/matrix_transform.hpp>
|
#include <glm/ext/matrix_transform.hpp>
|
||||||
#include <glm/gtx/quaternion.hpp>
|
#include <glm/gtx/quaternion.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
@ -31,14 +33,14 @@ static int l_tostring(lua::State* L) {
|
||||||
lua::check_argc(L, 1);
|
lua::check_argc(L, 1);
|
||||||
auto quat = lua::toquat(L, 1);
|
auto quat = lua::toquat(L, 1);
|
||||||
|
|
||||||
|
float components[4] = {quat.w, quat.x, quat.y, quat.z};
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "quat"
|
ss << "quat {";
|
||||||
<< "{";
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
ss << ", ";
|
ss << ", ";
|
||||||
}
|
}
|
||||||
ss << quat[i];
|
ss << components[i];
|
||||||
}
|
}
|
||||||
ss << "}";
|
ss << "}";
|
||||||
return lua::pushstring(L, ss.str());
|
return lua::pushstring(L, ss.str());
|
||||||
|
|
@ -52,10 +54,42 @@ static int l_from_euler(lua::State *L) {
|
||||||
return lua::pushquat(L, glm::quat(euler));
|
return lua::pushquat(L, glm::quat(euler));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_mul(lua::State* L) {
|
||||||
|
uint argc = lua::check_argc(L, 2, 3);
|
||||||
|
glm::quat a = lua::toquat(L, 1);
|
||||||
|
glm::quat b = lua::toquat(L, 2);
|
||||||
|
|
||||||
|
glm::quat result = a * b;
|
||||||
|
|
||||||
|
if (argc == 3) {
|
||||||
|
lua::setquat(L, 3, result);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lua::pushquat(L, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_mul_vec3(lua::State* L) {
|
||||||
|
uint argc = lua::check_argc(L, 2, 3);
|
||||||
|
glm::quat q = lua::toquat(L, 1);
|
||||||
|
glm::vec3 v = lua::tovec3(L, 2);
|
||||||
|
|
||||||
|
glm::vec3 result = q * v;
|
||||||
|
|
||||||
|
if (argc == 3) {
|
||||||
|
lua::setvec(L, 3, result);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lua::pushvec3(L, result);
|
||||||
|
}
|
||||||
|
|
||||||
const luaL_Reg quatlib[] = {
|
const luaL_Reg quatlib[] = {
|
||||||
{"from_mat4", lua::wrap<l_from_mat4>},
|
{"from_mat4", lua::wrap<l_from_mat4>},
|
||||||
{"from_euler", lua::wrap<l_from_euler>},
|
{"from_euler", lua::wrap<l_from_euler>},
|
||||||
{"slerp", lua::wrap<l_slerp>},
|
{"slerp", lua::wrap<l_slerp>},
|
||||||
{"tostring", lua::wrap<l_tostring>},
|
{"tostring", lua::wrap<l_tostring>},
|
||||||
|
{"mul", lua::wrap<l_mul>},
|
||||||
|
{"mul_vec3", lua::wrap<l_mul_vec3>},
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "world/World.hpp"
|
#include "world/World.hpp"
|
||||||
|
#include "world/Weather.hpp"
|
||||||
|
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
|
|
||||||
|
|
@ -9,6 +10,11 @@ static Weather& require_weather() {
|
||||||
if (level == nullptr) {
|
if (level == nullptr) {
|
||||||
throw std::runtime_error("world is not open");
|
throw std::runtime_error("world is not open");
|
||||||
}
|
}
|
||||||
|
if (renderer == nullptr) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"world frontend is not initialized yet (wait for on_hud_open event)"
|
||||||
|
);
|
||||||
|
}
|
||||||
return renderer->getWeather();
|
return renderer->getWeather();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ static Level& require_level() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static WorldInfo& require_world_info() {
|
static WorldInfo& require_world_info() {
|
||||||
return require_level().getWorld()->getInfo();
|
return require_level().getWorld().getInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_is_open(lua::State* L) {
|
static int l_is_open(lua::State* L) {
|
||||||
|
|
@ -139,7 +139,7 @@ static int l_get_chunk_data(lua::State* L) {
|
||||||
auto voxelData = std::make_unique<ubyte[]>(CHUNK_DATA_LEN);
|
auto voxelData = std::make_unique<ubyte[]>(CHUNK_DATA_LEN);
|
||||||
std::vector<ubyte> chunkData;
|
std::vector<ubyte> chunkData;
|
||||||
if (chunk == nullptr) {
|
if (chunk == nullptr) {
|
||||||
auto& regions = level->getWorld()->wfile->getRegions();
|
auto& regions = level->getWorld().wfile->getRegions();
|
||||||
if (!regions.getVoxels(x, z, voxelData.get())) {
|
if (!regions.getVoxels(x, z, voxelData.get())) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -218,7 +218,7 @@ static int l_save_chunk_data(lua::State* L) {
|
||||||
reinterpret_cast<const ubyte*>(buffer.data()),
|
reinterpret_cast<const ubyte*>(buffer.data()),
|
||||||
reinterpret_cast<const ubyte*>(buffer.data()) + buffer.size()
|
reinterpret_cast<const ubyte*>(buffer.data()) + buffer.size()
|
||||||
),
|
),
|
||||||
level->getWorld()->wfile->getRegions()
|
level->getWorld().wfile->getRegions()
|
||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,12 @@ static std::unordered_map<std::string, lua_CFunction> methods {
|
||||||
{"min", lua::wrap<l_binop_func<util::min>>},
|
{"min", lua::wrap<l_binop_func<util::min>>},
|
||||||
{"max", lua::wrap<l_binop_func<util::max>>},
|
{"max", lua::wrap<l_binop_func<util::max>>},
|
||||||
{"abs", lua::wrap<l_unaryop_func<util::abs>>},
|
{"abs", lua::wrap<l_unaryop_func<util::abs>>},
|
||||||
|
{"floor", lua::wrap<l_unaryop_func<util::floor>>},
|
||||||
|
{"round", lua::wrap<l_unaryop_func<util::round>>},
|
||||||
|
{"ceil", lua::wrap<l_unaryop_func<util::ceil>>},
|
||||||
|
{"sin", lua::wrap<l_unaryop_func<util::sin>>},
|
||||||
|
{"cos", lua::wrap<l_unaryop_func<util::cos>>},
|
||||||
|
{"tan", lua::wrap<l_unaryop_func<util::tan>>},
|
||||||
{"resize", lua::wrap<l_resize>},
|
{"resize", lua::wrap<l_resize>},
|
||||||
{"crop", lua::wrap<l_crop>},
|
{"crop", lua::wrap<l_crop>},
|
||||||
{"at", lua::wrap<l_at>},
|
{"at", lua::wrap<l_at>},
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue