mirror of
https://github.com/Relintai/material-maker.git
synced 2024-11-13 06:27:18 +01:00
Updated switch node and refactored generator edit feature.
This commit is contained in:
parent
cdfc247fc1
commit
c8414f47df
@ -40,6 +40,12 @@ func _ready():
|
||||
func can_be_deleted() -> bool:
|
||||
return true
|
||||
|
||||
func toggle_editable():
|
||||
return false
|
||||
|
||||
func is_editable():
|
||||
return false
|
||||
|
||||
func init_parameters():
|
||||
for p in get_parameter_defs():
|
||||
if !parameters.has(p.name):
|
||||
|
@ -5,12 +5,26 @@ class_name MMGenGraph
|
||||
var label : String = "Graph"
|
||||
var connections = []
|
||||
|
||||
var editable = false
|
||||
|
||||
|
||||
func get_type():
|
||||
return "graph"
|
||||
|
||||
func get_type_name():
|
||||
return label
|
||||
|
||||
|
||||
func toggle_editable():
|
||||
editable = !editable
|
||||
if editable:
|
||||
model = null
|
||||
return true
|
||||
|
||||
func is_editable():
|
||||
return editable
|
||||
|
||||
|
||||
func get_parameter_defs():
|
||||
if has_node("gen_parameters"):
|
||||
return get_node("gen_parameters").get_parameter_defs()
|
||||
|
@ -5,6 +5,17 @@ class_name MMGenShader
|
||||
var shader_model : Dictionary = {}
|
||||
var uses_seed = false
|
||||
|
||||
var editable = false
|
||||
|
||||
func toggle_editable():
|
||||
editable = !editable
|
||||
if editable:
|
||||
model = null
|
||||
return true
|
||||
|
||||
func is_editable():
|
||||
return editable
|
||||
|
||||
func get_type():
|
||||
return "shader"
|
||||
|
||||
|
@ -6,32 +6,48 @@ class_name MMGenSwitch
|
||||
Texture generator switch
|
||||
"""
|
||||
|
||||
var editable = false
|
||||
|
||||
func get_type():
|
||||
return "switch"
|
||||
|
||||
func get_type_name():
|
||||
return "Switch"
|
||||
|
||||
func toggle_editable():
|
||||
editable = !editable
|
||||
return true
|
||||
|
||||
func is_editable():
|
||||
return editable
|
||||
|
||||
func get_parameter_defs():
|
||||
var choices = parameters.choices if parameters.has("choices") else 2
|
||||
return [
|
||||
{ name="outputs", label="Outputs", type="float", min=1, max=5, step=1, default=2 },
|
||||
{ name="choices", label="Choices", type="float", min=2, max=5, step=1, default=2 },
|
||||
{ name="source", label="Source", type="float", min=0, max=1, step=1, default=0 },
|
||||
{ name="source", label="Source", type="float", min=0, max=choices-1, step=1, default=0 },
|
||||
]
|
||||
|
||||
func get_input_defs():
|
||||
var rv : Array = []
|
||||
for c in range(parameters.choices):
|
||||
for o in range(parameters.outputs):
|
||||
rv.push_back({ name=PoolByteArray([64+o]).get_string_from_ascii()+str(c), type="rgba" })
|
||||
var n = PoolByteArray([65+o]).get_string_from_ascii()+str(c)
|
||||
rv.push_back({ name=n, label=n, type="rgba" })
|
||||
return rv
|
||||
|
||||
func get_output_defs():
|
||||
var rv : Array = []
|
||||
for o in range(parameters.outputs):
|
||||
rv.push_back({ name=PoolByteArray([64+o]).get_string_from_ascii(), type="rgba" })
|
||||
var n = PoolByteArray([65+o]).get_string_from_ascii()
|
||||
rv.push_back({ name=n, type="rgba" })
|
||||
return rv
|
||||
|
||||
func set_parameter(p, v):
|
||||
.set_parameter(p, v)
|
||||
emit_signal("parameter_changed", "__update_all__", null)
|
||||
|
||||
func source_changed(input_index : int):
|
||||
notify_output_change(input_index % int(parameters.outputs))
|
||||
|
||||
@ -42,7 +58,7 @@ func _get_shader_code(uv : String, output_index : int, context : MMGenContext):
|
||||
while rv is GDScriptFunctionState:
|
||||
rv = yield(rv, "completed")
|
||||
return rv
|
||||
return { defs="", code="", textures={} }
|
||||
return { globals=[], defs="", code="", textures={} }
|
||||
|
||||
func _serialize(data):
|
||||
return data
|
||||
|
@ -30,7 +30,7 @@ const MENU = [
|
||||
{ menu="View", command="view_reset_zoom", shortcut="Control+0", description="Reset zoom" },
|
||||
{ menu="Tools", submenu="create", description="Create" },
|
||||
{ menu="Tools", command="create_subgraph", shortcut="Control+G", description="Create group" },
|
||||
{ menu="Tools", command="make_selected_nodes_editable", shortcut="Control+F", description="Make selected nodes editable" },
|
||||
{ menu="Tools", command="make_selected_nodes_editable", shortcut="Control+E", description="Make selected nodes editable" },
|
||||
{ menu="Tools" },
|
||||
{ menu="Tools", command="add_to_user_library", description="Add selected node to user library" },
|
||||
{ menu="Tools", command="save_user_library", description="Save user library" },
|
||||
@ -308,8 +308,8 @@ func make_selected_nodes_editable():
|
||||
var selected_nodes = get_selected_nodes()
|
||||
if !selected_nodes.empty():
|
||||
for n in selected_nodes:
|
||||
n.generator.model = null
|
||||
n.update_node()
|
||||
if n.generator.toggle_editable():
|
||||
n.update_node()
|
||||
|
||||
func add_to_user_library():
|
||||
var selected_nodes = get_selected_nodes()
|
||||
|
@ -62,7 +62,7 @@ margin_left = 125.0
|
||||
margin_right = 171.0
|
||||
margin_bottom = 20.0
|
||||
text = "Tools"
|
||||
items = [ "Create", null, 0, false, false, -1, 0, null, "PopupMenu", false, "Create group", null, 0, false, false, 18, 268435527, null, "", false, "Make selected nodes editable", null, 0, false, false, 19, 268435526, null, "", false, "", null, 0, false, false, -1, 0, null, "", true, "Add selected node to user library", null, 0, false, false, 21, 0, null, "", false, "Save user library", null, 0, false, false, 22, 0, null, "", false ]
|
||||
items = [ "Create", null, 0, false, false, -1, 0, null, "PopupMenu", false, "Create group", null, 0, false, false, 18, 268435527, null, "", false, "Make selected nodes editable", null, 0, false, false, 19, 268435525, null, "", false, "", null, 0, false, false, -1, 0, null, "", true, "Add selected node to user library", null, 0, false, false, 21, 0, null, "", false, "Save user library", null, 0, false, false, 22, 0, null, "", false ]
|
||||
|
||||
[node name="Help" type="MenuButton" parent="VBoxContainer/Menu"]
|
||||
margin_left = 175.0
|
||||
|
@ -1,53 +0,0 @@
|
||||
tool
|
||||
extends "res://addons/material_maker/nodes/node_generic.gd"
|
||||
|
||||
func _on_Edit_pressed():
|
||||
var edit_window = load("res://addons/material_maker/widgets/node_editor/node_editor.tscn").instance()
|
||||
get_parent().add_child(edit_window)
|
||||
if model_data != null:
|
||||
edit_window.set_model_data(model_data)
|
||||
edit_window.connect("node_changed", self, "update_node")
|
||||
edit_window.popup_centered()
|
||||
|
||||
func _on_Load_pressed():
|
||||
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("*.mmn;Material Maker Node")
|
||||
dialog.connect("file_selected", self, "do_load_node")
|
||||
dialog.popup_centered()
|
||||
|
||||
func do_load_node(file_name):
|
||||
set_model(file_name)
|
||||
|
||||
func _on_Save_pressed():
|
||||
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("*.mmn;Material Maker Node")
|
||||
dialog.connect("file_selected", self, "do_save_node")
|
||||
dialog.popup_centered()
|
||||
|
||||
func do_save_node(file_name):
|
||||
var file = File.new()
|
||||
if file.open(file_name, File.WRITE) == OK:
|
||||
file.store_string(to_json(model_data))
|
||||
file.close()
|
||||
model = file_name
|
||||
|
||||
func deserialize(data):
|
||||
if data.has("model_data"):
|
||||
update_node(data.model_data)
|
||||
.deserialize(data)
|
||||
|
||||
func serialize():
|
||||
var file = model
|
||||
model = "custom"
|
||||
var return_value = .serialize()
|
||||
model = file
|
||||
return_value.model_data = model_data
|
||||
return return_value
|
@ -1,65 +0,0 @@
|
||||
[gd_scene load_steps=6 format=2]
|
||||
|
||||
[ext_resource path="res://addons/material_maker/nodes/custom.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/material_maker/icons/icons.svg" type="Texture" id=2]
|
||||
|
||||
[sub_resource type="AtlasTexture" id=1]
|
||||
flags = 4
|
||||
atlas = ExtResource( 2 )
|
||||
region = Rect2( 16, 16, 15, 17 )
|
||||
|
||||
[sub_resource type="AtlasTexture" id=2]
|
||||
flags = 4
|
||||
atlas = ExtResource( 2 )
|
||||
region = Rect2( 48, 16, 16, 16 )
|
||||
|
||||
[sub_resource type="AtlasTexture" id=3]
|
||||
flags = 4
|
||||
atlas = ExtResource( 2 )
|
||||
region = Rect2( 32, 16, 16, 16 )
|
||||
|
||||
[node name="Custom" type="GraphNode"]
|
||||
margin_left = 1.0
|
||||
margin_top = 2.0
|
||||
margin_right = 95.0
|
||||
margin_bottom = 53.0
|
||||
mouse_filter = 1
|
||||
title = "Custom"
|
||||
show_close = true
|
||||
slot/0/left_enabled = false
|
||||
slot/0/left_type = 0
|
||||
slot/0/left_color = Color( 1, 1, 1, 1 )
|
||||
slot/0/right_enabled = false
|
||||
slot/0/right_type = 0
|
||||
slot/0/right_color = Color( 1, 1, 1, 1 )
|
||||
script = ExtResource( 1 )
|
||||
model = null
|
||||
|
||||
[node name="CustomNodeButtons" type="HBoxContainer" parent="."]
|
||||
margin_left = 16.0
|
||||
margin_top = 24.0
|
||||
margin_right = 92.0
|
||||
margin_bottom = 47.0
|
||||
|
||||
[node name="Edit" type="Button" parent="CustomNodeButtons"]
|
||||
margin_right = 27.0
|
||||
margin_bottom = 23.0
|
||||
icon = SubResource( 1 )
|
||||
flat = true
|
||||
|
||||
[node name="Load" type="Button" parent="CustomNodeButtons"]
|
||||
margin_left = 31.0
|
||||
margin_right = 59.0
|
||||
margin_bottom = 23.0
|
||||
icon = SubResource( 2 )
|
||||
flat = true
|
||||
|
||||
[node name="Save" type="Button" parent="CustomNodeButtons"]
|
||||
margin_left = 63.0
|
||||
margin_right = 76.0
|
||||
margin_bottom = 23.0
|
||||
icon = SubResource( 3 )
|
||||
flat = true
|
||||
[connection signal="pressed" from="CustomNodeButtons/Edit" to="." method="_on_Edit_pressed"]
|
||||
[connection signal="pressed" from="CustomNodeButtons/Load" to="." method="_on_Load_pressed"]
|
||||
[connection signal="pressed" from="CustomNodeButtons/Save" to="." method="_on_Save_pressed"]
|
@ -28,7 +28,9 @@ func on_offset_changed():
|
||||
func on_parameter_changed(p, v):
|
||||
if ignore_parameter_change == p:
|
||||
return
|
||||
if controls.has(p):
|
||||
if p == "__update_all__":
|
||||
call_deferred("update_node")
|
||||
elif controls.has(p):
|
||||
var o = controls[p]
|
||||
if o is LineEdit:
|
||||
o.text = str(v)
|
||||
@ -140,14 +142,33 @@ func create_parameter_control(p : Dictionary):
|
||||
control = preload("res://addons/material_maker/widgets/gradient_editor.tscn").instance()
|
||||
return control
|
||||
|
||||
func update_node():
|
||||
# Clean node
|
||||
var custom_node_buttons = null
|
||||
func save_preview_widget():
|
||||
if preview != null:
|
||||
remove_child(preview)
|
||||
if preview_timer != null:
|
||||
preview_timer.stop()
|
||||
remove_child(preview_timer)
|
||||
|
||||
func restore_preview_widget():
|
||||
if preview == null:
|
||||
preview = TextureRect.new()
|
||||
preview.visible = false
|
||||
preview_position = get_child_count()
|
||||
if preview.visible:
|
||||
add_child(preview)
|
||||
update_preview()
|
||||
set_slot(preview_position, false, 0, Color(0.0, 0.0, 0.0), false, 0, Color(0.0, 0.0, 0.0))
|
||||
# Preview timer
|
||||
if preview_timer == null:
|
||||
preview_timer = Timer.new()
|
||||
preview_timer.one_shot = true
|
||||
preview_timer.connect("timeout", self, "do_update_preview")
|
||||
add_child(preview_timer)
|
||||
|
||||
func update_node():
|
||||
# Clean node
|
||||
var custom_node_buttons = null
|
||||
save_preview_widget()
|
||||
for c in get_children():
|
||||
remove_child(c)
|
||||
c.free()
|
||||
@ -275,23 +296,14 @@ func update_node():
|
||||
var empty_control : Control = Control.new()
|
||||
empty_control.rect_min_size.x = button_width
|
||||
hsizer.add_child(empty_control)
|
||||
# Preview
|
||||
if preview == null:
|
||||
preview = TextureRect.new()
|
||||
preview.visible = false
|
||||
preview_position = get_child_count()
|
||||
# Edit buttons
|
||||
if generator.model == null:
|
||||
if generator.is_editable():
|
||||
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")
|
||||
# Preview timer
|
||||
if preview_timer == null:
|
||||
preview_timer = Timer.new()
|
||||
preview_timer.one_shot = true
|
||||
preview_timer.connect("timeout", self, "do_update_preview")
|
||||
add_child(preview_timer)
|
||||
|
||||
set_slot(edit_buttons.get_index(), false, 0, Color(0.0, 0.0, 0.0), false, 0, Color(0.0, 0.0, 0.0))
|
||||
# Preview
|
||||
restore_preview_widget()
|
||||
|
||||
func edit_generator():
|
||||
if generator.has_method("edit"):
|
||||
@ -350,15 +362,18 @@ func do_save_generator(file_name : String):
|
||||
file.store_string(to_json(data))
|
||||
file.close()
|
||||
|
||||
func update_preview_buttons(index : int):
|
||||
for i in range(output_count):
|
||||
if i != index:
|
||||
var line = get_child(i)
|
||||
line.get_child(line.get_child_count()-1).pressed = false
|
||||
|
||||
func on_preview_button(pressed : bool, index : int):
|
||||
if pressed:
|
||||
preview_index = index
|
||||
var width
|
||||
if preview.visible:
|
||||
for i in range(output_count):
|
||||
if i != index:
|
||||
var line = get_child(i)
|
||||
line.get_child(line.get_child_count()-1).pressed = false
|
||||
update_preview_buttons(index)
|
||||
update_preview()
|
||||
else:
|
||||
var status = update_preview(get_child(0).rect_size.x)
|
||||
|
@ -1,5 +1,5 @@
|
||||
tool
|
||||
extends "res://addons/material_maker/node_base.gd"
|
||||
extends GraphNode
|
||||
|
||||
var generator = null
|
||||
|
||||
|
@ -1,281 +0,0 @@
|
||||
tool
|
||||
extends "res://addons/material_maker/node_base.gd"
|
||||
|
||||
export(String) var model = null setget set_model
|
||||
var model_data = null
|
||||
var uses_seed = false
|
||||
|
||||
func _ready():
|
||||
show_close = true
|
||||
connect("offset_changed", self, "_on_offset_changed")
|
||||
|
||||
func set_model(m):
|
||||
if m != null and typeof(m) == TYPE_STRING:
|
||||
var file = File.new()
|
||||
var file_name = m
|
||||
if !file.file_exists(file_name):
|
||||
file_name = "res://addons/material_maker/nodes/%s.mmn" % [ m ]
|
||||
if file.file_exists(file_name):
|
||||
if file.open(file_name, File.READ) != OK:
|
||||
return
|
||||
var data = file.get_as_text()
|
||||
var status = validate_json(data)
|
||||
file.close()
|
||||
if status != "":
|
||||
print("Incorrect node description (%s)" % status)
|
||||
return
|
||||
data = parse_json(data)
|
||||
model = m
|
||||
update_node(data)
|
||||
else:
|
||||
print("set_model error "+str(m))
|
||||
|
||||
func update_node(data):
|
||||
print("node_generic.update_node")
|
||||
if typeof(data) != TYPE_DICTIONARY:
|
||||
return
|
||||
if !data.has("name"):
|
||||
return
|
||||
# Clean node
|
||||
parameters = {}
|
||||
var custom_node_buttons = null
|
||||
for c in get_children():
|
||||
if c.name != "CustomNodeButtons":
|
||||
remove_child(c)
|
||||
c.queue_free()
|
||||
else:
|
||||
custom_node_buttons = c
|
||||
# Rebuild node
|
||||
title = data.name
|
||||
model_data = data
|
||||
uses_seed = false
|
||||
if model_data.has("instance") and model_data.instance.find("$(seed)"):
|
||||
uses_seed = true
|
||||
if model_data.has("parameters") and typeof(model_data.parameters) == TYPE_ARRAY:
|
||||
var control_list = []
|
||||
var sizer = null
|
||||
for p in model_data.parameters:
|
||||
if !p.has("name") or !p.has("type"):
|
||||
continue
|
||||
var control = null
|
||||
if p.type == "float":
|
||||
if p.has("widget") and p.widget == "spinbox":
|
||||
control = SpinBox.new()
|
||||
else:
|
||||
control = HSlider.new()
|
||||
control.min_value = p.min
|
||||
control.max_value = p.max
|
||||
control.step = 0 if !p.has("step") else p.step
|
||||
if p.has("default"):
|
||||
control.value = p.default
|
||||
control.rect_min_size.x = 80
|
||||
parameters[p.name] = 0.5*(p.min+p.max)
|
||||
elif p.type == "size":
|
||||
control = OptionButton.new()
|
||||
for i in range(p.first, p.last+1):
|
||||
var s = pow(2, i)
|
||||
control.add_item("%dx%d" % [ s, s ])
|
||||
control.selected = 0 if !p.has("default") else p.default-p.first
|
||||
elif p.type == "enum":
|
||||
control = OptionButton.new()
|
||||
for i in range(p.values.size()):
|
||||
var value = p.values[i]
|
||||
control.add_item(value.name)
|
||||
control.selected = 0 if !p.has("default") else p.default
|
||||
elif p.type == "boolean":
|
||||
control = CheckBox.new()
|
||||
elif p.type == "color":
|
||||
control = ColorPickerButton.new()
|
||||
elif p.type == "gradient":
|
||||
control = preload("res://addons/material_maker/widgets/gradient_editor.tscn").instance()
|
||||
if control != null:
|
||||
var label = p.name
|
||||
control.name = label
|
||||
control_list.append(control)
|
||||
if p.has("label"):
|
||||
label = p.label
|
||||
if sizer == null or label != "nonewline":
|
||||
sizer = HBoxContainer.new()
|
||||
sizer.size_flags_horizontal = SIZE_EXPAND | SIZE_FILL
|
||||
add_child(sizer)
|
||||
if label != "" && label != "nonewline":
|
||||
var label_widget = Label.new()
|
||||
label_widget.text = label
|
||||
label_widget.size_flags_horizontal = SIZE_EXPAND | SIZE_FILL
|
||||
sizer.add_child(label_widget)
|
||||
control.size_flags_horizontal = SIZE_EXPAND | SIZE_FILL
|
||||
sizer.add_child(control)
|
||||
initialize_properties(control_list)
|
||||
else:
|
||||
model_data.parameters = []
|
||||
if model_data.has("inputs") and typeof(model_data.inputs) == TYPE_ARRAY:
|
||||
for i in range(model_data.inputs.size()):
|
||||
var input = model_data.inputs[i]
|
||||
var enable_left = false
|
||||
var color_left = Color(0.5, 0.5, 0.5)
|
||||
if typeof(input) == TYPE_DICTIONARY:
|
||||
if input.type == "rgb":
|
||||
enable_left = true
|
||||
color_left = Color(0.5, 0.5, 1.0)
|
||||
elif input.type == "rgba":
|
||||
enable_left = true
|
||||
color_left = Color(0.0, 0.5, 0.0, 0.5)
|
||||
else:
|
||||
enable_left = true
|
||||
set_slot(i, enable_left, 0, color_left, false, 0, Color())
|
||||
else:
|
||||
model_data.inputs = []
|
||||
if model_data.has("outputs") and typeof(model_data.outputs) == TYPE_ARRAY:
|
||||
for i in range(model_data.outputs.size()):
|
||||
var output = model_data.outputs[i]
|
||||
var enable_right = false
|
||||
var color_right = Color(0.5, 0.5, 0.5)
|
||||
if typeof(output) == TYPE_DICTIONARY:
|
||||
if output.has("rgb"):
|
||||
enable_right = true
|
||||
color_right = Color(0.5, 0.5, 1.0)
|
||||
elif output.has("rgba"):
|
||||
enable_right = true
|
||||
color_right = Color(0.0, 0.5, 0.0, 0.5)
|
||||
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)
|
||||
else:
|
||||
model_data.outputs = []
|
||||
if custom_node_buttons != null:
|
||||
move_child(custom_node_buttons, get_child_count()-1)
|
||||
|
||||
func find_keyword_call(string, keyword):
|
||||
var search_string = "$%s(" % keyword
|
||||
var position = string.find(search_string)
|
||||
if position == -1:
|
||||
return null
|
||||
var parenthesis_level = 0
|
||||
var parameter_begin = position+search_string.length()
|
||||
var parameter_end = -1
|
||||
for i in range(parameter_begin, string.length()):
|
||||
if string[i] == '(':
|
||||
parenthesis_level += 1
|
||||
elif string[i] == ')':
|
||||
if parenthesis_level == 0:
|
||||
return string.substr(parameter_begin, i-parameter_begin)
|
||||
parenthesis_level -= 1
|
||||
return ""
|
||||
|
||||
func replace_input(string, input, type, src, default):
|
||||
var required_defs = ""
|
||||
var required_code = ""
|
||||
while true:
|
||||
var uv = find_keyword_call(string, input)
|
||||
if uv == null:
|
||||
break
|
||||
elif uv == "":
|
||||
print("syntax error")
|
||||
break
|
||||
var src_code
|
||||
if src == null:
|
||||
src_code = subst(default, "(%s)" % uv)
|
||||
else:
|
||||
src_code = src.get_shader_code(uv)
|
||||
src_code.string = src_code[type]
|
||||
required_defs += src_code.defs
|
||||
required_code += src_code.code
|
||||
string = string.replace("$%s(%s)" % [ input, uv ], src_code.string)
|
||||
return { string=string, defs=required_defs, code=required_code }
|
||||
|
||||
func is_word_letter(l):
|
||||
return "azertyuiopqsdfghjklmwxcvbnAZERTYUIOPQSDFGHJKLMWXCVBN1234567890_".find(l) != -1
|
||||
|
||||
func replace_variable(string, variable, value):
|
||||
string = string.replace("$(%s)" % variable, value)
|
||||
var keyword_size = variable.length()+1
|
||||
var new_string = ""
|
||||
while !string.empty():
|
||||
var pos = string.find("$"+variable)
|
||||
if pos == -1:
|
||||
new_string += string
|
||||
break
|
||||
new_string += string.left(pos)
|
||||
string = string.right(pos)
|
||||
if string.empty() or !is_word_letter(string[0]):
|
||||
new_string += value
|
||||
else:
|
||||
new_string += "$"+variable
|
||||
string = string.right(keyword_size)
|
||||
return new_string
|
||||
|
||||
func subst(string, uv = ""):
|
||||
var required_defs = ""
|
||||
var required_code = ""
|
||||
string = replace_variable(string, "name", name)
|
||||
string = replace_variable(string, "seed", str(get_seed()))
|
||||
if uv != "":
|
||||
string = replace_variable(string, "uv", "("+uv+")")
|
||||
if model_data.has("parameters") and typeof(model_data.parameters) == TYPE_ARRAY:
|
||||
for p in model_data.parameters:
|
||||
if !p.has("name") or !p.has("type"):
|
||||
continue
|
||||
var value = parameters[p.name]
|
||||
var value_string = null
|
||||
if p.type == "float":
|
||||
value_string = "%.9f" % value
|
||||
elif p.type == "size":
|
||||
value_string = "%.9f" % pow(2, value+p.first)
|
||||
elif p.type == "enum":
|
||||
value_string = p.values[value].value
|
||||
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"
|
||||
if value_string != null:
|
||||
string = replace_variable(string, p.name, value_string)
|
||||
if model_data.has("inputs") and typeof(model_data.inputs) == TYPE_ARRAY:
|
||||
for i in range(model_data.inputs.size()):
|
||||
var input = model_data.inputs[i]
|
||||
var source = get_source(i)
|
||||
var result = replace_input(string, input.name, input.type, source, input.default)
|
||||
string = result.string
|
||||
required_defs += result.defs
|
||||
required_code += result.code
|
||||
return { string=string, defs=required_defs, code=required_code }
|
||||
|
||||
func _get_shader_code(uv, slot = 0):
|
||||
var output_info = [ { field="rgba", type="vec4" }, { field="rgb", type="vec3" }, { field="f", type="float" } ]
|
||||
var rv = { defs="", code="" }
|
||||
var variant_string = uv+","+str(slot)
|
||||
if model_data != null and model_data.has("outputs") and model_data.outputs.size() > slot:
|
||||
var output = model_data.outputs[slot]
|
||||
rv.defs = ""
|
||||
if model_data.has("instance") && generated_variants.empty():
|
||||
rv.defs += subst(model_data.instance).string
|
||||
for p in model_data.parameters:
|
||||
if p.type == "gradient":
|
||||
rv.defs += parameters[p.name].get_shader(p.name+"_gradient_fct")
|
||||
var variant_index = generated_variants.find(variant_string)
|
||||
if variant_index == -1:
|
||||
variant_index = generated_variants.size()
|
||||
generated_variants.append(variant_string)
|
||||
for t in output_info:
|
||||
if output.has(t.field):
|
||||
var subst_output = subst(output[t.field], uv)
|
||||
rv.defs += subst_output.defs
|
||||
rv.code += subst_output.code
|
||||
rv.code += "%s %s_%d_%d_%s = %s;\n" % [ t.type, name, slot, variant_index, t.field, subst_output.string ]
|
||||
for t in output_info:
|
||||
if output.has(t.field):
|
||||
rv[t.field] = "%s_%d_%d_%s" % [ name, slot, variant_index, t.field ]
|
||||
return rv
|
||||
|
||||
func get_globals():
|
||||
var list = .get_globals()
|
||||
if typeof(model_data) == TYPE_DICTIONARY and model_data.has("global") and list.find(model_data.global) == -1:
|
||||
list.append(model_data.global)
|
||||
return list
|
||||
|
||||
func _on_offset_changed():
|
||||
update_shaders()
|
||||
|
||||
func serialize():
|
||||
var return_value = .serialize()
|
||||
return_value.type = model
|
||||
return return_value
|
87
addons/material_maker/nodes/switch.gd
Normal file
87
addons/material_maker/nodes/switch.gd
Normal file
@ -0,0 +1,87 @@
|
||||
tool
|
||||
extends MMGraphNodeGeneric
|
||||
|
||||
var fixed_lines : int = 0
|
||||
|
||||
func _ready():
|
||||
update_node()
|
||||
|
||||
func update_preview_buttons(index : int):
|
||||
for i in range(generator.parameters.outputs):
|
||||
if i != index:
|
||||
var line = get_child(i)
|
||||
line.get_child(2).pressed = false
|
||||
|
||||
func update_node():
|
||||
print("update_node")
|
||||
if generator == null or !generator.parameters.has("outputs") or !generator.parameters.has("choices"):
|
||||
return
|
||||
save_preview_widget()
|
||||
var new_fixed_lines = 3 if generator.editable else 1
|
||||
if new_fixed_lines != fixed_lines:
|
||||
fixed_lines = new_fixed_lines
|
||||
# Remove all lines
|
||||
while get_child_count() > 0:
|
||||
var remove = get_child(0)
|
||||
remove_child(remove)
|
||||
remove.free()
|
||||
var lines_list = []
|
||||
if generator.editable:
|
||||
lines_list.push_back( { name="outputs", min=1, max=5 } )
|
||||
lines_list.push_back( { name="choices", min=2, max=5 } )
|
||||
lines_list.push_back( { name="source", min=0, max=generator.parameters.choices-1 } )
|
||||
for l in lines_list:
|
||||
var sizer = HBoxContainer.new()
|
||||
var input_label = Label.new()
|
||||
sizer.add_child(input_label)
|
||||
var control : HSlider = HSlider.new()
|
||||
control.name = l.name
|
||||
control.value = generator.parameters[l.name]
|
||||
control.min_value = l.min
|
||||
control.max_value = l.max
|
||||
control.step = 1
|
||||
control.rect_min_size.x = 75
|
||||
sizer.add_child(control)
|
||||
control.connect("value_changed", self, "_on_value_changed", [ l.name ])
|
||||
controls[l.name] = control
|
||||
sizer.add_child(preload("res://addons/material_maker/widgets/preview_button.tscn").instance())
|
||||
add_child(sizer)
|
||||
else:
|
||||
# Keep lines with controls
|
||||
while get_child_count() > output_count and get_child_count() > fixed_lines:
|
||||
var remove = get_child(get_child_count()-1)
|
||||
remove_child(remove)
|
||||
remove.free()
|
||||
# Populate the GraphNode
|
||||
var output_count : int = generator.parameters.outputs
|
||||
var input_count : int = output_count * generator.parameters.choices
|
||||
controls["source"].max_value = generator.parameters.choices-1
|
||||
while get_child_count() < input_count:
|
||||
var sizer = HBoxContainer.new()
|
||||
var input_label = Label.new()
|
||||
sizer.add_child(input_label)
|
||||
if get_child_count() < 5:
|
||||
var space = Control.new()
|
||||
space.size_flags_horizontal = SIZE_EXPAND | SIZE_FILL
|
||||
sizer.add_child(space)
|
||||
sizer.add_child(preload("res://addons/material_maker/widgets/preview_button.tscn").instance())
|
||||
add_child(sizer)
|
||||
rect_size = Vector2(0, 0)
|
||||
for i in range(get_child_count()):
|
||||
var sizer = get_child(i)
|
||||
var has_input = true
|
||||
var has_output = false
|
||||
if i < 5:
|
||||
has_output = i < output_count
|
||||
sizer.get_child(2).visible = has_output
|
||||
sizer.get_child(2).connect("toggled", self, "on_preview_button", [ i ])
|
||||
if i >= input_count:
|
||||
sizer.get_child(0).text = ""
|
||||
has_input = false
|
||||
else:
|
||||
sizer.get_child(0).text = PoolByteArray([65+i%int(output_count)]).get_string_from_ascii()+str(1+i/int(output_count))
|
||||
sizer.get_child(0).add_color_override("font_color", Color(1.0, 1.0, 1.0) if i/int(output_count) == generator.parameters.source else Color(0.5, 0.5, 0.5))
|
||||
set_slot(i, has_input, 0, Color(0.0, 0.5, 0.0, 0.5), has_output, 0, Color(0.0, 0.5, 0.0, 0.5))
|
||||
# Preview
|
||||
restore_preview_widget()
|
||||
print("update_node end")
|
129
addons/material_maker/nodes/switch.tscn
Normal file
129
addons/material_maker/nodes/switch.tscn
Normal file
@ -0,0 +1,129 @@
|
||||
[gd_scene load_steps=4 format=2]
|
||||
|
||||
[ext_resource path="res://addons/material_maker/nodes/switch.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/material_maker/widgets/preview_button.tscn" type="PackedScene" id=2]
|
||||
|
||||
[sub_resource type="Theme" id=1]
|
||||
|
||||
[node name="Switch" type="GraphNode"]
|
||||
margin_left = -1.0
|
||||
margin_top = -1.0
|
||||
margin_right = 150.0
|
||||
margin_bottom = 78.0
|
||||
mouse_filter = 1
|
||||
size_flags_stretch_ratio = 0.13
|
||||
theme = SubResource( 1 )
|
||||
title = "Switch"
|
||||
show_close = true
|
||||
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 = true
|
||||
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 )
|
||||
slot/2/left_enabled = false
|
||||
slot/2/left_type = 0
|
||||
slot/2/left_color = Color( 1, 1, 1, 1 )
|
||||
slot/2/right_enabled = false
|
||||
slot/2/right_type = 0
|
||||
slot/2/right_color = Color( 1, 1, 1, 1 )
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="HBox1" type="HBoxContainer" parent="."]
|
||||
margin_left = 16.0
|
||||
margin_top = 24.0
|
||||
margin_right = 135.0
|
||||
margin_bottom = 40.0
|
||||
|
||||
[node name="Label" type="Label" parent="HBox1"]
|
||||
margin_top = 1.0
|
||||
margin_right = 20.0
|
||||
margin_bottom = 15.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
text = "A0"
|
||||
|
||||
[node name="outputs" type="HSlider" parent="HBox1"]
|
||||
margin_left = 24.0
|
||||
margin_right = 99.0
|
||||
margin_bottom = 16.0
|
||||
rect_min_size = Vector2( 75, 0 )
|
||||
hint_tooltip = "Output count"
|
||||
min_value = 1.0
|
||||
max_value = 5.0
|
||||
value = 2.0
|
||||
|
||||
[node name="PreviewButton" parent="HBox1" instance=ExtResource( 2 )]
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
margin_left = 103.0
|
||||
margin_right = 119.0
|
||||
margin_bottom = 16.0
|
||||
|
||||
[node name="HBox2" type="HBoxContainer" parent="."]
|
||||
margin_left = 16.0
|
||||
margin_top = 40.0
|
||||
margin_right = 135.0
|
||||
margin_bottom = 56.0
|
||||
|
||||
[node name="Label" type="Label" parent="HBox2"]
|
||||
margin_top = 1.0
|
||||
margin_right = 20.0
|
||||
margin_bottom = 15.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
text = "A0"
|
||||
|
||||
[node name="choices" type="HSlider" parent="HBox2"]
|
||||
margin_left = 24.0
|
||||
margin_right = 99.0
|
||||
margin_bottom = 16.0
|
||||
rect_min_size = Vector2( 75, 0 )
|
||||
hint_tooltip = "Choice count"
|
||||
min_value = 2.0
|
||||
max_value = 5.0
|
||||
value = 2.0
|
||||
|
||||
[node name="PreviewButton" parent="HBox2" instance=ExtResource( 2 )]
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
margin_left = 103.0
|
||||
margin_right = 119.0
|
||||
margin_bottom = 16.0
|
||||
|
||||
[node name="HBox3" type="HBoxContainer" parent="."]
|
||||
margin_left = 16.0
|
||||
margin_top = 57.0
|
||||
margin_right = 135.0
|
||||
margin_bottom = 73.0
|
||||
|
||||
[node name="Label" type="Label" parent="HBox3"]
|
||||
margin_top = 1.0
|
||||
margin_right = 20.0
|
||||
margin_bottom = 15.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
text = "A0"
|
||||
|
||||
[node name="source" type="HSlider" parent="HBox3"]
|
||||
margin_left = 24.0
|
||||
margin_right = 99.0
|
||||
margin_bottom = 16.0
|
||||
rect_min_size = Vector2( 75, 0 )
|
||||
hint_tooltip = "Choice"
|
||||
max_value = 4.0
|
||||
value = 1.0
|
||||
ticks_on_borders = true
|
||||
|
||||
[node name="PreviewButton" parent="HBox3" instance=ExtResource( 2 )]
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
margin_left = 103.0
|
||||
margin_right = 119.0
|
||||
margin_bottom = 16.0
|
||||
[connection signal="value_changed" from="HBox1/outputs" to="." method="_on_value_changed" binds= [ "outputs" ]]
|
||||
[connection signal="value_changed" from="HBox2/choices" to="." method="_on_value_changed" binds= [ "choices" ]]
|
||||
[connection signal="value_changed" from="HBox3/source" to="." method="_on_value_changed" binds= [ "source" ]]
|
@ -179,5 +179,4 @@ file_logging/enable_file_logging=true
|
||||
|
||||
[rendering]
|
||||
|
||||
quality/filters/anisotropic_filter_level=16
|
||||
environment/default_environment="res://default_env.tres"
|
||||
|
Loading…
Reference in New Issue
Block a user