diff --git a/doc/en/block-properties.md b/doc/en/block-properties.md index cef58a9a..18c068ca 100644 --- a/doc/en/block-properties.md +++ b/doc/en/block-properties.md @@ -39,6 +39,12 @@ Block model type from list: 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 profile (set of available block rotations and behaviour of placing block rotation) from list: diff --git a/doc/en/scripting/builtins/libhud.md b/doc/en/scripting/builtins/libhud.md index 4beb5d93..963e428c 100644 --- a/doc/en/scripting/builtins/libhud.md +++ b/doc/en/scripting/builtins/libhud.md @@ -24,7 +24,9 @@ hud.open_block(x: int, y: int, z: int) -> int, str ```lua -- Show overlay with layout specified. -- 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. -- The element will be removed on world close only. diff --git a/doc/en/scripting/builtins/libplayer.md b/doc/en/scripting/builtins/libplayer.md index 4ef892dd..99482c68 100644 --- a/doc/en/scripting/builtins/libplayer.md +++ b/doc/en/scripting/builtins/libplayer.md @@ -56,12 +56,26 @@ player.set_noclip(bool) 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 player.set_spawnpoint(playerid: int, x: number, y: number, z: number) player.get_spawnpoint(playerid: int) -> number, number, number ``` -Point setter and getter added by player +Spawn point setter and getter ```lua player.get_selected_block(playerid: int) -> x,y,z diff --git a/doc/en/scripting/builtins/libutf8.md b/doc/en/scripting/builtins/libutf8.md index 86714fce..bd8dd22b 100644 --- a/doc/en/scripting/builtins/libutf8.md +++ b/doc/en/scripting/builtins/libutf8.md @@ -27,4 +27,7 @@ utf8.upper(text: str) -> str -- Converts a string to lowercase utf8.lower(text: str) -> str + +-- Escapes a string +utf8.escape(text: str) -> str ``` diff --git a/doc/en/scripting/events.md b/doc/en/scripting/events.md index ccc61673..8dcd1a06 100644 --- a/doc/en/scripting/events.md +++ b/doc/en/scripting/events.md @@ -38,7 +38,13 @@ Called on random block update (grass growth) 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 diff --git a/doc/en/scripting/ui.md b/doc/en/scripting/ui.md index 0a6ad4c3..4f132462 100644 --- a/doc/en/scripting/ui.md +++ b/doc/en/scripting/ui.md @@ -78,14 +78,18 @@ Properties: | 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 | | multiline | bool | yes | yes | multiline support | +| lineNumbers | bool | yes | yes | display line numbers | | textWrap | bool | yes | yes | automatic text wrapping (only with multiline: "true") | | valid | bool | yes | no | is the entered text correct | +| textColor | vec4 | yes | yes | text color | Methods: -| Method | Description | -| ----------- | ------------------------------------------------ | -| paste(text) | inserts the specified text at the caret position | +| Method | Description | +| ------------------------- | ---------------------------------------------------------------- | +| 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) diff --git a/doc/en/xml-ui-layouts.md b/doc/en/xml-ui-layouts.md index ac657e44..788814e5 100644 --- a/doc/en/xml-ui-layouts.md +++ b/doc/en/xml-ui-layouts.md @@ -56,7 +56,8 @@ Buttons and panels are also containers. - `padding` - element padding. Type: 4D vector. *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 @@ -104,7 +105,9 @@ Inner text - initially entered text - `multiline` - allows display of multiline text. - `text-wrap` - allows automatic text wrapping (works only with multiline: "true") - `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. +- `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. - `onup` - lua function called when the up arrow is pressed. - `ondown` - lua function called when the down arrow is pressed. diff --git a/doc/ru/block-properties.md b/doc/ru/block-properties.md index 62ef8ec4..79b08da2 100644 --- a/doc/ru/block-properties.md +++ b/doc/ru/block-properties.md @@ -40,6 +40,12 @@ Целое число определяющее номер группы отрисовки данного блока. Актуально для полупрозрачных блоков - решает проблемы невидимых сторон блоков за этим блоком. +### Полупрозрачность - *translucent* + +Включает поддержку полупрозрачности в текстурах блока (примеры: вода, лёд). +Следует использовать только при надобности, так как влияет на производительность. +Не требуется для полной прозрачности (трава, цветы). + ### Вращение - *rotation* Профиль вращения (набор положений, в которые можно установить блок) из списка: diff --git a/doc/ru/particles.md b/doc/ru/particles.md index bc20a4a3..167337f8 100644 --- a/doc/ru/particles.md +++ b/doc/ru/particles.md @@ -17,6 +17,7 @@ | acceleration | Ускорение частиц. | {0, -16, 0} | | explosion | Сила разлёта частиц при спавне. | {2, 2, 2} | | size | Размер частиц. | {0.1, 0.1, 0.1} | +| size_spread | Максимальное отклонение времени размера частиц. | 0.2 | | spawn_shape | Форма области спавна частиц. (ball/sphere/box) | ball | | spawn_spread | Размер области спавна частиц. | {0, 0, 0} | | random_sub_uv | Размер случайного подрегиона текстуры (1 - будет использована вся текстура). | 1.0 | diff --git a/doc/ru/scripting/builtins/libhud.md b/doc/ru/scripting/builtins/libhud.md index ae5a69c3..4bf14d99 100644 --- a/doc/ru/scripting/builtins/libhud.md +++ b/doc/ru/scripting/builtins/libhud.md @@ -25,7 +25,9 @@ hud.open_block(x: int, y: int, z: int) -> int, str ```lua -- Показывает элемент в режиме оверлея. -- Также показывает инвентарь игрока, если playerinv - **true**. -hud.show_overlay(layoutid: str, playerinv: bool) +-- Через args можно указать массив значений параметров, что будут переданы +-- в on_open показываемого оверлея. +hud.show_overlay(layoutid: str, playerinv: bool, [опционально] args: table) -- Добавляет постоянный элемент на экран. Элемент не удаляется при -- закрытии инвентаря. Чтобы не перекрывать затенение в режиме diff --git a/doc/ru/scripting/builtins/libplayer.md b/doc/ru/scripting/builtins/libplayer.md index bd2e8f65..179e6b18 100644 --- a/doc/ru/scripting/builtins/libplayer.md +++ b/doc/ru/scripting/builtins/libplayer.md @@ -56,6 +56,20 @@ player.set_noclip(bool) Геттер и сеттер 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 player.set_spawnpoint(playerid: int, x: number, y: number, z: number) player.get_spawnpoint(playerid: int) -> number, number, number diff --git a/doc/ru/scripting/builtins/libutf8.md b/doc/ru/scripting/builtins/libutf8.md index 394eb522..ce933417 100644 --- a/doc/ru/scripting/builtins/libutf8.md +++ b/doc/ru/scripting/builtins/libutf8.md @@ -27,4 +27,7 @@ utf8.upper(text: str) -> str -- Переводит строку в нижний регистр utf8.lower(text: str) -> str + +-- Экранирует строку +utf8.escape(text: str) -> str ``` diff --git a/doc/ru/scripting/events.md b/doc/ru/scripting/events.md index 31c958fc..6d908459 100644 --- a/doc/ru/scripting/events.md +++ b/doc/ru/scripting/events.md @@ -38,7 +38,13 @@ function on_random_update(x, y, z) 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()`. ## События предметов diff --git a/doc/ru/scripting/extensions.md b/doc/ru/scripting/extensions.md index a6b40a38..d4502f64 100644 --- a/doc/ru/scripting/extensions.md +++ b/doc/ru/scripting/extensions.md @@ -4,92 +4,109 @@ ## Расширения для table -Создаёт и возвращает копию переданной таблицы путём создания новой и копирования в неё всех элементов из переданной ```lua -function table.copy(t: table) -> table +table.copy(t: table) -> table ``` -Возвращает количество пар в переданной таблице +Создаёт и возвращает копию переданной таблицы путём создания новой и копирования в неё всех элементов из переданной. + ```lua -function table.count_pairs(t: table) -> integer +table.count_pairs(t: table) -> integer ``` -Возвращает один элемент из переданной таблицы на случайной позиции +Возвращает количество пар в переданной таблице. + ```lua -function table.random(t: table) -> object +table.random(t: table) -> object ``` -Возвращает **true**, если **x** содержится в **t** +Возвращает один элемент из переданной таблицы на случайной позиции. + ```lua -function table.has(t: table, x: object) -> bool +table.has(t: table, x: object) -> bool ``` -Возвращает индекс обьекта **x** в **t**. Если переданный обьект не содержится в таблице, то функция вернёт значение **-1** +Возвращает **true**, если **x** содержится в **t**. + ```lua -function table.index(t: table, x: object) -> integer +table.index(t: table, x: object) -> integer ``` -Удаляет элемент **x** из **t** +Возвращает индекс обьекта **x** в **t**. Если переданный обьект не содержится в таблице, то функция вернёт значение **-1**. + ```lua -function table.remove_value(t: table, x: object) +table.remove_value(t: table, x: object) ``` -Конвертирует переданную таблицу в строку +Удаляет элемент **x** из **t**. + ```lua -function table.tostring(t: table) -> string +table.tostring(t: table) -> string ``` +Конвертирует переданную таблицу в строку. + ## Расширения для string -Разбивает строку **str** на части по указанному разделителю/выражению **separator** и возвращает результат ввиде таблицы из строк. Если **withpattern** равен **true**, то параметр **separator** будет определяться как регулярное выражение ```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 -function string.split(str: string, delimiter: string) -> table[string] +string.split(str: string, delimiter: string) -> table[string] ``` -Экранирует специальные символы в строке, такие как `()[]+-.$%^?*` в формате `%символ`. Символ `NUL` (`\0`) будет преобразован в `%z` +Разбивает строку **str** на части по указанному разделителю **delimiter** и возвращает результат ввиде таблицы из строк. + ```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 -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 -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** не определен, то будут выбраны все пустые символы. + ```lua -function string.trim(str: string, char: string) -> string +string.trim_left(str: string, char: string) -> string ``` Удаляет все символы, равные **char** из строки **str** с левого конца и возвращает результат. Если параметр **char** не определен, то будут выбраны все пустые символы. + ```lua -function string.trim_left(str: string, char: string) -> string +string.trim_right(str: string, char: string) -> string ``` Удаляет все символы, равные **char** из строки **str** с правого конца и возвращает результат. Если параметр **char** не определен, то будут выбраны все пустые символы. + ```lua -function string.trim_right(str: string, char: string) -> string +string.starts_with(str: string, start: string) -> bool ``` Возвращает **true**, если строка **str** начинается на подстроку **start** + ```lua -function string.starts_with(str: string, start: string) -> bool +string.ends_with(str: string, endStr: string) -> bool ``` Возвращает **true**, если строка **str** заканчивается на подстроку **endStr** -```lua -function string.ends_with(str: string, endStr: string) -> bool -``` Также важно подметить, что все выше перечисленные функции, расширяющие **string** можно использовать как мета-методы на экземплярах строк, т.е.: @@ -103,39 +120,51 @@ end Также функции `string.lower` и `string.upper` переопределены на `utf8.lower` и `utf8.upper` +```lua +string.escape(str: string) -> string +``` + +Экранирует строку. Является псевдонимом `utf8.escape`. + ## Расширения для math -Ограничивает число **_in** по лимитам **low** и **high**. Т.е.: Если **_in** больше чем **high** - вернётся **high**, если **_in** меньше чем **low** - вернётся **low**. В противном случае вернётся само число ```lua -function math.clamp(_in, low, high) +math.clamp(_in, low, high) ``` -Возвращает случайное дробное число в диапазоне от **low** до **high** +Ограничивает число **_in** по лимитам **low** и **high**. Т.е.: Если **_in** больше чем **high** - вернётся **high**, если **_in** меньше чем **low** - вернётся **low**. В противном случае вернётся само число. + ```lua -function math.rand(low, high) +math.rand(low, high) ``` +Возвращает случайное дробное число в диапазоне от **low** до **high**. + ## Дополнительные глобальные функции В этом же скрипте также определены и другие глобальные функции которые доступны для использования. Ниже их список -Возвращает **true**, если переданная таблица является массивом, тоесть если каждый ключ это целое число больше или равное единице и если каждый ключ следует за прошлым ```lua -function is_array(x: table) -> bool +is_array(x: table) -> bool ``` -Разбивает путь на две части и возвращает их: входную точку и путь к файлу +Возвращает **true**, если переданная таблица является массивом, тоесть если каждый ключ это целое число больше или равное единице и если каждый ключ следует за прошлым. + ```lua function parse_path(path: string) -> string, string ``` -Вызывает функцию **func** **iters** раз, передавая ей аргументы `...`, а после выводит в консоль время в микросекундах, которое прошло с момента вызова **timeit** +Разбивает путь на две части и возвращает их: входную точку и путь к файлу. + ```lua function timeit(iters: integer, func: func, ...) ``` -Вызывает остановку корутины до тех пор, пока не пройдёт количество секунд, указанное в **timesec**. Функция может быть использована только внутри корутины +Вызывает функцию **func** **iters** раз, передавая ей аргументы `...`, а после выводит в консоль время в микросекундах, которое прошло с момента вызова **timeit**. + ```lua function sleep(timesec: number) -``` \ No newline at end of file +``` + +Вызывает остановку корутины до тех пор, пока не пройдёт количество секунд, указанное в **timesec**. Функция может быть использована только внутри корутины. diff --git a/doc/ru/scripting/ui.md b/doc/ru/scripting/ui.md index 0a955947..4d244210 100644 --- a/doc/ru/scripting/ui.md +++ b/doc/ru/scripting/ui.md @@ -78,14 +78,18 @@ document["worlds-panel"]:clear() | caret | int | да | да | позиция каретки. `textbox.caret = -1` установит позицию в конец текста | | editable | bool | да | да | изменяемость текста | | multiline | bool | да | да | поддержка многострочности | +| lineNumbers | bool | да | да | отображение номеров строк | | textWrap | bool | да | да | автоматический перенос текста (только при multiline: "true") | | valid | bool | да | нет | является ли введенный текст корректным | +| textColor | vec4 | да | да | цвет текста | Методы: -| Метод | Описание | -| ----------- | -------------------------------------------- | -| paste(text) | вставляет указанный текст на позицию каретки | +| Метод | Описание | +| ------------------------- | -------------------------------------------- | +| paste(text: str) | вставляет указанный текст на позицию каретки | +| lineAt(pos: int) -> int | определяет номер строки по позиции в тексте | +| linePos(line: int) -> int | определяет позицию начала строки в тексте | ## Ползунок (trackbar) diff --git a/doc/ru/xml-ui-layouts.md b/doc/ru/xml-ui-layouts.md index bdf82f1b..28777950 100644 --- a/doc/ru/xml-ui-layouts.md +++ b/doc/ru/xml-ui-layouts.md @@ -59,7 +59,8 @@ В число контейнеров также входят панели и кнопки. - `padding` - внутренний отступ элемента. Тип: 4D вектор. Порядок: `"left,top,right,bottom"` -- `scrollable` - возможность скроллинга. Работает только у Panel. Тип: логический. +- `scrollable` - возможность скроллинга. Тип: логический. +- `scroll-step` - шаг скроллинга. Тип: целочисленный. # Общие атрибуты панелей @@ -105,7 +106,9 @@ - `multiline` - разрешает отображение многострочного текста. - `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true") - `editable`- определяет возможность редактирования текста. +- `line-numbers` - включает отображение номеров строк. - `error-color` - цвет при вводе некорректных данных (текст не проходит проверку валидатора). Тип: RGBA цвет. +- `text-color` - цвет текста. Тип: RGBA цвет. - `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен. - `onup` - lua функция вызываемая при нажатии стрелки вверх. - `ondown` - lua функция вызываемая при нажатии стрелки вниз. diff --git a/res/content/base/blocks/glass.json b/res/content/base/blocks/glass.json index 902a2cf1..9bd4f021 100644 --- a/res/content/base/blocks/glass.json +++ b/res/content/base/blocks/glass.json @@ -3,5 +3,6 @@ "material": "base:glass", "draw-group": 2, "light-passing": true, - "sky-light-passing": true + "sky-light-passing": true, + "translucent": true } diff --git a/res/content/base/blocks/ice.json b/res/content/base/blocks/ice.json new file mode 100644 index 00000000..19d9af12 --- /dev/null +++ b/res/content/base/blocks/ice.json @@ -0,0 +1,7 @@ +{ + "texture": "ice", + "material": "base:glass", + "draw-group": 4, + "light-passing": true, + "translucent": true +} diff --git a/res/content/base/blocks/water.json b/res/content/base/blocks/water.json index 943640e8..f7785044 100644 --- a/res/content/base/blocks/water.json +++ b/res/content/base/blocks/water.json @@ -6,5 +6,6 @@ "sky-light-passing": false, "obstacle": false, "selectable": false, - "replaceable": true + "replaceable": true, + "translucent": true } diff --git a/res/content/base/content.json b/res/content/base/content.json index cec825a8..db13bae2 100644 --- a/res/content/base/content.json +++ b/res/content/base/content.json @@ -1,6 +1,8 @@ { - "items": [ - "bazalt_breaker" + "entities": [ + "drop", + "player", + "falling_block" ], "blocks": [ "dirt", @@ -27,11 +29,10 @@ "lightbulb", "torch", "wooden_door", - "coal_ore" + "coal_ore", + "ice" ], - "entities": [ - "drop", - "player", - "falling_block" + "items": [ + "bazalt_breaker" ] } \ No newline at end of file diff --git a/res/content/base/entities/falling_block.json b/res/content/base/entities/falling_block.json index cd0459b5..cf5146fd 100644 --- a/res/content/base/entities/falling_block.json +++ b/res/content/base/entities/falling_block.json @@ -3,5 +3,5 @@ "base:falling_block" ], "skeleton-name": "base:block", - "hitbox": [0.8, 0.8, 0.8] + "hitbox": [0.98, 0.98, 0.98] } diff --git a/res/content/base/preload.json b/res/content/base/preload.json index 33f38f36..56b9896e 100644 --- a/res/content/base/preload.json +++ b/res/content/base/preload.json @@ -1,4 +1,7 @@ { + "atlases": [ + {"name": "cracks", "type": "separate"} + ], "sounds": [ "blocks/door_open", "blocks/door_close", diff --git a/res/content/base/texts/ru_RU.txt b/res/content/base/texts/ru_RU.txt index f6a3ab51..cf55ec54 100644 --- a/res/content/base/texts/ru_RU.txt +++ b/res/content/base/texts/ru_RU.txt @@ -1,3 +1,4 @@ +bazalt breaker=крушитель базальта bazalt=базальт blue lamp=синяя лампа brick=кирпич @@ -5,8 +6,8 @@ dirt=земля flower=цветок glass=стекло grass block=дёрн -tall grass=высокая трава green lamp=зелёная лампа +ice=лёд lamp=лампа leaves=листва light bulb=лампочка @@ -18,8 +19,8 @@ red lamp=красная лампа rust=ржавчина sand=песок stone=камень +tall grass=высокая трава +torch=факел water=вода wood=бревно -torch=факел -bazalt breaker=крушитель базальта wooden door=деревянная дверь diff --git a/res/content/base/textures/blocks/ice.png b/res/content/base/textures/blocks/ice.png new file mode 100644 index 00000000..707ee89a Binary files /dev/null and b/res/content/base/textures/blocks/ice.png differ diff --git a/res/content/base/textures/cracks/cracks_0.png b/res/content/base/textures/cracks/cracks_0.png new file mode 100644 index 00000000..84d56c43 Binary files /dev/null and b/res/content/base/textures/cracks/cracks_0.png differ diff --git a/res/content/base/textures/cracks/cracks_1.png b/res/content/base/textures/cracks/cracks_1.png new file mode 100644 index 00000000..40bd0ce8 Binary files /dev/null and b/res/content/base/textures/cracks/cracks_1.png differ diff --git a/res/content/base/textures/cracks/cracks_10.png b/res/content/base/textures/cracks/cracks_10.png new file mode 100644 index 00000000..8253dd6d Binary files /dev/null and b/res/content/base/textures/cracks/cracks_10.png differ diff --git a/res/content/base/textures/cracks/cracks_2.png b/res/content/base/textures/cracks/cracks_2.png new file mode 100644 index 00000000..d13052d2 Binary files /dev/null and b/res/content/base/textures/cracks/cracks_2.png differ diff --git a/res/content/base/textures/cracks/cracks_3.png b/res/content/base/textures/cracks/cracks_3.png new file mode 100644 index 00000000..40275992 Binary files /dev/null and b/res/content/base/textures/cracks/cracks_3.png differ diff --git a/res/content/base/textures/cracks/cracks_4.png b/res/content/base/textures/cracks/cracks_4.png new file mode 100644 index 00000000..ced0d189 Binary files /dev/null and b/res/content/base/textures/cracks/cracks_4.png differ diff --git a/res/content/base/textures/cracks/cracks_5.png b/res/content/base/textures/cracks/cracks_5.png new file mode 100644 index 00000000..3db95bf4 Binary files /dev/null and b/res/content/base/textures/cracks/cracks_5.png differ diff --git a/res/content/base/textures/cracks/cracks_6.png b/res/content/base/textures/cracks/cracks_6.png new file mode 100644 index 00000000..1eda5a64 Binary files /dev/null and b/res/content/base/textures/cracks/cracks_6.png differ diff --git a/res/content/base/textures/cracks/cracks_7.png b/res/content/base/textures/cracks/cracks_7.png new file mode 100644 index 00000000..a9b9092d Binary files /dev/null and b/res/content/base/textures/cracks/cracks_7.png differ diff --git a/res/content/base/textures/cracks/cracks_8.png b/res/content/base/textures/cracks/cracks_8.png new file mode 100644 index 00000000..79669ab1 Binary files /dev/null and b/res/content/base/textures/cracks/cracks_8.png differ diff --git a/res/content/base/textures/cracks/cracks_9.png b/res/content/base/textures/cracks/cracks_9.png new file mode 100644 index 00000000..18eb1a53 Binary files /dev/null and b/res/content/base/textures/cracks/cracks_9.png differ diff --git a/res/layouts/console.xml b/res/layouts/console.xml index 3e1b802d..c367545c 100644 --- a/res/layouts/console.xml +++ b/res/layouts/console.xml @@ -1,5 +1,18 @@ - + + + + + + + + + + + + + + + - + @devtools.traceback") + 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() local pid = hud.get_player() local x,y,z = player.get_pos(pid) @@ -56,10 +136,19 @@ function add_to_history(text) end function submit(text) + text = text:trim() 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() - text = text:trim() local name for s in text:gmatch("%S+") do name = s @@ -84,6 +173,37 @@ function submit(text) document.prompt.focused = true end -function on_open() - document.prompt.focused = true +function set_mode(mode) + 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 diff --git a/res/layouts/pages/main.xml b/res/layouts/pages/main.xml index 11f53136..9c562fb4 100644 --- a/res/layouts/pages/main.xml +++ b/res/layouts/pages/main.xml @@ -1,7 +1,5 @@ - - - + diff --git a/res/layouts/pages/settings.xml b/res/layouts/pages/settings.xml index f5ec33d7..00437e68 100644 --- a/res/layouts/pages/settings.xml +++ b/res/layouts/pages/settings.xml @@ -1,9 +1,9 @@ - - - - + + + + @@ -11,7 +11,7 @@ - + diff --git a/res/layouts/pages/settings.xml.lua b/res/layouts/pages/settings.xml.lua index 24256c84..8b41491a 100644 --- a/res/layouts/pages/settings.xml.lua +++ b/res/layouts/pages/settings.xml.lua @@ -3,15 +3,13 @@ function on_open() "%s: %s", gui.str("Language", "settings"), gui.get_locales_info()[core.get_setting("ui.language")].name ) - set_page("s_gfx", "settings_graphics") -end - -function set_page(btn, page) - document.s_aud.enabled = true - document.s_dsp.enabled = true - document.s_gfx.enabled = true - document.s_ctl.enabled = true - document.s_rst.enabled = true - document[btn].enabled = false - document.menu.page = page + sections = RadioGroup({ + audio=document.s_aud, + display=document.s_dsp, + graphics=document.s_gfx, + controls=document.s_ctl, + reset=document.s_rst + }, function (page) + document.menu.page = "settings_"..page + end, "graphics") end diff --git a/res/layouts/pages/worlds.xml b/res/layouts/pages/worlds.xml new file mode 100644 index 00000000..13765930 --- /dev/null +++ b/res/layouts/pages/worlds.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/res/layouts/pages/main.xml.lua b/res/layouts/pages/worlds.xml.lua similarity index 100% rename from res/layouts/pages/main.xml.lua rename to res/layouts/pages/worlds.xml.lua diff --git a/res/layouts/templates/problem.xml b/res/layouts/templates/problem.xml index 0dfb2794..17085392 100644 --- a/res/layouts/templates/problem.xml +++ b/res/layouts/templates/problem.xml @@ -1,6 +1,9 @@ - + - + + + diff --git a/res/layouts/templates/stack_frame.xml b/res/layouts/templates/stack_frame.xml new file mode 100644 index 00000000..c73890bb --- /dev/null +++ b/res/layouts/templates/stack_frame.xml @@ -0,0 +1,3 @@ + diff --git a/res/scripts/stdcmd.lua b/res/scripts/stdcmd.lua index 98609688..ccb65a71 100644 --- a/res/scripts/stdcmd.lua +++ b/res/scripts/stdcmd.lua @@ -256,6 +256,14 @@ console.add_command( end ) +console.add_command( + "chat text:str", + "Send chat message", + function (args, kwargs) + console.log("[you] "..args[1]) + end +) + console.cheats = { "blocks.fill", "tp", diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index f53323db..2c48bdfc 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -84,6 +84,32 @@ function Document.new(docname) }) 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") _MENU = _GUI_ROOT.menu menu = _MENU @@ -243,7 +269,7 @@ function _rules.clear() _rules.create("allow-cheats", true) end -function __vc_create_hud_rules() +function __vc_on_hud_open() _rules.create("allow-content-access", hud._is_content_access(), function(value) hud._set_content_access(value) input.set_enabled("player.pick", value) @@ -269,6 +295,14 @@ function __vc_create_hud_rules() _rules.create("allow-debug-cheats", true, function(value) hud._set_debug_cheats(value) 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 local RULES_FILE = "world:rules.toml" diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 530ec091..14479c7d 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -162,6 +162,7 @@ end string.lower = utf8.lower string.upper = utf8.upper +string.escape = utf8.escape local meta = getmetatable("") @@ -227,8 +228,22 @@ function file.readlines(path) return lines 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 = { - loaded={} + loaded = {} } local __cached_scripts = {} local __warnings_hidden = {} @@ -238,7 +253,7 @@ function on_deprecated_call(name, alternatives) return end __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 debug.warning("deprecated function called ("..name.."), use ".. alternatives.." instead\n"..debug.traceback()) @@ -277,6 +292,10 @@ function __load_script(path, nocache) end 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) return __load_script(prefix..":modules/"..file..".lua") end @@ -292,3 +311,17 @@ function __scripts_cleanup() 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 diff --git a/res/shaders/entity.glslf b/res/shaders/entity.glslf index 5da4142c..143b3eaa 100644 --- a/res/shaders/entity.glslf +++ b/res/shaders/entity.glslf @@ -9,6 +9,7 @@ uniform samplerCube u_cubemap; uniform vec3 u_fogColor; uniform float u_fogFactor; uniform float u_fogCurve; +uniform bool u_alphaClip; void main() { vec3 fogColor = texture(u_cubemap, a_dir).rgb; @@ -16,7 +17,7 @@ void main() { float depth = (a_distance/256.0); float alpha = a_color.a * tex_color.a; // anyway it's any alpha-test alternative required - if (alpha < 0.5f) + if (alpha < (u_alphaClip ? 0.5f : 0.2f)) discard; f_color = mix(a_color * tex_color, vec4(fogColor,1.0), min(1.0, pow(depth*u_fogFactor, u_fogCurve))); diff --git a/res/shaders/main.glslf b/res/shaders/main.glslf index 4c1cb014..7db1a544 100644 --- a/res/shaders/main.glslf +++ b/res/shaders/main.glslf @@ -9,14 +9,14 @@ uniform samplerCube u_cubemap; uniform vec3 u_fogColor; uniform float u_fogFactor; uniform float u_fogCurve; +uniform bool u_alphaClip; void main() { vec3 fogColor = texture(u_cubemap, a_dir).rgb; vec4 tex_color = texture(u_texture0, a_texCoord); float depth = (a_distance/256.0); float alpha = a_color.a * tex_color.a; - // anyway it's any alpha-test alternative required - if (alpha < 0.3f) + if (u_alphaClip && alpha < 0.9f) discard; f_color = mix(a_color * tex_color, vec4(fogColor,1.0), min(1.0, pow(depth*u_fogFactor, u_fogCurve))); diff --git a/res/texts/be_BY.txt b/res/texts/be_BY.txt index 9bbff6a9..4d325060 100644 --- a/res/texts/be_BY.txt +++ b/res/texts/be_BY.txt @@ -33,6 +33,8 @@ menu.Display=Дысплей menu.Graphics=Графіка menu.missing-content=Адсутнічае Кантэнт! menu.New World=Новы Свет +menu.Open worlds folder=Адкрыць папку з сусветамі +menu.Worlds=Сусветы menu.Page not found=Старонка не знойдзена menu.Quit=Выхад menu.Save and Quit to Menu=Захаваць і Выйсці ў Меню diff --git a/res/texts/de_DE.txt b/res/texts/de_DE.txt index 0251f395..8e125159 100644 --- a/res/texts/de_DE.txt +++ b/res/texts/de_DE.txt @@ -23,6 +23,8 @@ menu.Controls=Kontrolle menu.Create World=Erschaffe eine Welt menu.missing-content=Fehlender Inhalt! menu.New World=Neue Welt +menu.Worlds=Welten +menu.Open worlds folder=Weltenordner öffnen menu.Quit=Ausfahrt menu.Save and Quit to Menu=Speichern und zum Menü zurückkehren menu.Settings=Einstellungen diff --git a/res/texts/en_US.txt b/res/texts/en_US.txt index 9c9cc2a5..c19fe00a 100644 --- a/res/texts/en_US.txt +++ b/res/texts/en_US.txt @@ -11,6 +11,8 @@ world.delete-confirm=Do you want to delete world forever? world.generators.default=Default world.generators.flat=Flat +devtools.traceback=Traceback (most recent call first) + # Tooltips graphics.gamma.tooltip=Lighting brightness curve graphics.backlight.tooltip=Backlight to prevent total darkness diff --git a/res/texts/fi_FI.txt b/res/texts/fi_FI.txt index 6a84f24d..40c5dadc 100644 --- a/res/texts/fi_FI.txt +++ b/res/texts/fi_FI.txt @@ -23,6 +23,8 @@ menu.Controls=Ohjaus menu.Graphics=Grafiikka menu.missing-content=Sisältö Puuttuu! menu.New World=Uusi Maailma +menu.Worlds=Maailmat +menu.Open worlds folder=Avaa maailmat-kansio menu.Page not found=Sivu ei löytynyt! menu.Quit=Poistu menu.Save and Quit to Menu=Tallenna ja Takaisin Valikkoon diff --git a/res/texts/pl_PL.txt b/res/texts/pl_PL.txt index 39f1a1a7..caae775b 100644 --- a/res/texts/pl_PL.txt +++ b/res/texts/pl_PL.txt @@ -21,6 +21,8 @@ menu.Graphics=Grafika menu.Create World=Stwórz świat menu.missing-content=Brakująca treść! 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.Quit=Wyjście menu.Save and Quit to Menu=Zapisz i wyjdź do menu diff --git a/res/texts/ru_RU.txt b/res/texts/ru_RU.txt index 74717336..ae591042 100644 --- a/res/texts/ru_RU.txt +++ b/res/texts/ru_RU.txt @@ -12,7 +12,15 @@ Dependencies=Зависимости Description=Описание Converting world...=Выполняется конвертация мира... Unlimited=Неограниченно +Chat=Чат +Console=Консоль +Log=Лог +Problems=Проблемы +Monitor=Мониторинг +Debug=Отладка +File=Файл +devtools.traceback=Стек вызовов (от последнего) error.pack-not-found=Не удалось найти пакет error.dependency-not-found=Используемая зависимость не найдена pack.remove-confirm=Удалить весь поставляемый паком/паками контент из мира (безвозвратно)? @@ -33,6 +41,8 @@ menu.Display=Дисплей menu.Graphics=Графика menu.missing-content=Отсутствует Контент! menu.New World=Новый Мир +menu.Open worlds folder=Открыть папку с мирами +menu.Worlds=Миры menu.Page not found=Страница не найдена menu.Quit=Выход menu.Save and Quit to Menu=Сохранить и Выйти в Меню diff --git a/res/texts/uk_UA.txt b/res/texts/uk_UA.txt index 175c172f..b5eab139 100644 --- a/res/texts/uk_UA.txt +++ b/res/texts/uk_UA.txt @@ -21,6 +21,8 @@ menu.Controls=Керування menu.Graphics=Графіка menu.missing-content=Відсутній Контент! menu.New World=Новий Світ +menu.Open worlds folder=Відкрити папку зі світами +menu.Worlds=Світи menu.Page not found=Сторінка не знайдена menu.Quit=Вихід menu.Save and Quit to Menu=Зберегти і Вийти в Меню diff --git a/res/texts/uz_UZ.txt b/res/texts/uz_UZ.txt index ae525503..0ad6ec37 100644 --- a/res/texts/uz_UZ.txt +++ b/res/texts/uz_UZ.txt @@ -33,6 +33,8 @@ menu.Display=Displey menu.Graphics=Grafika menu.missing-content=Kontent etishmayapti! menu.New World=Yangi Dunyo +menu.Worlds=Dunyo +menu.Open worlds folder=Dunyo papkasini ochish menu.Page not found=Sahifa topilmadi menu.Quit=Chiqish menu.Save and Quit to Menu=Saqlash va menyuga chiqish diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index 142934d1..d3806157 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -43,7 +43,11 @@ void AssetsLoader::add( const std::string& alias, std::shared_ptr settings ) { + if (enqueued.find({tag, alias}) != enqueued.end()){ + return; + } entries.push(aloader_entry {tag, filename, alias, std::move(settings)}); + enqueued.insert({tag, alias}); } bool AssetsLoader::hasNext() const { @@ -148,6 +152,16 @@ void AssetsLoader::processPreload( std::make_shared(map.at("keep-pcm").get(keepPCM))); 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(type)); + break; + } default: add(tag, path, name); break; diff --git a/src/assets/AssetsLoader.hpp b/src/assets/AssetsLoader.hpp index 2321f9a3..cd6db950 100644 --- a/src/assets/AssetsLoader.hpp +++ b/src/assets/AssetsLoader.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -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< assetload:: postfunc(AssetsLoader*, const ResPaths*, const std::string&, const std::string&, std::shared_ptr)>; @@ -52,6 +64,7 @@ class AssetsLoader { Assets* assets; std::map loaders; std::queue entries; + std::set> enqueued; const ResPaths* paths; void tryAddSound(const std::string& name); diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index b73a3882..813d1c87 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -103,12 +103,25 @@ static bool append_atlas(AtlasBuilder& atlas, const fs::path& file) { } assetload::postfunc assetload::atlas( - AssetsLoader*, + AssetsLoader* loader, const ResPaths* paths, const std::string& directory, const std::string& name, - const std::shared_ptr& + const std::shared_ptr& config ) { + auto atlasConfig = std::dynamic_pointer_cast(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; for (const auto& file : paths->listdir(directory)) { if (!imageio::is_read_supported(file.extension().u8string())) continue; @@ -172,7 +185,9 @@ assetload::postfunc assetload::layout( return [=](auto assets) { try { auto cfg = std::dynamic_pointer_cast(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) { throw std::runtime_error( "failed to parse layout XML '" + file + "':\n" + err.errorLog() diff --git a/src/coders/commons.cpp b/src/coders/commons.cpp index 985c3440..de4fb755 100644 --- a/src/coders/commons.cpp +++ b/src/coders/commons.cpp @@ -373,7 +373,7 @@ std::string BasicParser::parseString(char quote, bool closeRequired) { case 'b': ss << '\b'; break; case 't': ss << '\t'; break; case 'f': ss << '\f'; break; - case '\'': ss << '\\'; break; + case '\'': ss << '\''; break; case '"': ss << '"'; break; case '\\': ss << '\\'; break; case '/': ss << '/'; break; diff --git a/src/coders/json.cpp b/src/coders/json.cpp index 05db4e13..f6b27f51 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -265,5 +265,5 @@ dv::value json::parse( } dv::value json::parse(std::string_view source) { - return parse("", source); + return parse("[string]", source); } diff --git a/src/coders/xml.cpp b/src/coders/xml.cpp index 6a8777c5..faf84f15 100644 --- a/src/coders/xml.cpp +++ b/src/coders/xml.cpp @@ -250,7 +250,8 @@ std::string Parser::parseText() { } 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) { @@ -336,7 +337,7 @@ xmldocument Parser::parse() { 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); return parser.parse(); } diff --git a/src/coders/xml.hpp b/src/coders/xml.hpp index 136c60df..54a2b589 100644 --- a/src/coders/xml.hpp +++ b/src/coders/xml.hpp @@ -140,6 +140,6 @@ namespace xml { /// @param source xml source code string /// @return xml document extern xmldocument parse( - const std::string& filename, const std::string& source + std::string_view filename, std::string_view source ); } diff --git a/src/constants.hpp b/src/constants.hpp index 12e12c75..84d8b22c 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -35,9 +35,6 @@ inline constexpr int CHUNK_D = 16; inline constexpr uint VOXEL_USER_BITS = 8; 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) 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; } +/// @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 TEXTURES_FOLDER = "textures"; inline const std::string FONTS_FOLDER = "fonts"; diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index a801500e..cb50e4a5 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -333,6 +333,7 @@ void ContentLoader::loadBlock( root.at("inventory-size").get(def.inventorySize); root.at("tick-interval").get(def.tickInterval); root.at("overlay-texture").get(def.overlayTexture); + root.at("translucent").get(def.translucent); if (root.has("fields")) { def.dataStruct = std::make_unique(); @@ -484,7 +485,13 @@ void ContentLoader::loadBlock( auto scriptfile = folder / fs::path("scripts/" + def.scriptName + ".lua"); 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) { 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"); 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"); if (fs::is_regular_file(scriptFile)) { 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"); foreach_file(componentsDir, [this](const fs::path& file) { 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 diff --git a/src/content/ContentPack.hpp b/src/content/ContentPack.hpp index 9d25d752..b790d0e7 100644 --- a/src/content/ContentPack.hpp +++ b/src/content/ContentPack.hpp @@ -99,6 +99,7 @@ struct world_funcs_set { bool onblockplaced : 1; bool onblockbroken : 1; bool onblockinteract : 1; + bool onplayertick : 1; }; class ContentPackRuntime { diff --git a/src/core_defs.hpp b/src/core_defs.hpp index 4a02cac9..9f26b1c8 100644 --- a/src/core_defs.hpp +++ b/src/core_defs.hpp @@ -10,7 +10,6 @@ inline const std::string CORE_STRUCT_AIR = "core:struct_air"; inline const std::string TEXTURE_NOTFOUND = "notfound"; // 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_MOVE_FORWARD = "movement.forward"; inline const std::string BIND_MOVE_BACK = "movement.back"; diff --git a/src/frontend/UiDocument.cpp b/src/frontend/UiDocument.cpp index e218d975..fff98f3f 100644 --- a/src/frontend/UiDocument.cpp +++ b/src/frontend/UiDocument.cpp @@ -53,7 +53,12 @@ scriptenv UiDocument::getEnvironment() const { return env; } -std::unique_ptr UiDocument::read(const scriptenv& penv, const std::string& name, const fs::path& file) { +std::unique_ptr 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); auto xmldoc = xml::parse(file.u8string(), text); @@ -69,12 +74,16 @@ std::unique_ptr UiDocument::read(const scriptenv& penv, const std::s uidocscript script {}; auto scriptFile = fs::path(file.u8string()+".lua"); 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(name, script, view, env); } -std::shared_ptr UiDocument::readElement(const fs::path& file) { - auto document = read(nullptr, file.filename().u8string(), file); +std::shared_ptr UiDocument::readElement( + const fs::path& file, const std::string& fileName +) { + auto document = read(nullptr, file.filename().u8string(), file, fileName); return document->getRoot(); } diff --git a/src/frontend/UiDocument.hpp b/src/frontend/UiDocument.hpp index dcd2a34e..dbf91ef1 100644 --- a/src/frontend/UiDocument.hpp +++ b/src/frontend/UiDocument.hpp @@ -45,6 +45,13 @@ public: const uidocscript& getScript() const; scriptenv getEnvironment() const; - static std::unique_ptr read(const scriptenv& parent_env, const std::string& name, const fs::path& file); - static std::shared_ptr readElement(const fs::path& file); + static std::unique_ptr read( + const scriptenv& parent_env, + const std::string& name, + const fs::path& file, + const std::string& fileName + ); + static std::shared_ptr readElement( + const fs::path& file, const std::string& fileName + ); }; diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index bfaac141..cc9c54d0 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -229,13 +229,9 @@ void Hud::processInput(bool visible) { setPause(true); } } - if (!pause && Events::jactive(BIND_DEVTOOLS_CONSOLE)) { - showOverlay(assets->get("core:console"), false); - } if (!Window::isFocused() && !pause && !isInventoryOpen()) { setPause(true); } - if (!pause && visible && Events::jactive(BIND_HUD_INVENTORY)) { if (inventoryOpen) { 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()) { closeInventory(); } @@ -476,7 +474,8 @@ void Hud::showOverlay(UiDocument* doc, bool playerInventory) { showExchangeSlot(); 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) { @@ -508,13 +507,18 @@ void Hud::closeInventory() { cleanup(); } -void Hud::add(const HudElement& element) { +void Hud::add(const HudElement& element, const dv::value& argsArray) { gui->add(element.getNode()); auto document = element.getDocument(); if (document) { auto invview = std::dynamic_pointer_cast(element.getNode()); auto inventory = invview ? invview->getInventory() : nullptr; std::vector args; + if (argsArray != nullptr) { + for (const auto& arg : argsArray) { + args.push_back(arg); + } + } args.emplace_back(inventory ? inventory.get()->getId() : 0); for (int i = 0; i < 3; i++) { args.emplace_back(static_cast(blockPos[i])); @@ -571,7 +575,7 @@ void Hud::draw(const DrawContext& ctx){ // Crosshair if (!pause && !inventoryOpen && !player->debug) { - DrawContext chctx = ctx.sub(); + DrawContext chctx = ctx.sub(batch); chctx.setBlendMode(BlendMode::inversion); auto texture = assets->get("gui/crosshair"); batch->texture(texture); @@ -615,8 +619,11 @@ void Hud::updateElementsPosition(const Viewport& viewport) { } if (secondUI->getPositionFunc() == nullptr) { secondUI->setPos(glm::vec2( - glm::min(width/2-invwidth/2, width-caWidth-(inventoryView ? 10 : 0)-invwidth), - height/2-totalHeight/2 + glm::min( + width / 2.f - invwidth / 2.f, + width - caWidth - (inventoryView ? 10 : 0) - invwidth + ), + height / 2.f - totalHeight / 2.f )); } } diff --git a/src/frontend/hud.hpp b/src/frontend/hud.hpp index c5708527..4a2ecfd5 100644 --- a/src/frontend/hud.hpp +++ b/src/frontend/hud.hpp @@ -2,6 +2,7 @@ #include "typedefs.hpp" #include "util/ObjectsKeeper.hpp" +#include "data/dv.hpp" #include #include @@ -173,7 +174,10 @@ public: /// @brief Show element in inventory-mode /// @param doc element layout /// @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 void closeInventory(); @@ -182,7 +186,7 @@ public: /// @param doc element layout 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 remove(const std::shared_ptr& node); diff --git a/src/frontend/menu.cpp b/src/frontend/menu.cpp index 7955fad5..0591441e 100644 --- a/src/frontend/menu.cpp +++ b/src/frontend/menu.cpp @@ -62,7 +62,10 @@ gui::page_loader_func menus::create_page_loader(Engine* engine) { auto fullname = "core:pages/"+name; 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(); engine->getAssets()->store(std::move(document_ptr), fullname); @@ -110,7 +113,7 @@ UiDocument* menus::show(Engine* engine, const std::string& name, std::vectorgetAssets()->store(std::move(document_ptr), fullname); diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index fed19cdd..1acd9200 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -83,7 +83,12 @@ void LevelScreen::initializePack(ContentPackRuntime* pack) { const ContentPack& info = pack->getInfo(); fs::path scriptFile = info.folder/fs::path("scripts/hud.lua"); 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" + ); } } diff --git a/src/graphics/core/Batch2D.cpp b/src/graphics/core/Batch2D.cpp index 13b48586..c4c67db2 100644 --- a/src/graphics/core/Batch2D.cpp +++ b/src/graphics/core/Batch2D.cpp @@ -11,7 +11,7 @@ inline constexpr uint B2D_VERTEX_SIZE = 8; Batch2D::Batch2D(size_t capacity) : capacity(capacity), color(1.0f){ - const vattr attrs[] = { + const VertexAttribute attrs[] = { {2}, {2}, {4}, {0} }; diff --git a/src/graphics/core/Batch3D.cpp b/src/graphics/core/Batch3D.cpp index 1770fc21..a81f4ffb 100644 --- a/src/graphics/core/Batch3D.cpp +++ b/src/graphics/core/Batch3D.cpp @@ -12,7 +12,7 @@ inline constexpr uint B3D_VERTEX_SIZE = 9; Batch3D::Batch3D(size_t capacity) : capacity(capacity) { - const vattr attrs[] = { + const VertexAttribute attrs[] = { {3}, {2}, {4}, {0} }; diff --git a/src/graphics/core/DrawContext.cpp b/src/graphics/core/DrawContext.cpp index 29afae2e..7062ad56 100644 --- a/src/graphics/core/DrawContext.cpp +++ b/src/graphics/core/DrawContext.cpp @@ -91,6 +91,7 @@ DrawContext DrawContext::sub(Flushable* flushable) const { auto ctx = DrawContext(*this); ctx.parent = this; ctx.flushable = flushable; + ctx.scissorsCount = 0; return ctx; } @@ -148,7 +149,7 @@ void DrawContext::setBlendMode(BlendMode mode) { set_blend_mode(mode); } -void DrawContext::setScissors(glm::vec4 area) { +void DrawContext::setScissors(const glm::vec4& area) { Window::pushScissor(area); scissorsCount++; } diff --git a/src/graphics/core/DrawContext.hpp b/src/graphics/core/DrawContext.hpp index 9174be1b..736b053e 100644 --- a/src/graphics/core/DrawContext.hpp +++ b/src/graphics/core/DrawContext.hpp @@ -34,6 +34,6 @@ public: void setDepthTest(bool flag); void setCullFace(bool flag); void setBlendMode(BlendMode mode); - void setScissors(glm::vec4 area); + void setScissors(const glm::vec4& area); void setLineWidth(float width); }; diff --git a/src/graphics/core/LineBatch.cpp b/src/graphics/core/LineBatch.cpp index dd3f8067..aed8a249 100644 --- a/src/graphics/core/LineBatch.cpp +++ b/src/graphics/core/LineBatch.cpp @@ -6,7 +6,7 @@ inline constexpr uint LB_VERTEX_SIZE = (3+4); LineBatch::LineBatch(size_t capacity) : capacity(capacity) { - const vattr attrs[] = { {3},{4}, {0} }; + const VertexAttribute attrs[] = { {3},{4}, {0} }; buffer = std::make_unique(capacity * LB_VERTEX_SIZE * 2); mesh = std::make_unique(buffer.get(), 0, attrs); index = 0; diff --git a/src/graphics/core/Mesh.cpp b/src/graphics/core/Mesh.cpp index 78380ae1..e8cffe6f 100644 --- a/src/graphics/core/Mesh.cpp +++ b/src/graphics/core/Mesh.cpp @@ -4,7 +4,7 @@ int Mesh::meshesCount = 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; for (int i = 0; attrs[i].size; i++) { vertexSize += attrs[i].size; @@ -19,10 +19,10 @@ Mesh::Mesh(const MeshData& data) data.indices.size(), 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), - vertices(vertices), - indices(indices) + vertices(0), + indices(0) { meshesCount++; vertexSize = 0; @@ -58,10 +58,9 @@ void Mesh::reload(const float* vertexBuffer, size_t vertices, const int* indexBu glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vbo); if (vertexBuffer != nullptr && vertices != 0) { - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertexSize * vertices, vertexBuffer, GL_STATIC_DRAW); - } - else { - glBufferData(GL_ARRAY_BUFFER, 0, {}, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertexSize * vertices, vertexBuffer, GL_STREAM_DRAW); + } else { + glBufferData(GL_ARRAY_BUFFER, 0, {}, GL_STREAM_DRAW); } if (indexBuffer != nullptr && indices != 0) { 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; } -void Mesh::draw(unsigned int primitive){ +void Mesh::draw(unsigned int primitive) const { drawCalls++; glBindVertexArray(vao); if (ibo != 0) { @@ -87,6 +86,6 @@ void Mesh::draw(unsigned int primitive){ glBindVertexArray(0); } -void Mesh::draw() { +void Mesh::draw() const { draw(GL_TRIANGLES); } diff --git a/src/graphics/core/Mesh.hpp b/src/graphics/core/Mesh.hpp index 87c8e69b..482c82bf 100644 --- a/src/graphics/core/Mesh.hpp +++ b/src/graphics/core/Mesh.hpp @@ -14,8 +14,8 @@ class Mesh { size_t vertexSize; public: 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 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 VertexAttribute* attrs) : Mesh(vertexBuffer, vertices, nullptr, 0, attrs) {}; ~Mesh(); @@ -28,10 +28,10 @@ public: /// @brief Draw mesh with specified primitives type /// @param primitive primitives type - void draw(unsigned int primitive); + void draw(unsigned int primitive) const; /// @brief Draw mesh as triangles - void draw(); + void draw() const; /// @brief Total numbers of alive mesh objects static int meshesCount; diff --git a/src/graphics/core/MeshData.hpp b/src/graphics/core/MeshData.hpp index 62b32903..39220119 100644 --- a/src/graphics/core/MeshData.hpp +++ b/src/graphics/core/MeshData.hpp @@ -6,7 +6,7 @@ #include "util/Buffer.hpp" /// @brief Vertex attribute info -struct vattr { +struct VertexAttribute { ubyte size; }; @@ -14,7 +14,7 @@ struct vattr { struct MeshData { util::Buffer vertices; util::Buffer indices; - util::Buffer attrs; + util::Buffer attrs; MeshData() = default; @@ -24,7 +24,7 @@ struct MeshData { MeshData( util::Buffer vertices, util::Buffer indices, - util::Buffer attrs + util::Buffer attrs ) : vertices(std::move(vertices)), indices(std::move(indices)), attrs(std::move(attrs)) {} diff --git a/src/graphics/core/PostProcessing.cpp b/src/graphics/core/PostProcessing.cpp index 7352c3b5..3478ca08 100644 --- a/src/graphics/core/PostProcessing.cpp +++ b/src/graphics/core/PostProcessing.cpp @@ -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 }; - vattr attrs[] {{2}, {0}}; + VertexAttribute attrs[] {{2}, {0}}; quadMesh = std::make_unique(vertices, 6, attrs); } diff --git a/src/graphics/render/BlockWrapsRenderer.cpp b/src/graphics/render/BlockWrapsRenderer.cpp new file mode 100644 index 00000000..ddb93d3a --- /dev/null +++ b/src/graphics/render/BlockWrapsRenderer.cpp @@ -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(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("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 {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); +} diff --git a/src/graphics/render/BlockWrapsRenderer.hpp b/src/graphics/render/BlockWrapsRenderer.hpp new file mode 100644 index 00000000..728e664a --- /dev/null +++ b/src/graphics/render/BlockWrapsRenderer.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + +#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 batch; + + std::unordered_map> 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); +}; diff --git a/src/graphics/render/BlocksRenderer.cpp b/src/graphics/render/BlocksRenderer.cpp index bf55ef12..cb590bcb 100644 --- a/src/graphics/render/BlocksRenderer.cpp +++ b/src/graphics/render/BlocksRenderer.cpp @@ -12,7 +12,6 @@ #include -const uint BlocksRenderer::VERTEX_SIZE = 6; const glm::vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f); BlocksRenderer::BlocksRenderer( @@ -21,7 +20,7 @@ BlocksRenderer::BlocksRenderer( const ContentGfxCache& cache, const EngineSettings& settings ) : content(content), - vertexBuffer(std::make_unique(capacity * VERTEX_SIZE)), + vertexBuffer(std::make_unique(capacity * CHUNK_VERTEX_SIZE)), indexBuffer(std::make_unique(capacity)), vertexOffset(0), indexOffset(0), @@ -85,7 +84,7 @@ void BlocksRenderer::face( const glm::vec4(&lights)[4], const glm::vec4& tint ) { - if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) { + if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) { overflow = true; return; } @@ -125,7 +124,7 @@ void BlocksRenderer::faceAO( const UVRegion& region, bool lights ) { - if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) { + if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) { overflow = true; return; } @@ -163,7 +162,7 @@ void BlocksRenderer::face( glm::vec4 tint, bool lights ) { - if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) { + if (vertexOffset + CHUNK_VERTEX_SIZE * 4 > capacity) { overflow = true; return; } @@ -288,7 +287,7 @@ void BlocksRenderer::blockCustomModel( const auto& model = cache.getModel(block->rt.id); 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; return; } @@ -433,21 +432,9 @@ glm::vec4 BlocksRenderer::pickSoftLight( right, up); } -void BlocksRenderer::render(const voxel* 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; - } +void BlocksRenderer::render( + const voxel* voxels, int beginEnds[256][2] +) { for (const auto drawGroup : *content.drawGroups) { int begin = beginEnds[drawGroup][0]; if (begin == 0) { @@ -462,13 +449,13 @@ void BlocksRenderer::render(const voxel* voxels) { 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) + 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); @@ -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(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(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) { this->chunk = chunk; voxelsBuffer->setPosition( chunk->x * CHUNK_W - voxelBufferPadding, 0, chunk->z * CHUNK_D - voxelBufferPadding); chunks->getVoxels(voxelsBuffer.get(), settings.graphics.backlight.get()); - overflow = false; - vertexOffset = 0; - indexOffset = indexSize = 0; + if (voxelsBuffer->pickBlockId( chunk->x * CHUNK_W, 0, chunk->z * CHUNK_D ) == BLOCK_VOID) { cancelled = true; return; } - cancelled = false; 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() { - const vattr attrs[]{ {3}, {2}, {1}, {0} }; - return MeshData( - util::Buffer(vertexBuffer.get(), vertexOffset), - util::Buffer(indexBuffer.get(), indexSize), - util::Buffer({{3}, {2}, {1}, {0}}) - ); +ChunkMeshData BlocksRenderer::createMesh() { + return ChunkMeshData { + MeshData( + util::Buffer(vertexBuffer.get(), vertexOffset), + util::Buffer(indexBuffer.get(), indexSize), + util::Buffer( + CHUNK_VATTRS, sizeof(CHUNK_VATTRS) / sizeof(VertexAttribute) + ) + ), + std::move(sortingMesh)}; } -std::shared_ptr BlocksRenderer::render(const Chunk* chunk, const Chunks* chunks) { +ChunkMesh BlocksRenderer::render(const Chunk* chunk, const Chunks* chunks) { build(chunk, chunks); - const vattr attrs[]{ {3}, {2}, {1}, {0} }; - size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE; - return std::make_shared( - vertexBuffer.get(), vcount, indexBuffer.get(), indexSize, attrs - ); + size_t vcount = vertexOffset / CHUNK_VERTEX_SIZE; + return ChunkMesh{std::make_unique( + vertexBuffer.get(), vcount, indexBuffer.get(), indexSize, CHUNK_VATTRS + ), std::move(sortingMesh)}; } VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const { diff --git a/src/graphics/render/BlocksRenderer.hpp b/src/graphics/render/BlocksRenderer.hpp index 828ddf7d..c0e0086e 100644 --- a/src/graphics/render/BlocksRenderer.hpp +++ b/src/graphics/render/BlocksRenderer.hpp @@ -12,6 +12,7 @@ #include "voxels/VoxelsVolume.hpp" #include "graphics/core/MeshData.hpp" #include "maths/util.hpp" +#include "commons.hpp" class Content; class Mesh; @@ -26,7 +27,6 @@ struct UVRegion; class BlocksRenderer { static const glm::vec3 SUN_VECTOR; - static const uint VERTEX_SIZE; const Content& content; std::unique_ptr vertexBuffer; std::unique_ptr indexBuffer; @@ -45,6 +45,8 @@ class BlocksRenderer { util::PseudoRandom randomizer; + SortingMeshData sortingMesh; + 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); @@ -115,7 +117,6 @@ class BlocksRenderer { bool isOpenForLight(int x, int y, int z) const; - // Does block allow to see other blocks sides (is it transparent) inline bool isOpen(const glm::ivec3& pos, ubyte group) const { auto id = voxelsBuffer->pickBlockId( @@ -135,7 +136,9 @@ class BlocksRenderer { 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(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: BlocksRenderer( size_t capacity, @@ -146,8 +149,8 @@ public: virtual ~BlocksRenderer(); void build(const Chunk* chunk, const Chunks* chunks); - std::shared_ptr render(const Chunk* chunk, const Chunks* chunks); - MeshData createMesh(); + ChunkMesh render(const Chunk* chunk, const Chunks* chunks); + ChunkMeshData createMesh(); VoxelsVolume* getVoxelsBuffer() const; bool isCancelled() const { diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index d0ae56b4..78ed45bf 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -14,10 +14,6 @@ #include "util/listutil.hpp" #include "settings.hpp" -#include -#include -#include - static debug::Logger logger("chunks-render"); size_t ChunksRenderer::visibleChunks = 0; @@ -62,7 +58,11 @@ ChunksRenderer::ChunksRenderer( [&](){return std::make_shared(*level, cache, settings);}, [&](RendererResult& result){ if (!result.cancelled) { - meshes[result.key] = std::make_shared(result.meshData); + auto meshData = std::move(result.meshData); + meshes[result.key] = ChunkMesh { + std::make_unique(meshData.mesh), + std::move(meshData.sortingMesh) + }; } inwork.erase(result.key); }, settings.graphics.chunkMaxRenderers.get()) @@ -78,12 +78,16 @@ ChunksRenderer::ChunksRenderer( ChunksRenderer::~ChunksRenderer() { } -std::shared_ptr ChunksRenderer::render(const std::shared_ptr& chunk, bool important) { +const Mesh* ChunksRenderer::render( + const std::shared_ptr& chunk, bool important +) { chunk->flags.modified = false; if (important) { auto mesh = renderer->render(chunk.get(), level.chunks.get()); - meshes[glm::ivec2(chunk->x, chunk->z)] = mesh; - return mesh; + meshes[glm::ivec2(chunk->x, chunk->z)] = ChunkMesh { + 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); if (inwork.find(key) != inwork.end()) { @@ -107,7 +111,9 @@ void ChunksRenderer::clear() { threadPool.clearQueue(); } -std::shared_ptr ChunksRenderer::getOrRender(const std::shared_ptr& chunk, bool important) { +const Mesh* ChunksRenderer::getOrRender( + const std::shared_ptr& chunk, bool important +) { auto found = meshes.find(glm::ivec2(chunk->x, chunk->z)); if (found == meshes.end()) { return render(chunk, important); @@ -115,19 +121,19 @@ std::shared_ptr ChunksRenderer::getOrRender(const std::shared_ptr& if (chunk->flags.modified) { render(chunk, important); } - return found->second; + return found->second.mesh.get(); } void ChunksRenderer::update() { threadPool.update(); } -bool ChunksRenderer::drawChunk( +const Mesh* ChunksRenderer::retrieveChunk( size_t index, const Camera& camera, Shader& shader, bool culling ) { auto chunk = level.chunks->getChunks()[index]; if (chunk == nullptr || !chunk->flags.lighted) { - return false; + return nullptr; } float distance = glm::distance( camera.position, @@ -139,7 +145,7 @@ bool ChunksRenderer::drawChunk( ); auto mesh = getOrRender(chunk, distance < CHUNK_W * 1.5f); if (mesh == nullptr) { - return false; + return nullptr; } if (culling) { 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 ); - 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); - glm::mat4 model = glm::translate(glm::mat4(1.0f), coord); - shader.uniformMatrix("u_model", model); - mesh->draw(); - return true; + return mesh; } void ChunksRenderer::drawChunks( @@ -191,11 +193,109 @@ void ChunksRenderer::drawChunks( bool culling = settings.graphics.frustumCulling.get(); visibleChunks = 0; - //if (GLEW_ARB_multi_draw_indirect && false) { - // TODO: implement Multi Draw Indirect chunks draw - //} else { - for (size_t i = 0; i < indices.size(); i++) { - visibleChunks += drawChunk(indices[i].index, camera, shader, culling); + shader.uniform1i("u_alphaClip", true); + + // TODO: minimize draw calls number + for (size_t i = 0; i < indices.size(); i++) { + 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& 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("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( + entry.vertexData.data(), + entry.vertexData.size() / CHUNK_VERTEX_SIZE, + CHUNK_VATTRS + ); + } + found->second.sortedMesh->draw(); + continue; + } + for (auto& entry : chunkEntries) { + entry.distance = static_cast( + 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 buffer; + if (buffer.size() < size) { + buffer = util::Buffer(size); + } + write_sorting_mesh_entries(buffer.data(), chunkEntries); + found->second.sortedMesh = std::make_unique( + buffer.data(), size / CHUNK_VERTEX_SIZE, CHUNK_VATTRS + ); + } + found->second.sortedMesh->draw(); + } } diff --git a/src/graphics/render/ChunksRenderer.hpp b/src/graphics/render/ChunksRenderer.hpp index c08266bb..859bca9e 100644 --- a/src/graphics/render/ChunksRenderer.hpp +++ b/src/graphics/render/ChunksRenderer.hpp @@ -4,19 +4,21 @@ #include #include #include + #include +#define GLM_ENABLE_EXPERIMENTAL +#include #include "voxels/Block.hpp" -#include "voxels/ChunksStorage.hpp" #include "util/ThreadPool.hpp" #include "graphics/core/MeshData.hpp" +#include "commons.hpp" class Mesh; class Chunk; class Level; class Camera; class Shader; -class Chunks; class Assets; class Frustum; class BlocksRenderer; @@ -35,7 +37,7 @@ struct ChunksSortEntry { struct RendererResult { glm::ivec2 key; bool cancelled; - MeshData meshData; + ChunkMeshData meshData; }; class ChunksRenderer { @@ -45,12 +47,11 @@ class ChunksRenderer { const EngineSettings& settings; std::unique_ptr renderer; - std::unordered_map> meshes; + std::unordered_map meshes; std::unordered_map inwork; std::vector indices; util::ThreadPool, RendererResult> threadPool; - - bool drawChunk( + const Mesh* retrieveChunk( size_t index, const Camera& camera, Shader& shader, bool culling ); public: @@ -63,17 +64,19 @@ public: ); virtual ~ChunksRenderer(); - std::shared_ptr render( + const Mesh* render( const std::shared_ptr& chunk, bool important ); void unload(const Chunk* chunk); void clear(); - std::shared_ptr getOrRender( + const Mesh* getOrRender( const std::shared_ptr& chunk, bool important ); void drawChunks(const Camera& camera, Shader& shader); + void drawSortedMeshes(const Camera& camera, Shader& shader); + void update(); static size_t visibleChunks; diff --git a/src/graphics/render/MainBatch.cpp b/src/graphics/render/MainBatch.cpp index 1cbb6c42..a440a43b 100644 --- a/src/graphics/render/MainBatch.cpp +++ b/src/graphics/render/MainBatch.cpp @@ -6,7 +6,7 @@ #include "voxels/Chunks.hpp" #include "voxels/Chunk.hpp" -static const vattr attrs[] = { +static const VertexAttribute attrs[] = { {3}, {2}, {3}, {1}, {0} }; @@ -97,38 +97,38 @@ void MainBatch::cube( const glm::vec3 Z(0.0f, 0.0f, 1.0f); quad( - coord + glm::vec3(0.0f, 0.0f, 0.0f), + coord + Z * size.z * 0.5f, X, Y, glm::vec2(size.x, size.y), (shading ? do_tint(0.8) * tint : tint), glm::vec3(1.0f), texfaces[5] ); quad( - coord + glm::vec3(size.x, 0.0f, -size.z), + coord - Z * size.z * 0.5f, -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] ); quad( - coord + glm::vec3(0.0f, size.y, 0.0f), - X, -Z, glm::vec2(size.x, size.z), + coord + Y * size.y * 0.5f, + -X, Z, glm::vec2(size.x, size.z), (shading ? do_tint(1.0f) * tint : tint), glm::vec3(1.0f), texfaces[3] ); quad( - coord + glm::vec3(0.0f, 0.0f, -size.z), + coord - Y * size.y * 0.5f, X, Z, glm::vec2(size.x, size.z), (shading ? do_tint(0.7f) * tint : tint), glm::vec3(1.0f), texfaces[2] ); quad( - coord + glm::vec3(0.0f, 0.0f, -size.z), - Z, Y, glm::vec2(size.z, size.y), - (shading ? do_tint(0.9f) * tint : tint), - glm::vec3(1.0f), texfaces[0] + coord + X * size.x * 0.5f, + -Z, Y, glm::vec2(size.z, size.y), + (shading ? do_tint(0.8f) * tint : tint), + glm::vec3(1.0f), texfaces[1] ); quad( - coord + glm::vec3(size.x, 0.0f, 0.0f), - -Z, Y, glm::vec2(size.z, size.y), + coord - X * size.x * 0.5f, + Z, Y, glm::vec2(size.z, size.y), (shading ? do_tint(0.9f) * tint : tint), glm::vec3(1.0f), texfaces[1] ); diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index 1fd8e860..0e2292a5 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -89,11 +89,13 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { ); light *= 0.9f + (particle.random % 100) * 0.001f; } + float scale = 1.0f + ((particle.random ^ 2628172) % 1000) * + 0.001f * preset.sizeSpread; batch->quad( particle.position, right, preset.globalUpVector ? glm::vec3(0, 1, 0) : up, - preset.size, + preset.size * scale, light, glm::vec3(1.0f), particle.region diff --git a/src/graphics/render/Skybox.cpp b/src/graphics/render/Skybox.cpp index e96e894a..af3a1587 100644 --- a/src/graphics/render/Skybox.cpp +++ b/src/graphics/render/Skybox.cpp @@ -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 }; - vattr attrs[] {{2}, {0}}; + VertexAttribute attrs[] {{2}, {0}}; mesh = std::make_unique(vertices, 6, attrs); sprites.push_back(skysprite { diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 1ad16a62..7084d2de 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -41,6 +41,7 @@ #include "graphics/core/Shader.hpp" #include "graphics/core/Texture.hpp" #include "graphics/core/Font.hpp" +#include "BlockWrapsRenderer.hpp" #include "ParticlesRenderer.hpp" #include "TextsRenderer.hpp" #include "ChunksRenderer.hpp" @@ -80,7 +81,8 @@ WorldRenderer::WorldRenderer( *frustumCulling, frontend.getContentGfxCache(), engine->getSettings() - )) { + )), + blockWraps(std::make_unique(assets, level)) { auto& settings = engine->getSettings(); level.events->listen( EVT_CHUNK_HIDDEN, @@ -153,6 +155,7 @@ void WorldRenderer::renderLevel( frustumCulling->update(camera.getProjView()); } + entityShader.uniform1i("u_alphaClip", true); level.entities->render( assets, *modelBatch, @@ -164,9 +167,18 @@ void WorldRenderer::renderLevel( particles->render(camera, delta * !pause); auto& shader = assets.require("main"); + auto& linesShader = assets.require("lines"); + setupWorldShader(shader, camera, settings, fogFactor); chunks->drawChunks(camera, shader); + blockWraps->draw(ctx, *player); + + if (hudVisible) { + renderLines(camera, linesShader, ctx); + } + shader.use(); + chunks->drawSortedMeshes(camera, shader); if (!pause) { scripting::on_frontend_render(); @@ -326,7 +338,6 @@ void WorldRenderer::draw( ctx, camera, *lineBatch, linesShader, showChunkBorders ); } - renderLines(camera, linesShader, ctx); if (player->currentCamera == player->fpCamera) { renderHands(camera, delta * !pause); } diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index b94694bd..c3ea72f1 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -17,6 +17,7 @@ class Batch3D; class LineBatch; class ChunksRenderer; class ParticlesRenderer; +class BlockWrapsRenderer; class GuidesRenderer; class TextsRenderer; class Shader; @@ -68,6 +69,7 @@ class WorldRenderer { public: std::unique_ptr texts; std::unique_ptr particles; + std::unique_ptr blockWraps; static bool showChunkBorders; static bool showEntitiesDebug; diff --git a/src/graphics/render/commons.hpp b/src/graphics/render/commons.hpp new file mode 100644 index 00000000..79b77393 --- /dev/null +++ b/src/graphics/render/commons.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + +#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 vertexData; + long long distance; + + inline bool operator<(const SortingMeshEntry& o) const noexcept { + return distance > o.distance; + } +}; + +struct SortingMeshData { + std::vector entries; +}; + +struct ChunkMeshData { + MeshData mesh; + SortingMeshData sortingMesh; +}; + +struct ChunkMesh { + std::unique_ptr mesh; + SortingMeshData sortingMeshData; + std::unique_ptr sortedMesh = nullptr; +}; diff --git a/src/graphics/ui/elements/Container.cpp b/src/graphics/ui/elements/Container.cpp index 3b8fd0ac..fdd0bc02 100644 --- a/src/graphics/ui/elements/Container.cpp +++ b/src/graphics/ui/elements/Container.cpp @@ -90,7 +90,7 @@ void Container::draw(const DrawContext* pctx, Assets* assets) { if (!nodes.empty()) { batch->flush(); 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) { if (node->isVisible()) node->draw(pctx, assets); @@ -108,7 +108,7 @@ void Container::drawBackground(const DrawContext* pctx, Assets*) { auto batch = pctx->getBatch2D(); batch->texture(nullptr); 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 &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() { std::stable_sort(nodes.begin(), nodes.end(), [](const auto& a, const auto& b) { return a->getZIndex() < b->getZIndex(); diff --git a/src/graphics/ui/elements/Container.hpp b/src/graphics/ui/elements/Container.hpp index a5cca04c..bfe40ab5 100644 --- a/src/graphics/ui/elements/Container.hpp +++ b/src/graphics/ui/elements/Container.hpp @@ -32,6 +32,8 @@ namespace gui { void listenInterval(float interval, ontimeout callback, int repeat=-1); virtual glm::vec2 getContentOffset() override {return glm::vec2(0.0f, scroll);}; virtual void setSize(glm::vec2 size) override; + virtual int getScrollStep() const; + virtual void setScrollStep(int step); virtual void refresh() override; const std::vector>& getNodes() const; diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 9fe44327..6a54d682 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -1,5 +1,6 @@ #include "TextBox.hpp" +#include #include #include @@ -14,24 +15,39 @@ using namespace gui; +inline constexpr int LINE_NUMBERS_PANE_WIDTH = 40; + TextBox::TextBox(std::wstring placeholder, glm::vec4 padding) - : Panel(glm::vec2(200,32), padding, 0), + : Container(glm::vec2(200,32)), + padding(padding), input(L""), placeholder(std::move(placeholder)) { setOnUpPressed(nullptr); setOnDownPressed(nullptr); + setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.75f)); label = std::make_shared