Saturday, March 06, 2010

Plugin-Drawn Objects Do Not Exist

Version 2.0 of the plugin API (available in all versions of X-Plane 9) introduces three new routines that, for the first time, allow plugins to work with OBJs.

XPLMLoadObject and XPLMUnloadObject load an OBJ file (a model mesh) into memory, and then purge it when done. That part most users understand. The routine that causes confusion is XPLMDrawObjects. When you draw an OBJ with XPLMDrawObjects, you are not creating anything long-lasting or persistent in the world. Your object will be visible for one frame on screen, and then will disappear unless your draw it again. Your object is not part of any of the physics calculations.

To put this in perspective: when you make a new window using the Widgets API, the window is persistent - it exists until (1) you delete it or (2) your plugin is unloaded for some reason. You don't have to do anything per frame to "maintain" the window - you make it and it exists.

Objects are not like that. You cannot make an object "exist" in the X-Plane world - you can only draw it once per frame using the drawing callbacks. Essentially the draw-objects API is a lower level API.

Building a Layered System

Plugins operate in a layered environment, with lower level code on the bottom and your plugin at the top. The layer stack might only be 2 layers deep (XPLM on the bottom, plugin on top), or there might be several layers. Consider XSquawkBox:
  • The UI is drawn using the XPWidgets API. The XPWidgets API gets its drawing from the XPUIGraphics API, and the XPUIGraphics API changes OpenGL state using the XPLMGraphics API. So we've built up a layered system: basic OpenGL supports drawing, drawing supports user interface, user interface supports the plugin.
  • Similarly, multiplayer is done using a library that isn't part of the basic plugin system (but is open source): libXplaneMP. So here the XPLM supports drawing airplanes, libXplaneMP uses that to create a full multiplayer API, then XSquawkBox uses it.
The alternative to a layered system would be a "monolithic" one. Under a monolithic system, the only API for airplanes would be libXplaneMP, and the only way to create user interface would be widgets. Sandy and I usually prefer the layered approach because it provides a lot more flexibility. If you like widgets, great, use them. If not, no problem - roll your own on top of XPLMDisplay.

The Plugin System Is a Foundation

When Sandy and I cannot provide all of the layers, we have a strong bias toward providing the bottom layer, for an obvious reason: if the bottom layer isn't in the plugin system, it may be impossible for anyone else to create it. So typically if we have a choice between a high level vs. low level API, we'll put the low level API in first.

This is precisely what is happening with object drawing - we have the low level API ("draw an object") but not the high level one ("create an object in the scenery system"). Since we have provided the lowest level, it is possible to code persistent objects in your plugin by layering on top of our API. By comparison, had we only provided "create an object" it would be pretty close to impossible to draw an object for one frame - if you didn't want a scenery object, the API would be inflexible and useless.


Quino said...

Hello Ben,

Just curiosity. Does X-Plane use a scenegraph (like openscenegraph)? I suppose you have written your own OSG. I used to work with OpenGL Perfomer, which had tons of useful features, and was easy for the programmer to manage the scene graph objects. I don't know if you are referring to offer in the future access to that kind of high level api, in order to manage the scenery objects.



Benjamin Supnik said...

Hi Quino,

X-Plane does have a "Home rolled" scene graph, but I use the term very loosely - it doesn't look like a scene graph - rather it is a set of optimized data structures we use to cram the scene down the GPU as fast as we possibly can.

See also Tom Forsyth's comments on the gap between formal scene graphs and what you really need to talk nice to the GPU.[[Scene%20Graphs%20-%20just%20say%20no]]

Anyway, there is zero chance of getting direct SG access (because the API is too weird for words) but it is possible that in the future the SDK might provide higher level abstractions that _leverage_ the SG for you.

For example, we could have "insert persistent scenery object" which places the obj in the scene graph so you don't have to maintain it. For VERY large numbers of unrelated objects, this might be a win. But we'd talk to the SG for you so you don't have to worry about the API being strange or unstable.

In other words, we have an SG, but it is a _low level_ API. :-)

(There is a high level wrapper around the SG but it too looks a bit weird. Basically as soon as you go from the file formats to inside the ren engine, things stop looking like the abstractions we expose fairly quickly.)

Quino said...

Thank you for the explanation, Ben. And a very good comment about SGs from Tom Forsyth.