Google
 

Sunday, April 15, 2007

Engine Design - Shader, Textured Quad and Camera Objects

RCShader & RCShaderManager
I will start with the Shader classes. The ShaderManager is as the tutorial has it but has the Hashtable replaced with a Dictionary.

Basically I have redefined the myShaders container and added a variable to manage the index like this:

private static Dictionary<string, RCShader> myShaders = new Dictionary<string,RCShader>();
private static NameValueCollection keys = new NameValueCollection();



Altered the AddShader method like this:

public static void AddShader(RCShader newShader,string shaderLabel)
{
    myShaders.Add(shaderLabel, newShader);
    keys.Add((myShaders.Count - 1).ToString(), shaderLabel);
}



And replaced the foreach loop with a for loop in the LoadGraphicsContent method lie this:

public static void LoadGraphicsContent(GraphicsDevice myDevice,ContentManager myLoader)
{
    for (int sh = 0; sh < myShaders.Count; sh++)
        myShaders[keys[sh.ToString()]].LoadGraphicsContent(
                    myDevice, myLoader);
}



The Shader class it's self however has been reworked to use Semantics. The class has stayed essentially the same, however the SetParameters method has been totally redone.


public void SetParameters(RCObject myObject)
{
    Matrix World = Matrix.Identity;
    Matrix View = Matrix.Identity;
    Matrix Projection = Matrix.Identity;
    Matrix WVP = Matrix.Identity;
    Matrix WorldView = Matrix.Identity;
    Matrix ViewProjection = Matrix.Identity;

    if (!myObject.AlwaysFacingCamera)
    {
        if (myObject.UseLeftHandedWorldCalc)
        {
            World = Matrix.CreateScale(myObject.Scaling) *
                Matrix.CreateTranslation(myObject.Position) *
                Matrix.CreateFromQuaternion(myObject.Rotation);
        }
        else
        {
            World = Matrix.CreateScale(myObject.Scaling) *
                Matrix.CreateFromQuaternion(myObject.Rotation) *
                Matrix.CreateTranslation(myObject.Position);
        }
    }
    else
    {
        World = Matrix.CreateScale(myObject.Scaling) *
            Matrix.CreateFromQuaternion(RCCameraManager.ActiveCamera.Rotation * -1) *
            Matrix.CreateTranslation(myObject.Position);
    }

    if (RCHelpers.RCHelper.UseRefelctionViewMatrix)
        View = RCHelpers.RCHelper.reflectionViewMatrix;
    else
        View = RCCameraManager.ActiveCamera.View;

    Projection = RCCameraManager.ActiveCamera.Projection;

    ViewProjection = View * Projection;
    WorldView = World * View;
    WVP = World * View * Projection;

    for (int parm = 0; parm < myEffect.Parameters.Count; parm++)
    {
        string paramSemantic = "";
        paramSemantic = myEffect.Parameters[parm].Semantic;
        if (paramSemantic != null)
        {
            switch (paramSemantic.ToLower())
            {
                case "worldviewprojection":
                    myEffect.Parameters[parm].SetValue(WVP);
                    break;
                case "world":
                    myEffect.Parameters[parm].SetValue(World);
                    break;
                case "view":
                    myEffect.Parameters[parm].SetValue(View);
                    break;
                case "projection":
                    myEffect.Parameters[parm].SetValue(Projection);
                    break;
                case "cameraposition":
                    myEffect.Parameters[parm].SetValue(RCCameraManager.ActiveCamera.Position);
                    break;
                case "worldinversetranspose":
                    myEffect.Parameters[parm].SetValue(Matrix.Transpose(Matrix.Invert(World)));
                    break;
                case "worldinverse":
                    myEffect.Parameters[parm].SetValue(Matrix.Invert(World));
                break;
                case "worldview":
                    myEffect.Parameters[parm].SetValue(WorldView);
                    break;
                case "viewprojection":
                    myEffect.Parameters[parm].SetValue(ViewProjection);
                    break;
                case "viewinverse":
                    myEffect.Parameters[parm].SetValue(Matrix.Invert(View));
                    break;
            }
        }
    }
}


You will see above three ways of creating the World matrix, one is the one given in the tutorial, Scale * Rotation * Position, another is used when the UseLeftHandedWorldCalc property is set to true in myObject. I put this in as I was having some issues with a few shaders I found. The issue was that the models where not rotating but revolving when I called the rotate method on them, I thought this was due to the shaders having been written for a left handed system (XNA is right), being new to all this 3D stuff I could not alter the shader to behave properly so altered the world calculation for them so it would and the Scale * Position * Rotation method seemed to do the trick. If I ever get a better solution to this or find why it behaves like it does for the shaders I will post the fix here. The third method is only ever really used for my billboard class to keep the Textured Quad facing the camera.

RCTexturedQuad & RC2SidedTexturdeQuad
With the base Textured Quad code I have just added the ability to allow for alpha checking (image transparency). I have done this by adding a field and associated property called alphacheck. If set to true the Render method of the TQ sets the render state to use the alpha test.

This code is called before the Vertex declaration:

bool alphaTest = myDevice.RenderState.AlphaTestEnable;
bool alphaBlend = myDevice.RenderState.AlphaBlendEnable;
CompareFunction alphaFunc = myDevice.RenderState.AlphaFunction;

if (alphaCheck)
{
    if (myDevice.RenderState.AlphaTestEnable != true)
        myDevice.RenderState.AlphaTestEnable = true;
    if (myDevice.RenderState.AlphaBlendEnable != true)
        myDevice.RenderState.AlphaBlendEnable = true;
    if (myDevice.RenderState.AlphaFunction != CompareFunction.NotEqual)
        myDevice.RenderState.AlphaFunction = CompareFunction.NotEqual;
}



and this after to put the states back as they were if needed:

if (alphaCheck)
{
    if (myDevice.RenderState.AlphaTestEnable != alphaTest)
        myDevice.RenderState.AlphaTestEnable = alphaTest;
    if (myDevice.RenderState.AlphaBlendEnable != alphaBlend)
        myDevice.RenderState.AlphaBlendEnable = alphaBlend;
    if (myDevice.RenderState.AlphaFunction != alphaFunc)
        myDevice.RenderState.AlphaFunction = alphaFunc;
}



The bounding box for the Textured Quad is managed like this in the Render method:

myBounds = new BoundingBox(myPosition - (myScaling / 2), myScaling / 2);



I have also added a double sided textured quad, this is basically 2 textured quads back to back, displaying the same image.

RCCamera & RCCameraManager
Again there is very little deviation here, I have added an enumerator to manage the different camera types I want to use and added a field with an associated property of this type to the RCCamera class.


public enum CameraViewType
{
    Floating,
    ThirdPerson,
    POV
}



This tells me what kind of movement the camera should have and is managed in the calling assembly.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.