2018-08-05 18:46:23 +02:00
|
|
|
extends Panel
|
|
|
|
|
2019-01-24 22:27:41 +01:00
|
|
|
var recent_files = []
|
|
|
|
|
2019-11-16 23:23:36 +01:00
|
|
|
var config_cache : ConfigFile = ConfigFile.new()
|
|
|
|
|
2018-09-04 21:45:14 +02:00
|
|
|
var editor_interface = null
|
2018-08-27 17:22:57 +02:00
|
|
|
var current_tab = null
|
2018-08-05 18:46:23 +02:00
|
|
|
|
2019-11-05 22:28:01 +01:00
|
|
|
var updating : bool = false
|
|
|
|
var need_update : bool = false
|
|
|
|
|
2019-09-29 21:53:32 +02:00
|
|
|
onready var projects = $VBoxContainer/HBoxContainer/ProjectsPane/Projects
|
2019-10-07 20:41:57 +02:00
|
|
|
onready var library = $VBoxContainer/HBoxContainer/VBoxContainer/Library
|
2019-08-12 15:58:42 +02:00
|
|
|
|
2019-12-08 08:50:14 +01:00
|
|
|
onready var preview_2d = $VBoxContainer/HBoxContainer/VBoxContainer/Preview/Preview2D
|
|
|
|
onready var preview_3d = $VBoxContainer/HBoxContainer/VBoxContainer/Preview/Preview3D
|
|
|
|
|
2020-02-10 23:13:10 +01:00
|
|
|
onready var preview_2d_background = $VBoxContainer/HBoxContainer/ProjectsPane/Preview2D
|
|
|
|
|
2019-11-17 07:40:50 +01:00
|
|
|
const RECENT_FILES_COUNT = 15
|
|
|
|
|
2020-02-02 10:54:03 +01:00
|
|
|
const THEMES = [ "Dark", "Default", "Light" ]
|
|
|
|
|
2018-08-05 18:46:23 +02:00
|
|
|
const MENU = [
|
|
|
|
{ menu="File", command="new_material", description="New material" },
|
2018-08-07 23:21:12 +02:00
|
|
|
{ menu="File", command="load_material", shortcut="Control+O", description="Load material" },
|
2019-01-24 22:27:41 +01:00
|
|
|
{ menu="File", submenu="load_recent", description="Load recent", standalone_only=true },
|
2018-08-05 18:46:23 +02:00
|
|
|
{ menu="File" },
|
2018-08-07 23:21:12 +02:00
|
|
|
{ menu="File", command="save_material", shortcut="Control+S", description="Save material" },
|
|
|
|
{ menu="File", command="save_material_as", shortcut="Control+Shift+S", description="Save material as..." },
|
2018-08-05 18:46:23 +02:00
|
|
|
{ menu="File", command="save_all_materials", description="Save all materials..." },
|
|
|
|
{ menu="File" },
|
2020-02-17 08:54:46 +01:00
|
|
|
{ menu="File", submenu="export_material", description="Export material" },
|
|
|
|
#{ menu="File", command="export_material", shortcut="Control+E", description="Export material" },
|
2018-08-05 18:46:23 +02:00
|
|
|
{ menu="File" },
|
|
|
|
{ menu="File", command="close_material", description="Close material" },
|
2018-08-07 23:21:12 +02:00
|
|
|
{ menu="File", command="quit", shortcut="Control+Q", description="Quit" },
|
2019-11-10 01:19:52 +01:00
|
|
|
|
2018-08-19 02:11:52 +02:00
|
|
|
{ menu="Edit", command="edit_cut", shortcut="Control+X", description="Cut" },
|
|
|
|
{ menu="Edit", command="edit_copy", shortcut="Control+C", description="Copy" },
|
|
|
|
{ menu="Edit", command="edit_paste", shortcut="Control+V", description="Paste" },
|
2019-11-17 07:40:50 +01:00
|
|
|
{ menu="Edit", command="edit_duplicate", shortcut="Control+D", description="Duplicate" },
|
2020-02-02 10:54:03 +01:00
|
|
|
{ menu="Edit" },
|
|
|
|
{ menu="Edit", submenu="set_theme", description="Set theme" },
|
2019-11-10 01:19:52 +01:00
|
|
|
|
2019-10-17 19:03:18 +02:00
|
|
|
{ menu="View", command="view_center", shortcut="C", description="Center view" },
|
|
|
|
{ menu="View", command="view_reset_zoom", shortcut="Control+0", description="Reset zoom" },
|
2019-11-10 01:19:52 +01:00
|
|
|
|
2019-10-07 20:41:57 +02:00
|
|
|
{ menu="Tools", submenu="create", description="Create" },
|
2019-10-05 11:04:50 +02:00
|
|
|
{ menu="Tools", command="create_subgraph", shortcut="Control+G", description="Create group" },
|
2019-11-17 07:40:50 +01:00
|
|
|
{ menu="Tools", command="make_selected_nodes_editable", shortcut="Control+W", description="Make selected nodes editable" },
|
2019-10-07 20:41:57 +02:00
|
|
|
{ menu="Tools" },
|
2018-08-07 23:21:12 +02:00
|
|
|
{ menu="Tools", command="add_to_user_library", description="Add selected node to user library" },
|
2019-11-05 20:00:28 +01:00
|
|
|
{ menu="Tools", command="export_library", description="Export the nodes library" },
|
2020-02-15 22:20:26 +01:00
|
|
|
|
2020-02-17 08:54:46 +01:00
|
|
|
#{ menu="Tools", command="generate_screenshots", description="Generate screenshots for the library nodes" },
|
2020-02-15 22:20:26 +01:00
|
|
|
|
|
|
|
|
2019-11-16 23:23:36 +01:00
|
|
|
|
2019-11-10 15:02:00 +01:00
|
|
|
{ menu="Help", command="show_doc", shortcut="F1", description="User manual" },
|
|
|
|
{ menu="Help", command="show_library_item_doc", shortcut="Control+F1", description="Show selected library item documentation" },
|
2018-08-11 11:33:24 +02:00
|
|
|
{ menu="Help", command="bug_report", description="Report a bug" },
|
|
|
|
{ menu="Help" },
|
2018-08-05 18:46:23 +02:00
|
|
|
{ menu="Help", command="about", description="About" }
|
|
|
|
]
|
|
|
|
|
2018-09-04 21:45:14 +02:00
|
|
|
signal quit
|
|
|
|
|
2019-10-17 17:29:17 +02:00
|
|
|
var is_mac = false
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func _ready() -> void:
|
2019-11-16 23:23:36 +01:00
|
|
|
# Restore the window position/size if values are present in the configuration cache
|
|
|
|
config_cache.load("user://cache.ini")
|
2020-02-02 10:54:03 +01:00
|
|
|
if config_cache.has_section_key("window", "screen"):
|
|
|
|
OS.current_screen = config_cache.get_value("window", "screen")
|
|
|
|
if config_cache.has_section_key("window", "maximized"):
|
|
|
|
OS.window_maximized = config_cache.get_value("window", "maximized")
|
|
|
|
|
|
|
|
if !OS.window_maximized:
|
|
|
|
if config_cache.has_section_key("window", "position"):
|
|
|
|
OS.window_position = config_cache.get_value("window", "position")
|
|
|
|
if config_cache.has_section_key("window", "size"):
|
|
|
|
OS.window_size = config_cache.get_value("window", "size")
|
2019-11-22 07:48:14 +01:00
|
|
|
|
2020-02-02 10:54:03 +01:00
|
|
|
# Restore the theme
|
|
|
|
var theme_name : String = "default"
|
|
|
|
if config_cache.has_section_key("window", "theme"):
|
|
|
|
theme_name = config_cache.get_value("window", "theme")
|
|
|
|
set_theme(theme_name)
|
2019-11-16 23:23:36 +01:00
|
|
|
|
2019-10-17 17:29:17 +02:00
|
|
|
if OS.get_name() == "OSX":
|
|
|
|
is_mac = true
|
2019-10-20 16:22:06 +02:00
|
|
|
|
2019-10-19 14:20:18 +02:00
|
|
|
# In HTML5 export, copy all examples to the filesystem
|
|
|
|
if OS.get_name() == "HTML5":
|
2019-10-20 11:21:02 +02:00
|
|
|
print("Copying samples")
|
2019-10-19 14:20:18 +02:00
|
|
|
var dir : Directory = Directory.new()
|
|
|
|
dir.make_dir("/examples")
|
2020-01-27 08:17:18 +01:00
|
|
|
dir.open("res://material_maker/examples/")
|
2019-10-19 14:20:18 +02:00
|
|
|
dir.list_dir_begin(true)
|
|
|
|
while true:
|
|
|
|
var f = dir.get_next()
|
|
|
|
if f == "":
|
|
|
|
break
|
|
|
|
if f.ends_with(".ptex"):
|
2019-10-20 11:21:02 +02:00
|
|
|
print(f)
|
2020-01-27 08:17:18 +01:00
|
|
|
dir.copy("res://material_maker/examples/"+f, "/examples/"+f)
|
2019-10-20 11:21:02 +02:00
|
|
|
print("Done")
|
2019-10-20 16:22:06 +02:00
|
|
|
|
2019-10-18 00:26:11 +02:00
|
|
|
# Upscale everything if the display requires it (crude hiDPI support).
|
2019-10-17 14:46:16 +02:00
|
|
|
# This prevents UI elements from being too small on hiDPI displays.
|
|
|
|
if OS.get_screen_dpi() >= 192 and OS.get_screen_size().x >= 2048:
|
|
|
|
get_tree().set_screen_stretch(SceneTree.STRETCH_MODE_DISABLED, SceneTree.STRETCH_ASPECT_IGNORE, Vector2(), 2)
|
|
|
|
|
2019-10-18 00:26:11 +02:00
|
|
|
# Set a minimum window size to prevent UI elements from collapsing on each other.
|
2019-10-17 14:28:46 +02:00
|
|
|
# This property is only available in 3.2alpha or later, so use `set()` to fail gracefully if it doesn't exist.
|
|
|
|
OS.set("min_window_size", Vector2(1024, 600))
|
|
|
|
|
2020-02-02 10:54:03 +01:00
|
|
|
OS.set_window_title(ProjectSettings.get_setting("application/config/name")+" v"+ProjectSettings.get_setting("application/config/release"))
|
2019-01-24 22:27:41 +01:00
|
|
|
load_recents()
|
2018-08-05 18:46:23 +02:00
|
|
|
for m in $VBoxContainer/Menu.get_children():
|
2018-08-27 12:16:37 +02:00
|
|
|
var menu = m.get_popup()
|
|
|
|
create_menu(menu, m.name)
|
|
|
|
m.connect("about_to_show", self, "menu_about_to_show", [ m.name, menu ])
|
2018-08-09 22:19:27 +02:00
|
|
|
new_material()
|
2020-02-19 08:31:49 +01:00
|
|
|
|
|
|
|
do_load_materials(OS.get_cmdline_args())
|
2018-08-05 18:46:23 +02:00
|
|
|
|
2019-11-16 23:27:07 +01:00
|
|
|
func _input(event: InputEvent) -> void:
|
|
|
|
if event.is_action_pressed("toggle_fullscreen"):
|
|
|
|
OS.window_fullscreen = !OS.window_fullscreen
|
|
|
|
|
2019-10-07 20:41:57 +02:00
|
|
|
func get_current_graph_edit() -> MMGraphEdit:
|
2019-09-29 10:07:32 +02:00
|
|
|
var graph_edit = projects.get_current_tab_control()
|
2019-09-27 08:28:33 +02:00
|
|
|
if graph_edit != null and graph_edit is GraphEdit:
|
|
|
|
return graph_edit
|
|
|
|
return null
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func create_menu(menu, menu_name) -> PopupMenu:
|
2018-08-16 15:08:09 +02:00
|
|
|
menu.clear()
|
2018-08-05 18:46:23 +02:00
|
|
|
menu.connect("id_pressed", self, "_on_PopupMenu_id_pressed")
|
|
|
|
for i in MENU.size():
|
2019-01-24 22:27:41 +01:00
|
|
|
if MENU[i].has("standalone_only") and MENU[i].standalone_only and Engine.editor_hint:
|
|
|
|
continue
|
2019-11-10 15:02:00 +01:00
|
|
|
if MENU[i].has("editor_only") and MENU[i].editor_only and !Engine.editor_hint:
|
|
|
|
continue
|
2018-08-05 18:46:23 +02:00
|
|
|
if MENU[i].menu != menu_name:
|
|
|
|
continue
|
|
|
|
if MENU[i].has("submenu"):
|
|
|
|
var submenu = PopupMenu.new()
|
2019-01-24 22:27:41 +01:00
|
|
|
var submenu_function = "create_menu_"+MENU[i].submenu
|
|
|
|
if has_method(submenu_function):
|
2019-10-07 20:41:57 +02:00
|
|
|
submenu.connect("about_to_show", self, submenu_function, [ submenu ]);
|
2019-01-24 22:27:41 +01:00
|
|
|
else:
|
|
|
|
create_menu(submenu, MENU[i].submenu)
|
2018-08-05 18:46:23 +02:00
|
|
|
menu.add_child(submenu)
|
|
|
|
menu.add_submenu_item(MENU[i].description, submenu.get_name())
|
|
|
|
elif MENU[i].has("description"):
|
2018-08-07 23:21:12 +02:00
|
|
|
var shortcut = 0
|
|
|
|
if MENU[i].has("shortcut"):
|
|
|
|
for s in MENU[i].shortcut.split("+"):
|
|
|
|
if s == "Alt":
|
|
|
|
shortcut |= KEY_MASK_ALT
|
|
|
|
elif s == "Control":
|
2019-10-17 17:29:17 +02:00
|
|
|
shortcut |= KEY_MASK_CMD if is_mac else KEY_MASK_CTRL
|
2018-08-07 23:21:12 +02:00
|
|
|
elif s == "Shift":
|
|
|
|
shortcut |= KEY_MASK_SHIFT
|
|
|
|
else:
|
|
|
|
shortcut |= OS.find_scancode_from_string(s)
|
|
|
|
menu.add_item(MENU[i].description, i, shortcut)
|
2018-08-05 18:46:23 +02:00
|
|
|
else:
|
|
|
|
menu.add_separator()
|
|
|
|
return menu
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func create_menu_load_recent(menu) -> void:
|
2019-10-07 20:41:57 +02:00
|
|
|
menu.clear()
|
2019-10-21 15:54:15 +02:00
|
|
|
if recent_files.empty():
|
|
|
|
menu.add_item("No items found", 0)
|
|
|
|
menu.set_item_disabled(0, true)
|
|
|
|
else:
|
|
|
|
for i in recent_files.size():
|
|
|
|
menu.add_item(recent_files[i], i)
|
|
|
|
if !menu.is_connected("id_pressed", self, "_on_LoadRecent_id_pressed"):
|
|
|
|
menu.connect("id_pressed", self, "_on_LoadRecent_id_pressed")
|
2019-10-07 20:41:57 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func _on_LoadRecent_id_pressed(id) -> void:
|
2020-02-26 23:42:19 +01:00
|
|
|
if !do_load_material(recent_files[id]):
|
|
|
|
recent_files.remove(id)
|
2019-01-24 22:27:41 +01:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func load_recents() -> void:
|
2019-01-24 22:27:41 +01:00
|
|
|
var f = File.new()
|
|
|
|
if f.open("user://recent_files.bin", File.READ) == OK:
|
|
|
|
recent_files = parse_json(f.get_as_text())
|
|
|
|
f.close()
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func add_recent(path) -> void:
|
2019-01-24 22:27:41 +01:00
|
|
|
while true:
|
|
|
|
var index = recent_files.find(path)
|
|
|
|
if index >= 0:
|
|
|
|
recent_files.remove(index)
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
recent_files.push_front(path)
|
2019-11-17 07:40:50 +01:00
|
|
|
while recent_files.size() > RECENT_FILES_COUNT:
|
|
|
|
recent_files.pop_back()
|
2019-01-24 22:27:41 +01:00
|
|
|
var f = File.new()
|
|
|
|
f.open("user://recent_files.bin", File.WRITE)
|
|
|
|
f.store_string(to_json(recent_files))
|
|
|
|
f.close()
|
|
|
|
|
2020-02-17 08:54:46 +01:00
|
|
|
|
|
|
|
func create_menu_export_material(menu) -> void:
|
|
|
|
menu.clear()
|
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
|
|
|
if graph_edit != null:
|
|
|
|
var material_node = graph_edit.get_material_node()
|
|
|
|
for p in material_node.get_export_profiles():
|
|
|
|
menu.add_item(p)
|
|
|
|
if !menu.is_connected("id_pressed", self, "_on_ExportMaterial_id_pressed"):
|
|
|
|
menu.connect("id_pressed", self, "_on_ExportMaterial_id_pressed")
|
|
|
|
|
|
|
|
func export_material(file_path : String, profile : String) -> void:
|
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
|
|
|
if graph_edit == null:
|
|
|
|
return
|
2020-02-24 23:29:33 +01:00
|
|
|
var export_prefix = file_path.trim_suffix("."+file_path.get_extension())
|
|
|
|
graph_edit.export_material(export_prefix, profile)
|
2020-02-17 08:54:46 +01:00
|
|
|
|
|
|
|
func _on_ExportMaterial_id_pressed(id) -> void:
|
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
|
|
|
if graph_edit == null:
|
|
|
|
return
|
|
|
|
var material_node = graph_edit.get_material_node()
|
|
|
|
if material_node == null:
|
|
|
|
return
|
|
|
|
var profile = material_node.get_export_profiles()[id]
|
|
|
|
var dialog : FileDialog = FileDialog.new()
|
|
|
|
dialog.rect_min_size = Vector2(500, 500)
|
|
|
|
dialog.access = FileDialog.ACCESS_FILESYSTEM
|
|
|
|
dialog.mode = FileDialog.MODE_SAVE_FILE
|
|
|
|
dialog.add_filter("*."+material_node.get_export_extension(profile)+";"+profile+" Material")
|
2020-02-24 23:29:33 +01:00
|
|
|
add_child(dialog)
|
2020-02-17 08:54:46 +01:00
|
|
|
dialog.connect("file_selected", self, "export_material", [ profile ])
|
|
|
|
dialog.popup_centered()
|
|
|
|
|
|
|
|
|
2020-02-02 10:54:03 +01:00
|
|
|
func create_menu_set_theme(menu) -> void:
|
|
|
|
menu.clear()
|
|
|
|
for t in THEMES:
|
|
|
|
menu.add_item(t)
|
|
|
|
if !menu.is_connected("id_pressed", self, "_on_SetTheme_id_pressed"):
|
|
|
|
menu.connect("id_pressed", self, "_on_SetTheme_id_pressed")
|
|
|
|
|
|
|
|
func set_theme(theme_name) -> void:
|
|
|
|
theme = load("res://material_maker/theme/"+theme_name+".tres")
|
|
|
|
|
|
|
|
func _on_SetTheme_id_pressed(id) -> void:
|
|
|
|
var theme_name : String = THEMES[id].to_lower()
|
|
|
|
set_theme(theme_name)
|
|
|
|
config_cache.set_value("window", "theme", theme_name)
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func create_menu_create(menu) -> void:
|
2020-01-28 11:47:29 +01:00
|
|
|
var gens = mm_loader.get_generator_list()
|
2019-10-07 20:41:57 +02:00
|
|
|
menu.clear()
|
|
|
|
for i in gens.size():
|
|
|
|
menu.add_item(gens[i], i)
|
|
|
|
if !menu.is_connected("id_pressed", self, "_on_Create_id_pressed"):
|
|
|
|
menu.connect("id_pressed", self, "_on_Create_id_pressed")
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func _on_Create_id_pressed(id) -> void:
|
2019-10-07 20:41:57 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
|
|
|
if graph_edit != null:
|
2020-01-28 11:47:29 +01:00
|
|
|
var gens = mm_loader.get_generator_list()
|
2019-10-07 20:41:57 +02:00
|
|
|
graph_edit.create_gen_from_type(gens[id])
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func menu_about_to_show(name, menu) -> void:
|
2018-08-27 12:16:37 +02:00
|
|
|
for i in MENU.size():
|
|
|
|
if MENU[i].menu != name:
|
|
|
|
continue
|
|
|
|
if MENU[i].has("submenu"):
|
|
|
|
pass
|
|
|
|
elif MENU[i].has("command"):
|
|
|
|
var command_name = MENU[i].command+"_is_disabled"
|
|
|
|
if has_method(command_name):
|
|
|
|
var is_disabled = call(command_name)
|
|
|
|
menu.set_item_disabled(menu.get_item_index(i), is_disabled)
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func new_pane() -> GraphEdit:
|
2020-01-25 15:20:08 +01:00
|
|
|
var graph_edit = preload("res://material_maker/graph_edit.tscn").instance()
|
2018-10-13 17:31:37 +02:00
|
|
|
graph_edit.node_factory = $NodeFactory
|
2018-09-04 21:45:14 +02:00
|
|
|
graph_edit.editor_interface = editor_interface
|
2019-09-29 10:07:32 +02:00
|
|
|
projects.add_child(graph_edit)
|
|
|
|
projects.current_tab = graph_edit.get_index()
|
2019-10-20 16:22:06 +02:00
|
|
|
return graph_edit
|
2018-08-07 09:31:41 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func new_material() -> void:
|
2018-08-07 09:31:41 +02:00
|
|
|
var graph_edit = new_pane()
|
2019-08-14 15:55:39 +02:00
|
|
|
graph_edit.new_material()
|
2018-08-05 18:46:23 +02:00
|
|
|
graph_edit.update_tab_title()
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func load_material() -> void:
|
2018-08-05 18:46:23 +02:00
|
|
|
var dialog = FileDialog.new()
|
|
|
|
add_child(dialog)
|
|
|
|
dialog.rect_min_size = Vector2(500, 500)
|
|
|
|
dialog.access = FileDialog.ACCESS_FILESYSTEM
|
2018-08-27 17:22:57 +02:00
|
|
|
dialog.mode = FileDialog.MODE_OPEN_FILES
|
2019-10-20 19:00:11 +02:00
|
|
|
dialog.add_filter("*.ptex;Procedural Textures File")
|
2018-08-27 17:22:57 +02:00
|
|
|
dialog.connect("files_selected", self, "do_load_materials")
|
2018-08-05 18:46:23 +02:00
|
|
|
dialog.popup_centered()
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func do_load_materials(filenames) -> void:
|
2018-08-27 17:22:57 +02:00
|
|
|
for f in filenames:
|
|
|
|
do_load_material(f)
|
|
|
|
|
2020-02-26 23:42:19 +01:00
|
|
|
func do_load_material(filename) -> bool:
|
2019-10-07 20:41:57 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
2019-08-12 15:58:42 +02:00
|
|
|
var node_count = 2 # So test below succeeds if graph_edit is null...
|
|
|
|
if graph_edit != null:
|
2019-08-14 15:55:39 +02:00
|
|
|
node_count = 0
|
2018-08-09 22:19:27 +02:00
|
|
|
for c in graph_edit.get_children():
|
|
|
|
if c is GraphNode:
|
|
|
|
node_count += 1
|
|
|
|
if node_count > 1:
|
|
|
|
break
|
|
|
|
if node_count > 1:
|
|
|
|
graph_edit = new_pane()
|
2020-02-26 23:42:19 +01:00
|
|
|
if graph_edit.load_file(filename):
|
|
|
|
add_recent(filename)
|
|
|
|
return true
|
|
|
|
return false
|
2018-08-05 18:46:23 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func save_material() -> void:
|
2019-10-07 20:41:57 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
2018-08-10 12:31:15 +02:00
|
|
|
if graph_edit != null:
|
2019-08-12 15:58:42 +02:00
|
|
|
if graph_edit.save_path != null:
|
|
|
|
graph_edit.save_file(graph_edit.save_path)
|
|
|
|
else:
|
|
|
|
save_material_as()
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func save_material_as() -> void:
|
2019-10-07 20:41:57 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
2018-08-10 12:31:15 +02:00
|
|
|
if graph_edit != null:
|
2019-08-12 15:58:42 +02:00
|
|
|
var dialog = FileDialog.new()
|
|
|
|
add_child(dialog)
|
|
|
|
dialog.rect_min_size = Vector2(500, 500)
|
|
|
|
dialog.access = FileDialog.ACCESS_FILESYSTEM
|
|
|
|
dialog.mode = FileDialog.MODE_SAVE_FILE
|
2019-10-20 19:00:11 +02:00
|
|
|
dialog.add_filter("*.ptex;Procedural Textures File")
|
2019-08-12 15:58:42 +02:00
|
|
|
dialog.connect("file_selected", graph_edit, "save_file")
|
|
|
|
dialog.popup_centered()
|
2018-08-05 18:46:23 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func close_material() -> void:
|
2019-09-29 10:07:32 +02:00
|
|
|
projects.close_tab()
|
2018-08-10 12:31:15 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func quit() -> void:
|
2020-02-02 10:54:03 +01:00
|
|
|
dim_window()
|
|
|
|
get_tree().quit()
|
2018-08-07 09:31:41 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func edit_cut() -> void:
|
2019-10-07 20:41:57 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
2018-08-19 02:11:52 +02:00
|
|
|
if graph_edit != null:
|
|
|
|
graph_edit.cut()
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func edit_cut_is_disabled() -> bool:
|
2019-10-07 20:41:57 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
2018-08-27 12:16:37 +02:00
|
|
|
return graph_edit == null or !graph_edit.can_copy()
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func edit_copy() -> void:
|
2019-10-07 20:41:57 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
2018-08-19 02:11:52 +02:00
|
|
|
if graph_edit != null:
|
|
|
|
graph_edit.copy()
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func edit_copy_is_disabled() -> bool:
|
2018-08-27 12:16:37 +02:00
|
|
|
return edit_cut_is_disabled()
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func edit_paste() -> void:
|
2019-10-07 20:41:57 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
2018-08-19 02:11:52 +02:00
|
|
|
if graph_edit != null:
|
|
|
|
graph_edit.paste()
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func edit_paste_is_disabled() -> bool:
|
2018-08-27 12:16:37 +02:00
|
|
|
var data = parse_json(OS.clipboard)
|
|
|
|
return data == null
|
2019-10-20 16:22:06 +02:00
|
|
|
|
2019-11-17 07:40:50 +01:00
|
|
|
func edit_duplicate() -> void:
|
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
|
|
|
if graph_edit != null:
|
|
|
|
graph_edit.duplicate_selected()
|
|
|
|
|
|
|
|
func edit_duplicate_is_disabled() -> bool:
|
|
|
|
return edit_cut_is_disabled()
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func view_center() -> void:
|
2019-10-17 19:03:18 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
|
|
|
graph_edit.center_view()
|
2019-10-20 16:22:06 +02:00
|
|
|
|
|
|
|
func view_reset_zoom() -> void:
|
2019-10-17 19:03:18 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
|
|
|
graph_edit.zoom = 1
|
2018-08-27 12:16:37 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
|
|
|
|
func get_selected_nodes() -> Array:
|
2019-10-07 20:41:57 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
2019-09-27 08:28:33 +02:00
|
|
|
if graph_edit != null:
|
2019-09-29 10:07:32 +02:00
|
|
|
return graph_edit.get_selected_nodes()
|
2019-09-09 22:00:18 +02:00
|
|
|
else:
|
|
|
|
return []
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func create_subgraph() -> void:
|
2019-10-07 20:41:57 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
2019-09-29 10:07:32 +02:00
|
|
|
if graph_edit != null:
|
|
|
|
graph_edit.create_subgraph()
|
2019-09-27 08:28:33 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func make_selected_nodes_editable() -> void:
|
2019-09-09 22:00:18 +02:00
|
|
|
var selected_nodes = get_selected_nodes()
|
|
|
|
if !selected_nodes.empty():
|
|
|
|
for n in selected_nodes:
|
2019-10-20 00:59:51 +02:00
|
|
|
if n.generator.toggle_editable():
|
|
|
|
n.update_node()
|
2019-09-09 22:00:18 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func add_to_user_library() -> void:
|
2019-09-09 22:00:18 +02:00
|
|
|
var selected_nodes = get_selected_nodes()
|
|
|
|
if !selected_nodes.empty():
|
2020-01-25 15:20:08 +01:00
|
|
|
var dialog = preload("res://material_maker/widgets/line_dialog.tscn").instance()
|
2019-10-13 10:14:43 +02:00
|
|
|
dialog.set_value(library.get_selected_item_name())
|
2019-09-09 22:00:18 +02:00
|
|
|
dialog.set_texts("New library element", "Select a name for the new library element")
|
|
|
|
add_child(dialog)
|
|
|
|
dialog.connect("ok", self, "do_add_to_user_library", [ selected_nodes ])
|
|
|
|
dialog.popup_centered()
|
2018-08-22 07:33:50 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func do_add_to_user_library(name, nodes) -> void:
|
2019-10-07 20:41:57 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
2018-08-22 07:33:50 +02:00
|
|
|
var data
|
|
|
|
if nodes.size() == 1:
|
2019-10-07 20:41:57 +02:00
|
|
|
data = nodes[0].generator.serialize()
|
2018-08-22 07:33:50 +02:00
|
|
|
data.erase("node_position")
|
2019-09-27 08:28:33 +02:00
|
|
|
elif graph_edit != null:
|
2018-08-22 07:33:50 +02:00
|
|
|
data = graph_edit.serialize_selection()
|
2018-08-07 23:21:12 +02:00
|
|
|
var dir = Directory.new()
|
|
|
|
dir.make_dir("user://library")
|
|
|
|
dir.make_dir("user://library/user")
|
|
|
|
data.library = "user://library/user.json"
|
2019-11-10 15:38:54 +01:00
|
|
|
data.icon = library.get_icon_name(name)
|
2019-12-15 12:41:00 +01:00
|
|
|
var result = nodes[0].generator.render(0, 64, true)
|
2019-10-07 20:41:57 +02:00
|
|
|
while result is GDScriptFunctionState:
|
|
|
|
result = yield(result, "completed")
|
2019-11-10 15:38:54 +01:00
|
|
|
result.save_to_file("user://library/user/"+data.icon+".png")
|
2019-10-07 20:41:57 +02:00
|
|
|
result.release()
|
2019-10-13 10:14:43 +02:00
|
|
|
library.add_item(data, name, library.get_preview_texture(data))
|
2019-10-07 20:41:57 +02:00
|
|
|
library.save_library("user://library/user.json")
|
2018-08-07 23:21:12 +02:00
|
|
|
|
2019-11-05 20:00:28 +01:00
|
|
|
func export_library() -> void:
|
|
|
|
var dialog : FileDialog = FileDialog.new()
|
|
|
|
add_child(dialog)
|
|
|
|
dialog.rect_min_size = Vector2(500, 500)
|
|
|
|
dialog.access = FileDialog.ACCESS_FILESYSTEM
|
|
|
|
dialog.mode = FileDialog.MODE_SAVE_FILE
|
|
|
|
dialog.add_filter("*.json;JSON files")
|
|
|
|
dialog.connect("file_selected", self, "do_export_library")
|
|
|
|
dialog.popup_centered()
|
|
|
|
|
|
|
|
func do_export_library(path : String) -> void:
|
|
|
|
library.export_libraries(path)
|
|
|
|
|
2019-11-10 15:02:00 +01:00
|
|
|
func get_doc_dir() -> String:
|
2019-10-19 01:46:25 +02:00
|
|
|
var base_dir = OS.get_executable_path().replace("\\", "/").get_base_dir()
|
|
|
|
# In release builds, documentation is expected to be located in
|
|
|
|
# a subdirectory of the program directory
|
2019-11-10 15:02:00 +01:00
|
|
|
var release_doc_path = base_dir.plus_file("doc")
|
2019-10-19 01:46:25 +02:00
|
|
|
# In development, documentation is part of the project files.
|
|
|
|
# We can use a globalized `res://` path here as the project isn't exported.
|
2020-01-27 08:17:18 +01:00
|
|
|
var devel_doc_path = ProjectSettings.globalize_path("res://material_maker/doc/_build/html")
|
2019-11-10 15:02:00 +01:00
|
|
|
for p in [ release_doc_path, devel_doc_path ]:
|
|
|
|
var file = File.new()
|
|
|
|
if file.file_exists(p+"/index.html"):
|
|
|
|
return p
|
|
|
|
return ""
|
|
|
|
|
|
|
|
func show_doc() -> void:
|
|
|
|
var doc_dir = get_doc_dir()
|
|
|
|
if doc_dir != "":
|
|
|
|
OS.shell_open(doc_dir+"/index.html")
|
|
|
|
|
|
|
|
func show_doc_is_disabled() -> bool:
|
|
|
|
return get_doc_dir() == ""
|
|
|
|
|
|
|
|
func show_library_item_doc() -> void:
|
|
|
|
var doc_dir : String = get_doc_dir()
|
|
|
|
if doc_dir != "":
|
|
|
|
var doc_name = library.get_selected_item_doc_name()
|
|
|
|
if doc_name != "":
|
|
|
|
var doc_path : String = doc_dir+"/node_"+doc_name+".html"
|
|
|
|
OS.shell_open(doc_path)
|
|
|
|
|
|
|
|
func show_library_item_doc_is_disabled() -> bool:
|
|
|
|
return get_doc_dir() == "" or library.get_selected_item_doc_name() == ""
|
2018-08-25 08:07:22 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func bug_report() -> void:
|
2018-08-11 11:33:24 +02:00
|
|
|
OS.shell_open("https://github.com/RodZill4/godot-procedural-textures/issues")
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func about() -> void:
|
2020-01-25 15:20:08 +01:00
|
|
|
var about_box = preload("res://material_maker/widgets/about/about.tscn").instance()
|
2018-08-11 11:33:24 +02:00
|
|
|
add_child(about_box)
|
|
|
|
about_box.popup_centered()
|
2019-10-20 16:22:06 +02:00
|
|
|
|
|
|
|
func _on_PopupMenu_id_pressed(id) -> void:
|
2018-08-05 18:46:23 +02:00
|
|
|
var node_type = null
|
|
|
|
if MENU[id].has("command"):
|
2018-08-10 12:31:15 +02:00
|
|
|
var command = MENU[id].command
|
|
|
|
if has_method(command):
|
|
|
|
call(command)
|
2019-04-13 19:44:37 +02:00
|
|
|
|
2018-08-07 09:31:41 +02:00
|
|
|
# Preview
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func update_preview() -> void:
|
2019-11-05 22:28:01 +01:00
|
|
|
var status
|
|
|
|
need_update = true
|
|
|
|
if updating:
|
|
|
|
return
|
|
|
|
updating = true
|
|
|
|
while need_update:
|
|
|
|
need_update = false
|
|
|
|
status = update_preview_2d()
|
|
|
|
while status is GDScriptFunctionState:
|
|
|
|
status = yield(status, "completed")
|
2019-12-10 06:54:10 +01:00
|
|
|
status = update_preview_3d([ $VBoxContainer/HBoxContainer/VBoxContainer/Preview/Preview3D, $VBoxContainer/HBoxContainer/ProjectsPane/Preview3D])
|
2019-11-05 22:28:01 +01:00
|
|
|
while status is GDScriptFunctionState:
|
|
|
|
status = yield(status, "completed")
|
|
|
|
updating = false
|
2018-08-07 09:31:41 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func update_preview_2d(node = null) -> void:
|
2019-10-07 20:41:57 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
2019-09-29 10:07:32 +02:00
|
|
|
if graph_edit != null:
|
|
|
|
if node == null:
|
|
|
|
for n in graph_edit.get_children():
|
|
|
|
if n is GraphNode and n.selected:
|
|
|
|
node = n
|
|
|
|
break
|
|
|
|
if node != null:
|
2019-12-15 12:41:00 +01:00
|
|
|
var result = node.generator.render(0, 1024, true)
|
2019-10-05 11:04:50 +02:00
|
|
|
while result is GDScriptFunctionState:
|
|
|
|
result = yield(result, "completed")
|
|
|
|
var tex = ImageTexture.new()
|
|
|
|
result.copy_to_texture(tex)
|
|
|
|
result.release()
|
2019-12-08 08:50:14 +01:00
|
|
|
preview_2d.set_preview_texture(tex)
|
2020-02-10 23:13:10 +01:00
|
|
|
preview_2d_background.set_preview_texture(tex)
|
2019-12-27 22:30:49 +01:00
|
|
|
else:
|
|
|
|
preview_2d.set_preview_texture(null)
|
2020-02-10 23:13:10 +01:00
|
|
|
preview_2d_background.set_preview_texture(null)
|
2019-09-29 10:07:32 +02:00
|
|
|
|
2019-12-10 06:54:10 +01:00
|
|
|
func update_preview_3d(previews : Array) -> void:
|
2019-10-07 20:41:57 +02:00
|
|
|
var graph_edit : MMGraphEdit = get_current_graph_edit()
|
2019-09-29 10:07:32 +02:00
|
|
|
if graph_edit != null and graph_edit.top_generator != null and graph_edit.top_generator.has_node("Material"):
|
|
|
|
var gen_material = graph_edit.top_generator.get_node("Material")
|
2019-11-06 22:33:01 +01:00
|
|
|
var status = gen_material.render_textures()
|
2019-08-17 17:35:48 +02:00
|
|
|
while status is GDScriptFunctionState:
|
|
|
|
status = yield(status, "completed")
|
2019-12-10 06:54:10 +01:00
|
|
|
for p in previews:
|
|
|
|
gen_material.update_materials(p.get_materials())
|
2018-08-05 18:46:23 +02:00
|
|
|
|
2020-02-15 11:17:41 +01:00
|
|
|
var selected_node = null
|
2019-12-27 22:30:49 +01:00
|
|
|
func on_selected_node_change(node) -> void:
|
2020-02-15 11:17:41 +01:00
|
|
|
if node != selected_node:
|
|
|
|
selected_node = node
|
|
|
|
preview_2d.setup_controls(node.generator if node != null else null)
|
|
|
|
update_preview_2d(node)
|
2019-12-27 22:30:49 +01:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func _on_Projects_tab_changed(tab) -> void:
|
2019-09-29 10:07:32 +02:00
|
|
|
var new_tab = projects.get_current_tab_control()
|
2018-08-27 17:22:57 +02:00
|
|
|
if new_tab != current_tab:
|
|
|
|
if new_tab != null:
|
|
|
|
for c in get_incoming_connections():
|
|
|
|
if c.method_name == "update_preview" or c.method_name == "update_preview_2d":
|
|
|
|
c.source.disconnect(c.signal_name, self, c.method_name)
|
|
|
|
new_tab.connect("graph_changed", self, "update_preview")
|
2020-01-03 16:03:53 +01:00
|
|
|
if !new_tab.is_connected("node_selected", self, "on_selected_node_change"):
|
|
|
|
new_tab.connect("node_selected", self, "on_selected_node_change")
|
2018-08-27 17:22:57 +02:00
|
|
|
current_tab = new_tab
|
2018-08-07 09:31:41 +02:00
|
|
|
update_preview()
|
2019-10-01 08:34:08 +02:00
|
|
|
|
2019-11-16 23:23:36 +01:00
|
|
|
func _exit_tree() -> void:
|
|
|
|
# Save the window position and size to remember it when restarting the application
|
2020-02-02 10:54:03 +01:00
|
|
|
config_cache.set_value("window", "screen", OS.current_screen)
|
|
|
|
config_cache.set_value("window", "maximized", OS.window_maximized || OS.window_fullscreen)
|
|
|
|
config_cache.set_value("window", "position", OS.window_position)
|
|
|
|
config_cache.set_value("window", "size", OS.window_size)
|
|
|
|
config_cache.save("user://cache.ini")
|
2019-12-10 06:54:10 +01:00
|
|
|
|
2020-01-15 22:12:21 +01:00
|
|
|
func _notification(what : int) -> void:
|
2020-02-02 10:54:03 +01:00
|
|
|
if what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST:
|
|
|
|
dim_window()
|
2020-01-15 22:12:21 +01:00
|
|
|
|
|
|
|
func dim_window() -> void:
|
|
|
|
# Darken the UI to denote that the application is currently exiting
|
|
|
|
# (it won't respond to user input in this state).
|
|
|
|
modulate = Color(0.5, 0.5, 0.5)
|
|
|
|
|
2020-02-10 23:13:10 +01:00
|
|
|
func show_background_preview_2d(button_pressed):
|
|
|
|
$VBoxContainer/HBoxContainer/ProjectsPane/Preview2D.visible = button_pressed
|
|
|
|
if button_pressed:
|
|
|
|
$VBoxContainer/HBoxContainer/ProjectsPane/PreviewUI/Preview3DButton.pressed = false
|
|
|
|
|
|
|
|
func show_background_preview_3d(button_pressed):
|
2019-12-10 06:54:10 +01:00
|
|
|
$VBoxContainer/HBoxContainer/ProjectsPane/Preview3D.visible = button_pressed
|
2020-02-10 23:13:10 +01:00
|
|
|
$VBoxContainer/HBoxContainer/ProjectsPane/PreviewUI/Panel.visible = button_pressed
|
|
|
|
if button_pressed:
|
|
|
|
$VBoxContainer/HBoxContainer/ProjectsPane/PreviewUI/Preview2DButton.pressed = false
|
|
|
|
|
|
|
|
|
2020-02-15 22:20:26 +01:00
|
|
|
func generate_screenshots():
|
|
|
|
var result = library.generate_screenshots(get_current_graph_edit())
|
|
|
|
while result is GDScriptFunctionState:
|
|
|
|
result = yield(result, "completed")
|
|
|
|
print(result)
|