Nice n' Easy JQuery Image Rotator
Installing Xdebug for use with Eclipse or Netbeans on Linux
Symfony 2 Crash Course
ENUMs, User Preferences, and the MySQL SET Datatype
Getting Set up with Ogre 3D on Ubuntu
Native Linux Space Warfare: Freespace 2

The First G3D Example

Thursday, 22 December 05, 12:00 am
Right, so now I've checked everything is set up correctly, I've decided to take a look at the examples that come with the G3D library.

There are seemingly eight, described on the G3D web site; it's not clear if there's any natural progression through them.
  • ArticulatedModel
  • loads models from files in 3DS, IFS, and PLY2 formats. Creates a scene graph, uses 'Pixel Shader 2.0' and 'fixed function' pipelines, apparently.
  • GLSL_Shader_Demo
  • uses pixel and vertex shaders to make a 2D texture appear to have shape.
  • VAR Demo
  • demonstrates rendering using dynamic vertex arrays, static vertex arrays, and the 'traditional per primitive rendering'.
  • MD2Model_Demo
  • uses animated models from Quake 2.
  • Collision_Demo
  • as name indicates, this uses the collision detection features of G3D.
  • Win32_Demo
  • uses non-OpenGL Win32 routines to display a JPG.
  • Cg_Shader_Demo
  • appears to show different approaches to texture mapping with Cg. Bump mapping, per-pixel shading, and normal mapping.
  • ASM_Shader_Demo
  • using programmable hardware of ATi/nVidia's chips to manipulate a 3D model.
The VAR demo seems the best place to start.
2 

compton

7:00 am, Thursday, 22 December 05

The demo looks pretty cool. I'm not sure what step I did to make my first G3D project (G3Dtest) find all the files, but when I opened the VAR demo it couldn't find everything and wouldn't run.



I got it to run by adding its main.cpp to the G3Dtest project as an existing item. It then ran fine, and works very well. There are 50 p-51 Mustangs flying around the origin, all bobbing and swaying quite pleasingly, some going slow, others faster. Pressing Space switched the rendering method, the best being static vertex array. The dynamic array (or streaming) was nearly as good, and the traditional per-primitive rendering (which is what I was doing in G3Dtest) was very jerky.



The code is all contained within a single main.cpp file. In this file, there are 3 classes defined, Model, Demo and App.

compton

7:00 am, Monday, 26 December 05

Model represents a 3D model - its constructor takes a filename and VARAreaRef as arguments, and loads an IFS model from the specfied file using G3D's BinaryInput object. First a couple of general parameters are loaded using readString32() and readFloat32() on the BinaryInput instance.



The vertices are then read using readVector3() into the vertex Vector3 array (a private member of Model). Next, the triangles which make up the shape are read, as sets of 3 integers using readUInt32(). These integers are indices of the vertex array representing the corners of a triangle. For each triangle, the 'face normal' is computed, and added to whatever is held in the normal Vector3 array (another private member of Model). The 3 corner indicies are stored in the index uint32 array by calling append() on it.



Once this is done, the vectors in the normal array are set to unit length, and the varVertex and varNormal private VAR members are initialised using the vertex and normal arrays respectively.



The public numPolys() method simply returns the number of triangles in the model, as the size of the index array divided by 3. Not sure when this would be useful.



The other public method is render(), which draws the model. If the traditional per-primitive rendering has been chosen, we call beginPrimitive(RenderDevice::TRIANGLES) and then loop through each element of the index array, sending the element from the normal array indexed by the value from the index array to setNormal(), and the element from the vertex array indexed by the same value to sendVertex(). Once we've looped through all indices in the index array, we call endPrimitive() on the render device, and we're done.



If we have chosen the streaming vertex array, we begin with a call to beginIndexedPrimitives(), and now initialise two new VAR objects, n and v, using the normal and vertex private member arrays and varStream rather than varStatic. These are passed to the render device's setNormalArray() and setVertexArray() methods respectively. We then call renderDevice->sendIndices(), passing in the index array. We finish off with a call to renderDevice->endIndexedPrimitives().



So the final rendering approach, using static vertex arrays, is just the same as for streaming ones, except that setNormalArray() and setVertexArray() take VAR objects that were initialised with varStatic rather than varStream. You may remember that we've stored these as private member variables, so we can just pass these into the two render device methods. The only other difference is that with streaming arrays, we need to check for 'headspace' on the varStream, and reset it if we don't have enough.

compton

7:00 am, Monday, 26 December 05

The App class inherits from GApp, provided by G3D. GApp represents a 3D application, and can comprise several GApplet instances.



In this case, App contains a pointer to a single GApplet, as well as defining a constructor which takes a GAppSettings object as its parameter. The constructor appears to be empty. The class also defines a protected main() method and a public destructor. The destructor simply deletes the GApplet.



App::main() is the starting point. It enables G3D's debug mode (which provides the default camera and movement controls), and then points the applet GApplet member variable to a new instance of the Demo class. It then calls run() on applet.

compton

7:00 am, Monday, 26 December 05

The last class, Demo, must contain all the application-specific functionality. Remember this class is instantiated within App, which calls a new one using the constructor that takes an App object as argument (passing itself - ie the App instance - as the parameter), and stores a pointer to that new Demo instance.



Demo itself inherits GApplet, which has method declarations but no definitions in the gapp.h header file. In addition to the constructor (which takes a pointer to a GApp as its only argument) and destructor, there are just four public methods:

  • run()

  • beginRun()

  • oneFrame()

  • endRun()


run() handles the main loop, while the other three allow you to handle the loop outside of the GApplet, by calling beginRun() at first, then oneFrame(), and endRun() at cleanup. So these methods are simply two different ways of doing the same thing.



Demo doesn't itself define a run() method - it must use one defined by its base class, GApplet. Which means that GApplet must have some other file that contains, presumably compiled, method implementations. The G3D DLL then?
2 

Please enter your comment in the box below. Comments will be moderated before going live. Thanks for your feedback!

Cancel Post

/xkcd/ Europa Clipper