mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-04 10:31:50 +00:00
add block material shaders
- 'shader' property of block materials: shaders/materials/<name>.glslv, .glslf with material_vertex / material_fragment hooks and #param uniforms - material shaders code is included into main, translucent and shadows programs through generated headers, no extra draw calls - chunk vertex normal.w byte replaced with 'flags': bit 7 - emission flag, bits 0-6 - material shader index (vertex format size is not changed) - doc/*/block-materials.md
This commit is contained in:
parent
f90750c066
commit
81489a13cf
33 changed files with 761 additions and 53 deletions
97
doc/en/block-materials.md
Normal file
97
doc/en/block-materials.md
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
# Block materials
|
||||
|
||||
A material is a common kit of properties applied to groups of blocks.
|
||||
Material definitions are located in the `block_materials/` folder of a content pack.
|
||||
A block refers to a material with the `material` property in the `pack:material_name` format
|
||||
(see [block properties](block-properties.md)).
|
||||
|
||||
Example (`block_materials/glass.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"steps-sound": "steps/glass",
|
||||
"place-sound": "blocks/glass_place",
|
||||
"break-sound": "blocks/glass_break",
|
||||
"sound-absorption": 0.3
|
||||
}
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Name | Type | Description |
|
||||
|------------------|--------|----------------------------------------------------------|
|
||||
| steps-sound | string | steps sound |
|
||||
| place-sound | string | block placing sound |
|
||||
| break-sound | string | block breaking sound |
|
||||
| hit-sound | string | block hitting sound (`steps-sound` by default) |
|
||||
| sound-absorption | number | ambient sounds absorption when the camera is in the block|
|
||||
| shader | string | material shader name (see below) |
|
||||
|
||||
## Material shaders
|
||||
|
||||
A material shader allows to customize blocks and entities rendering without replacing the engine shaders.
|
||||
Material shader code is included in the world shaders (opaque, translucent and shadows passes of blocks
|
||||
and the entities program), so blocks with different materials are still rendered within a single draw call per chunk.
|
||||
|
||||
An entity uses the shader of its `material` property (see [entity properties](entity-properties.md)).
|
||||
The material may be overridden at runtime with `rig:set_material` (see [skeleton](scripting/ecs.md)).
|
||||
|
||||
The `shader` property specifies the name of a shader located in the `shaders/materials/` folder of a pack.
|
||||
Each of two files is optional, but an existing file must define its function:
|
||||
|
||||
- `shaders/materials/name.glslv` - vertex stage, must define the function:
|
||||
```glsl
|
||||
// position - vertex world position
|
||||
vec3 material_vertex(vec3 position)
|
||||
```
|
||||
The function may also modify the vertex normal `a_realnormal` (world space),
|
||||
for example when displacing a surface.
|
||||
- `shaders/materials/name.glslf` - fragment stage, must define the function:
|
||||
```glsl
|
||||
// color - block texture color before lighting applied
|
||||
vec4 material_fragment(vec4 color)
|
||||
```
|
||||
|
||||
All the world shader uniforms (`u_timer`, `u_cameraPos`, `u_dayTime`, etc.),
|
||||
vertex attributes (`v_position`, `v_texCoord`, `v_light`, `v_normal`) in the vertex stage and
|
||||
varyings (`a_texCoord`, `a_modelpos`, `a_realnormal`, `a_skyLight`, etc.) in the fragment stage are available.
|
||||
The same material shader code is compiled into all the programs (blocks, shadows and entities), so only
|
||||
the common interface declared in `shaders/lib/world_vertex_header.glsl`, `world_fragment_header.glsl`
|
||||
and `world_uniforms.glsl` may be used.
|
||||
Shader parameters are declared with the `#param` directive, the same way as in [post-effects](scripting/builtins/libgfx-posteffects.md),
|
||||
and controlled from scripts with the [gfx.materials](scripting/builtins/libgfx-materials.md) library.
|
||||
|
||||
Example (`shaders/materials/leaves.glslv`):
|
||||
|
||||
```glsl
|
||||
#param float p_amplitude = 0.03
|
||||
#param float p_speed = 1.2
|
||||
|
||||
vec3 material_vertex(vec3 position) {
|
||||
float phase = position.x * 0.7 + position.z * 1.1 + position.y * 0.4;
|
||||
position.x += sin(u_timer * p_speed + phase) * p_amplitude;
|
||||
return position;
|
||||
}
|
||||
```
|
||||
|
||||
Materials with the same `shader` share the shader code and its params.
|
||||
Hook functions and params names are made unique for every shader automatically,
|
||||
so different shaders may declare the same params.
|
||||
Other functions and global variables of all the material shaders are compiled
|
||||
into one program, so their names must be unique (use a prefix).
|
||||
|
||||
> [!NOTE]
|
||||
> Compilation errors refer to a material shader by its index: `3:12` means line 12
|
||||
> of the shader number 3. The indices are printed to the log on content loading,
|
||||
> `128` is the generated dispatch code, `0` is the engine shader.
|
||||
|
||||
Limitations:
|
||||
|
||||
- up to 127 material shaders may be loaded at the same time
|
||||
- array params are not supported, params names must not start with `_`
|
||||
- the same param declared in both stages must have the same type
|
||||
- vertex displacement must be continuous (depend on the vertex position only),
|
||||
otherwise gaps between faces appear; it must not depend on `u_cameraPos`,
|
||||
because the shadows pass is rendered from the light's point of view
|
||||
- `texture()` calls inside `material_fragment` have undefined derivatives
|
||||
at borders between materials, use `textureLod` there
|
||||
|
|
@ -81,7 +81,7 @@ until the block is destroyed or the camera moves away a certain distance.
|
|||
### Material - *material*
|
||||
|
||||
Defines the name of the block's material in the format `pack:material_name`, which affects the selection of block interaction sounds.
|
||||
Material definitions are located in /block_materials.
|
||||
Material definitions are located in /block_materials (see [block materials](block-materials.md)).
|
||||
|
||||
## Variants
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ Don't be intimidated by the following text, as a minimal pack only requires `pac
|
|||
- `textures/` - Textures
|
||||
- `shaders/` - Shaders
|
||||
- `effects/` - Post-processing effects
|
||||
- `materials/` - Block material shaders
|
||||
- `sounds/` - Sounds and Music
|
||||
- `texts/` - Localization files
|
||||
- GUI:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
- [Assets preloading](assets-preload.md)
|
||||
- [Audio](audio.md)
|
||||
- [Block materials](block-materials.md)
|
||||
- [Block models](block-models.md)
|
||||
- [Block properties](block-properties.md)
|
||||
- [Console](console.md)
|
||||
|
|
|
|||
97
doc/ru/block-materials.md
Normal file
97
doc/ru/block-materials.md
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
# Материалы блоков
|
||||
|
||||
Материал - общий набор свойств, применяемых к группам блоков.
|
||||
Определения материалов расположены в папке `block_materials/` контент-пака.
|
||||
Блок ссылается на материал свойством `material` в формате `пак:имя_материала`
|
||||
(см. [свойства блоков](block-properties.md)).
|
||||
|
||||
Пример (`block_materials/glass.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"steps-sound": "steps/glass",
|
||||
"place-sound": "blocks/glass_place",
|
||||
"break-sound": "blocks/glass_break",
|
||||
"sound-absorption": 0.3
|
||||
}
|
||||
```
|
||||
|
||||
## Свойства
|
||||
|
||||
| Имя | Тип | Описание |
|
||||
|------------------|--------|------------------------------------------------------------|
|
||||
| steps-sound | string | звук шагов |
|
||||
| place-sound | string | звук установки блока |
|
||||
| break-sound | string | звук разрушения блока |
|
||||
| hit-sound | string | звук удара по блоку (по умолчанию - `steps-sound`) |
|
||||
| sound-absorption | number | поглощение окружающих звуков, когда камера внутри блока |
|
||||
| shader | string | имя шейдера материала (см. ниже) |
|
||||
|
||||
## Шейдеры материалов
|
||||
|
||||
Шейдер материала позволяет настроить отрисовку блоков и сущностей без замены шейдеров движка.
|
||||
Код шейдера материала включается в мировые шейдеры (проходы непрозрачных, полупрозрачных блоков, теней
|
||||
и программу сущностей), поэтому блоки с разными материалами по-прежнему отрисовываются одним вызовом на чанк.
|
||||
|
||||
Сущность использует шейдер материала из своего свойства `material` (см. [свойства сущностей](entity-properties.md)).
|
||||
Материал можно переопределить в рантайме через `rig:set_material` (см. [скелет](scripting/ecs.md)).
|
||||
|
||||
Свойство `shader` указывает имя шейдера, расположенного в папке `shaders/materials/` пака.
|
||||
Каждый из двух файлов опционален, но существующий файл обязан определять свою функцию:
|
||||
|
||||
- `shaders/materials/имя.glslv` - вершинная стадия, должен определять функцию:
|
||||
```glsl
|
||||
// position - мировая позиция вершины
|
||||
vec3 material_vertex(vec3 position)
|
||||
```
|
||||
Функция также может изменять нормаль вершины `a_realnormal` (мировые координаты),
|
||||
например, при смещении поверхности.
|
||||
- `shaders/materials/имя.glslf` - фрагментная стадия, должен определять функцию:
|
||||
```glsl
|
||||
// color - цвет текстуры блока до применения освещения
|
||||
vec4 material_fragment(vec4 color)
|
||||
```
|
||||
|
||||
Доступны все uniform-переменные мировых шейдеров (`u_timer`, `u_cameraPos`, `u_dayTime` и т.д.),
|
||||
атрибуты вершин (`v_position`, `v_texCoord`, `v_light`, `v_normal`) в вершинной стадии и
|
||||
varying-переменные (`a_texCoord`, `a_modelpos`, `a_realnormal`, `a_skyLight` и т.д.) во фрагментной.
|
||||
Один и тот же код материала компилируется во все программы (блоки, тени и сущности), поэтому можно
|
||||
использовать только общий интерфейс, объявленный в `shaders/lib/world_vertex_header.glsl`,
|
||||
`world_fragment_header.glsl` и `world_uniforms.glsl`.
|
||||
Параметры шейдера объявляются директивой `#param`, так же, как в [пост-эффектах](scripting/builtins/libgfx-posteffects.md),
|
||||
и управляются из скриптов библиотекой [gfx.materials](scripting/builtins/libgfx-materials.md).
|
||||
|
||||
Пример (`shaders/materials/leaves.glslv`):
|
||||
|
||||
```glsl
|
||||
#param float p_amplitude = 0.03
|
||||
#param float p_speed = 1.2
|
||||
|
||||
vec3 material_vertex(vec3 position) {
|
||||
float phase = position.x * 0.7 + position.z * 1.1 + position.y * 0.4;
|
||||
position.x += sin(u_timer * p_speed + phase) * p_amplitude;
|
||||
return position;
|
||||
}
|
||||
```
|
||||
|
||||
Материалы с одинаковым `shader` используют общий код шейдера и его параметры.
|
||||
Имена функций-хуков и параметров автоматически делаются уникальными для каждого шейдера,
|
||||
поэтому разные шейдеры могут объявлять одинаковые параметры.
|
||||
Остальные функции и глобальные переменные всех шейдеров материалов компилируются
|
||||
в одну программу, поэтому их имена должны быть уникальными (используйте префикс).
|
||||
|
||||
> [!NOTE]
|
||||
> Ошибки компиляции ссылаются на шейдер материала по его номеру: `3:12` означает строку 12
|
||||
> шейдера номер 3. Номера печатаются в лог при загрузке контента,
|
||||
> `128` это сгенерированный код диспетчера, `0` шейдер движка.
|
||||
|
||||
Ограничения:
|
||||
|
||||
- одновременно может быть загружено до 127 шейдеров материалов
|
||||
- параметры-массивы не поддерживаются, имена параметров не могут начинаться с `_`
|
||||
- один и тот же параметр в обеих стадиях должен иметь одинаковый тип
|
||||
- смещение вершин должно быть непрерывным (зависеть только от позиции вершины),
|
||||
иначе между гранями появляются щели; оно не должно зависеть от `u_cameraPos`,
|
||||
так как проход теней рисуется с точки зрения источника света
|
||||
- у вызовов `texture()` внутри `material_fragment` не определены производные
|
||||
на границах материалов, используйте там `textureLod`
|
||||
|
|
@ -82,7 +82,7 @@
|
|||
### Материал - *material*
|
||||
|
||||
Определяет имя материала блока в формате `пак:имя_материала`, что влияет на выбор звуков взаимодействия с блоком.
|
||||
Определения материалов расположены в /block_materials.
|
||||
Определения материалов расположены в /block_materials (см. [материалы блоков](block-materials.md)).
|
||||
|
||||
## Варианты
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ print(file.read("пак:package.json")) -- выведет содержимое p
|
|||
- `textures/` - текстуры
|
||||
- `shaders/` - шейдеры
|
||||
- `effects/` - эффекты пост-обработки
|
||||
- `materials/` - шейдеры материалов блоков
|
||||
- `sounds/` - звуки и музыка
|
||||
- `texts/` - файлы локализации
|
||||
- GUI:
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
- [Движок генерации мира](world-generator.md)
|
||||
- [Консоль](console.md)
|
||||
- [Контент‐паки](content-packs.md)
|
||||
- [Материалы блоков](block-materials.md)
|
||||
- [Модели блоков](block-models.md)
|
||||
- [Предзагрузка ассетов](assets-preload.md)
|
||||
- [Рекомендации по использованию движка](engine-use-recommendations.md)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ void main() {
|
|||
a_fog = calc_fog(length(viewmodel * vec4(pos3d * FOG_POS_SCALE, 0.0)) / 256.0);
|
||||
#endif
|
||||
|
||||
a_emission = v_normal.w;
|
||||
a_emission = unpack_emission(v_normal.w);
|
||||
|
||||
vec4 viewmodelpos = u_view * a_modelpos;
|
||||
a_position = viewmodelpos.xyz;
|
||||
|
|
|
|||
|
|
@ -8,4 +8,13 @@ vec3 apply_planet_curvature(vec3 modelPos, vec3 pos3d) {
|
|||
return modelPos;
|
||||
}
|
||||
|
||||
// chunk vertex flags (normal.w): bit 7 - emission flag, bits 0-6 - material
|
||||
float unpack_emission(float w) {
|
||||
return float(int(w * 255.0 + 0.5) >> 7);
|
||||
}
|
||||
|
||||
int unpack_material(float w) {
|
||||
return int(w * 255.0 + 0.5) & 127;
|
||||
}
|
||||
|
||||
#endif // COMMONS_GLSL_
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ in vec3 a_realnormal;
|
|||
in vec3 a_skyLight;
|
||||
in vec4 a_modelpos;
|
||||
in float a_emission;
|
||||
flat in int a_material;
|
||||
|
||||
#include <world_uniforms>
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ out vec3 a_realnormal;
|
|||
out vec3 a_skyLight;
|
||||
out vec4 a_modelpos;
|
||||
out float a_emission;
|
||||
flat out int a_material;
|
||||
|
||||
#include <world_uniforms>
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,12 @@ uniform bool u_alphaClip;
|
|||
uniform bool u_debugLights;
|
||||
uniform bool u_debugNormals;
|
||||
|
||||
#include <__materials_fragment__>
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(u_texture0, a_texCoord);
|
||||
vec4 texColor = apply_material_fragment(
|
||||
a_material, texture(u_texture0, a_texCoord)
|
||||
);
|
||||
float alpha = texColor.a;
|
||||
if (u_alphaClip) {
|
||||
if (alpha < 0.2f)
|
||||
|
|
|
|||
|
|
@ -14,11 +14,16 @@ uniform float u_dayTime;
|
|||
|
||||
out vec4 a_torchLight;
|
||||
|
||||
#include <__materials_vertex__>
|
||||
|
||||
void main() {
|
||||
a_material = unpack_material(v_normal.w);
|
||||
a_realnormal = v_normal.xyz * 2.0 - 1.0;
|
||||
a_modelpos = u_model * vec4(v_position, 1.0f);
|
||||
// material shader may also modify a_realnormal
|
||||
a_modelpos.xyz = apply_material_vertex(a_material, a_modelpos.xyz);
|
||||
vec3 pos3d = a_modelpos.xyz - u_cameraPos;
|
||||
|
||||
a_realnormal = v_normal.xyz * 2.0 - 1.0;
|
||||
a_normal = calc_screen_normal(a_realnormal);
|
||||
|
||||
a_torchLight = vec4(calc_torch_light(
|
||||
|
|
@ -37,7 +42,7 @@ void main() {
|
|||
a_fog = calc_fog(length(viewmodel * vec4(pos3d * FOG_POS_SCALE, 0.0)) / 256.0);
|
||||
#endif
|
||||
|
||||
a_emission = v_normal.w;
|
||||
a_emission = unpack_emission(v_normal.w);
|
||||
|
||||
vec4 viewmodelpos = u_view * a_modelpos;
|
||||
a_position = viewmodelpos.xyz;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
in vec2 a_texCoord;
|
||||
#include <world_fragment_header>
|
||||
|
||||
uniform sampler2D u_texture0;
|
||||
|
||||
#include <__materials_fragment__>
|
||||
|
||||
void main() {
|
||||
vec4 tex_color = texture(u_texture0, a_texCoord);
|
||||
vec4 tex_color = apply_material_fragment(
|
||||
a_material, texture(u_texture0, a_texCoord)
|
||||
);
|
||||
if (tex_color.a < 0.5) {
|
||||
discard;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,13 +5,18 @@ layout (location = 1) in vec2 v_texCoord;
|
|||
layout (location = 2) in vec4 v_light;
|
||||
layout (location = 3) in vec4 v_normal;
|
||||
|
||||
out vec2 a_texCoord;
|
||||
// same vertex stage interface as world shaders for material shaders
|
||||
#include <world_vertex_header>
|
||||
|
||||
uniform mat4 u_model;
|
||||
uniform mat4 u_proj;
|
||||
uniform mat4 u_view;
|
||||
uniform float u_dayTime;
|
||||
|
||||
#include <__materials_vertex__>
|
||||
|
||||
void main() {
|
||||
a_texCoord = v_texCoord;
|
||||
gl_Position = u_proj * u_view * u_model * vec4(v_position, 1.0f);
|
||||
a_material = unpack_material(v_normal.w);
|
||||
a_realnormal = v_normal.xyz * 2.0 - 1.0;
|
||||
a_modelpos = u_model * vec4(v_position, 1.0f);
|
||||
a_modelpos.xyz = apply_material_vertex(a_material, a_modelpos.xyz);
|
||||
gl_Position = u_proj * u_view * a_modelpos;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,12 @@ uniform bool u_debugNormals;
|
|||
|
||||
#include <shadows>
|
||||
|
||||
#include <__materials_fragment__>
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(u_texture0, a_texCoord);
|
||||
vec4 texColor = apply_material_fragment(
|
||||
a_material, texture(u_texture0, a_texCoord)
|
||||
);
|
||||
float alpha = texColor.a;
|
||||
if (u_alphaClip) {
|
||||
if (alpha < 0.2f)
|
||||
|
|
|
|||
|
|
@ -14,11 +14,15 @@ out vec4 a_torchLight;
|
|||
|
||||
uniform float u_dayTime;
|
||||
|
||||
#include <__materials_vertex__>
|
||||
|
||||
void main() {
|
||||
a_material = unpack_material(v_normal.w);
|
||||
a_realnormal = v_normal.xyz * 2.0 - 1.0;
|
||||
a_modelpos = u_model * vec4(v_position, 1.0f);
|
||||
a_modelpos.xyz = apply_material_vertex(a_material, a_modelpos.xyz);
|
||||
vec3 pos3d = a_modelpos.xyz - u_cameraPos;
|
||||
|
||||
a_realnormal = v_normal.xyz * 2.0 - 1.0;
|
||||
a_normal = calc_screen_normal(a_realnormal);
|
||||
|
||||
a_torchLight = vec4(calc_torch_light(
|
||||
|
|
@ -33,7 +37,7 @@ void main() {
|
|||
mat4 viewmodel = u_view * u_model;
|
||||
a_distance = length(viewmodel * vec4(pos3d, 0.0));
|
||||
a_fog = calc_fog(length(viewmodel * vec4(pos3d * FOG_POS_SCALE, 0.0)) / 256.0);
|
||||
a_emission = v_normal.w;
|
||||
a_emission = unpack_emission(v_normal.w);
|
||||
|
||||
vec4 viewmodelpos = u_view * a_modelpos;
|
||||
a_position = viewmodelpos.xyz;
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ inline constexpr blockid_t BLOCK_VOID = std::numeric_limits<blockid_t>::max();
|
|||
inline constexpr itemid_t ITEM_VOID = std::numeric_limits<itemid_t>::max();
|
||||
/// @brief max number of block definitions possible
|
||||
inline constexpr blockid_t MAX_BLOCKS = BLOCK_VOID;
|
||||
/// @brief max number of material shaders (7 bits in chunk vertex)
|
||||
inline constexpr int MAX_BLOCK_MATERIAL_SHADERS = 127;
|
||||
|
||||
/// @brief calculates a 1D array index from 3D array indices
|
||||
inline constexpr uint vox_index(uint x, uint y, uint z, uint w=CHUNK_W, uint d=CHUNK_D) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
#include "ContentBuilder.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "constants.hpp"
|
||||
|
||||
ContentBuilder::~ContentBuilder() = default;
|
||||
|
||||
void ContentBuilder::add(std::unique_ptr<ContentPackRuntime> pack) {
|
||||
|
|
@ -13,7 +19,48 @@ BlockMaterial& ContentBuilder::createBlockMaterial(const std::string& id) {
|
|||
return material;
|
||||
}
|
||||
|
||||
/// @brief Assign indices to the material shaders (materials using the same
|
||||
/// shader share its index)
|
||||
static void index_material_shaders(
|
||||
const UptrsMap<std::string, BlockMaterial>& blockMaterials
|
||||
) {
|
||||
std::set<std::string> shaders;
|
||||
for (const auto& [name, material] : blockMaterials) {
|
||||
if (!material->shader.empty()) {
|
||||
shaders.insert(material->shader);
|
||||
}
|
||||
}
|
||||
if (shaders.size() > MAX_BLOCK_MATERIAL_SHADERS) {
|
||||
throw std::runtime_error(
|
||||
"too many material shaders (max " +
|
||||
std::to_string(MAX_BLOCK_MATERIAL_SHADERS) + ")"
|
||||
);
|
||||
}
|
||||
std::unordered_map<std::string, uint8_t> indices;
|
||||
for (const auto& shader : shaders) {
|
||||
indices[shader] = indices.size() + 1;
|
||||
}
|
||||
for (const auto& [name, material] : blockMaterials) {
|
||||
if (!material->shader.empty()) {
|
||||
material->rt.shaderId = indices.at(material->shader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t material_shader_id(
|
||||
const UptrsMap<std::string, BlockMaterial>& blockMaterials,
|
||||
const std::string& material
|
||||
) {
|
||||
const auto& found = blockMaterials.find(material);
|
||||
if (found == blockMaterials.end()) {
|
||||
return 0;
|
||||
}
|
||||
return found->second->rt.shaderId;
|
||||
}
|
||||
|
||||
std::unique_ptr<Content> ContentBuilder::build() {
|
||||
index_material_shaders(blockMaterials);
|
||||
|
||||
std::vector<Block*> blockDefsIndices;
|
||||
auto groups = std::make_unique<DrawGroups>();
|
||||
for (const std::string& name : blocks.names) {
|
||||
|
|
@ -50,6 +97,7 @@ std::unique_ptr<Content> ContentBuilder::build() {
|
|||
if (def.material.empty()) {
|
||||
defaults.at("block-material").get(def.material);
|
||||
}
|
||||
def.rt.materialShader = material_shader_id(blockMaterials, def.material);
|
||||
|
||||
if (def.rotatable) {
|
||||
for (uint i = 0; i < BlockRotProfile::MAX_COUNT; i++) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "engine/Engine.hpp"
|
||||
#include "EnginePaths.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/render/MaterialShaders.hpp"
|
||||
#include "graphics/render/ModelsGenerator.hpp"
|
||||
#include "graphics/ui/GUI.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
|
|
@ -63,6 +64,9 @@ void AssetsManagement::loadAssets(Content* content) {
|
|||
loader.addDefaults(content);
|
||||
|
||||
try {
|
||||
material_shaders::build_headers(
|
||||
*Shader::preprocessor, paths.resPaths, content
|
||||
);
|
||||
// no need
|
||||
// correct log messages order is more useful
|
||||
// todo: before setting to true, check if GLSLExtension thread safe
|
||||
|
|
|
|||
|
|
@ -20,27 +20,22 @@ PostEffect::PostEffect(
|
|||
: advanced(advanced), shader(std::move(shader)), params(std::move(params)) {
|
||||
}
|
||||
|
||||
static void apply_uniform_value(
|
||||
const PostEffect::Param& param,
|
||||
Shader& shader,
|
||||
const std::string& name
|
||||
) {
|
||||
using Type = PostEffect::Param::Type;
|
||||
switch (param.type) {
|
||||
void PostEffect::Param::apply(Shader& shader, const std::string& name) const {
|
||||
switch (type) {
|
||||
case Type::INT:
|
||||
shader.uniform1i(name, std::get<int>(param.value));
|
||||
shader.uniform1i(name, std::get<int>(value));
|
||||
break;
|
||||
case Type::FLOAT:
|
||||
shader.uniform1f(name, std::get<float>(param.value));
|
||||
shader.uniform1f(name, std::get<float>(value));
|
||||
break;
|
||||
case Type::VEC2:
|
||||
shader.uniform2f(name, std::get<glm::vec2>(param.value));
|
||||
shader.uniform2f(name, std::get<glm::vec2>(value));
|
||||
break;
|
||||
case Type::VEC3:
|
||||
shader.uniform3f(name, std::get<glm::vec3>(param.value));
|
||||
shader.uniform3f(name, std::get<glm::vec3>(value));
|
||||
break;
|
||||
case Type::VEC4:
|
||||
shader.uniform4f(name, std::get<glm::vec4>(param.value));
|
||||
shader.uniform4f(name, std::get<glm::vec4>(value));
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
|
|
@ -93,7 +88,7 @@ Shader& PostEffect::use() {
|
|||
}
|
||||
apply_uniform_array(param, *shader, name, found->second);
|
||||
} else {
|
||||
apply_uniform_value(param, *shader, name);
|
||||
param.apply(*shader, name);
|
||||
}
|
||||
param.dirty = false;
|
||||
}
|
||||
|
|
@ -119,30 +114,49 @@ static void set_value(PostEffect::Param::Value& dst, const dv::value& value) {
|
|||
dst = vec;
|
||||
}
|
||||
|
||||
void PostEffect::Param::set(const dv::value& src) {
|
||||
switch (type) {
|
||||
case Type::INT:
|
||||
value = static_cast<int>(src.asInteger());
|
||||
break;
|
||||
case Type::FLOAT:
|
||||
value = static_cast<float>(src.asNumber());
|
||||
break;
|
||||
case Type::VEC2:
|
||||
set_value<2>(value, src);
|
||||
break;
|
||||
case Type::VEC3:
|
||||
set_value<3>(value, src);
|
||||
break;
|
||||
case Type::VEC4:
|
||||
set_value<4>(value, src);
|
||||
break;
|
||||
}
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
dv::value PostEffect::Param::get() const {
|
||||
switch (type) {
|
||||
case Type::INT:
|
||||
return std::get<int>(value);
|
||||
case Type::FLOAT:
|
||||
return std::get<float>(value);
|
||||
case Type::VEC2:
|
||||
return dv::to_value(std::get<glm::vec2>(value));
|
||||
case Type::VEC3:
|
||||
return dv::to_value(std::get<glm::vec3>(value));
|
||||
case Type::VEC4:
|
||||
return dv::to_value(std::get<glm::vec4>(value));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PostEffect::setParam(const std::string& name, const dv::value& value) {
|
||||
const auto& found = params.find(name);
|
||||
if (found == params.end()) {
|
||||
return;
|
||||
}
|
||||
auto& param = found->second;
|
||||
switch (param.type) {
|
||||
case Param::Type::INT:
|
||||
param.value = static_cast<int>(value.asInteger());
|
||||
break;
|
||||
case Param::Type::FLOAT:
|
||||
param.value = static_cast<float>(value.asNumber());
|
||||
break;
|
||||
case Param::Type::VEC2:
|
||||
set_value<2>(param.value, value);
|
||||
break;
|
||||
case Param::Type::VEC3:
|
||||
set_value<3>(param.value, value);
|
||||
break;
|
||||
case Param::Type::VEC4:
|
||||
set_value<4>(param.value, value);
|
||||
break;
|
||||
}
|
||||
param.dirty = true;
|
||||
found->second.set(value);
|
||||
}
|
||||
|
||||
void PostEffect::setArray(const std::string& name, std::vector<ubyte>&& values) {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,15 @@ public:
|
|||
|
||||
Param();
|
||||
Param(Type type, Value defValue, bool array);
|
||||
|
||||
/// @brief Upload current value to the shader uniform (non-array only)
|
||||
void apply(Shader& shader, const std::string& name) const;
|
||||
|
||||
/// @brief Set value from a number or a vector (list) value
|
||||
void set(const dv::value& value);
|
||||
|
||||
/// @brief Current value as a number or a vector (list) value
|
||||
dv::value get() const;
|
||||
};
|
||||
|
||||
PostEffect(
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ public:
|
|||
Shader(uint id, Source&& vertexSource, Source&& fragmentSource);
|
||||
~Shader();
|
||||
|
||||
uint getId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
void use();
|
||||
void uniformMatrix(const std::string&, const glm::mat4& matrix);
|
||||
void uniformMatrix(const std::string&, const glm::mat3& matrix);
|
||||
|
|
|
|||
|
|
@ -54,8 +54,11 @@ void BlocksRenderer::vertex(
|
|||
static_cast<uint8_t>(normal.x * 127 + 128),
|
||||
static_cast<uint8_t>(normal.y * 127 + 128),
|
||||
static_cast<uint8_t>(normal.z * 127 + 128),
|
||||
static_cast<uint8_t>(emission * 255)
|
||||
}
|
||||
},
|
||||
static_cast<uint8_t>(
|
||||
(emission > 0.5f ? VERTEX_EMISSION_BIT : 0) |
|
||||
(materialShader & VERTEX_MATERIAL_MASK)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -513,6 +516,7 @@ void BlocksRenderer::render(
|
|||
if (def.translucent) {
|
||||
continue;
|
||||
}
|
||||
materialShader = def.rt.materialShader;
|
||||
const UVRegion texfaces[6] {
|
||||
cache.getRegion(id, variantId, 0, densePass),
|
||||
cache.getRegion(id, variantId, 1, densePass),
|
||||
|
|
@ -584,6 +588,7 @@ SortingMeshData BlocksRenderer::renderTranslucent(
|
|||
if (!def.translucent) {
|
||||
continue;
|
||||
}
|
||||
materialShader = def.rt.materialShader;
|
||||
const UVRegion texfaces[6] {
|
||||
cache.getRegion(id, variantId, 0, densePass),
|
||||
cache.getRegion(id, variantId, 1, densePass),
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ private:
|
|||
bool cancelled = false;
|
||||
bool densePass = false;
|
||||
bool denseRender = false;
|
||||
/// @brief material shader index of the block being rendered
|
||||
uint8_t materialShader = 0;
|
||||
AABB meshAABB {};
|
||||
const Chunk* chunk = nullptr;
|
||||
const VoxelsRenderVolume* voxelsBuffer = nullptr;
|
||||
|
|
|
|||
|
|
@ -113,8 +113,8 @@ private:
|
|||
static_cast<uint8_t>(normal.r * 127 + 128),
|
||||
static_cast<uint8_t>(normal.g * 127 + 128),
|
||||
static_cast<uint8_t>(normal.b * 127 + 128),
|
||||
255
|
||||
};
|
||||
vert.flags = VERTEX_EMISSION_BIT;
|
||||
vert.color = {
|
||||
0, 0, 0, static_cast<uint8_t>((coord.y / 8.0f * 0.25f + 0.75f) * 255)
|
||||
};
|
||||
|
|
|
|||
317
src/graphics/render/MaterialShaders.cpp
Normal file
317
src/graphics/render/MaterialShaders.cpp
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
#include "MaterialShaders.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "coders/GLSLExtension.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "data/dv.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "engine/EnginePaths.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "io/io.hpp"
|
||||
#include "util/stringutil.hpp"
|
||||
#include "voxels/Block.hpp"
|
||||
|
||||
static debug::Logger logger("material-shaders");
|
||||
|
||||
namespace {
|
||||
struct MaterialShader {
|
||||
std::string name;
|
||||
uint8_t index;
|
||||
GLSLExtension::ParamsMap params;
|
||||
std::vector<std::string> materials;
|
||||
|
||||
std::string prefix() const {
|
||||
return "material_" + std::to_string(index) + "_";
|
||||
}
|
||||
};
|
||||
|
||||
struct Stage {
|
||||
const char* header;
|
||||
const char* extension;
|
||||
const char* function;
|
||||
/// @brief hook argument type and name
|
||||
const char* type;
|
||||
const char* arg;
|
||||
};
|
||||
|
||||
const Stage STAGES[] {
|
||||
{material_shaders::VERTEX_HEADER,
|
||||
".glslv",
|
||||
"material_vertex",
|
||||
"vec3",
|
||||
"position"},
|
||||
{material_shaders::FRAGMENT_HEADER,
|
||||
".glslf",
|
||||
"material_fragment",
|
||||
"vec4",
|
||||
"color"},
|
||||
};
|
||||
|
||||
/// @brief shader index -> shader
|
||||
std::map<uint8_t, MaterialShader> shaders;
|
||||
/// @brief material name -> shader index
|
||||
std::unordered_map<std::string, uint8_t> materials;
|
||||
/// @brief source index of the generated dispatch code in GLSL errors
|
||||
const int GENERATED_SOURCE = 128;
|
||||
/// @brief incremented on any params change
|
||||
uint64_t paramsVersion = 1;
|
||||
|
||||
struct UploadState {
|
||||
uint program;
|
||||
uint64_t version;
|
||||
};
|
||||
/// @brief shader -> uploaded params state
|
||||
std::unordered_map<const Shader*, UploadState> uploaded;
|
||||
}
|
||||
|
||||
static bool is_identifier_char(char c) {
|
||||
return std::isalnum(static_cast<unsigned char>(c)) || c == '_';
|
||||
}
|
||||
|
||||
/// @brief Remove comments from GLSL code
|
||||
static std::string strip_comments(const std::string& code) {
|
||||
std::string out;
|
||||
out.reserve(code.size());
|
||||
for (size_t i = 0; i < code.size(); i++) {
|
||||
if (code.compare(i, 2, "//") == 0) {
|
||||
i = code.find('\n', i);
|
||||
if (i == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
} else if (code.compare(i, 2, "/*") == 0) {
|
||||
i = code.find("*/", i + 2);
|
||||
if (i == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
out += code[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/// @brief Check if the function is defined in the code
|
||||
static bool has_function(const std::string& source, const std::string& name) {
|
||||
std::string code = strip_comments(source);
|
||||
size_t pos = 0;
|
||||
while ((pos = code.find(name, pos)) != std::string::npos) {
|
||||
size_t end = pos + name.size();
|
||||
bool word = (pos == 0 || !is_identifier_char(code[pos - 1])) &&
|
||||
(end == code.size() || !is_identifier_char(code[end]));
|
||||
pos = end;
|
||||
if (!word) {
|
||||
continue;
|
||||
}
|
||||
while (end < code.size() &&
|
||||
std::isspace(static_cast<unsigned char>(code[end]))) {
|
||||
end++;
|
||||
}
|
||||
if (end < code.size() && code[end] == '(') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @brief Include the material shader stage renaming the hook function and
|
||||
/// the params to unique names (GLSL has no namespaces)
|
||||
/// @return true if the stage file exists
|
||||
static bool include_shader(
|
||||
GLSLExtension& preprocessor,
|
||||
const ResPaths& paths,
|
||||
MaterialShader& shader,
|
||||
const Stage& stage,
|
||||
std::stringstream& ss
|
||||
) {
|
||||
io::path file =
|
||||
paths.find("shaders/materials/" + shader.name + stage.extension);
|
||||
if (!io::exists(file)) {
|
||||
return false;
|
||||
}
|
||||
auto result = preprocessor.process(file, io::read_string(file), true, {});
|
||||
std::string prefix = shader.prefix();
|
||||
|
||||
ss << "// " << shader.name << " (" << file.string() << ")\n";
|
||||
// compilation errors refer to the shader by its index: <index>:<line>
|
||||
ss << "#line 1 " << static_cast<int>(shader.index) << "\n";
|
||||
ss << "#define " << stage.function << " " << prefix << stage.function
|
||||
<< "\n";
|
||||
for (const auto& [name, param] : result.params) {
|
||||
if (param.array) {
|
||||
throw std::runtime_error(
|
||||
file.string() + ": array params are not supported"
|
||||
);
|
||||
}
|
||||
if (name[0] == '_') {
|
||||
throw std::runtime_error(
|
||||
file.string() +
|
||||
": param name must not start with '_': " + util::quote(name)
|
||||
);
|
||||
}
|
||||
const auto& found = shader.params.find(name);
|
||||
if (found != shader.params.end() && found->second.type != param.type) {
|
||||
throw std::runtime_error(
|
||||
file.string() + ": param " + util::quote(name) +
|
||||
" type differs from the other stage"
|
||||
);
|
||||
}
|
||||
ss << "#define " << name << " " << prefix << name << "\n";
|
||||
shader.params[name] = param;
|
||||
}
|
||||
ss << result.code << "\n";
|
||||
ss << "#undef " << stage.function << "\n";
|
||||
for (const auto& entry : result.params) {
|
||||
ss << "#undef " << entry.first << "\n";
|
||||
}
|
||||
if (!has_function(result.code, stage.function)) {
|
||||
throw std::runtime_error(
|
||||
file.string() + ": function " + stage.function + " is not defined"
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void build_stage_header(
|
||||
GLSLExtension& preprocessor, const ResPaths& paths, const Stage& stage
|
||||
) {
|
||||
std::stringstream ss;
|
||||
std::vector<const MaterialShader*> hooked;
|
||||
|
||||
for (auto& [index, shader] : shaders) {
|
||||
if (include_shader(preprocessor, paths, shader, stage, ss)) {
|
||||
hooked.push_back(&shader);
|
||||
}
|
||||
}
|
||||
// generated code source index, then back to the host shader source
|
||||
ss << "#line 1 " << GENERATED_SOURCE << "\n";
|
||||
ss << stage.type << " apply_" << stage.function << "(int material, "
|
||||
<< stage.type << " " << stage.arg << ") {\n";
|
||||
if (!hooked.empty()) {
|
||||
ss << " switch (material) {\n";
|
||||
for (const auto* shader : hooked) {
|
||||
ss << " case " << static_cast<int>(shader->index)
|
||||
<< ": return " << shader->prefix() << stage.function << "("
|
||||
<< stage.arg << ");\n";
|
||||
}
|
||||
ss << " }\n";
|
||||
}
|
||||
ss << " return " << stage.arg << ";\n";
|
||||
ss << "}\n";
|
||||
ss << "#line 1 0\n";
|
||||
|
||||
preprocessor.addHeader(stage.header, {ss.str(), {}});
|
||||
}
|
||||
|
||||
void material_shaders::build_headers(
|
||||
GLSLExtension& preprocessor, const ResPaths& paths, const Content* content
|
||||
) {
|
||||
shaders.clear();
|
||||
materials.clear();
|
||||
uploaded.clear();
|
||||
paramsVersion++;
|
||||
|
||||
if (content) {
|
||||
for (const auto& [name, material] : content->getBlockMaterials()) {
|
||||
uint8_t index = material->rt.shaderId;
|
||||
if (index == 0) {
|
||||
continue;
|
||||
}
|
||||
auto& shader = shaders[index];
|
||||
shader.name = material->shader;
|
||||
shader.index = index;
|
||||
shader.materials.push_back(name);
|
||||
materials[name] = index;
|
||||
}
|
||||
}
|
||||
for (const auto& stage : STAGES) {
|
||||
build_stage_header(preprocessor, paths, stage);
|
||||
}
|
||||
for (const auto& [index, shader] : shaders) {
|
||||
bool found = false;
|
||||
for (const auto& stage : STAGES) {
|
||||
found |= io::exists(
|
||||
paths.find("shaders/materials/" + shader.name + stage.extension)
|
||||
);
|
||||
}
|
||||
if (!found) {
|
||||
logger.warning() << "material shader " << util::quote(shader.name)
|
||||
<< " files not found";
|
||||
}
|
||||
logger.info() << "material shader " << static_cast<int>(index) << ": "
|
||||
<< shader.name << " ("
|
||||
<< util::join(shader.materials, ',') << ")";
|
||||
}
|
||||
}
|
||||
|
||||
void material_shaders::setup_shader(Shader& shader) {
|
||||
auto& state = uploaded[&shader];
|
||||
if (state.program == shader.getId() && state.version == paramsVersion) {
|
||||
return;
|
||||
}
|
||||
for (const auto& [index, materialShader] : shaders) {
|
||||
for (const auto& [name, param] : materialShader.params) {
|
||||
param.apply(shader, materialShader.prefix() + name);
|
||||
}
|
||||
}
|
||||
state = {shader.getId(), paramsVersion};
|
||||
}
|
||||
|
||||
static MaterialShader& require_shader(const std::string& material) {
|
||||
const auto& found = materials.find(material);
|
||||
if (found == materials.end()) {
|
||||
throw std::runtime_error(
|
||||
"material " + util::quote(material) + " has no shader"
|
||||
);
|
||||
}
|
||||
return shaders.at(found->second);
|
||||
}
|
||||
|
||||
void material_shaders::set_params(
|
||||
const std::string& material, const dv::value& params
|
||||
) {
|
||||
auto& shader = require_shader(material);
|
||||
// convert all values before updating
|
||||
GLSLExtension::ParamsMap updated;
|
||||
for (const auto& [name, value] : params.asObject()) {
|
||||
const auto& found = shader.params.find(name);
|
||||
if (found == shader.params.end()) {
|
||||
throw std::runtime_error(
|
||||
"material " + util::quote(material) + " has no param " +
|
||||
util::quote(name)
|
||||
);
|
||||
}
|
||||
auto param = found->second;
|
||||
try {
|
||||
param.set(value);
|
||||
} catch (const std::exception& err) {
|
||||
throw std::runtime_error(
|
||||
"material " + util::quote(material) + " param " +
|
||||
util::quote(name) + ": " + err.what()
|
||||
);
|
||||
}
|
||||
updated[name] = std::move(param);
|
||||
}
|
||||
for (auto& [name, param] : updated) {
|
||||
shader.params[name] = param;
|
||||
}
|
||||
paramsVersion++;
|
||||
}
|
||||
|
||||
dv::value material_shaders::get_params(const std::string& material) {
|
||||
const auto& found = materials.find(material);
|
||||
if (found == materials.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto table = dv::object();
|
||||
for (const auto& [name, param] : shaders.at(found->second).params) {
|
||||
table[name] = param.get();
|
||||
}
|
||||
return table;
|
||||
}
|
||||
39
src/graphics/render/MaterialShaders.hpp
Normal file
39
src/graphics/render/MaterialShaders.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "data/dv_fwd.hpp"
|
||||
|
||||
class Content;
|
||||
class Shader;
|
||||
class ResPaths;
|
||||
class GLSLExtension;
|
||||
|
||||
/// @brief Material shaders (shaders/materials/<name>.glslv, .glslf) are
|
||||
/// included into the world shaders (main, translucent, shadows, entity)
|
||||
namespace material_shaders {
|
||||
inline constexpr const char* VERTEX_HEADER = "__materials_vertex__";
|
||||
inline constexpr const char* FRAGMENT_HEADER = "__materials_fragment__";
|
||||
|
||||
/// @brief Generate the world shaders headers from the material shaders.
|
||||
/// Must be called before the world shaders (re)compilation.
|
||||
/// @param content loaded content (nullable)
|
||||
void build_headers(
|
||||
GLSLExtension& preprocessor,
|
||||
const ResPaths& paths,
|
||||
const Content* content
|
||||
);
|
||||
|
||||
/// @brief Upload the material shaders params to the shader
|
||||
/// if changed since the previous call for this shader program
|
||||
void setup_shader(Shader& shader);
|
||||
|
||||
/// @brief Set the material shader params values ('param' directives)
|
||||
/// @param material material name (pack:name)
|
||||
/// @throws std::runtime_error if material or param does not exist
|
||||
void set_params(const std::string& material, const dv::value& params);
|
||||
|
||||
/// @brief Get the material shader params current values
|
||||
/// @return table of param name -> value or null if material has no shader
|
||||
dv::value get_params(const std::string& material);
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@
|
|||
#include "graphics/core/Shadows.hpp"
|
||||
#include "graphics/core/Cubemap.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "graphics/render/MaterialShaders.hpp"
|
||||
#include "items/Inventory.hpp"
|
||||
#include "items/ItemDef.hpp"
|
||||
#include "items/ItemStack.hpp"
|
||||
|
|
@ -200,6 +201,7 @@ void WorldRenderer::setupWorldShader(
|
|||
|
||||
shader.uniform1f("u_timer", timer);
|
||||
shader.uniform1f("u_gamma", settings.graphics.gamma.get());
|
||||
material_shaders::setup_shader(shader);
|
||||
shader.uniform1f("u_fogFactor", fogFactor);
|
||||
shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
|
||||
shader.uniform1i("u_debugLights", lightsDebug);
|
||||
|
|
|
|||
|
|
@ -5,18 +5,28 @@
|
|||
#include "maths/aabb.hpp"
|
||||
#include "util/Buffer.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
/// @brief ChunkVertex::flags bit: vertex is not affected by lighting
|
||||
inline constexpr uint8_t VERTEX_EMISSION_BIT = 0x80;
|
||||
/// @brief ChunkVertex::flags mask: material shader index
|
||||
inline constexpr uint8_t VERTEX_MATERIAL_MASK = 0x7F;
|
||||
static_assert(VERTEX_MATERIAL_MASK == MAX_BLOCK_MATERIAL_SHADERS);
|
||||
|
||||
/// @brief Chunk mesh vertex format
|
||||
struct ChunkVertex {
|
||||
glm::vec3 position;
|
||||
glm::vec2 uv;
|
||||
std::array<uint8_t, 4> color;
|
||||
std::array<uint8_t, 4> normal;
|
||||
std::array<uint8_t, 3> normal;
|
||||
/// @brief emission flag and material shader index, fourth component
|
||||
/// of the normal attribute (see VERTEX_EMISSION_BIT, VERTEX_MATERIAL_MASK)
|
||||
uint8_t flags;
|
||||
|
||||
static constexpr VertexAttribute ATTRIBUTES[] = {
|
||||
{VertexAttribute::Type::FLOAT, false, 3},
|
||||
|
|
@ -25,6 +35,11 @@ struct ChunkVertex {
|
|||
{VertexAttribute::Type::UNSIGNED_BYTE, true, 4},
|
||||
{{}, 0}};
|
||||
};
|
||||
// normal and flags are passed as one 4-components vertex attribute
|
||||
static_assert(sizeof(ChunkVertex) == 28);
|
||||
static_assert(
|
||||
offsetof(ChunkVertex, flags) == offsetof(ChunkVertex, normal) + 3
|
||||
);
|
||||
|
||||
template<typename VertexStructure>
|
||||
class Mesh;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ dv::value BlockMaterial::serialize() const {
|
|||
{"place-sound", placeSound},
|
||||
{"break-sound", breakSound},
|
||||
{"hit-sound", hitSound},
|
||||
{"sound-absorption", soundAbsorption}
|
||||
{"sound-absorption", soundAbsorption},
|
||||
{"shader", shader}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -36,6 +37,7 @@ void BlockMaterial::deserialize(const dv::value& src) {
|
|||
src.at("break-sound").get(breakSound);
|
||||
src.at("hit-sound").get(hitSound);
|
||||
src.at("sound-absorption").get(soundAbsorption);
|
||||
src.at("shader").get(shader);
|
||||
}
|
||||
|
||||
CoordSystem::CoordSystem(glm::ivec3 axisX, glm::ivec3 axisY, glm::ivec3 axisZ)
|
||||
|
|
|
|||
|
|
@ -166,6 +166,13 @@ struct BlockMaterial : Serializable {
|
|||
std::string breakSound;
|
||||
std::string hitSound;
|
||||
float soundAbsorption = 0.5f;
|
||||
/// @brief material shader name (shaders/materials/<name>), may be empty
|
||||
std::string shader;
|
||||
|
||||
struct {
|
||||
/// @brief material shader index (0 - no shader)
|
||||
uint8_t shaderId = 0;
|
||||
} rt {};
|
||||
|
||||
dv::value toTable() const; // for compatibility
|
||||
dv::value serialize() const override;
|
||||
|
|
@ -322,6 +329,9 @@ public:
|
|||
|
||||
blockid_t surfaceReplacement = 0;
|
||||
|
||||
/// @brief block material shader index (see BlockMaterial::rt.shaderId)
|
||||
uint8_t materialShader = 0;
|
||||
|
||||
std::set<int> tags;
|
||||
|
||||
BlockFuncNamesCache eventNames;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue