Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm trying to find a way of setting any kind of material class on any kind of primitive class (all the material classes have a base material class, same goes for the primitive classes).

What I mean by that, is, I'm looking for a way to do something like this:

Cube a = Cube( ColorMaterial( COLOR_RED ) );
Cube b = Cube( TexturedMaterial( ambienTex, diffuseTex, specularTex ) );

Ok, so that looks pretty easy, but I've got a few problems. First of all, inside my Cube class I've got a function in which I build the cube's geometry, as such:

VertexPositionColor vertices[36] =  { VertexPositionColor( pos, color ), ... }

Because of this, I can't just pass any type of material to my primitive class, because that would mean that the function that builds the geometry would need to be able to change the type of vertex it uses. I can't use templates either, because, even though it would solve the type issue, different vertex types are not constructed the same way.

So, is there any way of associating a material to a primitive?

share|improve this question
    
I removed your references/tags for D3D11 since there's nothing in your question (aside from the casual reference at the end) that pertains specifically to D3D11; in fact, it's a pretty graphics-API-agnostic problem. –  Josh Petrie Mar 26 '11 at 4:00

1 Answer 1

The first problem I see here is that you have a "cube" class. You really shouldn't -- within the domain of a rendering system, what about a cube is different from a sphere, in terms of type or interface? Since rendering interfaces should not be responsible for non-rendering task (such as collision, et cetera) then the answer is really that there's nothing different.

On the whole, the only difference between a cube and a sphere is the data (the actual vertex elements), which can be abstracted. Instead of Cube, you should have a Geometry class. This class would contains two important pieces of data:

  • A list (more accurately, an array or vector) of vertices.
  • Information describing the format of an individual vertex.

The vertex format can be as simple or complex as you need -- you can simply decide to pre-define a set of vertex formats your game will use ({position, color}; {position, color, normal, texture coordinates}; et cetera) and represent the format as enumerated type. At the other end of the spectrum you can implement a free-form VertexFormat object that allows specification of arbitrary vertex element semantics at arbitrary byte offsets within a vertex.

The actual vertex data should be represented as an unsigned char* and you can cast to an appropriate vertex format structure if needed, based on the vertex format enumeration. This allows you to store any shape/mesh in any vertex format without having to create subclasses -- having to create those subclasses is actually a design and maintainability problem.

Once you have this in place, it should be pretty straightforward to then add a Material* to the Geometry class. If you give the Material class a vertex format as well, you can even enforce correct bindings between materials and geometry: you can raise errors when you try to assign a material whose shader requires {position, color, normal} to a geometry object that only has {position, color} for example.

This also allows you to externalize data definitions and thus make your APIs more data-driven. You can now construct Geometry objects with arbitrary collections of vertices. In terms of that API you can then implement another interface (it should be separate from Geometry) that loads vertex lists from a data file format of your choice, or an interface that generates a list of vertices that form a cube, or sphere, or whatever other procedural primitive you'd like, with whatever vertex properties you desire.

EDIT: A clarification on some ways you can handle vertex formats: The Geometry object should accept and store only raw, untyped bytes of vertex data. You can work directly with these raw bytes, although I do understand the appeal of having a 'vertex structure' type that represents an individual vertex for ease of manipulation. In order to be useful that type must perforce be a POD (or you couldn't send arrays of them to the GPU anyhow), and so it is sane to be casting between the raw bytes and the vertex structure, provided the data in the bytes does actually match up to the structure's schema. You could thus provide an interface to the vertex data in the geometry object like:

template< typename VERTEX_STRUCTURE >
VERTEX_STRUCTURE* GetVertexData() {
  // Assume 'vertices' is an unsigned char* instance member.
  return reinterpret_cast<VERTEX_STRUCTURE*>(vertices);
}

You still need to know the appropriate format at compile time when you call this method, but that's usually not a problem because you are going to be directly manipulating the individual properties of the vertices when you call this (usually) so you'll know anyhow.

But if that's still a problem you can even manipulate the raw bytes directly according to the vertex format at runtime. Assuming you have a series of memory IO routines that allow you to write arbitrary primitive types (ints, floats, et cetera) into arbitrary byte buffers, and you have something that can query a vertex format object for information about whether or not that format contains normals or colors or whatever, you could write to an arbitrary format buffer like this:

unsigned char* buffer = /* get raw buffer from Geometry object */
VertexFormat format = /* get format description from Geometry object */

// WriteToBuffer() writes the provided value, advances the buffer pointer
// by an appropriate size and returns that updated pointer, allowing for us
// to do sequential writes. It's a relatively easy thing to implement.    
buffer = WriteToBuffer(buffer, x);
buffer = WriteToBuffer(buffer, y);
buffer = WriteToBuffer(buffer, z);

if(format.HasNormals()) {
  buffer = WriteToBuffer(buffer, normal.x);
  buffer = WriteToBuffer(buffer, normal.y);
  buffer = WriteToBuffer(buffer, normal.z);
}

if(format.HasColor()) {
  buffer = WriteToBuffer(buffer, color);
}

// Et cetera.

This latter technique would be useful in code that generated a vertex buffer for a sphere procedurally, for example: you could pass that code information about what types of vertex properties you wanted your generated sphere to include, and generate them at runtime.

share|improve this answer
    
So far I have a the equivalent of the Geometry class, on the other hand I'm not sure I understand how you suggest I should handle the vertex formats. Even by using interfaces to build the geometry from a file or analytically, I'm still faced with having to call the vertex ctor at some point. That's my main problem. In the case of a {pos, color} vertex, the function will do something like: VertexPosColor v[] = { VertexPosColor(), ... }. but now if I want to use a VertexTexture I need to change the vertex array to: VertexTexture v[] = { VertexTexture(), ... } I hope I'm making sense. –  dotminic Mar 26 '11 at 17:05
    
Sure. You don't need to call the vertex structure constructor -- and in fact you don't need a vertex structure at all. But you can still work directly with raw bytes (first allocate enough raw bytes, and then reinterpret_cast the raw byte pointer to a VertexPosColor* for example). –  Josh Petrie Mar 26 '11 at 19:49
    
I updated my answer with what I hope is some clarification. Note that there are other solutions; for example you could attempt to employ a factory for creating the vertex buffers, using the vertex format as a key. Just ensure that your vertex structures remain POD. –  Josh Petrie Mar 26 '11 at 20:05
    
Ok, I see what you mean. Nonetheless, I still need a classed/functions that will build meshes from files or in the shape of a sphere, cone... an so on. Thanks a bunch, it's pretty clear now anyway! –  dotminic Mar 28 '11 at 17:30
    
No problem; you can always ask another question about designs for handling procedural mesh construction (building spheres or cones based on parameters, etc). –  Josh Petrie Mar 28 '11 at 17:45

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.