mirror of
https://github.com/Relintai/material-maker.git
synced 2024-12-23 21:16:54 +01:00
More shader generation refactoring
This commit is contained in:
parent
cfae489ad0
commit
66f88f3f46
@ -1,4 +1,68 @@
|
|||||||
|
tool
|
||||||
extends Node
|
extends Node
|
||||||
class_name MMGenBase
|
class_name MMGenBase
|
||||||
|
|
||||||
|
class OutputPort:
|
||||||
|
var generator : MMGenBase = null
|
||||||
|
var output_index : int = 0
|
||||||
|
|
||||||
|
func _init(g : MMGenBase, o : int):
|
||||||
|
generator = g
|
||||||
|
output_index = o
|
||||||
|
|
||||||
|
func get_shader():
|
||||||
|
return generator.get_shader(output_index)
|
||||||
|
|
||||||
|
func get_globals():
|
||||||
|
return generator.get_globals()
|
||||||
|
|
||||||
|
var parameters = null
|
||||||
|
|
||||||
|
func get_seed():
|
||||||
|
return 0
|
||||||
|
|
||||||
|
func get_source(input_index : int):
|
||||||
|
return get_parent().get_port_source(name, input_index)
|
||||||
|
|
||||||
|
func get_input_shader(input_index : int):
|
||||||
|
var source = get_source(input_index)
|
||||||
|
if source != null:
|
||||||
|
return source.get_shader()
|
||||||
|
|
||||||
|
func get_shader(output_index : int):
|
||||||
|
return get_shader_code("UV", output_index);
|
||||||
|
|
||||||
|
# this will need an output index for switch
|
||||||
|
func get_globals():
|
||||||
|
var list = []
|
||||||
|
for i in range(10):
|
||||||
|
var source = get_source(i)
|
||||||
|
if source != null:
|
||||||
|
var source_list = source.get_globals()
|
||||||
|
for g in source_list:
|
||||||
|
if list.find(g) == -1:
|
||||||
|
list.append(g)
|
||||||
|
return list
|
||||||
|
|
||||||
|
func get_shader_code(uv, slot = 0):
|
||||||
|
var rv
|
||||||
|
rv = _get_shader_code(uv, slot)
|
||||||
|
if !rv.has("f"):
|
||||||
|
if rv.has("rgb"):
|
||||||
|
rv.f = "(dot("+rv.rgb+", vec3(1.0))/3.0)"
|
||||||
|
elif rv.has("rgba"):
|
||||||
|
rv.f = "(dot("+rv.rgba+".rgb, vec3(1.0))/3.0)"
|
||||||
|
else:
|
||||||
|
rv.f = "0.0"
|
||||||
|
if !rv.has("rgb"):
|
||||||
|
if rv.has("rgba"):
|
||||||
|
rv.rgb = rv.rgba+".rgb"
|
||||||
|
else:
|
||||||
|
rv.rgb = "vec3("+rv.f+")"
|
||||||
|
if !rv.has("rgba"):
|
||||||
|
rv.rgba = "vec4("+rv.rgb+", 1.0)"
|
||||||
|
rv.globals = get_globals()
|
||||||
|
return rv
|
||||||
|
|
||||||
|
func _get_shader_code(uv : String, output_index : int):
|
||||||
|
return null
|
||||||
|
@ -1,2 +1,11 @@
|
|||||||
|
tool
|
||||||
extends MMGenBase
|
extends MMGenBase
|
||||||
class_name MMGenGraph
|
class_name MMGenGraph
|
||||||
|
|
||||||
|
var connections = null
|
||||||
|
|
||||||
|
func get_port_source(gen_name: String, input_index: int) -> OutputPort:
|
||||||
|
for c in connections:
|
||||||
|
if c.to == gen_name and c.to_port == input_index:
|
||||||
|
return OutputPort.new(get_node(c.from), c.from_port)
|
||||||
|
return null
|
||||||
|
@ -1,11 +1,122 @@
|
|||||||
|
tool
|
||||||
extends MMGenBase
|
extends MMGenBase
|
||||||
class_name MMGenMaterial
|
class_name MMGenMaterial
|
||||||
|
|
||||||
|
var texture_list
|
||||||
|
|
||||||
|
var material
|
||||||
|
var generated_textures = {}
|
||||||
|
|
||||||
|
const TEXTURE_LIST = [
|
||||||
|
{ port=0, texture="albedo" },
|
||||||
|
{ port=1, texture="metallic" },
|
||||||
|
{ port=2, texture="roughness" },
|
||||||
|
{ port=3, texture="emission" },
|
||||||
|
{ port=4, texture="normal_map" },
|
||||||
|
{ port=5, texture="ambient_occlusion" },
|
||||||
|
{ port=6, texture="depth_map" }
|
||||||
|
]
|
||||||
|
|
||||||
|
const ADDON_TEXTURE_LIST = [
|
||||||
|
{ port=0, texture="albedo" },
|
||||||
|
{ port=3, texture="emission" },
|
||||||
|
{ port=4, texture="normal_map" },
|
||||||
|
{ ports=[1, 2, 5], default_values=["0.0", "1.0", "1.0"], texture="mrao" },
|
||||||
|
{ port=6, texture="depth_map" }
|
||||||
|
]
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
texture_list = TEXTURE_LIST
|
||||||
|
if Engine.editor_hint:
|
||||||
|
texture_list = ADDON_TEXTURE_LIST
|
||||||
|
for t in texture_list:
|
||||||
|
generated_textures[t.texture] = { shader=null, source=null, texture=null }
|
||||||
|
material = SpatialMaterial.new()
|
||||||
|
|
||||||
func generate_material():
|
func generate_material():
|
||||||
print("Generating material")
|
print("Generating material")
|
||||||
var material = SpatialMaterial.new()
|
print(get_source(0).get_shader())
|
||||||
return material
|
return material
|
||||||
|
|
||||||
func initialize(data: Dictionary):
|
func initialize(data: Dictionary):
|
||||||
if data.has("name"):
|
if data.has("name"):
|
||||||
name = data.name
|
name = data.name
|
||||||
|
|
||||||
|
func get_generated_texture(slot, file_prefix = null):
|
||||||
|
if file_prefix != null:
|
||||||
|
var file_name = "%s_%s.png" % [ file_prefix, slot ]
|
||||||
|
if File.new().file_exists(file_name):
|
||||||
|
return load(file_name)
|
||||||
|
else:
|
||||||
|
return null
|
||||||
|
else:
|
||||||
|
return generated_textures[slot].texture
|
||||||
|
|
||||||
|
func update_spatial_material(m, file_prefix = null):
|
||||||
|
var texture
|
||||||
|
m.albedo_color = parameters.albedo_color
|
||||||
|
m.albedo_texture = get_generated_texture("albedo", file_prefix)
|
||||||
|
m.metallic = parameters.metallic
|
||||||
|
m.roughness = parameters.roughness
|
||||||
|
if Engine.editor_hint:
|
||||||
|
texture = get_generated_texture("mrao", file_prefix)
|
||||||
|
m.metallic_texture = texture
|
||||||
|
m.metallic_texture_channel = SpatialMaterial.TEXTURE_CHANNEL_RED
|
||||||
|
m.roughness_texture = texture
|
||||||
|
m.roughness_texture_channel = SpatialMaterial.TEXTURE_CHANNEL_GREEN
|
||||||
|
else:
|
||||||
|
m.metallic_texture = get_generated_texture("metallic", file_prefix)
|
||||||
|
m.roughness_texture = get_generated_texture("roughness", file_prefix)
|
||||||
|
texture = get_generated_texture("emission", file_prefix)
|
||||||
|
if texture != null:
|
||||||
|
m.emission_enabled = true
|
||||||
|
m.emission_energy = parameters.emission_energy
|
||||||
|
m.emission_texture = texture
|
||||||
|
else:
|
||||||
|
m.emission_enabled = false
|
||||||
|
texture = get_generated_texture("normal_map", file_prefix)
|
||||||
|
if texture != null:
|
||||||
|
m.normal_enabled = true
|
||||||
|
m.normal_texture = texture
|
||||||
|
else:
|
||||||
|
m.normal_enabled = false
|
||||||
|
if Engine.editor_hint:
|
||||||
|
if (generated_textures.mrao.mask & (1 << 2)) != 0:
|
||||||
|
m.ao_enabled = true
|
||||||
|
m.ao_light_affect = parameters.ao_light_affect
|
||||||
|
m.ao_texture = m.metallic_texture
|
||||||
|
m.ao_texture_channel = SpatialMaterial.TEXTURE_CHANNEL_BLUE
|
||||||
|
else:
|
||||||
|
m.ao_enabled = false
|
||||||
|
else:
|
||||||
|
texture = get_generated_texture("ambient_occlusion", file_prefix)
|
||||||
|
if texture != null:
|
||||||
|
m.ao_enabled = true
|
||||||
|
m.ao_light_affect = parameters.ao_light_affect
|
||||||
|
m.ao_texture = texture
|
||||||
|
else:
|
||||||
|
m.ao_enabled = false
|
||||||
|
texture = get_generated_texture("depth_map", file_prefix)
|
||||||
|
if texture != null:
|
||||||
|
m.depth_enabled = true
|
||||||
|
m.depth_scale = parameters.depth_scale
|
||||||
|
m.depth_texture = texture
|
||||||
|
else:
|
||||||
|
m.depth_enabled = false
|
||||||
|
|
||||||
|
func export_textures(prefix, size = null):
|
||||||
|
if size == null:
|
||||||
|
size = int(pow(2, 8+parameters.resolution))
|
||||||
|
for t in texture_list:
|
||||||
|
var texture = generated_textures[t.texture].texture
|
||||||
|
if texture != null:
|
||||||
|
var image = texture.get_data()
|
||||||
|
image.save_png("%s_%s.png" % [ prefix, t.texture ])
|
||||||
|
if Engine.editor_hint:
|
||||||
|
var resource_filesystem = get_parent().editor_interface.get_resource_filesystem()
|
||||||
|
resource_filesystem.scan()
|
||||||
|
yield(resource_filesystem, "filesystem_changed")
|
||||||
|
var new_material = SpatialMaterial.new()
|
||||||
|
update_spatial_material(new_material, prefix)
|
||||||
|
ResourceSaver.save("%s.tres" % [ prefix ], new_material)
|
||||||
|
resource_filesystem.scan()
|
||||||
|
@ -1,14 +1,136 @@
|
|||||||
|
tool
|
||||||
extends MMGenBase
|
extends MMGenBase
|
||||||
class_name MMGenShader
|
class_name MMGenShader
|
||||||
|
|
||||||
var config
|
var model_data = null
|
||||||
var parameters
|
var generated_variants = []
|
||||||
|
|
||||||
func configure(c: Dictionary):
|
func set_model_data(data: Dictionary):
|
||||||
config = c
|
model_data = data
|
||||||
|
|
||||||
func initialize(data: Dictionary):
|
func initialize(data: Dictionary):
|
||||||
if data.has("name"):
|
if data.has("name"):
|
||||||
name = data.name
|
name = data.name
|
||||||
if data.has("parameters"):
|
if data.has("parameters"):
|
||||||
parameters = data.parameters
|
parameters = data.parameters
|
||||||
|
|
||||||
|
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 ]
|
||||||
|
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]
|
||||||
|
if model_data.has("instance") && generated_variants.empty():
|
||||||
|
rv.defs = subst(model_data.instance).string
|
||||||
|
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
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
tool
|
||||||
extends Object
|
extends Object
|
||||||
class_name MMGenLoader
|
class_name MMGenLoader
|
||||||
|
|
||||||
@ -16,18 +17,22 @@ func create_gen(data) -> MMGenBase:
|
|||||||
var g = create_gen(n)
|
var g = create_gen(n)
|
||||||
if g != null:
|
if g != null:
|
||||||
generator.add_child(g)
|
generator.add_child(g)
|
||||||
|
generator.connections = data.connections
|
||||||
elif data.has("type"):
|
elif data.has("type"):
|
||||||
if data.type == "material":
|
if data.type == "material":
|
||||||
generator = MMGenMaterial.new()
|
generator = MMGenMaterial.new()
|
||||||
else:
|
else:
|
||||||
generator = MMGenShader.new()
|
generator = MMGenShader.new()
|
||||||
var file = File.new()
|
if data.type == "custom":
|
||||||
if file.open("res://addons/material_maker/nodes/"+data.type+".mmn", File.READ) == OK:
|
pass
|
||||||
var config = parse_json(file.get_as_text())
|
|
||||||
print("loaded description "+data.type+".mmn")
|
|
||||||
generator.configure(config)
|
|
||||||
else:
|
else:
|
||||||
print("Cannot find description for "+data.type)
|
var file = File.new()
|
||||||
|
if file.open("res://addons/material_maker/nodes/"+data.type+".mmn", File.READ) == OK:
|
||||||
|
var model_data = parse_json(file.get_as_text())
|
||||||
|
print("loaded description "+data.type+".mmn")
|
||||||
|
generator.set_model_data(model_data)
|
||||||
|
else:
|
||||||
|
print("Cannot find description for "+data.type)
|
||||||
if generator != null and data.has("parameters"):
|
if generator != null and data.has("parameters"):
|
||||||
generator.initialize(data)
|
generator.initialize(data)
|
||||||
return generator
|
return generator
|
||||||
|
@ -63,7 +63,7 @@ func get_textures():
|
|||||||
list[name] = saved_texture
|
list[name] = saved_texture
|
||||||
return list
|
return list
|
||||||
|
|
||||||
func _get_shader_code(uv):
|
func _get_shader_code(uv, slot = 0):
|
||||||
var rv = { defs="", code="" }
|
var rv = { defs="", code="" }
|
||||||
var src = get_source()
|
var src = get_source()
|
||||||
if src == null:
|
if src == null:
|
||||||
|
@ -47,4 +47,5 @@ func generate_material(ptex_filename: String) -> Material:
|
|||||||
var loader = MMGenLoader.new()
|
var loader = MMGenLoader.new()
|
||||||
var generator = loader.load_gen(ptex_filename)
|
var generator = loader.load_gen(ptex_filename)
|
||||||
add_child(generator)
|
add_child(generator)
|
||||||
return generator.get_node("Material").generate_material()
|
var material = generator.get_node("Material")
|
||||||
|
return material.generate_material()
|
||||||
|
Loading…
Reference in New Issue
Block a user