Woah... back again, who would have thought :)
Refactoring is now almost complete. I finally have rendering once more. I did have a screenshot, but it's on my home PC and I'm currently at work on my lunch break, but it was boring as it just showed my main menu screen anyway, so no major loss.
But yes, that's right, I now have a UI that is rendering once more. The system worked beautifully when adapted to my bitmap font class and UI classes. I had to make some minor changes to the design once I tried it out in practise, but it all still goes together nice and logically, if not more so since my latest changes.
The main thing that I struggled with was the concept of Shader constants. What object should look after them, where should they be set, etc? The problem is that some constants require changing on a per renderobject type basis, some require changing on a per-renderobject instance basis, some require setting on a per-scene basis and some require setting on a global basis.
So how in the heck can you code something flexible enough to handle all of that without obfuscating game level code with SetShaderConstant function calls which seem out of place?
In the end, I went with some template code madness. The shader constants are now set by default in the render method and the system works by applying overrides. Overrides are only ever set from within a render object, but can be altered through more sensible functions that control them.
So now, I can create a scene:
Add some objects to the scene:
Then I can modify a scene shader constant by something like:
GameScene.SetLightDirection( D3DXVECTOR(1,0,0) );
Within the renderobject/scene is where the template madness comes in. In this case, it would be doing something like as follows:
lightDirection.value( lightDirection_ );
shaderConstants_.push_back( lightDirection.proxy() );
What happens here is that shaderConstants_ is a vector of shaderConstantProxy pointers. LightDirection is a derived specialized template that operates on D3DXVECTOR4's. The proxy function call returns a proxy object which deconstructs the supplied value into a series of D3DXVECTOR4's (so something like a matrix would automatically be broken down into 4 D3DXVECTOR4's, or a float would be put into a single D3DXVECTOR4). The proxy object allows us to store the templated shader constant type in a vector.
So this allows us to easily set any type of constant (vector, float, matrix, whatever). The code will not compile if we try and set the wrong type of data, e.g. setting a float value for the world matrix, because of the templating, so you really can't mess it up and supply incorrect values. Each derived specialized templated Shader constant (phew, what a a mouthful!) has to override a type function, which returns the type of constant.
Now this means that because we are able to obtain the type of constant at runtime, any given shader can request a constant of a specified type and set it in Direct3D at will. If the object has overridden the constant, the override will be applied. If it does not exist, the default will be set.
This finally means that we can apply any given shader to any given object without messing around with any constants directly. If a given object hasn't overriden a shader constant, the shader just uses a default value (and maybe reports this to a log, so we don't get confused!).
It all sounds complex, but from an end coders perspective, they simply change sensible object specific data (such as the light direction of a scene) and the object completely takes care of setting up all the shader constants for you for both vertex and pixel shaders regardless of what shader type you have applied, be it a 2D GUI shader or a 3D bumpmapping shader.
Now, I usually miss the blindingly obvious easy ways to do what I try and do, so maybe this could have been done in a much more simplistic manner, but assuming not, I think this system is quite nice :)