Designing a tool: 3D Exporters
It seems like most exporters I come across are designed to export everything in the currently loaded scene in 3D Studio Max, Maya, Lightwave, and so on, but within the limits of the system the programmer designed. If anything in the scene is hidden, they don't export it, and for animation, they export based on the current setting of the time line in the user interface. Alternatively, they export the currently selected items. Some people refer to this export style as WYSIWYG exporting. It sounds like a good idea at first, and it might be great for testing your new engine. But with a little thought, it soon becomes clear that a different design would make things go smoother for everybody involved.
Let's see what some of the problems are, and the solutions that come up by applying the philosophies above.
- Make your tools show errors that can be noticed, understood and useful.
Asking an artist to look through ten thousand lines of diagnostic output, in order to find the three warnings that flew by while the data is converted is not useful. Following the "Don't force your artists to speak to a programmer" philosophy, the best way to deal with problems is to show them visually. If certain polygons are obviously wrong, then somehow mark them and show the artist.
In my tools, if certain polygons are bad, I'll still output the polygons or the rest of the model, but my file formats have an optional semi-generic error encoding system where I can put in error messages, error lines, error points, and so on. When the 3D objects are loaded up into the viewer or the game, any object that has error information can be displayed or marked in obvious colors such as flashing red.
Those objects can then be selected, and the errors specific to that object can be brought up. If the errors are visually representable, then the polygons associated with that error will also be displayed if the object is selected and a particular error highlighted. For example, an error selected in the debug menu that says "polygon has too many weights" will highlight the polygon in question in the game or previewer.
Another good idea is to have the programmer that wrote the error message put their name in the message. For example, it could state: "WARNING: <gman> found vertices with more than 4 influences". That way, the artist will immediately know who to talk to if they have a question about the message.
- Never let your tools fail.
I know some of you will disagree with this idea, but hear me out. This is a goal I tried to reach on my last game. No matter what the problem, try to output something into the game or previewer. This goes along with the philosophy of "Don't force your artists to speak to a programmer in order to work out why something doesn't work".
It's very frustrating when you can't build a level because something is broken, and even more so when the problem is unrelated to what you are currently working on. It's best to at least get the object or level working as soon as possible, even with errors or missing items. For example, if a texture path is incorrect and you can't find the source file for the texture, then use a special 'ERROR' texture and still write out the file. This will make it visually clear to the artist what is wrong, and they will most likely know how to fix it immediately, but they will also get to see their model now instead of later.
|
|
|||
|
Another example from my level building tools - if one object in the level has errors the level still gets built, but that object is just tagged as bad. There are often thousands of items in a level made by different people. If all it takes is for one of them to be bad to stop your level from loading, you are not going to be getting much work done.
|
||||
Some engines just crash if a texture is missing. Why? |
You can go into more detail still, using both warnings and errors in exported data. A warning means that the data needs to be fixed, but the object should still run. In other words, the game should feel safe to execute the A. I. code for that object without fear of crashing. An error means that the particular object is not in a state the game can use. After loading a level, all objects with warnings would be flashing and all objects with errors will just have a name displayed in their place. At that point, the artist or designer can select objects and find out the specific errors. This is much easier to fix than sifting through hundreds of lines of building diagnostics.
However, in some cases it might be appropriate to fail immediately, especially if building a level or object takes a long time. If there are things you can check for quickly that you know will cause a problem, you might want to bring those to the attention of the artist immediately. But if possible, visual evidence is almost always better than just an error message.
- Don't force your artists to remove helper objects.
Many exporters require your artists to have the data they need, and only that data, in their 3D editor. Artists are not allowed extra lights, extra models, extra cameras, and so on. But have you ever actually seen how your artists work? They need lights to light their models. They need extra models for constraints, for checking sizes, or for other references. Asking them to have to delete those every time they export is asking for trouble.
Firstly, you are making artists do this work manually every time they export. They may even have to rebuild all their lights and constraints if they accidentally save after they've deleted them for exporting. Alternatively, they may forget to remove them, export and thereby clutter the game with un-needed items. Even worse, they may accidentally make the game crash and you've got another hour or two as you try to figure out why.
Following the "make your tools support the way artists work" philosophy, it would be better to find a way to let artists specify what needs to be exported, so that you can ignore all the rest. That way your artists are unlikely to make as many mistakes. They don't need to remember to delete objects. They don't have to worry about accidentally saving after having deleted objects. They can keep all the objects, lights and cameras that make their job easier.
I'd also recommend that the artists should specify what to export, as opposed to specifying what not to export. If you choose the "not" route, then if they add something and forget to mark it to "not" be exported , you're going to end up with stuff you don't want in your data. This is worse than finding something is missing because it was not specified, since once it is specified it will never be missing again. It is usually easier to see what is missing in the game than to notice something extra that isn't supposed to be present.
- Don't force your artists to check export settings more than once.
Many exports have a gigantic amount of settings. For example, they include elements choosing whether to export vertex colors or animations; whether to save textures as filenames or re-write them; whether to save as an object or a character; what internal name to use for a character, and so on.
While these settings might be important, it's asking for trouble to ask your artists to check them every time they export the same file. Following both the "Make the tool, not the artist, do the non-artistic work" and the "make it bulletproof" philosophy, these options should be set once and saved somewhere for exporting that particular file. So, if the camera is not supposed to be exported in this file, then the next time the file is loaded and exported it will automatically not export the camera. If another file is supposed to export animations with a base node called "orcbase", then if it is loaded and exported again, it should automatically export animations as "orcbase".
Following the "make it bullet proof" rule, the artist should only have to look at settings if he or she needs to change them, and never once they are set. If they have to check them every time they export, odds are some percentage of those times they will miss checking or un-checking an option, and the coder and the artist will lose more time working things out.
- Don't use the time line as an exporter setting.
Lots of exporters export animation by looking at the current time line in the user interface. If it's set from frame 10 to frame 150, then they export 141 frames from frame 10 to frame 150. As above, this is also error-prone. Every time an artist exports, they have to remember to put the editor's UI in a certain state. If they forget to do so, then certain in-game animations will no longer work properly.
Following the "make your tools bullet proof" philosophy, it's better to have a way to specify once which frames to export. That way, artists don't have to remember to reset the time line. They know when they export, no matter what state the UI is in, they are going to get the frames they want.
An even better option would be to write a flexible specification system that allows your artists to put more than one animation in the same file. For example, your walk cycle might be frames 10 to 40, and your run cycle might be frames 100 to 130. Some artists find that method of animating much easier than forcing them to do every animation in a separate file. It also means global changes to a character, like adding a gun attachment point to the hand, will be reflected without changes needed to all the other animations.
- Don't force your artists to collapse hierarchies.
Another issue I see in lots of exporters is that they export the hierarchy in the 3D tool directly to the game hierarchy. In order to have the best speed in today's hardware, it's important to have as few separate models as possible.
Unfortunately exporters that export the hierarchy directly require the artists to take separated models, and merge them into one model. A simple example might be a static 2-wheeled cart. The artist might build it using 2 tubes for wheels, a cylinder for an axel, 6 cylinders per wheel for the spokes, and then a box minus 2 faces for the cart area. In some exporters, if those objects are not all merged into 1 model, they will be exported as 16 separate models. Most programmers don't give that a second thought; they just assume the artists will merge everything.
Once the artists merge the models, it becomes much more difficult to edit. For example, if an artist wanted to widen the cart, they would want to stretch the axel and the cart, but not the wheels or the spokes. If you forced them to collapse the model they can no longer do that easily.
"Making your tools support the way that artists work" means letting the artists leave their models in the state best suited for them to edit it to perfection, not the easiest state for a tools coder to export. If your exporter has a way to specify: "From this node down in the hierarchy, merge all children into one model", you'll allow your artists to leave their art in the best state for editing, and still get optimal models for the game.
- Don't use the hidden or frozen attribute to filter the export.
Going back to "Make your tools bullet proof", your exporter shouldn't care if something is marked as hidden or frozen. That's because your specification says what to export, regardless of whether a particular item is hidden or frozen. Like above, your artists may have hidden something to make the model easier to edit, and then saved the file. The next time the file is exported, the code should still be exporting everything required and not exporting extraneous items. Otherwise, you are going to run into time-consuming debug problems, next time you load up your level.
- Don't export function curves!
I've seen many exporters that pull function curves directly out of the 3D package. The documentation for the exporter then goes on to tell the user that, because of this *feature* the artists are only allowed to use certain kinds of animations. Does anybody see a problem here? All the interesting animation functions require features not supported by function curves. Probably the most important one is constraints. Constraints are incompatible with exporting function curves, as are expressions.
Following the "Make your tools support the way artists work" philosophy, a much better solution is to sample the output of the package in question. You can then write some curve fitting functions, if you happen to want to use function curves to compress your data. This way, your artists are free to use every tool at their disposal to make great animation, and you'll still be able to compress their results to something reasonable.
- Don't ever consider editing a middle file format.
Middle file formats are a great idea. They allow you to separate your processing tools from your exporters, and also make it possible to use different 3D packages on the same project.
|
||||
Two objects have errors in this figure. One, the sofa, had warnings. |
However, middle file formats should really be treated just like .obj or .o files in a compiled language. They are files that the build process makes that are temporary files, never to be touched by human hands. The reason? Obviously, if any changes need to be made to the 3D data, it's going to be made by the artists back in the original 3D package. Any changes applied to the middle format are going to be lost when the artist re-exports the new model.
This goes back to the goal of your tools being to help your artists make great art. If your processes involve editing the middle file formats, and that editing is lost every time your artists edit an original 3D source file, you are forcing someone to waste time manually re-tweaking the new middle file. You've just given them an incentive to not want to edit things to be better.
- Don't needlessly limit the possible applications of your exporter.
Design your exporter to handle as many cases as possible. To give you a concrete example, I once worked with an exporter that required the artists to manually export a "bind pose" for skinned characters. That one idea removed any possibility of using that exporter to export full real-time 3D cut-scenes, since a cut-scene would never have all the characters in a bind pose state. Instead, the exporter should lookup the bind pose automatically.
|
|
|||
|
Another example of this would be limiting your exporter to only exporting one item at a time. That limit would provent you from exporting multiple characters or multiple animations. If you removed that limit, your artists would be able to make real time cut-scenes with a single click export process, as well as make it easy for artists to animate throws and other multiple-character animations.
- Don't create or load bad data.
Your exporters should never create bad data, and your engine, previewer and tools should never load bad data. Obviously, if you have bugs in either, you might get bad data, but often there are other issues at play.
I put version numbers in all my data files to support the idea of making your tools bullet proof. This is done so that the art previewer and game code can check if they understand a certain type of file, and print an error on the screen if the version is out of date. The alternative is that your game loads the data, then crashes without showing a specific error, and you spend hours trying to figure out why.
There are other various ways to handle this issue. For example, at one company I worked at, the version numbers were used as part of the load path. So, for example, a version 2.1 character might come from "\gamedata\2.1\characters", where as code that needed 2.3 version characters would load them from "\gamedata\2.3\characters". Based on their version, all the tools knew where to put their results.
This method had the advantage that people using older versions of the code still had access to data that worked with their version. In turn, people using or working on newer tools and newer versions could safely build new assets, and not worry about breaking everyone. Personally I'd probably use both ideas just to be safe. But, however you do it, a similar approach will save everybody time not tracking down version issues.
In my version numbers, I have option bits - for example, a bit that says if the file is the kind of file with error information or not. I mentioned above that putting error info in the file is a good idea for development. But in the shipping product, that code and that data is a waste, so I compile it out. But I still have the code check for a version number, and that includes whether the file is a development file or a release file. Otherwise, during final bug testing it's likely that a development file is going to get accidentally used during testing. Without the check and corresponding error, there are going to be hours spent tracking down the cause of the crash.
- Don't require manual exporting.
Manual exporting breaks the advice of the one-step build. I can't believe how many teams put up with manually exporting things - it should be possible to type "make", and have all your 3D assets reprocessed from original Max/Maya/Lightwave files into the data for your game.
What happens when you don't do this? Every time you make a change to your 3D format, your artists have to manually re-export everything. When you start a new platform again, everything will have to be manually re-exported.
Yes, of course, for a previewer function you'll want a "preview" option that will export the data and pop it into your previewer. But when the data actually exports into the game, you want exporting to happen automatically every time you ask it to rebuild your data.