Merge branch 'main' into curl
@ -39,6 +39,12 @@ Block model type from list:
|
|||||||
|
|
||||||
Integer specifying number of block draw group (render order). Used for semi-transparent blocks.
|
Integer specifying number of block draw group (render order). Used for semi-transparent blocks.
|
||||||
|
|
||||||
|
### *translucent*
|
||||||
|
|
||||||
|
Enables translucency support in block textures (examples: water, ice).
|
||||||
|
Should only be used when needed, as it impacts performance.
|
||||||
|
Not required for full transparency (grass, flowers).
|
||||||
|
|
||||||
### *rotation*
|
### *rotation*
|
||||||
|
|
||||||
Rotation profile (set of available block rotations and behaviour of placing block rotation) from list:
|
Rotation profile (set of available block rotations and behaviour of placing block rotation) from list:
|
||||||
|
|||||||
@ -24,7 +24,9 @@ hud.open_block(x: int, y: int, z: int) -> int, str
|
|||||||
```lua
|
```lua
|
||||||
-- Show overlay with layout specified.
|
-- Show overlay with layout specified.
|
||||||
-- Shows player inventory also if playerinv is true.
|
-- Shows player inventory also if playerinv is true.
|
||||||
hud.show_overlay(layoutid: str, playerinv: bool)
|
-- Using `args` you can specify an array of parameter values that will be passed
|
||||||
|
-- to on_open of the overlay being shown.
|
||||||
|
hud.show_overlay(layoutid: str, playerinv: bool, [optional] args: table)
|
||||||
|
|
||||||
-- Add element to the screen.
|
-- Add element to the screen.
|
||||||
-- The element will be removed on world close only.
|
-- The element will be removed on world close only.
|
||||||
|
|||||||
@ -56,12 +56,26 @@ player.set_noclip(bool)
|
|||||||
|
|
||||||
Getter and setter for player noclip mode (collisions disabled)
|
Getter and setter for player noclip mode (collisions disabled)
|
||||||
|
|
||||||
|
```lua
|
||||||
|
player.is_infinite_items() -> bool
|
||||||
|
player.set_infinite_items(bool)
|
||||||
|
```
|
||||||
|
|
||||||
|
Getter and setter for infinite items (not removed from inventory after use)
|
||||||
|
|
||||||
|
```lua
|
||||||
|
player.is_instant_destruction() -> bool
|
||||||
|
player.set_instant_destruction(bool)
|
||||||
|
```
|
||||||
|
|
||||||
|
Getter and setter for instant destruction of blocks when the `player.destroy` binding is activated.
|
||||||
|
|
||||||
``` lua
|
``` lua
|
||||||
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
||||||
player.get_spawnpoint(playerid: int) -> number, number, number
|
player.get_spawnpoint(playerid: int) -> number, number, number
|
||||||
```
|
```
|
||||||
|
|
||||||
Point setter and getter added by player
|
Spawn point setter and getter
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
player.get_selected_block(playerid: int) -> x,y,z
|
player.get_selected_block(playerid: int) -> x,y,z
|
||||||
|
|||||||
@ -27,4 +27,7 @@ utf8.upper(text: str) -> str
|
|||||||
|
|
||||||
-- Converts a string to lowercase
|
-- Converts a string to lowercase
|
||||||
utf8.lower(text: str) -> str
|
utf8.lower(text: str) -> str
|
||||||
|
|
||||||
|
-- Escapes a string
|
||||||
|
utf8.escape(text: str) -> str
|
||||||
```
|
```
|
||||||
|
|||||||
@ -38,7 +38,13 @@ Called on random block update (grass growth)
|
|||||||
function on_blocks_tick(tps: int)
|
function on_blocks_tick(tps: int)
|
||||||
```
|
```
|
||||||
|
|
||||||
Called tps (20) times per second.
|
Called tps (20) times per second. Use 1/tps instead of `time.delta()`.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function on_player_tick(playerid: int, tps: int)
|
||||||
|
```
|
||||||
|
|
||||||
|
Called tps (20) times per second. Use 1/tps instead of `time.delta()`.
|
||||||
|
|
||||||
## Item events
|
## Item events
|
||||||
|
|
||||||
|
|||||||
@ -78,14 +78,18 @@ Properties:
|
|||||||
| caret | int | yes | yes | carriage position. `textbox.caret = -1` will set the position to the end of the text |
|
| caret | int | yes | yes | carriage position. `textbox.caret = -1` will set the position to the end of the text |
|
||||||
| editable | bool | yes | yes | text mutability |
|
| editable | bool | yes | yes | text mutability |
|
||||||
| multiline | bool | yes | yes | multiline support |
|
| multiline | bool | yes | yes | multiline support |
|
||||||
|
| lineNumbers | bool | yes | yes | display line numbers |
|
||||||
| textWrap | bool | yes | yes | automatic text wrapping (only with multiline: "true") |
|
| textWrap | bool | yes | yes | automatic text wrapping (only with multiline: "true") |
|
||||||
| valid | bool | yes | no | is the entered text correct |
|
| valid | bool | yes | no | is the entered text correct |
|
||||||
|
| textColor | vec4 | yes | yes | text color |
|
||||||
|
|
||||||
Methods:
|
Methods:
|
||||||
|
|
||||||
| Method | Description |
|
| Method | Description |
|
||||||
| ----------- | ------------------------------------------------ |
|
| ------------------------- | ---------------------------------------------------------------- |
|
||||||
| paste(text) | inserts the specified text at the caret position |
|
| paste(text: str) | inserts the specified text at the caret position |
|
||||||
|
| lineAt(pos: int) -> int | determines the line number by position in the text |
|
||||||
|
| linePos(line: int) -> int | determines the position of the beginning of the line in the text |
|
||||||
|
|
||||||
## Slider (trackbar)
|
## Slider (trackbar)
|
||||||
|
|
||||||
|
|||||||
@ -56,7 +56,8 @@ Buttons and panels are also containers.
|
|||||||
|
|
||||||
- `padding` - element padding. Type: 4D vector.
|
- `padding` - element padding. Type: 4D vector.
|
||||||
*left, top, right, bottom*
|
*left, top, right, bottom*
|
||||||
`scrollable` - element scrollability. Works on panels only. Type: boolean
|
- `scrollable` - element scrollability. Type: boolean.
|
||||||
|
- `scroll-step` - scrolling step. Type: integer.
|
||||||
|
|
||||||
# Common *panel* attributes
|
# Common *panel* attributes
|
||||||
|
|
||||||
@ -104,7 +105,9 @@ Inner text - initially entered text
|
|||||||
- `multiline` - allows display of multiline text.
|
- `multiline` - allows display of multiline text.
|
||||||
- `text-wrap` - allows automatic text wrapping (works only with multiline: "true")
|
- `text-wrap` - allows automatic text wrapping (works only with multiline: "true")
|
||||||
- `editable` - determines whether the text can be edited.
|
- `editable` - determines whether the text can be edited.
|
||||||
|
- `line-numbers` - enables line numbers display.
|
||||||
- `error-color` - color when entering incorrect data (the text does not pass the validator check). Type: RGBA color.
|
- `error-color` - color when entering incorrect data (the text does not pass the validator check). Type: RGBA color.
|
||||||
|
- `text-color` - text color. Type: RGBA color.
|
||||||
- `validator` - lua function that checks text for correctness. Takes a string as input, returns true if the text is correct.
|
- `validator` - lua function that checks text for correctness. Takes a string as input, returns true if the text is correct.
|
||||||
- `onup` - lua function called when the up arrow is pressed.
|
- `onup` - lua function called when the up arrow is pressed.
|
||||||
- `ondown` - lua function called when the down arrow is pressed.
|
- `ondown` - lua function called when the down arrow is pressed.
|
||||||
|
|||||||
@ -40,6 +40,12 @@
|
|||||||
Целое число определяющее номер группы отрисовки данного блока.
|
Целое число определяющее номер группы отрисовки данного блока.
|
||||||
Актуально для полупрозрачных блоков - решает проблемы невидимых сторон блоков за этим блоком.
|
Актуально для полупрозрачных блоков - решает проблемы невидимых сторон блоков за этим блоком.
|
||||||
|
|
||||||
|
### Полупрозрачность - *translucent*
|
||||||
|
|
||||||
|
Включает поддержку полупрозрачности в текстурах блока (примеры: вода, лёд).
|
||||||
|
Следует использовать только при надобности, так как влияет на производительность.
|
||||||
|
Не требуется для полной прозрачности (трава, цветы).
|
||||||
|
|
||||||
### Вращение - *rotation*
|
### Вращение - *rotation*
|
||||||
|
|
||||||
Профиль вращения (набор положений, в которые можно установить блок) из списка:
|
Профиль вращения (набор положений, в которые можно установить блок) из списка:
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
| acceleration | Ускорение частиц. | {0, -16, 0} |
|
| acceleration | Ускорение частиц. | {0, -16, 0} |
|
||||||
| explosion | Сила разлёта частиц при спавне. | {2, 2, 2} |
|
| explosion | Сила разлёта частиц при спавне. | {2, 2, 2} |
|
||||||
| size | Размер частиц. | {0.1, 0.1, 0.1} |
|
| size | Размер частиц. | {0.1, 0.1, 0.1} |
|
||||||
|
| size_spread | Максимальное отклонение времени размера частиц. | 0.2 |
|
||||||
| spawn_shape | Форма области спавна частиц. (ball/sphere/box) | ball |
|
| spawn_shape | Форма области спавна частиц. (ball/sphere/box) | ball |
|
||||||
| spawn_spread | Размер области спавна частиц. | {0, 0, 0} |
|
| spawn_spread | Размер области спавна частиц. | {0, 0, 0} |
|
||||||
| random_sub_uv | Размер случайного подрегиона текстуры (1 - будет использована вся текстура). | 1.0 |
|
| random_sub_uv | Размер случайного подрегиона текстуры (1 - будет использована вся текстура). | 1.0 |
|
||||||
|
|||||||
@ -25,7 +25,9 @@ hud.open_block(x: int, y: int, z: int) -> int, str
|
|||||||
```lua
|
```lua
|
||||||
-- Показывает элемент в режиме оверлея.
|
-- Показывает элемент в режиме оверлея.
|
||||||
-- Также показывает инвентарь игрока, если playerinv - **true**.
|
-- Также показывает инвентарь игрока, если playerinv - **true**.
|
||||||
hud.show_overlay(layoutid: str, playerinv: bool)
|
-- Через args можно указать массив значений параметров, что будут переданы
|
||||||
|
-- в on_open показываемого оверлея.
|
||||||
|
hud.show_overlay(layoutid: str, playerinv: bool, [опционально] args: table)
|
||||||
|
|
||||||
-- Добавляет постоянный элемент на экран. Элемент не удаляется при
|
-- Добавляет постоянный элемент на экран. Элемент не удаляется при
|
||||||
-- закрытии инвентаря. Чтобы не перекрывать затенение в режиме
|
-- закрытии инвентаря. Чтобы не перекрывать затенение в режиме
|
||||||
|
|||||||
@ -56,6 +56,20 @@ player.set_noclip(bool)
|
|||||||
|
|
||||||
Геттер и сеттер noclip режима (выключенная коллизия игрока)
|
Геттер и сеттер noclip режима (выключенная коллизия игрока)
|
||||||
|
|
||||||
|
```lua
|
||||||
|
player.is_infinite_items() -> bool
|
||||||
|
player.set_infinite_items(bool)
|
||||||
|
```
|
||||||
|
|
||||||
|
Геттер и сеттер бесконечных предметов (не удаляются из инвентаря при использовании)
|
||||||
|
|
||||||
|
```lua
|
||||||
|
player.is_instant_destruction() -> bool
|
||||||
|
player.set_instant_destruction(bool)
|
||||||
|
```
|
||||||
|
|
||||||
|
Геттер и сеттер мнгновенного разрушения блоков при активации привязки `player.destroy`.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
||||||
player.get_spawnpoint(playerid: int) -> number, number, number
|
player.get_spawnpoint(playerid: int) -> number, number, number
|
||||||
|
|||||||
@ -27,4 +27,7 @@ utf8.upper(text: str) -> str
|
|||||||
|
|
||||||
-- Переводит строку в нижний регистр
|
-- Переводит строку в нижний регистр
|
||||||
utf8.lower(text: str) -> str
|
utf8.lower(text: str) -> str
|
||||||
|
|
||||||
|
-- Экранирует строку
|
||||||
|
utf8.escape(text: str) -> str
|
||||||
```
|
```
|
||||||
|
|||||||
@ -38,7 +38,13 @@ function on_random_update(x, y, z)
|
|||||||
function on_blocks_tick(tps: int)
|
function on_blocks_tick(tps: int)
|
||||||
```
|
```
|
||||||
|
|
||||||
Вызывается tps (20) раз в секунду
|
Вызывается tps (20) раз в секунду. Используйте 1/tps вместо `time.delta()`.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function on_player_tick(playerid: int, tps: int)
|
||||||
|
```
|
||||||
|
|
||||||
|
Вызывается tps (20) раз в секунду. Используйте 1/tps вместо `time.delta()`.
|
||||||
|
|
||||||
## События предметов
|
## События предметов
|
||||||
|
|
||||||
|
|||||||
@ -4,92 +4,109 @@
|
|||||||
|
|
||||||
## Расширения для table
|
## Расширения для table
|
||||||
|
|
||||||
Создаёт и возвращает копию переданной таблицы путём создания новой и копирования в неё всех элементов из переданной
|
|
||||||
```lua
|
```lua
|
||||||
function table.copy(t: table) -> table
|
table.copy(t: table) -> table
|
||||||
```
|
```
|
||||||
|
|
||||||
Возвращает количество пар в переданной таблице
|
Создаёт и возвращает копию переданной таблицы путём создания новой и копирования в неё всех элементов из переданной.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function table.count_pairs(t: table) -> integer
|
table.count_pairs(t: table) -> integer
|
||||||
```
|
```
|
||||||
|
|
||||||
Возвращает один элемент из переданной таблицы на случайной позиции
|
Возвращает количество пар в переданной таблице.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function table.random(t: table) -> object
|
table.random(t: table) -> object
|
||||||
```
|
```
|
||||||
|
|
||||||
Возвращает **true**, если **x** содержится в **t**
|
Возвращает один элемент из переданной таблицы на случайной позиции.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function table.has(t: table, x: object) -> bool
|
table.has(t: table, x: object) -> bool
|
||||||
```
|
```
|
||||||
|
|
||||||
Возвращает индекс обьекта **x** в **t**. Если переданный обьект не содержится в таблице, то функция вернёт значение **-1**
|
Возвращает **true**, если **x** содержится в **t**.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function table.index(t: table, x: object) -> integer
|
table.index(t: table, x: object) -> integer
|
||||||
```
|
```
|
||||||
|
|
||||||
Удаляет элемент **x** из **t**
|
Возвращает индекс обьекта **x** в **t**. Если переданный обьект не содержится в таблице, то функция вернёт значение **-1**.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function table.remove_value(t: table, x: object)
|
table.remove_value(t: table, x: object)
|
||||||
```
|
```
|
||||||
|
|
||||||
Конвертирует переданную таблицу в строку
|
Удаляет элемент **x** из **t**.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function table.tostring(t: table) -> string
|
table.tostring(t: table) -> string
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Конвертирует переданную таблицу в строку.
|
||||||
|
|
||||||
## Расширения для string
|
## Расширения для string
|
||||||
|
|
||||||
Разбивает строку **str** на части по указанному разделителю/выражению **separator** и возвращает результат ввиде таблицы из строк. Если **withpattern** равен **true**, то параметр **separator** будет определяться как регулярное выражение
|
|
||||||
```lua
|
```lua
|
||||||
function string.explode(separator: string, str: string, withpattern: bool) -> table[string]
|
string.explode(separator: string, str: string, withpattern: bool) -> table[string]
|
||||||
```
|
```
|
||||||
|
|
||||||
Разбивает строку **str** на части по указанному разделителю **delimiter** и возвращает результат ввиде таблицы из строк
|
Разбивает строку **str** на части по указанному разделителю/выражению **separator** и возвращает результат ввиде таблицы из строк. Если **withpattern** равен **true**, то параметр **separator** будет определяться как регулярное выражение.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function string.split(str: string, delimiter: string) -> table[string]
|
string.split(str: string, delimiter: string) -> table[string]
|
||||||
```
|
```
|
||||||
|
|
||||||
Экранирует специальные символы в строке, такие как `()[]+-.$%^?*` в формате `%символ`. Символ `NUL` (`\0`) будет преобразован в `%z`
|
Разбивает строку **str** на части по указанному разделителю **delimiter** и возвращает результат ввиде таблицы из строк.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function string.pattern_safe(str: string)
|
string.pattern_safe(str: string)
|
||||||
```
|
```
|
||||||
|
|
||||||
Разбивает секунды на часы, минуты и миллисекунды и форматирует в **format** с следующим порядком параметров: `минуты, секунды, миллисекунды` и после возвращает результат. Если **format** не указан, то возвращает таблицу, где: **h** - hours, **m** - minutes, **s** - seconds, **ms** - milliseconds
|
Экранирует специальные символы в строке, такие как `()[]+-.$%^?*` в формате `%символ`. Символ `NUL` (`\0`) будет преобразован в `%z`.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function string.formatted_time(seconds: number, format: string) -> string | table
|
string.formatted_time(seconds: number, format: string) -> string | table
|
||||||
```
|
```
|
||||||
|
|
||||||
Заменяет все подстроки в **str**, равные **tofind** на **toreplace** и возвращает строку со всеми измененными подстроками
|
Разбивает секунды на часы, минуты и миллисекунды и форматирует в **format** с следующим порядком параметров: `минуты, секунды, миллисекунды` и после возвращает результат. Если **format** не указан, то возвращает таблицу, где: **h** - hours, **m** - minutes, **s** - seconds, **ms** - milliseconds.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function string.replace(str: string, tofind: string, toreplace: string) -> string
|
string.replace(str: string, tofind: string, toreplace: string) -> string
|
||||||
|
```
|
||||||
|
|
||||||
|
Заменяет все подстроки в **str**, равные **tofind** на **toreplace** и возвращает строку со всеми измененными подстроками.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
string.trim(str: string, char: string) -> string
|
||||||
```
|
```
|
||||||
|
|
||||||
Удаляет все символы, равные **char** из строки **str** с левого и правого конца и возвращает результат. Если параметр **char** не определен, то будут выбраны все пустые символы.
|
Удаляет все символы, равные **char** из строки **str** с левого и правого конца и возвращает результат. Если параметр **char** не определен, то будут выбраны все пустые символы.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function string.trim(str: string, char: string) -> string
|
string.trim_left(str: string, char: string) -> string
|
||||||
```
|
```
|
||||||
|
|
||||||
Удаляет все символы, равные **char** из строки **str** с левого конца и возвращает результат. Если параметр **char** не определен, то будут выбраны все пустые символы.
|
Удаляет все символы, равные **char** из строки **str** с левого конца и возвращает результат. Если параметр **char** не определен, то будут выбраны все пустые символы.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function string.trim_left(str: string, char: string) -> string
|
string.trim_right(str: string, char: string) -> string
|
||||||
```
|
```
|
||||||
|
|
||||||
Удаляет все символы, равные **char** из строки **str** с правого конца и возвращает результат. Если параметр **char** не определен, то будут выбраны все пустые символы.
|
Удаляет все символы, равные **char** из строки **str** с правого конца и возвращает результат. Если параметр **char** не определен, то будут выбраны все пустые символы.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function string.trim_right(str: string, char: string) -> string
|
string.starts_with(str: string, start: string) -> bool
|
||||||
```
|
```
|
||||||
|
|
||||||
Возвращает **true**, если строка **str** начинается на подстроку **start**
|
Возвращает **true**, если строка **str** начинается на подстроку **start**
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function string.starts_with(str: string, start: string) -> bool
|
string.ends_with(str: string, endStr: string) -> bool
|
||||||
```
|
```
|
||||||
|
|
||||||
Возвращает **true**, если строка **str** заканчивается на подстроку **endStr**
|
Возвращает **true**, если строка **str** заканчивается на подстроку **endStr**
|
||||||
```lua
|
|
||||||
function string.ends_with(str: string, endStr: string) -> bool
|
|
||||||
```
|
|
||||||
|
|
||||||
Также важно подметить, что все выше перечисленные функции, расширяющие **string** можно использовать как мета-методы на экземплярах строк, т.е.:
|
Также важно подметить, что все выше перечисленные функции, расширяющие **string** можно использовать как мета-методы на экземплярах строк, т.е.:
|
||||||
|
|
||||||
@ -103,39 +120,51 @@ end
|
|||||||
|
|
||||||
Также функции `string.lower` и `string.upper` переопределены на `utf8.lower` и `utf8.upper`
|
Также функции `string.lower` и `string.upper` переопределены на `utf8.lower` и `utf8.upper`
|
||||||
|
|
||||||
|
```lua
|
||||||
|
string.escape(str: string) -> string
|
||||||
|
```
|
||||||
|
|
||||||
|
Экранирует строку. Является псевдонимом `utf8.escape`.
|
||||||
|
|
||||||
## Расширения для math
|
## Расширения для math
|
||||||
|
|
||||||
Ограничивает число **_in** по лимитам **low** и **high**. Т.е.: Если **_in** больше чем **high** - вернётся **high**, если **_in** меньше чем **low** - вернётся **low**. В противном случае вернётся само число
|
|
||||||
```lua
|
```lua
|
||||||
function math.clamp(_in, low, high)
|
math.clamp(_in, low, high)
|
||||||
```
|
```
|
||||||
|
|
||||||
Возвращает случайное дробное число в диапазоне от **low** до **high**
|
Ограничивает число **_in** по лимитам **low** и **high**. Т.е.: Если **_in** больше чем **high** - вернётся **high**, если **_in** меньше чем **low** - вернётся **low**. В противном случае вернётся само число.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function math.rand(low, high)
|
math.rand(low, high)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Возвращает случайное дробное число в диапазоне от **low** до **high**.
|
||||||
|
|
||||||
## Дополнительные глобальные функции
|
## Дополнительные глобальные функции
|
||||||
|
|
||||||
В этом же скрипте также определены и другие глобальные функции которые доступны для использования. Ниже их список
|
В этом же скрипте также определены и другие глобальные функции которые доступны для использования. Ниже их список
|
||||||
|
|
||||||
|
|
||||||
Возвращает **true**, если переданная таблица является массивом, тоесть если каждый ключ это целое число больше или равное единице и если каждый ключ следует за прошлым
|
|
||||||
```lua
|
```lua
|
||||||
function is_array(x: table) -> bool
|
is_array(x: table) -> bool
|
||||||
```
|
```
|
||||||
|
|
||||||
Разбивает путь на две части и возвращает их: входную точку и путь к файлу
|
Возвращает **true**, если переданная таблица является массивом, тоесть если каждый ключ это целое число больше или равное единице и если каждый ключ следует за прошлым.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function parse_path(path: string) -> string, string
|
function parse_path(path: string) -> string, string
|
||||||
```
|
```
|
||||||
|
|
||||||
Вызывает функцию **func** **iters** раз, передавая ей аргументы `...`, а после выводит в консоль время в микросекундах, которое прошло с момента вызова **timeit**
|
Разбивает путь на две части и возвращает их: входную точку и путь к файлу.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function timeit(iters: integer, func: func, ...)
|
function timeit(iters: integer, func: func, ...)
|
||||||
```
|
```
|
||||||
|
|
||||||
Вызывает остановку корутины до тех пор, пока не пройдёт количество секунд, указанное в **timesec**. Функция может быть использована только внутри корутины
|
Вызывает функцию **func** **iters** раз, передавая ей аргументы `...`, а после выводит в консоль время в микросекундах, которое прошло с момента вызова **timeit**.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
function sleep(timesec: number)
|
function sleep(timesec: number)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Вызывает остановку корутины до тех пор, пока не пройдёт количество секунд, указанное в **timesec**. Функция может быть использована только внутри корутины.
|
||||||
|
|||||||
@ -78,14 +78,18 @@ document["worlds-panel"]:clear()
|
|||||||
| caret | int | да | да | позиция каретки. `textbox.caret = -1` установит позицию в конец текста |
|
| caret | int | да | да | позиция каретки. `textbox.caret = -1` установит позицию в конец текста |
|
||||||
| editable | bool | да | да | изменяемость текста |
|
| editable | bool | да | да | изменяемость текста |
|
||||||
| multiline | bool | да | да | поддержка многострочности |
|
| multiline | bool | да | да | поддержка многострочности |
|
||||||
|
| lineNumbers | bool | да | да | отображение номеров строк |
|
||||||
| textWrap | bool | да | да | автоматический перенос текста (только при multiline: "true") |
|
| textWrap | bool | да | да | автоматический перенос текста (только при multiline: "true") |
|
||||||
| valid | bool | да | нет | является ли введенный текст корректным |
|
| valid | bool | да | нет | является ли введенный текст корректным |
|
||||||
|
| textColor | vec4 | да | да | цвет текста |
|
||||||
|
|
||||||
Методы:
|
Методы:
|
||||||
|
|
||||||
| Метод | Описание |
|
| Метод | Описание |
|
||||||
| ----------- | -------------------------------------------- |
|
| ------------------------- | -------------------------------------------- |
|
||||||
| paste(text) | вставляет указанный текст на позицию каретки |
|
| paste(text: str) | вставляет указанный текст на позицию каретки |
|
||||||
|
| lineAt(pos: int) -> int | определяет номер строки по позиции в тексте |
|
||||||
|
| linePos(line: int) -> int | определяет позицию начала строки в тексте |
|
||||||
|
|
||||||
## Ползунок (trackbar)
|
## Ползунок (trackbar)
|
||||||
|
|
||||||
|
|||||||
@ -59,7 +59,8 @@
|
|||||||
В число контейнеров также входят панели и кнопки.
|
В число контейнеров также входят панели и кнопки.
|
||||||
- `padding` - внутренний отступ элемента. Тип: 4D вектор.
|
- `padding` - внутренний отступ элемента. Тип: 4D вектор.
|
||||||
Порядок: `"left,top,right,bottom"`
|
Порядок: `"left,top,right,bottom"`
|
||||||
- `scrollable` - возможность скроллинга. Работает только у Panel. Тип: логический.
|
- `scrollable` - возможность скроллинга. Тип: логический.
|
||||||
|
- `scroll-step` - шаг скроллинга. Тип: целочисленный.
|
||||||
|
|
||||||
# Общие атрибуты панелей
|
# Общие атрибуты панелей
|
||||||
|
|
||||||
@ -105,7 +106,9 @@
|
|||||||
- `multiline` - разрешает отображение многострочного текста.
|
- `multiline` - разрешает отображение многострочного текста.
|
||||||
- `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true")
|
- `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true")
|
||||||
- `editable`- определяет возможность редактирования текста.
|
- `editable`- определяет возможность редактирования текста.
|
||||||
|
- `line-numbers` - включает отображение номеров строк.
|
||||||
- `error-color` - цвет при вводе некорректных данных (текст не проходит проверку валидатора). Тип: RGBA цвет.
|
- `error-color` - цвет при вводе некорректных данных (текст не проходит проверку валидатора). Тип: RGBA цвет.
|
||||||
|
- `text-color` - цвет текста. Тип: RGBA цвет.
|
||||||
- `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен.
|
- `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен.
|
||||||
- `onup` - lua функция вызываемая при нажатии стрелки вверх.
|
- `onup` - lua функция вызываемая при нажатии стрелки вверх.
|
||||||
- `ondown` - lua функция вызываемая при нажатии стрелки вниз.
|
- `ondown` - lua функция вызываемая при нажатии стрелки вниз.
|
||||||
|
|||||||
@ -3,5 +3,6 @@
|
|||||||
"material": "base:glass",
|
"material": "base:glass",
|
||||||
"draw-group": 2,
|
"draw-group": 2,
|
||||||
"light-passing": true,
|
"light-passing": true,
|
||||||
"sky-light-passing": true
|
"sky-light-passing": true,
|
||||||
|
"translucent": true
|
||||||
}
|
}
|
||||||
|
|||||||
7
res/content/base/blocks/ice.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"texture": "ice",
|
||||||
|
"material": "base:glass",
|
||||||
|
"draw-group": 4,
|
||||||
|
"light-passing": true,
|
||||||
|
"translucent": true
|
||||||
|
}
|
||||||
@ -6,5 +6,6 @@
|
|||||||
"sky-light-passing": false,
|
"sky-light-passing": false,
|
||||||
"obstacle": false,
|
"obstacle": false,
|
||||||
"selectable": false,
|
"selectable": false,
|
||||||
"replaceable": true
|
"replaceable": true,
|
||||||
|
"translucent": true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"items": [
|
"entities": [
|
||||||
"bazalt_breaker"
|
"drop",
|
||||||
|
"player",
|
||||||
|
"falling_block"
|
||||||
],
|
],
|
||||||
"blocks": [
|
"blocks": [
|
||||||
"dirt",
|
"dirt",
|
||||||
@ -27,11 +29,10 @@
|
|||||||
"lightbulb",
|
"lightbulb",
|
||||||
"torch",
|
"torch",
|
||||||
"wooden_door",
|
"wooden_door",
|
||||||
"coal_ore"
|
"coal_ore",
|
||||||
|
"ice"
|
||||||
],
|
],
|
||||||
"entities": [
|
"items": [
|
||||||
"drop",
|
"bazalt_breaker"
|
||||||
"player",
|
|
||||||
"falling_block"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -3,5 +3,5 @@
|
|||||||
"base:falling_block"
|
"base:falling_block"
|
||||||
],
|
],
|
||||||
"skeleton-name": "base:block",
|
"skeleton-name": "base:block",
|
||||||
"hitbox": [0.8, 0.8, 0.8]
|
"hitbox": [0.98, 0.98, 0.98]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
"atlases": [
|
||||||
|
{"name": "cracks", "type": "separate"}
|
||||||
|
],
|
||||||
"sounds": [
|
"sounds": [
|
||||||
"blocks/door_open",
|
"blocks/door_open",
|
||||||
"blocks/door_close",
|
"blocks/door_close",
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
bazalt breaker=крушитель базальта
|
||||||
bazalt=базальт
|
bazalt=базальт
|
||||||
blue lamp=синяя лампа
|
blue lamp=синяя лампа
|
||||||
brick=кирпич
|
brick=кирпич
|
||||||
@ -5,8 +6,8 @@ dirt=земля
|
|||||||
flower=цветок
|
flower=цветок
|
||||||
glass=стекло
|
glass=стекло
|
||||||
grass block=дёрн
|
grass block=дёрн
|
||||||
tall grass=высокая трава
|
|
||||||
green lamp=зелёная лампа
|
green lamp=зелёная лампа
|
||||||
|
ice=лёд
|
||||||
lamp=лампа
|
lamp=лампа
|
||||||
leaves=листва
|
leaves=листва
|
||||||
light bulb=лампочка
|
light bulb=лампочка
|
||||||
@ -18,8 +19,8 @@ red lamp=красная лампа
|
|||||||
rust=ржавчина
|
rust=ржавчина
|
||||||
sand=песок
|
sand=песок
|
||||||
stone=камень
|
stone=камень
|
||||||
|
tall grass=высокая трава
|
||||||
|
torch=факел
|
||||||
water=вода
|
water=вода
|
||||||
wood=бревно
|
wood=бревно
|
||||||
torch=факел
|
|
||||||
bazalt breaker=крушитель базальта
|
|
||||||
wooden door=деревянная дверь
|
wooden door=деревянная дверь
|
||||||
|
|||||||
BIN
res/content/base/textures/blocks/ice.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
res/content/base/textures/cracks/cracks_0.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
res/content/base/textures/cracks/cracks_1.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
res/content/base/textures/cracks/cracks_10.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
res/content/base/textures/cracks/cracks_2.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
res/content/base/textures/cracks/cracks_3.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
res/content/base/textures/cracks/cracks_4.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
res/content/base/textures/cracks/cracks_5.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
res/content/base/textures/cracks/cracks_6.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
res/content/base/textures/cracks/cracks_7.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
res/content/base/textures/cracks/cracks_8.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
res/content/base/textures/cracks/cracks_9.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
@ -1,5 +1,18 @@
|
|||||||
<container color='#00000080' size='400' size-func="unpack(gui.get_viewport())">
|
<container color='#00000080' size='400' size-func="unpack(gui.get_viewport())">
|
||||||
<container size-func="gui.get_viewport()[1],gui.get_viewport()[2]-40">
|
<panel interval="0"
|
||||||
|
orientation="horizontal"
|
||||||
|
color="#00000010"
|
||||||
|
size-func="gui.get_viewport()[1]-350,30">
|
||||||
|
<button id="s_chat" size="110,30" onclick="modes:set('chat')">@Chat</button>
|
||||||
|
<button id="s_console" size="110,30" onclick="modes:set('console')">@Console</button>
|
||||||
|
<button id="s_debug" size="110,30" onclick="modes:set('debug')">@Debug</button>
|
||||||
|
</panel>
|
||||||
|
<container pos="0,30" size-func="gui.get_viewport()[1]-350,30" color="#00000020">
|
||||||
|
<label id="title" pos="8,8"></label>
|
||||||
|
</container>
|
||||||
|
|
||||||
|
<container id="logContainer" pos="0,60"
|
||||||
|
size-func="unpack(vec2.add(gui.get_viewport(), {0,-100}))">
|
||||||
<textbox
|
<textbox
|
||||||
id='log'
|
id='log'
|
||||||
color='0'
|
color='0'
|
||||||
@ -11,12 +24,33 @@
|
|||||||
gravity="bottom-left"
|
gravity="bottom-left"
|
||||||
></textbox>
|
></textbox>
|
||||||
</container>
|
</container>
|
||||||
|
<container id="editorContainer" pos="0,60" color="#00000080"
|
||||||
|
size-func="unpack(vec2.add(gui.get_viewport(), {-350,-230}))">
|
||||||
|
<textbox
|
||||||
|
id='editor'
|
||||||
|
color='0'
|
||||||
|
autoresize='true'
|
||||||
|
margin='0'
|
||||||
|
padding='5'
|
||||||
|
editable='false'
|
||||||
|
multiline='true'
|
||||||
|
line-numbers='true'
|
||||||
|
text-color="#FFFFFFA0"
|
||||||
|
size-func="gui.get_viewport()[1]-350,40"
|
||||||
|
gravity="top-left"
|
||||||
|
text-wrap='false'
|
||||||
|
scroll-step='50'
|
||||||
|
></textbox>
|
||||||
|
</container>
|
||||||
|
<panel id="traceback" gravity="bottom-left" padding="4" color="#000000A0"
|
||||||
|
max-length="170" size-func="gui.get_viewport()[1]-350,170">
|
||||||
|
</panel>
|
||||||
<panel id="problemsLog"
|
<panel id="problemsLog"
|
||||||
color="#00000010"
|
color="#00000010"
|
||||||
position-func="gui.get_viewport()[1]-350,0"
|
position-func="gui.get_viewport()[1]-350,0"
|
||||||
size-func="351,gui.get_viewport()[2]-40"
|
size-func="350,gui.get_viewport()[2]-40"
|
||||||
padding="5,15,5,15">
|
padding="5,15,5,15">
|
||||||
<label>@Problems</label>
|
<label margin="0,0,0,5">@Problems</label>
|
||||||
</panel>
|
</panel>
|
||||||
<textbox id='prompt'
|
<textbox id='prompt'
|
||||||
consumer='submit'
|
consumer='submit'
|
||||||
|
|||||||
@ -1,21 +1,101 @@
|
|||||||
|
console_mode = "console"
|
||||||
|
|
||||||
history = session.get_entry("commands_history")
|
history = session.get_entry("commands_history")
|
||||||
history_pointer = #history
|
history_pointer = #history
|
||||||
|
|
||||||
local warnings_all = {}
|
local warnings_all = {}
|
||||||
|
local errors_all = {}
|
||||||
|
|
||||||
local warning_id = 0
|
local warning_id = 0
|
||||||
events.on("core:warning", function (wtype, text)
|
local error_id = 0
|
||||||
|
|
||||||
|
events.on("core:warning", function (wtype, text, traceback)
|
||||||
local full = wtype..": "..text
|
local full = wtype..": "..text
|
||||||
if table.has(warnings_all, full) then
|
if table.has(warnings_all, full) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
local encoded = base64.encode(bjson.tobytes({frames=traceback}))
|
||||||
document.problemsLog:add(gui.template("problem", {
|
document.problemsLog:add(gui.template("problem", {
|
||||||
type="warning", text=full, id=tostring(warning_id)
|
type="warning",
|
||||||
|
text=full,
|
||||||
|
traceback=encoded,
|
||||||
|
id=tostring(warning_id)
|
||||||
}))
|
}))
|
||||||
warning_id = warning_id + 1
|
warning_id = warning_id + 1
|
||||||
table.insert(warnings_all, full)
|
table.insert(warnings_all, full)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
events.on("core:error", function (msg, traceback)
|
||||||
|
local _, endindex = string.find(msg, ": ")
|
||||||
|
local full = ""
|
||||||
|
for i,frame in ipairs(traceback) do
|
||||||
|
full = full..frame.source..tostring(frame.currentline)
|
||||||
|
end
|
||||||
|
if table.has(errors_all, full) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local encoded = base64.encode(bjson.tobytes({frames=traceback}))
|
||||||
|
document.problemsLog:add(gui.template("problem", {
|
||||||
|
type="error",
|
||||||
|
text=msg:sub(endindex),
|
||||||
|
traceback=encoded,
|
||||||
|
id=tostring(error_id)
|
||||||
|
}))
|
||||||
|
error_id = error_id + 1
|
||||||
|
table.insert(errors_all, full)
|
||||||
|
end)
|
||||||
|
|
||||||
|
events.on("core:open_traceback", function(traceback_b64)
|
||||||
|
local traceback = bjson.frombytes(base64.decode(traceback_b64))
|
||||||
|
modes:set('debug')
|
||||||
|
|
||||||
|
local tb_list = document.traceback
|
||||||
|
local srcsize = tb_list.size
|
||||||
|
tb_list:clear()
|
||||||
|
tb_list:add("<label enabled='false' margin='2'>@devtools.traceback</label>")
|
||||||
|
for _, frame in ipairs(traceback.frames) do
|
||||||
|
local callback = ""
|
||||||
|
local framestr = ""
|
||||||
|
if frame.what == "C" then
|
||||||
|
framestr = "C/C++ "
|
||||||
|
else
|
||||||
|
framestr = frame.source..":"..tostring(frame.currentline).." "
|
||||||
|
if file.exists(frame.source) then
|
||||||
|
callback = string.format(
|
||||||
|
"local editor = document.editor "..
|
||||||
|
"local source = file.read('%s'):gsub('\t', ' ') "..
|
||||||
|
"editor.text = source "..
|
||||||
|
"editor.focused = true "..
|
||||||
|
"time.post_runnable(function()"..
|
||||||
|
"editor.caret = editor:linePos(%s) "..
|
||||||
|
"end)",
|
||||||
|
frame.source, frame.currentline-1
|
||||||
|
)
|
||||||
|
else
|
||||||
|
callback = "document.editor.text = 'Could not open source file'"
|
||||||
|
end
|
||||||
|
callback = string.format(
|
||||||
|
"%s document.title.text = gui.str('File')..' - %s'",
|
||||||
|
callback,
|
||||||
|
frame.source
|
||||||
|
)
|
||||||
|
end
|
||||||
|
if frame.name then
|
||||||
|
framestr = framestr.."("..tostring(frame.name)..")"
|
||||||
|
end
|
||||||
|
local color = "#FFFFFF"
|
||||||
|
if frame.source:starts_with("core:") then
|
||||||
|
color = "#C0D0C5"
|
||||||
|
end
|
||||||
|
tb_list:add(gui.template("stack_frame", {
|
||||||
|
location=framestr,
|
||||||
|
color=color,
|
||||||
|
callback=callback
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
tb_list.size = srcsize
|
||||||
|
end)
|
||||||
|
|
||||||
function setup_variables()
|
function setup_variables()
|
||||||
local pid = hud.get_player()
|
local pid = hud.get_player()
|
||||||
local x,y,z = player.get_pos(pid)
|
local x,y,z = player.get_pos(pid)
|
||||||
@ -56,10 +136,19 @@ function add_to_history(text)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function submit(text)
|
function submit(text)
|
||||||
|
text = text:trim()
|
||||||
add_to_history(text)
|
add_to_history(text)
|
||||||
|
|
||||||
|
if console_mode == "chat" then
|
||||||
|
if not text:starts_with("/") then
|
||||||
|
text = "chat "..string.escape(text)
|
||||||
|
else
|
||||||
|
text = text:sub(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
setup_variables()
|
setup_variables()
|
||||||
|
|
||||||
text = text:trim()
|
|
||||||
local name
|
local name
|
||||||
for s in text:gmatch("%S+") do
|
for s in text:gmatch("%S+") do
|
||||||
name = s
|
name = s
|
||||||
@ -84,6 +173,37 @@ function submit(text)
|
|||||||
document.prompt.focused = true
|
document.prompt.focused = true
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_open()
|
function set_mode(mode)
|
||||||
document.prompt.focused = true
|
local show_prompt = mode == 'chat' or mode == 'console'
|
||||||
|
|
||||||
|
document.title.text = ""
|
||||||
|
document.editorContainer.visible = mode == 'debug'
|
||||||
|
document.logContainer.visible = mode ~= 'debug'
|
||||||
|
|
||||||
|
if mode == 'debug' then
|
||||||
|
document.root.color = {16, 18, 20, 220}
|
||||||
|
else
|
||||||
|
document.root.color = {0, 0, 0, 128}
|
||||||
|
end
|
||||||
|
|
||||||
|
document.traceback.visible = mode == 'debug'
|
||||||
|
document.prompt.visible = show_prompt
|
||||||
|
if show_prompt then
|
||||||
|
document.prompt.focused = true
|
||||||
|
end
|
||||||
|
console_mode = mode
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_open(mode)
|
||||||
|
if modes == nil then
|
||||||
|
modes = RadioGroup({
|
||||||
|
chat=document.s_chat,
|
||||||
|
console=document.s_console,
|
||||||
|
debug=document.s_debug
|
||||||
|
}, function (mode)
|
||||||
|
set_mode(mode)
|
||||||
|
end, mode or "console")
|
||||||
|
elseif mode then
|
||||||
|
modes:set(mode)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
<panel size='400' color='0' interval='1' context='menu'>
|
<panel size='400' color='0' interval='1' context='menu'>
|
||||||
<button onclick='menu.page="new_world"'>@New World</button>
|
<button onclick='menu.page="worlds"'>@Worlds</button>
|
||||||
<panel id='worlds' size='390,1' padding='5' color='#FFFFFF11' max-length='400'>
|
|
||||||
</panel>
|
|
||||||
<button onclick='menu.page="settings"'>@Settings</button>
|
<button onclick='menu.page="settings"'>@Settings</button>
|
||||||
<button onclick='menu.page="content_menu"'>@Contents Menu</button>
|
<button onclick='menu.page="content_menu"'>@Contents Menu</button>
|
||||||
<button onclick='core.quit()'>@Quit</button>
|
<button onclick='core.quit()'>@Quit</button>
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
<container size='668,418' color='#0F1E2DB2' context='menu'>
|
<container size='668,418' color='#0F1E2DB2' context='menu'>
|
||||||
<panel pos='6' size='250' color='0' interval='1'>
|
<panel pos='6' size='250' color='0' interval='1'>
|
||||||
<button id='s_aud' onclick='set_page("s_aud", "settings_audio")'>@Audio</button>
|
<button id='s_aud' onclick='sections:set("audio")'>@Audio</button>
|
||||||
<button id='s_dsp' onclick='set_page("s_dsp", "settings_display")'>@Display</button>
|
<button id='s_dsp' onclick='sections:set("display")'>@Display</button>
|
||||||
<button id='s_gfx' onclick='set_page("s_gfx", "settings_graphics")'>@Graphics</button>
|
<button id='s_gfx' onclick='sections:set("graphics")'>@Graphics</button>
|
||||||
<button id='s_ctl' onclick='set_page("s_ctl", "settings_controls")'>@Controls</button>
|
<button id='s_ctl' onclick='sections:set("controls")'>@Controls</button>
|
||||||
</panel>
|
</panel>
|
||||||
<pagebox id='menu' pos='260,6' size='400'>
|
<pagebox id='menu' pos='260,6' size='400'>
|
||||||
</pagebox>
|
</pagebox>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
<panel margin='6' gravity='bottom-left' size='250' color='0' interval='1'>
|
<panel margin='6' gravity='bottom-left' size='250' color='0' interval='1'>
|
||||||
<button onclick='menu.page="languages"' id='langs_btn'>-</button>
|
<button onclick='menu.page="languages"' id='langs_btn'>-</button>
|
||||||
<button onclick='core.open_folder("user:")'>@Open data folder</button>
|
<button onclick='core.open_folder("user:")'>@Open data folder</button>
|
||||||
<button id='s_rst' onclick='set_page("s_rst", "settings_reset")'>@Reset settings</button>
|
<button id='s_rst' onclick='sections:set("reset")'>@Reset settings</button>
|
||||||
<button onclick='menu:back()'>@Back</button>
|
<button onclick='menu:back()'>@Back</button>
|
||||||
</panel>
|
</panel>
|
||||||
</container>
|
</container>
|
||||||
|
|||||||
@ -3,15 +3,13 @@ function on_open()
|
|||||||
"%s: %s", gui.str("Language", "settings"),
|
"%s: %s", gui.str("Language", "settings"),
|
||||||
gui.get_locales_info()[core.get_setting("ui.language")].name
|
gui.get_locales_info()[core.get_setting("ui.language")].name
|
||||||
)
|
)
|
||||||
set_page("s_gfx", "settings_graphics")
|
sections = RadioGroup({
|
||||||
end
|
audio=document.s_aud,
|
||||||
|
display=document.s_dsp,
|
||||||
function set_page(btn, page)
|
graphics=document.s_gfx,
|
||||||
document.s_aud.enabled = true
|
controls=document.s_ctl,
|
||||||
document.s_dsp.enabled = true
|
reset=document.s_rst
|
||||||
document.s_gfx.enabled = true
|
}, function (page)
|
||||||
document.s_ctl.enabled = true
|
document.menu.page = "settings_"..page
|
||||||
document.s_rst.enabled = true
|
end, "graphics")
|
||||||
document[btn].enabled = false
|
|
||||||
document.menu.page = page
|
|
||||||
end
|
end
|
||||||
|
|||||||
9
res/layouts/pages/worlds.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<panel size='400' color='0' interval='1' context='menu'>
|
||||||
|
|
||||||
|
<button onclick='menu.page="new_world"'>@New World</button>
|
||||||
|
|
||||||
|
<panel id='worlds' size='390,1' padding='5' color='#FFFFFF11' max-length='400'>
|
||||||
|
</panel>
|
||||||
|
<button pos='485,525' size='500,40' onclick='core.open_folder("user:worlds")'>@Open worlds folder</button>
|
||||||
|
<button onclick='menu:back()'>@Back</button>
|
||||||
|
</panel>
|
||||||
@ -1,6 +1,9 @@
|
|||||||
<container id="%{id}" size="32" tooltip="%{text}">
|
<container id="%{id}" size="32" tooltip="%{text}"
|
||||||
|
onclick="events.emit('core:open_traceback', '%{traceback}')">
|
||||||
<image src="gui/%{type}" size="32"/>
|
<image src="gui/%{type}" size="32"/>
|
||||||
<label pos="36,2">%{text}</label>
|
<container pos="36,2" size="280,32" interactive="false">
|
||||||
|
<label>%{text}</label>
|
||||||
|
</container>
|
||||||
<image src="gui/cross" interactive="true" size="16" gravity="top-right"
|
<image src="gui/cross" interactive="true" size="16" gravity="top-right"
|
||||||
onclick="document['%{id}']:destruct()"></image>
|
onclick="document['%{id}']:destruct()"></image>
|
||||||
</container>
|
</container>
|
||||||
|
|||||||
3
res/layouts/templates/stack_frame.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<label hover-color="#A0A0FF" interactive="true" onclick="%{callback}" color="%{color}">
|
||||||
|
%{location}
|
||||||
|
</label>
|
||||||
@ -256,6 +256,14 @@ console.add_command(
|
|||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
|
console.add_command(
|
||||||
|
"chat text:str",
|
||||||
|
"Send chat message",
|
||||||
|
function (args, kwargs)
|
||||||
|
console.log("[you] "..args[1])
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
console.cheats = {
|
console.cheats = {
|
||||||
"blocks.fill",
|
"blocks.fill",
|
||||||
"tp",
|
"tp",
|
||||||
|
|||||||
@ -84,6 +84,32 @@ function Document.new(docname)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local _RadioGroup = {}
|
||||||
|
function _RadioGroup.set(self, key)
|
||||||
|
if type(self) ~= 'table' then
|
||||||
|
error("called as non-OOP via '.', use radiogroup:set")
|
||||||
|
end
|
||||||
|
if self.current then
|
||||||
|
self.elements[self.current].enabled = true
|
||||||
|
end
|
||||||
|
self.elements[key].enabled = false
|
||||||
|
self.current = key
|
||||||
|
if self.callback then
|
||||||
|
self.callback(key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function _RadioGroup.__call(self, elements, onset, default)
|
||||||
|
local group = setmetatable({
|
||||||
|
elements=elements,
|
||||||
|
callback=onset,
|
||||||
|
current=nil
|
||||||
|
}, {__index=_RadioGroup})
|
||||||
|
group:set(default)
|
||||||
|
return group
|
||||||
|
end
|
||||||
|
setmetatable(_RadioGroup, _RadioGroup)
|
||||||
|
RadioGroup = _RadioGroup
|
||||||
|
|
||||||
_GUI_ROOT = Document.new("core:root")
|
_GUI_ROOT = Document.new("core:root")
|
||||||
_MENU = _GUI_ROOT.menu
|
_MENU = _GUI_ROOT.menu
|
||||||
menu = _MENU
|
menu = _MENU
|
||||||
@ -243,7 +269,7 @@ function _rules.clear()
|
|||||||
_rules.create("allow-cheats", true)
|
_rules.create("allow-cheats", true)
|
||||||
end
|
end
|
||||||
|
|
||||||
function __vc_create_hud_rules()
|
function __vc_on_hud_open()
|
||||||
_rules.create("allow-content-access", hud._is_content_access(), function(value)
|
_rules.create("allow-content-access", hud._is_content_access(), function(value)
|
||||||
hud._set_content_access(value)
|
hud._set_content_access(value)
|
||||||
input.set_enabled("player.pick", value)
|
input.set_enabled("player.pick", value)
|
||||||
@ -269,6 +295,14 @@ function __vc_create_hud_rules()
|
|||||||
_rules.create("allow-debug-cheats", true, function(value)
|
_rules.create("allow-debug-cheats", true, function(value)
|
||||||
hud._set_debug_cheats(value)
|
hud._set_debug_cheats(value)
|
||||||
end)
|
end)
|
||||||
|
input.add_callback("devtools.console", function()
|
||||||
|
if hud.is_paused() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
time.post_runnable(function()
|
||||||
|
hud.show_overlay("core:console", false, {"console"})
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local RULES_FILE = "world:rules.toml"
|
local RULES_FILE = "world:rules.toml"
|
||||||
|
|||||||
@ -162,6 +162,7 @@ end
|
|||||||
|
|
||||||
string.lower = utf8.lower
|
string.lower = utf8.lower
|
||||||
string.upper = utf8.upper
|
string.upper = utf8.upper
|
||||||
|
string.escape = utf8.escape
|
||||||
|
|
||||||
local meta = getmetatable("")
|
local meta = getmetatable("")
|
||||||
|
|
||||||
@ -227,8 +228,22 @@ function file.readlines(path)
|
|||||||
return lines
|
return lines
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function debug.get_traceback(start)
|
||||||
|
local frames = {}
|
||||||
|
local n = 2 + (start or 0)
|
||||||
|
while true do
|
||||||
|
local info = debug.getinfo(n)
|
||||||
|
if info then
|
||||||
|
table.insert(frames, info)
|
||||||
|
else
|
||||||
|
return frames
|
||||||
|
end
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
package = {
|
package = {
|
||||||
loaded={}
|
loaded = {}
|
||||||
}
|
}
|
||||||
local __cached_scripts = {}
|
local __cached_scripts = {}
|
||||||
local __warnings_hidden = {}
|
local __warnings_hidden = {}
|
||||||
@ -238,7 +253,7 @@ function on_deprecated_call(name, alternatives)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
__warnings_hidden[name] = true
|
__warnings_hidden[name] = true
|
||||||
events.emit("core:warning", "deprecated call", name)
|
events.emit("core:warning", "deprecated call", name, debug.get_traceback(2))
|
||||||
if alternatives then
|
if alternatives then
|
||||||
debug.warning("deprecated function called ("..name.."), use "..
|
debug.warning("deprecated function called ("..name.."), use "..
|
||||||
alternatives.." instead\n"..debug.traceback())
|
alternatives.." instead\n"..debug.traceback())
|
||||||
@ -277,6 +292,10 @@ function __load_script(path, nocache)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function require(path)
|
function require(path)
|
||||||
|
if not string.find(path, ':') then
|
||||||
|
local prefix, _ = parse_path(debug.getinfo(2).source)
|
||||||
|
return require(prefix..':'..path)
|
||||||
|
end
|
||||||
local prefix, file = parse_path(path)
|
local prefix, file = parse_path(path)
|
||||||
return __load_script(prefix..":modules/"..file..".lua")
|
return __load_script(prefix..":modules/"..file..".lua")
|
||||||
end
|
end
|
||||||
@ -292,3 +311,17 @@ function __scripts_cleanup()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function __vc__error(msg, frame)
|
||||||
|
if events then
|
||||||
|
events.emit("core:error", msg, debug.get_traceback(1))
|
||||||
|
end
|
||||||
|
return debug.traceback(msg, frame)
|
||||||
|
end
|
||||||
|
|
||||||
|
function __vc_warning(msg, detail, n)
|
||||||
|
if events then
|
||||||
|
events.emit(
|
||||||
|
"core:warning", msg, detail, debug.get_traceback(1 + (n or 0)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
@ -9,6 +9,7 @@ uniform samplerCube u_cubemap;
|
|||||||
uniform vec3 u_fogColor;
|
uniform vec3 u_fogColor;
|
||||||
uniform float u_fogFactor;
|
uniform float u_fogFactor;
|
||||||
uniform float u_fogCurve;
|
uniform float u_fogCurve;
|
||||||
|
uniform bool u_alphaClip;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 fogColor = texture(u_cubemap, a_dir).rgb;
|
vec3 fogColor = texture(u_cubemap, a_dir).rgb;
|
||||||
@ -16,7 +17,7 @@ void main() {
|
|||||||
float depth = (a_distance/256.0);
|
float depth = (a_distance/256.0);
|
||||||
float alpha = a_color.a * tex_color.a;
|
float alpha = a_color.a * tex_color.a;
|
||||||
// anyway it's any alpha-test alternative required
|
// anyway it's any alpha-test alternative required
|
||||||
if (alpha < 0.5f)
|
if (alpha < (u_alphaClip ? 0.5f : 0.2f))
|
||||||
discard;
|
discard;
|
||||||
f_color = mix(a_color * tex_color, vec4(fogColor,1.0),
|
f_color = mix(a_color * tex_color, vec4(fogColor,1.0),
|
||||||
min(1.0, pow(depth*u_fogFactor, u_fogCurve)));
|
min(1.0, pow(depth*u_fogFactor, u_fogCurve)));
|
||||||
|
|||||||
@ -9,14 +9,14 @@ uniform samplerCube u_cubemap;
|
|||||||
uniform vec3 u_fogColor;
|
uniform vec3 u_fogColor;
|
||||||
uniform float u_fogFactor;
|
uniform float u_fogFactor;
|
||||||
uniform float u_fogCurve;
|
uniform float u_fogCurve;
|
||||||
|
uniform bool u_alphaClip;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 fogColor = texture(u_cubemap, a_dir).rgb;
|
vec3 fogColor = texture(u_cubemap, a_dir).rgb;
|
||||||
vec4 tex_color = texture(u_texture0, a_texCoord);
|
vec4 tex_color = texture(u_texture0, a_texCoord);
|
||||||
float depth = (a_distance/256.0);
|
float depth = (a_distance/256.0);
|
||||||
float alpha = a_color.a * tex_color.a;
|
float alpha = a_color.a * tex_color.a;
|
||||||
// anyway it's any alpha-test alternative required
|
if (u_alphaClip && alpha < 0.9f)
|
||||||
if (alpha < 0.3f)
|
|
||||||
discard;
|
discard;
|
||||||
f_color = mix(a_color * tex_color, vec4(fogColor,1.0),
|
f_color = mix(a_color * tex_color, vec4(fogColor,1.0),
|
||||||
min(1.0, pow(depth*u_fogFactor, u_fogCurve)));
|
min(1.0, pow(depth*u_fogFactor, u_fogCurve)));
|
||||||
|
|||||||
@ -33,6 +33,8 @@ menu.Display=Дысплей
|
|||||||
menu.Graphics=Графіка
|
menu.Graphics=Графіка
|
||||||
menu.missing-content=Адсутнічае Кантэнт!
|
menu.missing-content=Адсутнічае Кантэнт!
|
||||||
menu.New World=Новы Свет
|
menu.New World=Новы Свет
|
||||||
|
menu.Open worlds folder=Адкрыць папку з сусветамі
|
||||||
|
menu.Worlds=Сусветы
|
||||||
menu.Page not found=Старонка не знойдзена
|
menu.Page not found=Старонка не знойдзена
|
||||||
menu.Quit=Выхад
|
menu.Quit=Выхад
|
||||||
menu.Save and Quit to Menu=Захаваць і Выйсці ў Меню
|
menu.Save and Quit to Menu=Захаваць і Выйсці ў Меню
|
||||||
|
|||||||
@ -23,6 +23,8 @@ menu.Controls=Kontrolle
|
|||||||
menu.Create World=Erschaffe eine Welt
|
menu.Create World=Erschaffe eine Welt
|
||||||
menu.missing-content=Fehlender Inhalt!
|
menu.missing-content=Fehlender Inhalt!
|
||||||
menu.New World=Neue Welt
|
menu.New World=Neue Welt
|
||||||
|
menu.Worlds=Welten
|
||||||
|
menu.Open worlds folder=Weltenordner öffnen
|
||||||
menu.Quit=Ausfahrt
|
menu.Quit=Ausfahrt
|
||||||
menu.Save and Quit to Menu=Speichern und zum Menü zurückkehren
|
menu.Save and Quit to Menu=Speichern und zum Menü zurückkehren
|
||||||
menu.Settings=Einstellungen
|
menu.Settings=Einstellungen
|
||||||
|
|||||||
@ -11,6 +11,8 @@ world.delete-confirm=Do you want to delete world forever?
|
|||||||
world.generators.default=Default
|
world.generators.default=Default
|
||||||
world.generators.flat=Flat
|
world.generators.flat=Flat
|
||||||
|
|
||||||
|
devtools.traceback=Traceback (most recent call first)
|
||||||
|
|
||||||
# Tooltips
|
# Tooltips
|
||||||
graphics.gamma.tooltip=Lighting brightness curve
|
graphics.gamma.tooltip=Lighting brightness curve
|
||||||
graphics.backlight.tooltip=Backlight to prevent total darkness
|
graphics.backlight.tooltip=Backlight to prevent total darkness
|
||||||
|
|||||||
@ -23,6 +23,8 @@ menu.Controls=Ohjaus
|
|||||||
menu.Graphics=Grafiikka
|
menu.Graphics=Grafiikka
|
||||||
menu.missing-content=Sisältö Puuttuu!
|
menu.missing-content=Sisältö Puuttuu!
|
||||||
menu.New World=Uusi Maailma
|
menu.New World=Uusi Maailma
|
||||||
|
menu.Worlds=Maailmat
|
||||||
|
menu.Open worlds folder=Avaa maailmat-kansio
|
||||||
menu.Page not found=Sivu ei löytynyt!
|
menu.Page not found=Sivu ei löytynyt!
|
||||||
menu.Quit=Poistu
|
menu.Quit=Poistu
|
||||||
menu.Save and Quit to Menu=Tallenna ja Takaisin Valikkoon
|
menu.Save and Quit to Menu=Tallenna ja Takaisin Valikkoon
|
||||||
|
|||||||
@ -21,6 +21,8 @@ menu.Graphics=Grafika
|
|||||||
menu.Create World=Stwórz świat
|
menu.Create World=Stwórz świat
|
||||||
menu.missing-content=Brakująca treść!
|
menu.missing-content=Brakująca treść!
|
||||||
menu.New World=Nowy Świat
|
menu.New World=Nowy Świat
|
||||||
|
menu.Worlds=Światy
|
||||||
|
menu.Open worlds folder=Otwórz folder światów
|
||||||
menu.Page not found=Strona nie znaleziona
|
menu.Page not found=Strona nie znaleziona
|
||||||
menu.Quit=Wyjście
|
menu.Quit=Wyjście
|
||||||
menu.Save and Quit to Menu=Zapisz i wyjdź do menu
|
menu.Save and Quit to Menu=Zapisz i wyjdź do menu
|
||||||
|
|||||||
@ -12,7 +12,15 @@ Dependencies=Зависимости
|
|||||||
Description=Описание
|
Description=Описание
|
||||||
Converting world...=Выполняется конвертация мира...
|
Converting world...=Выполняется конвертация мира...
|
||||||
Unlimited=Неограниченно
|
Unlimited=Неограниченно
|
||||||
|
Chat=Чат
|
||||||
|
Console=Консоль
|
||||||
|
Log=Лог
|
||||||
|
Problems=Проблемы
|
||||||
|
Monitor=Мониторинг
|
||||||
|
Debug=Отладка
|
||||||
|
File=Файл
|
||||||
|
|
||||||
|
devtools.traceback=Стек вызовов (от последнего)
|
||||||
error.pack-not-found=Не удалось найти пакет
|
error.pack-not-found=Не удалось найти пакет
|
||||||
error.dependency-not-found=Используемая зависимость не найдена
|
error.dependency-not-found=Используемая зависимость не найдена
|
||||||
pack.remove-confirm=Удалить весь поставляемый паком/паками контент из мира (безвозвратно)?
|
pack.remove-confirm=Удалить весь поставляемый паком/паками контент из мира (безвозвратно)?
|
||||||
@ -33,6 +41,8 @@ menu.Display=Дисплей
|
|||||||
menu.Graphics=Графика
|
menu.Graphics=Графика
|
||||||
menu.missing-content=Отсутствует Контент!
|
menu.missing-content=Отсутствует Контент!
|
||||||
menu.New World=Новый Мир
|
menu.New World=Новый Мир
|
||||||
|
menu.Open worlds folder=Открыть папку с мирами
|
||||||
|
menu.Worlds=Миры
|
||||||
menu.Page not found=Страница не найдена
|
menu.Page not found=Страница не найдена
|
||||||
menu.Quit=Выход
|
menu.Quit=Выход
|
||||||
menu.Save and Quit to Menu=Сохранить и Выйти в Меню
|
menu.Save and Quit to Menu=Сохранить и Выйти в Меню
|
||||||
|
|||||||
@ -21,6 +21,8 @@ menu.Controls=Керування
|
|||||||
menu.Graphics=Графіка
|
menu.Graphics=Графіка
|
||||||
menu.missing-content=Відсутній Контент!
|
menu.missing-content=Відсутній Контент!
|
||||||
menu.New World=Новий Світ
|
menu.New World=Новий Світ
|
||||||
|
menu.Open worlds folder=Відкрити папку зі світами
|
||||||
|
menu.Worlds=Світи
|
||||||
menu.Page not found=Сторінка не знайдена
|
menu.Page not found=Сторінка не знайдена
|
||||||
menu.Quit=Вихід
|
menu.Quit=Вихід
|
||||||
menu.Save and Quit to Menu=Зберегти і Вийти в Меню
|
menu.Save and Quit to Menu=Зберегти і Вийти в Меню
|
||||||
|
|||||||
@ -33,6 +33,8 @@ menu.Display=Displey
|
|||||||
menu.Graphics=Grafika
|
menu.Graphics=Grafika
|
||||||
menu.missing-content=Kontent etishmayapti!
|
menu.missing-content=Kontent etishmayapti!
|
||||||
menu.New World=Yangi Dunyo
|
menu.New World=Yangi Dunyo
|
||||||
|
menu.Worlds=Dunyo
|
||||||
|
menu.Open worlds folder=Dunyo papkasini ochish
|
||||||
menu.Page not found=Sahifa topilmadi
|
menu.Page not found=Sahifa topilmadi
|
||||||
menu.Quit=Chiqish
|
menu.Quit=Chiqish
|
||||||
menu.Save and Quit to Menu=Saqlash va menyuga chiqish
|
menu.Save and Quit to Menu=Saqlash va menyuga chiqish
|
||||||
|
|||||||
@ -43,7 +43,11 @@ void AssetsLoader::add(
|
|||||||
const std::string& alias,
|
const std::string& alias,
|
||||||
std::shared_ptr<AssetCfg> settings
|
std::shared_ptr<AssetCfg> settings
|
||||||
) {
|
) {
|
||||||
|
if (enqueued.find({tag, alias}) != enqueued.end()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
entries.push(aloader_entry {tag, filename, alias, std::move(settings)});
|
entries.push(aloader_entry {tag, filename, alias, std::move(settings)});
|
||||||
|
enqueued.insert({tag, alias});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AssetsLoader::hasNext() const {
|
bool AssetsLoader::hasNext() const {
|
||||||
@ -148,6 +152,16 @@ void AssetsLoader::processPreload(
|
|||||||
std::make_shared<SoundCfg>(map.at("keep-pcm").get(keepPCM)));
|
std::make_shared<SoundCfg>(map.at("keep-pcm").get(keepPCM)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case AssetType::ATLAS: {
|
||||||
|
std::string typeName = "atlas";
|
||||||
|
map.at("type").get(typeName);
|
||||||
|
auto type = AtlasType::ATLAS;
|
||||||
|
if (typeName == "separate") {
|
||||||
|
type = AtlasType::SEPARATE;
|
||||||
|
}
|
||||||
|
add(tag, path, name, std::make_shared<AtlasCfg>(type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
add(tag, path, name);
|
add(tag, path, name);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <set>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -37,6 +38,17 @@ struct SoundCfg : AssetCfg {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class AtlasType {
|
||||||
|
ATLAS, SEPARATE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AtlasCfg : AssetCfg {
|
||||||
|
AtlasType type;
|
||||||
|
|
||||||
|
AtlasCfg(AtlasType type) : type(type) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
using aloader_func = std::function<
|
using aloader_func = std::function<
|
||||||
assetload::
|
assetload::
|
||||||
postfunc(AssetsLoader*, const ResPaths*, const std::string&, const std::string&, std::shared_ptr<AssetCfg>)>;
|
postfunc(AssetsLoader*, const ResPaths*, const std::string&, const std::string&, std::shared_ptr<AssetCfg>)>;
|
||||||
@ -52,6 +64,7 @@ class AssetsLoader {
|
|||||||
Assets* assets;
|
Assets* assets;
|
||||||
std::map<AssetType, aloader_func> loaders;
|
std::map<AssetType, aloader_func> loaders;
|
||||||
std::queue<aloader_entry> entries;
|
std::queue<aloader_entry> entries;
|
||||||
|
std::set<std::pair<AssetType, std::string>> enqueued;
|
||||||
const ResPaths* paths;
|
const ResPaths* paths;
|
||||||
|
|
||||||
void tryAddSound(const std::string& name);
|
void tryAddSound(const std::string& name);
|
||||||
|
|||||||
@ -103,12 +103,25 @@ static bool append_atlas(AtlasBuilder& atlas, const fs::path& file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assetload::postfunc assetload::atlas(
|
assetload::postfunc assetload::atlas(
|
||||||
AssetsLoader*,
|
AssetsLoader* loader,
|
||||||
const ResPaths* paths,
|
const ResPaths* paths,
|
||||||
const std::string& directory,
|
const std::string& directory,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
const std::shared_ptr<AssetCfg>&
|
const std::shared_ptr<AssetCfg>& config
|
||||||
) {
|
) {
|
||||||
|
auto atlasConfig = std::dynamic_pointer_cast<AtlasCfg>(config);
|
||||||
|
if (atlasConfig && atlasConfig->type == AtlasType::SEPARATE) {
|
||||||
|
for (const auto& file : paths->listdir(directory)) {
|
||||||
|
if (!imageio::is_read_supported(file.extension().u8string()))
|
||||||
|
continue;
|
||||||
|
loader->add(
|
||||||
|
AssetType::TEXTURE,
|
||||||
|
directory + "/" + file.stem().u8string(),
|
||||||
|
name + "/" + file.stem().u8string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return [](auto){};
|
||||||
|
}
|
||||||
AtlasBuilder builder;
|
AtlasBuilder builder;
|
||||||
for (const auto& file : paths->listdir(directory)) {
|
for (const auto& file : paths->listdir(directory)) {
|
||||||
if (!imageio::is_read_supported(file.extension().u8string())) continue;
|
if (!imageio::is_read_supported(file.extension().u8string())) continue;
|
||||||
@ -172,7 +185,9 @@ assetload::postfunc assetload::layout(
|
|||||||
return [=](auto assets) {
|
return [=](auto assets) {
|
||||||
try {
|
try {
|
||||||
auto cfg = std::dynamic_pointer_cast<LayoutCfg>(config);
|
auto cfg = std::dynamic_pointer_cast<LayoutCfg>(config);
|
||||||
assets->store(UiDocument::read(cfg->env, name, file), name);
|
assets->store(
|
||||||
|
UiDocument::read(cfg->env, name, file, "abs:" + file), name
|
||||||
|
);
|
||||||
} catch (const parsing_error& err) {
|
} catch (const parsing_error& err) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"failed to parse layout XML '" + file + "':\n" + err.errorLog()
|
"failed to parse layout XML '" + file + "':\n" + err.errorLog()
|
||||||
|
|||||||
@ -373,7 +373,7 @@ std::string BasicParser::parseString(char quote, bool closeRequired) {
|
|||||||
case 'b': ss << '\b'; break;
|
case 'b': ss << '\b'; break;
|
||||||
case 't': ss << '\t'; break;
|
case 't': ss << '\t'; break;
|
||||||
case 'f': ss << '\f'; break;
|
case 'f': ss << '\f'; break;
|
||||||
case '\'': ss << '\\'; break;
|
case '\'': ss << '\''; break;
|
||||||
case '"': ss << '"'; break;
|
case '"': ss << '"'; break;
|
||||||
case '\\': ss << '\\'; break;
|
case '\\': ss << '\\'; break;
|
||||||
case '/': ss << '/'; break;
|
case '/': ss << '/'; break;
|
||||||
|
|||||||
@ -265,5 +265,5 @@ dv::value json::parse(
|
|||||||
}
|
}
|
||||||
|
|
||||||
dv::value json::parse(std::string_view source) {
|
dv::value json::parse(std::string_view source) {
|
||||||
return parse("<string>", source);
|
return parse("[string]", source);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -250,7 +250,8 @@ std::string Parser::parseText() {
|
|||||||
}
|
}
|
||||||
nextChar();
|
nextChar();
|
||||||
}
|
}
|
||||||
return std::string(source.substr(start, pos - start));
|
return Parser("[string]", std::string(source.substr(start, pos - start)))
|
||||||
|
.parseString('\0', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_xml_identifier_start(char c) {
|
inline bool is_xml_identifier_start(char c) {
|
||||||
@ -336,7 +337,7 @@ xmldocument Parser::parse() {
|
|||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
|
||||||
xmldocument xml::parse(const std::string& filename, const std::string& source) {
|
xmldocument xml::parse(std::string_view filename, std::string_view source) {
|
||||||
Parser parser(filename, source);
|
Parser parser(filename, source);
|
||||||
return parser.parse();
|
return parser.parse();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -140,6 +140,6 @@ namespace xml {
|
|||||||
/// @param source xml source code string
|
/// @param source xml source code string
|
||||||
/// @return xml document
|
/// @return xml document
|
||||||
extern xmldocument parse(
|
extern xmldocument parse(
|
||||||
const std::string& filename, const std::string& source
|
std::string_view filename, std::string_view source
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,9 +35,6 @@ inline constexpr int CHUNK_D = 16;
|
|||||||
inline constexpr uint VOXEL_USER_BITS = 8;
|
inline constexpr uint VOXEL_USER_BITS = 8;
|
||||||
inline constexpr uint VOXEL_USER_BITS_OFFSET = sizeof(blockstate_t)*8-VOXEL_USER_BITS;
|
inline constexpr uint VOXEL_USER_BITS_OFFSET = sizeof(blockstate_t)*8-VOXEL_USER_BITS;
|
||||||
|
|
||||||
/// @brief pixel size of an item inventory icon
|
|
||||||
inline constexpr int ITEM_ICON_SIZE = 48;
|
|
||||||
|
|
||||||
/// @brief chunk volume (count of voxels per Chunk)
|
/// @brief chunk volume (count of voxels per Chunk)
|
||||||
inline constexpr int CHUNK_VOL = (CHUNK_W * CHUNK_H * CHUNK_D);
|
inline constexpr int CHUNK_VOL = (CHUNK_W * CHUNK_H * CHUNK_D);
|
||||||
|
|
||||||
@ -53,6 +50,11 @@ inline constexpr uint vox_index(uint x, uint y, uint z, uint w=CHUNK_W, uint d=C
|
|||||||
return (y * d + z) * w + x;
|
return (y * d + z) * w + x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief pixel size of an item inventory icon
|
||||||
|
inline constexpr int ITEM_ICON_SIZE = 48;
|
||||||
|
|
||||||
|
inline constexpr int TRANSLUCENT_BLOCKS_SORT_INTERVAL = 8;
|
||||||
|
|
||||||
inline const std::string SHADERS_FOLDER = "shaders";
|
inline const std::string SHADERS_FOLDER = "shaders";
|
||||||
inline const std::string TEXTURES_FOLDER = "textures";
|
inline const std::string TEXTURES_FOLDER = "textures";
|
||||||
inline const std::string FONTS_FOLDER = "fonts";
|
inline const std::string FONTS_FOLDER = "fonts";
|
||||||
|
|||||||
@ -333,6 +333,7 @@ void ContentLoader::loadBlock(
|
|||||||
root.at("inventory-size").get(def.inventorySize);
|
root.at("inventory-size").get(def.inventorySize);
|
||||||
root.at("tick-interval").get(def.tickInterval);
|
root.at("tick-interval").get(def.tickInterval);
|
||||||
root.at("overlay-texture").get(def.overlayTexture);
|
root.at("overlay-texture").get(def.overlayTexture);
|
||||||
|
root.at("translucent").get(def.translucent);
|
||||||
|
|
||||||
if (root.has("fields")) {
|
if (root.has("fields")) {
|
||||||
def.dataStruct = std::make_unique<StructLayout>();
|
def.dataStruct = std::make_unique<StructLayout>();
|
||||||
@ -484,7 +485,13 @@ void ContentLoader::loadBlock(
|
|||||||
|
|
||||||
auto scriptfile = folder / fs::path("scripts/" + def.scriptName + ".lua");
|
auto scriptfile = folder / fs::path("scripts/" + def.scriptName + ".lua");
|
||||||
if (fs::is_regular_file(scriptfile)) {
|
if (fs::is_regular_file(scriptfile)) {
|
||||||
scripting::load_block_script(env, full, scriptfile, def.rt.funcsset);
|
scripting::load_block_script(
|
||||||
|
env,
|
||||||
|
full,
|
||||||
|
scriptfile,
|
||||||
|
pack->id + ":scripts/" + def.scriptName + ".lua",
|
||||||
|
def.rt.funcsset
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!def.hidden) {
|
if (!def.hidden) {
|
||||||
auto& item = builder.items.create(full + BLOCK_ITEM_SUFFIX);
|
auto& item = builder.items.create(full + BLOCK_ITEM_SUFFIX);
|
||||||
@ -510,7 +517,13 @@ void ContentLoader::loadItem(
|
|||||||
|
|
||||||
auto scriptfile = folder / fs::path("scripts/" + def.scriptName + ".lua");
|
auto scriptfile = folder / fs::path("scripts/" + def.scriptName + ".lua");
|
||||||
if (fs::is_regular_file(scriptfile)) {
|
if (fs::is_regular_file(scriptfile)) {
|
||||||
scripting::load_item_script(env, full, scriptfile, def.rt.funcsset);
|
scripting::load_item_script(
|
||||||
|
env,
|
||||||
|
full,
|
||||||
|
scriptfile,
|
||||||
|
pack->id + ":scripts/" + def.scriptName + ".lua",
|
||||||
|
def.rt.funcsset
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -719,7 +732,11 @@ void ContentLoader::load() {
|
|||||||
fs::path scriptFile = folder / fs::path("scripts/world.lua");
|
fs::path scriptFile = folder / fs::path("scripts/world.lua");
|
||||||
if (fs::is_regular_file(scriptFile)) {
|
if (fs::is_regular_file(scriptFile)) {
|
||||||
scripting::load_world_script(
|
scripting::load_world_script(
|
||||||
env, pack->id, scriptFile, runtime->worldfuncsset
|
env,
|
||||||
|
pack->id,
|
||||||
|
scriptFile,
|
||||||
|
pack->id + ":scripts/world.lua",
|
||||||
|
runtime->worldfuncsset
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -794,7 +811,11 @@ void ContentLoader::load() {
|
|||||||
fs::path componentsDir = folder / fs::u8path("scripts/components");
|
fs::path componentsDir = folder / fs::u8path("scripts/components");
|
||||||
foreach_file(componentsDir, [this](const fs::path& file) {
|
foreach_file(componentsDir, [this](const fs::path& file) {
|
||||||
auto name = pack->id + ":" + file.stem().u8string();
|
auto name = pack->id + ":" + file.stem().u8string();
|
||||||
scripting::load_entity_component(name, file);
|
scripting::load_entity_component(
|
||||||
|
name,
|
||||||
|
file,
|
||||||
|
pack->id + ":scripts/components/" + file.filename().u8string()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Process content.json and load defined content units
|
// Process content.json and load defined content units
|
||||||
|
|||||||
@ -99,6 +99,7 @@ struct world_funcs_set {
|
|||||||
bool onblockplaced : 1;
|
bool onblockplaced : 1;
|
||||||
bool onblockbroken : 1;
|
bool onblockbroken : 1;
|
||||||
bool onblockinteract : 1;
|
bool onblockinteract : 1;
|
||||||
|
bool onplayertick : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ContentPackRuntime {
|
class ContentPackRuntime {
|
||||||
|
|||||||
@ -10,7 +10,6 @@ inline const std::string CORE_STRUCT_AIR = "core:struct_air";
|
|||||||
inline const std::string TEXTURE_NOTFOUND = "notfound";
|
inline const std::string TEXTURE_NOTFOUND = "notfound";
|
||||||
|
|
||||||
// built-in bindings
|
// built-in bindings
|
||||||
inline const std::string BIND_DEVTOOLS_CONSOLE = "devtools.console";
|
|
||||||
inline const std::string BIND_CHUNKS_RELOAD = "chunks.reload";
|
inline const std::string BIND_CHUNKS_RELOAD = "chunks.reload";
|
||||||
inline const std::string BIND_MOVE_FORWARD = "movement.forward";
|
inline const std::string BIND_MOVE_FORWARD = "movement.forward";
|
||||||
inline const std::string BIND_MOVE_BACK = "movement.back";
|
inline const std::string BIND_MOVE_BACK = "movement.back";
|
||||||
|
|||||||
@ -53,7 +53,12 @@ scriptenv UiDocument::getEnvironment() const {
|
|||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<UiDocument> UiDocument::read(const scriptenv& penv, const std::string& name, const fs::path& file) {
|
std::unique_ptr<UiDocument> UiDocument::read(
|
||||||
|
const scriptenv& penv,
|
||||||
|
const std::string& name,
|
||||||
|
const fs::path& file,
|
||||||
|
const std::string& fileName
|
||||||
|
) {
|
||||||
const std::string text = files::read_string(file);
|
const std::string text = files::read_string(file);
|
||||||
auto xmldoc = xml::parse(file.u8string(), text);
|
auto xmldoc = xml::parse(file.u8string(), text);
|
||||||
|
|
||||||
@ -69,12 +74,16 @@ std::unique_ptr<UiDocument> UiDocument::read(const scriptenv& penv, const std::s
|
|||||||
uidocscript script {};
|
uidocscript script {};
|
||||||
auto scriptFile = fs::path(file.u8string()+".lua");
|
auto scriptFile = fs::path(file.u8string()+".lua");
|
||||||
if (fs::is_regular_file(scriptFile)) {
|
if (fs::is_regular_file(scriptFile)) {
|
||||||
scripting::load_layout_script(env, name, scriptFile, script);
|
scripting::load_layout_script(
|
||||||
|
env, name, scriptFile, fileName + ".lua", script
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return std::make_unique<UiDocument>(name, script, view, env);
|
return std::make_unique<UiDocument>(name, script, view, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<gui::UINode> UiDocument::readElement(const fs::path& file) {
|
std::shared_ptr<gui::UINode> UiDocument::readElement(
|
||||||
auto document = read(nullptr, file.filename().u8string(), file);
|
const fs::path& file, const std::string& fileName
|
||||||
|
) {
|
||||||
|
auto document = read(nullptr, file.filename().u8string(), file, fileName);
|
||||||
return document->getRoot();
|
return document->getRoot();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,13 @@ public:
|
|||||||
const uidocscript& getScript() const;
|
const uidocscript& getScript() const;
|
||||||
scriptenv getEnvironment() const;
|
scriptenv getEnvironment() const;
|
||||||
|
|
||||||
static std::unique_ptr<UiDocument> read(const scriptenv& parent_env, const std::string& name, const fs::path& file);
|
static std::unique_ptr<UiDocument> read(
|
||||||
static std::shared_ptr<gui::UINode> readElement(const fs::path& file);
|
const scriptenv& parent_env,
|
||||||
|
const std::string& name,
|
||||||
|
const fs::path& file,
|
||||||
|
const std::string& fileName
|
||||||
|
);
|
||||||
|
static std::shared_ptr<gui::UINode> readElement(
|
||||||
|
const fs::path& file, const std::string& fileName
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -229,13 +229,9 @@ void Hud::processInput(bool visible) {
|
|||||||
setPause(true);
|
setPause(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!pause && Events::jactive(BIND_DEVTOOLS_CONSOLE)) {
|
|
||||||
showOverlay(assets->get<UiDocument>("core:console"), false);
|
|
||||||
}
|
|
||||||
if (!Window::isFocused() && !pause && !isInventoryOpen()) {
|
if (!Window::isFocused() && !pause && !isInventoryOpen()) {
|
||||||
setPause(true);
|
setPause(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pause && visible && Events::jactive(BIND_HUD_INVENTORY)) {
|
if (!pause && visible && Events::jactive(BIND_HUD_INVENTORY)) {
|
||||||
if (inventoryOpen) {
|
if (inventoryOpen) {
|
||||||
closeInventory();
|
closeInventory();
|
||||||
@ -465,7 +461,9 @@ void Hud::showExchangeSlot() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hud::showOverlay(UiDocument* doc, bool playerInventory) {
|
void Hud::showOverlay(
|
||||||
|
UiDocument* doc, bool playerInventory, const dv::value& args
|
||||||
|
) {
|
||||||
if (isInventoryOpen()) {
|
if (isInventoryOpen()) {
|
||||||
closeInventory();
|
closeInventory();
|
||||||
}
|
}
|
||||||
@ -476,7 +474,8 @@ void Hud::showOverlay(UiDocument* doc, bool playerInventory) {
|
|||||||
showExchangeSlot();
|
showExchangeSlot();
|
||||||
inventoryOpen = true;
|
inventoryOpen = true;
|
||||||
}
|
}
|
||||||
add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false));
|
add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false),
|
||||||
|
args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hud::openPermanent(UiDocument* doc) {
|
void Hud::openPermanent(UiDocument* doc) {
|
||||||
@ -508,13 +507,18 @@ void Hud::closeInventory() {
|
|||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hud::add(const HudElement& element) {
|
void Hud::add(const HudElement& element, const dv::value& argsArray) {
|
||||||
gui->add(element.getNode());
|
gui->add(element.getNode());
|
||||||
auto document = element.getDocument();
|
auto document = element.getDocument();
|
||||||
if (document) {
|
if (document) {
|
||||||
auto invview = std::dynamic_pointer_cast<InventoryView>(element.getNode());
|
auto invview = std::dynamic_pointer_cast<InventoryView>(element.getNode());
|
||||||
auto inventory = invview ? invview->getInventory() : nullptr;
|
auto inventory = invview ? invview->getInventory() : nullptr;
|
||||||
std::vector<dv::value> args;
|
std::vector<dv::value> args;
|
||||||
|
if (argsArray != nullptr) {
|
||||||
|
for (const auto& arg : argsArray) {
|
||||||
|
args.push_back(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
args.emplace_back(inventory ? inventory.get()->getId() : 0);
|
args.emplace_back(inventory ? inventory.get()->getId() : 0);
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
args.emplace_back(static_cast<integer_t>(blockPos[i]));
|
args.emplace_back(static_cast<integer_t>(blockPos[i]));
|
||||||
@ -571,7 +575,7 @@ void Hud::draw(const DrawContext& ctx){
|
|||||||
|
|
||||||
// Crosshair
|
// Crosshair
|
||||||
if (!pause && !inventoryOpen && !player->debug) {
|
if (!pause && !inventoryOpen && !player->debug) {
|
||||||
DrawContext chctx = ctx.sub();
|
DrawContext chctx = ctx.sub(batch);
|
||||||
chctx.setBlendMode(BlendMode::inversion);
|
chctx.setBlendMode(BlendMode::inversion);
|
||||||
auto texture = assets->get<Texture>("gui/crosshair");
|
auto texture = assets->get<Texture>("gui/crosshair");
|
||||||
batch->texture(texture);
|
batch->texture(texture);
|
||||||
@ -615,8 +619,11 @@ void Hud::updateElementsPosition(const Viewport& viewport) {
|
|||||||
}
|
}
|
||||||
if (secondUI->getPositionFunc() == nullptr) {
|
if (secondUI->getPositionFunc() == nullptr) {
|
||||||
secondUI->setPos(glm::vec2(
|
secondUI->setPos(glm::vec2(
|
||||||
glm::min(width/2-invwidth/2, width-caWidth-(inventoryView ? 10 : 0)-invwidth),
|
glm::min(
|
||||||
height/2-totalHeight/2
|
width / 2.f - invwidth / 2.f,
|
||||||
|
width - caWidth - (inventoryView ? 10 : 0) - invwidth
|
||||||
|
),
|
||||||
|
height / 2.f - totalHeight / 2.f
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
#include "util/ObjectsKeeper.hpp"
|
#include "util/ObjectsKeeper.hpp"
|
||||||
|
#include "data/dv.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -173,7 +174,10 @@ public:
|
|||||||
/// @brief Show element in inventory-mode
|
/// @brief Show element in inventory-mode
|
||||||
/// @param doc element layout
|
/// @param doc element layout
|
||||||
/// @param playerInventory show player inventory too
|
/// @param playerInventory show player inventory too
|
||||||
void showOverlay(UiDocument* doc, bool playerInventory);
|
/// @param arg first argument passing to on_open
|
||||||
|
void showOverlay(
|
||||||
|
UiDocument* doc, bool playerInventory, const dv::value& arg = nullptr
|
||||||
|
);
|
||||||
|
|
||||||
/// @brief Close all open inventories and overlay
|
/// @brief Close all open inventories and overlay
|
||||||
void closeInventory();
|
void closeInventory();
|
||||||
@ -182,7 +186,7 @@ public:
|
|||||||
/// @param doc element layout
|
/// @param doc element layout
|
||||||
void openPermanent(UiDocument* doc);
|
void openPermanent(UiDocument* doc);
|
||||||
|
|
||||||
void add(const HudElement& element);
|
void add(const HudElement& element, const dv::value& arg=nullptr);
|
||||||
void onRemove(const HudElement& element);
|
void onRemove(const HudElement& element);
|
||||||
void remove(const std::shared_ptr<gui::UINode>& node);
|
void remove(const std::shared_ptr<gui::UINode>& node);
|
||||||
|
|
||||||
|
|||||||
@ -62,7 +62,10 @@ gui::page_loader_func menus::create_page_loader(Engine* engine) {
|
|||||||
auto fullname = "core:pages/"+name;
|
auto fullname = "core:pages/"+name;
|
||||||
|
|
||||||
auto document_ptr = UiDocument::read(
|
auto document_ptr = UiDocument::read(
|
||||||
scripting::get_root_environment(), fullname, file
|
scripting::get_root_environment(),
|
||||||
|
fullname,
|
||||||
|
file,
|
||||||
|
"core:layouts/pages/" + name
|
||||||
);
|
);
|
||||||
auto document = document_ptr.get();
|
auto document = document_ptr.get();
|
||||||
engine->getAssets()->store(std::move(document_ptr), fullname);
|
engine->getAssets()->store(std::move(document_ptr), fullname);
|
||||||
@ -110,7 +113,7 @@ UiDocument* menus::show(Engine* engine, const std::string& name, std::vector<dv:
|
|||||||
auto fullname = "core:layouts/"+name;
|
auto fullname = "core:layouts/"+name;
|
||||||
|
|
||||||
auto document_ptr = UiDocument::read(
|
auto document_ptr = UiDocument::read(
|
||||||
scripting::get_root_environment(), fullname, file
|
scripting::get_root_environment(), fullname, file, "core:layouts/"+name
|
||||||
);
|
);
|
||||||
auto document = document_ptr.get();
|
auto document = document_ptr.get();
|
||||||
engine->getAssets()->store(std::move(document_ptr), fullname);
|
engine->getAssets()->store(std::move(document_ptr), fullname);
|
||||||
|
|||||||
@ -83,7 +83,12 @@ void LevelScreen::initializePack(ContentPackRuntime* pack) {
|
|||||||
const ContentPack& info = pack->getInfo();
|
const ContentPack& info = pack->getInfo();
|
||||||
fs::path scriptFile = info.folder/fs::path("scripts/hud.lua");
|
fs::path scriptFile = info.folder/fs::path("scripts/hud.lua");
|
||||||
if (fs::is_regular_file(scriptFile)) {
|
if (fs::is_regular_file(scriptFile)) {
|
||||||
scripting::load_hud_script(pack->getEnvironment(), info.id, scriptFile);
|
scripting::load_hud_script(
|
||||||
|
pack->getEnvironment(),
|
||||||
|
info.id,
|
||||||
|
scriptFile,
|
||||||
|
pack->getId() + ":scripts/hud.lua"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
inline constexpr uint B2D_VERTEX_SIZE = 8;
|
inline constexpr uint B2D_VERTEX_SIZE = 8;
|
||||||
|
|
||||||
Batch2D::Batch2D(size_t capacity) : capacity(capacity), color(1.0f){
|
Batch2D::Batch2D(size_t capacity) : capacity(capacity), color(1.0f){
|
||||||
const vattr attrs[] = {
|
const VertexAttribute attrs[] = {
|
||||||
{2}, {2}, {4}, {0}
|
{2}, {2}, {4}, {0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ inline constexpr uint B3D_VERTEX_SIZE = 9;
|
|||||||
|
|
||||||
Batch3D::Batch3D(size_t capacity)
|
Batch3D::Batch3D(size_t capacity)
|
||||||
: capacity(capacity) {
|
: capacity(capacity) {
|
||||||
const vattr attrs[] = {
|
const VertexAttribute attrs[] = {
|
||||||
{3}, {2}, {4}, {0}
|
{3}, {2}, {4}, {0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -91,6 +91,7 @@ DrawContext DrawContext::sub(Flushable* flushable) const {
|
|||||||
auto ctx = DrawContext(*this);
|
auto ctx = DrawContext(*this);
|
||||||
ctx.parent = this;
|
ctx.parent = this;
|
||||||
ctx.flushable = flushable;
|
ctx.flushable = flushable;
|
||||||
|
ctx.scissorsCount = 0;
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +149,7 @@ void DrawContext::setBlendMode(BlendMode mode) {
|
|||||||
set_blend_mode(mode);
|
set_blend_mode(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawContext::setScissors(glm::vec4 area) {
|
void DrawContext::setScissors(const glm::vec4& area) {
|
||||||
Window::pushScissor(area);
|
Window::pushScissor(area);
|
||||||
scissorsCount++;
|
scissorsCount++;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,6 @@ public:
|
|||||||
void setDepthTest(bool flag);
|
void setDepthTest(bool flag);
|
||||||
void setCullFace(bool flag);
|
void setCullFace(bool flag);
|
||||||
void setBlendMode(BlendMode mode);
|
void setBlendMode(BlendMode mode);
|
||||||
void setScissors(glm::vec4 area);
|
void setScissors(const glm::vec4& area);
|
||||||
void setLineWidth(float width);
|
void setLineWidth(float width);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
inline constexpr uint LB_VERTEX_SIZE = (3+4);
|
inline constexpr uint LB_VERTEX_SIZE = (3+4);
|
||||||
|
|
||||||
LineBatch::LineBatch(size_t capacity) : capacity(capacity) {
|
LineBatch::LineBatch(size_t capacity) : capacity(capacity) {
|
||||||
const vattr attrs[] = { {3},{4}, {0} };
|
const VertexAttribute attrs[] = { {3},{4}, {0} };
|
||||||
buffer = std::make_unique<float[]>(capacity * LB_VERTEX_SIZE * 2);
|
buffer = std::make_unique<float[]>(capacity * LB_VERTEX_SIZE * 2);
|
||||||
mesh = std::make_unique<Mesh>(buffer.get(), 0, attrs);
|
mesh = std::make_unique<Mesh>(buffer.get(), 0, attrs);
|
||||||
index = 0;
|
index = 0;
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
int Mesh::meshesCount = 0;
|
int Mesh::meshesCount = 0;
|
||||||
int Mesh::drawCalls = 0;
|
int Mesh::drawCalls = 0;
|
||||||
|
|
||||||
inline size_t calc_vertex_size(const vattr* attrs) {
|
inline size_t calc_vertex_size(const VertexAttribute* attrs) {
|
||||||
size_t vertexSize = 0;
|
size_t vertexSize = 0;
|
||||||
for (int i = 0; attrs[i].size; i++) {
|
for (int i = 0; attrs[i].size; i++) {
|
||||||
vertexSize += attrs[i].size;
|
vertexSize += attrs[i].size;
|
||||||
@ -19,10 +19,10 @@ Mesh::Mesh(const MeshData& data)
|
|||||||
data.indices.size(),
|
data.indices.size(),
|
||||||
data.attrs.data()) {}
|
data.attrs.data()) {}
|
||||||
|
|
||||||
Mesh::Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const vattr* attrs) :
|
Mesh::Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const VertexAttribute* attrs) :
|
||||||
ibo(0),
|
ibo(0),
|
||||||
vertices(vertices),
|
vertices(0),
|
||||||
indices(indices)
|
indices(0)
|
||||||
{
|
{
|
||||||
meshesCount++;
|
meshesCount++;
|
||||||
vertexSize = 0;
|
vertexSize = 0;
|
||||||
@ -58,10 +58,9 @@ void Mesh::reload(const float* vertexBuffer, size_t vertices, const int* indexBu
|
|||||||
glBindVertexArray(vao);
|
glBindVertexArray(vao);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
if (vertexBuffer != nullptr && vertices != 0) {
|
if (vertexBuffer != nullptr && vertices != 0) {
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertexSize * vertices, vertexBuffer, GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertexSize * vertices, vertexBuffer, GL_STREAM_DRAW);
|
||||||
}
|
} else {
|
||||||
else {
|
glBufferData(GL_ARRAY_BUFFER, 0, {}, GL_STREAM_DRAW);
|
||||||
glBufferData(GL_ARRAY_BUFFER, 0, {}, GL_STATIC_DRAW);
|
|
||||||
}
|
}
|
||||||
if (indexBuffer != nullptr && indices != 0) {
|
if (indexBuffer != nullptr && indices != 0) {
|
||||||
if (ibo == 0) glGenBuffers(1, &ibo);
|
if (ibo == 0) glGenBuffers(1, &ibo);
|
||||||
@ -75,7 +74,7 @@ void Mesh::reload(const float* vertexBuffer, size_t vertices, const int* indexBu
|
|||||||
this->indices = indices;
|
this->indices = indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::draw(unsigned int primitive){
|
void Mesh::draw(unsigned int primitive) const {
|
||||||
drawCalls++;
|
drawCalls++;
|
||||||
glBindVertexArray(vao);
|
glBindVertexArray(vao);
|
||||||
if (ibo != 0) {
|
if (ibo != 0) {
|
||||||
@ -87,6 +86,6 @@ void Mesh::draw(unsigned int primitive){
|
|||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::draw() {
|
void Mesh::draw() const {
|
||||||
draw(GL_TRIANGLES);
|
draw(GL_TRIANGLES);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,8 +14,8 @@ class Mesh {
|
|||||||
size_t vertexSize;
|
size_t vertexSize;
|
||||||
public:
|
public:
|
||||||
Mesh(const MeshData& data);
|
Mesh(const MeshData& data);
|
||||||
Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const vattr* attrs);
|
Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const VertexAttribute* attrs);
|
||||||
Mesh(const float* vertexBuffer, size_t vertices, const vattr* attrs) :
|
Mesh(const float* vertexBuffer, size_t vertices, const VertexAttribute* attrs) :
|
||||||
Mesh(vertexBuffer, vertices, nullptr, 0, attrs) {};
|
Mesh(vertexBuffer, vertices, nullptr, 0, attrs) {};
|
||||||
~Mesh();
|
~Mesh();
|
||||||
|
|
||||||
@ -28,10 +28,10 @@ public:
|
|||||||
|
|
||||||
/// @brief Draw mesh with specified primitives type
|
/// @brief Draw mesh with specified primitives type
|
||||||
/// @param primitive primitives type
|
/// @param primitive primitives type
|
||||||
void draw(unsigned int primitive);
|
void draw(unsigned int primitive) const;
|
||||||
|
|
||||||
/// @brief Draw mesh as triangles
|
/// @brief Draw mesh as triangles
|
||||||
void draw();
|
void draw() const;
|
||||||
|
|
||||||
/// @brief Total numbers of alive mesh objects
|
/// @brief Total numbers of alive mesh objects
|
||||||
static int meshesCount;
|
static int meshesCount;
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
#include "util/Buffer.hpp"
|
#include "util/Buffer.hpp"
|
||||||
|
|
||||||
/// @brief Vertex attribute info
|
/// @brief Vertex attribute info
|
||||||
struct vattr {
|
struct VertexAttribute {
|
||||||
ubyte size;
|
ubyte size;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ struct vattr {
|
|||||||
struct MeshData {
|
struct MeshData {
|
||||||
util::Buffer<float> vertices;
|
util::Buffer<float> vertices;
|
||||||
util::Buffer<int> indices;
|
util::Buffer<int> indices;
|
||||||
util::Buffer<vattr> attrs;
|
util::Buffer<VertexAttribute> attrs;
|
||||||
|
|
||||||
MeshData() = default;
|
MeshData() = default;
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ struct MeshData {
|
|||||||
MeshData(
|
MeshData(
|
||||||
util::Buffer<float> vertices,
|
util::Buffer<float> vertices,
|
||||||
util::Buffer<int> indices,
|
util::Buffer<int> indices,
|
||||||
util::Buffer<vattr> attrs
|
util::Buffer<VertexAttribute> attrs
|
||||||
) : vertices(std::move(vertices)),
|
) : vertices(std::move(vertices)),
|
||||||
indices(std::move(indices)),
|
indices(std::move(indices)),
|
||||||
attrs(std::move(attrs)) {}
|
attrs(std::move(attrs)) {}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ PostProcessing::PostProcessing() {
|
|||||||
-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
||||||
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f
|
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f
|
||||||
};
|
};
|
||||||
vattr attrs[] {{2}, {0}};
|
VertexAttribute attrs[] {{2}, {0}};
|
||||||
quadMesh = std::make_unique<Mesh>(vertices, 6, attrs);
|
quadMesh = std::make_unique<Mesh>(vertices, 6, attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
108
src/graphics/render/BlockWrapsRenderer.cpp
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#include "BlockWrapsRenderer.hpp"
|
||||||
|
|
||||||
|
#include "assets/Assets.hpp"
|
||||||
|
#include "assets/assets_util.hpp"
|
||||||
|
#include "constants.hpp"
|
||||||
|
#include "content/Content.hpp"
|
||||||
|
#include "graphics/core/Atlas.hpp"
|
||||||
|
#include "graphics/core/Shader.hpp"
|
||||||
|
#include "graphics/core/DrawContext.hpp"
|
||||||
|
#include "graphics/render/MainBatch.hpp"
|
||||||
|
#include "objects/Player.hpp"
|
||||||
|
#include "voxels/Block.hpp"
|
||||||
|
#include "voxels/Chunks.hpp"
|
||||||
|
#include "window/Window.hpp"
|
||||||
|
#include "world/Level.hpp"
|
||||||
|
|
||||||
|
BlockWrapsRenderer::BlockWrapsRenderer(const Assets& assets, const Level& level)
|
||||||
|
: assets(assets), level(level), batch(std::make_unique<MainBatch>(1024)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockWrapsRenderer::~BlockWrapsRenderer() = default;
|
||||||
|
|
||||||
|
void BlockWrapsRenderer::draw(const BlockWrapper& wrapper) {
|
||||||
|
const auto& chunks = *level.chunks;
|
||||||
|
|
||||||
|
auto textureRegion = util::get_texture_region(assets, wrapper.texture, "");
|
||||||
|
|
||||||
|
auto& shader = assets.require<Shader>("entity");
|
||||||
|
shader.use();
|
||||||
|
shader.uniform1i("u_alphaClip", false);
|
||||||
|
|
||||||
|
const UVRegion& cracksRegion = textureRegion.region;
|
||||||
|
UVRegion regions[6] {
|
||||||
|
cracksRegion, cracksRegion, cracksRegion,
|
||||||
|
cracksRegion, cracksRegion, cracksRegion
|
||||||
|
};
|
||||||
|
batch->setTexture(textureRegion.texture);
|
||||||
|
|
||||||
|
const voxel* vox = chunks.get(wrapper.position);
|
||||||
|
if (vox == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vox->id != BLOCK_VOID) {
|
||||||
|
const auto& def =
|
||||||
|
level.content->getIndices()->blocks.require(vox->id);
|
||||||
|
switch (def.model) {
|
||||||
|
case BlockModel::block:
|
||||||
|
batch->cube(
|
||||||
|
glm::vec3(wrapper.position) + glm::vec3(0.5f),
|
||||||
|
glm::vec3(1.01f),
|
||||||
|
regions,
|
||||||
|
glm::vec4(0),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case BlockModel::aabb: {
|
||||||
|
const auto& aabb = def.rt.hitboxes[vox->state.rotation].at(0);
|
||||||
|
const auto& size = aabb.size();
|
||||||
|
regions[0].scale(size.z, size.y);
|
||||||
|
regions[1].scale(size.z, size.y);
|
||||||
|
regions[2].scale(size.x, size.z);
|
||||||
|
regions[3].scale(size.x, size.z);
|
||||||
|
regions[4].scale(size.x, size.y);
|
||||||
|
regions[5].scale(size.x, size.y);
|
||||||
|
batch->cube(
|
||||||
|
glm::vec3(wrapper.position) + aabb.center(),
|
||||||
|
size * glm::vec3(1.01f),
|
||||||
|
regions,
|
||||||
|
glm::vec4(0),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockWrapsRenderer::draw(const DrawContext& pctx, const Player& player) {
|
||||||
|
auto ctx = pctx.sub();
|
||||||
|
for (const auto& [_, wrapper] : wrappers) {
|
||||||
|
draw(*wrapper);
|
||||||
|
}
|
||||||
|
batch->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
u64id_t BlockWrapsRenderer::add(
|
||||||
|
const glm::ivec3& position, const std::string& texture
|
||||||
|
) {
|
||||||
|
u64id_t id = nextWrapper++;
|
||||||
|
wrappers[id] = std::make_unique<BlockWrapper>(
|
||||||
|
BlockWrapper {position, texture}
|
||||||
|
);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockWrapper* BlockWrapsRenderer::get(u64id_t id) const {
|
||||||
|
const auto& found = wrappers.find(id);
|
||||||
|
if (found == wrappers.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return found->second.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockWrapsRenderer::remove(u64id_t id) {
|
||||||
|
wrappers.erase(id);
|
||||||
|
}
|
||||||
40
src/graphics/render/BlockWrapsRenderer.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "MainBatch.hpp"
|
||||||
|
#include "typedefs.hpp"
|
||||||
|
|
||||||
|
class Assets;
|
||||||
|
class Player;
|
||||||
|
class Level;
|
||||||
|
class DrawContext;
|
||||||
|
|
||||||
|
struct BlockWrapper {
|
||||||
|
glm::ivec3 position;
|
||||||
|
std::string texture;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BlockWrapsRenderer {
|
||||||
|
const Assets& assets;
|
||||||
|
const Level& level;
|
||||||
|
std::unique_ptr<MainBatch> batch;
|
||||||
|
|
||||||
|
std::unordered_map<u64id_t, std::unique_ptr<BlockWrapper>> wrappers;
|
||||||
|
u64id_t nextWrapper = 1;
|
||||||
|
|
||||||
|
void draw(const BlockWrapper& wrapper);
|
||||||
|
public:
|
||||||
|
BlockWrapsRenderer(const Assets& assets, const Level& level);
|
||||||
|
~BlockWrapsRenderer();
|
||||||
|
|
||||||
|
void draw(const DrawContext& ctx, const Player& player);
|
||||||
|
|
||||||
|
u64id_t add(const glm::ivec3& position, const std::string& texture);
|
||||||
|
|
||||||
|
BlockWrapper* get(u64id_t id) const;
|
||||||
|
|
||||||
|
void remove(u64id_t id);
|
||||||
|
};
|
||||||
@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
const uint BlocksRenderer::VERTEX_SIZE = 6;
|
|
||||||
const glm::vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f);
|
const glm::vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f);
|
||||||
|
|
||||||
BlocksRenderer::BlocksRenderer(
|
BlocksRenderer::BlocksRenderer(
|
||||||
@ -21,7 +20,7 @@ BlocksRenderer::BlocksRenderer(
|
|||||||
const ContentGfxCache& cache,
|
const ContentGfxCache& cache,
|
||||||
const EngineSettings& settings
|
const EngineSettings& settings
|
||||||
) : content(content),
|
) : content(content),
|
||||||
vertexBuffer(std::make_unique<float[]>(capacity * VERTEX_SIZE)),
|
vertexBuffer(std::make_unique<float[]>(capacity * CHUNK_VERTEX_SIZE)),
|
||||||
indexBuffer(std::make_unique<int[]>(capacity)),
|
indexBuffer(std::make_unique<int[]>(capacity)),
|
||||||
vertexOffset(0),
|
vertexOffset(0),
|
||||||
indexOffset(0),
|
indexOffset(0),
|
||||||
@ -85,7 +84,7 @@ void BlocksRenderer::face(
|
|||||||
const glm::vec4(&lights)[4],
|
const glm::vec4(&lights)[4],
|
||||||
const glm::vec4& tint
|
const glm::vec4& tint
|
||||||
) {
|
) {
|
||||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
|
if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) {
|
||||||
overflow = true;
|
overflow = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -125,7 +124,7 @@ void BlocksRenderer::faceAO(
|
|||||||
const UVRegion& region,
|
const UVRegion& region,
|
||||||
bool lights
|
bool lights
|
||||||
) {
|
) {
|
||||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
|
if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) {
|
||||||
overflow = true;
|
overflow = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -163,7 +162,7 @@ void BlocksRenderer::face(
|
|||||||
glm::vec4 tint,
|
glm::vec4 tint,
|
||||||
bool lights
|
bool lights
|
||||||
) {
|
) {
|
||||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
|
if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) {
|
||||||
overflow = true;
|
overflow = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -288,7 +287,7 @@ void BlocksRenderer::blockCustomModel(
|
|||||||
|
|
||||||
const auto& model = cache.getModel(block->rt.id);
|
const auto& model = cache.getModel(block->rt.id);
|
||||||
for (const auto& mesh : model.meshes) {
|
for (const auto& mesh : model.meshes) {
|
||||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * mesh.vertices.size() > capacity) {
|
if (vertexOffset + CHUNK_VERTEX_SIZE * mesh.vertices.size() > capacity) {
|
||||||
overflow = true;
|
overflow = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -433,21 +432,9 @@ glm::vec4 BlocksRenderer::pickSoftLight(
|
|||||||
right, up);
|
right, up);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlocksRenderer::render(const voxel* voxels) {
|
void BlocksRenderer::render(
|
||||||
int totalBegin = chunk->bottom * (CHUNK_W * CHUNK_D);
|
const voxel* voxels, int beginEnds[256][2]
|
||||||
int totalEnd = chunk->top * (CHUNK_W * CHUNK_D);
|
) {
|
||||||
|
|
||||||
int beginEnds[256][2] {};
|
|
||||||
for (int i = totalBegin; i < totalEnd; i++) {
|
|
||||||
const voxel& vox = voxels[i];
|
|
||||||
blockid_t id = vox.id;
|
|
||||||
const auto& def = *blockDefsCache[id];
|
|
||||||
|
|
||||||
if (beginEnds[def.drawGroup][0] == 0) {
|
|
||||||
beginEnds[def.drawGroup][0] = i+1;
|
|
||||||
}
|
|
||||||
beginEnds[def.drawGroup][1] = i;
|
|
||||||
}
|
|
||||||
for (const auto drawGroup : *content.drawGroups) {
|
for (const auto drawGroup : *content.drawGroups) {
|
||||||
int begin = beginEnds[drawGroup][0];
|
int begin = beginEnds[drawGroup][0];
|
||||||
if (begin == 0) {
|
if (begin == 0) {
|
||||||
@ -462,13 +449,13 @@ void BlocksRenderer::render(const voxel* voxels) {
|
|||||||
if (id == 0 || def.drawGroup != drawGroup || state.segment) {
|
if (id == 0 || def.drawGroup != drawGroup || state.segment) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (def.translucent) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const UVRegion texfaces[6] {
|
const UVRegion texfaces[6] {
|
||||||
cache.getRegion(id, 0),
|
cache.getRegion(id, 0), cache.getRegion(id, 1),
|
||||||
cache.getRegion(id, 1),
|
cache.getRegion(id, 2), cache.getRegion(id, 3),
|
||||||
cache.getRegion(id, 2),
|
cache.getRegion(id, 4), cache.getRegion(id, 5)
|
||||||
cache.getRegion(id, 3),
|
|
||||||
cache.getRegion(id, 4),
|
|
||||||
cache.getRegion(id, 5)
|
|
||||||
};
|
};
|
||||||
int x = i % CHUNK_W;
|
int x = i % CHUNK_W;
|
||||||
int y = i / (CHUNK_D * CHUNK_W);
|
int y = i / (CHUNK_D * CHUNK_W);
|
||||||
@ -503,43 +490,185 @@ void BlocksRenderer::render(const voxel* voxels) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SortingMeshData BlocksRenderer::renderTranslucent(
|
||||||
|
const voxel* voxels, int beginEnds[256][2]
|
||||||
|
) {
|
||||||
|
SortingMeshData sortingMesh {{}};
|
||||||
|
|
||||||
|
AABB aabb {};
|
||||||
|
bool aabbInit = false;
|
||||||
|
size_t totalSize = 0;
|
||||||
|
for (const auto drawGroup : *content.drawGroups) {
|
||||||
|
int begin = beginEnds[drawGroup][0];
|
||||||
|
if (begin == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int end = beginEnds[drawGroup][1];
|
||||||
|
for (int i = begin-1; i <= end; i++) {
|
||||||
|
const voxel& vox = voxels[i];
|
||||||
|
blockid_t id = vox.id;
|
||||||
|
blockstate state = vox.state;
|
||||||
|
const auto& def = *blockDefsCache[id];
|
||||||
|
if (id == 0 || def.drawGroup != drawGroup || state.segment) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!def.translucent) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const UVRegion texfaces[6] {
|
||||||
|
cache.getRegion(id, 0), cache.getRegion(id, 1),
|
||||||
|
cache.getRegion(id, 2), cache.getRegion(id, 3),
|
||||||
|
cache.getRegion(id, 4), cache.getRegion(id, 5)
|
||||||
|
};
|
||||||
|
int x = i % CHUNK_W;
|
||||||
|
int y = i / (CHUNK_D * CHUNK_W);
|
||||||
|
int z = (i / CHUNK_D) % CHUNK_W;
|
||||||
|
switch (def.model) {
|
||||||
|
case BlockModel::block:
|
||||||
|
blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless,
|
||||||
|
def.ambientOcclusion);
|
||||||
|
break;
|
||||||
|
case BlockModel::xsprite: {
|
||||||
|
blockXSprite(x, y, z, glm::vec3(1.0f),
|
||||||
|
texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BlockModel::aabb: {
|
||||||
|
blockAABB({x, y, z}, texfaces, &def, vox.state.rotation,
|
||||||
|
!def.shadeless, def.ambientOcclusion);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BlockModel::custom: {
|
||||||
|
blockCustomModel({x, y, z}, &def, vox.state.rotation,
|
||||||
|
!def.shadeless, def.ambientOcclusion);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (vertexOffset == 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<float>(indexSize * CHUNK_VERTEX_SIZE)};
|
||||||
|
|
||||||
|
totalSize += entry.vertexData.size();
|
||||||
|
|
||||||
|
for (int j = 0; j < indexSize; j++) {
|
||||||
|
std::memcpy(
|
||||||
|
entry.vertexData.data() + j * CHUNK_VERTEX_SIZE,
|
||||||
|
vertexBuffer.get() + indexBuffer[j] * CHUNK_VERTEX_SIZE,
|
||||||
|
sizeof(float) * CHUNK_VERTEX_SIZE
|
||||||
|
);
|
||||||
|
float& vx = entry.vertexData[j * CHUNK_VERTEX_SIZE + 0];
|
||||||
|
float& vy = entry.vertexData[j * CHUNK_VERTEX_SIZE + 1];
|
||||||
|
float& vz = entry.vertexData[j * CHUNK_VERTEX_SIZE + 2];
|
||||||
|
|
||||||
|
if (!aabbInit) {
|
||||||
|
aabbInit = true;
|
||||||
|
aabb.a = aabb.b = {vx, vy, vz};
|
||||||
|
} else {
|
||||||
|
aabb.addPoint(glm::vec3(vx, vy, vz));
|
||||||
|
}
|
||||||
|
vx += chunk->x * CHUNK_W + 0.5f;
|
||||||
|
vy += 0.5f;
|
||||||
|
vz += chunk->z * CHUNK_D + 0.5f;
|
||||||
|
}
|
||||||
|
sortingMesh.entries.push_back(std::move(entry));
|
||||||
|
vertexOffset = 0;
|
||||||
|
indexOffset = indexSize = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// additional powerful optimization
|
||||||
|
auto size = aabb.size();
|
||||||
|
if ((size.y < 0.01f || size.x < 0.01f || size.z < 0.01f) &&
|
||||||
|
sortingMesh.entries.size() > 1) {
|
||||||
|
SortingMeshEntry newEntry {
|
||||||
|
sortingMesh.entries[0].position,
|
||||||
|
util::Buffer<float>(totalSize)
|
||||||
|
};
|
||||||
|
size_t offset = 0;
|
||||||
|
for (const auto& entry : sortingMesh.entries) {
|
||||||
|
std::memcpy(
|
||||||
|
newEntry.vertexData.data() + offset,
|
||||||
|
entry.vertexData.data(), entry.vertexData.size() * sizeof(float)
|
||||||
|
);
|
||||||
|
offset += entry.vertexData.size();
|
||||||
|
}
|
||||||
|
return SortingMeshData {{std::move(newEntry)}};
|
||||||
|
}
|
||||||
|
return sortingMesh;
|
||||||
|
}
|
||||||
|
|
||||||
void BlocksRenderer::build(const Chunk* chunk, const Chunks* chunks) {
|
void BlocksRenderer::build(const Chunk* chunk, const Chunks* chunks) {
|
||||||
this->chunk = chunk;
|
this->chunk = chunk;
|
||||||
voxelsBuffer->setPosition(
|
voxelsBuffer->setPosition(
|
||||||
chunk->x * CHUNK_W - voxelBufferPadding, 0,
|
chunk->x * CHUNK_W - voxelBufferPadding, 0,
|
||||||
chunk->z * CHUNK_D - voxelBufferPadding);
|
chunk->z * CHUNK_D - voxelBufferPadding);
|
||||||
chunks->getVoxels(voxelsBuffer.get(), settings.graphics.backlight.get());
|
chunks->getVoxels(voxelsBuffer.get(), settings.graphics.backlight.get());
|
||||||
overflow = false;
|
|
||||||
vertexOffset = 0;
|
|
||||||
indexOffset = indexSize = 0;
|
|
||||||
if (voxelsBuffer->pickBlockId(
|
if (voxelsBuffer->pickBlockId(
|
||||||
chunk->x * CHUNK_W, 0, chunk->z * CHUNK_D
|
chunk->x * CHUNK_W, 0, chunk->z * CHUNK_D
|
||||||
) == BLOCK_VOID) {
|
) == BLOCK_VOID) {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cancelled = false;
|
|
||||||
const voxel* voxels = chunk->voxels;
|
const voxel* voxels = chunk->voxels;
|
||||||
render(voxels);
|
|
||||||
|
int totalBegin = chunk->bottom * (CHUNK_W * CHUNK_D);
|
||||||
|
int totalEnd = chunk->top * (CHUNK_W * CHUNK_D);
|
||||||
|
|
||||||
|
int beginEnds[256][2] {};
|
||||||
|
for (int i = totalBegin; i < totalEnd; i++) {
|
||||||
|
const voxel& vox = voxels[i];
|
||||||
|
blockid_t id = vox.id;
|
||||||
|
const auto& def = *blockDefsCache[id];
|
||||||
|
|
||||||
|
if (beginEnds[def.drawGroup][0] == 0) {
|
||||||
|
beginEnds[def.drawGroup][0] = i+1;
|
||||||
|
}
|
||||||
|
beginEnds[def.drawGroup][1] = i;
|
||||||
|
}
|
||||||
|
cancelled = false;
|
||||||
|
|
||||||
|
overflow = false;
|
||||||
|
vertexOffset = 0;
|
||||||
|
indexOffset = indexSize = 0;
|
||||||
|
|
||||||
|
sortingMesh = std::move(renderTranslucent(voxels, beginEnds));
|
||||||
|
|
||||||
|
overflow = false;
|
||||||
|
vertexOffset = 0;
|
||||||
|
indexOffset = indexSize = 0;
|
||||||
|
|
||||||
|
render(voxels, beginEnds);
|
||||||
}
|
}
|
||||||
|
|
||||||
MeshData BlocksRenderer::createMesh() {
|
ChunkMeshData BlocksRenderer::createMesh() {
|
||||||
const vattr attrs[]{ {3}, {2}, {1}, {0} };
|
return ChunkMeshData {
|
||||||
return MeshData(
|
MeshData(
|
||||||
util::Buffer<float>(vertexBuffer.get(), vertexOffset),
|
util::Buffer<float>(vertexBuffer.get(), vertexOffset),
|
||||||
util::Buffer<int>(indexBuffer.get(), indexSize),
|
util::Buffer<int>(indexBuffer.get(), indexSize),
|
||||||
util::Buffer<vattr>({{3}, {2}, {1}, {0}})
|
util::Buffer<VertexAttribute>(
|
||||||
);
|
CHUNK_VATTRS, sizeof(CHUNK_VATTRS) / sizeof(VertexAttribute)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
std::move(sortingMesh)};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Mesh> BlocksRenderer::render(const Chunk* chunk, const Chunks* chunks) {
|
ChunkMesh BlocksRenderer::render(const Chunk* chunk, const Chunks* chunks) {
|
||||||
build(chunk, chunks);
|
build(chunk, chunks);
|
||||||
|
|
||||||
const vattr attrs[]{ {3}, {2}, {1}, {0} };
|
size_t vcount = vertexOffset / CHUNK_VERTEX_SIZE;
|
||||||
size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE;
|
return ChunkMesh{std::make_unique<Mesh>(
|
||||||
return std::make_shared<Mesh>(
|
vertexBuffer.get(), vcount, indexBuffer.get(), indexSize, CHUNK_VATTRS
|
||||||
vertexBuffer.get(), vcount, indexBuffer.get(), indexSize, attrs
|
), std::move(sortingMesh)};
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const {
|
VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const {
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#include "voxels/VoxelsVolume.hpp"
|
#include "voxels/VoxelsVolume.hpp"
|
||||||
#include "graphics/core/MeshData.hpp"
|
#include "graphics/core/MeshData.hpp"
|
||||||
#include "maths/util.hpp"
|
#include "maths/util.hpp"
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
class Content;
|
class Content;
|
||||||
class Mesh;
|
class Mesh;
|
||||||
@ -26,7 +27,6 @@ struct UVRegion;
|
|||||||
|
|
||||||
class BlocksRenderer {
|
class BlocksRenderer {
|
||||||
static const glm::vec3 SUN_VECTOR;
|
static const glm::vec3 SUN_VECTOR;
|
||||||
static const uint VERTEX_SIZE;
|
|
||||||
const Content& content;
|
const Content& content;
|
||||||
std::unique_ptr<float[]> vertexBuffer;
|
std::unique_ptr<float[]> vertexBuffer;
|
||||||
std::unique_ptr<int[]> indexBuffer;
|
std::unique_ptr<int[]> indexBuffer;
|
||||||
@ -45,6 +45,8 @@ class BlocksRenderer {
|
|||||||
|
|
||||||
util::PseudoRandom randomizer;
|
util::PseudoRandom randomizer;
|
||||||
|
|
||||||
|
SortingMeshData sortingMesh;
|
||||||
|
|
||||||
void vertex(const glm::vec3& coord, float u, float v, const glm::vec4& light);
|
void vertex(const glm::vec3& coord, float u, float v, const glm::vec4& light);
|
||||||
void index(int a, int b, int c, int d, int e, int f);
|
void index(int a, int b, int c, int d, int e, int f);
|
||||||
|
|
||||||
@ -115,7 +117,6 @@ class BlocksRenderer {
|
|||||||
|
|
||||||
bool isOpenForLight(int x, int y, int z) const;
|
bool isOpenForLight(int x, int y, int z) const;
|
||||||
|
|
||||||
|
|
||||||
// Does block allow to see other blocks sides (is it transparent)
|
// Does block allow to see other blocks sides (is it transparent)
|
||||||
inline bool isOpen(const glm::ivec3& pos, ubyte group) const {
|
inline bool isOpen(const glm::ivec3& pos, ubyte group) const {
|
||||||
auto id = voxelsBuffer->pickBlockId(
|
auto id = voxelsBuffer->pickBlockId(
|
||||||
@ -135,7 +136,9 @@ class BlocksRenderer {
|
|||||||
glm::vec4 pickLight(const glm::ivec3& coord) const;
|
glm::vec4 pickLight(const glm::ivec3& coord) const;
|
||||||
glm::vec4 pickSoftLight(const glm::ivec3& coord, const glm::ivec3& right, const glm::ivec3& up) const;
|
glm::vec4 pickSoftLight(const glm::ivec3& coord, const glm::ivec3& right, const glm::ivec3& up) const;
|
||||||
glm::vec4 pickSoftLight(float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up) const;
|
glm::vec4 pickSoftLight(float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up) const;
|
||||||
void render(const voxel* voxels);
|
|
||||||
|
void render(const voxel* voxels, int beginEnds[256][2]);
|
||||||
|
SortingMeshData renderTranslucent(const voxel* voxels, int beginEnds[256][2]);
|
||||||
public:
|
public:
|
||||||
BlocksRenderer(
|
BlocksRenderer(
|
||||||
size_t capacity,
|
size_t capacity,
|
||||||
@ -146,8 +149,8 @@ public:
|
|||||||
virtual ~BlocksRenderer();
|
virtual ~BlocksRenderer();
|
||||||
|
|
||||||
void build(const Chunk* chunk, const Chunks* chunks);
|
void build(const Chunk* chunk, const Chunks* chunks);
|
||||||
std::shared_ptr<Mesh> render(const Chunk* chunk, const Chunks* chunks);
|
ChunkMesh render(const Chunk* chunk, const Chunks* chunks);
|
||||||
MeshData createMesh();
|
ChunkMeshData createMesh();
|
||||||
VoxelsVolume* getVoxelsBuffer() const;
|
VoxelsVolume* getVoxelsBuffer() const;
|
||||||
|
|
||||||
bool isCancelled() const {
|
bool isCancelled() const {
|
||||||
|
|||||||
@ -14,10 +14,6 @@
|
|||||||
#include "util/listutil.hpp"
|
#include "util/listutil.hpp"
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/ext.hpp>
|
|
||||||
|
|
||||||
static debug::Logger logger("chunks-render");
|
static debug::Logger logger("chunks-render");
|
||||||
|
|
||||||
size_t ChunksRenderer::visibleChunks = 0;
|
size_t ChunksRenderer::visibleChunks = 0;
|
||||||
@ -62,7 +58,11 @@ ChunksRenderer::ChunksRenderer(
|
|||||||
[&](){return std::make_shared<RendererWorker>(*level, cache, settings);},
|
[&](){return std::make_shared<RendererWorker>(*level, cache, settings);},
|
||||||
[&](RendererResult& result){
|
[&](RendererResult& result){
|
||||||
if (!result.cancelled) {
|
if (!result.cancelled) {
|
||||||
meshes[result.key] = std::make_shared<Mesh>(result.meshData);
|
auto meshData = std::move(result.meshData);
|
||||||
|
meshes[result.key] = ChunkMesh {
|
||||||
|
std::make_unique<Mesh>(meshData.mesh),
|
||||||
|
std::move(meshData.sortingMesh)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
inwork.erase(result.key);
|
inwork.erase(result.key);
|
||||||
}, settings.graphics.chunkMaxRenderers.get())
|
}, settings.graphics.chunkMaxRenderers.get())
|
||||||
@ -78,12 +78,16 @@ ChunksRenderer::ChunksRenderer(
|
|||||||
ChunksRenderer::~ChunksRenderer() {
|
ChunksRenderer::~ChunksRenderer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Mesh> ChunksRenderer::render(const std::shared_ptr<Chunk>& chunk, bool important) {
|
const Mesh* ChunksRenderer::render(
|
||||||
|
const std::shared_ptr<Chunk>& chunk, bool important
|
||||||
|
) {
|
||||||
chunk->flags.modified = false;
|
chunk->flags.modified = false;
|
||||||
if (important) {
|
if (important) {
|
||||||
auto mesh = renderer->render(chunk.get(), level.chunks.get());
|
auto mesh = renderer->render(chunk.get(), level.chunks.get());
|
||||||
meshes[glm::ivec2(chunk->x, chunk->z)] = mesh;
|
meshes[glm::ivec2(chunk->x, chunk->z)] = ChunkMesh {
|
||||||
return mesh;
|
std::move(mesh.mesh), std::move(mesh.sortingMeshData)
|
||||||
|
};
|
||||||
|
return meshes[glm::ivec2(chunk->x, chunk->z)].mesh.get();
|
||||||
}
|
}
|
||||||
glm::ivec2 key(chunk->x, chunk->z);
|
glm::ivec2 key(chunk->x, chunk->z);
|
||||||
if (inwork.find(key) != inwork.end()) {
|
if (inwork.find(key) != inwork.end()) {
|
||||||
@ -107,7 +111,9 @@ void ChunksRenderer::clear() {
|
|||||||
threadPool.clearQueue();
|
threadPool.clearQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Mesh> ChunksRenderer::getOrRender(const std::shared_ptr<Chunk>& chunk, bool important) {
|
const Mesh* ChunksRenderer::getOrRender(
|
||||||
|
const std::shared_ptr<Chunk>& chunk, bool important
|
||||||
|
) {
|
||||||
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||||
if (found == meshes.end()) {
|
if (found == meshes.end()) {
|
||||||
return render(chunk, important);
|
return render(chunk, important);
|
||||||
@ -115,19 +121,19 @@ std::shared_ptr<Mesh> ChunksRenderer::getOrRender(const std::shared_ptr<Chunk>&
|
|||||||
if (chunk->flags.modified) {
|
if (chunk->flags.modified) {
|
||||||
render(chunk, important);
|
render(chunk, important);
|
||||||
}
|
}
|
||||||
return found->second;
|
return found->second.mesh.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunksRenderer::update() {
|
void ChunksRenderer::update() {
|
||||||
threadPool.update();
|
threadPool.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChunksRenderer::drawChunk(
|
const Mesh* ChunksRenderer::retrieveChunk(
|
||||||
size_t index, const Camera& camera, Shader& shader, bool culling
|
size_t index, const Camera& camera, Shader& shader, bool culling
|
||||||
) {
|
) {
|
||||||
auto chunk = level.chunks->getChunks()[index];
|
auto chunk = level.chunks->getChunks()[index];
|
||||||
if (chunk == nullptr || !chunk->flags.lighted) {
|
if (chunk == nullptr || !chunk->flags.lighted) {
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
float distance = glm::distance(
|
float distance = glm::distance(
|
||||||
camera.position,
|
camera.position,
|
||||||
@ -139,7 +145,7 @@ bool ChunksRenderer::drawChunk(
|
|||||||
);
|
);
|
||||||
auto mesh = getOrRender(chunk, distance < CHUNK_W * 1.5f);
|
auto mesh = getOrRender(chunk, distance < CHUNK_W * 1.5f);
|
||||||
if (mesh == nullptr) {
|
if (mesh == nullptr) {
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (culling) {
|
if (culling) {
|
||||||
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
|
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
|
||||||
@ -149,13 +155,9 @@ bool ChunksRenderer::drawChunk(
|
|||||||
chunk->z * CHUNK_D + CHUNK_D
|
chunk->z * CHUNK_D + CHUNK_D
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!frustum.isBoxVisible(min, max)) return false;
|
if (!frustum.isBoxVisible(min, max)) return nullptr;
|
||||||
}
|
}
|
||||||
glm::vec3 coord(chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f);
|
return mesh;
|
||||||
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
|
||||||
shader.uniformMatrix("u_model", model);
|
|
||||||
mesh->draw();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunksRenderer::drawChunks(
|
void ChunksRenderer::drawChunks(
|
||||||
@ -191,11 +193,109 @@ void ChunksRenderer::drawChunks(
|
|||||||
bool culling = settings.graphics.frustumCulling.get();
|
bool culling = settings.graphics.frustumCulling.get();
|
||||||
|
|
||||||
visibleChunks = 0;
|
visibleChunks = 0;
|
||||||
//if (GLEW_ARB_multi_draw_indirect && false) {
|
shader.uniform1i("u_alphaClip", true);
|
||||||
// TODO: implement Multi Draw Indirect chunks draw
|
|
||||||
//} else {
|
// TODO: minimize draw calls number
|
||||||
for (size_t i = 0; i < indices.size(); i++) {
|
for (size_t i = 0; i < indices.size(); i++) {
|
||||||
visibleChunks += drawChunk(indices[i].index, camera, shader, culling);
|
auto chunk = chunks.getChunks()[indices[i].index];
|
||||||
|
auto mesh = retrieveChunk(indices[i].index, camera, shader, culling);
|
||||||
|
|
||||||
|
if (mesh) {
|
||||||
|
glm::vec3 coord(
|
||||||
|
chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f
|
||||||
|
);
|
||||||
|
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
||||||
|
shader.uniformMatrix("u_model", model);
|
||||||
|
mesh->draw();
|
||||||
|
visibleChunks++;
|
||||||
}
|
}
|
||||||
//}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void write_sorting_mesh_entries(
|
||||||
|
float* buffer, const std::vector<SortingMeshEntry>& chunkEntries
|
||||||
|
) {
|
||||||
|
for (const auto& entry : chunkEntries) {
|
||||||
|
const auto& vertexData = entry.vertexData;
|
||||||
|
std::memcpy(
|
||||||
|
buffer,
|
||||||
|
vertexData.data(),
|
||||||
|
vertexData.size() * sizeof(float)
|
||||||
|
);
|
||||||
|
buffer += vertexData.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) {
|
||||||
|
const int sortInterval = TRANSLUCENT_BLOCKS_SORT_INTERVAL;
|
||||||
|
static int frameid = 0;
|
||||||
|
frameid++;
|
||||||
|
|
||||||
|
bool culling = settings.graphics.frustumCulling.get();
|
||||||
|
const auto& chunks = level.chunks->getChunks();
|
||||||
|
const auto& cameraPos = camera.position;
|
||||||
|
const auto& atlas = assets.require<Atlas>("blocks");
|
||||||
|
|
||||||
|
shader.use();
|
||||||
|
atlas.getTexture()->bind();
|
||||||
|
shader.uniformMatrix("u_model", glm::mat4(1.0f));
|
||||||
|
shader.uniform1i("u_alphaClip", false);
|
||||||
|
|
||||||
|
for (const auto& index : indices) {
|
||||||
|
const auto& chunk = chunks[index.index];
|
||||||
|
if (chunk == nullptr || !chunk->flags.lighted) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||||
|
if (found == meshes.end() || found->second.sortingMeshData.entries.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
|
||||||
|
glm::vec3 max(
|
||||||
|
chunk->x * CHUNK_W + CHUNK_W,
|
||||||
|
chunk->top,
|
||||||
|
chunk->z * CHUNK_D + CHUNK_D
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!frustum.isBoxVisible(min, max)) continue;
|
||||||
|
|
||||||
|
auto& chunkEntries = found->second.sortingMeshData.entries;
|
||||||
|
|
||||||
|
if (chunkEntries.size() == 1) {
|
||||||
|
auto& entry = chunkEntries.at(0);
|
||||||
|
if (found->second.sortedMesh == nullptr) {
|
||||||
|
found->second.sortedMesh = std::make_unique<Mesh>(
|
||||||
|
entry.vertexData.data(),
|
||||||
|
entry.vertexData.size() / CHUNK_VERTEX_SIZE,
|
||||||
|
CHUNK_VATTRS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
found->second.sortedMesh->draw();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (auto& entry : chunkEntries) {
|
||||||
|
entry.distance = static_cast<long long>(
|
||||||
|
glm::distance2(entry.position, cameraPos)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (found->second.sortedMesh == nullptr ||
|
||||||
|
(frameid + chunk->x) % sortInterval == 0) {
|
||||||
|
std::sort(chunkEntries.begin(), chunkEntries.end());
|
||||||
|
size_t size = 0;
|
||||||
|
for (const auto& entry : chunkEntries) {
|
||||||
|
size += entry.vertexData.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static util::Buffer<float> buffer;
|
||||||
|
if (buffer.size() < size) {
|
||||||
|
buffer = util::Buffer<float>(size);
|
||||||
|
}
|
||||||
|
write_sorting_mesh_entries(buffer.data(), chunkEntries);
|
||||||
|
found->second.sortedMesh = std::make_unique<Mesh>(
|
||||||
|
buffer.data(), size / CHUNK_VERTEX_SIZE, CHUNK_VATTRS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
found->second.sortedMesh->draw();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,19 +4,21 @@
|
|||||||
#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 "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
#include "voxels/ChunksStorage.hpp"
|
|
||||||
#include "util/ThreadPool.hpp"
|
#include "util/ThreadPool.hpp"
|
||||||
#include "graphics/core/MeshData.hpp"
|
#include "graphics/core/MeshData.hpp"
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
class Mesh;
|
class Mesh;
|
||||||
class Chunk;
|
class Chunk;
|
||||||
class Level;
|
class Level;
|
||||||
class Camera;
|
class Camera;
|
||||||
class Shader;
|
class Shader;
|
||||||
class Chunks;
|
|
||||||
class Assets;
|
class Assets;
|
||||||
class Frustum;
|
class Frustum;
|
||||||
class BlocksRenderer;
|
class BlocksRenderer;
|
||||||
@ -35,7 +37,7 @@ struct ChunksSortEntry {
|
|||||||
struct RendererResult {
|
struct RendererResult {
|
||||||
glm::ivec2 key;
|
glm::ivec2 key;
|
||||||
bool cancelled;
|
bool cancelled;
|
||||||
MeshData meshData;
|
ChunkMeshData meshData;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ChunksRenderer {
|
class ChunksRenderer {
|
||||||
@ -45,12 +47,11 @@ class ChunksRenderer {
|
|||||||
const EngineSettings& settings;
|
const EngineSettings& settings;
|
||||||
|
|
||||||
std::unique_ptr<BlocksRenderer> renderer;
|
std::unique_ptr<BlocksRenderer> renderer;
|
||||||
std::unordered_map<glm::ivec2, std::shared_ptr<Mesh>> meshes;
|
std::unordered_map<glm::ivec2, ChunkMesh> meshes;
|
||||||
std::unordered_map<glm::ivec2, bool> inwork;
|
std::unordered_map<glm::ivec2, bool> inwork;
|
||||||
std::vector<ChunksSortEntry> indices;
|
std::vector<ChunksSortEntry> indices;
|
||||||
util::ThreadPool<std::shared_ptr<Chunk>, RendererResult> threadPool;
|
util::ThreadPool<std::shared_ptr<Chunk>, RendererResult> threadPool;
|
||||||
|
const Mesh* retrieveChunk(
|
||||||
bool drawChunk(
|
|
||||||
size_t index, const Camera& camera, Shader& shader, bool culling
|
size_t index, const Camera& camera, Shader& shader, bool culling
|
||||||
);
|
);
|
||||||
public:
|
public:
|
||||||
@ -63,17 +64,19 @@ public:
|
|||||||
);
|
);
|
||||||
virtual ~ChunksRenderer();
|
virtual ~ChunksRenderer();
|
||||||
|
|
||||||
std::shared_ptr<Mesh> render(
|
const Mesh* render(
|
||||||
const std::shared_ptr<Chunk>& chunk, bool important
|
const std::shared_ptr<Chunk>& chunk, bool important
|
||||||
);
|
);
|
||||||
void unload(const Chunk* chunk);
|
void unload(const Chunk* chunk);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
std::shared_ptr<Mesh> getOrRender(
|
const Mesh* getOrRender(
|
||||||
const std::shared_ptr<Chunk>& chunk, bool important
|
const std::shared_ptr<Chunk>& chunk, bool important
|
||||||
);
|
);
|
||||||
void drawChunks(const Camera& camera, Shader& shader);
|
void drawChunks(const Camera& camera, Shader& shader);
|
||||||
|
|
||||||
|
void drawSortedMeshes(const Camera& camera, Shader& shader);
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
static size_t visibleChunks;
|
static size_t visibleChunks;
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
#include "voxels/Chunks.hpp"
|
#include "voxels/Chunks.hpp"
|
||||||
#include "voxels/Chunk.hpp"
|
#include "voxels/Chunk.hpp"
|
||||||
|
|
||||||
static const vattr attrs[] = {
|
static const VertexAttribute attrs[] = {
|
||||||
{3}, {2}, {3}, {1}, {0}
|
{3}, {2}, {3}, {1}, {0}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -97,38 +97,38 @@ void MainBatch::cube(
|
|||||||
const glm::vec3 Z(0.0f, 0.0f, 1.0f);
|
const glm::vec3 Z(0.0f, 0.0f, 1.0f);
|
||||||
|
|
||||||
quad(
|
quad(
|
||||||
coord + glm::vec3(0.0f, 0.0f, 0.0f),
|
coord + Z * size.z * 0.5f,
|
||||||
X, Y, glm::vec2(size.x, size.y),
|
X, Y, glm::vec2(size.x, size.y),
|
||||||
(shading ? do_tint(0.8) * tint : tint),
|
(shading ? do_tint(0.8) * tint : tint),
|
||||||
glm::vec3(1.0f), texfaces[5]
|
glm::vec3(1.0f), texfaces[5]
|
||||||
);
|
);
|
||||||
quad(
|
quad(
|
||||||
coord + glm::vec3(size.x, 0.0f, -size.z),
|
coord - Z * size.z * 0.5f,
|
||||||
-X, Y, glm::vec2(size.x, size.y),
|
-X, Y, glm::vec2(size.x, size.y),
|
||||||
(shading ? do_tint(0.8) * tint : tint),
|
(shading ? do_tint(0.9f) * tint : tint),
|
||||||
glm::vec3(1.0f), texfaces[4]
|
glm::vec3(1.0f), texfaces[4]
|
||||||
);
|
);
|
||||||
quad(
|
quad(
|
||||||
coord + glm::vec3(0.0f, size.y, 0.0f),
|
coord + Y * size.y * 0.5f,
|
||||||
X, -Z, glm::vec2(size.x, size.z),
|
-X, Z, glm::vec2(size.x, size.z),
|
||||||
(shading ? do_tint(1.0f) * tint : tint),
|
(shading ? do_tint(1.0f) * tint : tint),
|
||||||
glm::vec3(1.0f), texfaces[3]
|
glm::vec3(1.0f), texfaces[3]
|
||||||
);
|
);
|
||||||
quad(
|
quad(
|
||||||
coord + glm::vec3(0.0f, 0.0f, -size.z),
|
coord - Y * size.y * 0.5f,
|
||||||
X, Z, glm::vec2(size.x, size.z),
|
X, Z, glm::vec2(size.x, size.z),
|
||||||
(shading ? do_tint(0.7f) * tint : tint),
|
(shading ? do_tint(0.7f) * tint : tint),
|
||||||
glm::vec3(1.0f), texfaces[2]
|
glm::vec3(1.0f), texfaces[2]
|
||||||
);
|
);
|
||||||
quad(
|
quad(
|
||||||
coord + glm::vec3(0.0f, 0.0f, -size.z),
|
coord + X * size.x * 0.5f,
|
||||||
Z, Y, glm::vec2(size.z, size.y),
|
-Z, Y, glm::vec2(size.z, size.y),
|
||||||
(shading ? do_tint(0.9f) * tint : tint),
|
(shading ? do_tint(0.8f) * tint : tint),
|
||||||
glm::vec3(1.0f), texfaces[0]
|
glm::vec3(1.0f), texfaces[1]
|
||||||
);
|
);
|
||||||
quad(
|
quad(
|
||||||
coord + glm::vec3(size.x, 0.0f, 0.0f),
|
coord - X * size.x * 0.5f,
|
||||||
-Z, Y, glm::vec2(size.z, size.y),
|
Z, Y, glm::vec2(size.z, size.y),
|
||||||
(shading ? do_tint(0.9f) * tint : tint),
|
(shading ? do_tint(0.9f) * tint : tint),
|
||||||
glm::vec3(1.0f), texfaces[1]
|
glm::vec3(1.0f), texfaces[1]
|
||||||
);
|
);
|
||||||
|
|||||||
@ -89,11 +89,13 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
|||||||
);
|
);
|
||||||
light *= 0.9f + (particle.random % 100) * 0.001f;
|
light *= 0.9f + (particle.random % 100) * 0.001f;
|
||||||
}
|
}
|
||||||
|
float scale = 1.0f + ((particle.random ^ 2628172) % 1000) *
|
||||||
|
0.001f * preset.sizeSpread;
|
||||||
batch->quad(
|
batch->quad(
|
||||||
particle.position,
|
particle.position,
|
||||||
right,
|
right,
|
||||||
preset.globalUpVector ? glm::vec3(0, 1, 0) : up,
|
preset.globalUpVector ? glm::vec3(0, 1, 0) : up,
|
||||||
preset.size,
|
preset.size * scale,
|
||||||
light,
|
light,
|
||||||
glm::vec3(1.0f),
|
glm::vec3(1.0f),
|
||||||
particle.region
|
particle.region
|
||||||
|
|||||||
@ -39,7 +39,7 @@ Skybox::Skybox(uint size, Shader& shader)
|
|||||||
-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
||||||
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f
|
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f
|
||||||
};
|
};
|
||||||
vattr attrs[] {{2}, {0}};
|
VertexAttribute attrs[] {{2}, {0}};
|
||||||
mesh = std::make_unique<Mesh>(vertices, 6, attrs);
|
mesh = std::make_unique<Mesh>(vertices, 6, attrs);
|
||||||
|
|
||||||
sprites.push_back(skysprite {
|
sprites.push_back(skysprite {
|
||||||
|
|||||||
@ -41,6 +41,7 @@
|
|||||||
#include "graphics/core/Shader.hpp"
|
#include "graphics/core/Shader.hpp"
|
||||||
#include "graphics/core/Texture.hpp"
|
#include "graphics/core/Texture.hpp"
|
||||||
#include "graphics/core/Font.hpp"
|
#include "graphics/core/Font.hpp"
|
||||||
|
#include "BlockWrapsRenderer.hpp"
|
||||||
#include "ParticlesRenderer.hpp"
|
#include "ParticlesRenderer.hpp"
|
||||||
#include "TextsRenderer.hpp"
|
#include "TextsRenderer.hpp"
|
||||||
#include "ChunksRenderer.hpp"
|
#include "ChunksRenderer.hpp"
|
||||||
@ -80,7 +81,8 @@ WorldRenderer::WorldRenderer(
|
|||||||
*frustumCulling,
|
*frustumCulling,
|
||||||
frontend.getContentGfxCache(),
|
frontend.getContentGfxCache(),
|
||||||
engine->getSettings()
|
engine->getSettings()
|
||||||
)) {
|
)),
|
||||||
|
blockWraps(std::make_unique<BlockWrapsRenderer>(assets, level)) {
|
||||||
auto& settings = engine->getSettings();
|
auto& settings = engine->getSettings();
|
||||||
level.events->listen(
|
level.events->listen(
|
||||||
EVT_CHUNK_HIDDEN,
|
EVT_CHUNK_HIDDEN,
|
||||||
@ -153,6 +155,7 @@ void WorldRenderer::renderLevel(
|
|||||||
frustumCulling->update(camera.getProjView());
|
frustumCulling->update(camera.getProjView());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entityShader.uniform1i("u_alphaClip", true);
|
||||||
level.entities->render(
|
level.entities->render(
|
||||||
assets,
|
assets,
|
||||||
*modelBatch,
|
*modelBatch,
|
||||||
@ -164,9 +167,18 @@ void WorldRenderer::renderLevel(
|
|||||||
particles->render(camera, delta * !pause);
|
particles->render(camera, delta * !pause);
|
||||||
|
|
||||||
auto& shader = assets.require<Shader>("main");
|
auto& shader = assets.require<Shader>("main");
|
||||||
|
auto& linesShader = assets.require<Shader>("lines");
|
||||||
|
|
||||||
setupWorldShader(shader, camera, settings, fogFactor);
|
setupWorldShader(shader, camera, settings, fogFactor);
|
||||||
|
|
||||||
chunks->drawChunks(camera, shader);
|
chunks->drawChunks(camera, shader);
|
||||||
|
blockWraps->draw(ctx, *player);
|
||||||
|
|
||||||
|
if (hudVisible) {
|
||||||
|
renderLines(camera, linesShader, ctx);
|
||||||
|
}
|
||||||
|
shader.use();
|
||||||
|
chunks->drawSortedMeshes(camera, shader);
|
||||||
|
|
||||||
if (!pause) {
|
if (!pause) {
|
||||||
scripting::on_frontend_render();
|
scripting::on_frontend_render();
|
||||||
@ -326,7 +338,6 @@ void WorldRenderer::draw(
|
|||||||
ctx, camera, *lineBatch, linesShader, showChunkBorders
|
ctx, camera, *lineBatch, linesShader, showChunkBorders
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
renderLines(camera, linesShader, ctx);
|
|
||||||
if (player->currentCamera == player->fpCamera) {
|
if (player->currentCamera == player->fpCamera) {
|
||||||
renderHands(camera, delta * !pause);
|
renderHands(camera, delta * !pause);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ class Batch3D;
|
|||||||
class LineBatch;
|
class LineBatch;
|
||||||
class ChunksRenderer;
|
class ChunksRenderer;
|
||||||
class ParticlesRenderer;
|
class ParticlesRenderer;
|
||||||
|
class BlockWrapsRenderer;
|
||||||
class GuidesRenderer;
|
class GuidesRenderer;
|
||||||
class TextsRenderer;
|
class TextsRenderer;
|
||||||
class Shader;
|
class Shader;
|
||||||
@ -68,6 +69,7 @@ class WorldRenderer {
|
|||||||
public:
|
public:
|
||||||
std::unique_ptr<TextsRenderer> texts;
|
std::unique_ptr<TextsRenderer> texts;
|
||||||
std::unique_ptr<ParticlesRenderer> particles;
|
std::unique_ptr<ParticlesRenderer> particles;
|
||||||
|
std::unique_ptr<BlockWrapsRenderer> blockWraps;
|
||||||
|
|
||||||
static bool showChunkBorders;
|
static bool showChunkBorders;
|
||||||
static bool showEntitiesDebug;
|
static bool showEntitiesDebug;
|
||||||
|
|||||||
40
src/graphics/render/commons.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
|
||||||
|
#include "graphics/core/MeshData.hpp"
|
||||||
|
#include "util/Buffer.hpp"
|
||||||
|
|
||||||
|
/// @brief Chunk mesh vertex attributes
|
||||||
|
inline const VertexAttribute CHUNK_VATTRS[]{ {3}, {2}, {1}, {0} };
|
||||||
|
/// @brief Chunk mesh vertex size divided by sizeof(float)
|
||||||
|
inline constexpr int CHUNK_VERTEX_SIZE = 6;
|
||||||
|
|
||||||
|
class Mesh;
|
||||||
|
|
||||||
|
struct SortingMeshEntry {
|
||||||
|
glm::vec3 position;
|
||||||
|
util::Buffer<float> vertexData;
|
||||||
|
long long distance;
|
||||||
|
|
||||||
|
inline bool operator<(const SortingMeshEntry& o) const noexcept {
|
||||||
|
return distance > o.distance;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SortingMeshData {
|
||||||
|
std::vector<SortingMeshEntry> entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChunkMeshData {
|
||||||
|
MeshData mesh;
|
||||||
|
SortingMeshData sortingMesh;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChunkMesh {
|
||||||
|
std::unique_ptr<Mesh> mesh;
|
||||||
|
SortingMeshData sortingMeshData;
|
||||||
|
std::unique_ptr<Mesh> sortedMesh = nullptr;
|
||||||
|
};
|
||||||
@ -90,7 +90,7 @@ void Container::draw(const DrawContext* pctx, 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, size.x, 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);
|
||||||
@ -108,7 +108,7 @@ void Container::drawBackground(const DrawContext* pctx, Assets*) {
|
|||||||
auto batch = pctx->getBatch2D();
|
auto batch = pctx->getBatch2D();
|
||||||
batch->texture(nullptr);
|
batch->texture(nullptr);
|
||||||
batch->setColor(color);
|
batch->setColor(color);
|
||||||
batch->rect(pos.x, pos.y, size.x, size.y);
|
batch->rect(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Container::add(const std::shared_ptr<UINode> &node) {
|
void Container::add(const std::shared_ptr<UINode> &node) {
|
||||||
@ -165,6 +165,14 @@ void Container::setSize(glm::vec2 size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Container::getScrollStep() const {
|
||||||
|
return scrollStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Container::setScrollStep(int step) {
|
||||||
|
scrollStep = step;
|
||||||
|
}
|
||||||
|
|
||||||
void Container::refresh() {
|
void Container::refresh() {
|
||||||
std::stable_sort(nodes.begin(), nodes.end(), [](const auto& a, const auto& b) {
|
std::stable_sort(nodes.begin(), nodes.end(), [](const auto& a, const auto& b) {
|
||||||
return a->getZIndex() < b->getZIndex();
|
return a->getZIndex() < b->getZIndex();
|
||||||
|
|||||||
@ -32,6 +32,8 @@ namespace gui {
|
|||||||
void listenInterval(float interval, ontimeout callback, int repeat=-1);
|
void listenInterval(float interval, ontimeout callback, int repeat=-1);
|
||||||
virtual glm::vec2 getContentOffset() override {return glm::vec2(0.0f, scroll);};
|
virtual glm::vec2 getContentOffset() override {return glm::vec2(0.0f, scroll);};
|
||||||
virtual void setSize(glm::vec2 size) override;
|
virtual void setSize(glm::vec2 size) override;
|
||||||
|
virtual int getScrollStep() const;
|
||||||
|
virtual void setScrollStep(int step);
|
||||||
virtual void refresh() override;
|
virtual void refresh() override;
|
||||||
|
|
||||||
const std::vector<std::shared_ptr<UINode>>& getNodes() const;
|
const std::vector<std::shared_ptr<UINode>>& getNodes() const;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#include "TextBox.hpp"
|
#include "TextBox.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@ -14,24 +15,39 @@
|
|||||||
|
|
||||||
using namespace gui;
|
using namespace gui;
|
||||||
|
|
||||||
|
inline constexpr int LINE_NUMBERS_PANE_WIDTH = 40;
|
||||||
|
|
||||||
TextBox::TextBox(std::wstring placeholder, glm::vec4 padding)
|
TextBox::TextBox(std::wstring placeholder, glm::vec4 padding)
|
||||||
: Panel(glm::vec2(200,32), padding, 0),
|
: Container(glm::vec2(200,32)),
|
||||||
|
padding(padding),
|
||||||
input(L""),
|
input(L""),
|
||||||
placeholder(std::move(placeholder))
|
placeholder(std::move(placeholder))
|
||||||
{
|
{
|
||||||
setOnUpPressed(nullptr);
|
setOnUpPressed(nullptr);
|
||||||
setOnDownPressed(nullptr);
|
setOnDownPressed(nullptr);
|
||||||
|
setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.75f));
|
||||||
label = std::make_shared<Label>(L"");
|
label = std::make_shared<Label>(L"");
|
||||||
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
||||||
|
label->setPos(glm::vec2(
|
||||||
|
padding.x + LINE_NUMBERS_PANE_WIDTH * showLineNumbers, padding.y
|
||||||
|
));
|
||||||
add(label);
|
add(label);
|
||||||
|
|
||||||
|
lineNumbersLabel = std::make_shared<Label>(L"");
|
||||||
|
lineNumbersLabel->setMultiline(true);
|
||||||
|
lineNumbersLabel->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
||||||
|
lineNumbersLabel->setVerticalAlign(Align::top);
|
||||||
|
add(lineNumbersLabel);
|
||||||
|
|
||||||
setHoverColor(glm::vec4(0.05f, 0.1f, 0.2f, 0.75f));
|
setHoverColor(glm::vec4(0.05f, 0.1f, 0.2f, 0.75f));
|
||||||
|
|
||||||
textInitX = label->getPos().x;
|
textInitX = label->getPos().x;
|
||||||
scrollable = true;
|
scrollable = true;
|
||||||
|
scrollStep = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextBox::draw(const DrawContext* pctx, Assets* assets) {
|
void TextBox::draw(const DrawContext* pctx, Assets* assets) {
|
||||||
Panel::draw(pctx, assets);
|
Container::draw(pctx, assets);
|
||||||
|
|
||||||
font = assets->get<Font>(label->getFontName());
|
font = assets->get<Font>(label->getFontName());
|
||||||
|
|
||||||
@ -76,6 +92,44 @@ void TextBox::draw(const DrawContext* pctx, Assets* assets) {
|
|||||||
batch->rect(lcoord.x, lcoord.y+label->getLineYOffset(endLine), end, lineHeight);
|
batch->rect(lcoord.x, lcoord.y+label->getLineYOffset(endLine), end, lineHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isFocused() && multiline) {
|
||||||
|
auto selectionCtx = subctx.sub(batch);
|
||||||
|
selectionCtx.setBlendMode(BlendMode::addition);
|
||||||
|
|
||||||
|
batch->setColor(glm::vec4(1, 1, 1, 0.1f));
|
||||||
|
|
||||||
|
uint line = label->getLineByTextIndex(caret);
|
||||||
|
while (label->isFakeLine(line)) {
|
||||||
|
line--;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
int lineY = label->getLineYOffset(line);
|
||||||
|
int lineHeight = font->getLineHeight() * label->getLineInterval();
|
||||||
|
|
||||||
|
batch->setColor(glm::vec4(1, 1, 1, 0.05f));
|
||||||
|
if (showLineNumbers) {
|
||||||
|
batch->rect(
|
||||||
|
lcoord.x - 8,
|
||||||
|
lcoord.y + lineY,
|
||||||
|
label->getSize().x,
|
||||||
|
lineHeight
|
||||||
|
);
|
||||||
|
batch->setColor(glm::vec4(1, 1, 1, 0.10f));
|
||||||
|
batch->rect(
|
||||||
|
lcoord.x - LINE_NUMBERS_PANE_WIDTH,
|
||||||
|
lcoord.y + lineY,
|
||||||
|
LINE_NUMBERS_PANE_WIDTH - 8,
|
||||||
|
lineHeight
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
batch->rect(
|
||||||
|
lcoord.x, lcoord.y + lineY, label->getSize().x, lineHeight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
line++;
|
||||||
|
} while (line < label->getLinesNumber() && label->isFakeLine(line));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextBox::drawBackground(const DrawContext* pctx, Assets*) {
|
void TextBox::drawBackground(const DrawContext* pctx, Assets*) {
|
||||||
@ -103,31 +157,31 @@ void TextBox::drawBackground(const DrawContext* pctx, Assets*) {
|
|||||||
if (!isFocused() && supplier) {
|
if (!isFocused() && supplier) {
|
||||||
input = supplier();
|
input = supplier();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFocused() && multiline) {
|
|
||||||
batch->setColor(glm::vec4(1, 1, 1, 0.1f));
|
|
||||||
glm::vec2 lcoord = label->calcPos();
|
|
||||||
lcoord.y -= 2;
|
|
||||||
|
|
||||||
uint line = label->getLineByTextIndex(caret);
|
|
||||||
while (label->isFakeLine(line)) {
|
|
||||||
line--;
|
|
||||||
}
|
|
||||||
batch->setColor(glm::vec4(1, 1, 1, 0.05f));
|
|
||||||
do {
|
|
||||||
int lineY = label->getLineYOffset(line);
|
|
||||||
int lineHeight = font->getLineHeight() * label->getLineInterval();
|
|
||||||
|
|
||||||
batch->rect(lcoord.x, lcoord.y+lineY, label->getSize().x, lineHeight);
|
|
||||||
line++;
|
|
||||||
} while (line < label->getLinesNumber() && label->isFakeLine(line));
|
|
||||||
}
|
|
||||||
refreshLabel();
|
refreshLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextBox::refreshLabel() {
|
void TextBox::refreshLabel() {
|
||||||
label->setColor(glm::vec4(input.empty() ? 0.5f : 1.0f));
|
label->setColor(textColor * glm::vec4(input.empty() ? 0.5f : 1.0f));
|
||||||
label->setText(input.empty() && !hint.empty() ? hint : getText());
|
label->setText(input.empty() && !hint.empty() ? hint : getText());
|
||||||
|
|
||||||
|
if (showLineNumbers) {
|
||||||
|
if (lineNumbersLabel->getLinesNumber() != label->getLinesNumber()) {
|
||||||
|
std::wstringstream ss;
|
||||||
|
int n = 1;
|
||||||
|
for (int i = 1; i <= label->getLinesNumber(); i++) {
|
||||||
|
if (!label->isFakeLine(i-1)) {
|
||||||
|
ss << n;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
if (i + 1 <= label->getLinesNumber()) {
|
||||||
|
ss << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lineNumbersLabel->setText(ss.str());
|
||||||
|
}
|
||||||
|
lineNumbersLabel->setPos(padding);
|
||||||
|
lineNumbersLabel->setColor(glm::vec4(1, 1, 1, 0.25f));
|
||||||
|
}
|
||||||
|
|
||||||
if (autoresize && font) {
|
if (autoresize && font) {
|
||||||
auto size = getSize();
|
auto size = getSize();
|
||||||
@ -293,7 +347,7 @@ bool TextBox::isAutoResize() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TextBox::onFocus(GUI* gui) {
|
void TextBox::onFocus(GUI* gui) {
|
||||||
Panel::onFocus(gui);
|
Container::onFocus(gui);
|
||||||
if (onEditStart){
|
if (onEditStart){
|
||||||
setCaret(input.size());
|
setCaret(input.size());
|
||||||
onEditStart();
|
onEditStart();
|
||||||
@ -302,8 +356,11 @@ void TextBox::onFocus(GUI* gui) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TextBox::refresh() {
|
void TextBox::refresh() {
|
||||||
Panel::refresh();
|
Container::refresh();
|
||||||
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
|
||||||
|
label->setPos(glm::vec2(
|
||||||
|
padding.x + LINE_NUMBERS_PANE_WIDTH * showLineNumbers, padding.y
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Clamp index to range [0, input.length()]
|
/// @brief Clamp index to range [0, input.length()]
|
||||||
@ -567,6 +624,14 @@ void TextBox::select(int start, int end) {
|
|||||||
setCaret(selectionEnd);
|
setCaret(selectionEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint TextBox::getLineAt(size_t position) const {
|
||||||
|
return label->getLineByTextIndex(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t TextBox::getLinePos(uint line) const {
|
||||||
|
return label->getTextLineOffset(line);
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<UINode> TextBox::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) {
|
std::shared_ptr<UINode> TextBox::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) {
|
||||||
return UINode::getAt(pos, self);
|
return UINode::getAt(pos, self);
|
||||||
}
|
}
|
||||||
@ -619,6 +684,15 @@ glm::vec4 TextBox::getFocusedColor() const {
|
|||||||
return focusedColor;
|
return focusedColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TextBox::setTextColor(glm::vec4 color) {
|
||||||
|
this->textColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec4 TextBox::getTextColor() const {
|
||||||
|
return textColor;
|
||||||
|
}
|
||||||
|
|
||||||
void TextBox::setErrorColor(glm::vec4 color) {
|
void TextBox::setErrorColor(glm::vec4 color) {
|
||||||
this->invalidColor = color;
|
this->invalidColor = color;
|
||||||
}
|
}
|
||||||
@ -673,9 +747,11 @@ void TextBox::setCaret(size_t position) {
|
|||||||
uint line = label->getLineByTextIndex(caret);
|
uint line = label->getLineByTextIndex(caret);
|
||||||
int offset = label->getLineYOffset(line) + getContentOffset().y;
|
int offset = label->getLineYOffset(line) + getContentOffset().y;
|
||||||
uint lineHeight = font->getLineHeight()*label->getLineInterval();
|
uint lineHeight = font->getLineHeight()*label->getLineInterval();
|
||||||
scrollStep = lineHeight;
|
if (scrollStep == 0) {
|
||||||
|
scrollStep = lineHeight;
|
||||||
|
}
|
||||||
if (offset < 0) {
|
if (offset < 0) {
|
||||||
scrolled(1);
|
scrolled(-glm::floor(offset/static_cast<double>(scrollStep)+0.5f));
|
||||||
} else if (offset >= getSize().y) {
|
} else if (offset >= getSize().y) {
|
||||||
offset -= getSize().y;
|
offset -= getSize().y;
|
||||||
scrolled(-glm::ceil(offset/static_cast<double>(scrollStep)+0.5f));
|
scrolled(-glm::ceil(offset/static_cast<double>(scrollStep)+0.5f));
|
||||||
@ -696,3 +772,20 @@ void TextBox::setCaret(ptrdiff_t position) {
|
|||||||
setCaret(static_cast<size_t>(position));
|
setCaret(static_cast<size_t>(position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextBox::setPadding(glm::vec4 padding) {
|
||||||
|
this->padding = padding;
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec4 TextBox::getPadding() const {
|
||||||
|
return padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBox::setShowLineNumbers(bool flag) {
|
||||||
|
showLineNumbers = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextBox::isShowLineNumbers() const {
|
||||||
|
return showLineNumbers;
|
||||||
|
}
|
||||||
|
|||||||
@ -8,11 +8,14 @@ class Font;
|
|||||||
namespace gui {
|
namespace gui {
|
||||||
class Label;
|
class Label;
|
||||||
|
|
||||||
class TextBox : public Panel {
|
class TextBox : public Container {
|
||||||
protected:
|
protected:
|
||||||
glm::vec4 focusedColor {0.0f, 0.0f, 0.0f, 1.0f};
|
glm::vec4 focusedColor {0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
glm::vec4 invalidColor {0.1f, 0.05f, 0.03f, 1.0f};
|
glm::vec4 invalidColor {0.1f, 0.05f, 0.03f, 1.0f};
|
||||||
|
glm::vec4 textColor {1.0f, 1.0f, 1.0f, 1.0f};
|
||||||
|
glm::vec4 padding {2};
|
||||||
std::shared_ptr<Label> label;
|
std::shared_ptr<Label> label;
|
||||||
|
std::shared_ptr<Label> lineNumbersLabel;
|
||||||
/// @brief Current user input
|
/// @brief Current user input
|
||||||
std::wstring input;
|
std::wstring input;
|
||||||
/// @brief Text will be used if nothing entered
|
/// @brief Text will be used if nothing entered
|
||||||
@ -52,6 +55,7 @@ namespace gui {
|
|||||||
bool multiline = false;
|
bool multiline = false;
|
||||||
bool editable = true;
|
bool editable = true;
|
||||||
bool autoresize = false;
|
bool autoresize = false;
|
||||||
|
bool showLineNumbers = false;
|
||||||
|
|
||||||
void stepLeft(bool shiftPressed, bool breakSelection);
|
void stepLeft(bool shiftPressed, bool breakSelection);
|
||||||
void stepRight(bool shiftPressed, bool breakSelection);
|
void stepRight(bool shiftPressed, bool breakSelection);
|
||||||
@ -106,6 +110,9 @@ namespace gui {
|
|||||||
virtual void setFocusedColor(glm::vec4 color);
|
virtual void setFocusedColor(glm::vec4 color);
|
||||||
virtual glm::vec4 getFocusedColor() const;
|
virtual glm::vec4 getFocusedColor() const;
|
||||||
|
|
||||||
|
virtual void setTextColor(glm::vec4 color);
|
||||||
|
virtual 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);
|
virtual void setErrorColor(glm::vec4 color);
|
||||||
|
|
||||||
@ -152,6 +159,16 @@ namespace gui {
|
|||||||
/// @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);
|
virtual void select(int start, int end);
|
||||||
|
|
||||||
|
/// @brief Get number of line at specific position in text
|
||||||
|
/// @param position target position
|
||||||
|
/// @return line number
|
||||||
|
virtual uint getLineAt(size_t position) const;
|
||||||
|
|
||||||
|
/// @brief Get specific line text position
|
||||||
|
/// @param line target line
|
||||||
|
/// @return line position in text
|
||||||
|
virtual size_t getLinePos(uint 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();
|
virtual bool validate();
|
||||||
@ -177,12 +194,18 @@ namespace gui {
|
|||||||
/// @brief Check if text editing feature is enabled
|
/// @brief Check if text editing feature is enabled
|
||||||
virtual bool isEditable() const;
|
virtual bool isEditable() const;
|
||||||
|
|
||||||
|
virtual void setPadding(glm::vec4 padding);
|
||||||
|
glm::vec4 getPadding() const;
|
||||||
|
|
||||||
/// @brief Set runnable called on textbox focus
|
/// @brief Set runnable called on textbox focus
|
||||||
virtual void setOnEditStart(runnable oneditstart);
|
virtual void setOnEditStart(runnable oneditstart);
|
||||||
|
|
||||||
virtual void setAutoResize(bool flag);
|
virtual void setAutoResize(bool flag);
|
||||||
virtual bool isAutoResize() const;
|
virtual bool isAutoResize() const;
|
||||||
|
|
||||||
|
virtual void setShowLineNumbers(bool flag);
|
||||||
|
virtual bool isShowLineNumbers() const;
|
||||||
|
|
||||||
virtual void onFocus(GUI*) override;
|
virtual void onFocus(GUI*) override;
|
||||||
virtual void refresh() override;
|
virtual void refresh() override;
|
||||||
virtual void doubleClick(GUI*, int x, int y) override;
|
virtual void doubleClick(GUI*, int x, int y) override;
|
||||||
|
|||||||
@ -81,7 +81,7 @@ namespace gui {
|
|||||||
/// @brief element color when clicked
|
/// @brief element color when clicked
|
||||||
glm::vec4 pressedColor {1.0f};
|
glm::vec4 pressedColor {1.0f};
|
||||||
/// @brief element margin (only supported for Panel sub-nodes)
|
/// @brief element margin (only supported for Panel sub-nodes)
|
||||||
glm::vec4 margin {1.0f};
|
glm::vec4 margin {0.0f};
|
||||||
/// @brief is element visible
|
/// @brief is element visible
|
||||||
bool visible = true;
|
bool visible = true;
|
||||||
/// @brief is mouse over the element
|
/// @brief is mouse over the element
|
||||||
|
|||||||