Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions docs/contributors/asset-system/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Asset System — Contributor Overview

This document explains the Stride asset system for engine contributors. It covers the complete
pipeline from design-time authoring through build compilation to runtime loading, and shows
how all parts are wired together.

> **For game-project custom assets** (not engine contributions), the same pipeline applies
> with minor differences noted in each spoke file. See also the external guide:
> [Creating custom assets](https://doc.stride3d.net/latest/en/manual/scripts/custom-assets.html).

## The Three-Phase Pipeline

```mermaid
flowchart LR
A[".sdXXX file<br/>(YAML on disk)"]
B["Asset class<br/>Asset subclass"]
C["Compiler<br/>AssetCompilerBase"]
D["Content database<br/>(binary)"]
E["Runtime type<br/>ContentManager.Load"]

A -->|"deserialized into"| B
B -->|"transformed by"| C
C -->|"writes to"| D
D -->|"loaded into"| E
```

**Design time:** The author edits asset properties in GameStudio. Properties are persisted to a
`.sdXXX` YAML file. The file is deserialized into an instance of the asset class.

**Build time:** The build pipeline invokes the compiler registered for the asset type. The
compiler reads the asset class instance and produces compiled binary content, which is written
to the content database.

**Runtime:** The game calls `ContentManager.Load<RuntimeType>(url)`. The engine reads the
compiled binary and returns a typed instance.

## Assembly Map

| Layer | Key Type | Assembly | Location |
|---|---|---|---|
| Runtime type | `DataContract`-decorated class | Engine feature assembly (e.g. `Stride.Graphics`) | `sources/engine/` |
| Asset class | `Asset` subclass | `Stride.Assets` or feature assembly (e.g. `Stride.Assets.Models`) | `sources/engine/` |
| Compiler | `AssetCompilerBase` subclass | Same assembly as the asset class | `sources/engine/` (same folder as asset class) |
| Editor Tier 2 | `AssetViewModel<T>` subclass | `Stride.Assets.Presentation` | `sources/editor/Stride.Assets.Presentation/ViewModel/` |
| Editor Tier 3 VM | `AssetEditorViewModel` subclass | `Stride.Assets.Presentation` | `sources/editor/Stride.Assets.Presentation/AssetEditors/` |
| Editor Tier 3 View | `IEditorView` XAML control | `Stride.Assets.Presentation` | `sources/editor/Stride.Assets.Presentation/AssetEditors/` |
| Template | `.sdtpl` YAML file | `Stride.Assets.Presentation` | `sources/editor/Stride.Assets.Presentation/Templates/Assets/` |

## Choose a Base Class for the Asset

> **Decision tree:**
>
> - Does the asset import its primary content from an external file (`.fbx`, `.png`, `.wav`...)?
> → **`AssetWithSource`** (provides a `Source: UFile` property)
> - Does the asset have named sub-parts with parent/child hierarchy (like scenes or prefabs)?
> → **`AssetCompositeHierarchy`**
> - Does the asset have named sub-parts in a flat, unordered collection?
> → **`AssetComposite`**
> - Otherwise → **`Asset`**

See [asset-class.md](asset-class.md) for the full base-class selection table.

## Choose an Editor Tier

> **Decision tree:**
>
> - Does the asset need a dedicated editor panel (canvas, node graph, timeline...)?
> → **Tier 3** — custom `AssetEditorViewModel` + XAML view
> - Does the asset need custom commands, computed display properties, or custom drag-and-drop?
> → **Tier 2** — custom `AssetViewModel<T>`
> - Otherwise the property grid is sufficient.
> → **Tier 1** — no editor code needed

See [editor.md](editor.md) for implementation details for each tier.

## Implementation Checklist

Use this checklist when adding a new asset type. Steps marked **optional** are only needed
in specific circumstances.

### Always required

- [ ] **Runtime type** — `[DataContract]`, `[ContentSerializer]`, `[ReferenceSerializer]`,
`[DataSerializerGlobal]` → see [runtime-type.md](runtime-type.md)
- [ ] **Asset class** — `[DataContract]`, `[AssetDescription]`; add `[AssetContentType]` and
`[AssetFormatVersion]` when the asset has a compiled runtime output → see [asset-class.md](asset-class.md)
- [ ] **Compiler** — `[AssetCompiler(typeof(%%Asset%%), typeof(AssetCompilationContext))]` on
the compiler class; implement the protected `Prepare` override → see [compiler.md](compiler.md)
- [ ] **Module initializer** — `AssemblyRegistry.Register(...)` in the assembly's `Module.cs`
→ see [registration.md](registration.md)

### Recommended

- [ ] **`.sdtpl` template** — adds the asset to the **Add Asset** menu in GameStudio →
see [registration.md](registration.md#sdtpl-template-files-new-asset-menu)

### Conditional

- [ ] **`AssetViewModel<T>` (Tier 2)** — only if custom editor commands or display logic is
needed → see [editor.md](editor.md#tier-2-custom-assetviewmodelt)
- [ ] **`AssetEditorViewModel` + XAML View (Tier 3)** — only if a dedicated editor panel is
needed → see [editor.md](editor.md#tier-3-full-custom-editor)
- [ ] **`GetInputFiles` override** — only if the compiler reads external files →
see [compiler.md](compiler.md#declare-external-file-dependencies-getinputfiles)
- [ ] **`GetInputTypes` override** — only if compilation depends on another asset being
compiled first → see [compiler.md](compiler.md#declare-asset-dependencies-getinputtypes)
- [ ] **Version upgrader** — only when bumping `[AssetFormatVersion]` on an existing asset →
see [asset-class.md](asset-class.md#versioning-and-upgraders)

## Key Terms

| Term | Definition |
|---|---|
| Asset class | C# class inheriting `Asset`. Design-time representation. Serialized to a `.sdXXX` YAML file. |
| Runtime type | C# class loaded by `ContentManager` at runtime. Compiled binary form of an asset. |
| `AssetItem` | Wrapper pairing an `Asset` instance with its on-disk location and owning package. |
| Content database | Binary storage produced by the build pipeline and consumed by `ContentManager`. |
| `AssetRegistry` | Static registry mapping asset types to file extensions, compilers, runtime types, and factories. Populated automatically from attributes during assembly registration. |
| Quantum | Stride's introspection framework (`Stride.Core.Quantum`). Builds a node graph from asset properties for use in the property grid and undo/redo. See also the [asset introspection doc](https://doc.stride3d.net/latest/en/manual/engine/asset-introspection.html). |
| `.sdXXX` | The file extension used by Stride asset files. Format is YAML. The extension is registered via `[AssetDescription]`. |
| `AssetCompilationContext` | The standard compilation context for game assets. Pass as the second argument to `[AssetCompiler]`. |
109 changes: 109 additions & 0 deletions docs/contributors/asset-system/asset-class.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Asset Class

## Role

The asset class is the design-time representation of an asset. An instance is serialized to a `.sdXXX` YAML file on disk and loaded by GameStudio, where it holds all the properties an author sets in the editor. During the build pipeline the compiler reads an instance of this class and transforms it into the runtime type. The asset class is never loaded at runtime — only the compiled output is.

## Choose a Base Class

| Base class | Use when | Example |
|---|---|---|
| `Asset` | The asset's data is entirely defined inside GameStudio (no external source file). | `MaterialAsset`, `SpriteSheetAsset` |
| `AssetWithSource` | The asset imports data from an external file (e.g. `.fbx`, `.png`). Provides a `Source` property of type `UFile`. | `TextureAsset`, `SoundAsset`, `HeightmapAsset` |
| `AssetComposite` | The asset is composed of named sub-parts that can be individually referenced and overridden in derived assets. | `SceneAsset`, `PrefabAsset` |
| `AssetCompositeHierarchy<TAssetPartDesign, TAssetPart>` | Like `AssetComposite` but with a parent/child hierarchy among parts. Used for scenes and prefabs. Prefer `AssetComposite` unless you need a tree structure. | `SceneAsset`, `PrefabAsset` |

> **Decision:** Start with `Asset`. Upgrade to `AssetWithSource` if the asset's primary content
> comes from a file on disk that Stride did not produce. Use `AssetComposite` only if your asset
> must support per-part archetype inheritance.

## Required Attributes

| Attribute | Required? | Purpose |
|---|---|---|
| `[DataContract("Name")]` | Yes | YAML serialization name. Must be unique across all assets. Use a short, stable PascalCase string (e.g. `"SpriteSheet"`, `"Texture"`). |
| `[AssetDescription(".sdXXX")]` | Yes | Registers the file extension(s) for this asset type. Multiple extensions: `".sdm3d;pdxm3d"` (only the first extension has a leading dot — subsequent extensions omit it). The primary extension must be unique across all asset types. |
| `[AssetContentType(typeof(RuntimeType))]` | Yes | Maps this design-time class to its runtime type. Used by `AssetRegistry` and the build pipeline. |
| `[AssetFormatVersion(packageName, currentConst, initialVersion)]` | Yes | Declares the current serialization version and the initial version (used to trigger upgraders). `packageName` is `StrideConfig.PackageName` (`"Stride"`) for engine assets. |
| `[AssetUpgrader(...)]` | When bumping the version | See the Versioning section below. |

## Property Conventions

- Use `[DataMember(N)]` on every public property that should be serialized, where `N` is an integer that determines the YAML field order. Leave gaps (e.g. `10`, `20`, `30`) to allow future insertion without renumbering.
- Use `[DataMemberIgnore]` on properties that must not be serialized (computed values, caches).
- Properties on the asset class are the editor-facing settings. Keep the asset class free of engine types that are not available at design time (shader objects, GPU resources, etc.).
- When the asset references another asset at design time, the member type should be the runtime type (e.g. `Texture`), not the asset type (`TextureAsset`). The `AttachedReferenceManager` records the asset reference as a URL on the runtime object during compilation.

## File Extension Naming

- Engine asset extensions use the `.sd` prefix followed by a short abbreviation: `.sdtex`, `.sdmat`, `.sdm3d`, `.sdsheet`, `.sdscene`, `.sdprefab`.
- Extensions are case-insensitive and must be globally unique across the engine.
- Register new extensions in `[AssetDescription]`. Verify uniqueness by searching:
```
grep -rn 'AssetDescription("\.sd' sources/ --include="*.cs"
```

## Versioning and Upgraders

Any change to a serialized property name or type that would make existing `.sdXXX` files unreadable requires a version bump. If a change is purely additive — a new optional property with a default value — a bump is not strictly required, but it is good practice because it makes the migration boundary explicit and allows the upgrader to set a sensible default for older files.

Bump sequence:

1. Update the `CurrentVersion` constant to a new version string (e.g. `"2.0.0.0"`).
2. Add an `[AssetUpgrader]` attribute pointing to a new upgrader class.
3. Implement the upgrader.

```csharp
[AssetUpgrader(StrideConfig.PackageName, "1.0.0.0", "2.0.0.0", typeof(%%AssetName%%V2Upgrader))]
public sealed class %%AssetName%%Asset : Asset { ... }

internal class %%AssetName%%V2Upgrader : AssetUpgraderBase
{
protected override void UpgradeAsset(
AssetMigrationContext context,
PackageVersion currentVersion,
PackageVersion targetVersion,
dynamic asset,
PackageLoadingAssetFile assetFile,
OverrideUpgraderHint overrideHint)
{
// Modify the asset dynamic object to match the new schema.
// e.g. asset.NewPropertyName = asset.OldPropertyName;
// asset.OldPropertyName = DynamicYamlEmpty.Default;
}
}
```

Prefer `AssetUpgraderBase` over implementing `IAssetUpgrader` directly — it handles the YAML plumbing and exposes a simpler `UpgradeAsset` override with a dynamic view of the node. Implementing `IAssetUpgrader` directly requires working with the raw `YamlMappingNode` and manually calling `SetSerializableVersion`.

## Assembly Placement

Engine asset classes live in `sources/engine/Stride.Assets/` (for core engine assets) or in a feature-specific assembly such as `sources/engine/Stride.Assets.Models/`. The assembly must be registered with `AssemblyCommonCategories.Assets` in its `Module.cs` initializer (see [registration.md](registration.md)). Asset classes must not live in editor-only assemblies — the build pipeline runs outside the editor process and must be able to load and instantiate the asset class without any editor dependency.

## Template

```csharp
using Stride.Core;
using Stride.Core.Assets;
using Stride.Core.Assets.Compiler;

namespace Your.Namespace;

[DataContract("%%AssetName%%")]
[AssetDescription(FileExtension)]
[AssetContentType(typeof(%%AssetName%%))] // runtime type
[AssetFormatVersion(StrideConfig.PackageName, CurrentVersion, "1.0.0.0")]
// Add [AssetUpgrader(...)] here when you bump CurrentVersion — see the Versioning section above
public sealed class %%AssetName%%Asset : Asset // or AssetWithSource
{
private const string CurrentVersion = "1.0.0.0";
public const string FileExtension = ".sd%%shortname%%"; // choose a unique extension

[DataMember(10)]
public SomeType SomeProperty { get; set; }
}
```

> [!NOTE] Game projects
> Replace `StrideConfig.PackageName` with a string literal matching your game's package name
> (e.g. `"MyGame"`). The rest of the pattern is identical.
Loading