Approaching Final Review
August 19, 2007 at 7:31 am | In Uncategorized | 3 CommentsLet me say this: This was the best summer I have ever had. I got to spend ALL of it doing what I love–programming, and getting paid for it to boot
. I got to enhance my knowledge of the C++ language, flesh out my fledgling OO-programming skills from the previous school-year, learn how to use a scenegraph API, learn how to (truly) use a widget set (I don’t really count Swing as a true widget set, even though I know how to use it), develop the largest single application I have ever created (~20,000 SLoC and counting, for whatever that’s worth), and work with the outstanding development team and community of one of the longest-standing 3D games ever made. August 31st can come and go; I’ll still be working on BZWB simply because developing it has been such a pleasure.
That said, here’s what is stable and complete enough to actually USE in BZWB:
- BZW 1.7.x support (boxes, pyramids, teleporters, water level, options, world size)
- 3D object manipulation
- Object plug-in system
- .bzw import and export (even for unsupported data)
- basic editor features (cut, copy, paste, duplicate, delete, new, open, save, save as, etc.)
- object transformations (shift, shear, spin, scale)
- material references
- simple materials (texture, addtexture, ambient, diffuse, specular, emission, shininess)
- Cones (mostly to test and implement BZW 2.o features; other BZW 2.0 objects can be easily added).
Here’s what I’m going to focus on until the end of GSoC:
- define/group (the code back-end is mostly there; rendering is what’s needed most)
- teleporter links (the code back-end is already there, as well as most of the geometry code. They just don’t render.)
- better material support (won’t get completely done, but commonly used keys like resetmat, notextures, notexcolor, notexalpha, color [i.e. keywords for color] might. More esoteric keys like spheremap, occluder, etc. have textual support, but not rendering support).
- physics drivers (after getting materials to work, this will be CAKE).
- improved 3D cursor rendering
- material creation/configuration dialogs (already started)
- zone (haven’t started; don’t know what to expect just yet)
Here’s what probably won’t get done by August 31st:
- texturematrix and dynamicColor (they can be parsed and saved, but not properly handled and rendered)
- full physics driver configuration dialog box (we’ll see how it goes; maybe I’ll get to it)
- arc, sphere, tetra, weapon, meshbox, meshpyr, mesh (there’s a reason I’m putting these off; keep reading)
- infinite undo/redo logs (possible to add; it’ll just take a while)
- tank ruler (not as important as the aforementioned features; easily added, however)
I’ve talked this over with my mentor, and we both agree: it is more important that I focus on the editor itself right now rather than object support. Mainly, configuration systems, usability, and more esoteric BZW objects that cannot be directly rendered or have no atomic manifestation (material, dynamicColor, phydrv, texturematrix, define, group, zone). As such, one of the features I’ve been working on as of late is a good source-level plug-in system. BZWB’s data model infrastructure has a mini-API to register and unregister object support.
To support an object, all that the model needs are these:
- the object’s header
- the object’s hierarchy (if it contains sub-objects)
- the object’s footer (usually this is “end”)
- a static initializer, which in implementation is a pointer to a method that takes a reference to a string (if the string has any data, it will be the object’s BZW representation) as it’s sole argument and returns a pointer to a new DataEntry (usually by calling the object’s constructor).
- a static configuration dialog generator, which in implementation is a pointer to a method that takes a pointer to a DataEntry (the object in question) and returns a pointer to a new ConfigurationDialog (the base class for all configuration dialogs in BZWB). The pointer to the new ConfigurationDialog is, in implementation, passed to an instance of the Master Configuration Dialog, and is activated when the user clicks “More…”
Every object in BZWB has thus far been implemented as a plug-in, and in the main() method an appropriate call to Model::registerObject() is made to add support for the said object BEFORE anything else is done (this is because before the Model class can begin loading and exporting worlds, it needs to know how to parse objects). In the near future, I’ll create a file in the root directory (plugins.cxx, or something) where all the registration happens, so object developers can easily add support for any objects of theirs.
To implement an object, you need to create a class that ultimately extends DataEntry. However, all objects that are rendered and support transformation keywords (position, size, rotation, spin, shift, shear, scale) out of the box need to extend a different class: bz2object. The whole point of me working on materials, physics drivers, defines/groups, water levels, etc. instead of spheres, arcs, tetras, meshes, etc. was because the former classes do NOT extend bz2object and are handled completely differently (and are often dependent on knowing the internals of BZWB), whereas the latter objects can easily be created by 3rd party developers, with little knowledge of the internals of BZWB. And after the 31st, it will be permissible for other developers to start contributing code to my project. My hopes are that once the 31st has passed, BZW 2.0 object support will become available faster than I myself could have implemented it because others will be able to help me. THAT is why I’m focusing on the editor-specific features–THOSE features I can implement faster than others, and these last few days would be better spent on improving editor features rather than object features.
Make no mistake, BZWB will become the ultimate BZFlag world editor. It will just take more time than allotted by GSoC, but I have every intention of continuing work on the project after GSoC ends. Believe me, I LOVE working with the BZFlag community on this
BZWParser: the BZW text-parsing API
August 9, 2007 at 7:36 am | In Uncategorized | Leave a CommentThe power behind the DataEntry class and all derived classes is the ability to parse BZW-formatted text. Fortunately, BZWB employs an API for doing just that. Eventually, this class will become a separate library for any apps to use.
////////////////////////////////////////////////////////////////////////////////////
class BZWParser {
public:
// unused
BZWParser() { }
virtual ~BZWParser() { }
// init method to get Model reference
// this MUST be called BEFORE any other method
static void init( Model* model ) { _modelRef = model; }
// simplest method: get a value from a non-repeatable key in a line
static string value(const char* key, const char* text);
// simpler method: get the key from a line
static string key(const char* text);
// get the terminator token of a key
static string terminatorOf(const char* key);
// get the header of a chunk of BZW text
static string headerOf(const char* text);
// get the hierarchy of an object
static string hierarchyOf(const char* text);
// get the individual lines out of a section
static vector<string> getLines(const char* header, const char* section);
// get all lines in a section that begin with the given key
static vector<string> getLinesByKey(const char* key, const char* header, const char* section);
// chunk of a long string of text into an array of sections
static const vector<string> getSections(const char* text);
// find sections starting with a particular header from a vector of sections
// (meant to be used in conjunction with getSections()
static const vector<string> findSections(const char* header, vector<string>& sections);
// get all sections from a chunk of text that start with header
static const vector<string> getSectionsByHeader(const char* header, const char* text);
static const vector<string> getSectionsByHeader(const char* header, const char* text, const char* footer);
// use this for sections with subobjects
static const vector<string> getSectionsByHeader(const char* header, const char* text, const char* footer, const char* internalSectionKeys, const char* sectionsToIgnore);
// get all values from a key in a section
static vector<string> getValuesByKey(const char* key, const char* header, const char* section);
// get lines starting with a key from a set of keys keys in a section, preserving the order
static vector<string> getLinesByKeys(vector<string> keys, const char* header, const char* section);
// get all values from a line
static vector<string> getLineElements(const char* line);
static vector<string> getLineElements(const char* line, int count);
// the big tamale: the top-level file loader
static vector<string> loadFile(const char* filename);
private:
static Model* _modelRef;
};
///////////////////////////////////////////////////////////////////////
As you can see, all methods in this class are static. DataEntry includes the header file containing this class, so any derived classes can call these methods statically at any time. Let’s have a looksie:
There are no constructors for this class; its methods are designed to be accessed statically. Instead, however, the init() method must be called prior to any other methods. The parameter of init() is a pointer to a Model class, which among other things keeps a pseudo registry of objects and how they can be identified, as well as methods used by BZWParser to determine the footer line of an object from its header, to determine which if any sub-objects might appear, to determine hierarchies of possible collections of objects, etc. Basically, BZWParser is completely ignorant of the types of objects BZWB supports, and won’t work without access to a Model (this is intentional, because it allows BZWParser to work with ANY type of object, even ones in future releases).
key() and value() are the bread and butter of BZW parsing. key(), as the comment suggests, takes a BZW key/value pair (i.e. one line of BZW text) and returns the key. value() does the opposite: given a key (i.e. “position”, “rotation”, “size”, etc) and a line of BZW text, it will return the value paired with that key.
The methods terminatorOf(), headerOf(), and heirarchyOf() are dependent on the existence of a Model, because they determine the basic structure of a BZW object. terminatorOf() takes an object header such as “box”, “pyramid”, “teleporter”, etc. and returns the token that terminates the object. This is usually “end”, but in the case of “face” this will be “endface” and in the case of “define” this will be “enddef”. headerOf() takes a header line and returns the name of the object. This is useful in objects like groups and teleporters, where additional data is stored after the object name on the same line. heirarchyOf() is a more complex beast–it takes an object header and returns a BZWB-formatted string that describes the hierarchy of that object. Normally, this is just a zero-length string, because most objects have no sub-objects and thus no hierarchy. The two exceptions are “mesh” and “define”. The hierarchy string format is as follows:
<object_name:<subobject_1><subobject_2>…<subobject_n>><subobject_1:<subsubobject_1>…>
For example, the hierarchy of “mesh” is
<mesh:<face><drawinfo>><drawinfo:<lod>><lod:<matref>>
because the “mesh” object contains both “face” and “drawinfo” sub-objects, “drawinfo” conains the “lod” sub-object, and “lod” contains the “matref” sub-object.
The getLines(), getLinesByKey(), getValuesByKey(), and getLinesByKeys() are probably the most widely used methods in terms of number of calls in BZWB–they are used to extract values from lines. The getLines() method takes as its first argument an object header, and as its second argument a chunk of BZW text containing exactly one BZW object of that type (the string can contain additional objects, even additional objects of that type, but only the first occurrence will be used). It returns a vector of strings (in the order in which they appear), each of which contains a key/value pair from the object. If there were no lines to be parsed, it returns an empty vector.
getLinesByKey() is a more useful version of getLines()–it takes a key in addition to a header and a chunk of text, and returns all key/value pairs that contain the key in the order in which they appear. Like with getLines(), getLinesByKey() will only look at the first occurrence (if any) of an object with the given header. If no lines match the key, getLinesByKey() returns an empty vector.
getValuesByKey() is an even more powerful version of getLinesByKey()–instead of returning a vector of string/value pairs, it returns a vector of just the values that correspond to the given key in the order in which they appear.
getLinesByKeys() is also a more useful form of getLinesByKey(), but instead of passing a single key, you pass a vector of keys. getLinesByKeys() returns a vector of key/value pairs which contain one of the given keys. This method is extremely useful when parsing objects where the order in which multiple objects appear is relevant (i.e. in meshes, the order in which “matref” and “face” keys appear determines which faces receive which materials; the mesh class uses this method to determine which materials refer to which faces).
BZWParser provides the methods findSections(), getSections(), and three types of getSectionsByHeader() for parsing entire objects (sections) from a BZW file. findSections() takes an object header and a vector of strings, where each string contains a BZW-formatted object definition. It will return a vector of strings (where each string is a BZW-formatted object definition) which have headers that match the given header. This method isn’t usually called within a DataEntry-derived class, since DataEntry-derived classes almost always will deal with just one object definition.
getSections() is a more general form of find sections: it takes multi-object chunk of BZW text and breaks it up into a vector of strings containing individual objects. This is also rarely called from DataEntry-derived objects.
getSectionsByHeader(), however, is somewhat more important. The first incarnation takes an object header and a chunk of BZW-formatted text, and returns a vector of strings containing the BZW-formatted object definitions with the given header. This is called by every DataEntry-derived class during the update(const string&) method to strip out relevant object(s) from the given string. If no objects are found, a vector containing the pre-defined string BZW_NOT_FOUND will be returned. This method should NOT be used with objects that have sub-objects.
The second incarnation of getSectionsByHeader() is a bit more specific–it takes an object terminator (the footer line; usually “end”) along with a header and a chunk of BZW text. It does the exact same thing as the first incarnation; in fact, the first incarnation calls this method and uses it’s internal Model reference to look up the terminator using the given header.
The third incarnation of getSectionsByHeader() is the general case of the method–unlike the first two methods, this one handles objects with complex hierarchies. It is advisable to call the first two methods wherever appropriate, because they are much faster. Like the second incarnation, this method takes an object header, a chunk of BZW text, and a terminator, but also takes a BZWB-formatted keystring containing the headers of all DIRECT sub-objects you wish for the method to parse and a BZWB-formatted keystring containing the headers of any possible DIRECT sub-objects you wish to ignore. The format of these last two arguments is “<key1><key2>…<keyN>”. The purpose of telling this method to ignore certain sub-objects is important when a key can have multiple meanings depending on its location in the hierarchy (i.e. “matref” in “mesh” can either reference a material for faces or it can designate the start of low-level geometric primitives, depending on where it’s at), so as to only focus on occurrences of that key within a certain scope. Also, passing a list of keys to ignore allows you to use this method to look specifically at different scopes. For example, here’s an excerpt from mesh.cpp:
////////////////////////////////////////////////////////
int mesh::update(string& data) {
const char* header = this->getHeader().c_str();
// get lines
vector<string> lines = BZWParser::getSectionsByHeader(header, data.c_str(), “end”, “<drawinfo><face>”, “”);
// get the lines without the drawinfo (so we don’t mistake drawinfo’s data with the global data)
vector<string> linesNoDrawInfo = BZWParser::getSectionsByHeader(header, data.c_str(), “end”, “<drawinfo><face>”, “<drawinfo>”);
// get lines without faces and without drawinfo (so bz2object doesn’t think there are multiple physics drivers)
vector<string> linesNoSubobjects = BZWParser::getSectionsByHeader(header, data.c_str(), “end”, “<drawinfo><face>”, “<drawinfo><face>”);
// break if there are none
if(lines[0] == BZW_NOT_FOUND) {
printf(“mesh not found\n”);
return 0;
}
// break if too many
if(!hasOnlyOne(lines, header))
return 0;
// get the data
const char* meshData = lines[0].c_str();
// get drawinfo-less data
const char* meshDataNoDrawInfo = meshData;
if(linesNoDrawInfo.size() > 0)
meshDataNoDrawInfo = linesNoDrawInfo[0].c_str();
// get no-subobject data
const char* meshDataNoSubobjects = meshData;
if(linesNoSubobjects.size() > 0)
meshDataNoSubobjects = linesNoSubobjects[0].c_str();
// get the vertices
vector<string> vertexVals = BZWParser::getValuesByKey(“vertex”, header, meshDataNoDrawInfo);
// get the texcoords
vector<string> texCoordVals = BZWParser::getValuesByKey(“texcoord”, header, meshDataNoDrawInfo);
// get faces
vector<string> faceVals = BZWParser::getSectionsByHeader(“face”, meshDataNoDrawInfo, “endface”);
/* …some more code… */
// get drawinfo
vector<string> drawInfoVals = BZWParser::getSectionsByHeader(“drawinfo”, meshData);
if(drawInfoVals.size() > 1) {
printf(“mesh::update(): Error! Defined \”drawinfo\” %d times\n”, drawInfoVals.size());
return 0;
}
/* …some more code… */
// parse drawinfo (DrawInfo is considered to be a separate object in BZWB)
DrawInfo drawInfoParam = DrawInfo();
bool doDrawInfo = false;
if(drawInfoVals[0] != BZW_NOT_FOUND) {
drawInfoParam = DrawInfo(drawInfoVals[0]);
doDrawInfo = true;
}
/* more code */
}
/////////////////////////////////////////////////////////////////////////
As you can see, it uses a combination of getValuesByKey() and the incarnations of getSectionsByHeader() to parse out and distinguish between the drawinfo data, the face data, and the mesh data.
The getLineElements() methods are pretty easy to understand: they take a string and break it up into a vector of contiguous substrings (i.e. strings separated by spaces, tabs, \n, and \r). For example, the string “this is a sentence”, when fed into getLineElements(), will become <”this”, “is”, “a”, “sentence”>. The first version of getLineElements() returns all substrings in the line, the second returns either all of the substrings, or ‘count’ substrings, whichever is smaller.
The last method, loadFile(), takes a filename as its argument, and returns a vector of strings, where each string contains an object definition. This is usually not called by DataEntry objects.
Next time: the Renderable class. Stay tuned
BZWB’s object base class
August 8, 2007 at 8:47 am | In Uncategorized | Leave a CommentFor any of you interested in developing additional object support and/or helping to maintain BZWB, in the next few posts I’ll be publishing some preliminary documentation on code specific to dealing with BZW objects.
Here is the base class of all BZW objects in BZWB: the DataEntry class:
//////////////////////////////////////////////////////////////////
class DataEntry {
public:
// universal getter method
virtual string get(void) = 0;
// universal setter method
virtual int update(string& data);
// universal setter method with a binary message
virtual int update(UpdateMessage& msg) { return 1; }
// universal output method
virtual string toString() {
return string(“# unknown DataEntry”);
}
// constructor to initialize the header and keys
DataEntry(const char* header, const char* keys);
// constructor to initialize the header and keys and data
DataEntry(const char* header, const char* keys, const char* data);
// destructor (does nothing)
virtual ~DataEntry() { }
// is the key supported?
bool isKey(const string& key);
// does a line have a key?
bool hasKey(string& line);
// helper method: ensures that a key occurs only once (useful for calling from update())
bool hasOnlyOne(vector<string> lines, const char* key);
// helper method: ensures that a line has the proper number of elements
bool hasNumElements(const string& data, unsigned int numElements);
// helper method: prints out unused key/value pairs
string getUnusedText() ;
// get the supported keys in the form “<key1><key2><key3>….”
string getKeyString(void) { return keys; }
// get the keys as a vector of strings, where each string is a key
vector<string> getKeys(void);
// get the header
string getHeader(void) { return header; }
// get the text
string getText() { return this->text; }
// set the header
void setHeader(const char* c) { this->header = string(c); }
// set the keys
void setKeys(const char* c) { this->keys = string(c); }
// set changed
void setChanged() { this->changed = true; }
void setChanged(bool value) { this->changed = value; }
protected:
string header;
string keys;
string text;
bool changed;
};
///////////////////////////////////////////////////////////////////////////
Let’s take a closer look at this class. It’s commonly known among map-makers that the BZW format specifies map objects as key/value pairs encapsulated within a header line containing the object type and a footer line marking the end of the object. DataEntry is designed to deal specifically with that format.
You’ll notice that both constructors have parameters for a header and a list of keys. The header argument, as the name implies, is a pointer to a C-style string (i.e. an array of chars) that spells out the type of object this is (i.e. “box”, “pyramid”, “teleporter”, “mesh”, etc) as it would appear in BZW-formatted text. The keys argument is a bit different–it contains a list of all of the keys this object supports, encapsulated within < and >. For example, the key list for “box” would be “<position><size><rotation>”. In the latter constructor, a third parameter–the data parameter–is a pointer to the textual representation in BZW format of the object. This is important for the update() methods.
Instead of having a bunch of type-specific getter and setter methods, DataEntry employs a single getter and two setter methods: get() and the two update()’s, respectively. The purpose of get() is to return all of the object’s data as a string, formatted as BZW text. In implementation, get() merely calls toString() and returns toString()’s result. toString(), as the name implies, is where the code responsible for converting any relevant data a derived class holds into a BZW-formatted string of text. As such, toString() will vary greatly between objects in implementation.
The first update() method takes only a string as an argument. It is expected that the string is the BZW-formatted representation of the object, because the purpose of the update() methods is to parse the passed string and set object-specific data from it. As such, it should also be able to catch syntax errors and blank/incomplete data strings (fortunately, BZWB has a BZW parsing API to make this task relatively easy; more on that later). The first update() method is called mainly for construction/initialization purposes. The second update() method, which takes a reference to an UpdateMessage, serves as an event-handler. The UpdateMessage class is a type-safe message class designed to pass editing messages to objects, and necessary data which which to act upon the message. Currently, these would include:
- setting the position (the new position is supplied in the message)
- moving the position by a displacement (the displacement is supplied in the message)
- setting the spin values about the X, Y, and Z axes (the spin values are supplied in the message)
- changing the spin values by a displacement (the displacement for each value are supplied in the message)
- setting the object’s scale (the scale is supplied in the message)
- scaling the object by a scale factor (the scale factor is supplied in the message)
- recomputing the object’s transformations (a vector of pointers to the new transformations is supplied in the message)
This update() method is called from the Master Configuration Dialog to pass changes on to the object it was configuring, as well as from the code that handles 3D manipulation of the 3D Blender-style cursor. How you wish to handle these messages is entirely up to you.
Since the header and key list of all objects in BZWB are vital to object processing, DataEntry provides some simple getters and setters for getting/setting the header and key string, and they’re pretty easy to understand. More important are the hasOnlyOne(), isKey(), hasKey(), and hasNumElements() methods, because these assist with parsing BZW text. isKey(), as the name implies, returns true or false as to whether or not the object supports the key it was passed. hasKey() takes a key/value pair as an argument and returns true or false as to whether or not that key/value pair has a supported key. hasOnlyOne() takes a vector of strings as its first argument (each of which is a key/value pair), and a key as its second, and returns true or false as to whether or not there is only one string with that key. This is a primitive method for catching key multiplicities or absences that are undesirable (i.e. multiple or non-existent “name” key(s)). hasNumElements() takes a key/value pair as a string as its first argument, and the number of expected line elements (i.e. contiguous substrings separated by spaces, tabs, \r, \n, etc), and returns true or false depending on whether the given line has that many elements. This is important for catching malformed lines, such as “position 0 1″.
One interesting feature about DataEntry is that it’s designed to store the BZW data it CAN’T parse as a string. This is important because if the BZW spec changes, DataEntry-derived classes can simply ignore text it doesn’t recognize without bailing. Also, when saving a world, the unused text is still written out to the file, preserving map attributes that BZWB doesn’t (yet) recognize. The method getUnusedText() returns a string containing the key/value pairs that the object doesn’t know how to interpret (i.e. lines that begin with a key that is not in the key-string).
The final two methods, the two forms of setChanged(), are in place to mark objects as having been changed. I haven’t used them in a long time, but to the extent of my knowledge setChanged() is called when the object is manipulated in the 3D scene (but not from a dialog box). They are depricated; do not use them.
Check back soon for further code documentation
.
Beta deadline pushed back
August 1, 2007 at 8:30 am | In Uncategorized | Leave a CommentRegretfully, as of August 1st, BZWB is nowhere near beta, feature-wise. The features that are implemented as of today work reasonably well; more will be added as quickly as I can. At this point, I’m just going to focus on the “ultimate” deadline of August 31st, when hopefully I can get all Priority 1 features implemented and get the editor to be reasonably stable. I have no excuse, other than I haven’t been able to code, test, and debug fast enough (but I am faster and more efficient now than I was at the start of the project
).
Because working with the BZFlag developers and community has been so much fun, I have every intention of sticking around after GSoC to continue to improve the editor. I need the experience, and the community needs an editor, so it all works out
. However, the development rate may slow down in a few weeks, because I start school in mid-August.
Thank you all for your support.
A minor setback
July 30, 2007 at 4:51 am | In Uncategorized | Leave a CommentUnfortunately, it turns out that BZW 1.x objects like boxes and pyramids render differently than I had expected–their textures tile their faces, rather than being stretched with them. Getting that to be reflected in the editor has been a long and tedious process this week, and I’m only just getting boxes to work. This means that my original goal of reaching beta by August 1st is pretty lofty; it’ll probably happen a few days to a week later if all goes well.
On the upside, the editor is BZW 1.x compliant, as far as what it can render. All four bases, boxes, pyramids, and now teleporters show up in the 3D view. There is still some work to be done with teleporters (i.e. creating the dialog boxes that allow you to link them, rendering the linkage, etc), but those will come as soon as I get the proper texturing working. From the looks of things, getting texturing to work properly is only especially difficult in BZW 1.x objects, since BZW 2.x objects are all meshes anyway, and as such more easily managed by OSG
Here are some screenies of BZWorkbench in action as of late July 29th:
As you can see, the main method of manipulation is a set of 3D axes like in Blender. Simply click and drag. If I want to move an object along the X axis, I double-click the object, and click-n-drag the X axis (and all selected objects will move along the X axis). Same goes for Y and Z. To scale objects, do the same but hold down the “S” key. The tips of the axes will be replaced by cubes to indicate you’re about to scale. To rotate objects, hold down the “R” key and click-n-drag perpendicular to the axis. The tips of the axes will be replaced by spheres; the idea of dragging perpendicular to the axes is that you’re rolling the sphere at the end like a trackball*. The 4th picture from the left is a pic of Winny’s Cornered map. That last picture is one of the texture distortion I’m trying to correct.
*Yes, I know the UI sucks right now. I’m open to any suggestions; I’m putting off changing it until most of if not all of the objects can be rendered and configured via FLTK GUIs, so you have until then to let me know
Nearing BZW 1.x compliance…
July 21, 2007 at 4:23 am | In Uncategorized | Leave a CommentSorry for the late-ish post…I haven’t had a reliable internet connection for a few days :/. Anyway, we are on schedule! BZWorkbench now supports boxes, pyramids, and all types of bases, it can load and save files using these objects, simple editing tools like “cut,” “copy,” and “paste” work properly, and objects can be individually tweaked using the Master Configuration Dialog. All I need is teleporter support, and it’ll be compliant with 1.x levels. It’s looking more and more like an editor now!
To do: Some of BZFlag’s developers have expressed distaste with the current user input scheme and menu organization…this will change soon! Also, I’ll be refactoring “material”, “dynamicColor”, and “texturematrix” to be extensions of OpenSceneGraph’s similar classes this week, making them easier to apply to objects.
mybzflag.net has posted a screenie of BZWorkbench in action…here’s the link: http://mybzflag.net/?p=61
3D Manipulation is Functional
July 10, 2007 at 9:19 pm | In Uncategorized | Leave a CommentAll the necessary code to allow objects to be manipulated in the 3D scene is functional. Although right now the user can only translate selected objects using a 3D cursor, adding scaling and rotating event handlers won’t be difficult. This week, we should see the development of a toolbar and the necessary code to invoke configuration dialogs for boxes. We should see some semblance of a functional (but simple) editor within the next 10 days. Once boxes are completely supported, I’ll add the rest of the BZW 1.0 objects, because they’ll behave in a similar fashion.
Implementing the 3D UI
July 3, 2007 at 7:17 am | In Uncategorized | Leave a CommentWhile I was able to meet the goals of last week’s to-do list, there haven’t been any commits for the last few days. This is because I am focusing on (1) solidifying the Model-View-Controller design pattern in this project, to make it easier to (2) implement a Strategy design pattern for the 3D UI to allow it to handle user input in different ways depending on how the user is currently interacting with it, which will let me (3) develop the code that will allow object manipulation in the 3D scene. Unfortunately, this all needs to be done before the next commit; otherwise there is nothing meaningful to upload (i.e. there would be a lot of unused code). Please be patient; the next commit will be worth it
.
In case you are wondering why the 2D UIs are still lacking, this is because the development methodology for this project has been changed at the recommendation of my mentor. Basically, I’m currently focusing my efforts on creating a level editor that will use only Boxes. Doing so will let me get the most of the infrastructure in place necessary for the other objects, while (a) still having something slightly useful and functional in SVN, and (b) allowing me to make necessary design changes early on without a ton of refactoring. Once things have been ironed out in the Box editor, I’ll start adding other objects. They’ll all come quickly once the infrastructure is in place
To-Do List for this Week
June 25, 2007 at 8:50 am | In Uncategorized | Leave a CommentNow that I’ve familiarized myself with OpenSceneGraph enough to load, manipulate, and render textures and meshes within FLTK, I can start writing the framework through which the Model will interact with the View. My to-do list for this week is to:
1. Get an API in place to load and store single instances of meshes and textures and distribute references to them to other objects (i.e. a data broker for the 3D scene).
2. Add a method to the simpler BZW objects to allow each one to refactor itself into an OSG node that can be added to the View.
3. Add an event handler to the View that will allow the user to select (and eventually manipulate) objects in the scene graph, using OSG’s API.
4. Add methods to add and remove objects from the scene graph and have their addition or removal reflected in the Model.
Although it’s a lofty goal, my hope is that the project will have enough features in place by my GSoC mid-summer review to allow the adventurous users to make BZW 2.0 maps with most of, if not all of the supported objects. We’ll just have to see how it goes
.
June 15th Deadline Criteria Changed
June 15, 2007 at 5:54 am | In Uncategorized | Leave a CommentThe June 15th deadline has been modified. Rather than requiring the simple UIs to be compete, they have been postponed to June 30th. Instead, the advanced features of the data model have been moved forward. So by June 15th, the entire data model and the top-level file parser should be working or near-working. Once the data model is completely in place, the UIs will follow. They probably won’t take as long as the data model to complete. The sooner I can start on the 3D scene, the more features I can add to it.
Blog at WordPress.com. | Theme: Pool by Borja Fernandez.
Entries and comments feeds.