Synkro framework consists of multiple interfaces, grouped into several namespaces. These interfaces describe all entities, of which the application consists, like rendering window, virtual scenes, cameras, light sources, visible geometry, etc.
Building a Synkro-based application begins with creating an instance of ISynkro interface,
which is done by calling SynkroCreate() function, declared in synkro.h header.
Typically, you want to create a class for your application and make ISynkro object
one of its properties. This way it can be accessed from event handlers.
ISynkro object is the root of the application functionality. It provides access to all the framework's sub-systems. One of them is the Window system, which enables the programmer to create OS window, which is used as an main output. During Synkro initialization, for each of the created OS windows, a rendering window wrapper is additionally created, which is then used as a rendering target in the applications's main loop.
Once the framework is configured, it's time to launch it. This is done by calling
ISynkro::Run() method, which starts the main loop. When this method returns,
the application exits. To exit main loop, set "running" parameter of the ISynkro::Run() method to false.
The application can react to various events, happening inside the main loop, by subscribing to them during the initialization stage and handling them at runtime.
The following chapters describe various aspects of the Synkro framework in more detail.
Synkro framework has a standard memory manager that enables the developer to detect memory leaks and corruptions.
To enable it, use SynkroMemoryInit() macro before calling SynkroCreate() function.
After the application exits, memory manager creates memory usage report
in the application folder. At runtime, the application can request memory usage details
by calling MemoryManager::GetStats() method.
The developer can measure application code performance by means of Synkro code profiler.
To enable it, use SynkroProfileInit() macro before calling SynkroCreate() function.
After the application exits, the profiler creates memory usage report in the application folder.
To identify the code blocks of interest, use SynkroProfile(), SynkroProfileCollapsed(),
SynkroProfileBegin() and SynkroProfileEnd() macros.
Synkro framework introduces a set of custom data types, used throughout its classes and interfaces.
These types are defined in the lang namespace. The custom data types include exceptions,
date and time, base enumeration and flag, point, rectangle, immutable Unicode string, etc.
The only template class here is Ptr, which represents a smart pointer to Synkro interface.
It's widely used to keep references to application's objects.
The math namespace contains classes applicable to affine transformations and trigonometry,
without which the 3D graphics would be impossible. These classes are vectors, matrices and quaternions.
The core namespace contains classes and interfaces representing
the base for the interfaces in other namespaces.
The base for almost all Synkro interfaces is the IObject interface.
It provides methods for reference counting that control objects lifetime.
The only interfaces that are not derived from IObject are IFactory and IResource
because their instances are created statically and exist during the application lifetime.
The ISynkro interface is derived from the IContext interface, which provides access
to all the framework sub-systems. The application should request sub-systems
to create the stuff and perform actions it needs. For example, to get access to
stream system, call _synkro->GetStreamSystem() method. Further, you can write
->GetStream( "photo.jpg" ) to retrieve IStream object, describing "photo.jpg" image
located in the framework's search paths. Or you can call _synkro->GetSceneManager()->CreateScene()
to create a virtual scene. All the Synkro sub-systems have similar design,
so, once you learn how to use one of them, you instantly learn how to use others.
Synkro is a flexible and configurable framework. It uses a set of configuration parameters that the programmer can use to customize application's behavior and select sub-system implementations. For example, the application may choose from either DirectSound or OpenAL as the Audio system implementation. It's possible to configure the framework either programmatically or visually by means of the configuration editor dialog.
Synkro provides access to its default configuration object via
_synkro->GetConfiguration().
The default configuration is immutable. To change synkro configuration, you need to clone
the default configuration by means of the Clone() method of the IConfiguration interface.
Custom configuration can be loaded from io::IStream by means of the Load() method.
The core::Param class contains symbolic parameter names you can use to override defaults.
The following types of configuration parameters are supported: Boolean, Integer, Enum, String.
Example:
config->Set( core::Param::GraphicsDisplayMode, gfx::DisplayMode::Maximum.Index() );
Once you are done with setting custom configuration parameters, you need to initialize synkro:
_synkro->Initialize( config );
During the initialization, Synkro uses Get() methods of the IConfiguration interface
to retrieve parameter values and initialize sub-systems as appropriate.
Synkro configuration can be edited visually in the special editor. To invoke it, use
ISynkro::Configure() method. The editor lists all configuration parameters to the right
and allows the user to override them. To the left there is a sample demonstration scene.
If the user hits the Run button, it updates configuration and returns true. Hitting Cancel
button returns false from the ISynkro::Configure() method.
Synkro uses Factory pattern to extend its capabilities. Many sub-systems, like graphics system
are implemented as plug-ins and so the user can easily switch between plug-in implementations.
Available plug-in types are listed in the core::Iface enumeration. The factories are exported
from the SynkroLibGetFactory() functions of the Synkro modules.
Synkro functionality is split into multiple modules. Each sub-system implmentation
resides in a separate module, for example 'synkro.gfx.dx11.dll' is the name of the module
which contains DirectX11 graphics system.
Synkro relies upon the exceptions mechanism to handle errors. The base class for Synkro error
is lang::Exception. It has several derived classes in various namespaces. By default, Synkro
handles errors that may occur during the initialization and main loop, by displaying
Exception dialog box. The user can override this behavior, by deriving application class from
SynkroListener and implementing its OnSynkroException() method. For example, you can use
Platform::Error() method to show OS-specific error message.
The application can emit messages (info, warnings and errors) into system log. The log file name
is set by Param::LogFileName parameter. To generate rich log file, set Param::LogFilePlain to false.
To control the number of messages that are emitted in the log file, use Param::LogLevel. Choose
from a set of values from Quiet to Extensive.
Synkro uses stream concept (IStream interface) as a means of storing/reading data.
To retrieve a stream located in the search paths, use IStreamSystem::GetStream() method.
To create a stream use CreateStream() method of stream system or stream directory
(IStreamDirectory interface). Synkro can work with compressed directories (zip files). The user can access files inside
zip file as if they were located in an ordinary directory, referrring them by file name.
Sometimes, several files are related to each other (for example, image files, representing
the sides of a cube map). In such a case the files have similar names. To retrieve such files
as a collection, use one of the IStreamDirectory::GetStreams() method overloads. Example:
_dir->GetStreams( L"SkyBox*.bmp" );
Images are one of the most widely-used resources in Synkro. They are represented by img::IImage
interface. The user can load an image from stream using the IImageManager::LoadImage() method.
This method allows loading image in its actual format, or converting it to a certain img::PixelFormat.
To load image asynchronously, use IImageManager::LoadImageAsync() method.
The supported image formats are described by the img::ImageCodec enumeration.
Synkro provides a set of named constants (more than a thousand) that represent various color tints.
They can be used to elegantly specify material, light or text color. These constants as well as some
handy methods are stored in the img::Color class, which uses RGB color model.
The mat namespace contains definitions of materials, which are used to create visible geometry
in the scene namespace. Materials are created by material manager (mat::IMaterialManager interface).
The are several types of materials in Synkro: simple (like IOpaqueMaterial) and complex (IMultiMaterial).
Multi-material is used with multi-subset geometry.
Application windows serve as the host for all the other stuff that exists in the application.
It's always the programmer's responsibility to create host windows. The are three kinds
of windows in synkro: frame window (win::IFrameWindowEx), view window (win::IViewWindowEx) and
icon window (win::IIconWindow).
To create non-sizeable main window with a frame, 800 pixels wide and 600 pixels tall, write:
_synkro->GetWindowSystem()->CreateWindow( false, false, L"Synkro application", 0, 800, 600 );
To create a 400x300 window within a frame window (win::IViewWindowEx interface), use the following code:
_synkro->GetWindowSystem()->CreateWindow( _frameWindow, 0, 0, 400, 300 );
To create a window, bound to a tray icon, write:
_synkro->GetWindowSystem()->CreateWindow( 0, L"Synkro application" );
To retrieve main window, write:
_synkro->GetWindowSystem()->GetFrameWindow( 0 );
To receive user input, the application needs to enable input (Param::InputEnable) and choose
input system implementation (Param::InputSystem).
Synkro supports the following input devices: Physical:
keyboard (input::IKeyboard), mouse (input::IMouse), joystick (input::IJoystick),
and logical: Arcball (input::IArcball).
To initialize certain type of input device, the application needs to call a create method. For example,
to initialize primary keyboard, use the following code:
_synkro->GetInputSystem()->CreateKeyboard( 0 );
Synkro allows applications to subscribe to specific input devices events. To make it possible, the application class
needs to implement a specific listener interface. For example, to intercept 'key up' events for the Space key, write:
_synkro->GetInputSystem()->GetKeyboard( 0 )->ListenKeyUp( this, Key::Space ); // Here 'this' implements KeyboardListener.
The corresponding event handler looks like this:
Bool App::OnKeyboardKeyUp( UInt device, const Key& key, Bool alt, Bool shift, Bool control )
{
if ( key == Key::Space )
{
// TODO: Process event.
return true; // Signal to Synkro the event is processed
}
return false;
}
To be able to playback sounds, the application needs to enable audio (Param::AudioEnable)
and choose audio system implementation (Param::AudioSystem). Then create an audio device:
_synkro->GetAudioSystem()->CreatePlayer( 0 );
This device will be used by sound manager (sound::ISoundManager).
To load 2D sound from stream, use the following code:
_synkro->GetSoundManager()->LoadSound2D( stream );
To start loaded sound playback, write:
sound->Play( true );
Viewport is a rectangular area inside application window where a scene is drawn.
Frame window can have arbitrary number of viewports, while view window always has one.
Viewports are created by calling one of the overloads of the IViewportManager::CreateViewport() method.
A viewport is essentially a wrapper around the gfx::IRenderView interface.
The image drawn in a view can be post-processed. This is done by creating viewport filters.
Available filter types are listed in the view::ViewportFilter enumeration. To create a filter,
use IViewport::CreateFilter() method.
The user can apply transition effects to viewports, for example 'Fade to black'.
Available transition effects are listed in the view::TransitionEffect enumeration.
To create a transition effect, write:
_synkro->GetViewportManager()->CreateTransitionController( this );
Where 'this' is an instance of ViewportListener class.
To pick triangle mesh under the cursor, use view::IViewport::PickMesh() method.
If there is a mesh under the cursor, this method returns a pointer to it. Otherwise,
it returns nullptr.
Each rendering window has an overlay that is capable of displaying sprites and texts.
Such pools use a single texture resource to display multiple sprites and texts.
Synkro's overlay manager (over::IOverlayManager) allows creating font templates and then
using them to create text objects.
Synkro allows the user to create user interface for their applications via ui::IUiEx interface.
The UI consists of various widgets, which are the descedents of ui::IWidget interface.
The widgets exist inside frames (ui::IFrame). The following widgets are available:
IAngle, IButton, IDropList, IEdit, IList,
IOption, IProgress, ISlider, ISwitch, ILabel, IPicture.
To intercept UI widgets events, the application class must implement UiListener interface and
call Listen() on IUiEx, like this:
_synkro->GetUi()->Listen( this ); // Here 'this' implements UiListener.
After that, the application can implement OnUiClick, OnUiDoubleClick and OnUiValueChanged methods.
If the application processes an event, it must return true from these methods.
To access scene manager, use the following code:
_synkro->GetSceneManager();
Synkro scene is the 3D virtual environment. It can contain cameras, light sources and visible geometry.
To create a scene, use CreateScene() method, like this:
_synkro->GetSceneManager()->CreateScene( L"World", DebugMode::Node, true );
The camera defines the point in the 3D space where the viewer is placed. The camera is associated
with the viewport (view::IViewport).
If the 'lit' parameter of the CreateScene() method is true, the application can call ISceneEx methods
that create light sources: CreateOmniLight(), CreateConeLight() and CreateDirectLight().
The application can create 3 types of mesh: point (scene::IPointMesh), line (scene::ILineMesh) and
triangle (scene::ITriangleMesh). A triangle mesh can be loaded from stream using ISceneEx::LoadMesh() method.
The available mesh files formats are listed in the scene::MeshCodec enumeration.
The application fill in empty mesh with geometric primitive, like this:
_synkro->GetSceneManager()->BuildMesh( mesh, type, param1, param2, transform );
The available mesh types are listed in the scene::MeshBuilder enumeration.
Many Synkro objects can be animated. Namely, if a specific interface has CreateAnimationController()
method, most of its properties can be changed over time with either keyframed or procedural animation.
The animation playback is started with IController::Start( true ) call.
If some interface has CreateRecordController() method, then its properties that change over time
can be saved to IAnimationSet object.