I must preface this progress report with an apology: Doing PR work isn’t exactly my greatest strength and sometimes that means skipping it for a while and keeping the public out of the development loop. Sorry everyone! Here it is: A quick update.
As some of you may have noticed, there recently was some talk on GitHub regarding the implementation of a BitmapFont / BMFont importer. It revealed several weaknesses in the Duality Font implementation (some of which were fixed thanks to helpful contributor “mika76”) and caused a medium-sized variety of cleanup, maintenance, documentation and feature tasks. If you’re using the latest binary release of Duality, you might have already noticed that the Object Inspector now looks slightly different for imported Resources: We finally have configurable and repeatable import operations.
The Ancestral Way
Back when Duality v1.x was still around, importing Resources was crude and hacky: There was a set of importers which acted as black boxes where the editor would put in a single file on one side and get back a Resource on the other. Over time, those input files would accumulate in the Source/Media directory, which ended up being a complete mess and was regarded as temporary anyway. There was no way to let a single importer handle multiple files at once and if a source file was modified, a contrived algorithm would try to figure out which Resource it corresponded to and trigger a re-import operation.
It worked most of the time but it certainly wasn’t the cleanest solution. This had to change and fortunately, with Duality v2.0, a proper Asset Management system was introduced.
The Old Way
With the new system in v2.0, a lot had changed. Instead of discovering input files by iteratively scanning changes in a folder, user dragdrop operations would explicitly trigger an import operation that included all the dragged files at once – and still knew where it came from, so it would also know where to search for any missing puzzle pieces.
The dreaded Source/Media directory was no longer a mess, but organized by Duality to match the structure of the Resource folder: When importing image file Foo.png to create a Foo Pixmap, moving that Pixmap to the Bar folder would cause the original Foo.png to be moved into a similar Bar subfolder of Source/Media as well. With all move and rename operations being mirrored, users would simply organize both folders at once. No additional work and a big improvement from a maintenance side.
On the code side, a proper Asset Import / Export API was introduced that is used by all importer implementations to specify which input files they’re going to use and which Resources they’re going to create – as well as actually doing so. Both import and export are now two-step processes: While the preparation step asks all available importers for a suggestion how to handle the specified input data, the main import / export step will actually perform this operation. Splitting it up and re-routing it through a well-defined API allows the system to gather meta-information about the relation between input files and output Resources, as well as properly resolving conflicts between multiple importers.
The Case That Broke It Anyway
With all the new and shiny API, any amount of input files per import, metadata, what could go wrong? There is actually one case that up until now wasn’t covered – neither by the old nor the new system. Say you have a source file that you want to import into Duality, but the Resource isn’t actually interested in the full content of the input file, but a transformed subset. No problem, just strip away what isn’t needed and transform the rest so it fits the system, right? What we will get is a unidirectional process where we can generate a Resource output from a source file input, but not the other way around, because we’ve discarded a lot of information.
This can become a problem when the user imports a file and decides that he doesn’t like the results, and he’d like to tweak them. A prime example for this is importing a TrueType / .ttf font file to generate a (bitmap) Font resource in Duality: Since we’re only interested in rendered glyph images, but not in the full vector art, we will render them during the import operation and discard the original TTF data. If the user now wants to change the size of that Font, he can’t. It’s gone. The only way would be to delete the Font resource and import it again from the same source file. But even then, there was no way to specify the size in the first place, because the import operation was not a configurable process. There were no parameters.
The solution to this was essentially a giant hack: When importing a Font, it would secretly stash away the original TTF data inside the imported Resource and use that to re-render itself when required by the user. As a result, there was a rat’s tail of specialized code to deal with the implications of this – which worked well enough for no one to notice, but broke when contributor “mika76” tried to write a BMFont importer. There was no TTF data to stash away and recover later, so the editor switched the imported Font resource into readonly mode and prohibited any changes. It may sound like a tiny thing, but it highlighted a much bigger issue.
The New Way
So, let’s not drag this on any longer: Importing stuff into Duality is now a repeatable, configurable process and neither game developers nor plugin developers will have to do much for it. It’s essentially free.
When you import a source file and select the resulting Resource afterwards, you will notice that the Object Inspector now shows two sections: One for the importes Resource itself, and one for its import / export parameters. You can edit both of them as needed and, after adjusting the parameter section, export or re-import the Resource at the click of a button.
When you implement a custom importer, you will notice that the import and export environment API has been extended to allow you to access these parameters. The following is an excerpt from the updated Font importer that will notify the system of the desired parameters, retrieve them and trigger a set of events that eventually results in serializing them persistently, as well as displaying the appropriate editors in the Object Inspector:
float size = env.GetOrInitParameter(targetRef, "Size" , 16.0f );
FontStyle style = env.GetOrInitParameter(targetRef, "Style" , FontStyle.Regular);
string extendedCharSet = env.GetOrInitParameter(targetRef, "ExtendedCharSet", string.Empty );
bool antialiasing = env.GetOrInitParameter(targetRef, "AntiAlias" , true );
bool monospace = env.GetOrInitParameter(targetRef, "Monospace" , false );
With the new additions, you could probably import anything from vector art through Tiled maps to live Google spreadsheets. Let’s see how this works out.