Overhaul silly material creator plugin demo (#1261)
9
plugins/.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
|
Before Width: | Height: | Size: 809 B |
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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).
|
||||
|
||||

|
||||
|
||||
## 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.
|
||||
|
||||

|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
uid://u2esoq3eygve
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cmgoxx63wybil
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
uid://b004gkmug1qlt
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cvaleef5ekitp
|
||||
145
plugins/addons/material_creator/editor/material_creator.gd
Normal file
@@ -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())
|
||||
151
plugins/addons/material_creator/editor/material_dock.tscn
Normal file
@@ -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"]
|
||||
9
plugins/addons/material_creator/examples/blue.tres
Normal file
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
{"albedo_color":[0.0666666701436043,0.933333337306976,0.933333337306976],"metallic_strength":0.8,"roughness_strength":0.2}
|
||||
@@ -0,0 +1 @@
|
||||
uid://fhh5emd0x6wb
|
||||
@@ -0,0 +1 @@
|
||||
{"albedo_color":[0.0666666701436043,0.933333337306976,0.0666666701436043],"metallic_strength":0.35,"roughness_strength":0.65}
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
{"albedo_color":[0.933333337306976,0.933333337306976,0.0666666701436043],"metallic_strength":0.65,"roughness_strength":0.35}
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
{"albedo_color":[0.933333337306976,0.933333337306976,0.0666666701436043],"metallic_strength":0.65,"roughness_strength":0.35}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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")
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
109
plugins/addons/material_creator/silly_material_resource.gd
Normal file
@@ -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
|
||||
@@ -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
|
||||
@@ -7,7 +7,7 @@ enum Preset {
|
||||
|
||||
|
||||
func _get_importer_name() -> String:
|
||||
return "demos.sillymaterial"
|
||||
return "demos.mtxt"
|
||||
|
||||
|
||||
func _get_visible_name() -> String:
|
||||
@@ -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"
|
||||
15
plugins/addons/simple_import_plugin/test.mtxt.import
Normal file
@@ -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
|
||||
@@ -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]
|
||||
|
||||
|
||||
BIN
plugins/screenshots/heart_custom_node.webp
Normal file
|
After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 62 KiB |
BIN
plugins/screenshots/main_screen_plugin.webp
Normal file
|
After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 93 KiB |
BIN
plugins/screenshots/material_creator_plugin_applied.webp
Normal file
|
After Width: | Height: | Size: 496 KiB |
BIN
plugins/screenshots/material_creator_plugin_dock.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 90 KiB |
BIN
plugins/screenshots/simple_import_plugin.webp
Normal file
|
After Width: | Height: | Size: 76 KiB |