built more assets and started playing with foliage painting
This commit is contained in:
229
addons/dreadpon.spatial_gardener/toolshed/toolshed.gd
Normal file
229
addons/dreadpon.spatial_gardener/toolshed/toolshed.gd
Normal file
@@ -0,0 +1,229 @@
|
||||
@tool
|
||||
extends "../utility/input_field_resource/input_field_resource.gd"
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# The manager of all brush types for a given Gardener
|
||||
# Handles interfacing between Toolshed_Brush, UI and plant painting
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
const Toolshed_Brush = preload("toolshed_brush.gd")
|
||||
const ui_category_brushes_SCN = preload("../controls/side_panel/ui_category_brushes.tscn")
|
||||
const ui_section_brush_SCN = preload("../controls/side_panel/ui_section_brush.tscn")
|
||||
|
||||
var brushes:Array = []
|
||||
var active_brush:Toolshed_Brush = null
|
||||
var ui_category_brushes_nd:Control = null
|
||||
var tab_container_brushes_nd:Control = null
|
||||
var panel_container_category_nd:Control = null
|
||||
|
||||
var _base_control:Control = null
|
||||
var _resource_previewer = null
|
||||
|
||||
|
||||
signal prop_action_executed_on_brush(prop_action, final_val, brush)
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Initialization
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
func _init(__brushes:Array = []):
|
||||
super()
|
||||
set_meta("class", "Toolshed")
|
||||
resource_name = "Toolshed"
|
||||
|
||||
brushes = _modify_prop("brush/brushes", __brushes)
|
||||
if brushes.size() > 0:
|
||||
active_brush = _modify_prop("brush/active_brush", brushes[0])
|
||||
_add_prop_dependency("brush/active_brush", ["brush/brushes"])
|
||||
|
||||
|
||||
# The UI is created here because we need to manage it afterwards
|
||||
# And I see no reason to get lost in a signal spaghetti of delegating it
|
||||
func create_ui(__base_control:Control, __resource_previewer):
|
||||
_base_control = __base_control
|
||||
_resource_previewer = __resource_previewer
|
||||
|
||||
if is_instance_valid(ui_category_brushes_nd):
|
||||
ui_category_brushes_nd.queue_free()
|
||||
|
||||
ui_category_brushes_nd = ui_category_brushes_SCN.instantiate()
|
||||
tab_container_brushes_nd = ui_category_brushes_nd.find_child('TabContainer_Brushes')
|
||||
panel_container_category_nd = ui_category_brushes_nd.find_child('Label_Category')
|
||||
|
||||
ui_category_brushes_nd.theme_type_variation = "InspectorPanelContainer"
|
||||
panel_container_category_nd.theme_type_variation = "PropertyCategory"
|
||||
|
||||
for brush in brushes:
|
||||
var section_brush = ui_section_brush_SCN.instantiate()
|
||||
var vbox_container_properties = section_brush.find_child('VBoxContainer_Properties')
|
||||
section_brush.name = FunLib.capitalize_string_array(brush.BrushType.keys())[brush.behavior_brush_type]
|
||||
tab_container_brushes_nd.add_child(section_brush)
|
||||
|
||||
for input_field in brush.create_input_fields(_base_control, _resource_previewer).values():
|
||||
vbox_container_properties.add_child(input_field)
|
||||
|
||||
section_brush.theme_type_variation = "InspectorPanelContainer"
|
||||
|
||||
if brushes.size() > 0:
|
||||
tab_container_brushes_nd.current_tab = brushes.find(active_brush)
|
||||
tab_container_brushes_nd.tab_changed.connect(on_active_brush_tab_changed)
|
||||
|
||||
return ui_category_brushes_nd
|
||||
|
||||
|
||||
func _fix_duplicate_signals(copy):
|
||||
copy._modify_prop("brush/brushes", copy.brushes)
|
||||
copy.active_brush = copy.brushes[0]
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Input
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
func forwarded_input(camera, event):
|
||||
var handled := false
|
||||
|
||||
var index_tab = -1
|
||||
|
||||
if is_instance_of(event, InputEventKey) && !event.pressed:
|
||||
var index_map := [KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0]
|
||||
index_tab = index_map.find(event.keycode)
|
||||
|
||||
if index_tab >= 0 && index_tab < brushes.size():
|
||||
handled = true
|
||||
on_active_brush_tab_changed(index_tab)
|
||||
return
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Syncing the Toolshed with it's UI
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
func on_active_brush_tab_changed(active_tab):
|
||||
var prop_action:PropAction = PA_PropSet.new("brush/active_brush", brushes[active_tab])
|
||||
request_prop_action(prop_action)
|
||||
|
||||
|
||||
func on_prop_action_executed(prop_action:PropAction, final_val):
|
||||
if is_instance_of(prop_action, PA_PropSet):
|
||||
if prop_action.prop == "brush/active_brush":
|
||||
if tab_container_brushes_nd:
|
||||
tab_container_brushes_nd.tab_changed.disconnect(on_active_brush_tab_changed)
|
||||
tab_container_brushes_nd.current_tab = brushes.find(final_val)
|
||||
tab_container_brushes_nd.tab_changed.connect(on_active_brush_tab_changed)
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Broadcast changes within the brushes themselves
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
func on_changed_brush():
|
||||
emit_changed()
|
||||
|
||||
|
||||
func on_prop_action_executed_on_brush(prop_action:PropAction, final_val, brush):
|
||||
prop_action_executed_on_brush.emit(prop_action, final_val, brush)
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Property export
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
func set_undo_redo(val):
|
||||
super.set_undo_redo(val)
|
||||
for brush in brushes:
|
||||
brush.set_undo_redo(_undo_redo)
|
||||
|
||||
|
||||
func _modify_prop(prop:String, val):
|
||||
match prop:
|
||||
"brush/brushes":
|
||||
for i in range(0, val.size()):
|
||||
if !is_instance_of(val[i], Toolshed_Brush):
|
||||
val[i] = Toolshed_Brush.new()
|
||||
|
||||
FunLib.ensure_signal(val[i].changed, on_changed_brush)
|
||||
FunLib.ensure_signal(val[i].prop_action_executed, on_prop_action_executed_on_brush, [val[i]])
|
||||
|
||||
if val[i]._undo_redo != _undo_redo:
|
||||
val[i].set_undo_redo(_undo_redo)
|
||||
"brush/active_brush":
|
||||
if !brushes.has(val):
|
||||
if brushes.size() > 0:
|
||||
val = brushes[0]
|
||||
else:
|
||||
val = null
|
||||
|
||||
return val
|
||||
|
||||
|
||||
func _get(property):
|
||||
match property:
|
||||
"brush/brushes":
|
||||
return brushes
|
||||
"brush/active_brush":
|
||||
return active_brush
|
||||
|
||||
return null
|
||||
|
||||
|
||||
func _set(prop, val):
|
||||
var return_val = true
|
||||
val = _modify_prop(prop, val)
|
||||
|
||||
match prop:
|
||||
"brush/brushes":
|
||||
brushes = val
|
||||
"brush/active_brush":
|
||||
active_brush = val
|
||||
_:
|
||||
return_val = false
|
||||
|
||||
if return_val:
|
||||
emit_changed()
|
||||
return return_val
|
||||
|
||||
|
||||
func _get_prop_dictionary():
|
||||
return {
|
||||
"brush/brushes":
|
||||
{
|
||||
"name": "brush/brushes",
|
||||
"type": TYPE_ARRAY,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
"hint": PROPERTY_HINT_NONE
|
||||
},
|
||||
"brush/active_brush":
|
||||
{
|
||||
"name": "brush/active_brush",
|
||||
"type": TYPE_OBJECT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
"hint": PROPERTY_HINT_NONE
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
func get_prop_tooltip(prop:String) -> String:
|
||||
match prop:
|
||||
"brush/brushes":
|
||||
return "The list of all brushes available in this toolshed"
|
||||
"brush/active_brush":
|
||||
return "The brush that is currently selected and used in the painting process"
|
||||
return ""
|
||||
296
addons/dreadpon.spatial_gardener/toolshed/toolshed_brush.gd
Normal file
296
addons/dreadpon.spatial_gardener/toolshed/toolshed_brush.gd
Normal file
@@ -0,0 +1,296 @@
|
||||
@tool
|
||||
extends "../utility/input_field_resource/input_field_resource.gd"
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# All the data that reflects a brush behavior
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
const Globals = preload("../utility/globals.gd")
|
||||
|
||||
enum BrushType {PAINT, ERASE, SINGLE, REAPPLY}
|
||||
enum OverlapMode {VOLUME, PROJECTION}
|
||||
|
||||
|
||||
var behavior_brush_type:int = BrushType.PAINT
|
||||
var shape_volume_size:float = 1.0
|
||||
var shape_projection_size:float = 1.0
|
||||
var behavior_strength:float = 1.0
|
||||
var behavior_passthrough: bool = false
|
||||
var behavior_overlap_mode: int = OverlapMode.VOLUME
|
||||
var behavior_no_settings_text: String = 'This brush has no additional settings'
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Initialization
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
func _init(__behavior_brush_type:int = BrushType.PAINT, __behavior_strength:float = 1.0, __shape_volume_size:float = 1.0,
|
||||
__shape_projection_size:float = 1.0, __behavior_passthrough: bool = false, __behavior_overlap_mode: int = OverlapMode.VOLUME):
|
||||
|
||||
input_field_blacklist = ['behavior/behavior_brush_type']
|
||||
|
||||
super()
|
||||
set_meta("class", "Toolshed_Brush")
|
||||
resource_name = "Toolshed_Brush"
|
||||
|
||||
behavior_brush_type = __behavior_brush_type
|
||||
behavior_strength = __behavior_strength
|
||||
shape_volume_size = __shape_volume_size
|
||||
shape_projection_size = __shape_projection_size
|
||||
behavior_passthrough = __behavior_passthrough
|
||||
behavior_overlap_mode = __behavior_overlap_mode
|
||||
|
||||
func _create_input_field(_base_control:Control, _resource_previewer, prop:String) -> UI_InputField:
|
||||
var input_field:UI_InputField = null
|
||||
|
||||
match prop:
|
||||
"shape/shape_volume_size":
|
||||
var max_value = FunLib.get_setting_safe("dreadpons_spatial_gardener/input_and_ui/brush_volume_size_slider_max_value", 100.0)
|
||||
var settings := {"min": 0.0, "max": max_value, "step": 0.01, "allow_greater": true, "allow_lesser": false,}
|
||||
input_field = UI_IF_RealSlider.new(shape_volume_size, "Volume Size", prop, settings)
|
||||
"shape/shape_projection_size":
|
||||
var max_value = FunLib.get_setting_safe("dreadpons_spatial_gardener/input_and_ui/brush_projection_size_slider_max_value", 1000.0)
|
||||
var settings := {"min": 1.0, "max": max_value, "step": 1.0, "allow_greater": true, "allow_lesser": false,}
|
||||
input_field = UI_IF_RealSlider.new(shape_projection_size, "Projection Size", prop, settings)
|
||||
"behavior/behavior_strength":
|
||||
var settings := {"min": 0.0, "max": 1.0, "step": 0.01, "allow_greater": false, "allow_lesser": false,}
|
||||
input_field = UI_IF_RealSlider.new(behavior_strength, "Strength", prop, settings)
|
||||
"behavior/behavior_passthrough":
|
||||
input_field = UI_IF_Bool.new(behavior_passthrough, "Passthrough", prop)
|
||||
"behavior/behavior_overlap_mode":
|
||||
var settings := {"enum_list": FunLib.capitalize_string_array(OverlapMode.keys())}
|
||||
input_field = UI_IF_Enum.new(behavior_overlap_mode, "Overlap Mode", prop, settings)
|
||||
"behavior/behavior_no_settings_text":
|
||||
var settings := {"label_visibility": false}
|
||||
input_field = UI_IF_PlainText.new(behavior_no_settings_text, "No Settings Text", prop, settings)
|
||||
|
||||
return input_field
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Property export
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
func _modify_prop(prop:String, val):
|
||||
match prop:
|
||||
"behavior/behavior_strength":
|
||||
val = clamp(val, 0.0, 1.0)
|
||||
"behavior/behavior_overlap_mode":
|
||||
match behavior_brush_type:
|
||||
BrushType.PAINT, BrushType.SINGLE:
|
||||
val = OverlapMode.VOLUME
|
||||
"shape/shape_volume_size":
|
||||
match behavior_brush_type:
|
||||
BrushType.SINGLE:
|
||||
val = 1.0
|
||||
return val
|
||||
|
||||
|
||||
func _set(prop, val):
|
||||
var return_val = true
|
||||
val = _modify_prop(prop, val)
|
||||
|
||||
match prop:
|
||||
"behavior/behavior_brush_type":
|
||||
behavior_brush_type = val
|
||||
_emit_property_list_changed_notify()
|
||||
"shape/shape_volume_size":
|
||||
shape_volume_size = val
|
||||
"shape/shape_projection_size":
|
||||
shape_projection_size = val
|
||||
"behavior/behavior_strength":
|
||||
behavior_strength = val
|
||||
"behavior/behavior_passthrough":
|
||||
behavior_passthrough = val
|
||||
"behavior/behavior_overlap_mode":
|
||||
behavior_overlap_mode = val
|
||||
_emit_property_list_changed_notify()
|
||||
"behavior/behavior_no_settings_text":
|
||||
behavior_no_settings_text = val
|
||||
_:
|
||||
return_val = false
|
||||
|
||||
if return_val:
|
||||
emit_changed()
|
||||
|
||||
return return_val
|
||||
|
||||
|
||||
func _get(prop):
|
||||
match prop:
|
||||
"behavior/behavior_brush_type":
|
||||
return behavior_brush_type
|
||||
"shape/shape_volume_size":
|
||||
return shape_volume_size
|
||||
"shape/shape_projection_size":
|
||||
return shape_projection_size
|
||||
"behavior/behavior_strength":
|
||||
return behavior_strength
|
||||
"behavior/behavior_passthrough":
|
||||
return behavior_passthrough
|
||||
"behavior/behavior_overlap_mode":
|
||||
return behavior_overlap_mode
|
||||
"behavior/behavior_no_settings_text":
|
||||
return behavior_no_settings_text
|
||||
|
||||
return null
|
||||
|
||||
|
||||
func _filter_prop_dictionary(prop_dict: Dictionary) -> Dictionary:
|
||||
var props_to_hide := ["behavior/behavior_brush_type"]
|
||||
|
||||
match behavior_overlap_mode:
|
||||
OverlapMode.VOLUME:
|
||||
match behavior_brush_type:
|
||||
BrushType.PAINT:
|
||||
props_to_hide.append_array([
|
||||
"shape/shape_projection_size",
|
||||
"behavior/behavior_passthrough",
|
||||
"behavior/behavior_overlap_mode",
|
||||
"behavior/behavior_no_settings_text"
|
||||
])
|
||||
BrushType.ERASE:
|
||||
props_to_hide.append_array([
|
||||
"shape/shape_projection_size",
|
||||
"behavior/behavior_passthrough",
|
||||
"behavior/behavior_no_settings_text"
|
||||
])
|
||||
BrushType.SINGLE:
|
||||
props_to_hide.append_array([
|
||||
"shape/shape_volume_size",
|
||||
"shape/shape_projection_size",
|
||||
"behavior/behavior_strength",
|
||||
"behavior/behavior_passthrough",
|
||||
"behavior/behavior_overlap_mode",
|
||||
])
|
||||
BrushType.REAPPLY:
|
||||
props_to_hide.append_array([
|
||||
"shape/shape_projection_size",
|
||||
"behavior/behavior_passthrough",
|
||||
"behavior/behavior_no_settings_text"
|
||||
])
|
||||
OverlapMode.PROJECTION:
|
||||
match behavior_brush_type:
|
||||
BrushType.ERASE:
|
||||
props_to_hide.append_array([
|
||||
"shape/shape_volume_size",
|
||||
"behavior/behavior_strength",
|
||||
"behavior/behavior_no_settings_text"
|
||||
])
|
||||
BrushType.REAPPLY:
|
||||
props_to_hide.append_array([
|
||||
"shape/shape_volume_size",
|
||||
"behavior/behavior_strength",
|
||||
"behavior/behavior_no_settings_text"
|
||||
])
|
||||
|
||||
for prop in props_to_hide:
|
||||
prop_dict[prop].usage = PROPERTY_USAGE_NO_EDITOR
|
||||
|
||||
return prop_dict
|
||||
|
||||
|
||||
func _get_prop_dictionary():
|
||||
return {
|
||||
"behavior/behavior_brush_type" : {
|
||||
"name": "behavior/behavior_brush_type",
|
||||
"type": TYPE_INT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": "Paint,Erase,Single,Reapply"
|
||||
},
|
||||
"shape/shape_volume_size" : {
|
||||
"name": "shape/shape_volume_size",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0.0,100.0,0.01,or_greater"
|
||||
},
|
||||
"shape/shape_projection_size" : {
|
||||
"name": "shape/shape_projection_size",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "1.0,1000.0,1.0,or_greater"
|
||||
},
|
||||
"behavior/behavior_strength" : {
|
||||
"name": "behavior/behavior_strength",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0.0,1.0,0.01"
|
||||
},
|
||||
"behavior/behavior_passthrough" : {
|
||||
"name": "behavior/behavior_passthrough",
|
||||
"type": TYPE_BOOL,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
},
|
||||
"behavior/behavior_overlap_mode" : {
|
||||
"name": "behavior/behavior_overlap_mode",
|
||||
"type": TYPE_INT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": "Volume,Projection"
|
||||
},
|
||||
"behavior/behavior_no_settings_text" : {
|
||||
"name": "behavior/behavior_no_settings_text",
|
||||
"type": TYPE_STRING,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
func get_prop_tooltip(prop:String) -> String:
|
||||
match prop:
|
||||
"behavior/behavior_brush_type":
|
||||
return "The brush type enum, that defines it's behavior (paint, erase, etc.)"
|
||||
"shape/shape_volume_size":
|
||||
return "The diameter of this brush, in world units\n" \
|
||||
+ "\n" \
|
||||
+ "Can be edited by dragging in the editor viewport while holding\n" \
|
||||
+ "[brush_prop_edit_button]\n" \
|
||||
+ Globals.AS_IN_SETTINGS_STRING
|
||||
"shape/shape_projection_size":
|
||||
return "The diameter of this brush, in screen pixels\n" \
|
||||
+ "\n" \
|
||||
+ "Can be edited by dragging in the editor viewport while holding\n" \
|
||||
+ "[brush_prop_edit_button]\n" \
|
||||
+ Globals.AS_IN_SETTINGS_STRING
|
||||
"behavior/behavior_strength":
|
||||
return "The plant density multiplier of this brush\n" \
|
||||
+ "\n" \
|
||||
+ "Can be edited by dragging in the editor viewport while holding\n" \
|
||||
+ "[brush_prop_edit_modifier] + [brush_prop_edit_button]\n" \
|
||||
+ Globals.AS_IN_SETTINGS_STRING
|
||||
"behavior/behavior_passthrough":
|
||||
return "The flag, that defines whether this brush can affect plants hidden behind terrain\n" \
|
||||
+ "Only active physics bodies masked by 'Gardening Collision Mask' can occlude plants\n" \
|
||||
+ "In simpler terms: whatever surface volume-brush sticks to, will block a projection-brush as well\n" \
|
||||
+ "\n" \
|
||||
+ "Enabling Passthrough will allow this brush to ignore any collision whatsoever\n" \
|
||||
+ "It also gives better performance when painting since it disables additional collision checks\n"
|
||||
"behavior/behavior_overlap_mode":
|
||||
return "The overlap mode enum, that defines how brush finds which plants to affect\n" \
|
||||
+ "Volume brush exists in 3D world and affects whichever plants it overlaps\n" \
|
||||
+ "Projection brush exists in screen-space and affects all plants that are visually inside it's area\n" \
|
||||
+ "\n" \
|
||||
+ "For normal painting use a Volumetric brush\n" \
|
||||
+ "If you have plants stuck in mid-air (say, you moved the ground beneath them),\n" \
|
||||
+ "Use a Projection brush to remove them (Volumetric brush simply won't reach them)\n" \
|
||||
+ "\n" \
|
||||
+ "Can be edited by pressing\n" \
|
||||
+ "[brush_overlap_mode_button]\n" \
|
||||
+ Globals.AS_IN_SETTINGS_STRING
|
||||
return ""
|
||||
Reference in New Issue
Block a user