🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

[.net] [XNA] HLSL / C# / Design Questions - Applying a material automatically.

Started by
0 comments, last by MindGames 16 years, 8 months ago
Let's say I have this level of objects. Let's say one of them is a shiny reflective ball, and another is a rough surfaced rock. When taken alone, each of these objects has different properties. The ball requires a cube map to reflect the surrounding, where as the rock requires a normal map to create its rough/bumpy surface. My question is how to generalize these objects to a point where I can automatically update parameters in a shader as needed. I have found two possible solutions, but if I am missing one, let me know. Solution 1: I use reflection to relate a shader parameter to a runtime object's property and/or field. Let's say the HLSL has a CubeMapTexture property, and I give my object a reference to a CubeMapEngine class with a CubeMap property. Solution 1 allows me to say "Get this.CubeMapEngine.CubeMap and set to Shader.CubeMapTexture" without knowing any of this at compile time. The problem with this is it is slow. Very slow. Solution 2: I have a limited number of properties that can be applied to a parameter. Specific parameters aren't known at compile time, but the values are known. Ie, Instead of using reflection to get access to this.CubeMapEngine.CubeMap, I simply just say (if parameter.type == Types.CubeMap then parameter.setvalue(CubeMapEngine.CubeMap). The problem with this is it limits the number of parameters that can be set and forces me to add more if statements or case statements as new possibilities arise. Solution 3: Leave the parameter setting to the user. This means for each object, they have to (possibly inherit basic stuff) set the shader's parameters manually in code. This just sucks and will not be used since everytime an artist changes a level, the coders would possibly have to change the code for each object, and at that point, levels might as well be hardcoded. The goal is to allow a user to come in and easily edit a world's object, a world's shader, and not worry about how the engine handles setting parameters. This is easy for stuff like setting matrices, light positions, etc, but not about random stuff they may need. For instance, what if they want to apply a random color to the object via a new parameter and they want to link it to a property in a static class that exists in some assemblies namespace. Solution 2 doesn't allow this, since it must know about the assembly and the possible parameter at compile time. Solution 1 does allow this, since it just looks up the property via reflection at runtime and uses it. So, how do I tackle this? Edit: I realize that this question is steeped in XNA/HLSL, but let me abstract it a bit to more of a general design question. Let's consider two classes: Foo and Bar. This demonstrates how solution one could work:

public class Foo{
     public List<string> Keys = new List<string>();
     public Bar BarInstance;

     public void PrintParameters(){
          foreach(string key in Keys){
               PropertyInfo propInfo;

               // grab propertyinfo reference...
               // store in propInfo

               Console.WriteLine(propInfo.GetValue(BarInstance).ToString());
          }
     }
}

public class Bar{
     public int High = 5;
     public int Low = 0;
}

public class Program{
     public static void Main(){
          Foo foo = new Foo();
          foo.Keys.Add("High");
          foo.BarInstance = new Bar();

          foo.Print();
     }
}
You can see that I can make the link between the key "High" and the instance of "Bar" even more abstract, relying on reflection to gain access to the BarInstance field of the Foo class at runtime. For instance, I could keep a list of objects and iterate through them until I find one that matches a specific type. Solution two does not allow this, as seen below.

public class Key{
     public enum Type { High, Medium, Low }

     public Type KeyType = Type.High;
}

public class Foo{
     public List<Key> Keys = new List<Key>();
     public Bar BarInstance;

     public void Print(){
          foreach(Key key in Keys){
               if(key.KeyType == Key.Type.High)
                    Console.WriteLine(BarInstance.High);
               else if ( ... )
                    ...
          }
     }
}

public class Bar{
     public int High = 5, Medium = 3, Low = 0;
}

public class Program{
     public static void Main(){
          Foo foo = new Foo();
          Foo.Keys.Add(new Key());
          foo.BarInstance = new Bar();
     }
}
You can see that using solution two, the code knows what properties to access via some enumeration that relates the key to a runtime value (in Bar). [Edited by - Krisc on October 29, 2007 6:56:32 PM]
Advertisement
Could you not do something of a combination of all three methods. Wrap the Effect in a class which sets the shader's properties directly. These Effects classes would implement an interface to provide a consistent interface for your level code. The Effects classes also have attributes designating them as being Effects classes, naming them with a string. Create an Effects manager (singleton for global access?). This uses reflection to load all your Effects classes and makes them accessible by name. If you add a new shader, the name of the shader need only be in a format such as MyEffectClassName_ShaderName or similar and a simple parser can work out which to use. Now this does not entirely divorce content from code but it does mean that you can dump all your Effects classes into their own dll so it can be separated from your main game/level code.

I hope I have explained that clearly enough, I am using a similar approach myself and it appears (so far!!!) to be relatively successful

This topic is closed to new replies.

Advertisement