Difference between revisions of "Material proxies"

From Valve Developer Community
Jump to: navigation, search
m (Inserted HeliBlade.)
m (minor stuff)
 
(32 intermediate revisions by 16 users not shown)
Line 1: Line 1:
== The basics ==
+
{{toc-right}}
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 together provide rudimentary scripting support within VMT files.
"LightmappedGeneric"
+
 
 +
Any number of proxies can be added to a material; they will be executed in the order in which they appear.
 +
 
 +
{{bug|Some users have reported that the tools mode will not run some functional proxies in game.}}
 +
{{todo|Extent of bug untested.}}
 +
 
 +
== 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=php>
 +
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>
 +
 
 +
=== 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.
  
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".
+
These custom variables might be used to pass results between proxies or to submit hard-coded data to them. They are often employed to chain mathematic function proxies (i.e. <code>Add</code>, <code>Subtract</code>, etc) together into longer equations. For 2D/3D vectors (<code>"[0 0 0]"</code>), the variable name can have <code>[0]</code> suffixed to read/write a specific index. Writes to indexed variables should be encased in quotes.
  
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.
+
This example extends the one above by staggering the starting position of the sine wave:
 +
 
 +
<source lang=php>
 +
LightmappedGeneric
 +
{
 +
$basetexture shadertest/LightmappedTexture
  
<pre>
+
$offset 0 // declare custom var
"Proxies"
+
{
+
Proxies
// This is executed first
 
"A"
 
 
{
 
{
. . .
+
EntityRandom
 +
{
 +
resultVar $offset // write to custom var
 +
}
 +
Sine
 +
{
 +
resultVar $alpha
 +
timeoffset $offset // read from custom var
 +
sineperiod 8
 +
sinemin 0
 +
sinemax 1
 +
}
 
}
 
}
// This is executed second
+
}
"B"
+
</source>
 +
 
 +
Now each entity this material is used on pulses to its own schedule.
 +
 
 +
Other examples for writing to indexed variables:
 +
 
 +
<source lang=php>
 +
$color "[0 0 0]"
 +
 
 +
proxies
 
{
 
{
. . .
+
sine
 +
{
 +
sineperiod 1.3
 +
sinemin 0
 +
sinemax 1
 +
timeoffset 0
 +
resultvar "$color[0]"
 +
}
 +
sine
 +
{
 +
sineperiod 1.7
 +
sinemin 0
 +
sinemax 1
 +
timeoffset 0
 +
resultvar "$color[1]"
 +
}
 +
sine
 +
{
 +
sineperiod 2.3
 +
sinemin 0
 +
sinemax 1
 +
timeoffset 0
 +
resultvar "$color[2]"
 +
}
 
}
 
}
}
+
</source>
</pre>
 
  
== Variables ==
+
An example of using a color vector to create 'random' color pulses.
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>
+
<source lang=php>
"LightmappedGeneric"
+
UnlitGeneric
 
{
 
{
"$basetexture" "shadertest/LightmappedTexture"
+
$basetexture "dev\gradient_dif"
 +
$color "[1 .8 .6]"
 +
 
 +
$detail "dev\noise_512x32"
 +
$detailscale 1
 +
$detailblendmode 0
 +
$detailblendfactor 4.0
 +
 
 +
$additive 1
 +
$nocull 1
 +
 
 +
$cvar "[.5 .5]"
 +
$svar "[1 .25]"
 +
$rvar 0
 +
$tvar "[0 0]"
 +
 
 +
$sine1 0
 +
$sine2 0
  
// A local variable for this material, defaulting to "0.5"
+
proxies
"$myScale" 0.5
 
"Proxies"
 
 
{
 
{
. . .
+
linearramp
 +
{
 +
rate .3
 +
initialvalue 0
 +
resultvar "$tvar[1]"
 +
}
 +
sine
 +
{
 +
sineperiod 1.3
 +
sinemin -.004
 +
sinemax .002
 +
timeoffset 0
 +
resultvar $sine1
 +
}
 +
sine
 +
{
 +
sineperiod 1.7
 +
sinemin -.003
 +
sinemax .007
 +
timeoffset .2
 +
resultvar $sine2
 +
}
 +
add
 +
{
 +
srcvar1 $sine1
 +
srcvar2 $sine2
 +
resultvar "$tvar[0]"
 +
}
 +
texturetransform
 +
{
 +
centervar $cvar
 +
scalevar $svar
 +
rotatevar $rvar
 +
translatevar $tvar
 +
resultvar $detailtexturetransform
 +
}
 
}
 
}
 
}
 
}
</pre>
+
</source>
  
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.
+
An example of a dynamic texture transform.
  
== Common result variables ==
+
== Splitting a vector ==
The following values are commonly used as $resultVar variables.
+
Using vectors is quirky. Not all proxies recognize vector components. If a vector's components need be processed separately, they need to be split into different variables first.
{|
 
| <code>$alpha</code> || Fade value (0 = transparent, 1 = opaque)
 
|-
 
| <code>$color</code> || Modulation color (R,G,B) (1,1,1) = white, (0,0,0) = black
 
|-
 
| <code>$envmapmaskscale</code> || An amount to scale the environment map mask
 
|-
 
| <code>$frame</code> || Frame number of an animated texture
 
|-
 
| <code>$envmaptint</code> || Modulation color for the environment map
 
|-
 
| <code>$selfillumtint</code> || Modulation color for the self-illumination
 
|-
 
|}
 
  
{{Note|<code>$alpha</code> and <code>$color</code> values are clamped between 0 and 1.}}
+
The following proxies, and only these keyvalues, can recognize vector components:
 +
<code>
 +
* '''Clamp: '''        min, max
 +
* '''Sine:'''          offset, max, min, period
 +
* '''LinearRamp:'''    rate, initial value
 +
* '''UniformNoise:'''  min, max
 +
* '''GaussianNoise:''' min, max, mean, halfwidth
 +
* '''WrapMinMax:'''    min, max
 +
* '''Exponential:'''  min, max, scale, offset
 +
</code>
 +
All of the above can be used to split a vector. However Clamp is the [[Cheap|cheapest]] to use:
 +
<source lang="php">
 +
        $pos "[0 0 0]"
 +
        $posX .0        //must be float or Clamp will not save the value properly
 +
        $posY .0        //must be float or Clamp will not save the value properly
 +
        $posZ .0        //must be float or Clamp will not save the value properly
 +
       
 +
        $zero 0
 +
       
 +
        //Proxy that outputs a 3d vector
 +
        PlayerPosition
 +
        {
 +
                scale                    1
 +
                resultVar              "$pos"
 +
        }
 +
       
 +
        //Split the 3d vector for further use
 +
        Clamp
 +
        {
 +
            srcVar1                      $zero
 +
            min                        "$pos[0]"
 +
            max                        "$pos[0]"
 +
            resultVar                    $posX
 +
        }
 +
       
 +
        Clamp
 +
        {
 +
            srcVar1                      $zero
 +
            min                        "$pos[1]"
 +
            max                        "$pos[1]"
 +
            resultVar                    $posY
 +
        }
 +
       
 +
        Clamp
 +
        {
 +
            srcVar1                      $zero
 +
            min                        "$pos[2]"
 +
            max                        "$pos[2]"
 +
            resultVar                    $posZ
 +
        }
 +
</source>
  
{{Note|Certain variables like <code>$color</code> and <code>$baseTextureOffset</code> can be accessed like an [[array]]. <code>"$color[0]"</code> would access the red component of the variable.}}
+
{{warning|Quotes are needed when addressing a vector's component, or when defining a vector.}}
  
== Writing material proxy implementations ==
+
== Writing new proxies ==
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>
+
New proxies are easy to create. They exist on the client only and should inherit from <code>IMaterialProxy</code> or one of its descendants.
bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
 
pMaterial
 
Material we're acting on
 
  
pKeyValues
+
You will need these #includes:
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.
+
* <code>"materialsystem/IMaterialProxy.h"</code>
 +
* <code>"materialsystem/IMaterialVar.h"</code>
  
<pre>
+
These functions are included in the interface:
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.
+
; <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).
 +
: <code>pKeyValues</code> contains the proxy parameters from the VMT file.
 +
; <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(C_BaseEntity*)</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.}}
  
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.
+
=== Interface ===
  
<pre>
+
The proxy must expose its interface to materials with the <code>EXPOSE_INTERFACE</code> macro:
EXPOSE_INTERFACE( className, interfaceName, proxyName IMATERIAL_PROXY_INTERFACE_VERSION );
 
</pre>
 
  
{{Note|See <code>DummyProxy.cpp</code> for an example of a simple material proxy.}}
+
<source lang=cpp>
 +
EXPOSE_INTERFACE( <className>, <interfaceName>, "<proxyName>" IMATERIAL_PROXY_INTERFACE_VERSION );
 +
</source>
  
== Proxy list ==
+
The lack of a comma between the proxy name and interface version is intentional.
The following proxies are defined in the client DLL for use in any Source game.
 
{|
 
! Proxy Name || Description || Variables
 
|-
 
| <code>Add</code> || Adds two variables together. ||
 
''srcVar1''
 
* Name of the first source variable.
 
''srcVar2''
 
* Name of the second source variable.
 
''resultVar''
 
* ( srcVar1 + ''srcVar2'')
 
|-
 
| <code>Multiply</code> || Multiplies two variables together. ||
 
''srcVar1''
 
* Name of the first source variable.
 
''srcVar2''
 
* Name of the second source variable.
 
''resultVar''
 
* ( ''srcVar1'' * ''srcVar2'')
 
|-
 
| <code>Subtract</code> || Subtracts the second variable from the first. ||
 
''srcVar1''
 
* Name of the first source variable.
 
''srcVar2''
 
* Name of the second source variable.
 
''resultVar''
 
* ( ''srcVar1'' - ''srcVar2'')
 
|-
 
| <code>Divide</code> || Divides the first variable by the second. ||
 
''srcVar1''
 
* Name of the first source variable.
 
''srcVar2''
 
* Name of the second source variable.
 
''resultVar''
 
* ( ''srcVar1'' / ''srcVar2'')
 
|-
 
| <code>Equals</code> || Copies a variable to the result variable. ||
 
''srcVar1''
 
* Name of the source variable.
 
''resultVar''
 
* Set equal to ''srcVar1''
 
|-
 
| <code>Abs</code> || Computes the absolute value of a variable. ||
 
''srcVar1''
 
* Name of the source variable.
 
''resultVar''
 
* abs( ''srcVar1'')
 
|-
 
| <code>Frac</code> || Returns the fractional component of a number.<br>{{Note|If a vector type variable is provided, this function will return the fractional component of each member of the array.}} ||
 
''srcVar1''
 
* Number to find the fractional component of
 
|-
 
| <code>Exponential</code> || Computes ( A * e (''srcVar1''+B) ). ||
 
''srcVar1''
 
* Name of the source variable.
 
''scale''
 
* Specifies the value of A
 
''offset''
 
* Specifies the value of B
 
''minVal''
 
* Minimum clamping value
 
''maxVal''
 
* Maximum clamping value
 
''resultVar''
 
* ( A * e (''srcVar1''+B) )
 
|-
 
| <code>Clamp</code> || Clamps a variable to a specified range of values. ||
 
''srcVar1''
 
* Name of the source variable.
 
''min''
 
* Minimum clamping value
 
''max''
 
* Maximum clamping value
 
''resultVar''
 
* clamp( ''srcVar1'', ''Min'', ''Max'' )
 
|-
 
| <code>LessOrEqual</code> || Returns different values based relation of supplied variables. ||
 
''srcVar1''
 
* Name of the first source variable.
 
''srcVar1''
 
* Name of the second source variable.
 
''lessEqualVar''
 
* Name of material variable to copy to result if (''srcVar1'' <= ''srcVar2'')
 
''greaterVar''
 
* Name of material var to copy to result if (''srcVar1'' > ''srcVar2'')
 
''resultVar''
 
* Resulting value
 
|-
 
| <code>Sine</code> || Creates a sine wave. ||
 
''sineperiod''
 
* Period of the sine wave in seconds.
 
''sinemin''
 
* Minimum value of the sine wave.
 
''sinemax''
 
* Maximum value of the sine wave.
 
''timeoffset''
 
* Used to start the sine wave at a different point in time
 
''resultVar''
 
* Resulting value.
 
|-
 
| <code>LinearRamp</code> || Produces an ever increasing value. ||
 
rate
 
* Rate of increase of the value.
 
''initialValue''
 
* Initial value to increase.
 
''resultVar''
 
* Resulting value.
 
|-
 
| <code>UniformNoise</code> || Produces a noisy signal where each value is equally likely to occur ||
 
''minVal''
 
* Minimum value range.
 
''maxVal''
 
* Maximum value range.
 
''resultVar''
 
* Resulting value.
 
|-
 
| <code>GaussianNoise</code> || Produces a noisy signal where values are near the average. ||
 
mean
 
* The average random value.
 
''halfWidth''
 
* The distance from the average at which it's only 30% likely to occur.
 
''minVal''
 
* Minimum value range (clamped).
 
''maxVal''
 
* Maximum value range (clamped).
 
''resultVar''
 
* Resulting value.
 
|-
 
| <code>MatrixRotate</code> || Creates a rotation matrix from the provided axis and angle. ||
 
''axisVar''
 
* Axis of rotation (x,y,z) = (0,1,2).
 
''angle''
 
* Degrees of rotation around axis.
 
''resultVar''
 
* Rotation matrix.
 
|-
 
| <code>PlayerProximity</code> || Stores the distance from the entity the material is on to the player into a variable. <br> {{Note|Does not work on world surfaces.}} ||
 
''scale''
 
* An amount to scale the distance by.
 
''resultVar''
 
* Resulting value.
 
|-
 
| <code>PlayerView</code> || Stores the dot product of the view direction + the direction from the camera to the entity the material is on.<br>{{Note|Does not work on world surfaces.}} ||
 
''scale''
 
* Amount to scale the dot product by.
 
''resultVar''
 
* Resulting value.
 
|-
 
| <code>PlayerSpeed</code> || Stores the speed of the player into a variable. ||
 
''scale''
 
* Amount to scale the speed by.
 
''resultVar''
 
* Resulting value.
 
|-
 
| <code>PlayerPosition</code> || Stores the player's position into a variable.<br>{{Note|Only works for vector variables like <code>$baseTextureOffset</code> or <code>$color</code>.}} ||
 
''scale''
 
* An amount to scale the position by.
 
''resultVar''
 
* Resulting value.
 
|-
 
| <code>EntitySpeed</code> || Stores the entity's speed into a variable.<br>{{Note|Does not work on world surfaces.}} ||
 
''scale''
 
* An amount to scale the speed by.
 
''resultVar''
 
* Resulting value.
 
|-
 
| <code>TextureTransform</code> || Generates a texture transform matrix. ||
 
''centerVar''
 
* Name of a variable holding the center of rotation and scaling. [Optional]
 
''scaleVar''
 
* Name of scale variable (2D vector). [Optional]
 
''rotateVar''
 
* Name of rotation angle variable (float). [Optional]
 
''translateVar''
 
* Name of the translation variable (2D vector).
 
''resultVar''
 
* Resulting value.
 
|-
 
| <code>Empty</code> || Used to comment out proxies. Surround a bunch of proxies with the empty proxy to cause those proxies to not operate.||
 
|-
 
| <code>TextureScroll</code> || Returns a transform matrix or vector that will translate a texture at a given angle at a given rate. ||
 
''textureScrollVar''
 
* Destination for the resulting transformation.
 
''textureScrollRate''
 
* Rate of scroll in units per second.
 
''textureScrollAngle''
 
* Angle of rotation to move along. (90 = right, 180 = left, etc)
 
|-
 
| <code>ToggleTexture</code> || Toggles a texture based on the frame number set by the attached entity.<br> {{Note|Must be attached to an entity.}} ||
 
''toggleTextureVar''
 
* Texture to modify based on frames.
 
''toggleTextureFrameNumVar''
 
* Variable used for frame number.
 
''toggleShouldWrap''
 
* Whether the animation should wrap over not.
 
|-
 
| <code>RandomEntity</code> || A proxy that returns a random number associated with the entity the material is applied to. Can be helpful for making animated textures not all be in sync. ||
 
''scale''
 
* Amount to scale the random number by.
 
''resultVar''
 
* Resulting value.
 
|-
 
| <code>CurrentTime</code> || Returns the current time as a float. ||
 
''resultVar''
 
* The current time.
 
|-
 
| <code>PlayerHealth</code> || Stores the player health (0-1) in a variable. ||
 
''scale''
 
* Amount to scale the health value by.
 
''resultVar''
 
* Resulting value.
 
|-
 
| <code>PlayerDamageTime</code> || Stores the time since the player was last damaged, in a variable. ||
 
''scale''
 
* Amount to scale the time by.
 
''resultVar''
 
* Resulting value.
 
|-
 
| <code>MaterialModify</code> || Sets the base texture to a value held by the entity (used for entity controlled texture animations).<br>{{Note|Only works when attached to an entity.}} ||
 
No return value.
 
|-
 
| <code>WaterLOD</code> || Coordinates water LOD values between the map's <code>env_waterlod</code> entity and the materials internal values. ||
 
<code>$CHEAPWATERSTARTDISTANCE</code>
 
* Start distance for cheap water
 
{{note|must be set outside of proxy block.}}
 
<code>$CHEAPWATERENDDISTANCE</code>
 
* End distance for cheap water
 
{{note|must be set outside of proxy block.}}
 
No return value.
 
|-
 
| <code>BreakableSurface</code> || Sets the base texture to a material name held by the entity (used for switching surface material on shatter)<br>{{Note|Must be attached to a <code>func_breakablesurface</code> entity.}} ||
 
No return value.
 
|-
 
| <code>ConveyorScroll</code> || Returns the scroll parameters for a texture used as a conveyor. <br>{{Note|Must be attached to <code>func_conveyor</code> entity.}} ||
 
''textureScrollVar''
 
* Name of variable to place result in.
 
{{note|must be a matrix or vector type variable (i.e. <code>$baseTextureOffset</code>).}}
 
|-
 
| <code>LampBeam</code> || Modulates the material's alpha value based on angle between the beam's direction and the viewer's eye point. This is used to make the beams of volumetric light on lights fade as you look at them dead on.<br>{{Note|Must be attached to entity for angle use.}}||
 
No return value.
 
|-
 
| <code>LampHalo</code> || Modulates the material's alpha value based on angle between the beam's direction and the viewer's eye point.<br>Like the LampBeam proxy, but used for the halo at the beam's base.<br>{{Note|Must be attached to entity for angle use.}}||
 
No return value.
 
|-
 
| <code>AnimatedTexture</code> || Increments frame variable ||
 
''animatedtexturevar''
 
* Texture to increment frame for (i.e. <code>$basetexture</code>, <code>$bumpmap</code>)
 
''animatedtextureframenumvar''
 
* Frame variable to increment (i.e. <code>$frame</code>)
 
''animatedtextureframerate''
 
* Framerate in frames per second
 
|-
 
| <code>Camo</code> || {{note|Only used by material "Dev\dev_camo.vmt".}} ||
 
''camopatterntexture''
 
  
''camoboundingboxmin''
+
=== Tools recording ===
  
''camoboundingboxmax''
+
This code was added to all proxies in the Orange Box:
  
''surfaceprop''
+
<source lang=cpp>
|-
+
#include "toolframework_client.h"
| <code>HeliBlade</code> || ||
+
 
|-
+
void OnBind(...)
| <code>ParticleSphereProxy</code> || {{note|Only used by material "particle\SmokeStack.vmt".}} Seems to be defined in <code>particle_proxies.cpp</code>. Valve remark: "FIXME:  Is this even needed any more?" ||
+
{
No known parameters.
+
//...
|-
+
 
| <code>Shadow</code> || {{note|Only used by material "Decals\rendershadow.vmt".}} ||
+
if ( ToolsEnabled() )
No known parameters.
+
ToolFramework_RecordMaterialParams( GetMaterial() );
|-
+
}
| <code>ShadowModel</code> || {{note|Only used by material "Decals\rendermodelshadow.vmt".}} ||
+
</source>
No known parameters.
+
 
|}
+
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 ==
 +
* [[List Of Material Proxies]]
 
* [[Material Creation]]
 
* [[Material Creation]]
 +
 +
== External links ==
 +
* [http://web.archive.org/web/20140719034333/http://www.nodraw.net/2010/01/dynamic-materials-with-proxies/ Dynamic Materials With Proxies] - An article covering practical uses of materials with proxies by [http://www.nodraw.net NoDraw.net]
  
 
[[Category:Material System]]
 
[[Category:Material System]]
 +
[[Category:Programming]]

Latest revision as of 12:35, 21 April 2019

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 together provide rudimentary scripting support within VMT files.

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

Bug: Some users have reported that the tools mode will not run some functional proxies in game.

To do: Extent of bug untested.

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.

These custom variables might be used to pass results between proxies or 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 2D/3D vectors ("[0 0 0]"), the variable name can have [0] suffixed to read/write a specific index. Writes to indexed variables should be encased in quotes.

This example extends the one above by staggering the starting position of the sine wave:

LightmappedGeneric
{
	$basetexture shadertest/LightmappedTexture

	$offset 0 // declare custom var
	
	Proxies
	{
		EntityRandom
		{
			resultVar $offset // write to custom var
		}
		Sine
		{
			resultVar	$alpha
			timeoffset	$offset // read from custom var
			sineperiod	8
			sinemin		0
			sinemax		1
		}
	}
}

Now each entity this material is used on pulses to its own schedule.

Other examples for writing to indexed variables:

	$color "[0 0 0]"

	proxies
	{
		sine
		{
			sineperiod	1.3
			sinemin		0
			sinemax		1
			timeoffset	0
			resultvar	"$color[0]"
		}
		sine
		{
			sineperiod	1.7
			sinemin		0
			sinemax		1
			timeoffset	0
			resultvar	"$color[1]"
		}
		sine
		{
			sineperiod	2.3
			sinemin		0
			sinemax		1
			timeoffset	0
			resultvar	"$color[2]"
		}
	}

An example of using a color vector to create 'random' color pulses.

UnlitGeneric
{
	$basetexture "dev\gradient_dif"
	$color "[1 .8 .6]"

	$detail "dev\noise_512x32"
	$detailscale 1
	$detailblendmode 0
	$detailblendfactor 4.0

	$additive 1
	$nocull 1

	$cvar "[.5 .5]"
	$svar "[1 .25]"
	$rvar 0
	$tvar "[0 0]"

	$sine1 0
	$sine2 0

	proxies
	{
		linearramp
		{
			rate .3
			initialvalue 0
			resultvar "$tvar[1]"
		}
		sine
		{
			sineperiod 1.3
			sinemin -.004
			sinemax .002
			timeoffset 0
			resultvar $sine1
		}
		sine
		{
			sineperiod 1.7
			sinemin -.003
			sinemax .007
			timeoffset .2
			resultvar $sine2
		}
		add
		{
			srcvar1 $sine1
			srcvar2 $sine2
			resultvar "$tvar[0]"
		}
		texturetransform
		{
			centervar $cvar
			scalevar $svar
			rotatevar $rvar
			translatevar $tvar
			resultvar $detailtexturetransform
		}
	}
}

An example of a dynamic texture transform.

Splitting a vector

Using vectors is quirky. Not all proxies recognize vector components. If a vector's components need be processed separately, they need to be split into different variables first.

The following proxies, and only these keyvalues, can recognize vector components:

  • Clamp: min, max
  • Sine: offset, max, min, period
  • LinearRamp: rate, initial value
  • UniformNoise: min, max
  • GaussianNoise: min, max, mean, halfwidth
  • WrapMinMax: min, max
  • Exponential: min, max, scale, offset

All of the above can be used to split a vector. However Clamp is the cheapest to use:

        $pos "[0 0 0]"
        $posX .0        //must be float or Clamp will not save the value properly
        $posY .0        //must be float or Clamp will not save the value properly
        $posZ .0        //must be float or Clamp will not save the value properly
        
        $zero 0
        
        //Proxy that outputs a 3d vector
        PlayerPosition
        {
                scale                    1
                resultVar               "$pos"
        }
        
        //Split the 3d vector for further use
        Clamp
        {
            srcVar1                      $zero
            min                         "$pos[0]"
            max                         "$pos[0]"
            resultVar                    $posX
        }
        
        Clamp
        {
            srcVar1                      $zero
            min                         "$pos[1]"
            max                         "$pos[1]"
            resultVar                    $posY
        }
        
        Clamp
        {
            srcVar1                      $zero
            min                         "$pos[2]"
            max                         "$pos[2]"
            resultVar                    $posZ
        }

Warning: Quotes are needed when addressing a vector's component, or when defining a vector.

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/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).
pKeyValues contains the proxy parameters from the VMT file.
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: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(C_BaseEntity*) overload it provides.
void Release()
To do: Called when the proxy is removed, but when is that?
IMaterial* GetMaterial()
The material the proxy is attached to.
Tip: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:CEntityMaterialProxy makes the call by itself.

See also

External links