Mesh tessellation and texture height vertex displacement
This commit is contained in:
parent
6665781676
commit
c227db03fa
|
|
@ -221,6 +221,8 @@
|
|||
<None Include="README.md" />
|
||||
<None Include="SConstruct" />
|
||||
<None Include="src\shaders\debug_views.glsl" />
|
||||
<None Include="src\shaders\displacement.glsl" />
|
||||
<None Include="src\shaders\displacement_buffer.glsl" />
|
||||
<None Include="src\shaders\main.glsl" />
|
||||
<None Include="src\shaders\overlays.glsl" />
|
||||
<None Include="src\shaders\pbr_views.glsl">
|
||||
|
|
|
|||
|
|
@ -270,6 +270,12 @@
|
|||
</None>
|
||||
<None Include="doc\docs\programming_languages.rst">
|
||||
<Filter>2. Docs</Filter>
|
||||
</None>
|
||||
<None Include="src\shaders\displacement.glsl">
|
||||
<Filter>4. Shaders</Filter>
|
||||
</None>
|
||||
<None Include="src\shaders\displacement_buffer.glsl">
|
||||
<Filter>4. Shaders</Filter>
|
||||
</None>
|
||||
<None Include="src\shaders\pbr_views.glsl">
|
||||
<Filter>4. Shaders</Filter>
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ mat3 align_to_vector(vec3 normal) {
|
|||
|
||||
void start() {
|
||||
// Create centered a grid
|
||||
vec3 pos = vec3(float(INDEX % instance_rows), 0.0, float(INDEX / instance_rows)) - float(instance_rows >> 1u);
|
||||
vec3 pos = vec3(float(INDEX % instance_rows), 0.0, float(INDEX / instance_rows)) - float(instance_rows >>1u);
|
||||
|
||||
// Apply spcaing
|
||||
pos *= instance_spacing;
|
||||
|
|
@ -239,7 +239,7 @@ void start() {
|
|||
floatBitsToUint(texelFetch(_control_maps, index[3], 0).r));
|
||||
bool hole = any(bvec4(control >> uvec4(2u) & uvec4(0x1u)));
|
||||
bool auto = any(bvec4(control & uvec4(0x1u)));
|
||||
int base = int(control[3] >>27u & 0x1Fu);
|
||||
int base = int(control[3] >> 27u & 0x1Fu);
|
||||
int over = int(control[3] >> 22u & 0x1Fu);
|
||||
float blend = float(control[3] >> 14u & 0xFFu) * 0.003921568627450; // 1. / 255.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlic
|
|||
|
||||
/* This is an example stripped down shader with maximum performance in mind.
|
||||
* Only Autoshader/Base/Over/Blend/Holes/Colormap are supported.
|
||||
* Displacement is not enabled. Mesh Tesselation level must be set to 0.
|
||||
* All terrain normal calculations take place in vertex().
|
||||
*
|
||||
* Control map indices are processed such that each ID only requires reading ONCE.
|
||||
|
|
@ -19,10 +20,10 @@ render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlic
|
|||
#define DIV_255 0.003921568627450 // 1. / 255.
|
||||
|
||||
// Inline Functions
|
||||
#define DECODE_BLEND(control) float(control >> 14u & 0xFFu) * DIV_255
|
||||
#define DECODE_BLEND(control) float(control >>14u & 0xFFu) * DIV_255
|
||||
#define DECODE_AUTO(control) bool(control & 0x1u)
|
||||
#define DECODE_BASE(control) int(control >> 27u & 0x1Fu)
|
||||
#define DECODE_OVER(control) int(control >> 22u & 0x1Fu)
|
||||
#define DECODE_BASE(control) int(control >>27u & 0x1Fu)
|
||||
#define DECODE_OVER(control) int(control >>22u & 0x1Fu)
|
||||
#define DECODE_HOLE(control) bool(control >>2u & 0x1u)
|
||||
|
||||
#if CURRENT_RENDERER == RENDERER_COMPATIBILITY
|
||||
|
|
@ -32,12 +33,13 @@ render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlic
|
|||
#endif
|
||||
|
||||
// Private uniforms
|
||||
uniform vec3 _camera_pos = vec3(0.f);
|
||||
uniform vec3 _target_pos = vec3(0.f);
|
||||
uniform float _mesh_size = 48.f;
|
||||
uniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2
|
||||
uniform uint _mouse_layer = 0x80000000u; // Layer 32
|
||||
uniform float _vertex_spacing = 1.0;
|
||||
uniform float _vertex_density = 1.0; // = 1./_vertex_spacing
|
||||
uniform float _subdiv = 1.0;
|
||||
uniform float _region_size = 1024.0;
|
||||
uniform float _region_texel_size = 0.0009765625; // = 1./region_size
|
||||
uniform int _region_map_size = 32;
|
||||
|
|
@ -72,7 +74,7 @@ uniform int auto_overlay_texture : hint_range(0, 31) = 1;
|
|||
group_uniforms;
|
||||
|
||||
group_uniforms macro_variation;
|
||||
uniform bool enable_macro_variation = true;
|
||||
uniform bool macro_variation_enabled = true;
|
||||
uniform vec3 macro_variation1 : source_color = vec3(1.);
|
||||
uniform vec3 macro_variation2 : source_color = vec3(1.);
|
||||
uniform float macro_variation_slope : hint_range(0., 1.) = 0.333;
|
||||
|
|
@ -86,8 +88,8 @@ group_uniforms;
|
|||
// Varyings & Types
|
||||
varying vec3 v_normal;
|
||||
varying vec3 v_vertex;
|
||||
varying vec3 v_camera_pos;
|
||||
varying mat3 TBN;
|
||||
varying vec2 bg_ddxy;
|
||||
|
||||
////////////////////////
|
||||
// Vertex
|
||||
|
|
@ -135,7 +137,7 @@ float check_region(const vec2 uv2) {
|
|||
|
||||
// Takes in UV2 region space coordinates, returns a blend value (0 - 1 range) between empty, and valid regions
|
||||
float get_region_blend(vec2 uv2) {
|
||||
uv2 -= 0.5;
|
||||
uv2 -= 0.4989; // Offset from - 0.5 to account for FP errors highlighted by Tessellation.
|
||||
const vec2 offset = vec2(0.0, 1.0);
|
||||
float a = check_region(uv2 + offset.xy);
|
||||
float b = check_region(uv2 + offset.yy);
|
||||
|
|
@ -146,26 +148,48 @@ float get_region_blend(vec2 uv2) {
|
|||
return 1.0 - blend;
|
||||
}
|
||||
|
||||
float interpolated_height(vec2 pos) {
|
||||
const vec2 offsets = vec2(0, 1);
|
||||
vec2 index_id = floor(pos);
|
||||
ivec3 index[4];
|
||||
index[0] = get_index_coord(index_id + offsets.xy, VERTEX_PASS);
|
||||
index[1] = get_index_coord(index_id + offsets.yy, VERTEX_PASS);
|
||||
index[2] = get_index_coord(index_id + offsets.yx, VERTEX_PASS);
|
||||
index[3] = get_index_coord(index_id + offsets.xx, VERTEX_PASS);
|
||||
float h0 = texelFetch(_height_maps, index[0], 0).r;
|
||||
float h1 = texelFetch(_height_maps, index[1], 0).r;
|
||||
float h2 = texelFetch(_height_maps, index[2], 0).r;
|
||||
float h3 = texelFetch(_height_maps, index[3], 0).r;
|
||||
vec2 f = fract(pos);
|
||||
vec2 i = 1.0 - f;
|
||||
vec4 w = vec4(i.x * f.y, f.x * f.y, f.x * i.y, i.x * i.y);
|
||||
float h = h0 * w[0] + h1 * w[1] + h2 * w[2] + h3 * w[3];
|
||||
return h;
|
||||
}
|
||||
|
||||
void vertex() {
|
||||
// Save Camera Position to varying for access in later functions
|
||||
v_camera_pos = MAIN_CAM_INV_VIEW_MATRIX[3].xyz;
|
||||
|
||||
// Get vertex of flat plane in world coordinates and set world UV
|
||||
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
|
||||
// Camera distance to vertex on flat plane
|
||||
float v_vertex_xz_dist = length(v_vertex.xz - _camera_pos.xz);
|
||||
float v_vertex_xz_dist = length(v_vertex.xz - _target_pos.xz);
|
||||
|
||||
// Geomorph vertex, set end and start for linear height interpolate
|
||||
float scale = MODEL_MATRIX[0][0];
|
||||
float vertex_lerp = smoothstep(0.55, 0.95, (v_vertex_xz_dist / scale - _mesh_size - 4.0) / (_mesh_size - 2.0));
|
||||
float inv_scale = 1.0 / scale;
|
||||
float vertex_lerp = smoothstep(0.55, 0.95, (v_vertex_xz_dist * inv_scale - _mesh_size - 4.0) / (_mesh_size - 2.0));
|
||||
vec2 v_fract = fract(VERTEX.xz * 0.5) * 2.0;
|
||||
// For LOD0 morph from a regular grid to an alternating grid to align with LOD1+
|
||||
vec2 shift = (scale < _vertex_spacing + 1e-6) ? // LOD0 or not
|
||||
// Shift from regular to symetric
|
||||
vec2 shift = (scale < _vertex_spacing / _subdiv + 1e-6) ? // LOD0 or not
|
||||
// Shift from regular to symmetric
|
||||
mix(v_fract, vec2(v_fract.x, -v_fract.y),
|
||||
round(fract(round(mod(v_vertex.z * _vertex_density, 4.0)) *
|
||||
round(mod(v_vertex.x * _vertex_density, 4.0)) * 0.25))
|
||||
) :
|
||||
// Symetric shift
|
||||
v_fract * round((fract(v_vertex.xz * 0.25 / scale) - 0.5) * 4.0);
|
||||
round(fract(round(mod(v_vertex.z * inv_scale, 4.0)) *
|
||||
round(mod(v_vertex.x * inv_scale, 4.0)) * 0.25))) :
|
||||
// symmetric shift
|
||||
v_fract * round((fract(v_vertex.xz * 0.25 * inv_scale) - 0.5) * 4.0);
|
||||
vec2 start_pos = v_vertex.xz * _vertex_density;
|
||||
vec2 end_pos = (v_vertex.xz - shift * scale) * _vertex_density;
|
||||
v_vertex.xz -= shift * scale * vertex_lerp;
|
||||
|
|
@ -186,24 +210,32 @@ void vertex() {
|
|||
(hole || (_background_mode == 0u && v_region.z == -1))) {
|
||||
v_vertex.x = 0. / 0.;
|
||||
} else {
|
||||
// Set final vertex height & calculate vertex normals. 3 lookups
|
||||
ivec3 uv_a = get_index_coord(start_pos, VERTEX_PASS);
|
||||
ivec3 uv_b = get_index_coord(end_pos, VERTEX_PASS);
|
||||
float h = mix(texelFetch(_height_maps, uv_a, 0).r, texelFetch(_height_maps, uv_b, 0).r, vertex_lerp);
|
||||
|
||||
// Set final vertex height, and vertex normal.
|
||||
float h, u, v;
|
||||
// This branch is static for each of the clipmap segments
|
||||
// Interpolated reads only occur where sub-texel values are required.
|
||||
if (scale < _vertex_spacing) {
|
||||
h = mix(interpolated_height(start_pos), interpolated_height(end_pos), vertex_lerp);
|
||||
u = mix(interpolated_height(start_pos + vec2(1, 0)), interpolated_height(end_pos + vec2(1, 0)), vertex_lerp);
|
||||
v = mix(interpolated_height(start_pos + vec2(0, 1)), interpolated_height(end_pos + vec2(0, 1)), vertex_lerp);
|
||||
} else {
|
||||
ivec3 coord_a = get_index_coord(start_pos, VERTEX_PASS);
|
||||
ivec3 coord_b = get_index_coord(end_pos, VERTEX_PASS);
|
||||
ivec3 coord_ua = get_index_coord(start_pos + vec2(1, 0), VERTEX_PASS);
|
||||
ivec3 coord_ub = get_index_coord(end_pos + vec2(1, 0), VERTEX_PASS);
|
||||
ivec3 coord_va = get_index_coord(start_pos + vec2(0, 1), VERTEX_PASS);
|
||||
ivec3 coord_vb = get_index_coord(end_pos + vec2(0, 1), VERTEX_PASS);
|
||||
h = mix(texelFetch(_height_maps, coord_a, 0).r, texelFetch(_height_maps, coord_b, 0).r, vertex_lerp);
|
||||
u = mix(texelFetch(_height_maps, coord_ua, 0).r, texelFetch(_height_maps, coord_ub, 0).r, vertex_lerp);
|
||||
v = mix(texelFetch(_height_maps, coord_va, 0).r, texelFetch(_height_maps, coord_vb, 0).r, vertex_lerp);
|
||||
}
|
||||
|
||||
// Apply background ground level and region blend
|
||||
float ground_h = ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2));
|
||||
v_vertex.y = h + ground_h;
|
||||
bg_ddxy.x = ground_h - ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2 + vec2(_region_texel_size, 0.)));
|
||||
bg_ddxy.y = ground_h - ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2 + vec2(0., _region_texel_size)));
|
||||
|
||||
// Vertex normals adding background normals
|
||||
float u = mix(texelFetch(_height_maps, get_index_coord(start_pos + vec2(1,0), VERTEX_PASS), 0).r,
|
||||
texelFetch(_height_maps, get_index_coord(end_pos + vec2(1,0), VERTEX_PASS), 0).r, vertex_lerp);
|
||||
float v = mix(texelFetch(_height_maps, get_index_coord(start_pos + vec2(0,1), VERTEX_PASS), 0).r,
|
||||
texelFetch(_height_maps, get_index_coord(end_pos + vec2(0,1), VERTEX_PASS), 0).r, vertex_lerp);
|
||||
u += bg_ddxy.x;
|
||||
v += bg_ddxy.y;
|
||||
h += ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2));
|
||||
u += ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2 + vec2(_region_texel_size, 0.)));
|
||||
v += ground_level * smoothstep(1.0 - region_blend, 1.0, get_region_blend(UV2 + vec2(0., _region_texel_size)));
|
||||
|
||||
v_vertex.y = h;
|
||||
v_normal = normalize(vec3(h - u, _vertex_spacing, h - v));
|
||||
}
|
||||
|
||||
|
|
@ -384,7 +416,7 @@ void fragment() {
|
|||
|
||||
// Macro variation. 2 lookups
|
||||
vec3 macrov = vec3(1.);
|
||||
if (enable_macro_variation) {
|
||||
if (macro_variation_enabled) {
|
||||
float noise1 = texture(noise_texture, (uv * noise1_scale * .1 + noise1_offset) * rotate_plane(noise1_angle)).r;
|
||||
float noise2 = texture(noise_texture, uv * noise2_scale * .1).r;
|
||||
macrov = mix(macro_variation1, vec3(1.), noise1);
|
||||
|
|
|
|||
|
|
@ -17,8 +17,9 @@ render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlic
|
|||
|
||||
// Private uniforms
|
||||
// Commented uniforms aren't needed for this shader, but are available for your own needs.
|
||||
uniform vec3 _camera_pos = vec3(0.f);
|
||||
uniform vec3 _target_pos = vec3(0.f);
|
||||
uniform float _mesh_size = 48.f;
|
||||
uniform float _subdiv = 1.f;
|
||||
uniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2
|
||||
uniform uint _mouse_layer = 0x80000000u; // Layer 32
|
||||
uniform float _vertex_spacing = 1.0;
|
||||
|
|
@ -48,6 +49,7 @@ uniform bool flat_terrain_normals = false;
|
|||
// Some are required for editor functions
|
||||
varying float v_vertex_xz_dist;
|
||||
varying vec3 v_vertex;
|
||||
varying vec3 v_camera_pos;
|
||||
|
||||
////////////////////////
|
||||
// Vertex
|
||||
|
|
@ -73,26 +75,48 @@ ivec3 get_index_coord(const vec2 uv, const int search) {
|
|||
return ivec3(ivec2(mod(r_uv,_region_size)), layer_index);
|
||||
}
|
||||
|
||||
float interpolated_height(vec2 pos) {
|
||||
const vec2 offsets = vec2(0, 1);
|
||||
vec2 index_id = floor(pos);
|
||||
ivec3 index[4];
|
||||
index[0] = get_index_coord(index_id + offsets.xy, VERTEX_PASS);
|
||||
index[1] = get_index_coord(index_id + offsets.yy, VERTEX_PASS);
|
||||
index[2] = get_index_coord(index_id + offsets.yx, VERTEX_PASS);
|
||||
index[3] = get_index_coord(index_id + offsets.xx, VERTEX_PASS);
|
||||
float h0 = texelFetch(_height_maps, index[0], 0).r;
|
||||
float h1 = texelFetch(_height_maps, index[1], 0).r;
|
||||
float h2 = texelFetch(_height_maps, index[2], 0).r;
|
||||
float h3 = texelFetch(_height_maps, index[3], 0).r;
|
||||
vec2 f = fract(pos);
|
||||
vec2 i = 1.0 - f;
|
||||
vec4 w = vec4(i.x * f.y, f.x * f.y, f.x * i.y, i.x * i.y);
|
||||
float h = h0 * w[0] + h1 * w[1] + h2 * w[2] + h3 * w[3];
|
||||
return h;
|
||||
}
|
||||
|
||||
void vertex() {
|
||||
// Save Camera Position to varying for access in later functions
|
||||
v_camera_pos = MAIN_CAM_INV_VIEW_MATRIX[3].xyz;
|
||||
|
||||
// Get vertex of flat plane in world coordinates and set world UV
|
||||
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
|
||||
// Camera distance to vertex on flat plane
|
||||
v_vertex_xz_dist = length(v_vertex.xz - _camera_pos.xz);
|
||||
// Target Object distance to vertex on flat plane
|
||||
v_vertex_xz_dist = length(v_vertex.xz - _target_pos.xz);
|
||||
|
||||
// Geomorph vertex, set end and start for linear height interpolate
|
||||
float scale = MODEL_MATRIX[0][0];
|
||||
float vertex_lerp = smoothstep(0.55, 0.95, (v_vertex_xz_dist / scale - _mesh_size - 4.0) / (_mesh_size - 2.0));
|
||||
float inv_scale = 1.0 / scale;
|
||||
float vertex_lerp = smoothstep(0.55, 0.95, (v_vertex_xz_dist * inv_scale - _mesh_size - 4.0) / (_mesh_size - 2.0));
|
||||
vec2 v_fract = fract(VERTEX.xz * 0.5) * 2.0;
|
||||
// For LOD0 morph from a regular grid to an alternating grid to align with LOD1+
|
||||
vec2 shift = (scale < _vertex_spacing + 1e-6) ? // LOD0 or not
|
||||
// Shift from regular to symetric
|
||||
vec2 shift = (scale < _vertex_spacing / _subdiv + 1e-6) ? // LOD0 or not
|
||||
// Shift from regular to symmetric
|
||||
mix(v_fract, vec2(v_fract.x, -v_fract.y),
|
||||
round(fract(round(mod(v_vertex.z * _vertex_density, 4.0)) *
|
||||
round(mod(v_vertex.x * _vertex_density, 4.0)) * 0.25))
|
||||
) :
|
||||
// Symetric shift
|
||||
v_fract * round((fract(v_vertex.xz * 0.25 / scale) - 0.5) * 4.0);
|
||||
round(fract(round(mod(v_vertex.z * inv_scale, 4.0)) *
|
||||
round(mod(v_vertex.x * inv_scale, 4.0)) * 0.25))) :
|
||||
// symmetric shift
|
||||
v_fract * round((fract(v_vertex.xz * 0.25 * inv_scale) - 0.5) * 4.0);
|
||||
vec2 start_pos = v_vertex.xz * _vertex_density;
|
||||
vec2 end_pos = (v_vertex.xz - shift * scale) * _vertex_density;
|
||||
v_vertex.xz -= shift * scale * vertex_lerp;
|
||||
|
|
@ -113,10 +137,17 @@ void vertex() {
|
|||
(hole || (_background_mode == 0u && v_region.z < 0))) {
|
||||
v_vertex.x = 0. / 0.;
|
||||
} else {
|
||||
// Interpolate Geomorph Start & End, set height. 2 Lookups.
|
||||
ivec3 uv_a = get_index_coord(start_pos, VERTEX_PASS);
|
||||
ivec3 uv_b = get_index_coord(end_pos, VERTEX_PASS);
|
||||
float h = mix(texelFetch(_height_maps, uv_a, 0).r, texelFetch(_height_maps, uv_b, 0).r, vertex_lerp);
|
||||
// Set final vertex height.
|
||||
float h;
|
||||
// This branch is static for each of the clipmap segments
|
||||
// Interpolated reads only occur where sub-texel values are required.
|
||||
if (scale < _vertex_spacing) {
|
||||
h = mix(interpolated_height(start_pos), interpolated_height(end_pos), vertex_lerp);
|
||||
} else {
|
||||
ivec3 coord_a = get_index_coord(start_pos, VERTEX_PASS);
|
||||
ivec3 coord_b = get_index_coord(end_pos, VERTEX_PASS);
|
||||
h = mix(texelFetch(_height_maps, coord_a, 0).r,texelFetch(_height_maps, coord_b, 0).r,vertex_lerp);
|
||||
}
|
||||
v_vertex.y = h;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ _shader_parameters = {
|
|||
"bias_distance": 512.0,
|
||||
"blend_sharpness": 0.5,
|
||||
"depth_blur": 0.0,
|
||||
&"displacement": null,
|
||||
"dual_scale_far": 170.0,
|
||||
"dual_scale_near": 100.0,
|
||||
"dual_scale_reduction": 0.3,
|
||||
|
|
@ -51,6 +52,7 @@ _shader_parameters = {
|
|||
&"macro_variation": true,
|
||||
"macro_variation1": Color(0.855, 0.8625, 0.9, 1),
|
||||
"macro_variation2": Color(0.9, 0.885, 0.81, 1),
|
||||
&"macro_variation_enabled": true,
|
||||
"macro_variation_slope": 0.333,
|
||||
"mipmap_bias": 1.0,
|
||||
&"mipmaps": null,
|
||||
|
|
@ -75,6 +77,7 @@ _shader_parameters = {
|
|||
world_background = 2
|
||||
auto_shader_enabled = true
|
||||
dual_scaling_enabled = true
|
||||
displacement_sharpness = 0.25
|
||||
|
||||
[node name="Demo" type="Node"]
|
||||
script = ExtResource("1_k7qca")
|
||||
|
|
@ -96,9 +99,11 @@ transform = Transform3D(0.176947, 0, -0.98422, 0, 1, 0, 0.98422, 0, 0.176947, 22
|
|||
data_directory = "res://demo/data"
|
||||
material = SubResource("Terrain3DMaterial_jrc01")
|
||||
assets = ExtResource("8_g2of2")
|
||||
collision_mask = 3
|
||||
collision_target = NodePath("../Player")
|
||||
collision_mask = 3
|
||||
clipmap_target = NodePath("../Player")
|
||||
mesh_size = 96
|
||||
tessellation_level = 3
|
||||
top_level = true
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[gd_scene load_steps=14 format=3 uid="uid://x34e4v60vdmy"]
|
||||
|
||||
[ext_resource type="Script" path="res://demo/src/DemoScene.gd" id="1_po4vw"]
|
||||
[ext_resource type="Script" uid="uid://chstoagn42gbr" path="res://demo/src/DemoScene.gd" id="1_po4vw"]
|
||||
[ext_resource type="PackedScene" uid="uid://bb2lp50sjndus" path="res://demo/components/Environment.tscn" id="2_i74wg"]
|
||||
[ext_resource type="PackedScene" uid="uid://dwnhqfjq7v1pq" path="res://demo/components/Borders.tscn" id="3_3upu0"]
|
||||
[ext_resource type="PackedScene" uid="uid://domhm87hbhbg1" path="res://demo/components/Player.tscn" id="4_fk3ul"]
|
||||
|
|
@ -37,7 +37,8 @@ _shader_parameters = {
|
|||
"auto_base_texture": 0,
|
||||
"auto_height_reduction": 0.1,
|
||||
"auto_overlay_texture": 1,
|
||||
"auto_slope": 1.0,
|
||||
&"auto_shader": null,
|
||||
"auto_slope": 0.5,
|
||||
"bias_distance": 512.0,
|
||||
"blend_sharpness": 0.87,
|
||||
"depth_blur": 0.0,
|
||||
|
|
@ -45,26 +46,28 @@ _shader_parameters = {
|
|||
"dual_scale_near": 100.0,
|
||||
"dual_scale_reduction": 0.3,
|
||||
"dual_scale_texture": 0,
|
||||
"enable_macro_variation": true,
|
||||
"enable_projection": true,
|
||||
"height_blending": true,
|
||||
&"dual_scaling": null,
|
||||
&"flat_terrain_normals": false,
|
||||
&"general": null,
|
||||
&"macro_variation": null,
|
||||
"macro_variation1": Color(0.878431, 0.862745, 0.901961, 1),
|
||||
"macro_variation2": Color(0.898039, 0.898039, 0.803922, 1),
|
||||
"macro_variation_enabled": true,
|
||||
"macro_variation_slope": 0.333,
|
||||
"mipmap_bias": 1.0,
|
||||
&"mipmaps": null,
|
||||
"noise1_angle": 0.1,
|
||||
"noise1_offset": Vector2(0.5, 0.5),
|
||||
"noise1_scale": 0.04,
|
||||
"noise2_scale": 0.076,
|
||||
"noise3_scale": 0.225,
|
||||
"noise_texture": SubResource("NoiseTexture2D_bov7h"),
|
||||
"projection_angular_division": 1.436,
|
||||
"projection_threshold": 0.8,
|
||||
"tri_scale_reduction": 0.3
|
||||
"tri_scale_reduction": 0.3,
|
||||
&"vertical_projection": true
|
||||
}
|
||||
world_background = 0
|
||||
auto_shader = true
|
||||
dual_scaling = true
|
||||
auto_shader_enabled = true
|
||||
dual_scaling_enabled = true
|
||||
|
||||
[node name="Demo" type="Node"]
|
||||
script = ExtResource("1_po4vw")
|
||||
|
|
|
|||
|
|
@ -30,37 +30,29 @@ noise = SubResource("FastNoiseLite_bfcw0")
|
|||
|
||||
[sub_resource type="Terrain3DMaterial" id="Terrain3DMaterial_klrp5"]
|
||||
_shader_parameters = {
|
||||
"auto_base_texture": 0,
|
||||
"auto_height_reduction": 0.1,
|
||||
"auto_overlay_texture": 1,
|
||||
"auto_slope": 1.0,
|
||||
"bias_distance": 512.0,
|
||||
"blend_sharpness": 0.87,
|
||||
"depth_blur": 0.0,
|
||||
"dual_scale_far": 170.0,
|
||||
"dual_scale_near": 100.0,
|
||||
"dual_scale_reduction": 0.3,
|
||||
"dual_scale_texture": 0,
|
||||
"enable_macro_variation": true,
|
||||
"enable_projection": true,
|
||||
"height_blending": true,
|
||||
&"displacement": null,
|
||||
&"displacement_scale": 1.0,
|
||||
&"displacement_sharpness": 0.5,
|
||||
&"flat_terrain_normals": false,
|
||||
&"general": null,
|
||||
&"macro_variation": null,
|
||||
"macro_variation1": Color(1, 1, 1, 1),
|
||||
"macro_variation2": Color(1, 1, 1, 1),
|
||||
"macro_variation_enabled": true,
|
||||
"macro_variation_slope": 0.333,
|
||||
"mipmap_bias": 1.0,
|
||||
&"mipmaps": null,
|
||||
"noise1_angle": 0.0,
|
||||
"noise1_offset": Vector2(0.5, 0.5),
|
||||
"noise1_scale": 0.04,
|
||||
"noise2_scale": 0.076,
|
||||
"noise3_scale": 0.225,
|
||||
"noise_texture": SubResource("NoiseTexture2D_quxx0"),
|
||||
"projection_angular_division": 1.436,
|
||||
"projection_threshold": 0.8,
|
||||
"tri_scale_reduction": 0.3,
|
||||
&"world_space_normal_blend": true
|
||||
&"vertical_projection": true
|
||||
}
|
||||
auto_shader = true
|
||||
dual_scaling = true
|
||||
|
||||
[node name="Demo" type="Node"]
|
||||
script = ExtResource("1_2gjn4")
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
[ext_resource type="PackedScene" uid="uid://bn5nf4esciwex" path="res://demo/assets/models/LOD5Example.tscn" id="1_4jrdu"]
|
||||
[ext_resource type="Texture2D" uid="uid://c88j3oj0lf6om" path="res://demo/assets/textures/rock023_alb_ht.png" id="2_pog6b"]
|
||||
[ext_resource type="Texture2D" uid="uid://ddprscrpsofah" path="res://demo/assets/textures/ground037_alb_ht.png" id="3_g8f2m"]
|
||||
[ext_resource type="Texture2D" uid="uid://c307hdmos4gtm" path="res://demo/assets/textures/rock023_nrm_rgh.png" id="3_wncaf"]
|
||||
[ext_resource type="Texture2D" uid="uid://c1ots7w6i0i1q" path="res://demo/assets/textures/ground037_nrm_rgh.png" id="4_aw5y1"]
|
||||
[ext_resource type="Texture2D" uid="uid://dabyathlpy04p" path="res://demo/assets/textures/rock023_nrm_rgh.png" id="3_wncaf"]
|
||||
[ext_resource type="Texture2D" uid="uid://g80pbqtklcws" path="res://demo/assets/textures/ground037_nrm_rgh.png" id="4_aw5y1"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_b2vqk"]
|
||||
transparency = 4
|
||||
|
|
@ -18,7 +18,9 @@ distance_fade_max_distance = 96.0
|
|||
|
||||
[sub_resource type="Terrain3DMeshAsset" id="Terrain3DMeshAsset_2qf8x"]
|
||||
name = "TextureCard"
|
||||
generated_type = 1
|
||||
height_offset = 0.5
|
||||
density = 10.0
|
||||
material_override = SubResource("StandardMaterial3D_b2vqk")
|
||||
last_lod = 0
|
||||
last_shadow_lod = 0
|
||||
|
|
@ -29,6 +31,7 @@ name = "LODExample"
|
|||
id = 1
|
||||
scene_file = ExtResource("1_4jrdu")
|
||||
height_offset = 0.5
|
||||
density = 10.0
|
||||
last_lod = 4
|
||||
last_shadow_lod = 4
|
||||
lod4_range = 256.0
|
||||
|
|
@ -37,9 +40,11 @@ lod4_range = 256.0
|
|||
name = "Cliff"
|
||||
albedo_texture = ExtResource("2_pog6b")
|
||||
normal_texture = ExtResource("3_wncaf")
|
||||
normal_depth = 1.0
|
||||
ao_strength = 2.0
|
||||
normal_depth = 1.5
|
||||
ao_strength = 1.0
|
||||
ao_light_affect = 0.8
|
||||
roughness = -0.05
|
||||
displacement_scale = 0.65
|
||||
vertical_projection = true
|
||||
|
||||
[sub_resource type="Terrain3DTextureAsset" id="Terrain3DTextureAsset_od0q7"]
|
||||
|
|
@ -48,8 +53,9 @@ id = 1
|
|||
albedo_color = Color(0.67451, 0.74902, 0.686275, 1)
|
||||
albedo_texture = ExtResource("3_g8f2m")
|
||||
normal_texture = ExtResource("4_aw5y1")
|
||||
normal_depth = 1.0
|
||||
ao_strength = 2.0
|
||||
normal_depth = 1.4
|
||||
ao_strength = 1.0
|
||||
displacement_scale = 0.2
|
||||
detiling_rotation = 0.161
|
||||
|
||||
[resource]
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ float check_region(const vec2 uv2) {
|
|||
|
||||
// Takes in UV2 region space coordinates, returns a blend value (0 - 1 range) between empty, and valid regions
|
||||
float get_region_blend(vec2 uv2) {
|
||||
uv2 -= 0.5;
|
||||
uv2 -= 0.4989; // Offset from - 0.5 to account for FP errors highlighted by Tessellation.
|
||||
const vec2 offset = vec2(0.0, 1.0);
|
||||
float a = check_region(uv2 + offset.xy);
|
||||
float b = check_region(uv2 + offset.yy);
|
||||
|
|
|
|||
|
|
@ -126,9 +126,9 @@ group_uniforms;
|
|||
__t_colors[31] = vec3(0.0125);
|
||||
ivec3 __uv = get_index_coord(floor(uv), SKIP_PASS);
|
||||
uint __control = floatBitsToUint(texelFetch(_control_maps, __uv, 0).r);
|
||||
vec3 __ctrl_base = __t_colors[int(__control >> 27u & 0x1Fu)];
|
||||
vec3 __ctrl_over = __t_colors[int(__control >> 22u & 0x1Fu)];
|
||||
float __blend = float(__control >> 14u & 0xFFu) * 0.003921568627450; // 1.0/255.0
|
||||
vec3 __ctrl_base = __t_colors[int(__control >>27u & 0x1Fu)];
|
||||
vec3 __ctrl_over = __t_colors[int(__control >>22u & 0x1Fu)];
|
||||
float __blend = float(__control >>14u & 0xFFu) * 0.003921568627450; // 1.0/255.0
|
||||
float base_over = (length(fract(uv) - 0.5) < fma(__blend, 0.45, 0.1) ? 1.0 : 0.0);
|
||||
ALBEDO = mix(__ctrl_base, __ctrl_over, base_over);
|
||||
ROUGHNESS = 1.0;
|
||||
|
|
@ -142,10 +142,10 @@ group_uniforms;
|
|||
{
|
||||
ivec3 __uv = get_index_coord(floor(uv), SKIP_PASS);
|
||||
uint __control = floatBitsToUint(texelFetch(_control_maps, __uv, 0).r);
|
||||
float __ctrl_blend = float(__control >> 14u & 0xFFu) * 0.003921568627450; // 1.0/255.0
|
||||
float __ctrl_blend = float(__control >>14u & 0xFFu) * 0.003921568627450; // 1.0/255.0
|
||||
float __is_auto = 0.;
|
||||
#ifdef AUTO_SHADER
|
||||
__is_auto = float( bool(__control & 0x1u) || __uv.z<0 );
|
||||
__is_auto = float( bool(__control & 0x1u) || __uv.z < 0 );
|
||||
#endif
|
||||
ALBEDO = vec3(__ctrl_blend) * (1.0 - __is_auto) + vec3(0.2, 0.0, 0.0) * __is_auto;
|
||||
ROUGHNESS = 1.;
|
||||
|
|
@ -210,4 +210,20 @@ group_uniforms;
|
|||
AO = 1.0;
|
||||
}
|
||||
|
||||
)"
|
||||
//INSERT: DEBUG_DISPLACEMENT_BUFFER
|
||||
// Show displacement buffer
|
||||
#ifdef DISPLACEMENT
|
||||
{
|
||||
float scale = MODEL_MATRIX[0][0];
|
||||
float vertex_lerp = smoothstep(0.55, 0.95, (v_vertex_xz_dist / scale - _mesh_size - 4.0) / (_mesh_size - 2.0));
|
||||
float disp = dot(w_normal, mix(get_displacement(uv, scale), get_displacement(uv, scale * 2.0), vertex_lerp));
|
||||
// Red values lower than collision, Green values above. Black if no deviation.
|
||||
ALBEDO = vec3(-disp, disp, 0.);
|
||||
ROUGHNESS = 0.7;
|
||||
SPECULAR = 0.;
|
||||
NORMAL_MAP = vec3(0.5, 0.5, 1.0);
|
||||
AO = 1.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
)"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||
|
||||
R"(
|
||||
|
||||
//INSERT: DISPLACEMENT_UNIFORMS
|
||||
#define DISPLACEMENT
|
||||
uniform float _displacement_scale : hint_range(0.0, 2.0, 0.01) = 1.0;
|
||||
uniform highp sampler2D _displacement_buffer : repeat_disable, filter_linear;
|
||||
|
||||
//INSERT: DISPLACEMENT_FUNCTIONS
|
||||
vec3 get_displacement(vec2 pos, float scale) {
|
||||
float s = floor(log2(1.0 / (scale * _vertex_density)));
|
||||
scale = pow(2.0, s) * 0.5;
|
||||
vec2 d_uv = (scale * pos - round(_target_pos.xz * _vertex_density * scale)) / (_mesh_size * 2.0) + 0.5;
|
||||
d_uv.x += s - 1.;
|
||||
d_uv.x /= log2(_subdiv);
|
||||
highp vec3 disp = vec3(0.);
|
||||
if (all(greaterThanEqual(d_uv, vec2(0.0))) && all(lessThanEqual(d_uv, vec2(1.0)))) {
|
||||
disp = textureLod(_displacement_buffer, d_uv, 0.).rgb * 2.0 - 1.0;
|
||||
disp *= _displacement_scale;
|
||||
}
|
||||
return disp;
|
||||
}
|
||||
|
||||
//INSERT: DISPLACEMENT_VERTEX
|
||||
if (!(CAMERA_VISIBLE_LAYERS == _mouse_layer)) {
|
||||
displacement = mix(get_displacement(start_pos, scale), get_displacement(end_pos, scale * 2.0), vertex_lerp);
|
||||
}
|
||||
|
||||
)"
|
||||
|
|
@ -0,0 +1,389 @@
|
|||
// Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
|
||||
|
||||
R"(shader_type canvas_item;
|
||||
|
||||
// Displacement buffer shader, mimics the main shader in 2d and outputs
|
||||
// xyz offset stored in RGB channels. A is unusuable when passing
|
||||
// the buffer directly via RID (avoids GPU > CPU > GPU copies) as alpha
|
||||
// is premultiplied by the renderer.
|
||||
|
||||
// Defined Constants
|
||||
#define SKIP_PASS 0
|
||||
#define VERTEX_PASS 1
|
||||
#define FRAGMENT_PASS 2
|
||||
#define COLOR_MAP_DEF vec4(1.0, 1.0, 1.0, 0.5)
|
||||
#define DIV_255 0.003921568627450 // 1. / 255.
|
||||
#define DIV_1024 0.0009765625 // 1. / 1024.
|
||||
#define TAU_16TH -0.392699081698724 // -TAU / 16.
|
||||
|
||||
// Inline Functions
|
||||
#define DECODE_BLEND(control) float(control >>14u & 0xFFu) * DIV_255
|
||||
#define DECODE_AUTO(control) bool(control & 0x1u)
|
||||
#define DECODE_BASE(control) int(control >>27u & 0x1Fu)
|
||||
#define DECODE_OVER(control) int(control >>22u & 0x1Fu)
|
||||
#define DECODE_ANGLE(control) float(control >>10u & 0xFu) * TAU_16TH
|
||||
// This math recreates the scale value directly rather than using an 8 float const array.
|
||||
#define DECODE_SCALE(control) (0.9 - float(((control >>7u & 0x7u) + 3u) % 8u + 1u) * 0.1)
|
||||
#define DECODE_HOLE(control) bool(control >>2u & 0x1u)
|
||||
|
||||
#define TEXTURE_ID_PROJECTED(id) bool((_texture_vertical_projections >> uint(id)) & 0x1u)
|
||||
|
||||
#if CURRENT_RENDERER == RENDERER_COMPATIBILITY
|
||||
#define fma(a, b, c) ((a) * (b) + (c))
|
||||
#define dFdxCoarse(a) dFdx(a)
|
||||
#define dFdyCoarse(a) dFdy(a)
|
||||
#endif
|
||||
|
||||
// Private uniforms
|
||||
uniform float _tessellation_level = 0.;
|
||||
uniform vec3 _target_pos = vec3(0.f);
|
||||
uniform float _mesh_size = 48.f;
|
||||
uniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2
|
||||
uniform float _vertex_spacing = 1.0;
|
||||
uniform float _vertex_density = 1.0; // = 1./_vertex_spacing
|
||||
uniform float _region_size = 1024.0;
|
||||
uniform float _region_texel_size = 0.0009765625; // = 1./region_size
|
||||
uniform int _region_map_size = 32;
|
||||
uniform int _region_map[1024];
|
||||
uniform vec2 _region_locations[1024];
|
||||
uniform float _texture_uv_scale_array[32];
|
||||
uniform uint _texture_vertical_projections;
|
||||
uniform vec2 _texture_detile_array[32];
|
||||
uniform vec2 _texture_displacement_array[32];
|
||||
uniform highp sampler2DArray _height_maps : repeat_disable;
|
||||
uniform highp sampler2DArray _control_maps : repeat_disable;
|
||||
//INSERT: TEXTURE_SAMPLERS_NEAREST
|
||||
//INSERT: TEXTURE_SAMPLERS_LINEAR
|
||||
|
||||
// Public uniforms
|
||||
group_uniforms general;
|
||||
uniform float blend_sharpness : hint_range(0, 1) = 0.5;
|
||||
uniform bool vertical_projection = true;
|
||||
uniform float projection_threshold : hint_range(0.0, 0.99, 0.01) = 0.8;
|
||||
group_uniforms;
|
||||
//INSERT: AUTO_SHADER_UNIFORMS
|
||||
|
||||
// Uniquely named displacement uniforms should be in this group.
|
||||
// Uniforms that are shared with the main shader are automatically synchronised.
|
||||
// Subgroups should work as expected.
|
||||
group_uniforms displacement;
|
||||
uniform float _displacement_sharpness : hint_range(0.0, 1.0, 0.01) = 0.25;
|
||||
group_uniforms;
|
||||
|
||||
// Varyings & Types
|
||||
|
||||
// We only care about texture height value.
|
||||
struct material {
|
||||
float height;
|
||||
float total_weight;
|
||||
};
|
||||
|
||||
////////////////////////
|
||||
// Vertex
|
||||
////////////////////////
|
||||
|
||||
// Takes in world space XZ (UV) coordinates & search depth (only applicable for background mode none)
|
||||
// Returns ivec3 with:
|
||||
// XY: (0 to _region_size - 1) coordinates within a region
|
||||
// Z: layer index used for texturearrays, -1 if not in a region
|
||||
ivec3 get_index_coord(const vec2 uv, const int search) {
|
||||
vec2 r_uv = round(uv);
|
||||
vec2 o_uv = mod(r_uv,_region_size);
|
||||
ivec2 pos;
|
||||
int bounds, layer_index = -1;
|
||||
for (int i = -1; i < clamp(search, SKIP_PASS, FRAGMENT_PASS); i++) {
|
||||
if ((layer_index == -1 && _background_mode == 0u ) || i < 0) {
|
||||
r_uv -= i == -1 ? vec2(0.0) : vec2(float(o_uv.x <= o_uv.y), float(o_uv.y <= o_uv.x));
|
||||
pos = ivec2(floor((r_uv) * _region_texel_size)) + (_region_map_size / 2);
|
||||
bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
|
||||
layer_index = (_region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1);
|
||||
}
|
||||
}
|
||||
return ivec3(ivec2(mod(r_uv,_region_size)), layer_index);
|
||||
}
|
||||
|
||||
// Takes in descaled (world_space / region_size) world to region space XZ (UV2) coordinates, returns vec3 with:
|
||||
// XY: (0. to 1.) coordinates within a region
|
||||
// Z: layer index used for texturearrays, -1 if not in a region
|
||||
vec3 get_index_uv(const vec2 uv2) {
|
||||
ivec2 pos = ivec2(floor(uv2)) + (_region_map_size / 2);
|
||||
int bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
|
||||
int layer_index = _region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1;
|
||||
return vec3(uv2 - _region_locations[layer_index], float(layer_index));
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
// Fragment
|
||||
////////////////////////
|
||||
|
||||
float random(in vec2 xy) {
|
||||
return fract(sin(dot(xy, vec2(12.9898, 78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
vec2 rotate_vec2(const vec2 v, const vec2 cs) {
|
||||
return vec2(fma(cs.x, v.x, cs.y * v.y), fma(cs.x, v.y, -cs.y * v.x));
|
||||
}
|
||||
|
||||
// 2-4 lookups ( 2-6 with dual scaling )
|
||||
void accumulate_material(const mat3 TNB, const float weight, const ivec3 index,
|
||||
const uint control, const vec2 texture_weight, const ivec2 texture_id, const vec3 i_normal,
|
||||
float h, inout material mat, const vec3 v_vertex) {
|
||||
|
||||
// Applying scaling before projection reduces the number of multiplys ops required.
|
||||
vec3 i_vertex = v_vertex;
|
||||
|
||||
// Control map scale
|
||||
float control_scale = DECODE_SCALE(control);
|
||||
i_vertex *= control_scale;
|
||||
h *= control_scale;
|
||||
|
||||
// Index position for detiling.
|
||||
vec2 i_pos = fma(_region_locations[index.z], vec2(_region_size), vec2(index.xy));
|
||||
i_pos *= _vertex_spacing * control_scale;
|
||||
|
||||
// Projection
|
||||
vec2 i_uv = i_vertex.xz;
|
||||
mat2 p_align = mat2(1.);
|
||||
vec2 p_uv = i_uv;
|
||||
vec2 p_pos = i_pos;
|
||||
if (i_normal.y <= projection_threshold && vertical_projection) {
|
||||
// Projected normal map alignment matrix
|
||||
p_align = mat2(vec2(i_normal.z, -i_normal.x), vec2(i_normal.x, i_normal.z));
|
||||
// Fast 45 degree snapping https://iquilezles.org/articles/noatan/
|
||||
vec2 xz = round(normalize(-i_normal.xz) * 1.3065629648763765); // sqrt(1.0 + sqrt(0.5))
|
||||
xz *= abs(xz.x) + abs(xz.y) > 1.5 ? 0.7071067811865475 : 1.0; // sqrt(0.5)
|
||||
xz = vec2(-xz.y, xz.x);
|
||||
p_pos = floor(vec2(dot(i_pos, xz), -h));
|
||||
p_uv = vec2(dot(i_vertex.xz, xz), -i_vertex.y);
|
||||
}
|
||||
|
||||
// Control map rotation. Must be applied seperatley from detiling to maintain UV continuity.
|
||||
float c_angle = DECODE_ANGLE(control);
|
||||
vec2 c_cs_angle = vec2(cos(c_angle), sin(c_angle));
|
||||
i_uv = rotate_vec2(i_uv, c_cs_angle);
|
||||
i_pos = rotate_vec2(i_pos, c_cs_angle);
|
||||
p_uv = rotate_vec2(p_uv, c_cs_angle);
|
||||
p_pos = rotate_vec2(p_pos, c_cs_angle);
|
||||
|
||||
// Blend adjustment of Higher ID from Lower ID normal map in world space.
|
||||
float world_normal = 1.;
|
||||
// mat3 multiply, reduced to 2x fma and 1x mult.
|
||||
#define FAST_WORLD_NORMAL(n) fma(TNB[0], vec3(n.x), fma(TNB[2], vec3(n.z), TNB[1] * vec3(n.y)))
|
||||
|
||||
float blend = DECODE_BLEND(control); // only used for branching.
|
||||
float sharpness = fma(60., blend_sharpness * _displacement_sharpness, 4.);
|
||||
|
||||
// 1st Texture Asset ID
|
||||
if (blend < 1.0) {
|
||||
int id = texture_id[0];
|
||||
bool projected = TEXTURE_ID_PROJECTED(id);
|
||||
float id_w = texture_weight[0];
|
||||
float id_scale = _texture_uv_scale_array[id];
|
||||
|
||||
// Detiling and Control map rotation
|
||||
vec2 id_pos = fma(p_pos, vec2(float(projected)), i_pos * vec2(float(!projected)));
|
||||
vec2 uv_center = floor(fma(id_pos, vec2(id_scale), vec2(0.5)));
|
||||
vec2 id_detile = fma(random(uv_center), 2.0, -1.0) * _texture_detile_array[id] * TAU;
|
||||
vec2 id_cs_angle = vec2(cos(id_detile.x), sin(id_detile.x));
|
||||
// Apply UV rotation and shift around pivot.
|
||||
vec2 id_uv = fma(p_uv, vec2(float(projected)), i_uv * vec2(float(!projected)));
|
||||
id_uv = rotate_vec2(fma(id_uv, vec2(id_scale), -uv_center), id_cs_angle) + uv_center + id_detile.y - 0.5;
|
||||
// Manual transpose to rotate derivatives and normals counter to uv rotation whilst also
|
||||
// including control map rotation. avoids extra matrix op, and sin/cos calls.
|
||||
id_cs_angle = vec2(
|
||||
fma(id_cs_angle.x, c_cs_angle.x, -id_cs_angle.y * c_cs_angle.y),
|
||||
fma(id_cs_angle.y, c_cs_angle.x, id_cs_angle.x * c_cs_angle.y));
|
||||
|
||||
float h = textureLod(_texture_array_albedo, vec3(id_uv, float(id)), 0.).a;
|
||||
vec4 nrm = textureLod(_texture_array_normal, vec3(id_uv, float(id)), 0.);
|
||||
// Unpack and rotate normal map.
|
||||
nrm.xyz = fma(nrm.xzy, vec3(2.0), vec3(-1.0));
|
||||
nrm.xz = rotate_vec2(nrm.xz, id_cs_angle);
|
||||
nrm.xz = fma((nrm.xz * p_align), vec2(float(projected)), nrm.xz * vec2(float(!projected)));
|
||||
|
||||
world_normal = FAST_WORLD_NORMAL(nrm).y;
|
||||
|
||||
float id_weight = exp2(sharpness * log2(weight + id_w + h)) * weight;
|
||||
|
||||
// height is modified after weight calculation so that asset offset and scale values do not interfear with material blending.
|
||||
float height_scale = (_texture_displacement_array[id].y * 0.04) / (control_scale * id_scale);
|
||||
float height_offset = 0.5 + _texture_displacement_array[id].x * height_scale - 0.5 * height_scale;
|
||||
h = clamp(fma(h, height_scale, height_offset), 0., 1.);
|
||||
|
||||
mat.height = fma(h, id_weight, mat.height);
|
||||
mat.total_weight += id_weight;
|
||||
}
|
||||
|
||||
// 2nd Texture Asset ID
|
||||
if (blend > 0.0 && texture_id[1] != texture_id[0]) {
|
||||
int id = texture_id[1];
|
||||
bool projected = TEXTURE_ID_PROJECTED(id);
|
||||
float id_w = texture_weight[1];
|
||||
float id_scale = _texture_uv_scale_array[id];
|
||||
|
||||
// Detiling and Control map rotation
|
||||
vec2 id_pos = fma(p_pos, vec2(float(projected)), i_pos * vec2(float(!projected)));
|
||||
vec2 uv_center = floor(fma(id_pos, vec2(id_scale), vec2(0.5)));
|
||||
vec2 id_detile = fma(random(uv_center), 2.0, -1.0) * _texture_detile_array[id] * TAU;
|
||||
vec2 id_cs_angle = vec2(cos(id_detile.x), sin(id_detile.x));
|
||||
// Apply UV rotation and shift around pivot.
|
||||
vec2 id_uv = fma(p_uv, vec2(float(projected)), i_uv * vec2(float(!projected)));
|
||||
id_uv = rotate_vec2(fma(id_uv, vec2(id_scale), -uv_center), id_cs_angle) + uv_center + id_detile.y - 0.5;
|
||||
// Manual transpose to rotate derivatives and normals counter to uv rotation whilst also
|
||||
// including control map rotation. avoids extra matrix op, and sin/cos calls.
|
||||
id_cs_angle = vec2(
|
||||
fma(id_cs_angle.x, c_cs_angle.x, -id_cs_angle.y * c_cs_angle.y),
|
||||
fma(id_cs_angle.y, c_cs_angle.x, id_cs_angle.x * c_cs_angle.y));
|
||||
|
||||
float h = textureLod(_texture_array_albedo, vec3(id_uv, float(id)), 0.).a;
|
||||
// Normals are not required for 2nd ID as they are not used to adjust the weights.
|
||||
|
||||
float id_weight = exp2(sharpness * log2(weight + id_w + h * clamp(world_normal, 0., 1.))) * weight;
|
||||
|
||||
// height is modified after weight calculation so that asset offset and scale values do not interfear with material blending.
|
||||
float height_scale = (_texture_displacement_array[id].y * 0.04) / (control_scale * id_scale);
|
||||
float height_offset = 0.5 + _texture_displacement_array[id].x * height_scale - 0.5 * height_scale;
|
||||
h = clamp(fma(h, height_scale, height_offset), 0., 1.);
|
||||
|
||||
mat.height = fma(h, id_weight, mat.height);
|
||||
mat.total_weight += id_weight;
|
||||
}
|
||||
}
|
||||
)"
|
||||
|
||||
R"(
|
||||
void fragment() {
|
||||
// Calculate Tiled UVs
|
||||
float scale = floor(UV.x * (_tessellation_level));
|
||||
float p_scale = pow(2.0, scale);
|
||||
vec2 uv = (vec2(fract(UV.x * _tessellation_level), UV.y) - 0.5) * (_mesh_size * 2.0) / p_scale;
|
||||
uv += round(_target_pos.xz * _vertex_density * p_scale) / p_scale;
|
||||
vec2 uv2 = uv * _region_texel_size;
|
||||
|
||||
// Lookup offsets, ID and blend weight
|
||||
vec3 region_uv = get_index_uv(uv2);
|
||||
const vec3 offsets = vec3(0, 1, 2);
|
||||
vec2 index_id = floor(uv);
|
||||
vec2 weight = fract(uv);
|
||||
vec2 invert = 1.0 - weight;
|
||||
vec4 weights = vec4(
|
||||
invert.x * weight.y, // 0
|
||||
weight.x * weight.y, // 1
|
||||
weight.x * invert.y, // 2
|
||||
invert.x * invert.y // 3
|
||||
);
|
||||
|
||||
ivec3 index[4];
|
||||
// control map lookups, used for some normal lookups as well
|
||||
index[0] = get_index_coord(index_id + offsets.xy, FRAGMENT_PASS);
|
||||
index[1] = get_index_coord(index_id + offsets.yy, FRAGMENT_PASS);
|
||||
index[2] = get_index_coord(index_id + offsets.yx, FRAGMENT_PASS);
|
||||
index[3] = get_index_coord(index_id + offsets.xx, FRAGMENT_PASS);
|
||||
|
||||
// Terrain normals
|
||||
vec3 index_normal[4];
|
||||
float h[4];
|
||||
// allows additional derivatives, eg world noise, brush previews etc
|
||||
float u = 0.0;
|
||||
float v = 0.0;
|
||||
|
||||
// Re-use index[] for the first lookups, skipping some math. 3 lookups
|
||||
h[3] = texelFetch(_height_maps, index[3], 0).r; // 0 (0,0)
|
||||
h[2] = texelFetch(_height_maps, index[2], 0).r; // 1 (1,0)
|
||||
h[0] = texelFetch(_height_maps, index[0], 0).r; // 2 (0,1)
|
||||
index_normal[3] = normalize(vec3(h[3] - h[2] + u, _vertex_spacing, h[3] - h[0] + v));
|
||||
|
||||
// 5 lookups
|
||||
// Fetch the additional required height values for smooth normals
|
||||
h[1] = texelFetch(_height_maps, index[1], 0).r; // 3 (1,1)
|
||||
float h_4 = texelFetch(_height_maps, get_index_coord(index_id + offsets.yz, FRAGMENT_PASS), 0).r; // 4 (1,2)
|
||||
float h_5 = texelFetch(_height_maps, get_index_coord(index_id + offsets.zy, FRAGMENT_PASS), 0).r; // 5 (2,1)
|
||||
float h_6 = texelFetch(_height_maps, get_index_coord(index_id + offsets.zx, FRAGMENT_PASS), 0).r; // 6 (2,0)
|
||||
float h_7 = texelFetch(_height_maps, get_index_coord(index_id + offsets.xz, FRAGMENT_PASS), 0).r; // 7 (0,2)
|
||||
|
||||
// Calculate the normal for the remaining index ids.
|
||||
index_normal[0] = normalize(vec3(h[0] - h[1] + u, _vertex_spacing, h[0] - h_7 + v));
|
||||
index_normal[1] = normalize(vec3(h[1] - h_5 + u, _vertex_spacing, h[1] - h_4 + v));
|
||||
index_normal[2] = normalize(vec3(h[2] - h_6 + u, _vertex_spacing, h[2] - h[1] + v));
|
||||
|
||||
// Set interpolated world normal
|
||||
vec3 w_normal =
|
||||
index_normal[0] * weights[0] +
|
||||
index_normal[1] * weights[1] +
|
||||
index_normal[2] * weights[2] +
|
||||
index_normal[3] * weights[3] ;
|
||||
vec3 w_tangent = normalize(cross(w_normal, vec3(0.0, 0.0, 1.0)));
|
||||
vec3 w_binormal = normalize(cross(w_normal, w_tangent));
|
||||
mat3 TNB = mat3(w_tangent, w_normal, w_binormal);
|
||||
|
||||
// We have to construct interpolted height value here, as we are operating solely in texture space.
|
||||
float terrain_height = h[0] * weights[0] + h[1] * weights[1] + h[2] * weights[2] + h[3] * weights[3];
|
||||
vec3 v_vertex = vec3(uv.x * _vertex_spacing, terrain_height, uv.y * _vertex_spacing);
|
||||
|
||||
// Get index control data
|
||||
// 1 - 4 lookups
|
||||
uvec4 control = uvec4(
|
||||
floatBitsToUint(texelFetch(_control_maps, index[0], 0).r),
|
||||
floatBitsToUint(texelFetch(_control_maps, index[1], 0).r),
|
||||
floatBitsToUint(texelFetch(_control_maps, index[2], 0).r),
|
||||
floatBitsToUint(texelFetch(_control_maps, index[3], 0).r));
|
||||
|
||||
//INSERT: AUTO_SHADER
|
||||
|
||||
// Vectorised Deocode of all texture IDs, then swizzle to per index mapping.
|
||||
// Passed to accumulate_material to avoid repeated decoding.
|
||||
ivec4 t_id[2] = {ivec4(control >> uvec4(27u) & uvec4(0x1Fu)),
|
||||
ivec4(control >> uvec4(22u) & uvec4(0x1Fu))};
|
||||
ivec2 texture_ids[4] = ivec2[4](
|
||||
ivec2(t_id[0].x, t_id[1].x),
|
||||
ivec2(t_id[0].y, t_id[1].y),
|
||||
ivec2(t_id[0].z, t_id[1].z),
|
||||
ivec2(t_id[0].w, t_id[1].w));
|
||||
|
||||
// uninterpolated weights.
|
||||
vec4 weights_id_1 = vec4(control >> uvec4(14u) & uvec4(0xFFu)) * DIV_255;
|
||||
vec4 weights_id_0 = 1.0 - weights_id_1;
|
||||
vec2 t_weights[4] = vec2[4](
|
||||
vec2(weights_id_0[0], weights_id_1[0]),
|
||||
vec2(weights_id_0[1], weights_id_1[1]),
|
||||
vec2(weights_id_0[2], weights_id_1[2]),
|
||||
vec2(weights_id_0[3], weights_id_1[3]));
|
||||
// interpolated weights
|
||||
|
||||
#if CURRENT_RENDERER == RENDERER_FORWARD_PLUS
|
||||
t_weights = {vec2(0), vec2(0), vec2(0), vec2(0)};
|
||||
weights_id_0 *= weights;
|
||||
weights_id_1 *= weights;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
vec2 w_0 = vec2(weights_id_0[i]);
|
||||
vec2 w_1 = vec2(weights_id_1[i]);
|
||||
ivec2 id_0 = texture_ids[i].xx;
|
||||
ivec2 id_1 = texture_ids[i].yy;
|
||||
t_weights[0] += fma(w_0, vec2(equal(texture_ids[0], id_0)), w_1 * vec2(equal(texture_ids[0], id_1)));
|
||||
t_weights[1] += fma(w_0, vec2(equal(texture_ids[1], id_0)), w_1 * vec2(equal(texture_ids[1], id_1)));
|
||||
t_weights[2] += fma(w_0, vec2(equal(texture_ids[2], id_0)), w_1 * vec2(equal(texture_ids[2], id_1)));
|
||||
t_weights[3] += fma(w_0, vec2(equal(texture_ids[3], id_0)), w_1 * vec2(equal(texture_ids[3], id_1)));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Struct to accumulate all texture data.
|
||||
material mat = material(0., 0.);
|
||||
accumulate_material(TNB, weights[3], index[3], control[3], t_weights[3],
|
||||
texture_ids[3], index_normal[3], h[3], mat, v_vertex);
|
||||
accumulate_material(TNB, weights[2], index[2], control[2], t_weights[2],
|
||||
texture_ids[2], index_normal[2], h[2], mat, v_vertex);
|
||||
accumulate_material(TNB, weights[1], index[1], control[1], t_weights[1],
|
||||
texture_ids[1], index_normal[1], h[1], mat, v_vertex);
|
||||
accumulate_material(TNB, weights[0], index[0], control[0], t_weights[0],
|
||||
texture_ids[0], index_normal[0], h[0], mat, v_vertex);
|
||||
|
||||
// normalize accumulated values back to 0.0 - 1.0 range.
|
||||
float weight_inv = 1.0 / max(mat.total_weight, 1e-8);
|
||||
mat.height *= weight_inv;
|
||||
|
||||
// Output
|
||||
COLOR.rgb = clamp(fma(w_normal * (mat.height - 0.5) * 2.0, vec3(0.5), vec3(0.5)), 0.0, 1.0);
|
||||
|
||||
}
|
||||
)"
|
||||
|
|
@ -13,7 +13,7 @@ group_uniforms;
|
|||
|
||||
//INSERT: DUAL_SCALING
|
||||
// dual scaling
|
||||
float far_factor = clamp(smoothstep(dual_scale_near, dual_scale_far, length(v_vertex - _camera_pos)), 0.0, 1.0);
|
||||
float far_factor = clamp(smoothstep(dual_scale_near, dual_scale_far, length(v_vertex - v_camera_pos)), 0.0, 1.0);
|
||||
vec4 far_alb = vec4(0.);
|
||||
vec4 far_nrm = vec4(0.);
|
||||
float far_ao = 1.0;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ uniform bool _editor_decal_visible[3]; // show decal: brush, slope point1, point
|
|||
uniform bool _editor_decal_part[2]; // show decal[0] component: texture, reticle
|
||||
|
||||
float get_reticle(vec2 uv, int index) {
|
||||
float cam_dist = clamp(length(_camera_pos - v_vertex), 0., 4000.);
|
||||
float cam_dist = clamp(length(v_camera_pos - v_vertex), 0., 4000.);
|
||||
float sq_cam_dist = sqrt(cam_dist);
|
||||
float view_scale = 16.0 / sq_cam_dist;
|
||||
vec2 cross_uv = (uv * _vertex_spacing - _editor_decal_position[index]) * view_scale;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
// multiple raw strings that are concatenated by the compiler.
|
||||
|
||||
R"(shader_type spatial;
|
||||
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx,skip_vertex_transform;
|
||||
render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx, skip_vertex_transform;
|
||||
|
||||
/* The terrain depends on this shader to function. Don't change most things in vertex() or
|
||||
* terrain normal calculations in fragment(). You probably only want to customize the
|
||||
|
|
@ -29,10 +29,10 @@ render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlic
|
|||
#define TAU_16TH -0.392699081698724 // -TAU / 16.
|
||||
|
||||
// Inline Functions
|
||||
#define DECODE_BLEND(control) float(control >> 14u & 0xFFu) * DIV_255
|
||||
#define DECODE_BLEND(control) float(control >>14u & 0xFFu) * DIV_255
|
||||
#define DECODE_AUTO(control) bool(control & 0x1u)
|
||||
#define DECODE_BASE(control) int(control >> 27u & 0x1Fu)
|
||||
#define DECODE_OVER(control) int(control >> 22u & 0x1Fu)
|
||||
#define DECODE_BASE(control) int(control >>27u & 0x1Fu)
|
||||
#define DECODE_OVER(control) int(control >>22u & 0x1Fu)
|
||||
#define DECODE_ANGLE(control) float(control >>10u & 0xFu) * TAU_16TH
|
||||
// This math recreates the scale value directly rather than using an 8 float const array.
|
||||
#define DECODE_SCALE(control) (0.9 - float(((control >>7u & 0x7u) + 3u) % 8u + 1u) * 0.1)
|
||||
|
|
@ -47,8 +47,9 @@ render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlic
|
|||
#endif
|
||||
|
||||
// Private uniforms
|
||||
uniform vec3 _camera_pos = vec3(0.f);
|
||||
uniform vec3 _target_pos = vec3(0.f);
|
||||
uniform float _mesh_size = 48.f;
|
||||
uniform float _subdiv = 1.f;
|
||||
uniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2
|
||||
uniform uint _mouse_layer = 0x80000000u; // Layer 32
|
||||
uniform float _vertex_spacing = 1.0;
|
||||
|
|
@ -81,9 +82,10 @@ uniform float projection_threshold : hint_range(0.0, 0.99, 0.01) = 0.8;
|
|||
group_uniforms;
|
||||
|
||||
//INSERT: AUTO_SHADER_UNIFORMS
|
||||
//INSERT: DISPLACEMENT_UNIFORMS
|
||||
//INSERT: DUAL_SCALING_UNIFORMS
|
||||
group_uniforms macro_variation;
|
||||
uniform bool macro_variation = true;
|
||||
uniform bool macro_variation_enabled = true;
|
||||
uniform vec3 macro_variation1 : source_color = vec3(1.);
|
||||
uniform vec3 macro_variation2 : source_color = vec3(1.);
|
||||
uniform float macro_variation_slope : hint_range(0., 1.) = 0.333;
|
||||
|
|
@ -116,6 +118,7 @@ struct material {
|
|||
|
||||
varying float v_vertex_xz_dist;
|
||||
varying vec3 v_vertex;
|
||||
varying vec3 v_camera_pos;
|
||||
)"
|
||||
|
||||
R"(
|
||||
|
|
@ -129,7 +132,7 @@ varying vec3 v_vertex;
|
|||
// Z: layer index used for texturearrays, -1 if not in a region
|
||||
ivec3 get_index_coord(const vec2 uv, const int search) {
|
||||
vec2 r_uv = round(uv);
|
||||
vec2 o_uv = mod(r_uv,_region_size);
|
||||
vec2 o_uv = mod(r_uv, _region_size);
|
||||
ivec2 pos;
|
||||
int bounds, layer_index = -1;
|
||||
for (int i = -1; i < clamp(search, SKIP_PASS, FRAGMENT_PASS); i++) {
|
||||
|
|
@ -140,7 +143,7 @@ ivec3 get_index_coord(const vec2 uv, const int search) {
|
|||
layer_index = (_region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1);
|
||||
}
|
||||
}
|
||||
return ivec3(ivec2(mod(r_uv,_region_size)), layer_index);
|
||||
return ivec3(ivec2(mod(r_uv, _region_size)), layer_index);
|
||||
}
|
||||
|
||||
// Takes in descaled (world_space / region_size) world to region space XZ (UV2) coordinates, returns vec3 with:
|
||||
|
|
@ -153,28 +156,52 @@ vec3 get_index_uv(const vec2 uv2) {
|
|||
return vec3(uv2 - _region_locations[layer_index], float(layer_index));
|
||||
}
|
||||
|
||||
float interpolated_height(vec2 pos) {
|
||||
const vec2 offsets = vec2(0, 1);
|
||||
vec2 index_id = floor(pos);
|
||||
ivec3 index[4];
|
||||
index[0] = get_index_coord(index_id + offsets.xy, VERTEX_PASS);
|
||||
index[1] = get_index_coord(index_id + offsets.yy, VERTEX_PASS);
|
||||
index[2] = get_index_coord(index_id + offsets.yx, VERTEX_PASS);
|
||||
index[3] = get_index_coord(index_id + offsets.xx, VERTEX_PASS);
|
||||
float h0 = texelFetch(_height_maps, index[0], 0).r;
|
||||
float h1 = texelFetch(_height_maps, index[1], 0).r;
|
||||
float h2 = texelFetch(_height_maps, index[2], 0).r;
|
||||
float h3 = texelFetch(_height_maps, index[3], 0).r;
|
||||
vec2 f = fract(pos);
|
||||
vec2 i = 1.0 - f;
|
||||
vec4 w = vec4(i.x * f.y, f.x * f.y, f.x * i.y, i.x * i.y);
|
||||
float h = h0 * w[0] + h1 * w[1] + h2 * w[2] + h3 * w[3];
|
||||
return h;
|
||||
}
|
||||
|
||||
//INSERT: DISPLACEMENT_FUNCTIONS
|
||||
//INSERT: FLAT_FUNCTIONS
|
||||
//INSERT: WORLD_NOISE_FUNCTIONS
|
||||
|
||||
void vertex() {
|
||||
// Save Camera Position to varying for access in later functions
|
||||
v_camera_pos = MAIN_CAM_INV_VIEW_MATRIX[3].xyz;
|
||||
|
||||
// Get vertex of flat plane in world coordinates and set world UV
|
||||
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
|
||||
// Camera distance to vertex on flat plane
|
||||
v_vertex_xz_dist = length(v_vertex.xz - _camera_pos.xz);
|
||||
// Taget Object distance to vertex on flat plane
|
||||
v_vertex_xz_dist = length(v_vertex.xz - _target_pos.xz);
|
||||
|
||||
// Geomorph vertex, set end and start for linear height interpolate
|
||||
float scale = MODEL_MATRIX[0][0];
|
||||
float vertex_lerp = smoothstep(0.55, 0.95, (v_vertex_xz_dist / scale - _mesh_size - 4.0) / (_mesh_size - 2.0));
|
||||
float inv_scale = 1.0 / scale;
|
||||
float vertex_lerp = smoothstep(0.55, 0.95, (v_vertex_xz_dist * inv_scale - _mesh_size - 4.0) / (_mesh_size - 2.0));
|
||||
vec2 v_fract = fract(VERTEX.xz * 0.5) * 2.0;
|
||||
// For LOD0 morph from a regular grid to an alternating grid to align with LOD1+
|
||||
vec2 shift = (scale < _vertex_spacing + 1e-6) ? // LOD0 or not
|
||||
// Shift from regular to symetric
|
||||
vec2 shift = (scale < _vertex_spacing / _subdiv + 1e-6) ? // LOD0 or not
|
||||
// Shift from regular to symmetric
|
||||
mix(v_fract, vec2(v_fract.x, -v_fract.y),
|
||||
round(fract(round(mod(v_vertex.z * _vertex_density, 4.0)) *
|
||||
round(mod(v_vertex.x * _vertex_density, 4.0)) * 0.25))
|
||||
) :
|
||||
// Symetric shift
|
||||
v_fract * round((fract(v_vertex.xz * 0.25 / scale) - 0.5) * 4.0);
|
||||
round(fract(round(mod(v_vertex.z * inv_scale, 4.0)) *
|
||||
round(mod(v_vertex.x * inv_scale, 4.0)) * 0.25))) :
|
||||
// symmetric shift
|
||||
v_fract * round((fract(v_vertex.xz * 0.25 * inv_scale) - 0.5) * 4.0);
|
||||
vec2 start_pos = v_vertex.xz * _vertex_density;
|
||||
vec2 end_pos = (v_vertex.xz - shift * scale) * _vertex_density;
|
||||
v_vertex.xz -= shift * scale * vertex_lerp;
|
||||
|
|
@ -190,22 +217,32 @@ void vertex() {
|
|||
uint control = floatBitsToUint(texelFetch(_control_maps, v_region, 0)).r;
|
||||
bool hole = DECODE_HOLE(control);
|
||||
|
||||
vec3 displacement = vec3(0.);
|
||||
// Show holes to all cameras except mouse camera (on exactly 1 layer)
|
||||
if ( !(CAMERA_VISIBLE_LAYERS == _mouse_layer) &&
|
||||
(hole || (_background_mode == 0u && v_region.z == -1))) {
|
||||
v_vertex.x = 0. / 0.;
|
||||
} else {
|
||||
// Set final vertex height & calculate vertex normals. 3 lookups
|
||||
ivec3 coord_a = get_index_coord(start_pos, VERTEX_PASS);
|
||||
ivec3 coord_b = get_index_coord(end_pos, VERTEX_PASS);
|
||||
float h = mix(texelFetch(_height_maps, coord_a, 0).r, texelFetch(_height_maps, coord_b, 0).r, vertex_lerp);
|
||||
//INSERT: FLAT_VERTEX
|
||||
} else {
|
||||
// Set final vertex height.
|
||||
float h;
|
||||
// This branch is static for each of the clipmap segments
|
||||
// Interpolated reads only occur where sub-texel values are required.
|
||||
if (scale < _vertex_spacing) {
|
||||
h = mix(interpolated_height(start_pos), interpolated_height(end_pos), vertex_lerp);
|
||||
} else {
|
||||
ivec3 coord_a = get_index_coord(start_pos, VERTEX_PASS);
|
||||
ivec3 coord_b = get_index_coord(end_pos, VERTEX_PASS);
|
||||
h = mix(texelFetch(_height_maps, coord_a, 0).r, texelFetch(_height_maps, coord_b, 0).r, vertex_lerp);
|
||||
}
|
||||
//INSERT: WORLD_NOISE_VERTEX
|
||||
//INSERT: FLAT_VERTEX
|
||||
//INSERT: DISPLACEMENT_VERTEX
|
||||
v_vertex.y = h;
|
||||
}
|
||||
|
||||
// Convert model space to view space w/ skip_vertex_transform render mode
|
||||
VERTEX = (VIEW_MATRIX * vec4(v_vertex, 1.0)).xyz;
|
||||
// Include displacement without modifying v_vertex.
|
||||
VERTEX = (VIEW_MATRIX * vec4(v_vertex + displacement, 1.0)).xyz;
|
||||
NORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);
|
||||
BINORMAL = normalize((MODELVIEW_MATRIX * vec4(BINORMAL, 0.0)).xyz);
|
||||
TANGENT = normalize((MODELVIEW_MATRIX * vec4(TANGENT, 0.0)).xyz);
|
||||
|
|
@ -226,8 +263,9 @@ vec2 rotate_vec2(const vec2 v, const vec2 cs) {
|
|||
}
|
||||
|
||||
// 2-4 lookups ( 2-6 with dual scaling )
|
||||
void accumulate_material(vec3 base_ddx, vec3 base_ddy, const float weight, const ivec3 index, const uint control,
|
||||
const vec2 texture_weight, const ivec2 texture_id, const vec3 i_normal, float h, inout material mat) {
|
||||
void accumulate_material(vec3 base_ddx, vec3 base_ddy, const mat3 TNB, const float weight, const ivec3 index,
|
||||
const uint control, const vec2 texture_weight, const ivec2 texture_id, const vec3 i_normal,
|
||||
float h, inout material mat) {
|
||||
|
||||
// Applying scaling before projection reduces the number of multiplys ops required.
|
||||
vec3 i_vertex = v_vertex;
|
||||
|
|
@ -274,12 +312,11 @@ void accumulate_material(vec3 base_ddx, vec3 base_ddy, const float weight, const
|
|||
|
||||
// Blend adjustment of Higher ID from Lower ID normal map in world space.
|
||||
float world_normal = 1.;
|
||||
vec3 T = normalize(base_ddx), B = -normalize(base_ddy);
|
||||
// mat3 multiply, reduced to 2x fma and 1x mult.
|
||||
#define FAST_WORLD_NORMAL(n) fma(T, vec3(n.x), fma(B, vec3(n.z), i_normal * vec3(n.y)))
|
||||
#define FAST_WORLD_NORMAL(n) fma(TNB[0], vec3(n.x), fma(TNB[2], vec3(n.z), TNB[1] * vec3(n.y)))
|
||||
|
||||
float blend = DECODE_BLEND(control); // only used for branching.
|
||||
float sharpness = fma(56., blend_sharpness, 8.);
|
||||
float sharpness = fma(60., blend_sharpness, 4.);
|
||||
|
||||
//INSERT: DUAL_SCALING
|
||||
|
||||
|
|
@ -428,9 +465,9 @@ void fragment() {
|
|||
//INSERT: FLAT_FRAGMENT
|
||||
//INSERT: WORLD_NOISE_FRAGMENT
|
||||
// Re-use index[] for the first lookups, skipping some math. 3 lookups
|
||||
h[3] = texelFetch(_height_maps, index[3], 0).r; // 0 (0,0)
|
||||
h[2] = texelFetch(_height_maps, index[2], 0).r; // 1 (1,0)
|
||||
h[0] = texelFetch(_height_maps, index[0], 0).r; // 2 (0,1)
|
||||
h[3] = texelFetch(_height_maps, index[3], 0).r; // 0 (0, 0)
|
||||
h[2] = texelFetch(_height_maps, index[2], 0).r; // 1 (1, 0)
|
||||
h[0] = texelFetch(_height_maps, index[0], 0).r; // 2 (0, 1)
|
||||
index_normal[3] = normalize(vec3(h[3] - h[2] + u, _vertex_spacing, h[3] - h[0] + v));
|
||||
|
||||
// Set flat world normal - overwritten if bilerp is true
|
||||
|
|
@ -467,11 +504,11 @@ void fragment() {
|
|||
|
||||
// 5 lookups
|
||||
// Fetch the additional required height values for smooth normals
|
||||
h[1] = texelFetch(_height_maps, index[1], 0).r; // 3 (1,1)
|
||||
float h_4 = texelFetch(_height_maps, get_index_coord(index_id + offsets.yz, FRAGMENT_PASS), 0).r; // 4 (1,2)
|
||||
float h_5 = texelFetch(_height_maps, get_index_coord(index_id + offsets.zy, FRAGMENT_PASS), 0).r; // 5 (2,1)
|
||||
float h_6 = texelFetch(_height_maps, get_index_coord(index_id + offsets.zx, FRAGMENT_PASS), 0).r; // 6 (2,0)
|
||||
float h_7 = texelFetch(_height_maps, get_index_coord(index_id + offsets.xz, FRAGMENT_PASS), 0).r; // 7 (0,2)
|
||||
h[1] = texelFetch(_height_maps, index[1], 0).r; // 3 (1, 1)
|
||||
float h_4 = texelFetch(_height_maps, get_index_coord(index_id + offsets.yz, FRAGMENT_PASS), 0).r; // 4 (1, 2)
|
||||
float h_5 = texelFetch(_height_maps, get_index_coord(index_id + offsets.zy, FRAGMENT_PASS), 0).r; // 5 (2, 1)
|
||||
float h_6 = texelFetch(_height_maps, get_index_coord(index_id + offsets.zx, FRAGMENT_PASS), 0).r; // 6 (2, 0)
|
||||
float h_7 = texelFetch(_height_maps, get_index_coord(index_id + offsets.xz, FRAGMENT_PASS), 0).r; // 7 (0, 2)
|
||||
|
||||
// Calculate the normal for the remaining index ids.
|
||||
index_normal[0] = normalize(vec3(h[0] - h[1] + u, _vertex_spacing, h[0] - h_7 + v));
|
||||
|
|
@ -486,14 +523,16 @@ void fragment() {
|
|||
index_normal[3] * weights[3] ;
|
||||
}
|
||||
|
||||
vec3 w_tangent = normalize(cross(w_normal, vec3(0.0, 0.0, 1.0)));
|
||||
vec3 w_binormal = normalize(cross(w_normal, w_tangent));
|
||||
mat3 TNB = mat3(w_tangent, w_normal, w_binormal);
|
||||
|
||||
// Apply terrain normals
|
||||
if (flat_terrain_normals) {
|
||||
NORMAL = normalize(cross(dFdyCoarse(VERTEX),dFdxCoarse(VERTEX)));
|
||||
NORMAL = normalize(cross(dFdyCoarse(VERTEX), dFdxCoarse(VERTEX)));
|
||||
TANGENT = normalize(cross(NORMAL, VIEW_MATRIX[2].xyz));
|
||||
BINORMAL = normalize(cross(NORMAL, TANGENT));
|
||||
} else {
|
||||
vec3 w_tangent = normalize(cross(w_normal, vec3(0.0, 0.0, 1.0)));
|
||||
vec3 w_binormal = normalize(cross(w_normal, w_tangent));
|
||||
NORMAL = mat3(VIEW_MATRIX) * w_normal;
|
||||
TANGENT = mat3(VIEW_MATRIX) * w_tangent;
|
||||
BINORMAL = mat3(VIEW_MATRIX) * w_binormal;
|
||||
|
|
@ -554,16 +593,16 @@ void fragment() {
|
|||
material mat = material(vec4(0.0), vec4(0.0), 0., 0., 0., 0.);
|
||||
|
||||
// 2 - 4 lookups, 2 - 6 if dual scale texture
|
||||
accumulate_material(base_ddx, base_ddy, weights[3], index[3], control[3], t_weights[3],
|
||||
accumulate_material(base_ddx, base_ddy, TNB, weights[3], index[3], control[3], t_weights[3],
|
||||
texture_ids[3], index_normal[3], h[3], mat);
|
||||
|
||||
// 6 - 12 lookups, 6 - 18 if dual scale texture
|
||||
if (bilerp) {
|
||||
accumulate_material(base_ddx, base_ddy, weights[2], index[2], control[2], t_weights[2],
|
||||
accumulate_material(base_ddx, base_ddy, TNB, weights[2], index[2], control[2], t_weights[2],
|
||||
texture_ids[2], index_normal[2], h[2], mat);
|
||||
accumulate_material(base_ddx, base_ddy, weights[1], index[1], control[1], t_weights[1],
|
||||
accumulate_material(base_ddx, base_ddy, TNB, weights[1], index[1], control[1], t_weights[1],
|
||||
texture_ids[1], index_normal[1], h[1], mat);
|
||||
accumulate_material(base_ddx, base_ddy, weights[0], index[0], control[0], t_weights[0],
|
||||
accumulate_material(base_ddx, base_ddy, TNB, weights[0], index[0], control[0], t_weights[0],
|
||||
texture_ids[0], index_normal[0], h[0], mat);
|
||||
}
|
||||
|
||||
|
|
@ -577,7 +616,7 @@ void fragment() {
|
|||
|
||||
// Macro variation. 2 lookups
|
||||
vec3 macrov = vec3(1.);
|
||||
if (macro_variation) {
|
||||
if (macro_variation_enabled) {
|
||||
float noise1 = texture(noise_texture, rotate_vec2(fma(uv, vec2(noise1_scale * .1), noise1_offset) , vec2(cos(noise1_angle), sin(noise1_angle)))).r;
|
||||
float noise2 = texture(noise_texture, uv * noise2_scale * .1).r;
|
||||
macrov = mix(macro_variation1, vec3(1.), noise1);
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ R"(
|
|||
// Show region grid
|
||||
{
|
||||
vec3 __pixel_pos = (INV_VIEW_MATRIX * vec4(VERTEX,1.0)).xyz;
|
||||
vec3 __camera_pos = INV_VIEW_MATRIX[3].xyz;
|
||||
float __region_line = 1.0; // Region line thickness
|
||||
__region_line *= .1*sqrt(length(_camera_pos - __pixel_pos));
|
||||
__region_line *= .1*sqrt(length(__camera_pos - __pixel_pos));
|
||||
if (mod(__pixel_pos.x * _vertex_density + __region_line*.5, _region_size) <= __region_line ||
|
||||
mod(__pixel_pos.z * _vertex_density + __region_line*.5, _region_size) <= __region_line ) {
|
||||
ALBEDO = vec3(1.);
|
||||
|
|
@ -20,8 +21,9 @@ R"(
|
|||
// Show region grid
|
||||
{
|
||||
vec3 __pixel_pos = (INV_VIEW_MATRIX * vec4(VERTEX,1.0)).xyz;
|
||||
vec3 __camera_pos = INV_VIEW_MATRIX[3].xyz;
|
||||
float __cell_line = 0.5; // Cell line thickness
|
||||
__cell_line *= .1*sqrt(length(_camera_pos - __pixel_pos));
|
||||
__cell_line *= .1*sqrt(length(__camera_pos - __pixel_pos));
|
||||
#define CELL_SIZE 32
|
||||
if (mod(__pixel_pos.x * _vertex_density + __cell_line*.5, CELL_SIZE) <= __cell_line ||
|
||||
mod(__pixel_pos.z * _vertex_density + __cell_line*.5, CELL_SIZE) <= __cell_line ) {
|
||||
|
|
@ -33,13 +35,14 @@ R"(
|
|||
// Show vertex grids
|
||||
{
|
||||
vec3 __pixel_pos = (INV_VIEW_MATRIX * vec4(VERTEX,1.0)).xyz;
|
||||
vec3 __camera_pos = INV_VIEW_MATRIX[3].xyz;
|
||||
float __grid_line = 0.05; // Vertex grid line thickness
|
||||
float __grid_step = 1.0; // Vertex grid size, 1.0 == integer units
|
||||
float __vertex_size = 4.; // Size of vertices
|
||||
float __view_distance = 300.0; // Visible distance of grid
|
||||
vec3 __vertex_mul = vec3(0.);
|
||||
vec3 __vertex_add = vec3(0.);
|
||||
float __distance_factor = clamp(1.-length(_camera_pos - __pixel_pos)/__view_distance, 0., 1.);
|
||||
float __distance_factor = clamp(1.-length(__camera_pos - __pixel_pos)/__view_distance, 0., 1.);
|
||||
// Draw vertex grid
|
||||
if ( mod(__pixel_pos.x * _vertex_density + __grid_line*.5, __grid_step) < __grid_line ||
|
||||
mod(__pixel_pos.z * _vertex_density + __grid_line*.5, __grid_step) < __grid_line ) {
|
||||
|
|
@ -60,8 +63,8 @@ uniform float contour_thickness : hint_range(0.0, 10.0, 0.001) = 1.0;
|
|||
uniform vec4 contour_color : source_color = vec4(.85, .85, .19, 1.);
|
||||
group_uniforms;
|
||||
|
||||
float fractal_contour_lines(float thickness, float interval, vec3 spatial_coords, vec3 normal, vec3 base_ddx, vec3 base_ddy) {
|
||||
float depth = max(log(length(spatial_coords - _camera_pos) / interval) * (1.0 / log2(2.0)) - 1.0, 1.0);
|
||||
float fractal_contour_lines(float thickness, float interval, vec3 spatial_coords, vec3 normal, vec3 base_ddx, vec3 base_ddy, vec3 __camera_pos) {
|
||||
float depth = max(log(length(spatial_coords - __camera_pos) / interval) * (1.0 / log2(2.0)) - 1.0, 1.0);
|
||||
|
||||
float interval_a = interval * exp2(max(floor(depth) - 1.0, 1.0)) * 0.5;
|
||||
float interval_b = interval * exp2(max(floor(depth), 1.0)) * 0.5;
|
||||
|
|
@ -103,10 +106,11 @@ float fractal_contour_lines(float thickness, float interval, vec3 spatial_coords
|
|||
// Show contour lines
|
||||
{
|
||||
vec3 __pixel_pos = (INV_VIEW_MATRIX * vec4(VERTEX,1.0)).xyz;
|
||||
vec3 __camera_pos = INV_VIEW_MATRIX[3].xyz;
|
||||
vec3 __base_ddx = dFdxCoarse(__pixel_pos);
|
||||
vec3 __base_ddy = dFdyCoarse(__pixel_pos);
|
||||
vec3 __w_normal = normalize(cross(__base_ddy, __base_ddx));
|
||||
float __line = fractal_contour_lines(contour_thickness, contour_interval, __pixel_pos, __w_normal, __base_ddx, __base_ddy);
|
||||
float __line = fractal_contour_lines(contour_thickness, contour_interval, __pixel_pos, __w_normal, __base_ddx, __base_ddy, __camera_pos);
|
||||
ALBEDO = mix(ALBEDO, contour_color.rgb, (1.-__line) * contour_color.a);
|
||||
}
|
||||
)"
|
||||
|
|
@ -4,18 +4,13 @@
|
|||
#include <godot_cpp/classes/editor_interface.hpp>
|
||||
#include <godot_cpp/classes/engine.hpp>
|
||||
#include <godot_cpp/classes/environment.hpp>
|
||||
#include <godot_cpp/classes/height_map_shape3d.hpp>
|
||||
#include <godot_cpp/classes/label3d.hpp>
|
||||
#include <godot_cpp/classes/os.hpp>
|
||||
#include <godot_cpp/classes/physics_direct_space_state3d.hpp>
|
||||
#include <godot_cpp/classes/physics_ray_query_parameters3d.hpp>
|
||||
#include <godot_cpp/classes/project_settings.hpp>
|
||||
#include <godot_cpp/classes/quad_mesh.hpp>
|
||||
#include <godot_cpp/classes/rendering_server.hpp>
|
||||
#include <godot_cpp/classes/shader_material.hpp>
|
||||
#include <godot_cpp/classes/surface_tool.hpp>
|
||||
#include <godot_cpp/classes/time.hpp>
|
||||
#include <godot_cpp/classes/viewport.hpp>
|
||||
#include <godot_cpp/classes/viewport_texture.hpp>
|
||||
#include <godot_cpp/classes/world3d.hpp>
|
||||
|
||||
|
|
@ -70,20 +65,20 @@ void Terrain3D::_initialize() {
|
|||
LOG(DEBUG, "Connecting _data::region_map_changed signal to build()");
|
||||
_data->connect("region_map_changed", callable_mp(_collision, &Terrain3DCollision::build));
|
||||
}
|
||||
// Any map was regenerated or regions changed, update material
|
||||
if (!_data->is_connected("maps_changed", callable_mp(_material.ptr(), &Terrain3DMaterial::_update_maps))) {
|
||||
LOG(DEBUG, "Connecting _data::maps_changed signal to _material->_update_maps()");
|
||||
_data->connect("maps_changed", callable_mp(_material.ptr(), &Terrain3DMaterial::_update_maps));
|
||||
// Any map was regenerated or regions changed, update material uniforms without rebuilding shaders
|
||||
if (!_data->is_connected("maps_changed", callable_mp(_material.ptr(), &Terrain3DMaterial::update).bind(false))) {
|
||||
LOG(DEBUG, "Connecting _data::maps_changed signal to _material->_update()");
|
||||
_data->connect("maps_changed", callable_mp(_material.ptr(), &Terrain3DMaterial::update).bind(false));
|
||||
}
|
||||
// Height map was regenerated, update aabbs
|
||||
if (!_data->is_connected("height_maps_changed", callable_mp(this, &Terrain3D::_update_mesher_aabbs))) {
|
||||
LOG(DEBUG, "Connecting _data::height_maps_changed signal to update_aabbs()");
|
||||
_data->connect("height_maps_changed", callable_mp(this, &Terrain3D::_update_mesher_aabbs));
|
||||
}
|
||||
// Texture assets changed, update material
|
||||
if (!_assets->is_connected("textures_changed", callable_mp(_material.ptr(), &Terrain3DMaterial::_update_texture_arrays))) {
|
||||
LOG(DEBUG, "Connecting _assets.textures_changed to _material->_update_texture_arrays()");
|
||||
_assets->connect("textures_changed", callable_mp(_material.ptr(), &Terrain3DMaterial::_update_texture_arrays));
|
||||
// Texture assets changed, update material uniforms without rebuilding shaders
|
||||
if (!_assets->is_connected("textures_changed", callable_mp(_material.ptr(), &Terrain3DMaterial::update).bind(false))) {
|
||||
LOG(DEBUG, "Connecting _assets.textures_changed to _material->update()");
|
||||
_assets->connect("textures_changed", callable_mp(_material.ptr(), &Terrain3DMaterial::update).bind(false));
|
||||
}
|
||||
// Initialize the system
|
||||
if (!_initialized && _is_inside_world && is_inside_tree()) {
|
||||
|
|
@ -94,6 +89,7 @@ void Terrain3D::_initialize() {
|
|||
_collision->initialize(this);
|
||||
_instancer->initialize(this);
|
||||
_mesher->initialize(this);
|
||||
_update_displacement_buffer();
|
||||
_initialized = true;
|
||||
snap();
|
||||
}
|
||||
|
|
@ -111,7 +107,21 @@ void Terrain3D::__physics_process(const double p_delta) {
|
|||
LOG(DEBUG, "Camera is null, getting the current one");
|
||||
_grab_camera();
|
||||
}
|
||||
if (_mesher) {
|
||||
if (_tessellation_level > 0) {
|
||||
if (_mesher && _d_buffer_vp && _material.is_valid()) {
|
||||
// If clipmap target has moved enough, re-center buffer on the target.
|
||||
Vector2 target_pos_2d = v3v2(get_clipmap_target_position());
|
||||
real_t tessellation_density = 1.f / pow(2.f, _tessellation_level);
|
||||
real_t vertex_spacing = _vertex_spacing * tessellation_density;
|
||||
if (!(MAX(abs(_last_buffer_position.x - target_pos_2d.x), abs(_last_buffer_position.y - target_pos_2d.y)) < vertex_spacing)) {
|
||||
_last_buffer_position = target_pos_2d;
|
||||
RS->material_set_param(_material->get_buffer_material_rid(), "_target_pos", get_clipmap_target_position());
|
||||
_d_buffer_vp->set_update_mode(SubViewport::UPDATE_ONCE);
|
||||
// Only call snap on _mesher if the buffer has snapped, prevents stuttering.
|
||||
_mesher->snap();
|
||||
}
|
||||
}
|
||||
} else if (_mesher) {
|
||||
_mesher->snap();
|
||||
}
|
||||
if (_collision && _collision->is_dynamic_mode()) {
|
||||
|
|
@ -194,7 +204,7 @@ void Terrain3D::_setup_mouse_picking() {
|
|||
_mouse_vp->set_size(V2I(2));
|
||||
_mouse_vp->set_scaling_3d_mode(Viewport::SCALING_3D_MODE_BILINEAR);
|
||||
_mouse_vp->set_update_mode(SubViewport::UPDATE_ONCE);
|
||||
_mouse_vp->set_handle_input_locally(false);
|
||||
_mouse_vp->set_disable_input(true);
|
||||
_mouse_vp->set_canvas_cull_mask(0);
|
||||
_mouse_vp->set_use_hdr_2d(true);
|
||||
_mouse_vp->set_anisotropic_filtering_level(Viewport::ANISOTROPY_DISABLED);
|
||||
|
|
@ -253,6 +263,53 @@ void Terrain3D::_destroy_mouse_picking() {
|
|||
memdelete_safely(_mouse_vp);
|
||||
}
|
||||
|
||||
void Terrain3D::_setup_displacement_buffer() {
|
||||
if (!is_inside_tree()) {
|
||||
LOG(ERROR, "Not inside the tree, skipping displacement buffer setup");
|
||||
return;
|
||||
}
|
||||
_destroy_displacement_buffer();
|
||||
LOG(INFO, "Setting up displacement buffer");
|
||||
_d_buffer_vp = memnew(SubViewport);
|
||||
_d_buffer_vp->set_name("DBufferViewport");
|
||||
add_child(_d_buffer_vp, true);
|
||||
_d_buffer_vp->set_size(Vector2i(2, 2));
|
||||
_d_buffer_vp->set_disable_3d(true);
|
||||
_d_buffer_vp->set_update_mode(SubViewport::UPDATE_ONCE);
|
||||
_d_buffer_vp->set_disable_input(true);
|
||||
_d_buffer_vp->set_default_canvas_item_texture_filter(Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
|
||||
|
||||
_d_buffer_rect = memnew(ColorRect);
|
||||
_d_buffer_rect->set_name("DBufferRect");
|
||||
_d_buffer_vp->add_child(_d_buffer_rect, true);
|
||||
_d_buffer_rect->set_anchors_preset(Control::PRESET_FULL_RECT);
|
||||
}
|
||||
|
||||
void Terrain3D::_update_displacement_buffer() {
|
||||
if (!_d_buffer_vp) {
|
||||
return;
|
||||
}
|
||||
if (_tessellation_level == 0) {
|
||||
_d_buffer_vp->set_size(V2I_ZERO);
|
||||
_d_buffer_rect->set_size(V2I_ZERO);
|
||||
} else {
|
||||
_d_buffer_vp->set_size(Vector2i(_mesh_size * 4 * _tessellation_level, _mesh_size * 4));
|
||||
_d_buffer_rect->set_size(Vector2i(_mesh_size * 4 * _tessellation_level, _mesh_size * 4));
|
||||
LOG(INFO, "Updating displacement buffer to Size: ", _d_buffer_vp->get_size());
|
||||
if (_material.is_valid() && _material->get_material_rid().is_valid()) {
|
||||
RS->canvas_item_set_material(_d_buffer_rect->get_canvas_item(), _material->get_buffer_material_rid());
|
||||
RS->material_set_param(_material->get_material_rid(), "_displacement_buffer", _d_buffer_vp->get_texture()->get_rid());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Terrain3D::_destroy_displacement_buffer() {
|
||||
LOG(DEBUG, "Freeing d_buffer_rect");
|
||||
memdelete_safely(_d_buffer_rect);
|
||||
LOG(DEBUG, "Freeing d_buffer_vp");
|
||||
memdelete_safely(_d_buffer_vp);
|
||||
}
|
||||
|
||||
void Terrain3D::_generate_triangles(PackedVector3Array &p_vertices, PackedVector2Array *p_uvs, const int32_t p_lod,
|
||||
const Terrain3DData::HeightFilter p_filter, const bool p_require_nav, const AABB &p_global_aabb) const {
|
||||
ERR_FAIL_COND(_data == nullptr);
|
||||
|
|
@ -446,7 +503,7 @@ void Terrain3D::set_editor(Terrain3DEditor *p_editor) {
|
|||
SET_IF_DIFF(_editor, p_editor);
|
||||
LOG(INFO, "Setting Terrain3DEditor: ", _editor);
|
||||
if (_material.is_valid()) {
|
||||
_material->update();
|
||||
_material->update(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -522,6 +579,9 @@ void Terrain3D::snap() {
|
|||
if (_collision) {
|
||||
_collision->reset_target_position();
|
||||
}
|
||||
if (_tessellation_level > 0) {
|
||||
_last_buffer_position = V2_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
void Terrain3D::set_region_size(const RegionSize p_size) {
|
||||
|
|
@ -536,8 +596,9 @@ void Terrain3D::set_region_size(const RegionSize p_size) {
|
|||
_data->_region_sizev = V2I(_region_size);
|
||||
}
|
||||
if (_material.is_valid()) {
|
||||
_material->_update_maps();
|
||||
_material->update();
|
||||
}
|
||||
_update_displacement_buffer();
|
||||
}
|
||||
|
||||
void Terrain3D::set_save_16_bit(const bool p_enabled) {
|
||||
|
|
@ -598,13 +659,15 @@ void Terrain3D::set_mesh_size(const int p_size) {
|
|||
if (_mesher && _material.is_valid()) {
|
||||
_material->update();
|
||||
_mesher->initialize(this);
|
||||
_update_displacement_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
void Terrain3D::set_mesh_lods(const int p_count) {
|
||||
SET_IF_DIFF(_mesh_lods, CLAMP(p_count, 1, 10));
|
||||
LOG(INFO, "Setting mesh levels: ", _mesh_lods);
|
||||
if (_mesher) {
|
||||
if (_mesher && _material.is_valid()) {
|
||||
_material->update();
|
||||
_mesher->initialize(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -617,15 +680,27 @@ void Terrain3D::set_vertex_spacing(const real_t p_spacing) {
|
|||
_data->_vertex_spacing = _vertex_spacing;
|
||||
update_region_labels();
|
||||
_mesher->reset_target_position();
|
||||
_material->_update_maps();
|
||||
_material->update();
|
||||
_collision->destroy();
|
||||
_collision->build();
|
||||
_update_displacement_buffer();
|
||||
}
|
||||
if (IS_EDITOR && _editor_plugin) {
|
||||
_editor_plugin->call("update_region_grid");
|
||||
}
|
||||
}
|
||||
|
||||
void Terrain3D::set_tessellation_level(const int p_level) {
|
||||
SET_IF_DIFF(_tessellation_level, CLAMP(p_level, 0, 6));
|
||||
LOG(INFO, "Setting tessellation level: ", p_level);
|
||||
if (_mesher && _material.is_valid()) {
|
||||
_material->update(true);
|
||||
_mesher->initialize(this);
|
||||
_update_displacement_buffer();
|
||||
}
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
void Terrain3D::set_render_layers(const uint32_t p_layers) {
|
||||
SET_IF_DIFF(_render_layers, p_layers);
|
||||
LOG(INFO, "Setting terrain render layers to: ", p_layers);
|
||||
|
|
@ -908,6 +983,7 @@ void Terrain3D::_notification(const int p_what) {
|
|||
set_notify_transform(true);
|
||||
set_meta("_edit_lock_", true);
|
||||
_setup_mouse_picking();
|
||||
_setup_displacement_buffer();
|
||||
// Reload editor textures - Also see READY
|
||||
if (_free_editor_textures && !IS_EDITOR && _assets.is_valid() && !_assets->get_path().contains("Terrain3DAssets")) {
|
||||
LOG(INFO, "free_editor_textures enabled, reloading Assets path: ", _assets->get_path());
|
||||
|
|
@ -1012,6 +1088,7 @@ void Terrain3D::_notification(const int p_what) {
|
|||
set_physics_process(false);
|
||||
_destroy_mesher();
|
||||
_destroy_mouse_picking();
|
||||
_destroy_displacement_buffer();
|
||||
if (_assets.is_valid()) {
|
||||
_assets->uninitialize();
|
||||
}
|
||||
|
|
@ -1049,6 +1126,18 @@ void Terrain3D::_notification(const int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void Terrain3D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (_tessellation_level == 0) {
|
||||
// Hide all displacement properties
|
||||
if (p_property.name == StringName("displacement_scale") ||
|
||||
p_property.name == StringName("displacement_sharpness") ||
|
||||
p_property.name == StringName("buffer_shader_override_enabled") ||
|
||||
p_property.name == StringName("buffer_shader_override")) {
|
||||
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Terrain3D::_bind_methods() {
|
||||
BIND_ENUM_CONSTANT(ERROR);
|
||||
BIND_ENUM_CONSTANT(INFO);
|
||||
|
|
@ -1118,13 +1207,23 @@ void Terrain3D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_physics_material", "material"), &Terrain3D::set_physics_material);
|
||||
ClassDB::bind_method(D_METHOD("get_physics_material"), &Terrain3D::get_physics_material);
|
||||
|
||||
// Mesh
|
||||
// Terrain Mesh
|
||||
ClassDB::bind_method(D_METHOD("set_mesh_lods", "count"), &Terrain3D::set_mesh_lods);
|
||||
ClassDB::bind_method(D_METHOD("get_mesh_lods"), &Terrain3D::get_mesh_lods);
|
||||
ClassDB::bind_method(D_METHOD("set_mesh_size", "size"), &Terrain3D::set_mesh_size);
|
||||
ClassDB::bind_method(D_METHOD("get_mesh_size"), &Terrain3D::get_mesh_size);
|
||||
ClassDB::bind_method(D_METHOD("set_vertex_spacing", "scale"), &Terrain3D::set_vertex_spacing);
|
||||
ClassDB::bind_method(D_METHOD("get_vertex_spacing"), &Terrain3D::get_vertex_spacing);
|
||||
ClassDB::bind_method(D_METHOD("set_tessellation_level", "size"), &Terrain3D::set_tessellation_level);
|
||||
ClassDB::bind_method(D_METHOD("get_tessellation_level"), &Terrain3D::get_tessellation_level);
|
||||
ClassDB::bind_method(D_METHOD("set_displacement_scale", "scale"), &Terrain3D::set_displacement_scale);
|
||||
ClassDB::bind_method(D_METHOD("get_displacement_scale"), &Terrain3D::get_displacement_scale);
|
||||
ClassDB::bind_method(D_METHOD("set_displacement_sharpness", "sharpness"), &Terrain3D::set_displacement_sharpness);
|
||||
ClassDB::bind_method(D_METHOD("get_displacement_sharpness"), &Terrain3D::get_displacement_sharpness);
|
||||
ClassDB::bind_method(D_METHOD("set_buffer_shader_override_enabled", "enabled"), &Terrain3D::set_buffer_shader_override_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_buffer_shader_override_enabled"), &Terrain3D::is_buffer_shader_override_enabled);
|
||||
ClassDB::bind_method(D_METHOD("set_buffer_shader_override", "shader"), &Terrain3D::set_buffer_shader_override);
|
||||
ClassDB::bind_method(D_METHOD("get_buffer_shader_override"), &Terrain3D::get_buffer_shader_override);
|
||||
|
||||
// Rendering
|
||||
ClassDB::bind_method(D_METHOD("set_render_layers", "layers"), &Terrain3D::set_render_layers);
|
||||
|
|
@ -1177,6 +1276,8 @@ void Terrain3D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_show_colormap"), &Terrain3D::get_show_colormap);
|
||||
ClassDB::bind_method(D_METHOD("set_show_roughmap", "enabled"), &Terrain3D::set_show_roughmap);
|
||||
ClassDB::bind_method(D_METHOD("get_show_roughmap"), &Terrain3D::get_show_roughmap);
|
||||
ClassDB::bind_method(D_METHOD("set_show_displacement_buffer", "enabled"), &Terrain3D::set_show_displacement_buffer);
|
||||
ClassDB::bind_method(D_METHOD("get_show_displacement_buffer"), &Terrain3D::get_show_displacement_buffer);
|
||||
|
||||
// PBR Views
|
||||
ClassDB::bind_method(D_METHOD("set_show_texture_albedo", "enabled"), &Terrain3D::set_show_texture_albedo);
|
||||
|
|
@ -1219,17 +1320,23 @@ void Terrain3D::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mode", PROPERTY_HINT_ENUM, "Disabled,Dynamic / Game,Dynamic / Editor,Full / Game,Full / Editor"), "set_collision_mode", "get_collision_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_shape_size", PROPERTY_HINT_RANGE, "8,64,8"), "set_collision_shape_size", "get_collision_shape_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_radius", PROPERTY_HINT_RANGE, "16,256,16"), "set_collision_radius", "get_collision_radius");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collision_target", PROPERTY_HINT_NODE_TYPE, "Node3D", PROPERTY_USAGE_DEFAULT, "Node3D"), "set_collision_target", "get_collision_target");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_priority", PROPERTY_HINT_RANGE, "0.1,256,.1"), "set_collision_priority", "get_collision_priority");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material", "get_physics_material");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collision_target", PROPERTY_HINT_NODE_TYPE, "Node3D", PROPERTY_USAGE_DEFAULT, "Node3D"), "set_collision_target", "get_collision_target");
|
||||
|
||||
ADD_GROUP("Clipmap Mesh", "");
|
||||
ADD_GROUP("Terrain Mesh", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "clipmap_target", PROPERTY_HINT_NODE_TYPE, "Node3D", PROPERTY_USAGE_DEFAULT, "Node3D"), "set_clipmap_target", "get_clipmap_target");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "mesh_lods", PROPERTY_HINT_RANGE, "1,10,1"), "set_mesh_lods", "get_mesh_lods");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "mesh_size", PROPERTY_HINT_RANGE, "8,256,2"), "set_mesh_size", "get_mesh_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vertex_spacing", PROPERTY_HINT_RANGE, "0.25,10.0,0.05,or_greater"), "set_vertex_spacing", "get_vertex_spacing");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "clipmap_target", PROPERTY_HINT_NODE_TYPE, "Node3D", PROPERTY_USAGE_DEFAULT, "Node3D"), "set_clipmap_target", "get_clipmap_target");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "tessellation_level", PROPERTY_HINT_RANGE, "0,6,1"), "set_tessellation_level", "get_tessellation_level");
|
||||
ADD_SUBGROUP("Displacement", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "displacement_scale", PROPERTY_HINT_RANGE, "0.0, 2.0, 0.01"), "set_displacement_scale", "get_displacement_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "displacement_sharpness", PROPERTY_HINT_RANGE, "0.0, 1.0, 0.01"), "set_displacement_sharpness", "get_displacement_sharpness");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "buffer_shader_override_enabled"), "set_buffer_shader_override_enabled", "is_buffer_shader_override_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "buffer_shader_override", PROPERTY_HINT_RESOURCE_TYPE, "Shader"), "set_buffer_shader_override", "get_buffer_shader_override");
|
||||
|
||||
ADD_GROUP("Rendering", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "render_layers", PROPERTY_HINT_LAYERS_3D_RENDER), "set_render_layers", "get_render_layers");
|
||||
|
|
@ -1259,6 +1366,7 @@ void Terrain3D::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_control_scale"), "set_show_control_scale", "get_show_control_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_colormap"), "set_show_colormap", "get_show_colormap");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_roughmap"), "set_show_roughmap", "get_show_roughmap");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_displacement_buffer"), "set_show_displacement_buffer", "get_show_displacement_buffer");
|
||||
|
||||
ADD_SUBGROUP("PBR", "show_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_texture_albedo"), "set_show_texture_albedo", "get_show_texture_albedo");
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@
|
|||
#define TERRAIN3D_CLASS_H
|
||||
|
||||
#include <godot_cpp/classes/camera3d.hpp>
|
||||
#include <godot_cpp/classes/color_rect.hpp>
|
||||
#include <godot_cpp/classes/geometry_instance3d.hpp>
|
||||
#include <godot_cpp/classes/mesh.hpp>
|
||||
#include <godot_cpp/classes/mesh_instance3d.hpp>
|
||||
#include <godot_cpp/classes/object.hpp>
|
||||
#include <godot_cpp/classes/rendering_server.hpp>
|
||||
#include <godot_cpp/classes/static_body3d.hpp>
|
||||
#include <godot_cpp/classes/sub_viewport.hpp>
|
||||
|
||||
#include "constants.h"
|
||||
|
|
@ -76,6 +76,7 @@ private:
|
|||
// Meshes
|
||||
int _mesh_lods = 7;
|
||||
int _mesh_size = 48;
|
||||
int _tessellation_level = 0;
|
||||
real_t _vertex_spacing = 1.0f;
|
||||
|
||||
// Rendering
|
||||
|
|
@ -91,6 +92,11 @@ private:
|
|||
MeshInstance3D *_mouse_quad = nullptr;
|
||||
uint32_t _mouse_layer = 32u;
|
||||
|
||||
// Displacement Buffer
|
||||
SubViewport *_d_buffer_vp = nullptr;
|
||||
ColorRect *_d_buffer_rect = nullptr;
|
||||
Vector2 _last_buffer_position = V2_MAX;
|
||||
|
||||
// Parent containers for child nodes
|
||||
Node3D *_label_parent;
|
||||
|
||||
|
|
@ -110,6 +116,10 @@ private:
|
|||
void _setup_mouse_picking();
|
||||
void _destroy_mouse_picking();
|
||||
|
||||
void _setup_displacement_buffer();
|
||||
void _update_displacement_buffer();
|
||||
void _destroy_displacement_buffer();
|
||||
|
||||
void _generate_triangles(PackedVector3Array &p_vertices, PackedVector2Array *p_uvs, const int32_t p_lod,
|
||||
const Terrain3DData::HeightFilter p_filter, const bool require_nav, const AABB &p_global_aabb) const;
|
||||
void _generate_triangle_pair(PackedVector3Array &p_vertices, PackedVector2Array *p_uvs, const int32_t p_lod,
|
||||
|
|
@ -165,13 +175,25 @@ public:
|
|||
int get_label_size() const { return _label_size; }
|
||||
void update_region_labels();
|
||||
|
||||
// Mesh
|
||||
// Terrain Mesh
|
||||
void set_mesh_lods(const int p_count);
|
||||
int get_mesh_lods() const { return _mesh_lods; }
|
||||
void set_mesh_size(const int p_size);
|
||||
int get_mesh_size() const { return _mesh_size; }
|
||||
void set_vertex_spacing(const real_t p_spacing);
|
||||
real_t get_vertex_spacing() const { return _vertex_spacing; }
|
||||
void set_tessellation_level(const int p_level);
|
||||
int get_tessellation_level() const { return _tessellation_level; }
|
||||
|
||||
// Material Displacement Aliases
|
||||
void set_displacement_scale(const real_t p_displacement_scale) { _material.is_valid() ? _material->set_displacement_scale(p_displacement_scale) : void(); }
|
||||
real_t get_displacement_scale() const { return _material.is_valid() ? _material->get_displacement_scale() : 1.f; }
|
||||
void set_displacement_sharpness(const real_t p_displacement_sharpness) { _material.is_valid() ? _material->set_displacement_sharpness(p_displacement_sharpness) : void(); }
|
||||
real_t get_displacement_sharpness() const { return _material.is_valid() ? _material->get_displacement_sharpness() : 0.25f; }
|
||||
void set_buffer_shader_override_enabled(const bool p_enabled) { _material.is_valid() ? _material->set_buffer_shader_override_enabled(p_enabled) : void(); }
|
||||
bool is_buffer_shader_override_enabled() const { return _material.is_valid() ? _material->is_buffer_shader_override_enabled() : false; }
|
||||
void set_buffer_shader_override(const Ref<Shader> &p_shader) { return _material.is_valid() ? _material->set_buffer_shader_override(p_shader) : void(); }
|
||||
Ref<Shader> get_buffer_shader_override() const { return _material.is_valid() ? _material->get_buffer_shader_override() : Ref<Shader>(); }
|
||||
|
||||
// Rendering
|
||||
void set_render_layers(const uint32_t p_layers);
|
||||
|
|
@ -253,6 +275,8 @@ public:
|
|||
bool get_show_colormap() const { return _material.is_valid() ? _material->get_show_colormap() : false; }
|
||||
void set_show_roughmap(const bool p_enabled) { _material.is_valid() ? _material->set_show_roughmap(p_enabled) : void(); }
|
||||
bool get_show_roughmap() const { return _material.is_valid() ? _material->get_show_roughmap() : false; }
|
||||
void set_show_displacement_buffer(const bool p_enabled) { _material.is_valid() ? _material->set_show_displacement_buffer(p_enabled) : void(); }
|
||||
bool get_show_displacement_buffer() const { return _material.is_valid() ? _material->get_show_displacement_buffer() : false; }
|
||||
|
||||
// PBR View Aliases
|
||||
void set_show_texture_albedo(const bool p_enabled) { _material.is_valid() ? _material->set_show_texture_albedo(p_enabled) : void(); }
|
||||
|
|
@ -268,6 +292,7 @@ public:
|
|||
|
||||
protected:
|
||||
void _notification(const int p_what);
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
static void _bind_methods();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -355,6 +355,7 @@ void Terrain3DAssets::_update_texture_settings() {
|
|||
_texture_uv_scales.clear();
|
||||
_texture_vertical_projections = 0u;
|
||||
_texture_detiles.clear();
|
||||
_texture_displacements.clear();
|
||||
|
||||
for (const Ref<Terrain3DTextureAsset> &ta : _texture_list) {
|
||||
if (ta.is_null()) {
|
||||
|
|
@ -372,6 +373,7 @@ void Terrain3DAssets::_update_texture_settings() {
|
|||
_texture_uv_scales.push_back(ta->get_uv_scale());
|
||||
_texture_vertical_projections |= (ta->get_vertical_projection() ? (uint32_t(1u) << uint32_t(ta->get_id())) : uint32_t(0u));
|
||||
_texture_detiles.push_back(Vector2(ta->get_detiling_rotation(), ta->get_detiling_shift()));
|
||||
_texture_displacements.push_back(Vector2(ta->get_displacement_offset(), ta->get_displacement_scale()));
|
||||
}
|
||||
}
|
||||
LOG(DEBUG, "Emitting textures_changed");
|
||||
|
|
@ -737,6 +739,7 @@ void Terrain3DAssets::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_texture_uv_scales"), &Terrain3DAssets::get_texture_uv_scales);
|
||||
ClassDB::bind_method(D_METHOD("get_texture_vertical_projections"), &Terrain3DAssets::get_texture_vertical_projections);
|
||||
ClassDB::bind_method(D_METHOD("get_texture_detiles"), &Terrain3DAssets::get_texture_detiles);
|
||||
ClassDB::bind_method(D_METHOD("get_texture_displacements"), &Terrain3DAssets::get_texture_displacements);
|
||||
ClassDB::bind_method(D_METHOD("clear_textures", "update"), &Terrain3DAssets::clear_textures, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("update_texture_list"), &Terrain3DAssets::update_texture_list);
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ private:
|
|||
PackedFloat32Array _texture_uv_scales;
|
||||
uint32_t _texture_vertical_projections;
|
||||
PackedVector2Array _texture_detiles;
|
||||
PackedVector2Array _texture_displacements;
|
||||
|
||||
// Mesh Thumbnail Generation
|
||||
RID _scenario;
|
||||
|
|
@ -85,6 +86,7 @@ public:
|
|||
PackedFloat32Array get_texture_uv_scales() const { return _texture_uv_scales; }
|
||||
uint32_t get_texture_vertical_projections() const { return _texture_vertical_projections; }
|
||||
PackedVector2Array get_texture_detiles() const { return _texture_detiles; }
|
||||
PackedVector2Array get_texture_displacements() const { return _texture_displacements; }
|
||||
void clear_textures(const bool p_update = false);
|
||||
void update_texture_list();
|
||||
|
||||
|
|
|
|||
|
|
@ -580,6 +580,9 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_
|
|||
if (_terrain->get_collision_mode() == Terrain3DCollision::DYNAMIC_EDITOR) {
|
||||
_terrain->get_collision()->update(true);
|
||||
}
|
||||
if (_tool == HEIGHT || _tool == SCULPT || _tool == TEXTURE || _tool == AUTOSHADER) {
|
||||
_terrain->snap();
|
||||
}
|
||||
}
|
||||
|
||||
void Terrain3DEditor::_store_undo() {
|
||||
|
|
@ -896,7 +899,7 @@ void Terrain3DEditor::set_tool(const Tool p_tool) {
|
|||
Tool old_tool = _tool;
|
||||
SET_IF_DIFF(_tool, CLAMP(p_tool, Tool(0), TOOL_MAX));
|
||||
if (_terrain && (_tool == Tool::NAVIGATION || old_tool == Tool::NAVIGATION)) {
|
||||
_terrain->get_material()->update();
|
||||
_terrain->get_material()->update(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ void Terrain3DMaterial::_preload_shaders() {
|
|||
#include "shaders/overlays.glsl"
|
||||
, "overlays");
|
||||
_parse_shader(
|
||||
#include "shaders/displacement.glsl"
|
||||
, "displacement");
|
||||
_parse_shader(
|
||||
#include "shaders/debug_views.glsl"
|
||||
, "debug_views");
|
||||
_parse_shader(
|
||||
|
|
@ -49,6 +52,9 @@ void Terrain3DMaterial::_preload_shaders() {
|
|||
_shader_code["main"] = String(
|
||||
#include "shaders/main.glsl"
|
||||
);
|
||||
_shader_code["displacement_buffer"] = String(
|
||||
#include "shaders/displacement_buffer.glsl"
|
||||
);
|
||||
|
||||
if (Terrain3D::debug_level >= DEBUG) {
|
||||
Array keys = _shader_code.keys();
|
||||
|
|
@ -158,6 +164,11 @@ String Terrain3DMaterial::_generate_shader_code() const {
|
|||
excludes.push_back("DUAL_SCALING_MIX");
|
||||
excludes.push_back("TRI_SCALING");
|
||||
}
|
||||
if (_terrain->get_tessellation_level() == 0) {
|
||||
excludes.push_back("DISPLACEMENT_UNIFORMS");
|
||||
excludes.push_back("DISPLACEMENT_FUNCTIONS");
|
||||
excludes.push_back("DISPLACEMENT_VERTEX");
|
||||
}
|
||||
String shader = _apply_inserts(_shader_code["main"], excludes);
|
||||
return shader;
|
||||
}
|
||||
|
|
@ -252,6 +263,24 @@ String Terrain3DMaterial::_strip_comments(const String &p_shader) const {
|
|||
return vector_to_string(stripped);
|
||||
}
|
||||
|
||||
String Terrain3DMaterial::_generate_buffer_shader_code() {
|
||||
LOG(INFO, "Generating default displacement buffer shader code");
|
||||
Array excludes;
|
||||
if (_texture_filtering == LINEAR) {
|
||||
excludes.push_back("TEXTURE_SAMPLERS_NEAREST");
|
||||
excludes.push_back("NOISE_SAMPLER_NEAREST");
|
||||
} else {
|
||||
excludes.push_back("TEXTURE_SAMPLERS_LINEAR");
|
||||
excludes.push_back("NOISE_SAMPLER_LINEAR");
|
||||
}
|
||||
if (!_auto_shader) {
|
||||
excludes.push_back("AUTO_SHADER_UNIFORMS");
|
||||
excludes.push_back("AUTO_SHADER");
|
||||
}
|
||||
String shader = _apply_inserts(_shader_code["displacement_buffer"], excludes);
|
||||
return shader;
|
||||
}
|
||||
|
||||
String Terrain3DMaterial::_inject_editor_code(const String &p_shader) const {
|
||||
String shader = _strip_comments(p_shader);
|
||||
|
||||
|
|
@ -376,6 +405,9 @@ String Terrain3DMaterial::_inject_editor_code(const String &p_shader) const {
|
|||
if (_pbr_view_tex_rough) {
|
||||
insert_names.push_back("PBR_TEXTURE_ROUGHNESS");
|
||||
}
|
||||
if (_debug_view_displacement_buffer) {
|
||||
insert_names.push_back("DEBUG_DISPLACEMENT_BUFFER");
|
||||
}
|
||||
// Overlays
|
||||
if (_show_contours) {
|
||||
insert_names.push_back("OVERLAY_CONTOURS_RENDER");
|
||||
|
|
@ -412,6 +444,7 @@ void Terrain3DMaterial::_update_shader() {
|
|||
Ref<RegEx> regex;
|
||||
Ref<RegExMatch> match;
|
||||
regex.instantiate();
|
||||
// Terrain Material
|
||||
if (_shader_override_enabled && _shader_override.is_valid()) {
|
||||
if (_shader_override->get_code().is_empty()) {
|
||||
_shader_override->set_code(_generate_shader_code());
|
||||
|
|
@ -428,6 +461,28 @@ void Terrain3DMaterial::_update_shader() {
|
|||
RS->material_set_shader(_material, get_shader_rid());
|
||||
LOG(DEBUG, "Material rid: ", _material, ", shader rid: ", get_shader_rid());
|
||||
|
||||
// Displacement Buffer
|
||||
if (_terrain->get_tessellation_level() > 0) {
|
||||
if (_buffer_shader_override_enabled && _buffer_shader_override.is_valid()) {
|
||||
if (_buffer_shader_override->get_code().is_empty()) {
|
||||
_buffer_shader_override->set_code(_generate_buffer_shader_code());
|
||||
}
|
||||
code = _buffer_shader_override->get_code();
|
||||
if (!_buffer_shader_override->is_connected("changed", callable_mp(this, &Terrain3DMaterial::_update_shader))) {
|
||||
LOG(DEBUG, "Connecting changed signal to _update_shader()");
|
||||
_buffer_shader_override->connect("changed", callable_mp(this, &Terrain3DMaterial::_update_shader));
|
||||
}
|
||||
} else {
|
||||
code = _generate_buffer_shader_code();
|
||||
}
|
||||
_buffer_shader->set_code(code);
|
||||
RS->material_set_shader(_buffer_material, get_buffer_shader_rid());
|
||||
LOG(DEBUG, "Buffer Material rid: ", _buffer_material, ", buffer shader rid: ", get_buffer_shader_rid());
|
||||
} else {
|
||||
_buffer_shader->set_code(String("shader_type spatial;"));
|
||||
RS->material_set_shader(_buffer_material, RID());
|
||||
}
|
||||
|
||||
// Update custom shader params in RenderingServer
|
||||
{
|
||||
// Populate _active_params
|
||||
|
|
@ -444,11 +499,14 @@ void Terrain3DMaterial::_update_shader() {
|
|||
Ref<Texture> tex = value;
|
||||
if (tex.is_valid()) {
|
||||
RS->material_set_param(_material, param, tex->get_rid());
|
||||
RS->material_set_param(_buffer_material, param, tex->get_rid());
|
||||
} else {
|
||||
RS->material_set_param(_material, param, Variant());
|
||||
RS->material_set_param(_buffer_material, param, Variant());
|
||||
}
|
||||
} else {
|
||||
RS->material_set_param(_material, param, value);
|
||||
RS->material_set_param(_buffer_material, param, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -494,9 +552,9 @@ void Terrain3DMaterial::_update_shader() {
|
|||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
void Terrain3DMaterial::_update_maps() {
|
||||
void Terrain3DMaterial::_update_uniforms(const RID &p_material) {
|
||||
IS_DATA_INIT(VOID);
|
||||
LOG(EXTREME, "Updating maps in shader");
|
||||
LOG(EXTREME, "Updating uniforms in shader");
|
||||
|
||||
Terrain3DData *data = _terrain->get_data();
|
||||
PackedInt32Array region_map = data->get_region_map();
|
||||
|
|
@ -505,8 +563,8 @@ void Terrain3DMaterial::_update_maps() {
|
|||
LOG(ERROR, "Expected region_map.size() of ", Terrain3DData::REGION_MAP_SIZE * Terrain3DData::REGION_MAP_SIZE);
|
||||
return;
|
||||
}
|
||||
RS->material_set_param(_material, "_region_map", region_map);
|
||||
RS->material_set_param(_material, "_region_map_size", Terrain3DData::REGION_MAP_SIZE);
|
||||
RS->material_set_param(p_material, "_region_map", region_map);
|
||||
RS->material_set_param(p_material, "_region_map_size", Terrain3DData::REGION_MAP_SIZE);
|
||||
if (Terrain3D::debug_level >= EXTREME) {
|
||||
LOG(EXTREME, "Region map");
|
||||
for (int i = 0; i < region_map.size(); i++) {
|
||||
|
|
@ -518,49 +576,53 @@ void Terrain3DMaterial::_update_maps() {
|
|||
|
||||
TypedArray<Vector2i> region_locations = data->get_region_locations();
|
||||
LOG(EXTREME, "Region_locations size: ", region_locations.size(), " ", region_locations);
|
||||
RS->material_set_param(_material, "_region_locations", region_locations);
|
||||
RS->material_set_param(p_material, "_region_locations", region_locations);
|
||||
|
||||
real_t region_size = real_t(_terrain->get_region_size());
|
||||
LOG(EXTREME, "Setting region size in material: ", region_size);
|
||||
RS->material_set_param(_material, "_region_size", region_size);
|
||||
RS->material_set_param(_material, "_region_texel_size", 1.0f / region_size);
|
||||
RS->material_set_param(p_material, "_region_size", region_size);
|
||||
RS->material_set_param(p_material, "_region_texel_size", 1.0f / region_size);
|
||||
|
||||
RS->material_set_param(_material, "_height_maps", data->get_height_maps_rid());
|
||||
RS->material_set_param(_material, "_control_maps", data->get_control_maps_rid());
|
||||
RS->material_set_param(_material, "_color_maps", data->get_color_maps_rid());
|
||||
RS->material_set_param(p_material, "_height_maps", data->get_height_maps_rid());
|
||||
RS->material_set_param(p_material, "_control_maps", data->get_control_maps_rid());
|
||||
RS->material_set_param(p_material, "_color_maps", data->get_color_maps_rid());
|
||||
LOG(EXTREME, "Height map RID: ", data->get_height_maps_rid());
|
||||
LOG(EXTREME, "Control map RID: ", data->get_control_maps_rid());
|
||||
LOG(EXTREME, "Color map RID: ", data->get_color_maps_rid());
|
||||
|
||||
real_t spacing = _terrain->get_vertex_spacing();
|
||||
LOG(EXTREME, "Setting vertex spacing in material: ", spacing);
|
||||
RS->material_set_param(_material, "_vertex_spacing", spacing);
|
||||
RS->material_set_param(_material, "_vertex_density", 1.0f / spacing);
|
||||
RS->material_set_param(p_material, "_vertex_spacing", spacing);
|
||||
RS->material_set_param(p_material, "_vertex_density", 1.0f / spacing);
|
||||
|
||||
real_t mesh_size = real_t(_terrain->get_mesh_size());
|
||||
RS->material_set_param(_material, "_mesh_size", mesh_size);
|
||||
}
|
||||
RS->material_set_param(p_material, "_mesh_size", mesh_size);
|
||||
|
||||
real_t tessellation_level = real_t(_terrain->get_tessellation_level());
|
||||
real_t subdiv = pow(2.f, tessellation_level);
|
||||
RS->material_set_param(p_material, "_subdiv", subdiv);
|
||||
RS->material_set_param(p_material, "_tessellation_level", tessellation_level);
|
||||
RS->material_set_param(p_material, "_displacement_scale", _displacement_scale);
|
||||
RS->material_set_param(p_material, "_displacement_sharpness", _displacement_sharpness);
|
||||
|
||||
// Called from signal connected in Terrain3D, emitted by texture_list
|
||||
void Terrain3DMaterial::_update_texture_arrays() {
|
||||
IS_DATA_INIT(VOID);
|
||||
Ref<Terrain3DAssets> asset_list = _terrain->get_assets();
|
||||
LOG(INFO, "Updating texture arrays in shader");
|
||||
if (asset_list.is_null() || !asset_list->is_initialized()) {
|
||||
LOG(ERROR, "Asset list is not initialized");
|
||||
LOG(INFO, "Asset list is not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
RS->material_set_param(_material, "_texture_array_albedo", asset_list->get_albedo_array_rid());
|
||||
RS->material_set_param(_material, "_texture_array_normal", asset_list->get_normal_array_rid());
|
||||
RS->material_set_param(_material, "_texture_color_array", asset_list->get_texture_colors());
|
||||
RS->material_set_param(_material, "_texture_normal_depth_array", asset_list->get_texture_normal_depths());
|
||||
RS->material_set_param(_material, "_texture_ao_strength_array", asset_list->get_texture_ao_strengths());
|
||||
RS->material_set_param(_material, "_texture_ao_affect_array", asset_list->get_texture_ao_light_affects());
|
||||
RS->material_set_param(_material, "_texture_roughness_mod_array", asset_list->get_texture_roughness_mods());
|
||||
RS->material_set_param(_material, "_texture_uv_scale_array", asset_list->get_texture_uv_scales());
|
||||
RS->material_set_param(_material, "_texture_vertical_projections", asset_list->get_texture_vertical_projections());
|
||||
RS->material_set_param(_material, "_texture_detile_array", asset_list->get_texture_detiles());
|
||||
RS->material_set_param(p_material, "_texture_array_albedo", asset_list->get_albedo_array_rid());
|
||||
RS->material_set_param(p_material, "_texture_array_normal", asset_list->get_normal_array_rid());
|
||||
RS->material_set_param(p_material, "_texture_color_array", asset_list->get_texture_colors());
|
||||
RS->material_set_param(p_material, "_texture_normal_depth_array", asset_list->get_texture_normal_depths());
|
||||
RS->material_set_param(p_material, "_texture_ao_strength_array", asset_list->get_texture_ao_strengths());
|
||||
RS->material_set_param(p_material, "_texture_ao_affect_array", asset_list->get_texture_ao_light_affects());
|
||||
RS->material_set_param(p_material, "_texture_roughness_mod_array", asset_list->get_texture_roughness_mods());
|
||||
RS->material_set_param(p_material, "_texture_uv_scale_array", asset_list->get_texture_uv_scales());
|
||||
RS->material_set_param(p_material, "_texture_vertical_projections", asset_list->get_texture_vertical_projections());
|
||||
RS->material_set_param(p_material, "_texture_detile_array", asset_list->get_texture_detiles());
|
||||
RS->material_set_param(p_material, "_texture_displacement_array", asset_list->get_texture_displacements());
|
||||
|
||||
// Enable checkered view if texture_count is 0, disable if not
|
||||
if (asset_list->get_texture_count() == 0) {
|
||||
|
|
@ -598,9 +660,12 @@ void Terrain3DMaterial::initialize(Terrain3D *p_terrain) {
|
|||
if (!_material.is_valid()) {
|
||||
_material = RS->material_create();
|
||||
}
|
||||
if (!_buffer_material.is_valid()) {
|
||||
_buffer_material = RS->material_create();
|
||||
}
|
||||
_shader.instantiate();
|
||||
_update_shader();
|
||||
_update_maps();
|
||||
_buffer_shader.instantiate();
|
||||
update(true);
|
||||
}
|
||||
|
||||
void Terrain3DMaterial::uninitialize() {
|
||||
|
|
@ -612,6 +677,7 @@ void Terrain3DMaterial::destroy() {
|
|||
LOG(INFO, "Destroying material");
|
||||
_terrain = nullptr;
|
||||
_shader.unref();
|
||||
_buffer_shader.unref();
|
||||
_shader_code.clear();
|
||||
_active_params.clear();
|
||||
_shader_params.clear();
|
||||
|
|
@ -619,11 +685,38 @@ void Terrain3DMaterial::destroy() {
|
|||
RS->free_rid(_material);
|
||||
_material = RID();
|
||||
}
|
||||
if (_buffer_material.is_valid()) {
|
||||
RS->free_rid(_buffer_material);
|
||||
_buffer_material = RID();
|
||||
}
|
||||
}
|
||||
|
||||
void Terrain3DMaterial::update() {
|
||||
_update_shader();
|
||||
_update_maps();
|
||||
void Terrain3DMaterial::update(bool p_full) {
|
||||
if (p_full) {
|
||||
_update_shader();
|
||||
}
|
||||
_update_uniforms(_material);
|
||||
IS_INIT(VOID);
|
||||
if (_terrain->get_tessellation_level() > 0) {
|
||||
_update_uniforms(_buffer_material);
|
||||
// Snap to update buffer
|
||||
_terrain->snap();
|
||||
}
|
||||
}
|
||||
|
||||
void Terrain3DMaterial::set_displacement_scale(const real_t p_displacement_scale) {
|
||||
SET_IF_DIFF(_displacement_scale, CLAMP(p_displacement_scale, 0.f, 2.f));
|
||||
LOG(INFO, "Setting displacement scale: ", p_displacement_scale);
|
||||
update();
|
||||
}
|
||||
|
||||
void Terrain3DMaterial::set_displacement_sharpness(const real_t p_displacement_sharpness) {
|
||||
SET_IF_DIFF(_displacement_sharpness, CLAMP(p_displacement_sharpness, 0.f, 1.f));
|
||||
LOG(INFO, "Setting displacement sharpness: ", p_displacement_sharpness);
|
||||
update();
|
||||
if (_terrain) {
|
||||
_terrain->snap();
|
||||
}
|
||||
}
|
||||
|
||||
void Terrain3DMaterial::set_world_background(const WorldBackground p_background) {
|
||||
|
|
@ -666,6 +759,22 @@ void Terrain3DMaterial::set_shader_override(const Ref<Shader> &p_shader) {
|
|||
_update_shader();
|
||||
}
|
||||
|
||||
void Terrain3DMaterial::set_buffer_shader_override_enabled(const bool p_enabled) {
|
||||
LOG(INFO, "Enable shader override: ", p_enabled);
|
||||
_buffer_shader_override_enabled = p_enabled;
|
||||
if (_buffer_shader_override_enabled && _buffer_shader_override.is_null()) {
|
||||
LOG(DEBUG, "Instantiating new _shader_override");
|
||||
_buffer_shader_override.instantiate();
|
||||
}
|
||||
_update_shader();
|
||||
}
|
||||
|
||||
void Terrain3DMaterial::set_buffer_shader_override(const Ref<Shader> &p_shader) {
|
||||
LOG(INFO, "Setting override shader");
|
||||
_buffer_shader_override = p_shader;
|
||||
_update_shader();
|
||||
}
|
||||
|
||||
void Terrain3DMaterial::set_shader_param(const StringName &p_name, const Variant &p_value) {
|
||||
LOG(INFO, "Setting shader parameter: ", p_name);
|
||||
_set(p_name, p_value);
|
||||
|
|
@ -797,6 +906,11 @@ void Terrain3DMaterial::set_show_texture_rough(const bool p_enabled) {
|
|||
LOG(INFO, "Enable show_texture_rough: ", p_enabled);
|
||||
_update_shader();
|
||||
}
|
||||
void Terrain3DMaterial::set_show_displacement_buffer(const bool p_enabled) {
|
||||
LOG(INFO, "Enable show_texture_rough: ", p_enabled);
|
||||
_debug_view_displacement_buffer = p_enabled;
|
||||
_update_shader();
|
||||
}
|
||||
|
||||
void Terrain3DMaterial::set_show_texture_ao(const bool p_enabled) {
|
||||
SET_IF_DIFF(_pbr_view_tex_ao, p_enabled);
|
||||
|
|
@ -821,6 +935,15 @@ Error Terrain3DMaterial::save(const String &p_path) {
|
|||
if (_shader_override.is_valid()) {
|
||||
param_list.append_array(_shader_override->get_shader_uniform_list(true));
|
||||
}
|
||||
if (_buffer_shader_override.is_valid()) {
|
||||
// Get shader parameters from custom buffer shader
|
||||
param_list.append_array(_buffer_shader_override->get_shader_uniform_list(true));
|
||||
} else {
|
||||
if (_terrain->get_tessellation_level() > 0) {
|
||||
// Get shader parameters from default buffer shader
|
||||
param_list.append_array(RS->get_shader_parameter_list(get_buffer_shader_rid()));
|
||||
}
|
||||
}
|
||||
|
||||
// Remove saved shader params that don't exist in either shader
|
||||
Array keys = _shader_params.keys();
|
||||
|
|
@ -870,29 +993,61 @@ void Terrain3DMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
|
|||
// Get shader parameters from default shader (eg world_noise)
|
||||
param_list = RS->get_shader_parameter_list(get_shader_rid());
|
||||
}
|
||||
int buffer_param = param_list.size();
|
||||
if (_buffer_shader_override_enabled && _buffer_shader_override.is_valid()) {
|
||||
// Get shader parameters from custom shader
|
||||
param_list.append_array(_buffer_shader_override->get_shader_uniform_list(true));
|
||||
} else {
|
||||
if (_terrain->get_tessellation_level() > 0) {
|
||||
// Get shader parameters from default shader (eg world_noise)
|
||||
param_list.append_array(RS->get_shader_parameter_list(get_buffer_shader_rid()));
|
||||
}
|
||||
}
|
||||
|
||||
_active_params.clear();
|
||||
for (const Dictionary &dict : param_list) {
|
||||
Dictionary grouped_params;
|
||||
StringName current_group = StringName("General");
|
||||
grouped_params[current_group] = Array();
|
||||
for (int i = 0; i < param_list.size(); i++) {
|
||||
Dictionary dict = param_list[i];
|
||||
StringName name = dict["name"];
|
||||
|
||||
// Filter out private uniforms that start with _
|
||||
// An empty name indicates a group being closed, reset to the "general" group.
|
||||
if (name.is_empty() && i < buffer_param) {
|
||||
current_group = StringName("General");
|
||||
}
|
||||
|
||||
// Filter out private uniforms that start with _ and nulls
|
||||
if (!name.begins_with("_") && !name.is_empty()) {
|
||||
// Populate Godot's property list
|
||||
PropertyInfo pi;
|
||||
uint64_t use = dict["usage"];
|
||||
if (use == PROPERTY_USAGE_GROUP) {
|
||||
PackedStringArray split_name = name.split("::");
|
||||
pi.name = split_name[MAX(split_name.size() - 1, 0)].capitalize();
|
||||
pi.usage = (name.contains("::") ? PROPERTY_USAGE_SUBGROUP : PROPERTY_USAGE_GROUP) | PROPERTY_USAGE_EDITOR;
|
||||
dict["name"] = split_name[MAX(split_name.size() - 1, 0)].capitalize();
|
||||
// Ensure sub groups are batched with their parent group
|
||||
current_group = split_name[0].capitalize();
|
||||
dict["usage"] = (name.contains("::") ? PROPERTY_USAGE_SUBGROUP : PROPERTY_USAGE_GROUP) | PROPERTY_USAGE_EDITOR;
|
||||
} else {
|
||||
pi.name = name;
|
||||
pi.usage = PROPERTY_USAGE_EDITOR;
|
||||
// Filter out duplicate non-groups entries from displacement buffer shader
|
||||
if (_active_params.has(name)) {
|
||||
continue;
|
||||
}
|
||||
dict["usage"] = PROPERTY_USAGE_EDITOR;
|
||||
}
|
||||
pi.class_name = dict["class_name"];
|
||||
pi.type = Variant::Type(int(dict["type"]));
|
||||
pi.hint = dict["hint"];
|
||||
pi.hint_string = dict["hint_string"];
|
||||
p_list->push_back(pi);
|
||||
// Filter out extraneous parameters from the inspector if they are not present in the terrain shader.
|
||||
if (i >= buffer_param && (!_active_params.has(name) && use != PROPERTY_USAGE_GROUP) && !current_group.contains("Displacement")) {
|
||||
LOG(INFO, "Displacement buffer has active parameter: ", name, " not present in terrain shader.");
|
||||
continue;
|
||||
}
|
||||
// Write dict to grouped arrays to ensure property list groups are populated contigiously.
|
||||
Array group;
|
||||
if (grouped_params.has(current_group)) {
|
||||
group = grouped_params[current_group];
|
||||
} else {
|
||||
grouped_params[current_group] = Array();
|
||||
group = grouped_params[current_group];
|
||||
}
|
||||
group.push_back(dict);
|
||||
grouped_params[current_group] = group;
|
||||
|
||||
// Populate list of public parameters for current shader
|
||||
_active_params.push_back(name);
|
||||
|
|
@ -907,6 +1062,24 @@ void Terrain3DMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate Godot's property list
|
||||
Array keys = grouped_params.keys();
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
StringName key = keys[i];
|
||||
Array group = grouped_params[key];
|
||||
for (int k = 0; k < group.size(); k++) {
|
||||
Dictionary dict = group[k];
|
||||
PropertyInfo pi;
|
||||
pi.class_name = dict["class_name"];
|
||||
pi.name = dict["name"];
|
||||
pi.type = Variant::Type(int(dict["type"]));
|
||||
pi.hint = dict["hint"];
|
||||
pi.hint_string = dict["hint_string"];
|
||||
pi.usage = dict["usage"];
|
||||
p_list->push_back(pi);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -922,7 +1095,12 @@ bool Terrain3DMaterial::_property_can_revert(const StringName &p_name) const {
|
|||
// Provide uniform default values in r_property
|
||||
bool Terrain3DMaterial::_property_get_revert(const StringName &p_name, Variant &r_property) const {
|
||||
IS_INIT_COND(!_active_params.has(p_name), Resource::_property_get_revert(p_name, r_property));
|
||||
r_property = RS->shader_get_parameter_default(get_shader_rid(), p_name);
|
||||
Variant prop = RS->shader_get_parameter_default(get_shader_rid(), p_name);
|
||||
// If the default is nil, check the buffer shader for the default value.
|
||||
if (prop.get_type() == Variant::NIL) {
|
||||
prop = RS->shader_get_parameter_default(get_buffer_shader_rid(), p_name);
|
||||
}
|
||||
r_property = prop;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -941,12 +1119,15 @@ bool Terrain3DMaterial::_set(const StringName &p_name, const Variant &p_property
|
|||
if (tex.is_valid()) {
|
||||
_shader_params[p_name] = tex;
|
||||
RS->material_set_param(_material, p_name, tex->get_rid());
|
||||
RS->material_set_param(_buffer_material, p_name, tex->get_rid());
|
||||
} else {
|
||||
RS->material_set_param(_material, p_name, Variant());
|
||||
RS->material_set_param(_buffer_material, p_name, Variant());
|
||||
}
|
||||
} else {
|
||||
_shader_params[p_name] = p_property;
|
||||
RS->material_set_param(_material, p_name, p_property);
|
||||
RS->material_set_param(_buffer_material, p_name, p_property);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -978,9 +1159,11 @@ void Terrain3DMaterial::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_shader_parameters", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "_set_shader_parameters", "_get_shader_parameters");
|
||||
|
||||
// Public
|
||||
ClassDB::bind_method(D_METHOD("update"), &Terrain3DMaterial::update);
|
||||
ClassDB::bind_method(D_METHOD("update", "full"), &Terrain3DMaterial::update, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("get_material_rid"), &Terrain3DMaterial::get_material_rid);
|
||||
ClassDB::bind_method(D_METHOD("get_shader_rid"), &Terrain3DMaterial::get_shader_rid);
|
||||
ClassDB::bind_method(D_METHOD("get_buffer_material_rid"), &Terrain3DMaterial::get_buffer_material_rid);
|
||||
ClassDB::bind_method(D_METHOD("get_buffer_shader_rid"), &Terrain3DMaterial::get_buffer_shader_rid);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_world_background", "background"), &Terrain3DMaterial::set_world_background);
|
||||
ClassDB::bind_method(D_METHOD("get_world_background"), &Terrain3DMaterial::get_world_background);
|
||||
|
|
@ -996,6 +1179,15 @@ void Terrain3DMaterial::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_shader_override", "shader"), &Terrain3DMaterial::set_shader_override);
|
||||
ClassDB::bind_method(D_METHOD("get_shader_override"), &Terrain3DMaterial::get_shader_override);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_buffer_shader_override_enabled", "enabled"), &Terrain3DMaterial::set_buffer_shader_override_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_buffer_shader_override_enabled"), &Terrain3DMaterial::is_buffer_shader_override_enabled);
|
||||
ClassDB::bind_method(D_METHOD("set_buffer_shader_override", "shader"), &Terrain3DMaterial::set_buffer_shader_override);
|
||||
ClassDB::bind_method(D_METHOD("get_buffer_shader_override"), &Terrain3DMaterial::get_buffer_shader_override);
|
||||
ClassDB::bind_method(D_METHOD("set_displacement_scale", "scale"), &Terrain3DMaterial::set_displacement_scale);
|
||||
ClassDB::bind_method(D_METHOD("get_displacement_scale"), &Terrain3DMaterial::get_displacement_scale);
|
||||
ClassDB::bind_method(D_METHOD("set_displacement_sharpness", "sharpness"), &Terrain3DMaterial::set_displacement_sharpness);
|
||||
ClassDB::bind_method(D_METHOD("get_displacement_sharpness"), &Terrain3DMaterial::get_displacement_sharpness);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_shader_param", "name", "value"), &Terrain3DMaterial::set_shader_param);
|
||||
ClassDB::bind_method(D_METHOD("get_shader_param", "name"), &Terrain3DMaterial::get_shader_param);
|
||||
|
||||
|
|
@ -1046,6 +1238,8 @@ void Terrain3DMaterial::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_show_texture_ao"), &Terrain3DMaterial::get_show_texture_ao);
|
||||
ClassDB::bind_method(D_METHOD("set_show_texture_rough", "enabled"), &Terrain3DMaterial::set_show_texture_rough);
|
||||
ClassDB::bind_method(D_METHOD("get_show_texture_rough"), &Terrain3DMaterial::get_show_texture_rough);
|
||||
ClassDB::bind_method(D_METHOD("set_show_displacement_buffer", "enabled"), &Terrain3DMaterial::set_show_displacement_buffer);
|
||||
ClassDB::bind_method(D_METHOD("get_show_displacement_buffer"), &Terrain3DMaterial::get_show_displacement_buffer);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("save", "path"), &Terrain3DMaterial::save, DEFVAL(""));
|
||||
|
||||
|
|
@ -1066,6 +1260,11 @@ void Terrain3DMaterial::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_navigation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_show_navigation", "get_show_navigation");
|
||||
|
||||
// Hidden in Material, aliased in Terrain3D
|
||||
// Displacement settings
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "displacement_scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_displacement_scale", "get_displacement_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "displacement_sharpness", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_displacement_sharpness", "get_displacement_sharpness");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "buffer_shader_override_enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_buffer_shader_override_enabled", "is_buffer_shader_override_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "buffer_shader_override", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_buffer_shader_override", "get_buffer_shader_override");
|
||||
//ADD_GROUP("Debug Views", "show_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_checkered", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_show_checkered", "get_show_checkered");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_grey", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_show_grey", "get_show_grey");
|
||||
|
|
@ -1086,4 +1285,5 @@ void Terrain3DMaterial::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_texture_normal", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_show_texture_normal", "get_show_texture_normal");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_texture_ao", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_show_texture_ao", "get_show_texture_ao");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_texture_rough", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_show_texture_rough", "get_show_texture_rough");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_displacement_buffer", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_show_displacement_buffer", "get_show_displacement_buffer");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ class Terrain3D;
|
|||
class Terrain3DMaterial : public Resource {
|
||||
GDCLASS(Terrain3DMaterial, Resource);
|
||||
CLASS_NAME();
|
||||
friend class Terrain3D;
|
||||
|
||||
public: // Constants
|
||||
enum WorldBackground {
|
||||
|
|
@ -38,6 +37,13 @@ private:
|
|||
mutable TypedArray<StringName> _active_params; // All shader params in the current shader
|
||||
mutable Dictionary _shader_params; // Public shader params saved to disk
|
||||
|
||||
RID _buffer_material;
|
||||
Ref<Shader> _buffer_shader; // Active buffer shader
|
||||
bool _buffer_shader_override_enabled = false;
|
||||
Ref<Shader> _buffer_shader_override; // User's shader we copy code from
|
||||
real_t _displacement_scale = 1.0f;
|
||||
real_t _displacement_sharpness = 0.5f;
|
||||
|
||||
// Material Features
|
||||
WorldBackground _world_background = FLAT;
|
||||
TextureFiltering _texture_filtering = LINEAR;
|
||||
|
|
@ -64,6 +70,7 @@ private:
|
|||
bool _debug_view_holes = false;
|
||||
bool _debug_view_colormap = false;
|
||||
bool _debug_view_roughmap = false;
|
||||
bool _debug_view_displacement_buffer = false;
|
||||
|
||||
// PBR Views
|
||||
bool _pbr_view_tex_albedo = false;
|
||||
|
|
@ -77,11 +84,11 @@ private:
|
|||
void _parse_shader(const String &p_shader, const String &p_name);
|
||||
String _apply_inserts(const String &p_shader, const Array &p_excludes = Array()) const;
|
||||
String _generate_shader_code() const;
|
||||
String _generate_buffer_shader_code();
|
||||
String _strip_comments(const String &p_shader) const;
|
||||
String _inject_editor_code(const String &p_shader) const;
|
||||
void _update_shader();
|
||||
void _update_maps();
|
||||
void _update_texture_arrays();
|
||||
void _update_uniforms(const RID &p_material);
|
||||
void _set_shader_parameters(const Dictionary &p_dict);
|
||||
Dictionary _get_shader_parameters() const { return _shader_params; }
|
||||
|
||||
|
|
@ -93,11 +100,18 @@ public:
|
|||
void uninitialize();
|
||||
void destroy();
|
||||
|
||||
void update();
|
||||
void update(bool p_full = false);
|
||||
RID get_material_rid() const { return _material; }
|
||||
RID get_shader_rid() const { return _shader.is_valid() ? _shader->get_rid() : RID(); }
|
||||
|
||||
RID get_buffer_material_rid() const { return _buffer_material; }
|
||||
RID get_buffer_shader_rid() const { return _buffer_shader.is_valid() ? _buffer_shader->get_rid() : RID(); }
|
||||
|
||||
// Material settings
|
||||
void set_displacement_scale(const real_t p_displacement_scale);
|
||||
real_t get_displacement_scale() const { return _displacement_scale; }
|
||||
void set_displacement_sharpness(const real_t p_displacement_sharpness);
|
||||
real_t get_displacement_sharpness() const { return _displacement_sharpness; }
|
||||
void set_world_background(const WorldBackground p_background);
|
||||
WorldBackground get_world_background() const { return _world_background; }
|
||||
void set_texture_filtering(const TextureFiltering p_filtering);
|
||||
|
|
@ -112,6 +126,11 @@ public:
|
|||
void set_shader_override(const Ref<Shader> &p_shader);
|
||||
Ref<Shader> get_shader_override() const { return _shader_override; }
|
||||
|
||||
void set_buffer_shader_override_enabled(const bool p_enabled);
|
||||
bool is_buffer_shader_override_enabled() const { return _buffer_shader_override_enabled; }
|
||||
void set_buffer_shader_override(const Ref<Shader> &p_shader);
|
||||
Ref<Shader> get_buffer_shader_override() const { return _buffer_shader_override; }
|
||||
|
||||
void set_shader_param(const StringName &p_name, const Variant &p_value);
|
||||
Variant get_shader_param(const StringName &p_name) const;
|
||||
|
||||
|
|
@ -150,6 +169,8 @@ public:
|
|||
bool get_show_colormap() const { return _debug_view_colormap; }
|
||||
void set_show_roughmap(const bool p_enabled);
|
||||
bool get_show_roughmap() const { return _debug_view_roughmap; }
|
||||
void set_show_displacement_buffer(const bool p_enabled);
|
||||
bool get_show_displacement_buffer() const { return _debug_view_displacement_buffer; }
|
||||
|
||||
// PBR Views
|
||||
void set_show_texture_albedo(const bool p_enabled);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ RID Terrain3DMesher::_generate_mesh(const Vector2i &p_size, const bool p_standar
|
|||
PackedVector3Array vertices;
|
||||
PackedInt32Array indices;
|
||||
AABB aabb = AABB(V3_ZERO, Vector3(p_size.x, 0.1f, p_size.y));
|
||||
LOG(DEBUG, "Generating verticies and indices for a", p_standard_grid ? " symetric " : " standard ", "grid mesh of width: ", p_size.x, " and height: ", p_size.y);
|
||||
LOG(DEBUG, "Generating verticies and indices for a", p_standard_grid ? " symmetric " : " standard ", "grid mesh of width: ", p_size.x, " and height: ", p_size.y);
|
||||
|
||||
// Generate vertices
|
||||
for (int y = 0; y <= p_size.y; ++y) {
|
||||
|
|
@ -292,7 +292,7 @@ void Terrain3DMesher::initialize(Terrain3D *p_terrain) {
|
|||
}
|
||||
LOG(INFO, "Initializing GeoMesh");
|
||||
int size = _terrain->get_mesh_size();
|
||||
int lods = _terrain->get_mesh_lods();
|
||||
int lods = _terrain->get_mesh_lods() + _terrain->get_tessellation_level();
|
||||
_generate_clipmap(size, lods, _terrain->get_world_3d()->get_scenario());
|
||||
update();
|
||||
update_aabbs();
|
||||
|
|
@ -314,18 +314,21 @@ void Terrain3DMesher::destroy() {
|
|||
|
||||
void Terrain3DMesher::snap() {
|
||||
IS_INIT(VOID);
|
||||
// If clipmap target has moved enough, re-center terrain on the target.
|
||||
// Always update target position in shader
|
||||
Vector3 target_pos = _terrain->get_clipmap_target_position();
|
||||
RS->material_set_param(_terrain->get_material()->get_material_rid(), "_target_pos", target_pos);
|
||||
|
||||
// If clipmap target hasn't moved enough, skip
|
||||
Vector2 target_pos_2d = v3v2(target_pos);
|
||||
if (_last_target_position.distance_squared_to(target_pos_2d) < 0.04f) {
|
||||
real_t tessellation_density = 1.f / pow(2.f, _terrain->get_tessellation_level());
|
||||
real_t vertex_spacing = _terrain->get_vertex_spacing() * tessellation_density;
|
||||
if (MAX(abs(_last_target_position.x - target_pos_2d.x), abs(_last_target_position.y - target_pos_2d.y)) < vertex_spacing) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Recenter terrain on the target
|
||||
_last_target_position = target_pos_2d;
|
||||
|
||||
real_t vertex_spacing = _terrain->get_vertex_spacing();
|
||||
Vector3 snapped_pos = (target_pos / vertex_spacing).floor() * vertex_spacing;
|
||||
RS->material_set_param(_terrain->get_material()->get_material_rid(), "_camera_pos", snapped_pos);
|
||||
|
||||
Vector3 pos = V3_ZERO;
|
||||
for (int lod = 0; lod < _clipmap_rids.size(); ++lod) {
|
||||
real_t snap_step = pow(2.f, lod + 1.f) * vertex_spacing;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ bool Terrain3DTextureAsset::_is_valid_format(const Ref<Texture2D> &p_texture) co
|
|||
LOG(DEBUG, "Provided texture is null.");
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<Image> img = p_texture->get_image();
|
||||
Image::Format format = Image::FORMAT_MAX;
|
||||
if (img.is_valid()) {
|
||||
|
|
@ -27,7 +26,6 @@ bool Terrain3DTextureAsset::_is_valid_format(const Ref<Texture2D> &p_texture) co
|
|||
LOG(ERROR, "Invalid texture format. See documentation for format specification.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -49,10 +47,13 @@ void Terrain3DTextureAsset::clear() {
|
|||
_albedo_texture.unref();
|
||||
_normal_texture.unref();
|
||||
_thumbnail.unref();
|
||||
|
||||
_normal_depth = 1.0f;
|
||||
_ao_strength = 0.5f;
|
||||
_ao_light_affect = 0.0f;
|
||||
_roughness = 0.f;
|
||||
_displacement_scale = 0.f;
|
||||
_displacement_offset = 0.f;
|
||||
_uv_scale = 0.1f;
|
||||
_vertical_projection = false;
|
||||
_detiling_rotation = 0.0f;
|
||||
|
|
@ -178,6 +179,20 @@ void Terrain3DTextureAsset::set_ao_light_affect(const real_t p_ao_light_affect)
|
|||
emit_signal("setting_changed");
|
||||
}
|
||||
|
||||
void Terrain3DTextureAsset::set_displacement_scale(const real_t p_displacement_scale) {
|
||||
SET_IF_DIFF(_displacement_scale, CLAMP(p_displacement_scale, 0.0f, 2.0f));
|
||||
LOG(INFO, "Setting displacement_scale: ", _displacement_scale);
|
||||
LOG(DEBUG, "Emitting setting_changed");
|
||||
emit_signal("setting_changed");
|
||||
}
|
||||
|
||||
void Terrain3DTextureAsset::set_displacement_offset(const real_t p_displacement_offset) {
|
||||
SET_IF_DIFF(_displacement_offset, CLAMP(p_displacement_offset, -1.0f, 1.0f));
|
||||
LOG(INFO, "Setting displacement_offset: ", _displacement_offset);
|
||||
LOG(DEBUG, "Emitting setting_changed");
|
||||
emit_signal("setting_changed");
|
||||
}
|
||||
|
||||
void Terrain3DTextureAsset::set_uv_scale(const real_t p_scale) {
|
||||
SET_IF_DIFF(_uv_scale, CLAMP(p_scale, 0.001f, 100.0f));
|
||||
LOG(INFO, "Setting uv_scale: ", _uv_scale);
|
||||
|
|
@ -238,6 +253,10 @@ void Terrain3DTextureAsset::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_ao_light_affect"), &Terrain3DTextureAsset::get_ao_light_affect);
|
||||
ClassDB::bind_method(D_METHOD("set_roughness", "roughness"), &Terrain3DTextureAsset::set_roughness);
|
||||
ClassDB::bind_method(D_METHOD("get_roughness"), &Terrain3DTextureAsset::get_roughness);
|
||||
ClassDB::bind_method(D_METHOD("set_displacement_scale", "displacement_scale"), &Terrain3DTextureAsset::set_displacement_scale);
|
||||
ClassDB::bind_method(D_METHOD("get_displacement_scale"), &Terrain3DTextureAsset::get_displacement_scale);
|
||||
ClassDB::bind_method(D_METHOD("set_displacement_offset", "displacement_offset"), &Terrain3DTextureAsset::set_displacement_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_displacement_offset"), &Terrain3DTextureAsset::get_displacement_offset);
|
||||
ClassDB::bind_method(D_METHOD("set_uv_scale", "scale"), &Terrain3DTextureAsset::set_uv_scale);
|
||||
ClassDB::bind_method(D_METHOD("get_uv_scale"), &Terrain3DTextureAsset::get_uv_scale);
|
||||
ClassDB::bind_method(D_METHOD("set_vertical_projection", "projection"), &Terrain3DTextureAsset::set_vertical_projection);
|
||||
|
|
@ -256,7 +275,10 @@ void Terrain3DTextureAsset::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ao_strength", PROPERTY_HINT_RANGE, "0.0, 1.0"), "set_ao_strength", "get_ao_strength");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ao_light_affect", PROPERTY_HINT_RANGE, "0.0, 1.0"), "set_ao_light_affect", "get_ao_light_affect");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "roughness", PROPERTY_HINT_RANGE, "-1.0, 1.0"), "set_roughness", "get_roughness");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "displacement_scale", PROPERTY_HINT_RANGE, "0.0, 2.0"), "set_displacement_scale", "get_displacement_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "displacement_offset", PROPERTY_HINT_RANGE, "-1.0, 1.0"), "set_displacement_offset", "get_displacement_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "uv_scale", PROPERTY_HINT_RANGE, "0.001, 2.0, or_greater"), "set_uv_scale", "get_uv_scale");
|
||||
ADD_GROUP("Projection", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical_projection", PROPERTY_HINT_NONE), "set_vertical_projection", "get_vertical_projection");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detiling_rotation", PROPERTY_HINT_RANGE, "0.0, 1.0"), "set_detiling_rotation", "get_detiling_rotation");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detiling_shift", PROPERTY_HINT_RANGE, "0.0, 1.0"), "set_detiling_shift", "get_detiling_shift");
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ class Terrain3DTextureAsset : public Terrain3DAssetResource {
|
|||
real_t _ao_strength = 0.5f;
|
||||
real_t _ao_light_affect = 0.0f;
|
||||
real_t _roughness = 0.f;
|
||||
real_t _displacement_scale = 0.0f;
|
||||
real_t _displacement_offset = 0.0f;
|
||||
real_t _uv_scale = 0.1f;
|
||||
bool _vertical_projection = false;
|
||||
real_t _detiling_rotation = 0.0f;
|
||||
|
|
@ -70,6 +72,12 @@ public:
|
|||
void set_roughness(const real_t p_roughness);
|
||||
real_t get_roughness() const { return _roughness; }
|
||||
|
||||
void set_displacement_scale(const real_t p_displacement_scale);
|
||||
real_t get_displacement_scale() const { return _displacement_scale; }
|
||||
|
||||
void set_displacement_offset(const real_t p_displacement_offset);
|
||||
real_t get_displacement_offset() const { return _displacement_offset; }
|
||||
|
||||
void set_uv_scale(const real_t p_scale);
|
||||
real_t get_uv_scale() const { return _uv_scale; }
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue