1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-02-23 14:03:13 +00:00

Compare commits

..

2 commits

Author SHA1 Message Date
Variable
4a7f7cbde5
Added a way to modify shader textures (#1096)
* experimental support for texture changes

* fix some typos

* formatting + some improvements

* Some final touches, i think it's ready now

* moved a function to the shaderloader

* formatting

* add a void
2024-09-03 03:38:56 +03:00
Variable
fa9536ce4a
Fix typos and stuff (#1099) 2024-09-03 00:20:50 +03:00
5 changed files with 111 additions and 29 deletions

View file

@ -162,14 +162,17 @@ class GeneralAPI:
func get_canvas() -> Canvas: func get_canvas() -> Canvas:
return Global.canvas return Global.canvas
## Returns a new ValueSlider. Useful for editing floating values
func create_value_slider() -> ValueSlider: func create_value_slider() -> ValueSlider:
return ValueSlider.new() return ValueSlider.new()
## Returns a new ValueSliderV2. Useful for editing 2D vectors.
func create_value_slider_v2() -> ValueSliderV2: func create_value_slider_v2() -> ValueSliderV2:
return ValueSliderV2.new() return preload("res://src/UI/Nodes/ValueSliderV2.tscn").instantiate()
## Returns a new ValueSliderV3. Useful for editing 3D vectors.
func create_value_slider_v3() -> ValueSliderV3: func create_value_slider_v3() -> ValueSliderV3:
return ValueSliderV3.new() return preload("res://src/UI/Nodes/ValueSliderV3.tscn").instantiate()
## Gives ability to add/remove items from menus in the top bar. ## Gives ability to add/remove items from menus in the top bar.
@ -238,7 +241,7 @@ class DialogAPI:
func get_dialogs_parent_node() -> Node: func get_dialogs_parent_node() -> Node:
return Global.control.get_node("Dialogs") return Global.control.get_node("Dialogs")
## Tells pixelorama that some dialog is about to open or close. ## Informs Pixelorama that some dialog is about to open or close.
func dialog_open(open: bool) -> void: func dialog_open(open: bool) -> void:
Global.dialog_open(open) Global.dialog_open(open)
@ -255,7 +258,7 @@ class PanelAPI:
return dockable.tabs_visible return dockable.tabs_visible
## Adds the [param node] as a tab. Initially it's placed on the same panel as the tools tab, ## Adds the [param node] as a tab. Initially it's placed on the same panel as the tools tab,
## but can be changed through adding custom layouts. ## but it's position can be changed through editing a layout.
func add_node_as_tab(node: Node) -> void: func add_node_as_tab(node: Node) -> void:
var dockable := _get_dockable_container_ui() var dockable := _get_dockable_container_ui()
var top_menu_container := Global.top_menu_container var top_menu_container := Global.top_menu_container
@ -401,7 +404,7 @@ class ThemeAPI:
push_error("No theme found at index: ", idx) push_error("No theme found at index: ", idx)
return false return false
## Remove the [param theme] from preferences. ## Removes the [param theme] from preferences.
func remove_theme(theme: Theme) -> void: func remove_theme(theme: Theme) -> void:
Themes.remove_theme(theme) Themes.remove_theme(theme)
ExtensionsApi.remove_action("ThemeAPI", "add_theme") ExtensionsApi.remove_action("ThemeAPI", "add_theme")
@ -417,9 +420,9 @@ class ToolAPI:
## on [param layer_types] defined by [constant LayerTypes], ## on [param layer_types] defined by [constant LayerTypes],
## [param extra_hint] (text that appears when mouse havers tool icon), primary shortcut ## [param extra_hint] (text that appears when mouse havers tool icon), primary shortcut
## name [param shortcut] and any extra shortcuts [param extra_shortcuts]. ## name [param shortcut] and any extra shortcuts [param extra_shortcuts].
## [br][br]At the moment extensions can't make their own shortcuts so you can ignore ## [br][br]At the moment extensions can't make their own shortcuts so you can leave
## [param shortcut] and [param extra_shortcuts]. ## [param shortcut] and [param extra_shortcuts] as [code][][/code].
## [br] to determine the position of tool in tool list, use [param insert_point] ## [br] To determine the position of tool in tool list, use [param insert_point]
## (if you leave it empty then the added tool will be placed at bottom) ## (if you leave it empty then the added tool will be placed at bottom)
func add_tool( func add_tool(
tool_name: String, tool_name: String,
@ -527,14 +530,14 @@ class SelectionAPI:
func paste(in_place := false) -> void: func paste(in_place := false) -> void:
Global.canvas.selection.paste(in_place) Global.canvas.selection.paste(in_place)
## Deletes the drawing on current cel enclosed within the selection's area. ## Erases the drawing on current cel enclosed within the selection's area.
func delete_content(selected_cels := true) -> void: func delete_content(selected_cels := true) -> void:
Global.canvas.selection.delete(selected_cels) Global.canvas.selection.delete(selected_cels)
## Gives access to basic project manipulation functions. ## Gives access to basic project manipulation functions.
class ProjectAPI: class ProjectAPI:
## The project currently in focus ## The project currently in focus.
var current_project: Project: var current_project: Project:
set(value): set(value):
Global.tabs.current_tab = Global.projects.find(value) Global.tabs.current_tab = Global.projects.find(value)
@ -575,9 +578,9 @@ class ProjectAPI:
func get_project_info(project: Project) -> Dictionary: func get_project_info(project: Project) -> Dictionary:
return project.serialize() return project.serialize()
## Selects the cels and makes the last entry of [param selected_array] as the current cel ## Selects the cels and makes the last entry of [param selected_array] as the current cel.
## [param selected_array] is an [Array] of [Arrays] of 2 integers (frame & layer).[br] ## [param selected_array] is an [Array] of [Arrays] of 2 integers (frame & layer respectively).
## Frames are counted from left to right, layers are counted from bottom to top. ## [br]Frames are counted from left to right, layers are counted from bottom to top.
## Frames/layers start at "0" and end at [param project.frames.size() - 1] and ## Frames/layers start at "0" and end at [param project.frames.size() - 1] and
## [param project.layers.size() - 1] respectively. ## [param project.layers.size() - 1] respectively.
func select_cels(selected_array := [[0, 0]]) -> void: func select_cels(selected_array := [[0, 0]]) -> void:
@ -664,7 +667,8 @@ class ExportAPI:
## (Note: [code]processed_images[/code] is an array of ProcessedImage resource which further ## (Note: [code]processed_images[/code] is an array of ProcessedImage resource which further
## has parameters [param image] and [param duration])[br] ## has parameters [param image] and [param duration])[br]
## If the value of [param tab] is not in [constant ExportTab] then the format will be added to ## If the value of [param tab] is not in [constant ExportTab] then the format will be added to
## both tabs. Returns the index of exporter, which can be used to remove exporter later. ## both tabs.
## [br]Returns the index of exporter, which can be used to remove exporter later.
func add_export_option( func add_export_option(
format_info: Dictionary, format_info: Dictionary,
exporter_generator: Object, exporter_generator: Object,
@ -704,17 +708,17 @@ class ExportAPI:
## Gives access to adding custom import options. ## Gives access to adding custom import options.
class ImportAPI: class ImportAPI:
## [param import_scene] is a scene preload that will be instanced and added to "import options" ## [param import_scene] is a scene preload that will be instanced and added to "import options"
## section of pixelorama's import dialogs and will appears whenever [param import_name] is ## section of pixelorama's import dialogs and will appear whenever [param import_name] is
## chosen from import menu. ## chosen from import menu.
## [br] ## [br]
## [param import_scene] must have a a script containing:[br] ## [param import_scene] must have a script containing:[br]
## 1. An optional variable named [code]import_preview_dialog[/code] of type [ConfirmationDialog], ## 1. An optional variable named [code]import_preview_dialog[/code] of type [ConfirmationDialog],
## If present, it will automatically be assigned a reference to the relevant import dialog's ## If present, it will automatically be assigned a reference to the relevant import dialog's
## [code]ImportPreviewDialog[/code] class so that you can easily access variables and ## [code]ImportPreviewDialog[/code] class so that you can easily access variables and
## methods of that class. (This variable is meant to be read-only)[br] ## methods of that class. (This variable is meant to be read-only)[br]
## 2. The method [method initiate_import] which takes 2 arguments: [code]path[/code], ## 2. The method [method initiate_import], which takes 2 arguments: [code]path[/code],
## [code]image[/code], which are automatically passed to [method initiate_import] at ## [code]image[/code]. Values will automatically be passed to these arguments at the
## time of import. ## time of import.[br]Returns the id of the importer.
func add_import_option(import_name: StringName, import_scene_preload: PackedScene) -> int: func add_import_option(import_name: StringName, import_scene_preload: PackedScene) -> int:
var id := OpenSave.add_import_option(import_name, import_scene_preload) var id := OpenSave.add_import_option(import_name, import_scene_preload)
ExtensionsApi.add_action("ImportAPI", "add_import_option") ExtensionsApi.add_action("ImportAPI", "add_import_option")
@ -728,11 +732,11 @@ class ImportAPI:
ExtensionsApi.remove_action("ImportAPI", "add_import_option") ExtensionsApi.remove_action("ImportAPI", "add_import_option")
## Gives access to palettes. ## Gives access to palette related stuff.
class PaletteAPI: class PaletteAPI:
## Creates and adds a new [Palette] with name [param palette_name] with [param data] ## Creates and adds a new [Palette] with name [param palette_name] containing [param data].
## in the form of a [Dictionary]. ## [param data] is a [Dictionary] containing the palette information.
## An example of [code]data[/code] dictionary will be:[codeblock] ## An example of [code]data[/code] will be:[codeblock]
## { ## {
## "colors": [ ## "colors": [
## { ## {
@ -811,7 +815,8 @@ class SignalsAPI:
# PROJECT RELATED SIGNALS # PROJECT RELATED SIGNALS
## Connects/disconnects a signal to [param callable], that emits ## Connects/disconnects a signal to [param callable], that emits
## whenever a new project is created.[br] ## whenever a new project is created.[br]
## [b]Binds: [/b]It has one bind of type [code]Project[/code] which is the newly created project ## [b]Binds: [/b]It has one bind of type [code]Project[/code] which is the newly
## created project.
func signal_project_created(callable: Callable, is_disconnecting := false) -> void: func signal_project_created(callable: Callable, is_disconnecting := false) -> void:
_connect_disconnect(Global.project_created, callable, is_disconnecting) _connect_disconnect(Global.project_created, callable, is_disconnecting)
@ -867,7 +872,7 @@ class SignalsAPI:
## Connects/disconnects a signal to [param callable], that emits ## Connects/disconnects a signal to [param callable], that emits
## whenever preview is about to be drawn.[br] ## whenever preview is about to be drawn.[br]
## [b]Binds: [/b]It has one bind of type [Dictionary] with keys: [code]exporter_id[/code], ## [b]Binds: [/b]It has one bind of type [Dictionary] with keys: [code]exporter_id[/code],
## [code]export_tab[/code], [code]preview_images[/code], [code]durations[/code] ## [code]export_tab[/code], [code]preview_images[/code], [code]durations[/code].[br]
## [br] Use this if you plan on changing preview of export ## Use this if you plan on changing preview of export.
func signal_export_about_to_preview(callable: Callable, is_disconnecting := false) -> void: func signal_export_about_to_preview(callable: Callable, is_disconnecting := false) -> void:
_connect_disconnect(Global.export_dialog.about_to_preview, callable, is_disconnecting) _connect_disconnect(Global.export_dialog.about_to_preview, callable, is_disconnecting)

View file

@ -132,7 +132,18 @@ var config_cache := ConfigFile.new()
var loaded_locales: PackedStringArray = LANGUAGES_DICT.keys() var loaded_locales: PackedStringArray = LANGUAGES_DICT.keys()
var projects: Array[Project] = [] ## Array of currently open projects. var projects: Array[Project] = [] ## Array of currently open projects.
var current_project: Project ## The project that currently in focus. var current_project: Project: ## The project that currently in focus.
set(value):
current_project = value
if top_menu_container.file_menu:
if current_project is ResourceProject:
top_menu_container.file_menu.set_item_disabled(FileMenu.SAVE_AS, true)
top_menu_container.file_menu.set_item_disabled(FileMenu.EXPORT, true)
top_menu_container.file_menu.set_item_disabled(FileMenu.EXPORT_AS, true)
else:
top_menu_container.file_menu.set_item_disabled(FileMenu.SAVE_AS, false)
top_menu_container.file_menu.set_item_disabled(FileMenu.EXPORT, false)
top_menu_container.file_menu.set_item_disabled(FileMenu.EXPORT_AS, false)
## The index of project that is currently in focus. ## The index of project that is currently in focus.
var current_project_index := 0: var current_project_index := 0:
set(value): set(value):

View file

@ -0,0 +1,8 @@
class_name ResourceProject
extends Project
signal resource_updated
func _init(_frames: Array[Frame] = [], _name := tr("untitled"), _size := Vector2i(64, 64)) -> void:
super._init(_frames, _name + " (Virtual Resource)", _size)

View file

@ -212,7 +212,7 @@ static func create_ui_for_shader_uniforms(
func(_gradient, _cc): value_changed.call(gradient_edit.texture, u_name) func(_gradient, _cc): value_changed.call(gradient_edit.texture, u_name)
) )
hbox.add_child(gradient_edit) hbox.add_child(gradient_edit)
else: else: ## Simple texture
var file_dialog := FileDialog.new() var file_dialog := FileDialog.new()
file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
file_dialog.access = FileDialog.ACCESS_FILESYSTEM file_dialog.access = FileDialog.ACCESS_FILESYSTEM
@ -224,7 +224,19 @@ static func create_ui_for_shader_uniforms(
button.pressed.connect(file_dialog.popup_centered) button.pressed.connect(file_dialog.popup_centered)
button.size_flags_horizontal = Control.SIZE_EXPAND_FILL button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
var mod_button := Button.new()
mod_button.text = "Modify"
mod_button.pressed.connect(
func(): _modify_texture_resource(
_get_loaded_texture(params, u_name),
u_name,
_shader_update_texture.bind(value_changed, u_name)
)
)
mod_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
mod_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
hbox.add_child(button) hbox.add_child(button)
hbox.add_child(mod_button)
parent_node.add_child(file_dialog) parent_node.add_child(file_dialog)
parent_node.add_child(hbox) parent_node.add_child(hbox)
@ -334,3 +346,43 @@ static func _shader_update_palette_texture(
palette: Palette, value_changed: Callable, parameter_name: String palette: Palette, value_changed: Callable, parameter_name: String
) -> void: ) -> void:
value_changed.call(ImageTexture.create_from_image(palette.convert_to_image()), parameter_name) value_changed.call(ImageTexture.create_from_image(palette.convert_to_image()), parameter_name)
static func _get_loaded_texture(params: Dictionary, parameter_name: String) -> Image:
if parameter_name in params:
if params[parameter_name] is ImageTexture:
return params[parameter_name].get_image()
var image = Image.create_empty(64, 64, false, Image.FORMAT_RGBA8)
return image
static func _shader_update_texture(
resource_proj: ResourceProject, value_changed: Callable, parameter_name: String
) -> void:
var warnings = ""
if resource_proj.frames.size() > 1:
warnings += "This resource is intended to have 1 frame only. Extra frames will be ignored."
if resource_proj.layers.size() > 1:
warnings += "\nThis resource is intended to have 1 layer only. layers will be blended."
var updated_image = Image.create_empty(
resource_proj.size.x, resource_proj.size.y, false, Image.FORMAT_RGBA8
)
var frame = resource_proj.frames[0]
DrawingAlgos.blend_layers(updated_image, frame, Vector2i.ZERO, resource_proj)
value_changed.call(ImageTexture.create_from_image(updated_image), parameter_name)
if not warnings.is_empty():
Global.popup_error(warnings)
static func _modify_texture_resource(
image: Image, resource_name: StringName, update_callable: Callable
) -> void:
var resource_proj = ResourceProject.new([], resource_name, image.get_size())
resource_proj.layers.append(PixelLayer.new(resource_proj))
resource_proj.frames.append(resource_proj.new_empty_frame())
resource_proj.frames[0].cels[0].set_content(image)
resource_proj.resource_updated.connect(update_callable)
Global.projects.append(resource_proj)
Global.tabs.current_tab = Global.tabs.get_tab_count() - 1
Global.canvas.camera_zoom()

View file

@ -545,6 +545,12 @@ func _on_open_last_project_file_menu_option_pressed() -> void:
func _save_project_file() -> void: func _save_project_file() -> void:
if Global.current_project is ResourceProject:
Global.current_project.resource_updated.emit(Global.current_project)
if Global.current_project.has_changed:
Global.current_project.has_changed = false
Global.notification_label("Resource Updated")
return
var path: String = Global.current_project.save_path var path: String = Global.current_project.save_path
if path == "": if path == "":
Global.control.show_save_dialog() Global.control.show_save_dialog()