Add //!steps directive, some code cleanup, updated README & examples
This commit is contained in:
parent
1b87cafb13
commit
1d9f838668
7 changed files with 89 additions and 50 deletions
|
@ -4,15 +4,10 @@ extends SubViewport
|
|||
@onready var image_sprite = %ImageSprite
|
||||
@onready var image_viewport_display = %ImageViewportDisplay
|
||||
|
||||
var _fragment_function_regex: RegEx
|
||||
var _fragment_function_regex: RegEx = RegEx.create_from_string(r'\s*void\s+fragment\s*\(\s*\)\s*{\s*')
|
||||
|
||||
func _init():
|
||||
_fragment_function_regex = RegEx.new()
|
||||
_fragment_function_regex.compile(r'\s*void\s+fragment\s*\(\s*\)\s*{\s*')
|
||||
|
||||
func validate_shader_compilation() -> bool:
|
||||
func validate_shader_compilation(shader: Shader) -> bool:
|
||||
# Inject code to validate shader compilation
|
||||
var shader: Shader = Filesystem.shader.duplicate()
|
||||
var shader_code = shader.code;
|
||||
# -> get position of fragment shader
|
||||
var fragment_function_match = _fragment_function_regex.search(shader.code)
|
||||
|
@ -29,19 +24,28 @@ func validate_shader_compilation() -> bool:
|
|||
# test if uniform list is empty -> if it is empty, the shader compilation failed
|
||||
return len(shader.get_shader_uniform_list()) > 0
|
||||
|
||||
func inject_step_uniform(shader_code: String) -> Shader:
|
||||
var shader = Shader.new()
|
||||
# this should run after validate_shader_compilation()
|
||||
var fragment_function_match = _fragment_function_regex.search(shader_code)
|
||||
shader.code = shader_code.insert(fragment_function_match.get_start(), "\nuniform int STEP;")
|
||||
return shader
|
||||
|
||||
func update() -> Array: # returns error messages (strings)
|
||||
if not validate_shader_compilation():
|
||||
# inject STEP uniform & get number of steps
|
||||
var shader: Shader = inject_step_uniform(Filesystem.shader_code)
|
||||
var step: int = ShaderDirectiveParser.parse_steps_directive(shader)
|
||||
# validate shader
|
||||
if not validate_shader_compilation(shader):
|
||||
return ["Shader compilation failed!"]
|
||||
var errors = []
|
||||
var fit_image = false
|
||||
# load texture(s)
|
||||
Filesystem.clear_additional_images()
|
||||
# ... from //!load directive -> TEXTURE
|
||||
var m = ShaderDirectiveParser.parse_load_directive(Filesystem.shader)
|
||||
# load texture(s) from //!load directive -> TEXTURE
|
||||
var m = ShaderDirectiveParser.parse_load_directive(shader)
|
||||
if len(m) < 1:
|
||||
errors.append("Didn't find a load directive!")
|
||||
return errors
|
||||
var original_image_path = Filesystem.get_absolute_path(m[1])
|
||||
var fit_image = false
|
||||
if original_image_path != Filesystem.last_original_image_path:
|
||||
fit_image = true
|
||||
var err = Filesystem.load_original_image(original_image_path)
|
||||
|
@ -50,7 +54,8 @@ func update() -> Array: # returns error messages (strings)
|
|||
image_viewport_display.hide()
|
||||
return errors
|
||||
# ... from //!load+ directives
|
||||
for n in ShaderDirectiveParser.parse_load_additional_directive(Filesystem.shader):
|
||||
Filesystem.clear_additional_images()
|
||||
for n in ShaderDirectiveParser.parse_load_additional_directive(shader):
|
||||
err = Filesystem.load_additional_image(n[1], Filesystem.get_absolute_path(n[2]))
|
||||
if err != "":
|
||||
errors.append(err)
|
||||
|
@ -60,22 +65,27 @@ func update() -> Array: # returns error messages (strings)
|
|||
image_sprite.texture = Filesystem.original_image
|
||||
image_sprite.offset = Filesystem.original_image.get_size() / 2
|
||||
self.size = Filesystem.original_image.get_size()
|
||||
# create shader material
|
||||
var mat = ShaderMaterial.new()
|
||||
mat.shader = Filesystem.shader
|
||||
# ... as shader parameters
|
||||
mat.shader = shader
|
||||
# add images as shader parameters
|
||||
for key in Filesystem.additional_images:
|
||||
mat.set_shader_parameter(
|
||||
key, # uniform param name
|
||||
Filesystem.additional_images[key])
|
||||
# assign material
|
||||
image_sprite.material = mat
|
||||
# Get viewport texture
|
||||
await RenderingServer.frame_post_draw # for good measure
|
||||
Filesystem.result = get_texture().get_image()
|
||||
image_sprite.material = null
|
||||
image_sprite.texture = ImageTexture.create_from_image(Filesystem.result)
|
||||
if fit_image:
|
||||
camera.fit_image()
|
||||
# iterate n times
|
||||
for i in range(step):
|
||||
# set STEP param
|
||||
mat.set_shader_parameter("STEP", i)
|
||||
# assign material
|
||||
image_sprite.material = mat
|
||||
# Get viewport texture
|
||||
await RenderingServer.frame_post_draw # for good measure
|
||||
Filesystem.result = get_texture().get_image()
|
||||
image_sprite.material = null
|
||||
image_sprite.texture = ImageTexture.create_from_image(Filesystem.result)
|
||||
if fit_image:
|
||||
camera.fit_image()
|
||||
camera.update_vd_zoomlevel()
|
||||
image_viewport_display.show()
|
||||
# done
|
||||
|
|
|
@ -208,7 +208,7 @@ func _input(event):
|
|||
_on_save_shader_button_pressed()
|
||||
|
||||
func update_code_edit():
|
||||
code_editor.text = Filesystem.shader.code
|
||||
code_editor.text = Filesystem.shader_code
|
||||
|
||||
enum Status {OKAY, ERROR, UNKNOWN = -1}
|
||||
|
||||
|
@ -239,14 +239,14 @@ func _on_open_shader_button_pressed():
|
|||
open_shader_dialog.show()
|
||||
|
||||
func _on_save_shader_button_pressed():
|
||||
Filesystem.shader.code = code_editor.text
|
||||
Filesystem.shader_code = code_editor.text
|
||||
if Filesystem.last_shader_savepath == "":
|
||||
_on_save_shader_as_button_pressed()
|
||||
else:
|
||||
_on_save_shader_dialog_file_selected(Filesystem.last_shader_savepath)
|
||||
|
||||
func _on_save_shader_as_button_pressed() -> void:
|
||||
Filesystem.shader.code = code_editor.text
|
||||
Filesystem.shader_code = code_editor.text
|
||||
if Filesystem.last_shader_savepath == "":
|
||||
save_shader_dialog.current_file = "filter.gdshader"
|
||||
else:
|
||||
|
@ -257,7 +257,7 @@ func _on_fit_image_button_pressed():
|
|||
camera.fit_image()
|
||||
|
||||
func _on_apply_shader_button_pressed():
|
||||
Filesystem.shader.code = code_editor.text
|
||||
Filesystem.shader_code = code_editor.text
|
||||
var errors = await compositor.update()
|
||||
if len(errors) > 0:
|
||||
update_status(Status.ERROR, "\n".join(errors))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
extends Node
|
||||
|
||||
@onready var template_shader: Shader = load("res://src/shader/template.gdshader")
|
||||
@onready var shader: Shader = template_shader.duplicate()
|
||||
@onready var shader_code: String = template_shader.code
|
||||
|
||||
var original_image: ImageTexture
|
||||
var additional_images: Dictionary
|
||||
|
@ -13,7 +13,7 @@ var last_shader_savepath = ""
|
|||
var last_original_image_path = ""
|
||||
|
||||
func reset():
|
||||
self.shader = self.template_shader.duplicate()
|
||||
self.shader_code = self.template_shader.code
|
||||
self.last_image_savepath = ""
|
||||
self.last_shader_savepath = ""
|
||||
self.last_original_image_path = ""
|
||||
|
@ -61,9 +61,7 @@ func save_result(path: String):
|
|||
func load_shader(path: String):
|
||||
print("Load ", path)
|
||||
var file = FileAccess.open(path, FileAccess.READ)
|
||||
var shader_code = file.get_as_text()
|
||||
self.shader = Shader.new()
|
||||
shader.code = shader_code
|
||||
self.shader_code = file.get_as_text()
|
||||
if "/" in path: # update current working directory
|
||||
self.cwd = path.substr(0, path.rfind("/"))
|
||||
self.last_shader_savepath = path
|
||||
|
@ -71,7 +69,7 @@ func load_shader(path: String):
|
|||
func save_shader(path: String):
|
||||
print("Save ", path)
|
||||
var file = FileAccess.open(path, FileAccess.WRITE)
|
||||
file.store_string(self.shader.code)
|
||||
file.store_string(self.shader_code)
|
||||
if "/" in path: # update current working directory
|
||||
self.cwd = path.substr(0, path.rfind("/"))
|
||||
self.last_shader_savepath = path
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
extends Node
|
||||
|
||||
var _load_regex: RegEx
|
||||
var _load_additional_regex: RegEx
|
||||
|
||||
func _ready():
|
||||
self._load_regex = RegEx.new()
|
||||
self._load_additional_regex = RegEx.new()
|
||||
self._load_regex.compile(r'\/\/!load\s(.*)')
|
||||
self._load_additional_regex.compile(r'\/\/!load\+\s(\w*)\s(.*)')
|
||||
var _load_regex: RegEx = RegEx.create_from_string(r'\/\/!load\s(.*)')
|
||||
var _load_additional_regex: RegEx = RegEx.create_from_string(r'\/\/!load\+\s(\w*)\s(.*)')
|
||||
var _iterate_regex: RegEx = RegEx.create_from_string(r'\/\/!steps\s([0-9]+)\s*')
|
||||
|
||||
func parse_load_directive(shader: Shader) -> PackedStringArray:
|
||||
var regex_match = self._load_regex.search(Filesystem.shader.code)
|
||||
var regex_match = self._load_regex.search(shader.code)
|
||||
if regex_match == null:
|
||||
return []
|
||||
return regex_match.strings
|
||||
|
@ -20,3 +15,10 @@ func parse_load_additional_directive(shader: Shader) -> Array[PackedStringArray]
|
|||
for m in self._load_additional_regex.search_all(shader.code):
|
||||
results.append(m.strings)
|
||||
return results
|
||||
|
||||
func parse_steps_directive(shader: Shader) -> int:
|
||||
var regex_match = self._iterate_regex.search(shader.code)
|
||||
if regex_match == null:
|
||||
return 1
|
||||
else:
|
||||
return max(0, int(regex_match.strings[1]))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue