Updated loader and random seed handling (#15)

Loader is not a lot more generic and deserialization code moved to generators.

There is now a small dice button on nodes that create random patterns that can be used to freeze the seed. Frozen nodes can thus be moved without affecting the seed.
Graph nodes can also transmit their seed to their children (this behavior can be enabled/disabled using the dice button at the top right of the graph pane).
This commit is contained in:
RodZill4 2019-11-04 07:58:17 +01:00
parent c466d20229
commit c121f7c00a
11 changed files with 178 additions and 76 deletions

View File

@ -34,6 +34,9 @@ var position : Vector2 = Vector2(0, 0)
var model = null
var parameters = {}
var seed_locked : bool = false
var seed_value : int = 0
func _ready() -> void:
init_parameters()
@ -49,9 +52,28 @@ func toggle_editable() -> bool:
func is_editable() -> bool:
return false
func has_randomness():
return false
func get_seed() -> int:
if !seed_locked:
var s : int = ((int(position.x) * 0x1f1f1f1f) ^ int(position.y)) % 65536
if get_parent().get("transmits_seed") != null and get_parent().transmits_seed:
s += get_parent().get_seed()
return s
else:
return seed_value
func toggle_lock_seed():
if !seed_locked:
seed_value = get_seed()
seed_locked = !seed_locked
return seed_locked
func is_seed_locked():
return seed_locked
func init_parameters() -> void:
for p in get_parameter_defs():
if !parameters.has(p.name):
@ -64,6 +86,9 @@ func init_parameters() -> void:
func set_position(p) -> void:
position = p
if has_randomness() and !is_seed_locked():
for i in range(get_output_defs().size()):
notify_output_change(i)
func get_type() -> String:
return "generic"
@ -163,6 +188,7 @@ func get_shader_code(uv : String, output_index : int, context : MMGenContext) ->
func _get_shader_code(__, __, __) -> Dictionary:
return {}
func _serialize(data: Dictionary) -> Dictionary:
print("cannot save "+name)
return data
@ -171,8 +197,35 @@ func serialize():
var rv = { name=name, type=get_type(), parameters={}, node_position={ x=position.x, y=position.y } }
for p in parameters.keys():
rv.parameters[p] = MMType.serialize_value(parameters[p])
if seed_locked:
rv.seed_value = seed_value
if model != null:
rv.type = model
else:
rv = _serialize(rv)
return rv
func _deserialize(data : Dictionary) -> void:
pass
func deserialize(data : Dictionary) -> void:
_deserialize(data)
if data.has("name"):
name = data.name
if data.has("node_position"):
position.x = data.node_position.x
position.y = data.node_position.y
if data.has("parameters"):
for p in data.parameters.keys():
set_parameter(p, MMType.deserialize_value(data.parameters[p]))
else:
for p in get_parameter_defs():
if data.has(p.name) and p.name != "type":
set_parameter(p.name, MMType.deserialize_value(data[p.name]))
if data.has("seed_value"):
seed_locked = true
seed_value = data.seed_value
else:
seed_locked = false
_post_load()

View File

@ -33,3 +33,9 @@ func _serialize(data: Dictionary) -> Dictionary:
data.text = text
data.size = { x=size.x, y=size.y }
return data
func _deserialize(data : Dictionary) -> void:
if data.has("text"):
text = data.text
if data.has("size"):
size = Vector2(data.size.x, data.size.y)

View File

@ -175,6 +175,10 @@ func _get_shader_code(uv : String, output_index : int, context : MMGenContext) -
rv[convolution_params.output_type] = "%s_%d" % [ genname, variant_index ]
return rv
func _serialize(data: Dictionary) -> Dictionary:
data.convolution_params = convolution_params
return data
func _deserialize(data : Dictionary) -> void:
set_convolution_params(data.convolution_params)

View File

@ -5,10 +5,13 @@ class_name MMGenGraph
var label : String = "Graph"
var connections = []
var editable = false
var editable : bool = false
var transmits_seed : bool = true
signal connections_changed(removed_connections, added_connections)
func fix_remotes() -> void:
for c in get_children():
if c is MMGenRemote:
@ -17,6 +20,11 @@ func fix_remotes() -> void:
func _post_load() -> void:
fix_remotes()
func has_randomness():
return transmits_seed
func get_type() -> String:
return "graph"
@ -203,13 +211,6 @@ func _get_shader_code(uv : String, output_index : int, context : MMGenContext) -
return rv
return { globals=[], defs="", code="", textures={} }
func _serialize(data: Dictionary) -> Dictionary:
data.label = label
data.nodes = []
for c in get_children():
data.nodes.append(c.serialize())
data.connections = connections
return data
func edit(node) -> void:
node.get_parent().call_deferred("update_view", self)
@ -300,3 +301,19 @@ func create_subgraph(gens : Array) -> void:
new_graph.add_child(gen_parameters)
fix_remotes()
new_graph.fix_remotes()
func _serialize(data: Dictionary) -> Dictionary:
data.label = label
data.nodes = []
for c in get_children():
data.nodes.append(c.serialize())
data.connections = connections
return data
func _deserialize(data : Dictionary) -> void:
if data.has("label"):
label = data.label
var nodes = data.nodes if data.has("nodes") else []
var connections = data.connections if data.has("connections") else []
load("res://addons/material_maker/engine/loader.gd").add_to_gen_graph(self, nodes, connections)

View File

@ -97,3 +97,6 @@ func _serialize(data: Dictionary) -> Dictionary:
data.type = "ios"
data.ports = ports
return data
func _deserialize(data : Dictionary) -> void:
ports = data.ports

View File

@ -134,11 +134,6 @@ func set_parameter(p : String, v) -> void:
if name == "gen_parameters":
get_parent().parameters[p] = v
func _serialize(data: Dictionary) -> Dictionary:
data.type = "remote"
data.widgets = widgets
return data
func create_linked_control(label : String) -> String:
var n = get_next_widget_name()
widgets.push_back({ name=n, label=label, type="linked_control", linked_widgets=[] })
@ -219,3 +214,12 @@ func remove_configuration(widget_name : String, config_name : String) -> void:
if widget.type == "config_control":
widget.configurations.erase(config_name)
emit_signal("parameter_changed", "__update_all__", null)
func _serialize(data: Dictionary) -> Dictionary:
data.type = "remote"
data.widgets = widgets
return data
func _deserialize(data : Dictionary) -> void:
set_widgets(data.widgets.duplicate(true))

View File

@ -16,6 +16,9 @@ func toggle_editable() -> bool:
func is_editable() -> bool:
return editable
func has_randomness():
return uses_seed
func get_type() -> String:
return "shader"
@ -45,24 +48,30 @@ func get_output_defs():
func set_shader_model(data: Dictionary):
shader_model = data
init_parameters()
uses_seed = false
if shader_model.has("outputs"):
for i in range(shader_model.outputs.size()):
var output = shader_model.outputs[i]
var output_code = ""
if output.has("rgba"):
shader_model.outputs[i].type = "rgba"
output_code = output.rgba
elif output.has("rgb"):
shader_model.outputs[i].type = "rgb"
else:
output_code = output.rgb
elif output.has("f"):
shader_model.outputs[i].type = "f"
func set_position(p) -> void:
.set_position(p)
if uses_seed:
source_changed(0)
func get_seed() -> int:
var s = ((int(position.x) * 0x1f1f1f1f) ^ int(position.y)) % 65536
return s
output_code = output.f
else:
print("Unsupported output type")
if output_code.find("$seed") != -1 or output_code.find("$(seed)") != -1:
uses_seed = true
if shader_model.has("code"):
if shader_model.code.find("$seed") != -1 or shader_model.code.find("$(seed)") != -1:
uses_seed = true
if shader_model.has("instance"):
if shader_model.instance.find("$seed") != -1 or shader_model.instance.find("$(seed)") != -1:
uses_seed = true
func find_keyword_call(string, keyword):
var search_string = "$%s(" % keyword
@ -156,7 +165,6 @@ func subst(string, context, uv = "") -> Dictionary:
var tmp_string = replace_variable(string, "seed", str(get_seed()))
if tmp_string != string:
string = tmp_string
uses_seed = true
if uv != "":
string = replace_variable(string, "uv", "("+uv+")")
if shader_model.has("parameters") and typeof(shader_model.parameters) == TYPE_ARRAY:
@ -213,7 +221,6 @@ func subst(string, context, uv = "") -> Dictionary:
return { string=string, globals=required_globals, defs=required_defs, code=required_code, textures=required_textures }
func _get_shader_code(uv : String, output_index : int, context : MMGenContext) -> Dictionary:
uses_seed = false
var genname = "o"+str(get_instance_id())
var output_info = [ { field="rgba", type="vec4" }, { field="rgb", type="vec3" }, { field="f", type="float" } ]
var rv = { globals=[], defs="", code="", textures={} }
@ -278,10 +285,18 @@ func _get_shader_code(uv : String, output_index : int, context : MMGenContext) -
rv.globals.push_back(shader_model.global)
return rv
func _serialize(data: Dictionary) -> Dictionary:
data.shader_model = shader_model
return data
func _deserialize(data : Dictionary) -> void:
if data.has("shader_model"):
set_shader_model(data.shader_model)
elif data.has("model_data"):
set_shader_model(data.model_data)
func edit(node) -> void:
if shader_model != null:
var edit_window = load("res://addons/material_maker/widgets/node_editor/node_editor.tscn").instance()

View File

@ -40,6 +40,7 @@ static func create_gen(data) -> MMGenBase:
{ keyword="connections", type=MMGenGraph },
{ keyword="nodes", type=MMGenGraph },
{ keyword="shader_model", type=MMGenShader },
{ keyword="convolution_params", type=MMGenConvolution },
{ keyword="model_data", type=MMGenShader },
{ keyword="convolution_params", type=MMGenConvolution },
{ keyword="widgets", type=MMGenRemote }
@ -48,40 +49,20 @@ static func create_gen(data) -> MMGenBase:
material = MMGenMaterial,
buffer = MMGenBuffer,
image = MMGenImage,
ios = MMGenIOs,
switch = MMGenSwitch,
export = MMGenExport,
comment = MMGenComment,
debug = MMGenDebug
}
var generator = null
if data.has("connections") and data.has("nodes"):
generator = MMGenGraph.new()
if data.has("label"):
generator.label = data.label
add_to_gen_graph(generator, data.nodes, data.connections)
elif data.has("shader_model"):
generator = MMGenShader.new()
generator.set_shader_model(data.shader_model)
elif data.has("convolution_params"):
generator = MMGenConvolution.new()
generator.set_convolution_params(data.convolution_params)
elif data.has("model_data"):
generator = MMGenShader.new()
generator.set_shader_model(data.model_data)
elif data.has("widgets"):
generator = MMGenRemote.new()
generator.set_widgets(data.widgets.duplicate(true))
elif data.has("type"):
for g in guess:
if data.has(g.keyword):
generator = g.type.new()
break
if generator == null and data.has("type"):
if types.has(data.type):
generator = types[data.type].new()
elif data.type == "comment":
generator = MMGenComment.new()
if data.has("text"):
generator.text = data.text
if data.has("size"):
generator.size = Vector2(data.size.x, data.size.y)
elif data.type == "ios":
generator = MMGenIOs.new()
generator.ports = data.ports
else:
var file = File.new()
var gen_paths = [ STD_GENDEF_PATH, OS.get_executable_path().get_base_dir()+"/generators" ]
@ -101,22 +82,10 @@ static func create_gen(data) -> MMGenBase:
print("Cannot find description for "+data.type)
if generator != null:
generator.name = data.type
else:
if generator == null:
print("LOADER: data not supported:"+str(data))
if generator != null:
if data.has("name"):
generator.name = data.name
if data.has("node_position"):
generator.position.x = data.node_position.x
generator.position.y = data.node_position.y
if data.has("parameters"):
for p in data.parameters.keys():
generator.set_parameter(p, MMType.deserialize_value(data.parameters[p]))
else:
for p in generator.get_parameter_defs():
if data.has(p.name) and p.name != "type":
generator.set_parameter(p.name, MMType.deserialize_value(data[p.name]))
generator._post_load()
generator.deserialize(data)
return generator
static func get_generator_list() -> Array:

View File

@ -15,6 +15,7 @@ var generator = null
onready var timer : Timer = $Timer
onready var subgraph_ui : HBoxContainer = $GraphUI/SubGraphUI
onready var button_transmits_seed : Button = $GraphUI/SubGraphUI/ButtonTransmitsSeed
signal save_path_changed
signal graph_changed
@ -127,6 +128,12 @@ func update_view(g) -> void:
subgraph_ui.visible = generator != top_generator
subgraph_ui.get_node("Label").text = generator.label
center_view()
if generator.get_parent() is MMGenGraph:
button_transmits_seed.visible = true
button_transmits_seed.pressed = generator.transmits_seed
else:
button_transmits_seed.visible = false
func clear_material() -> void:
if top_generator != null:
@ -325,3 +332,8 @@ func edit_subgraph(g : MMGenGraph) -> void:
if !g.is_editable():
g.toggle_editable()
update_view(g)
func _on_ButtonTransmitsSeed_toggled(button_pressed):
if button_pressed != generator.transmits_seed:
generator.transmits_seed = button_pressed

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=6 format=2]
[gd_scene load_steps=7 format=2]
[ext_resource path="res://addons/material_maker/graph_edit.gd" type="Script" id=1]
[ext_resource path="res://addons/material_maker/icons/icons.svg" type="Texture" id=2]
@ -9,11 +9,16 @@ bg_color = Color( 0.6, 0.6, 0.6, 0 )
[sub_resource type="AtlasTexture" id=2]
flags = 4
atlas = ExtResource( 2 )
region = Rect2( 15.4016, 47.1451, 16.7512, 17.8319 )
region = Rect2( 64, 32, 16, 16 )
[sub_resource type="AtlasTexture" id=3]
flags = 4
atlas = ExtResource( 2 )
region = Rect2( 15.4016, 47.1451, 16.7512, 17.8319 )
[sub_resource type="AtlasTexture" id=4]
flags = 4
atlas = ExtResource( 2 )
region = Rect2( 0, 48, 16, 16 )
[node name="GraphEdit" type="GraphEdit"]
@ -37,7 +42,7 @@ margin_bottom = 24.0
alignment = 2
[node name="SubGraphUI" type="HBoxContainer" parent="GraphUI"]
margin_left = 362.0
margin_left = 330.0
margin_right = 544.0
margin_bottom = 24.0
size_flags_horizontal = 9
@ -48,23 +53,33 @@ margin_bottom = 24.0
rect_min_size = Vector2( 150, 0 )
size_flags_horizontal = 9
[node name="ButtonUp" type="Button" parent="GraphUI/SubGraphUI"]
[node name="ButtonTransmitsSeed" type="Button" parent="GraphUI/SubGraphUI"]
margin_left = 154.0
margin_right = 182.0
margin_bottom = 24.0
hint_tooltip = "Affect children seeds"
size_flags_horizontal = 9
toggle_mode = true
icon = SubResource( 2 )
[node name="ButtonUp" type="Button" parent="GraphUI/SubGraphUI"]
margin_left = 186.0
margin_right = 214.0
margin_bottom = 24.0
hint_tooltip = "Back to parent"
size_flags_horizontal = 9
icon = SubResource( 2 )
icon = SubResource( 3 )
[node name="ButtonShowTree" type="Button" parent="GraphUI"]
margin_left = 548.0
margin_right = 576.0
margin_bottom = 24.0
hint_tooltip = "Show hierarchy"
icon = SubResource( 3 )
icon = SubResource( 4 )
[connection signal="connection_request" from="." to="." method="connect_node"]
[connection signal="disconnection_request" from="." to="." method="disconnect_node"]
[connection signal="timeout" from="Timer" to="." method="do_send_changed_signal"]
[connection signal="text_changed" from="GraphUI/SubGraphUI/Label" to="." method="_on_Label_text_changed"]
[connection signal="toggled" from="GraphUI/SubGraphUI/ButtonTransmitsSeed" to="." method="_on_ButtonTransmitsSeed_toggled"]
[connection signal="pressed" from="GraphUI/SubGraphUI/ButtonUp" to="." method="on_ButtonUp_pressed"]
[connection signal="pressed" from="GraphUI/ButtonShowTree" to="." method="_on_ButtonShowTree_pressed"]

View File

@ -3,15 +3,19 @@ extends GraphNode
class_name MMGraphNodeBase
var generator : MMGenBase = null setget set_generator
var fixed_seed = false
func _ready() -> void:
connect("offset_changed", self, "_on_offset_changed")
func _draw():
if generator.has_randomness():
var icon = preload("res://addons/material_maker/icons/randomness_unlocked.tres") if fixed_seed else preload("res://addons/material_maker/icons/randomness_locked.tres")
if generator != null and generator.has_randomness():
var icon = preload("res://addons/material_maker/icons/randomness_locked.tres") if generator.is_seed_locked() else preload("res://addons/material_maker/icons/randomness_unlocked.tres")
draw_texture_rect(icon, Rect2(rect_size.x-48, 4, 16, 16), false)
if !is_connected("gui_input", self, "_on_gui_input"):
connect("gui_input", self, "_on_gui_input")
else:
if is_connected("gui_input", self, "_on_gui_input"):
disconnect("gui_input", self, "_on_gui_input")
func set_generator(g) -> void:
generator = g
@ -21,5 +25,5 @@ func _on_offset_changed() -> void:
func _on_gui_input(event):
if event is InputEventMouseButton and event.pressed and event.button_index == BUTTON_LEFT and Rect2(rect_size.x-48, 4, 16, 16).has_point(event.position):
fixed_seed = !fixed_seed
generator.toggle_lock_seed()
update()