diff --git a/src/examples/evas/Makefile.am b/src/examples/evas/Makefile.am index 72fd97f47f..24d3bc45ba 100644 --- a/src/examples/evas/Makefile.am +++ b/src/examples/evas/Makefile.am @@ -217,6 +217,11 @@ evas_3d_obj_SOURCES = evas-3d-obj.c evas_3d_obj_LDADD = $(ECORE_EVAS_COMMON_LDADD) @EFL_PTHREAD_LIBS@ evas_3d_obj_CPPFLAGS = $(ECORE_EVAS_COMMON_CPPFLAGS) +EXTRA_PROGRAMS += evas_gl +evas_gl_SOURCES = evas-gl.c +evas_gl_LDADD = $(ECORE_EVAS_COMMON_LDADD) @EFL_PTHREAD_LIBS@ +evas_gl_CPPFLAGS = $(ECORE_EVAS_COMMON_CPPFLAGS) + .edc.edj: $(AM_V_EDJ)$(EDJE_CC) $(EDJE_CC_FLAGS) $< $(builddir)/$(@F) diff --git a/src/examples/evas/evas-gl.c b/src/examples/evas/evas-gl.c new file mode 100644 index 0000000000..6593d64ea4 --- /dev/null +++ b/src/examples/evas/evas-gl.c @@ -0,0 +1,488 @@ +//Add Evas_GL.h for Evas GL APIs access. +#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 ((right - left) == 0.0f || (top - bottom) == 0.0f || (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 *gldata) +{ + Evas_GL_API *gl = gldata->glapi; + const char *p; + + p = vertex_shader; + gldata->vtx_shader = gl->glCreateShader(GL_VERTEX_SHADER); + gl->glShaderSource(gldata->vtx_shader, 1, &p, NULL); + gl->glCompileShader(gldata->vtx_shader); + + p = fragment_shader; + gldata->fgmt_shader = gl->glCreateShader(GL_FRAGMENT_SHADER); + gl->glShaderSource(gldata->fgmt_shader, 1, &p, NULL); + gl->glCompileShader(gldata->fgmt_shader); + + gldata->program = gl->glCreateProgram(); + gl->glAttachShader(gldata->program, gldata->vtx_shader); + gl->glAttachShader(gldata->program, gldata->fgmt_shader); + gl->glBindAttribLocation(gldata->program, 0, "vPosition"); + gl->glBindAttribLocation(gldata->program, 1, "inColor"); + + gl->glLinkProgram(gldata->program); + gl->glUseProgram(gldata->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 *gldata = data; + Evas_GL_API *glapi = gldata->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(gldata->evasgl, gldata->sfc, gldata->ctx); + + //Initialize gl stuff just one time. + if (!gldata->initialized) + { + float aspect; + init_shaders(gldata); + glapi->glGenBuffers(1, &gldata->vbo); + glapi->glBindBuffer(GL_ARRAY_BUFFER, gldata->vbo); + glapi->glBufferData(GL_ARRAY_BUFFER, 3 * 72 * 4, cube_vertices,GL_STATIC_DRAW); + init_matrix(gldata->view); + if(w > h) + { + aspect = (float)w/h; + view_set_ortho(gldata->view, -1.0 * aspect, 1.0 * aspect, -1.0, 1.0, -1.0, 1.0); + } + else + { + aspect = (float)h/w; + view_set_ortho(gldata->view, -1.0, 1.0, -1.0 * aspect, 1.0 * aspect, -1.0, 1.0); + } + gldata->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, gldata->xangle, gldata->yangle, 0.0f); + multiply_matrix(mvp, gldata->view, model); + + glapi->glUseProgram(gldata->program); + glapi->glBindBuffer(GL_ARRAY_BUFFER, gldata->vbo); + glapi->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, 0); + glapi->glEnableVertexAttribArray(0); + + glapi->glBindBuffer(GL_ARRAY_BUFFER, gldata->vbo); + glapi->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*)(sizeof(float)*3)); + glapi->glEnableVertexAttribArray(1); + + glapi->glUniformMatrix4fv( glapi->glGetUniformLocation(gldata->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 *gldata = data; + Evas_GL_API *glapi = gldata->glapi; + + //Free the gl resources when image object is deleted. + evas_gl_make_current(gldata->evasgl, gldata->sfc, gldata->ctx); + + glapi->glDeleteShader(gldata->vtx_shader); + glapi->glDeleteShader(gldata->fgmt_shader); + glapi->glDeleteProgram(gldata->program); + glapi->glDeleteBuffers(1, &gldata->vbo); + + evas_gl_surface_destroy(gldata->evasgl, gldata->sfc); + evas_gl_context_destroy(gldata->evasgl, gldata->ctx); + + evas_gl_free(gldata->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); + + 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 *gldata = data; + gldata->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 *gldata = data; + float dx = 0, dy = 0; + + if( gldata->mouse_down) + { + dx = (ev->cur.canvas.x - ev->prev.canvas.x); + dy = (ev->cur.canvas.y - ev->prev.canvas.y); + gldata->xangle += dy; + gldata->yangle += dx; + } +} + +static void +_mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + GLData *gldata = data; + gldata->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 *gldata = data; + evas_object_resize(gldata->img, w, h); + + init_matrix(gldata->view); + if(w > h) + { + aspect = (float)w/h; + view_set_ortho(gldata->view, (-1.0 * aspect), (1.0 * aspect), -1.0, 1.0, -1.0, 1.0); + } + else + { + aspect = (float)h/w; + view_set_ortho(gldata->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_RGBA_8888, + EVAS_GL_DEPTH_BIT_32, + EVAS_GL_STENCIL_NONE + }; + + //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); + + //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 sysmtem 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; +}