Running Syncker with AppProjection
If computers connected via Syncker use projectors instead of monitors to render an application, it is required to use the AppProjection plugin, which enables to configure the created projections over LAN.
See Also
- projection_00 sample
Script Implementation
- Set a fixed FPS value to make simulations consistent across all computers and initialize Syncker in the script running on the master computer.
Also you should initialize a console to allow running master command-line options.
int init() { engine.game.setFTime(1.0f / 60.0f); Unigine::Syncker::Master::Console::init(); Unigine::Syncker::Master::init(master_address); return 1; }
- In the init() function, create a world: add nodes, implement interaction logic, etc.
-
Get names of the slaves and add them to the interface of the Projection panel. It will allow you to choose the slave, whose projection should be configured from the master computer.
The master should also be added to the interface of the Projection panel. It will allow you to clear the projection configuration on the master computer after configuring of the slaves' projections.
You should implement this logic in the init() function of the world script.engine.system.call("projectionClearAuxiliary"); // add the master to the Projection panel interface engine.system.call("projectionAddAuxiliaryItem","Master"); forloop(int i = 0; Unigine::Syncker::Master::getNumSlaves()) { string name = Unigine::Syncker::Master::getSlaveName(i); string address = Unigine::Syncker::Master::getSlaveAddress(i); engine.system.call("projectionAddAuxiliaryItem",format("%s (%s)",name,address)); }
-
Set custom callback functions in the init() function of the world script.
The wallSetChangedWorldCallback() and projectionSetChangedWorldCallback() functions are specified in the wall.h and projection.h scripts respectively and receive the identifiers of the custom callback functions.
engine.system.call("wallSetChangedWorldCallback",functionid(my_wall_changed_callback)); engine.system.call("projectionSetChangedWorldCallback",functionid(my_projection_changed_callback));
-
Implement the custom callback function my_wall_changed_callback(), which has been set in the previous step and will be invoked when the Bezel X and Bezel Y settings are changed in the Wall tab.
The wallGetBezelX(), wallGetBezelY(), wallSetBezelX(), wallSetBezelY() functions are defined in the data/core/scripts/system/wall.h script.Another way to set the changed values is to run the slave_bezel console command from the script on each slave.
void my_wall_changed_callback() { // get the currently changed values of bezel parameters float bezel_x = engine.system.call("wallGetBezelX"); float bezel_y = engine.system.call("wallGetBezelY"); // set this values as current for all of the slaves via the system script functions Unigine::Syncker::Master::systemCall(~0,"wallSetBezelX",(bezel_x)); Unigine::Syncker::Master::systemCall(~0,"wallSetBezelY",(bezel_y)); }
Use the code below instead of calling the wallSetBezelX() and wallSetBezelY() functions.// run the slave_bezel console command on all of the slaves to update their bezel compensation Unigine::Syncker::Master::systemCall(~0,"engine.console.run",(format("slave_bezel %f %f",bezel_x,bezel_y)));
-
Implement the my_projection_changed_callback() callback function, which has been set in the step 3 and will be invoked when you change the projection settings on the Projection panel:
The system script functions called below are defined in the data/core/scripts/system/projection.h file.
void my_projection_changed_callback() { // update grid mode if it was changed if(engine.system.call("projectionGetActivity")) { int mode = engine.system.call("projectionGetGridMode"); int step = engine.system.call("projectionGetGridStep"); Unigine::Syncker::Master::systemCall(~0,"projectionSetGrid",(mode,step)); } else { Unigine::Syncker::Master::systemCall(~0,"projectionClearGrid",()); } // update projection parameters for the chosen projection int item = engine.system.call("projectionGetAuxiliaryItem"); if (item != 0) { int mask = 1 << (item - 1); Buffer buffer = new Buffer(); engine.projection.saveState(class_cast(classid(Stream),buffer)); // restore the state of the slave display Unigine::Syncker::Master::systemStream(mask,"engine.projection.restoreState",buffer); delete buffer; } }
- Implement logic, which allows loading the world for all of the slaves on the master side, and call it in the init() function after the Syncker initialization.
You can skip this step and load the world on each slave individually via the console.
void sync_world() { // load slave world Unigine::Syncker::Master::systemCall(~0,"engine.console.run",("world_load " + engine.world.getName())); // waiting for the slave world int ret[0]; float begin = clock(); while(clock() - begin < 2.0f) { int is_loaded = 1; Unigine::Syncker::Master::systemCall(~0,"engine.world.isLoaded",(),ret); foreach(int i = 0; ret) is_loaded &= i; if(is_loaded) break; } }
- Implement shut down logic for the console and Syncker, clear the slaves' names and system script callbacks:
int shutdown() { // clear slaves' names engine.system.call("projectionClearAuxiliary"); // clear system script callbacks engine.system.call("wallSetChangedWorldCallback",NULL); engine.system.call("projectionSetChangedWorldCallback",NULL); // quit the slave world Unigine::Syncker::Master::systemCall(~0,"engine.console.run",("world_quit")); // Shut down the console Unigine::Syncker::Master::Console::shutdown(); // shutdown Syncker Unigine::Syncker::Master::shutdown(); return 1; }
-
Implement synchronization logic in the render() function.
int render() { // synchronize frame Unigine::Syncker::Master::syncFrame(); // synchronize player Unigine::Syncker::Master::syncPlayer(); // synchronize render Unigine::Syncker::Master::syncRender(); // synchronize end Unigine::Syncker::Master::syncEnd(); return 1; }
If you use the same world script on the master and slave sides, you need to wrap your code around with #ifndef DEFINE_NAME ... #else ... #endif as follows to separate the script logic:
#include <core/unigine.h>
// logic to run on the master
#ifndef DEFINE_NAME
#include <core/systems/syncker/syncker_master.h>
#include <core/systems/syncker/syncker_master_console.h>
// init() function defined above
// shutdown() functions defined above
// render() function defined above
// implementation of the custom callback functions
#else
// logic to run on a slave
int init() {
// slave logic implementation
return 1;
}
#endif
Running Project
To run Syncker with the AppProjection plugin, perform the following:
- On the slave application start-up, override a default editor script with the Syncker script for slaves, activate the AppProjection plugin and
specify the PROJECTION_USER and DEFINE_NAME definitions:
Do not forget to add the other required start-up arguments.
main_x86 -data_path "../" -editor_script "core/systems/syncker/syncker_slave.cpp" -extern_plugin "AppProjection" -extern_define "PROJECTION_USER,DEFINE_NAME"
The PROJECTION_USER definition is used to avoid overwriting the camera configuration by the core/scripts/system/wall.h script. It disables the Wall::render() function and allows you to configure slave projections manually.
-
If the editor in slave applications is not loaded, load it via the following console command:
editor_load
-
Run the application on the master computer
main_x86 -data_path "../" -extern_plugin "AppProjection"
-
Change the broadcasting network address of the master:
And reload the world by using the world_reload console option.
master_address xxx.xxx.xxx.xxx
Configuring Projections
By default all computers project the same screen as a master computer. So, after launching your application on the master and slave computers, you need to configure a viewport of each slave depending on the required screen configuration and then adjust each projection.
Changing Screen Configuration
For example, if you have 3 slave computers that project the application onto the curved screen, you should perform the following:
-
First, set a projection configuration on the master side via the master_grid console command:
master_grid 3 1
-
Set the same configuration for all slaves:
master_run slave_grid 3 1
-
On each slave, run the slave_view console option with the corresponding arguments to set the number of the projection:
-
For the left projection:
slave_view 0 0
-
For the central projection:
slave_view 1 0
-
For the right projection:
slave_view 2 0
You can also set the projection number by specifying the -slave_view_x command-line option with the required value on the start-up of each slave.To check the current viewport configuration of all the slaves, type the master_view console option on the master side.
-
For the left projection:
-
On each slave computer, run the following console command to set the required turning angle compensation :
slave_angle <angle_value>
You can also set the turning angle compensation by specifying the -slave_angle command-line option with the required value on the start-up of each slave. -
Set bezel compensation for all of the slaves, for example:
You can set the bezel compensation for each slave individually via its console by using the slave_bezel console option.
master_bezel 0.2 0
You can also adjust the bezel compensation values in the Wall tab of the main menu, if you implemented the corresponding logic in the script.
Changing Projections
When the viewports of the slaves are configured, you can adjust each projection from the master computer via the Projection panel.
- On the master computer, call the main menu by pressing the Esc button, go to the Wall tab and press the Projection button to open the Projection panel.
-
In the lower left corner of the Projection panel, choose the slave, whose projection need to be configured:
-
Adjust the projection of the chosen slave and press the Save button. Then specify a name of the projection and press OK. Your projection configuration will be saved in the *.projection file.
When you change the slave projection, the projection on the master computer is also changed. To clear the projection configuration on the master computer, perform the following after saving the slave projection into the file:
- In the lower left corner, choose the Master from the drop-down menu.
- Press the Clear button.
- In the dialog box that opens, press the OK button.
If you have saved all of the required projection configurations, you can load them at the next application start:
- On the master computer, open the Projection panel.
- Choose the slave, for which you want to load the projection.
- Press the Load button and choose one of the *.projection files.
- Press OK.