Page Example

EvasGLCube Sample Overview

Mobile native

The EvasGLCube sample application demonstrates how to implement a rotatable cube on the screen using EvasGL with the Tizen SDK. The sample shows how to handle polygon geometry, and how to write a simple vertex and fragment shader for the cube. EvasGL is similar to the EGL™ layer and is related to GLView.

Note
The application presented in this overview is not a full Tizen application, since it does not use the Application Framework. The application simply starts and runs.

A 3D application using the OpenGL ES must use the GLView. EvasGL can be used in special cases, such as a multi-thread.

The following figure illustrates the main screen of the EvasGLCube.

Figure: EvasGLCube screen

EvasGLCube screen

Setting up the OpenGL ES Surface

The EvasGL interface is similar to the EGL interface. It can be connected to Evas so that OpenGL ES code works with Evas 2D objects.

This sample shows how to implement with EvasGL to use OpenGL ES:

Create the application:

#include <Evas_GL.h>
#include <Evas_GL_GLES2_Helpers.h>

The <Evas_GL.h> and <Evas_GL_GLES2_Helpers.h> header files are needed for Evas GL applications.

EVAS_GL_GLOBAL_GLES2_DEFINE();

The EvasGL or GLView program uses the wrapper function by the supported Evas GL. The wrapper function's interface is the same as OpenGL ES's. This macro can provide a set of convenience wrapper function tables.

Evas_Object *add_win(const char *name)
{
   Evas_Object *win;

   elm_config_accel_preference_set("opengl");
   win = elm_win_util_standard_add(name, "UI Template");
   if (!win)
      return NULL;

   evas_object_show(win);

   return win;
}

Before creating the Elm Window, set the GL Rendering Engine using the elm_config_accel_preference_set("opengl") function because EvasGL runs with EvasGL Render Engine.

Setting up Callbacks

To set up callbacks:

  1. Initialize the OpenGL ES.

    The following example creates a shader:

    static void init_evasgl(appdata_s *ad)
    {
       // Set config of the surface for Evas GL
       ad->cfg = evas_gl_config_new();
       ad->cfg->color_format = EVAS_GL_RGB_888;
       ad->cfg->depth_bits = EVAS_GL_DEPTH_BIT_24;
       ad->cfg->stencil_bits = EVAS_GL_STENCIL_NONE;
       ad->cfg->options_bits = EVAS_GL_OPTIONS_NONE;
    
       // Get the window size
       Evas_Coord w, h;
       evas_object_geometry_get(ad->win, NULL, NULL, &ad->surface_w, &ad->surface_h);
    
       // Get the Evas GL handle for doing GL things
       ad->evasgl = evas_gl_new(evas_object_evas_get(ad->win));
    
       // Create a surface and context
       ad->sfc = evas_gl_surface_create(ad->evasgl, ad->cfg, ad->surface_w, ad->surface_h);
       ad->ctx = evas_gl_context_create(ad->evasgl, NULL);
    
       EVAS_GL_GLOBAL_GLES2_USE(ad->evasgl, ad->ctx);
    
       // Set rotation variables
       ad->xangle = 45.0f;
       ad->yangle = 45.0f;
       ad->mouse_down = EINA_FALSE;
       ad->initialized = EINA_FALSE;
    
       ad->img = evas_object_image_filled_add(evas_object_evas_get(ad->conform));
       evas_object_event_callback_add(ad->img, EVAS_CALLBACK_DEL, img_del_cb, ad);
       evas_object_image_pixels_get_callback_set(ad->img, img_pixel_cb, ad);
    
       // Add mouse event callbacks
       evas_object_event_callback_add(ad->img, EVAS_CALLBACK_MOUSE_DOWN, mouse_down_cb, ad);
       evas_object_event_callback_add(ad->img, EVAS_CALLBACK_MOUSE_UP, mouse_up_cb, ad);
       evas_object_event_callback_add(ad->img, EVAS_CALLBACK_MOUSE_MOVE, mouse_move_cb, ad);
    
       ad->ani = ecore_animator_add(animate_cb, ad->img);
       elm_object_content_set(ad->conform, ad->img);
    }
    

    To use EvasGL, create it, set its configuration, and create the Evas GL surface and context. For rendering, connect EvasGL with the Evas Object Image using the evas_object_image_native_surface_set() function.

  2. Resize callbacks.

    The EvasGL resize behavior is related to Evas Object Image or Elm Window. In the following example, the Elm Window resize callback is used. When resizing occurs, EvasGL Surface is recreated and reconnected to Evas Object Image.

    static void win_resize_cb(void *data, Evas *e , Evas_Object *obj , void *event_info)
    {
       appdata_s *ad = data;
    
       if (ad->sfc) 
       {
          evas_object_image_native_surface_set(ad->img, NULL);
          evas_gl_surface_destroy(ad->evasgl, ad->sfc);
          ad->sfc = NULL;
       }
    
       evas_object_geometry_get(obj, NULL, NULL, &ad->surface_w, &ad->surface_h);
       evas_object_image_size_set(ad->img, ad->surface_w, ad->surface_h);
       evas_object_resize(ad->img, ad->surface_w, ad->surface_h);
       evas_object_show(ad->img);
    
       if (!ad->sfc) 
       {
          Evas_Native_Surface ns;
    
          ad->sfc = evas_gl_surface_create(ad->evasgl, ad->cfg, ad->surface_w, ad->surface_h);
          evas_gl_native_surface_get(ad->evasgl, ad->sfc, &ns);
          evas_object_image_native_surface_set(ad->img, &ns);
          evas_object_image_pixels_dirty_set(ad->img, EINA_TRUE);
       }
    }
    
  3. Draw callbacks.

    The Evas GL draw callback function is registered by the following Evas Object Image function:

    evas_object_image_pixels_get_callback_set(ad->img, img_pixel_cb, ad);
    

    This callback function is provided for on-demand mode. Especially, if Evas GL uses EVAS_GL_OPTIONS_DIRECT, all OpenGL ES options are called in this callback function.

    static void img_pixel_cb(void *data, Evas_Object *obj)
    {
       // Define the model view projection matrix
       float model[16], mvp[16];
       static float view[16];
       appdata_s *ad = data;
    
       Evas_Coord w, h;
       evas_object_image_size_get(obj, &w, &h);
    
       // Set up the context and surface as the current one
       evas_gl_make_current(ad->evasgl, ad->sfc, ad->ctx);
    
       // Initialize GL just once
       if (ad->initialized == EINA_FALSE) 
       {
          float aspect;
          init_shaders(ad);
          glGenBuffers(1, &ad->vbo);
          glBindBuffer(GL_ARRAY_BUFFER, ad->vbo);
          glBufferData(GL_ARRAY_BUFFER, 3 * 72 * 4, cube_vertices, GL_STATIC_DRAW);
          init_matrix(view);
          if (ad->surface_w > ad->surface_h) 
          {
             aspect = (float)ad->surface_w / ad->surface_h;
             view_set_ortho(view, -1.0 * aspect, 1.0 * aspect, -1.0, 1.0, -1.0, 1.0);
          }
          else 
          {
             aspect = (float)ad->surface_h / ad->surface_w;
             view_set_ortho(view, -1.0, 1.0, -1.0 * aspect,  1.0 *aspect, -1.0, 1.0);
          }
          ad->initialized = EINA_TRUE;
       }
    
       glViewport(0, 0, ad->surface_w, ad->surface_h);
       glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
       init_matrix(model);
       rotate_xyz(model, ad->xangle, ad->yangle, 0.0f);
       multiply_matrix(mvp, view, model);
    
       glUseProgram(ad->program);
       glBindBuffer(GL_ARRAY_BUFFER, ad->vbo);
       glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, 0);
       glEnableVertexAttribArray(0);
    
       glBindBuffer(GL_ARRAY_BUFFER, ad->vbo);
       glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*)(sizeof(float)*3));
       glEnableVertexAttribArray(1);
    
       glUniformMatrix4fv(glGetUniformLocation(ad->program, "mvpMatrix"), 1, GL_FALSE, mvp);
       glDrawArrays(GL_TRIANGLES, 0, 36);
    
       glFlush();
    }
    
  4. Add animators.

    The scene does not get updated unless the object is marked as such. Games usually use an animator to have a regular update of the scene. The following example shows the default update refresh rate.

    The animator callback function is also triggered when the display is off. Use the ecore_animator_freeze() and ecore_animator_thaw() functions in the app_pause_cb and app_resume_cb callbacks for power saving.

    static Eina_Bool animate_cb(void *data)
    {
       Evas_Object *img = data;
    
       // Animate here when an animation tick happens and mark the image as "dirty"
       // meaning it needs an update next time Evas renders it
       evas_object_image_pixels_dirty_set(img, EINA_TRUE);
    
       return ECORE_CALLBACK_RENEW;
    }
    
    static void
    app_pause(void *data)
    {
       appdata_s *ad = data;
       ecore_animator_freeze(ad->ani);
    }
    
    static void
    app_resume(void *data)
    {
       appdata_s *ad = data;
       ecore_animator_thaw(ad->ani);
    }
    

    The evas_object_image_pixels_dirty_set() function sets the dirty bit of the Evas Object image which is connected Evas GL. After that, the evas_object_image_pixels_get_callback_set() callback function is called whenever Evas renders.

  5. Delete callbacks.

    The delete callback is triggered when the EvasGL is destroyed from the main loop. No other callback can be called on the same object afterwards.

    static void img_del_cb(void *data, Evas *e , Evas_Object *obj , void *event_info)
    {
       appdata_s *ad = data;
       ecore_animator_del(ad->ani);
    
       // Free the GL resources when image object is deleted
       evas_gl_make_current(ad->evasgl, ad->sfc, ad->ctx);
    
       glDeleteShader(ad->vtx_shader);
       glDeleteShader(ad->fgmt_shader);
       glDeleteProgram(ad->program);
       glDeleteBuffers(1, &ad->vbo);
    
       evas_gl_surface_destroy(ad->evasgl, ad->sfc);
       evas_gl_context_destroy(ad->evasgl, ad->ctx);
       evas_gl_config_free(ad->cfg);
    
       evas_gl_free(ad->evasgl);
    }