Started adding the "remote node"

Started adding the "remote" node, that can control important parameters of the graph and save/restore configurations of groups of parameters. This should help creating easily configurable materials.

Added new "mosaic" example
This commit is contained in:
RodZill4 2018-09-08 12:25:28 +02:00
parent 2621ff4b46
commit 95a737029a
23 changed files with 805 additions and 3 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"connections":[{"from":"blend_0","from_port":0,"to":"Material","to_port":0},{"from":"voronoi_0","from_port":2,"to":"blend_0","to_port":1},{"from":"voronoi_0","from_port":1,"to":"colorize_0","to_port":0},{"from":"colorize_0","from_port":0,"to":"blend_0","to_port":0},{"from":"voronoi_0","from_port":1,"to":"colorize_1","to_port":0},{"from":"colorize_1","from_port":0,"to":"normal_map_0","to_port":0},{"from":"normal_map_0","from_port":0,"to":"Material","to_port":4},{"from":"uniform_0","from_port":0,"to":"Material","to_port":1},{"from":"voronoi_0","from_port":1,"to":"colorize_2","to_port":0},{"from":"colorize_2","from_port":0,"to":"Material","to_port":2}],"nodes":[{"intensity":1,"name":"voronoi_0","node_position":{"x":-543.5,"y":-11.5},"scale_x":32,"scale_y":32,"type":"voronoi"},{"amount":0.5,"name":"normal_map_0","node_position":{"x":-165.5,"y":133.5},"size":5,"type":"normal_map"},{"albedo_color":{"a":1,"b":1,"g":1,"r":1,"type":"Color"},"ao_light_affect":1,"depth_scale":1,"emission_energy":1,"metallic":1,"name":"Material","node_position":{"x":30,"y":-44},"normal_scale":1,"resolution":1,"roughness":1,"type":"material"},{"gradient":[{"b":1,"g":1,"pos":0.045455,"r":1},{"b":0,"g":0,"pos":0.109091,"r":0}],"name":"colorize_1","node_position":{"x":-349.5,"y":138.5},"type":"colorize"},{"gradient":[{"b":1,"g":1,"pos":0.063636,"r":1},{"b":0,"g":0,"pos":0.072727,"r":0}],"name":"colorize_0","node_position":{"x":-317.5,"y":-169.5},"type":"colorize"},{"color":{"a":1,"b":0.109375,"g":0.109375,"r":0.109375,"type":"Color"},"name":"uniform_0","node_position":{"x":-127.5,"y":-3.5},"type":"uniform"},{"gradient":[{"b":1,"g":1,"pos":0,"r":1},{"b":0.109375,"g":0.109375,"pos":0.236364,"r":0.109375}],"name":"colorize_2","node_position":{"x":-306.5,"y":39.5},"type":"colorize"},{"amount":1,"blend_type":3,"name":"blend_0","node_position":{"x":-294.5,"y":-94.5},"type":"blend"}]}

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 B

View File

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="icons.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="127.97633"
inkscape:cy="1033.5955"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid815" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
id="rect4533"
width="238.125"
height="174.625"
x="-10.583333"
y="-3.3020833"
ry="0.82414269" />
<g
id="g4582"
transform="matrix(1.2003144,0.26398218,-0.26389538,1.2007092,-4.1100858,-17.15364)"
inkscape:export-filename="link.png"
inkscape:export-xdpi="15.926786"
inkscape:export-ydpi="15.926786">
<path
id="path4540"
d="m 52.110175,11.249997 c -2.182497,-2.09336 -5.682294,-2.0795701 -7.847077,0.03092 l -5.999358,5.848887 c -1.693669,1.651191 -2.053303,4.081962 -1.084529,6.064327 l 2.668997,-2.602056 c 0.06832,-0.475315 0.288771,-0.934614 0.666689,-1.303055 l 5.999358,-5.848887 c 0.927764,-0.904495 2.427677,-0.910404 3.363033,-0.01325 0.935356,0.897155 0.941467,2.347581 0.0137,3.252076 l -5.999358,5.848886 c -0.305037,0.297386 -0.672368,0.495758 -1.060703,0.598709 l -2.79438,2.724294 c 2.027594,0.834538 4.455047,0.445994 6.106241,-1.163787 l 5.999357,-5.848886 C 54.306929,16.727686 54.292669,13.34336 52.110175,11.25 Z"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.5892244;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
inkscape:connector-curvature="0" />
<path
id="path4542"
d="m 47.703334,18.314275 c -2.043073,-0.877257 -4.511855,-0.499459 -6.184325,1.131064 l -5.999358,5.848887 c -2.164785,2.11049 -2.150524,5.494815 0.03197,7.588174 2.182497,2.09336 5.682293,2.079573 7.847078,-0.03092 l 5.999358,-5.848887 c 1.67245,-1.630503 2.042985,-4.020862 1.11922,-5.989301 l -2.724165,2.655841 c -0.08761,0.429031 -0.302522,0.839174 -0.646213,1.174244 l -5.999357,5.848887 c -0.927764,0.904494 -2.427678,0.910405 -3.363034,0.01325 -0.935356,-0.897154 -0.941466,-2.34758 -0.0137,-3.252075 l 5.999358,-5.848887 c 0.343694,-0.335073 0.766059,-0.54623 1.209002,-0.634439 z"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.5892244;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
inkscape:connector-curvature="0" />
</g>
<g
inkscape:export-ydpi="16.531826"
inkscape:export-xdpi="16.531826"
inkscape:export-filename="remove.png"
id="g4564"
transform="matrix(0.82426401,0.81188676,-0.82426401,0.81188676,84.358228,-6.2929802)"
style="fill:#ff0909;fill-opacity:1">
<rect
style="opacity:1;fill:#ff0909;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
id="rect4560"
width="5.2916665"
height="26.458334"
x="10.583333"
y="8.604167"
ry="0" />
<rect
style="opacity:1;fill:#ff0909;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
id="rect4562"
width="26.458332"
height="5.2916665"
x="0"
y="19.1875"
ry="0" />
</g>
<g
id="g4645"
inkscape:export-filename="config.png"
inkscape:export-xdpi="16.559999"
inkscape:export-ydpi="16.559999"
transform="matrix(1.0254261,0,0,1.0022479,-9.1237225,-33.22013)">
<path
inkscape:connector-curvature="0"
id="path4598"
d="m 23.783078,44.110649 -4.508983,4.648736 -3.980261,-5.27284 a 12.770835,12.770835 0 0 0 -2.631282,1.045173 l 0.825522,6.441784 -6.571368,-0.17469 a 12.770835,12.770835 0 0 0 -0.7896172,2.557631 12.770835,12.770835 0 0 0 -0.027504,0.173796 l 5.5120272,3.347264 -4.1861363,4.972095 a 12.770835,12.770835 0 0 0 1.6563998,2.37002 l 5.9760855,-2.194035 1.267643,6.324612 a 12.770835,12.770835 0 0 0 2.907828,0.192046 l 1.997862,-6.001057 5.727797,2.948399 a 12.770835,12.770835 0 0 0 1.98019,-2.135962 l -3.455675,-5.317616 5.926699,-2.662342 a 12.770835,12.770835 0 0 0 -0.414249,-2.850168 l -6.383635,-0.620945 1.631047,-6.361785 a 12.770835,12.770835 0 0 0 -2.46039,-1.430116 z m -4.472442,8.529047 a 3.1927084,3.1927084 0 0 1 2.523332,3.744269 3.1927084,3.1927084 0 0 1 -3.744267,2.523336 3.1927084,3.1927084 0 0 1 -2.523335,-3.744269 3.1927084,3.1927084 0 0 1 3.74427,-2.523336 z"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.80446196;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

View File

@ -197,5 +197,9 @@
{
"tree_item":"Miscellaneous/Export",
"type":"export"
},
{
"tree_item":"Miscellaneous/Remote",
"type":"remote"
}
]}

View File

@ -6,7 +6,7 @@
[ext_resource path="res://addons/material_maker/widgets/tabs.gd" type="Script" id=4]
[ext_resource path="res://addons/material_maker/renderer.tscn" type="PackedScene" id=5]
[node name="MainWindow" type="Panel" index="0"]
[node name="MainWindow" type="Panel"]
anchor_left = 0.0
anchor_top = 0.0

View File

@ -0,0 +1,63 @@
tool
extends "res://addons/material_maker/node_base.gd"
const LinkedControl = preload("res://addons/material_maker/widgets/linked_widgets/linked_control.tscn")
const ConfigControl = preload("res://addons/material_maker/widgets/linked_widgets/config_control.tscn")
func _ready():
pass
func _get_shader_code(uv):
var rv = { defs="", code="" }
rv.rgb = "vec3(1.0)"
return rv
func add_control(widget):
var controls = widget.get_associated_controls()
$Controls.add_child(controls.label)
$Controls.add_child(widget)
$Controls.add_child(controls.buttons)
func _on_AddLink_pressed():
var widget = LinkedControl.instance()
add_control(widget)
widget.pick_linked()
func _on_AddConfig_pressed():
var widget = ConfigControl.instance()
add_control(widget)
widget.pick_linked()
func _on_Remote_resize_request(new_minsize):
print("_on_Remote_resize_request")
rect_size = new_minsize
func _on_HBoxContainer_minimum_size_changed():
print("_on_HBoxContainer_minimum_size_changed "+str($HBoxContainer.rect_min_size))
func serialize():
var widgets = []
for i in range(1, $Controls.get_child_count(), 3):
widgets.append($Controls.get_child(i).serialize())
var data = { type="remote", node_position={x=offset.x,y=offset.y}, editable=true, widgets=widgets }
return data
func deserialize(data):
if data.has("node_position"):
offset.x = data.node_position.x
offset.y = data.node_position.y
call_deferred("do_deserialize", data)
func do_deserialize(data):
if data.has("widgets"):
for w in data.widgets:
var widget
if w.type == "linked_control":
widget = LinkedControl.instance()
elif w.type == "config_control":
widget = ConfigControl.instance()
else:
continue
add_control(widget)
widget.deserialize(w)

View File

@ -0,0 +1,139 @@
[gd_scene load_steps=5 format=2]
[ext_resource path="res://addons/material_maker/nodes/remote.gd" type="Script" id=1]
[ext_resource path="res://addons/material_maker/icons/link.png" type="Texture" id=2]
[ext_resource path="res://addons/material_maker/icons/config.png" type="Texture" id=3]
[sub_resource type="Theme" id=1]
[node name="Remote" type="GraphNode"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_right = 95.0
margin_bottom = 55.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 1
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
theme = SubResource( 1 )
title = "Remote"
offset = Vector2( 0, 0 )
show_close = true
resizable = false
selected = false
comment = false
overlay = 0
slot/0/left_enabled = false
slot/0/left_type = 0
slot/0/left_color = Color( 0.5, 0.5, 1, 1 )
slot/0/right_enabled = false
slot/0/right_type = 0
slot/0/right_color = Color( 0.5, 0.5, 1, 1 )
slot/1/left_enabled = false
slot/1/left_type = 0
slot/1/left_color = Color( 1, 1, 1, 1 )
slot/1/right_enabled = false
slot/1/right_type = 0
slot/1/right_color = Color( 1, 1, 1, 1 )
script = ExtResource( 1 )
_sections_unfolded = [ "Theme", "slot" ]
[node name="Controls" type="GridContainer" parent="." index="0"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = 16.0
margin_top = 24.0
margin_right = 79.0
margin_bottom = 24.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 1
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
columns = 3
_sections_unfolded = [ "Hint" ]
[node name="HBoxContainer" type="HBoxContainer" parent="." index="1"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = 16.0
margin_top = 24.0
margin_right = 79.0
margin_bottom = 46.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 1
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
alignment = 0
[node name="AddLink" type="Button" parent="HBoxContainer" index="0"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_right = 28.0
margin_bottom = 22.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
hint_tooltip = "Add linked control"
focus_mode = 2
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 0
size_flags_vertical = 1
toggle_mode = false
enabled_focus_mode = 2
shortcut = null
group = null
icon = ExtResource( 2 )
flat = false
align = 1
_sections_unfolded = [ "Hint", "Rect", "Size Flags" ]
[node name="AddConfig" type="Button" parent="HBoxContainer" index="1"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = 32.0
margin_right = 60.0
margin_bottom = 22.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
hint_tooltip = "Add configurations"
focus_mode = 2
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
toggle_mode = false
enabled_focus_mode = 2
shortcut = null
group = null
icon = ExtResource( 3 )
flat = false
align = 1
_sections_unfolded = [ "Hint" ]
[connection signal="pressed" from="HBoxContainer/AddLink" to="." method="_on_AddLink_pressed"]
[connection signal="pressed" from="HBoxContainer/AddConfig" to="." method="_on_AddConfig_pressed"]

View File

@ -6,6 +6,15 @@ signal ok
func _ready():
pass
func set_texts(title, label):
window_title = title
$VBoxContainer/Label.text = label
$VBoxContainer/LineEdit.grab_focus()
$VBoxContainer/LineEdit.grab_click_focus()
func _on_OK_pressed():
emit_signal("ok", $VBoxContainer/LineEdit.text)
_on_LineEdit_text_entered($VBoxContainer/LineEdit.text)
func _on_LineEdit_text_entered(new_text):
emit_signal("ok", new_text)
queue_free()

View File

@ -146,6 +146,8 @@ flat = false
align = 1
_sections_unfolded = [ "Rect" ]
[connection signal="text_entered" from="VBoxContainer/LineEdit" to="." method="_on_LineEdit_text_entered"]
[connection signal="pressed" from="VBoxContainer/HBoxContainer/OK" to="." method="_on_OK_pressed"]
[connection signal="pressed" from="VBoxContainer/HBoxContainer/Cancel" to="." method="queue_free"]

View File

@ -0,0 +1,121 @@
tool
extends "res://addons/material_maker/widgets/linked_widgets/linked_control_base.gd"
var configurations = {}
var current = null
onready var button = null
func _ready():
update_options()
func update_options():
# Seems "clear" might cause crashes, so we create a new button...
if button != null:
button.hide()
button.queue_free()
button = OptionButton.new()
button.connect("item_selected", self, "_on_item_selected")
button.connect("mouse_entered", self, "_on_mouse_entered")
button.connect("mouse_exited", self, "_on_mouse_exited")
add_child(button)
# Create list of configurations
var keys = configurations.keys()
keys.sort()
# if no configuration is selected, select the first one
if current == null and !keys.empty():
current = keys[0]
for c in keys:
button.add_item(c)
button.add_separator()
button.add_item("<add configuration>")
if current != null:
button.add_separator()
button.add_item("<update "+current+">")
button.add_item("<remove "+current+">")
for i in range(button.get_item_count()):
if button.get_item_text(i) == current:
button.selected = i
break
func add_linked(node, widget):
linked_widgets.append({ node=node, widget=widget })
func apply_configuration(c):
for w in configurations[c]:
w.widget.set(WIDGETS[get_widget_type(w.widget)].value_attr , w.value)
w.node.set(w.widget.name, w.value)
var graph_node = get_parent()
while !(graph_node is GraphNode):
graph_node = graph_node.get_parent()
graph_node.update_shaders()
func do_update_configuration(name):
var configuration = []
for w in linked_widgets:
configuration.append({ node=w.node, widget=w.widget, value=w.node.get(w.widget.name) })
configurations[name] = configuration
current = name
update_options()
func update_configuration():
var dialog = preload("res://addons/material_maker/widgets/line_dialog.tscn").instance()
add_child(dialog)
dialog.set_texts("Configuration", "Enter a name for the new configuration")
dialog.connect("ok", self, "do_update_configuration", [])
dialog.popup_centered()
func _on_item_selected(ID):
var count = configurations.keys().size()
if ID >= 0 && ID < count:
current = button.get_item_text(ID)
update_options()
apply_configuration(current)
elif ID == count+1:
button.selected = 0
update_configuration()
elif ID == count+3:
do_update_configuration(current)
else:
configurations.erase(current)
current = null
update_options()
func serialize():
var data = .serialize()
data.type = "config_control"
var data_configurations = {}
var keys = configurations.keys()
for k in keys:
var c = configurations[k]
var data_configuration = []
for e in c:
data_configuration.append({ node=e.node.name, widget=e.widget.name, value=e.value })
data_configurations[k] = data_configuration
data.configurations = data_configurations
return data
func deserialize(data):
.deserialize(data)
graph_edit = get_parent()
while graph_edit != null && !(graph_edit is GraphEdit):
graph_edit = graph_edit.get_parent()
if graph_edit == null:
return
var keys = data.configurations.keys()
for k in keys:
var c = data.configurations[k]
var configuration = []
for e in c:
var node = graph_edit.get_node(e.node)
var widget = null
for w in node.property_widgets:
if w.name == e.widget:
widget = w
break
configuration.append({ node=node, widget=widget, value=e.value })
configurations[k] = configuration
update_options()

View File

@ -0,0 +1,22 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/material_maker/widgets/linked_widgets/config_control.gd" type="Script" id=1]
[node name="ConfigControl" type="VBoxContainer"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
rect_min_size = Vector2( 70, 0 )
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 1
mouse_default_cursor_shape = 0
size_flags_horizontal = 3
size_flags_vertical = 1
alignment = 0
script = ExtResource( 1 )
_sections_unfolded = [ "Rect", "Size Flags" ]

View File

@ -0,0 +1,22 @@
extends Node2D
var end
var source = null
var target = null
func closest(rect, point):
return Vector2(max(rect.position.x, min(rect.end.x, point.x)), max(rect.position.y, min(rect.end.y, point.y)))
func _draw():
var start = source.rect_global_position+0.5*source.rect_size*source.get_global_transform().get_scale()
var color = Color(1, 0.5, 0.5, 0.5)
var rect
if target != null:
color = Color(0.5, 1, 0.5, 0.5)
rect = Rect2(target.rect_global_position, target.rect_size*target.get_global_transform().get_scale())
draw_rect(rect, color, false)
end = closest(rect, start)
rect = Rect2(source.rect_global_position, source.rect_size*source.get_global_transform().get_scale())
draw_rect(rect, color, false)
start = closest(rect, end)
draw_line(start, end, color, 1, true)

View File

@ -0,0 +1,52 @@
tool
extends "res://addons/material_maker/widgets/linked_widgets/linked_control_base.gd"
func add_linked(node, widget):
if linked_widgets.empty():
var new_widget = null
var type
if widget is SpinBox:
new_widget = SpinBox.new()
type = "SpinBox"
elif widget is ColorPickerButton:
new_widget = ColorPickerButton.new()
type = "ColorPickerButton"
elif widget is HSlider:
new_widget = HSlider.new()
type = "HSlider"
elif widget is OptionButton:
new_widget = OptionButton.new()
type = "OptionButton"
for i in range(widget.get_item_count()):
new_widget.add_item(widget.get_item_text(i), widget.get_item_id(i))
if new_widget != null:
add_child(new_widget)
mirror(new_widget, widget, type)
new_widget.connect("mouse_entered", self, "_on_mouse_entered")
new_widget.connect("mouse_exited", self, "_on_mouse_exited")
new_widget.connect(WIDGETS[type].sig, self, WIDGETS[type].sig_handler)
linked_widgets.append({ node=node, widget=widget })
func mirror(to, from, type):
for a in WIDGETS[type].attrs:
to.set(a, from.get(a))
func _on_value_changed(v):
for l in linked_widgets:
l.widget.value = v
l.node.set(l.widget.name, v)
func _on_color_changed(c):
for l in linked_widgets:
l.widget.color = c
l.node.set(l.widget.name, c)
func _on_item_selected(i):
for l in linked_widgets:
l.widget.selected = i
l.node.set(l.widget.name, i)
func serialize():
var data = .serialize()
data.type = "linked_control"
return data

View File

@ -0,0 +1,22 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/material_maker/widgets/linked_widgets/linked_control.gd" type="Script" id=1]
[node name="LinkedControl" type="VBoxContainer"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
rect_min_size = Vector2( 70, 0 )
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 1
mouse_default_cursor_shape = 0
size_flags_horizontal = 3
size_flags_vertical = 1
alignment = 0
script = ExtResource( 1 )
_sections_unfolded = [ "Rect", "Size Flags" ]

View File

@ -0,0 +1,134 @@
tool
extends VBoxContainer
var label = null
var buttons = null
var linked_widgets = []
var links = null
const Link = preload("res://addons/material_maker/widgets/linked_widgets/link.gd")
const WIDGETS = {
SpinBox={ attrs=[ "min_value", "max_value", "step", "value" ], value_attr="value", sig="value_changed", sig_handler="_on_value_changed" },
HSlider={ attrs=[ "min_value", "max_value", "step", "value" ], value_attr="value", sig="value_changed", sig_handler="_on_value_changed" },
ColorPickerButton={ attrs=[ "edit_alpha", "color" ], value_attr="color", sig="color_changed", sig_handler="_on_color_changed" },
OptionButton={ attrs=[ "selected" ], value_attr="selected", sig="item_selected", sig_handler="_on_item_selected" }
}
func get_widget_type(widget):
if widget is SpinBox:
return "SpinBox"
elif widget is ColorPickerButton:
return "ColorPickerButton"
elif widget is OptionButton:
return "OptionButton"
else:
return null
func _ready():
set_process_input(false)
func get_associated_controls():
label = Label.new()
label.set_text("Unnamed")
buttons = preload("res://addons/material_maker/widgets/linked_widgets/linked_control_buttons.tscn").instance()
buttons.control = self
return { label=label, buttons=buttons }
func delete():
var parent = get_parent()
label.queue_free()
buttons.queue_free()
queue_free()
func _on_mouse_entered():
_on_mouse_exited()
links = []
var viewport = get_viewport()
for w in linked_widgets:
var link = Link.new()
link.source = self
link.target = w.widget
viewport.add_child(link)
links.append(link)
func _on_mouse_exited():
if links != null:
for l in links:
l.queue_free()
links = null
# Capture mouse to link to a control
var link = null
var graph_edit = null
var pointed_control = null
const Link = preload("res://addons/material_maker/widgets/linked_widgets/link.gd")
func find_control(gp):
for c in graph_edit.get_children():
if c is GraphNode:
if c.get("property_widgets") != null:
for w in c.property_widgets:
if Rect2(w.rect_global_position, w.rect_size*w.get_global_transform().get_scale()).has_point(gp):
return { node=c, widget=w }
return null
func _input(event):
if event is InputEventKey:
if event.scancode == KEY_ESCAPE:
set_process_input(false)
link.queue_free()
link = null
elif event is InputEventMouseMotion:
var control = find_control(event.global_position)
link.end = event.global_position
link.target = control.widget if control != null else null
link.update()
elif event is InputEventMouseButton:
if event.pressed:
if event.button_index == BUTTON_LEFT:
var control = find_control(event.global_position)
if control != null:
add_linked(control.node, control.widget)
set_process_input(false)
link.queue_free()
link = null
get_tree().set_input_as_handled()
func pick_linked():
# Verify we are in a graph edit
graph_edit = get_parent()
while graph_edit != null && !(graph_edit is GraphEdit):
graph_edit = graph_edit.get_parent()
if graph_edit == null:
return
# Create line that will be shown when looking for a target
var viewport = get_viewport()
link = Link.new()
link.source = self
link.end = rect_global_position+0.5*rect_size*get_global_transform().get_scale()
viewport.add_child(link)
set_process_input(true)
pointed_control = null
func serialize():
var data = { linked_widgets=[] }
for w in linked_widgets:
data.linked_widgets.append( { node=w.node.name, widget=w.widget.name } )
return data
func deserialize(data):
if !data.has("linked_widgets"):
return
var graph_edit = get_parent()
while !(graph_edit is GraphEdit):
graph_edit = graph_edit.get_parent()
for w in data.linked_widgets:
var node = graph_edit.get_node(w.node)
if node != null && node.get("property_widgets") != null:
for widget in node.property_widgets:
if widget.name == w.widget:
add_linked(node, widget)

View File

@ -0,0 +1,15 @@
tool
extends HBoxContainer
var control = null
func _ready():
# Called when the node is added to the scene for the first time.
# Initialization here
pass
func _on_Link_pressed():
control.pick_linked()
func _on_Remove_pressed():
control.delete()

View File

@ -0,0 +1,75 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://addons/material_maker/widgets/linked_widgets/linked_control_buttons.gd" type="Script" id=1]
[ext_resource path="res://addons/material_maker/icons/link.png" type="Texture" id=2]
[ext_resource path="res://addons/material_maker/icons/remove.png" type="Texture" id=3]
[node name="Buttons" type="HBoxContainer"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_right = 60.0
margin_bottom = 22.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 1
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
alignment = 0
script = ExtResource( 1 )
[node name="Link" type="Button" parent="." index="0"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_right = 28.0
margin_bottom = 22.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
focus_mode = 2
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
toggle_mode = false
enabled_focus_mode = 2
shortcut = null
group = null
icon = ExtResource( 2 )
flat = false
align = 1
[node name="Remove" type="Button" parent="." index="1"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = 32.0
margin_right = 60.0
margin_bottom = 22.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
focus_mode = 2
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
toggle_mode = false
enabled_focus_mode = 2
shortcut = null
group = null
icon = ExtResource( 3 )
flat = false
align = 1
[connection signal="pressed" from="Link" to="." method="_on_Link_pressed"]
[connection signal="pressed" from="Remove" to="." method="_on_Remove_pressed"]