The obj format can only specify simple objects and cannot form hierarchical groups of objects. The code in gfx_parser_obj_simple.* can parse a simple object from a input file. The abstract struct that defines the internal representation of a simple object is in gfx_obj_simple.h. The function render_obj_simple in test_basic_gfx.cpp displays simple objects using OpenGL by converting the struct to appropriate OpenGL commands.
OBJECT {name} SIMPLE {.obj syntax} ENDor
OBJECT {name} SIMPLE FILE {filename}.obj ENDwhere {name} and {filename} can be any alphanumeric string that begins with a letter.
LIGHT {name} # color for ambient, diffuse, and specular AMBIENT {r g b a} DIFFUSE {r g b a} SPECULAR {r g b a} # constant, linear, and quadratic attenuation CA {float} LA {float} QA {float} ENDAny of the fields above are optional, and the alpha value is optional in the color tags. Color values should be in the range 0 to 1, inclusive. Attenuation values should be positive. See gfx_command_light.h for details on how you should store information for lighting.
OBJECT {name} COMPOSITE {List of Composite Commands} ENDA scene object has the form:
SCENE {name} {List of Composite and Scene Commands} ENDEach .gfx must have a SCENE named main. Commands are described below. Unless otherwise specified, a given command can appear in either a composite object or a scene.
TRANSLATE {x y z} ROTATE {deg x y z} SCALE {x y z} COLOR {r g b a} PUSH POP SEPARATORFor the color command, the alpha component is optional. A separator command is a POP followed by a PUSH. (Ironically, typing SEPARATOR is longer than typing POP\nPUSH, but you should support it anyways)
SUBOBJ {name}The command SUBOBJ draws the specified object. The parameter name should refer to a composite or simple object defined earlier in the .gfx file.
GLUT {type} {params}The GLUT command draws a solid glut object. Valid types are SPHERE, CUBE, TORUS, ICOSAHEDRON, OCTAHEDRON, TETRAHEDRON, DODECAHEDRON, CONE, and TEAPOT. Some of these shapes take one or more parameters. For a full description see pg764 of the OpenGL redbook or the glut docs. The struct GfxCGlut defined in gfx_command_glut.h can hold the necessary info for each type of GLUT object. The LOOKAT and LIGHT command are only valid in scenes and not in composite objects.
LOOKAT {eyex eyey eyez aimx aimy aimz upx upy upz} LIGHT {name} {x y z} {w}The light command indicates that the scene should place the light defined earlier in the .gfx with the name name should be placed at the location specified. The parameter w should be 0 for directional lights or 1 for positional lights. If unspecified, w should be interpreted as 0.
Parsers for most of the commands that can appear inside composite objects and scenes are defined in gfx_parser_core.h and implemented in gfx_parser_core.cpp. The exceptions are GLUT commands and LIGHT commands which should be implemented in gfx_parser_glut.cpp and gfx_parser_light.cpp.
Writing the parser shouldn't be too bad once you understand some of the basic techniques used to parse simple objects and a few of the commands in gfx_parser_core.cpp.
Add camera controls so that you can navigate through your scene. You should just need to adjust your lookat function to walk through the scene. You should be able to translate and rotate the camera using keyboard or mouse controls.
Parser commands is handled somewhat differently. The implementations for parsing TRANSLATE, PUSH, and POP should give you a good idea of how to parse the others. SUBOBJ, LIGHT, and GLUT may be a bit more tricky. Recall that the commands for LIGHT and GLUT are implemented in separate files to keep the code a bit more manageable.
The utilities in gfx_input.h will do most of the heavy lifting related to file I/O and getting string, name, and number tokens. You can get an idea of how it works by looking at some of the parsers mentioned above.
The renderer is pretty easy once the parser is done. Just read the list of commands from the GfxScene object (g_pScene in test_basic_gfx.cpp) and translate the data in the command into OpenGL. Note that GfxScene is identical to GfxObjComposite defined in gfx_command_core.h.
If you have any questions about the structure or use of the supplied code, let me know and I will add updates and clarifications as need to this page.
Looking at the project top-down may also provide some insight. The actual executable is test_basic_gfx.cpp. The renderer needs to be implemented in the function render_composite, which takes a single GfxObjComposite* as a parameter. This pointer corresponds to the main scene and is found by the find_scene function in test_basic_gfx.cpp which takes as one of its two parameters a list of group items (OBJECTS, LIGHTS, and SCENES). This GfxGroup object is set up using the method readGfxGroup in the GfxReader class in the gfx_basic_reader.* files. The GfxReader class is the main parser, but most of the work is offloaded to several helper classes and functions.
readGfxGroup is just a thin wrapper around doReadGfxGroup which reads individual group items (OBJECT, LIGHT, and SCENE). The simple object case is done for you, but you need to add code to handle the other cases. In each case, you follow a similar pattern to the simple object case and call an appropriate parser, e.g., GfxLightParser in gfx_parser_light.cpp for LIGHT group items, and GfxObjCompositeParser in gfx_parser_composite.cpp for composite objects and scenes.
Scenes and composite objects are similar enough that they can be processed by the same function. The only real difference is that some commands are valid only for scenes and not composite objects. To handle this, the GfxReader class contains two hash tables that map command names to their appropriate parsers. See m_t_cps_parser and m_t_scene_parser for examples. These hash tables are passed as the third parameter to GfxObjCompositeParser::process. The header file lists this hash table as void * lookup, but you can/should change this to GfxHashTable<string, GfxCommandParser*> * lookup. You may need to also add #include "gfx_parser_core.h" to the list of header files in gfx_parser_composite.h.
In the composite object parser, you will just be parsing a list of commands. The general model for this is read in each line until you reach the "END" token. For each line, pull off the first string of any non-empty or non-commented line for the name of the command and check if the command has a parser in the hash table. If so, call that command parser and save the command info in a list of commands inside the GfxObjComposite * pObj.