@tool extends "../utility/input_field_resource/input_field_resource.gd" #------------------------------------------------------------------------------- # All the data needed to generate a plant, it's transform # And a containing octree #------------------------------------------------------------------------------- var Globals = preload("../utility/globals.gd") const Greenhouse_LODVariant = preload("greenhouse_LOD_variant.gd") enum ScalingType {UNIFORM, FREE, LOCK_XY, LOCK_ZY, LOCK_XZ} enum DirectionVectorType {UNUSED, WORLD_X, WORLD_Y, WORLD_Z, NORMAL, CUSTOM} # Different LODs for a plant starting from the most detailed var mesh_LOD_variants:Array = [] # Keep a reference to selected resource to easily display it var selected_for_edit_resource:Resource = null # Distance after which the final LOD is shown var mesh_LOD_max_distance:float = 10.0 # Distance after which the mesh is hidden var mesh_LOD_kill_distance:float = -1.0 # How many members fit into an octree node before it's subdivided var mesh_LOD_max_capacity:int = 75 # Minimum size of an octree node. Will not subdivide after this treshold # Chunks of minimum size might contain members beyond the capacity limit var mesh_LOD_min_size:float = 1.0 # A dummy variable to show a dialog for recreating an octree var octree_reconfigure_button:bool = false # A dummy variable to recenter an octree var octree_recenter_button:bool = false # How many members spawn in a 2D square of PLANT_DENSITY_UNITS aligned to a brush # These members will then be projected onto a surface var density_per_units:float = 100.0 # How we constraint the scale (uniform scale, unrestricted or to specific 3D plane) var scale_scaling_type:int = ScalingType.UNIFORM # A range of scale to randomize into var scale_range:Array = [Vector3(1,1,1), Vector3(1,1,1)] # How we choose the primary up vector var up_vector_primary_type:int = DirectionVectorType.WORLD_Y # Custom value for primary up vector if enabled var up_vector_primary:Vector3 = Vector3() # How we choose the secondary up vector var up_vector_secondary_type:int = DirectionVectorType.WORLD_Y # Custom value for secondary up vector if enabled var up_vector_secondary:Vector3 = Vector3() # Weight for blending between two up vectors var up_vector_blending:float = 0.0 # How we choose the primary forward vector var fwd_vector_primary_type:int = DirectionVectorType.UNUSED # Custom value for primary forward vector if enabled var fwd_vector_primary:Vector3 = Vector3() # How we choose the secondary forward vector var fwd_vector_secondary_type:int = DirectionVectorType.UNUSED # Custom value for secondary forward vector if enabled var fwd_vector_secondary:Vector3 = Vector3() # Weight for blending between two forward vectors var fwd_vector_blending:float = 0.0 # A range of vertical scale to randomize into (local to plant y axis) var offset_y_range:Array = [0.0, 0.0] # A random horizontal jitter applied when placing each instance # A fraction of a cell, that plant is placed in (i.e. is in range (0.0, 1.0) var offset_jitter_fraction:float = 0.6 # Random rotation in y axis (will be between (-val, val)) var rotation_random_y:float = 180.0 # Random rotation in x axis (will be between (-val, val)) var rotation_random_x:float = 0.0 # Random rotation in z axis (will be between (-val, val)) var rotation_random_z:float = 0.0 # Limits placement onto a sloped surface # "Sloped" in relation to the chosen primary up vector var slope_allowed_range:Array = [0.0, 180.0] # A dummy variable to export plant data var import_export_import_plant_data_button:bool = false # A dummy variable to import plant data var import_export_export_plant_data_button:bool = false # A dummy variable to export greenhouse data var import_export_import_greenhouse_data_button:bool = false # A dummy variable to import greenhouse data var import_export_export_greenhouse_data_button:bool = false var total_instances_in_gardener:int = 0 var select_container = null var settings_container = null signal prop_action_executed_on_LOD_variant(prop_action, final_val, LOD_variant) signal req_octree_reconfigure signal req_octree_recenter signal req_import_plant_data signal req_export_plant_data signal req_import_greenhouse_data signal req_export_greenhouse_data #------------------------------------------------------------------------------- # Initialization #------------------------------------------------------------------------------- func _init(): input_field_blacklist = ["mesh/mesh_LOD_max_capacity", "mesh/mesh_LOD_min_size"] super() set_meta("class", "Greenhouse_Plant") resource_name = "Greenhouse_Plant" _add_prop_dependency("mesh/mesh_LOD_kill_distance", ["mesh/mesh_LOD_max_distance"]) _add_prop_dependency("scale/scale_range", ["scale/scale_scaling_type"]) _add_res_edit_source_array("mesh/mesh_LOD_variants", "mesh/selected_for_edit_resource") func _create_input_field(_base_control:Control, _resource_previewer, prop:String) -> UI_InputField: var input_field:UI_InputField = null match prop: "mesh/mesh_LOD_variants": var accepted_classes := [Greenhouse_LODVariant, PackedScene, Mesh] var settings := { "add_create_inst_button": true, "accepted_classes": accepted_classes, "element_display_size": 75 * FunLib.get_setting_safe("dreadpons_spatial_gardener/input_and_ui/greenhouse_thumbnail_scale", 1.0), "element_interaction_flags": UI_IF_ThumbnailArray.PRESET_LOD_VARIANT, } input_field = UI_IF_ThumbnailArray.new(mesh_LOD_variants, "LOD Variants", prop, settings) "mesh/selected_for_edit_resource": var settings := { "label_visibility": false, "tab": 1 } input_field = UI_IF_Object.new(selected_for_edit_resource, "LOD Variant", prop, settings) "mesh/mesh_LOD_max_distance": var max_value = FunLib.get_setting_safe("dreadpons_spatial_gardener/input_and_ui/plant_max_distance_slider_max_value", 1000.0) var settings := {"min": 0.0, "max": max_value, "step": 0.01, "allow_greater": true, "allow_lesser": false,} input_field = UI_IF_RealSlider.new(mesh_LOD_max_distance, "LOD Max Distance", prop, settings) "mesh/mesh_LOD_kill_distance": var max_value = FunLib.get_setting_safe("dreadpons_spatial_gardener/input_and_ui/plant_kill_distance_slider_max_value", 2000.0) var settings := {"min": -1.0, "max": max_value, "step": 0.01, "allow_greater": true, "allow_lesser": false,} input_field = UI_IF_RealSlider.new(mesh_LOD_kill_distance, "LOD Kill Distance", prop, settings) #====================================================== "mesh/mesh_LOD_max_capacity": input_field = UI_IF_IntLineEdit.new(mesh_LOD_max_capacity, "Max chunk capacity", prop) "mesh/mesh_LOD_min_size": var max_value = FunLib.get_setting_safe("dreadpons_spatial_gardener/input_and_ui/octree_min_node_size_slider_max_value", 500.0) var settings := {"min": 0.0, "max": max_value, "step": 0.01, "allow_greater": true, "allow_lesser": false,} input_field = UI_IF_RealSlider.new(mesh_LOD_min_size, "Min node size", prop, settings) "octree/octree_reconfigure_button": var bound_input_fields:Array = create_input_fields( _base_control, _resource_previewer, ["mesh/mesh_LOD_max_capacity", "mesh/mesh_LOD_min_size"]).values() var settings := { "button_text": "Configure Octree", "bound_input_fields": bound_input_fields } input_field = UI_IF_ApplyChanges.new(octree_reconfigure_button, "Octree Configuration", prop, settings) input_field.applied_changes.connect(on_dialog_if_applied_changes.bind(input_field)) input_field.canceled_changes.connect(on_dialog_if_canceled_changes.bind(input_field)) "octree/octree_recenter_button": var settings := {"button_text": "Recenter Octree"} input_field = UI_IF_Button.new(octree_recenter_button, "Octree Centring", prop, settings) input_field.pressed.connect(on_if_button.bind(input_field)) #====================================================== "density/density_per_units": var max_value = FunLib.get_setting_safe("dreadpons_spatial_gardener/input_and_ui/plant_density_slider_max_value", 2000.0) var settings := {"min": 0.0, "max": max_value, "step": 0.01, "allow_greater": true, "allow_lesser": false,} var field_name = "Plants Per %d Unit" % [Globals.PLANT_DENSITY_UNITS] if Globals.PLANT_DENSITY_UNITS != 1: field_name += "s" input_field = UI_IF_RealSlider.new(density_per_units, field_name, prop, settings) #====================================================== "scale/scale_scaling_type": var settings := {"enum_list": FunLib.capitalize_string_array(ScalingType.keys())} input_field = UI_IF_Enum.new(scale_scaling_type, "Scaling Type", prop, settings) "scale/scale_range": var settings := { "is_range": true, "value_count": 3, "representation_type": UI_IF_MultiRange.RepresentationType.VECTOR, } input_field = UI_IF_MultiRange.new(scale_range, "Random Scale Range", prop, settings) #====================================================== "up_vector/up_vector_primary_type": var settings := {"enum_list": FunLib.capitalize_string_array(DirectionVectorType.keys())} input_field = UI_IF_Enum.new(up_vector_primary_type, "Primary Up-Vector", prop, settings) "up_vector/up_vector_primary": var settings := { "is_range": false, "value_count": 3, "representation_type": UI_IF_MultiRange.RepresentationType.VECTOR, } input_field = UI_IF_MultiRange.new(up_vector_primary, "Up-Vector Primary", prop, settings) "up_vector/up_vector_secondary_type": var settings := {"enum_list": FunLib.capitalize_string_array(DirectionVectorType.keys())} input_field = UI_IF_Enum.new(up_vector_secondary_type, "Secondary Up-Vector", prop, settings) "up_vector/up_vector_secondary": var settings := { "is_range": false, "value_count": 3, "representation_type": UI_IF_MultiRange.RepresentationType.VECTOR, } input_field = UI_IF_MultiRange.new(up_vector_secondary, "Up-Vector Secondary", prop, settings) "up_vector/up_vector_blending": var settings := {"min": 0.0, "max": 1.0, "step": 0.01, "allow_greater": false, "allow_lesser": false,} input_field = UI_IF_RealSlider.new(up_vector_blending, "Up-Vector Blending", prop, settings) #====================================================== "fwd_vector/fwd_vector_primary_type": var settings := {"enum_list": FunLib.capitalize_string_array(DirectionVectorType.keys())} input_field = UI_IF_Enum.new(fwd_vector_primary_type, "Primary Forward-Vector", prop, settings) "fwd_vector/fwd_vector_primary": var settings := { "is_range": false, "value_count": 3, "representation_type": UI_IF_MultiRange.RepresentationType.VECTOR, } input_field = UI_IF_MultiRange.new(fwd_vector_primary, "Forward-Vector Primary", prop, settings) "fwd_vector/fwd_vector_secondary_type": var settings := {"enum_list": FunLib.capitalize_string_array(DirectionVectorType.keys())} input_field = UI_IF_Enum.new(fwd_vector_secondary_type, "Secondary Forward-Vector", prop, settings) "fwd_vector/fwd_vector_secondary": var settings := { "is_range": false, "value_count": 3, "representation_type": UI_IF_MultiRange.RepresentationType.VECTOR, } input_field = UI_IF_MultiRange.new(fwd_vector_secondary, "Forward-Vector Secondary", prop, settings) "fwd_vector/fwd_vector_blending": var settings := {"min": 0.0, "max": 1.0, "step": 0.01, "allow_greater": false, "allow_lesser": false,} input_field = UI_IF_RealSlider.new(fwd_vector_blending, "Forward-Vector Blending", prop, settings) #====================================================== "offset/offset_y_range": var settings := { "is_range": true, "value_count": 1, "representation_type": UI_IF_MultiRange.RepresentationType.VALUE, } input_field = UI_IF_MultiRange.new(offset_y_range, "Random Offset Range Y", prop, settings) "offset/offset_jitter_fraction": var settings := {"min": 0.0, "max": 1.0, "step": 0.01, "allow_greater": false, "allow_lesser": false,} input_field = UI_IF_RealSlider.new(offset_jitter_fraction, "Offset Jitter Fraction", prop, settings) #====================================================== "rotation/rotation_random_y": var settings := {"min": 0.0, "max": 180.0, "step": 0.01, "allow_greater": false, "allow_lesser": false,} input_field = UI_IF_RealSlider.new(rotation_random_y, "Random Rotation Y", prop, settings) "rotation/rotation_random_x": var settings := {"min": 0.0, "max": 180.0, "step": 0.01, "allow_greater": false, "allow_lesser": false,} input_field = UI_IF_RealSlider.new(rotation_random_x, "Random Rotation X", prop, settings) "rotation/rotation_random_z": var settings := {"min": 0.0, "max": 180.0, "step": 0.01, "allow_greater": false, "allow_lesser": false,} input_field = UI_IF_RealSlider.new(rotation_random_x, "Random Rotation Z", prop, settings) #====================================================== "slope/slope_allowed_range": var settings := { "is_range": true, "value_count": 1, "representation_type": UI_IF_MultiRange.RepresentationType.VALUE, } input_field = UI_IF_MultiRange.new(slope_allowed_range, "Allowed Slope Range", prop, settings) #====================================================== "import_export/import_plant_data_button": var settings := {"button_text": "Import"} input_field = UI_IF_Button.new(import_export_import_plant_data_button, "Import Plant Data", prop, settings) input_field.pressed.connect(on_if_button.bind(input_field)) "import_export/export_plant_data_button": var settings := {"button_text": "Export"} input_field = UI_IF_Button.new(import_export_export_plant_data_button, "Export Plant Data", prop, settings) input_field.pressed.connect(on_if_button.bind(input_field)) "import_export/import_greenhouse_data_button": var settings := {"button_text": "Import"} input_field = UI_IF_Button.new(import_export_import_greenhouse_data_button, "Import Greenhouse Data", prop, settings) input_field.pressed.connect(on_if_button.bind(input_field)) "import_export/export_greenhouse_data_button": var settings := {"button_text": "Export"} input_field = UI_IF_Button.new(import_export_export_greenhouse_data_button, "Export Greenhouse Data", prop, settings) input_field.pressed.connect(on_if_button.bind(input_field)) return input_field #------------------------------------------------------------------------------- # Signal forwarding #------------------------------------------------------------------------------- func on_changed_LOD_variant(): emit_changed() func reconfigure_octree(): req_octree_reconfigure.emit() #------------------------------------------------------------------------------- # Prop actions #------------------------------------------------------------------------------- # A prop action was executed on one of the LOD variants # If a mesh was changed - update thumbnail # If a spawned spatial was changed - spawn or deelte them func on_prop_action_executed_on_LOD_variant(prop_action, final_val, LOD_variant): var index = mesh_LOD_variants.find(LOD_variant) var update_thumbnail = prop_action.prop == "mesh" if update_thumbnail: prop_action_executed.emit(PA_ArraySet.new("mesh/mesh_LOD_variants", LOD_variant, index), mesh_LOD_variants) prop_action_executed_on_LOD_variant.emit(prop_action, final_val, LOD_variant) #------------------------------------------------------------------------------- # UI management #------------------------------------------------------------------------------- # Handle changes applied by input field dialog func on_dialog_if_applied_changes(initial_values:Array, final_values:Array, input_field:UI_InputField): match input_field.prop_name: "octree/octree_reconfigure_button": UndoRedoInterface.create_action(_undo_redo, "Reconfigure Octree", 0, false, self) UndoRedoInterface.add_do_method(_undo_redo, input_field.set_values.bind(final_values)) UndoRedoInterface.add_do_method(_undo_redo, self.reconfigure_octree) UndoRedoInterface.add_undo_method(_undo_redo, input_field.set_values.bind(initial_values)) UndoRedoInterface.add_undo_method(_undo_redo, self.reconfigure_octree) UndoRedoInterface.commit_action(_undo_redo, true) # Handle changes canceled by input field dialog func on_dialog_if_canceled_changes(input_field:UI_InputField): pass # Handle an input field button press func on_if_button(input_field:UI_InputField): match input_field.prop_name: "octree/octree_recenter_button": req_octree_recenter.emit() "import_export/import_plant_data_button": req_import_plant_data.emit() "import_export/export_plant_data_button": req_export_plant_data.emit() "import_export/import_greenhouse_data_button": req_import_greenhouse_data.emit() "import_export/export_greenhouse_data_button": req_export_greenhouse_data.emit() #------------------------------------------------------------------------------- # Property management #------------------------------------------------------------------------------- func _modify_prop(prop:String, val): match prop: "mesh/mesh_LOD_variants": # TODO retain Greenhouse_LODVariant if it already exists when drag-and-dropping a .mesh or a .tscn resource for i in range(0, val.size()): if !is_instance_of(val[i], Greenhouse_LODVariant): val[i] = Greenhouse_LODVariant.new() FunLib.ensure_signal(val[i].changed, on_changed_LOD_variant) FunLib.ensure_signal(val[i].prop_action_executed, on_prop_action_executed_on_LOD_variant, [val[i]]) if val[i]._undo_redo != _undo_redo: val[i].set_undo_redo(_undo_redo) "scale/scale_range": val = val.duplicate() match scale_scaling_type: ScalingType.UNIFORM: val[0].y = val[0].x val[0].z = val[0].x val[1].y = val[1].x val[1].z = val[1].x ScalingType.LOCK_XY: val[0].y = val[0].x val[1].y = val[1].x ScalingType.LOCK_XZ: val[0].z = val[0].x val[1].z = val[1].x ScalingType.LOCK_ZY: val[0].z = val[0].y val[1].z = val[1].y "mesh/mesh_LOD_kill_distance": if val < mesh_LOD_max_distance && val >= 0: val = mesh_LOD_max_distance # "mesh/mesh_LOD_max_distance": # if mesh_LOD_kill_distance < val: # mesh_LOD_kill_distance = val "slope/slope_allowed_range": for i in range(0, val.size()): var element = val[i] if element < 0.0: val[i] = 0.0 elif element > 180.0: val[i] = 180.0 return val # A special override just for the plants # To allow drag-and-dropping .mesh and .tscn instead of editing a resource func request_prop_action(prop_action:PropAction): match prop_action.prop: "mesh/mesh_LOD_variants": if is_instance_of(prop_action, PA_ArraySet): var new_prop_action = null if is_instance_of(prop_action.val, PackedScene): new_prop_action = PA_PropSet.new("spawned_spatial", prop_action.val) elif is_instance_of(prop_action.val, Mesh): new_prop_action = PA_PropSet.new("mesh", prop_action.val) # else: # for mesh_class in Globals.MESH_CLASSES: # if FunLib.obj_is_class_string(prop_action.val, mesh_class): # new_prop_action = PA_PropSet.new("mesh", prop_action.val) # break if new_prop_action != null: mesh_LOD_variants[prop_action.index].request_prop_action(new_prop_action) return on_prop_action_requested(prop_action) #------------------------------------------------------------------------------- # Property export #------------------------------------------------------------------------------- func set_undo_redo(val): super.set_undo_redo(val) for LOD_variant in mesh_LOD_variants: LOD_variant.set_undo_redo(_undo_redo) func _set(prop, val): var return_val = true val = _modify_prop(prop, val) match prop: "mesh/mesh_LOD_variants": mesh_LOD_variants = val "mesh/selected_for_edit_resource": selected_for_edit_resource = val "mesh/mesh_LOD_max_distance": mesh_LOD_max_distance = val "mesh/mesh_LOD_kill_distance": mesh_LOD_kill_distance = val "mesh/mesh_LOD_max_capacity": mesh_LOD_max_capacity = val "mesh/mesh_LOD_min_size": mesh_LOD_min_size = val "octree/octree_reconfigure_button": octree_reconfigure_button = val "octree/octree_recenter_button": octree_recenter_button = val "density/density_per_units": density_per_units = val "scale/scale_scaling_type": scale_scaling_type = val _emit_property_list_changed_notify() "scale/scale_range": scale_range = val "up_vector/up_vector_primary_type": up_vector_primary_type = val _emit_property_list_changed_notify() "up_vector/up_vector_primary": up_vector_primary = val "up_vector/up_vector_secondary_type": up_vector_secondary_type = val _emit_property_list_changed_notify() "up_vector/up_vector_secondary": up_vector_secondary = val "up_vector/up_vector_blending": up_vector_blending = val "fwd_vector/fwd_vector_primary_type": fwd_vector_primary_type = val _emit_property_list_changed_notify() "fwd_vector/fwd_vector_primary": fwd_vector_primary = val "fwd_vector/fwd_vector_secondary_type": fwd_vector_secondary_type = val _emit_property_list_changed_notify() "fwd_vector/fwd_vector_secondary": fwd_vector_secondary = val "fwd_vector/fwd_vector_blending": fwd_vector_blending = val "offset/offset_y_range": offset_y_range = val "offset/offset_jitter_fraction": offset_jitter_fraction = val "rotation/rotation_random_y": rotation_random_y = val "rotation/rotation_random_x": rotation_random_x = val "rotation/rotation_random_z": rotation_random_z = val "slope/slope_allowed_range": slope_allowed_range = val "import_export/import_plant_data_button": import_export_import_plant_data_button = val "import_export/export_plant_data_button": import_export_export_plant_data_button = val "import_export/import_greenhouse_data_button": import_export_import_greenhouse_data_button = val "import_export/export_greenhouse_data_button": import_export_export_greenhouse_data_button = val _: return_val = false if return_val: emit_changed() return return_val func _get(property): match property: "mesh/mesh_LOD_variants": return mesh_LOD_variants "mesh/selected_for_edit_resource": return selected_for_edit_resource "mesh/mesh_LOD_max_distance": return mesh_LOD_max_distance "mesh/mesh_LOD_kill_distance": return mesh_LOD_kill_distance "mesh/mesh_LOD_max_capacity": return mesh_LOD_max_capacity "mesh/mesh_LOD_min_size": return mesh_LOD_min_size "octree/octree_reconfigure_button": return octree_reconfigure_button "octree/octree_recenter_button": return octree_recenter_button "density/density_per_units": return density_per_units "scale/scale_scaling_type": return scale_scaling_type "scale/scale_range": return scale_range "up_vector/up_vector_primary_type": return up_vector_primary_type "up_vector/up_vector_primary": return up_vector_primary "up_vector/up_vector_secondary_type": return up_vector_secondary_type "up_vector/up_vector_secondary": return up_vector_secondary "up_vector/up_vector_blending": return up_vector_blending "fwd_vector/fwd_vector_primary_type": return fwd_vector_primary_type "fwd_vector/fwd_vector_primary": return fwd_vector_primary "fwd_vector/fwd_vector_secondary_type": return fwd_vector_secondary_type "fwd_vector/fwd_vector_secondary": return fwd_vector_secondary "fwd_vector/fwd_vector_blending": return fwd_vector_blending "offset/offset_y_range": return offset_y_range "offset/offset_jitter_fraction": return offset_jitter_fraction "rotation/rotation_random_y": return rotation_random_y "rotation/rotation_random_x": return rotation_random_x "rotation/rotation_random_z": return rotation_random_z "slope/slope_allowed_range": return slope_allowed_range "import_export/import_plant_data_button": return import_export_import_plant_data_button "import_export/export_plant_data_button": return import_export_export_plant_data_button "import_export/import_greenhouse_data_button": return import_export_import_greenhouse_data_button "import_export/export_greenhouse_data_button": return import_export_export_greenhouse_data_button return null func _filter_prop_dictionary(prop_dict: Dictionary) -> Dictionary: var props_to_hide := [] if up_vector_primary_type != DirectionVectorType.CUSTOM: props_to_hide.append("up_vector/up_vector_primary") if up_vector_secondary_type != DirectionVectorType.CUSTOM: props_to_hide.append("up_vector/up_vector_secondary") if fwd_vector_primary_type != DirectionVectorType.CUSTOM: props_to_hide.append("fwd_vector/fwd_vector_primary") if fwd_vector_secondary_type != DirectionVectorType.CUSTOM: props_to_hide.append("fwd_vector/fwd_vector_secondary") if up_vector_primary_type == up_vector_secondary_type && up_vector_primary_type != DirectionVectorType.CUSTOM: props_to_hide.append("up_vector/up_vector_blending") if fwd_vector_primary_type == fwd_vector_secondary_type && fwd_vector_primary_type != DirectionVectorType.CUSTOM: props_to_hide.append("fwd_vector/fwd_vector_blending") for prop in props_to_hide: prop_dict[prop].usage = PROPERTY_USAGE_NO_EDITOR return prop_dict func _get_prop_dictionary(): return { "mesh/mesh_LOD_variants": { "name": "mesh/mesh_LOD_variants", "type": TYPE_ARRAY, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "mesh/selected_for_edit_resource": { "name": "mesh/selected_for_edit_resource", "type": TYPE_OBJECT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "mesh/mesh_LOD_max_distance": { "name": "mesh/mesh_LOD_max_distance", "type": TYPE_FLOAT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "mesh/mesh_LOD_kill_distance": { "name": "mesh/mesh_LOD_kill_distance", "type": TYPE_FLOAT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "mesh/mesh_LOD_max_capacity": { "name": "mesh/mesh_LOD_max_capacity", "type": TYPE_INT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "mesh/mesh_LOD_min_size": { "name": "mesh/mesh_LOD_min_size", "type": TYPE_FLOAT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "octree/octree_reconfigure_button": { "name": "octree/octree_reconfigure_button", "type": TYPE_BOOL, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "octree/octree_recenter_button": { "name": "octree/octree_recenter_button", "type": TYPE_BOOL, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, #====================================================== "density/density_per_units": { "name": "density/density_per_units", "type": TYPE_FLOAT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, #====================================================== "scale/scale_scaling_type": { "name": "scale/scale_scaling_type", "type": TYPE_INT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_ENUM, "hint_string": "Uniform,Free,Lock XY,Lock ZY,Lock XZ" }, "scale/scale_range": { "name": "scale/scale_range", "type": TYPE_ARRAY, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, #====================================================== "up_vector/up_vector_primary_type": { "name": "up_vector/up_vector_primary_type", "type": TYPE_INT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_ENUM, "hint_string": "Unused,World X,World Y,World Z,Normal,Custom" }, "up_vector/up_vector_primary": { "name": "up_vector/up_vector_primary", "type": TYPE_VECTOR3, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "up_vector/up_vector_secondary_type": { "name": "up_vector/up_vector_secondary_type", "type": TYPE_INT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_ENUM, "hint_string": "Unused,World X,World Y,World Z,Normal,Custom" }, "up_vector/up_vector_secondary": { "name": "up_vector/up_vector_secondary", "type": TYPE_VECTOR3, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "up_vector/up_vector_blending": { "name": "up_vector/up_vector_blending", "type": TYPE_FLOAT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_RANGE, "hint_string": "0.0,1.0" }, #====================================================== "fwd_vector/fwd_vector_primary_type": { "name": "fwd_vector/fwd_vector_primary_type", "type": TYPE_INT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_ENUM, "hint_string": "Unused,World X,World Y,World Z,Normal,Custom" }, "fwd_vector/fwd_vector_primary": { "name": "fwd_vector/fwd_vector_primary", "type": TYPE_VECTOR3, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "fwd_vector/fwd_vector_secondary_type": { "name": "fwd_vector/fwd_vector_secondary_type", "type": TYPE_INT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_ENUM, "hint_string": "Unused,World X,World Y,World Z,Normal,Custom" }, "fwd_vector/fwd_vector_secondary": { "name": "fwd_vector/fwd_vector_secondary", "type": TYPE_VECTOR3, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "fwd_vector/fwd_vector_blending": { "name": "fwd_vector/fwd_vector_blending", "type": TYPE_FLOAT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_RANGE, "hint_string": "0.0,1.0" }, #====================================================== "offset/offset_y_range": { "name": "offset/offset_y_range", "type": TYPE_ARRAY, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "offset/offset_jitter_fraction": { "name": "offset/offset_jitter_fraction", "type": TYPE_FLOAT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, #====================================================== "rotation/rotation_random_y": { "name": "rotation/rotation_random_y", "type": TYPE_FLOAT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_RANGE, "hint_string": "0.0,180.0" }, "rotation/rotation_random_x": { "name": "rotation/rotation_random_x", "type": TYPE_FLOAT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_RANGE, "hint_string": "0.0,180.0" }, "rotation/rotation_random_z": { "name": "rotation/rotation_random_z", "type": TYPE_FLOAT, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_RANGE, "hint_string": "0.0,180.0" }, #====================================================== "slope/slope_allowed_range": { "name": "slope/slope_allowed_range", "type": TYPE_ARRAY, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, #====================================================== "import_export/import_plant_data_button": { "name": "import_export/import_plant_data_button", "type": TYPE_BOOL, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "import_export/export_plant_data_button": { "name": "import_export/export_plant_data_button", "type": TYPE_BOOL, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "import_export/import_greenhouse_data_button": { "name": "import_export/import_greenhouse_data_button", "type": TYPE_BOOL, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, "import_export/export_greenhouse_data_button": { "name": "import_export/export_greenhouse_data_button", "type": TYPE_BOOL, "usage": PROPERTY_USAGE_DEFAULT, "hint": PROPERTY_HINT_NONE }, } func _fix_duplicate_signals(copy): copy._modify_prop("mesh/mesh_LOD_variants", copy.mesh_LOD_variants) copy.selected_for_edit_resource = null func get_prop_tooltip(prop:String) -> String: match prop: "mesh/mesh_LOD_variants": return "The array of Level Of Detail resources that's used to swap meshes depending on distance to the camera\n" \ + "This allows to have high-detailed meshes when the player is close, but low-detailed when they're far way\n" \ + "This technique is used to optimize performance\n" \ + "\n" \ + "This property supports drag-and-drop of Greenhouse_LODVariant resources\n" \ + "As well as .mesh and .tscn resources. In this case, Greenhouse_LODVariant will be created automatically" "mesh/selected_for_edit_resource": return "The plant currently selected for editing" "mesh/mesh_LOD_max_distance": return "The distance after which the lowest-detailed LOD (last one in the array) is chosen\n" \ + "LODs in-between are spread evenly across this distance\n" "mesh/mesh_LOD_kill_distance": return "The distance after which the mesh and it's Spawned Node3D are removed entirely\n" \ + "Used to save perfomance by rejecting small objects like grass or rocks at big distances\n" \ + "A default value of '-1' disables this behavior (the object will be active forever)" "mesh/mesh_LOD_max_capacity": return "The soft limit of how many instances can an octree node contain before it is subdivided\n" \ + "This can be bypassed by the node minimum size" "mesh/mesh_LOD_min_size": return "The hard limit of how small an octree node can get\n" \ + "If both capacity and size limits are approached, only the size will be honored\n" \ + "I.e. octree nodes will contain more instances than their capacity allows" "octree/octree_reconfigure_button": return "The button to reconfigure octree nodes' capacity and minimum size" "octree/octree_recenter_button": return "The button to recenter the entire octree according to the space it's instances occupy\n" \ + "This operation is recommended once you finished painting your plants\n" \ + "As it optimizes both the performance (slightly) and the LOD switching behavior (more importantly)" "density/density_per_units": return "How many plants will be placed in a square of %dx%d units\n" % [Globals.PLANT_DENSITY_UNITS, Globals.PLANT_DENSITY_UNITS] \ + "This is an approximate amount and it should not be taken as an absolute\n" "scale/scale_scaling_type": return "Defines the rules for scaling randomization\n" \ + "By default it scales uniformly, meaning all axes will have the same value\n" \ + "You can remove the lock entirely and allow each axis to have it's own randomized value (typically results in wonky-looking meshes)\n" \ + "Or you can lock one of the planes\n" \ + "e.g. XZ-lock with make horizontal scaling uniform (plants won't appear twisted), but allow varying vertical scaling" "scale/scale_range": return "The range in which scaling is randomized. Honors the scaling type\n" \ + "When any scaling lock is active (including Uniform), only the first variable can be edited, the rest will update automatically" "up_vector/up_vector_primary_type": return "Defines the source for the up vector used to orient our plant\n" \ + "Can use world-based vectors, normals of the surface or a custom vector in world-space" "up_vector/up_vector_primary": return "The custom value to be used as primary up vector" "up_vector/up_vector_secondary_type": return "Defines the source for second vector to be used in blending/interpolation\n" \ + "Can use world-based vectors, normals of the surface or a custom vector in world-space" "up_vector/up_vector_secondary": return "The custom value to be used as secondary up vector" "up_vector/up_vector_blending": return "How much we blend between our vectors\n" \ + "0.0 - use primary, 0.5 - use in-between the two, 1.0 - use secondary and so on\n" \ + "Is used to specify the main orientation for our plant and then a small inclination towards something\n" \ + "E.g. a tree that grows upward, but is slightly tilted with it's surface" "fwd_vector/fwd_vector_primary_type": return "Defines the source for the forward vector used to orient our plant\n" \ + "Can use world-based vectors, normals of the surface or a custom vector in world-space" "fwd_vector/fwd_vector_primary": return "The custom value to be used as primary forward vector" "fwd_vector/fwd_vector_secondary_type": return "Defines the source for second vector to be used in blending/interpolation\n" \ + "Can use world-based vectors, normals of the surface or a custom vector in world-space" "fwd_vector/fwd_vector_secondary": return "The custom value to be used as secondary forward vector" "fwd_vector/fwd_vector_blending": return "How much we blend between our vectors\n" \ + "0.0 - use primary, 0.5 - use in-between the two, 1.0 - use secondary and so on\n" \ + "Is used to specify the main orientation for our plant and then a small inclination towards something\n" \ + "E.g. a signpost that points forward in world space, but is slightly tilted to it's target" "offset/offset_y_range": return "The range of random vertical offset in local space\n" \ + "Used to hide things like roots below the surface" "offset/offset_jitter_fraction": return "The random 'cell offset' for each instance\n" \ + "All instances are placed on a virtual grid, and with a jitter of 0.0 will appear placed using a ruler\n" \ + "Jitter specifies how far in local space of each cell can an instance be offset\n" \ + "E.g. 0.0 - instance in the center, 0.5 - instance can be halfway to the cell's border, 1.0 - instance can appear on the border\n" \ + "The values of 0.0 and 1.0 should generally be avoided" "rotation/rotation_random_y": return "The range of random rotation on Y axis (Yaw)\n" \ + "E.g. 45 means it can be rotated to a random angle between 45 degress clokwise and counter clockwise" "rotation/rotation_random_x": return "The range of random rotation on X axis (Pitch)\n" \ + "E.g. 45 means it can be rotated to a random angle between 45 degress clokwise and counter clockwise" "rotation/rotation_random_z": return "The range of random rotation on Z axis (Roll)\n" \ + "E.g. 45 means it can be rotated to a random angle between 45 degress clokwise and counter clockwise" "slope/slope_allowed_range": return "The range of slopes (in degrees) where our plant can be placed\n" \ + "Can be used to avoid placing plants on steep slopes or vertical walls\n" \ + "\n" \ + "NOTE: slope is calculated in relation to the Primary Up Vector\n" \ + "If you wish to align your plant to Surface Normal and use the slope\n" \ + "Set Primary Up Vector to World Y, secondary to Normal and just blend all the way to the secondary vector (blend = 1.0)" "import_export/import_plant_data_button": return "The button to import plant settings and transforms\n" \ + "For the current plant inside a current Gardener\n" \ + "Instances are ADDED to the existing ones; to replace you'll need to manually erase the old instances first\n" \ + "\n" \ + "NOTE: import recreates your octree nodes anew and they won't be the same\n" \ + "(but they were killed already by an export operation to begin with)\n" \ + "Most of the time this can be ignored since you likely Rebuild/Recenter your octrees on a regular basis anyway" "import_export/export_plant_data_button": return "The button to export plant settings and transforms\n" \ + "Of current plant inside a current Gardener\n" \ + "To import them to a different scene\n" \ + "Or when switching between plugin versions (whenever necessary)\n" \ + "\n" \ + "NOTE: export kills whatever octree nodes you have\n" \ + "(and import recreates them anew but they won't be the same)\n" \ + "Most of the time this can be ignored since you likely Rebuild/Recenter your octrees on a regular basis anyway" "import_export/import_greenhouse_data_button": return "The button to import all greenhouse plant settings and transforms\n" \ + "Inside a current Gardener\n" \ + "Instances are ADDED to the existing ones; to replace you'll need to manually erase the old instances first\n" \ + "\n" \ + "NOTE: import recreates your octree nodes anew and they won't be the same\n" \ + "(but they were killed already by an export operation to begin with)\n" \ + "Most of the time this can be ignored since you likely Rebuild/Recenter your octrees on a regular basis anyway" "import_export/export_greenhouse_data_button": return "The button to export all greenhouse plant settings and transforms\n" \ + "Inside a current Gardener\n" \ + "To import them to a different scene\n" \ + "Or when switching between plugin versions (whenever necessary)\n" \ + "\n" \ + "NOTE: export kills whatever octree nodes you have\n" \ + "(and import recreates them anew but they won't be the same)\n" \ + "Most of the time this can be ignored since you likely Rebuild/Recenter your octrees on a regular basis anyway" return ""