Import a custom format: RGB material
A complete C# importer, roughness settings and reimport.
An importer turns a custom source format into a regular Reckfell resource. Here paint.rfcolor contains three integer RGB channels from 0 to 255; the result is a StandardMaterial3D with configurable roughness. After import, load the source path asset://paint.rfcolor: the resource system resolves the prepared material.
Use the Reckfell 1.0.0 C# editor and a project that already builds. This is an editor plugin, not a game Entity script. Read making an editor plugin first if you have not enabled project plugins before.
1. Create three files
Put them in asset://addons/docs_rgb/. Each C# filename must match its class name.
plugin.cfg:
[plugin]
name="RGB Material Importer"
description="Imports RGB bytes as a material."
author="Reckfell"
version="1.0.0"
script="RgbImportPlugin.cs"RgbImportPlugin.cs registers the importer on enable and unregisters it on disable. EditorImportPlugin is reference-counted: after removing it from the registry, release the owned C# wrapper with Dispose(), not QueueFree().
using Reckfell;
[Tool]
public partial class RgbImportPlugin : EditorPlugin
{
private RgbMaterialImporter? _importer;
public override void _EnterTree()
{
_importer = new RgbMaterialImporter();
AddImportPlugin(_importer);
}
public override void _ExitTree()
{
if (_importer is null) return;
RemoveImportPlugin(_importer);
_importer.Dispose();
_importer = null;
}
}RgbMaterialImporter.cs declares the format and performs conversion:
using System.Globalization;
using Reckfell;
using Reckfell.Collections;
[Tool]
public partial class RgbMaterialImporter : EditorImportPlugin
{
public override string _GetImporterName() => "reckfell.docs.rgb_material";
public override string _GetVisibleName() => "RGB Material";
public override string[] _GetRecognizedExtensions() => ["rfcolor"];
public override string _GetSaveExtension() => "material";
public override string _GetResourceType() => "StandardMaterial3D";
public override int _GetPresetCount() => 1;
public override string _GetPresetName(int presetIndex) => "Default";
public override int _GetFormatVersion() => 1;
public override bool _CanImportThreaded() => false;
public override Array<Dictionary> _GetImportOptions(string path, int presetIndex) =>
[
new Dictionary
{
{ "name", "roughness" },
{ "default_value", 0.7 },
{ "property_hint", (int)PropertyHint.Range },
{ "hint_string", "0,1,0.01" }
}
];
public override Error _Import(string sourceFile, string savePath,
Dictionary options, Array<string> platformVariants, Array<string> genFiles)
{
using var file = FileAccess.Open(sourceFile, FileAccess.ModeFlags.Read);
if (file is null) return FileAccess.GetOpenError();
if (file.GetLength() > 128) return Error.ParseError;
string[] parts = file.GetAsText().Trim().Split(',');
if (parts.Length != 3) return Error.ParseError;
byte[] rgb = new byte[3];
for (int i = 0; i < rgb.Length; i++)
if (!byte.TryParse(parts[i], NumberStyles.Integer,
CultureInfo.InvariantCulture, out rgb[i])) return Error.ParseError;
double roughness = 0.7;
if (options.ContainsKey("roughness"))
{
Variant value = options["roughness"];
if (value.VariantType != Variant.Type.Float &&
value.VariantType != Variant.Type.Int) return Error.InvalidParameter;
roughness = value.AsDouble();
}
if (!double.IsFinite(roughness) || roughness < 0 || roughness > 1)
return Error.InvalidParameter;
using var material = new StandardMaterial3D
{
AlbedoColor = Color.Color8(rgb[0], rgb[1], rgb[2]),
Roughness = (float)roughness
};
return ResourceSaver.Save(material, savePath + "." + _GetSaveExtension());
}
}_Import() does not access the editor tree here. Parallel import is deliberately disabled: enable _CanImportThreaded() only after verifying the thread safety of the entire handler and its dependencies.
2. Enable the importer and check the result
- Build the C# project using the editor build button. Fix build errors first: an unbuilt plugin cannot load.
- Enable RGB Material Importer in Project → Project Settings → Plugins. Registration schedules a resource scan; wait for completion.
- Create
asset://paint.rfcolorcontaining0,128,255and save it. Do not name it.txt: the importer recognizes therfcolorextension. - Select the file in FileSystem. Import should show RGB Material with Roughness set to
0.7. - Assign this file to Material Override on a
MeshInstance3Dwith a mesh. The resulting material has RGB(0,128,255). Screen brightness depends on lighting, exposure and tone mapping; material channels do not guarantee an identical screen pixel color. - In Import, change Roughness to
0.25and click Reimport. This changes material roughness, not source RGB. Then change the source contents to255,32,0and reimport: the color changes while the saved0.25setting remains.
Load with ResourceLoader.Load<StandardMaterial3D>("asset://paint.rfcolor"). Do not use a path inside .reckfell/imported: it belongs to the cache and may change. When checking reimport programmatically, account for already loaded resources; a test that reads from disk can explicitly use cacheMode: ResourceLoader.CacheMode.Ignore.
Stored files and version control
| File | Purpose |
|---|---|
paint.rfcolor | Source data. Keep it in version control. |
paint.rfcolor.import | Importer metadata, settings and the result reference. Keep it beside the source. |
.reckfell/imported/…material | Generated cache with the default project configuration. Do not edit manually. |
addons/docs_rgb/… | Importer code and configuration. The editor needs these to rebuild the cache. |
The editor supplies savePath without an extension. The importer saves savePath + ".material" and returns the result of ResourceSaver.Save(). Do not return Error.Ok after a failed save. Empty platformVariants and genFiles are correct here: there is one primary material and no extra variants or generated dependencies.
Importer contract
| Method | Requirement |
|---|---|
_GetImporterName() | Unique stable identifier stored in .import. Do not rename it merely to change a label. |
_GetVisibleName() | Readable Import dock label; it may differ from the identifier. |
_GetRecognizedExtensions() | Extensions without dots. Use your own rfcolor instead of capturing every .txt or .json file. |
_GetResourceType() / _GetSaveExtension() | Actual resource class and output format: StandardMaterial3D / material. |
_GetImportOptions() | Array of dictionaries. Each setting requires name and default_value; the value determines the property type. A range hint improves the UI but _Import() must still validate data. |
_GetPresetCount() / _GetPresetName() | One Default preset here. Even without presets, _GetImportOptions() must return a valid array. |
_GetFormatVersion() | Importer format version, not the engine version. Increment it for incompatible changes to generated data. |
If multiple importers recognize an extension, account for _GetPriority() and explicit importer selection in the file settings. AddImportPlugin()'s firstPriority flag changes registration order; it does not bypass numerical priority comparison. A unique custom extension needs no extra priority settings.
Diagnose failures
| Symptom | Check |
|---|---|
| Plugin missing from the list | Check addons/docs_rgb/plugin.cfg, its script field, a successful C# build and [Tool]. |
| File is not imported | Is the plugin enabled? Is the extension correct? Has scanning completed? Never reimport recursively from _Import(). |
ParseError | Supply exactly three decimal integers 0…255 separated by commas. 256, -1, NaN, fractional channels and files larger than 128 bytes are rejected. |
InvalidParameter | roughness must be a finite number from 0 to 1. The string "0.7" is not a numeric setting value. |
| Open error | Check source existence and access permissions; the importer returns FileAccess.GetOpenError(). |
| Edited code still behaves as before | Rebuild C#, disable and enable the plugin, then Reimport the file. Changing code does not guarantee reimport of all existing results. |
| Import stops working after disabling the plugin | Rebuilding the cache requires the same importer. Enable the plugin; do not hide the problem by deleting the source and .import file. |
Verification limits
The installed 1.0.0 editor verified actual FileSystem import, .import metadata, loading by source path, reimport after option and source edits, uppercase extension recognition, invalid data and plugin re-enable. Checks ran without a graphical window: material appearance, physical Import dock interaction, game export, parallel processing, feature-tag variants and extra generated dependencies are not established by this test.