[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 gambling site on 19 de agosto de 2017 - 2:11 pm

    We’re a group of volunteers and opening a new scheme in our community. Your web site provided us with valuable information to work on. You have done an impressive job and our whole community will be grateful to you.

  2. #2 by Car on 19 de agosto de 2017 - 2:21 pm

    I haven¡¦t checked in here for some time since I thought it was getting boring, but the last several posts are great quality so I guess I will add you back to my everyday bloglist. You deserve it my friend :)

  3. #3 by http://santaland.dk on 19 de agosto de 2017 - 2:41 pm

    You are my inspiration , I possess few blogs and sometimes run out from to brand.

  4. #4 by cheap nike roshe run on 19 de agosto de 2017 - 2:49 pm

    I had gotten the headphonesof the mom for mothers time, then she completely enjoyed things! Their completely cute headphonesas well as the saying in the card which comes into the container is very sentimental!! As well as the high quality of the beads is actually awesome!

  5. #5 by Sports Venue Small business on 19 de agosto de 2017 - 3:11 pm

    Definitely, what a great site and enlightening posts, I surely will bookmark your website.All the Best!

  6. #6 by dobphp on 19 de agosto de 2017 - 3:22 pm

     先日、彼女は新しい飴を買ってきてくれた↓ハチミツのやつ。 周りの大人もウンソンに癒されます感想どれを見ようか迷いましたが、こちらを選びました。 [url=http://exploramondo.altervista.org/grado/dvdbox_1/index.html]主君 太陽[/url]
    彼女は苦しんでいるのだけれど傍からすればとても迷惑な存在。 「SAMETIMES」  ジア   ドラマの中ではオンチなソンジュンを演じたパク・ソジュン   本当は歌、上手いんです。
    [url=http://monsterwarlord.altervista.org/warl/dvdbox_1/index.html]ピノキオ キャスト[/url] 最近、むしょーに、  「ぼくらの勇気~未満都市」が見たくなり、   レンタル店を探し歩いたわけですが。 イ・ビョンホンさんはすごくカリスマ性のある人でした。
    [url=http://www.gefsc.com/haizei/dvdbox_1/index.html]one piece dvdジャケット[/url]
    著者はライターで女性を支えるNPO法人も設立した人でテレビでもたまに見かけます。 MBC週末特別企画「結婚契約」は人生の価値がお金だけの男と、人生の崖っぷちに立った女が劇的に出会い、真の愛の意味を探していく過程を明るく軽快ながらも切なく描く恋愛ドラマだ。 [url=http://m-tanpopo.com/doctor/dvdbox_1/index.html]外科医·大門未知子 DVD[/url]
    ファンサービスって言葉を、このコンサートで初めて知ったくらいです。 ここではお酒を飲みながら告白をしているんですけど、告白後に色々な展開があってイイ感じになっていきます。
    [url=http://kamhootroksoong.com/wp-includes/facil/dvdbox_1/index.html]イソジン 結婚 契約[/url] この日の放送では、すべての準備を終えてついに客を迎えた『ユン食堂』が、オープンから一日でグローバルな客があふれる様子が描かれる。 」「と、言う事は娘を嫁がせたつもりで様子を見ながら機会を狙っていたのかもしれない。
    [url=http://blijmoed.be/paoke/dvdbox_1/index.html]逃げるは恥だが役に立つ 新垣結衣ドレス[/url]

  7. #7 by http://www.ljusihus.se/cartierlove.aspx on 19 de agosto de 2017 - 4:43 pm

    I have it headphonesof the mother to mothers evening, furthermore she completely adored that it! Their truly adorable headphonesas well as the suggesting in the card it goes in the box is really emotional!! And good of beads is ideal!

  8. #8 by http://sejlergiganten.dk on 19 de agosto de 2017 - 4:48 pm

    Howdy! This is my 1st comment here so I just wanted to give a quick shout out and say I really enjoy reading your posts. Can you suggest any other blogs/websites/forums that deal with the same subjects? Many thanks!

  9. #9 by Dominic Netkowicz on 19 de agosto de 2017 - 5:01 pm

    when u laugh mid stroke… i swear idk why i laugh harder while watching porn than jew jokes am i fucked up? http://www.freehdpornx.com/category/threesome/

  10. #10 by diese website on 19 de agosto de 2017 - 5:18 pm

    Angesichts dieser verliebten Hartnäckigkeit des Ritters entblößte die schöne Genueserin zum Beweis, daß der Schein trügt, ihre Brust, die vom Krebs zerfressen war.

  11. #11 by watch tv on 19 de agosto de 2017 - 5:22 pm

    I like reading and I think this website got some really useful stuff on it! .

  12. #12 by ECCO Online Shop on 19 de agosto de 2017 - 5:42 pm

    Hello have you visited ssk sorgulama? Laters!
    ECCO Online Shop

  13. #13 by cheap chanel bags on 19 de agosto de 2017 - 5:42 pm

    This website is awesome. I constantly come across something new & different right here and my seotons. Thank you for that data.
    cheap chanel bags

  14. #14 by patagonia jackets sale on 19 de agosto de 2017 - 5:43 pm

    Emergency foodstuff are the most up-to-date embellishments which may have grocery stores putting up their own varieties on their shelves. Theyre what individuals usually are calling ‘food upon demand’. By simply including water on the packet, foods fanatics can be able to sit a while and enjoy a new wholesome meal.
    patagonia jackets sale

  15. #15 by cheap adidas shoes on 19 de agosto de 2017 - 5:43 pm

    That is how to stop worrying and be happy about your.
    cheap adidas shoes

  16. #16 by Fatimah Ona on 19 de agosto de 2017 - 5:47 pm

    Great video! Loved the ending so much, that face is priceless.http://www.freehdpornx.com/category/anal/

  17. #17 by watch tv on 19 de agosto de 2017 - 5:58 pm

    Great wordpress blog here.. It’s hard to find quality writing like yours these days. I really appreciate people like you! take care

  18. #18 by the north face online outlet on 19 de agosto de 2017 - 6:05 pm

    I really enjoy this theme youve got going on on your website. What is the name of the template by the way? I was thinking of using this style for the site I am going to put together for my class project.
    the north face online outlet

  19. #19 by barbour online store usa on 19 de agosto de 2017 - 6:05 pm

    I have been examinating out a few of your stories and its nice stuff. I will definitely bookmark your blog.
    barbour online store usa

  20. #20 by callaway for sale on 19 de agosto de 2017 - 6:06 pm

    Can I copy some of this post, if a put a link back to your blog?
    callaway for sale

  21. #21 by louis vuitton bags outlet on 19 de agosto de 2017 - 6:06 pm

    Odd , this page shows up with a black color to it, what color is the primary color on your webpage?
    louis vuitton bags outlet

  22. #22 by toms price outlet on 19 de agosto de 2017 - 6:06 pm

    I was questioning in case you can be interested by changing into a guest poster on my weblog? and in exchange you could possibly put a hyperlink the post? Please let me know once you get an opportunity and Ill send you my contact details – thanks. Anyway, in my language, there are not much good source like this.
    toms price outlet

  23. #23 by where can i buy a barbour jacket on 19 de agosto de 2017 - 6:07 pm

    kinda enjoyed the post you wrote . it just isnt that easy to discover even remotely good posts toactually read (you know.. READ! and not simply browsing through it like some uniterested and flesh eating zombie before going to yet another post to just ignore), so cheers man for not wasting my time! 😉
    where can i buy a barbour jacket

  24. #24 by free car insurance quotes East Stroudsburg PA on 19 de agosto de 2017 - 8:13 pm

    A few things that were left out: use the drops not the tops. It lowers your centre of gravity and gives you a stronger braking position. A lot of descents involve sweeping turns so use your cornering skills (inside foot up and outside foot down with lots of weight). Finally, on a fast straight downhill hang your bum back off the seat with your thighs squeezing the seat. It is a mtb technique and will prepare you for unexpected bumpsTanya BoschLevel 2 coachSydney CC80kph

  25. #25 by hermes bracelet replica on 19 de agosto de 2017 - 8:45 pm

    I have this particular headphonesfor our mother concerning parents evening, and/or she completely liked things! Their actually sweet headphonesand the saying in the card that will come within the package is really sentimental!! As well as the excellence of the beads was ideal!

  26. #26 by http://www.communitycapitalny.org/wp-shoes.php on 19 de agosto de 2017 - 8:45 pm

    I had gotten our headphonesconcerning my personal mother towards mothers time, plus she definitely liked that! Their truly attractive headphonesas well as the stating in the card that goes in container is very sentimental!! As well as the top quality of the beads is great!

1 507 508 509
(não será publicado)