Implemented save and shader generator editing...

This commit is contained in:
RodZill4 2019-09-09 22:00:18 +02:00
parent 0bcdbb2204
commit 27d5ddbe97
17 changed files with 212 additions and 39 deletions

View File

@ -18,6 +18,7 @@ class OutputPort:
return generator.name+"("+str(output_index)+")"
var position : Vector2 = Vector2(0, 0)
var model = null
var parameters = {}
func _ready():
@ -112,3 +113,18 @@ func get_shader_code(uv : String, output_index : int, context : MMGenContext):
func _get_shader_code(uv : String, output_index : int, context : MMGenContext):
return null
func _serialize(data):
print("cannot save "+name)
return data
func serialize():
var rv = { name=name, parameters={}, node_position={ x=position.x, y=position.y } }
for p in parameters.keys():
rv.parameters[p] = MMType.serialize_value(parameters[p])
if model != null:
rv.type = model
else:
rv = _serialize(rv)
return rv

View File

@ -38,3 +38,7 @@ func _get_shader_code(uv : String, output_index : int, context : MMGenContext):
while rv is GDScriptFunctionState:
rv = yield(rv, "completed")
return rv
func _serialize(data):
data.type = "buffer"
return data

View File

@ -21,6 +21,14 @@ func remove_generator(generator : MMGenBase):
if c.from != generator.name and c.to != generator.name:
new_connections.append(c)
connections = new_connections
generator.queue_free()
func replace_generator(old : MMGenBase, new : MMGenBase):
new.name = old.name
new.position = old.position
remove_child(old)
old.free()
add_child(new)
func connect_children(from, from_port : int, to, to_port : int):
# disconnect target
@ -47,4 +55,11 @@ func disconnect_children(from, from_port : int, to, to_port : int):
if remove == -1:
break
connections.remove(remove)
return true
return true
func _serialize(data):
data.nodes = []
for c in get_children():
data.nodes.append(c.serialize())
data.connections = connections
return data

View File

@ -129,3 +129,7 @@ func export_textures(prefix, size = null):
update_spatial_material(new_material, prefix)
ResourceSaver.save("%s.tres" % [ prefix ], new_material)
resource_filesystem.scan()
func _serialize(data):
data.type = "material"
return data

View File

@ -120,7 +120,7 @@ func subst(string, context, uv = ""):
elif p.type == "color":
value_string = "vec4(%.9f, %.9f, %.9f, %.9f)" % [ value.r, value.g, value.b, value.a ]
elif p.type == "gradient":
value_string = p.name+"_gradient_fct"
value_string = name+"__"+p.name+"_gradient_fct"
if value_string != null:
string = replace_variable(string, p.name, value_string)
if shader_model.has("inputs") and typeof(shader_model.inputs) == TYPE_ARRAY:
@ -157,7 +157,7 @@ func _get_shader_code(uv : String, output_index : int, context : MMGenContext):
if !(g is MMGradient):
g = MMGradient.new()
g.deserialize(parameters[p.name])
rv.defs += g.get_shader(p.name+"_gradient_fct")
rv.defs += g.get_shader(name+"__"+p.name+"_gradient_fct")
var variant_index = context.get_variant(self, variant_string)
if variant_index == -1:
variant_index = context.get_variant(self, variant_string)
@ -181,3 +181,7 @@ func get_globals():
if typeof(shader_model) == TYPE_DICTIONARY and shader_model.has("global") and list.find(shader_model.global) == -1:
list.append(shader_model.global)
return list
func _serialize(data):
data.shader_model = shader_model
return data

View File

@ -2,14 +2,14 @@ tool
extends Object
class_name MMGenLoader
func load_gen(filename: String) -> MMGenBase:
static func load_gen(filename: String) -> MMGenBase:
var file = File.new()
if file.open(filename, File.READ) == OK:
var data = parse_json(file.get_as_text())
return create_gen(data)
return null
func add_to_gen_graph(gen_graph, generators, connections):
static func add_to_gen_graph(gen_graph, generators, connections):
var rv = { generators=[], connections=[] }
for n in generators:
var g = create_gen(n)
@ -27,7 +27,7 @@ func add_to_gen_graph(gen_graph, generators, connections):
rv.connections.append(c)
return rv
func create_gen(data) -> MMGenBase:
static func create_gen(data) -> MMGenBase:
var generator = null
if data.has("connections") and data.has("nodes"):
generator = MMGenGraph.new()
@ -35,6 +35,9 @@ func create_gen(data) -> MMGenBase:
elif data.has("shader_model"):
generator = MMGenShader.new()
generator.set_shader_model(data.shader_model)
elif data.has("model_data"):
generator = MMGenShader.new()
generator.set_shader_model(data.model_data)
elif data.has("type"):
if data.type == "material":
generator = MMGenMaterial.new()
@ -44,13 +47,13 @@ func create_gen(data) -> MMGenBase:
generator = MMGenImage.new()
else:
var file = File.new()
if file.open("res://addons/material_maker/library/"+data.type+".mml", File.READ) == OK:
print("loaded description "+data.type+".mml")
if file.open("res://addons/material_maker/nodes/"+data.type+".mmg", File.READ) == OK:
generator = create_gen(parse_json(file.get_as_text()))
generator.model = data.type
file.close()
elif file.open("res://addons/material_maker/nodes/"+data.type+".mmn", File.READ) == OK:
generator = MMGenShader.new()
print("loaded description "+data.type+".mmn")
generator.model = data.type
generator.set_shader_model(parse_json(file.get_as_text()))
file.close()
else:
@ -58,7 +61,7 @@ func create_gen(data) -> MMGenBase:
if generator != null:
generator.name = data.type
else:
print(data)
print("LOADER: data not supported:"+str(data))
if generator != null:
if data.has("name"):
generator.name = data.name
@ -67,5 +70,5 @@ func create_gen(data) -> MMGenBase:
generator.position.y = data.node_position.y
if data.has("parameters"):
for p in data.parameters.keys():
generator.parameters[p] = data.parameters[p]
generator.parameters[p] = MMType.deserialize_value(data.parameters[p])
return generator

View File

@ -134,16 +134,14 @@ func create_nodes(data, position : Vector2 = Vector2(0, 0)):
if data.has("type"):
data = { nodes=[data], connections=[] }
if typeof(data.nodes) == TYPE_ARRAY and typeof(data.connections) == TYPE_ARRAY:
var loader = MMGenLoader.new()
var new_stuff = loader.add_to_gen_graph(generator, data.nodes, data.connections)
var new_stuff = MMGenLoader.add_to_gen_graph(generator, data.nodes, data.connections)
for g in new_stuff.generators:
g.position += position
update_graph(new_stuff.generators, new_stuff.connections)
func load_file(filename):
clear_material()
var loader = MMGenLoader.new()
generator = loader.load_gen(filename)
generator = MMGenLoader.load_gen(filename)
if generator != null:
add_child(generator)
update_graph(generator.get_children(), generator.connections)
@ -152,11 +150,7 @@ func load_file(filename):
center_view()
func save_file(filename):
var data = { nodes = [] }
for c in get_children():
if c is GraphNode:
data.nodes.append(c.serialize())
data.connections = get_connection_list()
var data = generator.serialize()
var file = File.new()
if file.open(filename, File.WRITE) == OK:
file.store_string(to_json(data))
@ -191,7 +185,7 @@ func serialize_selection():
center += n.offset+0.5*n.rect_size
center /= nodes.size()
for n in nodes:
var s = n.serialize()
var s = n.generator.serialize()
var p = n.offset-center
s.node_position = { x=p.x, y=p.y }
data.nodes.append(s)

View File

@ -254,7 +254,8 @@
},
{
"tree_item":"Miscellaneous/Custom",
"type":"custom"
"type":"custom",
"shader_model":{}
},
{
"tree_item":"Miscellaneous/Remote",

View File

@ -25,6 +25,7 @@ const MENU = [
{ 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="make_selected_nodes_editable", 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" },
@ -233,19 +234,33 @@ func edit_paste_is_disabled():
var data = parse_json(OS.clipboard)
return data == null
func add_to_user_library():
func get_selected_nodes():
var graph_edit = $VBoxContainer/HBoxContainer/Projects.get_current_tab_control()
if graph_edit != null and graph_edit is GraphEdit:
var selected_nodes = []
for n in graph_edit.get_children():
if n is GraphNode and n.selected:
selected_nodes.append(n)
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()
return selected_nodes
else:
return []
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 data

View File

@ -0,0 +1 @@
{"name":"uniform","node_position":{"x":-130,"y":-66},"parameters":{"color":{"a":1,"b":1,"g":1,"r":1,"type":"Color"},"name":0},"shader_model":{"global":"","inputs":[{"default":"0.0","label":"R","name":"r","type":"f"},{"default":"0.0","label":"G","name":"g","type":"f"},{"default":"0.0","label":"B","name":"b","type":"f"},{"default":"1.0","label":"A","name":"a","type":"f"}],"instance":"","name":"Combine","outputs":[{"rgba":"vec4($r($uv), $g($uv), $b($uv), $a($uv))"}],"parameters":[]}}

View File

@ -0,0 +1,6 @@
extends HBoxContainer
func connect_buttons(object, edit_fct, load_fct, save_fct):
$Edit.connect("pressed", object, edit_fct)
$Load.connect("pressed", object, load_fct)
$Save.connect("pressed", object, save_fct)

View File

@ -0,0 +1,31 @@
[gd_scene load_steps=5 format=2]
[ext_resource path="res://addons/material_maker/nodes/edit_buttons.gd" type="Script" id=1]
[ext_resource path="res://addons/material_maker/icons/edit.png" type="Texture" id=2]
[ext_resource path="res://addons/material_maker/icons/load.png" type="Texture" id=3]
[ext_resource path="res://addons/material_maker/icons/save.png" type="Texture" id=4]
[node name="NodeEditButtons" type="HBoxContainer"]
margin_right = 91.0
margin_bottom = 22.0
script = ExtResource( 1 )
[node name="Edit" type="Button" parent="."]
margin_right = 27.0
margin_bottom = 22.0
icon = ExtResource( 2 )
flat = true
[node name="Load" type="Button" parent="."]
margin_left = 31.0
margin_right = 59.0
margin_bottom = 22.0
icon = ExtResource( 3 )
flat = true
[node name="Save" type="Button" parent="."]
margin_left = 63.0
margin_right = 91.0
margin_bottom = 22.0
icon = ExtResource( 4 )
flat = true

View File

@ -12,6 +12,13 @@ func set_generator(g):
generator = g
update_node()
func on_close_request():
print("close")
generator.get_parent().remove_generator(generator)
func on_offset_changed():
generator.position = offset
func initialize_properties():
for o in controls:
if o == null:
@ -73,6 +80,7 @@ func update_node():
c.queue_free()
else:
custom_node_buttons = c
rect_size = Vector2(0, 0)
# Rebuild node
title = generator.get_type_name()
# Parameters
@ -133,7 +141,6 @@ func update_node():
var inputs = generator.get_input_defs()
for i in range(inputs.size()):
var input = inputs[i]
print(input)
var enable_left = false
var color_left = Color(0.5, 0.5, 0.5)
if typeof(input) == TYPE_DICTIONARY:
@ -146,11 +153,14 @@ func update_node():
else:
enable_left = true
set_slot(i, enable_left, 0, color_left, false, 0, Color())
if i >= get_child_count():
var control = Control.new()
control.rect_min_size = Vector2(0, 16)
add_child(control)
# Outputs
var outputs = generator.get_output_defs()
for i in range(outputs.size()):
var output = outputs[i]
print(output)
var enable_right = false
var color_right = Color(0.5, 0.5, 0.5)
if typeof(output) == TYPE_DICTIONARY:
@ -163,5 +173,69 @@ func update_node():
elif output.has("f"):
enable_right = true
set_slot(i, is_slot_enabled_left(i), get_slot_type_left(i), get_slot_color_left(i), enable_right, 0, color_right)
if custom_node_buttons != null:
move_child(custom_node_buttons, get_child_count()-1)
if i >= get_child_count():
var control = Control.new()
control.rect_min_size = Vector2(0, 16)
add_child(control)
if generator.model == null:
var edit_buttons = preload("res://addons/material_maker/nodes/edit_buttons.tscn").instance()
add_child(edit_buttons)
edit_buttons.connect_buttons(self, "edit_generator", "load_generator", "save_generator")
func edit_generator():
var edit_window = load("res://addons/material_maker/widgets/node_editor/node_editor.tscn").instance()
get_parent().add_child(edit_window)
if generator.shader_model != null:
edit_window.set_model_data(generator.shader_model)
edit_window.connect("node_changed", self, "update_generator")
edit_window.popup_centered()
func update_generator(shader_model):
generator.shader_model = shader_model
update_node()
func load_generator():
var dialog = FileDialog.new()
add_child(dialog)
dialog.rect_min_size = Vector2(500, 500)
dialog.access = FileDialog.ACCESS_FILESYSTEM
dialog.mode = FileDialog.MODE_OPEN_FILE
dialog.add_filter("*.mmg,*.mmn;Material Maker Generator")
dialog.connect("file_selected", self, "do_load_generator")
dialog.popup_centered()
func do_load_generator(file_name : String):
print(file_name)
var new_generator = null
if file_name.ends_with(".mmn"):
var file = File.new()
if file.open(file_name, File.READ) == OK:
new_generator = MMGenShader.new()
new_generator.set_shader_model(parse_json(file.get_as_text()))
file.close()
else:
new_generator = MMGenLoader.load_gen(file_name)
if new_generator != null:
var parent_generator = generator.get_parent()
print("before: "+generator.name)
parent_generator.replace_generator(generator, new_generator)
generator = new_generator
print("after: "+generator.name)
update_node()
func save_generator():
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("*.mmg;Material Maker Generator")
dialog.connect("file_selected", self, "do_save_generator")
dialog.popup_centered()
func do_save_generator(file_name : String):
var data = generator.serialize()
var file = File.new()
if file.open(file_name, File.WRITE) == OK:
file.store_string(to_json(data))
file.close()

View File

@ -3,8 +3,10 @@
[ext_resource path="res://addons/material_maker/nodes/generic.gd" type="Script" id=1]
[node name="Generic" type="GraphNode"]
margin_right = 82.0
margin_right = 95.0
margin_bottom = 29.0
title = "Generic"
show_close = true
script = ExtResource( 1 )
[connection signal="close_request" from="." to="." method="on_close_request"]
[connection signal="offset_changed" from="." to="." method="on_offset_changed"]

View File

@ -47,8 +47,7 @@ func close_material_maker():
material_maker = null
func generate_material(ptex_filename: String) -> Material:
var loader = MMGenLoader.new()
var generator = loader.load_gen(ptex_filename)
var generator = MMGenLoader.load_gen(ptex_filename)
add_child(generator)
var material = generator.get_node("Material")
var return_value = material.generate_material(renderer)

View File

@ -36,7 +36,7 @@ func sort():
func get_color(x):
sort()
if points.size() >0:
if points.size() > 0:
if x < points[0].v:
return points[0].c
var s = points.size()-1
@ -96,3 +96,7 @@ func deserialize(v):
for i in v.points:
if !i.has("a"): i.a = 1.0
add_point(i.pos, Color(i.r, i.g, i.b, i.a))
elif typeof(v) == TYPE_OBJECT and v.get_script() == get_script():
clear()
for p in v.points:
add_point(p.v, p.c)

View File

@ -5,8 +5,8 @@ const Gradient = preload("res://addons/material_maker/types/gradient.gd")
static func serialize_value(value):
if typeof(value) == TYPE_COLOR:
return { type= "Color", r=value.r, g=value.g, b=value.b, a=value.a }
elif typeof(value) == TYPE_OBJECT && value.has_method("serialize"):
return { type="Color", r=value.r, g=value.g, b=value.b, a=value.a }
elif typeof(value) == TYPE_OBJECT and value.has_method("serialize"):
return value.serialize()
return value