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:
parameters[n] = v
source_changed(0)
emit_signal("parameter_changed", n, v)
if is_inside_tree():
var parameter_def : Dictionary = get_parameter_def(n)
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)
else:
source_changed(0)
func notify_output_change(output_index : int) -> void:
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)
"""
var updated : bool = false
var material : ShaderMaterial = null
var updating : bool = false
var update_again : bool = false
func _ready() -> void:
material = ShaderMaterial.new()
material.shader = Shader.new()
if !parameters.has("size"):
parameters.size = 9
add_to_group("preview")
func get_type() -> String:
return "buffer"
@ -31,20 +36,51 @@ func get_input_defs() -> Array:
func get_output_defs() -> Array:
return [ { type="rgba" }, { type="rgba" } ]
func source_changed(input_port_index : int) -> void:
updated = false
.source_changed(input_port_index)
func source_changed(_input_port_index : int) -> void:
if !is_inside_tree():
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)
if source != null and !updated:
if source != null:
var result = source.generator.render(source.output_index, pow(2, parameters.size))
while result is GDScriptFunctionState:
result = yield(result, "completed")
result.copy_to_texture(texture)
result.release()
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)
while rv is GDScriptFunctionState:
rv = yield(rv, "completed")

View File

@ -5,7 +5,11 @@ class_name MMGenMaterial
var export_paths = {}
var material : SpatialMaterial
var shader_materials = {}
var need_update = {}
var generated_textures = {}
var updating : bool = false
var update_again : bool = false
const TEXTURE_LIST = [
{ port=0, texture="albedo", sources=[0] },
@ -38,7 +42,11 @@ const TEXTURE_SIZE_DEFAULT = 10 # 1024x1024
func _ready() -> void:
for t in TEXTURE_LIST:
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()
add_to_group("preview")
func can_be_deleted() -> bool:
return false
@ -74,34 +82,63 @@ func set_parameter(p, v) -> void:
func source_changed(input_index : int) -> void:
for t in TEXTURE_LIST:
if t.has("sources") and t.sources.find(input_index) != -1:
generated_textures[t.texture] = null
need_update[t.texture] = true
update_preview()
func render_textures() -> void:
for t in TEXTURE_LIST:
var texture = null
var result
if t.has("port"):
if generated_textures[t.texture] != null:
if !need_update[t.texture]:
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:
generated_textures[t.texture] = null
need_update[t.texture] = false
continue
while result is GDScriptFunctionState:
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.release()
# To work, this must be set after calling `copy_to_texture()`
texture.flags |= ImageTexture.FLAG_ANISOTROPIC_FILTER
# Disable filtering for small textures, as they're considered to be used
# for a pixel art style
if texture.get_size().x <= 128:
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:
render_textures()

View File

@ -5,19 +5,24 @@ class_name MMGenRenderer
export(String) var debug_path = ""
var debug_file_index : int = 0
var common_shader : String
var rendering : bool = false
signal done
func _ready() -> void:
$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
code = "shader_type canvas_item;\n"
code += "render_mode blend_disabled;\n"
var file = File.new()
file.open("res://addons/material_maker/common.shader", File.READ)
code += file.get_as_text()
code += common_shader
code += "\n"
if src_code.has("textures"):
for t in src_code.textures.keys():
@ -38,13 +43,11 @@ static func generate_shader(src_code) -> String:
code += shader_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
code = "shader_type canvas_item;\n"
code += "render_mode blend_disabled;\n"
var file = File.new()
file.open("res://addons/material_maker/common.shader", File.READ)
code += file.get_as_text()
code += common_shader
code += "\n"
var globals = []
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.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:
if debug_path != null and debug_path != "":
var file_name = debug_path+str(debug_file_index)+".shader"

View File

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

View File

@ -5,15 +5,12 @@ export(String, MULTILINE) var shader : String = ""
var generator : MMGenBase = null
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" }
if is_instance_valid(g):
generator = g
var param_defs : Array = generator.get_parameter_defs()
for c in get_children():
c.setup_control(generator, param_defs)
generator.connect("float_param_changed", self, "on_float_param_changed")
var gen_output_defs = generator.get_output_defs()
if ! gen_output_defs.empty():
var context : MMGenContext = MMGenContext.new()
@ -27,6 +24,9 @@ func set_generator(g : MMGenBase) -> void:
for c in get_children():
c.setup_control(generator, [])
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:
material.set_shader_param(n, v)

View File

@ -1,9 +1,5 @@
extends ViewportContainer
const ENVIRONMENTS = [
"experiment", "lobby", "night", "park", "schelde"
]
const CAMERA_DISTANCE_MIN = 1.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_scene.tscn" type="PackedScene" id=2]
[sub_resource type="World" id=1]
[node name="Preview3D" type="ViewportContainer"]
[node name="Preview3D" type="ViewportContainer" groups=[
"preview",
]]
visible = false
anchor_right = 1.0
anchor_bottom = 1.0