From f7e39257d49a234bdfa350831dfef8f577663c37 Mon Sep 17 00:00:00 2001 From: Xtarsia <69606701+Xtarsia@users.noreply.github.com> Date: Fri, 18 Jul 2025 10:15:42 +0100 Subject: [PATCH] return float slope alignment value instead of bool --- src/terrain_3d_data.cpp | 22 +++++++++++++++++----- src/terrain_3d_data.h | 2 +- src/terrain_3d_editor.cpp | 20 ++++++++------------ src/terrain_3d_instancer.cpp | 4 ++-- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/terrain_3d_data.cpp b/src/terrain_3d_data.cpp index 812a9ffc8..defeb0561 100644 --- a/src/terrain_3d_data.cpp +++ b/src/terrain_3d_data.cpp @@ -692,18 +692,18 @@ Vector3 Terrain3DData::get_normal(const Vector3 &p_global_position) const { return normal; } -bool Terrain3DData::is_in_slope(const Vector3 &p_global_position, const Vector2 &p_slope_range, const bool p_invert) const { +real_t Terrain3DData::is_in_slope(const Vector3 &p_global_position, const Vector2 &p_slope_range, const bool p_invert) const { // If slope is full range, it's disabled const Vector2 slope_range = CLAMP(p_slope_range, V2_ZERO, Vector2(90.f, 90.f)); if (slope_range.y - slope_range.x > 89.99f) { - return true; + return real_t(1.f); } // Adapted from get_normal to work with holes Vector3 slope_normal; { if (get_region_idp(p_global_position) < 0) { - return false; + return real_t(0.f); } // Adapted from get_height() to work with holes auto get_height = [&](Vector3 pos) -> real_t { @@ -723,10 +723,22 @@ bool Terrain3DData::is_in_slope(const Vector3 &p_global_position, const Vector2 } const real_t slope_angle = Math::acos(slope_normal.dot(Vector3(0.f, 1.f, 0.f))); - const real_t slope_angle_degrees = Math::rad_to_deg(slope_angle); + const real_t slope_angle_degrees = CLAMP(Math::rad_to_deg(slope_angle), 0.f, 90.f); // XOR: If invert return !a || !b else return a && b - return p_invert ^ ((slope_range.x <= slope_angle_degrees) && (slope_angle_degrees <= slope_range.y)); + if (p_invert ^ ((slope_range.x <= slope_angle_degrees) && (slope_angle_degrees <= slope_range.y))) { + return real_t(1.f); + } + // 1.0 -> 0.0 -> 1.0 inside range + if (p_invert) { + return real_t(ABS(2.f * (slope_angle_degrees - slope_range.x) / (slope_range.y - slope_range.x) - 1.f)); + } + // 0.0 > 1.0 below range + if (slope_angle_degrees < slope_range.x) { + return real_t(slope_angle_degrees / slope_range.x); + } + // 1.0 -> 0.0 above above + return real_t((90.f - slope_angle_degrees) / (90.f - slope_range.y)); } /** diff --git a/src/terrain_3d_data.h b/src/terrain_3d_data.h index ad6d8290f..874b7df9b 100644 --- a/src/terrain_3d_data.h +++ b/src/terrain_3d_data.h @@ -168,7 +168,7 @@ class Terrain3DData : public Object { bool get_control_auto(const Vector3 &p_global_position) const; Vector3 get_normal(const Vector3 &p_global_position) const; - bool is_in_slope(const Vector3 &p_global_position, const Vector2 &p_slope_range, const bool p_invert = false) const; + real_t is_in_slope(const Vector3 &p_global_position, const Vector2 &p_slope_range, const bool p_invert = false) const; Vector3 get_texture_id(const Vector3 &p_global_position) const; Vector3 get_mesh_vertex(const int32_t p_lod, const HeightFilter p_filter, const Vector3 &p_global_position) const; diff --git a/src/terrain_3d_editor.cpp b/src/terrain_3d_editor.cpp index fef3b51d3..d56b164d8 100644 --- a/src/terrain_3d_editor.cpp +++ b/src/terrain_3d_editor.cpp @@ -338,13 +338,11 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_ switch (_tool) { case TEXTURE: { - if (!data->is_in_slope(brush_global_position, slope_range, modifier_alt)) { - continue; - } + real_t slope_factor = pow(data->is_in_slope(brush_global_position, slope_range, modifier_alt), 8.f); switch (_operation) { // Base Paint case REPLACE: { - if (brush_alpha > 0.5f) { + if (brush_alpha > 0.5f && slope_factor > 0.999f) { if (enable_texture) { // Set base & overlay texture base_id = asset_id; @@ -376,7 +374,7 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_ // Add asset id, and increase weighting case ADD: { real_t spray_strength = CLAMP(strength * 0.05f, 0.004f, .25f); - real_t brush_value = CLAMP(brush_alpha * spray_strength, 0.f, 1.f); + real_t brush_value = CLAMP(brush_alpha * spray_strength * slope_factor, 0.f, 1.f); if (enable_texture && brush_alpha * strength * 11.f > 0.1f) { // Pick lowest weighted id, and lower to zero before setting new asset id. if (asset_id != base_id && asset_id != overlay_id) { @@ -430,7 +428,7 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_ // Lower weight of current asset id case SUBTRACT: { real_t spray_strength = CLAMP(strength * 0.05f, 0.004f, .25f); - real_t brush_value = CLAMP(brush_alpha * spray_strength, 0.f, 1.f); + real_t brush_value = CLAMP(brush_alpha * spray_strength * slope_factor, 0.f, 1.f); if (base_id == asset_id) { blend = CLAMP(blend + brush_value, 0.f, 1.f); } @@ -494,12 +492,10 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_ continue; } } - if (!data->is_in_slope(brush_global_position, slope_range, modifier_alt)) { - continue; - } + real_t slope_factor = pow(data->is_in_slope(brush_global_position, slope_range, modifier_alt), 8.f); switch (_tool) { case COLOR: - dest = src.lerp((_operation == ADD) ? color : COLOR_WHITE, brush_alpha * strength); + dest = src.lerp((_operation == ADD) ? color : COLOR_WHITE, brush_alpha * strength * slope_factor); dest.a = src.a; break; case ROUGHNESS: @@ -511,10 +507,10 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_ */ if (_operation == ADD) { real_t target = .5f + .5f * roughness; - dest.a = Math::lerp(real_t(src.a), target, brush_alpha * strength); + dest.a = Math::lerp(real_t(src.a), target, brush_alpha * strength * slope_factor); dest.a = float(int(dest.a * 255.f)) / 255.f; // Quantize explicitly so picked values match painted values } else { - dest.a = Math::lerp(real_t(src.a), real_t(.5f), brush_alpha * strength); + dest.a = Math::lerp(real_t(src.a), real_t(.5f), brush_alpha * strength * slope_factor); dest.a = float(int(dest.a * 255.f)) / 255.f; } break; diff --git a/src/terrain_3d_instancer.cpp b/src/terrain_3d_instancer.cpp index adcb2fadc..839f18155 100644 --- a/src/terrain_3d_instancer.cpp +++ b/src/terrain_3d_instancer.cpp @@ -420,7 +420,7 @@ Array Terrain3DInstancer::_get_usable_height(const Vector3 &p_global_position, c height = raycast_height; } // No hole or collision, use height if in slope or quit - else if (!data->is_in_slope(p_global_position, p_slope_range, p_invert)) { + else if (!(data->is_in_slope(p_global_position, p_slope_range, p_invert) >= 0.999f)) { return Array(); } Array triple; @@ -715,7 +715,7 @@ void Terrain3DInstancer::remove_instances(const Vector3 &p_global_position, cons Vector3 height_offset = t.basis.get_column(1) * mesh_height_offset; if (radial_distance < radius && UtilityFunctions::randf() < CLAMP(0.175f * strength, 0.005f, 10.f) && - data->is_in_slope(t.origin + global_local_offset - height_offset, slope_range, invert)) { + data->is_in_slope(t.origin + global_local_offset - height_offset, slope_range, invert) > 0.9999f) { _backup_region(region); continue; } else {