Material proxies:zh-cn

From Valve Developer Community
Revision as of 10:12, 23 December 2019 by 求生的兔 (talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
English 简体中文 

材质代理允许游戏编译C++代码来控制贴图。 许多代理执行特定的任务, 但是 这里也有一些通常的,在VMT文件内提供一些基础向的脚本语言支持。

可以添加任意数量的代理,它们会按照顺序执行。

Bug: 一些用户说工具模式下的游戏不会启用一些材质代理功能。

To do: 没测试过。

用途

这是一个具有Sine(正弦)的材质代理,允许在8秒内对它进行淡入淡出操作。

LightmappedGeneric
{
	$basetexture shadertest/LightmappedTexture

	Proxies // 代理的代码块
	{
		Sine // 提供正弦波的代理
		{
			resultVar	$alpha // 需要控制的参数部分
			sineperiod	8
			sinemin		0
			sinemax		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. 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