/** * Example of working with OpenGL APIs in Evas. * * Evas_GL.h should be added for Evas GL APIs access. * * @verbatim * gcc -o evas-gl evas-gl.c `pkg-config --libs --cflags evas ecore ecore-evas` -lm * @endverbatim */ #include #include #include #include #define WIDTH 500 #define HEIGHT 500 //GL related data here... typedef struct _GLData { Evas_Object *win; Evas_GL *evasgl; Evas_GL_API *glapi; Evas_GL_Context *ctx; Evas_GL_Surface *sfc; Evas_Object *img; unsigned int program; unsigned int vtx_shader; unsigned int fgmt_shader; unsigned int vbo; float view[16]; float xangle; float yangle; Eina_Bool mouse_down : 1; Eina_Bool initialized : 1; } GLData; static GLData gldata; //Define the cube's vertices //Each vertex consist of x, y, z, r, g, b const float cube_vertices[] = { //front surface is blue 0.5, 0.5, 0.5, 0.0, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 0.5, 0.5, 0.5, 0.0, 0.0, 1.0, -0.5, 0.5, 0.5, 0.0, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, 1.0, //left surface is green -0.5, 0.5, 0.5, 0.0, 1.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, 0.0, -0.5, -0.5, 0.5, 0.0, 1.0, 0.0, -0.5, 0.5, 0.5, 0.0, 1.0, 0.0, -0.5, 0.5, -0.5, 0.0, 1.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, 0.0, //top surface is red -0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 0.5, 0.5, -0.5, 1.0, 0.0, 0.0, -0.5, 0.5, -0.5, 1.0, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 0.5, 0.5, -0.5, 1.0, 0.0, 0.0, //right surface is yellow 0.5, 0.5, -0.5, 1.0, 1.0, 0.0, 0.5, -0.5, 0.5, 1.0, 1.0, 0.0, 0.5, -0.5, -0.5, 1.0, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 0.0, 0.5, -0.5, 0.5, 1.0, 1.0, 0.0, //back surface is cyan -0.5, 0.5, -0.5, 0.0, 1.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, 1.0, 0.5, 0.5, -0.5, 0.0, 1.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 1.0, //bottom surface is magenta -0.5, -0.5, -0.5, 1.0, 0.0, 1.0, 0.5, -0.5, 0.5, 1.0, 0.0, 1.0, -0.5, -0.5, 0.5, 1.0, 0.0, 1.0, -0.5, -0.5, -0.5, 1.0, 0.0, 1.0, 0.5, -0.5, -0.5, 1.0, 0.0, 1.0, 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 }; //Vertext Shader Source static const char vertex_shader[] = "attribute vec4 vPosition;\n" "attribute vec3 inColor;\n" "uniform mat4 mvpMatrix;" "varying vec3 outColor;\n" "void main()\n" "{\n" " outColor = inColor;\n" " gl_Position = mvpMatrix * vPosition;\n" "}\n"; //Fragment Shader Source static const char fragment_shader[] = "#ifdef GL_ES\n" "precision mediump float;\n" "#endif\n" "varying vec3 outColor;\n" "void main()\n" "{\n" " gl_FragColor = vec4 ( outColor, 1.0 );\n" "}\n"; //Rotation Operation related functions here... static void init_matrix(float matrix[16]) { matrix[0] = 1.0f; matrix[1] = 0.0f; matrix[2] = 0.0f; matrix[3] = 0.0f; matrix[4] = 0.0f; matrix[5] = 1.0f; matrix[6] = 0.0f; matrix[7] = 0.0f; matrix[8] = 0.0f; matrix[9] = 0.0f; matrix[10] = 1.0f; matrix[11] = 0.0f; matrix[12] = 0.0f; matrix[13] = 0.0f; matrix[14] = 0.0f; matrix[15] = 1.0f; } static void multiply_matrix(float matrix[16], const float matrix0[16], const float matrix1[16]) { int i; int row; int column; float temp[16]; for (column = 0; column < 4; column++) { for (row = 0; row < 4; row++) { temp[(column * 4) + row] = 0.0f; for (i = 0; i < 4; i++) temp[(column * 4) + row] += matrix0[(i * 4) + row] * matrix1[(column * 4) + i]; } } for (i = 0; i < 16; i++) matrix[i] = temp[i]; } static void rotate_xyz(float matrix[16], const float anglex, const float angley, const float anglez) { const float pi = 3.141592f; float temp[16]; float rx = 2.0f * pi * anglex / 360.0f; float ry = 2.0f * pi * angley / 360.0f; float rz = 2.0f * pi * anglez / 360.0f; float sy = sinf(ry); float cy = cosf(ry); float sx = sinf(rx); float cx = cosf(rx); float sz = sinf(rz); float cz = cosf(rz); init_matrix(temp); temp[0] = (cy * cz) - (sx * sy * sz); temp[1] = (cz * sx * sy) + (cy * sz); temp[2] = -cx * sy; temp[4] = -cx * sz; temp[5] = cx * cz; temp[6] = sx; temp[8] = (cz * sy) + (cy * sx * sz); temp[9] = (-cy * cz * sx) + (sy * sz); temp[10] = cx * cy; multiply_matrix(matrix, matrix, temp); } static int view_set_ortho(float result[16], const float left, const float right, const float bottom, const float top, const float near_, const float far_) { if (EINA_FLT_EQ((right - left), 0.0f) || EINA_FLT_EQ((top - bottom), 0.0f) || EINA_FLT_EQ((far_ - near_), 0.0f)) return 0; result[0] = 2.0f / (right - left); result[1] = 0.0f; result[2] = 0.0f; result[3] = 0.0f; result[4] = 0.0f; result[5] = 2.0f / (top - bottom); result[6] = 0.0f; result[7] = 0.0f; result[8] = 0.0f; result[9] = 0.0f; result[10] = -2.0f / (far_ - near_); result[11] = 0.0f; result[12] = -(right + left) / (right - left); result[13] = -(top + bottom) / (top - bottom); result[14] = -(far_ + near_) / (far_ - near_); result[15] = 1.0f; return 1; } static void init_shaders(GLData *gl_data) { Evas_GL_API *gl = gl_data->glapi; const char *p; p = vertex_shader; gl_data->vtx_shader = gl->glCreateShader(GL_VERTEX_SHADER); gl->glShaderSource(gl_data->vtx_shader, 1, &p, NULL); gl->glCompileShader(gl_data->vtx_shader); p = fragment_shader; gl_data->fgmt_shader = gl->glCreateShader(GL_FRAGMENT_SHADER); gl->glShaderSource(gl_data->fgmt_shader, 1, &p, NULL); gl->glCompileShader(gl_data->fgmt_shader); gl_data->program = gl->glCreateProgram(); gl->glAttachShader(gl_data->program, gl_data->vtx_shader); gl->glAttachShader(gl_data->program, gl_data->fgmt_shader); gl->glBindAttribLocation(gl_data->program, 0, "vPosition"); gl->glBindAttribLocation(gl_data->program, 1, "inColor"); gl->glLinkProgram(gl_data->program); gl->glUseProgram(gl_data->program); gl->glEnable(GL_DEPTH_TEST); } static void img_pixel_cb(void *data, Evas_Object *obj) { //Define the model view projection matrix float model[16], mvp[16]; GLData *gl_data = data; Evas_GL_API *glapi = gl_data->glapi; 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(gl_data->evasgl, gl_data->sfc, gl_data->ctx); //Initialize gl stuff just one time. if (!gl_data->initialized) { float aspect; init_shaders(gl_data); glapi->glGenBuffers(1, &gl_data->vbo); glapi->glBindBuffer(GL_ARRAY_BUFFER, gl_data->vbo); glapi->glBufferData(GL_ARRAY_BUFFER, 3 * 72 * 4, cube_vertices,GL_STATIC_DRAW); init_matrix(gl_data->view); if(w > h) { aspect = (float)w/h; view_set_ortho(gl_data->view, -1.0 * aspect, 1.0 * aspect, -1.0, 1.0, -1.0, 1.0); } else { aspect = (float)h/w; view_set_ortho(gl_data->view, -1.0, 1.0, -1.0 * aspect, 1.0 * aspect, -1.0, 1.0); } gl_data->initialized = EINA_TRUE; } glapi->glViewport(0, 0, w, h); glapi->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glapi->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); init_matrix(model); rotate_xyz(model, gl_data->xangle, gl_data->yangle, 0.0f); multiply_matrix(mvp, gl_data->view, model); glapi->glUseProgram(gl_data->program); glapi->glBindBuffer(GL_ARRAY_BUFFER, gl_data->vbo); glapi->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, 0); glapi->glEnableVertexAttribArray(0); glapi->glBindBuffer(GL_ARRAY_BUFFER, gl_data->vbo); glapi->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*)(sizeof(float)*3)); glapi->glEnableVertexAttribArray(1); glapi->glUniformMatrix4fv( glapi->glGetUniformLocation(gl_data->program, "mvpMatrix"), 1, GL_FALSE, mvp); glapi->glDrawArrays(GL_TRIANGLES, 0, 36); glapi->glFinish(); } static void img_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { GLData *gl_data = data; Evas_GL_API *glapi = gl_data->glapi; //Free the gl resources when image object is deleted. evas_gl_make_current(gl_data->evasgl, gl_data->sfc, gl_data->ctx); glapi->glDeleteShader(gl_data->vtx_shader); glapi->glDeleteShader(gl_data->fgmt_shader); glapi->glDeleteProgram(gl_data->program); glapi->glDeleteBuffers(1, &gl_data->vbo); evas_gl_surface_destroy(gl_data->evasgl, gl_data->sfc); evas_gl_context_destroy(gl_data->evasgl, gl_data->ctx); evas_gl_free(gl_data->evasgl); } static Eina_Bool _animator_cb(void *data) { Evas_Object *img = data; //Animate here whenever an animation tick happens and then mark the image as //"dirty" meaning it needs an update next time evas renders. it will call the //pixel get callback then. evas_object_image_pixels_dirty_set(img, EINA_TRUE); //If you know the specific region to be updated, specify here for the patial rendering. //Otherwise, full image region will be redrawn on the canvas. Evas_Coord w, h; evas_object_geometry_get(img, NULL, NULL, &w, &h); evas_object_image_data_update_add(img, 0, 0, w, h); return ECORE_CALLBACK_RENEW; } static void _mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { GLData *gl_data = data; gl_data->mouse_down = EINA_TRUE; } static void _mouse_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Mouse_Move *ev; ev = (Evas_Event_Mouse_Move *)event_info; GLData *gl_data = data; float dx = 0, dy = 0; if( gl_data->mouse_down) { dx = (ev->cur.canvas.x - ev->prev.canvas.x); dy = (ev->cur.canvas.y - ev->prev.canvas.y); gl_data->xangle += dy; gl_data->yangle += dx; } } static void _mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { GLData *gl_data = data; gl_data->mouse_down = EINA_FALSE; } static void _win_resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { float aspect; Evas_Coord w,h; evas_object_geometry_get( obj, NULL, NULL, &w, &h); GLData *gl_data = data; evas_object_resize(gl_data->img, w, h); init_matrix(gl_data->view); if(w > h) { aspect = (float)w/h; view_set_ortho(gl_data->view, (-1.0 * aspect), (1.0 * aspect), -1.0, 1.0, -1.0, 1.0); } else { aspect = (float)h/w; view_set_ortho(gl_data->view, -1.0, 1.0, (-1.0 * aspect), (1.0 * aspect), -1.0, 1.0); } } static void evas_gl_exam(Evas_Object *win) { Evas_Native_Surface ns; //Config for the surface for evas gl Evas_GL_Config *config = evas_gl_config_new(); config->color_format = EVAS_GL_RGBA_8888; config->depth_bits = EVAS_GL_DEPTH_BIT_24; //Get the window size Evas_Coord w,h; evas_object_geometry_get(win, NULL, NULL, &w, &h); //Get the evas gl handle for doing gl things gldata.evasgl = evas_gl_new(evas_object_evas_get(win)); gldata.glapi = evas_gl_api_get(gldata.evasgl); //Create a surface and context gldata.sfc = evas_gl_surface_create(gldata.evasgl, config, w, h); gldata.ctx = evas_gl_context_create(gldata.evasgl, NULL); evas_gl_config_free(config); //Set rotation variables gldata.xangle = 45.0f; gldata.yangle = 45.0f; gldata.mouse_down = EINA_FALSE; //Set up the image object. A filled one by default. gldata.img = evas_object_image_filled_add(evas_object_evas_get(win)); evas_object_event_callback_add(gldata.img, EVAS_CALLBACK_DEL, img_del_cb, &gldata); //Set up an actual pixel size for the buffer data. It may be different to the //output size. Any windowing system has something like this, just evas has 2 //sizes, a pixel size and the output object size. evas_object_image_size_set(gldata.img, w, h); //Set up the native surface info to use the context and surface created //above. evas_gl_native_surface_get(gldata.evasgl, gldata.sfc, &ns); evas_object_image_native_surface_set(gldata.img, &ns); evas_object_image_pixels_get_callback_set(gldata.img, img_pixel_cb, &gldata); evas_object_resize(gldata.img, w, h); evas_object_show(gldata.img); //Add Mouse Event Callbacks evas_object_event_callback_add(gldata.img, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, &gldata); evas_object_event_callback_add(gldata.img, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, &gldata); evas_object_event_callback_add(gldata.img, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move_cb, &gldata); //Add Window Resize Event Callback evas_object_event_callback_add(win, EVAS_CALLBACK_RESIZE, _win_resize_cb, &gldata); ecore_animator_add(_animator_cb, gldata.img); } static void _on_delete_cb(Ecore_Evas *ee EINA_UNUSED) { ecore_main_loop_quit(); } static void _on_canvas_resize_cb(Ecore_Evas *ee) { int w, h; ecore_evas_geometry_get(ee, NULL, NULL, &w, &h); evas_object_resize(gldata.win, w, h); } int main(void) { if (!ecore_evas_init()) return 0; Ecore_Evas *ecore_evas = ecore_evas_new("opengl_x11", 0, 0, WIDTH, HEIGHT, NULL); if(!ecore_evas) return 0; ecore_evas_callback_delete_request_set(ecore_evas, _on_delete_cb); ecore_evas_callback_resize_set(ecore_evas, _on_canvas_resize_cb); ecore_evas_show(ecore_evas); Evas_Object *evas = ecore_evas_get(ecore_evas); gldata.win = evas_object_rectangle_add(evas); evas_object_color_set(gldata.win, 255, 255, 255, 255); evas_object_resize(gldata.win, WIDTH, HEIGHT); evas_object_show(gldata.win); evas_gl_exam(gldata.win); ecore_main_loop_begin(); ecore_evas_free(ecore_evas); ecore_evas_shutdown(); return 0; }