reckfellENGINE

Import a custom format: RGB material

A complete C# importer, roughness settings and reimport.

On this page

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:

Configuration
[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().

C#
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:

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

  1. Build the C# project using the editor build button. Fix build errors first: an unbuilt plugin cannot load.
  2. Enable RGB Material Importer in Project → Project Settings → Plugins. Registration schedules a resource scan; wait for completion.
  3. Create asset://paint.rfcolor containing 0,128,255 and save it. Do not name it .txt: the importer recognizes the rfcolor extension.
  4. Select the file in FileSystem. Import should show RGB Material with Roughness set to 0.7.
  5. Assign this file to Material Override on a MeshInstance3D with 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.
  6. In Import, change Roughness to 0.25 and click Reimport. This changes material roughness, not source RGB. Then change the source contents to 255,32,0 and reimport: the color changes while the saved 0.25 setting 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

FilePurpose
paint.rfcolorSource data. Keep it in version control.
paint.rfcolor.importImporter metadata, settings and the result reference. Keep it beside the source.
.reckfell/imported/…materialGenerated 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

MethodRequirement
_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

SymptomCheck
Plugin missing from the listCheck addons/docs_rgb/plugin.cfg, its script field, a successful C# build and [Tool].
File is not importedIs the plugin enabled? Is the extension correct? Has scanning completed? Never reimport recursively from _Import().
ParseErrorSupply exactly three decimal integers 0…255 separated by commas. 256, -1, NaN, fractional channels and files larger than 128 bytes are rejected.
InvalidParameterroughness must be a finite number from 0 to 1. The string "0.7" is not a numeric setting value.
Open errorCheck source existence and access permissions; the importer returns FileAccess.GetOpenError().
Edited code still behaves as beforeRebuild 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 pluginRebuilding 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.

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.