2019-08-02 08:17:32 +02:00
|
|
|
tool
|
2019-07-31 20:53:00 +02:00
|
|
|
extends Node
|
|
|
|
class_name MMGenBase
|
|
|
|
|
2019-08-25 23:27:07 +02:00
|
|
|
"""
|
|
|
|
Base class for texture generators, that defines their API
|
|
|
|
"""
|
|
|
|
|
2020-03-25 22:01:02 +01:00
|
|
|
signal parameter_changed(n, v)
|
2019-09-22 22:17:26 +02:00
|
|
|
|
2019-09-14 09:14:27 +02:00
|
|
|
class InputPort:
|
|
|
|
var generator : MMGenBase = null
|
|
|
|
var input_index : int = 0
|
2019-10-20 16:22:06 +02:00
|
|
|
|
|
|
|
func _init(g : MMGenBase, i : int) -> void:
|
2019-09-14 09:14:27 +02:00
|
|
|
generator = g
|
|
|
|
input_index = i
|
2019-10-20 16:22:06 +02:00
|
|
|
|
|
|
|
func to_str() -> String:
|
2019-09-14 09:14:27 +02:00
|
|
|
return generator.name+".in("+str(input_index)+")"
|
|
|
|
|
2019-08-02 08:17:32 +02:00
|
|
|
class OutputPort:
|
|
|
|
var generator : MMGenBase = null
|
|
|
|
var output_index : int = 0
|
2019-10-20 16:22:06 +02:00
|
|
|
|
|
|
|
func _init(g : MMGenBase, o : int) -> void:
|
2019-08-02 08:17:32 +02:00
|
|
|
generator = g
|
|
|
|
output_index = o
|
2019-10-20 16:22:06 +02:00
|
|
|
|
|
|
|
func to_str() -> String:
|
2019-09-14 09:14:27 +02:00
|
|
|
return generator.name+".out("+str(output_index)+")"
|
2019-08-02 08:17:32 +02:00
|
|
|
|
2019-08-12 15:58:42 +02:00
|
|
|
var position : Vector2 = Vector2(0, 0)
|
2019-09-09 22:00:18 +02:00
|
|
|
var model = null
|
2019-08-12 15:58:42 +02:00
|
|
|
var parameters = {}
|
|
|
|
|
2019-11-04 07:58:17 +01:00
|
|
|
var seed_locked : bool = false
|
|
|
|
var seed_value : int = 0
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func _ready() -> void:
|
2019-08-17 17:35:48 +02:00
|
|
|
init_parameters()
|
|
|
|
|
2019-10-24 09:00:31 +02:00
|
|
|
func _post_load() -> void:
|
|
|
|
pass
|
|
|
|
|
2019-10-19 01:30:25 +02:00
|
|
|
func can_be_deleted() -> bool:
|
|
|
|
return true
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func toggle_editable() -> bool:
|
2019-10-20 00:59:51 +02:00
|
|
|
return false
|
2019-10-20 16:22:06 +02:00
|
|
|
|
2020-03-02 23:01:27 +01:00
|
|
|
func is_template() -> bool:
|
|
|
|
return model != null
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func is_editable() -> bool:
|
2019-10-20 00:59:51 +02:00
|
|
|
return false
|
|
|
|
|
2019-11-04 07:58:17 +01:00
|
|
|
|
2019-11-06 23:49:26 +01:00
|
|
|
func has_randomness() -> bool:
|
2019-11-03 10:04:23 +01:00
|
|
|
return false
|
|
|
|
|
2019-11-04 07:58:17 +01:00
|
|
|
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
|
|
|
|
|
2019-11-06 23:49:26 +01:00
|
|
|
func toggle_lock_seed() -> bool:
|
2019-11-04 07:58:17 +01:00
|
|
|
if !seed_locked:
|
|
|
|
seed_value = get_seed()
|
|
|
|
seed_locked = !seed_locked
|
|
|
|
return seed_locked
|
|
|
|
|
2019-11-06 23:49:26 +01:00
|
|
|
func is_seed_locked() -> bool:
|
2019-11-04 07:58:17 +01:00
|
|
|
return seed_locked
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func init_parameters() -> void:
|
2019-08-17 17:35:48 +02:00
|
|
|
for p in get_parameter_defs():
|
|
|
|
if !parameters.has(p.name):
|
|
|
|
if p.has("default"):
|
|
|
|
parameters[p.name] = MMType.deserialize_value(p.default)
|
2019-10-14 20:42:11 +02:00
|
|
|
if p.type == "size":
|
|
|
|
parameters[p.name] -= p.first
|
2019-08-17 17:35:48 +02:00
|
|
|
else:
|
|
|
|
print("No default value for parameter "+p.name)
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func set_position(p) -> void:
|
2019-10-02 23:06:20 +02:00
|
|
|
position = p
|
2020-03-24 19:21:28 +01:00
|
|
|
if has_randomness() and !is_seed_locked() and is_inside_tree():
|
2020-03-26 21:24:50 +01:00
|
|
|
get_tree().call_group("preview", "on_float_parameters_changed", { "seed_o"+str(get_instance_id()): get_seed() })
|
2019-08-02 08:17:32 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func get_type() -> String:
|
2019-08-12 15:58:42 +02:00
|
|
|
return "generic"
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func get_type_name() -> String:
|
2019-08-17 17:35:48 +02:00
|
|
|
return "Unnamed"
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func get_parameter_defs() -> Array:
|
2019-08-17 17:35:48 +02:00
|
|
|
return []
|
|
|
|
|
2019-10-24 09:00:31 +02:00
|
|
|
func get_parameter_def(param_name : String) -> Dictionary:
|
|
|
|
var parameter_defs = get_parameter_defs()
|
|
|
|
for p in parameter_defs:
|
|
|
|
if p.name == param_name:
|
|
|
|
return p
|
|
|
|
return {}
|
|
|
|
|
2019-12-27 22:30:49 +01:00
|
|
|
func get_parameter(n : String):
|
2020-03-13 07:50:04 +01:00
|
|
|
if parameters.has(n):
|
|
|
|
return parameters[n]
|
|
|
|
else:
|
|
|
|
return get_parameter_def(n).default
|
2019-12-27 22:30:49 +01:00
|
|
|
|
2020-03-26 08:56:55 +01:00
|
|
|
class CustomGradientSorter:
|
|
|
|
static func compare(a, b) -> bool:
|
|
|
|
return a.pos < b.pos
|
|
|
|
|
2019-11-06 23:49:26 +01:00
|
|
|
func set_parameter(n : String, v) -> void:
|
2020-03-26 08:56:55 +01:00
|
|
|
var old_value = parameters[n] if parameters.has(n) else null
|
2019-08-25 23:27:07 +02:00
|
|
|
parameters[n] = v
|
2019-09-22 22:17:26 +02:00
|
|
|
emit_signal("parameter_changed", n, v)
|
2020-03-13 07:50:04 +01:00
|
|
|
if is_inside_tree():
|
|
|
|
var parameter_def : Dictionary = get_parameter_def(n)
|
2020-03-26 08:56:55 +01:00
|
|
|
if parameter_def.has("type"):
|
|
|
|
if parameter_def.type == "float":
|
|
|
|
var parameter_name = "p_o%s_%s" % [ str(get_instance_id()), n ]
|
2020-03-26 21:24:50 +01:00
|
|
|
get_tree().call_group("preview", "on_float_parameters_changed", { parameter_name:v })
|
2020-03-26 08:56:55 +01:00
|
|
|
return
|
|
|
|
elif parameter_def.type == "gradient":
|
|
|
|
if v.interpolation == old_value.interpolation && v.points.size() == old_value.points.size():
|
|
|
|
# convert from old format
|
|
|
|
for i in range(old_value.points.size()):
|
|
|
|
if old_value.points[i].has("v"):
|
|
|
|
var old = old_value.points[i]
|
|
|
|
old_value.points[i] = { pos=old.v, r=old.c.r, g=old.c.g, b=old.c.b, a=old.c.a }
|
|
|
|
old_value.points.sort_custom(CustomGradientSorter, "compare")
|
|
|
|
v.points.sort_custom(CustomGradientSorter, "compare")
|
2020-03-26 21:24:50 +01:00
|
|
|
var parameter_changes = {}
|
2020-03-26 08:56:55 +01:00
|
|
|
for i in range(old_value.points.size()):
|
|
|
|
for f in [ "pos", "r", "g", "b", "a" ]:
|
|
|
|
if v.points[i][f] != old_value.points[i][f]:
|
|
|
|
var parameter_name = "p_o%s_%s_%d_%s" % [ str(get_instance_id()), n, i, f ]
|
2020-03-26 21:24:50 +01:00
|
|
|
parameter_changes[parameter_name] = v.points[i][f]
|
|
|
|
get_tree().call_group("preview", "on_float_parameters_changed", parameter_changes)
|
2020-03-26 08:56:55 +01:00
|
|
|
return
|
|
|
|
source_changed(0)
|
2019-09-22 22:17:26 +02:00
|
|
|
|
2019-11-06 23:49:26 +01:00
|
|
|
func notify_output_change(output_index : int) -> void:
|
2019-09-22 22:17:26 +02:00
|
|
|
var targets = get_targets(output_index)
|
|
|
|
for target in targets:
|
|
|
|
target.generator.source_changed(target.input_index)
|
2020-03-25 22:01:02 +01:00
|
|
|
emit_signal("parameter_changed", "__output_changed__", output_index)
|
2019-09-14 09:14:27 +02:00
|
|
|
|
2020-03-25 22:01:02 +01:00
|
|
|
func source_changed(input_index : int) -> void:
|
|
|
|
emit_signal("parameter_changed", "__input_changed__", input_index)
|
2019-09-14 09:14:27 +02:00
|
|
|
for i in range(get_output_defs().size()):
|
2019-09-22 22:17:26 +02:00
|
|
|
notify_output_change(i)
|
2019-08-25 23:27:07 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func get_input_defs() -> Array:
|
2019-08-17 17:35:48 +02:00
|
|
|
return []
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func get_output_defs() -> Array:
|
2019-08-17 17:35:48 +02:00
|
|
|
return []
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func get_source(input_index : int) -> OutputPort:
|
2019-08-02 08:17:32 +02:00
|
|
|
return get_parent().get_port_source(name, input_index)
|
2019-10-20 16:22:06 +02:00
|
|
|
|
|
|
|
func get_targets(output_index : int) -> Array:
|
2019-10-21 23:58:14 +02:00
|
|
|
var parent = get_parent()
|
|
|
|
if parent != null:
|
|
|
|
return get_parent().get_port_targets(name, output_index)
|
|
|
|
return []
|
2019-08-02 08:17:32 +02:00
|
|
|
|
2019-10-19 01:30:25 +02:00
|
|
|
# get the list of outputs that depend on the input whose index is passed as parameter
|
2020-02-13 08:37:01 +01:00
|
|
|
func follow_input(_input_index : int) -> Array:
|
2019-10-19 01:30:25 +02:00
|
|
|
var rv = []
|
|
|
|
for i in range(get_output_defs().size()):
|
|
|
|
rv.push_back(OutputPort.new(self, i))
|
|
|
|
return rv
|
|
|
|
|
2019-11-06 23:49:26 +01:00
|
|
|
func get_input_shader(input_index : int) -> Dictionary:
|
2019-08-02 08:17:32 +02:00
|
|
|
var source = get_source(input_index)
|
|
|
|
if source != null:
|
|
|
|
return source.get_shader()
|
2019-11-06 23:49:26 +01:00
|
|
|
return {}
|
2019-08-02 08:17:32 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func get_shader(output_index : int, context) -> Dictionary:
|
|
|
|
return get_shader_code("UV", output_index, context)
|
2019-08-02 08:17:32 +02:00
|
|
|
|
2020-03-13 07:50:04 +01:00
|
|
|
static func generate_preview_shader(src_code, type, main_fct = "void fragment() { COLOR = preview_2d(UV); }") -> String:
|
2020-01-28 11:47:29 +01:00
|
|
|
var code
|
|
|
|
code = "shader_type canvas_item;\n"
|
|
|
|
code += "render_mode blend_disabled;\n"
|
|
|
|
code += "uniform float preview_size = 64;\n"
|
|
|
|
var file = File.new()
|
|
|
|
file.open("res://addons/material_maker/common.shader", File.READ)
|
|
|
|
code += file.get_as_text()
|
|
|
|
code += "\n"
|
|
|
|
if src_code.has("textures"):
|
|
|
|
for t in src_code.textures.keys():
|
|
|
|
code += "uniform sampler2D "+t+";\n"
|
|
|
|
if src_code.has("globals"):
|
|
|
|
for g in src_code.globals:
|
|
|
|
code += g
|
|
|
|
var shader_code = src_code.defs
|
2020-01-28 22:40:03 +01:00
|
|
|
if src_code.has(type):
|
|
|
|
var preview_code : String = mm_io_types.types[type].preview
|
|
|
|
preview_code = preview_code.replace("$(code)", src_code.code)
|
|
|
|
preview_code = preview_code.replace("$(value)", src_code[type])
|
|
|
|
shader_code += preview_code
|
2020-01-28 11:47:29 +01:00
|
|
|
#print("GENERATED SHADER:\n"+shader_code)
|
|
|
|
code += shader_code
|
2020-03-13 07:50:04 +01:00
|
|
|
code += main_fct
|
2020-01-28 11:47:29 +01:00
|
|
|
return code
|
|
|
|
|
2019-12-15 12:41:00 +01:00
|
|
|
func render(output_index : int, size : int, preview : bool = false) -> Object:
|
2019-11-06 22:33:01 +01:00
|
|
|
var context : MMGenContext = MMGenContext.new()
|
2019-12-15 12:41:00 +01:00
|
|
|
var source = get_shader_code("uv", output_index, context)
|
2019-08-18 16:28:50 +02:00
|
|
|
while source is GDScriptFunctionState:
|
|
|
|
source = yield(source, "completed")
|
2019-10-20 16:22:06 +02:00
|
|
|
if source.empty():
|
2019-10-05 11:04:50 +02:00
|
|
|
source = { defs="", code="", textures={}, rgba="vec4(0.0)" }
|
2019-12-15 12:41:00 +01:00
|
|
|
var shader : String
|
|
|
|
if preview:
|
2020-01-28 22:40:03 +01:00
|
|
|
var output_type = "rgba"
|
2020-01-28 11:47:29 +01:00
|
|
|
var outputs = get_output_defs()
|
|
|
|
if outputs.size() > output_index:
|
2020-01-28 22:40:03 +01:00
|
|
|
output_type = outputs[output_index].type
|
|
|
|
shader = generate_preview_shader(source, output_type)
|
2019-12-15 12:41:00 +01:00
|
|
|
else:
|
|
|
|
shader = mm_renderer.generate_shader(source)
|
2019-11-06 22:33:01 +01:00
|
|
|
var result = mm_renderer.render_shader(shader, source.textures, size)
|
2019-10-05 11:04:50 +02:00
|
|
|
while result is GDScriptFunctionState:
|
|
|
|
result = yield(result, "completed")
|
|
|
|
return result
|
2019-08-17 17:35:48 +02:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func get_shader_code(uv : String, output_index : int, context : MMGenContext) -> Dictionary:
|
2019-08-17 17:35:48 +02:00
|
|
|
var rv = _get_shader_code(uv, output_index, context)
|
2019-08-18 16:28:50 +02:00
|
|
|
while rv is GDScriptFunctionState:
|
|
|
|
rv = yield(rv, "completed")
|
2020-02-06 21:51:14 +01:00
|
|
|
if rv.has("type") and mm_io_types.types.has(rv.type):
|
2020-01-28 22:40:03 +01:00
|
|
|
if mm_io_types.types[rv.type].has("convert"):
|
|
|
|
for c in mm_io_types.types[rv.type].convert:
|
|
|
|
if !rv.has(c.type):
|
|
|
|
var expr = c.expr.replace("$(value)", rv[rv.type])
|
|
|
|
rv[c.type] = expr
|
|
|
|
else:
|
|
|
|
print("Missing type for node ")
|
|
|
|
print(rv)
|
2019-08-02 08:17:32 +02:00
|
|
|
return rv
|
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func _get_shader_code(__, __, __) -> Dictionary:
|
|
|
|
return {}
|
2019-09-09 22:00:18 +02:00
|
|
|
|
2019-11-04 07:58:17 +01:00
|
|
|
|
2019-10-20 16:22:06 +02:00
|
|
|
func _serialize(data: Dictionary) -> Dictionary:
|
2019-09-09 22:00:18 +02:00
|
|
|
print("cannot save "+name)
|
|
|
|
return data
|
|
|
|
|
2020-02-19 23:24:34 +01:00
|
|
|
func _serialize_data(data: Dictionary) -> Dictionary:
|
|
|
|
return data
|
|
|
|
|
2019-11-06 23:49:26 +01:00
|
|
|
func serialize() -> Dictionary:
|
2019-09-16 20:45:47 +02:00
|
|
|
var rv = { name=name, type=get_type(), parameters={}, node_position={ x=position.x, y=position.y } }
|
2020-02-12 08:05:45 +01:00
|
|
|
for p in get_parameter_defs():
|
|
|
|
if parameters.has(p.name):
|
|
|
|
rv.parameters[p.name] = MMType.serialize_value(parameters[p.name])
|
2020-02-19 23:24:34 +01:00
|
|
|
elif p.has("default"):
|
2020-02-12 08:05:45 +01:00
|
|
|
rv.parameters[p.name] = p.default
|
2019-11-04 07:58:17 +01:00
|
|
|
if seed_locked:
|
|
|
|
rv.seed_value = seed_value
|
2019-09-09 22:00:18 +02:00
|
|
|
if model != null:
|
|
|
|
rv.type = model
|
|
|
|
else:
|
|
|
|
rv = _serialize(rv)
|
2020-02-19 23:24:34 +01:00
|
|
|
rv = _serialize_data(rv)
|
2019-10-18 08:35:54 +02:00
|
|
|
return rv
|
2019-11-04 07:58:17 +01:00
|
|
|
|
2020-02-13 08:37:01 +01:00
|
|
|
func _deserialize(_data : Dictionary) -> void:
|
2019-11-04 07:58:17 +01:00
|
|
|
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()
|