mirror of
https://github.com/Relintai/material-maker.git
synced 2024-11-13 06:27:18 +01:00
ff191538f6
Moved handling of the "global" definitions of shaders into main shader generation code. Added group creation (does not create inputs yet, and remotes should be cleaned). updated all preview meshes (to fix problems with depth).
366 lines
11 KiB
GDScript
366 lines
11 KiB
GDScript
tool
|
|
extends Panel
|
|
|
|
var recent_files = []
|
|
var recent_files_submenu = null
|
|
|
|
var editor_interface = null
|
|
var current_tab = null
|
|
|
|
onready var renderer = $Renderer
|
|
onready var projects = $VBoxContainer/HBoxContainer/Projects
|
|
|
|
const MENU = [
|
|
{ menu="File", command="new_material", description="New material" },
|
|
{ menu="File", command="load_material", shortcut="Control+O", description="Load material" },
|
|
{ menu="File", submenu="load_recent", description="Load recent", standalone_only=true },
|
|
{ menu="File" },
|
|
{ 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..." },
|
|
{ menu="File", command="save_all_materials", description="Save all materials..." },
|
|
{ menu="File" },
|
|
{ menu="File", command="export_material", shortcut="Control+E", description="Export material" },
|
|
{ menu="File" },
|
|
{ menu="File", command="close_material", description="Close material" },
|
|
{ menu="File", command="quit", shortcut="Control+Q", description="Quit" },
|
|
{ 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" },
|
|
{ menu="Tools", command="create_subgraph", shortcut="Control+G", description="Create subgraph" },
|
|
{ menu="Tools", command="make_selected_nodes_editable", shortcut="Control+F", description="Make selected nodes editable" },
|
|
{ menu="Tools", command="add_to_user_library", description="Add selected node to user library" },
|
|
{ menu="Tools", command="save_user_library", description="Save user library" },
|
|
{ menu="Help", command="show_doc", description="User manual" },
|
|
{ menu="Help", command="bug_report", description="Report a bug" },
|
|
{ menu="Help" },
|
|
{ menu="Help", command="about", description="About" }
|
|
]
|
|
|
|
signal quit
|
|
|
|
func _ready():
|
|
if !Engine.editor_hint:
|
|
OS.set_window_title(ProjectSettings.get_setting("application/config/name")+" v"+ProjectSettings.get_setting("application/config/release"))
|
|
load_recents()
|
|
for m in $VBoxContainer/Menu.get_children():
|
|
var menu = m.get_popup()
|
|
create_menu(menu, m.name)
|
|
m.connect("about_to_show", self, "menu_about_to_show", [ m.name, menu ])
|
|
new_material()
|
|
|
|
func get_current_graph_edit():
|
|
var graph_edit = projects.get_current_tab_control()
|
|
if graph_edit != null and graph_edit is GraphEdit:
|
|
return graph_edit
|
|
return null
|
|
|
|
func create_menu(menu, menu_name):
|
|
menu.clear()
|
|
menu.connect("id_pressed", self, "_on_PopupMenu_id_pressed")
|
|
for i in MENU.size():
|
|
if MENU[i].has("standalone_only") and MENU[i].standalone_only and Engine.editor_hint:
|
|
continue
|
|
if MENU[i].menu != menu_name:
|
|
continue
|
|
if MENU[i].has("submenu"):
|
|
var submenu = PopupMenu.new()
|
|
var submenu_function = "create_menu_"+MENU[i].submenu
|
|
if has_method(submenu_function):
|
|
call(submenu_function, submenu)
|
|
else:
|
|
create_menu(submenu, MENU[i].submenu)
|
|
menu.add_child(submenu)
|
|
menu.add_submenu_item(MENU[i].description, submenu.get_name())
|
|
elif MENU[i].has("description"):
|
|
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":
|
|
shortcut |= KEY_MASK_CTRL
|
|
elif s == "Shift":
|
|
shortcut |= KEY_MASK_SHIFT
|
|
else:
|
|
shortcut |= OS.find_scancode_from_string(s)
|
|
menu.add_item(MENU[i].description, i, shortcut)
|
|
else:
|
|
menu.add_separator()
|
|
return menu
|
|
|
|
func update_recents_menu():
|
|
if recent_files_submenu != null:
|
|
recent_files_submenu.clear()
|
|
for i in recent_files.size():
|
|
recent_files_submenu.add_item(recent_files[i], i)
|
|
|
|
func create_menu_load_recent(menu):
|
|
menu.connect("id_pressed", self, "_on_LoadRecent_id_pressed")
|
|
recent_files_submenu = menu
|
|
update_recents_menu()
|
|
|
|
func load_recents():
|
|
var f = File.new()
|
|
if f.open("user://recent_files.bin", File.READ) == OK:
|
|
recent_files = parse_json(f.get_as_text())
|
|
f.close()
|
|
|
|
func add_recent(path):
|
|
while true:
|
|
var index = recent_files.find(path)
|
|
if index >= 0:
|
|
recent_files.remove(index)
|
|
else:
|
|
break
|
|
recent_files.push_front(path)
|
|
update_recents_menu()
|
|
var f = File.new()
|
|
f.open("user://recent_files.bin", File.WRITE)
|
|
f.store_string(to_json(recent_files))
|
|
f.close()
|
|
|
|
func menu_about_to_show(name, menu):
|
|
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)
|
|
|
|
func new_pane():
|
|
var graph_edit = preload("res://addons/material_maker/graph_edit.tscn").instance()
|
|
graph_edit.node_factory = $NodeFactory
|
|
graph_edit.renderer = $Renderer
|
|
graph_edit.editor_interface = editor_interface
|
|
projects.add_child(graph_edit)
|
|
projects.current_tab = graph_edit.get_index()
|
|
return graph_edit
|
|
|
|
func new_material():
|
|
var graph_edit = new_pane()
|
|
graph_edit.new_material()
|
|
graph_edit.update_tab_title()
|
|
|
|
func load_material():
|
|
var dialog = FileDialog.new()
|
|
add_child(dialog)
|
|
dialog.rect_min_size = Vector2(500, 500)
|
|
dialog.access = FileDialog.ACCESS_FILESYSTEM
|
|
dialog.mode = FileDialog.MODE_OPEN_FILES
|
|
dialog.add_filter("*.ptex;Procedural textures file")
|
|
dialog.connect("files_selected", self, "do_load_materials")
|
|
dialog.popup_centered()
|
|
|
|
func do_load_materials(filenames):
|
|
for f in filenames:
|
|
do_load_material(f)
|
|
|
|
func do_load_material(filename):
|
|
var graph_edit = get_current_graph_edit()
|
|
var node_count = 2 # So test below succeeds if graph_edit is null...
|
|
if graph_edit != null:
|
|
node_count = 0
|
|
for c in graph_edit.get_children():
|
|
if c is GraphNode:
|
|
print(c.name)
|
|
node_count += 1
|
|
if node_count > 1:
|
|
break
|
|
print(node_count)
|
|
if node_count > 1:
|
|
graph_edit = new_pane()
|
|
graph_edit.load_file(filename)
|
|
add_recent(filename)
|
|
|
|
func save_material():
|
|
var graph_edit = get_current_graph_edit()
|
|
if graph_edit != null:
|
|
if graph_edit.save_path != null:
|
|
graph_edit.save_file(graph_edit.save_path)
|
|
else:
|
|
save_material_as()
|
|
|
|
func save_material_as():
|
|
var graph_edit = get_current_graph_edit()
|
|
if graph_edit != null:
|
|
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
|
|
dialog.add_filter("*.ptex;Procedural textures file")
|
|
dialog.connect("file_selected", graph_edit, "save_file")
|
|
dialog.popup_centered()
|
|
|
|
func close_material():
|
|
projects.close_tab()
|
|
|
|
func export_material():
|
|
var graph_edit = get_current_graph_edit()
|
|
if graph_edit != null :
|
|
graph_edit.export_textures()
|
|
|
|
func export_material_is_disabled():
|
|
var graph_edit = get_current_graph_edit()
|
|
if graph_edit == null or graph_edit.save_path == null:
|
|
return true
|
|
return false
|
|
|
|
func quit():
|
|
if Engine.editor_hint:
|
|
emit_signal("quit")
|
|
else:
|
|
get_tree().quit()
|
|
|
|
|
|
func edit_cut():
|
|
var graph_edit = get_current_graph_edit()
|
|
if graph_edit != null:
|
|
graph_edit.cut()
|
|
|
|
func edit_cut_is_disabled():
|
|
var graph_edit = get_current_graph_edit()
|
|
return graph_edit == null or !graph_edit.can_copy()
|
|
|
|
func edit_copy():
|
|
var graph_edit = get_current_graph_edit()
|
|
if graph_edit != null:
|
|
graph_edit.copy()
|
|
|
|
func edit_copy_is_disabled():
|
|
return edit_cut_is_disabled()
|
|
|
|
func edit_paste():
|
|
var graph_edit = get_current_graph_edit()
|
|
if graph_edit != null:
|
|
graph_edit.paste()
|
|
|
|
func edit_paste_is_disabled():
|
|
var data = parse_json(OS.clipboard)
|
|
return data == null
|
|
|
|
func get_selected_nodes():
|
|
var graph_edit = get_current_graph_edit()
|
|
if graph_edit != null:
|
|
return graph_edit.get_selected_nodes()
|
|
else:
|
|
return []
|
|
|
|
func create_subgraph():
|
|
var graph_edit = get_current_graph_edit()
|
|
if graph_edit != null:
|
|
graph_edit.create_subgraph()
|
|
|
|
func make_selected_nodes_editable():
|
|
var selected_nodes = get_selected_nodes()
|
|
if !selected_nodes.empty():
|
|
for n in selected_nodes:
|
|
print(n.name)
|
|
n.generator.model = null
|
|
n.update_node()
|
|
|
|
func add_to_user_library():
|
|
var selected_nodes = get_selected_nodes()
|
|
if !selected_nodes.empty():
|
|
var dialog = preload("res://addons/material_maker/widgets/line_dialog.tscn").instance()
|
|
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()
|
|
|
|
func do_add_to_user_library(name, nodes):
|
|
var graph_edit = get_current_graph_edit()
|
|
var data
|
|
if nodes.size() == 1:
|
|
data = nodes[0].serialize()
|
|
data.erase("node_position")
|
|
elif graph_edit != null:
|
|
data = graph_edit.serialize_selection()
|
|
var dir = Directory.new()
|
|
dir.make_dir("user://library")
|
|
dir.make_dir("user://library/user")
|
|
data.library = "user://library/user.json"
|
|
data.icon = name.right(name.rfind("/")+1).to_lower()
|
|
$VBoxContainer/HBoxContainer/VBoxContainer/Library.add_item(data, name)
|
|
graph_edit.export_texture(nodes[0], "user://library/user/"+data.icon+".png", 64)
|
|
|
|
func save_user_library():
|
|
print("Saving user library")
|
|
$VBoxContainer/HBoxContainer/VBoxContainer/Library.save_library("user://library/user.json")
|
|
|
|
func show_doc():
|
|
var doc_path = OS.get_executable_path()
|
|
doc_path = doc_path.replace("\\", "/")
|
|
doc_path = doc_path.left(doc_path.rfind("/")+1)+"doc/index.html"
|
|
var file = File.new()
|
|
if file.exists(doc_path):
|
|
OS.shell_open(doc_path)
|
|
|
|
func bug_report():
|
|
OS.shell_open("https://github.com/RodZill4/godot-procedural-textures/issues")
|
|
|
|
func about():
|
|
var about_box = preload("res://addons/material_maker/widgets/about.tscn").instance()
|
|
add_child(about_box)
|
|
about_box.popup_centered()
|
|
|
|
func _on_PopupMenu_id_pressed(id):
|
|
var node_type = null
|
|
if MENU[id].has("command"):
|
|
var command = MENU[id].command
|
|
if has_method(command):
|
|
call(command)
|
|
|
|
func _on_LoadRecent_id_pressed(id):
|
|
do_load_material(recent_files[id])
|
|
|
|
# Preview
|
|
|
|
func update_preview():
|
|
update_preview_2d()
|
|
update_preview_3d()
|
|
|
|
func update_preview_2d(node = null):
|
|
var graph_edit = get_current_graph_edit()
|
|
if graph_edit != null:
|
|
var preview = $VBoxContainer/HBoxContainer/VBoxContainer/Preview
|
|
if node == null:
|
|
for n in graph_edit.get_children():
|
|
if n is GraphNode and n.selected:
|
|
node = n
|
|
break
|
|
if node != null:
|
|
var status = node.generator.render(0, renderer, 1024)
|
|
while status is GDScriptFunctionState:
|
|
status = yield(status, "completed")
|
|
if status:
|
|
var image = renderer.get_texture().get_data()
|
|
var tex = ImageTexture.new()
|
|
tex.create_from_image(image)
|
|
preview.set_2d(tex)
|
|
|
|
func update_preview_3d():
|
|
var graph_edit = get_current_graph_edit()
|
|
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")
|
|
var status = gen_material.render_textures(renderer)
|
|
while status is GDScriptFunctionState:
|
|
status = yield(status, "completed")
|
|
gen_material.update_materials($VBoxContainer/HBoxContainer/VBoxContainer/Preview.get_materials())
|
|
|
|
func _on_Projects_tab_changed(tab):
|
|
var new_tab = projects.get_current_tab_control()
|
|
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")
|
|
new_tab.connect("node_selected", self, "update_preview_2d")
|
|
current_tab = new_tab
|
|
update_preview()
|