One area that you really have to plan for is the material system and material management (Fuzztrek touched on this a bit). Basically, a "material" can be defined as containing the following properties:
- Vertex and Pixel Shader
- Shader Constants
- Texture Sets
Each Material can have multiple texture sets, because you may want to use the same material, just with different textures.
If you really want to get in-depth, you could also add physics properties and sounds to your material. This way, the material automatically contains all physical properties (not just the graphical ones). It would then be easier to have objects demonstrate behavior, based on their composition (ie the object's strength, it's sound when walked on, ect...)
In one of my projects, I use a very modular, pluggable material system. Each material is contained in it's own separate DLL. All of the requested materials have their DLL's dynamically linked at runtime. My IMaterial interface is what all materials inherit from. It is kinda laid out like this:
public class IMaterial
{
public:
virtual HRESULT EnterMaterial() = 0;
virtual HRESULT ExitMaterial() = 0;
virtual HRESULT SetupForEntity( IEntity* entityIn ) = 0;
};
EnterShader() is called when the Material is selected to be used for multiple batches of rendering. It handles configuring the IDirect3DDevice9 to use the selected shader. On the other hand, ExitShader() is called after all batches have been completed, and handles any cleanup.
SetupForEntity() configures the shader for the current entity. This is where all of the custom shader constant are set. IMaterial casts the entity passed in to an acceptable class type and gets all of it's independent information from there.
For example, if we were using our Water material, SetupForEntity() would cast the entity to a CWater object, then extract all of it's properties (ie wave height, color, ect...).
Rendering does not occur inside of the Material itself. I've always liked to keep the material properties separate from the actually geometry data. Some people prefer to have the Material system render all of the geometry, but I let the Renderer interface handle that instead.
I have found that this setup is quite accomodating, in that it provides a general foundation for all materials, but still allows for specific actions to be performed. I recommend you take a look at
this thread.