Copying Materials
From Multiverse
Contents |
Overview
When creating a new material script that is a slight variation of another material script, it is sufficient to copy and paste code for simple materials. However, this process is tedious for complex materials that use multiple passes and vertex and pixel. To make this process easier, The Multiverse Client supports copying material scripts, which greatly reduces the redundancy.
Using this feature, new materials can copy from previously defined materials and modify specific techniques, passes, texture units or add new ones. For example, many materials use the same vertex programs and the same pass structure, and only vary in the textures they reference. In these cases, you can define a "base" material with the pass structure, and in each material that shares that structure, all you need specify is the "base" material and the "texture_alias" unique to the new material.
Example
To show the impact of material copying, consider the "before" and "after" script for the barrel material in the Multiverse Sampleworld asset repository.
Before material copying, the barrel material file looks like this:
material barrel.barrel_material
{
technique
{
pass
{
shading phong
ambient 1.00000 1.00000 1.00000 1.00000
diffuse 1.00000 1.00000 1.00000 1.00000
specular 0.00000 0.00000 0.00000 1.00000 0.00000
emissive 0.00000 0.00000 0.00000 1.00000
texture_unit
{
texture barrel.dds
tex_coord_set 0
}
}
}
}
With material copying, this script becomes:
material barrel.barrel_material : MVSimpleLighting
{
set_texture_alias DiffuseTex barrel.dds
}
The base material, MVSimpleLighting, defines a texture_alias DiffuseTex, and the derived material just invokes set_texture_alias.
Syntax
To make a new material based on one previously defined, add a colon (:) after the new material name followed by the name of the material that is to be copied.
Format:
material NewMaterial : ReferenceMaterial
Where NewMaterial is the unique name of the "child" material being created, and ReferenceMaterial is the original "parent" material script.
The parent material must be defined and parsed before a child material script. The easiest way to achieve this is to place parents at the beginning of the material script file. After material scripts are loaded into the Multiverse Client, materials no longer maintain their copy inheritance structure. If a parent material is modified through code at runtime, the changes have no effect on child materials that were copied from it in the script.
Material copying within the script alleviates some drudgery from copy/paste but having the ability to identify specific techniques, passes, and texture units to modify makes material copying easier. Techniques, passes, texture units can be identified directly in the child material without having to layout previous techniques, passes, texture units by associating a name with them, Techniques and passes can take a name and texture units can be numbered within the material script.
NOTE: Using names is optional, so older scripts will still work just fine and you can inherit from them as well. Names become very useful in materials that copy from other materials. In order to override values they must be in the correct technique, pass, texture unit, and so on. The script could be laid out using the sequence of techniques, passes, texture units in the child material but if only one parameter needs to change in say the fifth pass then the first four passes prior to the fifth would have to be placed in the script:
For example:
material test2 : test1
{
technique
{
pass
{
}
pass
{
}
pass
{
}
pass
{
}
pass
{
ambient 0.5 0.7 0.3 1.0
}
}
}
This method is tedious for materials that only have slight variations to their parent. An easier way is to name the pass directly without listing the previous passes:
material test2 : test1
{
technique 0
{
pass 4
{
ambient 0.5 0.7 0.3 1.0
}
}
}
The parent pass name must be known and the pass must be in the correct technique in order for this to work correctly. Specifying the technique name and the pass name is the best method. If the parent technique/pass are not named then use their index values for their name as done in the example.
Adding new techniques and passes to copied materials
To add a new technique or pass to a copied material, use a unique name for the technique or pass that does not exist in the parent material. Using an index for the name that is one greater than the last index in the parent will do the same thing. The new technique/pass will be added to the end of the techniques/passes copied from the parent material.
Note: if passes or techniques aren't given a name, they will take on a default name based on their index. For example the first pass has index 0 so its name will be 0.
Dealing with program references and shader parameters
For a child material that only needs to change some shader parameters within a pass, do the following:
material furtest2 : furtest1
{
technique ati8500
{
pass fur3
{
fragment_program_ref
{
param_named furlength float 0.5
}
}
}
}
There is no requirement to give the name of the fragment program reference since the parent material has already done this and the same applies to a vertex program reference, but dropping the program reference name only applies if one is defined in the pass copied from the parent material. If the parent didn't have a program reference then an exception is generated since the material parser has no clue as to what the script intended.
Program references can be added to copied passes that had no program references. The whole program reference will have to be defined with in the pass.
If a different program ref is to be used in the copied pass then just put in the name of the different vertex/fragment program reference. This is usefull when you want to inherit from a parent material but want to use a different shader program for one of the passes.
Identifying texture units to overide values
A specific texture unit state (TUS) can be given a unique name within a pass of a material so that it can be identified later in cloned materials that need to override specified texture unit states in the pass without declaring previous texture units. Using a unique name for a Texture unit in a pass of a cloned material adds a new texture unit at the end of the texture unit list for the pass.
material BumpMap2 : BumpMap1
{
technique ati8500
{
pass 0
{
texture_unit NormalMap
{
texture BumpyMetalNM.png
}
}
}
}
Texture aliases
Texture aliases are useful for when only the textures used in texture units need to be specified for a cloned material. In the source material ie the original material to be cloned, each texture unit can be given a texture alias name. The cloned material in the script can then specify what textures should be used for each texture alias.
Using texture aliases within texture units
Format:
texture_unit unitName
{
texture_alias name
}
Default: name defaults to texture_unit unitName, if set.
For example, in the following texture_alias defaults to DiffuseTex:
texture_unit DiffuseTex
{
texture diffuse.jpg
}
For example, the base material to be cloned:
material TSNormalSpecMapping
{
technique Cg
{
pass
{
ambient 0.1 0.1 0.1
diffuse 0.7 0.7 0.7
specular 0.7 0.7 0.7 128
vertex_program_ref CgDemo/OffsetMappingVS
{
param_named_auto lightPosition light_position_object_space 0
param_named_auto eyePosition camera_position_object_space
param_named textureScale float 1.0
}
fragment_program_ref CgDemo/TSNormalSpecMappingFS
{
param_named normalMap int 0
param_named diffuseMap int 1
param_named fxMap int 2
}
// Normal map
texture_unit NormalMap
{
texture defaultNM.png
tex_coord_set 0
filtering trilinear
}
// Base diffuse texture map
texture_unit DiffuseMap
{
texture defaultDiff.png
filtering trilinear
tex_coord_set 1
}
// spec map for shinnines
texture_unit SpecMap
{
texture defaultSpec.png
filtering trilinear
tex_coord_set 2
}
}
}
technique HLSL_DX9
{
pass
{
vertex_program_ref FxMap_HLSL_VS
{
param_named_auto worldViewProj_matrix worldviewproj_matrix
param_named_auto lightPosition light_position_object_space 0
param_named_auto eyePosition camera_position_object_space
}
fragment_program_ref FxMap_HLSL_PS
{
param_named ambientColor float4 0.2 0.2 0.2 0.2
}
// Normal map
texture_unit
{
texture_alias NormalMap
texture defaultNM.png
tex_coord_set 0
filtering trilinear
}
// Base diffuse texture map
texture_unit
{
texture_alias DiffuseMap
texture defaultDiff.png
filtering trilinear
tex_coord_set 1
}
// spec map for shinnines
texture_unit
{
texture_alias SpecMap
texture defaultSpec.png
filtering trilinear
tex_coord_set 2
}
}
}
}
The Cg and HLSL techniques use the same textures. For each texture usage type a texture alias describes what the texture is used for. So the first texture unit in the Cg technique has the same alias as the TUS in the HLSL technique since its the same texture used. Same goes for the second and third texture units. For demonstration purposes, the Cg technique makes use of texture_unit naming and therefore the texture_alias name does not have to be set since it defaults to the texture unit name. So why not use the default all the time since its less typing? For most situations you can. Its when you clone a material that and then want to change the alias that you must use the texture_alias command in the script. You cannot change the name of a texture_unit in a cloned material so texture_alias provides a facility to assign an alias name.
Now we want to clone the material but only want to change the textures used. We could copy and paste the whole material but if we decide to change the base material later then we also have to update the copied material in the script. With set_texture_alias, copying a material is very easy now. set_texture_alias is specified at the top of the material definition. All techniques using the specified texture alias will be effected by set_texture_alias.
Format:
set_texture_alias alias-name texture-name
Example:
material fxTest : TSNormalSpecMapping
{
set_texture_alias NormalMap fxTestNMap.png
set_texture_alias DiffuseMap fxTestDiff.png
set_texture_alias SpecMap fxTestMap.png
}
In both techniques, the textures in the child material are replaced automatically with the new ones.
You can follow the same process in code as long you set up the texture alias names so then there is no need to traverse technique/pass/TUS to change a texture. You just call myMaterialPtr->applyTextureAliases(myAliasTextureNameList) which will update all textures in all texture units that match the alias names in the map container reference you passed as a parameter.
You don't have to supply all the textures in the copied material.
For example, in the following script, material fxTest2 only changes the diffuse and specular maps of material fxTest and uses the same normal map.
material fxTest2 : fxTest
{
set_texture_alias DiffuseMap fxTest2Diff.png
set_texture_alias SpecMap fxTest2Map.png
}
In the next example, fxTest3 ends up with the default textures for the normal map and specular map set up in TSNormalSpecMapping material but will have a different diffuse map. So the base material can define the default textures to use and then the child materials can override specific textures.
material fxTest3 : TSNormalSpecMapping
{
set_texture_alias DiffuseMap fxTest2Diff.png
}
This document is based on the OGRE Manual and is licensed under the Creative Commons Attribution-ShareAlike 2.5 License.
