881 lines
34 KiB
GDScript
881 lines
34 KiB
GDScript
# plugin.gd
|
|
# This file is part of: SimpleGrassTextured
|
|
# Copyright (c) 2023 IcterusGames
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining
|
|
# a copy of this software and associated documentation files (the
|
|
# "Software"), to deal in the Software without restriction, including
|
|
# without limitation the rights to use, copy, modify, merge, publish,
|
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
# permit persons to whom the Software is furnished to do so, subject to
|
|
# the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be
|
|
# included in all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
@tool
|
|
extends EditorPlugin
|
|
|
|
const DEFAULT_POINTER_DEPTH := 10.0
|
|
|
|
enum EVENT_MOUSE {
|
|
EVENT_NONE,
|
|
EVENT_MOVE,
|
|
EVENT_CLICK,
|
|
}
|
|
|
|
enum TOOL {
|
|
NONE,
|
|
PENCIL,
|
|
AIRBRUSH,
|
|
ERASER
|
|
}
|
|
|
|
enum TOOL_SHAPE {
|
|
SPHERE,
|
|
CYLINDER,
|
|
CYLINDER_INF_H,
|
|
BOX,
|
|
BOX_INF_H
|
|
}
|
|
|
|
var _raycast_3d : RayCast3D = null
|
|
var _pointer_decal : Decal = null
|
|
var _pointer_img_circle = load("res://addons/simplegrasstextured/images/pointer.png")
|
|
var _pointer_img_rect = load("res://addons/simplegrasstextured/images/pointer_rect.png")
|
|
var _pointer_depth: int = DEFAULT_POINTER_DEPTH
|
|
var _pointer_rotate: bool = true
|
|
var _grass_selected = null
|
|
var _position_draw := Vector3.ZERO
|
|
var _normal_draw := Vector3.ZERO
|
|
var _object_draw : Object = null
|
|
var _edit_density := 25
|
|
var _edit_radius := 2.0
|
|
var _edit_slope := Vector2(0, 45)
|
|
var _edit_scale := Vector3.ONE
|
|
var _edit_rotation := 0.0
|
|
var _edit_rotation_rand := 1.0
|
|
var _edit_tool: TOOL = TOOL.AIRBRUSH : set = _on_set_tool
|
|
var _gui_toolbar = null
|
|
var _gui_toolbar_up = null
|
|
var _time_draw := 0
|
|
var _draw_paused := true
|
|
var _mouse_event := EVENT_MOUSE.EVENT_NONE
|
|
var _project_ray_origin := Vector3.INF
|
|
var _project_ray_normal := Vector3.INF
|
|
var _inspector_plugin : EditorInspectorPlugin = null
|
|
var _evaluate_draw_time: int = 100
|
|
var _prev_config := ""
|
|
var _custom_settings := [{
|
|
"name": "SimpleGrassTextured/General/default_terrain_physics_layer",
|
|
"type": TYPE_INT,
|
|
"hint": PROPERTY_HINT_LAYERS_3D_PHYSICS,
|
|
"hint_string": "",
|
|
"default": pow(2, 32) - 1,
|
|
"basic": true
|
|
},{
|
|
"name": "SimpleGrassTextured/General/evaluate_draw_time",
|
|
"type": TYPE_INT,
|
|
"hint": PROPERTY_HINT_ENUM,
|
|
"hint_string": "Slow:150,Normal:100,Fast:50,Very fast:25",
|
|
"default": 50,
|
|
"basic": false
|
|
},{
|
|
"name": "SimpleGrassTextured/General/interactive_resolution",
|
|
"type": TYPE_INT,
|
|
"hint": PROPERTY_HINT_ENUM,
|
|
"hint_string": "Low:256,High:512",
|
|
"default": 512,
|
|
"basic": false
|
|
},{
|
|
"name": "SimpleGrassTextured/General/interactive_resolution.android",
|
|
"type": TYPE_INT,
|
|
"hint": PROPERTY_HINT_ENUM,
|
|
"hint_string": "Low:256,High:512",
|
|
"default": 256,
|
|
"basic": false
|
|
},{
|
|
"name": "SimpleGrassTextured/General/interactive_resolution.ios",
|
|
"type": TYPE_INT,
|
|
"hint": PROPERTY_HINT_ENUM,
|
|
"hint_string": "Low:256,High:512",
|
|
"default": 256,
|
|
"basic": false
|
|
},{
|
|
"name": "SimpleGrassTextured/Shortcuts/airbrush_tool",
|
|
"type": TYPE_OBJECT,
|
|
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
|
"hint_string": "Shortcut",
|
|
"default": _create_shortcut(KEY_D),
|
|
"basic": true
|
|
},{
|
|
"name": "SimpleGrassTextured/Shortcuts/pencil_tool",
|
|
"type": TYPE_OBJECT,
|
|
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
|
"hint_string": "Shortcut",
|
|
"default": _create_shortcut(KEY_B),
|
|
"basic": true
|
|
},{
|
|
"name": "SimpleGrassTextured/Shortcuts/eraser_tool",
|
|
"type": TYPE_OBJECT,
|
|
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
|
"hint_string": "Shortcut",
|
|
"default": _create_shortcut(KEY_X),
|
|
"basic": true
|
|
},{
|
|
"name": "SimpleGrassTextured/Shortcuts/radius_increment",
|
|
"type": TYPE_OBJECT,
|
|
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
|
"hint_string": "Shortcut",
|
|
"default": _create_shortcut(KEY_BRACKETRIGHT),
|
|
"basic": true
|
|
},{
|
|
"name": "SimpleGrassTextured/Shortcuts/radius_decrement",
|
|
"type": TYPE_OBJECT,
|
|
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
|
"hint_string": "Shortcut",
|
|
"default": _create_shortcut(KEY_BRACKETLEFT),
|
|
"basic": true
|
|
},{
|
|
"name": "SimpleGrassTextured/Shortcuts/density_increment",
|
|
"type": TYPE_OBJECT,
|
|
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
|
"hint_string": "Shortcut",
|
|
"default": _create_shortcut(KEY_EQUAL),
|
|
"basic": true
|
|
},{
|
|
"name": "SimpleGrassTextured/Shortcuts/density_decrement",
|
|
"type": TYPE_OBJECT,
|
|
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
|
"hint_string": "Shortcut",
|
|
"default": _create_shortcut(KEY_MINUS),
|
|
"basic": true
|
|
}
|
|
]
|
|
|
|
|
|
func _enter_tree() -> void:
|
|
if not get_tree().has_user_signal(&"sgt_globals_params_changed"):
|
|
get_tree().add_user_signal(&"sgt_globals_params_changed")
|
|
_verify_global_shader_parameters()
|
|
_enable_shaders(true)
|
|
_init_default_project_settings()
|
|
_prev_config = _custom_config_memorize()
|
|
if ProjectSettings.has_signal(&"settings_changed"):
|
|
ProjectSettings.connect(&"settings_changed", _on_project_settings_changed)
|
|
# Must ensure the resource file of default_mesh.tres match the current Godot version
|
|
var default_mesh = load("res://addons/simplegrasstextured/default_mesh.tres")
|
|
if not default_mesh or not default_mesh.has_meta(&"GodotVersion") or default_mesh.get_meta(&"GodotVersion") != Engine.get_version_info()["string"]:
|
|
push_warning("SimpleGrassTextured, updating file res://addons/simplegrasstextured/default_mesh.tres")
|
|
default_mesh = null
|
|
var mesh_builder = load("res://addons/simplegrasstextured/default_mesh_builder.gd").new()
|
|
mesh_builder.rebuild_and_save_default_mesh()
|
|
default_mesh = load("res://addons/simplegrasstextured/default_mesh.tres")
|
|
if default_mesh:
|
|
default_mesh.emit_changed()
|
|
print("SimpleGrassTextured, file updated successfully res://addons/simplegrasstextured/default_mesh.tres")
|
|
else:
|
|
push_error("SimpleGrassTextured, error updating file res://addons/simplegrasstextured/default_mesh.tres")
|
|
add_custom_type(
|
|
"SimpleGrassTextured",
|
|
"MultiMeshInstance3D",
|
|
load("res://addons/simplegrasstextured/grass.gd"),
|
|
load("res://addons/simplegrasstextured/sgt_icon.svg")
|
|
)
|
|
|
|
_gui_toolbar = load("res://addons/simplegrasstextured/gui/toolbar.tscn").instantiate()
|
|
_gui_toolbar.visible = false
|
|
add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_BOTTOM, _gui_toolbar)
|
|
_gui_toolbar.set_plugin(self)
|
|
|
|
_gui_toolbar_up = load("res://addons/simplegrasstextured/gui/toolbar_up.tscn").instantiate()
|
|
_gui_toolbar_up.visible = false
|
|
add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, _gui_toolbar_up)
|
|
_gui_toolbar_up.set_plugin(self)
|
|
|
|
_inspector_plugin = load("res://addons/simplegrasstextured/sgt_inspector.gd").new()
|
|
add_inspector_plugin(_inspector_plugin)
|
|
|
|
_raycast_3d = RayCast3D.new()
|
|
_raycast_3d.collision_mask = pow(2, 32) - 1
|
|
_raycast_3d.visible = false
|
|
_pointer_decal = Decal.new()
|
|
_pointer_decal.set_texture(Decal.TEXTURE_ALBEDO, _pointer_img_circle)
|
|
_pointer_decal.visible = false
|
|
_pointer_decal.extents = Vector3(_edit_radius, _pointer_depth, _edit_radius)
|
|
_pointer_decal.upper_fade = 0
|
|
_pointer_decal.lower_fade = 0
|
|
add_child(_raycast_3d)
|
|
add_child(_pointer_decal)
|
|
|
|
_gui_toolbar.slider_radius.value_changed.connect(_on_slider_radius_value_changed)
|
|
_gui_toolbar.slider_density.value_changed.connect(_on_slider_density_value_changed)
|
|
_gui_toolbar.button_airbrush.toggled.connect(_on_button_airbrush_toggled)
|
|
_gui_toolbar.button_pencil.toggled.connect(_on_button_pencil_toggled)
|
|
_gui_toolbar.button_eraser.toggled.connect(_on_button_eraser_toggled)
|
|
_gui_toolbar.edit_slope_range.value_changed.connect(_on_edit_slope_range_changed)
|
|
_gui_toolbar.edit_scale.value_changed.connect(_on_edit_scale_value_changed)
|
|
_gui_toolbar.edit_rotation.value_changed.connect(_on_edit_rotation_value_changed)
|
|
_gui_toolbar.edit_rotation_rand.value_changed.connect(_on_edit_rotation_rand_value_changed)
|
|
_gui_toolbar.edit_distance.value_changed.connect(_on_edit_distance_value_changed)
|
|
_edit_tool = TOOL.AIRBRUSH
|
|
|
|
|
|
func _exit_tree() -> void:
|
|
var current_config := _custom_config_memorize()
|
|
if current_config != _prev_config:
|
|
# Force save settings if some shortcut has been changed
|
|
ProjectSettings.save()
|
|
_grass_selected = null
|
|
_raycast_3d.queue_free()
|
|
_pointer_decal.queue_free()
|
|
remove_custom_type("SimpleGrassTextured")
|
|
remove_control_from_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_BOTTOM, _gui_toolbar)
|
|
_gui_toolbar.queue_free()
|
|
remove_control_from_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, _gui_toolbar_up)
|
|
_gui_toolbar_up.queue_free()
|
|
if _inspector_plugin != null:
|
|
remove_inspector_plugin(_inspector_plugin)
|
|
|
|
|
|
func _enable_plugin() -> void:
|
|
_verify_global_shader_parameters()
|
|
|
|
|
|
func _disable_plugin() -> void:
|
|
_enable_shaders(false)
|
|
remove_autoload_singleton("SimpleGrass")
|
|
if ProjectSettings.has_setting("shader_globals/sgt_legacy_renderer"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_legacy_renderer", null)
|
|
if ProjectSettings.has_setting("shader_globals/sgt_player_position"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_player_position", null)
|
|
if ProjectSettings.has_setting("shader_globals/sgt_player_mov"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_player_mov", null)
|
|
if ProjectSettings.has_setting("shader_globals/sgt_normal_displacement"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_normal_displacement", null)
|
|
if ProjectSettings.has_setting("shader_globals/sgt_motion_texture"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_motion_texture", null)
|
|
if ProjectSettings.has_setting("shader_globals/sgt_wind_direction"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_wind_direction", null)
|
|
if ProjectSettings.has_setting("shader_globals/sgt_wind_movement"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_wind_movement", null)
|
|
if ProjectSettings.has_setting("shader_globals/sgt_wind_strength"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_wind_strength", null)
|
|
if ProjectSettings.has_setting("shader_globals/sgt_wind_turbulence"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_wind_turbulence", null)
|
|
if ProjectSettings.has_setting("shader_globals/sgt_wind_pattern"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_wind_pattern", null)
|
|
# Remove custom settings from Project Settings
|
|
for entry in _custom_settings:
|
|
if ProjectSettings.has_setting(entry["name"]):
|
|
ProjectSettings.set_setting(entry["name"], null)
|
|
# Fix editor crash when disable plugin while SimpleGrassTextured node is selected
|
|
_grass_selected = null
|
|
var editor = get_editor_interface()
|
|
if editor != null:
|
|
var scene_root = editor.get_edited_scene_root()
|
|
if scene_root != null:
|
|
editor.edit_node(scene_root)
|
|
var selection = editor.get_selection()
|
|
if selection != null:
|
|
selection.clear()
|
|
|
|
|
|
func _get_plugin_name() -> String:
|
|
return "SimpleGrassTextured"
|
|
|
|
|
|
func _handles(object) -> bool:
|
|
if object != null and object.has_meta("SimpleGrassTextured") and object.visible:
|
|
_grass_selected = object
|
|
_update_gui()
|
|
_update_pointer()
|
|
return true
|
|
_grass_selected = null
|
|
return false
|
|
|
|
|
|
func _edit(object) -> void:
|
|
_grass_selected = object
|
|
_update_gui()
|
|
_update_pointer()
|
|
|
|
|
|
func _make_visible(visible : bool) -> void:
|
|
if visible:
|
|
if _grass_selected != null:
|
|
_update_gui()
|
|
_gui_toolbar.visible = true
|
|
_gui_toolbar_up.visible = true
|
|
else:
|
|
_gui_toolbar.visible = false
|
|
_gui_toolbar_up.visible = false
|
|
_pointer_decal.visible = false
|
|
_grass_selected = null
|
|
_gui_toolbar.set_current_grass(null)
|
|
_gui_toolbar_up.set_current_grass(null)
|
|
|
|
|
|
func _physics_process(_delta) -> void:
|
|
if _mouse_event == EVENT_MOUSE.EVENT_CLICK:
|
|
_raycast_3d.global_transform.origin = _project_ray_origin
|
|
_raycast_3d.global_transform.basis.y = _project_ray_normal
|
|
_raycast_3d.target_position = Vector3(0, 100000, 0)
|
|
_raycast_3d.collision_mask = _grass_selected.collision_mask
|
|
_raycast_3d.force_raycast_update()
|
|
if _raycast_3d.is_colliding():
|
|
_position_draw = _raycast_3d.get_collision_point()
|
|
_normal_draw = _raycast_3d.get_collision_normal()
|
|
_object_draw = _raycast_3d.get_collider()
|
|
_eval_brush()
|
|
_time_draw = Time.get_ticks_msec()
|
|
_draw_paused = false
|
|
else:
|
|
_time_draw = 0
|
|
_draw_paused = true
|
|
_object_draw = null
|
|
_mouse_event = EVENT_MOUSE.EVENT_NONE
|
|
elif _mouse_event == EVENT_MOUSE.EVENT_MOVE:
|
|
_raycast_3d.global_transform.origin = _project_ray_origin
|
|
_raycast_3d.global_transform.basis.y = _project_ray_normal
|
|
_raycast_3d.target_position = Vector3(0, 100000, 0)
|
|
_raycast_3d.collision_mask = _grass_selected.collision_mask
|
|
_raycast_3d.force_raycast_update()
|
|
if ( not _raycast_3d.is_colliding()
|
|
or ( _object_draw != null and _raycast_3d.get_collider() != _object_draw )):
|
|
_pointer_decal.visible = false
|
|
_draw_paused = true
|
|
_mouse_event = EVENT_MOUSE.EVENT_NONE
|
|
return
|
|
else:
|
|
_draw_paused = false
|
|
_position_draw = _raycast_3d.get_collision_point()
|
|
_normal_draw = _raycast_3d.get_collision_normal()
|
|
if _pointer_rotate:
|
|
var trans := Transform3D()
|
|
if abs(_normal_draw.z) == 1:
|
|
trans.basis.x = Vector3(1,0,0)
|
|
trans.basis.y = Vector3(0,0,_normal_draw.z)
|
|
trans.basis.z = Vector3(0,_normal_draw.z,0)
|
|
else:
|
|
trans.basis.y = _normal_draw
|
|
trans.basis.x = _normal_draw.cross(trans.basis.z)
|
|
trans.basis.z = trans.basis.x.cross(_normal_draw)
|
|
trans.basis = trans.basis.orthonormalized()
|
|
trans.origin = _position_draw
|
|
_pointer_decal.global_transform = trans
|
|
else:
|
|
_pointer_decal.global_position = _position_draw
|
|
_pointer_decal.rotation = Vector3.ZERO
|
|
_pointer_decal.extents = Vector3(_edit_radius, _pointer_depth, _edit_radius)
|
|
_pointer_decal.visible = _edit_tool != TOOL.NONE
|
|
_mouse_event = EVENT_MOUSE.EVENT_NONE
|
|
|
|
if _time_draw > 0:
|
|
if not _draw_paused:
|
|
if Time.get_ticks_msec() - _time_draw >= _evaluate_draw_time:
|
|
_time_draw = Time.get_ticks_msec()
|
|
_eval_brush()
|
|
|
|
|
|
func _forward_3d_gui_input(viewport_camera: Camera3D, event: InputEvent) -> int:
|
|
if _grass_selected == null:
|
|
return EditorPlugin.AFTER_GUI_INPUT_PASS
|
|
if _grass_selected.multimesh != null:
|
|
_gui_toolbar.label_stats.text = "Count: " + str(_grass_selected.multimesh.instance_count)
|
|
if event is InputEventMouseButton:
|
|
if event.button_index == MOUSE_BUTTON_LEFT:
|
|
if _edit_tool == TOOL.NONE:
|
|
return EditorPlugin.AFTER_GUI_INPUT_PASS
|
|
if event.pressed:
|
|
if _edit_tool == TOOL.AIRBRUSH:
|
|
get_undo_redo().create_action(_grass_selected.name + " Airbrush tool")
|
|
elif _edit_tool == TOOL.PENCIL:
|
|
get_undo_redo().create_action(_grass_selected.name + " Pencil tool")
|
|
elif _edit_tool == TOOL.ERASER:
|
|
get_undo_redo().create_action(_grass_selected.name + " Eraser tool")
|
|
else:
|
|
get_undo_redo().create_action(_grass_selected.name)
|
|
get_undo_redo().add_undo_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
|
|
get_undo_redo().add_undo_property(_grass_selected, &"multimesh", _grass_selected.multimesh)
|
|
_project_ray_origin = viewport_camera.project_ray_origin(event.position)
|
|
_project_ray_normal = viewport_camera.project_ray_normal(event.position)
|
|
_mouse_event = EVENT_MOUSE.EVENT_CLICK
|
|
else:
|
|
get_undo_redo().add_do_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
|
|
get_undo_redo().add_do_property(_grass_selected, &"multimesh", _grass_selected.multimesh)
|
|
get_undo_redo().commit_action()
|
|
_time_draw = 0
|
|
_object_draw = null
|
|
_mouse_event = EVENT_MOUSE.EVENT_NONE
|
|
return EditorPlugin.AFTER_GUI_INPUT_STOP
|
|
if event is InputEventMouseMotion:
|
|
if _mouse_event != EVENT_MOUSE.EVENT_CLICK:
|
|
_project_ray_origin = viewport_camera.project_ray_origin(event.position)
|
|
_project_ray_normal = viewport_camera.project_ray_normal(event.position)
|
|
_mouse_event = EVENT_MOUSE.EVENT_MOVE
|
|
return EditorPlugin.AFTER_GUI_INPUT_PASS
|
|
|
|
|
|
func _verify_global_shader_parameters() -> void:
|
|
if not ProjectSettings.has_setting("shader_globals/sgt_legacy_renderer"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_legacy_renderer", {
|
|
"type": "int",
|
|
"value": 0
|
|
})
|
|
if RenderingServer.global_shader_parameter_get("sgt_legacy_renderer") == null:
|
|
var using_legacy_renderer = ProjectSettings.get_setting_with_override("rendering/renderer/rendering_method") == "gl_compatibility"
|
|
RenderingServer.global_shader_parameter_add("sgt_legacy_renderer", RenderingServer.GLOBAL_VAR_TYPE_INT, 1 if using_legacy_renderer else 0)
|
|
|
|
if not ProjectSettings.has_setting("shader_globals/sgt_player_position"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_player_position", {
|
|
"type": "vec3",
|
|
"value": Vector3(1000000, 1000000, 1000000)
|
|
})
|
|
if RenderingServer.global_shader_parameter_get("sgt_player_position") == null:
|
|
RenderingServer.global_shader_parameter_add("sgt_player_position", RenderingServer.GLOBAL_VAR_TYPE_VEC3, Vector3(1000000,1000000,1000000))
|
|
if not ProjectSettings.has_setting("shader_globals/sgt_player_mov"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_player_mov", {
|
|
"type": "vec3",
|
|
"value": Vector3.ZERO
|
|
})
|
|
if RenderingServer.global_shader_parameter_get("sgt_player_mov") == null:
|
|
RenderingServer.global_shader_parameter_add("sgt_player_mov", RenderingServer.GLOBAL_VAR_TYPE_VEC3, Vector3.ZERO)
|
|
if not ProjectSettings.has_setting("shader_globals/sgt_normal_displacement"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_normal_displacement", {
|
|
"type": "sampler2D",
|
|
"value": "res://addons/simplegrasstextured/images/normal.png"
|
|
})
|
|
if RenderingServer.global_shader_parameter_get("sgt_normal_displacement") == null:
|
|
RenderingServer.global_shader_parameter_add("sgt_normal_displacement", RenderingServer.GLOBAL_VAR_TYPE_SAMPLER2D, load("res://addons/simplegrasstextured/images/normal.png"))
|
|
if not ProjectSettings.has_setting("shader_globals/sgt_motion_texture"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_motion_texture", {
|
|
"type": "sampler2D",
|
|
"value": "res://addons/simplegrasstextured/images/motion.png"
|
|
})
|
|
if RenderingServer.global_shader_parameter_get("sgt_motion_texture") == null:
|
|
RenderingServer.global_shader_parameter_add("sgt_motion_texture", RenderingServer.GLOBAL_VAR_TYPE_SAMPLER2D, load("res://addons/simplegrasstextured/images/motion.png"))
|
|
if not ProjectSettings.has_setting("shader_globals/sgt_wind_direction"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_wind_direction", {
|
|
"type": "vec3",
|
|
"value": Vector3(1, 0, 0)
|
|
})
|
|
if RenderingServer.global_shader_parameter_get("sgt_wind_direction") == null:
|
|
RenderingServer.global_shader_parameter_add("sgt_wind_direction", RenderingServer.GLOBAL_VAR_TYPE_VEC3, Vector3(1, 0, 0))
|
|
if not ProjectSettings.has_setting("shader_globals/sgt_wind_movement"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_wind_movement", {
|
|
"type": "vec3",
|
|
"value": Vector2.ZERO
|
|
})
|
|
if RenderingServer.global_shader_parameter_get("sgt_wind_movement") == null:
|
|
RenderingServer.global_shader_parameter_add("sgt_wind_movement", RenderingServer.GLOBAL_VAR_TYPE_VEC3, Vector3.ZERO)
|
|
if not ProjectSettings.has_setting("shader_globals/sgt_wind_strength"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_wind_strength", {
|
|
"type": "float",
|
|
"value": 0.15
|
|
})
|
|
if RenderingServer.global_shader_parameter_get("sgt_wind_strength") == null:
|
|
RenderingServer.global_shader_parameter_add("sgt_wind_strength", RenderingServer.GLOBAL_VAR_TYPE_FLOAT, 0.15)
|
|
if not ProjectSettings.has_setting("shader_globals/sgt_wind_turbulence"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_wind_turbulence", {
|
|
"type": "float",
|
|
"value": 1.0
|
|
})
|
|
if RenderingServer.global_shader_parameter_get("sgt_wind_turbulence") == null:
|
|
RenderingServer.global_shader_parameter_add("sgt_wind_turbulence", RenderingServer.GLOBAL_VAR_TYPE_FLOAT, 1.0)
|
|
if not ProjectSettings.has_setting("shader_globals/sgt_wind_pattern"):
|
|
ProjectSettings.set_setting("shader_globals/sgt_wind_pattern", {
|
|
"type": "sampler2D",
|
|
"value": "res://addons/simplegrasstextured/images/wind_pattern.png"
|
|
})
|
|
if RenderingServer.global_shader_parameter_get("sgt_wind_pattern") == null:
|
|
RenderingServer.global_shader_parameter_add("sgt_wind_pattern", RenderingServer.GLOBAL_VAR_TYPE_SAMPLER2D, load("res://addons/simplegrasstextured/images/wind_pattern.png"))
|
|
if not ProjectSettings.has_setting("autoload/SimpleGrass"):
|
|
add_autoload_singleton("SimpleGrass", "res://addons/simplegrasstextured/singleton.tscn")
|
|
|
|
|
|
func _enable_shaders(enable :bool) -> void:
|
|
if enable:
|
|
var dir := DirAccess.open("res://")
|
|
var scan := false
|
|
if dir.file_exists("res://addons/simplegrasstextured/shaders/.gdignore"):
|
|
dir.remove("res://addons/simplegrasstextured/shaders/.gdignore")
|
|
scan = true
|
|
if dir.file_exists("res://addons/simplegrasstextured/materials/.gdignore"):
|
|
dir.remove("res://addons/simplegrasstextured/materials/.gdignore")
|
|
scan = true
|
|
if scan:
|
|
var editor = get_editor_interface()
|
|
editor.get_resource_filesystem().scan.call_deferred()
|
|
else:
|
|
var file := FileAccess.open("res://addons/simplegrasstextured/shaders/.gdignore", FileAccess.WRITE)
|
|
file.close()
|
|
file = FileAccess.open("res://addons/simplegrasstextured/materials/.gdignore", FileAccess.WRITE)
|
|
file.close()
|
|
var editor = get_editor_interface()
|
|
editor.get_resource_filesystem().scan.call_deferred()
|
|
|
|
|
|
func _create_shortcut(keycode :Key) -> Shortcut:
|
|
var shortcut := Shortcut.new()
|
|
var key := InputEventKey.new()
|
|
key.keycode = keycode
|
|
key.pressed = true
|
|
shortcut.events.append(key)
|
|
return shortcut
|
|
|
|
|
|
func _custom_config_memorize() -> String:
|
|
var config := ConfigFile.new()
|
|
for entry in _custom_settings:
|
|
config.set_value("s", entry["name"], ProjectSettings.get_setting_with_override(entry["name"]))
|
|
return config.encode_to_text()
|
|
|
|
|
|
func get_custom_setting(name :String) -> Variant:
|
|
if ProjectSettings.has_setting(name):
|
|
return ProjectSettings.get_setting_with_override(name)
|
|
for entry in _custom_settings:
|
|
if entry["name"] != name:
|
|
continue
|
|
return entry["default"]
|
|
push_error("SimpleGrassTextured, setting not found: ", name)
|
|
return null
|
|
|
|
|
|
func set_tool(tool_name: String) -> void:
|
|
match tool_name:
|
|
"none":
|
|
_edit_tool = TOOL.NONE
|
|
"airbrush":
|
|
_edit_tool = TOOL.AIRBRUSH
|
|
"pencil":
|
|
_edit_tool = TOOL.PENCIL
|
|
"eraser":
|
|
_edit_tool = TOOL.ERASER
|
|
|
|
|
|
func set_tool_shape(tool_name: String, shape_name: String) -> void:
|
|
if _grass_selected == null:
|
|
return
|
|
var shape_id: TOOL_SHAPE
|
|
match shape_name:
|
|
"sphere":
|
|
shape_id = TOOL_SHAPE.SPHERE
|
|
"cylinder":
|
|
shape_id = TOOL_SHAPE.CYLINDER
|
|
"cylinder_inf_h":
|
|
shape_id = TOOL_SHAPE.CYLINDER_INF_H
|
|
"box":
|
|
shape_id = TOOL_SHAPE.BOX
|
|
"box_inf_h":
|
|
shape_id = TOOL_SHAPE.BOX_INF_H
|
|
_grass_selected.sgt_tool_shape[tool_name] = shape_id
|
|
_update_pointer()
|
|
|
|
|
|
func get_tool_shape_name(shape_id: int) -> String:
|
|
match shape_id:
|
|
TOOL_SHAPE.SPHERE:
|
|
return "sphere"
|
|
TOOL_SHAPE.CYLINDER:
|
|
return "cylinder"
|
|
TOOL_SHAPE.CYLINDER_INF_H:
|
|
return "cylinder_inf_h"
|
|
TOOL_SHAPE.BOX:
|
|
return "box"
|
|
TOOL_SHAPE.BOX_INF_H:
|
|
return "box_inf_h"
|
|
return ""
|
|
|
|
|
|
func _init_default_project_settings() -> void:
|
|
for entry in _custom_settings:
|
|
if not ProjectSettings.has_setting(entry["name"]):
|
|
ProjectSettings.set(entry["name"], entry["default"])
|
|
ProjectSettings.set_initial_value(entry["name"], entry["default"])
|
|
ProjectSettings.add_property_info(entry)
|
|
if entry.has("basic") and ProjectSettings.has_method(&"set_as_basic"):
|
|
ProjectSettings.call(&"set_as_basic", entry["name"], entry["basic"])
|
|
|
|
|
|
func _update_gui() -> void:
|
|
if _grass_selected != null:
|
|
if not _grass_selected.sgt_tool_shape.has("airbrush"):
|
|
_grass_selected.sgt_tool_shape["airbrush"] = TOOL_SHAPE.CYLINDER
|
|
if not _grass_selected.sgt_tool_shape.has("pencil"):
|
|
_grass_selected.sgt_tool_shape["pencil"] = TOOL_SHAPE.CYLINDER
|
|
if not _grass_selected.sgt_tool_shape.has("eraser"):
|
|
_grass_selected.sgt_tool_shape["eraser"] = TOOL_SHAPE.SPHERE
|
|
_gui_toolbar.slider_radius.value = _grass_selected.sgt_radius
|
|
_gui_toolbar.slider_density.value = _grass_selected.sgt_density
|
|
_gui_toolbar.edit_scale.value = _grass_selected.sgt_scale
|
|
_gui_toolbar.edit_rotation.value = _grass_selected.sgt_rotation
|
|
_gui_toolbar.edit_rotation_rand.value = _grass_selected.sgt_rotation_rand
|
|
_gui_toolbar.edit_distance.value = _grass_selected.sgt_dist_min
|
|
_gui_toolbar.edit_slope_range.set_value(_grass_selected.sgt_slope.x, _grass_selected.sgt_slope.y)
|
|
_gui_toolbar.set_current_grass(_grass_selected)
|
|
_gui_toolbar_up.set_current_grass(_grass_selected)
|
|
if _grass_selected.multimesh != null:
|
|
_gui_toolbar.label_stats.text = "Count: " + str(_grass_selected.multimesh.instance_count)
|
|
_raycast_3d.collision_mask = _grass_selected.collision_mask
|
|
|
|
|
|
func _update_pointer() -> void:
|
|
if _grass_selected == null:
|
|
_pointer_decal.visible = false
|
|
return
|
|
_pointer_decal.visible = true
|
|
var tool_name := ""
|
|
match _edit_tool:
|
|
TOOL.NONE:
|
|
_pointer_decal.visible = false
|
|
return
|
|
TOOL.PENCIL:
|
|
tool_name = "pencil"
|
|
_pointer_rotate = true
|
|
TOOL.AIRBRUSH:
|
|
tool_name = "airbrush"
|
|
_pointer_rotate = true
|
|
TOOL.ERASER:
|
|
tool_name = "eraser"
|
|
_pointer_rotate = false
|
|
match _grass_selected.sgt_tool_shape[tool_name]:
|
|
TOOL_SHAPE.SPHERE:
|
|
if _edit_tool == TOOL.ERASER:
|
|
_pointer_rotate = true
|
|
_pointer_depth = _edit_radius
|
|
_pointer_decal.set_texture(Decal.TEXTURE_ALBEDO, _pointer_img_circle)
|
|
TOOL_SHAPE.CYLINDER:
|
|
if _edit_tool == TOOL.ERASER:
|
|
_pointer_rotate = true
|
|
_pointer_depth = DEFAULT_POINTER_DEPTH
|
|
_pointer_decal.set_texture(Decal.TEXTURE_ALBEDO, _pointer_img_circle)
|
|
TOOL_SHAPE.CYLINDER_INF_H:
|
|
_pointer_depth = 1000000
|
|
_pointer_decal.set_texture(Decal.TEXTURE_ALBEDO, _pointer_img_circle)
|
|
TOOL_SHAPE.BOX:
|
|
if _edit_tool == TOOL.ERASER:
|
|
_pointer_rotate = true
|
|
_pointer_depth = _edit_radius
|
|
_pointer_decal.set_texture(Decal.TEXTURE_ALBEDO, _pointer_img_rect)
|
|
TOOL_SHAPE.BOX_INF_H:
|
|
_pointer_depth = 1000000
|
|
_pointer_decal.set_texture(Decal.TEXTURE_ALBEDO, _pointer_img_rect)
|
|
|
|
|
|
func _on_project_settings_changed() -> void:
|
|
_prev_config = _custom_config_memorize()
|
|
_evaluate_draw_time = get_custom_setting("SimpleGrassTextured/General/evaluate_draw_time")
|
|
|
|
|
|
func _on_button_airbrush_toggled(pressed : bool) -> void:
|
|
if pressed:
|
|
_edit_tool = TOOL.AIRBRUSH
|
|
elif _edit_tool == TOOL.AIRBRUSH:
|
|
_edit_tool = TOOL.NONE
|
|
_update_pointer()
|
|
|
|
|
|
func _on_button_pencil_toggled(pressed : bool) -> void:
|
|
if pressed:
|
|
_edit_tool = TOOL.PENCIL
|
|
elif _edit_tool == TOOL.PENCIL:
|
|
_edit_tool = TOOL.NONE
|
|
_update_pointer()
|
|
|
|
|
|
func _on_button_eraser_toggled(pressed : bool) -> void:
|
|
if pressed:
|
|
_edit_tool = TOOL.ERASER
|
|
elif _edit_tool == TOOL.ERASER:
|
|
_edit_tool = TOOL.NONE
|
|
_update_pointer()
|
|
|
|
|
|
func _on_slider_radius_value_changed(value : float) -> void:
|
|
_edit_radius = value
|
|
_update_pointer()
|
|
_pointer_decal.extents = Vector3(_edit_radius, _pointer_depth, _edit_radius)
|
|
if _grass_selected != null:
|
|
_grass_selected.sgt_radius = value
|
|
|
|
|
|
func _on_slider_density_value_changed(value : float) -> void:
|
|
_edit_density = value
|
|
if _grass_selected != null:
|
|
_grass_selected.sgt_density = value
|
|
|
|
|
|
func _on_edit_slope_range_changed(value_min: float, value_max: float) -> void:
|
|
_edit_slope = Vector2(value_min, value_max)
|
|
if _grass_selected != null:
|
|
_grass_selected.sgt_slope = _edit_slope
|
|
|
|
|
|
func _on_edit_scale_value_changed(value : float) -> void:
|
|
_edit_scale = Vector3(value, value, value)
|
|
if _grass_selected != null:
|
|
_grass_selected.sgt_scale = value
|
|
|
|
|
|
func _on_edit_rotation_value_changed(value : float) -> void:
|
|
_edit_rotation = value
|
|
if _grass_selected != null:
|
|
_grass_selected.sgt_rotation = value
|
|
|
|
|
|
func _on_edit_rotation_rand_value_changed(value : float) -> void:
|
|
_edit_rotation_rand = value
|
|
if _grass_selected != null:
|
|
_grass_selected.sgt_rotation_rand = value
|
|
|
|
|
|
func _on_edit_distance_value_changed(value : float) -> void:
|
|
if _grass_selected != null:
|
|
_grass_selected.sgt_dist_min = value
|
|
|
|
|
|
func _on_set_tool(value : TOOL) -> void:
|
|
_edit_tool = value
|
|
if _edit_tool == TOOL.AIRBRUSH:
|
|
_pointer_decal.modulate = Color.WHITE
|
|
_gui_toolbar.slider_density.editable = true
|
|
_gui_toolbar.button_density.disabled = false
|
|
_gui_toolbar.button_airbrush.button_pressed = true
|
|
_gui_toolbar.button_pencil.button_pressed = false
|
|
_gui_toolbar.button_eraser.button_pressed = false
|
|
_gui_toolbar.set_density_modulate(Color.WHITE)
|
|
elif _edit_tool == TOOL.PENCIL:
|
|
_pointer_decal.modulate = Color.YELLOW
|
|
_gui_toolbar.slider_density.editable = false
|
|
_gui_toolbar.button_density.disabled = true
|
|
_gui_toolbar.button_airbrush.button_pressed = false
|
|
_gui_toolbar.button_pencil.button_pressed = true
|
|
_gui_toolbar.button_eraser.button_pressed = false
|
|
_gui_toolbar.set_density_modulate(Color(1, 1, 1, 0.25))
|
|
elif _edit_tool == TOOL.ERASER:
|
|
_pointer_decal.modulate = Color.RED
|
|
_gui_toolbar.slider_density.editable = false
|
|
_gui_toolbar.button_density.disabled = true
|
|
_gui_toolbar.button_airbrush.button_pressed = false
|
|
_gui_toolbar.button_pencil.button_pressed = false
|
|
_gui_toolbar.button_eraser.button_pressed = true
|
|
_gui_toolbar.set_density_modulate(Color(1, 1, 1, 0.25))
|
|
if _grass_selected != null:
|
|
_pointer_decal.visible = _edit_tool != TOOL.NONE
|
|
else:
|
|
_pointer_decal.visible = false
|
|
|
|
|
|
func _eval_brush() -> void:
|
|
if _grass_selected == null:
|
|
return
|
|
if _edit_tool == TOOL.PENCIL:
|
|
var steep : float = _grass_selected.sgt_dist_min
|
|
var list_trans := []
|
|
var follow_normal : bool = _grass_selected.sgt_follow_normal
|
|
var slope := Vector2(deg_to_rad(_grass_selected.sgt_slope.x), deg_to_rad(_grass_selected.sgt_slope.y))
|
|
if steep < 0.05:
|
|
steep = 0.4
|
|
_grass_selected.temp_dist_min = steep
|
|
var x := -_edit_radius
|
|
while x < _edit_radius:
|
|
var z := -_edit_radius
|
|
while z < _edit_radius:
|
|
var variation: Vector3
|
|
match _grass_selected.sgt_tool_shape["pencil"]:
|
|
TOOL_SHAPE.SPHERE, TOOL_SHAPE.CYLINDER, TOOL_SHAPE.CYLINDER_INF_H:
|
|
variation = Vector3(x + (randf() * steep * 0.5), 0, z + (randf() * steep * 0.5))
|
|
if variation.length() >= _edit_radius:
|
|
z += steep
|
|
continue
|
|
TOOL_SHAPE.BOX, TOOL_SHAPE.BOX_INF_H:
|
|
variation = Vector3(x + (randf() * steep * 0.5), 0, z + (randf() * steep * 0.5))
|
|
_:
|
|
variation = Vector3(x + (randf() * steep * 0.5), 0, z + (randf() * steep * 0.5))
|
|
if variation.length() >= _edit_radius:
|
|
z += steep
|
|
continue
|
|
variation = _pointer_decal.to_global(variation) - _pointer_decal.global_position
|
|
_raycast_3d.global_transform.basis.x = Vector3.RIGHT
|
|
_raycast_3d.global_transform.basis.y = _normal_draw * -1
|
|
_raycast_3d.global_transform.basis.z = Vector3.BACK
|
|
_raycast_3d.global_transform.origin = _position_draw + _normal_draw + variation
|
|
_raycast_3d.target_position = Vector3(0, _pointer_depth, 0)
|
|
_raycast_3d.collision_mask = _grass_selected.collision_mask
|
|
_raycast_3d.force_raycast_update()
|
|
var pos_grass : Vector3 = _raycast_3d.get_collision_point()
|
|
if _raycast_3d.is_colliding() and _raycast_3d.get_collider() == _object_draw:
|
|
var normal := _raycast_3d.get_collision_normal()
|
|
if normal.angle_to(Vector3.UP) < slope.x or normal.angle_to(Vector3.UP) > slope.y:
|
|
z += steep
|
|
continue
|
|
if not follow_normal:
|
|
normal = Vector3.UP
|
|
list_trans.append(_grass_selected.eval_grass_transform(
|
|
_raycast_3d.get_collision_point() - _grass_selected.global_position,
|
|
normal,
|
|
_edit_scale,
|
|
deg_to_rad(_edit_rotation) + (PI * (_edit_rotation_rand - (randf() * _edit_rotation_rand * 2.0)))
|
|
))
|
|
z += steep
|
|
x += steep
|
|
_grass_selected.add_grass_batch(list_trans)
|
|
elif _edit_tool == TOOL.AIRBRUSH:
|
|
var follow_normal : bool = _grass_selected.sgt_follow_normal
|
|
var slope := Vector2(deg_to_rad(_grass_selected.sgt_slope.x), deg_to_rad(_grass_selected.sgt_slope.y))
|
|
for i in _edit_density:
|
|
var variation: Vector3
|
|
match _grass_selected.sgt_tool_shape["airbrush"]:
|
|
TOOL_SHAPE.SPHERE, TOOL_SHAPE.CYLINDER, TOOL_SHAPE.CYLINDER_INF_H:
|
|
variation = Vector3.RIGHT * _edit_radius * randf()
|
|
variation = variation.rotated(Vector3.UP, randf() * TAU)
|
|
TOOL_SHAPE.BOX, TOOL_SHAPE.BOX_INF_H:
|
|
variation = Vector3(randf_range(-1, 1), 0, randf_range(-1, 1)) * _edit_radius
|
|
_:
|
|
variation = Vector3.RIGHT * _edit_radius * randf()
|
|
variation = variation.rotated(Vector3.UP, randf() * TAU)
|
|
variation = _pointer_decal.to_global(variation) - _pointer_decal.global_position
|
|
_raycast_3d.global_transform.basis.x = Vector3.RIGHT
|
|
_raycast_3d.global_transform.basis.y = _normal_draw * -1
|
|
_raycast_3d.global_transform.basis.z = Vector3.BACK
|
|
_raycast_3d.global_transform.origin = _position_draw + _normal_draw + variation
|
|
_raycast_3d.target_position = Vector3(0, _pointer_depth, 0)
|
|
_raycast_3d.collision_mask = _grass_selected.collision_mask
|
|
_raycast_3d.force_raycast_update()
|
|
if _raycast_3d.is_colliding() and _raycast_3d.get_collider() == _object_draw:
|
|
var normal := _raycast_3d.get_collision_normal()
|
|
if normal.angle_to(Vector3.UP) < slope.x or normal.angle_to(Vector3.UP) > slope.y:
|
|
continue
|
|
if not follow_normal:
|
|
normal = Vector3.UP
|
|
_grass_selected.add_grass(
|
|
_raycast_3d.get_collision_point() - _grass_selected.global_position,
|
|
normal,
|
|
_edit_scale,
|
|
deg_to_rad(_edit_rotation) + (PI * (_edit_rotation_rand - (randf() * _edit_rotation_rand * 2.0)))
|
|
)
|
|
elif _edit_tool == TOOL.ERASER:
|
|
match _grass_selected.sgt_tool_shape["eraser"]:
|
|
TOOL_SHAPE.SPHERE:
|
|
_grass_selected.erase(_position_draw - _grass_selected.global_position, _edit_radius)
|
|
TOOL_SHAPE.CYLINDER:
|
|
_grass_selected.erase_cylinder(_position_draw - _grass_selected.global_position, _edit_radius, _pointer_depth, _edit_radius, _pointer_decal.global_transform)
|
|
TOOL_SHAPE.CYLINDER_INF_H:
|
|
_grass_selected.erase_cylinder(_position_draw - _grass_selected.global_position, _edit_radius, 1000000, _edit_radius, _pointer_decal.global_transform)
|
|
TOOL_SHAPE.BOX:
|
|
_grass_selected.erase_box(_position_draw, Vector3(_edit_radius, _edit_radius, _edit_radius) * 2, _pointer_decal.global_transform)
|
|
TOOL_SHAPE.BOX_INF_H:
|
|
_grass_selected.erase_box(_position_draw - _grass_selected.global_position, Vector3(_edit_radius, 1000000, _edit_radius) * 2, _pointer_decal.global_transform)
|
|
if _grass_selected.multimesh != null:
|
|
_gui_toolbar.label_stats.text = "Count: " + str(_grass_selected.multimesh.instance_count)
|