C++ Tutorial 2: Integrating in a Third-Party Application
Vortex Studio applications can be integrated with third-party applications using various methods. This module covers how the entire Vortex Studio Dynamics engine can be integrated into a C++ application.
Requirements
Participants must have some basic C/C++ training before taking this training module. To be able to compile and run code, participants must have Visual Studio 2022 (VC143) or later installed. The provided tutorials solution settings are expecting Windows SDK 10.0.14393.0 and the vc143 toolset (this can be changed, these restrictions C++ binary compatibility 2015-2022 apply).
Introduction
A second use of the SDK is to embed the Vortex Studio dynamics engine in a larger C++ application. In this situation, Vortex Studio does not control its main application loop. The parent application loads up the Vortex Studio engine, then can either:
load content that was created using the Editor
dynamically create all mechanisms, assemblies and parts through code
Once all content is loaded or created, the parent application tells the engine to perform simulation steps when needed. Data can be injected into the engine before a step is executed, while data can be extracted from the engine after execution took place.
This type of integration allows for a very tight coupling of Vortex Studio with another application, with direct access to data in either direction.
More information on this topic can be found in Integrating the Application.
Running ExVHLIntegration Example
A great example of integrating Vortex Studio dynamics in a third-party application is the ExVHLIntegration example found in the C:\CM Labs\Vortex Studio <version>\tutorials directory. You can either compile the code for this application or run the pre-compiled version by running the runExVHLIntegration.bat script located in the C:\CM Labs\Vortex Studio <version>\tutorials\bin directory.
This example opens up two windows when it starts up. The window shown on the left in the image above is a Vortex Studio 3D view showing a mechanism made from simple collision geometries. The window on the right is rendered using a utility library called SDL. At every step of this application's execution, C++ code queries the keyboard to see if the paddle should move left or right, sends this data to Vortex Studio, asks Vortex to run its physics, then queries the position of the paddle and ball to render its graphics. The left-hand Vortex Studio window renders automatically as part of the Vortex application execution. This type of window is especially useful when developing an integration in a third-party application. It can be activated or hidden based on the content of the setup document that the code reads in at initialization. We'll take a closer look at the code of this sample below.
The file main.cpp contains the application's main function, the typical starting point for most C/C++ applications. It follows by instantiating the application's main class and asks that class to execute.
#include "ExVHLIntegration.h"
// main is a macro in SDL_main.h
#ifdef main
#undef main
#endif
// Start the game's loop
int main(int argc, const char *argv[])
{
ExVHLIntegration app;
return app.exec();
}
Looking at the exec method, the first thing it does it to call the class's _init method, which we'll look at in a moment. It then goes into a main loop until the value of the mRunning variable is called. In this main loop, the application will handle incoming events, update the physics, render graphics, then wait a bit to run at 60 Hz and go through the same loop.
int ExVHLIntegration::exec()
{
int status = 0;
try
{
// Pre-Loop Initialization
_init();
Uint32 last = 0;
while (mRunning)
{
Uint32 now = SDL_GetTicks();
// Handle the frame rate to step every 1/60 of a second
if (now > last + (1000/60))
{
// Handles the event received by SDL
_events();
// Step the physic engine and manage the custom integration rules
_update();
// Draw the entities on the 2d display buffer
_render();
// Step done, let's wait for the next one
last = now;
}
else
{
SDL_Delay(1);
}
}
}
catch(...)
{
// Something's wrong.
status = 1;
}
// Happy ending
_cleanup();
return status;
}
The first part of the _init method is related to the SDL portion of this application, creating a window for the 2D view, creating a renderer and configuring it, as well as loading some graphic assets.
void ExVHLIntegration::_init()
{
// Initialize the SDL library's video component and events.
if(SDL_Init(SDL_INIT_VIDEO) < 0)
{
throw std::runtime_error("Error initializing the SDL library.");
}
// 400x800 window
mWindow = SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 400, 800, 0);
if (mWindow == nullptr)
{
throw std::runtime_error("Cannot create the SDL window.");
}
// Graphic renderer
mRenderer = SDL_CreateRenderer(mWindow, -1, SDL_RENDERER_ACCELERATED);
if (mRenderer == nullptr)
{
throw std::runtime_error("Cannot create the SDL renderer.");
}
SDL_SetRenderDrawColor(mRenderer, 94, 255, 94, 255);
SDL_RenderClear(mRenderer);
SDL_RenderPresent(mRenderer);
// Load the ball bitmap
mBallTexture = _load("../resources/ExVHLIntegration/Bitmap/ball.bmp", true);
if (mBallTexture == nullptr)
{
throw std::runtime_error("Cannot load ../resources/ExVHLIntegration/Bitmap/ball.bmp.");
}
// Load the paddle bitmap
mPaddleTexture = _load("../Resources/ExVHLIntegration/Bitmap/paddle.bmp");
if (mPaddleTexture == nullptr)
{
throw std::runtime_error("Cannot load ../resources/ExVHLIntegration/Bitmap/paddle.bmp.");
}
Once SDL initialization is complete, the code moves on to initializing the Vortex Studio part of the application, first by called the VortexCreateApplication function and passing to it a setup document (ExVHLIntegration.vxc), then loading a Vortex vxscene file and storing a pointer to the resulting scene in a variable.
After the scene is loaded, the following two blocks of code retrieve pointers to the ball and paddle mechanisms.
As mentioned before, the _events, _update, and _render methods are then called sequentially every iteration. The _events method's main function is to listen for keyboard events and determine if the paddle should be moved to the left or right and store the resulting outcome in some global variables. We won't go into the details of this code here.
The _update method is more interesting, as we can see below. It first determines if the paddle is moving left or right, then it uses a function called VortexSetInputReal whose parameters are the mechanism pointer, a string to the name of a VHL interface under this mechanism, and another string containing the name of the VHL interface element that we want to write to. This simple function allows developers to easily write to any variable that's been added to a VHL interface.
Next up is the VortexUpdateApplication function, which will get Vortex to simulate. If that function returns false, then we set the mRunning variable to false, as it would indicate that the Vortex component has terminated its execution. The mRunning variable is checked every iteration in the exec method to determine if the parent application should exit.
After running the dynamics, we use the _getPosition function to retrieve a position matrix for the ball using the ball mechanism pointer, the name of its VHL interface, and the parameter name for the position in that interface. With this information in hand, the code checks to see if the ball has fallen lower than a certain position and flags the application to exit if that condition is met.
Last but not least, the _render function retrieves the position of the paddle and the ball using each mechanism's VHL interface, then calls some SDL functions to prepare data and update the graphics in the 2D application.
The final bit of code relating to embedding Vortex Studio in a third-party application is the clean-up phase, found in the _cleanup method. As can be seen in the code below, pointers are free by being set to nullptr, the VortexUnloadScene and VortexDestroyApplication functions are called to unload and free the content of the dynamics simulation, as well as free all memory related to Vortex Studio. Finally, some SDL cleanup functions are called at the same time.
Compiling this code produces an executable application that will be able to load and simulate Vortex Studio Content. When executed, the application will load a number of DLLs (Dynamic Link Libraries) from the Vortex Studio installation directory. You must therefore make sure that your path includes the bin and plugins folders of Vortex Studio for the correct version that was used to link your application.