CSPSim Initialization
From CSP
The following paragraphs are based on SVN revision 2322 and higher.
| Table of contents |
sim.py
The components of the sim are compiled into a couple of dynamic libraries (dlls on Windows, .so files on Linux). These libraries are registered as Python extensions, making them available to Python. The sim.py script loads the extensions, does some initialization work and finally starts the user interface and the main application loop. Thus, there is no main() function in the C++ code.
options = csp.base.app.options
...
loadExtensionModules()
setDefaultJoystick()
...
csp.cspsim.openConfig(config, 1)
cachepath = csp.cspsim.getCachePath()
dar = os.path.join(cachepath, "sim.dar")
...
sim = csp.cspsim.CSPSim()
...
ensureDefaultHID('aircraft')
ensureDefaultHID('gamescreen')
try:
... (deprecated stuff)
else:
sim.displayLogoScreen()
from csp.data.ui.scripts.startup import UserInterfaceStartup
userInterface = UserInterfaceStartup(sim)
userInterface.run();
sim.run()
After loading the extension modules and initializing the default joystick, the path to the sim database "sim.dar" is read from the config file. It is possible to use any number of databases, but for the moment we only need one. sim.dar contains the XML data from the /csp/data directory in binary format for faster access at runtime. It is created by running sim.py -c.
ensureDefaultHID verifes that the specified hid input definition exist. If not, it is generated from the example input maps in csp/tools/hid/examples.
In sim.displayLogoScreen(), the logo screen is instantiated and displayed. The LogoScreen class is derived from BaseScreen, which in turn represents a generic view of the simulation or GUI. Each screen within the game / sim should subclass this class. The active screen receives input events and renders the view via the onRender callback.
After that, the user interface is initialized and activated. The UI is a mix of C++ and Python code. The specific UI elements (e.g. menu contents and buttons) are defined in Python scripts (see /data/ui/scripts for details), while the underlying framework code is C++ (check wf namespace code for details). These scripts are also responsible for placing dynamic objects (like airplanes controlled by the player or an AI agent) into the scene. For now, let's focus on "instant action" from the main menu (the "tutorials" section is not too different from that). The following code snippet shows a part of mainmenu.py, which is instantiated from UserInterfaceStartup:
def instantAction_Click(self):
self.getWindowManager().closeAll()
self.cspsim.displayLogoScreen()
self.cspsim.loadSimulation()
vec = csp.csplib.Vector3
m2k = "sim:vehicles.aircraft.m2k"
f16dj = "sim:vehicles.aircraft.f16dj"
self.cspsim.createVehicle(f16dj, vec(-29495, -10530, 91.3), vec(0, 0, 0), vec(0.0, 0.0, 180.0), True)
self.cspsim.createVehicle(f16dj, vec(-29510, -10530, 91.3), vec(0, 0, 0), vec(0.0, 0.0, 180.0), False)
The script first shows the logo screen, then loads the simulation (static objects, terrain etc.), and finally creates two dynamic objects. The first F-16 is controlled by the player (last argument in createVehicle function call is set to True; other arguments: #1 is the path to the class of the vehicle to create, #2 is the position in meters from the center of the terrain, #3 is the initial velocity, #4 is the initial attitude (3 angles in degree)), while the second is controlled by AI (AI behavior is hard coded at present - check the classes in the AI namespace as a reference).
Class CSPSim
The CSPSim class initializes the whole simulator and contains the application's main loop. Some of its functions are introduced in the following paragraph.
CSPSim::CSPSim()
Nothing really exciting is happening in the ctor...
CSPSim::displayLogoScreen()
Here we have the first occurence of a function that renders something on the screen. Therefore, a new osgUtil::SceneView is created (m_LogoView) that represents a 2D view of a scene. This scene consists only of a simple textured rectangle, and the texture is the CSP background image. The rectangle's geometry is created as an OSG primitive set and attached to the SceneView in LogoScreen::onInit(). The geode registers a callback function (class ImageUpdateCallback: public osg::NodeCallback) that randomly loads an image from the /data/imgaes/logo directory. CSPSim::loadSimulation() frequently calls this function (m_CurrentScreen->onUpdate(0.0);) to change the background during the loading process. At present, we only have one image in this directory, so there are no visible updates. Please note that osgUtil::SceneView still exists, but is marked as deprecated in the current OSG release. We are currently refactoring the code and will replace the various SceneViews with an approach based on osgViever.
CSPSim::loadSimulation()
As the first step, a DataManager is instantiated, and the sim.dar archive is attached to it, and the initial sim time is read from the config file.
m_DataManager = new DataManager();
// open the primary data archive
std::string cache_path = getCachePath();
std::string archive_file = ospath::join(cache_path, "sim.dar");
try {
DataArchive *sim = new DataArchive(archive_file.c_str(), 1);
assert(sim);
m_DataManager->addArchive(sim);
}
...
std::string date_string = g_Config.getString("Testing", "Date", "2000-01-01 00:00:00.0", true);
SimDate date;
try {
date.parseXML(date_string.c_str());
}
Next, an instance of the Theater object is created from data in sim.dar. Among others, the theater definition contains a reference to the terrain and to a list of objectives (right now, the only objective is the airbase). A TerrainObject is created, and the terrain engine is finally activated. TerrainObject is an abstract base class for terrain engines. Any terrain engine that operates in a flat X-Y-Z coordinate space and implements this interface can be used with CSPSim. The current CSPSim demo uses the Demeter engine.
StoresDatabase::getInstance().load iterates through the stores data and preloads all object models in the stores directory. See StoresDatabase::_load() for details.
std::string theater = g_Config.getPath("Testing", "Theater", "sim:theater.balkan", false);
m_Theater = m_DataManager->getObject(theater.c_str());
assert(m_Theater.valid());
//CSP_VERIFY(m_Theater->initialize(*m_DataManager));
m_Terrain = m_Theater->getTerrain();
assert(m_Terrain.valid());
m_Terrain->setScreenSizeHint(screenSettings.width, screenSettings.height);
m_Terrain->activate();
...
StoresDatabase::getInstance().load(*m_DataManager, "sim:stores");
After that, the scene graph is initialized and populated. The VirtualScene class manages the 3D scene and provides the interface to OSG. buildScene() creates various SceneViews for individual components of the scene, like sky, static objects, terrain and cockpit. These views are derived from the deprecated osgUtil::SceneView class. As already stated above, we are currently working on a more up-to-date replacement for this class.
m_Scene = new VirtualScene(m_GlobalState.get(), screenSettings.width, screenSettings.height); m_Scene->buildScene(); m_Scene->setTerrain(m_Terrain);
The following code section initiates the battlefield. LocalBattlefield is derived from the Battlefield class. Battlefield is a base class for managing static and dynamic objects in the game. It maintains spatial indices to keep track of object locations. It also controls the camera position and manages the connection to a server.
SceneManager acts as an adapter between the Battlefield and the scene graph (e.g. VirtualScene). The Battlefield class uses the SceneManager to schedule the addition and removal of objects from the scene based on visibility (ie. proximity to the camera). In addition, camera position updates are passed to the scene graph class from the battlefield via the SceneManager.
If there is a valid theater, all of the theater's feature groups are added to the battlefield as static objects. There is currently no check for distance from camera. This will probably become necessary in more complex theaters.
int visual_radius = g_Config.getInt("Testing", "VisualRadius", 40000, true);
m_Battlefield = new LocalBattlefield(m_DataManager);
m_Battlefield->setSceneManager(new SimpleSceneManager(m_Scene, visual_radius));
if (m_Theater.valid()) {
FeatureGroupRef::list groups = m_Theater->getAllFeatureGroups();
CSPLOG(DEBUG, BATTLEFIELD) << "Adding " << groups.size() << " features to the battlefield";
for (FeatureGroupRef::list::iterator iter = groups.begin(); iter != groups.end(); ++iter) {
m_Battlefield->addStatic(*iter);
}
}

