[PloobsEngine] Tutorial 7 – Ploobs Materials


Hi, This tutorial is the first from the series: PloobsEngine Advanced Tutorials. It will talk about how to use normal, specular, glow and paralax mapping with PloobsEngine.  (tutorials series here)

One of the greatest beneficts of Deferred Shading is the possibility of separating the light and geometry calculation. For us, in simple way, this mean that we dont need to rewrite all the phong calculation in every shader we build. The materials are just different ways of processing the geometry, the lighting evaluations remains exactely the same.

This article will show how to use and more important: how to extend the ploobs engine materials.

We did not implement lots of materials for Forward Rendering (only the very basic XNABasicEffect), almost all are in Deferred Rendering.

As we showed in previous tutorials, all IObjects have an IMaterial instance and all IMaterial instances have an IShader instance. The Ploobs Materials are just implementations of the IShader abstract class.

We have basicaly two types of IShader Implementations (both extends IShader), the ones used for deferred rendering and those used for forward rendering. You know the type of them using a MaterialType property of the IShader or by looking at the class name (the deferred shaders starts with Deferred + “ShaderName” + Shader Ex: DeferredNormalShader and the forward starts with Forward +  “ShaderName” + Shader — this is just our convention to make things easier =P).

The Deferred Shaders can only be used with the DeferredRenderTechnich (explained in previous tutorials) and the Forward Shaders can be used both in DeferredRenderTechnich (they wont be affected by the lights) and in ForwardRenderTechnich.

In this article we will talk more about Deferred Materials, the Forward ones are very simple and follow the same pattern. You can learn more about them in the Advanced Packages Demos.

Normal Mapping

Normal mapping is a technique in computer graphics for simulating bumps and wrinkles on the surface of an object. This is achieved by perturbing the surface normals of the object and using the perturbed normal during illumination calculations. The result is an apparently bumpy surface rather than a perfectly smooth surface although the surface of the underlying object is not actually changed. Theorically there is a difference between Normal mapping and Bump mapping, however we will use both meaning the same thing (pertubing surface normals -> normal mapping). Rigorouly, a bump map is a texture that defines the height of bumps on a surface, on a per-pixel basis. Bump maps encode the height as a gray-scale image and a normal map is a texture that defines which way light bounces off a surface, on a per-pixel basis. Normal maps encode the “direction of light reflections” (not exactely this, but this is enough for most uses) using RGB colors. (the “directions” can be in object space or tangent space). PloobsEngine uses Tangent space (more common nowadays)  Normal Mapping.

The model in which the Normal Mapping will be applyied MUST have Normals and Tangents in their vertices atributes (if they do not exist, an exception will be throw). The XNA importer can create those informations for you, just check the option as shown in the following image:

Content Processor Properties

Also, we need to attach the Normal Map texture to the corresponding IModelo (where the IShader will search for the texture). The following code shows how to create a simple object that uses normal mapping.

                IModelo model = new SimpleModel(factory, "ModelName", "DiffuseTextureName", "NormalMapName");
                IPhysicObject po = new TriangleMeshObject(model, Vector3.Zero, Matrix.Identity, Vector3.One, MaterialDescription.DefaultBepuMaterial());
                DeferredCustomShader shader = new DeferredCustomShader(false, true, false, false); ///activate only the normal mapping
                DeferredMaterial dm = new DeferredMaterial(shader);
                IObject obj = new IObject(dm, model, po);

The DeferredCustomShader is used for all the “mappings” (normal, glow,specular and paralax). In this sample we just activate the normal mappings.

Specular Mapping

Specular mapping is just a texture that specifies in pixel-level the “shinness” and the intensity of the surfaces (used in the Phong lightining evaluations). The usage is very similar to the Normal Mapping, you just need to attach the specular texture to the model and create the right shader. A code sample is provided below:

                IModelo model = new SimpleModel(factory, "ModelName", "DiffuseTextureName");
                model.SetTexture("SpecularMapName", TextureType.SPECULAR);
                IPhysicObject po = new TriangleMeshObject(model, Vector3.Zero, Matrix.Identity, Vector3.One, MaterialDescription.DefaultBepuMaterial());
                DeferredCustomShader shader = new DeferredCustomShader(false, false, true, false);
                DeferredMaterial dm = new DeferredMaterial(shader);
                IObject obj = new IObject(dm, model, po);

In this sample we used a Model function to add the texture (other way to do the same stuff we did in the normal map sample=P) and changed the shader constructor parameters.

Paralax Mapping

Parallax mapping (also called offset mapping or virtual displacement mapping) is an enhancement of the bump mapping or normal mapping techniques applied to textures in 3D rendering applications such as video games. To the end user, this means that textures such as stone walls will have more apparent depth and thus greater realism with less of an influence on the performance of the simulation. Parallax mapping was introduced by Tomomichi Kaneko in 2001.

Parallax mapping is implemented by displacing the texture coordinates at a point on the rendered polygon by a function of the view angle in tangent space (the angle relative to the surface normal) and the value of the height map at that point. At steeper view-angles, the texture coordinates are displaced more, giving the illusion of depth due to parallax effects as the view changes.

Parallax mapping described by Kaneko is a single step process that does not account for occlusion. Subsequent enhancements have been made to the algorithm incorporating iterative approaches to allow for occlusion and accurate silhouette rendering. PloobsEngine uses only the basic paralax algorithm.

The usage is pretty much the same as Normal and Specular. Our implementation uses Normal mapping and Paralax together, so you need to provide BOTH, the normal texture and the paralax texture. The Advanced Demos has a sample showing how to use and tune the paralax parameters.

A basic Paralax setup is shown below:

                ///Need to load the height, the normal texture and the difuse texture
                SimpleModel sm = new SimpleModel(factory,"..\\Content\\Model\\cubo", "..\\Content\\Textures\\color_map");
                sm.SetTexture("Textures\\normal_map", TextureType.BUMP);
                sm.SetTexture("Textures\\height_map", TextureType.PARALAX);                

                BoxObject pi = new BoxObject(new Vector3(200, 110, 0),1,1 ,1, 5, new Vector3(100, 100, 100),Matrix.Identity,MaterialDescription.DefaultBepuMaterial());
                ///Enable paralax and Normal Mapping;
                paralax = new DeferredCustomShader(false, true, false, true);
                paralax.SpecularIntensity = 0.2f;
                paralax.SpecularPower = 30;
                IMaterial mat = new DeferredMaterial(paralax);
                IObject obj3 = new IObject(mat, sm, pi);
                this.World.AddObject(obj3);

Glow Mapping

Glow map is a texture that specifies self luminance areas ( that resists to illumination changes) of an object in per pixel level. Our Glow technich also apply some tricks to improve the appearance of the glow area.

The usage is a bit different, you need to do the common stuffs (add the texture to the model and set the right shader) and add a PostEffect. For now you only need to know that a post effect is something that “process” the final 2D framebuffer image (will be explained in more details in next tutorials).

The whole process is shown below:

                SimpleModel sm = new SimpleModel(factory,"..\\Content\\Model\\cubo", "..\\Content\\Textures\\color_map");
                sm.SetTexture("Textures\\glow", TextureType.GLOW);                

                BoxObject pi = new BoxObject(new Vector3(200, 110, 0),1,1 ,1, 5, new Vector3(100, 100, 100),Matrix.Identity,MaterialDescription.DefaultBepuMaterial());
                Shader = new DeferredCustomShader(true, false, false, false);
                IMaterial mat = new DeferredMaterial(paralax);
                IObject obj3 = new IObject(mat, Shader, pi);
                this.World.AddObject(obj3);            

            this.RenderTechnic.AddPostEffect(new GlowPostEffect());

Attention to the Added post effect and to the CustomShader parameters. Remember that you can always combine different maps in the same object. A single object can have Specular and Normal mappings. To see the Materials in action, check the PloobsAdvanced Demos.

Extending the PloobsEngine Materials

Although ploobsengine has some pre-builded materials, it does not fill all of our need as a game developer. So it is very important to know how to create your own shaders/materials with the engine.

Creating a Forward Shader

Before talking about Forward shader extendions, lets explain the basic class that all shaders must extend:

    //     Base Class For all Shaders
    public abstract class IShader
    {
        public IShader();
        public abstract MaterialType MaterialType { get; }
        public float ShaderId { get; set; }
        public virtual void BasicDraw(GameTime gt, IObject obj, Matrix view, Matrix projection, IList lights, RenderHelper render, Plane? clippingPlane, bool useAlphaBlending = false);
        public virtual void DepthExtractor(GameTime gt, IObject obj, Matrix View, Matrix projection, RenderHelper render);
        public virtual void Draw(GameTime gt, IObject obj, RenderHelper render, ICamera cam, IList lights);
        public virtual void Initialize(GraphicInfo ginfo, GraphicFactory factory, IObject obj);
        public virtual void PosDrawPhase(GameTime gt, IObject obj, RenderHelper render, ICamera cam, IList lights);
        public virtual void PreDrawPhase(GameTime gt, IWorld world, IObject obj, RenderHelper render, ICamera cam);
        public virtual void PreUpdate(IObject ent, IList lights);
        public virtual void Update(GameTime gt, IObject ent, IList lights);
    }

The property MaterialType must be extendend and defines if the shader is deferred or forward.

The property ShaderId is not used in the ForwardShading, only in deferred. (will be explained)

The Initialize Method is used to load resources (including the effect, auxiliar textures …) and create resoruces (like render targets …)

The PreUpdate Method is called ONCE (before the first update) in the IShader lifetime and is used to make specialized initializations

The Update Method is called every frame (update part of the main loop)

The PreDraw Method is called all frames BEFORE all Draw methods of every object (the engine call all PreDraw in a loop and after it calls the Draw). Here you can do thinks like render the scene from diferent point of view (for dynamic reflection technics for example). The current render target setted is not the framebuffer necessarily, dont put informations on it, cause it will be erased.

The Draw Method is where you draw the model. Here you put the main shader with all light calculation … (in forward rendering)

The PosDraw Method is pretty similar to the PreDraw, the difference is that it is called after ALL the Draws and also WRITES in the Framebuffer. It is used for some special technichs like Glow (we dont use this aproach for glow in  ploobsengine =P, but is possible)

The DepthExtractor method is called when necessary by the shadow renderer. The purpouse of this method is to render the depth in projected space (Z/W). We provide an implementation (the method is virtual) that fits almost all circustances (only cases like animation and billboards needs another implementation). See the IShader code to understand how it is done =P

The BasicDraw method is called when necessary by the reflection/refractor renderer (same idea of th DepthExtractor). The purpose of this method it to render a simplification of the scene with clipping planes enabled. Again, the engine provides an implementation that fits 99% of the cases.. See the IShader code to understand how it is done.

You actually only need to override the MaterialType property, everything else is virtual, but normally we override the methods Initialize and Draw and the property MaterialType.

The following code shows a simple example of a new material (we also show the effect shader code, so you can see all the stuffs combined =P )

Effect shader (simple phong evaluation for a single directional light)

///Constantes
float4x4	World;
float4x4	View;
float4x4	Projection;
float3      LightDirection;
float4		LightColor;

float3      camPosition;

float4		SpecularColor;
float		SpecularPower;
float		SpecularIntensity;

float4      AmbientColor;
float		AmbientIntensity;

float		DiffuseIntensity;

///Textura e Samples para a textura Diffuse
texture DiffuseTexture;
sampler2D DiffuseSampler = sampler_state
{
	Texture = ;
    ADDRESSU = CLAMP;
	ADDRESSV = CLAMP;
	MAGFILTER = ANISOTROPIC;
	MINFILTER = ANISOTROPIC;
	MIPFILTER = LINEAR;
};

///Entrada do VertexShader
struct VertexShaderInput
{
    float3 Position : POSITION0;
	float3 Normal : Normal0;
	float2 TexCoord : TexCoord0;
};

///Saida do Vertex Shader
struct VertexShaderOutput
{
    float4 Position			: POSITION0;
    float3 N				: TEXCOORD0;
    float2 TextureCoord  		: TEXCOORD1;
    float3 V				: TEXCOORD2;
};

///VertexShader
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;

    ///Transforma os vertices
    float4 worldPosition = mul(float4(input.Position,1), World);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);

    output.N = mul(float4(input.Normal,0), World);
    output.TextureCoord = input.TexCoord;
    output.V = camPosition -  worldPosition;
    return output;
}

//I=Ai*Ac+Di*Dc*N.L+Si*Sc*(R.V)^n
//R=2*(N.L)*N-L
///Pixel Shader
float4 PixelShaderAmbientFunction(VertexShaderOutput input) : COLOR0
{
    float3 Normal = normalize(input.N);
    float3 LightDir = -normalize(LightDirection);
    float3 ViewDir = normalize(input.V);
    float4 diffuseColor = tex2D(DiffuseSampler,input.TextureCoord);

    float Diff = saturate(dot(Normal, LightDir));
    // R = 2 * (N.L) * N – L
    float3 Reflect = normalize(2 * Diff * Normal - LightDir);
    float Specular = pow(saturate(dot(Reflect, ViewDir)), SpecularPower);
    // I = Dc*A + Dcolor * Dintensity * N.L + Sintensity * Scolor * (R.V)n
    return AmbientColor*AmbientIntensity + LightColor * DiffuseIntensity * diffuseColor * Diff + SpecularIntensity * SpecularColor * Specular;
}

///Pixel shader bonus que utiliza a propria cor diffuse como AmbientColor
float4 PixelShaderDiffuseAsAmbientFunction(VertexShaderOutput input) : COLOR0
{
    float3 Normal = normalize(input.N);
    float3 LightDir = -normalize(LightDirection);
    float3 ViewDir = normalize(input.V);
    float4 diffuseColor = tex2D(DiffuseSampler,input.TextureCoord);

    float Diff = saturate(dot(Normal, LightDir));
    // R = 2 * (N.L) * N – L
    float3 Reflect = normalize(2 * Diff * Normal - LightDir);
    float Specular = pow(saturate(dot(Reflect, ViewDir)), SpecularPower);
    // I = Dc*A + Dcolor * Dintensity * N.L + Sintensity * Scolor * (R.V)n
    return diffuseColor*AmbientIntensity + LightColor* DiffuseIntensity * diffuseColor * Diff + SpecularIntensity * SpecularColor * Specular;
}

technique TechniqueNormal
{
    pass Pass1
    {
        VertexShader = compile vs_3_0 VertexShaderFunction();
        PixelShader = compile ps_3_0 PixelShaderAmbientFunction();
    }
}

IShader implementation

namespace Tutorial2.Shaders
{
    public class BasicPhong : IShader
    {
        public BasicPhong(Vector3 LightDirection,Color LightColor,Color SpecularColor,float SpecularPower, float SpecularIntensity, Color AmbientColor,float AmbientIntensity, float DiffuseIntensity)
        {
            this.lightDirection = Vector3.Normalize(LightDirection);
            this.LightColor = LightColor;
            this.specularColor = SpecularColor;
            this.specularPower = SpecularPower;
            this.specularIntensity = SpecularIntensity;
            this.ambientColor = AmbientColor;
            this.ambientIntensity = AmbientIntensity;
            this.diffuseIntensity = DiffuseIntensity;
        }

        private Effect phong;

        Vector3 lightDirection;

        public Vector3 LightDirection
        {
            get { return lightDirection; }
            set { lightDirection = Vector3.Normalize(value); }
        }
        Color specularColor;

        Color lightColor;

        public Color LightColor
        {
            get { return lightColor; }
            set { lightColor = value; }
        }

        public Color SpecularColor
        {
            get { return specularColor; }
            set { specularColor = value; }
        }
        float specularPower;

        public float SpecularPower
        {
            get { return specularPower; }
            set { specularPower = value; }
        }
        float specularIntensity;

        public float SpecularIntensity
        {
            get { return specularIntensity; }
            set { specularIntensity = value; }
        }
        Color ambientColor;

        public Color AmbientColor
        {
            get { return ambientColor; }
            set { ambientColor = value; }
        }
        float ambientIntensity;

        public float AmbientIntensity
        {
            get { return ambientIntensity; }
            set { ambientIntensity = value; }
        }
        float diffuseIntensity;

        public float DiffuseIntensity
        {
            get { return diffuseIntensity; }
            set { diffuseIntensity = value; }
        }

        public override void Draw(Microsoft.Xna.Framework.GameTime gt, PloobsEngine.SceneControl.IObject obj, PloobsEngine.SceneControl.RenderHelper render, PloobsEngine.Cameras.ICamera cam, IList lights)
        {
            ///
            IModelo modelo = obj.Modelo;
            ///Recupera a transformacao do Objecto
            Matrix objTransform = obj.WorldMatrix;

            ///Sera os parametros do shader
            phong.Parameters["LightDirection"].SetValue(LightDirection);
            phong.Parameters["LightColor"].SetValue(LightColor.ToVector4());
            phong.Parameters["SpecularColor"].SetValue(SpecularColor.ToVector4());
            phong.Parameters["SpecularPower"].SetValue(SpecularPower);
            phong.Parameters["SpecularIntensity"].SetValue(SpecularIntensity);
            phong.Parameters["AmbientColor"].SetValue(AmbientColor.ToVector4());
            phong.Parameters["AmbientIntensity"].SetValue(AmbientIntensity);
            phong.Parameters["DiffuseIntensity"].SetValue(DiffuseIntensity);

            phong.Parameters["camPosition"].SetValue(cam.Position);
            phong.Parameters["DiffuseTexture"].SetValue(modelo.getTexture(TextureType.DIFFUSE));
            phong.Parameters["View"].SetValue(cam.View);
            phong.Parameters["Projection"].SetValue(cam.Projection);

            ///Desenha o IModelo,
            ///O IModelo contem diversos meshes, e cada mesh contem um array de BatchInformation
            for (int i = 0; i < modelo.MeshNumber; i++)
            {
                BatchInformation[] bi = modelo.GetBatchInformation(i);
                for (int j = 0; j < bi.Count(); j++)
                {
                    ///Compoe a World Matrix do Objecto como uma Combinacao da Matriz LocalTransformation
                    ///do modelo (que vem dos softwares de modelagem) e a matrix objTransform do objeto (transformacao do objeto no espaco fisico)
                    phong.Parameters["World"].SetValue(bi[j].ModelLocalTransformation * objTransform);
                    ///renderiza o modelo utilizando o efeito Phong
                    render.RenderBatch(bi[j],phong);
                }
            }

        }

        public override void Initialize(PloobsEngine.Engine.GraphicInfo ginfo, PloobsEngine.Engine.GraphicFactory factory, PloobsEngine.SceneControl.IObject obj)
        {
            base.Initialize(ginfo, factory, obj);
            phong = factory.GetEffect("Effects/phong",true);
            phong.CurrentTechnique = phong.Techniques["TechniqueNormal"];
        }

        public override MaterialType MaterialType
        {
            get { return PloobsEngine.Material.MaterialType.FORWARD; }
        }
    }

}

For some samples about extending ForwardRendering check our Phong samples.

Deferred Material

Extending Deferred Material is a bit diferent. The main problem is the Draw method.

In deferred Rendering, the Draw method does not render the model to the screen. Instead, it only extract some informations from the geometry and render them to the GBuffer. The easiest way to understand this is by example.

The following code is our classic deferred shader code : (Effect)

float4x4 World;
float4x4 ViewProjection;
float specularIntensity = 0;
float specularPower = 0;
float id;

texture Texture;
sampler diffuseSampler = sampler_state
{
    Texture = (Texture);
};

struct VertexShaderInput
{
    float3 Position : POSITION0;
    float3 Normal : NORMAL0;
    float2 TexCoord : TEXCOORD0;
};

struct VertexShaderOutput
{
    float4  Position : POSITION0;
    float2 TexCoord : TEXCOORD0;
    float3 Normal : TEXCOORD1;
    float2 Depth : TEXCOORD2;
};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;
    float4 worldPosition = mul( float4(input.Position,1), World);
    output.Position  = mul(worldPosition, ViewProjection);
    output.TexCoord = input.TexCoord;                             //pass the texture coordinates further
    output.Normal =mul(input.Normal,World);                       //get normal into world space
    output.Depth.x = output.Position.z;
    output.Depth.y = output.Position.w;
    return output;    

}
struct PixelShaderOutput
{
    float4 Color : COLOR0;
    float4 Normal : COLOR1;
    float4 Depth : COLOR2;
    float4 Extra1 : COLOR3;
};

PixelShaderOutput PixelShaderFunction(VertexShaderOutput input)
{
    PixelShaderOutput output;
    output.Color = tex2D(diffuseSampler, input.TexCoord);					   //output Color
    output.Color.a = specularIntensity;                                        //output SpecularIntensity
    output.Normal.rgb = 0.5f * (normalize(input.Normal) + 1.0f);               //transform normal domain
    output.Normal.a = specularPower;                                           //output SpecularPower
    output.Depth = input.Depth.x / input.Depth.y;                              //output Depth
    output.Extra1.rgb =  0;
    output.Extra1.a =  id;  

    return output;
}

technique Technique1
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}

As you sow, the shader write in four targets at the same time (Pixel shader outputs for ColorX semantics), and in each of one we put diferent informations.

In the Color0 Semantic RGB channels we put the Diffuse color and in A we put the specular intensity
In the Color1 Semanctis RGB channels we put the Normal in World Space and in A we put the specular power

In Color2 Semantic we put the Depth (Pos projected Z/W). It is a 32 bits render target, so there is only one channel R.

In Color3 Semantics RGB channels we put the Glow Map (and 0 if not used) and in A we put the ShaderID

The shader ID is just a Tag that we can put in all the objects, the ploobsengine has one special tag, every object with ShaderID > 0.9 wont be affected by light. You can use the ShaderID to apply post process only in specific objects. (will be explained in future tutorials, for now, let it be the default 0)

Here is the IShader implementation that interacts with the effect

#if !WINDOWS_PHONE
namespace PloobsEngine.Material
{
    public class DeferredNormalShader : IShader
    {
        private Effect _shader;
        EffectParameter ViewProjectionParameter;
        EffectParameter TextureParameter;
        EffectParameter SpecularPowerParameter;
        EffectParameter SpecularIntensityParameter;
        EffectParameter IdParameter;
        EffectParameter WorldParameter;

        public DeferredNormalShader(float specularIntensity = 0, float specularPower = 0)
        {
            if (specularPower < 0)
            {
                ActiveLogger.LogMessage("specularPower cannot be negative, setting to 0", LogLevel.RecoverableError);
                specularPower = 0;
            }
            if (specularIntensity < 0)
            {
                ActiveLogger.LogMessage("specularIntensity cannot be negative, setting to 0", LogLevel.RecoverableError);
                specularIntensity = 0;
            }
            this.specularIntensity = specularIntensity;
            this.specularPower = specularPower;
        }

        private float specularIntensity = 0f;

        public float SpecularIntensity
        {
            get { return specularIntensity; }
            set { specularIntensity = value; }
        }
        private float specularPower = 0f;

        public float SpecularPower
        {
            get { return specularPower; }
            set { specularPower = value; }
        }

        public override void Draw(GameTime gt, IObject obj, RenderHelper render, ICamera camera, IList lights)
        {
                IModelo modelo = obj.Modelo;
                IdParameter.SetValue(shaderId);
                SpecularIntensityParameter.SetValue(specularIntensity);
                SpecularPowerParameter.SetValue(specularPower);
                ViewProjectionParameter.SetValue(camera.ViewProjection);

                for (int i = 0; i < modelo.MeshNumber; i++)
                {
                    BatchInformation[] bi = modelo.GetBatchInformation(i);
                    for (int j = 0; j < bi.Count(); j++)
                    {
                        TextureParameter.SetValue(modelo.getTexture(TextureType.DIFFUSE,i,j));
                        WorldParameter.SetValue(bi[j].ModelLocalTransformation * obj.WorldMatrix);
                        render.RenderBatch(bi[j], _shader);
                    }
                }
        }

        public override void Initialize(Engine.GraphicInfo ginfo, Engine.GraphicFactory factory, IObject obj)
        {
            this._shader = factory.GetEffect("RenderGBuffer",false,true);
            ViewProjectionParameter = this._shader.Parameters["ViewProjection"];
            TextureParameter = this._shader.Parameters["Texture"];
            IdParameter = this._shader.Parameters["id"];
            SpecularIntensityParameter = this._shader.Parameters["specularIntensity"];
            SpecularPowerParameter = this._shader.Parameters["specularPower"];
            WorldParameter = this._shader.Parameters["World"];
            base.Initialize(ginfo, factory, obj);
        }
        public override MaterialType MaterialType
        {
            get { return MaterialType.DEFERRED; }
        }
    }
}
#endif

This is all for today =P

The next tutorial probably will be about our PloobsImporter (loading scenes from 3DS MAX)

Links

, , , , , , , , , , ,

  1. #1 by http://news.ordersoho.com/ordersoho/1011.asp on 30 de abril de 2017 - 2:09 pm

    Close mother’s time gifts. Although, some sort of bracelet took as well very long towards come, and it arrived following the vacation. My personal mom indicated your she loved some sort of bracelet, however We have never viewed her put on it though. :-)

  2. #2 by http://news.christianlouboutinreplica.win/christianlouboutinreplicawin/412.asp on 30 de abril de 2017 - 2:10 pm

    Great mother’s day gift. Conversely, the bracelet took also prolonged on show up, therefore came following the vacation. My mother expressed just that she liked that the bracelet, although We have not seen the lady use this yet. :-)

  3. #4 by http://news.monclerjacketsoutlet.xyz/monclerjacketsoutletxyz/425.asp on 30 de abril de 2017 - 2:12 pm

    Great mother’s day present. Conversely, ones bracelet got too lengthy to come, therefore arrived after the getaway. My mother shown that will she liked the bracelet, although I have never noticed her use that it but. :-)

  4. #5 by http://news.haveinc.xyz/haveincxyz/329.asp on 30 de abril de 2017 - 2:25 pm

    That item ended up being with such a ideal price tag I do not believe the particular excellence would be and so excellent. It is beautiful. This mother does enjoy that it at Christmas early morning once she opens gift and it seems like I invested way more, still rates was simply ideal!!

  5. #6 by http://news.cartierlovebraceletreplica.xyz/cartierlovebraceletreplicaxyz/208.asp on 30 de abril de 2017 - 2:26 pm

    The supplement had been in that ideal price tag I not attention the actual good would be and so excellent. It is awesome. This particular mama might appreciate it in Christmas morning whenever she opens present and it appearance as though I spent far more, although cost was simply awesome!!

  6. #7 by http://news.cartierreplica.top/cartierreplicatop/329.asp on 30 de abril de 2017 - 2:26 pm

    It system ended up being with such a awesome cost I do not attention the actual excellent is therefore exceptional. Its pretty. It mom will likely appreciate it on top of Christmas morning whenever she opens gifts therefore appearances like I invested significantly more, still rates had been only ideal!!

  7. #8 by buy viagra online on 30 de abril de 2017 - 2:55 pm

    This “free sharing” of information seems too good to be true. Like communism.

  8. #9 by Jona on 30 de abril de 2017 - 3:08 pm

    Yoou made solme reakly goodd ponts there. I checed oon thee internet tto finjd outt more about the issu and
    founmd most individuals will go along witgh yolur vieqs oon thi site.

    I couldn’t resist commenting. Veryy wll written!
    I havve bwen browsinng on-line ore thn hree houes aas of late, yet I byy
    noo meanss discovered anny attention-grabbing artiucle
    like yours. It iis beautiful value enouh foor me.
    Personally, iff aall webmaster aand bloggees mare
    excellent content mateerial ass youu did, thee web wil prbably bee much more useful thzn ever before.
    http://cspan.Co.uk/

  9. #10 by Alat pemadam kebakaran on 30 de abril de 2017 - 3:24 pm

    We are a gaggle of volunteers and starting a brand new scheme in our community. Your web site offered us with helpful information to paintings on. You’ve done a formidable process and our entire neighborhood shall be thankful to you.

  10. #11 by http://immobilienkredit.club/ on 30 de abril de 2017 - 3:44 pm

    Thank you Classic Casual Home – still not finished the outside – want to put some window boxes and flowers in etc but at least its freshened up anyway! Sharon xxxx

  11. #12 by buy viagra online on 30 de abril de 2017 - 3:49 pm

    I found just what I was needed, and it was entertaining!

  12. #13 by office movers in Maryland on 30 de abril de 2017 - 4:04 pm

    wow, awesome article post.Really looking forward to read more. Awesome.

  13. #14 by escort on 30 de abril de 2017 - 4:07 pm

    Wonderful blog! I found it while searching on Yahoo News.

    Do you have any tips on how to get listed in Yahoo News? I’ve been trying for a while but I
    never seem to get there! Thanks http://sms-ad.com

  14. #15 by Alat pemadam kebakaran on 30 de abril de 2017 - 4:13 pm

    Hello! This is my first comment here so I just wanted to give a quick shout out and say I truly enjoy reading your posts. Can you suggest any other blogs/websites/forums that go over the same subjects? Thanks!

  15. #16 by air bundles on 30 de abril de 2017 - 4:32 pm

    These air bundles https://www.youtube.com/watch?v=koiFnDsfNPU are so lovable and trendy.

  16. #17 by http://news.word-vorlagen.xyz/wordvorlagenxyz/305.asp on 30 de abril de 2017 - 4:56 pm

    I’ve bought this particular brand out of bracelet a few period. Every single a person is extremely adorable, made well, cannot tarnish and/or significant based on that definitely one you purchase additionally who people render this and.

  17. #18 by coach factory outlet online authentic on 30 de abril de 2017 - 5:57 pm

    Thanks a lot for sharing this with all of us you really know what youre talking about! Bookmarked. Please also visit my website =). We could have a link exchange agreement between us!
    coach factory outlet online authentic http://www.bedcapdealers.com/coach/

  18. #19 by purchase viagra on 30 de abril de 2017 - 6:08 pm

    Jeg vil nu sige tak til dig fordi du gider sætte dette i gang, det har været en sjov udfordring og en anderledes måde at komme rundt om de forskellige blogge, som jeg ikke var kommet rundt til hvis jeg ikke havde været med her.Jeg glæder mig til næste runde.

  19. #20 by buy viagra online on 30 de abril de 2017 - 6:10 pm

    Deadly accurate answer. You’ve hit the bullseye!

  20. #21 by swing set installation on 30 de abril de 2017 - 6:11 pm

    modified by way of flipping armrests. With these ensembles, you could transform a few

  21. #22 by Callaway Golf outlet online on 30 de abril de 2017 - 6:53 pm

    I have been reading out some of your articles and i can state pretty clever stuff. I will make sure to bookmark your blog.
    Callaway Golf outlet online http://www.callawaygolfoutlet.online

  22. #23 by http://news.arpelsreplica.xyz/arpelsreplicaxyz/203.asp on 30 de abril de 2017 - 7:05 pm

    Your item was with that great amount I not attention the actual excellent is and so exceptional. Its beautiful. That mom will adore this on top of Holiday morning when she opens gifts and it appearance like I devoted more, then again prices had been really great!!

  23. #24 by cavalli outlet on 30 de abril de 2017 - 8:04 pm

    Odd , this page shows up with a black color to it, what color is the primary color on your webpage?
    cavalli outlet http://www.robertocavallisale.online

  24. #25 by Tantric Satin on 30 de abril de 2017 - 8:24 pm

    Wonderful story, reckoned we could combine a few unrelated data, nevertheless actually worth taking a search, whoa did a single discover about Mid East has got additional problerms also

  25. #26 by coping with std on 30 de abril de 2017 - 8:37 pm

    that would be the finish of this post. Right here youll obtain some web-sites that we feel you will appreciate, just click the hyperlinks over

  26. #27 by Tabung Pemadam kebakaran on 30 de abril de 2017 - 8:45 pm

    The next time I read a blog, I hope that it doesnt disappoint me as much as this one. I mean, I know it was my choice to read, but I actually thought youd have something interesting to say. All I hear is a bunch of whining about something that you could fix if you werent too busy looking for attention.

  27. #28 by lk bennett pumps sale on 30 de abril de 2017 - 8:50 pm

    Excellent brief and this article helped me alot. Say thank you I looking for your information.
    lk bennett pumps sale http://www.lkbennettstore.online

  28. #29 by http://www.dairyindia.in/contrcol/3.html on 30 de abril de 2017 - 9:50 pm

    This headset was as beautiful like inside image. That it came promptly. I’d encourage getting a towel over every one level while you hit things away w / a vapor iron. It does not steam off along with only a steamer. Some sort of iron was appropriate. It is very fine, when you never trust yourself with all the towel and also steam iron, well choose that to a professional. Breathtaking seem.

  29. #30 by http://livsenkelt.se/media/stnap/351.html on 30 de abril de 2017 - 9:51 pm

    That headset ended up being as breathtaking because within the picture. This came immediately. I would advise choosing a towel more than every coating while you press things away w / one vapor iron. It does not steam outside and merely a steamer. The actual iron had been needed. It can be sensitive, so if you you should not trust yourself aided by the towel then steam iron, subsequently take things up to a expert. Striking sound.

  30. #31 by arcteryx outlet wisconsin on 30 de abril de 2017 - 10:36 pm

    Are all of those articles written you or did you hire a ghost writer?
    arcteryx outlet wisconsin http://www.arcteryxsale.online

  31. #32 by hire a superhero on 30 de abril de 2017 - 10:54 pm

    I would like to thank you for the efforts you’ve put in writing this web site. I’m hoping the same high-grade website post from you in the upcoming as well. Actually your creative writing skills has encouraged me to get my own website now. Actually the blogging is spreading its wings fast. Your write up is a good example of it.

1 381 382 383
(não será publicado)