This project is read-only.

Model with custom shader

Nov 16, 2008 at 1:53 PM
Hello, Status Unknown!
Thank you for awesome Xen libary!
So, i am from Russia and i using Epic English, so sorry for errs! :D
I have next problem: I can't bind my custom shader to my model.
I have separated mesh and texture, so i am loading it separated.
First of all, i am making ModelInstance and loading my file. ( Processor is Xen - Model )
After that i am load my texture.
[code]
            _model.ModelData = manager.Load<ModelData>(@"Tanks\Bradley\Bradley");
            Texture2D tex = manager.Load<Texture2D>(@"Tanks\Bradley\tank1");
[/code]
I am sure that i am making it correctly, so, after that i am init my shader ( i have taked shader from 09 tutorial )
_shader = new EpicTanks.Tanks.Shader.BradleyTechnique();
_shader.DisplayTexture = tex;

And, after that i am binding it:

        public void Draw(DrawState state)
        {
            state.PushWorldTranslate(ref _position);
            if (CullTest(state))
            {
                _shader.Bind(state);
                _model.Draw(state);
            }
            state.PopWorldMatrix();
        }

But, since model.Draw has it's own shader, my own is not working %-O
So, i can't even apply my own texture, but i found next trick:
            foreach (MeshData mesh in _model.ModelData.Meshes)
            {
                foreach (GeometryData geom in mesh.Geometry)
                {
                    MaterialShader material = geom.MaterialShader;
                    material.TextureMap = tex;
                }
            }
Texture is appliyed, but my custom shader still don't have influence on my model. What should i do? :)
Write my own draw method? Or use  _model.SetShaderOverride? But how? I can't understand how should i use inrehited ModelInstanceShaderProvider class.
Thank you! And sorry for my "epic" english again.







Nov 16, 2008 at 1:55 PM
Ah yes, i've also tried:
            foreach (MeshData mesh in _model.ModelData.Meshes)
            {
                foreach (GeometryData geom in mesh.Geometry)
                {
                    geom.MaterialShader = _shader;
                    material.TextureMap = tex;
                }
            }
But geom's shader is readonly :S
Nov 16, 2008 at 2:03 PM

Hi :-)

I'd love to help you right now, but I was just going to bed :-) (it's 3am here in New Zealand :-)

Short answer is you do have to implement the ModelInstanceShaderProvider class.

This class has a lot of methods you can choose to override (but don't have to).

When you implement the class, it will force you to implement the property 'ProviderModifiesWorldMatrixInBeginDraw'. Return false here, since you will not be modifying the world matrix in the BeginDraw method. :-)

Then, override the 'BeginGeometryShaderOverride' method, which is called before each peice of Geometry is drawn.

if BeginGeometryShaderOverride returns true, then the ModelInstance will assume you have bound a custom shader. If you return false, then it will use the geometry MaterialShader.

I hope that helps, I can understand it's not that simple, but I wanted to keep it as open as I could.

I'll won't be able to reply until the morning, sorry! :-)

Nov 16, 2008 at 2:24 PM
Yeah, Easy to Do!
All working now! Yahoooo! :)
In Sovie...Thank you, Comrade!
Nov 16, 2008 at 10:00 PM

That is great to hear :-)

I just noticed something:

Even though you are no longer using this code,

  if (CullTest(state))
{
_shader.Bind(state);
_model.Draw(state);
}

should be:

  if (_model.CullTest(state))
{
_shader.Bind(state);
_model.Draw(state);
}

And your own CullTest method should probably just be returning true.

I realised that in the tutorials I never properly explained how CullTest was intended to work, My reply here may make things easier to understand.

Nov 17, 2008 at 3:24 AM
Edited Nov 17, 2008 at 3:28 AM
Thank you!
What about Normal Mapping via MaterialShader?
What i shall do is: just bind that material and load NormalMap. ( optional is NormalMapSampler )
Is it all? I just can't see normal map on my fail plane :D ( that's why i am asking )
Nov 17, 2008 at 4:17 AM
Edited Nov 17, 2008 at 4:50 AM

If you set the NormalMap for MaterialShader to a valid texture, it will use normal mapping. If MaterialShader.NormalMap is null, then it won't use normal mapping. (Pretty simple I think :-)

This normal map uses the same texture coordinates as the TextureMap. 

By default, per-pixel specular highlights are disabled when using MaterialShaders, this is because diffuse + specular is more than 3x more complex than just diffuse lighting (for a number of reasons). So you may need to set 'MaterialShader.UsePerPixelSpecular' to true to see the effect more clearly. (The model importer will set 'UsePerPixelSpecular' to true if the material has a specular colour and a normal map). Using specular limits you to just 2 per-pixel lights, while you can use 4 with just diffuse.

(I almost forgot) Make sure your lights are actually per-pixel! When you add a light in the MaterialLightCollection, the first arguement is a true/false to say if they are per-pixel. You can't use normal mapping in the vertex shader ;-)

Finally, the model must have Tangents and Binormals. The model importer will not generate these by default (unless it detects a normal map). So you will need to set the 'Xen - Model' content processor property 'generate tangent frames' to true.

(However, you should be getting a runtime exception if you are trying to use normal mapping without tangents...)

Let me know if this helps.

Nov 17, 2008 at 9:34 AM
All working now! Thank you, very much!
Your answers VERY useful! :D
PS. Do you plan to change license of Xen? Or it will be always free?
Nov 17, 2008 at 9:45 AM
Edited Nov 17, 2008 at 9:52 AM

It will always be free.

I may accept donations in the future, but right now things will stay as they are.

I have a comfortable job as a game dev, so I'm not doing this to make me rich :-P

Nov 17, 2008 at 9:59 AM
Yeah, that's why you and Xen rox!
I will try to make a good game with it! :)
Thank you for answers on my dumb questions!