I am re-implementing my materials and shaders as what I had was an absolute mess. I want to improve it. Currently, there's a question of how to support shaders with different constant buffers (DirectX 11). I'd prefer to be able to reuse one class (or a template of it at least) in order to create flexible shaders. What I have:
class Material
{
...
public:
bool _opaque; //transparent or opaque queue - different sorting
VertexShader* _vertexShader; //most important sorting criteria
PixelShader* _pixelShader;
std::vector<Texture*> textures; //second most important sorting criteria
unsigned char texturesAdded = 0u; //determines how many textures are added by the shader itself
unsigned int stride = sizeof(Vert3D);
unsigned int offset = 0u;
D3D11_PRIMITIVE_TOPOLOGY primitiveTopology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
}
////////////////////////////////////////////////////////////////////////////////////another file ofc
class Shader
{
static UINT ID_COUNTER;
public:
UINT _id;
SHADER_TYPE type;
std::string path;
std::vector<ID3D11Buffer*> _cbuffers;
}
VertexShader and PixelShader inherit from this (which made sense to me as they required different pointer types to directX shader objects anyways, seemed like overengineering to try and abstract everything away).
Obviously, this is simple but it can become a mess... Materials might need to support shaders other than the vertex and fragment/pixel types, but that's fine. I was considering simply inheriting from base Model in case that's required. But what about all the different shaders of the same type that simply want some different constant buffers? It doesn't make sense to create a new class for each one (ooor does it?)
One option is to try and template the VertexShader class with structs/classes of it's cbuffers (example, VertexBuffer<Matrix4x4> for the typical world matrix buffer)- this, I think, would necessitate templating the materials too... which could become messy. Template with base class trick can get around this but... I don't want to dynamic cast and whatnot everywhere... This doesn't look good to me.
Can I make this completely data driven instead? Currently I have a list of buffers and they are initialized in the constructor using a list of D3D11BUFFERFER_DESC objects... but then... how can I make the update function know what to do? Store some kind of array of raw byte data the size of the buffers and just pass in with void*?
Obviously, I could make a different class for each and every shader but that seems like a ton of work. I'd have to pass it a structs (or a ptr/ref to it) of a different kind depending on the cbuffers... that's why I considered templates in the first place! Or am I just overthinking this and it's not actually hard at all. Pls hep!
I was considering calling this function from a renderable object (mesh, volumetric effect or whatever) even though, technically, shader x requires cbuffer y always so it kind of makes sense the update function belongs in the shader class, because the shader class might know about its cbuffers but doesn't have the DATA needed for it. I think this part of design is sound - shader shouldn't really know where the mesh is or whatever else...