Pooka SDK Developer Guide
Rendering Cycle and Animation

Rendering Cycle Works Likes an Engine

The rendering cycle (or rendering kernel) is one of three important running stages of UI Thread.

When rendering cycle is running, it repeatedly repaint the application content as fast as possible. Every one repainting generates one frame, if the application content changes in each frame, the animation effect is generated.

When application is "still" and thus it does not needs animation, there is no need to keep rendering cycle running. In this case, the rendering cycle is paused and application does not repaint itself until the following things happen:

  • Function Application::repaint() is called
  • Application is resized
  • Application is partially obscured by other applications
  • Any situations make the platform decides to repaint the application

How the Application Decides to Keep Rendering Cycle Running

The application content is contained inside OGCanvas , the solid implementation of the rudimentary abstract class Window . The figure shows the hierarchical structure of Pooka SDK® application:

Application makes the rendering cycle running

If at least one visible OGCanvas (implementation of Window) is detected as animating, then the application keeps the rendering cycle running.

Application pause the rendering cycle

If none of visible OGCanvas (implementation of Window) is detected as animating, then the application pauses the rendering cycle.

 See  Window::startAnimation()
 See  Window::stopAnimation()
 See  Window::isAnimating()
 See  Window::visible

How Pooka SDK® Application Generates the Animation

Animation is one of most important function that an interactive application provides. Some applications may be mainly a "still" application, some others such as real-time games or multi-media applications are heavily relied on animation. OGCanvas provides all functions needed to play, stop and fine control animation at run-time. When animation is not play, application content appear to be still.

The Essence of Animation

Regardless of the platform, the essence of animation is simply rendering application content continuously and change the animated part of content in each rendering cycle. A single rendering result is usually referred to as an "animation frame". Animation logic needs to be performed between each two animation frames to make the content animated.

Alternative Way to Generate Animation

Using OGCanvas to generate animation is the recommended way for most of cases, however there are two more alternative solutions to generate animation, each has its own appropriate use case. The following chart illustrate the all three animation mechanisms available for Pooka SDK® application:

Animation Type Definition Implementation
Continuing Animation Continuing animation lasts for long time or even runs throughout the application life-cycle.
Example: a real-time game application, a video player
Implemented by OGCanvas
 Sample Project  Spinning Cube
Transient Animation Transient animation is a one-time animation and lasts only for a short period of time.
Example: a page transition, or a game piece sliding from one cell into a new cell on game board.
Implemented by a ScheduledTask . See Application::postTask() for more details.
 Sample Project  Animation
Thread-driven Animation Thread-driven animation is the animation driven by a worker thread. This type of animation is started, stopped and managed by the thread and best fit in multi-threading scenarios.
Example: showing an animating "waiting" cursor while retrieving remote data asynchronously.
Implemented by Thread
 Sample Project  Animation

Spinning Cube - a simple animation sample

There are several sample projects that can help developers understand the concept of animation in Pooka SDK® application. One of them, the Spinning Cube Sample Project, is a very simple application shows a 3D colored cube spinning along the Y-axis. This animation is implemented by the following steps.

Step 1 - Create the application content to display a 3D colored cube in 3D scene

We construct the 3D scene in OGCanvas::templateOnLoad() , once the content is ready, we call function Window::startAnimation() to make the OGCanvas animating.

void MainPage::templateOnLoad(wchar_t * documentBase, wchar_t * templateName)
{
// This is the place where we can do some initialization jobs for each template (content) just loaded
String strTemplateName(templateName); //Get the current template name
if(strTemplateName.equals(L"MainPage.properties"))
{
//Load 3D content: a cube located at (0.f, 0.f, 0.f)
const int SOLUTION = L'A'; //A: By template; B: by coding
if(SOLUTION == L'A')//Solution A: load the content by OGTemplate
{
wchar_t * strTemplate = //OmniGTemplate source (can also be a template asset)
L"scene3D = type:Scene3D, bkgColor:#303030, ambient:DARKGRAY, cameraLens:(50.f; 1.f; 300.f), cameraPos:(0.f; 20.f; -40.f), cameraLookAt:(0.f; 0.f; 0.f)\n"
L"light1 = type:Light, container:scene3D, dir:(0.5f; 0.5f; 1.f), color:WHITE\n"
L"grpCube = type:Group3D, container:scene3D, model:ColoredCube.obj, radius:10.f, detailLevel:HIGH_DETAIL\n";
this->loadTemplateByString(strTemplate, false);
}
else //Solution B: construct the content by coding
{
//Create Scene3D
Scene3D * pScene3D = new Scene3D(0, 0, this->width, this->height, 0x303030);
pScene3D->ambient = Color::DARKGRAY;
this->addScene(L"scene3D", pScene3D);
//Set up camera
Camera * pCamera = pScene3D->camera; //Warning: camera is only available after the Scene3D is added into OGCanvas
pCamera->setLens(50.f, 1.f, 300.f);
pCamera->setPos(0.f, 20.f, -40.f);
pCamera->setLookAt(0.f, 0.f, 0.f);
//Add directional light
Vector3f dir(0.5f, 0.5f, 1.f);
Light * pLight = new Light(dir, Color::WHITE, Color::WHITE);
pScene3D->addLight(L"light1", pLight);
//Add colored cube
Group3D * pGrpCube = OBJLoader::load(Application::getAssetsPath(), L"ColoredCube.obj", 10.f);
pGrpCube->setDetailLevel(Material3D::HIGH_DETAIL);
pScene3D->addGroup(L"grpCube", pGrpCube);
}
this->startAnimation(); //Lets the animation start
this->_runningTime = 0.f;
}
}

Step 2 - Spin the cube in each animation frame

To make cube appear to be spinning, we spin it by a small amount of angle in virtual function OGCanvas::animationPreRender()

void MainPage::animationPreRender(float interval)
{
//Spin the cube by a small angle in each animation frame
Group3D * pGrpCube = this->getGroup3D(L"grpCube");
float spinningAngle = interval * -1.f;
Matrix4f rotMat;
rotMat.rotY(spinningAngle);
pGrpCube->matrix.mul(rotMat);
}

Step 3 - Print the FPS (frame/second) rate

We print the FPS rate in the virtual function OGCanvas::animationPostRender()

void MainPage::animationPostRender(float interval)
{
/*
Note:
In this sample, we use GraphicsEngine class to draw FPS reading directly on the screen. The approach is
adding a SceneSprite and a Sprite used to display FPS, then display the FPS by Sprite text:
pSptFPS.setText(StrBuf(L"FPS = ").appendFormatted(L"%3.1f", timer->getFpsFloat()).toString());
*/
//Print the FPS (frame/second) reading on the screen
GraphicsEngine * pEngine = GraphicsEngine::instance;
Timer * timer = this->getTimer();
int x = Content::pointToPixel(10);
int y = Content::pointToPixel(5);
pEngine->drawText(x, y, StrBuf(L"FPS = ").appendFormatted(L"%3.1f", timer->getFpsFloat()).toString(), Color::YELLOW, Content::pointToPixel(15));
}

Step 4 - Do some low-priority task

Since low-priority task is the task that does not need to be executed in every animation frame, we do it in the virtual function OGCanvas::animationIdleTask() . In this sample, we accumulate the total running time and display it from time to time.

void MainPage::animationIdleTask(float interval)
{
/*
Note:
1. By default, the idle task is called at low frequency at every 2 seconds. The frequency can be changed by property
Timer::idleTaskInterval
2. Since the animationIdleTask() is not called in every animation frame, the text printed in this function does not "remain"
on the screen, as the result, user can only see some text quickly "flash" on the bottom of screen. The better approach is
adding a SceneSprite and a Sprite used to display running time, then display the running time by Sprite text:
pSptRunningTime.setText(StrBuf(L"Running Time = ").appendFormatted(L"%3.1f", this->_runningTime).toString());
This way, the text can be seen in every animation frame and is not flashing.
*/
//Print the total running time since application start
this->_runningTime += interval;
GraphicsEngine * pEngine = GraphicsEngine::instance;
int x = Content::pointToPixel(10);
int y = this->height - Content::pointToPixel(30);
pEngine->drawText(x, y, StrBuf(L"Running Time = ").appendFormatted(L"%3.1f", this->_runningTime).toString(), Color::YELLOW, Content::pointToPixel(15));
}

Step 5 - Respond to the pointing device event (mouse, touch) to toggle the animation

In the event listener function, we let the animation running by Window::startAnimation() or stopped by Window::stopAnimation() .

void MainPage::handleMouseEvent(Window * window, int eventId, int keyIndicator, int x, int y)
{
String templateName(getTemplateName()); // Get the current template name
if(eventId == Window::MOUSE_STATUS_UP) // The pointer device (mouse, finger) is released
{
//Toggle animation
if(this->isAnimating())
this->stopAnimation();
else
this->startAnimation();
}
...
}