Project Orbiter StackEditor

And I will. As I said, about the only change to vesselscenenode would a new constructor, which I'll implement once I need it, and switching the mesh to use a pointer. If the code you're going to add involves references to the mesh, it might be smarter to make that switch now, before a lot of new code is added that will have to be changed later on. If the switch to pointers will only affect the current code, then it doesn't matter.
Thanks :thumbup:

I just pushed the most recent revision which fixed a bug where material definitions would not be read correctly. IMS modules still display strangely, but other weirdly colored meshes (MIR) are now normal.
 
I finished the backend, that is the data manager and the changes to shipyard and vesselscenenode.

Now I'm going to try to put my GUI in front of it. It will be big and uggly for the beginning, using scenario editor images. I hope I can switch this to render the images directly to texture from the meshes, although I'm not actually quite sure if this can be done offscreen in Irrlicht...

---------- Post added at 02:03 PM ---------- Previous post was at 01:11 PM ----------

Whoopsa, I just noticed another slight problem: Helpers::ReadLine tokenises only for whitespace... note that a whitespace between the parameter name and the "=" in the configs is customary, but not mandatory.
Orbiter will accept input from a config file that is for example written like "MeshName=Ninuninu", while stackeditor currently does not.

I suggest expanding the ReadLine function with a parameter than can accept a custom character set to accept for tokenisation (well, I'm already doing it on my end, really... just so you know that I'm making a few changes there too).

In effect, putting whitespace and "=" in there will return a vector with only two tokens, the parameter name and the value, so it will affect quite a bit of code.
 
I finished the backend, that is the data manager and the changes to shipyard and vesselscenenode.
Can you push it, at least part of it if it is in a non-broken state? If we push/pull more, we'll make sure we aren't working on outdated code that someone else has fixed in the meantime.

I hope I can switch this to render the images directly to texture from the meshes, although I'm not actually quite sure if this can be done offscreen in Irrlicht...
Actually, when I first envisioned this project, I was thinking of doing the same thing, rendering the images directly. You can easily create a new Irrlicht device and just call createScreenshot to generate a picture of each module. The question is whether you can create a hidden device that screenshots still work on. For example, I use a null device to find the screen size; it may be possible to use a null device to render the GUI pictures.

I suggest expanding the ReadLine function with a parameter than can accept a custom character set to accept for tokenisation (well, I'm already doing it on my end, really... just so you know that I'm making a few changes there too).
Feel free to fix or improve anything. Nice catch on the whitespace. I would also watch out for things that occur in some meshes/configs that aren't "techincally" allowed in the Orbiter standard - for example, I had to fix the mesh loader, as I found a couple meshes which ommited normal coordinates without the NONORMAL flag, even though it should be illegal.
 
Fully functional GUI is in. I start thinking that pre-loading all the meshes might not be such a great Idea after all. The loading times are a killer.

I have created Toolboxes for IMS, using nothing but the GUI tools for creating and editing them. Here's how it works:

Select toolbox from the list at the left. Double-click on image to create vessel and position, repeat ad infinitum.

Edit toolboxes: Right-click on Toolbox area to bring up context menu (toolbar must have focus first, so if the menu doesn't appear click with the left button first... Wasn't yet able to figure out how to focus when pushing the right mouse button).

Add vessel opens the file dialog, and you can add new vessels to the toolbox. Dialog will stay open after adding until you close it, to make it easier to add several vessels.
Remove vessel removes the vessel from the toolbox where the right-click occured.
Create new toolbox brings up a naming dialog to create a completely new toolbox.

That's about that so far. The docking code needs lots of work as I found out when playing with more complex constructions. I'll have to think how to best handle the loading. You'll see that the loading times currently are abysmal... :/
 
Thanks!

Again, I'll finish the docking code. I'm not sure when I'll be able to get time to program it, but I should be able to get to it some time in the next two weeks.

My plan for docking is any selected vessel selects the whole stack of vessels. Any operation (copy, move, rotate) operates on the whole stack.

Then, to undock vessels, the user would have to hit some button (shift?). When they press shift, the vessels are drawn partially-transparently, and the docking port nodes are displayed. In this mode, any clicks selects a docking port SceneNode instead of VesselSceneNodes. When a node is selected, that docking port is undocked.

The system isn't quite as slick as the KSP-way, where vessel stacks auto-undock, but KSP also locks construction into one axis (basically). We can't do that, so we'lll have to go with manual undocking.
 
Again, I'll finish the docking code. I'm not sure when I'll be able to get time to program it, but I should be able to get to it some time in the next two weeks.

Sure, no hurry. I just had the first opportunity to really play around with it, what with the GUI needing testing :lol:

Then, to undock vessels, the user would have to hit some button (shift?

Just make it a double-click ;) (though, if I remember correctly, those will need a bit of tweaking when integrating into orbiter... I'll cross that bridge when I get there). The concept sounds solid.
 
Just make it a double-click ;) (though, if I remember correctly, those will need a bit of tweaking when integrating into orbiter... I'll cross that bridge when I get there). The concept sounds solid.
Good idea on the double click.

Why will it be difficult to integrate into Orbiter? Irrlicht is independent of everything else, all we have to do to integrate into Orbiter is create a new "custom function", and call the "main" function when StackEditor is created. Then, StackEditor would pop up normally as it occurs now.

Obviously there would have to be some code to handle import/export to Orbiter, but that isn't that difficult.

Were you thinking that it should be live-editing of Orbiter vessels?

I imagined a simple import/export system. After the user activated StackEditor, the currently focused vessel/all docked vessels would be imported into StackEditor and Orbiter would be paused. After the user was finished with SE, they press "export", and the new docked vessels are imported back into Orbiter, the main SE window is closed, and Orbiter is unpaused.
 
Why will it be difficult to integrate into Orbiter? Irrlicht is independent of everything else, all we have to do to integrate into Orbiter is create a new "custom function", and call the "main" function when StackEditor is created. Then, StackEditor would pop up normally as it occurs now.

Actually it will have to run in a child thread, or communication with Orbiter will get difficult, using protocols and stuff. But that's not the problem, we can run irrlicht in a child thread and let it render into a Dialog Box within orbiter. The problem is also not really a big problem:
The eventreceiver will have to receive the events from the orbiter thread though, so you have to post them manually (I have all the code for that ready, don't worry. With the structure as it currently is, it's really just a matter of switching out main.cpp). However, last time I did this trick, there was trouble with double clicks. For some reason the event didn't get posted, I had to create the event manually by measuring the time between two mouse clicks. Which was a bit easier then since I had one global eventhandler. With the event-handling architecture of StackEditor (which is perfectly well suited for its purposes with all nodes being able to receive their own events independantly), I'm not sure yet where to move the timer. Probably right where the events are posted from the orbiter thread. I'm sure it'll be solvable, that's just one problem I had with the implementation the last time I worked with it.


I imagined a simple import/export system. After the user activated StackEditor, the currently focused vessel/all docked vessels would be imported into StackEditor and Orbiter would be paused. After the user was finished with SE, they press "export", and the new docked vessels are imported back into Orbiter, the main SE window is closed, and Orbiter is unpaused.

Interesting. I didn't think of that, but it seems like a straight forward enough solution. There's one (unimportant) detail: If you pause Orbiter you pause all child threads, so you'll have to leave the sim running, but the concept should still work splendidly.

Were you thinking that it should be live-editing of Orbiter vessels?

Yes, I was. The reason for that is that I will probably adapt the code to make an IMS2 specific editor later on that directly communicates with the IMS2 vessels inside orbiter. With that goal in mind, it simply didn't occur to me that there is a much simpler solution for what we're trying to do currently (the import-export scheme you just suggested).
 
Actually it will have to run in a child thread, or communication with Orbiter will get difficult, using protocols and stuff. But that's not the problem, we can run irrlicht in a child thread and let it render into a Dialog Box within orbiter. The problem is also not really a big problem:
The eventreceiver will have to receive the events from the orbiter thread though, so you have to post them manually (I have all the code for that ready, don't worry. With the structure as it currently is, it's really just a matter of switching out main.cpp). However, last time I did this trick, there was trouble with double clicks. For some reason the event didn't get posted, I had to create the event manually by measuring the time between two mouse clicks. Which was a bit easier then since I had one global eventhandler. With the event-handling architecture of StackEditor (which is perfectly well suited for its purposes with all nodes being able to receive their own events independantly), I'm not sure yet where to move the timer. Probably right where the events are posted from the orbiter thread. I'm sure it'll be solvable, that's just one problem I had with the implementation the last time I worked with it.

:uhh:
Why complicate things?

Just let Irrlicht run in it's own window. I just whipped together a quick integration test (the relevant changes are here), and it works fine. Just go F4->custom->stackEditor
SE_inside_orbiter.JPG


You'll have to do a git pull and then checkout the testOrbiterIntegration branch
Code:
git pull
git checkout -b testOrbiterIntegration origin/testOrbiterIntegration
As a side effect, it has my partially un-finished docking code. Also, for some reason the debugger really, really hates relative paths, so you'll have to start Orbiter manually and enable stackEditor's module.

If you just let Irrlicht make a new window, we don't have to modify any of the event handler code, nor worry about manually posting events or some of the issues you described above.

Right now, it just creates a new thread for SE and detaches it so Orbiter doesn't hang.

For future integration, we could just create a normal Orbiter module with some functions/members that SE could access. Then, we just manage multi-threading by protecting those members with CriticalSections. I imagine we would create some structs that represent docked vessels, and create a vector of these structs inside the Orbiter module. Any access to the vector is protected by CriticalSections. Then, the import function is as simple as pushing structs to the vector, and letting SE access them, then clear the vector, and the export function is as simple as having SE push structs to the vector, and the orbiter module access them and create the vessels.

As a note, let's continue developing StackEditor as a stand-alone executable at the moment (just continue working off of the develop branch, not testOrbiterIntegration), as it is easier to debug. This method of integration is simple, so we can just redo the integration when we reach a release-able point.

---------- Post added at 08:19 PM ---------- Previous post was at 07:42 PM ----------

As to the abysmal loading times, I think I have a solution. Due to the excellent way you have set up GetGlobalMesh, the solution is just to background load the meshes.

Basically, at load, split off another thread that continues to load meshes, so SE doesn't hang. Then, if the user selects a mesh that has not been background loaded, it just manually loads because of the design of GetGlobalMesh, and the user may see a slight unresponsiveness as the mesh loads. If the user selects a mesh that already has been background loaded, then it is created without any lag time.

The only caveat is that you have to again protect the vector with CriticalSections (basically just wrap all the code in GetGlobalMesh/GetGlobalConfig in the protection code), but that isn't that bad.
 
Last edited:
If you just let Irrlicht make a new window, we don't have to modify any of the event handler code, nor worry about manually posting events or some of the issues you described above.

Why, I'll be darned... I never thought it would work to actually let irrlicht spawn its own window inside orbiter, I expected context conflicts... My implementation so far was to let irrlicht use the window spawned by orbiter as a rendering surface... It looks like I'm thinking too much out of the box, ignoring the obvious, simple way :lol:

Basically, at load, split off another thread that continues to load meshes, so SE doesn't hang. Then, if the user selects a mesh that has not been background loaded, it just manually loads because of the design of GetGlobalMesh, and the user may see a slight unresponsiveness as the mesh loads. If the user selects a mesh that already has been background loaded, then it is created without any lag time.

There is a slight problem here... not pre-loading the meshes pretty much dumps the render-to-texture Idea, i.e. I'll have to stick with the scenario editor images. (I can scale them so they're not that big... can also make it user defined so everyone can have the cake he likes).
On the other hand, when I don't need the meshes preloaded for that, I might as well save memory and pre-load only configs and images, and only load meshes on demand when they are created the first time... Or do you think that would disrupt the workflow too much?
 
Last edited:
Why, I'll be darned... I never thought it would work to actually let irrlicht spawn its own window inside orbiter, I expected context conflicts... My implementation so far was to let irrlicht use the window spawned by orbiter as a rendering surface... It looks like I'm thinking too much out of the box, ignoring the obvious, simple way :lol:

Is it working with full-screen mode, too? IIRC, we had troubles with this approach in Orbiter Galaxy back then.
 
Is it working with full-screen mode, too? IIRC, we had troubles with this approach in Orbiter Galaxy back then.

No, but neither does it when rendering into an orbiter dialog box, so that's not really a drawback.
 
Last edited:
There is a slight problem here... not pre-loading the meshes pretty much dumps the render-to-texture Idea, i.e. I'll have to stick with the scenario editor images.
What about caching those images? What if on first-run, it pre-loads all of the meshes and renders out the meshes to some place, like Images/StackEditor/IMS/. This wouldn't be that bad because it is only done once (or when more meshes are added for the additional meshes), especially if a loading bar is displayed to the user. After first-run, meshes could be background loaded.

On the other hand, when I don't need the meshes preloaded for that, I might as well save memory and pre-load only configs and images, and only load meshes on demand when they are created the first time... Or do you think that would disrupt the workflow too much?
Depends if they are using modules beyond SSBB/IMS. For example, I noticed a significant (3-5 second) delay when loading the ProjectAlpha_ISS mesh in my old implementation. Obviously, not all vessels are that detailed, but that lag time would be annoying for some high-resolution meshes to load on demand.
 
Last edited:
What about caching those images? What if on first-run, it pre-loads all of the meshes and renders out the meshes to some place, like Images/StackEditor/IMS/.

Nice idea!

For example, I noticed a significant (3-5 second) delay when loading the ProjectAlpha_ISS mesh in my old implementation. Obviously, not all vessels are that detailed, but that lag time would be annoying for some high-resolution meshes to load on demand.

Yeah, that's to be expected for large meshes. I do wonder a bit what someone might want to do with a bunch of these in stack editor, though. Still, probably better to plan for every case.

Now, if only I wouldn't suck so much at multi-threading... :lol:

---------- Post added at 10:55 PM ---------- Previous post was at 05:10 PM ----------

I did a bit of groundwork, implementing a config file where people can configure their screen resolution. I also retooled the toolboxes a bit, so a "toolbox set" is now stored in its own subfolder, and the toolbox set to load can also be set in the config file.

Most important addition is probably the log file, though. A simple call to Helpers::writeToLog() will now dump a message there. I have added several logings to the data manager to diagnose common problems, maybe putting some into the meshloader could help finding the problem with the weird appearing meshes...

Oh yes, the data manager now also cleans up after itself, as one should be able to expect from something that calls itself "data manager" :P
 
Ok, some updates after the short hiatus.

I added some of the docking code, but I am stuck at the moment. I converted all the shipyard code over to using a VesselStack and added some functions, and a selected stack moves nicely together as a single entity.

However, I'm stuck with rotating a stack. The current code, which just rotates all of the individual vessels in the stack equally, obviously does not work. What is the best way to go about rotating the stack? I was considering something around the lines of calculating the geometric center of the stack (using the bounding boxes), and using some polar system to rotate each around the point. Any other ideas?

As such, the "snapping" code to a docking port does not look pretty. If a stack is snapped to another stack, some of the stack is "left behind". This is broken until I can figure out how to rotate a stack correctly.

In other improvements, I added camera re-centering code, which runs when the user presses 'c', and it also runs when the first vessel is added to the scene. No more searching for that vessel that you added at the beginning :woohoo:
 
I was considering something around the lines of calculating the geometric center of the stack (using the bounding boxes), and using some polar system to rotate each around the point. Any other ideas?

Why do you need the geometric center first? You can designate any arbitrary vessel in the stack as zero and rotate around it...

Anyways, I don't know how much I'll be able to contribute the next few weeks, sorry about that.
 
I think I figured out a solution to the rotation. I believe all of the nodes should be rotated the same amount, but it looks strange because they need to be translated to keep "in-sync". That should be as simple as making a vector from the center of the node/vessel to the pivot point (which could be an arbitrary vessel, thanks) and rotating that vector to figure out the translation amount.

Also, I converted the rotation code to use quaternions, as it avoids the prevalent gimbal-lock that occurred before.


Anyways, I don't know how much I'll be able to contribute the next few weeks, sorry about that.
No problem, I know how life is :thumbup:
 
I believe all of the nodes should be rotated the same amount, but it looks strange because they need to be translated to keep "in-sync".

I'm not sure how it works mechanically in your setup, but you should be able to just rotate every vertex in the stack around the same center by the same amount (wherever that center may be) and they should be in the right position. Alternatively, if that works better, you do two rotations per node, one of the node around itself to allign it properly, and then rotate the position around the common zero of the rotation of the stack. There shouldn't be any translation neccessary.
 
Alternatively, if that works better, you do two rotations per node, one of the node around itself to allign it properly, and then rotate the position around the common zero of the rotation of the stack. There shouldn't be any translation neccessary.
Because each vessel/node is its own ISceneNode, I can easily control each node's rotation and position. It's easier than manually rotating vertices.

My plan is basically the above, but your second "rotation" described above will actually be implemented as a translation, as I can figure out the distance the vessel needs to be moved to move it into the correct position.
 
I just pushed revision 095fd4f to github, which includes multithreading support and background loading. :woohoo:

I revised the Toolbox class to take a ToolboxData instead of a VesselData, as the only thing the toolbox actually needs is the image location. Now, when loading the toolboxes, only the images are loaded, and the actual vessel data loading code is put into a separate thread.

This means that StackEditor now loads (near) instantly, and users can immediately begin adding nodes and moving them. Sometimes, if the vessels are still background loading, there is a short delay when the user adds a node, as, if the vessel data for the selected node hasn't loaded yet, SE has to load it on demand.

@jedidia
Irrlicht's VideoDriver is not thread-safe, so I added a global mutex inside Helpers to make sure only one thread is accessing it at one time. From now on, any calls involving the video driver need to be surrounded in the mutex code:
Code:
Helpers::videoDriverMutex.lock();
driver->drawCoolStuff(); //any call involving driver
Helpers::videoDriverMutex.unlock();
 
Back
Top