2025-10-10 22:05:07 +02:00
|
|
|
class_name ImageCompositor extends SubViewport
|
|
|
|
|
|
|
|
var image_sprite: Sprite2D
|
|
|
|
|
|
|
|
func _init() -> void:
|
|
|
|
# Overwrite some variables
|
|
|
|
self.render_target_update_mode = SubViewport.UPDATE_ALWAYS
|
|
|
|
self.disable_3d = true
|
|
|
|
self.transparent_bg = true
|
|
|
|
self.canvas_item_default_texture_filter = Viewport.DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST
|
|
|
|
self.image_sprite = Sprite2D.new()
|
|
|
|
|
|
|
|
@onready var camera = %Camera
|
|
|
|
@onready var image_viewport_display = %ImageViewportDisplay
|
|
|
|
|
|
|
|
func _ready() -> void:
|
|
|
|
# Add image sprite as child to be rendered
|
|
|
|
self.add_child(image_sprite)
|
|
|
|
|
|
|
|
var _fragment_function_regex: RegEx = RegEx.create_from_string(r'\s*void\s+fragment\s*\(\s*\)\s*{\s*')
|
|
|
|
|
|
|
|
func validate_shader_compilation(shader: Shader) -> bool:
|
|
|
|
# Inject code to validate shader compilation
|
|
|
|
var shader_code = shader.code;
|
|
|
|
# -> get position of fragment shader
|
|
|
|
var fragment_function_match = _fragment_function_regex.search(shader.code)
|
|
|
|
if fragment_function_match == null:
|
|
|
|
return false
|
|
|
|
# -> inject uniform
|
|
|
|
var uniform_name = "shader_compilation_validate_" + str(randi_range(999999999, 100000000))
|
|
|
|
var uniform_code_line = "\nuniform bool " + uniform_name + ";\n"
|
|
|
|
shader_code = shader_code.insert(fragment_function_match.get_start(), uniform_code_line)
|
|
|
|
# -> inject variable access to prevent that the uniform gets optimized away
|
|
|
|
shader_code = shader_code.insert(fragment_function_match.get_end() + len(uniform_code_line), "\n" + uniform_name + ";\n")
|
|
|
|
# apply shader code
|
|
|
|
shader.code = shader_code
|
|
|
|
# test if uniform list is empty -> if it is empty, the shader compilation failed
|
|
|
|
return len(shader.get_shader_uniform_list()) > 0
|
|
|
|
|
|
|
|
func shader_has_uniform(shader: Shader, var_name: String, type: int) -> bool:
|
|
|
|
for u in shader.get_shader_uniform_list():
|
|
|
|
if u["name"] == var_name && u["type"] == type:
|
|
|
|
return true
|
|
|
|
return false
|
|
|
|
|
|
|
|
func set_vsync(enabled: bool):
|
|
|
|
if enabled:
|
|
|
|
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED)
|
|
|
|
else:
|
|
|
|
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
|
|
|
|
|
|
|
|
func update() -> Array: # returns error messages (strings)
|
2025-10-10 22:33:29 +02:00
|
|
|
if Filesystem.input_image_path == "":
|
2025-10-10 22:05:07 +02:00
|
|
|
return ["No image loaded!"]
|
|
|
|
var shader = Filesystem.shader # read from disk
|
|
|
|
if shader == null:
|
|
|
|
return ["No shader opened!"]
|
|
|
|
# validate shader
|
|
|
|
if not validate_shader_compilation(shader):
|
|
|
|
return ["Shader compilation failed!"]
|
|
|
|
var errors = []
|
|
|
|
# load texture
|
2025-10-10 22:33:29 +02:00
|
|
|
var fit_image = Filesystem.input_image_path != Filesystem.last_input_image_path
|
2025-10-10 22:05:07 +02:00
|
|
|
var err = Filesystem.load_image()
|
|
|
|
if err != "":
|
|
|
|
errors.append(err)
|
|
|
|
image_viewport_display.hide()
|
|
|
|
return errors
|
|
|
|
# apply texture
|
2025-10-10 22:33:29 +02:00
|
|
|
image_sprite.texture = Filesystem.input_image_texture
|
|
|
|
image_sprite.offset = Filesystem.input_image_texture.get_size() / 2
|
|
|
|
self.size = Filesystem.input_image_texture.get_size()
|
2025-10-10 22:05:07 +02:00
|
|
|
# show the image viewport & fit the image
|
|
|
|
if fit_image: camera.fit_image()
|
|
|
|
image_viewport_display.show()
|
|
|
|
# create shader material
|
|
|
|
var mat = ShaderMaterial.new()
|
|
|
|
mat.shader = shader
|
|
|
|
# assign material
|
|
|
|
image_sprite.material = mat
|
|
|
|
# iterate n times
|
|
|
|
set_vsync(false) # speed up processing
|
|
|
|
# Get viewport texture
|
|
|
|
await RenderingServer.frame_post_draw # wait for next frame to get drawn
|
|
|
|
Filesystem.result = get_texture().get_image()
|
|
|
|
image_sprite.texture = ImageTexture.create_from_image(Filesystem.result)
|
|
|
|
set_vsync(true) # reenable vsync
|
|
|
|
image_sprite.material = null
|
|
|
|
# done
|
|
|
|
return errors
|