+ Reply to Thread
Results 1 to 8 of 8

Thread: The Mesh and Mesh Instance classes

  1. #1
    Join Date
    Mar 2005
    Location
    California
    Posts
    2,098

    Default The Mesh and Mesh Instance classes

    Okay, having finished one important subsystem for our renderer (the timer), I now will turn my attention to another one. Or three, actually. We will need classes for the vertex buffers and vertex buffer indeces, and as a corillary to these, we will need a vertex class as well. These three classes are what will provide the actual inputs to the renderer that tells it what to render (in terms of geometry).

    Now, I probably won't have a whole lot to say about these. They are internal classes used by the engine. The clients are not going to be dealing with these at all, and there won't be any documented functions for them.

    But, as you guys know, I like to document what I'm working on (and in fact when it comes to the engine, I have a bit more reason than usual to do so).

    So I'll probably spend about the next week or two putting these together. I think once those are done, we can actually send some test vertices (along with a fake material and a fake light) and see some geometry on the screen. Whoot
    Last edited by RonHiler; 07-28-2010 at 07:44 AM.
    "They laughed when I said I was going to be a comedian ... They're not laughing now." - Bob Monkhouse

  2. #2
    Join Date
    Mar 2005
    Location
    California
    Posts
    2,098

    Default

    I've changed the name of this thread. After some further reflection (and perhaps a bit of research), I don't think we need/want to have classes for the vertex buffers and indices. I could change that later, but for right now, a simple structure for the vertices will suffice. I'm not sure I see any need for functions performed on a vertex level (that's what the shaders are for).

    I'm trying to be quite careful here. Decisions made at this point are going to have far reaching implications to the engine long into the future, and I'm endeavouring to make the right ones

    I think what we really want here is a Mesh class. Internal to that class will, of course, be the vertex data (as a structure), the vertex indices (integers), the material (an index into a different class), and the vertex topology type (point list, triangle strip, etc etc).

    We will want the ability for each mesh to have multiple copies (so you can have 20 goblins in the world all using the same mesh). So we shall have another class (Mesh Instance). This class will contain, obviously, some sort of link to its parent Mesh. It will also contain the instance's World Matrix (that will allow us to translate, rotate, and scale each instance individually).

    One additional wrinkle regarding meshes. Often, a "mesh" is not just one mesh, but in fact can have a series of submeshes (think of something complex, like a car, which has the body, the tires, the windows, and so on, lots of different materials there). All these submeshes would share the same instance (thus, they all have the same World Matrix). From that standpoint, what we really need is a mesh class that supports an arbitrary (but equal) number of vertex buffers, indices, and materials. But I think we only need one topology type (I *think*).

    So what do we need in terms of functionality from these classes?

    We'll need to add a mesh, and a mesh instance (through the Graphics Manager). We will need to be able to affect the mesh instance's World Matrix. And of course, we need to be able to send that mesh (and all of its instance's World Matrices) to the renderer for drawing.

    Now, that last bit is a tad tricky. I want to be easily able to sort mesh instances by two criteria. One, by distance from camera (we will need that for the z-prepass to reduce overdraw, and also for any mesh with alphas (transparency) which will need to be drawn back-to-front). Two, they need to sort by material for final rendering (drawing by material groups reduces state changes to the renderer, which are very slow). So there is that aspect to consider as well.
    "They laughed when I said I was going to be a comedian ... They're not laughing now." - Bob Monkhouse

  3. #3
    Join Date
    Mar 2005
    Location
    California
    Posts
    2,098

    Default

    Alright, some structural decisions have been made. I've coded some of this, but not all of it.

    Basically, the flow will be:

    Code:
    GraphicsManager->MeshController->MeshClass
                            |
                            --------->MeshInstanceClass
    Graphics Manager, of course, is the client interface. With regards to meshes, the functions in the manager will be AddMesh(), AddSubMesh(), and AddMeshInstance(). Also related will be the BeginScene() and EndScene() functions. Those will do for a start, I'll be adding more mesh functions as we go (for instance, we willl need a way to adjust a mesh instance's World Matrix sooner rather than later, and we'll need some destruction functions in there too).

    The primary job of the MeshController is to create and maintain all the Meshes (and submeshes) and Mesh Instances. It will also keep two lists of sub-mesh instances (sorted by distance from camera and by material). Naturally, these lists will be malleable depending on what instances are visible at any given time, not to mention that if they are mobile mesh instances (like a goblin might be), the depth sort might change around over time. A linked list is probably our best choice for those lists. On a BeginScene() call (passed in from the Manager), it will provide those lists to the renderer, along with the mesh data and each mesh instance's world matrices.

    The Mesh class holds the data for a mesh, obviously. This will include the vertex buffer, the vertex indices, the material, and the vertex topology type for each submesh associated with that mesh. Each mesh will have 1..N submeshes. Each mesh will also have a name and a handle (just a hash derived from the name) for later access.

    Finally, the Mesh Instance, which holds a World Matrix as well as a handle into the Mesh class (or something like that. We do need a lightning fast way to go from Mesh->Mesh Instance, or vica versa. Normally I would use a pointer for that, but pointers into Arrays (which is what we have here) are dangerous, especially so in this engine, so I'm going to have to think about how best to do that).

    Anyone see any issues? Comments? Questions?
    "They laughed when I said I was going to be a comedian ... They're not laughing now." - Bob Monkhouse

  4. #4
    Join Date
    Mar 2005
    Location
    California
    Posts
    2,098

    Default

    Getting closer. I think we should have something on the screen pretty soon now. This is in the initialization code, and serves to illustrate how meshes are built:

    Code:
    Vector BoxVerts[] = {Vector(12.0f, 14.0f, 12.0f), Vector(8.0f, 15.0f, 22.0f), Vector(9.0f, 3.0f, 7.0f), Vector(11.0f, 64.0f, 12.0f),
    Vector(43.0f, 1.0f, 17.0f), Vector(11.0f, 4.0f, 9.0f), Vector(6.0f, 12.0f, 13.0f), Vector(7.0f, 19.0f, 14.0f)};
    unsigned int BoxIndices[] = {0, 7, 4, 0, 3, 7, 1, 3, 0, 1, 2, 3, 3, 6, 7, 3, 2, 6, 2, 5, 6, 2, 1, 5, 4, 6, 5, 4, 7, 6, 1, 4, 5, 1, 0, 4};
    Vector PyramidVerts[] = {Vector(34.0f, 12.0f, 9.0f), Vector(11.0f, 12.0f, 15.0f), Vector(3.0f, 9.0f, 12.0f), Vector(6.0f, 8.0f, 7.0f), Vector(0.0f, 0.0f, 0.0f)};
    unsigned int PyramidIndices[] = {1, 3, 2, 1, 4, 3, 1, 0, 4, 4, 0, 3, 3, 0, 2, 2, 0, 1};
    GraphicsMgr.AddMesh("Box");
    GraphicsMgr.AddMesh("Pyramid");
    GraphicsMgr.AddSubMesh("Box", 8, BoxVerts, 36, BoxIndices, TRIANGLE_LIST);
    GraphicsMgr.AddSubMesh("Pyramid", 5, PyramidVerts, 18, PyramidIndices, TRIANGLE_LIST);


    The float values are not real, I just made them up, and I'll go back and make them real to get the right test shapes once we have something appearing on the screen. The indices, however, should be real values to get the shapes I want.

    Of course, in actual game code, you won't build meshes manually like this, meshes will come either from the engine world builder (when the client places geometry) or through the collada importer (when the client imports a model). Which begs the question on whether or not AddMesh and AddSubMesh should even be documented functions. Probably not, actually.

    At some point I will need a better way to find particular meshes (right now it's using a hash of the string to produce a searchable value). But I think I want to hold off on that until we get groups (and portals) into the engine. Then I will have a better idea of how the meshes are organized and how we will need to search through them. For now, doing a straight O(n) search will work okay. I have other fish to fry at the moment
    "They laughed when I said I was going to be a comedian ... They're not laughing now." - Bob Monkhouse

  5. #5
    Join Date
    Mar 2005
    Location
    California
    Posts
    2,098

    Default

    Alright, I've made a few structural changes to the meshes and rendering systems. I've made the SubMesh it's own class. That allows it to have it's own static vertex and index buffers, which is useful. And in the future it allows for each submesh to have it's own material. So the new scheme looks like this currently:

    Code:
    GraphicsManager->MeshController->MeshClass->SubMesh
                                         |
                                         ------>MeshInstanceClass
    Where each mesh can have 1..n submeshes and 1..n mesh instances. The mesh controller can control 1..n meshes. Does that all make sense?

    I think the other thing I am going to do is make the APIWrapper class inheritable into both the Renderer class and the SubMesh class. The reason being that I need API functions in the Renderer (obviously), but also (perhaps less obviously) in the SubMesh class (in order to create those vertex/index buffers). And in doing that, I can also move a few functions and member variables out of the renderer and into the wrapper (things like, which is the current renderer, and what are the potential renderers, things that are more related to API function selection rather than actual rendering).

    By doing those things, I think we'll have a much more solid base. Things were getting a little shaky there for a bit, as I was adding in functions/variables into classes where they really didn't make much sense, but just because that's where I needed them.
    Last edited by RonHiler; 08-09-2010 at 10:11 AM.
    "They laughed when I said I was going to be a comedian ... They're not laughing now." - Bob Monkhouse

  6. #6
    Join Date
    Mar 2005
    Location
    California
    Posts
    2,098

    Default

    Some minor trouble with the vertex/index buffers. The buffers live in the Mesh classes (actually the SubMesh class, to be precise). To create them, you use the D3DDevice. The device lives in the renderer class (which is where it should be). There is no direct connection between the renderer and submesh classes (and, in a OOD sense, there shouldn't be).

    So the trick is to get the device passed over from the renderer to the submesh class so that it can be used to create the buffers. Which is a little tricky, considering there are actually four different devices so far (depending on the currently active API). It's not like you can just use a Get() function, because the return value will change depending on the API being used.

    Probably the best thing would be to return a void*, which would allow us to return a pointer to any of the renderer devices. Of course, then it has to be cast back into the proper type once we're back in the SubMesh class.

    Anyway, I'm working on it. Fun times.

    My OpenGL book is scheduled to arrive today. Should be an interesting read.
    "They laughed when I said I was going to be a comedian ... They're not laughing now." - Bob Monkhouse

  7. #7
    Join Date
    Mar 2005
    Location
    California
    Posts
    2,098

    Default

    Whew, okay, I think I've worked my way out of this particular Gordian knot, and in a way that is not a hack, but rather is consistent with the functions that I want the classes in question to perform.

    That was reallly the hard part. Getting a device pointer over to the mesh class can be done in any number of ways, and I think I considered just about all of them But the real trick is to do it in such a way that you are not comprimising on what the classes are meant to do. Otherwise you are violating the rules of OOD, and are just asking for trouble later on down the road.

    As one example, I considered putting the device pointers (as there are four of them at the moment) into the APIWrapper class, and making them static (and therefore available to all the classes that inherit APIWrapper). However, that's not really the function of the APIWrapper (to hold the device pointers). That task more properly belongs to the Rendering class. So that's where I left them.

    Anyway, it's done, and it looks like the vertex buffers are finally being created okay, on a per-submesh basis, at least for DX10. I'll be adding the index bufers in just a minute (it's done in the same function as it is a very similar process and uses all the same data), and then I'll be abstracting that functionality back to the other three renderers.
    "They laughed when I said I was going to be a comedian ... They're not laughing now." - Bob Monkhouse

  8. #8
    Join Date
    Mar 2005
    Location
    California
    Posts
    2,098

    Default

    Okay, as I said before, the renderers and mesh classes are all kind of being developed simultaneously, so that's why I'm switching back and forth between threads a bit.

    As far as the meshes go, I've set up their vertex and index buffers for D3D10. Once I get the other APIs caught up, we'll have to add some functionality for destroying and recreating those buffers on an API change, but for now I'm not worrying about it.

    With that done, I've started dealing with the three matrices that are required to get something on the screen (the World, View, and Projection matrices). For that, I needed quite a few vector and matrix functions (normalize, cross, dot, etc), so I've filled in those classes a bit (and those will require docs, so I'll be adding them to the documentation soon).

    The World matrix will live in the mesh instance class (each instance gets its own world matrix). The other two are really part of a Camera class. I haven't yet started that class, but I will shortly. As I want to support multiple cameras (if nothing else, that will be quite useful for the world builder, and sometimes games like to use more than one camera (think rear-view mirrors!)), we are going to have a full blown class for the cameras.

    Setting up the camera shouldn't be too difficult. The View Matrix requires a few vector operations (normalization, dot, cross, subtraction, and negation), which are the operations I added this morning. The Projection Matrix is slightly harder (it actually is different for orthographic and perspective cameras, as you might guess), but it really shouldn't be too bad. I'll probably start a new thread for the Camera class once I really get into it (as the camera will eventually have to move around, we'll need functionality to modify the View Matrix. For that matter, we will also need to modify the Projection Matrix under certain conditions, like when the aspect ratio of the viewport changes).

    So, so far, we have initialized four of our five rendering APIs (all except for OGL), created our test objects as meshes/submeshes, and set up vertex/index buffers for them (but only in D3D10 so far). Next we'll be placing them in the world (World Matrix) as well as adding our camera (View/Projection Matrices). The final steps will be binding the buffers to the GPU and then the actual DrawIndexed calls (which are trivial).

    I think that puts us a good chunk of the way through what is among the most complex parts of a game engine (the renderer). At least for the basics. There are a lot more things to add over time (transparency, scene management, various texture mappings, and so on). But those can be added a bit at a time. In my experience, getting those first few triangles on the screen is the really tricky bit
    "They laughed when I said I was going to be a comedian ... They're not laughing now." - Bob Monkhouse

+ Reply to Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts