This is a compatibility wrapper for using the new XPLMInstance APIs in plugins that support old versions of X-Plane. It consists of 2 files (a .h and a .cpp)

xplm_instance.h

/**
 * This is a backward-compatibility wrapper for the new XPLM300
 * instancing APIs.
 *
 * It can be used to provide compatibility with plugins that support 
 * both X-Plane 10 and 11. X-Plane 10 plugins will not see the performance
 * benefit, of course, but this saves you from needing two separate
 * implementations.
 */

#ifndef xplm_instance_h
#define xplm_instance_h

#include <XPLMScenery.h>

typedef void *	XPLMInstanceRef;

XPLMInstanceRef XPLMCreateInstance(XPLMObjectRef obj, const char * drefs[]);
void			XPLMDestroyInstance(XPLMInstanceRef inst);

// Data is consecutive floats, one for each dataref
void			XPLMInstanceSetPosition(
								XPLMInstanceRef			inst,
								const XPLMDrawInfo_t *	position,
								const float *			data);




// These are boiler plate needed only for the 'lib' implementation of the above APIs.
void			XPLMInstanceInit();
void			XPLMInstanceCleanup();

#endif /* xplm_instance_h */

xplm_instance.cpp

#define XPLM200 1
#define XPLM210 1

#include "xplm_instance.h"
#include "XPLMDataAccess.h"
#include "XPLMDisplay.h"

#include <vector>
#include <algorithm>
using std::vector;
using std::find;

struct	xplm_instance_t {

	XPLMObjectRef				model;
	vector<XPLMDataRef>			datarefs;
	
	XPLMDrawInfo_t				position;
	vector<float>				data;
	
};

static vector<xplm_instance_t *>	s_instances;

XPLMInstanceRef XPLMCreateInstance(XPLMObjectRef obj, const char * drefs[])
{
	xplm_instance_t * i = new xplm_instance_t;
	i->model = obj;
	if(drefs)
	{
		while(*drefs)
		{
			XPLMDataRef	r = XPLMFindDataRef(*drefs);
			i->datarefs.push_back(r);
			i->data.push_back(0);
			
			++drefs;
		}
	}
	memset(&i->position,0,sizeof(i->position));
	s_instances.push_back(i);
	return i;
}

void			XPLMDestroyInstance(XPLMInstanceRef i)
{
	xplm_instance_t * ii = (xplm_instance_t *) i;
	
	vector<xplm_instance_t *>::iterator it = find(s_instances.begin(), s_instances.end(), ii);
	if(it != s_instances.end())
		s_instances.erase(it);
	delete ii;
}

void			XPLMInstanceSetPosition(
								XPLMInstanceRef			instance,
								const XPLMDrawInfo_t *	position,
								const float *			data)
{
	xplm_instance_t * i = (xplm_instance_t *) instance;
	if(data && !i->data.empty())
	{
		memcpy(&i->data[0], data, i->data.size() * sizeof(float));
	}
	memcpy(&i->position, position, sizeof(i->position));
}

static int draw_cb(
                                   XPLMDrawingPhase     inPhase,    
                                   int                  inIsBefore,    
                                   void *               inRefcon)
{
	static XPLMDataRef draw_type = XPLMFindDataRef("sim/graphics/view/plane_render_type");
	
	if(!draw_type || XPLMGetDatai(draw_type) == 1)
	{
		for(vector<xplm_instance_t *>::iterator it = s_instances.begin(); it != s_instances.end(); ++it)
		{
			xplm_instance_t * i = *it;
			for(int d = 0; d < i->data.size(); ++d)
				XPLMSetDataf(i->datarefs[d],i->data[d]);
			XPLMDrawObjects(i->model, 1, &i->position, 1, 0);
		}
	}
	return 1;
}

void			XPLMInstanceInit()
{
	XPLMRegisterDrawCallback(draw_cb, xplm_Phase_Airplanes, 0, NULL);
}

void			XPLMInstanceCleanup()
{
	XPLMUnregisterDrawCallback(draw_cb, xplm_Phase_Airplanes, 0, NULL);
}

10 comments on “X-Plane 10 Instancing Compatibility Wrapper

  1. I am not criticizing, but the code is so pre C++11 times 🙂
    Since you defined the new OSs and Compilers as your base line, whty not use “auto” in your examples, can me easier and cleaner. Something like:

    Instead of:
    for(vector::iterator it = s_instances.begin(); it != s_instances.end(); ++it)
    {
    xplm_instance_t * i = *it;
    for(int d = 0; d data.size(); ++d)
    XPLMSetDataf(i->datarefs[d],i->data[d]);
    XPLMDrawObjects(i->model, 1, &i->position, 1, 0);
    }

    We could write:
    for(auto it : s_instances )
    {
    xplm_instance_t * i = *it; (not sure if you need the “*” in this case)
    for(int d = 0; d data.size(); ++d)
    XPLMSetDataf(i->datarefs[d],i->data[d]);
    XPLMDrawObjects(i->model, 1, &i->position, 1, 0);
    }

    I believe we should let the new compilers “work” for us too. I mean I hate these all iterator overhead…

    Cheers and keep the great work
    Saar

  2. Tyler & Ben: Will there need to be a similar compatibility layer added for things like drawing on the 3D cockpit, namely the panel.png as SASL is frequently used? I see the potential for lots of products breaking if this gets gnarly as Vulkan/Metal are integrated.

    Or is it possible that there will be a compatibility layer added within the SDK libraries themselves so that OpenGL calls are automatically translated on build, allowing current drawing routines to remain valid throughout the transistion?

    1. Drawing to the _panel texture_ (which ends up in the 3-d cockpit) is 2-d drawing to X-Plane, so we are hoping to wrap it in compatibility, just like 2-d window overlays.

      The main issue is that we don’t see a way to share _depth buffer_ information between any of the modern drivers (metal, vulkan) and legacy GL, and this means we can’t correctly integrate 3-d plugin drawing with our content.

      Our current expectation is to run a GL context and composite 2-d plugin content in place; we are not planning to ‘intercept’ the calls. But if an OpenGL 2.x or 3.x layer on top of Vulkan or Metal became available we could run plugins on top of that. Molten-GL has the problem of beign payware and (more importantly) GL ES 2.0, while plugins can basically assume full GL 2.1 on desktop (and 3.x + compatibility on windows).

  3. Hi,

    I need some clarification regarding Instancing:
    1. The instancing example, not the wrapper, is only good for LR libraries that are probably being loads on X-Plane start up and used by LR team. Is this true or am I missing something (can we do the same?)

    2. The only way to load custom 3D Objects is just through the Wrapper, I’ll appreciate if there is other option. I mean it is not just for compatibility staff, it should be the only way to load custom 3D Objects in xp11.10 when using instancing, so why use the term “wrapper” ?

    3. On X-Plane 10 and I believe 11 prior to 11.10 to draw 3D Objects we had to call XPLMWorldToLocal when certain events occur, like: “XPLM_MSG_SCENERY_LOADED” so 3D Objects will keep their visibility.
    When I use instancing, I do not use “draw call backs” anymore, and it seem that the call to XPLMWorldToLocal is not needed in the case of “XPLM_MSG_SCENERY_LOADED”.
    If that is true, any other hidden benefits ?

    4. If I want to show/hide an instanced object, should I only use: “XPLMInstanceSetPosition” and “XPLMDestroyInstance”. I mean in X-Plane 10 if I wanted the “cool” 3D Objects that rendered far away from plane, I just skip them in the “draw” call back.
    In XP 11.10 there is no such option. So, from performance perspective, should I keep an instanced object all the time without “cooling” logic? is there no performance price to code “position”/”destroy”/”position” repeatedly ?

    Thanks
    Saar

    1. First, let’s be totally clear. The point of this code sample is NOT to show you how to use the new XPLMInstancing API. It is to give you utility code to replicate the new SDK function in OLDER versions of the SDK, so that you can write a plugin using the new API and have some compatibility with XP10 and XP11.05. With that in mind:

      1. This doesn’t make any sense. You can load ANY OBJ you want at any time – you are responsible for providing a loaded object to XPLMCreateInstance, and you would get it fro XPLMLoadObject[Async] – any physical or virtual library path is fair game.

      2. This doesn’t make sense to me either – I think you’re confused about the point of this code. The only future-proof way to draw objects is via the XPLMInstancing API – the old way (calling XPLMDrawObject from a draw callback) is going to be incompatible with future drawing APIs like Vulkan and Metal.

      So you should rework your code to use the new instancing API, and use these utility functions if you need 11.05 and earlier compatibility.

      If there is a use case for XPMDrawObject that is not covered by this API, we’d like to know what you were doing, but I fear it was probably illegal.

      3. This is still necessary – since your coordinates are in OpenGL coordinates, you have to reset your position when the coordinate system changes. This isn’t fantastic, and we may revisit it. (For dynamic objects, you’re going to have to cope with shifts because you’re telling us the position a lot. For relatively static objects it would be nice if the SDK would rotate it for you. But if you are going to update the object, that’s wasted work.

      4. You can use ATTR_hide or XPLMInstanceSetPosition. Generally you should destroy the instance if it’s going away for a while, but move it ‘far away’ if you need a high frequency effect.

      What are you doing that requires you to show/hide the object so much? Note that X-Plane will skip drawing if it detects that the object is off-screen – you get this ‘for free’.

      1. Hi Ben,

        Thanks for the clarifications, here are some explanations from my side:
        1 and 2. When I tried “instancing” I started with the original example: “https://developer.x-plane.com/code-sample/instanced-drawing-sample/”.
        Unfortunately I failed to load “physical” files from HDD.
        This is when I tried the code in this page, but I was surprised it was a set of functions and not a full plugin.
        Once I understood it was a snippet of code from “XPLMInstance.h” which is part of XPSDK300 version I was able to load my custom object or any object file.
        This is why I wrote that I do not understand why the name holds wrapper in it since it is the only correct way to load a 3D Object and create an instance in XP11, I neglected XP10.

        3. Regarding coordination location refresh. I currently use static objects that are placed in certain location. I expected that if My plane will be position far away (more than 1000nm) from the object and then I position back in same place I’ll have to re-calculate local world coordination.
        The tests that I did prove me wrong, the 3D Objects where visible in the exact same place.
        I haven’t conducted other tests, but I remembered from XP10, If a 3D Object was located in adjacent DSF, and there was a scenery load, I had to re-calculate the 3D Object local coordinate, or it will disappear.
        I might need more test to prove my point, but so far it just works without recalculation.
        I’ll update if I will prove my self wrong.

        4. Regarding hiding an Object – this is only for cooling reasons. Back in XP10 I gave a designer the option to place a simple 3D Object in the XP World. This was useful when a mission designer wanted to use “show/hide” object to drive the mission.
        Other reasons to use show/hide:
        1. Save drawing if plane is far away, or at certain distance (mission designer decide).
        If I understand correctly, it is automatic when using instancing ?
        2. Part of mission, example: a passenger that waits for a plane(show), and passenger get on a plane(hide).

        My rule for this feature was: if you can’t do it from WED, you should try “dynamic 3D Objects”.
        My intension’s are the same for XP11.
        So, there are not many “show/hide” actions and it is only for specific driven ones.

        My next implementation will probably be – moving objects. In this case, I’ll have to re-position the instance every frame to make the “movement”. Of-course I prefer to use XPSDK for this task, but as I understand from your answer, it is not implemented.

        Last, I have no idea how ATTR_hide is manipulated from a plugin and I’ll consider it if it is simple to implement.

        Thanks
        Saar

        1. You don’t need to hide the object for performance reasons – we do that.
          If a script is going to show and hide the object, I’d suggest positioning, not deleting the instance, because the script could “thrash” it. But it’s a hard call – if the script hides it permanently a delete would be ideal.
          The coordinate changes happen at specific times – you have to monitor the lat_ref and lon_ref datarefs to detect jumps. IF the coordinate system jumps back to how it was before, your old coordinates may seem okay. But you can definitely be at one airport in two different coordinate systems depending on flight direction.

Leave a Reply

Your email address will not be published. Required fields are marked *

Please do not report bugs in the blog comments.
Only bugs reported via the X-Plane Bug Reporter are tracked.