Running on Empty

The few things I know, I like to share.

XNA Framework GameEngine Development. (Part 4, ShaderManager:GameComponent)

Introduction

Welcome to Part 4 of the XNA Framework GameEngine Development series.  In this article, I will introduce the shader interface, ShaderManager and the custom effect code generator.  This article is the first part of a sub series that will deal with shaders, textures and materials.  I know many readers are itching for eye candy by now, it is coming, but for now I am focusing on good base classes and design.

First of many interfaces.

Just a reminder this series of articles is not about using C#, design practices or architecture.  That being said, as an Engine developer we need to focus on keeping common interfaces and design principles.  This first interface will be used to group effects together into a common parent class.  We want to make certain every effect that is used in the engine have common features and are stored in a common way.

Since I use interfaces almost exclusively to accomplish this goal.  I added the folder Interfaces to the project and added a class called IRoeShader to the engine project.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace RoeEngine2.Interfaces
{
    public interface IRoeShader
    {  
        Effect BaseEffect
        {
            get;
        }

        bool ReadyToRender
        {
            get;
        }

        void Initialize(GraphicsDevice graphicsDevice);

        void Initialize(GraphicsDevice graphicsDevice, CompilerOptions compilerOptions);

        void Initialize(GraphicsDevice graphicsDevice, CompilerOptions compilerOptions, EffectPool effectPool);
    }
}

Simple enough, every shader needs to have an effect, indicate if it is ready to be rendered and be initialized.  So every class that inherits from IRoeShader will need to implement everything you see here.  This will guaruntee the engine can identify a shader effect from every other object in the engine, a nice thing in a huge and complex application.

Building an Effect Proxy Generator.

It might just be the WCF developer in me, but I simply love proxy classes.  There is just something wonderfully elegant about using code generators to create useful and powerful object classes.  That is why I loved this CodePlex project.  With a few simple adjustments we can adjust the code to include our IRoeShader interface.

Note:  Many developers with experience using XNA may want to use the generic Content Pipeline for their effects.  In a way we are using it here, just I would much rather have code that looks like effect.World = CameraManager.World, than effect.Parameter[“World”].SetValue(CameraManager.World).  To me it is much more natural to use Properties than Parameter setters. 

Create a new console project, I called mine ShaderEffectGenerator.

using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework.Graphics;
using System.Windows.Forms;
using System.Text;

namespace ShaderEffectCodeGenerator
{
    internal class Program
    {
        // Fields
        private static List<string> effectParameters = new List<string>();
        private static StreamWriter streamWriter;
        private static int tabLevel = 0;

        // Methods
        private static int LoadEffectFromFile(string fullPath, out Effect effect, out byte[] windowsByteCode, out byte[] xboxByteCode)
        {
            try
            {
                Form form = new Form();
                PresentationParameters parameters = new PresentationParameters();
                GraphicsDevice device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware, form.Handle, parameters);
                CompiledEffect effect2 = Effect.CompileEffectFromFile(fullPath, null, null, CompilerOptions.None, Microsoft.Xna.Framework.TargetPlatform.Windows);
                windowsByteCode = effect2.GetEffectCode();
                effect = new Effect(device, effect2.GetEffectCode(), CompilerOptions.None, null);
                xboxByteCode = Effect.CompileEffectFromFile(fullPath, null, null, CompilerOptions.None, Microsoft.Xna.Framework.TargetPlatform.Xbox360).GetEffectCode();
            }
            catch (Exception exception)
            {
                ReportError(exception.ToString());
                effect = null;
                windowsByteCode = null;
                xboxByteCode = null;
                return -1;
            }
            return 0;
        }

        private static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                WriteInstructions();
                ReportError("Please drag an .fx file into the app.");
            }
            else
            {
                Effect effect;
                byte&#91;&#93; buffer;
                byte&#91;&#93; buffer2;
                string path = args&#91;0&#93;;
                if (path.StartsWith("\"") && path.EndsWith("\""))
                {
                    path = path.Substring(0, path.Length - 1).Substring(1);
                }
                path = Path.GetFullPath(path);
                string str2 = Path.GetDirectoryName(path) + "/" + Path.GetFileNameWithoutExtension(path) + "Effect.cs";
                if (LoadEffectFromFile(path, out effect, out buffer, out buffer2) >= 0)
                {
                    if (File.Exists(str2))
                    {
                        File.Delete(str2);
                    }
                    string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
                    FileStream stream = new FileStream(str2, FileMode.Create);
                    using (StreamWriter writer = new StreamWriter(stream))
                    {
                        streamWriter = writer;
                        WriteUsingStatements(writer);
                        WriteLine("namespace RoeEngine2.Shaders");
                        WriteLine("{");
                        tabLevel++;
                        WriteLine(string.Format("public class {0}Effect : IRoeShader", fileNameWithoutExtension));
                        WriteLine("{");
                        tabLevel++;
                        WriteTechniqueEnumeration(effect);
                        streamWriter.WriteLine();
                        WriteRoeSpecificProperties(writer);
                        WriteLine("#region Effect Parameters");
                        writer.WriteLine();
                        foreach (EffectParameter parameter in effect.Parameters)
                        {
                            WriteEffectParameter(parameter);
                            writer.WriteLine();
                        }
                        WriteLine("#endregion");
                        writer.WriteLine();
                        WriteLine("#region Effect Techniques");
                        writer.WriteLine();
                        foreach (EffectTechnique technique in effect.Techniques)
                        {
                            WriteLine(string.Format("private EffectTechnique _{0}Technique;", technique.Name));
                            writer.WriteLine();
                        }
                        WriteLine("#endregion");
                        writer.WriteLine();
                        WriteLine("#region Initialize Methods");
                        writer.WriteLine();
                        WriteLine("///<summary>");
                        WriteLine("///Initializes the Effect from byte code for the given GraphicsDevice.");
                        WriteLine("///</summary");
                        WriteLine("///<param name=\"graphicsDevice\">The GraphicsDevice for which the effect is being created.</param>");
                        WriteLine("public void Initialize(GraphicsDevice graphicsDevice)");
                        WriteLine("{");
                        tabLevel++;
                        WriteLine("Initialize(graphicsDevice, CompilerOptions.None, null);");
                        tabLevel--;
                        WriteLine("}");
                        writer.WriteLine();
                        WriteLine("///<summary>");
                        WriteLine("///Initializes the Effect from byte code for the given GraphicsDevice and CompilerOptions.");
                        WriteLine("///</summary");
                        WriteLine("///<param name=\"graphicsDevice\">The GraphicsDevice for which the effect is being created.</param>");
                        WriteLine("///<param name=\"compilerOptions\">The CompilerOptions to use when creating the effect.</param>");
                        WriteLine("public void Initialize(GraphicsDevice graphicsDevice, CompilerOptions compilerOptions)");
                        WriteLine("{");
                        tabLevel++;
                        WriteLine("Initialize(graphicsDevice, compilerOptions, null);");
                        tabLevel--;
                        WriteLine("}");
                        writer.WriteLine();
                        WriteLine("///<summary>");
                        WriteLine("///Initializes the Effect from byte code for the given GraphicsDevice, CompilerOptions, and EffectPool.");
                        WriteLine("///</summary");
                        WriteLine("///<param name=\"graphicsDevice\">The GraphicsDevice for which the effect is being created.</param>");
                        WriteLine("///<param name=\"compilerOptions\">The CompilerOptions to use when creating the effect.</param>");
                        WriteLine("///<param name=\"effectPools\">The EffectPool to use with the effect.</param>");
                        WriteLine("public void Initialize(GraphicsDevice graphicsDevice, CompilerOptions compilerOptions, EffectPool effectPool)");
                        WriteLine("{");
                        tabLevel++;
                        WriteLine("_baseEffect = new Effect(graphicsDevice, byteCode, compilerOptions, effectPool);");
                        WriteLine("_readyToRender = true;");
                        writer.WriteLine();
                        foreach (string str4 in effectParameters)
                        {
                            WriteLine(string.Format("_{0}Param = _baseEffect.Parameters[\"{0}\"];", str4));
                        }
                        writer.WriteLine();
                        foreach (EffectTechnique technique2 in effect.Techniques)
                        {
                            WriteLine(string.Format("_{0}Technique = _baseEffect.Techniques[\"{0}\"];", technique2.Name));
                        }
                        tabLevel--;
                        WriteLine("}");
                        writer.WriteLine();
                        WriteLine("#endregion");
                        writer.WriteLine();
                        WriteLine("///<summary>");
                        WriteLine("///Sets the current technique for the effect.");
                        WriteLine("///</summary>");
                        WriteLine("///<param name=\"technique\">The technique to use for the current technique.</param>");
                        WriteLine(string.Format("public void SetCurrentTechnique({0}Effect.Techniques technique)", fileNameWithoutExtension));
                        WriteLine("{");
                        tabLevel++;
                        WriteLine("switch (technique)");
                        WriteLine("{");
                        tabLevel++;
                        foreach (EffectTechnique technique3 in effect.Techniques)
                        {
                            WriteLine(string.Format("case {0}Effect.Techniques.{1}:", fileNameWithoutExtension, technique3.Name));
                            tabLevel++;
                            WriteLine(string.Format("_baseEffect.CurrentTechnique = _{0}Technique;", technique3.Name));
                            WriteLine("break;");
                            writer.WriteLine();
                            tabLevel--;
                        }
                        tabLevel--;
                        WriteLine("}");
                        tabLevel--;
                        WriteLine("}");
                        writer.WriteLine();
                        WriteLine("#region Compiled Byte Code");
                        writer.WriteLine();
                        WriteLine("#if XBOX");
                        StringBuilder builder = new StringBuilder();
                        builder.Append("static readonly byte[] byteCode = { ");
                        foreach (byte num in buffer2)
                        {
                            builder.Append(num.ToString() + ",");
                        }
                        builder.Append(" };");
                        WriteLine(builder.ToString());
                        WriteLine("#else");
                        builder = new StringBuilder();
                        builder.Append("static readonly byte[] byteCode = { ");
                        foreach (byte num2 in buffer)
                        {
                            builder.Append(num2.ToString() + ",");
                        }
                        builder.Append(" };");
                        WriteLine(builder.ToString());
                        WriteLine("#endif");
                        writer.WriteLine();
                        WriteLine("#endregion");
                        tabLevel--;
                        WriteLine("}");
                        tabLevel--;
                        WriteLine("}");
                    }
                }
            }
        }

        private static void WriteUsingStatements(StreamWriter writer)
        {
            WriteLine("using System;");
            WriteLine("using Microsoft.Xna.Framework;");
            WriteLine("using Microsoft.Xna.Framework.Content;");
            WriteLine("using Microsoft.Xna.Framework.Graphics;");
            WriteLine("using RoeEngine2.Interfaces;");
            writer.WriteLine();
        }

        private static void WriteRoeSpecificProperties(StreamWriter writer)
        {
            WriteLine("private Effect _baseEffect;");
            WriteLine("///<summary>");
            WriteLine("///Gets the underlying Effect.");
            WriteLine("///</summary>");
            WriteLine("public Effect BaseEffect");
            WriteLine("{");
            tabLevel++;
            WriteLine("get { return _baseEffect; }");
            tabLevel--;
            WriteLine("}");
            writer.WriteLine();
            WriteLine("private bool _readyToRender = false;");
            WriteLine("///<summary>");
            WriteLine("///Is the shader ready to be rendered.");
            WriteLine("///</summary>");
            WriteLine("public bool ReadyToRender");
            WriteLine("{");
            tabLevel++;
            WriteLine("get { return _readyToRender; }");
            tabLevel--;
            WriteLine("}");
            writer.WriteLine();
        }       

        private static void WriteEffectParameter(EffectParameter param)
        {
            string item = param.Name;
            string str2 = String.Empty;
            string str3 = String.Empty;
            string str5 = String.Empty;
            switch (param.ParameterType)
            {
                case EffectParameterType.Bool:
                    str2 = "bool";
                    str3 = "Boolean";
                    break;
                case EffectParameterType.Int32:
                    str2 = "int";
                    str3 = "Int32";
                    break;
                case EffectParameterType.Single:
                    if ((param.RowCount > 1) && (param.ColumnCount > 1))
                    {
                        str2 = "Matrix";
                        str3 = "Matrix";
                    }
                    else if (param.ColumnCount > 1)
                    {
                        str2 = "Vector" + param.ColumnCount.ToString();
                        str3 = str2;
                    }
                    break;
                case EffectParameterType.String:
                    str2 = "string";
                    str3 = "String";
                    break;
                case EffectParameterType.Texture:
                    str2 = "Texture2D";
                    str3 = "Texture2D";
                    break;
                case EffectParameterType.Texture1D:
                    str2 = "Texture1D";
                    str3 = "Texture1D";
                    break;
                case EffectParameterType.Texture2D:
                    str3 = "Texture2D";
                    str3 = "Texture2D";
                    break;
                case EffectParameterType.Texture3D:
                    str2 = "Texture3D";
                    str3 = "Texture3D";
                    break;
                case EffectParameterType.TextureCube:
                    str2 = "TextureCube";
                    str3 = "TextureCube";
                    break;
                default:
                    return;
            }
            if (param.Elements.Count > 0)
            {
                str2 = str2 + "[]";
            }
            effectParameters.Add(item);
            string str4 = item.Substring(0, 1).ToUpper() + item.Substring(1);
            WriteLine(string.Format("private EffectParameter _{0}Param;", item));
            WriteLine(string.Format("public {0} {1}", str2, str4));
            WriteLine("{");
            tabLevel++;
            WriteLine("get");
            WriteLine("{");
            tabLevel++;
            WriteLine(string.Format("if (_{0}Param == null)", item));
            tabLevel++;
            WriteLine(string.Format("throw new Exception(\"Cannot get value of {0}; {0} EffectParameter is null.\");", str4));
            tabLevel--;
            if (param.Elements.Count > 0)
            {
                WriteLine(string.Format("return _{0}Param.GetValue{1}Array({2});", item, str3, param.Elements.Count));
            }
            else
            {
                WriteLine(string.Format("return _{0}Param.GetValue{1}();", item, str3));
            }
            tabLevel--;
            WriteLine("}");
            WriteLine("set");
            WriteLine("{");
            tabLevel++;
            WriteLine(string.Format("if (_{0}Param == null)", item));
            tabLevel++;
            WriteLine(string.Format("throw new Exception(\"Cannot set value of {0}; {0} EffectParameter is null.\");", str4));
            tabLevel--;
            WriteLine(string.Format("_{0}Param.SetValue(value);", item));
            tabLevel--;
            WriteLine("}");
            tabLevel--;
            WriteLine("}");
        }

        private static void WriteInstructions()
        {
            Console.WriteLine("The EffectCodeGenerator is a drop-in application. Simply drop in any .fx file to have");
            Console.WriteLine("the EffectCodeGenerator produce you a new class written in C# that enables easy use");
            Console.WriteLine("with your .fx file.");
        }

        private static void WriteLine(string line)
        {
            string str = string.Empty;
            for (int i = 0; i < tabLevel; i++)
            {
                str = str + "\t";
            }
            streamWriter.WriteLine(str + line);
        }

        private static void WriteTechniqueEnumeration(Effect effect)
        {
            WriteLine("public enum Techniques");
            WriteLine("{");
            tabLevel++;
            foreach (EffectTechnique technique in effect.Techniques)
            {
                WriteLine(string.Format("{0},", technique.Name));
            }
            tabLevel--;
            WriteLine("}");
        }
       
        private static void ReportError(string error)
        {
            Console.WriteLine("Error: {0}", error);
            Console.ReadKey(true);
        }
    }
}&#91;/sourcecode&#93;
 
<strong>ShaderManager class (Updated to use Dictionary)</strong> 

<strong></strong>

Note:  Thanks again to KaBaL, Please notice I changed the Manager namespace to Managers.  This is because there will be more than one. 

This class is our main container for everything that has to do with effects and shaders.  It includes some very simple principles that you may have seen from the Ramblings of a Hazy Mind series.  There are some minor adjustments, but I am not ashamed to say, the Ramblings series was a major contributer to this design principle.  Feel free to read the series, it is an excellent primer for many of the next articles in this series.

Add a new class to the Managers folder, I called mine ShaderManager.

Note:  Updated to use Dictionary thanks to leaf and KaBaL.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using System.Collections;
using RoeEngine2.Interfaces;
using Microsoft.Xna.Framework.Graphics;
using RoeEngine2.Shaders;

namespace RoeEngine2.Managers
{
    public class ShaderManager : GameComponent
    {
        private static Dictionary<string, IRoeShader> _shaders = new Dictionary<string, IRoeShader>();

        private static bool _initialized = false;
        /// <summary>
        /// Is the ShaderManagers Initialized, used for test cases and setup of Effects.
        /// </summary>
        public static bool Initialized
        {
            get { return _initialized; }
        }

        /// <summary>
        /// Create the shader Managers.
        /// </summary>
        /// <param name="game"></param>
        public ShaderManager(Game game)
            : base(game)
        {
        }

        /// <summary>
        /// Add a shader of type IRoeShader.
        /// </summary>
        /// <param name="newShader"></param>
        /// <param name="shaderLabel"></param>
        public static void AddShader(IRoeShader newShader, string shaderLabel)
        {
            if (shaderLabel != null && !_shaders.ContainsKey(shaderLabel))
            {
                _shaders.Add(shaderLabel, newShader);

                newShader.Initialize(EngineManager.Device);
            }
        }

        /// <summary>
        /// Get a shader of type IRoeShader.
        /// </summary>
        /// <param name="shaderLabel"></param>
        /// <returns></returns>
        public static IRoeShader GetShader(string shaderLabel)
        {
            if (shaderLabel != null && _shaders.ContainsKey(shaderLabel))
            {
                return _shaders[shaderLabel];
            }
            return null;
        }

        /// <summary>
        /// Create the shaders.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();

            foreach (IRoeShader shader in _shaders.Values)
            {
                if (!shader.ReadyToRender)
                {
                    shader.Initialize(EngineManager.Device);
                }
            }

            AddShader(new basicEffect(), "BasicEffect");

            _initialized = true;
        }
    }
}

There may be some readers that will ask why not use a generic list instead of a hashtable?  I am okay with either design, I chose a HashTable because I liked having instant access to each object rathre than getting an item out of a list by index.  If you are the kind of person that wants to explore deeper into engine design you might write test cases for each design.  It is very possible, a hashtable is sub optimal for the small number of shaders we will most likely be adding to the project. If you can prove it with numbers to support your findings, I will definitely redesign what you see here and give you credit for your work.

Using the ShaderManager GameComponent

Add the following code to the Properties section of RoeEngine2

private static ShaderManager _shaderManagers = null;

And in the RoeEngine2 Constructor

// Init shader Managers
_shaderManagers = new ShaderManager(this);
Components.Add(_shaderManagers);

//TODO include other inits here!

 
Encapsulating the BasicEffect

This might not be necessary, but since I would like to use the BasicEffect in some samples I will encapsulate it here.  This will most likely grow some in the future, it is here now for test cases.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using RoeEngine2.Interfaces;
using Microsoft.Xna.Framework;
namespace RoeEngine2.Shaders
{
    public class basicEffect : IRoeShader
    {
        private BasicEffect _baseEffect;
        ///<summary>
        ///Gets the underlying Effect.
        ///</summary>
        public Effect BaseEffect
        {
            get { return _baseEffect; }
        }
        private bool _readyToRender = false;
        ///<summary>
        ///Is the shader ready to be rendered.
        ///</summary>
        public bool ReadyToRender
        {
            get { return _readyToRender; }
        }
        ///<summary>
        ///Initializes the Effect from byte code for the given GraphicsDevice.
        ///</summary
        ///<param name="graphicsDevice">The GraphicsDevice for which the effect is being created.</param>
        public void Initialize(GraphicsDevice graphicsDevice)
        {
            Initialize(graphicsDevice, CompilerOptions.None, null);
        }
        ///<summary>
        ///Initializes the Effect from byte code for the given GraphicsDevice and CompilerOptions.
        ///</summary
        ///<param name="graphicsDevice">The GraphicsDevice for which the effect is being created.</param>
        ///<param name="compilerOptions">The CompilerOptions to use when creating the effect.</param>
        public void Initialize(GraphicsDevice graphicsDevice, CompilerOptions compilerOptions)
        {
            Initialize(graphicsDevice, compilerOptions, null);
        }
        ///<summary>
        ///Initializes the Effect from byte code for the given GraphicsDevice, CompilerOptions, and EffectPool.
        ///</summary
        ///<param name="graphicsDevice">The GraphicsDevice for which the effect is being created.</param>
        ///<param name="compilerOptions">The CompilerOptions to use when creating the effect.</param>
        ///<param name="effectPools">The EffectPool to use with the effect.</param>
        public void Initialize(GraphicsDevice graphicsDevice, CompilerOptions compilerOptions, EffectPool effectPool)
        {
            _baseEffect = new BasicEffect(graphicsDevice, effectPool);
            _readyToRender = true;
        }
    }
}

 
Optional TDD – Updated, no longer using InvalidOperationException

Add the following test cases to the RoeEngineTestCases class.

        [Test, Category("ShaderManager")]
        public void ShaderManagerCreated()
        {
            Assert.IsTrue(ShaderManager.Initialized);
            Assert.IsNotNull(ShaderManager.GetShader("BasicEffect"));
            Assert.AreEqual(typeof(basicEffect), ShaderManager.GetShader("BasicEffect").GetType());
        }

        [Test, Category("ShaderManager")]
        public void ShaderManagerAddGetFailed()
        {
            // It is always a good idea to test for Failures too!
            Assert.IsNull(ShaderManager.GetShader("testshader"));  
        }

Conclusion

In this article I introduced the IRoeShader, shaders, shader proxy generation and the ShaderManager.  I will be revisting this class from time to time, in a growing engine it is always necessary to Refactor your work.  Hopefully, you can use some of these design practices in other projects as well.

Your feedback is very important, please leave me a comments or suggestions.  I will respond as soon as possible.

Advertisements

January 14, 2008 - Posted by | C#, GameComponent, TDD, XBOX360, XNA

25 Comments »

  1. Challenge accepted!

    Comment by KaBaL | January 14, 2008 | Reply

  2. Awesome KaBaL, options here are HashTable, generic List, or Dictionary to name a few. Each will have slightly different performances.

    Comment by roecode | January 14, 2008 | Reply

  3. A update here: http://www.phase9studios.com/2008/01/08/DictionaryVSHashTable.aspx
    It would appear Dictionary is superior in every way, due to the fact a HashTable needs to box and unbox objects. I will most likely be updating the code to use a Dictionary.

    Comment by roecode | January 14, 2008 | Reply

  4. Grrr… You beat me too it, haha.

    Comment by KaBaL | January 14, 2008 | Reply

  5. Note, I would still prefer Dictionary over Hashtable because the former is strongly typed but in your particular case the performance difference would have been minimal. The Dictionary vs Hashtable article you link to is comparing storing Guids, which are value types (struct in C#). Boxing and unboxing only occurs with value types, not with reference types such as your shader classes.

    Also, a minor comment but you seem to be suggesting that using the content pipeline would mean looking up parameters by string. This is not the case, you could have generated effect classes that work with the content pipeline or use other ways to reference parameters such as using an effect parameter standard like DXSAS, or rolling your own.

    Comment by leaf | January 15, 2008 | Reply

  6. Thank you for your feedback leaf. Very good information to know about Hashtables and reference types.

    Yes, you are correct, I could have generated effect classes… which I do. I am using the content pipeline to do so. Just I do not compile the effects into xnb files using the pipeline. Rather I compile the effects into bytecode stored in the generated effect “proxy”.

    Sorry for any confusion, thank you for the Hashtable info.

    Comment by roecode | January 15, 2008 | Reply

  7. There is a missing condition I think in WriteEffectParameter function, the condition of (“Single”,”float”), if the effect file contains a float variable, it won’t be detected so this condition must be increased to detect it :
    case EffectParameterType.Single:
    if (…)
    {

    }
    else if (…)
    {

    }
    // Missing Condition
    else
    {
    str2 = “float”;
    str3 = “Single”;
    }
    break;

    Comment by Karim Kenawy | March 26, 2008 | Reply

  8. Yup, those conditions can be added, by no means is this code complete. These are just ideas to spark other peoples interests.

    Also, I am considering depreciating this code, I am working on a new materials processor that will take the place of this code.

    Comment by roecode | March 26, 2008 | Reply

  9. Hi, roecode, could you tell what is this new material ? and why not any longer using this one ? is a great site, I hope I can learn a lot from here.

    Comment by samairu | July 19, 2008 | Reply

  10. I’ve tried to follow all of your tutorials step by step and so far, nothing, i can’t make anything new cause you won’t explain exactly what file needs to be made, which folder it goes into, not to mention you even forgot the whole part where you MADE a managers folder, this “Tutorial” is not for beginners to XNA!

    Comment by Xsnip3rX | August 22, 2008 | Reply

  11. Wow, you certainly sound frustrated. Without a doubt this tutorial series is not for beginners to XNA, nor is it for beginners to development in general. Actually, it isn’t even for people interested in game development.

    I think I did make this clear in the first tutorial. Rather, my goal here is to outline basic, repeatable concepts for making a workable game engine. I do not want, nor do I expect every single line of code to be copied from this series to make a working engine.

    You can definitely use this as a helper for developing your own engine, but copying code is a bad way to make an engine. You might want to look for a prebuilt engine, there are plenty available.

    Later in this series I started posting links to code and working examples. Getting to this step is very good, but not a whole lot can be seen at this point in the engine.

    Comment by roecode | August 22, 2008 | Reply

  12. Sorry, i was a bit frustrated, i just got off of a hard day of work and i tried an hour or so of following 2 of your tutorials but no luck, is there any chance you can help me Via MSN?

    Comment by Xsnip3rX | August 22, 2008 | Reply

  13. The generator miss a condition that is the array of a parameters. i.e. float a[8] = {1.2f, 2.0f, …..};
    bool b[6] = {true, false, …..}

    Comment by Ken | August 28, 2008 | Reply

  14. baseEffect has a property where it returns itself, but as an Effect class, but its not of Effect type. What am I missing?

    Comment by Shane | August 29, 2008 | Reply

  15. Just wanted to be able to use the BaseEffect as an IRoeShader.

    Comment by roecode | August 29, 2008 | Reply

  16. I’m just wondering was is exactly is the use of this shader manager ? Does it allows us to compile hlsl in runtime ?

    I didn’t follows any of this tutorial but I think I would be able to learn fews tricks. Keep going

    cheers

    Comment by Jeremie | January 13, 2009 | Reply

    • The shader manager does not compile shaders. Rather it is a “container” that groups logical objects together… in this case shaders. The shader itself is in fact HLSL, but the content pipeline in XNA is used to actually load and compile the shader.

      This is actually the 4th installment in the series of many many articles on creating a game engine. The key concepts here are not loading and compiling shaders. Rather the focus here is to maintain a logical group of shaders that will be used by other “drawable” objects in the engine.

      Comment by roecode | January 13, 2009 | Reply

  17. Note:

    If you are getting FileNotFoundExceptions for the Console application and are running VS on a 64-bit OS, make sure you go to the build properties (right click on solution > properties > build) and change the target platform to x86.

    You should no longer have the issue.

    Comment by Ash | July 16, 2009 | Reply

  18. using RoeEngine2.Shaders;

    Getting back into this series after some time away. Missing
    RoeEngine2.Shaders namespace. Did i miss a step or has this namespace not been created yet? Thanks. Awsome stuff here!!

    Comment by Joe | March 3, 2010 | Reply

    • I must have been realy tierd last night because today i can see that yes the shaders namespace is created when we create the basic effect class….wow sorry for clogging the blog with this crap

      Comment by Joe | March 3, 2010 | Reply

  19. sorry im so late to the conversation…hopefully theres someone still out there.

    Comment by Joe | March 3, 2010 | Reply

  20. […] skeleton Part II: An FPS counter GameComponent Part III: Applying TDD (Test Driven Development) Part IV: ShaderManager GameComponent Part V: TextureManager GameComponent Part VI: Input GameComponent Part VII: ScreenManager […]

    Pingback by Getting up with XNA GS 2.0 [UPDATED] - Kartones Blog | July 8, 2010 | Reply

  21. Nice work.

    For the formatting, in fact it is possible you can do in C# like this (taking Main() as an example):

    private static void Main(string[] args)
    {
    if (args.Length = 0)
    {
    if (File.Exists(str2))
    {
    File.Delete(str2);
    }
    string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
    FileStream stream = new FileStream(str2, FileMode.Create);
    using (StreamWriter writer = new StreamWriter(stream))
    {
    streamWriter = writer;
    WriteUsingStatements(writer);
    Console.WriteLine(
    @”namespace RoeEngine2.Shaders
    {
    public class ” + fileNameWithoutExtension + @”Effect : IRoeShader
    {“);
    WriteTechniqueEnumeration(effect);
    streamWriter.WriteLine();
    WriteRoeSpecificProperties(writer);
    Console.WriteLine(
    @” #region Effect Parameters
    “);

    foreach (EffectParameter parameter in effect.Parameters)
    {
    WriteEffectParameter(parameter);
    writer.WriteLine();
    }
    Console.WriteLine(
    @” #endregion

    #region Effect Techniques
    “);

    foreach (EffectTechnique technique in effect.Techniques)
    {
    Console.WriteLine(
    @” private EffectTechnique _” + technique.Name + @”Technique;
    “);
    }

    Console.WriteLine(
    @” #endregion

    #region Initialize Methods

    ///
    ///Initializes the Effect from byte code for the given GraphicsDevice.
    ///</summary
    ///The GraphicsDevice for which the effect is being created.
    public void Initialize(GraphicsDevice graphicsDevice)
    {
    Initialize(graphicsDevice, CompilerOptions.None, null);
    }

    ///
    ///Initializes the Effect from byte code for the given GraphicsDevice and CompilerOptions.
    ///</summary
    ///The GraphicsDevice for which the effect is being created.
    ///The CompilerOptions to use when creating the effect.
    public void Initialize(GraphicsDevice graphicsDevice, CompilerOptions compilerOptions)
    {
    Initialize(graphicsDevice, compilerOptions, null);
    }

    ///
    ///Initializes the Effect from byte code for the given GraphicsDevice, CompilerOptions, and EffectPool.
    ///</summary
    ///The GraphicsDevice for which the effect is being created.
    ///The CompilerOptions to use when creating the effect.
    ///The EffectPool to use with the effect.
    public void Initialize(GraphicsDevice graphicsDevice, CompilerOptions compilerOptions, EffectPool effectPool)
    {
    _baseEffect = new Effect(graphicsDevice, byteCode, compilerOptions, effectPool);
    _readyToRender = true;
    “);

    foreach (string str4 in effectParameters)
    {
    Console.WriteLine(
    @” _” + str4 + @”Param = _baseEffect.Parameters[“”” + str4 + @”””];”);
    }
    writer.WriteLine();
    foreach (EffectTechnique technique2 in effect.Techniques)
    {
    Console.WriteLine(string.Format(“_{0}Technique = _baseEffect.Techniques[\”{0}\”];”, technique2.Name));
    }

    Console.WriteLine(
    @” }

    #endregion

    ///
    ///Sets the current technique for the effect.
    ///
    ///The technique to use for the current technique.
    public void SetCurrentTechnique(” + fileNameWithoutExtension + @”Effect.Techniques technique
    {
    switch (technique)
    {“);

    foreach (EffectTechnique technique3 in effect.Techniques)
    {
    Console.WriteLine(
    @” case ” + fileNameWithoutExtension + @”Effect.Techniques.” + technique3.Name + @”:
    _baseEffect.CurrentTechnique = _” + technique3.Name + @”Technique;
    break;
    “);
    }

    Console.WriteLine(
    @” }
    }

    #region Compiled Byte Code

    #if XBOX”);
    StringBuilder builder = new StringBuilder();
    builder.Append(“static readonly byte[] byteCode = { “);
    foreach (byte num in buffer2)
    {
    builder.Append(num.ToString() + “,”);
    }
    builder.Append(” };”);
    Console.WriteLine(
    @” ” + builder.ToString() + @”
    #else”);
    builder = new StringBuilder();
    builder.Append(“static readonly byte[] byteCode = { “);
    foreach (byte num2 in buffer)
    {
    builder.Append(num2.ToString() + “,”);
    }
    builder.Append(” };”);
    Console.WriteLine(
    @” ” + builder.ToString() + @”
    #endif

    #endregion
    }
    }”);
    }
    }
    }
    }

    Comment by Flying Bag | August 24, 2010 | Reply

  22. the formatting of the code is totally gone. >.<

    Actually what I meant is:

    Console.WriteLine("\t\tpublic void Main();");
    Console.WriteLine("\t\t{");
    Console.WriteLine("\t\t}");

    can be written with advantage of '@' symbol:

    ConsoleWriteLine(
    @"    public void Main()
        {
        }");

    Comment by Flying Bag | August 24, 2010 | Reply

  23. Sorry for the spam guys. =]

            private static void Main(string[] args)
            {
                if (args.Length = 0)
                    {
                        if (File.Exists(str2))
                        {
                            File.Delete(str2);
                        }
                        string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
                        FileStream stream = new FileStream(str2, FileMode.Create);
                        using (StreamWriter writer = new StreamWriter(stream))
                        {
                            streamWriter = writer;
                            WriteUsingStatements(writer);
                            Console.WriteLine(
    @”namespace RoeEngine2.Shaders
    {
        public class ” + fileNameWithoutExtension + @”Effect : IRoeShader
        {“);
                            WriteTechniqueEnumeration(effect);
                            streamWriter.WriteLine();
                            WriteRoeSpecificProperties(writer);
                            Console.WriteLine(
    @”        #region Effect Parameters
    “);

                            foreach (EffectParameter parameter in effect.Parameters)
                            {
                                WriteEffectParameter(parameter);
                                writer.WriteLine();
                            }
                            Console.WriteLine(
    @”        #endregion

            #region Effect Techniques
    “);

                            foreach (EffectTechnique technique in effect.Techniques)
                            {
                                Console.WriteLine(
    @”        private EffectTechnique _” + technique.Name + @”Technique;
    “);
                            }

                            Console.WriteLine(
    @”        #endregion

            #region Initialize Methods

            ///
            ///Initializes the Effect from byte code for the given GraphicsDevice.
            ///</summary
            ///The GraphicsDevice for which the effect is being created.
            public void Initialize(GraphicsDevice graphicsDevice)
            {
                Initialize(graphicsDevice, CompilerOptions.None, null);
            }

            ///
            ///Initializes the Effect from byte code for the given GraphicsDevice and CompilerOptions.
            ///</summary
            ///The GraphicsDevice for which the effect is being created.
            ///The CompilerOptions to use when creating the effect.
            public void Initialize(GraphicsDevice graphicsDevice, CompilerOptions compilerOptions)
            {
                Initialize(graphicsDevice, compilerOptions, null);
            }

            ///
            ///Initializes the Effect from byte code for the given GraphicsDevice, CompilerOptions, and EffectPool.
            ///</summary
            ///The GraphicsDevice for which the effect is being created.
            ///The CompilerOptions to use when creating the effect.
            ///The EffectPool to use with the effect.
            public void Initialize(GraphicsDevice graphicsDevice, CompilerOptions compilerOptions, EffectPool effectPool)
            {
                _baseEffect = new Effect(graphicsDevice, byteCode, compilerOptions, effectPool);
                _readyToRender = true;
    “);

                            foreach (string str4 in effectParameters)
                            {
                                Console.WriteLine(
    @”            _” + str4 + @”Param = _baseEffect.Parameters[“”” + str4 + @”””];”);
                            }
                            writer.WriteLine();
                            foreach (EffectTechnique technique2 in effect.Techniques)
                            {
                                Console.WriteLine(string.Format(“_{0}Technique = _baseEffect.Techniques[\”{0}\”];”, technique2.Name));
                            }

                            Console.WriteLine(
    @”        }

            #endregion

            ///
            ///Sets the current technique for the effect.
            ///
            ///The technique to use for the current technique.
            public void SetCurrentTechnique(” + fileNameWithoutExtension + @”Effect.Techniques technique
            {
                switch (technique)
                {“);

                            foreach (EffectTechnique technique3 in effect.Techniques)
                            {
                                Console.WriteLine(
    @”            case ” + fileNameWithoutExtension + @”Effect.Techniques.” + technique3.Name + @”:
                    _baseEffect.CurrentTechnique = _” + technique3.Name + @”Technique;
                    break;
    “);
                            }

                            Console.WriteLine(
    @”            }
            }

            #region Compiled Byte Code

            #if XBOX”);
                            StringBuilder builder = new StringBuilder();
                            builder.Append(“static readonly byte[] byteCode = { “);
                            foreach (byte num in buffer2)
                            {
                                builder.Append(num.ToString() + “,”);
                            }
                            builder.Append(” };”);
                            Console.WriteLine(
    @”        ” + builder.ToString() + @”
            #else”);
                            builder = new StringBuilder();
                            builder.Append(“static readonly byte[] byteCode = { “);
                            foreach (byte num2 in buffer)
                            {
                                builder.Append(num2.ToString() + “,”);
                            }
                            builder.Append(” };”);
                            Console.WriteLine(
    @”        ” + builder.ToString() + @”
            #endif

            #endregion
        }
    }”);
                        }
                    }
                }
            }

    Comment by Flying Bag | August 24, 2010 | Reply


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: