This post is just targeted at plugin developers who are modernizing their object drawing – if you don’t write plugin code, the Cincinnati Zoo has been showing their animals on Youtube – it’ll be a lot more entertaining than this post. (An XPLMInstance cannot tunnel down two feet in fifteen seconds – one point for the zoo animals.)
XPLMInstance makes a persistent object that lives inside X-Plane that is visible in the 3-d world. It changes how you draw from “run some drawing code every frame” to “tell X-Plane that there is a thing and update its data every now and then.”
Instancing is actually a lot easier than draw callbacks! But there are two tricky gotchas:
1. You must create the custom DataRefs for your OBJ’s animation before you load the object itself with the SDK. (If the DataRefs do not exist at load time, the animations are disabled as “unresolved to any DataRef”.)
2. When you create the instance, make sure your custom DataRefs are on the list of DataRefs for that instance.
Here’s the really baffling thing: if you create the custom DataRef and then add it to the instance’s list, your DataRef callbacks will not be called.
Wha?
Here’s the trick: the DataRef you register is a global identifier, allowing the object to refer to what it wants to listen to. That’s why you have to create the DataRef – so that the identifier exists.
But when you create an instance, each instance has memory that holds a different copy of those DataRefs.
For example, let’s say you have a truck with four DataRefs, and you make five instances. X-Plane allocates 20 slots (four DataRefs times five instances) to store five copies of each DataRef’s values.
The instances never look at the DataRef itself. They only look at their local copies. That’s why when you push different data to the instance with XPLMSetInstancePosition, each instance animates with its own values – each instance looks at its own local data.
This is also why you won’t see your DataRef callbacks called (unless you use DataRefEditor or some other tool). The object rendering engine isn’t looking at the DataRefs themselves, it’s looking at the local copies.
In other words, XPLMInstance turns DataRefs from the pull model you are used to (X-Plane pulls on your read function to get the value) to a push model (you push set with XPLMSetInstancePosition into the instance’s memory).
This implies two things about your add-on:
- It doesn’t really matter what your DataRef read functions do – they can just return zero, and
- You can’t use tools like DataRefEditor or DataRefTool to debug your animations. (That didn’t work well in legacy code either, but it really won’t work now.)
If you try the obvious optimization of not creating your custom DataRefs (“hey, no one calls them”) before you create your instance, you will find that animation just stops working. This is because we need the DataRef to be that global identifier to match your instance data with the animations of the object itself.
One last note: if your old code used sim/graphics/animation/draw_object_x/y/z to determine which object was being animated (from inside a plugin “get” function) you do not need to do this anymore. Because each instance has its own local copies and your DataRef function isn’t called, this technique is obsolete.
In summary:
- You must register custom DataRefs.
- Their callbacks can just return 0 – they’ll never be called.
- Always list your custom DataRefs for animation when you create an instance.
- Do not use draw_object_x/y/z; use XPLMSetInstancePosition to create per-specific-instance animation.
Let’s see how many extra followers this will produce for the Cincinnatti Zoo..
I am in the Zzz ooo (oone)…
Ben could you give a run down on the changes to reflections and what changes on each step of the slider? also maybe before 11.50 goes live change the labels? having 2 settings both that say “medium” is pretty meaningless
may i make the suggestion of renaming the reflection levels
“very low, low, medium, high, very high, ultra” this would be in line with other games and people already know what to expect if they put the settings to that level. ie if you crank it to Ultra your going to have bad time unless you have top end hardware
I have partially unrelated question. What is the most proper way to verify that bug report was actually received through X-Plane bug reporter web page and, first of all, is there any way at all? The thing is, a couple of weeks ago I wanted to report the CTD in the XPLM related to XPLMInstance API, but I’m not 100% sure that it was actually sent.
Thus, taking into account that it might be already processed and rejected, it’s probably not the best idea to send it again (according to bug reporter instructions), so my question cound be rephrased like this: do you have any confirmed or rejected bug reports related to XPLMInstance API crashes?
Thanks in advance.
Hi Yuri,
Sigh…there isn’t really a good way right now. Jennifer tries to make sure that every single kind of bug that’s _like this (this new API that the world is going to use blows up in the new beta) gets filed and you’d get a notification of an XPD number. We prefer to err on the side of caution for things like API breakage bugs because a dev has to look at the crash.
On the other hand, we have _hundreds_ of GPU device lost bugs, and a lot of them like this:
Summary= CTD Vulkan Device Loss
Description= Crash while startup.
Steps= Sporadic at startup.
Now in the user’s defense, there IS a log.txt attached as well as some screen shots and that probably is all he knows. But if Jennifer hand-files 400 of those, she’s going to lose her mind. So they are in a folder on the ticket system so that if/when Sidney and I need to ‘find some users’ or go searching for something specific, we can find them.
For third party developers, honestly the best thing to do is to ping us on the dev-rel slack channel if you have an issue that seems to not have gotten _any_ attention after a 4-5 days. (No XPD number, no other bug fix that matches, no contact, no nothing.). If you have an XPD number and the issue is just “lost” that’s easy to look up.
Ok, got it. Slack channel is the answer and I’m pretty sure I’ve heard about it earlier, but somehow managed to forgot about this.
Thank you again.
I view my “job” is to find issues, and when I do, report them. It’s Laminar Research’s job to track those reports however they decide. If those reports disappear into a black hole, well… I did my best. I did my part.
I wish there were some kind of public feedback bug-tracking system for X-Plane. Other projects have them, but it’s really up to Laminar Research as to whether or not they decide to find value in the idea, and if they have the resources to set it up.
After second line of this post I started to watch “Fiesta the Macaw”..
Greetings to developers,
Instancing API works great. There is just a little third trick:
When loading objects with animations in it but with no ambitions to drive them yourself, do not use nullptr as the XPLMCreateInstance second parameter nor XPLMSetInstancePositron to demonstrate your intent. It will crash.
Provide array like this static const char * drefs[] = { “”, NULL }; for create instance and dummy float data for set position.
Vita
Wait, Vita, this is wrong! You do not need to provide dummy dataref names to your instance. DO NOT DO THIS. Just pass a datarer list like
const char * drefs[] = { NULL };
That is a ZERO-length list of datarefs.
Btw, this is the exact issue I’ve mentioned earlier in the topic. There’s no way to use zero-length list of datarefs and pass nullptr as datarefs values for instance position without a crash, if the instanced object actually has any dataref attached.
Using at least one dataref to control the object instance – is a workaround we’re currently doing for such objects.
You are right Ben, it works. Thank you for correction. I still have to provide a dummy float as data for XPLMInstanceSetPosition to avoid the crash.
To my excuse, the “const char * drefs[] = { “”, NULL };” idea came to me at 2:30 AM while trying to solve my problem with animated model usage.
I originally provided nullptr as input for both functions as I tried to indicate I am not interested in driving any datarefs myself. It worked well for objects without animations, but as soon as there was at least 1 animation in the model, I got a crash.
Anyway, thank you, I am happy now and so will be our virtual airline simulating HEMS in X-Plane!
Vita
Let me see if I understand: you are saying that if you pass:
const char * drefs[] = { NULL };
as your dref list, then you crash if you set the instance data to NULL, but NOT if you set the instance data to a dummy ptr?
(This wouldn’t surprise me and is not great by us – I think we are exposing the undefined behavior of memcpy.)
Passing NULL as the dataref list is not allowed – we need a null terminated list.
This works for animated objects:
mXPLInstance = XPLMCreateInstance(mpDef->XPLObject(), nullptr);
static float dummy = 0;
if (mXPLInstance)
XPLMInstanceSetPosition(mXPLInstance, &mActualInfo, &dummy);
This works too:
static const char * drefs[] = { NULL };
mXPLInstance = XPLMCreateInstance(mpDef->XPLObject(), drefs);
static float dummy = 0;
if (mXPLInstance)
XPLMInstanceSetPosition(mXPLInstance, &mActualInfo, &dummy);
This does not work for animated objects but works for objects without animation:
mXPLInstance = XPLMCreateInstance(mpDef->XPLObject(), nullptr);
if (mXPLInstance)
XPLMInstanceSetPosition(mXPLInstance, &mActualInfo, nullptr);
This does not make sense. Passing null for the dataref string list should crash 100% of the time on all platforms, regardless of the OBJ.
Anyway, during my tests I was able to replicate the crash by taking the code from Instancing Drawing example here in the Sample Code section, changing dataref list argument in XPLMCreateInstance to { NULL } and data pointer in XPLMSetInstancePosition to nullptr.
Yuri – right. I think this is a known bug – if your instance list is null you have to pass us SOME non-null ptr. This will be fixed in beta 7, but if you want to run on all eversions, pass a ptr to ad ummy float.
Thanks Ben for this post. It’s actually quite useful as I’m playing around with drawing instances at this very moment.
I have a question tho. What I would like to add a manipulator to the instanced object?
You can’t add manipulators to instanced objects. Only the cockpit object can have manipulators.
In relation to Instanced objects should the ATTR_hard work when set in the object? I’ve found it doesn’t and I’m not sure if it’s a bug or if it’s intended.
No, ATTR_hard will not work with instancing. ATtributes that require being DSF attached and ACF attached won’t work – this means solid wall, cockpit texture, hard and draped among others.