material-maker/addons/material_maker/engine/gen_convolution.gd

188 lines
6.9 KiB
GDScript

tool
extends MMGenBase
class_name MMGenConvolution
var convolution_params : Dictionary = {}
func get_type() -> String:
return "convolution"
func get_type_name() -> String:
if convolution_params.has("name"):
return convolution_params.name
return .get_type_name()
func get_parameter_defs() -> Array:
var rv : Array = [ { name="size", type="size", first=4, last=11, default=7 } ]
if convolution_params.has("parameters"):
for p in convolution_params.parameters:
rv.push_back(p)
return rv
func get_input_defs() -> Array:
return [ { name="in", type=convolution_params.input_type } ]
func get_output_defs() -> Array:
return [ { type=convolution_params.output_type } ]
func set_convolution_params(data: Dictionary) -> void:
convolution_params = data
func _get_shader_code(uv : String, output_index : int, context : MMGenContext) -> Dictionary:
var genname = "o"+str(get_instance_id())
var epsilon = 1.0/pow(2, parameters.size)
var types = { "rgba": { type="vec4", init="vec4(0.0)" }, "rgb": { type="vec3", init="vec3(0.0)" }, "f": { type="float", init="0.0" } }
var rv = { globals=[], defs="", code="", textures={} }
var source = get_source(0)
if source == null:
return rv
var variant_index = context.get_variant(self, uv)
if variant_index == -1:
variant_index = context.get_variant(self, uv)
# Calculate matrix
var errors = 0
var sum = [ 0.0, 0.0, 0.0, 0.0 ]
var matrix = []
var expr : Expression = null
var expr_variables : PoolStringArray
var expr_values : Array
var expr_variables_x_index : int
if convolution_params.has("matrix_function"):
expr = Expression.new()
expr_variables = PoolStringArray(["size"])
expr_values = [ pow(2, parameters.size) ]
if convolution_params.has("parameters"):
for p in convolution_params.parameters:
expr_variables.push_back(p.name)
if parameters.has(p.name):
expr_values.push_back(parameters[p.name])
elif p.has("default"):
expr_values.push_back(p.default)
else:
expr_values.push_back(0)
errors += 1
print("No value for "+p.name)
expr_variables_x_index = expr_values.size()
expr_variables.push_back("x")
expr_values.push_back(0)
expr_variables.push_back("y")
expr_values.push_back(0)
var error = expr.parse(convolution_params.matrix_function, expr_variables)
if error != OK:
print("Error in expression: "+expr.get_error_text())
return rv
for dy in range(-convolution_params.y, convolution_params.y+1):
var line = []
for dx in range(-convolution_params.x, convolution_params.x+1):
var coef = 0.0
if convolution_params.has("matrix") and dy+convolution_params.y < convolution_params.matrix.size() and dx+convolution_params.x < convolution_params.matrix[dy+convolution_params.y].size() and convolution_params.matrix[dy+convolution_params.y][dx+convolution_params.x] != null:
coef = convolution_params.matrix[dy+convolution_params.y][dx+convolution_params.x]
elif convolution_params.has("matrix_sparse") and convolution_params.matrix_sparse.has(str(dy)) and convolution_params.matrix_sparse[str(dy)].has(str(dx)):
coef = convolution_params.matrix_sparse[str(dy)][str(dx)]
elif expr != null:
expr_values[expr_variables_x_index] = dx
expr_values[expr_variables_x_index+1] = dy
coef = expr.execute(expr_values)
if typeof(coef) == TYPE_INT:
coef = float(coef)
match convolution_params.output_type:
"f":
if typeof(coef) == TYPE_REAL or convolution_params.input_type == "f":
sum[0] += coef
else:
errors += 1
"rgb":
if typeof(coef) == TYPE_REAL:
sum[0] += coef
sum[1] += coef
sum[2] += coef
coef = [ coef, coef, coef ]
if convolution_params.input_type != "f" and convolution_params.input_type != "rgb":
errors += 1
elif typeof(coef) == TYPE_ARRAY and coef.size() == 3:
if convolution_params.input_type == "f" or convolution_params.input_type == "rgb":
sum[0] += coef[0]
sum[1] += coef[1]
sum[2] += coef[2]
else:
errors += 1
else:
errors += 1
"rgba":
if typeof(coef) == TYPE_REAL:
sum[0] += coef
sum[1] += coef
sum[2] += coef
sum[3] += coef
coef = [ coef, coef, coef, coef ]
if convolution_params.input_type != "f" and convolution_params.input_type != "rgba":
errors += 1
elif typeof(coef) == TYPE_ARRAY and coef.size() == 4:
if convolution_params.input_type == "f" or convolution_params.input_type == "rgba":
sum[0] += coef[0]
sum[1] += coef[1]
sum[2] += coef[2]
sum[3] += coef[3]
else:
errors += 1
else:
errors += 1
line.push_back(coef)
matrix.push_back(line)
# Generate code
rv.code += "%s %s_%d = %s;\n" % [ types[convolution_params.output_type].type, genname, variant_index, types[convolution_params.output_type].init ]
if errors > 0:
pass
else:
if convolution_params.has("normalized") and convolution_params.normalized:
for i in range(sum.size()):
if sum[i] != 0:
sum[i] = 1.0/sum[i]
else:
sum[i] = 1.0
else:
sum = [ 1.0, 1.0, 1.0, 1.0 ]
for dy in range(-convolution_params.y, convolution_params.y+1):
var line = matrix[dy+convolution_params.y]
for dx in range(-convolution_params.x, convolution_params.x+1):
var coef = line[dx+convolution_params.x]
var uv_str = "(%s)+vec2(%.9f,%.9f)" % [ uv, dx*epsilon, dy*epsilon ]
var src_code = source.generator.get_shader_code(uv_str, source.output_index, context)
while src_code is GDScriptFunctionState:
src_code = yield(src_code, "completed")
# Add global definitions
if src_code.has("globals"):
for d in src_code.globals:
if rv.globals.find(d) == -1:
rv.globals.push_back(d)
# Add generated definitions
if src_code.has("defs"):
rv.defs += src_code.defs
# Add generated code
if src_code.has("code"):
rv.code += src_code.code
var coef_str : String
match convolution_params.output_type:
"f":
coef_str = "%.9f" % [ coef * sum[0] ]
"rgb":
coef_str = "vec3(%.9f, %.9f, %.9f)" % [ coef[0] * sum[0], coef[1] * sum[1], coef[2] * sum[2] ]
"rgba":
coef_str = "vec4(%.9f, %.9f, %.9f, %.9f)" % [ coef[0] * sum[0], coef[1] * sum[1], coef[2] * sum[2], coef[3] * sum[3] ]
if src_code.has(convolution_params.input_type):
rv.code += "%s_%d += %s*%s;\n" % [ genname, variant_index, coef_str, src_code[convolution_params.input_type] ]
for t in src_code.textures.keys():
rv.textures[t] = src_code.textures[t]
rv[convolution_params.output_type] = "%s_%d" % [ genname, variant_index ]
rv.type = convolution_params.output_type
return rv
func _serialize(data: Dictionary) -> Dictionary:
data.convolution_params = convolution_params
return data
func _deserialize(data : Dictionary) -> void:
if data.has("convolution_params"):
set_convolution_params(data.convolution_params)