Skip to content

WIP: Instance drawing #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fefe3b0
Added necessary types for XPLMInstance and created XPlaneInstance. St…
Nov 9, 2018
eb051e1
Merge branch 'master' into InstanceDrawing
Nov 13, 2018
0eaf212
Add back XPInstance and the factory from the XPSceneryObject
Nov 14, 2018
2c8f513
Make InstanceDrawing working, including the test harness.
Nov 21, 2018
829c8aa
Merge branch 'upstream/master' into InstanceDrawing
Nov 21, 2018
aeb99ca
Clean up the GraphicsTestPlugin. Add xpnetcfg.json to .gitignore
Nov 21, 2018
6f905fb
Trying to figure out how to pass a reference to DrawInfos
Nov 24, 2018
b64b2d7
Fix the drawing an object at multiple locations
Nov 25, 2018
30df4cb
Rename and move some structures that directly map to the X-Plane API.
Nov 25, 2018
9cf6c1d
Improve documentatino for XPDrawInfo
Nov 28, 2018
c2fe9b2
Document XPProbeResult enum
Nov 28, 2018
6a80edd
Add documentation to XPDrawingPhase
Nov 28, 2018
410fcee
Cosmetic change
Nov 28, 2018
becea96
Remove unintended line break
Nov 28, 2018
dead143
Cleaned up opening brackets of some blocks according to coding standard
Nov 28, 2018
e03c9bb
Clean up opening brackets for functions according to coding standard
Dec 1, 2018
007afe0
- Add Instance API to the available APIs
Dec 2, 2018
ec81927
For instance creation, change IEnumerable<string> for datarefs to str…
Dec 2, 2018
4801011
Instantiate 10 instances of the same object instead of just one instance
Dec 2, 2018
784a86e
Add documentation to XPlaneInstance
Dec 2, 2018
dd1de73
Remove some unnecessary comments and do cosmetic changes
Dec 2, 2018
ee20683
Add documentation to XPlaneScenery
Dec 6, 2018
8e3107c
Merge remote-tracking branch 'upstream/master' into InstanceDrawing
Dec 6, 2018
fd7f955
Merge branch 'master' into InstanceDrawing
mbrachner May 4, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ PublishProfiles
plugin
package
.DS_Store
*.user
*.user
/XPNetPluginTestHost/xpnetcfg.json
41 changes: 41 additions & 0 deletions XPLMTestHarness/XPLMTestHarness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "XPLMScenery.h"
#include "XPLMGraphics.h"
#include <map>
#include <list>
#include <tuple>
#include <vector>
#include <string>
Expand Down Expand Up @@ -118,6 +119,9 @@ static commandmap configuredCommands;
static map<XPLMFlightLoop_f, flightloop> registeredFlightLoops;
static map<tuple<XPLMDrawCallback_f, XPLMDrawingPhase, int>, drawcallback> registeredDrawCallbacks;

static unsigned int instanceCounter = 0;
static map<XPLMInstanceRef, std::list<string>> registeredInstances;

template <typename T>
void SetDataRef(const string name, const T& value, datarefmap<T>& container)
{
Expand Down Expand Up @@ -608,6 +612,43 @@ XPLM_API void XPLMLocalToWorld(
*outLatitude = inZ;
}

XPLM_API XPLMInstanceRef XPLMCreateInstance(XPLMObjectRef obj, const char ** datarefs)
{
instanceCounter++;
std::cout << "XPLMTestHarness: Creating instance for object " << obj << std::endl;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would prefer to have the test harness just store and provide data, without logging to the console. The data can then be verified/asserted/logged either over in the test plugin (C#) or in XPNetPluginTestHost.cpp. If you didn't notice, there were no log messages here in the harness prior to your push last month. One of the directions I want to move with XPNet is to turn the test host into an actual automated unit test suite, which would mean that the places where it currently says, "look for log message XYZ to see if the test succeeded" would turn into tests that are checked for pass/fail. In preparation for that, all of the test output was isolated to XPNetPluginTestHost.cpp, and the harness here just tried/tries to be a simple stand-in for X-Plane. It's OK to do testing by checking logging output currently (that's how the other tests work now as well), but the logging and checking should be done in the test plugin or the test host, not the test harness, to reduce the amount of rework we'll have to do to make those tests automated. (If you're not sure how to do what I'm talking about, or are unfamiliar with automated unit testing, let me know and we'll discuss).

unsigned int i = 0;
std::list<string> dRefList;
for (i = 0; datarefs[i] != NULL; i++) {
std::cout << "XPLMTestHarness: DataRef " << datarefs[i] << std::endl;
dRefList.push_back(datarefs[i]);
}
XPLMInstanceRef instRef = reinterpret_cast<XPLMInstanceRef>(static_cast<uintptr_t>(instanceCounter));
registeredInstances.emplace(instRef, dRefList);

return instRef;
}

XPLM_API void XPLMDestroyInstance(XPLMInstanceRef instance)
{
std::cout << "XPLMTestHarness: Destroying instance " << instance << std::endl;
registeredInstances.erase(instance);
}

XPLM_API void XPLMInstanceSetPosition(XPLMInstanceRef instance, const XPLMDrawInfo_t * new_position, const float * data)
{
std::cout << "XPLMTestHarness: Instance set position for instance " << instance << std::endl;
auto it = registeredInstances.find(instance);
if (it != registeredInstances.end()) {
auto datarefs = it->second;
unsigned int i = 0;
for (string dref : datarefs) {
std::cout << "XPLMTestHarness: DataRef " << dref << " set to value " << data[i++] << std::endl;
}
}
}



XPLM_API void XPHarnessInvokeFlightLoop(float elapsedSinceLastCall, float elapsedTimeSinceLastFlightLoop, int counter)
{
// Before invoking clean up all the unregistered flight loops.
Expand Down
1 change: 1 addition & 0 deletions XPLMTestHarness/XPLMTestHarness.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "XPLMDataAccess.h"
#include "XPLMProcessing.h"
#include "XPLMDisplay.h"
#include "XPLMInstance.h"
#include "XPLMUtilities.h"

typedef enum {
Expand Down
54 changes: 54 additions & 0 deletions XPNet.CLR/Instance/XPlaneInstance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace XPNet
{
public interface IXPlaneInstance
{
void CreateInstance();
void DestroyInstance();
}

internal class XPlaneInstance : IXPlaneInstance
{
public void CreateInstance()
{
throw new NotImplementedException();
}

public void DestroyInstance()
{
throw new NotImplementedException();
}
}

public interface IXPInstance : IDisposable
{
void SetPosition(XPLMDrawInfo_t xPLMDrawInfo_t, IEnumerable<float> v);
}

internal unsafe class XPInstance : IXPInstance
{
readonly void* m_instanceRef;

public XPInstance(void* instanceRef)
{
m_instanceRef = instanceRef;
}

public void Dispose()
{
PluginBridge.ApiFunctions.XPLMDestroyInstance(m_instanceRef);
}

public void SetPosition(XPLMDrawInfo_t xPLMDrawInfo_t, IEnumerable<float> v)
{
var floatArray = v.ToArray();
fixed (float* p = &floatArray[0])
{
PluginBridge.ApiFunctions.XPLMInstanceSetPosition(m_instanceRef, xPLMDrawInfo_t, p);
}
}
}
}
34 changes: 33 additions & 1 deletion XPNet.CLR/Plugin/XPNetPluginBridge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,26 @@ internal unsafe delegate int XPLMLocalToWorld
);

#endregion X-Plane Graphics API
#region X-Plane Instance API
internal unsafe delegate void* XPLMCreateInstance
(
void* inObj,
[MarshalAs(UnmanagedType.LPArray,
ArraySubType=UnmanagedType.LPStr)] string[] inDataRefs
);

internal unsafe delegate void XPLMDestroyInstance
(
void* instance
);

internal unsafe delegate void XPLMInstanceSetPosition
(
void* instance,
XPLMDrawInfo_t inNewPosition,
float* data
);
#endregion X-Plane Instance API

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 2)]
public struct ApiFunctionPointers
Expand Down Expand Up @@ -779,6 +799,10 @@ public struct ApiFunctionPointers
internal IntPtr XPLMWorldToLocal;
internal IntPtr XPLMLocalToWorld;

// Instance
internal IntPtr XPLMCreateInstance;
internal IntPtr XPLMDestroyInstance;
internal IntPtr XPLMInstanceSetPosition;
}

public unsafe struct ApiFunctions
Expand Down Expand Up @@ -833,10 +857,14 @@ public ApiFunctions(ApiFunctionPointers p)
XPLMUnloadObject = Marshal.GetDelegateForFunctionPointer<XPLMUnloadObject>(p.XPLMUnloadObject);
XPLMLookupObjects = Marshal.GetDelegateForFunctionPointer<XPLMLookupObjects>(p.XPLMLookupObjects);


// Graphics
XPLMWorldToLocal = Marshal.GetDelegateForFunctionPointer<XPLMWorldToLocal>(p.XPLMWorldToLocal);
XPLMLocalToWorld = Marshal.GetDelegateForFunctionPointer<XPLMLocalToWorld>(p.XPLMLocalToWorld);

// InstanceDrawing
XPLMCreateInstance = Marshal.GetDelegateForFunctionPointer<XPLMCreateInstance>(p.XPLMCreateInstance);
XPLMDestroyInstance = Marshal.GetDelegateForFunctionPointer<XPLMDestroyInstance>(p.XPLMDestroyInstance);
XPLMInstanceSetPosition = Marshal.GetDelegateForFunctionPointer<XPLMInstanceSetPosition>(p.XPLMInstanceSetPosition);
}

// Data
Expand Down Expand Up @@ -889,6 +917,10 @@ public ApiFunctions(ApiFunctionPointers p)
internal XPLMWorldToLocal XPLMWorldToLocal;
internal XPLMLocalToWorld XPLMLocalToWorld;

// Instance
internal XPLMCreateInstance XPLMCreateInstance;
internal XPLMDestroyInstance XPLMDestroyInstance;
internal XPLMInstanceSetPosition XPLMInstanceSetPosition;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 2)]
Expand Down
10 changes: 10 additions & 0 deletions XPNet.CLR/Scenery/XPlaneScenery.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace XPNet
{
Expand Down Expand Up @@ -56,6 +57,7 @@ unsafe void Enumerator(string inFilePath, void* inRef)
public interface IXPSceneryObject : IDisposable
{
void Draw(int lighting, int earthRelativ, XPLMDrawInfo_t[] drawInfos);
IXPInstance CreateInstance(IEnumerable<string> inDataRefs);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you mentioned in your comments on the pull request, my general intent is to map each X-Plane top-level functional area to a top-level class in XPNet, to make it easy to use the X-Plane SDK docs as a general guide to how to use XPNet. Not that we map everything perfectly item-for-item, but rather that, if the X-Plane SDK says that there's something called the Instance API that you can call CreateInstance on, that you could make a pretty good guess that there'll be something in XPNet called Instance that you can call Create() or CreateInstance() on. Putting it on Scenery means we'd have to somehow lead people there, and it may make it harder for us to adjust if X-Plane adds major new features to their Instance API in the future (it's pretty tiny right now and kinda seems to go with Scenery, but who's to say that it will be forever - I'm sure they separated it for a reason).

So where in X-Plane you have XPLMInstance and XPLMCreateInstance, in XPNet you'd have m_api.Instance.Create(). (Not CreateInstance b/c it's an Object-Oriented API, and you already called the function on an object named Instance, so you don't need to repeat the word Instance twice).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I can do that. Just one thought, if we create the XPInstance via the scenery object, we would have the chance to keep track of the created instances and automatically dispose them with the object. That would speak for the current approach. Would that change your opinion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is another problem here, where the XPSceneryObject.CreateInstance would be a much more elegant solution: The XPSceneryObject holds a private reference to the SceneryObject (m_objectRef). So, if we have a Instance.Create(xpSceneryObject, dataRefs) in the API, how do we get that reference to call XPLMCreateInstance(m_objectRef, ...)? Making the field that holds this reference internal instead does not work, because we expect the interface IXPSceneryObject in Instance.Create, I guess we still need to use the interface, and we probably do not want to make it public. If we would stay with XPSceneryObject.CreateInstance, we have this reference easily available. Any ideas?

}

internal unsafe class XPSceneryObject : IXPSceneryObject
Expand All @@ -71,6 +73,14 @@ public void Dispose()
{
PluginBridge.ApiFunctions.XPLMUnloadObject(m_objectRef);
}
public IXPInstance CreateInstance(IEnumerable<string> inDataRefs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at some examples of how XPLMCreateInstance is called, it may be useful for our callers for this to either look like Create(params string[] data) or to at least have an override that looks like that. Then you can write something like this:

var inst = m_api.Instance.Create(
"sim/graphics/animation/ground_traffic/tire_steer_deg_a",
"sim/graphics/animation/ground_traffic/tire_steer_deg_b",
"sim/graphics/animation/ground_traffic/tire_steer_deg_c"
);

I.e., then the caller doesn't even have to deal with making an array/list for simple cases. The compiler will treat this as an array allocation so there'd be no performance penalty to doing that.

{
List<string> dataRefList = inDataRefs.ToList();
dataRefList.Add(null);
string[] dataRefArray = dataRefList.ToArray();
var instanceRef = PluginBridge.ApiFunctions.XPLMCreateInstance(m_objectRef, dataRefArray);
return new XPInstance(instanceRef);
}

public void Draw(int lighting, int earthRelative, XPLMDrawInfo_t[] drawInfos)
{
Expand Down
73 changes: 55 additions & 18 deletions XPNet.GraphicsTestPlugin/GraphicsTestPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace XPNet
{
/// <summary>
/// This is a testbed for getting the graphics going.
/// This is a testbed for checking graphics functions
/// </summary>
[XPlanePlugin(
name: "GraphicsTestPlugin",
Expand All @@ -14,20 +14,22 @@ namespace XPNet
public class GraphicsTestPlugin : IXPlanePlugin
{
private const string TUGPATH = "lib/airport/vehicles/pushback/tug.obj";
//private const string TUGPATH = "lib/airport/vehicles/pushback/*.obj";
private readonly IXPlaneApi m_api;
private readonly IXPProbe m_probe;
private readonly IXPDrawingLoopHook m_drawingLoopHook;
private IXPFlightLoopHook m_flightLoopHook;
private IXPSceneryObject testTug;
private float m_tireAngle = 0;
private IXPFlightLoopHook m_firstFlightLoopHook;
private IXPFlightLoopHook m_tireTurning;
private IXPSceneryObject m_testTug;
private IXPInstance m_testTugInstance;

public GraphicsTestPlugin(IXPlaneApi api)
{
m_api = api ?? throw new ArgumentNullException(nameof(api));

m_api.Log.Log("GraphicsTestPlugin: Displaytest started");
m_drawingLoopHook = m_api.Display.RegisterDrawHook(DrawingHook, XPLMDrawingPhase.xplm_Phase_Airplanes, 0);
m_flightLoopHook = m_api.Processing.RegisterFlightLoopHook(FlightLoopTime.FromCycles(1), SimLoaded);
m_firstFlightLoopHook = m_api.Processing.RegisterFlightLoopHook(FlightLoopTime.FromCycles(1), SimLoaded);
m_api.Log.Log("GraphicsTestPlugin: And now create a probe");
m_probe = m_api.Scenery.CreateProbe();
m_api.Log.Log("GraphicsTestPlugin: Probe created");
Expand All @@ -36,13 +38,11 @@ public GraphicsTestPlugin(IXPlaneApi api)
public void Dispose()
{
// Clean up whatever we attached / registered for / etc.

m_drawingLoopHook.Dispose();
testTug.Dispose();
m_probe.Dispose();

if (m_flightLoopHook != null)
m_flightLoopHook.Dispose();
m_drawingLoopHook?.Dispose();
m_testTug?.Dispose();
m_tireTurning?.Dispose();
m_probe?.Dispose();
m_firstFlightLoopHook?.Dispose();
}

/// <summary>
Expand All @@ -54,21 +54,56 @@ public void Dispose()
/// </summary>
private FlightLoopTime SimLoaded(TimeSpan elapsedTimeSinceLastCall, TimeSpan elapsedTimeSinceLastFlightLoop, int counter)
{
m_api.Log.Log($"GraphicsTestPlugin: Loading objects with path {TUGPATH}");
m_api.Log.Log($"GraphicsTestPlugin: Entered SimLoaded flightloop (just called once)");

m_api.Log.Log($"GraphicsTestPlugin: Loading objects with path {TUGPATH}");
var tugs = m_api.Scenery.LookupObjects(TUGPATH, 0, 0);
foreach (var p in tugs)
m_api.Log.Log($"GraphicsTestPlugin: Filename: {p}");

testTug = m_api.Scenery.LoadObject(tugs.First());
m_api.Log.Log($"GraphicsTestPlugin: Loaded and still living, reference is {testTug}");
m_testTug = m_api.Scenery.LoadObject(tugs.First());
m_testTugInstance = m_testTug.CreateInstance(new string[]
{
"sim/graphics/animation/ground_traffic/tire_steer_deg"
});

m_tireTurning = m_api.Processing.RegisterFlightLoopHook(FlightLoopTime.FromCycles(1), TurnTheWheel);

m_flightLoopHook.Dispose();
m_flightLoopHook = null;
m_api.Log.Log($"Loaded and still living, reference is {m_testTug}");
m_firstFlightLoopHook.Dispose();
m_firstFlightLoopHook = null;
m_api.Log.Log($"GraphicsTestPlugin: Leaving SimLoaded flightloop");

return FlightLoopTime.Unscheduled;
}

private FlightLoopTime TurnTheWheel(TimeSpan elapsedTimeSinceLastCall, TimeSpan elapsedTimeSinceLastFlightLoop, int counter)
{
m_api.Log.Log($"GraphicsTestPlugin: Entering TurnTheWheel flightloop");
var (x, y, z) = m_api.Graphics.WorldToLocal(47.437644, 19.259498, 0);
var res = m_probe.ProbeTerrainXYZ((float)x, 0, (float)z);
m_api.Log.Log($"Probed terrain, got result {res.LocationY} with code {res.Result}");

var (lat, lon, alt) = m_api.Graphics.LocalToWorld(res.LocationX, res.LocationY, res.LocationZ);

m_testTugInstance.SetPosition(new XPLMDrawInfo_t((float)res.LocationX, (float)res.LocationY, (float)res.LocationZ, (float)0, (float)0, (float)0),
new float[] { m_tireAngle });

m_tireAngle++;
if (m_tireAngle > 45)
{
m_tireAngle -= 90;
}
m_api.Log.Log($"GraphicsTestPlugin: Leaving TurnTheWheel flightloop");

return FlightLoopTime.FromCycles(1);
}

/// <summary>
/// This hook is called for drawing an object. Be aware that this
/// is a deprecated way of drawing, but we still support it as long
/// as the SDK supports it.
/// </summary>
private int DrawingHook(XPLMDrawingPhase inPhase, int inIsBefore)
{
m_api.Log.Log("GraphicsTestPlugin: Entering drawing hook");
Expand All @@ -79,7 +114,9 @@ private int DrawingHook(XPLMDrawingPhase inPhase, int inIsBefore)

var (lat, lon, alt) = m_api.Graphics.LocalToWorld(res.LocationX, res.LocationY, res.LocationZ);

testTug.Draw(0, 0, new XPLMDrawInfo_t[] { new XPLMDrawInfo_t(res.LocationX, res.LocationY, res.LocationZ, 0.0f, 0.0f, 0.0f) });
m_testTug.Draw(0, 0, new XPLMDrawInfo_t[] { new XPLMDrawInfo_t(res.LocationX, res.LocationY, res.LocationZ, 0.0f, 0.0f, 0.0f) });

m_api.Log.Log("GraphicsTestPlugin: Leaving drawing hook");
return 1;
}

Expand Down
18 changes: 16 additions & 2 deletions XPNet.Native/XPNetPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ typedef int(*PXPLMLookupObjects)(const char*, float, float, XPLMLibraryEnumerato
typedef void(*PXPLMWorldToLocal)(double, double, double, double*, double*, double*);
typedef void(*PXPLMLocalToWorld)(double, double, double, double*, double*, double*);

// Instance - X-Plane API Function Pointer Types
typedef XPLMInstanceRef(*PXPLMCreateInstance)(XPLMObjectRef, const char**);
typedef void(*PXPLMDestroyInstance)(XPLMInstanceRef);
typedef void(*PXPLMInstanceSetPosition)(XPLMInstanceRef, const XPLMDrawInfo_t*, const float*);

// Function types for calling into XPNet.PluginBridge in the CLR.
typedef int (STDMETHODCALLTYPE *PXPluginStart)(void*, void*);
typedef void (STDMETHODCALLTYPE *PXPluginStop)();
Expand Down Expand Up @@ -142,11 +147,15 @@ typedef struct
PXPLMUnloadObject XPLMUnloadObject;
PXPLMLookupObjects XPLMLookupObjects;


// Graphics
PXPLMWorldToLocal XPLMWorldToLocal;
PXPLMLocalToWorld XPLMLocalToWorld;

// Instance
PXPLMCreateInstance XPLMCreateInstance;
PXPLMDestroyInstance XPLMDestroyInstance;
PXPLMInstanceSetPosition XPLMInstanceSetPosition;

} ApiFunctions;


Expand Down Expand Up @@ -298,7 +307,12 @@ XPNETPLUGIN_API int XPluginStart(char* outName, char* outSig, char* outDesc)

// Graphics
XPLMWorldToLocal,
XPLMLocalToWorld
XPLMLocalToWorld,

// Instance
XPLMCreateInstance,
XPLMDestroyInstance,
XPLMInstanceSetPosition
};

auto ret = ClrPluginStart(&sp, &api);
Expand Down
Loading