Skip to content

Commit

Permalink
Implements vertex spacing (mesh density)
Browse files Browse the repository at this point in the history
  • Loading branch information
lfxu authored and lfxu committed Jan 14, 2024
1 parent 1f7dc1c commit cce10db
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 34 deletions.
5 changes: 4 additions & 1 deletion project/addons/terrain_3d/editor/editor.gd
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ func _forward_3d_gui_input(p_viewport_camera: Camera3D, p_event: InputEvent) ->
ui.decal.albedo_mix = 1.0
ui.decal_timer.start()

# Incooporate vertex spacing
mouse_global_position /= 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 @@ -216,7 +219,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
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
44 changes: 28 additions & 16 deletions src/shaders/main.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ 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_density = 1.0;
uniform int _region_map_size = 16;
uniform int _region_map[256];
uniform vec2 _region_offsets[256];
Expand Down Expand Up @@ -61,9 +62,11 @@ struct Material {
};

varying vec3 v_vertex; // World coordinate vertex location
varying vec3 v_camera_pos;
varying flat vec3 v_camera_pos;
varying 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 +113,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 = v_vertex.xz * _mesh_vertex_density;

// Discard vertices if designated as a hole or background disabled. 1 lookup.
v_region = get_region_uv(UV);
Expand All @@ -120,14 +123,19 @@ void vertex() {
VERTEX.x = 0./0.;
} else {
// UV coordinates in region space + texel offset. Values are 0 to 1 within regions
UV2 = (UV + vec2(0.5)) * _region_texel_size;
UV2 = (round(v_vertex.xz * _mesh_vertex_density) + vec2(0.5)) * _region_texel_size;

// Get final vertex location and save it
VERTEX.y = get_height(UV2);
v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
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;
UV -= v_uv_offset;
v_uv2_offset = v_uv_offset * _region_texel_size;
UV2 -= v_uv_offset;
}

////////////////////////
Expand Down Expand Up @@ -248,16 +256,20 @@ float blend_weights(float weight, float detail) {
}

void fragment() {
// Recover UVs
vec2 uv = UV + v_uv_offset;
vec2 uv2 = UV2 + v_uv_offset;

// 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 +290,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 +325,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 +336,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
50 changes: 35 additions & 15 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,11 +518,12 @@ 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 (!Util::is_hole(control1) && !Util::is_hole(control2) && !Util::is_hole(control3)) {
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;
p_vertices.push_back(v1);
p_vertices.push_back(v2);
p_vertices.push_back(v3);
Expand All @@ -537,9 +540,9 @@ void Terrain3D::_generate_triangle_pair(PackedVector3Array &p_vertices, PackedVe
control3 = _storage->get_control(Vector3(x + step, 0.0, z + step));
if (!Util::is_hole(control1) && !Util::is_hole(control2) && !Util::is_hole(control3)) {
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;
p_vertices.push_back(v1);
p_vertices.push_back(v2);
p_vertices.push_back(v3);
Expand Down Expand Up @@ -604,6 +607,18 @@ void Terrain3D::set_mesh_size(int p_size) {
}
}

void Terrain3D::set_mesh_vertex_spacing(float p_spacing) {
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 +710,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 +773,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 @@ -1016,6 +1033,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 +1096,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.5,50,0.5"), "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;
float _mesh_vertex_spacing = 1;

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(float p_spacing);
float 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
1 change: 0 additions & 1 deletion src/terrain_3d_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ void Terrain3DEditor::_operate_region(Vector3 p_global_position) {
if (!has_region) {
_terrain->get_storage()->add_region(p_global_position);
modified = true;

}
}
if (_operation == SUBTRACT) {
Expand Down
6 changes: 6 additions & 0 deletions src/terrain_3d_material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,12 @@ Variant Terrain3DMaterial::get_shader_param(const StringName &p_name) const {
return value;
}

void Terrain3DMaterial::set_mesh_vertex_spacing(float p_spacing) {
LOG(INFO, "Setting mesh vertex spacing in material: ", p_spacing);
_mesh_vertex_spacing = p_spacing;
RS->material_set_param(_material, "_mesh_vertex_density", 1.0f / p_spacing);
}

void Terrain3DMaterial::set_show_checkered(bool p_enabled) {
LOG(INFO, "Enable set_show_checkered: ", p_enabled);
_debug_view_checkered = p_enabled;
Expand Down
3 changes: 3 additions & 0 deletions src/terrain_3d_material.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class Terrain3DMaterial : public Resource {
// Cached data from Storage
int _texture_count = 0;
int _region_size = 1024;
float _mesh_vertex_spacing = 1;
Vector2i _region_sizev = Vector2i(_region_size, _region_size);
PackedInt32Array _region_map;
GeneratedTex _generated_region_blend_map; // 512x512 blurred image of region_map
Expand Down Expand Up @@ -108,6 +109,8 @@ class Terrain3DMaterial : public Resource {
void set_shader_param(const StringName &p_name, const Variant &p_value);
Variant get_shader_param(const StringName &p_name) const;

void set_mesh_vertex_spacing(float p_spacing);

// Editor functions / Debug views
void set_show_checkered(bool p_enabled);
bool get_show_checkered() const { return _debug_view_checkered; }
Expand Down

0 comments on commit cce10db

Please sign in to comment.