Dynamic Texture Banks
Dynamic texture banks are optionally used with dynamic characters to allow for texture swapping and tinting on character parts. A texture bank is a collection of texture sets, each set representing a swappable "piece". Sets are comprised of a diffuse map, a normal map (optional) and a tint map, and may be organized into two layers - a base layer and an optional detail layer. Dynamic texture banks are described by .dtb files.
Note: Only textures associated with characters need to be in texture banks. Other textures are stand-alone files. Note: Your uv's on the model must be with in 0-1 space. If they are outside of 0-1 space then they will not tint properly.
Creating a new texture bank
- Create a new folder in the
Character\Dynamic_2\Texturebanks\
directory of the repository and give it the name of the texture bank.- The name of a texture bank used by a material is determined by taking the material name and removing the "~dt2_" prefix for 3dsMax, "dt2_" for Maya, (so a mesh mapped with a material named ~dt2_lower_body or dt2_lower_body will use a texture bank named lower_body). The "~dt_" and "dt2_" prefix is what tells the engine that it's dealing with a dynamic material.
- Create a .dtb file (also named after the texture bank) in the texture bank directory you just created.
- Add the [Root] section.
- Add the one or two layers for this texture bank to the [Layout] section. (remember: the base--or bottommost layer--is specified first).
- Add the [Layers] section.
- For each layer you added in the previous step, do the following:
- Add the layer name in {}.
- Add the layer Type property.
- Add the layer Region property.
- (Optional) Add the {Settings} section and any appropriate settings for the layer.
- Add the {Files} and fill it with the paths to the appropriate .dds files for this layer.
- It makes a lot of sense to put the .dds files you reference in the texture bank directory. However, there may be cases where multiple texture banks share a texture, calling for a shared texture directory. This should be rare, though.
- See the {Files} section reference for more in-depth coverage of how to specify the .dds files.
- Add the {Pieces} and fill it with piece names of the content you're adding.
- For Detail layer, add the Empty piece first if you want to allow empty as an option (this works only for the detail layer since the base layer cannot be empty).
- (Optional) Add Type to the pieces you added in the last step for Tint, HairShade, or Skin.
- For each layer you added in the previous step, do the following:
- Go back and add in any layer and/or piece {Rules} (see Designing Rules for more information, just replace slot with layer and part with piece).
Download Sample Files
Download Link: DynamicCharacterSampleFiles.zip
This zip file contains a set of .dtb files in the Character\Dynamic_2\TextureBanks folder
- cs_clothes uses Type=Tint4
- cs_hair uses Type=HairShade
- cs_skin uses Type=Skin
Format reference
.dtb files have a few things in common with parts files:
- Lines beginning with a
!
are comments - Properties are of the form PropertyName=PropertyValue
- Rules start with
%
! ! Texture bank definition ! [Root] Character\Dynamic\TextureBanks\human_female_ranger_upper_body_shirt\ [Layout] -Shirt -Leather Armor [Layers] {Shirt Base} Type=Tint4 Region=(0,0,511,511) {Rules} ! This is where rules go, but this one happens to be commented out ! % hides(Leather Armor) {Settings} Palette1=Human Ranger Cloth 1,Human_Palette_1.dds DefaultColor1=#.2,.27,.15,1.0 Palette2=Human Ranger Studded Leather 1,palette_leather.dds DefaultColor2=#.35,.29,.22,1.0 Palette3=Human Ranger Detail 1,palette_metallics.dds DefaultColor3=#.86,.62,.14,1.0 Palette4=Human Metal 1,palette_metallics.dds DefaultColor4=#.37,.37,.37,1.0 {Files} (512,512) human_female_ranger_upper_body_shirt_1_d.dds,human_female_ranger_upper_body_shirt_1_n.dds,human_female_ranger_upper_body_shirt_1_t.dds {Pieces} Default {Leather Armor} Type=Tint2 Region=(0,0,511,511) {Settings} Palette1=Human Ranger Studded Leather 2,palette_leather.dds DefaultColor1=#.35,.29,.22,1.0 Palette2=Human Ranger Detail 2,palette_metallics.dds DefaultColor2=#.85,.74,.21,1.0 {Files} (512,512) studded_vest_1_d.dds,studded_vest_1_n.dds,studded_vest_1_t.dds leather_corset_1_d.dds,leather_corset_1_n.dds,leather_corset_1_t.dds mail_shirt_1_d.dds,mail_shirt_1_n.dds,mail_shirt_1_t.dds {Pieces} Empty Studded Vest Leather Corset Type=Tint4 Palette1=Human Ranger Corset 1,palette_leather.dds DefaultColor1=#.35,.29,.22,1.0 Palette2=Human Ranger Studded Leather 8,palette_leather.dds DefaultColor2=#.35,.29,.22,1.0 Palette3=Human Ranger Detail 8,Human_Palette_1.dds DefaultColor3=#.86,.62,.14,1.0 Palette4=Human Ranger Detail 9,palette_metallics.dds DefaultColor4=#.86,.62,.14,1.0 Mail Shirt Type=Tint3 Palette1=Human Ranger Studded Leather 9,palette_leather.dds DefaultColor1=#.35,.29,.22,1.0 Palette2=Human Metal 1,palette_metallics.dds DefaultColor2=#.37,.37,.37,1.0 Palette3=Human Ranger Detail 10,Human_Palette_1.dds DefaultColor3=#.86,.62,.14,1.0 AdjustableShininess=True
[Root]
This is the directory where the .dtb file that you're editing should be, and where piece DDSs will be searched for if the texture specified isn't a complete path (that is, if it's textureName.dds
instead of Character\SomeDirectory\textureName.dds
).
[Layout]
Layers are specified in reverse vertical order, so that the layers that come at the top of the Layout section are visually on the bottom of the layer stack. Think Photoshop, but upside-down.
-Wristwatch -Shirt
Anything in the shirt layer will be drawn over whatever's in the wristwatch layer (again, it's upside-down from Photoshop).
Guidelines
- Don't specify any rules or properties in this section! It's only for layout.
- Only two layers are allowed per .dtb file.
[Layers]
This section is where individual layers are described in more detail. A layer is specified by putting its name in {}. Layer names must be unique; no two layer names in any bank should be the same. A layer's specification is divided up into several subsections, which are:
- Type
- Region
- Files
- Rules
- Settings
- Pieces
All but the Type and Region are delineated by the section name in {}, i.e. {Files} and {Rules} (see above example .dtb).
Type
The type determines, among other things, the number of colors available to the user and the shader used to render the piece.
Current allowed types are
- Tint1
- Tint2
- Tint3
- Tint4
- HairShade - 4 colors are available (base hair color, 2 highlight colors, 1 specular shine color)
- Skin - 2 colors are available (skin blend, normal map blend)
The Tint1, Tint2, Tint3, and Tint4 layers have 1, 2, 3, and 4 colors available, respectively. They color using a simple multiply algorithm (final color = texture color * user color).
Regions
Note: Regions should match the texture file size.
The region property specifies the rectangle that encompasses the layer. Its format is as follows:
(left,top,right,bottom)
If the texture files are 512x512, then type this line:
Region=(0,0,511,511)
{Rules}
Available rules here are
- % hides(layerName) - Completely hides layerName, making it inaccessible to the user.
- % onif(layerName) - Causes the layer whose rule this is to be hidden if layerName is hidden or is currently empty.
- % substitutes(layerName:pieceNameToSwapOut,pieceNameToSwapIn) - Replaces PieceNameToSwapOut in layerName with PieceNameToSwapIn. Assumes that PieceNameToSwapIn also exists in layerName.
Note: You cannot use the substitutes() rule to affect a skin layer. |
Evaluation order begins with the base layer and works its way up the layer stack until it gets to the topmost layer. Layer rules are evaluated first this way, then rules for currently selected pieces are evaluated in the same way.
{Settings}
There are a few properties available to each layer in the {Settings} section:
- Keyword
- Palette
- DefaultColor
- AdjustableShininess - Can be true or false, meaning the user is allowed to set how shiny they want this layer to be.
- DefaultShininess - If AdjustableShininess is true, this determines how shiny things in this layer are by default, until the user manually changes their shininess.
Palettes
Palettes are textures that define what colors are available to the user for a given layer or piece.
HSL can tell the engine where on a palette the user clicked (via SetTexturePieceColor
), and the engine will sample the texture to determine what color was picked. It can also bypass that and set the color directly (SetTexturePieceColorDirectly
). You can also use a combination of the two functions to implement a "color theme" where colors from palettes are collected and reused over multiple pieces.
There are two kinds of palettes: Shared and Non-Shared.
A shared palette is specified like so:
Palette1=SharedPaletteName,paletteFileName.dds
And a non-shared palette looks like this:
Palette1=paletteFileName.dds
You can specify a different palette for each available color in the layer or piece type. The number in "Palette1" tells which color this palette corresponds to. The layer or piece type determines the number of colors available for assigning palettes and default colors (so a Tint2 layer can have a Palette1 and/or a Palette2 defined). If a palette isn't specifically defined, the engine uses the default palette, which is currently located at Character\Dynamic\TextureBanks\Palettes\palettedefault.dds.
paletteFileName.dds is the palette's texture name. This can be a full path, or just the file name. If it's just a file name, the palette is assumed to be in a "palettes" subdirectory of the current "texturebanks" directory
- So if the palette is defined in a texture bank located in \Character\Humanoids\Dwarf\TextureBanks\polka_the_dots\, the engine looks for the palette texture in \Character\Humanoids\Dwarf\TextureBanks\palettes\.
The thing that makes shared palettes special is that any place where there's a palette with that name (in the above example, SharedPaletteName), the colors are automatically synched. This is nice for color coordination. For example, you may choose to have pants and socks share a palette, so that you never have the problem of characters with dark pants and white socks (perish the thought!). It's a balance, though, because forcing colors means less customization for the user.
Here's an example palette that's appropriate for picking colors for metal objects:
Default Colors
(Read the Palettes section before you tackle this, and beware that skin is handled a little bit differently.)
Default colors are specified like so:
DefaultColor1=#Red,Green,Blue,Alpha
Notes:
- Like palettes, the number after "DefaultColor" tells which color is being specified. The layer or piece type determines the number of colors available for assigning default colors (so a Tint2 layer can have DefaultColor1 and/or DefaultColor2 defined).
- Any colors that are available but don't have a default color specified will default to white.
- The values for the color components are between 0.0 and 1.0, not 0 and 255.
- Alpha is ignored at the moment.
It's not required, but probably a good idea that you get your default colors from the palettes available for the piece, for consistency's sake (you don't want the user to start out with a white staff if they can never manually pick that color themselves).
{Files}
This is where the actual textures go that are used for all of the pieces. It follows this format:
(texture_width,texture_height) first_diffuse_map_d.dds,first_normal_map_n.dds,first_tintmask_map_t.dds second_diffuse_map_d.dds,second_normal_map_n.dds,second_tintmask_map_t.dds
Note: If a Part will be swapped out for a larger Part that needs a Piece which uses a larger texture file size, make sure the Region is set for the largest file size expected as well as the files section.
You do not need to setup multiple (texture_width, texture_height) lines above the list of {Files} if the are different sizes per piece.
There's a naming convention that the engine uses to know what each texture functions as: diffuse maps should have _d right before the extension, normal maps should have _n, and tint mask maps should have _t (see above example).
You can bundle up multiple pieces per texture if you'd like; the engine can automatically calculate where and in what texture a specific piece is located based on the layer region and the texture dimensions provided in the {Files} section. When doing this, though, watch out for a couple of things:
- Make sure that extra padding space added to non-power-of-two textures is less than the size of a piece; otherwise, the automatic calculation will be thrown off. For example, say a particular layer was 127x127, but you left an extra couple of pixels on the edge of the texture for a border, making it 129x129. It gets exported as a power-of-two .dds, which causes it to be written out at 256x256. Now the automatic calculation will think there are up to 4 pieces in that texture. The next 3 pieces after the one that was meant for this texture will also be picked up from this texture, and they'll turn out black.
- You can generally pack pieces without any pixel space between them, but beware that mip filtering may cause problems at lower mip levels; you may want to add slight borders between the pieces.
- If there are multiple rows and columns of pieces in one texture, the order they are used will be left-to-right, top-to-bottom.
{Pieces}
Pieces are specified one per line in the {Pieces} section, and must be unique only within a layer (so layer A and layer B can both have a piece named "leather glove").
The first piece specified becomes the default piece, which is the piece that's selected in the layer when the layer first becomes visible.
You can allow for nothing to be selected in a layer by specifying an empty piece, which is simply a piece named "Empty" (without quotes). There are two requirements for the empty piece:
- It must be the first piece specified (that is, the default piece)
- It cannot have any properties, rules, etc.
Layer settings (properties in the {Settings}) are automatically applied to all pieces in the layer; however, you can have a piece override any or all of the settings.
- For example, this comes in handy if you have a shirt layer with all kinds of shirts, where some of the shirts are leather and some are chain mail. Since leather and mail are very different types of materials, it makes sense to have separate leather and metal palettes. Pick the palette that's shared by the largest number of pieces in the layer and make that the layer palette. Then specify the other palette as individual overrides on the pieces that use it.
HSL Usage
Palettes (with the exception of skin palettes) are sampled using the HSL function SetTexturePieceColor()
. Pass in an x and y pixel offset from the top left corner of the texture and HeroEngine automatically samples the palette and assigns the color at that location to the piece you specify. Areas that have an alpha of 0 are not sampled, and the current color is returned.
Skin
HeroEngine features a special 3-way texture blending for layers with a type of Skin. This allows more control over skin than a simple color tint.
Skin Color Concepts
Skin works by blending between 1 to N number of textures (called components: current technology limits N to 3 components) based on blend weights passed in through the HSL functions SetSkinComponentBlend(s). In general, the blend amounts of the skin components should add up to 1.0 to get a smooth blend between the different textures, though you might experiment with values that don't add up to 1 to see what different effects you can get.
As an example, to get the Caucasian-Asian-Zombie here (equal blends of each) you'd give each skin component a weight of .333 by batching them together in a single list of floats and passing them to SetSkinComponentBlends
.
Naturally, setting the weight for a single component to 1 and all other component weights to 0 will give a result as if the character were textured with just that single component texture. Also, take care that your components sum to at least your alpharef value or else your character will become invisible.
Note that because a character has only one "skin," you don't have to worry about specifying banks, layers, or pieces with skin functions. In fact, don't try: the standard bank/layer/piece functions won't work with skin.
Specifying Skin in a .dtb
To hook up skin, first create a new texture bank file with the Skin type.
A {Pieces} for a skin layer has some additional detail to it. Here's a sample one:
{Pieces} (Diffuse) Blends=3 Caucasian DefaultBlend=.333 African DefaultBlend=.333 Asian DefaultBlend=.333 Zombie (Normal) Blends=2 Human DefaultBlend=.5 Zombie Bump DefaultBlend=.5
Three things make this section special from other {Pieces} sections:
- Two subsections named
(Diffuse)
and(Normal)
. The pieces in the(Diffuse)
subsection are components that can be selected using the SetSkinComponent HSL function. The selected components are then blended based on blend weights set withSetSkinComponentBlend(s)
to create the character's final diffuse texture. The(Normal)
subsection is similarly for the creation of the final normal map texture (use SetSkinBumpComponent and SetSkinBumpComponentBlend for these).Note: Make sure that no (Diffuse) pieces have the same name as any of the (Normal) pieces. - The
Blends
property tells HeroEngine the maximum number of components that can be blended at any given time. HeroEngine defaults to having the first Blends number of components available for blending (in the above example, the first 3 diffuse components are the ones available by default, which are Caucasian, African, and Asian). - The
DefaultBlend
property tells HeroEngine the blend weight a component should initially have when a new character is created.
In the above example, a new character's diffuse texture would be an equal blend between the Caucasian, African, and Asian diffuse textures. Its normal map would be an equal blend between the Human and Zombie Bump textures.
Note: Make sure that any Palettes, skin options, piece numbers and piece names between skin layers in different banks are exactly the same; otherwise, undefined behavior will result. The easiest way to do this is to make a "master" skin layer in one bank and copy and paste it into all other banks that need skin layers, changing the layer name and filenames as appropriate. If done correctly, all character parts with a skin blend applied will blend synchronously. |
Skin Palettes
Since skin is handled different from other layer types, HeroEngine doesn't directly sample palettes for it. However, for user interface purposes it may be useful to display a palette to the user.
Since a linear blend requires all components to sum to 1.0, an excellent palette to use for a 3-way blend is a triangle with each pixel's color being determined by using the Barycentric coordinates of the point in the triangle as weights for blending between three corner colors. Here's what it looks like using pure red, green, and blue as the three corner colors:
Tip: You can easily customize the above palette for your own needs by bringing it into Photoshop and using the red, green, and blue channels as masks to determine where to fill in your own colors. |
Display the palette in a GUI control (call GetSkinPalettes to get all palette texture names for the character) and when the user clicks on the control, convert their mouse position from (x,y) to a Barycentric coordinate. Pass the Barycentric coordinate components as a list of floats to SetSkinComponentBlends
to update your character's look.
Using the Caucasian/Asian/Zombie example here, here's how clicking in various places will give different blends:
Troubleshooting
Double-check that the two offending layers don't have the same name. Remember, all layer names must be unique!
A bunch of pieces are black!
- Do they have black default colors?
- Do you have multiple textures, with several pieces packaged into each? Check that none of the textures has blank space around the edges large enough for a piece to fit in (make sure you check the texture saved from Photoshop, not your source PSD).
Pieces are always white by default, even though I've specified DefaultColors.
Make sure that the components of your DefaultColor are between 0.0 and 1.0. If you're accidentally missing decimal points, you can easily have 213 instead of .213.
One tint channel seems to effect the entire map instead of one specified portion
Make sure your tint texture wasn't inadvertently saved out with a full white alpha channel.
All portions of a character using a skin blend are invisible
Check to make sure that all texture banks used by the character that specify a skin blend share the exact same settings, palettes, and piece names.