2026-03-19 11:35:38 -05:00

267 lines
12 KiB
GDScript

# toolbar_menu.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 MenuButton
enum MENU_ID {
TOOL_FOLLOW_NORMAL,
TOOL_SHAPE_AIRBRUSH,
TOOL_SHAPE_PENCIL,
TOOL_SHAPE_ERASER,
TOOL_SNAP_TO_TERRAIN,
AUTO_CENTER_POSITION,
CAST_SHADOW,
BAKE_HEIGHT_MAP,
GLOBAL_PARAMETERS,
CLEAR_ALL,
HELP_ABOUT,
RECALCULATE_AABB,
}
enum MENU_SHAPE_ID {
TOOL_SHAPE_SPHERE,
TOOL_SHAPE_CYLINDER,
TOOL_SHAPE_CYLINDER_INF_H,
TOOL_SHAPE_BOX,
TOOL_SHAPE_BOX_INF_H,
}
var _plugin: EditorPlugin = null
var _grass_selected = null
var _tools_menu: PopupMenu = null
var _airbrush_shape_menu: PopupMenu = null
var _pencil_shape_menu: PopupMenu = null
var _eraser_shape_menu: PopupMenu = null
func set_plugin(plugin :EditorPlugin) -> void:
var popup := get_popup()
_plugin = plugin
_airbrush_shape_menu = PopupMenu.new()
_airbrush_shape_menu.name = &"AirbrushShapeMenu"
_airbrush_shape_menu.add_radio_check_item("Cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
_airbrush_shape_menu.add_radio_check_item("Box", MENU_SHAPE_ID.TOOL_SHAPE_BOX)
_pencil_shape_menu = PopupMenu.new()
_pencil_shape_menu.name = &"PencilShapeMenu"
_pencil_shape_menu.add_radio_check_item("Cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
_pencil_shape_menu.add_radio_check_item("Box", MENU_SHAPE_ID.TOOL_SHAPE_BOX)
_eraser_shape_menu = PopupMenu.new()
_eraser_shape_menu.name = &"EraserShapeMenu"
_eraser_shape_menu.add_radio_check_item("Sphere", MENU_SHAPE_ID.TOOL_SHAPE_SPHERE)
_eraser_shape_menu.add_radio_check_item("Cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
_eraser_shape_menu.add_radio_check_item("Infinite vertical cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER_INF_H)
_eraser_shape_menu.add_radio_check_item("Box", MENU_SHAPE_ID.TOOL_SHAPE_BOX)
_eraser_shape_menu.add_radio_check_item("Infinite vertical box", MENU_SHAPE_ID.TOOL_SHAPE_BOX_INF_H)
_tools_menu = PopupMenu.new()
_tools_menu.add_child(_airbrush_shape_menu)
_tools_menu.add_child(_pencil_shape_menu)
_tools_menu.add_child(_eraser_shape_menu)
_tools_menu.name = &"ToolsMenu"
_tools_menu.add_check_item("Follow terrain's normal", MENU_ID.TOOL_FOLLOW_NORMAL)
_tools_menu.add_submenu_item("Airbrush shape", "AirbrushShapeMenu")
_tools_menu.add_submenu_item("Pencil shape", "PencilShapeMenu")
_tools_menu.add_submenu_item("Eraser shape", "EraserShapeMenu")
_tools_menu.add_separator()
_tools_menu.add_item("Snap to terrain", MENU_ID.TOOL_SNAP_TO_TERRAIN)
popup.add_child(_tools_menu)
popup.clear()
popup.add_submenu_item("Tools", "ToolsMenu")
popup.add_separator()
popup.add_item("Auto center position", MENU_ID.AUTO_CENTER_POSITION)
popup.add_item("Recalculate custom AABB", MENU_ID.RECALCULATE_AABB)
popup.add_item("Bake height map", MENU_ID.BAKE_HEIGHT_MAP)
popup.add_check_item("Cast shadow", MENU_ID.CAST_SHADOW)
popup.add_item("Global parameters", MENU_ID.GLOBAL_PARAMETERS)
popup.add_separator()
popup.add_item("Clear all", MENU_ID.CLEAR_ALL)
popup.add_separator()
popup.add_item("About SimpleGrassTextured", MENU_ID.HELP_ABOUT)
popup.id_pressed.connect(_on_sgt_menu_button)
_tools_menu.id_pressed.connect(_on_sgt_tools_menu_button)
_airbrush_shape_menu.id_pressed.connect(_on_sgt_shape_menu_pressed.bind("airbrush", _airbrush_shape_menu))
_pencil_shape_menu.id_pressed.connect(_on_sgt_shape_menu_pressed.bind("pencil", _pencil_shape_menu))
_eraser_shape_menu.id_pressed.connect(_on_sgt_shape_menu_pressed.bind("eraser", _eraser_shape_menu))
about_to_popup.connect(_on_about_to_popup)
func set_current_grass(grass_selected) -> void:
_grass_selected = grass_selected
if grass_selected == null:
return
var popup := get_popup()
if grass_selected.cast_shadow == GeometryInstance3D.SHADOW_CASTING_SETTING_OFF:
popup.set_item_checked(popup.get_item_index(MENU_ID.CAST_SHADOW), false)
else:
popup.set_item_checked(popup.get_item_index(MENU_ID.CAST_SHADOW), true)
if _grass_selected.baked_height_map != null:
popup.set_item_text(popup.get_item_index(MENU_ID.BAKE_HEIGHT_MAP), "Bake height map (already baked)")
popup.set_item_disabled(popup.get_item_index(MENU_ID.BAKE_HEIGHT_MAP), true)
else:
popup.set_item_text(popup.get_item_index(MENU_ID.BAKE_HEIGHT_MAP), "Bake height map")
popup.set_item_disabled(popup.get_item_index(MENU_ID.BAKE_HEIGHT_MAP), false)
_tools_menu.set_item_checked(_tools_menu.get_item_index(MENU_ID.TOOL_FOLLOW_NORMAL), _grass_selected.sgt_follow_normal)
for tool_name in _grass_selected.sgt_tool_shape:
match tool_name:
"airbrush":
_update_shape_menu_from_grass(_airbrush_shape_menu, _grass_selected.sgt_tool_shape[tool_name])
"pencil":
_update_shape_menu_from_grass(_pencil_shape_menu, _grass_selected.sgt_tool_shape[tool_name])
"eraser":
_update_shape_menu_from_grass(_eraser_shape_menu, _grass_selected.sgt_tool_shape[tool_name])
func _update_shape_menu_from_grass(popupmenu: PopupMenu, plugin_id_shape: int) -> void:
for i in popupmenu.item_count:
popupmenu.set_item_checked(i, false)
var idx := -1
match _plugin.get_tool_shape_name(plugin_id_shape):
"sphere":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_SPHERE)
"cylinder":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
"cylinder_inf_h":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER_INF_H)
"box":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_BOX)
"box_inf_h":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_BOX_INF_H)
_:
idx = -1
if idx == -1:
return
popupmenu.set_item_checked(idx, true)
func _on_sgt_menu_button(id :int) -> void:
if _grass_selected == null:
return
match id:
MENU_ID.AUTO_CENTER_POSITION:
_plugin.get_undo_redo().create_action(_grass_selected.name + " Auto Center Position")
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"multimesh", _grass_selected.multimesh)
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"global_position", _grass_selected.global_position)
_grass_selected.auto_center_position()
_plugin.get_undo_redo().add_do_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
_plugin.get_undo_redo().add_do_property(_grass_selected, &"multimesh", _grass_selected.multimesh)
_plugin.get_undo_redo().add_do_property(_grass_selected, &"global_position", _grass_selected.global_position)
_plugin.get_undo_redo().commit_action()
MENU_ID.RECALCULATE_AABB:
_plugin.get_undo_redo().create_action(_grass_selected.name + " Recalculate Custom AABB")
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"custom_aabb", _grass_selected.custom_aabb)
_grass_selected.recalculate_custom_aabb()
_plugin.get_undo_redo().add_do_property(_grass_selected, &"custom_aabb", _grass_selected.custom_aabb)
_plugin.get_undo_redo().commit_action()
MENU_ID.CAST_SHADOW:
_plugin.get_undo_redo().create_action(_grass_selected.name + " Toogle Cast Shadow")
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"cast_shadow", _grass_selected.cast_shadow)
for child in _grass_selected.get_children():
if child.has_meta(&"SimpleGrassTexturedRegion"):
_plugin.get_undo_redo().add_undo_property(child, &"cast_shadow", child.cast_shadow)
if _grass_selected.cast_shadow == GeometryInstance3D.SHADOW_CASTING_SETTING_OFF:
_grass_selected.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_ON
else:
_grass_selected.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
_plugin.get_undo_redo().add_do_property(_grass_selected, &"cast_shadow", _grass_selected.cast_shadow)
for child in _grass_selected.get_children():
if child.has_meta(&"SimpleGrassTexturedRegion"):
child.cast_shadow = _grass_selected.cast_shadow
_plugin.get_undo_redo().add_do_property(child, &"cast_shadow", child.cast_shadow)
_plugin.get_undo_redo().commit_action()
MENU_ID.BAKE_HEIGHT_MAP:
_grass_selected.bake_height_map()
MENU_ID.GLOBAL_PARAMETERS:
var _global_parameters = load("res://addons/simplegrasstextured/gui/global_parameters.tscn").instantiate()
get_window().add_child(_global_parameters)
_global_parameters.popup_centered()
MENU_ID.CLEAR_ALL:
var win = load("res://addons/simplegrasstextured/gui/clear_all_confirmation_dialog.tscn").instantiate()
get_window().add_child(win)
win.confirmed.connect(_on_clear_all_confirmation_dialog_confirmed)
win.popup_centered()
MENU_ID.HELP_ABOUT:
var win = load("res://addons/simplegrasstextured/gui/about.tscn").instantiate()
get_window().add_child(win)
win.popup_centered()
func _on_sgt_tools_menu_button(id :int) -> void:
if _grass_selected == null:
return
match id:
MENU_ID.TOOL_FOLLOW_NORMAL:
var idx := _tools_menu.get_item_index(id)
_tools_menu.set_item_checked(idx, not _tools_menu.is_item_checked(idx))
_grass_selected.sgt_follow_normal = _tools_menu.is_item_checked(idx)
MENU_ID.TOOL_SNAP_TO_TERRAIN:
_plugin.get_undo_redo().create_action(_grass_selected.name + " Snap to terrain")
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"multimesh", _grass_selected.multimesh.duplicate())
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"global_position", _grass_selected.global_position)
_grass_selected.snap_to_terrain()
_plugin.get_undo_redo().add_do_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
_plugin.get_undo_redo().add_do_property(_grass_selected, &"multimesh", _grass_selected.multimesh)
_plugin.get_undo_redo().add_do_property(_grass_selected, &"global_position", _grass_selected.global_position)
_plugin.get_undo_redo().commit_action()
func _on_sgt_shape_menu_pressed(id: int, tool_name: String, popupmenu: PopupMenu) -> void:
if _grass_selected == null:
return
for i in popupmenu.item_count:
popupmenu.set_item_checked(i, false)
var idx := popupmenu.get_item_index(id)
if idx == -1:
return
popupmenu.set_item_checked(idx, true)
var shape_name := ""
match id:
MENU_SHAPE_ID.TOOL_SHAPE_SPHERE:
shape_name = "sphere"
MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER:
shape_name = "cylinder"
MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER_INF_H:
shape_name = "cylinder_inf_h"
MENU_SHAPE_ID.TOOL_SHAPE_BOX:
shape_name = "box"
MENU_SHAPE_ID.TOOL_SHAPE_BOX_INF_H:
shape_name = "box_inf_h"
_plugin.set_tool_shape(tool_name, shape_name)
func _on_about_to_popup() -> void:
set_current_grass(_grass_selected)
func _on_clear_all_confirmation_dialog_confirmed() -> void:
if _grass_selected == null:
return
_plugin.get_undo_redo().create_action(_grass_selected.name + " Clear All Grass")
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"multimesh", _grass_selected.multimesh)
_grass_selected.clear_all()
_plugin.get_undo_redo().add_do_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
_plugin.get_undo_redo().add_do_property(_grass_selected, &"multimesh", _grass_selected.multimesh)
_plugin.get_undo_redo().commit_action()