Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements vertex spacing (mesh density) #296

Merged
merged 1 commit into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions project/addons/terrain_3d/editor/components/point_picker.gd
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,12 @@ func add_point(p_value: Vector3) -> void:
if points.has(p_value):
return

# If manually selecting a point individually
if picking_index != -1:
points[picking_index] = p_value
picking_index = -1
else:
# Else picking a sequence of points (non-drawable)
for i in range(MAX_POINTS):
if points[i] == Vector3.ZERO:
points[i] = p_value
Expand Down
4 changes: 2 additions & 2 deletions project/addons/terrain_3d/editor/components/ui.gd
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ func update_decal() -> void:
await get_tree().create_timer(.05).timeout
decal.visible = true

decal.size = Vector3.ONE * brush_data["size"]
decal.size = Vector3.ONE * brush_data["size"] * plugin.terrain.get_mesh_vertex_spacing()
if brush_data["align_to_view"]:
var cam: Camera3D = plugin.terrain.get_camera();
if (cam):
Expand Down Expand Up @@ -311,7 +311,7 @@ func update_decal() -> void:
if point != Vector3.ZERO:
var point_decal: Decal = _get_gradient_decal(index)
point_decal.visible = true
point_decal.position = point
point_decal.position = point * plugin.terrain.get_mesh_vertex_spacing()
index += 1


Expand Down
7 changes: 5 additions & 2 deletions project/addons/terrain_3d/editor/editor.gd
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,14 @@ func _forward_3d_gui_input(p_viewport_camera: Camera3D, p_event: InputEvent) ->
var t = -Vector3(0, 1, 0).dot(camera_pos) / Vector3(0, 1, 0).dot(camera_dir)
mouse_global_position = (camera_pos + t * camera_dir)

# Update decal
## Update decal
ui.decal.global_position = mouse_global_position
ui.decal.albedo_mix = 1.0
ui.decal_timer.start()

## Incorporate vertex spacing into operations
mouse_global_position.x /= terrain.get_mesh_vertex_spacing()
mouse_global_position.z /= terrain.get_mesh_vertex_spacing()
## Update region highlight
var region_size = terrain.get_storage().get_region_size()
var region_position: Vector2 = (Vector2(mouse_global_position.x, mouse_global_position.z) / region_size).floor()
Expand Down Expand Up @@ -214,7 +217,7 @@ func update_region_grid() -> void:
region_gizmo.show_rect = editor.get_tool() == Terrain3DEditor.REGION
region_gizmo.use_secondary_color = editor.get_operation() == Terrain3DEditor.SUBTRACT
region_gizmo.region_position = current_region_position
region_gizmo.region_size = terrain.get_storage().get_region_size()
region_gizmo.region_size = terrain.get_storage().get_region_size() * terrain.get_mesh_vertex_spacing()
region_gizmo.grid = terrain.get_storage().get_region_offsets()

terrain.update_gizmos()
Expand Down
14 changes: 7 additions & 7 deletions src/shaders/debug_views.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
R"(
//INSERT: DEBUG_CHECKERED
// Show a checkered grid
vec2 __p = UV * 1.0; // scale
vec2 __p = uv * 1.0; // scale
vec2 __ddx = dFdx(__p);
vec2 __ddy = dFdy(__p);
vec2 __w = max(abs(__ddx), abs(__ddy)) + 0.01;
Expand All @@ -25,7 +25,7 @@ R"(

//INSERT: DEBUG_HEIGHTMAP
// Show heightmap
ALBEDO = vec3(smoothstep(-0.1, 2.0, 0.5 + get_height(UV2)/300.0));
ALBEDO = vec3(smoothstep(-0.1, 2.0, 0.5 + get_height(uv2)/300.0));
ROUGHNESS = 0.7;
SPECULAR = 0.;
NORMAL_MAP = vec3(0.5, 0.5, 1.0);
Expand Down Expand Up @@ -74,7 +74,7 @@ R"(
NORMAL_MAP = vec3(0.5, 0.5, 1.0);

//INSERT: DEBUG_AUTOSHADER
ivec3 __ruv = get_region_uv(floor(UV));
ivec3 __ruv = get_region_uv(floor(uv));
uint __control = texelFetch(_control_maps, __ruv, 0).r;
float __autoshader = float( bool(__control & 0x1u) || __ruv.z<0 );
ALBEDO = vec3(__autoshader);
Expand Down Expand Up @@ -113,16 +113,16 @@ R"(
float __view_distance = 300.0; // Visible distance of grid
// Draw region grid
__region_line = .1*sqrt(__camera_dist);
if (mod(__pixel_pos.x + __region_line*.5, _region_size) <= __region_line ||
mod(__pixel_pos.z + __region_line*.5, _region_size) <= __region_line ) {
if (mod(__pixel_pos.x * _mesh_vertex_density + __region_line*.5, _region_size) <= __region_line ||
mod(__pixel_pos.z * _mesh_vertex_density + __region_line*.5, _region_size) <= __region_line ) {
ALBEDO = vec3(1.);
}
vec3 __vertex_mul = vec3(0.);
vec3 __vertex_add = vec3(0.);
float __distance_factor = clamp(1.-__camera_dist/__view_distance, 0., 1.);
// Draw vertex grid
if ( mod(__pixel_pos.x + __grid_line*.5, __grid_step) < __grid_line ||
mod(__pixel_pos.z + __grid_line*.5, __grid_step) < __grid_line ) {
if ( mod(__pixel_pos.x * _mesh_vertex_density + __grid_line*.5, __grid_step) < __grid_line ||
mod(__pixel_pos.z * _mesh_vertex_density + __grid_line*.5, __grid_step) < __grid_line ) {
__vertex_mul = vec3(0.5) * __distance_factor;
}
// Draw Vertices
Expand Down
2 changes: 1 addition & 1 deletion src/shaders/editor_functions.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
R"(
//INSERT: EDITOR_NAVIGATION
// Show navigation
if(bool(texelFetch(_control_maps, get_region_uv(floor(UV)), 0).r >>1u & 0x1u)) {
if(bool(texelFetch(_control_maps, get_region_uv(floor(uv)), 0).r >>1u & 0x1u)) {
ALBEDO *= vec3(.5, .0, .85);
}

Expand Down
50 changes: 32 additions & 18 deletions src/shaders/main.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlic
// Private uniforms
uniform float _region_size = 1024.0;
uniform float _region_texel_size = 0.0009765625; // = 1/1024
uniform float _mesh_vertex_spacing = 1.0;
uniform float _mesh_vertex_density = 1.0; // = 1/_mesh_vertex_spacing
uniform int _region_map_size = 16;
uniform int _region_map[256];
uniform vec2 _region_offsets[256];
Expand Down Expand Up @@ -60,10 +62,12 @@ struct Material {
float blend;
};

varying vec3 v_vertex; // World coordinate vertex location
varying vec3 v_camera_pos;
varying float v_vertex_dist;
varying flat vec3 v_vertex; // World coordinate vertex location
varying flat vec3 v_camera_pos;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this flat?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it not? Does view space to world space transform matrix change per vertex?

Copy link
Owner

@TokisanGames TokisanGames Jan 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't really familiar with it. I see it disables interpolation, not truncates to integer, which is what it sounds like. So why not also make v_vertex and v_vertex_dist flat also? All of the varyings we're using currently are there to provide fixed vertex data. I see no visual or performance difference when I try it, but it's good to be explicit.

varying flat float v_vertex_dist;
varying flat ivec3 v_region;
varying flat vec2 v_uv_offset;
varying flat vec2 v_uv2_offset;

////////////////////////
// Vertex
Expand Down Expand Up @@ -110,7 +114,7 @@ void vertex() {
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;

// UV coordinates in world space. Values are 0 to _region_size within regions
UV = v_vertex.xz;
UV = round(v_vertex.xz * _mesh_vertex_density);
TokisanGames marked this conversation as resolved.
Show resolved Hide resolved

// Discard vertices if designated as a hole or background disabled. 1 lookup.
v_region = get_region_uv(UV);
Expand All @@ -128,6 +132,12 @@ void vertex() {
v_vertex_dist = length(v_vertex - v_camera_pos);
//INSERT: DUAL_SCALING_VERTEX
}

// Transform UVs to local to avoid poor precision during varying interpolation.
v_uv_offset = MODEL_MATRIX[3].xz * _mesh_vertex_density;
TokisanGames marked this conversation as resolved.
Show resolved Hide resolved
UV -= v_uv_offset;
v_uv2_offset = v_uv_offset * _region_texel_size;
UV2 -= v_uv2_offset;
}

////////////////////////
Expand All @@ -140,8 +150,8 @@ vec3 get_normal(vec2 uv, out vec3 tangent, out vec3 binormal) {
float right = get_height(uv + vec2(_region_texel_size, 0));
float back = get_height(uv + vec2(0, -_region_texel_size));
float front = get_height(uv + vec2(0, _region_texel_size));
vec3 horizontal = vec3(2.0, right - left, 0.0);
vec3 vertical = vec3(0.0, back - front, 2.0);
vec3 horizontal = vec3(2.0 * _mesh_vertex_spacing, right - left, 0.0);
vec3 vertical = vec3(0.0, back - front, 2.0 * _mesh_vertex_spacing);
TokisanGames marked this conversation as resolved.
Show resolved Hide resolved
vec3 normal = normalize(cross(vertical, horizontal));
normal.z *= -1.0;
tangent = cross(normal, vec3(0, 0, 1));
Expand Down Expand Up @@ -248,16 +258,20 @@ float blend_weights(float weight, float detail) {
}

void fragment() {
// Recover UVs
vec2 uv = UV + v_uv_offset;
vec2 uv2 = UV2 + v_uv2_offset;
TokisanGames marked this conversation as resolved.
Show resolved Hide resolved

// Calculate Terrain Normals. 4 lookups
vec3 w_tangent, w_binormal;
vec3 w_normal = get_normal(UV2, w_tangent, w_binormal);
vec3 w_normal = get_normal(uv2, w_tangent, w_binormal);
NORMAL = mat3(VIEW_MATRIX) * w_normal;
TANGENT = mat3(VIEW_MATRIX) * w_tangent;
BINORMAL = mat3(VIEW_MATRIX) * w_binormal;

// Idenfity 4 vertices surrounding this pixel
vec2 texel_pos = UV;
highp vec2 texel_pos_floor = floor(UV);
vec2 texel_pos = uv;
highp vec2 texel_pos_floor = floor(uv);

// Create a cross hatch grid of alternating 0/1 horizontal and vertical stripes 1 unit wide in XY
vec4 mirror = vec4(fract(texel_pos_floor * 0.5) * 2.0, 1.0, 1.0);
Expand All @@ -278,18 +292,18 @@ void fragment() {

// Get the textures for each vertex. 8-16 lookups (2-4 ea)
Material mat[4];
get_material(UV, control00, index00UV, w_normal, mat[0]);
get_material(UV, control01, index01UV, w_normal, mat[1]);
get_material(UV, control10, index10UV, w_normal, mat[2]);
get_material(UV, control11, index11UV, w_normal, mat[3]);
get_material(uv, control00, index00UV, w_normal, mat[0]);
get_material(uv, control01, index01UV, w_normal, mat[1]);
get_material(uv, control10, index10UV, w_normal, mat[2]);
get_material(uv, control11, index11UV, w_normal, mat[3]);

// Calculate weight for the pixel position between the vertices
// Bilinear interpolation of difference of UV and floor(UV)
// Bilinear interpolation of difference of uv and floor(uv)
vec2 weights1 = clamp(texel_pos - texel_pos_floor, 0, 1);
weights1 = mix(weights1, vec2(1.0) - weights1, mirror.xy);
vec2 weights0 = vec2(1.0) - weights1;
// Adjust final weights by noise. 1 lookup
float noise3 = texture(noise_texture, UV*noise3_scale).r;
float noise3 = texture(noise_texture, uv*noise3_scale).r;
vec4 weights;
weights.x = blend_weights(weights0.x * weights0.y, noise3);
weights.y = blend_weights(weights0.x * weights1.y, noise3);
Expand All @@ -313,7 +327,7 @@ void fragment() {
mat[3].nrm_rg * weights.w );

// Determine if we're in a region or not (region_uv.z>0)
vec3 region_uv = get_region_uv2(UV2);
vec3 region_uv = get_region_uv2(uv2);

// Colormap. 1 lookup
vec4 color_map = vec4(1., 1., 1., .5);
Expand All @@ -324,8 +338,8 @@ void fragment() {
}

// Macro variation. 2 Lookups
float noise1 = texture(noise_texture, rotate(UV*noise1_scale*.1, cos(noise1_angle), sin(noise1_angle)) + noise1_offset).r;
float noise2 = texture(noise_texture, UV*noise2_scale*.1).r;
float noise1 = texture(noise_texture, rotate(uv*noise1_scale*.1, cos(noise1_angle), sin(noise1_angle)) + noise1_offset).r;
float noise2 = texture(noise_texture, uv*noise2_scale*.1).r;
vec3 macrov = mix(macro_variation1, vec3(1.), clamp(noise1 + v_vertex_dist*.0002, 0., 1.));
macrov *= mix(macro_variation2, vec3(1.), clamp(noise2 + v_vertex_dist*.0002, 0., 1.));

Expand Down
53 changes: 37 additions & 16 deletions src/terrain_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ void Terrain3D::_initialize() {
// Initialize the system
if (!_initialized && _is_inside_world && is_inside_tree()) {
_material->initialize(_storage->get_region_size());
_material->set_mesh_vertex_spacing(_mesh_vertex_spacing);
_storage->update_regions(true); // generate map arrays
_texture_list->update_list(); // generate texture arrays
_build(_mesh_lods, _mesh_size);
Expand Down Expand Up @@ -363,6 +364,7 @@ void Terrain3D::_update_collision() {
// Rotated shape Y=90 for -90 rotated array index
Transform3D xform = Transform3D(Basis(Vector3(0, 1.0, 0), Math_PI * .5),
global_pos + Vector3(region_size, 0, region_size) * .5);
xform.scale(Vector3(_mesh_vertex_spacing, 1, _mesh_vertex_spacing));

if (!_show_debug_collision) {
RID shape = PhysicsServer3D::get_singleton()->heightmap_shape_create();
Expand Down Expand Up @@ -494,10 +496,10 @@ void Terrain3D::_generate_triangles(PackedVector3Array &p_vertices, PackedVector
}
}
} else {
int32_t z_start = (int32_t)Math::ceil(p_global_aabb.position.z);
int32_t z_end = (int32_t)Math::floor(p_global_aabb.get_end().z) + 1;
int32_t x_start = (int32_t)Math::ceil(p_global_aabb.position.x);
int32_t x_end = (int32_t)Math::floor(p_global_aabb.get_end().x) + 1;
int32_t z_start = (int32_t)Math::ceil(p_global_aabb.position.z / _mesh_vertex_spacing);
int32_t z_end = (int32_t)Math::floor(p_global_aabb.get_end().z / _mesh_vertex_spacing) + 1;
int32_t x_start = (int32_t)Math::ceil(p_global_aabb.position.x / _mesh_vertex_spacing);
int32_t x_end = (int32_t)Math::floor(p_global_aabb.get_end().x / _mesh_vertex_spacing) + 1;

for (int32_t z = z_start; z < z_end; ++z) {
for (int32_t x = x_start; x < x_end; ++x) {
Expand All @@ -516,10 +518,11 @@ void Terrain3D::_generate_triangle_pair(PackedVector3Array &p_vertices, PackedVe
uint32_t control1 = _storage->get_control(Vector3(x, 0.0, z));
uint32_t control2 = _storage->get_control(Vector3(x + step, 0.0, z + step));
uint32_t control3 = _storage->get_control(Vector3(x, 0.0, z + step));
Vector3 vertex_scaler = Vector3(_mesh_vertex_spacing, 1.0, _mesh_vertex_spacing);
if (!p_require_nav || (Util::is_nav(control1) && Util::is_nav(control2) && Util::is_nav(control3))) {
Vector3 v1 = _storage->get_mesh_vertex(p_lod, p_filter, Vector3(x, 0.0, z));
Vector3 v2 = _storage->get_mesh_vertex(p_lod, p_filter, Vector3(x + step, 0.0, z + step));
Vector3 v3 = _storage->get_mesh_vertex(p_lod, p_filter, Vector3(x, 0.0, z + step));
Vector3 v1 = _storage->get_mesh_vertex(p_lod, p_filter, Vector3(x, 0.0, z)) * vertex_scaler;
Vector3 v2 = _storage->get_mesh_vertex(p_lod, p_filter, Vector3(x + step, 0.0, z + step)) * vertex_scaler;
Vector3 v3 = _storage->get_mesh_vertex(p_lod, p_filter, Vector3(x, 0.0, z + step)) * vertex_scaler;
if (!UtilityFunctions::is_nan(v1.y) && !UtilityFunctions::is_nan(v2.y) && !UtilityFunctions::is_nan(v3.y)) {
p_vertices.push_back(v1);
p_vertices.push_back(v2);
Expand All @@ -536,9 +539,9 @@ void Terrain3D::_generate_triangle_pair(PackedVector3Array &p_vertices, PackedVe
control2 = _storage->get_control(Vector3(x + step, 0.0, z));
control3 = _storage->get_control(Vector3(x + step, 0.0, z + step));
if (!p_require_nav || (Util::is_nav(control1) && Util::is_nav(control2) && Util::is_nav(control3))) {
Vector3 v1 = _storage->get_mesh_vertex(p_lod, p_filter, Vector3(x, 0.0, z));
Vector3 v2 = _storage->get_mesh_vertex(p_lod, p_filter, Vector3(x + step, 0.0, z));
Vector3 v3 = _storage->get_mesh_vertex(p_lod, p_filter, Vector3(x + step, 0.0, z + step));
Vector3 v1 = _storage->get_mesh_vertex(p_lod, p_filter, Vector3(x, 0.0, z)) * vertex_scaler;
Vector3 v2 = _storage->get_mesh_vertex(p_lod, p_filter, Vector3(x + step, 0.0, z)) * vertex_scaler;
Vector3 v3 = _storage->get_mesh_vertex(p_lod, p_filter, Vector3(x + step, 0.0, z + step)) * vertex_scaler;
if (!UtilityFunctions::is_nan(v1.y) && !UtilityFunctions::is_nan(v2.y) && !UtilityFunctions::is_nan(v3.y)) {
p_vertices.push_back(v1);
p_vertices.push_back(v2);
Expand Down Expand Up @@ -604,6 +607,19 @@ void Terrain3D::set_mesh_size(int p_size) {
}
}

void Terrain3D::set_mesh_vertex_spacing(real_t p_spacing) {
p_spacing = CLAMP(p_spacing, 0.25f, 100.0f);
if (_mesh_vertex_spacing != p_spacing) {
LOG(INFO, "Setting mesh vertex spacing: ", p_spacing);
_mesh_vertex_spacing = p_spacing;
_clear();
_initialize();
}
if (Engine::get_singleton()->is_editor_hint() && _plugin != nullptr) {
_plugin->call("update_region_grid");
}
}

void Terrain3D::set_material(const Ref<Terrain3DMaterial> &p_material) {
if (_storage != p_material) {
LOG(INFO, "Setting material");
Expand Down Expand Up @@ -695,17 +711,19 @@ void Terrain3D::snap(Vector3 p_cam_pos) {
p_cam_pos.y = 0;
LOG(DEBUG_CONT, "Snapping terrain to: ", String(p_cam_pos));

Transform3D t = Transform3D(Basis(), p_cam_pos.floor());
Vector3 snapped_pos = (p_cam_pos / _mesh_vertex_spacing).floor() * _mesh_vertex_spacing;
Transform3D t = Transform3D().scaled(Vector3(_mesh_vertex_spacing, 1, _mesh_vertex_spacing));
t.origin = snapped_pos;
RS->instance_set_transform(_data.cross, t);

int edge = 0;
int tile = 0;

for (int l = 0; l < _mesh_lods; l++) {
real_t scale = real_t(1 << l);
real_t scale = real_t(1 << l) * _mesh_vertex_spacing;
Vector3 snapped_pos = (p_cam_pos / scale).floor() * scale;
Vector3 tile_size = Vector3(real_t(_mesh_size << l), 0, real_t(_mesh_size << l));
Vector3 base = snapped_pos - Vector3(real_t(_mesh_size << (l + 1)), 0, real_t(_mesh_size << (l + 1)));
Vector3 tile_size = Vector3(real_t(_mesh_size << l), 0, real_t(_mesh_size << l)) * _mesh_vertex_spacing;
Vector3 base = snapped_pos - Vector3(real_t(_mesh_size << (l + 1)), 0, real_t(_mesh_size << (l + 1))) * _mesh_vertex_spacing;

// Position tiles
for (int x = 0; x < 4; x++) {
Expand Down Expand Up @@ -756,7 +774,7 @@ void Terrain3D::snap(Vector3 p_cam_pos) {

// Position seams
{
Vector3 next_base = next_snapped_pos - Vector3(real_t(_mesh_size << (l + 1)), 0, real_t(_mesh_size << (l + 1)));
Vector3 next_base = next_snapped_pos - Vector3(real_t(_mesh_size << (l + 1)), 0, real_t(_mesh_size << (l + 1))) * _mesh_vertex_spacing;
Transform3D t = Transform3D().scaled(Vector3(scale, 1, scale));
t.origin = next_base;
RS->instance_set_transform(_data.seams[edge], t);
Expand Down Expand Up @@ -832,7 +850,7 @@ Vector3 Terrain3D::get_intersection(Vector3 p_position, Vector3 p_direction) {
if (_storage.is_valid()) {
for (int i = 0; i < 3000; i++) {
test_point += test_dir;
test_point.y = _storage->get_height(test_point);
test_point.y = _storage->get_height(test_point / _mesh_vertex_spacing);
Vector3 test_vec = (test_point - p_position).normalized();

real_t test_dotp = p_direction.dot(test_vec);
Expand Down Expand Up @@ -1016,6 +1034,8 @@ void Terrain3D::_bind_methods() {
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_mesh_vertex_spacing", "scale"), &Terrain3D::set_mesh_vertex_spacing);
ClassDB::bind_method(D_METHOD("get_mesh_vertex_spacing"), &Terrain3D::get_mesh_vertex_spacing);

ClassDB::bind_method(D_METHOD("set_material", "material"), &Terrain3D::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &Terrain3D::get_material);
Expand Down Expand Up @@ -1077,6 +1097,7 @@ void Terrain3D::_bind_methods() {
ADD_GROUP("Mesh", "mesh_");
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,64,1"), "set_mesh_size", "get_mesh_size");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mesh_vertex_spacing", PROPERTY_HINT_RANGE, "0.25,10.0,0.05,or_greater"), "set_mesh_vertex_spacing", "get_mesh_vertex_spacing");

ADD_GROUP("Debug", "debug_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_level", PROPERTY_HINT_ENUM, "Errors,Info,Debug,Debug Continuous"), "set_debug_level", "get_debug_level");
Expand Down
3 changes: 3 additions & 0 deletions src/terrain_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Terrain3D : public Node3D {
// Terrain settings
int _mesh_size = 48;
int _mesh_lods = 7;
real_t _mesh_vertex_spacing = 1.0f;

Ref<Terrain3DStorage> _storage;
Ref<Terrain3DMaterial> _material;
Expand Down Expand Up @@ -104,6 +105,8 @@ class Terrain3D : public Node3D {
int get_mesh_lods() const { return _mesh_lods; }
void set_mesh_size(int p_size);
int get_mesh_size() const { return _mesh_size; }
void set_mesh_vertex_spacing(real_t p_spacing);
real_t get_mesh_vertex_spacing() const { return _mesh_vertex_spacing; }

void set_storage(const Ref<Terrain3DStorage> &p_storage);
Ref<Terrain3DStorage> get_storage() const { return _storage; }
Expand Down
Loading