Naming conventions
Describes how to write a project’s naming conventions, which set how each asset is named and where it is stored, in Unreal and on disk. You write one document per project.
Configure and test your first naming rule
You need an Admin account, a project, and a small saved source asset for testing. Start with one asset type and one branch of the taxonomy, then add the rest.
- In the portal, open Projects, select your project, and open the Conventions tab. Copy the current document somewhere safe so you can restore it later.
- Edit the document, or choose a starting point in Preset and click Load. The example below is another starting point. Set the asset type, its prefix, the categories, and any dimensions your artists need.
- Click Save. If the portal shows an error, fix it. Then reopen the editor and check the saved document.
- Launch a test DCC for this project from DousenDesktop. Set the collected instance’s type, taxonomy, and dimension tags, which must match the names in the document, and check the name and destination that Dousen suggests.
- Validate a correctly named test asset and one that is named wrong on purpose. Check that each gives the result you expect at the configured severity.
- Publish the correct asset and check its name and folder in Unreal. Clients cache conventions for 5 minutes, so wait that long or restart them before you test a change.
Changing a naming rule doesn’t rename Unreal assets that already exist. Reimport keeps the conventions
stored with each asset unless you set bRefreshConventionsOnReimport=true in the plugin
settings. Before you change a live project, read Reimport behavior.
How conventions are compiled and used
- You write the document in the web portal, as YAML (the default) or as a Lua script when you need computed rules. See Authoring in Lua.
- DousenCore compiles it. It resolves the taxonomy tree, adds the full set of validators to every node, and reports errors such as an invalid regex or a malformed structure.
- Clients fetch the compiled form from
GET /api/projects/:id/conventions/compiledand cache it for 5 minutes, so a change can take that long to reach an open DCC.
A complete example
The current schema is version 3.
YAMLversion: 3
dimensions:
variant:
required: false
values: [Base, Hero, Damaged]
lod:
required: false
values: [LOD0, LOD1, LOD2]
number:
required: false
values: [] # empty = free-form
validators: # global — apply to every asset
- id: naming.convention
severity: error
- id: naming.no_special_chars
severity: error
asset_types:
static_mesh:
prefix: SM
suffix: ""
naming:
pattern: "^SM_[A-Za-z0-9_]+$" # guard rail the final name must satisfy
max_length: 64
source_path:
mirrors_engine: true
validators: # scoped to this asset type
- id: mesh.poly_count
severity: warning
params: { max_tris: 50000 }
categories:
Props:
folder: true
subcategories:
Weapons:
folder: true
prefix: Wep
validators: # scoped to this node + everything below
- id: mesh.poly_count
severity: error # tightens the asset-type rule
params: { max_tris: 20000 }
subcategories:
Melee:
folder: true
With this document, a Static Mesh named Sword at Props/Weapons/Melee with the variant
Hero gets the name SM_Wep_Sword_Hero and the engine folder
Props/Weapons/Melee.
Asset type keys must be the snake_case ids that the clients use, such as static_mesh,
skeletal_mesh, texture, animation_sequence, hda, and
vat. If you use a different key, such as StaticMesh, no instance matches it, so its
prefix isn’t enforced and the portal’s rule picker hides the mesh rules under it.
The taxonomy tree
The first level under an asset type is categories. Each node in it can nest
subcategories, and every node has the same fields at every depth.
There’s no maximum depth, and a leaf node can be just {}.
static_mesh asset_type — prefix "SM"
└─ Props folder ✓ prefix "" suffix ""
└─ Weapons folder ✓ prefix "Wep"
└─ Melee folder ✓ prefix ""
└─ Daggers folder ✓ prefix "Dgr"
└─ (leaf — no subcategories)
A client finds an asset’s place in the tree with an ordered list of node names, the taxonomy
path, for example ["Props", "Weapons", "Melee", "Daggers"]. For that path, a Static Mesh
named Sword resolves to:
- Name:
SM_Wep_Dgr_Sword: the asset type’sSM, thenWepfrom Weapons andDgrfrom Daggers. Props and Melee have no prefix. - Engine folder:
Props/Weapons/Melee/Daggers, because every node on the path hasfolder: true(the default).
A node’s folder and its name tokens are independent. A node with a prefix or
suffix and folder: false adds a token to the name without creating a subfolder, so
every weapon can get a Wep token without a Weapons/ folder. A node with neither, like
Props and Melee above, only adds a folder segment.
Keyword reference
Top level
| Key | Type | Required | Notes |
|---|---|---|---|
version |
int | yes | Must be 3. A missing or different value is a compile error. |
dimensions |
map | no | Extra naming parts that are appended to names, such as a variant or a LOD. Default
{}. |
validators |
list | no | Rules that apply to every asset. Default []. |
asset_types |
map | no | One entry per asset type. Default {}. |
interchange |
map | no | Unreal Interchange import profiles. DousenCore validates it when you save and passes it through the compiled document. See Set up an import profile. |
dimensions.<name>
| Key | Type | Default | Notes |
|---|---|---|---|
required |
bool | false |
Advisory. Client UIs show it, but name resolution doesn’t enforce it. |
values |
list of strings | [] |
The pick-list shown in the DCC Set Up Asset panels. Empty means free-form. Name
resolution doesn’t check that a value is in the list. For a dimension that isn’t required, the
pick-list starts at (none), and the tag stays unset until the artist chooses a value. |
You can declare any dimension key, but only variant, lod, and number
are added to names, in that order. Other dimensions appear only in client UIs and in the convention tags.
variant is left out of the name when its value is exactly Base.
The import_profile dimension selects an Interchange profile. Each of its values must name a
profile in the interchange block, and the dimension can’t be required. DousenCore checks
both when you save. Artists can also choose a profile for one instance with Set Import Profile in
the Publish tab. See Set up an import profile.
Validator entries
The same entry format works at all three levels: global, asset type, and taxonomy node. An entry applies at the level where you place it and to everything below it. For what each rule does and how levels override each other, see Asset validation.
| Key | Type | Default | Notes |
|---|---|---|---|
id |
string | required | The rule id, for example mesh.poly_count. The portal accepts an id that no DCC
implements. A DCC with no plugin for it reports a warning that the rule was skipped. |
severity |
string | — | error blocks the export and warning is advisory. If you leave it out, the
DCC addons treat the rule as error. |
enabled |
bool | true |
Set false to turn the rule off at this level and below. |
params |
map | {} |
Rule-specific parameters. When a deeper level restates a rule, its params are merged key by key
over the shallower ones, so a node can change only max_tris. |
name / category |
string | — | Optional display metadata. The portal fills these in from the catalog. |
asset_types.<name>
| Key | Type | Default | Notes |
|---|---|---|---|
prefix |
string | "" |
The first token of every name of this type (for example SM). |
suffix |
string | "" |
The last token of every name of this type. |
naming.pattern |
regex | — | A guard rail that the final assembled name must match. It doesn’t build the name. An invalid regex is a compile error that names the asset type. |
naming.max_length |
int | — | The maximum length of the final name. |
source_path.template_override |
template | — | Where the working file lives when it doesn’t mirror the engine path. See Source paths. |
source_path.mirrors_engine |
bool | true |
true unless the asset type has a template_override. DousenCore derives this
value and ignores what you write. |
validators |
list | [] |
Rules for this asset type, merged over the global rules. |
categories |
map | {} |
The first level of the taxonomy tree. |
The first level under an asset type must be categories. Putting subcategories
directly under an asset type is a compile error.
Taxonomy nodes (categories and subcategories)
| Key | Type | Default | Notes |
|---|---|---|---|
folder |
bool | true |
When true, the node’s name becomes a folder segment in the engine path. Set
false for a node that only adds to the asset name. |
prefix |
string | "" |
A name token that comes before the asset name. |
suffix |
string | "" |
A name token that comes after the asset name. |
required |
bool | false |
Advisory flag for client UIs. |
validators |
list | [] |
Rules for this node and everything below it. |
subcategories |
map | {} |
Child nodes with the same fields, to any depth. |
How a name is assembled
Dousen joins these parts with _ and skips empty ones, in this order:
- the asset type’s
prefix - each taxonomy node’s
prefix, along the path from root to leaf - the asset name
- each taxonomy node’s
suffix, also from root to leaf - the
variant(unless it isBase), thenlod, thennumber - the asset type’s
suffix
The engine folder path is built separately: Dousen joins the names of the nodes
whose folder is true with /. Node prefixes and suffixes don’t affect
it.
When you export, the naming.convention validator checks the assembled name against the asset
type’s naming.pattern and naming.max_length.
Source paths
By default (mirrors_engine), an asset’s working file has the same relative path as its engine
folder. A mesh that goes to Props/Weapons keeps its .blend or .ma file
under Props/Weapons in the source root. To store sources somewhere else, give the asset type a
template_override:
YAMLsource_path:
template_override: "Art/Source/{{ category }}/{{ subcategory }}"
Use double-brace placeholders such as {{ category }}. Single-brace placeholders such as
{ category } aren’t substituted. These variables are available:
| Variable | Value |
|---|---|
category |
The first taxonomy segment (for example Props). |
subcategory |
The second taxonomy segment (for example Weapons). |
variant, lod, number, … |
The dimension values selected for the asset (any selection key the DCC sends). |
template_override is the only place where v3 uses templates. The DCC addons and the Unreal
plugin define only category and subcategory from the taxonomy path, so don’t
reference deeper segments. The asset’s name isn’t a template variable.
Worked examples
The DCC addons, the Unreal plugin, and DousenCore all render a template_override string to the
same result.
template_override |
Taxonomy path | Selections | Resolved source path |
|---|---|---|---|
Art/Source/{{ category }}/{{ subcategory }}/Textures |
Props/Weapons |
— | Art/Source/Props/Weapons/Textures |
Rigs/{{ subcategory }}/{{ variant }} |
Characters/Heroes |
variant: Hero |
Rigs/Heroes/Hero |
{{ category }}/{{ subcategory }} |
Props/Weapons/Melee/Daggers |
— | Props/Weapons (the third and fourth segments are ignored) |
{{ category }}/LODs/{{ lod }} |
Environment |
lod: LOD1 |
Environment/LODs/LOD1 |
If a template uses a variable that the asset doesn’t have, for example {{ subcategory }} when
the taxonomy path has only one level, the DCC addons fall back to the engine folder path, and the Unreal
plugin leaves the placeholder text in the path. Saving doesn’t check template_override, so
resolve a real asset to test a template.
Authoring in Lua
Write the document as a Lua script when your rules are computed, for example when dozens of
asset types share a pattern, prefixes come from a table, or poly budgets follow a formula. In the editor, set
Mode to Lua Script, or save through the API with ?type=lua.
The script defines a config() function that returns a table with the same version 3
structure as the YAML. DousenCore runs it when you save and when it compiles the document, so clients
get the same compiled JSON whichever format you use.
LUA-- Generate several asset types from one table.
local types = {
{ key = "static_mesh", prefix = "SM" },
{ key = "skeletal_mesh", prefix = "SK" },
{ key = "texture", prefix = "T" },
{ key = "animation", prefix = "A" },
}
function config()
local asset_types = {}
for _, t in ipairs(types) do
asset_types[t.key] = {
prefix = t.prefix,
naming = { pattern = "^" .. t.prefix .. "_[A-Za-z0-9_]+$" },
categories = { Characters = {}, Environment = {}, Props = {} },
}
end
return {
version = 3,
dimensions = { number = { required = true, values = {} } },
validators = {
{ id = "naming.convention", severity = "error" },
},
asset_types = asset_types,
}
end
The script runs in a sandbox inside DousenCore. Besides the base functions such as
ipairs, only the table, string, and math libraries are
available. os, io, require, load, loadfile,
dofile, loadstring, dostring, package, debug,
and collectgarbage are removed, and string.dump is disabled so that a script can’t
produce bytecode.
A script can use 1 MiB of memory and 10,000 VM instructions. After that, it stops with the error
"instruction limit exceeded". There is no wall-clock timeout.
When you save, DousenCore rejects a script that has a compile error, has no config(), or returns
something that isn’t a valid version 3 table. The editor shows the error.
Older Lua conventions defined resolve() and validate() functions. DousenCore no
longer supports that format and rejects a script that defines resolve(). There is no automatic
migration, so move the logic into a config() that returns the version 3 table.
The portal editor
- The Conventions tab has a code editor with Mode set to Template
YAML or Lua Script. Preset and Load replace the
editor content with one of these starting points: UE5 Allar Style Guide, Minimal,
Nanite + Lumen — Static, and Nanite + Lumen — Foliage for YAML, and the Allar and Minimal
presets for Lua. Upload File loads a
.yaml,.yml, or.luafile. Save checks the document and stores it. - The Validators tab shows the compiled taxonomy as a tree, where you edit each node’s rules. These edits change the YAML document for you. For a project that uses Lua, the tree is read-only. See Enable and test a rule.
- In Lua Script mode, a side panel adds Validate Names. Enter asset names,
one per line. The panel checks each name against the
naming.patternandnaming.max_lengthof the asset type named in theasset_typefield of the Context (JSON) box, and shows which names pass or fail, with the reason.
How each client resolves conventions
The DCC addons and the Unreal plugin read only the compiled JSON from /conventions/compiled. They
never parse the YAML or Lua. Both follow How a name is assembled, so
the same input gives the same name in Blender and in Unreal.
DCC addons (Blender, Maya, 3ds Max, …)
The addons fetch the compiled JSON as the signed-in artist and cache it for 5 minutes. They use it to:
- Fill the cascading category and subcategory dropdowns in each addon’s Set Up Asset panel. Each pick narrows the next level’s options, to whatever depth the tree has.
- Suggest the name, engine path, and source path, and build a flat
convention_tagsdictionary with the asset type, the/-joined taxonomy, and the selected dimension values. Dousen embeds the tags as custom properties or FBX metadata, so Unreal’s importer recovers the asset’s taxonomy without the artist entering it again. - Infer the tags of an asset that was never tagged, when validation finds no saved tags. Dousen matches the longest asset-type prefix on the file name, matches path segments to taxonomy node names (ignoring case), and scans path segments for dimension values.
If a fetch fails, see DousenCore is unreachable.
The Unreal Engine plugin
The plugin fetches the compiled JSON when the editor starts and then every 5 minutes. It resolves names from the same flat tags (asset_type, taxonomy
joined by /, variant, lod, and number), joins the
folder-flagged node names for the engine path, and fills in template_override for the source path.
When a bridge event arrives without a suggested name, the plugin builds the name and engine path itself. If a
fetch fails, see DousenCore is unreachable.
The web portal
The portal is the only client that also writes the document. The Validators tab compiles the stored document on every request, and Validate Names compiles the text in the editor. Neither uses the 5-minute cache.
What is checked when
| Moment | What can fail |
|---|---|
| Saving YAML | Invalid YAML, and any compile error: a missing or wrong version, an invalid
naming.pattern regex, subcategories directly under an asset type, or a
malformed validator entry, or an invalid interchange block. The error names the field, for example
Convention error: 'interchange.profiles.<name>.<field>' must be …. It also fails if an
import_profile value names no profile, or if that dimension is required. |
| Saving Lua | Lua compile errors, a missing config(), a return value that isn’t a table, and the same
compile errors as for YAML. |
| Fetching the compiled document | DousenCore compiles the stored document again. If it no longer compiles, the request returns HTTP 500
with the error as the body. An invalid interchange block that was stored earlier is left out
instead, and the Conventions page shows a warning. |
| Never checked | Validator ids that no DCC implements (the DCC reports a skipped-rule warning), dimension values
outside values (advisory), and the contents of template_override. |
Who can view and edit
Any signed-in user, and any DCC addon or Unreal plugin that authenticates with a token, can fetch the
document or the compiled JSON. Only an Admin can save. The portal editor requires the Admin
role, and PUT /api/projects/:id/conventions also returns 403 Forbidden for any caller
whose role isn’t Admin.
API reference
| Endpoint | Purpose |
|---|---|
GET /api/projects/:id/conventions |
The document as you wrote it, as YAML or Lua source. A project that has never saved a document gets the bundled UE5 Allar-style preset, which is the same document the portal offers as a starting point. |
PUT /api/projects/:id/conventions |
Replaces the document. Admin only. ?type=lua validates and stores it as a Lua script.
?type=template, or no parameter, validates it as YAML. Any other type value
returns 400. |
GET /api/projects/:id/conventions/compiled |
The compiled JSON that clients use, with the taxonomy resolved and effective_validators
added to every asset type and node. |
GET /api/projects/:id/conventions/json |
An alias of /compiled. |
Troubleshooting
A change doesn’t appear in a DCC or in Unreal
To fetch conventions before the 5-minute cache expires:
- DCC addons: restart the DCC. There is no button to reload conventions, and a new process starts with an empty cache.
- Unreal: restart the editor or reload the plugin.
DousenCore is unreachable
If a fetch fails (a network error, a non-2xx response, or malformed JSON), the DCC addons keep using the last copy they loaded. If they never loaded one, they skip convention-based suggestions, and validators use the validator catalog if DousenCore still serves it, and otherwise the default rules built into the addon, instead of the project’s rules.
The Unreal plugin logs a LogDousenUE warning and marks the conventions as unavailable until the
next successful fetch, within 5 minutes of DousenCore coming back. While they are unavailable, the Dousen import
pipeline (on by default) refuses to import new assets with the message Project conventions unavailable;
fetch conventions before importing new assets. Reimporting an existing asset keeps using the conventions
stored with that asset.
A saved change has no effect
DousenCore compiles the document when you save it, so the portal and PUT reject a document that
doesn’t compile. See What is checked when. If
/conventions/compiled still returns HTTP 500 with a compile error, the DCC addons keep their last
good copy and Unreal marks the conventions as unavailable. To see the error, open the document in the portal
editor and save it again.