CoherentApplication is simple multiplatform abstraction for an application loop and rendering. You can find it in Coherent/Libraries/Application
.
The framework supplies the following components:
Coherent/Libraries/Application/Entry.h
, which abstracts the entry point (main function) of the application.The PlatformWindow
class is designed to abstract the platform differences for creating a window and running a game loop. The PlatformWindow::GetNativeWindowInfo
returns a structure that exposes the native window handles for custom code.
PlatformWindow::Initialize
creates the window and PlatformWindow::Run
starts the game loop an processes events until the application is closed. While running the game loop, PlatfromWindow
fires various events. The full list is as follows:
The most widely used ones are the first three - OnIdle
is called every frame so user code can be executed, and OnKeyEvent
, OnMouseEvent
and OnTouchEvent
are called when an input event is received.
These events are public, so you can attach your methods by doing something in the line of:
You should call Run
in your main function, after you've subscribed for the events you want.
The IRenderer (Coherent/Libraries/Application/Renderer.h
) interface enacapsulates low level rendering functions, such as DrawPrimive
, DrawIndexedPrimitive
, CreateEffect
and so on.
There are two implementations of IRenderer - one using OpenGL and another one using DirectX 9.
The renderer manages resources, such as textures, vertex declarations, effects and buffers. Resources can be created with the Create***
methods of the renderer, which return an integer. This integer is a unique ID that represents the resource and can be used by other IRenderer
methods, such as SetTexture
.
There are auxiliary structures (descriptions), representing an abstract resource (TextureDesc
, BufferDesc
, etc.), and specific implementations for each renderer, such as TextureDescOpenGL
or TextureDescDirectX
. There are interface methods for getting a pointer to base description, which can be cast to the appropriate implementation if you know what renderer you're using, thus giving you raw access to an OpenGL/DirectX resource.
To draw anything with the IRenderer
, you have to enclose the drawing code inside a IRenderer::BeginFrame
- IRenderer::EndFrame
section. You can create resources outside such section, but all drawing code, setting of buffers and effects must take place inside one. There usually should be only one such section and all the drawing in the frame happens there.
When drawing, you also need an effect. An effect is a combination of a vertex and pixel shaders (Effect in D3DX terminology, program in OpenGL). Due to differences in OpenGL and DirectX, the IRenderer has 3 methods for creating an effect - CreateEffectFromFile(const wchar_t* fileName, const char* technique)
, CreateEffectFromMemory(const char* code, const char* technique)
, CreateEffect(const char* vertexShaderCode, const char* pixelShaderCode)
, but in reality CreateEffectFromFile
and CreateEffectFromMemory
can only be used when the renderer is using DirectX, and CreateEffect
can only be used by the OpenGL renderer.
Each effect has various parameters that can be set. Currently the types supported are float, matrix and texture. You can obtain a handle to the parameter by calling the size_t GetParameterHandleByName(int effectId, const char* name)
method, and set a value for that parameter by using one of the Set***
functions, such as IRenderer::SetTexture
. When using an effect, any Set***
and Draw***
functions must take place in a IRenderer::EffectBegin
- IRenderer::EffectEnd
section.
There's also a convenience method, IRenderer::RenderFullscreenTexture
, which renders a texture on the whole screen (given its ID).
Other methods include enabling/disabling alpha blending, enabling/disabling the Z-buffer, clearing the backbuffer and so on. Check Renderer.h for the full list.
Any other special behavior should be documented in the header file for the specific renderer. For example, when using the OpenGL renderer, you should always call SetVertexBuffer
before SetVertexDeclaration
for it to work.
You can check the samples for an actual usage of the IRenderer
interface, or the FullScreenQuadOpenGL
/ FullScreenQuadDirectX
classes, located in Coherent/Libraries/Application/Detail/RendererOpenGL.cpp
/ Coherent/Libraries/Application/Detail/RendererDirectXWin.cpp
respectively.
In order to forward input events to Coherent UI, you must first convert them to Coherent UI input events, as defined in Coherent/UI/InputEvents.h
.
Converting mouse events is usually no problem, as they are represented by position, pressed buttons and wheel value, which can be easily obtained in most game frameworks.
Keyboard events, on the other side are different on each platform, and most games have their own events, handled by the input subsystem. Coherent UI uses Windows virtual key codes, which means that you have to convert your game specific key code to a Windows key code before sending it to Coherent UI. This is not always trivial, as you have to translate a native (or game) keyboard event to a Windows event. For example, printable characters, such as 'a', 'b', 'c', etc. must be sent as Char
events, while pressing the arrow keys, for example, must be KeyDown
events. This code is very game specific, and the best we can do is supply platform specific conversion utilities that you can adapt for your game.
For Windows, it's easiest to convert native events to Coherent events, since they are largely the same. If you have access to the message loop of the application, you can use Coherent/Libraries/Application/Detail/CoherentInputEventsConvertorWin.h
for converting key/mouse/wheel/touch events.
For Linux, we have supplied a sample conversion utility for key/mouse/wheel events in Coherent/Libraries/Application/Detail/CoherentInputEventsConvertorX11Linux.h
. It works with X11 events, but adapting it to GTK is trivial. The utility function translates X11 events to Windows events and also supports Unicode. It uses 2 auxiliary files, WindowsKeyCodeForKeyEventX11Linux.h
and KeySym2UCSLinux.h
for doing that.
By including Coherent/Libraries/Application/Entry.h
, you'll get predefined platform specific main functions. Those function call another function, called coherent_sample_main
, having the signature int (*)()
.
This is the main function that you have to define in your code and will be used as the entry point of the program.