mandag den 6. februar 2012

Creating materials at runtime in Source engine

An example on a common material used in the source engine can be:

    "$baseTexture" "models\props_equipment/Cargo_container03"
    "$Envmap" "env_cubemap"
    "$EnvmapMask" "models\props_equipment\Cargo_container03_ref"
    "$BlendTintByBaseAlpha" "1"
    "$envmapfresnel" "1"
    "$envmapFresnelMinMaxExp" "[0.03 0.03 2]"

...a .vmt file(Valve Material Type). So we need a way to create these materials at runtime, dynamically.

After looking through the old SDK, and reading about it on valve wiki it was pretty obvious what needed to be done. Please see below code sample.

#define MAT( _TYPE_ ) "\"" + _TYPE_ + "\"\n{\n\t\"$basetexture\" \"VGUI/white_additive\"\n\t\"$ignorez\" \"1\"\n\t\"$nofog\" \"1\"\n\t\"$halflambert\" \"1\"\n}"
#define MAT_IGNOREZ( _TYPE_ ) "\"" + _TYPE_ + "\"\n{\n\t\"$basetexture\" \"VGUI/white_additive\"\n\t\"$ignorez\" \"0\"\n\t\"$nofog\" \"1\"\n\t\"$halflambert\" \"1\"\n}"

IMaterial* CreateMaterial( bool shouldIgnoreZ )
 static int created = 0;

 //this is a pointer to the cvar I use for controlling lighting on the materials,
 //I registered it in the source engines linked list of ConVars,
 //and it's changable through the games console
 static ConVar* shadows = Singleton::Get()->Interfaces()->m_Cvar->FindVar( "cheat_chams_shadows" );

 std::string type = ( shadows->GetFloat() == 0.f ) ? "UnlitGeneric" : "VertexLitGeneric";

 //materialBuffer holds the vmt "script" or what to call it
 char materialBuffer[2048];
 if ( shouldIgnoreZ )
  std::string tmp( MAT( type ) );
  sprintf( materialBuffer, tmp.c_str() );
  std::string tmp( MAT_IGNOREZ( type ) );
  sprintf( materialBuffer, tmp.c_str() );

 //make a unique name for our material
 char materialName[512];
 sprintf( materialName, "custom_material_%i.vmt", created );

 //construct keyvalues based on the material type
 KeyValues* keyValues = new KeyValues( type.c_str() );

 //load vmt "script" into our instance of keyvalues
    keyValues->LoadFromBuffer( materialName, materialBuffer );

 //create the actual material
 IMaterial* createdMaterial = Singleton::Get()->Interfaces()->m_MaterialSystem->CreateMaterial( materialName, keyValues );
    return createdMaterial;