[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Showing posts with label Meshroom. Show all posts

Drivers and Selections #

First of all, I'm happy to say that 3dfx has released OpenGL drivers for my Voodoo3 card, which unlike the Mesa (an open-source implementation of OpenGL for almost all platforms) drivers that I was using until now, support windowed rendering, so Meshroom is now accelerated. There's still some bugs (points aren't drawn, I'll have to make a simple program to see if they're not drawn at all, of if it's just a context thing in my program), and some other things I've observed when running some sample programs, but overall life is good. For unsupported software (the whole Voodoo3-on-a-Mac thing is unsupported, I'm guessing they're using this as a way to familiarize themselves with the Mac platform in anticipation of their next product, where it'll be official) this is getting pretty good too.

Other than that, I've started to work on implementing selections (as in, you can click on an object in the 3D view, and it'll be selected by showing it's bounding box with the corner handles). So far it's working pretty well, except I have to do some better parsing of the selection data that OpenGL returns to me (it gives me all the objects which occur at that point, even if they are hidden behind others, so I should parse the array and select the front-most one, but things get a bit more complicated when you have objects completely contained within one another). I'm also wondering whether I should implement multiple selections or not, in the end I guess I will, because it's more dynamic data structures for the IB, but I might wait until I make the switch to the Mac interface (I'm still using GLUT to prototype the underlying architecture, although making the initial switch shouldn't be that hard, things will get more complicated when I implement the proper UI, with the four views (top, left, front, and a 3D camera view) and the editing tools (this will probably require me to implement controls to the MFrame (my application framework), so far (in Iconographer) I've been implementing them by hand, but this doesn't go well with the whole flexible approach that I'm using with the factory method). Right now I'm thinking of having each control have an unique (four letter of course) code, and a pointer to the class which owns it, or a static function of it (e.g. the sphere tool would point to the sphere class), and each class would also include a method for initially loading the controls, etc.

Template-based factory #

I'd mentioned earlier that I had implemented cameras unsing a factory method, and that it mostly required copying and pasting the object factory code and doing a search and replace, and that I would have to do a similar thing for the lights when I implemented them, and so it would be best if I could find a generic method (using templates) of implementing a factory for any kind of object. Well, on Wednesday I finally did this, and now the code is quite elegant. The Model3D class (which as its name suggests is used to take care of an entire model) has the following members:

public:

	...

	static MFactoryDatabase	objectDatabase;
	static MFactoryDatabase	cameraDatabase;
		
private:
	
	MFactory<Object3D>	objectFactory;
	MFactory<Camera3D>	cameraFactory;

pretty factories and boxesThe factory object uses a database (which is passed to it as a constructor parameter) and when asked for an object (through a four letter code) it finds the instatiator in the database, runs it, and inserts the new object into the (double) linked list inside it. To take care of object IDs and and previous/next pointers, an additional MFactoryObject class is required. It is assumed that all objects which the factory is designed to produce are derived from this one.

The separation of the factory from the database is quite logical. Each instance of the factory class must maintain its own linked list, while the object database should be shared across all objects (hence the static keyword). Making the database a static member of the MFactory class would not have worked since then the camera and object factory would have the same database, and then you couldn't tell in which one an object belonged.

Other than this, I've also added bounding boxes. This is a first step towards selections (when selected an object will display its bounding box and resize handles). It involved adding GetBoundingBox method to the base class which returns 2 3D points (for opposing corners of the parallelipiped), and then implementing it for each of the derived classes (the torus took a while, since it turns out that the z coordinate for it is constant, no matter what the inner and outer radii is, and for the teapot I didn't have the actual dimensions (the glutSolidTeapot function takes only a generic scale as a parameter) so I had to guess them, run the programs and tweak them). Then the Display method uses the returned values to draw the box. The picture is what the whole thing looks like right now.

You're on Object3D Camera #

I've added camera support, so that the camera placement is loaded from the scene file as well. I support two kinds of cameras, perspective (which cause perspective distortions, e.g. parallel lines appear to converge) and orthographic (no distortion, used in architecture sketches to maintain proportion, etc.). Since cameras are treated differently from objects (they're not rendered, etc.) I can't store them in the same linked list, and thus I ended up having to copy and paste the functions and variables used to implement the factory method for the objects and replace all the occurrences of 'Object3D' with 'Camera3D' (the Camera3D class is still based on the Object3D class, but it itself serves as a base, for the PerspectiveCamera3D and OrthographicCamera3D objects). Obvviously this isn't the best way to go (and when I add lights, this would require another set of of linked lists and factory) so I'm thinking of using templates somehow to create a generic way of implementing the factory method. This ought to be fun, but I need some time to think about how this will work.

On a somewhat related note (i.e. also 3D related), a couple of days ago I found this cool OpenGL game (although it's more like a demo now, since you can't do that much except fly around) called glUFO. From the screenshots it looked really cool, except that it was for Windows. Fortunately, the source code was provided, and as it turned out the author used GLUT (Graphics Library Utility Toolkit) for the user-interface, so I figured I should try to port it. The first problem was that the author used GLUT 3.7 (a.k.a. GameGLUT, since it includes a bunch of functions for setting the resolution, etc. which are useful for games) which hasn't been ported yet to the Mac. Then I had to redo the key handling, since he'd used Windows specific code there (GLUT only supports one key down at a time, and provides no way to detect if a certain key has been released). This involved adding some #define's (my goal here was to modify the source code as little as possible, so most of the modifications were made in the header files) to map the KEY_DOWN macro to the appropriate function. Then I had to find all the key-codes, and #define their Mac counterparts. Finally, there were a bunch of compiler errors with the function calls, mostly that he'd define function a as 'void a(int)' and then called it with 'a();'. I guess Visual C++ is more forgiving than CodeWarrior, but fixing them didn't take that long.

Now the program finally compiled, but it would quit immediately. The fix for this was rather simple, I had forgotten to actually create a window to display the game in. The program then worked, except it was very slow. However, this was as expected, since I was using the software renderer. I added the fullscreen flag to the initialization options, so that the 3D accelerator would kick-in (the current drivers only support full-screen acceleration, although the hardware itself can do windowed rendering). To my dismay, nothing showed up on the screen. The main problem here is that since I was working in full-screen mode, it was very hard to debug. I obviously couldn't switch in to the Metrowerks debugger, but even MacsBug wouldn't display anything (the screen would go gray, but no text would show up on the screen). With a set of Debugger (which invokes the current debugger, whatever it may be) calls placed at various points throughout the code, and the use of the StdLog (which logs, among other things, the name of the current function) command typed blindly into MacsBug, I was able to determine that the code was indeed entering the main execution loop. After examining the code more carefully, I discovered a commented out call to glutPostRedisplay (which causes the display to be redrawn) in the idle function. After uncommenting this, and modifying the display function a bit, I finally got the app to show something on the screen. My theory is that when in software mode, the window was redrawn automatically the first time, but in full screen mode it wasn't, so I had to call it myself.

Although there was finally something on the screen, things still weren't quite right. The coulds, exhaust and bullets would show up as black rectangles, sign that there was something wrong with the transparency, and the mountains weren't textured at all. Fixing the transparency involved changing the destination alpha (the alpha channel being the transparency channel in an RGBA environment) from GL_DST_ALPHA to GL_ONE_MINUS_SRC_ALPHA. I guess the destination alpha was one value by default in software mode, and another when the 3D accelerator was enabled. The problem with the texture took me a while to figure out. As I was reading the release notes for the program, I saw something about Voodoo cards only being able to handle 256 x 256 textures (I already knew this) and that thus the 1024 x 1024 ground texture will appear rather blurry. I thought that perhaps the Mac driver, being still in development, doesn't do any scaling to make the textures fit, and rather simply rejetecs. I opened up the texture file in Photoshop (it was in a raw format) and scaled it down to 256 by 256. Finally, that worked too, and I was able to play around with the program as expected. If I can get it into a stable state (there's still some more issues with one other texture) I'll see about submitting the changes and the executable back to the author, so that he can provide a Mac version too.

Coding Begins #

I've finally started to code. So far I'm doing the things I've spec'ed out. This basically means that I'm loading basic objects (spheres, teapots, cones, tori, and cubes) and displaying them. To give an example, the following file:

Sphr
0.0 0.0 0.0
0.0 0.0 0.0
1.0 1.0 1.0
1.0

Cone
-3.0 0.0 0.0
0.0 0.0 0.0
1.0 1.0 1.0
1.0 2.0

Tpot
3.0 0.0 0.0
0.0 0.0 0.0
1.0 1.0 1.0
1.0

Tors
0.0 3.0 0.0
0.0 0.0 0.0
1.0 1.0 1.0
1.0 2.0

Cube
0.0 -3.0 0.0
0.0 0.0 0.0
1.0 1.0 1.0
2.0

Meshroom Screenshot This results in the picture on the left. Although this might seem rather simplistic, the basic structure is quite extensible. First of all, the various object types that can be handled are registered (a four letter code is associated with a class). Then when the model attempts to load the file, it gets the code from the file, and calls the appropriate class. All of these classes (currently Sphere3D, Teapot3D, Cone3D, Torus3D, Cube3D) are derived from a base class (Object3D). The base class takes care of a few common things (when creating/destroying the object it takes care of maintaining the linked list, when loading, the first three sets of coordinates are the position, rotation and scale and when displaying it sets up the model matrix). The rest of the data represents object specific information.

When displaying, the list of objects is iterated through, and the Display function of each object is called in turn (the main class treats them all as being of type Object3D, but in reality the appropriate Display function is called). A similar appropach will be taken when dealing with user input.

There is no user interface right now, I'm just using GLUT (GL Utility Toolkit) for the windowing. My plan is to add camera control (right now it's hardcoded, I want to create a Camera3D class which I can manipulate in order to change the viewpoint, perhaps even create two sub-classes of that, PerspectiveCamera3D and OrthographicCamera3D, for the two projection types) and then implement the other half of the project, the window-based interface. This will require me to first learn about the Mac-specific OpenGL calls (which would be required for setting up a drawing context and rendering into a GWorld), and then figuring out the architecture of the UI (Iconographer's isn't very C++ like, but I'm not sure if I want to go as far as have each control be a separate class).

I also have to implement the mesh tool, which will be one of the harder parts, especially when it comes to editing it. But I'm delaying it until after I have an UI up, since it'll be a lot easier to debug when I can view it from different directions and manipulate it. And of course I musn't forget colors (I guess this will be another attribute that will be handled by the base class, since I'm not planning for texture mapping, although I might have the option of adding colors to each vertex of the mesh). The reason why I can get away with no texture mapping is because I'm only doing a modeler, not a fancy renderer, and besides texture mapping would be too slow in software rendering (which I have to use since the current (third-party, unsupported) OpenGL driver for my Voodoo3 doesn't do windowed or offscreen rendering, only on the whole screen).

Still Planning Away #

Haven't had that much time for this project lately. Unlike Sonar, Grendel and Iconographer, it's still in the early stages, and thus I can't just pick it up anytime, work for a bit, and then move on, rather it requires some concentrated thought. However, I am tempted to start to implement the architecture I described earlier for storing the models, and leave the user interface for later.

I'm starting to have an idea of how the user interface will work. First of all, when the user clicks, I have to determine which object was hit. This isn't quite as simple as it sounds, since the scene can be at a weird camera angle, with objects obstructing one another, etc. OpenGL has a picking/selection mode which allows you to see which object was clicked on. What you basically do is assign each object a distinct color, turn off lighting and other effcts, and render a very small area around the location that was clicked. Then by looking at the color, you can determine which object was hit. However, this doesn't tell you where exactly the click was. I thought I was going to have to get the inverse of the modelview and projection matrices, and do this myself, but apparently there's a function called gluUnproject which does this for me. Once I have the click transformed to be in terms of the hit object's coordinates, then I can actually process it.

One of the bug remaining issues is that of the tools. I've mentioned earlier how I had come up with a flexible and extensible architecture for handling various object types (geometric primitives, meshes, etc.). I'm thinking of extending this to the tools, but I'm not quite sure how it'll work yet. If each object type has a single tool associated with it, then things should work out pretty well, but I don't know if that will be the case. I guess I should draw up some mock-ups of how the user-interface should look, and then the class hierarchy behind it which would be used to implement it.

Once I get an idea of how the whole application will work, I can see two possible ways of starting. I can work on the back-end (the loading and rendering of models) and use GLUT as a temporary user interface until I get to that, or I can work on the user interface, and use simple placeholders (e.g. OpenGL has a teapot model built-in) for the actual models. Right now I'm leaning towards the first option, because it'll be more satisfying (3D models are cooler than the user interface), and I need to learn more about the rendering contexts.

Basic Architecture #

Data storage

OpenGL includes the concept of vertex arrays. The idea is that instead of calling glVertex3f (and it's cousins) everytime a polygon is defined, you create a list of vertices (which can be stored in the 3D accelerator card for optimal perfomance) and then when displaying polygons, you simply make references to positions within that list. Parallel lists can be maintained for surface normals, colors, etc. or they can be put in the same array, and a "skip" value can be used to jump from one set of coordinates to another.

What I'm currently considering is to store all of the vertices used in a model in a central location, and then each of the objects make references to that central vertex array. In this way, sharing vertices between different objects will be very easy. The model file would then start off with a big list of points, followed by the actual objects (e.g. in the case of polygons there would be an enumeration of vertex numbers to define the corners of the polygon). As far as I know, this is how games such as Marathon store the world levels (which is why with the level editor you first lay down the points, and then in a separate step connect them).

As an alternative, I can store the vertices (still within a vertex array) within each object. The main advantage of this would be that when deleting or inserting vertices, I wouldn't have to search for the objects which make use of the vertices whose positions are affected and have to adjust the indices appropriately. There might be some wasted space (since there might be duplicate vertices), but there can't be that much. I really can't think of too many situations where you would want different objects to share a vertex (e.g. for a sphere's center to be attached to a corner of a polygon). For things such as triangle meshes, each vertex array will encompass the entire mesh (as opposed to a single triangle) so the big savings are still there. Actually, for things like spheres (which only need a single point and a scalar to be defined) there's no point in using vertex arrays. The only problem which I foresee is if there can be a limited number of distinct vertex arrays be loaded in memory at the same time. I don't mean limitations due to not enough memory, I mean limitations caused by OpenGL's implementation (similarly how some 3D accelerator cards can only handle 8 lights at a time). If that's the case, I'll have to implement an architecture which keeps track of the vertex arrays as they are loaded, and disposes of the least used one (or the one used the longest ago) if a new one is to be loaded but there isn't enough space.

Heh, looks like I totally eliminated the first choice. Well, this isn't quite the IB dossier write-up yet, so I can ramble on.

Classes

I'll be needing a base Object3D class which will a stub-like thing (that is, it'll never be used directly in the application, rather I'll be using classes which use Object3D as a base). It'll have basic fields (and the appropriate functions to set them, I'll try to have as little public variable as possible, and instead rely on accessor functions) for position, rotation. There'll be virtual functions for loading, displaying, scaling, etc.

For object identification, I plan to have both a name field, and a four letter code (which translates into a nice long) which shows the object type. Then the loading function could look at the four letter code in the file, and create the appropriate instace of the class and call it's loading function (which is virtual, remember).

First of all, I said "the appropriate instance" above. As implied by the fact that Object3D is a base class, I'll be having other classes layer on top of it. There'll be simple ones like Sphere3D, Cone3D, Cube3D, etc. and more complicated ones like Mesh3D. Since they share a base class, the rendering loop can treat them all like Object3Ds and call the display function. Things might get a little bit more complicated with the user interaction, but it's the same idea.

I want the program to be as flexible as possible. Ideally, if I wanted to add a new object type, I shouldn't have to chase down all of the case statements which involve the four letter object codes and add the new one. A plug-in architecture is overkill, but I can implement an way to "register" new object class types. Basically, there's a central "hub" which stores pointers to "mini hubs" for each object type. These "mini hubs" would share a base class which has a virtual function to create a new instace. The "mini hub" class is only a few lines really, since all it has is a "return new ". Perhaps it could also be done as a static function in the actual object class. In any case, all that would be required to add a new object type (once the class has been written) would be to "register" it with the "hub" (e.g. ObjectHub.Register('Cube', &CubeHub); or perhaps ObjectHub.Register('Cube', Cube3D::Instantiator)). Then, while reading the file, the main function would look up the four letter code, if it finds a function pointer then it calls it and assigns the value to an Object3D class pointer (in reality the class is of a specific, high level type, but the function doesn't care which one, as long as it can call the loading function, etc. which are virtual).

I'm not sure if the above is totally coherent. In any case, as far as I can tell (from what I've just been told) the process I'm describing is the factory method (as described in Design Patterns : Elements of Reusable Object-Oriented Software (I have this book, but I haven't really used it) and other places). My plan is to implement the architecture as best I can, and then read the relevant chapter(s) in the book. Then I can 1. make much better sense of what they're trying to say and 2. say to myself "that's how I should have done it."