Setting up a basic OpenGL ES application

This section covers some of the basics of OpenGL ES on the BlackBerry 10 OS and the BlackBerry PlayBook OS. We have three main APIs we're using for a basic OpenGL ES application, the native window (Screen API), EGL, and a version of OpenGL ES. To render OpenGL ES models to the native window, we need to connect the APIs. Once we establish a connection between the native window and EGL, we can use OpenGL ES to render to EGL surfaces.

This section also covers the native windowing system, which is used for both Cascades and core native applications. Core native applications use the Navigator API (lifecycle and state manager) and BlackBerry Platform Services events, while Cascades applications handle these lifecycle events through other means. There is also a section about the bbutil API, which you can find in the OpenGL ES project templates. To use the bbutil API, you will need to create a new project with the BlackBerry 10 OpenGL ES project template.

Adding a native window

The native windowing system that the BlackBerry 10 OS and the BlackBerry PlayBook OS use is the Screen API (also known as libscreen), which is defined in screen.h. The Screen API is a resource manager that provides compositing and low-level windowing services. You use this native windowing system and EGL to render OpenGL ES models to the screen.

Add the Screen API

To include the Screen API in your application, add the following line to your code:

#include <screen/screen.h>

To link the Screen API to your application, in the Momentics IDE for BlackBerry, complete the following:

  1. In the Project Explorer view, right-click a project and select Configure > Add Library. Alternatively, you can select a project, and on the Project menu, click Add Library.
  2. In the Add Library Dependency dialog, select Standard BlackBerry platform library. Click Next.
  3. In the Standard BlackBerry platform library dialog box, expand Graphics and windowing, then click Screen.

If you receive any errors with the linker settings, consult the documentation depending on your project build. If you're using a makefile build, see the sections under Conventions for Recursive Makefiles and Directories. If you're using a managed build, take a look at Manage projects.

Set up a native window

To set up a native window, we start by creating a screen context and then creating a window from that context.
    screen_context_t screen_ctx;
    screen_window_t screen_win;
    
    screen_create_context(&screen_ctx, 0);
    screen_create_window(&screen_win, screen_ctx);
We set a usage variable for properties of the window depending on our application's needs. These properties are predefined enumerations from screen.h. For a basic native window, we set the usage variable to SCREEN_USAGE_NATIVE. We must set the usage property to correspond to the the version of OpenGL ES we're using. We use SCREEN_USAGE_OPENGL_ES1 for OpenGL ES 1.1, SCREEN_USAGE_OPENGL_ES2 for OpenGL ES 2.0, and SCREEN_USAGE_OPENGL_ES3 for OpenGL ES 3.0. To combine flags into a variable, use a bitwise OR (usage = SCREEN_USAGE_OPENGL_ES2 | SCREEN_USAGE_ROTATION). As an example, we'll use OpenGL ES 1.1 and allow screen rotations.
    const int usage = SCREEN_USAGE_OPENGL_ES1 | SCREEN_USAGE_ROTATION;
    screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_USAGE, &usage);

And that's the basic window setup! Now, we can attach buffers to the window and post them to the window.

Adding buffers to the window

To create and attach buffers to your window, you can call screen_create_window_buffers() to let libscreen create the buffers and attach them to the window.

    screen_create_window_buffers(screen_win, 2);

All BlackBerry 10 applications must use two window buffers to avoid visual glitches, such as tearing and other artifacts. This can happen when the application changes state, such as from a maximized application to minimized application.

All BlackBerry 10 application windows require you to have at least one buffer created or attached to be visible; however, using one buffer risks tearing, visual glitches, and other artifacts. The Screen API blends windows together due to transparency values of windows or when windows transition from navigator. A single-buffered window that is actively drawing (at least 60 fps) likely gets visual glitches, because the application is rendering to the same buffer as the compositor is using.

In a double-buffered window, the application renders to the back buffer, and the compositor samples the front buffer.

Query the buffer and post the window

Once we attach the buffers to the window, we obtain the buffer to use by querying the SCREEN_PROPERTY_RENDER_BUFFERS bitmask. In the code sample below, the rect array acts as the bounds of the screen buffer: the first two values are the x and y coordinates of top-left corner, and the next two values are the x and y coordinates of bottom-right corner.

    int rect[4] = { 0, 0, 0, 0 };
        screen_buffer_t screen_buf = NULL;

    screen_get_window_property_pv(screen_win, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)&screen_buf);
    screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_BUFFER_SIZE, rect+2);

Now, we can make changes to the buffer. To post to changes to the window, we call the screen_post_window() function, passing in the window, buffers, the number of bounding rectangles, the rect array, and any posting flags.

    screen_post_window(screen_win, screen_buf, 1, rect, 0);

Clean up your native window

When your app doesn't need the window and context anymore, you clean them up by using the following code:

    screen_destroy_window(screen_win);
    screen_destroy_context(screen_ctx);

For more information about using the Screen API, take a look at the Screen API.

Adding the lifecycle manager

The Navigator API, defined in navigator.h, manages your application's lifecycle events. The Navigator API is a high-level windowing framework that handles many aspects of the application's lifecycle including application window resizing, permissions, focus control, orientation changes, application start-termination, and more.

The navigator service is provided by BlackBerry Platform Services library. The BlackBerry Platform Services library also contains an event API for some of its service, such as navigator or screen. You will also need to include the BlackBerry Platform Services event API to obtain navigator and screen events. For more information on navigator event including life cycle events, see the App Life Cycle section.

Add the libraries

To include the Navigator API, BlackBerry Platform Services event APIs, and Screen API in your application, add the following lines to your code:

#include <bps/navigator.h>
#include <bps/event.h>
#include <bps/screen.h>
#include <screen/screen.h>

To link the APIs in your application, in the Momentics IDE for BlackBerry, complete the following:

  1. In the Project Explorer view, right-click a project and select Configure > Add Library. Alternatively, you can select a project, and on the Project menu, click Add Library.
  2. In the Add Library Dependency dialog, select Standard BlackBerry platform library > click Next.
  3. In the Standard BlackBerry platform library dialog box, expand Common Services, then click BlackBerry Platform Services.

If you have any trouble loading with the linker settings, consult the documentation depending on your project build. If you're using a makefile build, see the sections under Conventions for Recursive Makefiles and Directories. If you're using a managed build, take a look at Manage projects.

Set up the navigator and event APIs

To add the Navigator API to your application, you need to call bps_initialize() to start the BlackBerry Platform Services (BPS). Then, we need to start receiving navigator events using the navigator_request_events() function. A parameter of 0 indicates that all regular events are requested. To request screen events, we call screen_request_events(), passing the initialized screen context.

screen_context_t screen_ctx;
bps_initialize();
navigator_request_events(0);
screen_request_events(screen_ctx);

Next, we need a main application loop to check for events. We use a flag variable for the loop condition to be set once the NAVIGATOR_EXIT event is received. We need a bps_event_t structure to hold the event, and then we can make a call to bps_get_event() to obtain the event. We use a negative timeout value to block until an event is available.

    int check = 0;

    while ( check == 0 ) {
        int rc, domain;

        bps_event_t *event = NULL;
        
        rc = bps_get_event(&event, -1);

To process the event, we get the event's domain code by calling bps_event_get_domain(event). We check the event against the domains we're looking for, so in this code sample, we'll handle only navigator and screen domain events. To check for navigator and screen domains, we verify if domain == navigator_get_domain() or if domain == screen_get_domain(), respectively, for each type. If we have the proper domain, we have an event to handle. We obtain the event code by calling bps_event_get_code(event) and use a switch statement to separate the event cases. You'll notice in the following code sample, for the Navigator API, we only handle swipe down and exit events. And, for the Screen API, we'll handle touch, move, and release events.

       if (event) {
            domain = bps_event_get_domain(event);
            if (domain == navigator_get_domain()) {
                switch (bps_event_get_code(event)) {
                case NAVIGATOR_SWIPE_DOWN:
                    fprintf(stderr,"Swipe down event");
                    break;
                case NAVIGATOR_EXIT:
                    fprintf(stderr,"Exit event");
                    check = 1;
                    break;
                default:
                    break;
                }
                fprintf(stderr,"\n");
            } else if (domain == screen_get_domain()) {
                    int screen_val;

                    screen_event_t screen_event = screen_event_get_event(event);
                    screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &screen_val);

                    switch (screen_val) {
                    case SCREEN_EVENT_MTOUCH_TOUCH:
                        fprintf(stderr,"Touch event");
                        break;
                    case SCREEN_EVENT_MTOUCH_MOVE:
                        fprintf(stderr,"Move event");
                        break;
                    case SCREEN_EVENT_MTOUCH_RELEASE:
                        fprintf(stderr,"Release event");
                        break;
                    default:
                        break;
                    }
                    fprintf(stderr,"\n");            }
            }
    }

For more information on the event codes for the Navigator API, take a look at the API reference for data types, which contains main event codes, window state events, and other events.

For more information on the event codes for the Screen API, see Screen event types.

Clean up the navigator and event APIs

Once we exit our main application loop, we need some cleanup to ensure a proper application exit. In this case, we need to stop receiving screen events by calling screen_stop_events(screen_ctx) and turn off the BlackBerry Platform Services services, which will also stop navigator events. If you're receiving other event types that are not included in this code sample, make sure you request to stop receiving events for that type.

screen_stop_events(screen_ctx);
bps_shutdown();

For more information regarding navigator, see the Navigator API reference.

Using OpenGL ES in your core application

When you use OpenGL ES in your application, you need to add the library into your code. If you're using OpenGL ES in your application, you will always need to include EGL and the Screen API, but the versions of OpenGL ES that are supported on BlackBerry 10 have different settings you need to include per version.

Add libraries

Add the following include directives depending on your application needs:

  • EGL
    #include <EGL/egl.h>
  • OpenGL ES 1.1 and extensions library
    #include <GLES/gl.h>
    #include <GLES/glext.h> /*OpenGL ES extensions library*/
  • OpenGL ES 2.0
    #include <GLES2/gl2.h>
  • OpenGL ES 3.0
    #include <GLES3/gl3.h>

To link the APIs to your application, in the Momentics IDE for BlackBerry, complete the following:

  1. In the Project Explorer view, right-click a project and select Configure > Add Library. Alternatively, you can select a project, and on the Project menu, click Add Library.
  2. In the Add Library Dependency dialog, select Standard BlackBerry platform library > click Next.
  3. In the Standard BlackBerry platform library dialog, expand Graphics and windowing, then select one of the following options depending on what version of OpenGL ES you're using:
    • To link OpenGL ES 1.1, select OpenGL ES 1.1 -libEGL. This will include OpenGL ES 1.1 and EGL.
    • To link OpenGL ES 2.0 or 3.0, select OpenGL ES 2 -libGLESv2. This will include OpenGL ES 2.0, 3.0, and EGL.

If you have any trouble loading with the linker settings, consult the documentation depending on your project build. If you're using a makefile build, see the sections under Conventions for Recursive Makefiles and Directories. If you're using a managed build, take a look at Manage projects.

Use OpenGL ES templates to start your project

The Momentics IDE provides two sample OpenGL ES projects that you can use to build your application. Each sample project corresponds to an OpenGL ES version, 1.1 or 2.0. The advantage of using a template is it provides boilerplate code to help you set up your application for OpenGL ES. To open a template, in the Momentics IDE do the following:
  1. Click File and select New > Example.
  2. In the New Example dialog box, select BlackBerry Example Project.
  3. Click Next.
  4. Locate and select BlackBerry 10 OS OpenGL ES <version> Application Template Project, where <version> is the version you want.
  5. Accept the default settings for the screens that follow, then click Finish to close the dialog box.
The bbutil API is a version-independent API, so you can use it for both OpenGL ES 1.1 and 2.0. If you import the bbutil API into a separate project, the API requires that you set a flag in your common.mk file or define a macro in your code to specify a version to use.
  • If you're using OpenGL ES 1.1, do one of the following:
    • Add the following line to your common.mk file: CCFLAGS+=-DUSING_GL11
    • Add the following line to bbutil.c immediately after the inclusion libraries: #define USING_GL11
  • If you're using OpenGL ES 2.0, do one of the following:
    • Add the following line to your common.mk file: CCFLAGS+=-DUSING_GL20
    • Add the following line to bbutil.c immediately after the inclusion libraries: #define USING_GL20

Find sample apps using the Native SDK APIs

If you're looking for some code using the Native SDK APIs and OpenGL ES, take a look at the following:

In your Cascades app

Using OpenGL ES in a Cascades-based application is different then your core application. For more information on using OpenGL ES in a Cascades app, see OpenGL ES.

Last modified: 2014-08-07



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus