Material proxies
Material proxiespermita o codigo C++ compilado manipular as propiedades de um material . Muitos proxies performan tarefas especificas, mas existem outros mais gerais que juntas providem um suporte para codigo rudimentar nos arquivos VMT. [Clarify]
Qualquer numero de proxies podem ser adicionado para um material; eles serão excetuados na ordem que eles aparecerem.
Como usar
Este material tem um proxy Sine
que o faz desaparecer dentro e fora de vista durante um período de oito segundos:
LightmappedGeneric
{
$basetexture shadertest/LightmappedTexture
Proxies // proxies estão listados dentro deste bloco
{
Sine //um proxy que produz uma onda senoidal
{
resultVar $alpha //O parâmetro shader a ser manipulado
sineperiod 8
sinemin 0
sinemax 1
}
}
}
Variáveis
Os materiais podem declarar suas próprias variáveis para uso interno. Tais variáveis devem ser declaradas fora do bloco Proxies, no corpo do material, e devem ter valores padrão especificados no lado direito.
Essas variáveis personalizadas podem ser usadas para transmitir resultados entre proxies ou para enviar dados codificados para eles. Eles são frequentemente empregados para encadear proxies de funções matemáticas (ou seja, Adicionar
, Subtrair
, etc) em equações mais longas. Para vetores 2D/3D ("[0 0 0]"
), o nome da variável pode ter [0]
com sufixo para ler/gravar um índice específico. As gravações em variáveis indexadas devem ser colocadas entre aspas.
O mecanismo codifica essas variáveis personalizadas usando um número inteiro assinado de 8 bits. Portanto, há um limite de 128 variáveis personalizadas exclusivas por material.
Este exemplo estende o exemplo acima escalonando a posição inicial da onda senoidal:
LightmappedGeneric
{
$basetexture shadertest/LightmappedTexture
$offset 0 // declare custom var
Proxies
{
EntityRandom
{
resultVar $offset // escreva na custom var
}
Sine
{
resultVar $alpha
timeoffset $offset // leia da custom var
sineperiod 8
sinemin 0
sinemax 1
}
}
}
Agora cada entidade utiliza esse material em pulsos de acordo com sua própria programação.
Outros exemplos para escrever em variáveis indexadas:
$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
}
}
}
Um exemplo de transformação de textura dinâmica.
Dividindo um vetor
Usar vetores é peculiar. Nem todos os proxies reconhecem componentes vetoriais. Se os componentes de um vetor precisarem ser processados separadamente, eles precisam primeiro ser divididos em variáveis diferentes.
Os seguintes proxies, e apenas estes valores-chave, podem reconhecer componentes de vetor:
- Clamp: min, max
- Sine: desvio , max, min, periodo
- LinearRamp: taxa, valor inicial
- UniformNoise: min, max
- GaussianNoise: min, max, mean, meia largura
- WrapMinMax: min, max
- Exponential: min, max, escala ,desvio
Todos os itens acima podem ser usados para dividir um vetor. No entanto, Clamp é o mais barato para usar:
$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.Nota: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()
- Pendência: Called when the proxy is removed, but when is that?
IMaterial * GetMaterial()
- The material the proxy is attached to. Dica: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
- List Of Material Proxies
- Material proxies programming
- Material Creation
- Steam guides, explaining more about various Proxies. Basics, RNG, "Programming"
External links
- Dynamic Materials With Proxies - An article covering practical uses of materials with proxies by NoDraw.net