EvasGLCube Sample Overview
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
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:
- Initialize the OpenGL ES.
The following example creates a shader:
static void init_evasgl(appdata_s *ad) { Ecore_Animator *ani; // 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); ani = ecore_animator_add(animate_cb, ad->img); evas_object_data_set(ad->img, "ani", ani); 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.
- 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); }
- 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(); }
- 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 is an example for a default update refresh rate:
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; }
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.
- 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 *ani = evas_object_data_get(ad->img, "ani"); ecore_animator_del(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); }