Material proxies
This page either contains information that is only partially or incorrectly translated, or there isn't a translation yet.
If this page cannot be translated for some reason, or is left untranslated for an extended period of time after this notice is posted, the page should be requested to be deleted.
Also, please make sure the article tries to comply with the alternate languages guide.
借助材质代理,游戏的已编译的C++代码得以控制材质 的属性。不少材质代理只用于执行特定的任务, 但是 这里也存在一些通用的材质代理 ,它们在VMT文件内提供一些最基本的脚本编程支持。
可以添加任意数量的代理,它们会按照顺序执行。
用法
这是一个Sine(正弦)
材质代理,提供在8秒内对材质进行淡入淡出 操作的功能:
LightmappedGeneric
{
$basetexture shadertest/LightmappedTexture
Proxies // 代理的代码块
{
Sine // 产生正弦波的代理
{
resultVar $alpha // 需要被控制的着色器参数
sineperiod 8
sinemin 0
sinemax 1
}
}
}
变量
材质可以在内部使用中声明它们的自有变量。这样的变量必须被声明于代理代码块之外(VMT的主体中),并且必须在右侧给定一个默认值。
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
}
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 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).
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.注意:pC_BaseEntity
doesn't lead to aC_BaseEntity
as its name suggests, but rather to the associatedIClientRenderable
. The easiest way to access the entity directly is to base your class onCEntityMaterialProxy
(inproxyentity.h
) and use theOnBind(C_BaseEntity*)
overload it provides. void Release()
- 待完善: Called when the proxy is removed, but when is that?
IMaterial * GetMaterial()
- The material the proxy is attached to. 提示:If you have a material var stored, you can return
IMaterialVar::GetOwningMaterial()
here instead of creating a newIMaterial
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!
CEntityMaterialProxy
makes the call by itself.See also
External links
- Dynamic Materials With Proxies - An article covering practical uses of materials with proxies by NoDraw.net