XEngine Programming Guide
Home | About XEngine | Engine API | SceneGraph | Changelog | Programming Guide |
Prerequisites
A basic undestanding of how XEngine is structured in terms of API calls is suggested. Please have a look at the Engine API, SceneGraph and the About page.
Getting Started
A Visual Studio 2010 example project is provided with the XEngine DLL files in the XEngineDist folder. The window management toolkit used is FreeGLUT. Its including folders are:
- 3rd Party. This folder contains binaries, lib files and include headers of the 3rd party libraries GLEW, FreeImage and TinyXML2.
- Batch. This folder contains a batch file which is used as a post-build step to transfer all necessary DLL's to the appropriate Bin folder.
- Bin. Contains all binaries.
- GlobalIncludes. All necessary include files for XEngine.
- Projects. Contains the source files of the example project (XEngine_GLUT).
- Solution. This folder contains the VS2010 solution files.
Class Description
The following cpp files are included in the XEngine_GLUT project:
- EPCH.cpp. This is a precompiled header file which includes the XEngine header files.
- Main.cpp. This file defines the entry point of the application, and all the FreeGLUT callbacks.
- Application.cpp. This file contains the definitions of the functions which have been used as callbacks in the Main.cpp file.
- Input.cpp. The Input class is a wrapper for all FreeGLUT input handling. It supports handling of mouse and keyboard events. Both regular and special GLUT keys are captured with the appropriate up and pressed events.
- TestData.cpp. The TestData class is a wrapper which provides an example of how data should be generated for interfacing with the Primitives API.
Initializing XEngine
Before rendering anything, we need to initialize XEngine, as well as any external data we need to use. The InitializeApplication function is responsible for this. The basic code structure is as follows:
- Enable the logger and the type of info it will log.
- Initialize configuration file.
- Create all internal engine pointers, memory allocations and log files using XE_engine_create
- Create the basic scene graph (specify any default configuration, geometry nodes, group nodes, input nodes, lights, cameras, etc).
For more information on this, look at the Initializing SceneGraph section below. - Create a device and attach it to an input node. For more information on this, look at the Handling Input section below.
- Inform the engine the basic setup is done by calling XE_engine_initialize. This should be the latest API call in the InitializeApplication function.
// First, initialize the logger. |
// read the configuration file |
// First important call, is calling XE_engine_create. |
// now, inform the engine that the initial setup is complete |
Initializing SceneGraph
After calling XE_engine_create, the scene graph contains only the root node. The most basic initialization is done using the Configuration API. This setups a default camera, a default user and a default input node, as well as a default lighting scheme.
// Create a default camera |
Alternatively, we need to perform this setup ourselves. This requires to create each item separately, and attach them to the appropriate nodes. The following example shows how to setup a default light.
// Retrieve the root node id. Alternatively, using -1 has the same effect. |
To setup a basic camera, user and input node, we perform the following steps:
// Create an input node, which is attached to the root node. |
To load a static geometry file, we need to:
- Add it's corresponding Directory (if any) to the data path(s).
- Add a Transformation Node and attach it to the root node, so that we can apply transformations to the object.
- Add a Geometry Node and attach it to the Transformation Node.
- Register the mesh (this does not verify that the mesh will be loaded, it's path is checked on a later stage and a corresponding log message is generated).
// Add the directory that contains the obj file // Add a transformation node, so that we can apply transformations to the object // Add a geometry node |
Render and Resize
Rendering happens within the Render function:
// Update the engine timer. This is set explicitly, so that the user can perform time-based calculations in the correct time intervals // Update any external data (such as updating the engine's input device information) // Update the engine (the graph, any new provided parameters, primitives, etc) // Render // Swap Buffers |
Resize happens within the Resize function:
// Prevent a divide by zero by making height equal to one // Inform the engine a resize has occurred |
Handling Input
To handle input events we need to do the following (the following should be performed before XE_engine_initialize if we wish to navigate within the scene when the application starts:
- Register a device to XEngine. This informs the engine that we will need to send input events. For single input environments, sending 0 is sufficient. This is equivalent to setting the devicename attribute to device0 at the XML file.
- Attach the device to an input node. Since the camera node follows the user node and the user node is attached to the input, then the camera and user transformations will be handled externally by the application.
- Set the max values for this device. This allows for multi-type devices to be used. By default, a mouse - keyboard configuration requires at maximum 5 buttons and 5 axes. Joystick configurations might require more.
// Retrieve the input node id |
The Input class handles all FreeGLUT input events. This includes mouse motion, regular and special keys with multi-key support and generation of events when a key is in an up, down or pressed state and when a mouse button is in an up or down state. Four functions are provided for this purpose:
- KeyPressedSpecialOperations, handling of pressed events of special keys (arrow keys, function keys, etc.)
- KeyDownSpecialOperations, handling of down events of special keys (arrow keys, function keys, etc.)
- KeyPressedOperations, handling of pressed events of regular keys (character keys)
- KeyDownOperations, handling of down events of regular keys (character keys)
For example, if we wish to use the Settings API and zoom to the extents of the scene's bounding box, we add the following to the KeyPressedSpecialOperations function.
void Input::KeyPressedSpecialOperations() |
To update input events with XEngine, we do the following in the UpdateApplication function:
- Update the input class's key events
- Send to XEngine all input values
- Perform a post update (important for pressed operations to work)
void UpdateApplication() |
Handling Primitives
In order to send primitive data to XEngine using the Primitives API a special class called TestData is provided. This contains a structure called GroupData which can contains all possible information that can be sent to XEngine. As a test case, it builds list of primitives per data type. A box made of triangles, a box bade of lines and a box made of points.
Two functions are important here:
- Build, which is called at the InitializeApplication stage and builds the basic data
- Update, which is called during the UpdateApplication and updates the primitives. In this case, they are not modified, so this call is not needed.
During the UpdateApplication, the AddPrimitives function is called which iterates through the TestData containers and sends them to XEngine using the Primitives API.
The actual AddPrimitives in the example project creates the lists twice, one with lighting enabled and one with lighting disabled. The lists with lighting disabled are rendered through a separate rendering pass, while the lists with the lighting enabled are merged with the default XEngine rendering pipeline.
Note that the list begin-end procedure needs to be repeated each frame the primitives are to be rendered.
This example performs the following actions:
- Sets global primitive parameters (shadow status, line width, point size, etc.)
- Creates a triangle list
- Creates a line list
- Creates a point list
// Set some primitive rendering states. These functions are global and operate similar to OpenGL's states' system |
// Set a unique color for the box |
// Add some lines |
// Add some points |
XML File Loading
Instead of using direct function calls to load the graph's nodes, an external XML file can be used according to the specifications of the Scene Description Language. The following example, performs the basic operations of loading a scene file, as well as setting up all basic nodes.
<?xml version="1.0" encoding="utf-8"?> <world background="0.3 0.4 0.7" ambient="0.26 0.31 0.35"> <user name="default_user" linear_speed="10.0" angular_speed = "1.0" position = "-3.3 32.9 88.9" lookat = "3.6 9.3 33.2" input="default_input"> </world> |
This file can be loaded either from the configuration file, or explicitly by calling the following Engine API functions.
// Explicitly set a scene file // Explicitly parse the file (otherwise it will be parsed on XE_engine_initialize |
Last updated: 27 May 2013