Optimized 3D preview and buffer nodes

This commit is contained in:
RodZill4 2020-03-18 21:40:02 +01:00
parent 455dcc7fde
commit 3c87f2c491
8 changed files with 124 additions and 33 deletions

View File

@ -117,12 +117,13 @@ func get_parameter(n : String):
func set_parameter(n : String, v) -> void: func set_parameter(n : String, v) -> void:
parameters[n] = v parameters[n] = v
source_changed(0)
emit_signal("parameter_changed", n, v) emit_signal("parameter_changed", n, v)
if is_inside_tree(): if is_inside_tree():
var parameter_def : Dictionary = get_parameter_def(n) var parameter_def : Dictionary = get_parameter_def(n)
if parameter_def.has("type") and parameter_def.type == "float": if parameter_def.has("type") and parameter_def.type == "float":
get_tree().call_group("preview", "on_float_parameter_changed", "p_o%s_%s" % [ str(get_instance_id()), n ], v) get_tree().call_group("preview", "on_float_parameter_changed", "p_o%s_%s" % [ str(get_instance_id()), n ], v)
else:
source_changed(0)
func notify_output_change(output_index : int) -> void: func notify_output_change(output_index : int) -> void:
var targets = get_targets(output_index) var targets = get_targets(output_index)

View File

@ -7,11 +7,16 @@ Texture generator buffers, that render their input in a specific resolution and
This is useful when using generators that sample their inputs several times (such as convolutions) This is useful when using generators that sample their inputs several times (such as convolutions)
""" """
var updated : bool = false var material : ShaderMaterial = null
var updating : bool = false
var update_again : bool = false
func _ready() -> void: func _ready() -> void:
material = ShaderMaterial.new()
material.shader = Shader.new()
if !parameters.has("size"): if !parameters.has("size"):
parameters.size = 9 parameters.size = 9
add_to_group("preview")
func get_type() -> String: func get_type() -> String:
return "buffer" return "buffer"
@ -31,20 +36,51 @@ func get_input_defs() -> Array:
func get_output_defs() -> Array: func get_output_defs() -> Array:
return [ { type="rgba" }, { type="rgba" } ] return [ { type="rgba" }, { type="rgba" } ]
func source_changed(input_port_index : int) -> void: func source_changed(_input_port_index : int) -> void:
updated = false if !is_inside_tree():
.source_changed(input_port_index) return
var context : MMGenContext = MMGenContext.new()
var source = {}
var source_output = get_source(0)
if source_output != null:
source = source_output.generator.get_shader_code("uv", source_output.output_index, context)
while source is GDScriptFunctionState:
source = yield(source, "completed")
if source.empty():
source = { defs="", code="", textures={}, rgba="vec4(0.0)" }
material.shader.code = mm_renderer.generate_shader(source)
if source.has("textures"):
for k in source.textures.keys():
material.set_shader_param(k, source.textures[k])
update_buffer()
func _get_shader_code(uv : String, output_index : int, context : MMGenContext) -> Dictionary: func on_float_parameter_changed(n : String, v : float) -> void:
material.set_shader_param(n, v)
update_buffer()
func update_buffer():
update_again = true
if !updating:
updating = true
while update_again:
update_again = false
var result = mm_renderer.render_material(material, pow(2, parameters.size))
while result is GDScriptFunctionState:
result = yield(result, "completed")
if !update_again:
result.copy_to_texture(texture)
result.release()
updating = false
func __get_shader_code(uv : String, output_index : int, context : MMGenContext) -> Dictionary:
var source = get_source(0) var source = get_source(0)
if source != null and !updated: if source != null:
var result = source.generator.render(source.output_index, pow(2, parameters.size)) var result = source.generator.render(source.output_index, pow(2, parameters.size))
while result is GDScriptFunctionState: while result is GDScriptFunctionState:
result = yield(result, "completed") result = yield(result, "completed")
result.copy_to_texture(texture) result.copy_to_texture(texture)
result.release() result.release()
texture.flags = Texture.FLAG_MIPMAPS texture.flags = Texture.FLAG_MIPMAPS
updated = true
var rv = ._get_shader_code_lod(uv, output_index, context, 0 if output_index == 0 else parameters.lod) var rv = ._get_shader_code_lod(uv, output_index, context, 0 if output_index == 0 else parameters.lod)
while rv is GDScriptFunctionState: while rv is GDScriptFunctionState:
rv = yield(rv, "completed") rv = yield(rv, "completed")

View File

@ -5,7 +5,11 @@ class_name MMGenMaterial
var export_paths = {} var export_paths = {}
var material : SpatialMaterial var material : SpatialMaterial
var shader_materials = {}
var need_update = {}
var generated_textures = {} var generated_textures = {}
var updating : bool = false
var update_again : bool = false
const TEXTURE_LIST = [ const TEXTURE_LIST = [
{ port=0, texture="albedo", sources=[0] }, { port=0, texture="albedo", sources=[0] },
@ -38,7 +42,11 @@ const TEXTURE_SIZE_DEFAULT = 10 # 1024x1024
func _ready() -> void: func _ready() -> void:
for t in TEXTURE_LIST: for t in TEXTURE_LIST:
generated_textures[t.texture] = null generated_textures[t.texture] = null
need_update[t.texture] = true
shader_materials[t.texture] = ShaderMaterial.new()
shader_materials[t.texture].shader = Shader.new()
material = SpatialMaterial.new() material = SpatialMaterial.new()
add_to_group("preview")
func can_be_deleted() -> bool: func can_be_deleted() -> bool:
return false return false
@ -74,34 +82,63 @@ func set_parameter(p, v) -> void:
func source_changed(input_index : int) -> void: func source_changed(input_index : int) -> void:
for t in TEXTURE_LIST: for t in TEXTURE_LIST:
if t.has("sources") and t.sources.find(input_index) != -1: if t.has("sources") and t.sources.find(input_index) != -1:
generated_textures[t.texture] = null need_update[t.texture] = true
update_preview() update_preview()
func render_textures() -> void: func render_textures() -> void:
for t in TEXTURE_LIST: for t in TEXTURE_LIST:
var texture = null
var result var result
if t.has("port"): if t.has("port"):
if generated_textures[t.texture] != null: if !need_update[t.texture]:
continue continue
result = render(t.port, get_image_size()) var context : MMGenContext = MMGenContext.new()
var source = get_shader_code("uv", t.port, context)
while source is GDScriptFunctionState:
source = yield(source, "completed")
if source.empty():
source = { defs="", code="", textures={}, rgba="vec4(0.0)" }
shader_materials[t.texture].shader.code = mm_renderer.generate_shader(source)
if source.has("textures"):
for k in source.textures.keys():
shader_materials[t.texture].set_shader_param(k, source.textures[k])
result = mm_renderer.render_material(shader_materials[t.texture], get_image_size())
else: else:
generated_textures[t.texture] = null generated_textures[t.texture] = null
need_update[t.texture] = false
continue continue
while result is GDScriptFunctionState: while result is GDScriptFunctionState:
result = yield(result, "completed") result = yield(result, "completed")
texture = ImageTexture.new() if generated_textures[t.texture] == null:
generated_textures[t.texture] = ImageTexture.new()
var texture = generated_textures[t.texture]
result.copy_to_texture(texture) result.copy_to_texture(texture)
result.release() result.release()
# To work, this must be set after calling `copy_to_texture()` # To work, this must be set after calling `copy_to_texture()`
texture.flags |= ImageTexture.FLAG_ANISOTROPIC_FILTER texture.flags |= ImageTexture.FLAG_ANISOTROPIC_FILTER
# Disable filtering for small textures, as they're considered to be used # Disable filtering for small textures, as they're considered to be used
# for a pixel art style # for a pixel art style
if texture.get_size().x <= 128: if texture.get_size().x <= 128:
texture.flags ^= ImageTexture.FLAG_FILTER texture.flags ^= ImageTexture.FLAG_FILTER
need_update[t.texture] = false
generated_textures[t.texture] = texture func on_float_parameter_changed(n : String, v : float) -> void:
var image_size = get_image_size()
for t in TEXTURE_LIST:
if generated_textures[t.texture] != null:
shader_materials[t.texture].set_shader_param(n, v)
update_again = true
if !updating:
updating = true
while update_again:
update_again = false
for t in TEXTURE_LIST:
if generated_textures[t.texture] != null:
var result = mm_renderer.render_material(shader_materials[t.texture], image_size)
while result is GDScriptFunctionState:
result = yield(result, "completed")
result.copy_to_texture(generated_textures[t.texture])
result.release()
updating = false
func update_materials(material_list) -> void: func update_materials(material_list) -> void:
render_textures() render_textures()

View File

@ -5,19 +5,24 @@ class_name MMGenRenderer
export(String) var debug_path = "" export(String) var debug_path = ""
var debug_file_index : int = 0 var debug_file_index : int = 0
var common_shader : String
var rendering : bool = false var rendering : bool = false
signal done signal done
func _ready() -> void: func _ready() -> void:
$ColorRect.material = $ColorRect.material.duplicate(true) $ColorRect.material = $ColorRect.material.duplicate(true)
var file = File.new()
file.open("res://addons/material_maker/common.shader", File.READ)
common_shader = file.get_as_text()
static func generate_shader(src_code) -> String: func generate_shader(src_code) -> String:
var code var code
code = "shader_type canvas_item;\n" code = "shader_type canvas_item;\n"
code += "render_mode blend_disabled;\n" code += "render_mode blend_disabled;\n"
var file = File.new() code += common_shader
file.open("res://addons/material_maker/common.shader", File.READ)
code += file.get_as_text()
code += "\n" code += "\n"
if src_code.has("textures"): if src_code.has("textures"):
for t in src_code.textures.keys(): for t in src_code.textures.keys():
@ -38,13 +43,11 @@ static func generate_shader(src_code) -> String:
code += shader_code code += shader_code
return code return code
static func generate_combined_shader(red_code, green_code, blue_code) -> String: func generate_combined_shader(red_code, green_code, blue_code) -> String:
var code var code
code = "shader_type canvas_item;\n" code = "shader_type canvas_item;\n"
code += "render_mode blend_disabled;\n" code += "render_mode blend_disabled;\n"
var file = File.new() code += common_shader
file.open("res://addons/material_maker/common.shader", File.READ)
code += file.get_as_text()
code += "\n" code += "\n"
var globals = [] var globals = []
var textures = {} var textures = {}
@ -84,6 +87,22 @@ func setup_material(shader_material, textures, shader_code) -> void:
shader_material.set_shader_param(k+"_tex", textures[k]) shader_material.set_shader_param(k+"_tex", textures[k])
shader_material.shader.code = shader_code shader_material.shader.code = shader_code
func render_material(material, render_size) -> Object:
while rendering:
yield(self, "done")
rendering = true
var shader_material = $ColorRect.material
size = Vector2(render_size, render_size)
$ColorRect.rect_position = Vector2(0, 0)
$ColorRect.rect_size = size
$ColorRect.material = material
render_target_update_mode = Viewport.UPDATE_ONCE
update_worlds()
yield(get_tree(), "idle_frame")
yield(get_tree(), "idle_frame")
$ColorRect.material = shader_material
return self
func render_shader(shader, textures, render_size) -> Object: func render_shader(shader, textures, render_size) -> Object:
if debug_path != null and debug_path != "": if debug_path != null and debug_path != "":
var file_name = debug_path+str(debug_file_index)+".shader" var file_name = debug_path+str(debug_file_index)+".shader"

View File

@ -95,7 +95,8 @@ func _on_value_changed(new_value, variable) -> void:
ignore_parameter_change = variable ignore_parameter_change = variable
generator.set_parameter(variable, new_value) generator.set_parameter(variable, new_value)
ignore_parameter_change = "" ignore_parameter_change = ""
update_shaders() if ! (new_value is float):
update_shaders()
func _on_color_changed(new_color, variable) -> void: func _on_color_changed(new_color, variable) -> void:
ignore_parameter_change = variable ignore_parameter_change = variable

View File

@ -5,15 +5,12 @@ export(String, MULTILINE) var shader : String = ""
var generator : MMGenBase = null var generator : MMGenBase = null
func set_generator(g : MMGenBase) -> void: func set_generator(g : MMGenBase) -> void:
if generator != null and is_instance_valid(generator):
generator.disconnect("float_param_changed", self, "on_float_param_changed")
var source = { defs="", code="", textures={}, type="f", f="1.0" } var source = { defs="", code="", textures={}, type="f", f="1.0" }
if is_instance_valid(g): if is_instance_valid(g):
generator = g generator = g
var param_defs : Array = generator.get_parameter_defs() var param_defs : Array = generator.get_parameter_defs()
for c in get_children(): for c in get_children():
c.setup_control(generator, param_defs) c.setup_control(generator, param_defs)
generator.connect("float_param_changed", self, "on_float_param_changed")
var gen_output_defs = generator.get_output_defs() var gen_output_defs = generator.get_output_defs()
if ! gen_output_defs.empty(): if ! gen_output_defs.empty():
var context : MMGenContext = MMGenContext.new() var context : MMGenContext = MMGenContext.new()
@ -27,6 +24,9 @@ func set_generator(g : MMGenBase) -> void:
for c in get_children(): for c in get_children():
c.setup_control(generator, []) c.setup_control(generator, [])
material.shader.code = MMGenBase.generate_preview_shader(source, source.type, shader) material.shader.code = MMGenBase.generate_preview_shader(source, source.type, shader)
if source.has("textures"):
for k in source.textures.keys():
material.set_shader_param(k, source.textures[k])
func on_float_parameter_changed(n : String, v : float) -> void: func on_float_parameter_changed(n : String, v : float) -> void:
material.set_shader_param(n, v) material.set_shader_param(n, v)

View File

@ -1,9 +1,5 @@
extends ViewportContainer extends ViewportContainer
const ENVIRONMENTS = [
"experiment", "lobby", "night", "park", "schelde"
]
const CAMERA_DISTANCE_MIN = 1.0 const CAMERA_DISTANCE_MIN = 1.0
const CAMERA_DISTANCE_MAX = 10.0 const CAMERA_DISTANCE_MAX = 10.0

View File

@ -3,10 +3,11 @@
[ext_resource path="res://material_maker/preview/preview_3d.gd" type="Script" id=1] [ext_resource path="res://material_maker/preview/preview_3d.gd" type="Script" id=1]
[ext_resource path="res://material_maker/preview/preview_3d_scene.tscn" type="PackedScene" id=2] [ext_resource path="res://material_maker/preview/preview_3d_scene.tscn" type="PackedScene" id=2]
[sub_resource type="World" id=1] [sub_resource type="World" id=1]
[node name="Preview3D" type="ViewportContainer"] [node name="Preview3D" type="ViewportContainer" groups=[
"preview",
]]
visible = false visible = false
anchor_right = 1.0 anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0