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

Improve brushing #492

Merged
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
5 changes: 3 additions & 2 deletions project/addons/terrain_3d/src/asset_dock.gd
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ func _on_meshes_pressed() -> void:
func _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor.Operation) -> void:
if p_tool == Terrain3DEditor.INSTANCER:
_on_meshes_pressed()
elif p_tool == Terrain3DEditor.TEXTURE:
elif p_tool in [ Terrain3DEditor.TEXTURE, Terrain3DEditor.COLOR, Terrain3DEditor.ROUGHNESS ]:
_on_textures_pressed()


Expand Down Expand Up @@ -542,7 +542,8 @@ class ListContainer extends Container:
plugin.select_terrain()

# Select Paint tool if clicking a texture
if type == Terrain3DAssets.TYPE_TEXTURE and plugin.editor.get_tool() != Terrain3DEditor.TEXTURE:
if type == Terrain3DAssets.TYPE_TEXTURE and \
not plugin.editor.get_tool() in [ Terrain3DEditor.TEXTURE, Terrain3DEditor.COLOR, Terrain3DEditor.ROUGHNESS ]:
var paint_btn: Button = plugin.ui.toolbar.get_node_or_null("PaintBaseTexture")
if paint_btn:
paint_btn.set_pressed(true)
Expand Down
5 changes: 4 additions & 1 deletion project/addons/terrain_3d/src/tool_settings.gd
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ func _ready() -> void:
"default":Terrain3DEditor.ROUGHNESS, "flags":NO_LABEL })

add_setting({ "name":"enable_texture", "label":"Texture", "type":SettingType.CHECKBOX,
"list":main_list, "default":true })
"list":main_list, "default":true, "flags":ADD_SEPARATOR })

add_setting({ "name":"margin", "type":SettingType.SLIDER, "list":main_list, "default":0,
"unit":"", "range":Vector3(-50, 50, 1), "flags":ALLOW_OUT_OF_BOUNDS })

add_setting({ "name":"enable_angle", "label":"Angle", "type":SettingType.CHECKBOX,
"list":main_list, "default":true, "flags":ADD_SEPARATOR })
Expand Down
4 changes: 4 additions & 0 deletions project/addons/terrain_3d/src/ui.gd
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ func _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor
to_show.push_back("strength")
to_show.push_back("color")
to_show.push_back("color_picker")
to_show.push_back("enable_texture")
to_show.push_back("margin")
to_show.push_back("remove")

Terrain3DEditor.ROUGHNESS:
Expand All @@ -190,6 +192,8 @@ func _on_tool_changed(p_tool: Terrain3DEditor.Tool, p_operation: Terrain3DEditor
to_show.push_back("strength")
to_show.push_back("roughness")
to_show.push_back("roughness_picker")
to_show.push_back("enable_texture")
to_show.push_back("margin")
to_show.push_back("remove")

Terrain3DEditor.AUTOSHADER, Terrain3DEditor.HOLES, Terrain3DEditor.NAVIGATION:
Expand Down
93 changes: 56 additions & 37 deletions src/terrain_3d_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,7 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_
bool navigation = is_nav(src.r);
bool autoshader = is_auto(src.r);

real_t alpha_clip = (brush_alpha > 0.1f) ? 1.f : 0.f;
uint32_t dest_id = uint32_t(Math::lerp(base_id, asset_id, alpha_clip));
real_t alpha_clip = (brush_alpha > 0.5f) ? 1.f : 0.f;
// Lookup to shift values saved to control map so that 0 (default) is the first entry
// Shader scale array is aligned to match this.
std::array<uint32_t, 8> scale_align = { 5, 6, 7, 0, 1, 2, 3, 4 };
Expand All @@ -326,16 +325,16 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_
switch (_operation) {
// Base Paint
case REPLACE: {
if (brush_alpha > 0.1f) {
if (brush_alpha > 0.5f) {
if (enable_texture) {
// Set base texture
base_id = dest_id;
base_id = asset_id;
// Erase blend value
blend = Math::lerp(blend, real_t(0.f), alpha_clip);
autoshader = false;
}
// Set angle & scale
if (enable_angle) {
if (base_id == asset_id && enable_angle && !autoshader) {
if (dynamic_angle) {
// Angle from mouse movement.
angle = Vector2(-_operation_movement.x, _operation_movement.z).angle();
Expand All @@ -345,7 +344,7 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_
// Convert from degrees to 0 - 15 value range
uvrotation = uint32_t(CLAMP(Math::round(angle / 22.5f), 0.f, 15.f));
}
if (enable_scale) {
if (base_id == asset_id && enable_scale && !autoshader) {
// Offset negative and convert from percentage to 0 - 7 bit value range
// Maintain 0 = 0, remap negatives to end.
uvscale = scale_align[uint8_t(CLAMP(Math::round((scale + 60.f) / 20.f), 0.f, 7.f))];
Expand All @@ -356,22 +355,27 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_

// Overlay Spray
case ADD: {
real_t spray_strength = CLAMP(strength * 0.05f, 0.003f, .25f);
real_t spray_strength = CLAMP(strength * 0.05f, 0.004f, .25f);
real_t brush_value = CLAMP(brush_alpha * spray_strength, 0.f, 1.f);
if (enable_texture) {
if (enable_texture && brush_alpha * strength * 11.f > 0.1f) {
// If overlay and base texture are the same, reduce blend value
if (dest_id == base_id) {
if (base_id == asset_id) {
blend = CLAMP(blend - brush_value, 0.f, 1.f);
if (blend < 0.5f && brush_alpha > 0.5f) {
autoshader = false;
}
} else {
// Else overlay and base are separate, set overlay texture and increase blend value
overlay_id = dest_id;
blend = CLAMP(blend + brush_value, 0.f, 1.f);
if (blend > 0.5f && brush_alpha > 0.5f) {
overlay_id = asset_id;
autoshader = false;
}
}
autoshader = false;
}
if (brush_alpha * strength * 11.f > 0.1f) {
if ((base_id == asset_id && blend < 0.5f) || (base_id != asset_id && blend >= 0.5f)) {
// Set angle & scale
if (enable_angle) {
if (enable_angle && !autoshader && brush_alpha > 0.5f) {
if (dynamic_angle) {
// Angle from mouse movement.
angle = Vector2(-_operation_movement.x, _operation_movement.z).angle();
Expand All @@ -381,7 +385,7 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_
// Convert from degrees to 0 - 15 value range
uvrotation = uint32_t(CLAMP(Math::round(angle / 22.5f), 0.f, 15.f));
}
if (enable_scale) {
if (enable_scale && !autoshader && brush_alpha > 0.5f) {
// Offset negative and convert from percentage to 0 - 7 bit value range
// Maintain 0 = 0, remap negatives to end.
uvscale = scale_align[uint8_t(CLAMP(Math::round((scale + 60.f) / 20.f), 0.f, 7.f))];
Expand All @@ -397,19 +401,21 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_
break;
}
case AUTOSHADER: {
if (brush_alpha > 0.1f) {
if (brush_alpha > 0.5f) {
autoshader = (_operation == ADD);
uvscale = 0.f;
uvrotation = 0.f;
}
break;
}
case HOLES: {
if (brush_alpha > 0.1f) {
if (brush_alpha > 0.5f) {
hole = (_operation == ADD);
}
break;
}
case NAVIGATION: {
if (brush_alpha > 0.1f) {
if (brush_alpha > 0.5f) {
navigation = (_operation == ADD);
}
break;
Expand All @@ -430,26 +436,38 @@ void Terrain3DEditor::_operate_map(const Vector3 &p_global_position, const real_
dest = Color(as_float(bits), 0.f, 0.f, 1.f);

} else if (map_type == TYPE_COLOR) {
switch (_tool) {
case COLOR:
dest = src.lerp((_operation == ADD) ? color : COLOR_WHITE, brush_alpha * strength);
dest.a = src.a;
break;
case ROUGHNESS:
/* Roughness received from UI is -100 to 100. Changed to 0,1 before storing.
* To convert 0,1 back to -100,100 use: 200 * (color.a - 0.5)
* However Godot stores values as 8-bit ints. Roundtrip is = int(a*255)/255.0
* Roughness 0 is saved as 0.5, but retreived is 0.498, or -0.4 roughness
* We round the final amount in tool_settings.gd:_on_picked().
*/
if (_operation == ADD) {
dest.a = Math::lerp(real_t(src.a), real_t(.5f + .5f * roughness), brush_alpha * strength);
} else {
dest.a = Math::lerp(real_t(src.a), real_t(.5f + .5f * 0.5f), brush_alpha * strength);
}
break;
default:
break;
// Filter by visible texture
bool can_write = true;
if (enable_texture) {
Ref<Image> map = region->get_map(TYPE_CONTROL);
real_t src_ctrl = map->get_pixelv(map_pixel_position).r;
int tex_id = (get_blend(src_ctrl) > 110 + int(_brush_data.get("margin", 0))) ? get_overlay(src_ctrl) : get_base(src_ctrl);
if (tex_id != asset_id) {
can_write = false;
}
}
if (can_write) {
switch (_tool) {
case COLOR:
dest = src.lerp((_operation == ADD) ? color : COLOR_WHITE, brush_alpha * strength);
dest.a = src.a;
break;
case ROUGHNESS:
/* Roughness received from UI is -100 to 100. Changed to 0,1 before storing.
* To convert 0,1 back to -100,100 use: 200 * (color.a - 0.5)
* However Godot stores values as 8-bit ints. Roundtrip is = int(a*255)/255.0
* Roughness 0 is saved as 0.5, but retreived is 0.498, or -0.4 roughness
* We round the final amount in tool_settings.gd:_on_picked().
*/
if (_operation == ADD) {
dest.a = Math::lerp(real_t(src.a), real_t(.5f + .5f * roughness), brush_alpha * strength);
} else {
dest.a = Math::lerp(real_t(src.a), real_t(.5f + .5f * 0.5f), brush_alpha * strength);
}
break;
default:
break;
}
}
}
backup_region(region);
Expand Down Expand Up @@ -646,6 +664,7 @@ void Terrain3DEditor::set_brush_data(const Dictionary &p_data) {

_brush_data["enable_texture"] = p_data.get("enable_texture", true);
_brush_data["asset_id"] = CLAMP(int(p_data.get("asset_id", 0)), 0, ((_tool == INSTANCER) ? Terrain3DAssets::MAX_MESHES : Terrain3DAssets::MAX_TEXTURES) - 1);
_brush_data["margin"] = CLAMP(int(p_data.get("margin", 0)), -100, 100);

_brush_data["enable_angle"] = p_data.get("enable_angle", true);
_brush_data["dynamic_angle"] = p_data.get("dynamic_angle", false);
Expand Down