This wiki is archived and useful information is being migrated to the main bzflag.org website

User:Jude-

From BZFlagWiki
Jump to: navigation, search

0. Abstract[edit]

Last year I began work on BZWorkbench, an extendable, graphical BZFlag world editor that would support map elements supported by the 2.x-series BZFlag game. While the framework and support for 1.x-series map elements was completed, unfortunately it still lacks rendering support for more complex map objects, such as meshes. Also, in its current implementation, the framework does not provide adequate support for allowing the user to manipulate geometric primitives in the scene.

This summer, I propose modifying the object framework within BZWB to make the implementation of more advanced objects simpler. This would entail redesigning the abstract renderable object class to behave more like the abstract renderable object class used by BZFlag, such that it provided easier, direct access to the map element's geometry. This would entail creating a geometric manipulation API to abstract the OpenSceneGraph-specific code from this base object (with the added benefit that it would become a simpler task to use a different scenegraph API sometime in the future). Once this is in place, I will then implement the remaining BZW 2.x objects still missing--mesh, meshbox, meshpyr, sphere, arch, tetrahedron, zone, weapon, and group.

Additionally, BZWorkbench's user interface is rather non-standard, and has been met with criticism by some users. Namely, 3D object manipulation, button design, mouse mappings, and widget appearance need to be re-considered. 3D object manipulation should go through the underlying scenegraph API (in OSG's case, there already exists a 3D manipulation framework that is closer to industry standards), buttons should be scalable and color-neutral for accessibility reasons, mouse mappings should be near-identical to those of a more mainstream, industry-standard 3D editor, and the widget set should appear native to the OS while still implementing the functionality offered by FLTK. Ideally, a second applicant could work with me in tandem on this this summer, but if time permits, I could also work on these usability improvements after BZWB's back-end has been finished.

I. Current Architectural Limitations in BZWorkbench[edit]

I.1 Limitations of the bz2object Class[edit]

The current base class for all renderable objects in BZWorkbench is the class bz2object (see include/objects/bz2object.h for the interface and src/objects/bz2object.cpp for the functionality). While it provides methods to manipulate BZW 2.x features like transformations and materials, it does not behave much like a scenegraph object, nor does it provide access to geometric primitives. In fact, it would be very difficult to describe a mesh object as a subclass of bz2object while allowing it to behave like a geometry node to OSG. The class bz2object, however, greatly simplifies the API for describing BZW 1.x objects, since they are represented by pre-defined meshes that only OSG manipluates in the current implementation. To support mesh-like properties, as well as more traditional scenegraph node properties, bz2object will need to be greatly modified.

In the scenegraph object hierarchy, bz2object currently manifests itself in this form:

[scenegraph root]
        \
         \
          \
           \
            |
            V
     [osg:PositionAttitudeTransform]
     [(part of Renderable)         ]
            |
            V
 |==>[start transformation]
 |          |
 |          V
 |   [transformation 0]<=============================|
 |          |                                        |
 |          V                                        |
 |   [transformation 1]<==========================|  |
 |          |                                     |  |
 |          V                                     |  |
 |   [transformation 2]<=======================|  |  |
 |          |                                  |  |  |
 |          V                                  |  |  |
 |        . . .                                |  |  |
 |          |                                  |  |  |
 |          V                                  |  |  |
 |   [transformation k]<===============|       |  |  |
 |          |                          |       |  |  |
 |          V                          |       |  |  |
 |==>[stop transformation]             |       |  |  |
 |          |                          |       |  |  |
 |          V                          |       |  |  |
 |===[bz2object            ]           |       |  |  |
     [transformation vector]---------->O . . . O->O->O
     [materials vector     ]---->[material 0]--->[material 1]. . .[material m]
     [accumulated material*]=====|
            |                    |
            V                    |
           /|\                   |
          / | \                  |
         /  |  \                 |
        /   |   \                |
        |   |   |                |
        V   V   V                |
     [object geometries]<========|
     [(handled by OSG) ]



Key:

[transformation 0]	object

  |==>			pointer reference
  |
==|

--->			vector (linked list) reference
|
V			scenegraph reference
*This material is generated by combining the attributes in the materials within the materials vector.

In this implementation, bz2object is stuck doing a lot of bookkeeping--it must keep track of all the transformations (including handling insertion and removal), it must keep track of all the materials and accumulate the material properties into a final material to apply to the geometries, and it must access the OSG geometric constructs directly (making it vulnerable to breakage in the event of an OSG API change), leading to messy code. Also, it makes grouping needlessly more complex.

A more scalable solution would involve the creation of an object called TransformedGeode (subclassed from osg::PositionAttitudeTransform), which combines all of the transformations of bz2object, an instance of a modified osg::Geode (let's call this a BZWB_Geode), and an improved instance of BZWB's current Material class. TransformedGeode will encapsulate a sub-tree of the scenegraph like bz2object currently does, but it will not keep a list of materials nor a list of transformations. Unlike bz2object, TransformedGeode will contain exactly one BZWB_Geode instance and exactly one BZWB Material reference (note that Materials are singletons and are distributed by the Model class). The BZWB_Geode object will behave like osg::Geode, but it will provide an additional API that simplifies the process of extracting geometric primitives into a form BZWB will understand. Specifically, a BZWB_Geode will be an osg::Geode that contains exactly one osg::Drawable, which happens to be an instance of osg::Geometry; BZWB_Geode will provide an API abstracting primitive manipulation API from osg::Geometry (since osg::Geometry's methods are very extensive, and not all of them will be necessary).

To create an arbitrarily complex mesh, TransformedGeodes can be linked together to form a larger subtree within the scenegraph. A bz2object, then, is just a class that stores a pointer to the root of one of these TransformedGeode trees, as well as non-renderable data (such as its name, physics driver, obstical flag, LOD information, etc).

Using this proposed architecture, here's how one might express a mesh box defined as

meshbox
  name my_meshbox
  position 20 -30 10
  rotation 45
  size 10 14 15
  shift 20 20 0
  shear 0 0 3
  scale 0.5 1.5 0
  spin 0 1 0 15
  outside matref wall_material
  top matref roof_material
  phydrv physics_driver
  obstacle
end

The object hierarchy would manifest itself this way:

[scenegraph root]
        |
        V
[bz2object meshbox]
[name: my_meshbox ]
[phydrv: pdrv     ]
[obsticle=true    ]
[rootNode         ]====>[TransformedGeode       ]
                        [       |               ]
                        [       V               ]
                        [  [Transformation*   ] ]
                        [  [spin 0 0 1 45**   ] ]
                        [       |               ]
                        [       V               ]
                        [  [Transformation    ] ]
                        [  [scale 1 1.4 1.5***] ]
                        [       |               ]
                        [       V               ]
                        [  [Transformation    ] ]
                        [  [shift 20 20 0     ] ]
                        [       |               ]
                        [       V               ]
                        [  [Transformation    ] ]
                        [  [shear 0 0 3       ] ]
                        [       |               ]
                        [       V               ]
                        [  [Transformation    ] ]
                        [  [spin 0 1 0 15     ] ]
                        [       |               ]
                        [       V               ]
                        [  [osg::Group geo    ] ]
                        [  /        |         \ ]
                        [ /         |          \]
                        [/          |           \
                        /           V           ]\
                       /[  [BZWB_Geode        ] ] \
                      / [  [wall geometry data] ]  \
                     /  [  [wall material     ] ]   \
                    /   [                       ]    \
                   /    [                       ]     \
                  /                                    \
                  |                                    |
                  V                                    V
[TransformedGeode     ]                              [TransformedGeode        ]
[         |           ]                              [         |              ]
[         V           ]                              [         V              ]
[ [osg::Group geo   ] ]                              [ [osg::Group geo      ] ]
[         |           ]                              [         |              ]
[         V           ]                              [         V              ]
[ [BZWB_Geode       ] ]                              [ [BZWB_Geode          ] ]
[ [top geometry     ] ]                              [ [bottom geometry     ] ]
[ [top material     ] ]                              [ [bottom material     ] ] 


*Transformation refers to the current Transform class in BZWB.  It is a subclass of osg::Transform and can parse out the four BZW 2.0 transformation types into transformation matrices.
**When representing meshes, the "rotation" descriptor is meaningless.  This will be converted to a spin transformation about the z axis.
***The default geometry of a meshbox is a 20x20x10 cube.  Like "rotation," "size" is a meaningless descriptor when talking about arbitrary meshes, so "size" descriptors will be transformed into a appropriate "scale" transformations.

Notice how much cleaner this implementation of bz2object is. To read and write user-defined transformations from the scenegraph, one only needs to look at the transformations in the root TransformedGeode node. To describe this entity as a Mesh object in BZW format, one only needs to perform a pre-order traversal of the TransformedGeode tree and record the material reference and the geometric data as it appears (note that dynamic colors and texture matrices can be applied through referencing a material). Also, to describe more advanced mesh features like LODs, one would define multiple object trees (one for each LOD) and specify one to be the rootNode in bz2object when rendering. In fact, this task is made easier in that OSG now provides a LOD class that implements the same functionality as BZFlag's LOD system. In this new proposed architecture, I would create a new object type BZWB_LOD, which inherits from OSG's LOD class and from BZWB_Geode, and it would be used in place of a BZWB_Geode wherever needed. This way, bz2object still has access to the geometric data of LOD nodes in the scenegraph, and BZWB_LOD would implement functionality to write itself in BZW format when it comes time to save the scene. BZWB_LOD would borrow components from the existing LOD and LODCommand classes to facilitate its creation.

The main advantage to these changes is that BZW 2.0 objects that inherit from this new design only need to know how to set up their default geometries and materials. When their constructors are called, they can construct their TransformedGeode tree accordingly. BZW 1.x objects that have no concept of a material or a maluable geometry so they can supply their TransformedGeode trees pre-determined textures, materials, and geometries listed in a file (or hard-coded). This architecture is much closer to how BZFlag represents its 3D data; the only significant difference is that BZFlag's scenegraph nodes need to know how to draw themselves, but in this case, each node only needs to know how to insert its data into the scenegraph so the scenegraph library can render it later.

I.2 OSG/BZWB Integration[edit]

Currently BZWB is rather heavily intertwined with OSG. This is problematic in that a large feature change on OSG would break BZWB. One solution is to subclass the necessary objects used in OSG into BZWB-specific objects (which interface with OSG). This way, in the event of a feature change in OSG, these subclasses would be the only components of BZWB that would need to be fixed (the rest of BZWB would be unharmed, since the interaction with OSG would be isolated). Since the reparing of bz2object described in I.1 already requires defining a new class BZWB_Geode for providing a means of getting/setting/rendering geometric data, isolation OSG/BZWB integration will be a natural consequence.

Ideally, BZWB will define its own primitive types--points, faces, triangle strips, quad strips, lines, line strips, triangle fans, normals, etc. BZWB_Geode will provide methods for inserting these primitive types into the scenegraph, and reading them back out. This way, BZWB_Geode abstracts the low-level OSG APIs into a facade interface specific to BZWB (and with less complexity). More ideally, there could exist a class that implemented all of these methods statically, and BZWB_Geode (and other classes) could invoke them as needed.

I.3 Zones, Weapons, and Groups[edit]

I didn't have a chance to implement the graphical representation of these last year (although they can be parsed and written correctly in BZWB). However, using the new bz2object class, it will be trivial to implement them as renderable entities within BZWB's scenegraph (especially since weapons only need to deal with rotation and translation, zones only have to deal with translation, and neither need to remember their geometries outside of the running instance of BZWB). A weapon will be represented with a pre-defined avatar like the one proposed in www.u.arizona.edu/~jnelson/bzworkbench.html, and a zone will be rendered as a wall-like structure determined at run-time, like in the original proposal.

Groups will be more challenging when it comes to transformations. In BZFlag, BZW 1.x objects like box and pyramid maintain their global orientation when their group is transformed. It appears that the only thing that happens to them is that they become translated. In implementation, a Group will maintain references to the root TransformedGeodes of each object it contains (or rather, the objects listed in the corresponding "define"), such that each root TransformedGeode is contained within one additional transformation which is the inverse transformation of the Group's orientation. For example, if one were to group two boxes, the Group would create two transformations to contain the root TransformedGeodes of the boxes (call these the root transformations) within the scenegraph. If one were to apply a transformation to the group, say, a 45-degree rotation about they Y axis, the Group must apply a -45 degree rotation to its two root transformations to keep the two boxes in an "upright" orientation (since that is how BZFlag will render them). Moreover, the Group must be able to distinguish between BZW 1.x and BZW 2.x objects, since this restriction does not apply to BZW 2.x objects. This can be done by looking at the object's name. These root transformations will not be recored--they are only for usability's sake. Additionally, when scaling a Group, the Group itself must not scale--the root transformations themselves must have the scale transformation applied to them (so their child objects scale). All this will be considered when finishing the Group class.

I.4 Dynamic Rendering[edit]

One problem I didn't fully anticipate last summer was that dynamic colors, texture matrices, and meshes that supply an "angvel" descriptor should be rendered dynamically, since they appear dynamically in BZFlag (i.e. they change on each frame). Currently, BZWB only updates the scene when it processes user input or refreshes the window, to save CPU cycles. This needs to change. In implementation, dynamic scene elements could be modified to have a callback function that causes them to modify their scenegraph data for the next frame. They could be subclassed from a simple DynamicSceneElement interface that provides the callback, for example (for example's sake, let's call the callback updateDynamicContent()). BZWB's Model class can be modified to keep tabs within the object registry of which ones are dynamic (and need to be updated). BZWB's main loop can be modified to call a global updateScene() method 15-30 times a second that will look through the Model's dynamic objects and tell them to update their scenegraph data in preparation for the next frame. If there are no dynamic objects, then the method will do nothing to save CPU power (this feature should be a global option to enable--not all users will want the scene to appear dynamic if they have a slow computer).

The procedure for updating a mesh with a non-zero "angvel" descriptor would entail encapsulating the mesh's rootNode member within an OSG transformation node that is updated slightly each time the mesh's updateDynamicContent() method is called. This transformation will be specific to the Mesh object definition, and will not be recorded when saving a Mesh (but it will be generated dynamically in the event that it is given an "angvel" descriptor).

The procedure for updating a dynamic color will be slightly more complicated. The current dynamicColor class will need to be modified to parse its color variation descriptors so as to compute the color for the next frame. Also, OSG does not have a concept of dynamic colors that I am aware of--geometry coloration is stored in an array of per-vertex (or per-primitive) colors within osg::Geometry (and, consequently, in BZWB_Geode). Thus, dynamic color simulation for mesh-based objects that exhibit per-face dynamic colors will entail modifying the mesh's BZWB_Geodes' per-face color arrays on each frame. Emulating a dynamic color should happen through the bz2object class, as part of its updateDynamicContent() method (since it will entail modifying bz2object's private geometry data). In implementation, dynamicColor can compile its descriptors into a format usable by the bz2object to which it is applied, so the bz2object can read its data to compute the state of its geometry for the next frame.

The procedure for updating a texture matrix will be similar to updating a dynamic color--instead of updating per-face colors on each frame, a texture matrix update will cause each face assigned to it to update its texture coodrinates according to the final matrix definition (i.e. each texture matrix will need to be modified to keep track of exactly what texture modifications it will do, once all "resetmat" descriptors have been considered). In implementation, the texturematrix class will be modified to compile its matrix into a form usable by bz2object, so bz2object can compute the state of its texture data for the next frame.

I.5 Mesh Loading[edit]

After the Mesh object is supported, BZWB should be able to import meshes at least statically. To do this, it must support at least one format. Thus far, it supports .obj and .osg, but it would be nice if it also suppoted additional formats. BZWB should at least provide a mesh-loading interface for future extension, and should be able to tell by file extension which loader to use.

The mesh-loading interface could be calle BZWB_MeshLoader. The base class would provide simple file I/O methods (i.e. open the file, read its contents into RAM, free the contents upon termination, etc), as well as a pure virtual method that returns a BZWB_Geode from the file's data. This functionality would be implemented by subclasses of BZWB_MeshLoader.

II. Usability Limitations of BZWorkbench[edit]

II.1 Non-Standard 3D Object Manipulation[edit]

When developing BZWB last summer, usability took a remote second place to feature-completeness. Now that BZWB is more evolved, usability becomes a bigger issue, especially 3D object manipulation. Fortunately for us, OSG currently provides a standard 3D scene manipulation API within the osgManipulator namespace. The current selection system and manipulator widget should be modified to obtain their functionality from the OSG's osgManipulator API, since it is far more feature-complete, will save time, and is closer in behavior to standard 3D editors. It will support translation along the three cartesian axes as well as free translation (i.e. parallel to the screen), scaling along one or two dimensions, and cylindrical and spherical rotation (the current system only supports translation along one axis, scaling along one dimension, and a non-standard cylindrical rotation).

II.2 Mouse Mappings (thanks to JeffM)[edit]

Since I developed BZWB on a laptop originally, I was not concerned with using a "true" mouse. Consequently, some mouse click mappings are out of place. Here are the proposed changes:

Left-Click

  • do nothing

Double Left-Click

  • select an object in the scene if the double-left-click occured on that object

Middle-Click

  • do nothing (at the moment)

Left-Click + Drag

  • If objects were not selected, then create a bounding box on the screen to select objects in the scene.
  • If objects were selected already, and if the user clicked on the center of the manipulator widget, then drag the object with the mouse, parallel to the screen.
  • If objects were selected already, and if the user clicked on an axis of the manipulator widget, then...
    • If the user clicked on the translation widget of the axis, then translate the object along that axis.
    • If the user clicked on the rotation widget of the axis, then rotate the object about that axis.
    • If the user clicked on the scale widget of the axis, then scale the object along that axis.

Scroll

  • zoom in/out

Right-Click

  • If an object is selected, pull up a menu about that object. Otherwise, bring up a menu to add/remove objects from the scene

Right-Click + Drag

  • rotate the scene about its geometric center (as if the scene was inside a trackball)

Ctrl + Right-Click + Drag

  • move the geometric of the scene (i.e. pan across the scene parallel to the screen).

II.3 Main Window UI[edit]

Last year I spent most of my time considering the user interfaces of the various dialog boxes. They are still available at www.u.arizona.edu/~jnelson/bzworkbench.html. The UI componets that are finished are faithful to the original designs, but one UI element that was not considered last year was the main window. Currently, the main window supports only one 3D viewport, and has a slew of buttons to add objects on the left-most panel. It also has a primitive menu bar at the top of the screen. Adding objects can also be done by right-clicking the scene and selecting an object to be added. This UI was improvised over the summer of 2007, and it has room for improvement.

First, the pictures on the buttons are too small, cannot scale, and are fully shaded (making it difficult for a visually-impared person to see). The images on the buttons must only portray the object they wish to add, and even then, it shouldn't be a full rendering of one. Rather, it should be an abstract glyph representing the object (i.e. a wire-frame cube for a box, a wire-frame pyramid for a pyramid, an arc-chape for an arch, a sphere-shape for a sphere, etc), since those convey meaning just as well and are more readily recognized. Second, the pictures should be located in a horizontal bar beneath the menu but above the scene. The first buttons (from left to right) should be for classic objects (box, pyramid, teleporter, base, in that order), and the buttons following should be the buttons for newer objects. The weapon button should look like a BZFlag tank shot, and the Zone button should look like a group of BZFlag flags. The meshbox and meshpyr buttons must be distinct from their older counterparts--the meshbox button should be altered to look like a shaded-in cube (instead of a wire-frame cube), and the meshpyr button should be a shaded-in, but abstract pyramid.

II.4 Undo/Redo[edit]

BZWB does not currently support undo/redo operations. It can be modified, however, to log actions the user takes. The creation of a BZWB_Action class is needed--it shall be the base class of all user-defined actions BZWB can take. It will contain the name of the action, a pure virtual function for undo-ing itself, a pure virtual function for re-doing itself, and possibly a timestamp. It can be subclassed into more specific actions, such as an action to add an object, an action to transform an object, etc; each sub-class will contain the necessary data to know the operation the user performed, and will implement the undo/redo functionality (i.e. a subclass that describes an object being added to the scene will store a reference to that object, so it can be removed by an Undo command later or re-added by a Redo command, for example).

The responsibility for managing BZWB_Action instances will be assumed by another new class--BZWB_Command. It will be instantiated in the View class, and will contain a circular buffer of BZWB_Action instances that represent actions carried out by the user. Undoing an action involves popping a BZWB_Action instance from the circular buffer and calling its Undo command, and then pushing an Undo action to the circular buffer, detailing the BZWB_Action that was un-done. Redoing an action involves peeking the circular buffer for an Undo action. If the most recent command is not an Undo action, then there is ambiguity as to which action is to be redone, and nothing will happen. If the most recent command IS an Undo action, however, then redoing a command involves extracting the BZWB_Action that was undone in the Undo action, and calling its Redo method.

II.5 Pre-determined Camera Angles[edit]

Once nice feature of Blender that I like is the ability to set the camera at a pre-defined location, such as looking down one of the global axes. This will not be difficult to implement in BZWB--a similar menu can be added to the menu bar (or possibly a secondary menu-bar beneath the scene) whose elements have call-backs to the View class to re-program the camera's location and viewpoint.

II.6 Multiple Viewports[edit]

Another nice feature of Blender is the ability to open multiple viewports of the scene. The MainWindow class can be modified to have multiple instances of the View class, each placed at different areas in the primary window and each operating independently of one another. The View class will have to be modified, however, will have to delegate the responsibility of the selection widget to the MainWindow class, since each View cannot have its own selection (View's selection methods really just call the Model's selection methods, and there is only one Model instance per BZWB instance).

II.7 Native Widget Set[edit]

FLTK, while having little overhead and a simple API, is ugly and does not blend in well with any of the three major target OS's. FLTK will have to be replaced with wxWidgets, which renders widgets native to the target OS and has a compatible license. It's license is similar to the LGPL, with the exception that binary-only distributions of derived works can have different licensing terms. Since BZWB is LGPL'ed anyway, this slight discrepancy should not be a problem.

If two people end up working on BZWB, it is unlikely that the person working on implementing the rest of the BZW 2.x objects' dialog boxes will also be working on this problem. The completion of this task must be a top priority, so the developer working on Section I can create the dialog boxes using wxWidgets directly.

II.8 Saving Selections for Future Use[edit]

One feature advertised last year that I still stand by is the ability to save selections from the current map to a BZW file to be imported into future scenes (i.e. to avoid duplication of effort). The Model class can be modified to save the current selection to a BZW file trivially, and can be modified to load objects from a BZW file into the scene without much effort as well.

III. Timeline[edit]

As mentioned before, BZWB is ideally a two-person project--one person should work on the problems listed in Section I, and the other should work on the problems listed in Section II. Since I am the original developer of BZWB, I would prefer to focus on Section I. If time permits, I can work on Section II.

III.1 Proposed Timeline for Section I[edit]

June 31st: bz2object will have been re-worked to behave more like BZFlag's map objects. It should implement a geometry manipulation API, and should provide all the necessary functionality for creating and representing newer BZW 2.x objects. At this time, the Mesh object should be somewhat supported (as well as arc, cone, sphere, tetrahedron, meshpyr, and meshbox). The corresponding dialog boxes for these objects will have been added as well. In the process of fixing bz2object, a BZWB-specific scenegraph manipulation API should be partially finished in BZWB_Geode.

July 20th: Weapon, Group, and Zone will have been added, along with their dialog boxes.

August 15th: Dynamic Rendering should be working, following the completion of the BZWB scenegraph manipulation API (to modify the coloration and texture-coordinates of geometric primitives).

August 31st: Dialog boxes for adding and configuring materials, dynamic colors, texture matrices, and physics drivers will be complete in time for a BZWB release.

III.2 Proposed Timeline for Section II[edit]

June 15th: Section II.7 must be complete in time for the completion of arc, cone, sphere, tetrahedron, meshpyr, and meshbox dialog boxes using wxWidgets.

June 30th: Section II.1 must be completed for testing out the new objects finished by this time in Section I.

July 15th: Sections II.2, II.8, and II.5 should be completed--they will not be difficult.

July 30th: Section II.3 should be completed.

August 31st: Section II.4 should be completed. II.6 is optional, but it would be nice.