Naming conventions

Updated October 2026

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.

  1. 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.
  2. 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.
  3. Click Save. If the portal shows an error, fix it. Then reopen the editor and check the saved document.
  4. 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.
  5. 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.
  6. 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

  1. 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.
  2. 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.
  3. Clients fetch the compiled form from GET /api/projects/:id/conventions/compiled and 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:

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

KeyTypeRequiredNotes
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>

KeyTypeDefaultNotes
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.

KeyTypeDefaultNotes
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>

KeyTypeDefaultNotes
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)

KeyTypeDefaultNotes
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:

  1. the asset type’s prefix
  2. each taxonomy node’s prefix, along the path from root to leaf
  3. the asset name
  4. each taxonomy node’s suffix, also from root to leaf
  5. the variant (unless it is Base), then lod, then number
  6. 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:

VariableValue
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

Conventions tab in Template YAML mode with the Minimal preset loaded in the code editor. The Mode and Preset selectors and the Load and Upload File buttons are above the editor, and Save and Cancel are below it.
The Conventions tab with the Minimal preset loaded. The screenshot uses a sample project.

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:

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

MomentWhat 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

EndpointPurpose
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:

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.