Documentation Index Fetch the complete documentation index at: https://mintlify.com/CadeEvs/FrostyToolsuite/llms.txt
Use this file to discover all available pages before exploring further.
Custom asset editors provide specialized UI for viewing and editing specific asset types. They inherit from FrostyAssetEditor and integrate with the Frosty Editor’s tab system.
FrostyAssetEditor Base Class
From ~/workspace/source/FrostyPlugin/Controls/FrostyAssetEditor.cs:32:
public class FrostyAssetEditor : Control
{
// Access to the loaded asset
public object RootObject => asset . RootObject ;
public IEnumerable < object > RootObjects => asset . RootObjects ;
public IEnumerable < object > Objects => asset . Objects ;
public EbxAsset Asset => asset ;
// The asset entry being edited
public AssetEntry AssetEntry { get ; private set ; }
// Marks the asset as modified
public bool AssetModified { get ; set ; }
// Logger for error messages
protected ILogger logger ;
// Loaded asset and dependencies
protected EbxAsset asset ;
protected Dictionary < Guid , EbxAsset > dependentObjects ;
public FrostyAssetEditor ( ILogger inLogger )
{
logger = inLogger ;
}
}
Creating a Basic Editor
Create Editor Class
Inherit from FrostyAssetEditor and pass the logger to the base constructor: using Frosty . Core . Controls ;
using FrostySdk . Interfaces ;
using System . Windows . Controls ;
namespace MyPlugin
{
public class MyAssetEditor : FrostyAssetEditor
{
public MyAssetEditor ( ILogger inLogger ) : base ( inLogger )
{
}
}
}
Define the UI Template
Create a WPF control template in your plugin’s XAML resource dictionary: < ResourceDictionary xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local = "clr-namespace:MyPlugin"
xmlns:frosty = "clr-namespace:Frosty.Core.Controls;assembly=FrostyCore" >
< Style TargetType = "{x:Type local:MyAssetEditor}" >
< Setter Property = "Template" >
< Setter.Value >
< ControlTemplate TargetType = "{x:Type local:MyAssetEditor}" >
< Grid >
< Grid.ColumnDefinitions >
< ColumnDefinition Width = "*" />
< ColumnDefinition Width = "Auto" />
< ColumnDefinition Width = "300" />
</ Grid.ColumnDefinitions >
<!-- Main viewer area -->
< Border Grid.Column = "0" Background = "#2D2D30" >
<!-- Your custom UI here -->
</ Border >
<!-- Splitter -->
< GridSplitter Grid.Column = "1" Width = "5"
HorizontalAlignment = "Stretch" />
<!-- Property grid -->
< frosty:FrostyPropertyGrid Grid.Column = "2"
x:Name = "PART_AssetPropertyGrid"
AssetModified = "{Binding AssetModified,
RelativeSource={RelativeSource
TemplatedParent},
Mode=TwoWay}" />
</ Grid >
</ ControlTemplate >
</ Setter.Value >
</ Setter >
</ Style >
</ ResourceDictionary >
Register the Template
Override the static constructor to register your control template: static MyAssetEditor ()
{
DefaultStyleKeyProperty . OverrideMetadata (
typeof ( MyAssetEditor ),
new FrameworkPropertyMetadata ( typeof ( MyAssetEditor )));
}
Load Asset Data
Override LoadAsset to perform custom loading: protected override EbxAsset LoadAsset ( EbxAssetEntry entry )
{
EbxAsset asset = base . LoadAsset ( entry );
// Access the root object
dynamic rootObj = asset . RootObject ;
// Perform custom loading logic
// e.g., load associated resources
return asset ;
}
Accessing Asset Data
Root Object Access
The RootObject property provides access to the main asset object:
public override void OnApplyTemplate ()
{
base . OnApplyTemplate ();
// Access asset data as dynamic
dynamic rootObj = RootObject ;
// Access typed properties
string assetName = rootObj . Name ;
// Load associated resources
if ( rootObj . Resource != null )
{
ResAssetEntry resEntry = App . AssetManager . GetResEntry ( rootObj . Resource );
// Load resource data
}
}
Loading Dependencies
Dependencies are automatically loaded. Access them via the dependentObjects dictionary:
public void LoadDependentAsset ( Guid guid )
{
EbxAsset dependentAsset = GetDependentObject ( guid );
if ( dependentAsset != null )
{
dynamic depObj = dependentAsset . RootObject ;
// Use dependent asset
}
}
Adding New Dependencies
From ~/workspace/source/FrostyPlugin/Controls/FrostyAssetEditor.cs:106:
public void AddDependentObject ( Guid guid )
{
if ( asset . AddDependency ( guid ))
{
if ( ! dependentObjects . ContainsKey ( guid ))
dependentObjects . Add ( guid , App . AssetManager . GetEbx ( App . AssetManager . GetEbxEntry ( guid )));
}
}
Modifying Assets
To mark an asset as modified, set the AssetModified property to true. This automatically saves changes:
From ~/workspace/source/FrostyPlugin/Controls/FrostyAssetEditor.cs:64:
private static void OnAssetModifiedChanged ( DependencyObject o , DependencyPropertyChangedEventArgs e )
{
bool isModified = ( bool ) e . NewValue ;
if ( isModified )
{
FrostyAssetEditor editor = o as FrostyAssetEditor ;
editor . asset . Update ();
App . AssetManager . ModifyEbx ( editor . AssetEntry . Name , editor . asset );
editor . InvokeOnAssetModified ();
editor . AssetModified = false ;
}
}
Example: Modifying Properties
private void OnPropertyChanged ()
{
dynamic rootObj = RootObject ;
// Modify a property
rootObj . SomeProperty = newValue ;
// Mark asset as modified
AssetModified = true ;
}
Property Grid Integration
The FrostyPropertyGrid control provides automatic property editing. Name it PART_AssetPropertyGrid in your template:
< frosty:FrostyPropertyGrid x:Name = "PART_AssetPropertyGrid"
AssetModified = "{Binding AssetModified,
RelativeSource={RelativeSource TemplatedParent},
Mode=TwoWay}" />
The property grid automatically:
Displays all properties of the root object
Binds to AssetModified to save changes
Provides type-appropriate editors (text boxes, dropdowns, etc.)
Add custom toolbar buttons to your editor:
public override List < ToolbarItem > RegisterToolbarItems ()
{
List < ToolbarItem > items = base . RegisterToolbarItems ();
items . Add ( new ToolbarItem (
"Export" , // Text
"Export asset to file" , // Tooltip
"Images/Export.png" , // Icon path
new RelayCommand ( Export_Click , CanExport ) // Command
));
return items ;
}
private void Export_Click ( object state )
{
// Handle export
}
private bool CanExport ( object state )
{
return AssetEntry != null ;
}
Handling Resources
Loading Texture Resources
private void LoadTexture ()
{
dynamic textureAsset = RootObject ;
// Get the resource entry
ResAssetEntry resEntry = App . AssetManager . GetResEntry ( textureAsset . Resource );
// Load as Texture
Texture texture = App . AssetManager . GetResAs < Texture >( resEntry );
// Access texture properties
int width = texture . Width ;
int height = texture . Height ;
// Get chunk data if needed
if ( texture . ChunkId != Guid . Empty )
{
ChunkAssetEntry chunkEntry = App . AssetManager . GetChunkEntry ( texture . ChunkId );
byte [] chunkData = App . AssetManager . GetChunk ( chunkEntry );
}
}
Loading MeshSet Resources
private void LoadMesh ()
{
dynamic meshAsset = RootObject ;
// Get mesh resource
ResAssetEntry resEntry = App . AssetManager . GetResEntry ( meshAsset . MeshSetResource );
MeshSet meshSet = App . AssetManager . GetResAs < MeshSet >( resEntry );
// Iterate LODs
foreach ( MeshSetLod lod in meshSet . Lods )
{
// Load chunk data for each LOD
if ( lod . ChunkId != Guid . Empty )
{
ChunkAssetEntry chunk = App . AssetManager . GetChunkEntry ( lod . ChunkId );
// Process chunk
}
}
}
Complete Example
Here’s a simplified texture editor:
using Frosty . Core . Controls ;
using FrostySdk . Interfaces ;
using FrostySdk . IO ;
using FrostySdk . Managers ;
using FrostySdk . Resources ;
using System . Windows ;
using System . Windows . Controls ;
using System . Windows . Media ;
using System . Windows . Media . Imaging ;
namespace TexturePlugin
{
public class FrostyTextureEditor : FrostyAssetEditor
{
private Image imageControl ;
static FrostyTextureEditor ()
{
DefaultStyleKeyProperty . OverrideMetadata (
typeof ( FrostyTextureEditor ),
new FrameworkPropertyMetadata ( typeof ( FrostyTextureEditor )));
}
public FrostyTextureEditor ( ILogger inLogger ) : base ( inLogger )
{
}
public override void OnApplyTemplate ()
{
base . OnApplyTemplate ();
// Get image control from template
imageControl = GetTemplateChild ( "PART_ImageViewer" ) as Image ;
// Load and display texture
LoadTexture ();
}
private void LoadTexture ()
{
dynamic textureAsset = RootObject ;
// Load resource
ResAssetEntry resEntry = App . AssetManager . GetResEntry ( textureAsset . Resource );
Texture texture = App . AssetManager . GetResAs < Texture >( resEntry );
// Convert to WPF image source
BitmapSource bitmap = ConvertTextureToBitmap ( texture );
// Display in UI
if ( imageControl != null )
{
imageControl . Source = bitmap ;
}
}
private BitmapSource ConvertTextureToBitmap ( Texture texture )
{
// Implementation depends on texture format
// Return converted BitmapSource
return null ;
}
}
}
Cleanup
Override Closed() to release resources when the editor is closed:
public override void Closed ()
{
base . Closed ();
// Release any resources
imageControl = null ;
// Dispose any disposable objects
}
Always clean up unmanaged resources (textures, 3D meshes, etc.) to prevent memory leaks.
Next Steps
Custom Actions Add context menu actions and commands
Attributes Reference Complete attribute reference