Material proxies: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(Added more info on variables (from digging through Valve's material files))
(redone)
Line 1: Line 1:
[[Category:List of Shader Parameters]]
{{toc-right}}
== The basics ==
Material proxies allow materials to change their rendering properties (color, alpha values, etc) using game state and scripted equations. Proxies are defined as a block of text, using key/value pairing, inside of the [[VMT]] description of a material. Proxies are available for all materials.
 
A proxy is defined as in the following example:


<pre>
'''Material proxies''' allow a game's compiled C++ code to manipulate the properties of a [[material]]. Many proxies perform specific tasks, but [[List Of Material Proxies|there are other, more general ones]] that when put together allow for rudimentary scripting.
"LightmappedGeneric"
 
Any number of proxies can be added to a material; they will be executed in the order in which they appear.
 
== Usage ==
 
This material has a <code>Sine</code> proxy which makes it fade [[$alpha|in and out of view]] over a period of eight seconds:
 
<source lang=javascript>
LightmappedGeneric
{
{
"$basetexture" "shadertest/LightmappedTexture"
$basetexture shadertest/LightmappedTexture


// Inside these braces are where all the proxies go
Proxies // proxies are listed inside this block
"Proxies"
{
{
// This is a sine proxy
Sine // a proxy which produces a sine wave
"Sine"
{
{
// This is the data for the sine proxy
resultVar $alpha // The shader parameter to be manipulated
// Notice there is no '$' on the proxy variables
sineperiod 8
"resultVar" "$alpha"
sinemin 0
"sineperiod" 8
sinemax 1
"sinemin" 0
"sinemax" 1
}
}
}
}
}
}
</pre>
</source>


The above block of text instructs the material to use the <code>Sine</code> material proxy to modify the <code>alpha</code> value of the material when rendered. The other entries define parameters for the proxy to use in its calculations. Here the sine wave has a <code>sineperiod</code> of 8 seconds and oscillates between a value of "0" and "1".
=== Variables ===


It is possible to define multiple proxies in one material within the Proxies section of that material's definition. If multiple proxies are defined, they are executed in a top to bottom order.
Materials can declare their own variables for internal use. Such variables must be declared outside the Proxies block, in the body of the material, and must have default values specified on the right-hand side:


<pre>
<source lang=javascript>
"Proxies"
LightmappedGeneric
{  
{
// This is executed first
$basetexture shadertest/LightmappedTexture
"A"
 
{
$myScale 0.5
. . .
}
Proxies
// This is executed second
"B"
{
{
. . .
// ...
}
}
}
}
</pre>
</source>


== Variables ==
Custom variables might be used to pass results between proxies, or perhaps to submit hard-coded data to them. They are often employed to chain mathematic function proxies (i.e. ''Add'', ''Subtract'', etc) together into longer equations for rendering values.
Like a standard programming language, the material proxies may declare temporary local variables to be used to store intermediate values for further use later in the proxy. Variables are declared outside of the ''Proxies'' block and have default values specified on their right-hand side.


<pre>
== Writing new proxies ==
"LightmappedGeneric"
 
{
New proxies are easy to create. They exist on the client only and should inherit from <code>IMaterialProxy</code> or one of its descendants.
"$basetexture" "shadertest/LightmappedTexture"
 
You will need these #includes:
 
* <code>"materialsystem/IMaterialProxy.h"</code>
* <code>"materialsystem/IMaterial.h"</code>
* <code>"materialsystem/IMaterialVar.h"</code>


// A local variable for this material, defaulting to "0.5"
These functions are included in the interface:
"$myScale" 0.5
"Proxies"
{
. . .
}
}
</pre>


There is no practical limit to the number of local variables declared. Local variables may be used to store results from proxies, or to pass data into proxies as parameters. They are often employed to chain mathematic function proxies (i.e. ''Add'', ''Subtract'', etc) together into longer equations for rendering values.
; <code>[[bool]] Init( [[IMaterial]]* pMaterial, [[KeyValues]]* pKeyValues )</code>
: Called when the material is first [[precache]]d. Use this function to initialise variables and grab references to the material vars you will be using. Return true on success and false on failure (in which case the proxy will not be run). {{tip|<code>pKeyValues</code> contains the parameters from the VMT file, but a material var does not have to be present there to exist in the shader! Use <code>pMaterial->FindVar()</code> instead.}}
; <code>void OnBind( void* pC_BaseEntity )</code>
: Called when the material is about to be rendered on an entity. This is where the work is done.
: When coding this function it is important to remember that all entities using a material share the same material object, and that if you change it on one entity it changes everywhere else too. Since <code>OnBind()</code> is called every time an entity comes up for rendering this is not a problem ''so long as you reassign the value you want every time''. Don't return early just because there has been no change, and don't store any input data in the proxy. {{note|<code>pC_BaseEntity</code> doesn't lead to a <code>[[C_BaseEntity]]</code> as its name suggests, but rather to the associated <code>[[IClientRenderable]]</code>. The easiest way to access the entity directly is to base your class on <code>CEntityMaterialProxy</code> (in <code>proxyentity.h</code>) and use the <code>OnBind(CBaseEntity*)</code> overload it provides.}}
; <code>void Release()</code>
: {{todo|Called when the proxy is removed, but when is that?}}
; <code>[[IMaterial]]* GetMaterial()</code>
: The material the proxy is attached to. {{tip|If you have a material var stored, you can return <code>IMaterialVar::GetOwningMaterial()</code> here instead of creating a new <code>IMaterial</code> pointer.}}


== Common result variables ==
=== Interface ===
The following values are commonly used as $resultVar variables.
{| class=standard-table
| <code>$alpha</code> || Fade value (0 = transparent, 1 = opaque).
Won't do anything under the UnlitTwoTexture shader - use $color instead.
|-
| <code>$color</code> || Modulation color (R,G,B) (1,1,1) = white, (0,0,0) = black. Accessed as a table.
Individual channels can be accessed using <code>"resultVar" "$color[0]"</code> for the Red channel, [1] for the Green channel and [2] for the Blue.
Can be used with UnlitTwoTexture materials to alter opacity (UnlitTwoTexture with $translucent considers black as transparent for both diffuse maps)
|-
| <code>$envmaptint</code> || Modulation color for the environment map. Behaves the same as $color above.
|-
| <code>$selfillumtint</code> || Modulation color for the self-illumination. Behaves the same as $color above.
|-
| <code>$envmapmaskscale</code> || An amount to scale the environment map mask.
|-
| <code>$frame</code> || Frame number of an animated texture. Used as the <code>animatedtextureframenumvar</code> with the AnimatedTexture property, but can be advanced/decreased by other proxies too.
|-
| <code>$frame2</code> || $frame for the second texture in two-texture shaders ({{todo|Will this work with WorldVertexTransition?}}).
|-
| <code>$bumpframe</code> || Frame number of an animated texture. {{note|There doesn't appear to be a frame control for $bumpmap2. Would probably be overkill anyway}}
|-
| <code>$basetexture</code> || Used by the AnimatedTexture proxy, as the <code>animatedtexturevar</code> in conjunction with $frame.
|-
| <code>$texture2</code> || Used by the AnimatedTexture proxy, as the <code>animatedtexturevar</code> in conjunction with $frame2.
|-
| <code>$normalmap</code> || Used by the AnimatedTexture proxy, as the <code>animatedtexturevar</code> in conjunction with $bumpframe.
|-
| <code>$basetexturetransform</code> || Another array, holding two values for x and y respectively. Used predominantly by TextureScroll and ConveyorScroll (as the <code>textureScrollVar</code> in both cases) to scroll the texture.
|-
| <code>$texture2transform</code> || Operates in the same way as $basetexturetransform above, but for the second texture in a two-texture shader ({{todo|Again, WorldVertexTransition?}})
|-
| <code>$bumptransform</code> || Operates in the same way as $basetexturetransform above, but for the bumpmap ({{todo|Again, second texture bumpmaps?}})
|-
|}


== Writing material proxy implementations ==
The proxy must expose its interface to materials with the <code>EXPOSE_INTERFACE</code> macro:
Although many generic proxies are included in the client already, it will sometimes be necessary to create a custom material proxy for your MOD. To do this, code will be required on the client-side. Material proxies are descended from the <code>IMaterialProxy</code> interface class. The proxy has three main functions to do its work in. The first is the Init() function, defined as:


<pre>
<source lang=cpp>
bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
EXPOSE_INTERFACE( <className>, <interfaceName>, "<proxyName>" IMATERIAL_PROXY_INTERFACE_VERSION );
pMaterial
</source>
Material we're acting on


pKeyValues
The lack of a comma between the proxy name and interface version is intentional.
List of key-value pairs for this material
</pre>


The <code>Init()</code> function is called when a material is first created in a session. This function allows you to setup internal state for the material proxy, usually consisting of obtaining references to ''material variables'' for later use.
=== Tools recording ===


<pre>
This code was added to all proxies in the Orange Box:
void OnBind( void *pC_BaseEntity );
pC_BaseEntity
Entity this material is being applied to (if any)
</pre>


The <code>OnBind()</code> function is called every time <code>Bind()</code> is called on a material. This is where most of the material proxy's work is done.
<source lang=cpp>
#include "toolframework_client.h"


Finally, the proxy must expose its interface so that the named reference to the proxy can be linked up to the class implementation. This is done via the <code>EXPOSE_INTERFACE</code> macro.
void OnBind(...)
{
//...


<pre>
if ( ToolsEnabled() )
EXPOSE_INTERFACE( className, interfaceName, proxyName IMATERIAL_PROXY_INTERFACE_VERSION );
ToolFramework_RecordMaterialParams( GetMaterial() );
</pre>
}
</source>


{{Note|See <code>DummyProxy.cpp</code> for an example of a simple material proxy.}}
It's probably related to the [[Source Filmmaker]]. It's a good idea to add it to your proxy too in case the Filmmaker is ever released!


{{tip|<code>CEntityMaterialProxy</code> makes the call by itself.}}


== See also ==
== See also ==
Line 138: Line 104:


[[Category:Material System]]
[[Category:Material System]]
[[Category:Programming]]

Revision as of 14:39, 6 May 2010

Material proxies allow a game's compiled C++ code to manipulate the properties of a material. Many proxies perform specific tasks, but there are other, more general ones that when put together allow for rudimentary scripting.

Any number of proxies can be added to a material; they will be executed in the order in which they appear.

Usage

This material has a Sine proxy which makes it fade in and out of view over a period of eight seconds:

LightmappedGeneric
{
	$basetexture shadertest/LightmappedTexture

	Proxies // proxies are listed inside this block
	{
		Sine // a proxy which produces a sine wave
		{
			resultVar	$alpha // The shader parameter to be manipulated
			sineperiod	8
			sinemin		0
			sinemax		1
		}
	}
}

Variables

Materials can declare their own variables for internal use. Such variables must be declared outside the Proxies block, in the body of the material, and must have default values specified on the right-hand side:

LightmappedGeneric
{
	$basetexture shadertest/LightmappedTexture

	$myScale 0.5
	
	Proxies
	{
		// ...
	}
}

Custom variables might be used to pass results between proxies, or perhaps to submit hard-coded data to them. They are often employed to chain mathematic function proxies (i.e. Add, Subtract, etc) together into longer equations for rendering values.

Writing new proxies

New proxies are easy to create. They exist on the client only and should inherit from IMaterialProxy or one of its descendants.

You will need these #includes:

  • "materialsystem/IMaterialProxy.h"
  • "materialsystem/IMaterial.h"
  • "materialsystem/IMaterialVar.h"

These functions are included in the interface:

bool Init( IMaterial* pMaterial, KeyValues* pKeyValues )
Called when the material is first precached. Use this function to initialise variables and grab references to the material vars you will be using. Return true on success and false on failure (in which case the proxy will not be run).
Tip.pngTip:pKeyValues contains the parameters from the VMT file, but a material var does not have to be present there to exist in the shader! Use pMaterial->FindVar() instead.
void OnBind( void* pC_BaseEntity )
Called when the material is about to be rendered on an entity. This is where the work is done.
When coding this function it is important to remember that all entities using a material share the same material object, and that if you change it on one entity it changes everywhere else too. Since OnBind() is called every time an entity comes up for rendering this is not a problem so long as you reassign the value you want every time. Don't return early just because there has been no change, and don't store any input data in the proxy.
Note.pngNote:pC_BaseEntity doesn't lead to a C_BaseEntity as its name suggests, but rather to the associated IClientRenderable. The easiest way to access the entity directly is to base your class on CEntityMaterialProxy (in proxyentity.h) and use the OnBind(CBaseEntity*) overload it provides.
void Release()
Todo: Called when the proxy is removed, but when is that?
IMaterial* GetMaterial()
The material the proxy is attached to.
Tip.pngTip:If you have a material var stored, you can return IMaterialVar::GetOwningMaterial() here instead of creating a new IMaterial pointer.

Interface

The proxy must expose its interface to materials with the EXPOSE_INTERFACE macro:

EXPOSE_INTERFACE( <className>, <interfaceName>, "<proxyName>" IMATERIAL_PROXY_INTERFACE_VERSION );

The lack of a comma between the proxy name and interface version is intentional.

Tools recording

This code was added to all proxies in the Orange Box:

#include "toolframework_client.h"

void OnBind(...)
{
	//...

	if ( ToolsEnabled() )
		ToolFramework_RecordMaterialParams( GetMaterial() );
}

It's probably related to the Source Filmmaker. It's a good idea to add it to your proxy too in case the Filmmaker is ever released!

Tip.pngTip:CEntityMaterialProxy makes the call by itself.

See also