diff --git a/plugins/.gitignore b/plugins/.gitignore index 0e3782ac..2970bf63 100644 --- a/plugins/.gitignore +++ b/plugins/.gitignore @@ -1,2 +1,7 @@ -# "Silly Material" files written by the editor plugin -*.silly_mat +# "Silly Material" files written by the editor plugin. +# The example files already tracked by Git are not affected. +*.silly_mat_importable +*.silly_mat_importable.import +*.silly_mat_loadable +*.silly_mat_loadable.uid +*.tres diff --git a/plugins/README.md b/plugins/README.md index b5beffd2..ca0fce82 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -21,15 +21,17 @@ This project contains 4 plugins: * The custom node plugin shows how to create a custom node type using `add_custom_type`. [More info](addons/custom_node). -* The material import plugin shows how to make a plugin handle importing - a custom file type (mtxt). [More info](addons/material_import_plugin). - -* The material creator plugin shows how to add a custom dock with some - simple functionality. [More info](addons/material_creator). - * The main screen plugin is a minimal example of how to create a plugin with a main screen. [More info](addons/main_screen). +* The material creator plugin shows how to add a custom dock with some + simple functionality, and shows how to create a custom Resource type + with custom loading, saving, importing, and exporting logic, + including editor integrations. [More info](addons/material_creator). + +* The simple import plugin shows how to make a simple plugin handle importing + a custom file type (mtxt). [More info](addons/simple_import_plugin). + To use these plugins in another project, copy any of these folders to the `addons/` folder in a Godot project, and then enable them in the project settings menu. @@ -44,12 +46,10 @@ This can be done via the terminal: `zip -r custom_node.zip custom_node/*` ## Screenshots -![Heart Plugin](screenshots/heart_plugin.png) +![Heart Custom Node](screenshots/heart_custom_node.webp) -![Main Screen Plugin](screenshots/main_screen_plugin.png) +![Main Screen Plugin](screenshots/main_screen_plugin.webp) -![Material Import Plugin](screenshots/material_import_plugin.png) +![Material Creator Plugin](screenshots/material_creator_plugin_applied.webp) -![Material Creator Plugin 1](screenshots/material_creator_plugin_1.png) - -![Material Creator Plugin 2](screenshots/material_creator_plugin_2.png) +![Simple Import Plugin](screenshots/simple_import_plugin.webp) diff --git a/plugins/addons/custom_node/heart_icon.png b/plugins/addons/custom_node/heart_icon.png deleted file mode 100644 index 2eb819aa..00000000 Binary files a/plugins/addons/custom_node/heart_icon.png and /dev/null differ diff --git a/plugins/addons/custom_node/heart_icon.png.import b/plugins/addons/custom_node/heart_icon.png.import deleted file mode 100644 index 7729e40c..00000000 --- a/plugins/addons/custom_node/heart_icon.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c3c8tfihdvaw7" -path="res://.godot/imported/heart_icon.png-8f04adf78b3bd1a5c39f790588a1fa78.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/custom_node/heart_icon.png" -dest_files=["res://.godot/imported/heart_icon.png-8f04adf78b3bd1a5c39f790588a1fa78.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/plugins/addons/custom_node/heart_plugin.gd b/plugins/addons/custom_node/heart_plugin.gd index c0961bfc..8d8868a9 100644 --- a/plugins/addons/custom_node/heart_plugin.gd +++ b/plugins/addons/custom_node/heart_plugin.gd @@ -4,7 +4,8 @@ extends EditorPlugin func _enter_tree() -> void: # When this plugin node enters tree, add the custom type. - add_custom_type("Heart", "Node2D", preload("res://addons/custom_node/heart.gd"), preload("res://addons/custom_node/heart_icon.png")) + var icon: Texture2D = preload("res://addons/custom_node/heart.png") + add_custom_type("Heart", "Node2D", preload("res://addons/custom_node/heart.gd"), icon) func _exit_tree() -> void: diff --git a/plugins/addons/material_creator/README.md b/plugins/addons/material_creator/README.md index 7a94f0f3..57200cff 100644 --- a/plugins/addons/material_creator/README.md +++ b/plugins/addons/material_creator/README.md @@ -1,25 +1,142 @@ # Material Creator Plugin Demo -This plugin demo contains a custom material creation dock -inside the Godot editor. +This plugin demo demonstrates these things: -Custom docks are made of Control nodes, they run in the -editor, and any behavior must be done through `tool` scripts. -For more information, see this documentation article: -https://docs.godotengine.org/en/latest/tutorials/plugins/editor/making_plugins.html#a-custom-dock +- How to create a custom dock in the Godot editor with basic functionality. + Custom docks are made of Control nodes, they run in the + editor, and any behavior must be done through `@tool` scripts. + For more information, see this documentation article: + https://docs.godotengine.org/en/latest/tutorials/plugins/editor/making_plugins.html#a-custom-dock -## Features -- Adjust albedo color, metallic, and rouphness values interactively. -- Apply the generated material to selected 3D nodes in the editor. -- Save and load materials in two ways: - - `.silly_mat`: Custom Godot Resource type, handled by custom saver/loader - included in the plygin. - - `.mtxt`: Plain-text format. Useful for external editing or as an - interchange format. - - `.tres`: Standard Godot resource format (works without the custom - loader). +- How to create a custom Resource type, and provide logic for + serializing, deserializing, and converting this Resource type. -## Implementation notes -- `.silly_mat` format is registered through `SillyMatFormatSaver` and -`SillyMatFormatLoader` in the plugin. -- Custm docks are built from `Control` nodes and run as `@tool` scripts. +- Editor integration with classes for loading, saving, and importing + this Resource type, including optional import customization. + +For a more comprehensive example, see the GLTF module in Godot's source code. + +For a less comprehensive example, see the "simple_import_plugin" folder. + +## Importing vs Loading + +The custom Resource type in this demo is supplemented by two different +sets of editor classes: + +- [EditorImportPlugin](https://docs.godotengine.org/en/stable/classes/class_editorimportplugin.html) + in the `importers/` folder, which allow customizing how + files are imported into Godot as Resources of different + types and optionally with import settings. + Imported files have `.import` files generated next to them. + +- [ResourceFormatLoader](https://docs.godotengine.org/en/stable/classes/class_resourceformatloader.html) + and [ResourceFormatSaver](https://docs.godotengine.org/en/stable/classes/class_resourceformatsaver.html) + in the `load_and_save/` folder, which allow easily + editing files in the inspector and saving them back. + Resource files have `.uid` files generated next to them. + +These two approaches are mutually exclusive. +You may only use one approach at a time for a given file extension. +This demo showcases both by using 2 different file extensions. + +In actual projects, you should either choose [EditorImportPlugin](https://docs.godotengine.org/en/stable/classes/class_editorimportplugin.html)(s) +for a configurable import, OR [ResourceFormatLoader](https://docs.godotengine.org/en/stable/classes/class_resourceformatloader.html) +for a writeable resource load. +The choice depends on if you treat the file as an external source asset +which should be imported and may be customized at import +([EditorImportPlugin](https://docs.godotengine.org/en/stable/classes/class_editorimportplugin.html)), +or if you treat the file as an internal Godot resource meant to be natively +and directly edited within Godot +([ResourceFormatLoader](https://docs.godotengine.org/en/stable/classes/class_resourceformatloader.html)). + +For example, a glTF file may be generated by Blender, and therefore +is not intended to be edited directly in Godot, so it should use +[EditorImportPlugin](https://docs.godotengine.org/en/stable/classes/class_editorimportplugin.html). +Similarly, a PNG file is typically generated by an image editor, and Godot +needs to convert it to a different internal format, like a `.ctex` file +for a VRAM-compressed texture, so it should use +[EditorImportPlugin](https://docs.godotengine.org/en/stable/classes/class_editorimportplugin.html). +However, files like `.tres` and `.tscn` are Godot-native formats meant to be +edited directly in Godot, so they should use +[ResourceFormatLoader](https://docs.godotengine.org/en/stable/classes/class_resourceformatloader.html). + +Once you choose one approach, create scripts deriving the appropriate classes, +override their callback functions, and register them in your plugin's +[`*_plugin.gd`](material_plugin.gd) script. + +## Example Files + +The [`examples/`](examples/) folder contains several example files: + +- `blue.tres`: Directly saving a SillyMaterialResource using Godot's built-in `.tres` format, + without any custom loader/saver logic or import/export logic, available for all Resource types. + This can be edited in Godot's inspector and saved back. + +- `cyan.silly_mat_loadable`: Storing a SillyMaterialResource as a custom format, + such as via [ResourceFormatSaver](https://docs.godotengine.org/en/stable/classes/class_resourceformatsaver.html), which is loaded back using a custom [ResourceFormatLoader](https://docs.godotengine.org/en/stable/classes/class_resourceformatloader.html). + This can be edited in Godot's inspector and saved back. + +- `green_as_standard_mat.silly_mat_importable`: Storing a SillyMaterialResource as a custom format, + which is imported as a StandardMaterial3D using custom import/export logic. This shows how + importers can import files as any Resource type, converting custom files to data Godot can use. + Imported files are read-only and cannot be edited in the inspector. + +- `yellow.silly_mat_importable`: Storing a SillyMaterialResource as a custom format, + which is imported as a SillyMaterialResource using custom import/export logic. + Imported files are read-only and cannot be edited in the inspector. + +- `yellow_tinted_red.silly_mat_importable`: Storing a SillyMaterialResource as a custom format, + which is imported as a SillyMaterialResource using custom import/export logic. + + - This file has the same exact contents as `yellow.silly_mat_importable`, but the + corresponding `.import` file has a flag set to tint the albedo color towards red, + which makes the material appear orange instead of yellow. + This demonstrates how importers can use import settings to modify data during import. + After import, imported files are read-only and cannot be edited in the inspector. + + - If you try to load this file using "Load Imported Material (EditorImportPlugin)" in the editor, + or call `ResourceLoader.load()` in GDScript, it will load the imported version, which + includes the red tint, so the albedo color will be orange. If you try to import this + file using "Import Material (directly at runtime)" in the editor, or call + `SillyMaterialResource.read_from_file()` in GDScript, it will directly read the original + file's contents, ignoring the import process, so the albedo color will be yellow. + This demonstrates how files can be loaded within Godot's import process (editor only), + or bypass the import process entirely (works in editor and at runtime). + +![Material Creator Plugin Imported File is Read-Only](../../screenshots/material_creator_plugin_imported_file_is_read_only.webp) + +## Editor Buttons + +The material creator dock has these 6 buttons: + +- "Apply Material": Applies the current material to all selected MeshInstance3D nodes in the editor. + +- "Save Material (ResourceFormatSaver)": Saves the current + material to a `.silly_mat_loadable` file using the custom + [ResourceFormatSaver](https://docs.godotengine.org/en/stable/classes/class_resourceformatsaver.html), + or to a `.tres` file using Godot's built-in ResourceFormatSaverText. + This can be edited in Godot's inspector and saved back. + +- "Export Material (directly at runtime)": Exports the current material + to a `.silly_mat_*` file using the functions on SillyMaterialResource. + This works for files outside of the `res://` folder, and can be done at runtime. + +- "Load Material (ResourceFormatLoader)": Loads a + `.silly_mat_loadable` file using the custom + [ResourceFormatLoader](https://docs.godotengine.org/en/stable/classes/class_resourceformatloader.html), + or loads a `.tres` file using Godot's built-in ResourceFormatLoaderText. + This can be edited in Godot's inspector and saved back. + +- "Load Imported Material (EditorImportPlugin)": Loads a + `.silly_mat_importable` that was imported by an + [EditorImportPlugin](https://docs.godotengine.org/en/stable/classes/class_editorimportplugin.html). + The loaded data actually comes from the corresponding + imported file saved as `res://.godot/imported/something.silly_mat_importable-hash.res`. + Imported files are read-only and cannot be edited in the inspector. + +- "Import Material (directly at runtime)": Imports a `.silly_mat_*` directly from the + source file, performing an import on request instead of loading data the editor imported earlier. + This ignores any editor import settings, works for files outside of the `res://` folder, + and can be done at runtime. + +![Material Creator Plugin Dock](../../screenshots/material_creator_plugin_dock.webp) diff --git a/plugins/addons/material_creator/editor/importers/import_silly_material.gd b/plugins/addons/material_creator/editor/importers/import_silly_material.gd new file mode 100644 index 00000000..852d2ba4 --- /dev/null +++ b/plugins/addons/material_creator/editor/importers/import_silly_material.gd @@ -0,0 +1,72 @@ +## Imports a `.silly_mat_importable` as a SillyMaterialResource. +## This class needs to be registered in the EditorPlugin to be used. +## +## Unlike loaders, importers can be configured, and multiple can exist in +## the same project. Selecting and configuring is done in the "Import" dock. +## However, imported files cannot be modified as easily as loaded files. +## See the "load_and_save" folder for an example of how to use loaders and savers. +## +## In actual projects, you should either choose EditorImportPlugin(s) for a +## configurable import, OR ResourceFormatLoader for a writeable resource load. +## Only one handling can exist at a given time for a given file extension. +## This demo exposes both by using 2 different file extensions. +@tool +class_name ImportSillyMatAsSillyMaterialResource +extends EditorImportPlugin + + +func _get_importer_name() -> String: + return "demos.silly_material_importable.silly_material_resource" + + +func _get_visible_name() -> String: + return "Silly Material Resource" + + +func _get_recognized_extensions() -> PackedStringArray: + return ["silly_mat_importable"] + + +func _get_save_extension() -> String: + return "res" + + +func _get_resource_type() -> String: + # Note: This MUST be a native Godot type, it can't be a GDScript type. + # Therefore it has to be "Resource" instead of "SillyMaterialResource". + return "Resource" + + +func _get_preset_count() -> int: + return 0 + + +func _get_preset_name(preset: int) -> String: + return "Default" + + +func _get_import_options(_path: String, preset: int) -> Array[Dictionary]: + var ret: Array[Dictionary] = [ + { + "name": "make_more_red", + "default_value": false, + } + ] + return ret + + +func _get_import_order() -> int: + return ResourceImporter.IMPORT_ORDER_DEFAULT + + +func _get_option_visibility(path: String, option: StringName, options: Dictionary) -> bool: + return true + + +func _import(source_file: String, save_path: String, options: Dictionary, r_platform_variants: Array[String], r_gen_files: Array[String]) -> Error: + var silly_mat_res := SillyMaterialResource.read_from_file(source_file) + if options.has("make_more_red") and options["make_more_red"]: + silly_mat_res.albedo_color = silly_mat_res.albedo_color.lerp(Color.RED, 0.5) + # This will save to a file path like `res://.godot/imported/something.res`. + var imported_path: String = "%s.%s" % [save_path, _get_save_extension()] + return ResourceSaver.save(silly_mat_res, imported_path) diff --git a/plugins/addons/material_creator/editor/importers/import_silly_material.gd.uid b/plugins/addons/material_creator/editor/importers/import_silly_material.gd.uid new file mode 100644 index 00000000..dc9c2fcf --- /dev/null +++ b/plugins/addons/material_creator/editor/importers/import_silly_material.gd.uid @@ -0,0 +1 @@ +uid://u2esoq3eygve diff --git a/plugins/addons/material_creator/editor/importers/import_standard_material_3d.gd b/plugins/addons/material_creator/editor/importers/import_standard_material_3d.gd new file mode 100644 index 00000000..4bc74e3f --- /dev/null +++ b/plugins/addons/material_creator/editor/importers/import_standard_material_3d.gd @@ -0,0 +1,71 @@ +## Imports a `.silly_mat_importable` as a StandardMaterial3D. +## This class needs to be registered in the EditorPlugin to be used. +## +## Unlike loaders, importers can be configured, and multiple can exist in +## the same project. Selecting and configuring is done in the "Import" dock. +## However, imported files cannot be modified as easily as loaded files. +## See the "load_and_save" folder for an example of how to use loaders and savers. +## +## In actual projects, you should either choose EditorImportPlugin(s) for a +## configurable import, OR ResourceFormatLoader for a writeable resource load. +## Only one handling can exist at a given time for a given file extension. +## This demo exposes both by using 2 different file extensions. +@tool +class_name ImportSillyMatAsStandardMaterial3D +extends EditorImportPlugin + + +func _get_importer_name() -> String: + return "demos.silly_material_importable.standard_material_3d" + + +func _get_visible_name() -> String: + return "Standard Material 3D" + + +func _get_recognized_extensions() -> PackedStringArray: + return ["silly_mat_importable"] + + +func _get_save_extension() -> String: + return "res" + + +func _get_resource_type() -> String: + return "StandardMaterial3D" + + +func _get_preset_count() -> int: + return 0 + + +func _get_preset_name(preset: int) -> String: + return "Default" + + +func _get_import_options(_path: String, preset: int) -> Array[Dictionary]: + var ret: Array[Dictionary] = [ + { + "name": "make_more_red", + "default_value": false, + } + ] + return ret + + +func _get_import_order() -> int: + return ResourceImporter.IMPORT_ORDER_DEFAULT + + +func _get_option_visibility(path: String, option: StringName, options: Dictionary) -> bool: + return true + + +func _import(source_file: String, save_path: String, options: Dictionary, r_platform_variants: Array[String], r_gen_files: Array[String]) -> Error: + var silly_mat_res := SillyMaterialResource.read_from_file(source_file) + if options.has("make_more_red") and options["make_more_red"]: + silly_mat_res.albedo_color = silly_mat_res.albedo_color.lerp(Color.RED, 0.5) + var standard_mat: StandardMaterial3D = silly_mat_res.to_material() + # This will save to a file path like `res://.godot/imported/something.res`. + var imported_path: String = "%s.%s" % [save_path, _get_save_extension()] + return ResourceSaver.save(standard_mat, imported_path) diff --git a/plugins/addons/material_creator/editor/importers/import_standard_material_3d.gd.uid b/plugins/addons/material_creator/editor/importers/import_standard_material_3d.gd.uid new file mode 100644 index 00000000..e0a912d7 --- /dev/null +++ b/plugins/addons/material_creator/editor/importers/import_standard_material_3d.gd.uid @@ -0,0 +1 @@ +uid://cmgoxx63wybil diff --git a/plugins/addons/material_creator/editor/load_and_save/material_format_loader.gd b/plugins/addons/material_creator/editor/load_and_save/material_format_loader.gd new file mode 100644 index 00000000..9f93783b --- /dev/null +++ b/plugins/addons/material_creator/editor/load_and_save/material_format_loader.gd @@ -0,0 +1,38 @@ +## Custom loader for the `.silly_mat_loadable` file format. +## Works together with `SillyMatFormatSaver` to support saving and loading. +## This class needs to be registered in the EditorPlugin to be used. +## +## Loaders can easily have the loaded data be modified and saved back into +## the file. However, only one loader can exist, and the loading cannot be +## configured, unlike importers which are configurable in the "Import" dock. +## See the "importers" folder for two examples of how to use importers. +## +## In actual projects, you should either choose ResourceFormatLoader for a +## writeable resource load, OR EditorImportPlugin(s) for a configurable import. +## Only one handling can exist at a given time for a given file extension. +## This demo exposes both by using 2 different file extensions. +@tool +class_name SillyMatFormatLoader +extends ResourceFormatLoader + + +## Callback to return an array of the file extensions this loader can load. +func _get_recognized_extensions() -> PackedStringArray: + return PackedStringArray(["silly_mat_loadable"]) + + +## Callback to return the resource type name based on file extension. +func _get_resource_type(path: String) -> String: + if path.get_extension() == "silly_mat_loadable": + return "SillyMaterialResource" + return "" + + +## Callback to return what resource type this loader handles. +func _handles_type(type_name: StringName) -> bool: + return type_name == &"SillyMaterialResource" + + +## Main callback to actually perform the loading. +func _load(path: String, original_path: String, use_sub_threads: bool, cache_mode: int) -> Variant: + return SillyMaterialResource.read_from_file(original_path) diff --git a/plugins/addons/material_creator/editor/load_and_save/material_format_loader.gd.uid b/plugins/addons/material_creator/editor/load_and_save/material_format_loader.gd.uid new file mode 100644 index 00000000..f4973f0a --- /dev/null +++ b/plugins/addons/material_creator/editor/load_and_save/material_format_loader.gd.uid @@ -0,0 +1 @@ +uid://b004gkmug1qlt diff --git a/plugins/addons/material_creator/editor/load_and_save/material_format_saver.gd b/plugins/addons/material_creator/editor/load_and_save/material_format_saver.gd new file mode 100644 index 00000000..1193f5bb --- /dev/null +++ b/plugins/addons/material_creator/editor/load_and_save/material_format_saver.gd @@ -0,0 +1,34 @@ +## Custom saver for the `.silly_mat_loadable` file format. +## Works together with `SillyMatFormatLoader` to support loading and saving. +## This class needs to be registered in the EditorPlugin to be used. +## +## Loaders can easily have the loaded data be modified and saved back into +## the file. However, only one loader can exist, and the loading cannot be +## configured, unlike importers which are configurable in the "Import" dock. +## See the "importers" folder for two examples of how to use importers. +## +## In actual projects, you should either choose ResourceFormatLoader for a +## writeable resource load, OR EditorImportPlugin(s) for a configurable import. +## Only one handling can exist at a given time for a given file extension. +## This demo exposes both by using 2 different file extensions. +@tool +class_name SillyMatFormatSaver +extends ResourceFormatSaver + + +## Callback to return an array of the file extensions this saver can write. +func _get_recognized_extensions(resource: Resource) -> PackedStringArray: + return PackedStringArray(["silly_mat_loadable"]) + + +## Callback to determine if a given Resource is supported by this saver. +func _recognize(resource: Resource) -> bool: + return resource is SillyMaterialResource + + +## Main callback to actually perform the saving. +func _save(resource: Resource, path: String, flags: int) -> Error: + var mat_res: SillyMaterialResource = resource as SillyMaterialResource + if mat_res == null: + return ERR_INVALID_DATA + return mat_res.write_to_file(path) diff --git a/plugins/addons/material_creator/editor/load_and_save/material_format_saver.gd.uid b/plugins/addons/material_creator/editor/load_and_save/material_format_saver.gd.uid new file mode 100644 index 00000000..84eb3877 --- /dev/null +++ b/plugins/addons/material_creator/editor/load_and_save/material_format_saver.gd.uid @@ -0,0 +1 @@ +uid://cvaleef5ekitp diff --git a/plugins/addons/material_creator/editor/material_creator.gd b/plugins/addons/material_creator/editor/material_creator.gd new file mode 100644 index 00000000..33593574 --- /dev/null +++ b/plugins/addons/material_creator/editor/material_creator.gd @@ -0,0 +1,145 @@ +# The word "silly" is used to make it obvious that the name is arbitrary. +@tool +extends Panel + + +var editor_interface: EditorInterface + +@onready var albedo_color_picker: ColorPickerButton = $VBoxContainer/AlbedoColorPicker +@onready var metallic_slider: HSlider = $VBoxContainer/MetallicSlider +@onready var roughness_slider: HSlider = $VBoxContainer/RoughnessSlider + +@onready var save_material_dialog: FileDialog = $SaveMaterialDialog +@onready var export_material_dialog: FileDialog = $ExportMaterialDialog +@onready var load_material_importer_dialog: FileDialog = $LoadMaterialImporterDialog +@onready var load_material_loader_dialog: FileDialog = $LoadMaterialLoaderDialog +@onready var import_material_directly_dialog: FileDialog = $ImportMaterialDirectlyDialog + + +func _ready() -> void: + if not name.contains(" "): + printerr("Warning: Material Creator dock doesn't have a space in its node name, so it will be displayed without any spacing.") + save_material_dialog.current_path = "res://addons/material_creator/example/" + save_material_dialog.current_file = "new_material.silly_mat_loadable" + export_material_dialog.current_path = "res://addons/material_creator/example/" + export_material_dialog.current_file = "new_material.silly_mat_importable" + load_material_importer_dialog.current_path = "res://addons/material_creator/example/" + load_material_loader_dialog.current_path = "res://addons/material_creator/example/" + import_material_directly_dialog.current_path = ProjectSettings.globalize_path("res://addons/material_creator/example/") + RenderingServer.canvas_item_set_clip(get_canvas_item(), true) + + +func _save_or_export_file(path: String) -> void: + if path.is_empty(): + printerr("Material Creator: No path chosen for saving.") + return + # Ensure directory exists before trying to save to it. + var dir: String = path.get_base_dir() + if not DirAccess.dir_exists_absolute(dir): + var err: Error = DirAccess.make_dir_recursive_absolute(dir) + if err != OK: + printerr("Material Creator: Can't create folder: %s (%s)" % [dir, error_string(err)]) + return + var silly_mat: SillyMaterialResource = _create_silly_material_from_editor_values() + var ext: String = path.get_extension().to_lower() + var err: Error + var is_in_project: bool = path.begins_with("res://") or path.begins_with("user://") + if ext == "tres": + err = ResourceSaver.save(silly_mat, path) + if not is_in_project: + printerr("Material Creator: Warning: When saving outside of the Godot project, " + + "prefer exporting instead. A Godot resource may not be functional " + + "without the context of its original project (ex: script paths).") + elif ext == "silly_mat_loadable" and is_in_project: + err = ResourceSaver.save(silly_mat, path) + else: + err = silly_mat.write_to_file(path) + if err != OK: + printerr("Material Creator: Failed to save to %s, reason: %s" % [path, error_string(err)]) + else: + print("Material Creator: Successfully saved to ", path) + # Inform the editor that files have changed on disk. + var efs: EditorFileSystem = editor_interface.get_resource_filesystem() + efs.scan() + + +func load_file_resource_loader(path: String) -> void: + var loaded_file: Resource = ResourceLoader.load(path) + if loaded_file == null: + printerr("Material Creator: Failed to load file at %s" % path) + return + if loaded_file is SillyMaterialResource: + edit_silly_material(loaded_file) + return + if loaded_file is StandardMaterial3D: + edit_silly_material(SillyMaterialResource.from_material(loaded_file)) + return + + +func load_file_directly(path: String) -> void: + var silly_mat := SillyMaterialResource.read_from_file(path) + if silly_mat == null: + printerr("Material Creator: Failed to directly load file at %s" % path) + edit_silly_material(silly_mat) + + +func edit_silly_material(silly_mat: SillyMaterialResource) -> void: + albedo_color_picker.color = silly_mat.albedo_color + metallic_slider.value = silly_mat.metallic_strength + roughness_slider.value = silly_mat.roughness_strength + + +func _create_silly_material_from_editor_values() -> SillyMaterialResource: + var color: Color = albedo_color_picker.color + var metallic: float = metallic_slider.value + var roughness: float = roughness_slider.value + var silly_res := SillyMaterialResource.new() + silly_res.albedo_color = color + silly_res.metallic_strength = metallic + silly_res.roughness_strength = roughness + return silly_res + + +func _apply_material_to_nodes(selected_nodes: Array[Node]) -> void: + if selected_nodes.is_empty(): + printerr("Material Creator: Can't apply the material because there are no nodes selected!") + return + var new_material: StandardMaterial3D = _create_silly_material_from_editor_values().to_material() + # Go through the selected nodes and see if they are MeshInstance3D nodes. + # If they do, then call it to set the material to the silly material. + var applied: bool = false + for node in selected_nodes: + if node is MeshInstance3D: + node.set_surface_override_material(0, new_material) + applied = true + if applied: + print("Material Creator: Applied material to selected MeshInstance3D nodes!") + else: + printerr("Material Creator: Can't apply the material because there are no MeshInstance3D nodes selected!") + + +func _on_apply_button_pressed() -> void: + # Using the passed in editor interface, get the selected nodes in the editor. + var editor_selection: EditorSelection = editor_interface.get_selection() + var selected_nodes: Array[Node] = editor_selection.get_selected_nodes() + _apply_material_to_nodes(selected_nodes) + + +func _on_save_button_pressed() -> void: + save_material_dialog.popup_centered(save_material_dialog.min_size * EditorInterface.get_editor_scale()) + + +func _on_export_button_pressed() -> void: + export_material_dialog.popup_centered(export_material_dialog.min_size * EditorInterface.get_editor_scale()) + + +func _on_load_button_importer_pressed() -> void: + load_material_importer_dialog.popup_centered(load_material_importer_dialog.min_size * EditorInterface.get_editor_scale()) + + +func _on_load_button_loader_pressed() -> void: + load_material_loader_dialog.popup_centered(load_material_loader_dialog.min_size * EditorInterface.get_editor_scale()) + + +func _on_import_button_directly_pressed() -> void: + import_material_directly_dialog.popup_centered(import_material_directly_dialog.min_size * EditorInterface.get_editor_scale()) diff --git a/plugins/addons/material_creator/material_creator.gd.uid b/plugins/addons/material_creator/editor/material_creator.gd.uid similarity index 100% rename from plugins/addons/material_creator/material_creator.gd.uid rename to plugins/addons/material_creator/editor/material_creator.gd.uid diff --git a/plugins/addons/material_creator/editor/material_dock.tscn b/plugins/addons/material_creator/editor/material_dock.tscn new file mode 100644 index 00000000..06caf642 --- /dev/null +++ b/plugins/addons/material_creator/editor/material_dock.tscn @@ -0,0 +1,151 @@ +[gd_scene load_steps=2 format=3 uid="uid://bo31028pgti5e"] + +[ext_resource type="Script" uid="uid://dy86u5ti4fb3m" path="res://addons/material_creator/editor/material_creator.gd" id="1"] + +[node name="Material Creator" type="Panel"] +custom_minimum_size = Vector2(220, 530) +offset_right = 220.0 +offset_bottom = 491.0 +script = ExtResource("1") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +custom_minimum_size = Vector2(200, 510) +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 10.0 +offset_top = 10.0 +offset_right = -10.0 +offset_bottom = -10.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="AlbedoLabel" type="Label" parent="VBoxContainer"] +custom_minimum_size = Vector2(16, 16) +layout_mode = 2 +text = "Albedo Color:" + +[node name="AlbedoColorPicker" type="ColorPickerButton" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 +color = Color(1, 1, 1, 1) + +[node name="MetallicLabel" type="Label" parent="VBoxContainer"] +custom_minimum_size = Vector2(16, 16) +layout_mode = 2 +text = "Metallic Strength:" + +[node name="MetallicSlider" type="HSlider" parent="VBoxContainer"] +custom_minimum_size = Vector2(16, 16) +layout_mode = 2 +max_value = 1.0 +step = 0.05 + +[node name="RoughnessLabel" type="Label" parent="VBoxContainer"] +custom_minimum_size = Vector2(16, 16) +layout_mode = 2 +text = "Roughness Strength:" + +[node name="RoughnessSlider" type="HSlider" parent="VBoxContainer"] +custom_minimum_size = Vector2(16, 16) +layout_mode = 2 +max_value = 1.0 +step = 0.05 +ticks_on_borders = true + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer"] +custom_minimum_size = Vector2(8, 8) +layout_mode = 2 + +[node name="ApplyButton" type="Button" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 35) +layout_mode = 2 +tooltip_text = "Applies this material to all selected MeshInstance3D nodes." +text = "Apply Material" + +[node name="SaveButton" type="Button" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 35) +layout_mode = 2 +tooltip_text = "Save a `.silly_mat_loadable` file using a custom ResourceFormatSaver or save a `.tres` using Godot's built-in ResourceFormatSaverText." +text = "Save Material +(ResourceFormatSaver)" + +[node name="ExportButton" type="Button" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 35) +layout_mode = 2 +tooltip_text = "Export a `.silly_mat_*` file using the functions on SillyMaterialResource. This works for files outside of the `res://` folder, and can be done at runtime." +text = "Export Material +(directly at runtime)" + +[node name="LoadButtonLoader" type="Button" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 60) +layout_mode = 2 +tooltip_text = "Load a `.silly_mat_loadable` using a custom ResourceFormatLoader or load a `.tres` using Godot's built-in ResourceFormatLoaderText." +text = "Load Material +(ResourceFormatLoader)" +clip_text = true + +[node name="LoadButtonImporter" type="Button" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 60) +layout_mode = 2 +tooltip_text = "Load a `.silly_mat_importable` that was imported by an EditorImportPlugin. The loaded data actually comes from the corresponding imported file saved as `res://.godot/imported/something.silly_mat_importable-hash.res`." +text = "Load Imported Material +(EditorImportPlugin)" +clip_text = true + +[node name="ImportButtonDirectly" type="Button" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 60) +layout_mode = 2 +tooltip_text = "Import a `.silly_mat_*` directly from the source file, performing an import on request instead of loading data the editor imported earlier. This ignores any editor import settings, works for files outside of the `res://` folder, and can be done at runtime." +text = "Import Material +(directly at runtime)" +clip_text = true + +[node name="SaveMaterialDialog" type="FileDialog" parent="."] +size = Vector2i(1000, 500) +min_size = Vector2i(1000, 500) +filters = PackedStringArray("*.silly_mat_loadable ; Loadable Silly Material (editable)", "*.tres ; Godot Resource (resource)") + +[node name="ExportMaterialDialog" type="FileDialog" parent="."] +size = Vector2i(1000, 500) +min_size = Vector2i(1000, 500) +access = 2 +filters = PackedStringArray("*.silly_mat_importable ; Importable Silly Material (customizable)", "*.silly_mat_loadable ; Loadable Silly Material (editable)") + +[node name="LoadMaterialLoaderDialog" type="FileDialog" parent="."] +title = "Open a File" +size = Vector2i(1000, 500) +min_size = Vector2i(1000, 500) +ok_button_text = "Open" +file_mode = 0 +filters = PackedStringArray("*.silly_mat_loadable ; Loadable Silly Material (editable)", "*.tres ; Godot Resource (resource)") + +[node name="LoadMaterialImporterDialog" type="FileDialog" parent="."] +title = "Open a File" +size = Vector2i(1000, 500) +min_size = Vector2i(1000, 500) +ok_button_text = "Open" +file_mode = 0 +filters = PackedStringArray("*.silly_mat_importable ; Importable Silly Material (customizable)") + +[node name="ImportMaterialDirectlyDialog" type="FileDialog" parent="."] +title = "Open a File" +size = Vector2i(1000, 500) +min_size = Vector2i(1000, 500) +ok_button_text = "Open" +file_mode = 0 +access = 2 +filters = PackedStringArray("*.silly_mat_importable ; Importable Silly Material (customizable)", "*.silly_mat_loadable ; Loadable Silly Material (editable)") + +[connection signal="pressed" from="VBoxContainer/ApplyButton" to="." method="_on_apply_button_pressed"] +[connection signal="pressed" from="VBoxContainer/SaveButton" to="." method="_on_save_button_pressed"] +[connection signal="pressed" from="VBoxContainer/ExportButton" to="." method="_on_export_button_pressed"] +[connection signal="pressed" from="VBoxContainer/LoadButtonLoader" to="." method="_on_load_button_loader_pressed"] +[connection signal="pressed" from="VBoxContainer/LoadButtonImporter" to="." method="_on_load_button_importer_pressed"] +[connection signal="pressed" from="VBoxContainer/ImportButtonDirectly" to="." method="_on_import_button_directly_pressed"] +[connection signal="file_selected" from="SaveMaterialDialog" to="." method="_save_or_export_file"] +[connection signal="file_selected" from="ExportMaterialDialog" to="." method="_save_or_export_file"] +[connection signal="file_selected" from="LoadMaterialLoaderDialog" to="." method="load_file_resource_loader"] +[connection signal="file_selected" from="LoadMaterialImporterDialog" to="." method="load_file_resource_loader"] +[connection signal="file_selected" from="ImportMaterialDirectlyDialog" to="." method="load_file_directly"] diff --git a/plugins/addons/material_creator/examples/blue.tres b/plugins/addons/material_creator/examples/blue.tres new file mode 100644 index 00000000..9b8f33e2 --- /dev/null +++ b/plugins/addons/material_creator/examples/blue.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="SillyMaterialResource" load_steps=2 format=3 uid="uid://d1o64hhg6sxuk"] + +[ext_resource type="Script" uid="uid://bjnq25fa3ptjc" path="res://addons/material_creator/silly_material_resource.gd" id="1_l7fpc"] + +[resource] +script = ExtResource("1_l7fpc") +albedo_color = Color(0.06666667, 0.06666667, 0.93333334, 1) +metallic_strength = 0.9 +roughness_strength = 0.1 diff --git a/plugins/addons/material_creator/examples/cyan.silly_mat_loadable b/plugins/addons/material_creator/examples/cyan.silly_mat_loadable new file mode 100644 index 00000000..e53a062f --- /dev/null +++ b/plugins/addons/material_creator/examples/cyan.silly_mat_loadable @@ -0,0 +1 @@ +{"albedo_color":[0.0666666701436043,0.933333337306976,0.933333337306976],"metallic_strength":0.8,"roughness_strength":0.2} diff --git a/plugins/addons/material_creator/examples/cyan.silly_mat_loadable.uid b/plugins/addons/material_creator/examples/cyan.silly_mat_loadable.uid new file mode 100644 index 00000000..8a75f71b --- /dev/null +++ b/plugins/addons/material_creator/examples/cyan.silly_mat_loadable.uid @@ -0,0 +1 @@ +uid://fhh5emd0x6wb diff --git a/plugins/addons/material_creator/examples/green_as_standard_mat.silly_mat_importable b/plugins/addons/material_creator/examples/green_as_standard_mat.silly_mat_importable new file mode 100644 index 00000000..9b7fa0ea --- /dev/null +++ b/plugins/addons/material_creator/examples/green_as_standard_mat.silly_mat_importable @@ -0,0 +1 @@ +{"albedo_color":[0.0666666701436043,0.933333337306976,0.0666666701436043],"metallic_strength":0.35,"roughness_strength":0.65} diff --git a/plugins/addons/material_creator/examples/green_as_standard_mat.silly_mat_importable.import b/plugins/addons/material_creator/examples/green_as_standard_mat.silly_mat_importable.import new file mode 100644 index 00000000..5aa03e40 --- /dev/null +++ b/plugins/addons/material_creator/examples/green_as_standard_mat.silly_mat_importable.import @@ -0,0 +1,15 @@ +[remap] + +importer="demos.silly_material_importable.standard_material_3d" +type="StandardMaterial3D" +uid="uid://sy27jfukvqij" +path="res://.godot/imported/green_as_standard_mat.silly_mat_importable-8bc4ac1c11ef82b73959db611c6e0025.res" + +[deps] + +source_file="res://addons/material_creator/examples/green_as_standard_mat.silly_mat_importable" +dest_files=["res://.godot/imported/green_as_standard_mat.silly_mat_importable-8bc4ac1c11ef82b73959db611c6e0025.res"] + +[params] + +make_more_red=false diff --git a/plugins/addons/material_creator/examples/yellow.silly_mat_importable b/plugins/addons/material_creator/examples/yellow.silly_mat_importable new file mode 100644 index 00000000..7caa0a9b --- /dev/null +++ b/plugins/addons/material_creator/examples/yellow.silly_mat_importable @@ -0,0 +1 @@ +{"albedo_color":[0.933333337306976,0.933333337306976,0.0666666701436043],"metallic_strength":0.65,"roughness_strength":0.35} diff --git a/plugins/addons/material_creator/examples/yellow.silly_mat_importable.import b/plugins/addons/material_creator/examples/yellow.silly_mat_importable.import new file mode 100644 index 00000000..7c3b95f2 --- /dev/null +++ b/plugins/addons/material_creator/examples/yellow.silly_mat_importable.import @@ -0,0 +1,15 @@ +[remap] + +importer="demos.silly_material_importable.silly_material_resource" +type="Resource" +uid="uid://b38mu7kfwyrt0" +path="res://.godot/imported/yellow.silly_mat_importable-3de5551b2840cbea5353335a74390454.res" + +[deps] + +source_file="res://addons/material_creator/examples/yellow.silly_mat_importable" +dest_files=["res://.godot/imported/yellow.silly_mat_importable-3de5551b2840cbea5353335a74390454.res"] + +[params] + +make_more_red=false diff --git a/plugins/addons/material_creator/examples/yellow_tinted_red.silly_mat_importable b/plugins/addons/material_creator/examples/yellow_tinted_red.silly_mat_importable new file mode 100644 index 00000000..7caa0a9b --- /dev/null +++ b/plugins/addons/material_creator/examples/yellow_tinted_red.silly_mat_importable @@ -0,0 +1 @@ +{"albedo_color":[0.933333337306976,0.933333337306976,0.0666666701436043],"metallic_strength":0.65,"roughness_strength":0.35} diff --git a/plugins/addons/material_creator/examples/yellow_tinted_red.silly_mat_importable.import b/plugins/addons/material_creator/examples/yellow_tinted_red.silly_mat_importable.import new file mode 100644 index 00000000..700d4430 --- /dev/null +++ b/plugins/addons/material_creator/examples/yellow_tinted_red.silly_mat_importable.import @@ -0,0 +1,15 @@ +[remap] + +importer="demos.silly_material_importable.silly_material_resource" +type="Resource" +uid="uid://dhhdox2m0kn0l" +path="res://.godot/imported/yellow_tinted_red.silly_mat_importable-d6610364b07d235546934af33df4be98.res" + +[deps] + +source_file="res://addons/material_creator/examples/yellow_tinted_red.silly_mat_importable" +dest_files=["res://.godot/imported/yellow_tinted_red.silly_mat_importable-d6610364b07d235546934af33df4be98.res"] + +[params] + +make_more_red=true diff --git a/plugins/addons/material_creator/material_creator.gd b/plugins/addons/material_creator/material_creator.gd deleted file mode 100644 index 881c8f28..00000000 --- a/plugins/addons/material_creator/material_creator.gd +++ /dev/null @@ -1,165 +0,0 @@ -@tool -extends Panel -# In this file, the word "silly" is used to make it obvious that the name is arbitrary. - -var silly_material_resource := preload("res://addons/material_creator/material_resource.gd") -var editor_interface: EditorInterface - - -func _ready() -> void: - # Connect all of the signals we'll need to save and load silly materials. - $VBoxContainer/ApplyButton.pressed.connect(apply_pressed) - $VBoxContainer/SaveButton.pressed.connect(save_pressed) - $VBoxContainer/LoadButton.pressed.connect(load_pressed) - - $SaveMaterialDialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE - $SaveMaterialDialog.access = FileDialog.ACCESS_RESOURCES - $SaveMaterialDialog.current_dir = "res://materials" - $SaveMaterialDialog.current_file = "new_material.silly_mat" - $SaveMaterialDialog.filters = PackedStringArray([ - "*.silly_mat ; Silly Material (resource)", - "*.tres ; Godot Resource (resource)", - "*.mtxt ; Silly Material (source)", - ]) - $SaveMaterialDialog.confirmed.connect(_on_save_confirmed) - - $LoadMaterialDialog.access = FileDialog.ACCESS_RESOURCES - $LoadMaterialDialog.filters = PackedStringArray([ - "*.silly_mat ; Silly Material (resource)", - "*.tres ; Godot Resource (resource)", - "*.mtxt ; Silly Material (source)", - ]) - $LoadMaterialDialog.file_selected.connect(load_file_selected) - - RenderingServer.canvas_item_set_clip(get_canvas_item(), true) - - -func save_pressed() -> void: - $SaveMaterialDialog.popup_centered_ratio() - - -func load_pressed() -> void: - $LoadMaterialDialog.popup_centered_ratio() - - -func _on_save_confirmed() -> void: - var path = $SaveMaterialDialog.get_current_path() - if path.is_empty(): - push_error("Material Creator: No path chosen for saving.") - return - - # If user typed no extension, default to .silly_mat (resource path). - if not path.get_file().contains("."): - path += ".silly_mat" - - var ext = path.get_extension().to_lower() - - # Ensure directory exists under res:// when saving inside project. - var dir = path.get_base_dir() - if path.begins_with("res://") and not DirAccess.dir_exists_absolute(dir): - var mk := DirAccess.make_dir_recursive_absolute(dir) - if mk != OK: - push_error("Material Creator: Can't create folder: \"%s\" (%s)." % [dir, error_string(mk)]) - return - - var res: Resource = _silly_resource_from_values() - - match ext: - "mtxt": - # Write SOURCE file (no ResourceSaver, works anywhere). - var ok := _write_source_silly(path, res) - if not ok: - push_error("Material Creator: Failed to write source .mtxt at \"%s\"." % path) - else: - print("Material Creator: Wrote source to ", path) - "silly_mat", "tres": - # Save RESOURCE (requires your custom saver for .silly_mat). - res.resource_path = path - var err := ResourceSaver.save(res, path) - if err != OK: - push_error("Material Creator: Failed to save resource: \"%s\" (%s)." % [path, error_string(err)]) - else: - print("Material Creator: Saved resource to ", path) - _: - push_error("Material Creator: Unsupported extension: ." + ext) - - -func apply_pressed() -> void: - # Using the passed in editor interface, get the selected nodes in the editor. - var editor_selection: EditorSelection = editor_interface.get_selection() - var selected_nodes := editor_selection.get_selected_nodes() - if selected_nodes.is_empty(): - push_error("Material Creator: Can't apply the material, because there are no nodes selected!") - return - - var new_material: StandardMaterial3D = _silly_resource_from_values().make_material() - # Go through the selected nodes and see if they have the "set_surface_override_material" - # function (which only MeshInstance3D has by default). If they do, then set the material - # to the silly material. - for node in selected_nodes: - if node.has_method(&"set_surface_override_material"): - node.set_surface_override_material(0, new_material) - - -func load_file_selected(path: String) -> bool: - var ext := path.get_extension().to_lower() - - if ext == "mtxt": - # Load SOURCE by manual parse (works inside/outside res://) - var loaded := _read_source_silly(path) - if loaded == null: - push_error("Material Creator: Failed to parse source at \"%s\"." % path) - return false - $VBoxContainer/AlbedoColorPicker.color = loaded.albedo_color - $VBoxContainer/MetallicSlider.value = loaded.metallic_strength - $VBoxContainer/RoughnessSlider.value = loaded.roughness_strength - return true - else: - # Load RESOURCE via ResourceLoader (silly_mat via your loader, tres via built-in) - var silly_resource: Resource = ResourceLoader.load(path) - if silly_resource == null: - push_error("Material Creator: Failed to load resource at \"%s\"." % path) - return false - $VBoxContainer/AlbedoColorPicker.color = silly_resource.albedo_color - $VBoxContainer/MetallicSlider.value = silly_resource.metallic_strength - $VBoxContainer/RoughnessSlider.value = silly_resource.roughness_strength - return true - - -func _silly_resource_from_values() -> Resource: - var color: Color = $VBoxContainer/AlbedoColorPicker.color - var metallic: float = $VBoxContainer/MetallicSlider.value - var roughness: float = $VBoxContainer/RoughnessSlider.value - var silly_res: Resource = silly_material_resource.new() - silly_res.albedo_color = color - silly_res.metallic_strength = metallic - silly_res.roughness_strength = roughness - return silly_res - -# --------------------------------------------------------------- -# Source (.mtxt) helpers. -# --------------------------------------------------------------- - -func _write_source_silly(path: String, res: Resource) -> bool: - var mat_file := FileAccess.open(path, FileAccess.WRITE) - if mat_file == null: - return false - mat_file.store_line("SILLY_MAT v1") - mat_file.store_line(res.albedo_color.to_html(true)) # RGBA hex - mat_file.store_line(str(res.metallic_strength)) - mat_file.store_line(str(res.roughness_strength)) - return true - - -func _read_source_silly(path: String) -> Resource: - var mat_file := FileAccess.open(path, FileAccess.READ) - if mat_file == null: - return null - var header := mat_file.get_line() - if not header.begins_with("SILLY_MAT"): - return null - var mat_res := silly_material_resource.new() - mat_res.albedo_color = Color(mat_file.get_line()) # from hex string - mat_res.metallic_strength = float(mat_file.get_line()) - mat_res.roughness_strength = float(mat_file.get_line()) - return mat_res diff --git a/plugins/addons/material_creator/material_dock.tscn b/plugins/addons/material_creator/material_dock.tscn deleted file mode 100644 index d19c83b2..00000000 --- a/plugins/addons/material_creator/material_dock.tscn +++ /dev/null @@ -1,83 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://bo31028pgti5e"] - -[ext_resource type="Script" uid="uid://dy86u5ti4fb3m" path="res://addons/material_creator/material_creator.gd" id="1"] - -[node name="Material Creator" type="Panel"] -custom_minimum_size = Vector2(208, 0) -offset_right = 220.0 -offset_bottom = 340.0 -script = ExtResource("1") - -[node name="VBoxContainer" type="VBoxContainer" parent="."] -layout_mode = 0 -anchor_left = 0.5 -anchor_right = 0.5 -anchor_bottom = 1.0 -offset_left = -100.0 -offset_right = 100.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="AlbedoLabel" type="Label" parent="VBoxContainer"] -layout_mode = 2 -text = "Albedo Color:" - -[node name="AlbedoColorPicker" type="ColorPickerButton" parent="VBoxContainer"] -custom_minimum_size = Vector2(0, 32) -layout_mode = 2 -color = Color(1, 1, 1, 1) - -[node name="MetallicLabel" type="Label" parent="VBoxContainer"] -layout_mode = 2 -text = "Metallic Strength:" - -[node name="MetallicSlider" type="HSlider" parent="VBoxContainer"] -layout_mode = 2 -max_value = 1.0 -step = 0.05 - -[node name="RoughnessLabel" type="Label" parent="VBoxContainer"] -layout_mode = 2 -text = "Roughness Strength:" - -[node name="RoughnessSlider" type="HSlider" parent="VBoxContainer"] -layout_mode = 2 -max_value = 1.0 -step = 0.05 -ticks_on_borders = true - -[node name="HSeparator" type="HSeparator" parent="VBoxContainer"] -layout_mode = 2 - -[node name="ApplyButton" type="Button" parent="VBoxContainer"] -layout_mode = 2 -text = "Apply Material" - -[node name="SaveButton" type="Button" parent="VBoxContainer"] -layout_mode = 2 -text = "Save Material" - -[node name="LoadButton" type="Button" parent="VBoxContainer"] -layout_mode = 2 -text = "Load Material" -clip_text = true - -[node name="Label" type="Label" parent="VBoxContainer/LoadButton"] -visible = false -layout_mode = 0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_bottom = -10.0 -grow_horizontal = 2 -grow_vertical = 2 -text = "Load silly material and -apply to selected node(s)" - -[node name="SaveMaterialDialog" type="FileDialog" parent="."] -filters = PackedStringArray("*.silly_mat") - -[node name="LoadMaterialDialog" type="FileDialog" parent="."] -title = "Open a File" -ok_button_text = "Open" -file_mode = 0 -filters = PackedStringArray("*.silly_mat") diff --git a/plugins/addons/material_creator/material_format_loader.gd b/plugins/addons/material_creator/material_format_loader.gd deleted file mode 100644 index 9039258f..00000000 --- a/plugins/addons/material_creator/material_format_loader.gd +++ /dev/null @@ -1,40 +0,0 @@ -@tool -extends ResourceFormatLoader -class_name SillyMatFormatLoader -## Custom loader for the .silly_mat file format. -## Allows Godot to recognize and load SillyMaterialResource files. -## Register this loader in the EditorPlugin to enable saving/loading resources. - - -## Returns the list of file extensions this loader supports. -func _get_recognized_extensions() -> PackedStringArray: - # Returns only ".silly_mat" - return PackedStringArray(["silly_mat"]) - - -## Returns what resource type this loader handles. -func _handles_type(typename: StringName) -> bool: - return typename == "SillyMaterialResource" - - -## Returns the resource type name based on file extension. -func _get_resource_type(path: String) -> String: - return "SillyMaterialResource" if path.get_extension() == "silly_mat" else "" - - -## Main load function. Reads .silly_mat and constructs a SillyMaterialResource. -func _load(path: String, original_path: String, use_sub_threads, cache_mode): - var mat_file = FileAccess.open(path, FileAccess.READ) - if mat_file == null: - return ERR_CANT_OPEN - - # Check header line to validate file format version. - if mat_file.get_line() != "SILLY_MAT v1": - return ERR_PARSE_ERROR - - # Create and Fill SillyMaterialResource - var mat_res: SillyMaterialResource = SillyMaterialResource.new() - mat_res.albedo_color = Color(mat_file.get_line()) - mat_res.metallic_strength = float(mat_file.get_line()) - mat_res.roughness_strength = float(mat_file.get_line()) - return mat_res diff --git a/plugins/addons/material_creator/material_format_saver.gd b/plugins/addons/material_creator/material_format_saver.gd deleted file mode 100644 index 71df9dbb..00000000 --- a/plugins/addons/material_creator/material_format_saver.gd +++ /dev/null @@ -1,35 +0,0 @@ -@tool -extends ResourceFormatSaver -class_name SillyMatFormatSaver -## Custom saver for the .silly_mat file format. -## Works together with SillyMatFormatLoader to make SillyMaterialResource. - - -## This saver only supports SilluMaterialResource. -func _recognize(resource: Resource) -> bool: - return resource is SillyMaterialResource - - -## Return list of file extensions this saver will write. -func _get_recognized_extensions(resource: Resource) -> PackedStringArray: - return PackedStringArray(["silly_mat"]) - - -## Main save function. -## Serializes a SillyMaterialResource into .silly_mat format. -## -## It will write simple text-based format, one property per line. -func _save(resource: Resource, path: String, flags: int) -> int: - var mat_res: SillyMaterialResource = resource as SillyMaterialResource - if mat_res == null: - return ERR_INVALID_DATA - - var mat_file := FileAccess.open(path, FileAccess.WRITE) - if mat_file == null: - return ERR_CANT_OPEN - - mat_file.store_line("SILLY_MAT v1") - mat_file.store_line(mat_res.albedo_color.to_html(true)) # Stored in HTML hex. - mat_file.store_line(str(mat_res.metallic_strength)) - mat_file.store_line(str(mat_res.roughness_strength)) - return OK diff --git a/plugins/addons/material_creator/material_plugin.gd b/plugins/addons/material_creator/material_plugin.gd index b69e6e1c..b267a0bf 100644 --- a/plugins/addons/material_creator/material_plugin.gd +++ b/plugins/addons/material_creator/material_plugin.gd @@ -1,38 +1,46 @@ -# A simple (and silly) material resource plugin. Allows you to make a really simple material -# from a custom dock, that you can save and load, and apply to selected MeshInstances. -# -# SPECIAL NOTE: This technically should be using EditorImportPlugin and EditorExportPlugin -# to handle the input and output of the silly material. However, currently you cannot export -# custom resources in Godot, so instead we're using JSON files instead. -# -# This example should be replaced when EditorImportPlugin and EditorExportPlugin are both -# fully working and you can save custom resources. - +## A simple (and silly) material resource plugin. Allows you to make a really +## simple material from a custom dock, which can be applied to meshes, +## saved to files, loaded from files, imported from files, and more. +## +## See the documentation in the `README.md` file for more information, +## and also the documentation in each class (Ctrl+Click on these in Godot): +## - SillyMaterialResource +## - ImportSillyMatAsSillyMaterialResource +## - ImportSillyMatAsStandardMaterial3D +## - SillyMatFormatLoader +## - SillyMatFormatSaver @tool extends EditorPlugin -var io_material_dialog: Panel -var _loader: SillyMatFormatLoader -var _saver: SillyMatFormatSaver + +var _material_creator_dock: Panel +var _silly_mat_loader := SillyMatFormatLoader.new() +var _silly_mat_saver := SillyMatFormatSaver.new() +var _import_as_silly_mat_res := ImportSillyMatAsSillyMaterialResource.new() +var _import_as_standard_mat := ImportSillyMatAsStandardMaterial3D.new() func _enter_tree() -> void: - _loader = SillyMatFormatLoader.new() - _saver = SillyMatFormatSaver.new() - ResourceLoader.add_resource_format_loader(_loader) - ResourceSaver.add_resource_format_saver(_saver) - - io_material_dialog = preload("res://addons/material_creator/material_dock.tscn").instantiate() - io_material_dialog.editor_interface = get_editor_interface() - add_control_to_dock(DOCK_SLOT_LEFT_UL, io_material_dialog) + # Set up the loader and saver. + ResourceLoader.add_resource_format_loader(_silly_mat_loader) + ResourceSaver.add_resource_format_saver(_silly_mat_saver) + # Set up the importers. + add_import_plugin(_import_as_silly_mat_res) + add_import_plugin(_import_as_standard_mat) + # Set up the silly material creator dock. + const dock_scene: PackedScene = preload("res://addons/material_creator/editor/material_dock.tscn") + _material_creator_dock = dock_scene.instantiate() + _material_creator_dock.editor_interface = get_editor_interface() + var dock_scale: float = EditorInterface.get_editor_scale() * 0.85 + _material_creator_dock.custom_minimum_size *= dock_scale + for child in _material_creator_dock.find_children("*", "Control"): + child.custom_minimum_size *= dock_scale + add_control_to_dock(DOCK_SLOT_LEFT_UL, _material_creator_dock) func _exit_tree() -> void: - remove_control_from_docks(io_material_dialog) - - if _loader: - ResourceLoader.remove_resource_format_loader(_loader) - _loader = null - if _saver: - ResourceSaver.remove_resource_format_saver(_saver) - _saver = null + remove_control_from_docks(_material_creator_dock) + ResourceLoader.remove_resource_format_loader(_silly_mat_loader) + ResourceSaver.remove_resource_format_saver(_silly_mat_saver) + remove_import_plugin(_import_as_silly_mat_res) + remove_import_plugin(_import_as_standard_mat) diff --git a/plugins/addons/material_creator/material_resource.gd b/plugins/addons/material_creator/material_resource.gd deleted file mode 100644 index 1da13d4f..00000000 --- a/plugins/addons/material_creator/material_resource.gd +++ /dev/null @@ -1,47 +0,0 @@ -@tool -extends Resource -class_name SillyMaterialResource - -# Use export to make properties visible and serializable in the inspector and for resource saving/loading. -@export var albedo_color: Color = Color.BLACK -@export var metallic_strength: float = 0.0 -@export var roughness_strength: float = 0.0 - - -# Create a StandardMaterial3D from the resource's properties. -# Convert our data into a dictionary so we can convert it -# into the JSON format. -func make_json() -> String: - var json_dict := {} - - json_dict["albedo_color"] = {} - json_dict["albedo_color"]["r"] = albedo_color.r - json_dict["albedo_color"]["g"] = albedo_color.g - json_dict["albedo_color"]["b"] = albedo_color.b - - json_dict["metallic_strength"] = metallic_strength - json_dict["roughness_strength"] = roughness_strength - - return JSON.stringify(json_dict) - - -# Convert the passed in string to a JSON dictionary, and then -# fill in our data. -func from_json(json_dict_as_string: String) -> void: - var json_dict: Dictionary = JSON.parse_string(json_dict_as_string) - - albedo_color.r = json_dict["albedo_color"]["r"] - albedo_color.g = json_dict["albedo_color"]["g"] - albedo_color.b = json_dict["albedo_color"]["b"] - - metallic_strength = json_dict["metallic_strength"] - roughness_strength = json_dict["roughness_strength"] - - -# Make a StandardMaterial3D using our variables. -func make_material() -> StandardMaterial3D: - var mat = StandardMaterial3D.new() - mat.albedo_color = albedo_color - mat.metallic = metallic_strength - mat.roughness = roughness_strength - return mat diff --git a/plugins/addons/material_creator/plugin.cfg b/plugins/addons/material_creator/plugin.cfg index 9bb29d51..286a7104 100644 --- a/plugins/addons/material_creator/plugin.cfg +++ b/plugins/addons/material_creator/plugin.cfg @@ -2,6 +2,6 @@ name="Material Creator Plugin Demo" description="Loads and saves a 3D Material from an external text file" -author="TwistedTwigleg" +author="Aaron Franke, TwistedTwigleg, Šarūnas Ramonas" version="1.0" script="material_plugin.gd" diff --git a/plugins/addons/material_creator/silly_material_resource.gd b/plugins/addons/material_creator/silly_material_resource.gd new file mode 100644 index 00000000..e8611fa6 --- /dev/null +++ b/plugins/addons/material_creator/silly_material_resource.gd @@ -0,0 +1,109 @@ +## Example class that can be imported, exported, loaded, saved, etc, in various ways. +## +## - To perform an editor import as a `SillyMaterialResource`, the class +## `ImportSillyMatAsSillyMaterialResource` will handle files in the `res://` +## folder ending in `.silly_mat_importable` and import them. +## as long as "Silly Material Resource" is selected in the Import dock. +## Then `ResourceLoader.load()` will return a read-only `SillyMaterialResource`. +## +## - To perform an editor import as a `StandardMaterial3D`, the class +## `ImportSillyMatAsStandardMaterial3D` will handle files in the `res://` +## folder ending in `.silly_mat_importable` and import them, +## as long as "Standard Material 3D" is selected in the Import dock. +## Then `ResourceLoader.load()` will return a read-only `StandardMaterial3D`. +## +## - To perform an editor load as a SillyMaterialResource, the class +## `SillyMatFormatLoader` will handle files in the `res://` +## folder ending in `.silly_mat_loadable` and load them. +## Then `ResourceLoader.load()` will return a writeable `SillyMaterialResource`. +## This can then be saved back to a file with `SillyMatFormatSaver`. +## +## - To perform a runtime (or editor) import into a StandardMaterial3D, run the +## `read_from_file` function, which reads the data from a file and runs +## `from_json_dictionary`, then run `to_material` to generate a material. +## +## - To perform a runtime (or editor) export of a StandardMaterial3D, run +## `from_material` to convert a material, then run the `write_to_file` +## function, which runs `to_json_dictionary` and saves this to a file. +## +## These functions should be placed in this class to support runtime imports +## and exports, but the editor classes can also make use of these functions, +## allowing the editor-only classes to be lightweight wrappers. +## +## For a more comprehensive example, see the GLTF module in Godot's source code. +## For a less comprehensive example, see the "simple_import_plugin" folder. +@tool +class_name SillyMaterialResource +extends Resource + + +# Use export to make properties visible in the inspector +# and serializable for resource saving/loading. +@export var albedo_color: Color = Color.BLACK +@export var metallic_strength: float = 0.0 +@export var roughness_strength: float = 0.0 + + +## Given a Dictionary parsed from JSON data, read in the data as a new SillyMaterialResource. +static func from_json_dictionary(json_dictionary: Dictionary) -> SillyMaterialResource: + var ret := SillyMaterialResource.new() + # Note: In an actual importer where you need to handle arbitrary user data, + # you may wish to do things like checking if the key exists, checking if + # the value is an array, checking if the array has a length of 3, checking + # if each value in the array is a number, and so on. + # For simplicity, these things are omitted from this demo's example code. + var albedo_array: Array = json_dictionary["albedo_color"] + ret.albedo_color.r = albedo_array[0] + ret.albedo_color.g = albedo_array[1] + ret.albedo_color.b = albedo_array[2] + ret.metallic_strength = json_dictionary["metallic_strength"] + ret.roughness_strength = json_dictionary["roughness_strength"] + return ret + + +## Convert SillyMaterialResource data into a Dictionary for saving as JSON. +## To perform a runtime export of a StandardMaterial3D, run this function after `from_material`. +func to_json_dictionary() -> Dictionary: + return { + "albedo_color": [albedo_color.r, albedo_color.g, albedo_color.b], + "metallic_strength": metallic_strength, + "roughness_strength": roughness_strength, + } + + +## Given a StandardMaterial3D, copy its data to a new SillyMaterialResource. +static func from_material(mat: StandardMaterial3D) -> SillyMaterialResource: + var ret := SillyMaterialResource.new() + ret.albedo_color = mat.albedo_color + ret.metallic_strength = mat.metallic + ret.roughness_strength = mat.roughness + return ret + + +## Create a new StandardMaterial3D using the data in this SillyMaterialResource. +func to_material() -> StandardMaterial3D: + var mat = StandardMaterial3D.new() + mat.albedo_color = albedo_color + mat.metallic = metallic_strength + mat.roughness = roughness_strength + return mat + + +## Wrapper around `from_json_dictionary` that reads from a file at the given path. +static func read_from_file(path: String) -> SillyMaterialResource: + var mat_file := FileAccess.open(path, FileAccess.READ) + if mat_file == null: + return null + var json_dict: Dictionary = JSON.parse_string(mat_file.get_as_text()) + return from_json_dictionary(json_dict) + + +## Wrapper around `to_json_dictionary` that writes to a file at the given path. +func write_to_file(path: String) -> Error: + var mat_file := FileAccess.open(path, FileAccess.WRITE) + if mat_file == null: + return ERR_CANT_OPEN + var json_dict: Dictionary = to_json_dictionary() + mat_file.store_string(JSON.stringify(json_dict)) + mat_file.store_string("\n") + return OK diff --git a/plugins/addons/material_creator/material_resource.gd.uid b/plugins/addons/material_creator/silly_material_resource.gd.uid similarity index 100% rename from plugins/addons/material_creator/material_resource.gd.uid rename to plugins/addons/material_creator/silly_material_resource.gd.uid diff --git a/plugins/addons/material_import_plugin/test.mtxt.import b/plugins/addons/material_import_plugin/test.mtxt.import deleted file mode 100644 index 1d942b1d..00000000 --- a/plugins/addons/material_import_plugin/test.mtxt.import +++ /dev/null @@ -1,15 +0,0 @@ -[remap] - -importer="demos.sillymaterial" -type="Material" -uid="uid://bqja7mgxfmfqa" -path="res://.godot/imported/test.mtxt-32ce4469df24b9f725d1e3476ff3b332.res" - -[deps] - -source_file="res://addons/material_import_plugin/test.mtxt" -dest_files=["res://.godot/imported/test.mtxt-32ce4469df24b9f725d1e3476ff3b332.res"] - -[params] - -use_red_anyway=false diff --git a/plugins/addons/material_import_plugin/README.md b/plugins/addons/simple_import_plugin/README.md similarity index 100% rename from plugins/addons/material_import_plugin/README.md rename to plugins/addons/simple_import_plugin/README.md diff --git a/plugins/addons/material_import_plugin/import.gd b/plugins/addons/simple_import_plugin/import.gd similarity index 98% rename from plugins/addons/material_import_plugin/import.gd rename to plugins/addons/simple_import_plugin/import.gd index 57cbcc95..443e5bb9 100644 --- a/plugins/addons/material_import_plugin/import.gd +++ b/plugins/addons/simple_import_plugin/import.gd @@ -7,7 +7,7 @@ enum Preset { func _get_importer_name() -> String: - return "demos.sillymaterial" + return "demos.mtxt" func _get_visible_name() -> String: diff --git a/plugins/addons/material_import_plugin/import.gd.uid b/plugins/addons/simple_import_plugin/import.gd.uid similarity index 100% rename from plugins/addons/material_import_plugin/import.gd.uid rename to plugins/addons/simple_import_plugin/import.gd.uid diff --git a/plugins/addons/material_import_plugin/plugin.cfg b/plugins/addons/simple_import_plugin/plugin.cfg similarity index 77% rename from plugins/addons/material_import_plugin/plugin.cfg rename to plugins/addons/simple_import_plugin/plugin.cfg index f9eab604..05c99408 100644 --- a/plugins/addons/material_import_plugin/plugin.cfg +++ b/plugins/addons/simple_import_plugin/plugin.cfg @@ -1,6 +1,6 @@ [plugin] -name="Material Importer Plugin Demo" +name="Simple Importer Plugin Demo" description="Imports a 3D Material from an external text file" author="George Marques" version="1.0" diff --git a/plugins/addons/material_import_plugin/plugin.gd b/plugins/addons/simple_import_plugin/plugin.gd similarity index 100% rename from plugins/addons/material_import_plugin/plugin.gd rename to plugins/addons/simple_import_plugin/plugin.gd diff --git a/plugins/addons/material_import_plugin/plugin.gd.uid b/plugins/addons/simple_import_plugin/plugin.gd.uid similarity index 100% rename from plugins/addons/material_import_plugin/plugin.gd.uid rename to plugins/addons/simple_import_plugin/plugin.gd.uid diff --git a/plugins/addons/material_import_plugin/test.mtxt b/plugins/addons/simple_import_plugin/test.mtxt similarity index 100% rename from plugins/addons/material_import_plugin/test.mtxt rename to plugins/addons/simple_import_plugin/test.mtxt diff --git a/plugins/addons/simple_import_plugin/test.mtxt.import b/plugins/addons/simple_import_plugin/test.mtxt.import new file mode 100644 index 00000000..527522e4 --- /dev/null +++ b/plugins/addons/simple_import_plugin/test.mtxt.import @@ -0,0 +1,15 @@ +[remap] + +importer="demos.mtxt" +type="Material" +uid="uid://bqja7mgxfmfqa" +path="res://.godot/imported/test.mtxt-cc369242bc971647fccdadd6e971f1d0.res" + +[deps] + +source_file="res://addons/simple_import_plugin/test.mtxt" +dest_files=["res://.godot/imported/test.mtxt-cc369242bc971647fccdadd6e971f1d0.res"] + +[params] + +use_red_anyway=false diff --git a/plugins/project.godot b/plugins/project.godot index ee109955..d9728e32 100644 --- a/plugins/project.godot +++ b/plugins/project.godot @@ -31,7 +31,7 @@ gdscript/warnings/untyped_declaration=1 [editor_plugins] -enabled=PackedStringArray("res://addons/custom_node/plugin.cfg", "res://addons/main_screen/plugin.cfg", "res://addons/material_creator/plugin.cfg", "res://addons/material_import_plugin/plugin.cfg") +enabled=PackedStringArray("res://addons/custom_node/plugin.cfg", "res://addons/main_screen/plugin.cfg", "res://addons/material_creator/plugin.cfg", "res://addons/simple_import_plugin/plugin.cfg") [rendering] diff --git a/plugins/screenshots/heart_custom_node.webp b/plugins/screenshots/heart_custom_node.webp new file mode 100644 index 00000000..c7078575 Binary files /dev/null and b/plugins/screenshots/heart_custom_node.webp differ diff --git a/plugins/screenshots/heart_plugin.png b/plugins/screenshots/heart_plugin.png deleted file mode 100644 index 57ad57aa..00000000 Binary files a/plugins/screenshots/heart_plugin.png and /dev/null differ diff --git a/plugins/screenshots/main_screen_plugin.png b/plugins/screenshots/main_screen_plugin.png deleted file mode 100644 index eb7ea539..00000000 Binary files a/plugins/screenshots/main_screen_plugin.png and /dev/null differ diff --git a/plugins/screenshots/main_screen_plugin.webp b/plugins/screenshots/main_screen_plugin.webp new file mode 100644 index 00000000..fbd5cbac Binary files /dev/null and b/plugins/screenshots/main_screen_plugin.webp differ diff --git a/plugins/screenshots/material_creator_plugin_1.png b/plugins/screenshots/material_creator_plugin_1.png deleted file mode 100644 index b9278b32..00000000 Binary files a/plugins/screenshots/material_creator_plugin_1.png and /dev/null differ diff --git a/plugins/screenshots/material_creator_plugin_2.png b/plugins/screenshots/material_creator_plugin_2.png deleted file mode 100644 index cbd42323..00000000 Binary files a/plugins/screenshots/material_creator_plugin_2.png and /dev/null differ diff --git a/plugins/screenshots/material_creator_plugin_applied.webp b/plugins/screenshots/material_creator_plugin_applied.webp new file mode 100644 index 00000000..a1fa75cd Binary files /dev/null and b/plugins/screenshots/material_creator_plugin_applied.webp differ diff --git a/plugins/screenshots/material_creator_plugin_dock.webp b/plugins/screenshots/material_creator_plugin_dock.webp new file mode 100644 index 00000000..23160823 Binary files /dev/null and b/plugins/screenshots/material_creator_plugin_dock.webp differ diff --git a/plugins/screenshots/material_creator_plugin_imported_file_is_read_only.webp b/plugins/screenshots/material_creator_plugin_imported_file_is_read_only.webp new file mode 100644 index 00000000..6e0d9857 Binary files /dev/null and b/plugins/screenshots/material_creator_plugin_imported_file_is_read_only.webp differ diff --git a/plugins/screenshots/material_import_plugin.png b/plugins/screenshots/material_import_plugin.png deleted file mode 100644 index 4e8dc104..00000000 Binary files a/plugins/screenshots/material_import_plugin.png and /dev/null differ diff --git a/plugins/screenshots/simple_import_plugin.webp b/plugins/screenshots/simple_import_plugin.webp new file mode 100644 index 00000000..5491688b Binary files /dev/null and b/plugins/screenshots/simple_import_plugin.webp differ