Skip to content

Materials & TextureSets

Kamzik123 edited this page Aug 2, 2026 · 1 revision

This tutorial explains how Material and TextureSet files work in Assassin's Creed games — how a material controls the way a surface looks and reacts to light, how it pulls its textures from a TextureSet or from direct overrides, and how the whole chain from mesh to pixel fits together.

Materials and TextureSets are part of the AnvilNext engine and are consistent in concept across titles (AC3, AC4, Rogue, Unity, Syndicate, Origins, Odyssey, Mirage, etc.). The exact set of render flags and TextureSet slots that a file stores changes between engine revisions, but the core structure — a material referencing a template and a texture set, and a texture set bundling texture maps by channel — is the same everywhere.

All examples are taken from real exported AC4: Black Flag files (Edward Kenway's default outfit Fabric material and its texture set), but the patterns apply to any AnvilNext title.

A note on Path vs ID: Throughout the XML you'll see Path attributes on references. These are not used by the game engine — it locates and loads every file by its numeric ID. AnvilToolkit fills the Path in as a human-readable convenience. When modding, the ID is what matters.


Table of Contents


Core Concept: The Material Chain

A model's surface is not described by a single file. It is a small chain, each link pointing at the next by ID:

Mesh
 │  references
 ▼
Material          ← how the surface looks (shiny, transparent, two-sided…) + which textures to use
 ├─ references ─► MaterialTemplate   ← the shader/technique (the actual lighting math)
 └─ references ─► TextureSet         ← a bundle of texture maps, one per channel
                     │  references
                     ▼
                  TextureMap(s)      ← the actual images (Diffuse, Normal, Specular…)
  • A Material is the hub. It points at a MaterialTemplate (the shader that decides how pixels are lit) and at a TextureSet (the textures fed into that shader), and it stores a pile of flags that tweak rendering (transparency, culling, shadows…).
  • A TextureSet is just an organised bundle: "Diffuse = this image, Normal = that image, Specular = this other image."
  • A TextureMap is the actual image data (the DDS you edited back in the Textures lesson).

Everything below is about the middle two links — the Material and the TextureSet.


TextureSet — Bundling Textures by Channel

A TextureSet answers one question: "for this material, which image is the Diffuse, which is the Normal, which is the Specular, and so on?" It is a fixed list of slots, one per texture channel, where each slot holds a reference (by ID) to a TextureMap file — or nothing.

Standard Format: Named Slots

Exported with the Standard exporter, a TextureSet is beautifully clear — every slot is a FileReference whose Name tells you exactly which channel it is. Here is the real CHR_P_EdwardKenway_Fabric_Set.TextureSet:

<TextureSet ID="11927604453">
    <FileReference Name="Diffuse"       IsGlobal="1" Path="...\CHR_P_EdwardKenway_Fabric_DiffuseMap...TextureMap">16223198188</FileReference>
    <FileReference Name="Normal"        IsGlobal="1" Path="...\CHR_P_EdwardKenway_Fabric_NormalMap...TextureMap">16223198192</FileReference>
    <FileReference Name="Specular"      IsGlobal="1" Path="...\CHR_P_EdwardKenway_Fabric_SpecularMap...TextureMap">29154175471</FileReference>
    <FileReference Name="SpecularPower" IsGlobal="0" Path="0">0</FileReference>
    <FileReference Name="OffsetBump"    IsGlobal="0" Path="0">0</FileReference>
    <FileReference Name="Emissive"      IsGlobal="0" Path="0">0</FileReference>
    <FileReference Name="Transmission"  IsGlobal="0" Path="0">0</FileReference>
    <FileReference Name="Occlusion"     IsGlobal="0" Path="0">0</FileReference>
    <FileReference Name="Mask1"         IsGlobal="0" Path="0">0</FileReference>
    <FileReference Name="Mask2"         IsGlobal="0" Path="0">0</FileReference>
    <FileReference Name="Generic"       IsGlobal="0" Path="0">0</FileReference>
    <Value Name="Source" Type="UInt64">11927604452</Value>
</TextureSet>

Read it top to bottom: this material's Diffuse is texture 16223198188, its Normal is 16223198192, its Specular is 29154175471, and every other channel is empty (0). To change a texture, you change the ID in the matching slot. That's the whole file.

  • IsGlobal1 when the slot points at a real (global) file, 0 for an empty slot. Leave it matching the reference: a filled slot is IsGlobal="1", an empty one is IsGlobal="0" Path="0">0.
  • Source — the ID of the TextureFile this set was generated from (a build-time source record). You normally leave it alone.

This is why the Standard exporter is the friendlier one for TextureSets — the channel of every slot is written right there in the Name.

Schema-Based Format: A Positional Array

The Schema-Based exporter stores the exact same data, but as an unlabeled positional array — the slot's meaning comes from its position, not a name:

<Object ID="11927604453" Type="TextureSet">
    <StaticArray Name="Maps" Type="Reference">
        <Reference Type="TextureBase" ...>16223198188</Reference>   <!-- position 0 = Diffuse -->
        <Reference Type="TextureBase" ...>16223198192</Reference>   <!-- position 1 = Normal -->
        <Reference Type="TextureBase" ...>29154175471</Reference>   <!-- position 2 = Specular -->
        <Reference Type="TextureBase" PointerType="Null" />          <!-- position 3 = SpecularPower (empty) -->
        <Reference Type="TextureBase" PointerType="Null" />          <!-- ... and so on -->
        ...
    </StaticArray>
    <Handle Name="Source" Type="TextureFile" ...>11927604452</Handle>
</Object>

Same three textures, same order — just harder to read at a glance because you have to count positions. Both formats import back identically; pick whichever you find clearer (Standard, for texture sets).

The Slot List Per Game

The set of slots a TextureSet stores depends on the game. AC4: Black Flag stores these 11 channels (in this order), followed by Source:

# Slot Typical use
0 Diffuse Base colour / albedo
1 Normal Surface detail (bumps, wrinkles)
2 Specular Reflectivity / gloss (may pack masks in its channels)
3 SpecularPower Sharpness of highlights
4 OffsetBump Parallax/height
5 Emissive Self-illumination
6 Transmission Light passing through (skin, cloth)
7 Occlusion Baked ambient occlusion
8 Mask1 Generic mask
9 Mask2 Generic mask
10 Generic Catch-all

Newer titles add more slots (Unity adds Cookie/EnvLighting; Origins/Odyssey/Mirage/Shadows add Mask0/DiffuseAlpha; Mirage appends neural-texture slots). The three you'll touch 95% of the time are the same everywhere: Diffuse, Normal, Specular.


Material — Anatomy of a Material File

A Material has three parts: two top-level references (template + texture set), a block of render flags, and a Parameters list that holds per-texture selectors and tunable shader values.

Here is the top of the real CHR_P_EdwardKenway_Fabric.Material (Standard exporter, trimmed):

<Material ID="11927604587">
    <FileReference Name="MaterialTemplate" IsGlobal="1" Path="...\CHR_Unique.MaterialTemplate">1870870824</FileReference>
    <FileReference Name="TextureSet"       IsGlobal="0" Path="...\CHR_P_EdwardKenway_Fabric_Set.TextureSet">11927604453</FileReference>
    ...
    <Value Name="BlendMode"        Type="Enum" EnumName="BlendMode" ValueName="Copy">0</Value>
    <Value Name="AlphaTestValue"   Type="Byte">0</Value>
    <Value Name="AlphaTestEnabled" Type="Bool">False</Value>
    <Value Name="TwoSided"         Type="Bool">True</Value>
    ...
    <List Name="Parameters" Type="DynamicProperty">
        ...
    </List>
</Material>

The Two Top-Level References

Reference What it points at Notes
MaterialTemplate The shader/technique file Defines how the surface is lit. You usually reuse an existing template (see below).
TextureSet The material's texture set The default source for the material's textures. Can be left empty (0) if every texture is supplied by overrides in the Parameters list instead.

Render Flags

Between the references and the Parameters list sits a block of flags. These are the knobs Lesson 2 introduced (TwoSided, transparency) plus many more. The most useful:

Flag Type What it does
TwoSided Bool Renders the surface on both sides of each face. Turn on for thin geometry (sashes, capes, leaves) that would otherwise vanish from one side.
AlphaTestEnabled Bool Enables hard cut-out transparency (each pixel is either fully visible or fully gone). Needed for masked textures.
AlphaTestValue Byte The cut-off threshold (0–255) used when AlphaTestEnabled is true. 128 is the common value.
BlendMode Enum How the surface blends with what's behind it (Copy = opaque; other modes give soft/additive transparency).
AlphaDisplayMode Enum How the alpha channel is interpreted (Standard is the default).
InvertCulling Bool Flips which face is culled (front vs back).
FlipNormalOnBackFace Bool On a two-sided material, flips the normal on the back face so lighting looks correct from both sides.
ShadowCasterOpaque / ShadowCasterAlpha Bool Whether the material casts shadows (opaque vs alpha-tested).
ZWriteDisabledOpaque / ZWriteDisabledAlpha / ZTestDisabled Bool Depth-buffer controls — mostly for transparent/special materials; leave default unless you know you need them.
MaterialDisabled Bool Skips the material entirely (an easy way to hide a surface).

Not every material stores every flag — the serialized flag set is per-game, so an AC2 material has fewer flags than an AC4 one. Edit the flags that are present; don't add ones the game doesn't write.

Parameters — The DynamicProperty List

The Parameters list is where a material's textures and tunable shader values live. Each entry (DynamicProperty) has a NameHash (a hashed parameter name — AnvilToolkit shows the readable HashName when it knows it) and a value whose type depends on the parameter. The ones you'll come across:

  1. A TextureSelector — binds a texture to a shader input (this is the important one, covered next).
  2. A Vector4 — e.g. DiffuseTileMap A UVScale = (30, 15, 0, 0) controls how a detail texture tiles.
  3. A Float — e.g. ForceWetness, NG normal detail a Strenght.
  4. A UVTransform — controls a texture's UV coordinates: ScaleU/ScaleV (tiling), TranslationU/TranslationV (offset), Rotation, and their animated counterparts TranslationSpeedU/TranslationSpeedV / RotationSpeed with AnimateTranslation / AnimateRotation toggles. Used for scrolling or rotating textures (flowing water, moving banners, spinning emblems).
  5. A TimeOscillator — drives a value that oscillates over time via Frequency, Amplitude, Bias and TimeBias (plus a Type). Used for pulsing effects such as a throbbing emissive glow.

The exact parameter set — and therefore which of these types appear — is defined by the material's MaterialTemplate and varies by material. Edit the parameters that are present rather than inventing new ones.

For example, the Fabric material's diffuse binding:

<DynamicProperty>
    <Value Name="NameHash" Type="UInt32" HashName="Layer0_Diffuse">1560926545</Value>
    <Value Name="DataType" Type="UInt32" HashName="TextureSelector">2097694221</Value>
    <Object IsManagedObject="true" IsManaged="false">
        <TextureSelector ID="0">
            <Value Name="SpecificationMethod" Type="Enum" EnumName="TextureSpecificationMethod" ValueName="MaterialTextureSet">0</Value>
            <Value Name="MapType" Type="Enum" EnumName="TextureMapType" ValueName="MapDiffuse">0</Value>
            <FileReference Name="TextureSet" IsGlobal="0" Path="0">0</FileReference>
            <FileReference Name="TextureMap" IsGlobal="1" Path="...\CHR_P_EdwardKenway_Fabric_DiffuseMap...TextureMap">16223198188</FileReference>
        </TextureSelector>
    </Object>
</DynamicProperty>

TextureSelector & TextureSpecificationMethod — TextureSet vs Override

This is the mechanism behind the "override" trick from Lesson 2, explained in full.

Each texture the shader needs (diffuse, normal, specular, detail tiles…) is bound by a TextureSelector inside the material's Parameters list. A selector has two important fields:

  • MapType — which channel this selector feeds: MapDiffuse=0, MapNormal=1, MapSpecular=2, … , MapGeneric=10.
  • SpecificationMethodwhere the texture comes from. This is the key decision:
SpecificationMethod Value Meaning
MaterialTextureSet 0 Pull this channel's texture from the material's TextureSet (the normal path). The selector's own TextureMap field is ignored by the engine.
OverridenTextureSet 1 Pull from a different whole TextureSet specified on the selector.
OverridenTextureMap 2 Use the single TextureMap specified directly on the selector, bypassing the set for this one channel.
ViewSurfaceTexture / ScreenshotTexture / FireItemTexture 3 / 4 / 5 Special engine-supplied textures; not used for normal outfit modding.

In the Fabric material you can see both approaches side by side:

  • Layer0_Diffuse, Layer0_Normal, Layer0_Masking use MaterialTextureSet (0) — their textures come from the Fabric_Set.TextureSet.
  • DiffuseTileMap A, DiffuseTileMap B, NG_NormalTileMap A use OverridenTextureMap (2) — each points directly at a shared detail/tiling texture, no texture set involved.

Why overrides are handy for modding: if a channel uses OverridenTextureMap, its texture ID lives right there on the selector — you change one number and you're done, no separate TextureSet file to edit. This is exactly what Lesson 2 meant by "convert a TextureSet call into an override." The trade-off: a TextureSet keeps a character's textures organised in one place, while overrides scatter them across the material.


MaterialTemplate — The Shader Behind the Material

The MaterialTemplate reference points at the file that actually defines the shading — the technique, the shader inputs, the default parameter values. In the example it's CHR_Unique.MaterialTemplate (1870870824), the template shared by unique/hero characters.

For modding you never author a template from scratch. You reuse an existing one that matches the surface type you want:

  • A character-skin template for skin (these often can't do transparency — see the warning in Lesson 2).
  • A cloth/fabric template for clothing.
  • A metal template for weapons and armour.

If a swapped mesh looks wrong (flat, unlit, wrong response to light), a mismatched or missing MaterialTemplate is a common cause — point the material at the same template the original outfit used.


Standard vs Schema-Based Exporter

Both exporters carry identical data; they just format it differently. Which one appears by default depends on the game (Standard for pre-Origins, Schema-Based for Origins+), and you can reveal the other with Shift+right-click → Export.

Concept Standard Exporter Schema-Based Exporter
Root (material) <Material ID="..."> <Object ID="..." Type="Material">
Root (texture set) <TextureSet ID="..."> <Object ID="..." Type="TextureSet">
Template / set refs <FileReference Name="MaterialTemplate"> <Reference Name="MaterialTemplate" Type="MaterialTemplate">
Flags <Value Name="TwoSided" Type="Bool"> <Bool Name="TwoSided">
Enums <Value Name="BlendMode" Type="Enum" ValueName="Copy">0</Value> <Enum Name="BlendMode" Value="Blend_Copy">0</Enum>
Parameters <List Name="Parameters" Type="DynamicProperty"> <DynamicProperties>
TextureSet slots named <FileReference Name="Diffuse"> positional <StaticArray Name="Maps"> of <Reference>
Encoding UTF-16, type="0" UTF-8, type="1"

Recommendation: for TextureSets, use the Standard exporter — named slots make it far easier to see and edit the right channel. For Materials, either works; the flag and parameter names are readable in both.


Practical: Retexture Through the TextureSet

The cleanest way to reskin a part whose material uses a TextureSet:

  1. Export the character's TextureSet (Shift+right-click → Standard XML export).
  2. Import your new image as a TextureMap (see the Textures lesson) and note its ID.
  3. In the TextureSet XML, put your new ID into the matching named slot:
    <FileReference Name="Diffuse" IsGlobal="1" Path="...">YOUR_NEW_DIFFUSE_ID</FileReference>
  4. Import the XML back and repack.

Every material that shares this TextureSet now uses your texture — you edited one file, not many.


Practical: Retexture With an Override

If the channel you want to change is bound with OverridenTextureMap (or you'd rather not touch the shared set), edit the material instead:

  1. Export the Material.
  2. Find the DynamicProperty for the channel (e.g. Layer0_Diffuse).
  3. In its TextureSelector, set SpecificationMethod to OverridenTextureMap (2) and point TextureMap at your new texture ID:
    <Value Name="SpecificationMethod" ... ValueName="OverridenTextureMap">2</Value>
    <FileReference Name="TextureMap" IsGlobal="1" Path="...">YOUR_NEW_TEXTURE_ID</FileReference>
  4. Import and repack.

Both edits are required. If SpecificationMethod is still MaterialTextureSet (0), the engine reads the channel's texture from the material's TextureSet and ignores the TextureMap you set on the selector — so your override silently does nothing. The method must be 2 for the selector's TextureMap to be used. (If the channel is already OverridenTextureMap, you only need to change the ID.)


Practical: Convert a TextureSet Call Into an Override

This is the Lesson 2 tip, now with the full picture. To stop a channel resolving through the TextureSet and instead use a texture you specify directly:

  1. In the channel's TextureSelector, change the method from MaterialTextureSet (0) to OverridenTextureMap (2):
    <!-- Before -->
    <Value Name="SpecificationMethod" ... ValueName="MaterialTextureSet">0</Value>
    <!-- After -->
    <Value Name="SpecificationMethod" ... ValueName="OverridenTextureMap">2</Value>
  2. Set the selector's TextureMap reference to your texture's ID (and make sure MapType matches the channel).
  3. Once every channel a material needs is supplied by overrides, the material no longer needs its top-level TextureSet reference — you can set it to 0, and the TextureSet file can be removed if nothing else uses it.

This makes a material self-contained: all its textures are named right on the material, so you never have to hunt down and edit a separate TextureSet when swapping textures.


Practical: Make a Material Two-Sided

For thin surfaces (sashes, capes, flags, leaves) that disappear when viewed from behind:

<Value Name="TwoSided" Type="Bool">True</Value>

If lighting looks wrong on the new back face, also confirm:

<Value Name="FlipNormalOnBackFace" Type="Bool">True</Value>

Not every template supports two-sided rendering — skin templates in particular may ignore it.


Practical: Make a Material Transparent

To use a texture's alpha channel for hard cut-out transparency (holes, frayed edges, hair cards):

  1. Make sure the Diffuse texture actually has proper alpha.
  2. Enable alpha testing and set the cut-off:
    <Value Name="AlphaTestEnabled" Type="Bool">True</Value>
    <Value Name="AlphaTestValue"   Type="Byte">128</Value>

For soft/blended transparency instead of a hard cut-out, you additionally change the BlendMode away from Copy — but soft transparency interacts with sort order and depth writes, so start with alpha test (above), which is what most outfit pieces use.

Transparency support depends entirely on the MaterialTemplate. Most templates support only alpha test — changing BlendMode on them does nothing (or breaks rendering), so alpha test is the reliable option. Only certain templates handle the soft/blended BlendMode modes. And some templates — skin being the classic example — support no transparency at all, alpha test included. If your flags seem to have no effect, the template is the limiter, not your edits: to get transparency there you'd need to reassign the material to a template that supports it (see MaterialTemplate).


Practical: Create a Brand-New Material

You don't have to start from an existing material — AnvilToolkit can generate a fresh one for you, built from a template:

  1. In the Game Explorer, right-click and choose New → Material. (This option only appears when a supported game and its schema are loaded.)
  2. A "Select MaterialTemplate file" picker opens. Choose the .MaterialTemplate the material will be built on. This decides the shader and which parameters the new material will have — its texture slots, floats, UV transforms, oscillators, etc. are all generated from the template's definition. (This is exactly why the template matters: it dictates what the material can do and what you can tune.)
  3. A save dialog appears. Save it as a .Material (binary, ready to repack) or as .xml if you'd rather edit it first. The material's ID is generated from the file name you give it, so name it something unique.
  4. Open the new material and fill it in: point its TextureSet (or per-channel overrides) at your textures and set whatever flags you need, exactly as covered in the sections above.

This is the cleanest way to make a material for a custom mesh — pick the template that matches the surface (cloth, metal, skin…) and you get a correctly-structured material with all the right parameter slots already in place; you just supply the textures.


Practical: Duplicate a Material With a Unique ID

When you'd rather base a new material on an existing one (e.g. a variant of an outfit's material) than build from a template:

  1. Export the material to XML.
  2. Change the root ID to a new number not used by any other file in the game:
    <Material ID="YOUR_UNIQUE_ID">
  3. Edit whatever you want (textures, flags).
  4. Import it as a new file into the data file, and point the mesh/BuildTable that should use it at YOUR_UNIQUE_ID.

The same "give it a unique ID" rule applies to TextureSets and TextureMaps — any file you add must have an ID that doesn't collide with an existing one, because the engine resolves everything by ID.


Quick Reference Summary

Concept What it does
Material The hub: references a template + a texture set, holds render flags, and binds textures/params via a Parameters list.
MaterialTemplate The shader/technique — how the surface is lit. Reuse an existing one that matches the surface type.
TextureSet A bundle of TextureMap references, one per channel (Diffuse, Normal, Specular…). Standard export names each slot.
TextureMap The actual image (DDS) for one channel.
TextureSelector Binds one texture to one shader channel inside a material's Parameters.
SpecificationMethod MaterialTextureSet (0) = use the set; OverridenTextureMap (2) = use a texture named directly on the selector.
MapType Which channel a selector feeds (MapDiffuse, MapNormal, MapSpecular, …).
TwoSided Render both faces — for thin geometry.
AlphaTestEnabled / AlphaTestValue Hard cut-out transparency and its threshold (commonly 128).
BlendMode Opaque (Copy) vs soft/additive blending.
Standard vs Schema export Same data, different format. Prefer Standard for TextureSets (named slots). Reveal the alternate with Shift+right-click.
Golden rule The engine loads by ID; Path text is just a label. Any file you add needs a unique ID.

Clone this wiki locally