diff options
author | ChunEon Park <hermet@hermet.pe.kr> | 2014-04-10 21:54:26 +0900 |
---|---|---|
committer | ChunEon Park <hermet@hermet.pe.kr> | 2014-04-10 21:54:26 +0900 |
commit | db453635a3c6670f3cacb119da4364dbfff0e32e (patch) | |
tree | 951426f68d13c00895a589f834d3d1dd661aacce | |
parent | f3c835d6fc1639b5a83b1496422f9f717e427c5d (diff) | |
parent | 398c4199d31411bdebedbf657b74f00f16e7d2d8 (diff) | |
download | efl-db453635a3c6670f3cacb119da4364dbfff0e32e.tar.gz |
Merge branch 'devs/cedric/evas-3d' of ssh://git.enlightenment.org/core/efl into devs/cedric/evas-3d
43 files changed, 13881 insertions, 6 deletions
diff --git a/configure.ac b/configure.ac index 78bbb7a731..11dd1672ef 100644 --- a/configure.ac +++ b/configure.ac @@ -1811,6 +1811,22 @@ AC_DEFINE_IF([EVAS_CSERVE2], [1], [Shared cache server.]) AM_CONDITIONAL([EVAS_CSERVE2], [test "x${want_evas_cserve2}" = "xyes"]) +# Evas 3D +AC_ARG_ENABLE([evas-3d], + [AC_HELP_STRING([--enable-evas-3d], + [enable 3D scene graph features on evas. @<:default=disabled@:>@])], + [ + if test "x${enableval}" = "xyes" ; then + want_evas_3d="yes" + else + want_evas_3d="no" + fi + ], + [want_evas_3d="no"]) + +AC_DEFINE_IF([EVAS_3D], [test "x${want_evas_3d}" = "xyes"], [1], [3D scene graph rendering.]) +AM_CONDITIONAL([EVAS_3D], [test "x${want_evas_3d}" = "xyes"]) + ### Configuration ## Tile rotation @@ -1852,6 +1868,7 @@ EFL_ADD_FEATURE([EVAS], [harfbuzz]) EFL_ADD_FEATURE([EVAS], [cserve], [${want_evas_cserve2}]) EFL_ADD_FEATURE([EVAS], [tile-rotate]) EFL_ADD_FEATURE([EVAS], [dither-mask], [${build_evas_dither_mask}]) +EFL_ADD_FEATURE([EVAS], [evas-3d], [${want_evas_3d}]) EFL_LIB_END([Evas]) #### End of Evas diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am index 61f6ee022c..1db9ec0080 100644 --- a/src/Makefile_Evas.am +++ b/src/Makefile_Evas.am @@ -219,6 +219,24 @@ lib/evas/file/evas_path.h lib_evas_libevas_la_SOURCES += \ $(lib_evas_file_SOURCES) +# 3D +dist_installed_evasmainheaders_DATA += lib/evas/Evas_3D.h + +noinst_HEADERS += \ +lib/evas/include/evas_3d_utils.h \ +lib/evas/include/evas_3d_private.h + +lib_evas_libevas_la_SOURCES += \ +lib/evas/canvas/evas_3d_object.c \ +lib/evas/canvas/evas_3d_scene.c \ +lib/evas/canvas/evas_3d_node.c \ +lib/evas/canvas/evas_3d_camera.c \ +lib/evas/canvas/evas_3d_light.c \ +lib/evas/canvas/evas_3d_mesh.c \ +lib/evas/canvas/evas_3d_texture.c \ +lib/evas/canvas/evas_3d_material.c \ +lib/evas/canvas/evas_3d_mesh_loader_md2.c + # Engine lib_evas_libevas_la_SOURCES += \ lib/evas/common/evas_op_copy_main_.c \ @@ -681,6 +699,14 @@ modules/evas/engines/gl_common/shader/yuy2_nomul_frag.h \ modules/evas/engines/gl_common/shader/yuy2_nomul_vert.h \ modules/evas/engines/gl_common/shader/yuy2_vert.h +# 3D +GL_COMMON_SOURCES += \ +modules/evas/engines/gl_common/evas_gl_3d_common.h \ +modules/evas/engines/gl_common/evas_gl_3d_private.h \ +modules/evas/engines/gl_common/evas_gl_3d.c \ +modules/evas/engines/gl_common/evas_gl_3d_renderer.c \ +modules/evas/engines/gl_common/evas_gl_3d_shader.c + EXTRA_DIST += \ modules/evas/engines/gl_common/shader/compile.sh \ modules/evas/engines/gl_common/shader/make-c-str.sh \ diff --git a/src/examples/evas/EarthDiffuse.png b/src/examples/evas/EarthDiffuse.png Binary files differnew file mode 100644 index 0000000000..3bcf58b3fe --- /dev/null +++ b/src/examples/evas/EarthDiffuse.png diff --git a/src/examples/evas/Makefile.am b/src/examples/evas/Makefile.am index 2b1a7c16d1..eecd520d75 100644 --- a/src/examples/evas/Makefile.am +++ b/src/examples/evas/Makefile.am @@ -170,6 +170,31 @@ evas_multi_touch_SOURCES = evas-multi-touch.c evas_multi_touch_LDADD = $(ECORE_EVAS_COMMON_LDADD) evas_multi_touch_CPPFLAGS = $(ECORE_EVAS_COMMON_CPPFLAGS) +EXTRA_PROGRAMS += evas_3d_cube +evas_3d_cube_SOURCES = evas-3d-cube.c +evas_3d_cube_LDADD = $(ECORE_EVAS_COMMON_LDADD) +evas_3d_cube_CPPFLAGS = $(ECORE_EVAS_COMMON_CPPFLAGS) + +EXTRA_PROGRAMS += evas_3d_cube2 +evas_3d_cube2_SOURCES = evas-3d-cube2.c +evas_3d_cube2_LDADD = $(ECORE_EVAS_COMMON_LDADD) +evas_3d_cube2_CPPFLAGS = $(ECORE_EVAS_COMMON_CPPFLAGS) + +EXTRA_PROGRAMS += evas_3d_proxy +evas_3d_proxy_SOURCES = evas-3d-proxy.c +evas_3d_proxy_LDADD = $(ECORE_EVAS_COMMON_LDADD) +evas_3d_proxy_CPPFLAGS = $(ECORE_EVAS_COMMON_CPPFLAGS) + +EXTRA_PROGRAMS += evas_3d_pick +evas_3d_pick_SOURCES = evas-3d-pick.c +evas_3d_pick_LDADD = $(ECORE_EVAS_COMMON_LDADD) +evas_3d_pick_CPPFLAGS = $(ECORE_EVAS_COMMON_CPPFLAGS) + +EXTRA_PROGRAMS += evas_3d_md2 +evas_3d_md2_SOURCES = evas-3d-md2.c +evas_3d_md2_LDADD = $(ECORE_EVAS_COMMON_LDADD) +evas_3d_md2_CPPFLAGS = $(ECORE_EVAS_COMMON_CPPFLAGS) + .edc.edj: $(AM_V_EDJ)$(EDJE_CC) $(EDJE_CC_FLAGS) $< $(builddir)/$(@F) diff --git a/src/examples/evas/evas-3d-cube.c b/src/examples/evas/evas-3d-cube.c new file mode 100644 index 0000000000..fddca2892b --- /dev/null +++ b/src/examples/evas/evas-3d-cube.c @@ -0,0 +1,260 @@ +#include <Ecore.h> +#include <Ecore_Evas.h> +#include <stdio.h> +#include <math.h> +#include <Evas_3D.h> + +#define WIDTH 1024 +#define HEIGHT 1024 + +typedef struct _Scene_Data +{ + Evas_3D_Scene *scene; + Evas_3D_Node *root_node; + Evas_3D_Node *camera_node; + Evas_3D_Node *light_node; + Evas_3D_Node *mesh_node; + + Evas_3D_Camera *camera; + Evas_3D_Light *light; + Evas_3D_Mesh *mesh; + Evas_3D_Material *material; +} Scene_Data; + +Ecore_Evas *ecore_evas = NULL; +Evas *evas = NULL; +Evas_Object *background = NULL; +Evas_Object *image = NULL; + +static const float cube_vertices[] = +{ + /* Front */ + -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, + 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, + -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, + + /* Back */ + 1.0, 1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, + -1.0, 1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, + 1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, + -1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, + + /* Left */ + -1.0, 1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, + -1.0, 1.0, 1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, + -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, + -1.0, -1.0, 1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, + + /* Right */ + 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, + 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, + 1.0, -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, + 1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, + + /* Top */ + -1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, + 1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, + -1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, + 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, + + /* Bottom */ + 1.0, -1.0, -1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, + -1.0, -1.0, -1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, -1.0, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, + -1.0, -1.0, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, +}; + +static const unsigned short cube_indices[] = +{ + /* Front */ + 0, 1, 2, 2, 1, 3, + + /* Back */ + 4, 5, 6, 6, 5, 7, + + /* Left */ + 8, 9, 10, 10, 9, 11, + + /* Right */ + 12, 13, 14, 14, 13, 15, + + /* Top */ + 16, 17, 18, 18, 17, 19, + + /* Bottom */ + 20, 21, 22, 22, 21, 23 +}; + +static void +_on_delete(Ecore_Evas *ee EINA_UNUSED) +{ + ecore_main_loop_quit(); +} + +static void +_on_canvas_resize(Ecore_Evas *ee) +{ + int w, h; + + ecore_evas_geometry_get(ee, NULL, NULL, &w, &h); + + evas_object_resize(background, w, h); + evas_object_resize(image, w, h); + evas_object_move(image, 0, 0); +} + +static Eina_Bool +_animate_scene(void *data) +{ + static float angle = 0.0f; + Scene_Data *scene = (Scene_Data *)data; + + angle += 0.5; + + evas_3d_node_orientation_angle_axis_set(scene->mesh_node, angle, 1.0, 1.0, 1.0); + + /* Rotate */ + if (angle > 360.0) + angle -= 360.0f; + + return EINA_TRUE; +} + +static void +_camera_setup(Scene_Data *data) +{ + data->camera = evas_3d_camera_add(evas); + evas_3d_camera_projection_perspective_set(data->camera, 60.0, 1.0, 2.0, 50.0); + + data->camera_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_CAMERA); + evas_3d_node_camera_set(data->camera_node, data->camera); + evas_3d_node_member_add(data->root_node, data->camera_node); + evas_3d_node_position_set(data->camera_node, 0.0, 0.0, 10.0); + evas_3d_node_look_at_set(data->camera_node, EVAS_3D_SPACE_PARENT, 0.0, 0.0, 0.0, + EVAS_3D_SPACE_PARENT, 0.0, 1.0, 0.0); +} + +static void +_light_setup(Scene_Data *data) +{ + data->light = evas_3d_light_add(evas); + evas_3d_light_ambient_set(data->light, 0.2, 0.2, 0.2, 1.0); + evas_3d_light_diffuse_set(data->light, 1.0, 1.0, 1.0, 1.0); + evas_3d_light_specular_set(data->light, 1.0, 1.0, 1.0, 1.0); + + data->light_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_LIGHT); + evas_3d_node_light_set(data->light_node, data->light); + evas_3d_node_member_add(data->root_node, data->light_node); + evas_3d_node_position_set(data->light_node, 0.0, 0.0, 10.0); + evas_3d_node_look_at_set(data->light_node, EVAS_3D_SPACE_PARENT, 0.0, 0.0, 0.0, + EVAS_3D_SPACE_PARENT, 0.0, 1.0, 0.0); +} + +static void +_mesh_setup(Scene_Data *data) +{ + /* Setup material. */ + data->material = evas_3d_material_add(evas); + + evas_3d_material_enable_set(data->material, EVAS_3D_MATERIAL_AMBIENT, EINA_TRUE); + evas_3d_material_enable_set(data->material, EVAS_3D_MATERIAL_DIFFUSE, EINA_TRUE); + evas_3d_material_enable_set(data->material, EVAS_3D_MATERIAL_SPECULAR, EINA_TRUE); + + evas_3d_material_color_set(data->material, EVAS_3D_MATERIAL_AMBIENT, 0.2, 0.2, 0.2, 1.0); + evas_3d_material_color_set(data->material, EVAS_3D_MATERIAL_DIFFUSE, 0.8, 0.8, 0.8, 1.0); + evas_3d_material_color_set(data->material, EVAS_3D_MATERIAL_SPECULAR, 1.0, 1.0, 1.0, 1.0); + evas_3d_material_shininess_set(data->material, 100.0); + + /* Setup mesh. */ + data->mesh = evas_3d_mesh_add(evas); + evas_3d_mesh_vertex_count_set(data->mesh, 24); + evas_3d_mesh_frame_add(data->mesh, 0); + + evas_3d_mesh_frame_vertex_data_set(data->mesh, 0, EVAS_3D_VERTEX_POSITION, + 12 * sizeof(float), &cube_vertices[ 0]); + evas_3d_mesh_frame_vertex_data_set(data->mesh, 0, EVAS_3D_VERTEX_NORMAL, + 12 * sizeof(float), &cube_vertices[ 3]); + evas_3d_mesh_frame_vertex_data_set(data->mesh, 0, EVAS_3D_VERTEX_COLOR, + 12 * sizeof(float), &cube_vertices[ 6]); + evas_3d_mesh_frame_vertex_data_set(data->mesh, 0, EVAS_3D_VERTEX_TEXCOORD, + 12 * sizeof(float), &cube_vertices[10]); + + evas_3d_mesh_index_data_set(data->mesh, EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT, + 36, &cube_indices[0]); + evas_3d_mesh_vertex_assembly_set(data->mesh, EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES); + + evas_3d_mesh_shade_mode_set(data->mesh, EVAS_3D_SHADE_MODE_PHONG); + + evas_3d_mesh_frame_material_set(data->mesh, 0, data->material); + + data->mesh_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_MESH); + evas_3d_node_member_add(data->root_node, data->mesh_node); + evas_3d_node_mesh_add(data->mesh_node, data->mesh); +} + +static void +_scene_setup(Scene_Data *data) +{ + data->scene = evas_3d_scene_add(evas); + evas_3d_scene_size_set(data->scene, WIDTH, HEIGHT); + evas_3d_scene_background_color_set(data->scene, 0.0, 0.0, 0.0, 0.0); + + data->root_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_NODE); + + _camera_setup(data); + _light_setup(data); + _mesh_setup(data); + + evas_3d_scene_root_node_set(data->scene, data->root_node); + evas_3d_scene_camera_node_set(data->scene, data->camera_node); +} + +int +main(void) +{ + Scene_Data data; + + if (!ecore_evas_init()) + return 0; + + ecore_evas = ecore_evas_new(NULL, 10, 10, WIDTH, HEIGHT, NULL); + + if (!ecore_evas) + return 0; + + ecore_evas_callback_delete_request_set(ecore_evas, _on_delete); + ecore_evas_callback_resize_set(ecore_evas, _on_canvas_resize); + ecore_evas_show(ecore_evas); + + evas = ecore_evas_get(ecore_evas); + + _scene_setup(&data); + + /* Add a background rectangle objects. */ + background = evas_object_rectangle_add(evas); + evas_object_color_set(background, 0, 0, 0, 255); + evas_object_move(background, 0, 0); + evas_object_resize(background, WIDTH, HEIGHT); + evas_object_show(background); + + /* Add an image object for 3D scene rendering. */ + image = evas_object_image_filled_add(evas); + evas_object_move(image, 0, 0); + evas_object_resize(image, WIDTH, HEIGHT); + evas_object_show(image); + + /* Set the image object as render target for 3D scene. */ + evas_object_image_3d_scene_set(image, data.scene); + + /* Add animation timer callback. */ + ecore_timer_add(0.016, _animate_scene, &data); + + /* Enter main loop. */ + ecore_main_loop_begin(); + + ecore_evas_free(ecore_evas); + ecore_evas_shutdown(); + + return 0; +} diff --git a/src/examples/evas/evas-3d-cube2.c b/src/examples/evas/evas-3d-cube2.c new file mode 100644 index 0000000000..fe4d6a8a36 --- /dev/null +++ b/src/examples/evas/evas-3d-cube2.c @@ -0,0 +1,321 @@ +#include <Ecore.h> +#include <Ecore_Evas.h> +#include <stdio.h> +#include <math.h> +#include <Evas_3D.h> + +#define WIDTH 1024 +#define HEIGHT 1024 + +typedef struct _Scene_Data +{ + Evas_3D_Scene *scene; + Evas_3D_Node *root_node; + Evas_3D_Node *camera_node; + Evas_3D_Node *light_node; + Evas_3D_Node *mesh_node; + + Evas_3D_Camera *camera; + Evas_3D_Light *light; + Evas_3D_Mesh *mesh; + Evas_3D_Material *material0; + Evas_3D_Material *material1; + + Evas_3D_Texture *texture0; + Evas_3D_Texture *texture1; + Evas_3D_Texture *texture_normal; +} Scene_Data; + +Ecore_Evas *ecore_evas = NULL; +Evas *evas = NULL; +Evas_Object *background = NULL; +Evas_Object *image = NULL; + +static const float cube_vertices[] = +{ + /* Front */ + -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, + 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, + -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, + + /* Back */ + 1.0, 1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, + -1.0, 1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, + 1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, + -1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, + + /* Left */ + -1.0, 1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, + -1.0, 1.0, 1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, + -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, + -1.0, -1.0, 1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, + + /* Right */ + 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, + 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, + 1.0, -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, + 1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, + + /* Top */ + -1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, + 1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, + -1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, + 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, + + /* Bottom */ + 1.0, -1.0, -1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, + -1.0, -1.0, -1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, -1.0, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, + -1.0, -1.0, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, +}; + +static const unsigned short cube_indices[] = +{ + /* Front */ + 0, 1, 2, 2, 1, 3, + + /* Back */ + 4, 5, 6, 6, 5, 7, + + /* Left */ + 8, 9, 10, 10, 9, 11, + + /* Right */ + 12, 13, 14, 14, 13, 15, + + /* Top */ + 16, 17, 18, 18, 17, 19, + + /* Bottom */ + 20, 21, 22, 22, 21, 23 +}; + +static const unsigned int pixels0[] = +{ + 0xff0000ff, 0xff0000ff, 0xffff0000, 0xffff0000, + 0xff0000ff, 0xff0000ff, 0xffff0000, 0xffff0000, + 0xff00ff00, 0xff00ff00, 0xff000000, 0xff000000, + 0xff00ff00, 0xff00ff00, 0xff000000, 0xff000000, +}; + +static const unsigned int pixels1[] = +{ + 0xffff0000, 0xffff0000, 0xff00ff00, 0xff00ff00, + 0xffff0000, 0xffff0000, 0xff00ff00, 0xff00ff00, + 0xff0000ff, 0xff0000ff, 0xffffffff, 0xffffffff, + 0xff0000ff, 0xff0000ff, 0xffffffff, 0xffffffff, +}; + +static void +_on_delete(Ecore_Evas *ee EINA_UNUSED) +{ + ecore_main_loop_quit(); +} + +static void +_on_canvas_resize(Ecore_Evas *ee) +{ + int w, h; + + ecore_evas_geometry_get(ee, NULL, NULL, &w, &h); + + evas_object_resize(background, w, h); + evas_object_resize(image, w, h); + evas_object_move(image, 0, 0); +} + +static Eina_Bool +_animate_scene(void *data) +{ + static float angle = 0.0f; + static int frame = 0; + static int inc = 1; + Scene_Data *scene = (Scene_Data *)data; + + angle += 0.2; + + evas_3d_node_orientation_angle_axis_set(scene->mesh_node, angle, 1.0, 1.0, 1.0); + + /* Rotate */ + if (angle > 360.0) + angle -= 360.0f; + + frame += inc; + + if (frame >= 20) + inc = -1; + else if (frame <= 0) + inc = 1; + + evas_3d_node_mesh_frame_set(scene->mesh_node, scene->mesh, frame); + + return EINA_TRUE; +} + +static void +_camera_setup(Scene_Data *data) +{ + data->camera = evas_3d_camera_add(evas); + evas_3d_camera_projection_perspective_set(data->camera, 60.0, 1.0, 2.0, 50.0); + + data->camera_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_CAMERA); + evas_3d_node_camera_set(data->camera_node, data->camera); + evas_3d_node_member_add(data->root_node, data->camera_node); + evas_3d_node_position_set(data->camera_node, 0.0, 0.0, 10.0); + evas_3d_node_look_at_set(data->camera_node, EVAS_3D_SPACE_PARENT, 0.0, 0.0, 0.0, + EVAS_3D_SPACE_PARENT, 0.0, 1.0, 0.0); +} + +static void +_light_setup(Scene_Data *data) +{ + data->light = evas_3d_light_add(evas); + evas_3d_light_ambient_set(data->light, 0.2, 0.2, 0.2, 1.0); + evas_3d_light_diffuse_set(data->light, 1.0, 1.0, 1.0, 1.0); + evas_3d_light_specular_set(data->light, 1.0, 1.0, 1.0, 1.0); + + data->light_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_LIGHT); + evas_3d_node_light_set(data->light_node, data->light); + evas_3d_node_member_add(data->root_node, data->light_node); + evas_3d_node_position_set(data->light_node, 0.0, 0.0, 10.0); + evas_3d_node_look_at_set(data->light_node, EVAS_3D_SPACE_PARENT, 0.0, 0.0, 0.0, + EVAS_3D_SPACE_PARENT, 0.0, 1.0, 0.0); +} + +static void +_mesh_setup(Scene_Data *data) +{ + /* Setup material. */ + data->material0 = evas_3d_material_add(evas); + data->material1 = evas_3d_material_add(evas); + + evas_3d_material_enable_set(data->material0, EVAS_3D_MATERIAL_AMBIENT, EINA_TRUE); + evas_3d_material_enable_set(data->material0, EVAS_3D_MATERIAL_DIFFUSE, EINA_TRUE); + evas_3d_material_enable_set(data->material0, EVAS_3D_MATERIAL_SPECULAR, EINA_TRUE); + evas_3d_material_enable_set(data->material0, EVAS_3D_MATERIAL_NORMAL, EINA_TRUE); + + evas_3d_material_color_set(data->material0, EVAS_3D_MATERIAL_AMBIENT, 0.2, 0.2, 0.2, 1.0); + evas_3d_material_color_set(data->material0, EVAS_3D_MATERIAL_DIFFUSE, 0.8, 0.8, 0.8, 1.0); + evas_3d_material_color_set(data->material0, EVAS_3D_MATERIAL_SPECULAR, 1.0, 1.0, 1.0, 1.0); + evas_3d_material_shininess_set(data->material0, 100.0); + + evas_3d_material_enable_set(data->material1, EVAS_3D_MATERIAL_AMBIENT, EINA_TRUE); + evas_3d_material_enable_set(data->material1, EVAS_3D_MATERIAL_DIFFUSE, EINA_TRUE); + evas_3d_material_enable_set(data->material1, EVAS_3D_MATERIAL_SPECULAR, EINA_TRUE); + evas_3d_material_enable_set(data->material1, EVAS_3D_MATERIAL_NORMAL, EINA_TRUE); + + evas_3d_material_color_set(data->material1, EVAS_3D_MATERIAL_AMBIENT, 0.2, 0.2, 0.2, 1.0); + evas_3d_material_color_set(data->material1, EVAS_3D_MATERIAL_DIFFUSE, 0.8, 0.8, 0.8, 1.0); + evas_3d_material_color_set(data->material1, EVAS_3D_MATERIAL_SPECULAR, 1.0, 1.0, 1.0, 1.0); + evas_3d_material_shininess_set(data->material1, 100.0); + + data->texture0 = evas_3d_texture_add(evas); + data->texture1 = evas_3d_texture_add(evas); + data->texture_normal = evas_3d_texture_add(evas); + + evas_3d_texture_data_set(data->texture0, EVAS_3D_COLOR_FORMAT_RGBA, + EVAS_3D_PIXEL_FORMAT_8888, 4, 4, &pixels0[0]); + evas_3d_texture_data_set(data->texture1, EVAS_3D_COLOR_FORMAT_RGBA, + EVAS_3D_PIXEL_FORMAT_8888, 4, 4, &pixels1[0]); + evas_3d_texture_file_set(data->texture_normal, "normal_lego.png", NULL); + + evas_3d_material_texture_set(data->material0, EVAS_3D_MATERIAL_DIFFUSE, data->texture0); + evas_3d_material_texture_set(data->material1, EVAS_3D_MATERIAL_DIFFUSE, data->texture1); + evas_3d_material_texture_set(data->material1, EVAS_3D_MATERIAL_NORMAL, data->texture_normal); + + /* Setup mesh. */ + data->mesh = evas_3d_mesh_add(evas); + evas_3d_mesh_vertex_count_set(data->mesh, 24); + evas_3d_mesh_frame_add(data->mesh, 0); + + evas_3d_mesh_frame_vertex_data_set(data->mesh, 0, EVAS_3D_VERTEX_POSITION, + 12 * sizeof(float), &cube_vertices[ 0]); + evas_3d_mesh_frame_vertex_data_set(data->mesh, 0, EVAS_3D_VERTEX_NORMAL, + 12 * sizeof(float), &cube_vertices[ 3]); + evas_3d_mesh_frame_vertex_data_set(data->mesh, 0, EVAS_3D_VERTEX_COLOR, + 12 * sizeof(float), &cube_vertices[ 6]); + evas_3d_mesh_frame_vertex_data_set(data->mesh, 0, EVAS_3D_VERTEX_TEXCOORD, + 12 * sizeof(float), &cube_vertices[10]); + + evas_3d_mesh_index_data_set(data->mesh, EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT, + 36, &cube_indices[0]); + evas_3d_mesh_vertex_assembly_set(data->mesh, EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES); + + evas_3d_mesh_shade_mode_set(data->mesh, EVAS_3D_SHADE_MODE_NORMAL_MAP); + + evas_3d_mesh_frame_material_set(data->mesh, 0, data->material0); + + evas_3d_mesh_frame_add(data->mesh, 20); + evas_3d_mesh_frame_material_set(data->mesh, 20, data->material1); + + data->mesh_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_MESH); + evas_3d_node_member_add(data->root_node, data->mesh_node); + evas_3d_node_mesh_add(data->mesh_node, data->mesh); +} + +static void +_scene_setup(Scene_Data *data) +{ + data->scene = evas_3d_scene_add(evas); + evas_3d_scene_size_set(data->scene, WIDTH, HEIGHT); + evas_3d_scene_background_color_set(data->scene, 0.0, 0.0, 0.0, 0.0); + + data->root_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_NODE); + + _camera_setup(data); + _light_setup(data); + _mesh_setup(data); + + evas_3d_scene_root_node_set(data->scene, data->root_node); + evas_3d_scene_camera_node_set(data->scene, data->camera_node); +} + +int +main(void) +{ + Scene_Data data; + + if (!ecore_evas_init()) + return 0; + + ecore_evas = ecore_evas_new(NULL, 10, 10, WIDTH, HEIGHT, NULL); + + if (!ecore_evas) + return 0; + + ecore_evas_callback_delete_request_set(ecore_evas, _on_delete); + ecore_evas_callback_resize_set(ecore_evas, _on_canvas_resize); + ecore_evas_show(ecore_evas); + + evas = ecore_evas_get(ecore_evas); + + _scene_setup(&data); + + /* Add a background rectangle objects. */ + background = evas_object_rectangle_add(evas); + evas_object_color_set(background, 0, 0, 0, 255); + evas_object_move(background, 0, 0); + evas_object_resize(background, WIDTH, HEIGHT); + evas_object_show(background); + + /* Add an image object for 3D scene rendering. */ + image = evas_object_image_filled_add(evas); + evas_object_move(image, 0, 0); + evas_object_resize(image, WIDTH, HEIGHT); + evas_object_show(image); + + /* Set the image object as render target for 3D scene. */ + evas_object_image_3d_scene_set(image, data.scene); + + /* Add animation timer callback. */ + ecore_timer_add(0.01, _animate_scene, &data); + + /* Enter main loop. */ + ecore_main_loop_begin(); + + ecore_evas_free(ecore_evas); + ecore_evas_shutdown(); + + return 0; +} diff --git a/src/examples/evas/evas-3d-md2.c b/src/examples/evas/evas-3d-md2.c new file mode 100644 index 0000000000..091e4eb757 --- /dev/null +++ b/src/examples/evas/evas-3d-md2.c @@ -0,0 +1,165 @@ +#include <Ecore.h> +#include <Ecore_Evas.h> +#include <stdio.h> +#include <math.h> +#include <Evas_3D.h> + +#define WIDTH 1024 +#define HEIGHT 1024 + +Ecore_Evas *ecore_evas = NULL; +Evas *evas = NULL; +Evas_Object *background = NULL; +Evas_Object *image = NULL; + +Evas_3D_Scene *scene = NULL; +Evas_3D_Node *root_node = NULL; +Evas_3D_Node *camera_node = NULL; +Evas_3D_Node *light_node = NULL; +Evas_3D_Camera *camera = NULL; +Evas_3D_Node *mesh_node = NULL; +Evas_3D_Mesh *mesh = NULL; +Evas_3D_Material *material = NULL; +Evas_3D_Texture *texture = NULL; +Evas_3D_Light *light = NULL; + +static Eina_Bool +_animate_scene(void *data) +{ + static int frame = 0; + + evas_3d_node_mesh_frame_set((Evas_3D_Node *)data, mesh, frame); + + frame += 32; + + if (frame > 256 * 50) + frame = 0; + + return EINA_TRUE; +} + +static void +_on_delete(Ecore_Evas *ee EINA_UNUSED) +{ + ecore_main_loop_quit(); +} + +static void +_on_canvas_resize(Ecore_Evas *ee) +{ + int w, h; + + ecore_evas_geometry_get(ee, NULL, NULL, &w, &h); + + evas_object_resize(background, w, h); + evas_object_resize(image, w, h); + evas_object_move(image, 0, 0); +} + +int +main(void) +{ + if (!ecore_evas_init()) + return 0; + + ecore_evas = ecore_evas_new(NULL, 10, 10, WIDTH, HEIGHT, NULL); + + if (!ecore_evas) + return 0; + + ecore_evas_callback_delete_request_set(ecore_evas, _on_delete); + ecore_evas_callback_resize_set(ecore_evas, _on_canvas_resize); + ecore_evas_show(ecore_evas); + + evas = ecore_evas_get(ecore_evas); + + /* Add a scene object .*/ + scene = evas_3d_scene_add(evas); + + /* Add the root node for the scene. */ + root_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_NODE); + + /* Add the camera. */ + camera = evas_3d_camera_add(evas); + evas_3d_camera_projection_perspective_set(camera, 60.0, 1.0, 1.0, 500.0); + + camera_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_CAMERA); + evas_3d_node_camera_set(camera_node, camera); + evas_3d_node_member_add(root_node, camera_node); + evas_3d_node_position_set(camera_node, 100.0, 0.0, 20.0); + evas_3d_node_look_at_set(camera_node, EVAS_3D_SPACE_PARENT, 0.0, 0.0, 20.0, + EVAS_3D_SPACE_PARENT, 0.0, 0.0, 1.0); + + /* Add the light. */ + light = evas_3d_light_add(evas); + evas_3d_light_ambient_set(light, 1.0, 1.0, 1.0, 1.0); + evas_3d_light_diffuse_set(light, 1.0, 1.0, 1.0, 1.0); + evas_3d_light_specular_set(light, 1.0, 1.0, 1.0, 1.0); + + light_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_LIGHT); + evas_3d_node_light_set(light_node, light); + evas_3d_node_member_add(root_node, light_node); + evas_3d_node_position_set(light_node, 1000.0, 0.0, 1000.0); + evas_3d_light_directional_set(light, EINA_TRUE); + evas_3d_node_look_at_set(light_node, EVAS_3D_SPACE_PARENT, 0.0, 0.0, 0.0, + EVAS_3D_SPACE_PARENT, 0.0, 1.0, 0.0); + + /* Add the mesh. */ + mesh = evas_3d_mesh_add(evas); + evas_3d_mesh_file_set(mesh, EVAS_3D_MESH_FILE_TYPE_MD2, "sonic.md2", NULL); + + material = evas_3d_material_add(evas); + evas_3d_mesh_frame_material_set(mesh, 0, material); + + texture = evas_3d_texture_add(evas); + + evas_3d_texture_file_set(texture, "sonic.png", NULL); + evas_3d_texture_filter_set(texture, EVAS_3D_TEXTURE_FILTER_NEAREST, EVAS_3D_TEXTURE_FILTER_NEAREST); + evas_3d_texture_wrap_set(texture, EVAS_3D_WRAP_MODE_REPEAT, EVAS_3D_WRAP_MODE_REPEAT); + + evas_3d_material_texture_set(material, EVAS_3D_MATERIAL_DIFFUSE, texture); + + evas_3d_material_enable_set(material, EVAS_3D_MATERIAL_AMBIENT, EINA_TRUE); + evas_3d_material_enable_set(material, EVAS_3D_MATERIAL_DIFFUSE, EINA_TRUE); + evas_3d_material_enable_set(material, EVAS_3D_MATERIAL_SPECULAR, EINA_TRUE); + evas_3d_material_enable_set(material, EVAS_3D_MATERIAL_NORMAL, EINA_TRUE); + + evas_3d_material_color_set(material, EVAS_3D_MATERIAL_AMBIENT, 0.01, 0.01, 0.01, 1.0); + evas_3d_material_color_set(material, EVAS_3D_MATERIAL_DIFFUSE, 1.0, 1.0, 1.0, 1.0); + evas_3d_material_color_set(material, EVAS_3D_MATERIAL_SPECULAR, 1.0, 1.0, 1.0, 1.0); + evas_3d_material_shininess_set(material, 50.0); + + mesh_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_MESH); + evas_3d_node_member_add(root_node, mesh_node); + evas_3d_node_mesh_add(mesh_node, mesh); + evas_3d_mesh_shade_mode_set(mesh, EVAS_3D_SHADE_MODE_PHONG); + + /* Set up scene. */ + evas_3d_scene_root_node_set(scene, root_node); + evas_3d_scene_camera_node_set(scene, camera_node); + evas_3d_scene_size_set(scene, WIDTH, HEIGHT); + + /* Add evas objects. */ + background = evas_object_rectangle_add(evas); + evas_object_color_set(background, 0, 0, 0, 255); + evas_object_move(background, 0, 0); + evas_object_resize(background, WIDTH, HEIGHT); + evas_object_show(background); + + image = evas_object_image_filled_add(evas); + evas_object_image_size_set(image, WIDTH, HEIGHT); + evas_object_image_3d_scene_set(image, scene); + evas_object_move(image, 0, 0); + evas_object_resize(image, WIDTH, HEIGHT); + evas_object_show(image); + + ecore_timer_add(0.01, _animate_scene, mesh_node); + + printf ("Enter main loop\n"); + ecore_main_loop_begin(); + + ecore_evas_free(ecore_evas); + ecore_evas_shutdown(); + + return 0; +} diff --git a/src/examples/evas/evas-3d-pick.c b/src/examples/evas/evas-3d-pick.c new file mode 100644 index 0000000000..02d11d548e --- /dev/null +++ b/src/examples/evas/evas-3d-pick.c @@ -0,0 +1,397 @@ +#include <Ecore.h> +#include <Ecore_Evas.h> +#include <stdio.h> +#include <math.h> +#include <Evas_3D.h> + +#define WIDTH 1024 +#define HEIGHT 1024 + +Ecore_Evas *ecore_evas = NULL; +Evas *evas = NULL; +Evas_Object *background = NULL; +Evas_Object *image = NULL; + +Evas_3D_Scene *scene = NULL; +Evas_3D_Node *root_node = NULL; +Evas_3D_Node *camera_node = NULL; +Evas_3D_Camera *camera = NULL; +Evas_3D_Node *mesh_node = NULL; +Evas_3D_Mesh *mesh = NULL; +Evas_3D_Material *material = NULL; +Evas_3D_Texture *texture_diffuse = NULL; + +static Eina_Bool +_animate_scene(void *data) +{ + static float angle = 0.0f; + + angle += 0.3; + + evas_3d_node_orientation_angle_axis_set((Evas_3D_Node *)data, angle, 0.0, 1.0, 0.0); + + /* Rotate */ + if (angle > 360.0) + angle -= 360.0f; + + return EINA_TRUE; +} + +static void +_on_delete(Ecore_Evas *ee EINA_UNUSED) +{ + ecore_main_loop_quit(); +} + +static void +_on_canvas_resize(Ecore_Evas *ee) +{ + int w, h; + + ecore_evas_geometry_get(ee, NULL, NULL, &w, &h); + + evas_object_resize(background, w, h); + evas_object_resize(image, w, h); + evas_object_move(image, 0, 0); +} + +typedef struct _vec4 +{ + float x; + float y; + float z; + float w; +} vec4; + +typedef struct _vec3 +{ + float x; + float y; + float z; +} vec3; + +typedef struct _vec2 +{ + float x; + float y; +} vec2; + +typedef struct _vertex +{ + vec3 position; + vec3 normal; + vec3 tangent; + vec4 color; + vec3 texcoord; +} vertex; + +static int vertex_count = 0; +static vertex *vertices = NULL; + +static int index_count = 0; +static unsigned short *indices = NULL; + +static inline vec3 +_normalize(const vec3 *v) +{ + double l = sqrt(v->x * v->x + v->y * v->y + v->z * v->z); + vec3 vec; + + vec.x = v->x / l; + vec.y = v->y / l; + vec.z = v->z / l; + + return vec; +} + +static void +_sphere_fini() +{ + if (vertices) + free(vertices); + + if (indices) + free(indices); +} + +static void +_sphere_init(int precision) +{ + int i, j; + unsigned short *index; + + vertex_count = (precision + 1) * (precision + 1); + index_count = precision * precision * 6; + + /* Allocate buffer. */ + vertices = malloc(sizeof(vertex) * vertex_count); + indices = malloc(sizeof(unsigned short) * index_count); + + for (i = 0; i <= precision; i++) + { + double lati = (M_PI * (double)i) / (double)precision; + double y = cos(lati); + double r = fabs(sin(lati)); + + for (j = 0; j <= precision; j++) + { + double longi = (M_PI * 2.0 * j) / precision; + vertex *v = &vertices[i * (precision + 1) + j]; + + if (j == 0 || j == precision) + v->position.x = 0.0; + else + v->position.x = r * sin(longi); + + v->position.y = y; + + if (j == 0 || j == precision) + v->position.z = r; + else + v->position.z = r * cos(longi); + + v->normal = v->position; + + if (v->position.x > 0.0) + { + v->tangent.x = -v->normal.y; + v->tangent.y = v->normal.x; + v->tangent.z = v->normal.z; + } + else + { + v->tangent.x = v->normal.y; + v->tangent.y = -v->normal.x; + v->tangent.z = v->normal.z; + } + + v->color.x = v->position.x; + v->color.y = v->position.y; + v->color.z = v->position.z; + v->color.w = 1.0; + + if (j == precision) + v->texcoord.x = 1.0; + else if (j == 0) + v->texcoord.x = 0.0; + else + v->texcoord.x = (double)j / (double)precision; + + if (i == precision) + v->texcoord.y = 1.0; + else if (i == 0) + v->texcoord.y = 0.0; + else + v->texcoord.y = 1.0 - (double)i / (double)precision; + } + } + + index = &indices[0]; + + for (i = 0; i < precision; i++) + { + for (j = 0; j < precision; j++) + { + *index++ = i * (precision + 1) + j; + *index++ = i * (precision + 1) + j + 1; + *index++ = (i + 1) * (precision + 1) + j; + + *index++ = (i + 1) * (precision + 1) + j; + *index++ = i * (precision + 1) + j + 1; + *index++ = (i + 1) * (precision + 1) + j + 1; + } + } + + for (i = 0; i < index_count; i += 3) + { + vertex *v0 = &vertices[indices[i + 0]]; + vertex *v1 = &vertices[indices[i + 1]]; + vertex *v2 = &vertices[indices[i + 2]]; + + vec3 e1, e2; + float du1, du2, dv1, dv2, f; + vec3 tangent; + + e1.x = v1->position.x - v0->position.x; + e1.y = v1->position.y - v0->position.y; + e1.z = v1->position.z - v0->position.z; + + e2.x = v2->position.x - v0->position.x; + e2.y = v2->position.y - v0->position.y; + e2.z = v2->position.z - v0->position.z; + + du1 = v1->texcoord.x - v0->texcoord.x; + dv1 = v1->texcoord.y - v0->texcoord.y; + + du2 = v2->texcoord.x - v0->texcoord.x; + dv2 = v2->texcoord.y - v0->texcoord.y; + + f = 1.0 / (du1 * dv2 - du2 * dv1); + + tangent.x = f * (dv2 * e1.x - dv1 * e2.x); + tangent.y = f * (dv2 * e1.y - dv1 * e2.y); + tangent.z = f * (dv2 * e1.z - dv1 * e2.z); + + v0->tangent = tangent; + } + + for (i = 0; i <= precision; i++) + { + for (j = 0; j <= precision; j++) + { + if (j == precision) + { + vertex *v = &vertices[i * (precision + 1) + j]; + v->tangent = vertices[i * (precision + 1)].tangent; + } + } + } +} + +static void +_on_mouse_down(void *data EINA_UNUSED, + Evas *e EINA_UNUSED, + Evas_Object *o, + void *einfo) +{ + Evas_Event_Mouse_Down *ev = einfo; + Evas_Coord x, y, w, h; + Evas_Coord obj_x, obj_y; + int scene_w, scene_h; + Evas_Real scene_x, scene_y; + Evas_Real s, t; + Evas_3D_Node *n; + Evas_3D_Mesh *m; + + evas_object_geometry_get(o, &x, &y, &w, &h); + + obj_x = ev->canvas.x - x; + obj_y = ev->canvas.y - y; + + evas_3d_scene_size_get(scene, &scene_w, &scene_h); + + scene_x = obj_x * scene_w / (Evas_Real)w; + scene_y = obj_y * scene_h / (Evas_Real)h; + + if (evas_3d_scene_pick(scene, scene_x, scene_y, &n, &m, &s, &t)) + printf("Picked : "); + else + printf("Not picked : "); + + printf("output(%d, %d) canvas(%d, %d) object(%d, %d) scene(%f, %f) texcoord(%f, %f) " + "node(%p) mesh(%p)\n", + ev->output.x, ev->output.y, + ev->canvas.x, ev->canvas.y, + obj_x, obj_y, + scene_x, scene_y, + s, t, n, m); +} + +int +main(void) +{ + if (!ecore_evas_init()) + return 0; + + ecore_evas = ecore_evas_new(NULL, 10, 10, WIDTH, HEIGHT, NULL); + + if (!ecore_evas) + return 0; + + ecore_evas_callback_delete_request_set(ecore_evas, _on_delete); + ecore_evas_callback_resize_set(ecore_evas, _on_canvas_resize); + ecore_evas_show(ecore_evas); + + evas = ecore_evas_get(ecore_evas); + + /* Add a scene object .*/ + scene = evas_3d_scene_add(evas); + + /* Add the root node for the scene. */ + root_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_NODE); + + /* Add the camera. */ + camera = evas_3d_camera_add(evas); + evas_3d_camera_projection_perspective_set(camera, 30.0, 1.0, 1.0, 100.0); + + camera_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_CAMERA); + evas_3d_node_camera_set(camera_node, camera); + evas_3d_node_member_add(root_node, camera_node); + evas_3d_node_position_set(camera_node, 0.0, 0.0, 5.0); + evas_3d_node_look_at_set(camera_node, EVAS_3D_SPACE_PARENT, 0.0, 0.0, 0.0, + EVAS_3D_SPACE_PARENT, 0.0, 1.0, 0.0); + + /* Add the cube mesh. */ + _sphere_init(100); + + mesh = evas_3d_mesh_add(evas); + evas_3d_mesh_vertex_count_set(mesh, vertex_count); + evas_3d_mesh_frame_add(mesh, 0); + evas_3d_mesh_frame_vertex_data_set(mesh, 0, EVAS_3D_VERTEX_POSITION, + sizeof(vertex), &vertices[0].position); + evas_3d_mesh_frame_vertex_data_set(mesh, 0, EVAS_3D_VERTEX_NORMAL, + sizeof(vertex), &vertices[0].normal); + evas_3d_mesh_frame_vertex_data_set(mesh, 0, EVAS_3D_VERTEX_TANGENT, + sizeof(vertex), &vertices[0].tangent); + evas_3d_mesh_frame_vertex_data_set(mesh, 0, EVAS_3D_VERTEX_COLOR, + sizeof(vertex), &vertices[0].color); + evas_3d_mesh_frame_vertex_data_set(mesh, 0, EVAS_3D_VERTEX_TEXCOORD, + sizeof(vertex), &vertices[0].texcoord); + + evas_3d_mesh_index_data_set(mesh, EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT, index_count, &indices[0]); + evas_3d_mesh_vertex_assembly_set(mesh, EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES); + + material = evas_3d_material_add(evas); + evas_3d_mesh_frame_material_set(mesh, 0, material); + + texture_diffuse = evas_3d_texture_add(evas); + + evas_3d_texture_file_set(texture_diffuse, "EarthDiffuse.png", NULL); + evas_3d_texture_filter_set(texture_diffuse, EVAS_3D_TEXTURE_FILTER_LINEAR, EVAS_3D_TEXTURE_FILTER_LINEAR); + evas_3d_material_texture_set(material, EVAS_3D_MATERIAL_DIFFUSE, texture_diffuse); + + evas_3d_material_enable_set(material, EVAS_3D_MATERIAL_AMBIENT, EINA_TRUE); + evas_3d_material_enable_set(material, EVAS_3D_MATERIAL_DIFFUSE, EINA_TRUE); + evas_3d_material_enable_set(material, EVAS_3D_MATERIAL_SPECULAR, EINA_TRUE); + + evas_3d_material_color_set(material, EVAS_3D_MATERIAL_AMBIENT, 0.01, 0.01, 0.01, 1.0); + evas_3d_material_color_set(material, EVAS_3D_MATERIAL_DIFFUSE, 1.0, 1.0, 1.0, 1.0); + evas_3d_material_color_set(material, EVAS_3D_MATERIAL_SPECULAR, 1.0, 1.0, 1.0, 1.0); + evas_3d_material_shininess_set(material, 50.0); + + mesh_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_MESH); + evas_3d_node_member_add(root_node, mesh_node); + evas_3d_node_mesh_add(mesh_node, mesh); + evas_3d_mesh_shade_mode_set(mesh, EVAS_3D_SHADE_MODE_DIFFUSE); + + /* Set up scene. */ + evas_3d_scene_root_node_set(scene, root_node); + evas_3d_scene_camera_node_set(scene, camera_node); + evas_3d_scene_size_set(scene, WIDTH, HEIGHT); + + /* Add evas objects. */ + background = evas_object_rectangle_add(evas); + evas_object_color_set(background, 0, 0, 0, 255); + evas_object_move(background, 0, 0); + evas_object_resize(background, WIDTH, HEIGHT); + evas_object_show(background); + + image = evas_object_image_filled_add(evas); + evas_object_image_size_set(image, WIDTH, HEIGHT); + evas_object_image_3d_scene_set(image, scene); + evas_object_move(image, 0, 0); + evas_object_resize(image, WIDTH, HEIGHT); + evas_object_show(image); + evas_object_event_callback_add(image, EVAS_CALLBACK_MOUSE_DOWN, _on_mouse_down, NULL); + + ecore_timer_add(0.01, _animate_scene, mesh_node); + + printf ("Enter main loop\n"); + ecore_main_loop_begin(); + + ecore_evas_free(ecore_evas); + ecore_evas_shutdown(); + _sphere_fini(); + + return 0; +} diff --git a/src/examples/evas/evas-3d-proxy.c b/src/examples/evas/evas-3d-proxy.c new file mode 100644 index 0000000000..66c483e848 --- /dev/null +++ b/src/examples/evas/evas-3d-proxy.c @@ -0,0 +1,297 @@ +#include <Ecore.h> +#include <Ecore_Evas.h> +#include <stdio.h> +#include <math.h> +#include <Evas_3D.h> + +#define WIDTH 1024 +#define HEIGHT 1024 + +#define IMG_WIDTH 256 +#define IMG_HEIGHT 256 + +typedef struct _Scene_Data +{ + Evas_3D_Scene *scene; + Evas_3D_Node *root_node; + Evas_3D_Node *camera_node; + Evas_3D_Node *light_node; + Evas_3D_Node *mesh_node; + + Evas_3D_Camera *camera; + Evas_3D_Light *light; + Evas_3D_Mesh *mesh; + Evas_3D_Material *material; + Evas_3D_Texture *texture; +} Scene_Data; + +Ecore_Evas *ecore_evas = NULL; +Evas *evas = NULL; +Evas_Object *background = NULL; +Evas_Object *image = NULL; +Evas_Object *source = NULL; + +static const float cube_vertices[] = +{ + /* Front */ + -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, + 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, + -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, + + /* Back */ + 1.0, 1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, + -1.0, 1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, + 1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, + -1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, + + /* Left */ + -1.0, 1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, + -1.0, 1.0, 1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, + -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, + -1.0, -1.0, 1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, + + /* Right */ + 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, + 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, + 1.0, -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, + 1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, + + /* Top */ + -1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, + 1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, + -1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, + 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, + + /* Bottom */ + 1.0, -1.0, -1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, + -1.0, -1.0, -1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, -1.0, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, + -1.0, -1.0, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, +}; + +static const unsigned short cube_indices[] = +{ + /* Front */ + 0, 1, 2, 2, 1, 3, + + /* Back */ + 4, 5, 6, 6, 5, 7, + + /* Left */ + 8, 9, 10, 10, 9, 11, + + /* Right */ + 12, 13, 14, 14, 13, 15, + + /* Top */ + 16, 17, 18, 18, 17, 19, + + /* Bottom */ + 20, 21, 22, 22, 21, 23 +}; + +static void +_on_delete(Ecore_Evas *ee EINA_UNUSED) +{ + ecore_main_loop_quit(); +} + +static void +_on_canvas_resize(Ecore_Evas *ee) +{ + int w, h; + + ecore_evas_geometry_get(ee, NULL, NULL, &w, &h); + + evas_object_resize(background, w, h); + evas_object_resize(image, w, h); + evas_object_move(image, 0, 0); +} + +static Eina_Bool +_animate_scene(void *data) +{ + static float angle = 0.0f; + Scene_Data *scene = (Scene_Data *)data; + unsigned int *pixels; + int i, j, stride; + + angle += 0.5; + + evas_3d_node_orientation_angle_axis_set(scene->mesh_node, angle, 1.0, 1.0, 1.0); + + /* Rotate */ + if (angle > 360.0) + angle -= 360.0f; + + pixels = (unsigned int *)evas_object_image_data_get(source, EINA_TRUE); + stride = evas_object_image_stride_get(source); + + for (i = 0; i < IMG_HEIGHT; i++) + { + unsigned int *row = (unsigned int *)((char *)pixels + stride * i); + + for (j = 0; j < IMG_WIDTH; j++) + { + *row++ = rand() | 0xff000000; + } + } + + evas_object_image_data_set(source, pixels); + evas_object_image_data_update_add(source, 0, 0, IMG_WIDTH, IMG_HEIGHT); + + return EINA_TRUE; +} + +static void +_camera_setup(Scene_Data *data) +{ + data->camera = evas_3d_camera_add(evas); + evas_3d_camera_projection_perspective_set(data->camera, 30.0, 1.0, 2.0, 50.0); + + data->camera_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_CAMERA); + evas_3d_node_camera_set(data->camera_node, data->camera); + evas_3d_node_member_add(data->root_node, data->camera_node); + evas_3d_node_position_set(data->camera_node, 0.0, 0.0, 10.0); + evas_3d_node_look_at_set(data->camera_node, EVAS_3D_SPACE_PARENT, 0.0, 0.0, 0.0, + EVAS_3D_SPACE_PARENT, 0.0, 1.0, 0.0); +} + +static void +_light_setup(Scene_Data *data) +{ + data->light = evas_3d_light_add(evas); + evas_3d_light_ambient_set(data->light, 0.2, 0.2, 0.2, 1.0); + evas_3d_light_diffuse_set(data->light, 1.0, 1.0, 1.0, 1.0); + evas_3d_light_specular_set(data->light, 1.0, 1.0, 1.0, 1.0); + + data->light_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_LIGHT); + evas_3d_node_light_set(data->light_node, data->light); + evas_3d_node_member_add(data->root_node, data->light_node); + evas_3d_node_position_set(data->light_node, 0.0, 0.0, 10.0); + evas_3d_node_look_at_set(data->light_node, EVAS_3D_SPACE_PARENT, 0.0, 0.0, 0.0, + EVAS_3D_SPACE_PARENT, 0.0, 1.0, 0.0); +} + +static void +_mesh_setup(Scene_Data *data) +{ + /* Setup material. */ + data->material = evas_3d_material_add(evas); + data->texture = evas_3d_texture_add(evas); + + evas_3d_texture_source_set(data->texture, source); + evas_3d_texture_source_visible_set(data->texture, EINA_TRUE); + + evas_3d_material_enable_set(data->material, EVAS_3D_MATERIAL_AMBIENT, EINA_TRUE); + evas_3d_material_enable_set(data->material, EVAS_3D_MATERIAL_DIFFUSE, EINA_TRUE); + evas_3d_material_enable_set(data->material, EVAS_3D_MATERIAL_SPECULAR, EINA_TRUE); + + evas_3d_material_color_set(data->material, EVAS_3D_MATERIAL_AMBIENT, 0.2, 0.2, 0.2, 1.0); + evas_3d_material_color_set(data->material, EVAS_3D_MATERIAL_DIFFUSE, 0.8, 0.8, 0.8, 1.0); + evas_3d_material_color_set(data->material, EVAS_3D_MATERIAL_SPECULAR, 1.0, 1.0, 1.0, 1.0); + evas_3d_material_shininess_set(data->material, 100.0); + + evas_3d_material_texture_set(data->material, EVAS_3D_MATERIAL_DIFFUSE, data->texture); + + /* Setup mesh. */ + data->mesh = evas_3d_mesh_add(evas); + evas_3d_mesh_vertex_count_set(data->mesh, 24); + evas_3d_mesh_frame_add(data->mesh, 0); + + evas_3d_mesh_frame_vertex_data_set(data->mesh, 0, EVAS_3D_VERTEX_POSITION, + 12 * sizeof(float), &cube_vertices[ 0]); + evas_3d_mesh_frame_vertex_data_set(data->mesh, 0, EVAS_3D_VERTEX_NORMAL, + 12 * sizeof(float), &cube_vertices[ 3]); + evas_3d_mesh_frame_vertex_data_set(data->mesh, 0, EVAS_3D_VERTEX_COLOR, + 12 * sizeof(float), &cube_vertices[ 6]); + evas_3d_mesh_frame_vertex_data_set(data->mesh, 0, EVAS_3D_VERTEX_TEXCOORD, + 12 * sizeof(float), &cube_vertices[10]); + + evas_3d_mesh_index_data_set(data->mesh, EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT, + 36, &cube_indices[0]); + evas_3d_mesh_vertex_assembly_set(data->mesh, EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES); + + evas_3d_mesh_shade_mode_set(data->mesh, EVAS_3D_SHADE_MODE_FLAT); + + evas_3d_mesh_frame_material_set(data->mesh, 0, data->material); + + data->mesh_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_MESH); + evas_3d_node_member_add(data->root_node, data->mesh_node); + evas_3d_node_mesh_add(data->mesh_node, data->mesh); +} + +static void +_scene_setup(Scene_Data *data) +{ + data->scene = evas_3d_scene_add(evas); + evas_3d_scene_size_set(data->scene, WIDTH, HEIGHT); + evas_3d_scene_background_color_set(data->scene, 0.0, 0.0, 0.0, 0.0); + + data->root_node = evas_3d_node_add(evas, EVAS_3D_NODE_TYPE_NODE); + + _camera_setup(data); + _light_setup(data); + _mesh_setup(data); + + evas_3d_scene_root_node_set(data->scene, data->root_node); + evas_3d_scene_camera_node_set(data->scene, data->camera_node); +} + +int +main(void) +{ + Scene_Data data; + + if (!ecore_evas_init()) + return 0; + + ecore_evas = ecore_evas_new(NULL, 10, 10, WIDTH, HEIGHT, NULL); + + if (!ecore_evas) + return 0; + + ecore_evas_callback_delete_request_set(ecore_evas, _on_delete); + ecore_evas_callback_resize_set(ecore_evas, _on_canvas_resize); + ecore_evas_show(ecore_evas); + + evas = ecore_evas_get(ecore_evas); + + /* Add a background rectangle objects. */ + background = evas_object_rectangle_add(evas); + evas_object_color_set(background, 0, 0, 0, 255); + evas_object_move(background, 0, 0); + evas_object_resize(background, WIDTH, HEIGHT); + evas_object_show(background); + + /* Add a background imageg. */ + source = evas_object_image_filled_add(evas); + evas_object_image_size_set(source, IMG_WIDTH, IMG_HEIGHT); + evas_object_move(source, 0, 0); + evas_object_resize(source, IMG_WIDTH, IMG_HEIGHT); + evas_object_show(source); + + /* Add an image object for 3D scene rendering. */ + image = evas_object_image_filled_add(evas); + evas_object_move(image, 0, 0); + evas_object_resize(image, WIDTH, HEIGHT); + evas_object_show(image); + + /* Setup scene */ + _scene_setup(&data); + + /* Set the image object as render target for 3D scene. */ + evas_object_image_3d_scene_set(image, data.scene); + + /* Add animation timer callback. */ + ecore_timer_add(0.016, _animate_scene, &data); + + /* Enter main loop. */ + ecore_main_loop_begin(); + + ecore_evas_free(ecore_evas); + ecore_evas_shutdown(); + + return 0; +} diff --git a/src/examples/evas/normal_lego.png b/src/examples/evas/normal_lego.png Binary files differnew file mode 100644 index 0000000000..6a74ad9774 --- /dev/null +++ b/src/examples/evas/normal_lego.png diff --git a/src/examples/evas/sonic.md2 b/src/examples/evas/sonic.md2 Binary files differnew file mode 100644 index 0000000000..228edb9fb7 --- /dev/null +++ b/src/examples/evas/sonic.md2 diff --git a/src/examples/evas/sonic.png b/src/examples/evas/sonic.png Binary files differnew file mode 100644 index 0000000000..01cb2ede8e --- /dev/null +++ b/src/examples/evas/sonic.png diff --git a/src/lib/evas/Evas.h b/src/lib/evas/Evas.h index dc4d435712..05ac2c01e7 100644 --- a/src/lib/evas/Evas.h +++ b/src/lib/evas/Evas.h @@ -292,7 +292,6 @@ extern "C" { #ifdef EFL_EO_API_SUPPORT #include <Evas_Eo.h> #endif - #ifdef __cplusplus } #endif diff --git a/src/lib/evas/Evas_3D.h b/src/lib/evas/Evas_3D.h new file mode 100644 index 0000000000..55b9d31551 --- /dev/null +++ b/src/lib/evas/Evas_3D.h @@ -0,0 +1,2151 @@ +#ifndef _EVAS_3D_H +#define _EVAS_3D_H + +#include <Evas.h> + +/** + * @defgroup Evas_3D Evas 3D Extensions + * + * Evas extension to support 3D rendering. + * + * @ingroup Evas + */ + +/** + * @page evas_3d_main Evas 3D + * + * @date 2014 (created) + * + * @section toc Table of Contents + * + * @li @ref evas_3d_intro + * @li @ref evas_3d_example + * + * @section evas_3d_intro Introduction + * + * Evas 3D is an extension to support 3D scene graph rendering into 2D Evas + * canvas supporting typicall tree-based scene graph manipulation and other 3D + * graphics rendering techniques. + * + * Evas 3D provides 3D objects which are used for describing 3D scene and APIs + * to connect the scene with an evas image object so that the scene is rendered + * on that image object. + * + * Contruction of a 3D scene is process of locating desired cameras, lights and + * meshes in the scene. Typically the scene is structured with some hierarchical + * data structure. Evas 3D support n-ary tree structure for describing the + * scene. Node is used to build the tree representation of the scene. Other + * objects, like camera, light and mesh can be located in the scene by being + * contained in a node. + * + * Like other 3D graphics engine, Evas 3D support standard 3D rendering method + * like flat shading, phong shading and normal map and other features like + * texture mapping, triangle meshes. + * + * Besides all the traditional 3D rendering things, one of the key feature of + * the Evas 3D is that it is able to use existing evas objects as textures + * inside of the 3D scene. "Existing evas objects" means all the EFL widgets + * and applications. By supporting this, it is easy to make 3D version of an + * application without modifying the original source that much. + * + * Also, 3D scene can be located on the canvas naturally stacked with existing + * evas objects. This can make it possible putting 3D things into existing 2D + * application layouts. + * + * @section evas_3d_example Introductory Example + * + * @include evas-3d-cube.c + */ + +/** + * @defgroup Evas_3D_Types Types & Enums + * @ingroup Evas_3D + * + * Primitive type definitions and enumations. + */ + +/** + * @defgroup Evas_3D_Object Generic 3D Object Descriptions + * @ingroup Evas_3D + * + * Evas 3D object is a generic type of all evas 3D objects like scene, node, + * camera, light, mesh, texture and material. Evas 3D object is basically + * reference counted. Any successful function call on an object which make a + * reference to an another object will increase the reference count. When the + * reference count gets to 0, the object will be actually deleted. + * + * Any modifications are automatically propagated to other objects referencing + * the modified objects. As a result, if the scene object is set to modified + * state, all image objects having the scene as a rendering source are marked + * as dirty, so that rendering will be updated at next frame. But all these + * things are done internally, so feel free to forget about calling some kind + * of update functions. + */ + +/** + * @defgroup Evas_3D_Scene Scene Object + * @ingroup Evas_3D + * + * A scene represents a captured image of a scene graph through its viewing + * camera. A scene can be set to an image object to be displayed on the Evas + * canvas by using evas_object_image_3d_scene_set() function. + */ + +/** + * @defgroup Evas_3D_Node Node Object + * @ingroup Evas_3D + * + * A node is used for hierarchical construction of a scene graph. Evas 3D + * provides n-ary tree structure for the scene graph construction.A node has + * its position, orientation and scale. Other objects, like camera, light and + * mesh can be contained in a node to be located in a 3D space. + */ + +/** + * @defgroup Evas_3D_Camera Camera Object + * @ingroup Evas_3D + * + * A camera object is used for taking a picture of a scene graph. A camera + * object itself is just a set of properties on how the camera should take the + * picture (like focus length and film size of the real world cameras). To be + * able to take a shot of the scene, a camera should be located in the scene, so + * that it has its viewing position and direction. It is done by containing the + * camera on a node. If one wants to locate several cameras having same + * properties, instead of creating multiple cameras, just create one camera and + * multiple nodes containing the camera and locate them at each desired position + * and direction. Just for convinience, use evas_3d_node_position_set() to move + * the camera to desired position and use evas_3d_node_look_at_set() to adjust + * the viewing direction of the camera. + */ + +/** + * @defgroup Evas_3D_Light Light Object + * @ingroup Evas_3D + * + * A light object represents a set of properties of a light source. Evas 3D + * provides standard reflection model that of ambient, diffuse and specular + * reflection model. Also, Evas 3D support 3 types of light model, directional, + * point and spot light. Light position and direction is determined by the node + * containing the light. + */ + +/** + * @defgroup Evas_3D_Mesh Mesh Object + * @ingroup Evas_3D + * + * A mesh object is a set of information on a visible geometrical object like + * character model, terrain or other structures and entities. Evas 3D support + * key-frame-based mesh animation, so a mesh can have multiple frames and each + * frame has its own material and geometric data. Like other data objects, a + * mesh can be located on a scene by being contained in a node. The mesh is + * transformed from its modeling coordinate space into the node's coordinate + * space. Also, the frame number is saved in the containing node. So, one can + * locate multiple nodes having same mesh object with different animation frame + * and transform. Unlike camera and light object, multiple meshes can be + * contained in a single node. + */ + +/** + * @defgroup Evas_3D_Texture Texture Object + * @ingroup Evas_3D + * + * A texture object is an image represents material of surfaces. A texture can + * be set to a slot of Evas_3D_Material by using evas_3d_material_texture_set() + * function. The data of a texture can be loaded from memory, file and other + * Evas_Object. + */ + +/** + * @defgroup Evas_3D_Material Material Object + * @ingroup Evas_3D + * + * A material object represents properties of surfaces. Evas 3D defines the + * properties with 5 material attributes, ambient, diffuse, specular emission + * and normal. Each attribute have its color value and texture map. Materials + * are used to determine the color of mesh surfaces. + */ + +/** + * @typedef Evas_Real + * + * Floating-point data type + * + * Evas 3D use its own floating-point type. Even though it's a standard IEEE + * 754 floating-point type always use Evas_Real for the type safety. Double + * precision and fixed-point types will be useful but it's not supported yet. + * + * @ingroup Evas_3D_Types + */ +typedef float Evas_Real; + +/** + * @typedef Evas_3D_Scene + * + * Scene object handle + * + * @ingroup Evas_3D_Scene + */ +typedef struct _Evas_3D_Scene Evas_3D_Scene; + +/** + * @typedef Evas_3D_Node + * + * Node object handle + * + * @ingroup Evas_3D_Node + */ +typedef struct _Evas_3D_Node Evas_3D_Node; + +/** + * @typedef Evas_3D_Camera + * + * Camera object handle + * + * @ingroup Evas_3D_Camera + */ +typedef struct _Evas_3D_Camera Evas_3D_Camera; + +/** + * @typedef Evas_3D_Light + * + * Light object handle + * + * @ingroup Evas_3D_Light + */ +typedef struct _Evas_3D_Light Evas_3D_Light; + +/** + * @typedef Evas_3D_Mesh + * + * Mesh object handle + * + * @ingroup Evas_3D_Mesh + */ +typedef struct _Evas_3D_Mesh Evas_3D_Mesh; + +/** + * @typedef Evas_3D_Texture + * + * Texture object handle + * + * @ingroup Evas_3D_Texture + */ +typedef struct _Evas_3D_Texture Evas_3D_Texture; + +/** + * @typedef Evas_3D_Material + * + * Material object handle + * + * @ingroup Evas_3D_Material + */ +typedef struct _Evas_3D_Material Evas_3D_Material; + +/** + * Transform space + * @ingroup Evas_3D_Types + */ +typedef enum _Evas_3D_Space +{ + EVAS_3D_SPACE_LOCAL, /**< Local coordinate space */ + EVAS_3D_SPACE_PARENT, /**< Parent coordinate space */ + EVAS_3D_SPACE_WORLD, /**< World coordinate space */ +} Evas_3D_Space; + +/** + * Types of a node + * @ingroup Evas_3D_Types + */ +typedef enum _Evas_3D_Node_Type +{ + EVAS_3D_NODE_TYPE_NODE, /**< Node with no items */ + EVAS_3D_NODE_TYPE_CAMERA, /**< Node which can contain camera object */ + EVAS_3D_NODE_TYPE_LIGHT, /**< Node which can contain light object */ + EVAS_3D_NODE_TYPE_MESH, /**< Node which can contain mesh objects */ +} Evas_3D_Node_Type; + +/** + * Vertex attribute IDs + * @ingroup Evas_3D_Types + */ +typedef enum _Evas_3D_Vertex_Attrib +{ + EVAS_3D_VERTEX_POSITION, /**< Vertex position */ + EVAS_3D_VERTEX_NORMAL, /**< Vertex normal */ + EVAS_3D_VERTEX_TANGENT, /**< Vertex tangent (for normal mapping) */ + EVAS_3D_VERTEX_COLOR, /**< Vertex color */ + EVAS_3D_VERTEX_TEXCOORD, /**< Vertex texture coordinate */ +} Evas_3D_Vertex_Attrib; + +/** + * Index formats + * @ingroup Evas_3D_Types + */ +typedef enum _Evas_3D_Index_Format +{ + EVAS_3D_INDEX_FORMAT_NONE, /**< Indexing is not used */ + EVAS_3D_INDEX_FORMAT_UNSIGNED_BYTE, /**< Index is of type unsigned byte */ + EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT, /**< Index is of type unsigned short */ +} Evas_3D_Index_Format; + +/** + * Vertex assembly modes + * @ingroup Evas_3D_Types + * + * Vertex assembly represents how the vertices are organized into geometric + * primitives. + */ +typedef enum _Evas_3D_Vertex_Assembly +{ + EVAS_3D_VERTEX_ASSEMBLY_POINTS, /**< A vertex is rendered as a point */ + EVAS_3D_VERTEX_ASSEMBLY_LINES, /**< Two vertices are organized as a line */ + EVAS_3D_VERTEX_ASSEMBLY_LINE_STRIP, /**< Vertices are organized as a connected line path */ + EVAS_3D_VERTEX_ASSEMBLY_LINE_LOOP, /**< Vertices are organized as a closed line path */ + EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES, /**< Three vertices are organized as a triangle */ + EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_STRIP, /**< Vertices are organized as connected triangles */ + EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_FAN, /**< Vertices are organized as a triangle fan */ +} Evas_3D_Vertex_Assembly; + +/** + * Color formats of pixel data + * @ingroup Evas_3D_Types + */ +typedef enum _Evas_3D_Color_Format +{ + EVAS_3D_COLOR_FORMAT_RGBA, /**< Color contains full components, red, green, blue and alpha */ + EVAS_3D_COLOR_FORMAT_RGB, /**< Color contains only red, green and blue components */ + EVAS_3D_COLOR_FORMAT_ALPHA, /**< Color contains only alpha component */ +} Evas_3D_Color_Format; + +/** + * Pixel formats + * @ingroup Evas_3D_Types + */ +typedef enum _Evas_3D_Pixel_Format +{ + EVAS_3D_PIXEL_FORMAT_8, /**< 8-bit pixel with single component */ + EVAS_3D_PIXEL_FORMAT_565, /**< 16-bit pixel with three components (5-6-5 bit) */ + EVAS_3D_PIXEL_FORMAT_888, /**< 24-bit pixel with three 8-bit components */ + EVAS_3D_PIXEL_FORMAT_8888, /**< 32-bit pixel with four 8-bit components */ + EVAS_3D_PIXEL_FORMAT_4444, /**< 16-bit pixel with four 4-bit components */ + EVAS_3D_PIXEL_FORMAT_5551, /**< 16-bit pixel with four components (5-5-5-1 bit) */ +} Evas_3D_Pixel_Format; + +/** + * Wrap modes + * @ingroup Evas_3D_Types + */ +typedef enum _Evas_3D_Wrap_Mode +{ + EVAS_3D_WRAP_MODE_CLAMP, /**< Values will be clamped to be in range [min, max] */ + EVAS_3D_WRAP_MODE_REPEAT, /**< Values will be repeated */ + EVAS_3D_WRAP_MODE_REFLECT, /**< Values will be repeated in a reflected manner */ +} Evas_3D_Wrap_Mode; + +/** + * Texture filters + * @ingroup Evas_3D_Types + */ +typedef enum _Evas_3D_Texture_Filter +{ + EVAS_3D_TEXTURE_FILTER_NEAREST, /**< Samples nearest texel */ + EVAS_3D_TEXTURE_FILTER_LINEAR, /**< Lineary interpolate nearby texels */ + EVAS_3D_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST, /**< Nearest sampling mipmap */ + EVAS_3D_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST, /**< Nearest sampling mipmap and interpolate */ + EVAS_3D_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR, /**< Linear sampling in nearest mipmap */ + EVAS_3D_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR, /**< Linear sampling in mipmap and interpolate */ +} Evas_3D_Texture_Filter; + +/** + * Shade modes + * @ingroup Evas_3D_Types + */ +typedef enum _Evas_3D_Shade_Mode +{ + EVAS_3D_SHADE_MODE_VERTEX_COLOR, /**< Shaded using vertex color attribute */ + EVAS_3D_SHADE_MODE_DIFFUSE, /**< Shaded using material diffuse term */ + EVAS_3D_SHADE_MODE_FLAT, /**< Per-vertex flat lighting */ + EVAS_3D_SHADE_MODE_PHONG, /**< Per-pixel phong shading */ + EVAS_3D_SHADE_MODE_NORMAL_MAP, /**< Per-pixel normal map shading */ +} Evas_3D_Shade_Mode; + +/** + * Material attributes + * @ingroup Evas_3D_Types + */ +typedef enum _Evas_3D_Material_Attrib +{ + EVAS_3D_MATERIAL_AMBIENT, /**< Ambient term */ + EVAS_3D_MATERIAL_DIFFUSE, /**< Diffuse term */ + EVAS_3D_MATERIAL_SPECULAR, /**< Specular term */ + EVAS_3D_MATERIAL_EMISSION, /**< Emission term */ + EVAS_3D_MATERIAL_NORMAL, /**< Normal map term */ +} Evas_3D_Material_Attrib; + +/** + * Mesh file type + * @ingroup Evas_3D_Types + */ +typedef enum _Evas_3D_Mesh_File_Type +{ + EVAS_3D_MESH_FILE_TYPE_MD2, /**< Quake's MD2 mesh file format */ +} Evas_3D_Mesh_File_Type; + +typedef enum _Evas_3D_Pick_Type +{ + EVAS_3D_PICK_NODE, + EVAS_3D_PICK_MESH, +} Evas_3D_Pick_Type; + +/** + * Set the scene on an image object. + * + * @param obj Image object. + * @param scene Scene object used as a content of the given image object. + * + * An image object can get its content from various sources like memory buffers, + * image files and other evas object. A scene also can be a source for an image + * object to display the rendered result onto evas canvas. + * + * Any existing content (data, file or proxy source) will be removed after this + * call. Setting @p src to @c NULL detach the 3D scene from the image object. + * + * @ingroup Evas_3D_Scene + */ +EAPI void evas_object_image_3d_scene_set(Evas_Object *obj, Evas_3D_Scene *scene) EINA_ARG_NONNULL(1); + +/** + * Get the current scene of an image object. + * + * @param obj Image object. + * @return Scene object handle (if any), or @c NULL if there's no scene attached. + * + * @ingroup Evas_3D_Scene + */ +EAPI Evas_3D_Scene *evas_object_image_t3d_scene_get(const Evas_Object *obj) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Create a new scene on the given Evas @p e canvas. + * + * @param e The given canvas. + * @return The created scene handle. + * + * @ingroup Evas_3D_Scene + */ +EAPI Evas_3D_Scene *evas_3d_scene_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Delete a scene from its belonging Evas canvas. + * + * @param scene The given scene to be deleted. + * + * @ingroup Evas_3D_Scene + */ +EAPI void evas_3d_scene_del(Evas_3D_Scene *scene) EINA_ARG_NONNULL(1); + +/** + * Get the Evas canvas where the given scene belongs to. + * + * @param scene The given scene. + * @return The Evas canvas. + * + * @ingroup Evas_3D_Scene + */ +EAPI Evas *evas_3d_scene_evas_get(const Evas_3D_Scene *scene) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the root node of a scene. + * + * @param scene The given scene. + * @param node A node which will be used as a root node for the scene. + * + * @ingroup Evas_3D_Scene + */ +EAPI void evas_3d_scene_root_node_set(Evas_3D_Scene *scene, Evas_3D_Node *node) EINA_ARG_NONNULL(1); + +/** + * Get the root node of a scene. + * + * @param scene The given scene. + * @return The root node of the given scene. + * + * @ingroup Evas_3D_Scene + */ +EAPI Evas_3D_Node *evas_3d_scene_root_node_get(const Evas_3D_Scene *scene) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the camera node of a scene. + * + * @param scene The given scene. + * @param node A node which will be used as a camera node for the scene. + * + * @ingroup Evas_3D_Scene + */ +EAPI void evas_3d_scene_camera_node_set(Evas_3D_Scene *scene, Evas_3D_Node *node) EINA_ARG_NONNULL(1); + +/** + * Get the camera node of a scene. + * + * @param scene The given scene. + * @return The camera node of the given scene. + * + * @ingroup Evas_3D_Scene + */ +EAPI Evas_3D_Node *evas_3d_scene_camera_node_get(const Evas_3D_Scene *scene) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the resolution of a scene. + * + * @param scene The given scene. + * @param w Width of the resolution. + * @param h Height of the resolution. + * + * A scene should be rendered to be displayed through an image objects. The + * resolution defines size of the internal surface holding the rendered result. + * + * @ingroup Evas_3D_Scene + */ +EAPI void evas_3d_scene_size_set(Evas_3D_Scene *scene, int w, int h) EINA_ARG_NONNULL(1); + +/** + * Get the internal resolution of a scene. + * + * @param scene The given scene. + * @param w Pointer to receive width of the resolution. + * @param h Pointer to receive height of the resolution. + * + * @ingroup Evas_3D_Scene + */ +EAPI void evas_3d_scene_size_get(const Evas_3D_Scene *scene, int *w, int *h) EINA_ARG_NONNULL(1); + +/** + * Set the background color of a scene. + * + * @param scene The given scene. + * @param r Red component of the background color. + * @param g Green component of the background color. + * @param b Blue component of the background color. + * @param a Alpha component of the background color. + * + * Background color defines initial color of pixels before a scene is rendered. + * If you want to display a scene with background evas objects are still + * remaining as if it was the background, set the alpha term to 0.0. + * + * Default background color is (0.0, 0.0, 0.0, 0.0). + * + * @ingroup Evas_3D_Scene + */ +EAPI void evas_3d_scene_background_color_set(Evas_3D_Scene *scene, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) EINA_ARG_NONNULL(1); + +/** + * Get the background color of a scene. + * + * @param scene The given scene. + * @param r Pointer to receive red component of the background color. + * @param g Pointer to receive green component of the background color. + * @param b Pointer to receive blue component of the background color. + * @param a Pointer to receive alpha component of the background color. + * + * @ingroup Evas_3D_Scene + */ +EAPI void evas_3d_scene_background_color_get(const Evas_3D_Scene *scene, Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) EINA_ARG_NONNULL(1); + +/** + * Get information on the most front visible mesh for the given position. + * + * @param scene The given scene. + * @param x X coordinate of the picking position. + * @param y Y coordinate of the picking position. + * @param node Pointer to receive the node contains the picked mesh. + * @param mesh Pointer to receive the picked mesh. + * @param s Pointer to receive the texture "s" coordinate. + * @param t Pointer to receive the texture "t" coordinate. + * + * (x, y) is the screen coordinate of the given scene. That is, left-top is + * (0, 0) and right-bottom is (w, h) where (w, h) is the size of the scene. + * The texture coordinate is useful when using proxy texture source. + * + * @ingroup Evas_3D_Scene + */ +EAPI Eina_Bool evas_3d_scene_pick(const Evas_3D_Scene *scene, Evas_Real x, Evas_Real y, Evas_3D_Node **node, Evas_3D_Mesh **mesh, Evas_Real *s, Evas_Real *t) EINA_ARG_NONNULL(1); + +/** + * Create a new node on the given Evas @p canvas. + * + * @param e The given canvas. + * @param type The type of the node. + * @return The created node handle. + * + * @ingroup Evas_3D_Node + */ +EAPI Evas_3D_Node *evas_3d_node_add(Evas *e, Evas_3D_Node_Type type) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Delete a node from its belonging Evas canvas. + * + * @param node The given node. + * + * @see evas_3d_node_add() + * + * @ingroup Evas_3D_Node + */ +EAPI void evas_3d_node_del(Evas_3D_Node *node) EINA_ARG_NONNULL(1); + +/** + * Get the type of the given node. + * + * @param node The given node. + * @return The type of the given node. + * + * @see evas_3d_node_add() + * + * @ingroup Evas_3D_Node + */ +EAPI Evas_3D_Node_Type evas_3d_node_type_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Get the Evas canvas where the given node belongs to. + * + * @param node The given node. + * @return The Evas canvas. + * + * @see evas_3d_node_add() + * + * @ingroup Evas_3D_Node + */ +EAPI Evas *evas_3d_node_evas_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Add a member node to the given node. + * + * @param node The given node which will be the parent. + * @param member Node object to be added. + * + * Nodes can be constructed into N-ary tree structure like other ordinary scene + * graph. Basically a node inherit transforms from its parent. + * + * @see evas_3d_node_parent_get() + * + * @ingroup Evas_3D_Node + */ +EAPI void evas_3d_node_member_add(Evas_3D_Node *node, Evas_3D_Node *member) EINA_ARG_NONNULL(1, 2); + +/** + * Delete a member node from the given node. + * + * @param node The given node. + * @param member Member node to be deleted from the given node. + * + * @see evas_3d_node_member_add() + * + * @ingroup Evas_3D_Node + */ +EAPI void evas_3d_node_member_del(Evas_3D_Node *node, Evas_3D_Node *member) EINA_ARG_NONNULL(1, 2); + +/** + * Get the parent node of the given node. + * + * @param node The given node. + * @return The parent node of the given node. + * + * @see evas_3d_node_member_add() + * + * @ingroup Evas_3D_Node + */ +EAPI Evas_3D_Node *evas_3d_node_parent_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Get the list of member nodes of the given node. + * + * @param node The given node. + * @return The list of member nodes if any or @c NULL if there are none. + * + * @see evas_3d_node_member_add() + * + * @ingroup Evas_3D_Node + */ +EAPI const Eina_List *evas_3d_node_member_list_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * @defgroup Evas_3D_Node_Transform + * @ingroup Evas_3D_Node + * + * Functions that manipulate node transforms. + * + * Evas_3D_Node does not provide standard 4x4 matrix to transform something. + * Instead, one can set position, orientation and scale of a node separately. + * A node will be first scaled and rotated and then translated according to its + * position, orientation and scale. Each transform attributes can be set to + * inherit from its parent or not. + */ + +/** + * Set the position of the given node. + * + * @param node The given node. + * @param x X coordinate of the position. + * @param y Y coordinate of the position. + * @param z Z coordinate of the position. + * + * According to the inheritance flag, (x, y, z) can be a world space position or + * parent space position. + * + * Default position is (0.0, 0.0, 0.0). + * + * @see evas_3d_node_position_inherit_set() + * + * @ingroup Evas_3D_Node_Transform + */ +EAPI void evas_3d_node_position_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z) EINA_ARG_NONNULL(1); + +/** + * Set the orientation of the given node using quaternion. + * + * @param node The given node. + * @param x X term of the orientation quaternion (w, x, y, z) + * @param y Y term of the orientation quaternion (w, x, y, z) + * @param z Z term of the orientation quaternion (w, x, y, z) + * @param w W term of the orientation quaternion (w, x, y, z) + * + * According the the inheritance flag, (w, x, y, z) can be a world space + * orientation or parent space orientation. + * + * Default orientation is (1.0, 0.0, 0.0, 0.0) (identity quaternion). + * + * @see evas_3d_node_orientation_inherit_set() + * + * @ingroup Evas_3D_Node_Transform + */ +EAPI void evas_3d_node_orientation_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z, Evas_Real w) EINA_ARG_NONNULL(1); + +/** + * Set the orientation of the given node using euler angle. + * + * @param node The given node. + * @param x Rotation angle about X-axis. + * @param y Rotation angle about Y-axis. + * @param z Rotation angle about Z-axis. + * + * @see evas_3d_node_orientation_set() + * + * @ingroup Evas_3D_Node_Transform + */ +EAPI void evas_3d_node_orientation_euler_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z) EINA_ARG_NONNULL(1); + +/** + * Set the orientation of the given node using axis-angle. + * + * @param node The given node. + * @param angle Rotation angle. + * @param x X term of the rotation axis. + * @param y Y term of the rotation axis. + * @param z Z term of the rotation axis. + * + * @see evas_3d_node_orientation_set() + * + * @ingroup Evas_3D_Node_Transform + */ +EAPI void evas_3d_node_orientation_angle_axis_set(Evas_3D_Node *node, Evas_Real angle, Evas_Real x, Evas_Real y, Evas_Real z) EINA_ARG_NONNULL(1); + +/** + * Set the scale of the given node. + * + * @param node The given node. + * @param x Scale factor along X-axis. + * @param y Scale factor along Y-axis. + * @param z Scale factor along Z-axis. + * + * According to the inheritance flag, (x, y, z) can be a world space scale or + * parent space scale. Be careful when using non-uniform scale factor with + * inheritance, each transform attributes are not affected by other attributes. + * + * Default scale is (1.0, 1.0, 1.0). + * + * @see evas_3d_node_scale_inherit_set() + * + * @ingroup Evas_3D_Node_Transform + */ +EAPI void evas_3d_node_scale_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z) EINA_ARG_NONNULL(1); + +/** + * Get the position of the given node. + * + * @param node The given node. + * @param x Pointer to receive X coordinate of the position. + * @param y Pointer to receive Y coordinate of the position. + * @param z Pointer to receive Z coordinate of the position. + * + * @see evas_3d_node_position_set() + * + * @ingroup Evas_3D_Node_Transform + */ +EAPI void evas_3d_node_position_get(const Evas_3D_Node *node, Evas_3D_Space space, Evas_Real *x, Evas_Real *y, Evas_Real *z) EINA_ARG_NONNULL(1); + +/** + * Get the orientation of the given node as quaternion. + * + * @param node The given node. + * @param x Pointer to receive X term of the orientation quaternion. + * @param y Pointer to receive Y term of the orientation quaternion. + * @param z Pointer to receive Z term of the orientation quaternion. + * @param w Pointer to receive W term of the orientation quaternion. + * + * @see evas_3d_node_orientation_set() + * + * @ingroup Evas_3D_Node_Transform + */ +EAPI void evas_3d_node_orientation_get(const Evas_3D_Node *node, Evas_3D_Space space, Evas_Real *x, Evas_Real *y, Evas_Real *z, Evas_Real *w) EINA_ARG_NONNULL(1); +EAPI void evas_3d_node_scale_get(const Evas_3D_Node *node, Evas_3D_Space space, Evas_Real *x, Evas_Real *y, Evas_Real *z) EINA_ARG_NONNULL(1); + +/** + * Set the position inheritance flag of the given node. + * + * @param node The given node. + * @param inherit Whether to inherit parent position @c EINA_TRUE or not @c EINA_FALSE. + * + * When inheritance is enabled, a node's world space position is determined by + * adding the parent node's world position and the node's position, otherwise, + * the node's position will be the world space position. + * + * @ingroup Evas_3D_Node_Transform + */ +EAPI void evas_3d_node_position_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) EINA_ARG_NONNULL(1); + +/** + * Set the orientation inheritance flag of the given node. + * + * @param node The given node. + * @param inherit Whether to inherit parent orientation @c EINA_TRUE or not @c EINA_FALSE. + * + * When inheritance is enabled, a node's world space orientation is determined + * by multiplying the parent node's world orientation and the node's + * orientation, otherwise, the node's orientation will be the world space + * orientation. + * + * @ingroup Evas_3D_Node_Transform + */ +EAPI void evas_3d_node_orientation_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) EINA_ARG_NONNULL(1); + +/** + * Set the scale inheritance flag of the given node. + * + * @param node The given node. + * @param inherit Whether to inherit parent scale @c EINA_TRUE or not @c EINA_FALSE. + * + * When inheritance is enabled, a node's world space scale is determined by + * multiplying the parent node's world scale and the node's scale, otherwise, + * the node's scale will be the world space scale. + * + * @ingroup Evas_3D_Node_Transform + */ +EAPI void evas_3d_node_scale_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) EINA_ARG_NONNULL(1); + +/** + * Get the position inheritance flag of the given node. + * + * @param node The given node. + * @return @c EINA_TRUE if inheritance is enabled, or @c EINA_FALSE if not. + * + * @see evas_3d_node_position_inherit_set() + * + * @ingroup Evas_3D_Node_Transform + */ +EAPI Eina_Bool evas_3d_node_position_inherit_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Get the orientation inheritance flag of the given node. + * + * @param node The given node. + * @return @c EINA_TRUE if inheritance is enabled, or @c EINA_FALSE if not. + * + * @see evas_3d_node_orientation_inherit_set() + * + * @ingroup Evas_3D_Node_Transform + */ +EAPI Eina_Bool evas_3d_node_orientation_inherit_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Get the scale inheritance flag of the given node. + * + * @param node The given node. + * @return @c EINA_TRUE if inheritance is enabled, or @c EINA_FALSE if not. + * + * @see evas_3d_node_scale_inherit_set() + * + * @ingroup Evas_3D_Node_Transform + */ +EAPI Eina_Bool evas_3d_node_scale_inherit_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Rotate the given node to look at desired position. + * + * @param node The given node. + * @param target_space Space where the target position belongs to. + * @param x X coordinate of the target position. + * @param y Y coordinate of the target position. + * @param z Z coordinate of the target position. + * @param up_space Space where the up vector belongs to. + * @param ux X term of the up vector. + * @param uy Y term of the up vector. + * @param uz Z term of the up vector. + * + * This function rotate the given node so that its forward vector (negative + * Z-axis) points to the desired position and the up vector coincide with the + * given up vector. + * + * @see evas_3d_node_orientation_set() + * + * @ingroup Evas_3D_Node_Transform + */ +EAPI void evas_3d_node_look_at_set(Evas_3D_Node *node, Evas_3D_Space target_space, Evas_Real x, Evas_Real y, Evas_Real z, Evas_3D_Space up_space, Evas_Real ux, Evas_Real uy, Evas_Real uz) EINA_ARG_NONNULL(1); + +/** + * Set a camera to the given node. + * + * @param node The given node. + * @param camera The camera to be set. + * + * If the node is not of type EVAS_3D_NODE_TYPE_CAMERA, error message will be + * generated and nothing happens. + * + * @see evas_3d_node_add() + * + * @ingroup Evas_3D_Node + */ +EAPI void evas_3d_node_camera_set(Evas_3D_Node *node, Evas_3D_Camera *camera) EINA_ARG_NONNULL(1); + +/** + * Get the camera of the given node. + * + * @param node The given node. + * @return The camera of the given node if any, or @c NULL if there're none. + * + * @see evas_3d_node_camera_set() + * + * @ingroup Evas_3D_Node + */ +EAPI Evas_3D_Camera *evas_3d_node_camera_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the light of the given node. + * + * @param node The given node. + * @param light The light to be set. + * + * If the node is not of type EVAS_3D_NODE_TYPE_LIGHT, error message will be + * generated and nothing happens. + * + * @see evas_3d_node_add() + * + * @ingroup Evas_3D_Node + */ +EAPI void evas_3d_node_light_set(Evas_3D_Node *node, Evas_3D_Light *light) EINA_ARG_NONNULL(1); + +/** + * Get the light of the given node. + * + * @param node The given node. + * @return The light of the given node if any, or @c NULL if there're none. + * + * @see evas_3d_node_light_set() + * + * @ingroup Evas_3D_Node + */ +EAPI Evas_3D_Light *evas_3d_node_light_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Add a mesh to the given node. + * + * @param node The given node. + * @param mesh The mesh to be added. + * + * If the node is not of type EVAS_3D_NODE_TYPE_MESH, error message will be + * generated and nothing happens. + * + * @see evas_3d_node_add() + * + * @ingroup Evas_3D_Node + */ +EAPI void evas_3d_node_mesh_add(Evas_3D_Node *node, Evas_3D_Mesh *mesh) EINA_ARG_NONNULL(1); + +/** + * Delete a mesh from the given node. + * + * @param node The given node. + * @param mesh The mesh to be deleted. + * + * If the node is not of type EVAS_3D_NODE_TYPE_MESH or the given mesh does not + * belong to the given node, error message will be gnerated and nothing happens. + * + * @see evas_3d_node_mesh_add() + * + * @ingroup Evas_3D_Node + */ +EAPI void evas_3d_node_mesh_del(Evas_3D_Node *node, Evas_3D_Mesh *mesh) EINA_ARG_NONNULL(1); + +/** + * Get the list of meshes of the given node. + * + * @param node The given node. + * @return The list of meshes if any, or @c NULL if there're none. + * + * If the node is not of type EVAS_3D_NODE_TYPE_MESH, error message will be + * generated and @c NULL will be returned. If there're no meshes in the given + * node, @c NULL will be returned. + * + * @see evas_3d_node_mesh_add() + * + * @ingroup Evas_3D_Node + */ +EAPI const Eina_List *evas_3d_node_mesh_list_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the animation frame number of the given node for the given mesh. + * + * @param node The given node. + * @param mesh The given mesh. + * @param frame The animation frame number. + * + * If the node is not of type EVAS_3D_NODE_TYPE_MESH or the given mesh does not + * belong to the given mesh error mesh will be generated and nothing happens. + * + * Default mesh frame is 0. + * + * @see evas_3d_node_mesh_add() + * + * @ingroup Evas_3D_Node + */ +EAPI void evas_3d_node_mesh_frame_set(Evas_3D_Node *node, Evas_3D_Mesh *mesh, int frame) EINA_ARG_NONNULL(1); + +/** + * Set the animation frame number of the given node for the given mesh. + * + * @param node The given node. + * @param mesh The given mesh. + * @param frame The animation frame number. + * + * If the node is not of type EVAS_3D_NODE_TYPE_MESH or the given mesh does not + * belong to the given mesh error mesh will be generated and nothing happens. + * + * @see evas_3d_node_mesh_add() + * + * @ingroup Evas_3D_Node + */ +EAPI int evas_3d_node_mesh_frame_get(const Evas_3D_Node *node, Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Create a new camera on the given Evas @p canvas. + * + * @param e The given canvas. + * @return The created camera handle. + * + * @ingroup Evas_3D_Camera + */ +EAPI Evas_3D_Camera *evas_3d_camera_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Delete a node from its belonging Evas canvas. + * + * @param camera The given camera. + * + * @see evas_3d_camera_add() + * + * @ingroup Evas_3D_Camera + */ +EAPI void evas_3d_camera_del(Evas_3D_Camera *camera) EINA_ARG_NONNULL(1); + +/** + * Get the Evas canvas where the given node belongs to. + * + * @param camera The given camera. + * @return The Evas canvas. + * + * @see evas_3d_node_add() + * + * @ingroup Evas_3D_Camera + */ +EAPI Evas *evas_3d_camera_evas_get(const Evas_3D_Camera *camera) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the projection matrix of the given camera. + * + * @param camera The given camera. + * @param matrix Pointer to the array of 16 Evas_Real values in column major order. + * + * Default projection matrix is identity matrix. + * + * @see evas_3d_camera_projection_perspective_set() + * @see evas_3d_camera_projection_ortho_set() + * @see evas_3d_camera_projection_frustum_set() + * + * @ingroup Evas_3D_Camera + */ +EAPI void evas_3d_camera_projection_matrix_set(Evas_3D_Camera *camera, const Evas_Real *matrix) EINA_ARG_NONNULL(1); + +/** + * Get the projection matrix of the given camera. + * + * @param camera The given camera. + * @param matrix Pointer to receive the 16 Evas_Real values in column major order. + * + * @see evas_3d_camera_projection_matrix_set() + * + * @ingroup Evas_3D_Camera + */ +EAPI void evas_3d_camera_projection_matrix_get(const Evas_3D_Camera *camera, Evas_Real *matrix) EINA_ARG_NONNULL(1, 2); + +/** + * Set the projection matrix of the given camera with perspective projection. + * + * @param camera The given camera. + * @param fovy Field of view angle in Y direction. + * @param aspect Aspect ratio. + * @param near Distance to near clipping plane. + * @param far Distance to far clipping plane. + * + * @see evas_3d_camera_projection_matrix_set() + * + * @ingroup Evas_3D_Camera + */ +EAPI void evas_3d_camera_projection_perspective_set(Evas_3D_Camera *camera, Evas_Real fovy, Evas_Real aspect, Evas_Real near, Evas_Real far) EINA_ARG_NONNULL(1); + +/** + * Set the projection matrix of the given camera with frustum projection. + * + * @param camera The given camera. + * @param left Left X coordinate of the near clipping plane. + * @param right Right X coordinate of the near clipping plane. + * @param top Top Y coordinate of the near clipping plane. + * @param bottom Bottom Y coordinate of the near clipping plane. + * @param near Distance to near clipping plane. + * @param far Distance to far clipping plane. + * + * @see evas_3d_camera_projection_matrix_set() + * + * @ingroup Evas_3D_Camera + */ +EAPI void evas_3d_camera_projection_frustum_set(Evas_3D_Camera *camera, Evas_Real left, Evas_Real right, Evas_Real bottom, Evas_Real top, Evas_Real near, Evas_Real far) EINA_ARG_NONNULL(1); + +/** + * Set the projection matrix of the given camera with orthogonal projection. + * + * @param camera The given camera. + * @param left Left X coordinate of the near clipping plane. + * @param right Right X coordinate of the near clipping plane. + * @param top Top Y coordinate of the near clipping plane. + * @param bottom Bottom Y coordinate of the near clipping plane. + * @param near Distance to near clipping plane. + * @param far Distance to far clipping plane. + * + * @see evas_3d_camera_projection_matrix_set() + * + * @ingroup Evas_3D_Camera + */ +EAPI void evas_3d_camera_projection_ortho_set(Evas_3D_Camera *camera, Evas_Real left, Evas_Real right, Evas_Real bottom, Evas_Real top, Evas_Real near, Evas_Real far) EINA_ARG_NONNULL(1); + +/** + * Create a new light on the given Evas @p canvas. + * + * @param e The given canvas. + * @return The created light handle. + * + * @ingroup Evas_3D_Light + */ +EAPI Evas_3D_Light *evas_3d_light_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Delete a node from its belonging Evas canvas. + * + * @param light The given light. + * + * @see evas_3d_light_add() + * + * @ingroup Evas_3D_Light + */ +EAPI void evas_3d_light_del(Evas_3D_Light *light) EINA_ARG_NONNULL(1); + +/** + * Get the Evas canvas where the given node belongs to. + * + * @param light The given light. + * @return The Evas canvas. + * + * @see evas_3d_node_add() + * + * @ingroup Evas_3D_Light + */ +EAPI Evas *evas_3d_light_evas_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the directional flag of the given light. + * + * @param light The given light. + * @param directional Whether the light is directional (@c EINA_TRUE), or not (@c EINA_FALSE). + * + * Directional light is a type of light which is infinitely far away with no + * attenuation. The light direction is determined by the containing node's + * forward vector (negative Z-axis). + * + * By default, directional is not enabled. + * + * @see evas_3d_node_look_at_set() + * + * @ingroup Evas_3D_Light + */ +EAPI void evas_3d_light_directional_set(Evas_3D_Light *light, Eina_Bool directional) EINA_ARG_NONNULL(1); + +/** + * Get the directional flag of the given light. + * + * @param light The given light. + * @return @c EINA_TRUE if the light is directional or @c EINA_FALSE if not. + * + * @see evas_3d_light_directional_set() + * + * @ingroup Evas_3D_Light + */ +EAPI Eina_Bool evas_3d_light_directional_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the ambient color of the given light. + * + * @param light The given light. + * @param r Red component of the ambient color between [0.0, 1.0]. + * @param g Green component of the ambient color between [0.0, 1.0]. + * @param b Blue component of the ambient color between [0.0, 1.0]. + * @param a Alpha component of the ambient color between [0.0, 1.0]. + * + * Default ambient color is (0.0, 0.0, 0.0, 1.0). + * + * @ingroup Evas_3D_Light + */ +EAPI void evas_3d_light_ambient_set(Evas_3D_Light *light, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) EINA_ARG_NONNULL(1); + +/** + * Get the ambient color of the given light. + * + * @param light The given light. + * @param r Pointer to receive the red component of the ambient color. + * @param g Pointer to receive the green component of the ambient color. + * @param b Pointer to receive the blue component of the ambient color. + * @param a Pointer to receive the alpha component of the ambient color. + * + * @see evas_3d_light_ambient_set() + * + * @ingroup Evas_3D_Light + */ +EAPI void evas_3d_light_ambient_get(const Evas_3D_Light *light, Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) EINA_ARG_NONNULL(1); + +/** + * Set the diffuse color of the given light. + * + * @param light The given light. + * @param r Red component of the diffuse color between [0.0, 1.0]. + * @param g Green component of the diffuse color between [0.0, 1.0]. + * @param b Blue component of the diffuse color between [0.0, 1.0]. + * @param a Alpha component of the diffuse color between [0.0, 1.0]. + * + * Default diffuse color is (1.0, 1.0, 1.0, 1.0). + * + * @ingroup Evas_3D_Light + */ +EAPI void evas_3d_light_diffuse_set(Evas_3D_Light *light, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) EINA_ARG_NONNULL(1); + +/** + * Get the diffuse color of the given light. + * + * @param light The given light. + * @param r Pointer to receive the red component of the diffuse color. + * @param g Pointer to receive the green component of the diffuse color. + * @param b Pointer to receive the blue component of the diffuse color. + * @param a Pointer to receive the alpha component of the diffuse color. + * + * @see evas_3d_light_diffuse_set() + * + * @ingroup Evas_3D_Light + */ +EAPI void evas_3d_light_diffuse_get(const Evas_3D_Light *light, Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) EINA_ARG_NONNULL(1); + +/** + * Get the specular color of the given light. + * + * @param light The given light. + * @param r Pointer to receive the red component of the specular color. + * @param g Pointer to receive the green component of the specular color. + * @param b Pointer to receive the blue component of the specular color. + * @param a Pointer to receive the alpha component of the specular color. + * + * Default specular color is (1.0, 1.0, 1.0, 1.0). + * + * @ingroup Evas_3D_Light + */ +EAPI void evas_3d_light_specular_set(Evas_3D_Light *light, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) EINA_ARG_NONNULL(1); + +/** + * Get the specular color of the given light. + * + * @param light The given light. + * @param r Pointer to receive the red component of the specular color. + * @param g Pointer to receive the green component of the specular color. + * @param b Pointer to receive the blue component of the specular color. + * @param a Pointer to receive the alpha component of the specular color. + * + * @see evas_3d_light_specular_set() + * + * @ingroup Evas_3D_Light + */ +EAPI void evas_3d_light_specular_get(const Evas_3D_Light *light, Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) EINA_ARG_NONNULL(1); + +/** + * Set the spot exponent of the given light. + * + * @param light The given light. + * @param exponent Spot exponent value. + * + * Higher spot exponent means intensity at the center of the cone is relatively + * stronger. Zero exponent means the light intensity is evenly distibuted. The + * spot exponent has no effect when the light is not spot light (spot cutoff + * angle is less than 180 degree). + * + * Default spot exponent is 0. + * + * @see evas_3d_light_spot_cutoff_set() + * + * @ingroup Evas_3D_Light + */ +EAPI void evas_3d_light_spot_exponent_set(Evas_3D_Light *light, Evas_Real exponent) EINA_ARG_NONNULL(1); + +/** + * Get the spot exponent of the given light. + * + * @param light The given light. + * @return The spot exponent value. + * + * @see evas_3d_light_spot_exponent_set() + * + * @ingroup Evas_3D_Light + */ +EAPI Evas_Real evas_3d_light_spot_exponent_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the spot cutoff angle of the given light. + * + * @param light The given light. + * @param cutoff Cutoff angle in degree. + * + * Only angle less than 180 degree will make it spot light, so that other spot + * light attribute will take effect. + * + * Default spot cutoff angle is 180. + * + * @ingroup Evas_3D_Light + */ +EAPI void evas_3d_light_spot_cutoff_set(Evas_3D_Light *light, Evas_Real cutoff) EINA_ARG_NONNULL(1); + +/** + * Get the spot cutoff angle of the given light. + * + * @param light The given light. + * @return Cutoff angle in degree. + * + * @see evas_3d_light_spot_cutoff_set() + * + * @ingroup Evas_3D_Light + */ +EAPI Evas_Real evas_3d_light_spot_cutoff_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the attenuation of the given light. + * + * @param light The given light. + * @param constant Constant attenuation term. + * @param linear Linear attenuation term. + * @param quadratic Quadratic attenuation term. + * + * Light attenuation has no effect with directional light. And the attenuation + * should be enabled first to take effect. The attenuation factor is calculated + * as follows. + * + * atten = 1.0 / constant + linear * distance + quadratic * distance * distance + * + * Default attenuation is constant = 1.0, linear = 0.0, quadratic = 0.0. + * + * @see evas_3d_light_attenuation_enable_set() + * + * @ingroup Evas_3D_Light + */ +EAPI void evas_3d_light_attenuation_set(Evas_3D_Light *light, Evas_Real constant, Evas_Real linear, Evas_Real quadratic) EINA_ARG_NONNULL(1); + +/** + * Get the attenuation of the given light. + * + * @param light The given light. + * @param constant Pointer to receive constant attenuation term. + * @param linear Pointer to receive linear attenuation term. + * @param quadratic Pointer to receive quadratic attenuation term. + * + * @see evas_3d_light_attenuation_set() + * + * @ingroup Evas_3D_Light + */ +EAPI void evas_3d_light_attenuation_get(const Evas_3D_Light *light, Evas_Real *constant, Evas_Real *linear, Evas_Real *quadratic) EINA_ARG_NONNULL(1); + +/** + * Set the attenuation enable flag of the given light. + * + * @param light The given light. + * @param enable Whether to enable attenuation (@c EINA_TRUE), or not (@c EINA_FALSE). + * + * By default, light attenuation is not enabled. + * + * @see evas_3d_light_attenuation_set() + * + * @ingroup Evas_3D_Light + */ +EAPI void evas_3d_light_attenuation_enable_set(Evas_3D_Light *light, Eina_Bool enable) EINA_ARG_NONNULL(1); + +/** + * Get the attenuation enable flag of the given light. + * + * @param light The given light. + * @return @c EINA_TRUE if enabled, or @c EINA_FALSE if not. + * + * @see evas_3d_light_attenuation_enable_set() + * + * @ingroup Evas_3D_Light + */ +EAPI Eina_Bool evas_3d_light_attenuation_enable_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Create a new mesh on the given Evas @p canvas. + * + * @param e The given canvas. + * @return The created mesh handle. + * + * @ingroup Evas_3D_Mesh + */ +EAPI Evas_3D_Mesh *evas_3d_mesh_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Delete a mesh from its belonging Evas canvas. + * + * @param mesh The given mesh. + * + * @see evas_3d_mesh_add() + * + * @ingroup Evas_3D_Mesh + */ +EAPI void evas_3d_mesh_del(Evas_3D_Mesh *mesh) EINA_ARG_NONNULL(1); + +/** + * Get the Evas canvas where the given node belongs to. + * + * @param mesh The given mesh. + * @return The Evas canvas. + * + * @see evas_3d_mesh_add() + * + * @ingroup Evas_3D_Mesh + */ +EAPI Evas *evas_3d_mesh_evas_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the shade mode of the given mesh. + * + * @param mesh The given mesh. + * @param mode The shade mode. + * + * Default shade mode is EVAS_3D_SHADE_MODE_VERTEX_COLOR. + * + * @ingroup Evas_3D_Mesh + */ +EAPI void evas_3d_mesh_shade_mode_set(Evas_3D_Mesh *mesh, Evas_3D_Shade_Mode mode) EINA_ARG_NONNULL(1); + +/** + * Get the shade mode of the given mesh. + * + * @param mesh The given mesh. + * @return The shade mode. + * + * @see eavs_3d_mesh_shade_mode_set() + * + * @ingroup Evas_3D_Mesh + */ +EAPI Evas_3D_Shade_Mode evas_3d_mesh_shade_mode_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Load mesh data from file. + * + * @param mesh The given mesh. + * @param type The type of the mesh file. + * @param file Path to the mesh file. + * @param key Key in the mesh file. + * + * Loading a mesh from existing file is supported. Currently, only MD2 file + * format is supported. + * + * @ingroup Evas_3D_Mesh + */ +EAPI void evas_3d_mesh_file_set(Evas_3D_Mesh *mesh, Evas_3D_Mesh_File_Type type, const char *file, const char *key) EINA_ARG_NONNULL(1); + +/** + * Set the vertex count of the given mesh. + * + * @param mesh The given mesh. + * @param count Vertex count. + * + * Each key frame should have same vertex count to be properly interpolated. + * Key frames have their own vertex data and the data should have more vertices + * than the mesh's vertex count. + * + * Default vertex count is 0. + * + * @ingroup Evas_3D_Mesh + */ +EAPI void evas_3d_mesh_vertex_count_set(Evas_3D_Mesh *mesh, unsigned int count) EINA_ARG_NONNULL(1); + +/** + * Get the vertex count of the given mesh. + * + * @param mesh The given mesh. + * @return Vertex count. + * + * @see evas_3d_mesh_vertex_count_set() + * + * @ingroup Evas_3D_Mesh + */ +EAPI int evas_3d_mesh_vertex_count_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Add a key frame to the given mesh. + * + * @param mesh The given mesh. + * @param frame The number of the key frame to be added. + * + * If specified frame is already exist, error message will be generated. + * + * @ingroup Evas_3D_Mesh + */ +EAPI void evas_3d_mesh_frame_add(Evas_3D_Mesh *mesh, int frame) EINA_ARG_NONNULL(1); + +/** + * Delete a key frame from the given mesh. + * + * @param mesh The given mesh. + * @param frame The number of the key frame to be deleted. + * + * @see evas_3d_mesh_frame_add() + * + * @ingroup Evas_3D_Mesh + */ +EAPI void evas_3d_mesh_frame_del(Evas_3D_Mesh *mesh, int frame) EINA_ARG_NONNULL(1); + +/** + * Set the material of the key frame of the given mesh. + * + * @param mesh The given mesh. + * @param frame The number of the key frame. + * @param material The material to be set to the key frame. + * + * Setting different materials for each key frame is useful for doing animations + * like GIF images or color changing animationas. + * + * @ingroup Evas_3D_Mesh + */ +EAPI void evas_3d_mesh_frame_material_set(Evas_3D_Mesh *mesh, int frame, Evas_3D_Material *material) EINA_ARG_NONNULL(1); + +/** + * Get the material of the key frame of the given mesh. + * + * @param mesh The given mesh. + * @param frame The number of the key frame. + * @return The material of the key frame. + * + * @see evas_3d_mesh_frame_material_set() + * + * @ingroup Evas_3D_Mesh + */ +EAPI Evas_3D_Material *evas_3d_mesh_frame_material_get(const Evas_3D_Mesh *mesh, int frame) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the vertex data of the key frame of the given mesh. + * + * @param mesh The given mesh. + * @param frame The number of the key frame. + * @param attrib Vertex attribute ID. + * @param stride Stride to go to the next vertex (in bytes). + * @param data Pointer to the vertex data buffer. + * + * This function make evas read from the given buffer whenever it requires. + * If you want to release the buffer after calling this functions, use + * evas_3d_mesh_frame_vertex_data_copy_set() instead. + * + * After setting the vertex data, further modifications should be protected + * by map/unmap pair. + * + * @see evas_3d_mesh_frame_vertex_data_copy_set() + * @see evas_3d_mesh_frame_vertex_data_map() + * @see evas_3d_mesh_frame_vertex_data_unmap() + * + * @ingroup Evas_3D_Mesh + */ +EAPI void evas_3d_mesh_frame_vertex_data_set(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib, int stride, const void *data) EINA_ARG_NONNULL(1); + +/** + * Set the vertex data of the key frame of the given mesh by copying from a buffer. + * + * @param mesh The given mesh. + * @param frame The number of the key frame. + * @param attrib Vertex attribute ID. + * @param stride Stride to go to the next vertex (in bytes). + * @param data Pointer to the vertex data buffer. + * + * This function allocates internal vertex buffer and copy from the given + * buffer. So you can release the buffer. If you want to modify the vertex data + * use evas_3d_mesh_frame_vertex_data_map(). After finishing the modifications, + * you should call evas_3d_mesh_frame_vertex_data_unmap(). + * + * @see evas_3d_mesh_frame_vertex_data_set() + * @see evas_3d_mesh_frame_vertex_data_map() + * @see evas_3d_mesh_frame_vertex_data_unmap() + * + * @ingroup Evas_3D_Mesh + */ +EAPI void evas_3d_mesh_frame_vertex_data_copy_set(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib, int stride, const void *data) EINA_ARG_NONNULL(1); + +/** + * Map the vertex buffer of the key frame of the given mesh. + * + * @param mesh The given mesh. + * @param frame The number of the key frame. + * @param attrib Vertex attribute ID. + * @return Starting address of the mapped vertex buffer. + * + * After manipulating the mapped buffer, evas_3d_mesh_frame_vertex_data_unmap() + * should be called to properly download the data to the engine. If the data + * was set using evas_3d_mesh_frame_vertex_data_set(), pointer to the original + * buffer will be returned. Otherwise, the returned pointer can differ every + * time calling this function. + * + * @see evas_3d_mesh_frame_vertex_data_unmap() + * + * @ingroup Evas_3D_Mesh + */ +EAPI void *evas_3d_mesh_frame_vertex_data_map(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Unmap the vertex buffer of the key frame of the given mesh. + * + * @param mesh The given mesh. + * @param frame The number of the key frame. + * @param attrib Vertex attribute ID. + * + * @see evas_3d_mesh_frame_vertex_data_map() + * + * @ingroup Evas_3D_Mesh + */ +EAPI void evas_3d_mesh_frame_vertex_data_unmap(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib) EINA_ARG_NONNULL(1); + +/** + * Get the vertex buffer stride of the key frame of the given mesh. + * + * @param mesh The given mesh. + * @param frame The number of the key frame. + * @param attrib Vertex attribute ID. + * @return Stride to go to the next vertex (in bytes). + * + * This function returns valid stride only when the vertex buffer is mapped. + * If the data was set with evas_3d_mesh_frame_vertex_data_set(), the original + * stride will be returned unchanged. + * + * @see evas_3d_mesh_frame_vertex_data_map() + * + * @ingroup Evas_3D_Mesh + */ +EAPI int evas_3d_mesh_frame_vertex_stride_get(const Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the vertex index data of the given mesh. + * + * @param mesh The given mesh. + * @param format Vertex index data format. + * @param count Vertex index count. + * @param indices Pointer to the index data. + * + * When the index data is set, Evas 3D assembles vertices using the index data. + * If you want to free the data buffer, use evas_3d_mesh_index_data_copy_set(). + * Further modifications should be made within map/unmap pair. + * + * @see evas_3d_mesh_index_data_copy_set() + * @see evas_3d_mesh_index_data_map() + * @see evas_3d_mesh_index_data_unmap() + * + * @ingroup Evas_3D_Mesh + */ +EAPI void evas_3d_mesh_index_data_set(Evas_3D_Mesh *mesh, Evas_3D_Index_Format format, int count, const void *indices) EINA_ARG_NONNULL(1); + +/** + * Set the vertex index data of the given mesh by copying from a buffer. + * + * @param mesh The given mesh. + * @param format Vertex index data format. + * @param count Vertex index count. + * @param indices Pointer to the vertex data. + * + * This function allocates internal index buffer any copy data from the given + * buffer. Futher modifications can be made within map/unmap pair. + * + * @see evas_3d_mesh_index_data_set() + * + * @ingroup Evas_3D_Mesh + */ +EAPI void evas_3d_mesh_index_data_copy_set(Evas_3D_Mesh *mesh, Evas_3D_Index_Format format, int count, const void *indices) EINA_ARG_NONNULL(1); + +/** + * Get the format of the index data of the given mesh. + * + * @param mesh The given mesh. + * @return Format of the index data. + * + * Returns valid format only when the index buffer is mapped. First map the + * index buffer and then query the properties of the mapped buffer. If the index + * data was set by evas_3d_mesh_index_data_set(), the original format will be + * returned. Otherwise the format can differ every time you call the + * evas_3d_mesh_index_data_map() function. + * + * @see evas_3d_mesh_index_data_map() + * + * @ingroup Evas_3D_Mesh + */ +EAPI Evas_3D_Index_Format evas_3d_mesh_index_format_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Get the count of the index data of the given mesh. + * + * @param mesh The given mesh. + * @return Index data count. + * + * This function returns the index count of the last called data_set function. + * + * @see evas_3d_mesh_index_data_set() + * @see evas_3d_mesh_index_data_copy_set() + * + * @ingroup Evas_3D_Mesh + */ +EAPI int evas_3d_mesh_index_count_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Map the index buffer of the given mesh. + * + * @param mesh The given mesh. + * @return Pointer to the mapped buffer. + * + * evas_3d_mesh_index_data_unmap() should be called after modifications. If the + * data was set using evas_3d_mesh_index_data_set(), the original pointer will + * be returned, otherwise, the returned pointer may differ every time you call + * this function. + * + * @see evas_3d_mesh_index_data_unmap() + * + * @ingroup Evas_3D_Mesh + */ +EAPI void *evas_3d_mesh_index_data_map(Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Unmap the index buffer of the given mesh. + * + * @param mesh The given mesh. + * + * @see evas_3d_mesh_index_data_map() + * + * @ingroup Evas_3D_Mesh + */ +EAPI void evas_3d_mesh_index_data_unmap(Evas_3D_Mesh *mesh) EINA_ARG_NONNULL(1); + +/** + * Set the vertex assembly of the given mesh. + * + * @param mesh The given mesh. + * @param assembly Vertex assembly. + * + * Vertex assembly defines how the engine organizes vertices into geometric + * primitives. + * + * Default vertex assembly is EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES. + * + * @ingroup Evas_3D_Mesh + */ +EAPI void evas_3d_mesh_vertex_assembly_set(Evas_3D_Mesh *mesh, Evas_3D_Vertex_Assembly assembly); + +/** + * Get the vertex assembly of the given mesh. + * + * @param mesh The given mesh. + * @return The vertex assembly. + * + * @see evas_3d_mesh_vertex_assembly_set() + * + * @ingroup Evas_3D_Mesh + */ +EAPI Evas_3D_Vertex_Assembly evas_3d_mesh_vertex_assembly_get(const Evas_3D_Mesh *mesh); + +/** + * Create a new texture on the given Evas @p canvas. + * + * @param e The given canvas. + * @return The created texture handle. + * + * @ingroup Evas_3D_Texture + */ +EAPI Evas_3D_Texture *evas_3d_texture_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Delete a texture from its belonging Evas canvas. + * + * @param texture The given texture. + * + * @see evas_3d_texture_add() + * + * @ingroup Evas_3D_Texture + */ +EAPI void evas_3d_texture_del(Evas_3D_Texture *texture) EINA_ARG_NONNULL(1); + +/** + * Get the Evas canvas where the given texture belongs to. + * + * @param texture The given texture. + * @return The Evas canvas. + * + * @see evas_3d_texture_add() + * + * @ingroup Evas_3D_Texture + */ +EAPI Evas *evas_3d_texture_evas_get(const Evas_3D_Texture *texture) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the data of the given texture. + * + * @param texure The given texture + * @param color_format Color format of the texture. + * @param pixel_format Pixel format of the data. + * @param w Width of the data. + * @param h Height of the data. + * @param data Pointer to the data. + * + * @see evas_3d_texture_file_set() + * + * @ingroup Evas_3D_Texture + */ +EAPI void evas_3d_texture_data_set(Evas_3D_Texture *texture, Evas_3D_Color_Format color_format, Evas_3D_Pixel_Format pixel_format, int w, int h, const void *data); + +/** + * Set the data of the given texture from file. + * + * @param texture The given texture. + * @param file Path to the image file. + * @param key Key in the image file. + * + * Only PNG format is supported. + * + * @ingroup Evas_3D_Texture + */ +EAPI void evas_3d_texture_file_set(Evas_3D_Texture *texture, const char *file, const char *key) EINA_ARG_NONNULL(1); + +/** + * Set the data of the given texture from an evas object. + * + * @param texture The given texture. + * @param source Source evas object to be used as the texture data. + * + * Evas 3D support using existing evas object as a texture source. This feature + * make it possible using any exisiting evas object inside 3D scene. + * + * @see evas_3d_texture_source_visible_set + * + * @ingroup Evas_3D_Texture + */ +EAPI void evas_3d_texture_source_set(Evas_3D_Texture *texture, Evas_Object *source) EINA_ARG_NONNULL(1); + +/** + * Set the visibility flag of the source evas object of the given texture. + * + * @param texture The given texture. + * @param visible @c EINA_TRUE for visible, @c EINA_FALSE for invisible. + * + * Recommend to call evas_object_show() on the source object and controll the + * visibility using this function. + * + * By default, source object is visible. + * + * @see evas_3d_texture_source_set() + * + * @ingroup Evas_3D_Texture + */ +EAPI void evas_3d_texture_source_visible_set(Evas_3D_Texture *texture, Eina_Bool visible) EINA_ARG_NONNULL(1); + +/** + * Get the visibility flag of the source evas object of the given texture. + * + * @param texture The given texture. + * @return @c EINA_TRUE if visible, @c EINA_FALSE if invisible. + * + * @see evas_3d_texture_source_visible_set() + * + * @ingroup Evas_3D_Texture + */ +EAPI Eina_Bool evas_3d_texture_source_visible_get(const Evas_3D_Texture *texture) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Get the color format of the given texture. + * + * @param texture The given texture. + * + * EVAS_3D_COLOR_FORMAT_RGBA will be returned if the texture has source object. + * Otherwise, the color format of the data will be returned. + * + * @see evas_3d_texture_data_set() + * @see evas_3d_texture_file_set() + * @see evas_3d_texture_source_set() + * + * @ingroup Evas_3D_Texture + */ +EAPI Evas_3D_Color_Format evas_3d_texture_color_format_get(const Evas_3D_Texture *texture) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Get the size of the given texture. + * + * @param texture The given texture. + * @param w Pointer to receive the width of the texture size. + * @param h Pointer to receive the height of the texture size. + * + * If the texture has source object, the size of the source object will be + * returned. Otherwise, the size of the data (or image file) will be returned. + * + * @see evas_3d_texture_data_set() + * @see evas_3d_texture_file_set() + * @see evas_3d_texture_source_set() + * + * @ingroup Evas_3D_Texture + */ +EAPI void evas_3d_texture_size_get(const Evas_3D_Texture *texture, int *w, int *h) EINA_ARG_NONNULL(1); + +/** + * Set the wrap mode of the given texture. + * + * @param texture The given texture. + * @param s Wrap mode for S-axis. + * @param t Wrap mode for T-axis. + * + * If the texture coordinate exceed range [0.0, 1.0] the values are modified + * according to the wrap mode. + * + * Default wrap modes are both EVAS_3D_WRAP_MODE_CLAMP for s and t. + * + * @ingroup Evas_3D_Texture + */ +EAPI void evas_3d_texture_wrap_set(Evas_3D_Texture *texture, Evas_3D_Wrap_Mode s, Evas_3D_Wrap_Mode t) EINA_ARG_NONNULL(1); + +/** + * Get the wrap mode of the given texture. + * + * @param texture The given texture. + * @param s Pointer to receive S-axis wrap mode. + * @param t Pointer to receive T-axis wrap mode. + * + * @see evas_3d_texture_wrap_set() + * + * @ingroup Evas_3D_Texture + */ +EAPI void evas_3d_texture_wrap_get(const Evas_3D_Texture *texture, Evas_3D_Wrap_Mode *s, Evas_3D_Wrap_Mode *t) EINA_ARG_NONNULL(1); + +/** + * Set the filter of the given texture. + * + * @param texture The given texture. + * @param min Minification filter used when down-scaling. + * @param mag Magnification filter used when up-scaling. + * + * Default filters are both EVAS_3D_TEXTURE_FILTER_NEAREST for s and t. + * + * @ingroup Evas_3D_Texture + */ +EAPI void evas_3d_texture_filter_set(Evas_3D_Texture *texture, Evas_3D_Texture_Filter min, Evas_3D_Texture_Filter mag) EINA_ARG_NONNULL(1); + +/** + * Get the filter of the given texture. + * + * @param texture The given texture. + * @param min Pointer to receive the minification filter. + * @param mag Pointer to receive the magnification filter. + * + * @see evas_3d_texture_filter_set() + * + * @ingroup Evas_3D_Texture + */ +EAPI void evas_3d_texture_filter_get(const Evas_3D_Texture *texture, Evas_3D_Texture_Filter *min, Evas_3D_Texture_Filter *mag) EINA_ARG_NONNULL(1); + +/** + * Create a new material on the given Evas @p canvas. + * + * @param e The given canvas. + * @param type The type of the material. + * @return The created material handle. + * + * @ingroup Evas_3D_Material + */ +EAPI Evas_3D_Material *evas_3d_material_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Delete a material from its belonging Evas canvas. + * + * @param material The given material. + * + * @see evas_3d_material_add() + * + * @ingroup Evas_3D_Material + */ +EAPI void evas_3d_material_del(Evas_3D_Material *material) EINA_ARG_NONNULL(1); + +/** + * Get the Evas canvas where the given material belongs to. + * + * @param material The given material. + * @return The Evas canvas. + * + * @see evas_3d_material_add() + * + * @ingroup Evas_3D_Material + */ +EAPI Evas *evas_3d_material_evas_get(const Evas_3D_Material *material) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the material attribute enable flag of the given material. + * + * @param material The given material. + * @param attrib Material attribute ID. + * @param enable Whether to enable the attribute (@c EINA_TRUE), or not (@c EINA_FALSE). + * + * You might want to disable some material reflection contribution. For + * example,Emission attribute is rarely used. Disabling unused attributes + * might help the shading less complex so that can get speed up. + * + * By default, diffuse and specular is enabled. + * + * @ingroup Evas_3D_Material + */ +EAPI void evas_3d_material_enable_set(Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, Eina_Bool enable) EINA_ARG_NONNULL(1); + +/** + * Get the material attribute enable flag of the given material. + * + * @param material The given material. + * @param attrib Material attribute ID. + * @return @c EINA_TRUE if enabled, or @c EINA_FALSE if not. + * + * @see evas_3d_material_enable_set() + * + * @ingroup Evas_3D_Material + */ +EAPI Eina_Bool evas_3d_material_enable_get(const Evas_3D_Material *material, Evas_3D_Material_Attrib attrib) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the material attribute color of the given material. + * + * @param material The given material. + * @param attrib Material attribute ID. + * @param r Red component of the color. + * @param g Green component of the color. + * @param b Blue component of the color. + * @param a Alpha component of the color. + * + * Material color is used also when texture map is enabled. The colors will be + * modulated (multiplied). To controll the color contribution of a material + * attribute, use gray color. Setting color value for normal attribute has no + * effect. + * + * Default color is as follows. + * + * Ambient : (0.2, 0.2, 0.2, 1.0) + * Diffuse : (0.8, 0.8, 0.8, 1.0) + * Specular : (1.0, 1.0, 1.0, 1.0) + * Emission : (0.0, 0.0, 0.0, 1.0) + * Normal : Not used + * + * @ingroup Evas_3D_Material + */ +EAPI void evas_3d_material_color_set(Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) EINA_ARG_NONNULL(1); + +/** + * Get the material attribute color of the given material. + * + * @param material The given material. + * @param attrib Material attribute ID. + * @param r Pointer to receive red component of the color. + * @param g Pointer to receive green component of the color. + * @param b Pointer to receive blue component of the color. + * @param a Pointer to receive alpha component of the color. + * + * @see evas_3d_material_color_set() + * + * @ingroup Evas_3D_Material + */ +EAPI void evas_3d_material_color_get(const Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) EINA_ARG_NONNULL(1); + +/** + * Set the shininess of the given material. + * + * @param material The given material. + * @param shininess Shininess value. + * + * Shininess is only used when specular attribute is enabled. Higher shininess + * value will make the object more shiny. + * + * Default shininess value is 150.0. + * + * @see evas_3d_material_enable_set() + * + * @ingroup Evas_3D_Material + */ +EAPI void evas_3d_material_shininess_set(Evas_3D_Material *material, Evas_Real shininess) EINA_ARG_NONNULL(1); + +/** + * Get the shininess of the given material. + * + * @param material The given material. + * @return The shininess value. + * + * @see evas_3d_material_shininess_set() + * + * @ingroup Evas_3D_Material + */ +EAPI Evas_Real evas_3d_material_shininess_get(const Evas_3D_Material *material) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * Set the texture of the given material. + * + * @param material The given material. + * @param attrib Material attribute ID. + * @param texture Texture to be set. + * + * You have to enable the desired attribute first. + * + * @see evas_3d_material_enable_set() + * + * @ingroup Evas_3D_Material + */ +EAPI void evas_3d_material_texture_set(Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, Evas_3D_Texture *texture) EINA_ARG_NONNULL(1); + +/** + * Get the texture of the given material. + * + * @param material The given material. + * @param attrib Material attribute ID. + * @return The texture that is set to the given material attribute. + * + * @see evas_3d_material_texture_set() + * + * @ingroup Evas_3D_Material + */ +EAPI Evas_3D_Texture *evas_3d_material_texture_get(const Evas_3D_Material *material, Evas_3D_Material_Attrib attrib) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +#endif /* _EVAS_3D_H */ diff --git a/src/lib/evas/Evas_Eo.h b/src/lib/evas/Evas_Eo.h index b0b408acb2..1c7ddb75cb 100644 --- a/src/lib/evas/Evas_Eo.h +++ b/src/lib/evas/Evas_Eo.h @@ -5741,6 +5741,10 @@ enum EVAS_OBJ_IMAGE_SUB_ID_SOURCE_EVENTS_GET, EVAS_OBJ_IMAGE_SUB_ID_SOURCE_CLIP_SET, EVAS_OBJ_IMAGE_SUB_ID_SOURCE_CLIP_GET, +#ifdef EVAS_3D + EVAS_OBJ_IMAGE_SUB_ID_3D_SCENE_SET, + EVAS_OBJ_IMAGE_SUB_ID_3D_SCENE_GET, +#endif EVAS_OBJ_IMAGE_SUB_ID_LAST }; @@ -5897,6 +5901,33 @@ enum */ #define evas_obj_image_source_clip_get(source_clip) EVAS_OBJ_IMAGE_ID(EVAS_OBJ_IMAGE_SUB_ID_SOURCE_CLIP_GET), EO_TYPECHECK(Eina_Bool *, source_clip) +#ifdef EVAS_3D +/** + * @def evas_obj_image_3d_scene_set + * @since 1.8 + * + * Set the 3D scene on an image object. + * + * @param[in] scene in + * + * @see evas_object_image_3d_scene_set + */ +#define evas_obj_image_3d_scene_set(scene) EVAS_OBJ_IMAGE_ID(EVAS_OBJ_IMAGE_SUB_ID_3D_SCENE_SET), EO_TYPECHECK(Evas_3D_Scene *, scene) + +/** + * @def evas_obj_image_3d_scene_get + * @since 1.8 + * + * Get the 3D scene on an image object. + * + * @param[in] scene in + * @param[out] result out + * + * @see evas_object_image_3d_scene_get + */ +#define evas_obj_image_3d_scene_get(scene) EVAS_OBJ_IMAGE_ID(EVAS_OBJ_IMAGE_SUB_ID_3D_SCENE_GET), EO_TYPECHECK(Evas_3D_Scene **, scene) +#endif + /** * @def evas_obj_image_border_set * @since 1.8 @@ -6796,4 +6827,4 @@ EO_TYPECHECK(Eina_Bool *, ret) EVAS_OUT_ID(EVAS_OUT_SUB_ID_ENGINE_INFO_GET), \ EO_TYPECHECK(Evas_Engine_Info **, ret) -#endif
\ No newline at end of file +#endif diff --git a/src/lib/evas/canvas/evas_3d_camera.c b/src/lib/evas/canvas/evas_3d_camera.c new file mode 100644 index 0000000000..fba4e7a4e7 --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_camera.c @@ -0,0 +1,159 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include "evas_common_private.h" +#include "evas_private.h" + +static void +_camera_free(Evas_3D_Object *obj) +{ + Evas_3D_Camera *camera = (Evas_3D_Camera *)obj; + + if (camera->nodes) + eina_hash_free(camera->nodes); + + free(camera); +} + +static Eina_Bool +_camera_node_change_notify(const Eina_Hash *hash EINA_UNUSED, const void *key, + void *data EINA_UNUSED, void *fdata) +{ + Evas_3D_Node *n = *(Evas_3D_Node **)key; + evas_3d_object_change(&n->base, EVAS_3D_STATE_NODE_CAMERA, (Evas_3D_Object *)fdata); + return EINA_TRUE; +} + +static void +_camera_change(Evas_3D_Object *obj, Evas_3D_State state EINA_UNUSED, + Evas_3D_Object *ref EINA_UNUSED) +{ + Evas_3D_Camera *camera = (Evas_3D_Camera *)obj; + + if (camera->nodes) + eina_hash_foreach(camera->nodes, _camera_node_change_notify, obj); +} + +static const Evas_3D_Object_Func camera_func = +{ + _camera_free, + _camera_change, + NULL, +}; + +void +evas_3d_camera_node_add(Evas_3D_Camera *camera, Evas_3D_Node *node) +{ + int count = 0; + + if (camera->nodes == NULL) + { + camera->nodes = eina_hash_pointer_new(NULL); + + if (camera->nodes == NULL) + { + ERR("Failed to create hash table."); + return; + } + } + else + count = (int)eina_hash_find(camera->nodes, &node); + + eina_hash_set(camera->nodes, &node, (const void *)(count + 1)); +} + +void +evas_3d_camera_node_del(Evas_3D_Camera *camera, Evas_3D_Node *node) +{ + int count = 0; + + if (camera->nodes == NULL) + { + ERR("No node to delete."); + return; + } + + count = (int)eina_hash_find(camera->nodes, &node); + + if (count == 1) + eina_hash_del(camera->nodes, &node, NULL); + else + eina_hash_set(camera->nodes, &node, (const void *)(count - 1)); +} + +Evas_3D_Camera * +evas_3d_camera_new(Evas *e) +{ + Evas_3D_Camera *camera = NULL; + + camera = (Evas_3D_Camera *)calloc(1, sizeof(Evas_3D_Camera)); + + if (camera == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + evas_3d_object_init(&camera->base, e, EVAS_3D_OBJECT_TYPE_CAMERA, &camera_func); + return camera; +} + +EAPI Evas_3D_Camera * +evas_3d_camera_add(Evas *e) +{ + return evas_3d_camera_new(e); +} + +EAPI void +evas_3d_camera_del(Evas_3D_Camera *camera) +{ + evas_3d_object_unreference(&camera->base); +} + +EAPI Evas * +evas_3d_camera_evas_get(const Evas_3D_Camera *camera) +{ + return camera->base.evas; +} + +EAPI void +evas_3d_camera_projection_matrix_set(Evas_3D_Camera *camera, const Evas_Real *matrix) +{ + evas_mat4_array_set(&camera->projection, matrix); + evas_3d_object_change(&camera->base, EVAS_3D_STATE_CAMERA_PROJECTION, NULL); +} + +EAPI void +evas_3d_camera_projection_matrix_get(const Evas_3D_Camera *camera, Evas_Real *matrix) +{ + memcpy(matrix, &camera->projection.m[0], sizeof(Evas_Real) * 16); +} + +EAPI void +evas_3d_camera_projection_perspective_set(Evas_3D_Camera *camera, Evas_Real fovy, Evas_Real aspect, Evas_Real near, Evas_Real far) +{ + Evas_Real xmax; + Evas_Real ymax; + + ymax = near * (Evas_Real)tan((double)fovy * M_PI / 360.0); + xmax = ymax * aspect; + + evas_mat4_frustum_set(&camera->projection, -xmax, xmax, -ymax, ymax, near, far); + evas_3d_object_change(&camera->base, EVAS_3D_STATE_CAMERA_PROJECTION, NULL); +} + +EAPI void +evas_3d_camera_projection_frustum_set(Evas_3D_Camera *camera, Evas_Real left, Evas_Real right, Evas_Real bottom, Evas_Real top, Evas_Real near, Evas_Real far) +{ + evas_mat4_frustum_set(&camera->projection, left, right, bottom, top, near, far); + evas_3d_object_change(&camera->base, EVAS_3D_STATE_CAMERA_PROJECTION, NULL); +} + +EAPI void +evas_3d_camera_projection_ortho_set(Evas_3D_Camera *camera, Evas_Real left, Evas_Real right, Evas_Real bottom, Evas_Real top, Evas_Real near, Evas_Real far) +{ + evas_mat4_ortho_set(&camera->projection, left, right, bottom, top, near, far); + evas_3d_object_change(&camera->base, EVAS_3D_STATE_CAMERA_PROJECTION, NULL); +} diff --git a/src/lib/evas/canvas/evas_3d_light.c b/src/lib/evas/canvas/evas_3d_light.c new file mode 100644 index 0000000000..356e8dd49b --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_light.c @@ -0,0 +1,273 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <math.h> +#include "evas_common_private.h" +#include "evas_private.h" + +static void +_light_free(Evas_3D_Object *obj) +{ + Evas_3D_Light *light = (Evas_3D_Light *)obj; + + if (light->nodes) + eina_hash_free(light->nodes); + + free(light); +} + +static Eina_Bool +_light_node_change_notify(const Eina_Hash *hash EINA_UNUSED, const void *key, + void *data EINA_UNUSED, void *fdata) +{ + Evas_3D_Node *n = *(Evas_3D_Node **)key; + evas_3d_object_change(&n->base, EVAS_3D_STATE_NODE_LIGHT, (Evas_3D_Object *)fdata); + return EINA_TRUE; +} + +static void +_light_change(Evas_3D_Object *obj, Evas_3D_State state EINA_UNUSED, + Evas_3D_Object *ref EINA_UNUSED) +{ + Evas_3D_Light *light = (Evas_3D_Light *)obj; + + if (light->nodes) + eina_hash_foreach(light->nodes, _light_node_change_notify, obj); +} + +static const Evas_3D_Object_Func light_func = +{ + _light_free, + _light_change, + NULL, +}; + +void +evas_3d_light_node_add(Evas_3D_Light *light, Evas_3D_Node *node) +{ + int count = 0; + + if (light->nodes == NULL) + { + light->nodes = eina_hash_pointer_new(NULL); + + if (light->nodes == NULL) + { + ERR("Failed to create hash table."); + return; + } + } + else + count = (int)eina_hash_find(light->nodes, &node); + + eina_hash_set(light->nodes, &node, (const void *)(count + 1)); +} + +void +evas_3d_light_node_del(Evas_3D_Light *light, Evas_3D_Node *node) +{ + int count = 0; + + if (light->nodes == NULL) + { + ERR("No node to delete."); + return; + } + + count = (int)eina_hash_find(light->nodes, &node); + + if (count == 1) + eina_hash_del(light->nodes, &node, NULL); + else + eina_hash_set(light->nodes, &node, (const void *)(count - 1)); +} + +Evas_3D_Light * +evas_3d_light_new(Evas *e) +{ + Evas_3D_Light *light = NULL; + + light = (Evas_3D_Light *)calloc(1, sizeof(Evas_3D_Light)); + + if (light == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + evas_3d_object_init(&light->base, e, EVAS_3D_OBJECT_TYPE_LIGHT, &light_func); + + evas_color_set(&light->ambient, 0.0, 0.0, 0.0, 1.0); + evas_color_set(&light->diffuse, 1.0, 1.0, 1.0, 1.0); + evas_color_set(&light->specular, 1.0, 1.0, 1.0, 1.0); + + light->spot_exp = 0.0; + light->spot_cutoff = 180.0; + light->spot_cutoff_cos = -1.0; + + light->atten_const = 1.0; + light->atten_linear = 0.0; + light->atten_quad = 0.0; + + return light; +} + +EAPI Evas_3D_Light * +evas_3d_light_add(Evas *e) +{ + return evas_3d_light_new(e); +} + +EAPI void +evas_3d_light_del(Evas_3D_Light *light) +{ + evas_3d_object_unreference(&light->base); +} + +EAPI Evas * +evas_3d_light_evas_get(const Evas_3D_Light *light) +{ + return light->base.evas; +} + +EAPI void +evas_3d_light_directional_set(Evas_3D_Light *light, Eina_Bool directional) +{ + if (light->directional != directional) + { + light->directional = directional; + evas_3d_object_change(&light->base, EVAS_3D_STATE_ANY, NULL); + } +} + +EAPI Eina_Bool +evas_3d_light_directional_get(const Evas_3D_Light *light) +{ + return light->directional; +} + +EAPI void +evas_3d_light_ambient_set(Evas_3D_Light *light, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) +{ + light->ambient.r = r; + light->ambient.g = g; + light->ambient.b = b; + light->ambient.a = a; + + evas_3d_object_change(&light->base, EVAS_3D_STATE_LIGHT_AMBIENT, NULL); +} + +EAPI void +evas_3d_light_ambient_get(const Evas_3D_Light *light, + Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) +{ + if (r) *r = light->ambient.r; + if (g) *g = light->ambient.g; + if (b) *b = light->ambient.b; + if (a) *a = light->ambient.a; +} + +EAPI void +evas_3d_light_diffuse_set(Evas_3D_Light *light, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) +{ + light->diffuse.r = r; + light->diffuse.g = g; + light->diffuse.b = b; + light->diffuse.a = a; + + evas_3d_object_change(&light->base, EVAS_3D_STATE_LIGHT_DIFFUSE, NULL); +} + +EAPI void +evas_3d_light_diffuse_get(const Evas_3D_Light *light, + Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) +{ + if (r) *r = light->diffuse.r; + if (g) *g = light->diffuse.g; + if (b) *b = light->diffuse.b; + if (a) *a = light->diffuse.a; +} + +EAPI void +evas_3d_light_specular_set(Evas_3D_Light *light, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) +{ + light->specular.r = r; + light->specular.g = g; + light->specular.b = b; + light->specular.a = a; + + evas_3d_object_change(&light->base, EVAS_3D_STATE_LIGHT_SPECULAR, NULL); +} + +EAPI void +evas_3d_light_specular_get(const Evas_3D_Light *light, + Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) +{ + if (r) *r = light->specular.r; + if (g) *g = light->specular.g; + if (b) *b = light->specular.b; + if (a) *a = light->specular.a; +} + +EAPI void +evas_3d_light_spot_exponent_set(Evas_3D_Light *light, Evas_Real exponent) +{ + light->spot_exp = exponent; + evas_3d_object_change(&light->base, EVAS_3D_STATE_LIGHT_SPOT_EXP, NULL); +} + +EAPI Evas_Real +evas_3d_light_spot_exponent_get(const Evas_3D_Light *light) +{ + return light->spot_exp; +} + +EAPI void +evas_3d_light_spot_cutoff_set(Evas_3D_Light *light, Evas_Real cutoff) +{ + light->spot_cutoff = cutoff; + light->spot_cutoff_cos = cos(cutoff * M_PI / 180.0); + evas_3d_object_change(&light->base, EVAS_3D_STATE_LIGHT_SPOT_CUTOFF, NULL); +} + +EAPI Evas_Real +evas_3d_light_spot_cutoff_get(const Evas_3D_Light *light) +{ + return light->spot_cutoff; +} + +EAPI void +evas_3d_light_attenuation_set(Evas_3D_Light *light, + Evas_Real constant, Evas_Real linear, Evas_Real quadratic) +{ + light->atten_const = constant; + light->atten_linear = linear; + light->atten_quad = quadratic; + evas_3d_object_change(&light->base, EVAS_3D_STATE_LIGHT_ATTENUATION, NULL); +} + +EAPI void +evas_3d_light_attenuation_get(const Evas_3D_Light *light, Evas_Real *constant, Evas_Real *linear, Evas_Real *quadratic) +{ + if (constant) *constant = light->atten_const; + if (linear) *linear = light->atten_linear; + if (quadratic) *quadratic = light->atten_quad; +} + +EAPI void +evas_3d_light_attenuation_enable_set(Evas_3D_Light *light, Eina_Bool enable) +{ + if (light->enable_attenuation != enable) + { + light->enable_attenuation = enable; + evas_3d_object_change(&light->base, EVAS_3D_STATE_LIGHT_ATTENUATION, NULL); + } +} + +EAPI Eina_Bool +evas_3d_light_attenuation_enable_get(const Evas_3D_Light *light) +{ + return light->enable_attenuation; +} diff --git a/src/lib/evas/canvas/evas_3d_material.c b/src/lib/evas/canvas/evas_3d_material.c new file mode 100644 index 0000000000..e9a131de91 --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_material.c @@ -0,0 +1,221 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include "evas_common_private.h" +#include "evas_private.h" + +static void +_material_free(Evas_3D_Object *obj) +{ + int i; + Evas_3D_Material *material = (Evas_3D_Material *)obj; + + if (material->meshes) + eina_hash_free(material->meshes); + + for (i = 0; i < EVAS_3D_MATERIAL_ATTRIB_COUNT; i++) + { + if (material->attribs[i].texture) + { + evas_3d_texture_material_del(material->attribs[i].texture, material); + evas_3d_object_unreference(&material->attribs[i].texture->base); + } + } + + free(material); +} + +static Eina_Bool +_material_mesh_change_notify(const Eina_Hash *hash EINA_UNUSED, const void *key, + void *data EINA_UNUSED, void *fdata) +{ + Evas_3D_Mesh *m = *(Evas_3D_Mesh **)key; + evas_3d_object_change(&m->base, EVAS_3D_STATE_MESH_MATERIAL, (Evas_3D_Object *)fdata); + return EINA_TRUE; +} + +static void +_material_change(Evas_3D_Object *obj, Evas_3D_State state EINA_UNUSED, + Evas_3D_Object *ref EINA_UNUSED) +{ + Evas_3D_Material *material = (Evas_3D_Material *)obj; + + if (material->meshes) + eina_hash_foreach(material->meshes, _material_mesh_change_notify, obj); +} + +static void +_material_update(Evas_3D_Object *obj) +{ + int i; + Evas_3D_Material *material = (Evas_3D_Material *)obj; + + for (i = 0; i < EVAS_3D_MATERIAL_ATTRIB_COUNT; i++) + { + if (material->attribs[i].enable) + { + if (material->attribs[i].texture) + evas_3d_object_update(&material->attribs[i].texture->base); + } + } +} + +static const Evas_3D_Object_Func material_func = +{ + _material_free, + _material_change, + _material_update, +}; + +void +evas_3d_material_mesh_add(Evas_3D_Material *material, Evas_3D_Mesh *mesh) +{ + int count = 0; + + if (material->meshes == NULL) + { + material->meshes = eina_hash_pointer_new(NULL); + + if (material->meshes == NULL) + { + ERR("Failed to create hash table."); + return; + } + } + else + count = (int)eina_hash_find(material->meshes, &mesh); + + eina_hash_set(material->meshes, &mesh, (const void *)(count + 1)); +} + +void +evas_3d_material_mesh_del(Evas_3D_Material *material, Evas_3D_Mesh *mesh) +{ + int count = 0; + + if (material->meshes == NULL) + { + ERR("No mesh to delete."); + return; + } + + count = (int)eina_hash_find(material->meshes, &mesh); + + if (count == 1) + eina_hash_del(material->meshes, &mesh, NULL); + else + eina_hash_set(material->meshes, &mesh, (const void *)(count - 1)); +} + +Evas_3D_Material * +evas_3d_material_new(Evas *e) +{ + Evas_3D_Material *material = NULL; + + material = (Evas_3D_Material *)calloc(1, sizeof(Evas_3D_Material)); + + if (material == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + evas_3d_object_init(&material->base, e, EVAS_3D_OBJECT_TYPE_MATERIAL, &material_func); + + evas_color_set(&material->attribs[EVAS_3D_MATERIAL_AMBIENT].color, 0.2, 0.2, 0.2, 1.0); + evas_color_set(&material->attribs[EVAS_3D_MATERIAL_DIFFUSE].color, 0.8, 0.8, 0.8, 1.0); + evas_color_set(&material->attribs[EVAS_3D_MATERIAL_SPECULAR].color, 1.0, 1.0, 1.0, 1.0); + evas_color_set(&material->attribs[EVAS_3D_MATERIAL_EMISSION].color, 0.0, 0.0, 0.0, 1.0); + material->shininess = 150.0; + + return material; +} + +EAPI Evas_3D_Material * +evas_3d_material_add(Evas *e) +{ + return evas_3d_material_new(e); +} + +EAPI void +evas_3d_material_del(Evas_3D_Material *material) +{ + evas_3d_object_unreference(&material->base); +} + +EAPI Evas * +evas_3d_material_evas_get(const Evas_3D_Material *material) +{ + return material->base.evas; +} + +EAPI void +evas_3d_material_enable_set(Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, + Eina_Bool enable) +{ + material->attribs[attrib].enable = enable; +} + +EAPI Eina_Bool +evas_3d_material_enable_get(const Evas_3D_Material *material, Evas_3D_Material_Attrib attrib) +{ + return material->attribs[attrib].enable; +} + +EAPI void +evas_3d_material_color_set(Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, + Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) +{ + evas_color_set(&material->attribs[attrib].color, r, g, b, a); + evas_3d_object_change(&material->base, EVAS_3D_STATE_MATERIAL_COLOR, NULL); +} + +EAPI void +evas_3d_material_color_get(const Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, + Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) +{ + if (r) *r = material->attribs[attrib].color.r; + if (g) *g = material->attribs[attrib].color.g; + if (b) *b = material->attribs[attrib].color.b; + if (a) *a = material->attribs[attrib].color.a; +} + +EAPI void +evas_3d_material_shininess_set(Evas_3D_Material *material, Evas_Real shininess) +{ + material->shininess = shininess; +} + +EAPI Evas_Real +evas_3d_material_shininess_get(const Evas_3D_Material *material) +{ + return material->shininess; +} + +EAPI void +evas_3d_material_texture_set(Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, + Evas_3D_Texture *texture) +{ + if (material->attribs[attrib].texture != texture) + { + if (material->attribs[attrib].texture) + { + evas_3d_texture_material_del(material->attribs[attrib].texture, material); + evas_3d_object_unreference(&material->attribs[attrib].texture->base); + } + + material->attribs[attrib].texture = texture; + evas_3d_texture_material_add(texture, material); + evas_3d_object_reference(&texture->base); + } + + evas_3d_object_change(&material->base, EVAS_3D_STATE_MATERIAL_TEXTURE, NULL); +} + +EAPI Evas_3D_Texture * +evas_3d_material_texture_get(const Evas_3D_Material *material, Evas_3D_Material_Attrib attrib) +{ + return material->attribs[attrib].texture; +} diff --git a/src/lib/evas/canvas/evas_3d_mesh.c b/src/lib/evas/canvas/evas_3d_mesh.c new file mode 100644 index 0000000000..37d767180f --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_mesh.c @@ -0,0 +1,828 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include "evas_common_private.h" +#include "evas_private.h" + +static Evas_3D_Mesh_Frame * +evas_3d_mesh_frame_new(Evas_3D_Mesh *mesh) +{ + Evas_3D_Mesh_Frame *frame = NULL; + + frame = (Evas_3D_Mesh_Frame *)calloc(1, sizeof(Evas_3D_Mesh_Frame)); + + if (frame == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + frame->mesh = mesh; + evas_box3_empty_set(&frame->aabb); + + return frame; +} + +static void +evas_3d_mesh_frame_free(Evas_3D_Mesh_Frame *frame) +{ + int i; + + if (frame->material) + { + evas_3d_material_mesh_del(frame->material, frame->mesh); + evas_3d_object_unreference(&frame->material->base); + } + + for (i = 0; i < EVAS_3D_VERTEX_ATTRIB_COUNT; i++) + { + if (frame->vertices[i].owns_data) + free(frame->vertices[i].data); + } + + free(frame); +} + +static Evas_3D_Mesh_Frame * +evas_3d_mesh_frame_find(Evas_3D_Mesh *mesh, int frame) +{ + Eina_List *l; + Evas_3D_Mesh_Frame *f; + + EINA_LIST_FOREACH(mesh->frames, l, f) + { + if (f->frame == frame) + return f; + } + + return NULL; +} + +static inline void +_mesh_init(Evas_3D_Mesh *mesh) +{ + mesh->vertex_count = 0; + mesh->frame_count = 0; + mesh->frames = NULL; + + mesh->index_format = EVAS_3D_INDEX_FORMAT_NONE; + mesh->index_count = 0; + mesh->indices = NULL; + mesh->owns_indices = EINA_FALSE; + mesh->assembly = EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES; + + mesh->nodes = NULL; +} + +static inline void +_mesh_fini(Evas_3D_Mesh *mesh) +{ + Eina_List *l; + Evas_3D_Mesh_Frame *f; + + if (mesh->frames) + { + EINA_LIST_FOREACH(mesh->frames, l, f) + { + evas_3d_mesh_frame_free(f); + } + + eina_list_free(mesh->frames); + } + + if (mesh->indices && mesh->owns_indices) + free(mesh->indices); + + if (mesh->nodes) + eina_hash_free(mesh->nodes); +} + +static void +_mesh_free(Evas_3D_Object *obj) +{ + Evas_3D_Mesh *mesh = (Evas_3D_Mesh *)obj; + _mesh_fini(mesh); + free(mesh); +} + +static Eina_Bool +_mesh_node_geometry_change_notify(const Eina_Hash *hash EINA_UNUSED, const void *key, + void *data EINA_UNUSED, void *fdata) +{ + Evas_3D_Node *n = *(Evas_3D_Node **)key; + evas_3d_object_change(&n->base, EVAS_3D_STATE_NODE_MESH_GEOMETRY, (Evas_3D_Object *)fdata); + return EINA_TRUE; +} + +static Eina_Bool +_mesh_node_material_change_notify(const Eina_Hash *hash EINA_UNUSED, const void *key, + void *data EINA_UNUSED, void *fdata) +{ + Evas_3D_Node *n = *(Evas_3D_Node **)key; + evas_3d_object_change(&n->base, EVAS_3D_STATE_NODE_MESH_MATERIAL, (Evas_3D_Object *)fdata); + return EINA_TRUE; +} + +static void +_mesh_change(Evas_3D_Object *obj, Evas_3D_State state, Evas_3D_Object *ref EINA_UNUSED) +{ + Evas_3D_Mesh *mesh = (Evas_3D_Mesh *)obj; + + if (state == EVAS_3D_STATE_MESH_MATERIAL) + { + if (mesh->nodes) + eina_hash_foreach(mesh->nodes, _mesh_node_material_change_notify, obj); + } + else + { + if (mesh->nodes) + eina_hash_foreach(mesh->nodes, _mesh_node_geometry_change_notify, obj); + } +} + +static void +_mesh_update(Evas_3D_Object *obj) +{ + Eina_List *l; + Evas_3D_Mesh_Frame *f; + Evas_3D_Mesh *mesh = (Evas_3D_Mesh *)obj; + + EINA_LIST_FOREACH(mesh->frames, l, f) + { + if (f->material) + evas_3d_object_update(&f->material->base); + } +} + +static const Evas_3D_Object_Func mesh_func = +{ + _mesh_free, + _mesh_change, + _mesh_update, +}; + +void +evas_3d_mesh_node_add(Evas_3D_Mesh *mesh, Evas_3D_Node *node) +{ + int count = 0; + + if (mesh->nodes == NULL) + { + mesh->nodes = eina_hash_pointer_new(NULL); + + if (mesh->nodes == NULL) + { + ERR("Failed to create hash table."); + return; + } + } + else + count = (int)eina_hash_find(mesh->nodes, &node); + + eina_hash_set(mesh->nodes, &node, (const void *)(count + 1)); +} + +void +evas_3d_mesh_node_del(Evas_3D_Mesh *mesh, Evas_3D_Node *node) +{ + int count = 0; + + if (mesh->nodes == NULL) + { + ERR("No node to delete."); + return; + } + + count = (int)eina_hash_find(mesh->nodes, &node); + + if (count == 1) + eina_hash_del(mesh->nodes, &node, NULL); + else + eina_hash_set(mesh->nodes, &node, (const void *)(count - 1)); +} + +Evas_3D_Mesh * +evas_3d_mesh_new(Evas *e) +{ + Evas_3D_Mesh *mesh = NULL; + + mesh = (Evas_3D_Mesh *)malloc(sizeof(Evas_3D_Mesh)); + + if (mesh == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + evas_3d_object_init(&mesh->base, e, EVAS_3D_OBJECT_TYPE_MESH, &mesh_func); + _mesh_init(mesh); + return mesh; +} + +EAPI Evas_3D_Mesh * +evas_3d_mesh_add(Evas *e) +{ + return evas_3d_mesh_new(e); +} + +EAPI void +evas_3d_mesh_del(Evas_3D_Mesh *mesh) +{ + evas_3d_object_unreference(&mesh->base); +} + +EAPI Evas * +evas_3d_mesh_evas_get(const Evas_3D_Mesh *mesh) +{ + return mesh->base.evas; +} + +EAPI void +evas_3d_mesh_shade_mode_set(Evas_3D_Mesh *mesh, Evas_3D_Shade_Mode mode) +{ + if (mesh->shade_mode != mode) + { + mesh->shade_mode = mode; + evas_3d_object_change(&mesh->base, EVAS_3D_STATE_MESH_SHADE_MODE, NULL); + } +} + +EAPI Evas_3D_Shade_Mode +evas_3d_mesh_shade_mode_get(const Evas_3D_Mesh *mesh) +{ + return mesh->shade_mode; +} + +EAPI void +evas_3d_mesh_vertex_count_set(Evas_3D_Mesh *mesh, unsigned int count) +{ + mesh->vertex_count = count; + evas_3d_object_change(&mesh->base, EVAS_3D_STATE_MESH_VERTEX_COUNT, NULL); +} + +EAPI int +evas_3d_mesh_vertex_count_get(const Evas_3D_Mesh *mesh) +{ + return mesh->vertex_count; +} + +EAPI void +evas_3d_mesh_frame_add(Evas_3D_Mesh *mesh, int frame) +{ + Evas_3D_Mesh_Frame *f = evas_3d_mesh_frame_find(mesh, frame); + + if (f != NULL) + { + ERR("Already existing frame."); + return; + } + + f = evas_3d_mesh_frame_new(mesh); + + if (f == NULL) + return; + + f->frame = frame; + mesh->frames = eina_list_append(mesh->frames, f); + evas_3d_object_change(&mesh->base, EVAS_3D_STATE_MESH_FRAME, NULL); +} + +EAPI void +evas_3d_mesh_frame_del(Evas_3D_Mesh *mesh, int frame) +{ + Evas_3D_Mesh_Frame *f = evas_3d_mesh_frame_find(mesh, frame); + + if (f == NULL) + { + ERR("Not existing mesh frame."); + return; + } + + mesh->frames = eina_list_remove(mesh->frames, f); + evas_3d_mesh_frame_free(f); + evas_3d_object_change(&mesh->base, EVAS_3D_STATE_MESH_FRAME, NULL); +} + +EAPI void +evas_3d_mesh_frame_material_set(Evas_3D_Mesh *mesh, int frame, Evas_3D_Material *material) +{ + Evas_3D_Mesh_Frame *f = evas_3d_mesh_frame_find(mesh, frame); + + if (f == NULL) + { + ERR("Not existing mesh frame."); + return; + } + + if (f->material == material) + return; + + if (f->material) + { + evas_3d_material_mesh_del(f->material, mesh); + evas_3d_object_unreference(&f->material->base); + } + + f->material = material; + evas_3d_object_reference(&material->base); + evas_3d_object_change(&mesh->base, EVAS_3D_STATE_MESH_MATERIAL, NULL); + evas_3d_material_mesh_add(material, mesh); +} + +EAPI Evas_3D_Material * +evas_3d_mesh_frame_material_get(const Evas_3D_Mesh *mesh, int frame) +{ + Evas_3D_Mesh_Frame *f = evas_3d_mesh_frame_find((Evas_3D_Mesh *)mesh, frame); + + if (f == NULL) + { + ERR("Not existing mesh frame."); + return NULL; + } + + return f->material; +} + +EAPI void +evas_3d_mesh_frame_vertex_data_set(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib, + int stride, const void *data) +{ + Evas_3D_Mesh_Frame *f = evas_3d_mesh_frame_find(mesh, frame); + int element_count; + + if (f == NULL) + { + ERR("Not existing mesh frame."); + return; + } + + if (attrib == EVAS_3D_VERTEX_POSITION) + { + element_count = 3; + } + else if (attrib == EVAS_3D_VERTEX_NORMAL) + { + element_count = 3; + } + else if (attrib == EVAS_3D_VERTEX_TANGENT) + { + element_count = 3; + } + else if (attrib == EVAS_3D_VERTEX_COLOR) + { + element_count = 4; + } + else if (attrib == EVAS_3D_VERTEX_TEXCOORD) + { + element_count = 2; + } + else + { + ERR("Invalid vertex attrib."); + return; + } + + if (f->vertices[attrib].owns_data && f->vertices[attrib].data) + free(f->vertices[attrib].data); + + f->vertices[attrib].size = 0; + f->vertices[attrib].stride = stride; + f->vertices[attrib].data = (void *)data; + f->vertices[attrib].owns_data = EINA_FALSE; + f->vertices[attrib].element_count = element_count; + + evas_3d_object_change(&mesh->base, EVAS_3D_STATE_MESH_VERTEX_DATA, NULL); +} + +EAPI void +evas_3d_mesh_frame_vertex_data_copy_set(Evas_3D_Mesh *mesh, int frame, + Evas_3D_Vertex_Attrib attrib, + int stride, const void *data) +{ + Evas_3D_Mesh_Frame *f = evas_3d_mesh_frame_find(mesh, frame); + Evas_3D_Vertex_Buffer *vb; + int size, element_count; + + if (f == NULL) + { + ERR("Not existing mesh frame."); + return; + } + + if (attrib == EVAS_3D_VERTEX_POSITION) + { + element_count = 3; + } + else if (attrib == EVAS_3D_VERTEX_NORMAL) + { + element_count = 3; + } + else if (attrib == EVAS_3D_VERTEX_TANGENT) + { + element_count = 3; + } + else if (attrib == EVAS_3D_VERTEX_COLOR) + { + element_count = 4; + } + else if (attrib == EVAS_3D_VERTEX_TEXCOORD) + { + element_count = 2; + } + else + { + ERR("Invalid vertex attrib."); + return; + } + + vb = &f->vertices[attrib]; + size = element_count * sizeof(float) * mesh->vertex_count; + + if (!vb->owns_data || vb->size < size) + { + if (vb->owns_data && vb->data) + free(vb->data); + + vb->data = malloc(size); + + if (vb->data == NULL) + { + vb->element_count = 0; + vb->size = 0; + vb->stride = 0; + vb->owns_data = EINA_FALSE; + + ERR("Failed to allocate memory."); + return; + } + + vb->size = size; + vb->owns_data = EINA_TRUE; + } + + vb->element_count = element_count; + vb->stride = 0; + + if (data == NULL) + return; + + if (stride == 0 || stride == (int)(element_count * sizeof(float))) + { + memcpy(vb->data, data, size); + } + else + { + int i; + float *dst = (float *)vb->data; + float *src = (float *)data; + + if (element_count == 1) + { + for (i = 0; i <mesh->vertex_count; i++) + { + *dst++ = src[0]; + + src = (float *)((char *)src + stride); + } + } + else if (element_count == 2) + { + for (i = 0; i <mesh->vertex_count; i++) + { + *dst++ = src[0]; + *dst++ = src[1]; + + src = (float *)((char *)src + stride); + } + } + else if (element_count == 3) + { + for (i = 0; i <mesh->vertex_count; i++) + { + *dst++ = src[0]; + *dst++ = src[1]; + *dst++ = src[2]; + + src = (float *)((char *)src + stride); + } + } + else if (element_count == 4) + { + for (i = 0; i <mesh->vertex_count; i++) + { + *dst++ = src[0]; + *dst++ = src[1]; + *dst++ = src[2]; + *dst++ = src[3]; + + src = (float *)((char *)src + stride); + } + } + } + + evas_3d_object_change(&mesh->base, EVAS_3D_STATE_MESH_VERTEX_DATA, NULL); +} + +EAPI void * +evas_3d_mesh_frame_vertex_data_map(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib) +{ + Evas_3D_Mesh_Frame *f = evas_3d_mesh_frame_find(mesh, frame); + + if (f == NULL) + { + ERR("Not existing mesh frame."); + return NULL; + } + + if (f->vertices[attrib].mapped) + { + ERR("Try to map alreadly mapped data."); + return NULL; + } + + f->vertices[attrib].mapped = EINA_TRUE; + return f->vertices[attrib].data; +} + +EAPI void +evas_3d_mesh_frame_vertex_data_unmap(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib) +{ + Evas_3D_Mesh_Frame *f = evas_3d_mesh_frame_find(mesh, frame); + + if (f == NULL) + { + ERR("Not existing mesh frame."); + return; + } + + if (!f->vertices[attrib].mapped) + { + ERR("Try to unmap data which is not mapped yet."); + return; + } + + f->vertices[attrib].mapped = EINA_FALSE; +} + +EAPI int +evas_3d_mesh_frame_vertex_stride_get(const Evas_3D_Mesh *mesh, int frame, + Evas_3D_Vertex_Attrib attrib) +{ + Evas_3D_Mesh_Frame *f = evas_3d_mesh_frame_find((Evas_3D_Mesh *)mesh, frame); + + if (f == NULL) + { + ERR("Not existing mesh frame."); + return 0; + } + + return f->vertices[attrib].stride; +} + +EAPI void +evas_3d_mesh_index_data_set(Evas_3D_Mesh *mesh, Evas_3D_Index_Format format, int count, + const void *indices) +{ + if (mesh->owns_indices && mesh->indices) + free(mesh->indices); + + mesh->index_format = format; + mesh->index_count = count; + mesh->index_size = 0; + mesh->indices = (void *)indices; + mesh->owns_indices = EINA_FALSE; + + evas_3d_object_change(&mesh->base, EVAS_3D_STATE_MESH_INDEX_DATA, NULL); +} + +EAPI void +evas_3d_mesh_index_data_copy_set(Evas_3D_Mesh *mesh, Evas_3D_Index_Format format, int count, const void *indices) +{ + int size; + + if (format == EVAS_3D_INDEX_FORMAT_UNSIGNED_BYTE) + { + size = count * sizeof(unsigned char); + } + else if (format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + { + size = count * sizeof(unsigned short); + } + else + { + ERR("Invalid index format."); + return; + } + + if (!mesh->owns_indices || mesh->index_size < size) + { + if (mesh->owns_indices && mesh->indices) + free(mesh->indices); + + mesh->indices = malloc(size); + + if (mesh->indices == NULL) + { + ERR("Failed to allocate memory."); + return; + } + + mesh->index_size = size; + mesh->owns_indices = EINA_TRUE; + } + + mesh->index_format = format; + mesh->index_count = count; + + if (indices) + memcpy(mesh->indices, indices, size); +} + +EAPI Evas_3D_Index_Format +evas_3d_mesh_index_format_get(const Evas_3D_Mesh *mesh) +{ + return mesh->index_format; +} + +EAPI int +evas_3d_mesh_index_count_get(const Evas_3D_Mesh *mesh) +{ + return mesh->index_count; +} + +EAPI void * +evas_3d_mesh_index_data_map(Evas_3D_Mesh *mesh) +{ + if (mesh->index_mapped) + { + ERR("Try to map alreadly mapped data."); + return NULL; + } + + mesh->index_mapped = EINA_TRUE; + return mesh->indices; +} + +EAPI void +evas_3d_mesh_index_data_unmap(Evas_3D_Mesh *mesh) +{ + if (!mesh->index_mapped) + { + ERR("Try to unmap data which is not mapped yet."); + return; + } + + mesh->index_mapped = EINA_FALSE; +} + +EAPI void +evas_3d_mesh_vertex_assembly_set(Evas_3D_Mesh *mesh, Evas_3D_Vertex_Assembly assembly) +{ + mesh->assembly = assembly; + evas_3d_object_change(&mesh->base, EVAS_3D_STATE_MESH_VERTEX_ASSEMBLY, NULL); +} + +EAPI Evas_3D_Vertex_Assembly +evas_3d_mesh_vertex_assembly_get(const Evas_3D_Mesh *mesh) +{ + return mesh->assembly; +} + +EAPI void +evas_3d_mesh_file_set(Evas_3D_Mesh *mesh, Evas_3D_Mesh_File_Type type, + const char *file, const char *key EINA_UNUSED) +{ + _mesh_fini(mesh); + _mesh_init(mesh); + + if (file == NULL) + return; + + switch (type) + { + case EVAS_3D_MESH_FILE_TYPE_MD2: + evas_3d_mesh_file_md2_set(mesh, file); + break; + default: + ERR("Invalid mesh file type."); + break; + } +} + +static inline void +_mesh_frame_find(Evas_3D_Mesh *mesh, int frame, + Eina_List **l, Eina_List **r) +{ + Eina_List *left, *right; + Evas_3D_Mesh_Frame *f0, *f1; + + left = mesh->frames; + right = eina_list_next(left); + + while (right) + { + f0 = (Evas_3D_Mesh_Frame *)eina_list_data_get(left); + f1 = (Evas_3D_Mesh_Frame *)eina_list_data_get(right); + + if (frame >= f0->frame && frame <= f1->frame) + break; + + left = right; + right = eina_list_next(left); + } + + if (right == NULL) + { + if (frame <= f0->frame) + { + *l = NULL; + *r = left; + } + else + { + *l = left; + *r = NULL; + } + } + + *l = left; + *r = right; +} + +void +evas_3d_mesh_interpolate_vertex_buffer_get(Evas_3D_Mesh *mesh, int frame, + Evas_3D_Vertex_Attrib attrib, + Evas_3D_Vertex_Buffer *buf0, + Evas_3D_Vertex_Buffer *buf1, + Evas_Real *weight) +{ + Eina_List *l, *r; + const Evas_3D_Mesh_Frame *f0 = NULL, *f1 = NULL; + + _mesh_frame_find(mesh, frame, &l, &r); + + while (l) + { + f0 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(l); + + if (f0->vertices[attrib].data != NULL) + break; + + l = eina_list_prev(l); + f0 = NULL; + } + + while (r) + { + f1 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(r); + + if (f1->vertices[attrib].data != NULL) + break; + + r = eina_list_next(r); + f1 = NULL; + } + + if (f0 == NULL && f1 == NULL) + return; + + if (f0 == NULL) + { + f0 = f1; + } + else if (f1 != NULL) + { + if (frame == f0->frame) + { + f1 = NULL; + } + else if (frame == f1->frame) + { + f0 = f1; + f1 = NULL; + } + } + + buf0->data = f0->vertices[attrib].data; + buf0->stride = f0->vertices[attrib].stride; + buf0->size = f0->vertices[attrib].size; + + if (f1) + { + buf1->data = f1->vertices[attrib].data; + buf1->stride = f1->vertices[attrib].stride; + buf1->size = f1->vertices[attrib].size; + + *weight = (f1->frame - frame) / (Evas_Real)(f1->frame - f0->frame); + } + else + { + buf1->data = NULL; + buf1->stride = 0; + buf1->size = 0; + + *weight = 1.0; + } +} diff --git a/src/lib/evas/canvas/evas_3d_mesh_loader_md2.c b/src/lib/evas/canvas/evas_3d_mesh_loader_md2.c new file mode 100644 index 0000000000..7472e27267 --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_mesh_loader_md2.c @@ -0,0 +1,440 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include "evas_common_private.h" +#include "evas_private.h" + +#define PACKED __attribute__((__packed__)) + +#define MD2_MAGIC_NUMBER 844121161 +#define MD2_VERSION 8 +#define MD2_FRAME_SCALE 256 + +/* Structures for reading data from file. */ +typedef struct _MD2_Header MD2_Header; +typedef struct _MD2_Vertex MD2_Vertex; +typedef struct _MD2_Frame MD2_Frame; +typedef struct _MD2_Triangle MD2_Triangle; +typedef struct _MD2_Texcoord MD2_Texcoord; + +struct PACKED _MD2_Header +{ + int magic; + int version; + + int skin_width; + int skin_height; + + int frame_size; + + int skins_count; + int vertex_count; + int texcoord_count; + int triangle_count; + int glcmd_count; + int frame_count; + + int offset_skins; + int offset_texcoords; + int offset_triangles; + int offset_frames; + int offset_glcmds; + int offset_end; +}; + +struct PACKED _MD2_Vertex +{ + unsigned char pos[3]; + unsigned char normal_idx; +}; + +struct PACKED _MD2_Frame +{ + float scale[3]; + float trans[3]; + char name[16]; + MD2_Vertex vertices[1]; +}; + +struct PACKED _MD2_Triangle +{ + unsigned short vertex_idx[3]; + unsigned short texcoord_idx[3]; +}; + +struct PACKED _MD2_Texcoord +{ + short s; + short t; +}; + +typedef struct _MD2_Loader +{ + Eina_File *file; + char *map; + int size; + + int skin_width; + int skin_height; + + int frame_count; + int frame_size; + char *frames; + + int vertex_count; + int triangle_count; + int texcoord_count; + + MD2_Triangle *triangles; + MD2_Texcoord *texcoords; +} MD2_Loader; + +static const float normal_table[162][3] = +{ + {-0.525731f, 0.000000f, 0.850651f}, + {-0.442863f, 0.238856f, 0.864188f}, + {-0.295242f, 0.000000f, 0.955423f}, + {-0.309017f, 0.500000f, 0.809017f}, + {-0.162460f, 0.262866f, 0.951056f}, + { 0.000000f, 0.000000f, 1.000000f}, + { 0.000000f, 0.850651f, 0.525731f}, + {-0.147621f, 0.716567f, 0.681718f}, + { 0.147621f, 0.716567f, 0.681718f}, + { 0.000000f, 0.525731f, 0.850651f}, + { 0.309017f, 0.500000f, 0.809017f}, + { 0.525731f, 0.000000f, 0.850651f}, + { 0.295242f, 0.000000f, 0.955423f}, + { 0.442863f, 0.238856f, 0.864188f}, + { 0.162460f, 0.262866f, 0.951056f}, + {-0.681718f, 0.147621f, 0.716567f}, + {-0.809017f, 0.309017f, 0.500000f}, + {-0.587785f, 0.425325f, 0.688191f}, + {-0.850651f, 0.525731f, 0.000000f}, + {-0.864188f, 0.442863f, 0.238856f}, + {-0.716567f, 0.681718f, 0.147621f}, + {-0.688191f, 0.587785f, 0.425325f}, + {-0.500000f, 0.809017f, 0.309017f}, + {-0.238856f, 0.864188f, 0.442863f}, + {-0.425325f, 0.688191f, 0.587785f}, + {-0.716567f, 0.681718f, -0.147621f}, + {-0.500000f, 0.809017f, -0.309017f}, + {-0.525731f, 0.850651f, 0.000000f}, + { 0.000000f, 0.850651f, -0.525731f}, + {-0.238856f, 0.864188f, -0.442863f}, + { 0.000000f, 0.955423f, -0.295242f}, + {-0.262866f, 0.951056f, -0.162460f}, + { 0.000000f, 1.000000f, 0.000000f}, + { 0.000000f, 0.955423f, 0.295242f}, + {-0.262866f, 0.951056f, 0.162460f}, + { 0.238856f, 0.864188f, 0.442863f}, + { 0.262866f, 0.951056f, 0.162460f}, + { 0.500000f, 0.809017f, 0.309017f}, + { 0.238856f, 0.864188f, -0.442863f}, + { 0.262866f, 0.951056f, -0.162460f}, + { 0.500000f, 0.809017f, -0.309017f}, + { 0.850651f, 0.525731f, 0.000000f}, + { 0.716567f, 0.681718f, 0.147621f}, + { 0.716567f, 0.681718f, -0.147621f}, + { 0.525731f, 0.850651f, 0.000000f}, + { 0.425325f, 0.688191f, 0.587785f}, + { 0.864188f, 0.442863f, 0.238856f}, + { 0.688191f, 0.587785f, 0.425325f}, + { 0.809017f, 0.309017f, 0.500000f}, + { 0.681718f, 0.147621f, 0.716567f}, + { 0.587785f, 0.425325f, 0.688191f}, + { 0.955423f, 0.295242f, 0.000000f}, + { 1.000000f, 0.000000f, 0.000000f}, + { 0.951056f, 0.162460f, 0.262866f}, + { 0.850651f, -0.525731f, 0.000000f}, + { 0.955423f, -0.295242f, 0.000000f}, + { 0.864188f, -0.442863f, 0.238856f}, + { 0.951056f, -0.162460f, 0.262866f}, + { 0.809017f, -0.309017f, 0.500000f}, + { 0.681718f, -0.147621f, 0.716567f}, + { 0.850651f, 0.000000f, 0.525731f}, + { 0.864188f, 0.442863f, -0.238856f}, + { 0.809017f, 0.309017f, -0.500000f}, + { 0.951056f, 0.162460f, -0.262866f}, + { 0.525731f, 0.000000f, -0.850651f}, + { 0.681718f, 0.147621f, -0.716567f}, + { 0.681718f, -0.147621f, -0.716567f}, + { 0.850651f, 0.000000f, -0.525731f}, + { 0.809017f, -0.309017f, -0.500000f}, + { 0.864188f, -0.442863f, -0.238856f}, + { 0.951056f, -0.162460f, -0.262866f}, + { 0.147621f, 0.716567f, -0.681718f}, + { 0.309017f, 0.500000f, -0.809017f}, + { 0.425325f, 0.688191f, -0.587785f}, + { 0.442863f, 0.238856f, -0.864188f}, + { 0.587785f, 0.425325f, -0.688191f}, + { 0.688191f, 0.587785f, -0.425325f}, + {-0.147621f, 0.716567f, -0.681718f}, + {-0.309017f, 0.500000f, -0.809017f}, + { 0.000000f, 0.525731f, -0.850651f}, + {-0.525731f, 0.000000f, -0.850651f}, + {-0.442863f, 0.238856f, -0.864188f}, + {-0.295242f, 0.000000f, -0.955423f}, + {-0.162460f, 0.262866f, -0.951056f}, + { 0.000000f, 0.000000f, -1.000000f}, + { 0.295242f, 0.000000f, -0.955423f}, + { 0.162460f, 0.262866f, -0.951056f}, + {-0.442863f, -0.238856f, -0.864188f}, + {-0.309017f, -0.500000f, -0.809017f}, + {-0.162460f, -0.262866f, -0.951056f}, + { 0.000000f, -0.850651f, -0.525731f}, + {-0.147621f, -0.716567f, -0.681718f}, + { 0.147621f, -0.716567f, -0.681718f}, + { 0.000000f, -0.525731f, -0.850651f}, + { 0.309017f, -0.500000f, -0.809017f}, + { 0.442863f, -0.238856f, -0.864188f}, + { 0.162460f, -0.262866f, -0.951056f}, + { 0.238856f, -0.864188f, -0.442863f}, + { 0.500000f, -0.809017f, -0.309017f}, + { 0.425325f, -0.688191f, -0.587785f}, + { 0.716567f, -0.681718f, -0.147621f}, + { 0.688191f, -0.587785f, -0.425325f}, + { 0.587785f, -0.425325f, -0.688191f}, + { 0.000000f, -0.955423f, -0.295242f}, + { 0.000000f, -1.000000f, 0.000000f}, + { 0.262866f, -0.951056f, -0.162460f}, + { 0.000000f, -0.850651f, 0.525731f}, + { 0.000000f, -0.955423f, 0.295242f}, + { 0.238856f, -0.864188f, 0.442863f}, + { 0.262866f, -0.951056f, 0.162460f}, + { 0.500000f, -0.809017f, 0.309017f}, + { 0.716567f, -0.681718f, 0.147621f}, + { 0.525731f, -0.850651f, 0.000000f}, + {-0.238856f, -0.864188f, -0.442863f}, + {-0.500000f, -0.809017f, -0.309017f}, + {-0.262866f, -0.951056f, -0.162460f}, + {-0.850651f, -0.525731f, 0.000000f}, + {-0.716567f, -0.681718f, -0.147621f}, + {-0.716567f, -0.681718f, 0.147621f}, + {-0.525731f, -0.850651f, 0.000000f}, + {-0.500000f, -0.809017f, 0.309017f}, + {-0.238856f, -0.864188f, 0.442863f}, + {-0.262866f, -0.951056f, 0.162460f}, + {-0.864188f, -0.442863f, 0.238856f}, + {-0.809017f, -0.309017f, 0.500000f}, + {-0.688191f, -0.587785f, 0.425325f}, + {-0.681718f, -0.147621f, 0.716567f}, + {-0.442863f, -0.238856f, 0.864188f}, + {-0.587785f, -0.425325f, 0.688191f}, + {-0.309017f, -0.500000f, 0.809017f}, + {-0.147621f, -0.716567f, 0.681718f}, + {-0.425325f, -0.688191f, 0.587785f}, + {-0.162460f, -0.262866f, 0.951056f}, + { 0.442863f, -0.238856f, 0.864188f}, + { 0.162460f, -0.262866f, 0.951056f}, + { 0.309017f, -0.500000f, 0.809017f}, + { 0.147621f, -0.716567f, 0.681718f}, + { 0.000000f, -0.525731f, 0.850651f}, + { 0.425325f, -0.688191f, 0.587785f}, + { 0.587785f, -0.425325f, 0.688191f}, + { 0.688191f, -0.587785f, 0.425325f}, + {-0.955423f, 0.295242f, 0.000000f}, + {-0.951056f, 0.162460f, 0.262866f}, + {-1.000000f, 0.000000f, 0.000000f}, + {-0.850651f, 0.000000f, 0.525731f}, + {-0.955423f, -0.295242f, 0.000000f}, + {-0.951056f, -0.162460f, 0.262866f}, + {-0.864188f, 0.442863f, -0.238856f}, + {-0.951056f, 0.162460f, -0.262866f}, + {-0.809017f, 0.309017f, -0.500000f}, + {-0.864188f, -0.442863f, -0.238856f}, + {-0.951056f, -0.162460f, -0.262866f}, + {-0.809017f, -0.309017f, -0.500000f}, + {-0.681718f, 0.147621f, -0.716567f}, + {-0.681718f, -0.147621f, -0.716567f}, + {-0.850651f, 0.000000f, -0.525731f}, + {-0.688191f, 0.587785f, -0.425325f}, + {-0.587785f, 0.425325f, -0.688191f}, + {-0.425325f, 0.688191f, -0.587785f}, + {-0.425325f, -0.688191f, -0.587785f}, + {-0.587785f, -0.425325f, -0.688191f}, + {-0.688191f, -0.587785f, -0.425325f}, +}; + +static inline void +_md2_loader_fini(MD2_Loader *loader) +{ + if (loader->map) + { + eina_file_map_free(loader->file, loader->map); + loader->map = NULL; + } + + if (loader->file) + { + eina_file_close(loader->file); + loader->file = NULL; + } +} + +static inline Eina_Bool +_md2_loader_init(MD2_Loader *loader, const char *file) +{ + MD2_Header header; + + memset(loader, 0x00, sizeof(MD2_Loader)); + + /* Open given file. */ + loader->file = eina_file_open(file, 0); + + if (loader->file == NULL) + { + ERR("Failed to open file %s\n", file); + goto error; + } + + /* Check file size. We require a file larger than MD2 header size. */ + loader->size = eina_file_size_get(loader->file); + + if (loader->size < (int)sizeof(MD2_Header)) + goto error; + + /* Map the file. */ + loader->map = eina_file_map_all(loader->file, EINA_FILE_SEQUENTIAL); + + if (loader->map == NULL) + goto error; + + /* Read header. */ + memcpy(&header, loader->map, sizeof(MD2_Header)); + + /* Check identity */ + if (header.magic != MD2_MAGIC_NUMBER || header.version != MD2_VERSION) + goto error; + + /* Check offsets */ + if (header.offset_skins > header.offset_end) + goto error; + + if (header.offset_texcoords > header.offset_end) + goto error; + + if (header.offset_triangles > header.offset_end) + goto error; + + if (header.offset_frames > header.offset_end) + goto error; + + if (header.offset_glcmds > header.offset_end) + goto error; + + if (header.offset_end > loader->size) + goto error; + + loader->skin_width = header.skin_width; + loader->skin_height = header.skin_height; + + loader->frame_count = header.frame_count; + loader->frame_size = header.frame_size; + loader->frames = loader->map + header.offset_frames; + + loader->vertex_count = header.vertex_count; + loader->triangle_count = header.triangle_count; + loader->texcoord_count = header.texcoord_count; + + loader->triangles = (MD2_Triangle *)(loader->map + header.offset_triangles); + loader->texcoords = (MD2_Texcoord *)(loader->map + header.offset_texcoords); + return EINA_TRUE; + +error: + _md2_loader_fini(loader); + return EINA_FALSE; +} + +void +evas_3d_mesh_file_md2_set(Evas_3D_Mesh *mesh, const char *file) +{ + MD2_Loader loader; + int i, j, k; + float *pos, *nor, *tex; + int stride_pos, stride_nor, stride_tex; + float s_scale, t_scale; + + /* Initialize MD2 loader (Open file and read MD2 head ant etc) */ + if (!_md2_loader_init(&loader, file)) + { + ERR("Failed to initialize MD2 loader."); + return; + } + + s_scale = 1.0 / (float)(loader.skin_width - 1); + t_scale = 1.0 / (float)(loader.skin_height - 1); + + evas_3d_mesh_vertex_count_set(mesh, loader.triangle_count * 3); + evas_3d_mesh_vertex_assembly_set(mesh, EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES); + + /* Load frames */ + for (i = 0; i < loader.frame_count; i++) + { + const MD2_Frame *frame = (const MD2_Frame *)(loader.frames + loader.frame_size * i); + int f = i * MD2_FRAME_SCALE; + + /* Add a mesh frame. */ + evas_3d_mesh_frame_add(mesh, f); + + /* Allocate vertex buffer for the frame. */ + evas_3d_mesh_frame_vertex_data_copy_set(mesh, f, EVAS_3D_VERTEX_POSITION, 0, NULL); + evas_3d_mesh_frame_vertex_data_copy_set(mesh, f, EVAS_3D_VERTEX_NORMAL, 0, NULL); + evas_3d_mesh_frame_vertex_data_copy_set(mesh, f, EVAS_3D_VERTEX_TEXCOORD, 0, NULL); + + /* Map vertex buffer. */ + pos = (float *)evas_3d_mesh_frame_vertex_data_map(mesh, f, EVAS_3D_VERTEX_POSITION); + nor = (float *)evas_3d_mesh_frame_vertex_data_map(mesh, f, EVAS_3D_VERTEX_NORMAL); + tex = (float *)evas_3d_mesh_frame_vertex_data_map(mesh, f, EVAS_3D_VERTEX_TEXCOORD); + + stride_pos = evas_3d_mesh_frame_vertex_stride_get(mesh, f, EVAS_3D_VERTEX_POSITION); + stride_nor = evas_3d_mesh_frame_vertex_stride_get(mesh, f, EVAS_3D_VERTEX_NORMAL); + stride_tex = evas_3d_mesh_frame_vertex_stride_get(mesh, f, EVAS_3D_VERTEX_TEXCOORD); + + if (stride_pos == 0) + stride_pos = sizeof(float) * 3; + + if (stride_nor == 0) + stride_nor = sizeof(float) * 3; + + if (stride_tex == 0) + stride_tex = sizeof(float) * 2; + + for (j = 0; j < loader.triangle_count; j++) + { + const MD2_Triangle *tri = &loader.triangles[j]; + + for (k = 0; k < 3; k++) + { + unsigned int tidx, vidx; + float *p, *n, *t; + + tidx = tri->texcoord_idx[k]; + vidx = tri->vertex_idx[k]; + + p = (float *)((char *)pos + stride_pos * (j * 3 + k)); + n = (float *)((char *)nor + stride_nor * (j * 3 + k)); + t = (float *)((char *)tex + stride_tex * (j * 3 + k)); + + p[0] = frame->vertices[vidx].pos[0] * frame->scale[0] + frame->trans[0]; + p[1] = frame->vertices[vidx].pos[1] * frame->scale[1] + frame->trans[1]; + p[2] = frame->vertices[vidx].pos[2] * frame->scale[2] + frame->trans[2]; + + n[0] = normal_table[frame->vertices[vidx].normal_idx][0]; + n[1] = normal_table[frame->vertices[vidx].normal_idx][1]; + n[2] = normal_table[frame->vertices[vidx].normal_idx][2]; + + t[0] = loader.texcoords[tidx].s * s_scale; + t[1] = 1.0 - loader.texcoords[tidx].t * t_scale; + } + } + + /* Unmap vertex buffer. */ + evas_3d_mesh_frame_vertex_data_unmap(mesh, f, EVAS_3D_VERTEX_POSITION); + evas_3d_mesh_frame_vertex_data_unmap(mesh, f, EVAS_3D_VERTEX_NORMAL); + evas_3d_mesh_frame_vertex_data_unmap(mesh, f, EVAS_3D_VERTEX_TEXCOORD); + } + + _md2_loader_fini(&loader); +} diff --git a/src/lib/evas/canvas/evas_3d_node.c b/src/lib/evas/canvas/evas_3d_node.c new file mode 100644 index 0000000000..90843ae216 --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_node.c @@ -0,0 +1,1197 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include "evas_common_private.h" +#include "evas_private.h" + +static inline Evas_3D_Node_Mesh * +_node_mesh_new(Evas_3D_Node *node, Evas_3D_Mesh *mesh) +{ + Evas_3D_Node_Mesh *nm = (Evas_3D_Node_Mesh *)malloc(sizeof(Evas_3D_Node_Mesh)); + + if (nm == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + nm->node = node; + nm->mesh = mesh; + nm->frame = 0; + + return nm; +} + +static inline void +_node_mesh_free(Evas_3D_Node_Mesh *nm) +{ + free(nm); +} + +static void +_node_mesh_free_func(void *data) +{ + _node_mesh_free((Evas_3D_Node_Mesh *)data); +} + +static Eina_Bool +_node_scene_root_change_notify(const Eina_Hash *hash EINA_UNUSED, const void *key, + void *data EINA_UNUSED, void *fdata) +{ + Evas_3D_Scene *s = *(Evas_3D_Scene **)key; + evas_3d_object_change(&s->base, EVAS_3D_STATE_SCENE_ROOT_NODE, (Evas_3D_Object *)fdata); + return EINA_TRUE; +} + +static Eina_Bool +_node_scene_camera_change_notify(const Eina_Hash *hash EINA_UNUSED, const void *key, + void *data EINA_UNUSED, void *fdata) +{ + Evas_3D_Scene *s = *(Evas_3D_Scene **)key; + evas_3d_object_change(&s->base, EVAS_3D_STATE_SCENE_CAMERA_NODE, (Evas_3D_Object *)fdata); + return EINA_TRUE; +} + +static void +_node_change(Evas_3D_Object *obj, Evas_3D_State state EINA_UNUSED, Evas_3D_Object *ref EINA_UNUSED) +{ + Evas_3D_Node *node = (Evas_3D_Node *)obj; + Eina_List *l; + Evas_3D_Node *n; + + /* Notify all scenes using this node that it has changed. */ + if (node->scenes_root) + eina_hash_foreach(node->scenes_root, _node_scene_root_change_notify, obj); + + if (node->scenes_camera) + eina_hash_foreach(node->scenes_camera, _node_scene_camera_change_notify, obj); + + /* Notify parent that a member has changed. */ + if (node->parent) + evas_3d_object_change(&node->parent->base, EVAS_3D_STATE_NODE_MEMBER, obj); + + /* Notify members that the parent has changed. */ + EINA_LIST_FOREACH(node->members, l, n) + { + evas_3d_object_change(&n->base, EVAS_3D_STATE_NODE_PARENT, obj); + } +} + +static Eina_Bool +_node_transform_update(Evas_3D_Node *node, void *data EINA_UNUSED) +{ + if (evas_3d_object_dirty_get(&node->base, EVAS_3D_STATE_NODE_TRANSFORM) || + evas_3d_object_dirty_get(&node->base, EVAS_3D_STATE_NODE_PARENT)) + { + if (node->parent) + { + const Evas_Vec3 *scale_parent = &node->parent->scale_world; + const Evas_Vec4 *orientation_parent = &node->parent->orientation_world; + + /* Orienatation */ + if (node->orientation_inherit) + { + evas_vec4_quaternion_multiply(&node->orientation_world, + orientation_parent, &node->orientation); + } + else + { + node->orientation_world = node->orientation; + } + + /* Scale */ + if (node->scale_inherit) + evas_vec3_multiply(&node->scale_world, scale_parent, &node->scale); + else + node->scale_world = node->scale; + + /* Position */ + if (node->position_inherit) + { + evas_vec3_multiply(&node->position_world, &node->position, scale_parent); + evas_vec3_quaternion_rotate(&node->position_world, &node->position_world, + orientation_parent); + evas_vec3_add(&node->position_world, &node->position_world, + &node->parent->position_world); + } + else + { + node->position_world = node->position; + } + } + else + { + node->position_world = node->position; + node->orientation_world = node->orientation; + node->scale_world = node->scale; + } + + if (node->type == EVAS_3D_NODE_TYPE_CAMERA) + { + evas_mat4_inverse_build(&node->data.camera.matrix_world_to_eye, + &node->position_world, &node->orientation_world, + &node->scale_world); + } + else if (node->type == EVAS_3D_NODE_TYPE_LIGHT) + { + } + else if (node->type == EVAS_3D_NODE_TYPE_MESH) + { + evas_mat4_build(&node->data.mesh.matrix_local_to_world, + &node->position_world, &node->orientation_world, &node->scale_world); + } +/* + if (node->parent) + { + evas_mat4_nocheck_multiply(&node->matrix_local_to_world, + &node->parent->matrix_local_to_world, + &node->matrix_local_to_parent); + } + else + { + evas_mat4_copy(&node->matrix_local_to_world, &node->matrix_local_to_parent); + }*/ + } + + return EINA_TRUE; +} + +static Eina_Bool +_node_item_update(Evas_3D_Node *node, void *data EINA_UNUSED) +{ + if (node->type == EVAS_3D_NODE_TYPE_CAMERA) + { + if (node->data.camera.camera) + evas_3d_object_update(&node->data.camera.camera->base); + } + else if (node->type == EVAS_3D_NODE_TYPE_LIGHT) + { + if (node->data.light.light) + evas_3d_object_update(&node->data.light.light->base); + } + else if (node->type == EVAS_3D_NODE_TYPE_MESH) + { + Eina_List *l; + Evas_3D_Mesh *m; + + EINA_LIST_FOREACH(node->data.mesh.meshes, l, m) + { + evas_3d_object_update(&m->base); + } + } + + return EINA_TRUE; +} + +static Eina_Bool +_node_aabb_update(Evas_3D_Node *node, void *data EINA_UNUSED) +{ + if (evas_3d_object_dirty_get(&node->base, EVAS_3D_STATE_NODE_TRANSFORM) || + evas_3d_object_dirty_get(&node->base, EVAS_3D_STATE_NODE_MESH_GEOMETRY) || + evas_3d_object_dirty_get(&node->base, EVAS_3D_STATE_NODE_MESH_FRAME) || + evas_3d_object_dirty_get(&node->base, EVAS_3D_STATE_NODE_MEMBER)) + { + Eina_List *l; + Evas_3D_Node *n; + + /* Update AABB of this node. */ + evas_box3_empty_set(&node->aabb); + + EINA_LIST_FOREACH(node->members, l, n) + { + evas_box3_union(&node->aabb, &node->aabb, &n->aabb); + } + + if (node->type == EVAS_3D_NODE_TYPE_MESH) + { + /* TODO: */ + } + } + + return EINA_TRUE; +} + +static Eina_Bool +_node_update_done(Evas_3D_Node *node, void *data EINA_UNUSED) +{ + evas_3d_object_update_done(&node->base); + return EINA_TRUE; +} + +static void +_node_update(Evas_3D_Object *obj) +{ + Evas_3D_Node *node = (Evas_3D_Node *)obj; + + /* Update transform. */ + evas_3d_node_tree_traverse(node, EVAS_3D_TREE_TRAVERSE_LEVEL_ORDER, EINA_FALSE, + _node_transform_update, NULL); + + /* Update AABB. */ + evas_3d_node_tree_traverse(node, EVAS_3D_TREE_TRAVERSE_POST_ORDER, EINA_FALSE, + _node_aabb_update, NULL); + + /* Update node item. */ + evas_3d_node_tree_traverse(node, EVAS_3D_TREE_TRAVERSE_ANY_ORDER, EINA_FALSE, + _node_item_update, NULL); + + /* Mark all nodes in the tree as up-to-date. */ + evas_3d_node_tree_traverse(node, EVAS_3D_TREE_TRAVERSE_ANY_ORDER, EINA_FALSE, + _node_update_done, NULL); +} + +static void +_node_free(Evas_3D_Object *obj) +{ + Evas_3D_Node *node = (Evas_3D_Node *)obj; + + if (node->members) + { + Eina_List *l; + Evas_3D_Node *n; + + EINA_LIST_FOREACH(node->members, l, n) + { + evas_3d_object_unreference(&n->base); + } + + eina_list_free(node->members); + } + + if (node->data.mesh.meshes) + { + Eina_List *l; + Evas_3D_Mesh *m; + + EINA_LIST_FOREACH(node->data.mesh.meshes, l, m) + { + evas_3d_mesh_node_del(m, node); + evas_3d_object_unreference(&m->base); + } + + eina_list_free(node->data.mesh.meshes); + } + + if (node->data.mesh.node_meshes) + eina_hash_free(node->data.mesh.node_meshes); + + if (node->scenes_root) + eina_hash_free(node->scenes_root); + + if (node->scenes_camera) + eina_hash_free(node->scenes_camera); + + free(node); +} + +static const Evas_3D_Object_Func node_func = +{ + _node_free, + _node_change, + _node_update, +}; + +void +evas_3d_node_scene_root_add(Evas_3D_Node *node, Evas_3D_Scene *scene) +{ + int count = 0; + + if (node->scenes_root == NULL) + { + node->scenes_root = eina_hash_pointer_new(NULL); + + if (node->scenes_root == NULL) + { + ERR("Failed to create hash table."); + return; + } + } + else + count = (int)eina_hash_find(node->scenes_root, &scene); + + eina_hash_set(node->scenes_root, &scene, (const void *)(count + 1)); +} + +void +evas_3d_node_scene_root_del(Evas_3D_Node *node, Evas_3D_Scene *scene) +{ + int count = 0; + + if (node->scenes_root == NULL) + { + ERR("No scene to delete."); + return; + } + + count = (int)eina_hash_find(node->scenes_root, &scene); + + if (count == 1) + eina_hash_del(node->scenes_root, &scene, NULL); + else + eina_hash_set(node->scenes_root, &scene, (const void *)(count - 1)); +} + +void +evas_3d_node_scene_camera_add(Evas_3D_Node *node, Evas_3D_Scene *scene) +{ + int count = 0; + + if (node->scenes_camera == NULL) + { + node->scenes_camera = eina_hash_pointer_new(NULL); + + if (node->scenes_camera == NULL) + { + ERR("Failed to create hash table."); + return; + } + } + else + count = (int)eina_hash_find(node->scenes_camera, &scene); + + eina_hash_set(node->scenes_camera, &scene, (const void *)(count + 1)); +} + +void +evas_3d_node_scene_camera_del(Evas_3D_Node *node, Evas_3D_Scene *scene) +{ + int count = 0; + + if (node->scenes_camera == NULL) + { + ERR("No scene to delete."); + return; + } + + count = (int)eina_hash_find(node->scenes_camera, &scene); + + if (count == 1) + eina_hash_del(node->scenes_camera, &scene, NULL); + else + eina_hash_set(node->scenes_camera, &scene, (const void *)(count - 1)); +} + +Evas_3D_Node * +evas_3d_node_new(Evas *e, Evas_3D_Node_Type type) +{ + Evas_3D_Node *node = NULL; + + node = (Evas_3D_Node *)calloc(1, sizeof(Evas_3D_Node)); + + if (node == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + evas_3d_object_init(&node->base, e, EVAS_3D_OBJECT_TYPE_NODE, &node_func); + + evas_vec3_set(&node->position, 0.0, 0.0, 0.0); + evas_vec4_set(&node->orientation, 0.0, 0.0, 0.0, 0.0); + evas_vec3_set(&node->scale, 1.0, 1.0, 1.0); + + evas_vec3_set(&node->position_world, 0.0, 0.0, 0.0); + evas_vec4_set(&node->orientation_world, 0.0, 0.0, 0.0, 1.0); + evas_vec3_set(&node->scale_world, 1.0, 1.0, 1.0); + + node->position_inherit = EINA_TRUE; + node->orientation_inherit = EINA_TRUE; + node->scale_inherit = EINA_TRUE; + + evas_box3_set(&node->aabb, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + + node->type = type; + + if (type == EVAS_3D_NODE_TYPE_MESH) + { + node->data.mesh.node_meshes = eina_hash_pointer_new(_node_mesh_free_func); + + if (node->data.mesh.node_meshes == NULL) + { + ERR("Failed to create node mesh table."); + _node_free(&node->base); + return NULL; + } + } + + return node; +} + +void +evas_3d_node_traverse(Evas_3D_Node *from, Evas_3D_Node *to, Evas_3D_Node_Traverse_Type type, + Eina_Bool skip, Evas_3D_Node_Func func, void *data) +{ + Eina_List *nodes = NULL, *n; + Evas_3D_Node *node = NULL; + + if (from == NULL || func == NULL) + goto error; + + if (type == EVAS_3D_NODE_TRAVERSE_DOWNWARD) + { + if (to == NULL) + goto error; + + node = to; + + do { + nodes = eina_list_prepend(nodes, (const void *)node); + + if (node == from) + break; + + node = node->parent; + + if (node == NULL) + goto error; + } while (1); + } + else if (type == EVAS_3D_NODE_TRAVERSE_UPWARD) + { + node = from; + + do { + nodes = eina_list_append(nodes, (const void *)node); + + if (node == to) + break; + + node = node->parent; + + if (node == NULL) + { + if (to == NULL) + break; + + goto error; + } + } while (1); + } + + EINA_LIST_FOREACH(nodes, n, node) + { + if (!func(node, data) && skip) + break; + } + + eina_list_free(nodes); + return; + +error: + ERR("Node traverse error."); + + if (nodes) + eina_list_free(nodes); +} + +void +evas_3d_node_tree_traverse(Evas_3D_Node *root, Evas_3D_Tree_Traverse_Type type, + Eina_Bool skip, Evas_3D_Node_Func func, void *data) +{ + Eina_List *nodes = NULL, *l; + Evas_3D_Node *node = NULL, *n, *last; + + if (root == NULL || func == NULL) + return; + + if (type == EVAS_3D_TREE_TRAVERSE_LEVEL_ORDER) + { + /* Put the root node in the queue. */ + nodes = eina_list_append(nodes, root); + + while (eina_list_count(nodes) > 0) + { + /* Dequeue a node. */ + node = eina_list_data_get(nodes); + nodes = eina_list_remove_list(nodes, nodes); + + /* Call node function on the node. */ + if (func(node, data) || !skip) + { + /* Enqueue member nodes. */ + EINA_LIST_FOREACH(node->members, l, n) + { + nodes = eina_list_append(nodes, n); + } + } + } + } + else if (type == EVAS_3D_TREE_TRAVERSE_PRE_ORDER) + { + /* Put the root node in the stack. */ + nodes = eina_list_append(nodes, root); + + while (eina_list_count(nodes) > 0) + { + /* Pop a node from the stack. */ + node = eina_list_data_get(nodes); + nodes = eina_list_remove_list(nodes, nodes); + + /* Call node function on the node. */ + if (func(node, data) || !skip) + { + /* Push member nodes into the stack. */ + EINA_LIST_REVERSE_FOREACH(node->members, l, n) + { + nodes = eina_list_prepend(nodes, n); + } + } + } + } + else if (type == EVAS_3D_TREE_TRAVERSE_POST_ORDER) + { + if (skip) + { + ERR("Using skip with post order traversal has no effect."); + return; + } + + /* Put the root node in the stack. */ + nodes = eina_list_append(nodes, root); + last = NULL; + + while (eina_list_count(nodes) > 0) + { + /* Peek a node from the stack. */ + node = eina_list_data_get(nodes); + + if (eina_list_count(node->members) == 0) + { + /* The peeked node is a leaf node, + * so visit it and pop from the stack. */ + func(node, data); + nodes = eina_list_remove_list(nodes, nodes); + + /* Save the last visited node. */ + last = node; + } + else + { + /* If the peeked node is not a leaf node, + * there can be only two possible cases. + * + * 1. the parent of the last visited node. + * 2. a sibling of the last visited node. + * + * If the last visited node is a direct child of the peeked node, + * we have no unvisted child nodes for the peeked node, so we have to visit + * the peeked node and pop from the stack. + * + * Otherwise it should be a sibling of the peeked node, so we have to push + * its childs into the stack. */ + + if (last && last->parent == node) + { + /* Visit the node as it doesn't have any unvisited child node. */ + func(node, data); + nodes = eina_list_remove_list(nodes, nodes); + + /* Save the last visited node. */ + last = node; + } + else + { + /* Push child nodes into the stack. */ + EINA_LIST_REVERSE_FOREACH(node->members, l, n) + { + nodes = eina_list_prepend(nodes, n); + } + } + } + } + } + + if (nodes != NULL) + eina_list_free(nodes); +} + +Eina_Bool +_node_is_visible(Evas_3D_Node *node EINA_UNUSED, Evas_3D_Node *camera_node EINA_UNUSED) +{ + /* TODO: */ + return EINA_TRUE; +} + +Eina_Bool +evas_3d_node_mesh_collect(Evas_3D_Node *node, void *data) +{ + Evas_3D_Scene_Data *scene_data = (Evas_3D_Scene_Data *)data; + + if (!_node_is_visible(node, scene_data->camera_node)) + { + /* Skip entire sub-tree of this node. */ + return EINA_FALSE; + } + + if (node->type == EVAS_3D_NODE_TYPE_MESH) + scene_data->mesh_nodes = eina_list_append(scene_data->mesh_nodes, node); + + return EINA_TRUE; +} + +Eina_Bool +evas_3d_node_light_collect(Evas_3D_Node *node, void *data) +{ + Evas_3D_Scene_Data *scene_data = (Evas_3D_Scene_Data *)data; + + if (node->type == EVAS_3D_NODE_TYPE_LIGHT) + scene_data->light_nodes = eina_list_append(scene_data->light_nodes, node); + + return EINA_TRUE; +} + +EAPI Evas_3D_Node * +evas_3d_node_add(Evas *e, Evas_3D_Node_Type type) +{ + return evas_3d_node_new(e, type); +} + +EAPI void +evas_3d_node_del(Evas_3D_Node *node) +{ + evas_3d_object_unreference(&node->base); +} + +EAPI Evas_3D_Node_Type +evas_3d_node_type_get(const Evas_3D_Node *node) +{ + return node->type; +} + +EAPI Evas * +evas_3d_node_evas_get(const Evas_3D_Node *node) +{ + return node->base.evas; +} + +EAPI void +evas_3d_node_member_add(Evas_3D_Node *node, Evas_3D_Node *member) +{ + if (node == member) + { + ERR("Failed to add a member node (adding to itself)."); + return; + } + + if (member->parent == node) + return; + + if (member->parent) + { + /* Detaching from previous parent. */ + member->parent->members = eina_list_remove(member->parent->members, member); + + /* Mark changed. */ + evas_3d_object_change(&member->parent->base, EVAS_3D_STATE_NODE_MEMBER, NULL); + } + else + { + /* Should get a new reference. */ + evas_3d_object_reference(&member->base); + } + + /* Add the member node. */ + node->members = eina_list_append(node->members, (const void *)member); + member->parent = node; + + /* Mark changed. */ + evas_3d_object_change(&member->base, EVAS_3D_STATE_NODE_PARENT, NULL); + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_MEMBER, NULL); +} + +EAPI void +evas_3d_node_member_del(Evas_3D_Node *node, Evas_3D_Node *member) +{ + if (member->parent != node) + { + ERR("Failed to delete a member node (not a member of the given node)"); + return; + } + + /* Delete the member node. */ + node->members = eina_list_remove(node->members, member); + member->parent = NULL; + + /* Mark modified object as changed. */ + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_MEMBER, NULL); + evas_3d_object_change(&member->base, EVAS_3D_STATE_NODE_PARENT, NULL); + + /* Decrease reference count. */ + evas_3d_object_unreference(&member->base); +} + +EAPI Evas_3D_Node * +evas_3d_node_parent_get(const Evas_3D_Node *node) +{ + return node->parent; +} + +EAPI const Eina_List * +evas_3d_node_member_list_get(const Evas_3D_Node *node) +{ + return node->members; +} + +EAPI void +evas_3d_node_position_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z) +{ + node->position.x = x; + node->position.y = y; + node->position.z = z; + + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI void +evas_3d_node_orientation_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z, Evas_Real w) +{ + node->orientation.x = x; + node->orientation.y = y; + node->orientation.z = z; + node->orientation.w = w; + + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI void +evas_3d_node_orientation_angle_axis_set(Evas_3D_Node *node, + Evas_Real angle, Evas_Real x, Evas_Real y, Evas_Real z) +{ + Evas_Real half_angle = 0.5 * DEGREE_TO_RADIAN(angle); + Evas_Real s = sin(half_angle); + Evas_Vec3 axis; + + evas_vec3_set(&axis, x, y, z); + evas_vec3_normalize(&axis, &axis); + + node->orientation.w = cos(half_angle); + node->orientation.x = s * axis.x; + node->orientation.y = s * axis.y; + node->orientation.z = s * axis.z; + + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI void +evas_3d_node_scale_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z) +{ + node->scale.x = x; + node->scale.y = y; + node->scale.z = z; + + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI void +evas_3d_node_position_get(const Evas_3D_Node *node, Evas_3D_Space space, + Evas_Real *x, Evas_Real *y, Evas_Real *z) +{ + if (space == EVAS_3D_SPACE_LOCAL) + { + if (x) *x = 0.0; + if (y) *y = 0.0; + if (z) *z = 0.0; + } + else if (space == EVAS_3D_SPACE_PARENT) + { + if (x) *x = node->position.x; + if (y) *y = node->position.y; + if (z) *z = node->position.z; + } + else if (space == EVAS_3D_SPACE_WORLD) + { + evas_3d_object_update((Evas_3D_Object *)&node->base); + + if (x) *x = node->position_world.x; + if (y) *y = node->position_world.y; + if (z) *z = node->position_world.z; + } +} + +EAPI void +evas_3d_node_orientation_get(const Evas_3D_Node *node, Evas_3D_Space space, + Evas_Real *x, Evas_Real *y, Evas_Real *z, Evas_Real *w) +{ + if (space == EVAS_3D_SPACE_LOCAL) + { + if (x) *x = 0.0; + if (y) *y = 0.0; + if (z) *z = 0.0; + if (w) *w = 0.0; + } + else if (space == EVAS_3D_SPACE_PARENT) + { + if (x) *x = node->orientation.x; + if (y) *y = node->orientation.y; + if (z) *z = node->orientation.z; + if (w) *w = node->orientation.w; + } + else if (space == EVAS_3D_SPACE_WORLD) + { + evas_3d_object_update((Evas_3D_Object *)&node->base); + + if (x) *x = node->orientation_world.x; + if (y) *y = node->orientation_world.y; + if (z) *z = node->orientation_world.z; + if (w) *w = node->orientation_world.w; + } + +} + +EAPI void +evas_3d_node_scale_get(const Evas_3D_Node *node, Evas_3D_Space space, + Evas_Real *x, Evas_Real *y, Evas_Real *z) +{ + if (space == EVAS_3D_SPACE_LOCAL) + { + if (x) *x = 0.0; + if (y) *y = 0.0; + if (z) *z = 0.0; + } + else if (space == EVAS_3D_SPACE_PARENT) + { + if (x) *x = node->scale.x; + if (y) *y = node->scale.y; + if (z) *z = node->scale.z; + } + else if (space == EVAS_3D_SPACE_WORLD) + { + evas_3d_object_update((Evas_3D_Object *)&node->base); + + if (x) *x = node->scale_world.x; + if (y) *y = node->scale_world.y; + if (z) *z = node->scale_world.z; + } +} + +EAPI void +evas_3d_node_position_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) +{ + node->position_inherit = inherit; + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI void +evas_3d_node_orientation_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) +{ + node->orientation_inherit = inherit; + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI void +evas_3d_node_scale_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) +{ + node->scale_inherit = inherit; + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI Eina_Bool +evas_3d_node_position_inherit_get(const Evas_3D_Node *node) +{ + return node->position_inherit; +} + +EAPI Eina_Bool +evas_3d_node_orientation_inherit_get(const Evas_3D_Node *node) +{ + return node->orientation_inherit; +} + +EAPI Eina_Bool +evas_3d_node_scale_inherit_get(const Evas_3D_Node *node) +{ + return node->scale_inherit; +} + +EAPI void +evas_3d_node_look_at_set(Evas_3D_Node *node, + Evas_3D_Space target_space, Evas_Real tx, Evas_Real ty, Evas_Real tz, + Evas_3D_Space up_space, Evas_Real ux, Evas_Real uy, Evas_Real uz) +{ + Evas_Vec3 target; + Evas_Vec3 up; + Evas_Vec3 x, y, z; + + /* Target position in parent space. */ + if (target_space == EVAS_3D_SPACE_LOCAL) + { + ERR("TODO:"); + return; + } + else if (target_space == EVAS_3D_SPACE_PARENT) + { + evas_vec3_set(&target, tx, ty, tz); + } + else if (target_space == EVAS_3D_SPACE_WORLD) + { + ERR("TODO:"); + return; + } + else + { + ERR("Invalid coordinate space."); + return; + } + + if (up_space == EVAS_3D_SPACE_LOCAL) + { + ERR("TODO:"); + return; + } + else if (up_space == EVAS_3D_SPACE_PARENT) + { + evas_vec3_set(&up, ux, uy, uz); + } + else if (up_space == EVAS_3D_SPACE_WORLD) + { + ERR("TODO:"); + return; + } + else + { + ERR("Invalid coordinate space."); + return; + } + + /* From now on, everything takes place in parent space. */ + evas_vec3_subtract(&z, &node->position, &target); + evas_vec3_normalize(&z, &z); + + evas_vec3_cross_product(&x, &up, &z); + evas_vec3_normalize(&x, &x); + + evas_vec3_cross_product(&y, &z, &x); + evas_vec3_normalize(&y, &y); + + /* Below matrix to quaternion conversion code taken from + * http://fabiensanglard.net/doom3_documentation/37726-293748.pdf + * When any license issue occurs, use ken shoemake's algorithm instead. + */ + + if (x.x + y.y + z.z > 0.0) + { + Evas_Real t = x.x + y.y + z.z + 1.0; + Evas_Real s = evas_reciprocal_sqrt(t) * 0.5; + + node->orientation.w = s * t; + node->orientation.z = (x.y - y.x) * s; + node->orientation.y = (z.x - x.z) * s; + node->orientation.x = (y.z - z.y) * s; + } + else if (x.x > y.y && x.x > z.z) + { + Evas_Real t = x.x - y.y - z.z + 1.0; + Evas_Real s = evas_reciprocal_sqrt(t) * 0.5; + + node->orientation.x = s * t; + node->orientation.y = (x.y + y.x) * s; + node->orientation.z = (z.x + x.z) * s; + node->orientation.w = (y.z - z.y) * s; + } + else if (y.y > z.z) + { + Evas_Real t = -x.x + y.y - z.z + 1.0; + Evas_Real s = evas_reciprocal_sqrt(t) * 0.5; + + node->orientation.y = s * t; + node->orientation.x = (x.y + y.x) * s; + node->orientation.w = (z.x - x.z) * s; + node->orientation.z = (y.z + z.y) * s; + } + else + { + Evas_Real t = -x.x - y.y + z.z + 1.0; + Evas_Real s = evas_reciprocal_sqrt(t) * 0.5; + + node->orientation.z = s * t; + node->orientation.w = (x.y - y.x) * s; + node->orientation.x = (z.x + x.z) * s; + node->orientation.y = (y.z + z.y) * s; + } + + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI void +evas_3d_node_camera_set(Evas_3D_Node *node, Evas_3D_Camera *camera) +{ + if (node->type != EVAS_3D_NODE_TYPE_CAMERA) + { + ERR("Node type mismatch."); + return; + } + + if (node->data.camera.camera == camera) + return; + + if (node->data.camera.camera) + { + /* Detach previous camera object. */ + evas_3d_camera_node_del(node->data.camera.camera, node); + evas_3d_object_unreference(&node->data.camera.camera->base); + } + + node->data.camera.camera = camera; + evas_3d_object_reference(&camera->base); + + /* Register change notification on the camera for this node. */ + evas_3d_camera_node_add(camera, node); + + /* Mark changed. */ + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_CAMERA, NULL); +} + +EAPI Evas_3D_Camera * +evas_3d_node_camera_get(const Evas_3D_Node *node) +{ + return node->data.camera.camera; +} + +EAPI void +evas_3d_node_light_set(Evas_3D_Node *node, Evas_3D_Light *light) +{ + if (node->type != EVAS_3D_NODE_TYPE_LIGHT) + { + ERR("Node type mismatch."); + return; + } + + if (node->data.light.light == light) + return; + + if (node->data.light.light) + { + /* Detach previous light object. */ + evas_3d_light_node_del(node->data.light.light, node); + evas_3d_object_unreference(&node->data.light.light->base); + } + + node->data.light.light = light; + evas_3d_object_reference(&light->base); + + /* Register change notification on the light for this node. */ + evas_3d_light_node_add(light, node); + + /* Mark changed. */ + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_LIGHT, NULL); +} + +EAPI Evas_3D_Light * +evas_3d_node_light_get(const Evas_3D_Node *node) +{ + return node->data.light.light; +} + +EAPI void +evas_3d_node_mesh_add(Evas_3D_Node *node, Evas_3D_Mesh *mesh) +{ + Evas_3D_Node_Mesh *nm = NULL; + + if (node->type != EVAS_3D_NODE_TYPE_MESH) + { + ERR("Node type mismatch."); + return; + } + + if (eina_hash_find(node->data.mesh.node_meshes, mesh) != NULL) + { + ERR("The mesh is already added to the node."); + return; + } + + if ((nm = _node_mesh_new(node, mesh)) == NULL) + { + ERR("Failed to create node mesh."); + return; + } + + /* TODO: Find node mesh and add if it does not exist. */ + if (!eina_hash_add(node->data.mesh.node_meshes, mesh, nm)) + { + ERR("Failed to add a mesh to mesh table."); + _node_mesh_free(nm); + return; + } + + node->data.mesh.meshes = eina_list_append(node->data.mesh.meshes, mesh); + evas_3d_object_reference(&mesh->base); + + /* Register change notification. */ + evas_3d_mesh_node_add(mesh, node); + + /* Mark changed. */ + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_MESH_GEOMETRY, NULL); + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_MESH_MATERIAL, NULL); +} + +EAPI void +evas_3d_node_mesh_del(Evas_3D_Node *node, Evas_3D_Mesh *mesh) +{ + if (node->type != EVAS_3D_NODE_TYPE_MESH) + { + ERR("Node type mismatch."); + return; + } + + if (!eina_hash_del(node->data.mesh.node_meshes, mesh, NULL)) + { + ERR("The given mesh doesn't belong to this node."); + return; + } + + node->data.mesh.meshes = eina_list_remove(node->data.mesh.meshes, mesh); + evas_3d_mesh_node_del(mesh, node); + evas_3d_object_unreference(&mesh->base); + + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_MESH_GEOMETRY, NULL); + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_MESH_MATERIAL, NULL); +} + +EAPI const Eina_List * +evas_3d_node_mesh_list_get(const Evas_3D_Node *node) +{ + return node->data.mesh.meshes; +} + +EAPI void +evas_3d_node_mesh_frame_set(Evas_3D_Node *node, Evas_3D_Mesh *mesh, int frame) +{ + Evas_3D_Node_Mesh *nm = NULL; + + if (node->type != EVAS_3D_NODE_TYPE_MESH) + { + ERR("Node type mismatch."); + return; + } + + if ((nm = eina_hash_find(node->data.mesh.node_meshes, mesh)) == NULL) + { + ERR("The given mesh doesn't belongs to this node."); + return; + } + + nm->frame = frame; + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_MESH_FRAME, NULL); +} + +EAPI int +evas_3d_node_mesh_frame_get(const Evas_3D_Node *node, Evas_3D_Mesh *mesh) +{ + Evas_3D_Node_Mesh *nm = NULL; + + if (node->type != EVAS_3D_NODE_TYPE_MESH) + { + ERR("Node type mismatch."); + return 0; + } + + if ((nm = eina_hash_find(node->data.mesh.node_meshes, mesh)) == NULL) + { + ERR("The given mesh doesn't belongs to this node."); + return 0; + } + + return nm->frame; +} diff --git a/src/lib/evas/canvas/evas_3d_object.c b/src/lib/evas/canvas/evas_3d_object.c new file mode 100644 index 0000000000..75bd241519 --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_object.c @@ -0,0 +1,129 @@ +#include "evas_common_private.h" +#include "evas_private.h" + +#define REF_COUNT_CHECK(obj) \ + do { \ + if ((obj)->ref_count < 1) \ + { \ + ERR("Invalid reference count.") + +#define REF_COUNT_CHECK_END() \ + } \ + } while (0) + +#define OBJ_TYPE_CHECK(obj, type) \ + do { \ + if ((obj)->type != type) \ + { \ + ERR("3D object type check failed.") + +#define OBJ_TYPE_CHECK_END() \ + } \ + } while (0) + +void +evas_3d_object_init(Evas_3D_Object *obj, + Evas *e, Evas_3D_Object_Type type, const Evas_3D_Object_Func *func) +{ + obj->evas = e; + obj->type = type; + obj->ref_count = 1; + obj->func = *func; + memset(&obj->dirty[0], 0x00, sizeof(Eina_Bool) * EVAS_3D_STATE_MAX); +} + +void +evas_3d_object_reference(Evas_3D_Object *obj) +{ + REF_COUNT_CHECK(obj); + return; + REF_COUNT_CHECK_END(); + + obj->ref_count++; +} + +void +evas_3d_object_unreference(Evas_3D_Object *obj) +{ + if (obj->ref_count < 1) + { + printf("gotcha\n"); + } + + REF_COUNT_CHECK(obj); + return; + REF_COUNT_CHECK_END(); + + obj->ref_count--; + + if (obj->ref_count == 0) + obj->func.free(obj); +} + +int +evas_3d_object_reference_count_get(const Evas_3D_Object *obj) +{ + REF_COUNT_CHECK(obj); + return 0; + REF_COUNT_CHECK_END(); + + return obj->ref_count; +} + +Evas * +evas_3d_object_evas_get(const Evas_3D_Object *obj) +{ + REF_COUNT_CHECK(obj); + return NULL; + REF_COUNT_CHECK_END(); + + return obj->evas; +} + +Evas_3D_Object_Type +evas_3d_object_type_get(const Evas_3D_Object *obj) +{ + REF_COUNT_CHECK(obj); + return EVAS_3D_OBJECT_TYPE_INVALID; + REF_COUNT_CHECK_END(); + + return obj->type; +} + +Eina_Bool +evas_3d_object_dirty_get(const Evas_3D_Object *obj, Evas_3D_State state) +{ + return obj->dirty[state]; +} + +void +evas_3d_object_change(Evas_3D_Object *obj, Evas_3D_State state, Evas_3D_Object *ref) +{ + /* Skip already dirty properties. */ + if (obj->dirty[state]) + return; + + obj->dirty[state] = EINA_TRUE; + obj->dirty[EVAS_3D_STATE_ANY] = EINA_TRUE; + + if (obj->func.change) + obj->func.change(obj, state, ref); +} + +void +evas_3d_object_update(Evas_3D_Object *obj) +{ + if (!obj->dirty[EVAS_3D_STATE_ANY]) + return; + + if (obj->func.update) + obj->func.update(obj); + + evas_3d_object_update_done(obj); +} + +void +evas_3d_object_update_done(Evas_3D_Object *obj) +{ + memset(&obj->dirty[0], 0x00, sizeof(Eina_Bool) * EVAS_3D_STATE_MAX); +} diff --git a/src/lib/evas/canvas/evas_3d_scene.c b/src/lib/evas/canvas/evas_3d_scene.c new file mode 100644 index 0000000000..3327fee559 --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_scene.c @@ -0,0 +1,622 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include "evas_common_private.h" +#include "evas_private.h" + +void +evas_3d_scene_data_init(Evas_3D_Scene_Data *data) +{ + data->camera_node = NULL; + data->light_nodes = NULL; + data->mesh_nodes = NULL; +} + +void +evas_3d_scene_data_fini(Evas_3D_Scene_Data *data) +{ + if (data->light_nodes) + eina_list_free(data->light_nodes); + + if (data->mesh_nodes) + eina_list_free(data->mesh_nodes); +} + +static void +_scene_change(Evas_3D_Object *obj, Evas_3D_State state EINA_UNUSED, Evas_3D_Object *ref EINA_UNUSED) +{ + Evas_3D_Scene *scene = (Evas_3D_Scene *)obj; + Eina_List *l; + Evas_Object *eo; + + EINA_LIST_FOREACH(scene->images, l, eo) + { + Evas_Object_Protected_Data *obj = eo_data_scope_get(eo, EVAS_OBJ_CLASS); + evas_object_change(eo, obj); + } +} + +static void +_scene_update(Evas_3D_Object *obj) +{ + Evas_3D_Scene *scene = (Evas_3D_Scene *)obj; + + if (scene->root_node) + evas_3d_object_update(&scene->root_node->base); + + if (scene->camera_node) + evas_3d_object_update(&scene->camera_node->base); +} + +static void +_scene_free(Evas_3D_Object *obj) +{ + Evas_3D_Scene *scene = (Evas_3D_Scene *)obj; + + if (scene->root_node) + { + evas_3d_node_scene_root_del(scene->root_node, scene); + evas_3d_object_unreference(&scene->root_node->base); + } + + if (scene->camera_node) + { + evas_3d_node_scene_camera_del(scene->camera_node, scene); + evas_3d_object_unreference(&scene->camera_node->base); + } + + if (scene->images) + eina_list_free(scene->images); + + free(scene); +} + +static const Evas_3D_Object_Func scene_func = +{ + _scene_free, + _scene_change, + _scene_update, +}; + +Evas_3D_Scene * +evas_3d_scene_new(Evas *e) +{ + Evas_3D_Scene *scene = NULL; + + scene = (Evas_3D_Scene *)calloc(1, sizeof(Evas_3D_Scene)); + + if (scene == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + evas_3d_object_init(&scene->base, e, EVAS_3D_OBJECT_TYPE_SCENE, &scene_func); + evas_color_set(&scene->bg_color, 0.0, 0.0, 0.0, 0.0); + + return scene; +} + +EAPI Evas_3D_Scene * +evas_3d_scene_add(Evas *e) +{ + return evas_3d_scene_new(e); +} + +EAPI void +evas_3d_scene_del(Evas_3D_Scene *scene) +{ + evas_3d_object_unreference(&scene->base); +} + +EAPI Evas * +evas_3d_scene_evas_get(const Evas_3D_Scene *scene) +{ + return scene->base.evas; +} + +EAPI void +evas_3d_scene_root_node_set(Evas_3D_Scene *scene, Evas_3D_Node *node) +{ + if (scene->root_node == node) + return; + + if (scene->root_node) + { + evas_3d_node_scene_root_del(scene->root_node, scene); + evas_3d_object_unreference(&scene->root_node->base); + } + + scene->root_node = node; + + if (node) + { + evas_3d_object_reference(&node->base); + evas_3d_node_scene_root_add(node, scene); + } + + evas_3d_object_change(&scene->base, EVAS_3D_STATE_SCENE_ROOT_NODE, NULL); +} + +EAPI Evas_3D_Node * +evas_3d_scene_root_node_get(const Evas_3D_Scene *scene) +{ + return scene->root_node; +} + +EAPI void +evas_3d_scene_camera_node_set(Evas_3D_Scene *scene, Evas_3D_Node *node) +{ + if (scene->camera_node == node) + return; + + if (scene->camera_node) + { + evas_3d_node_scene_camera_del(scene->camera_node, scene); + evas_3d_object_unreference(&scene->camera_node->base); + } + + scene->camera_node = node; + + if (node) + { + evas_3d_object_reference(&node->base); + evas_3d_node_scene_camera_add(node, scene); + } + + evas_3d_object_change(&scene->base, EVAS_3D_STATE_SCENE_CAMERA_NODE, NULL); +} + +EAPI Evas_3D_Node * +evas_3d_scene_camera_node_get(const Evas_3D_Scene *scene) +{ + return scene->camera_node; +} + +EAPI void +evas_3d_scene_size_set(Evas_3D_Scene *scene, int w, int h) +{ + scene->w = w; + scene->h = h; + evas_3d_object_change(&scene->base, EVAS_3D_STATE_SCENE_SIZE, NULL); +} + +EAPI void +evas_3d_scene_size_get(const Evas_3D_Scene *scene, int *w, int *h) +{ + if (w) *w = scene->w; + if (h) *h = scene->h; +} + +EAPI void +evas_3d_scene_background_color_set(Evas_3D_Scene *scene, + Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) +{ + evas_color_set(&scene->bg_color, r, g, b, a); + evas_3d_object_change(&scene->base, EVAS_3D_STATE_SCENE_BACKGROUND_COLOR, NULL); +} + +EAPI void +evas_3d_scene_background_color_get(const Evas_3D_Scene *scene, + Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) +{ + if (r) *r = scene->bg_color.r; + if (g) *g = scene->bg_color.g; + if (b) *b = scene->bg_color.b; + if (a) *a = scene->bg_color.a; +} + +static inline Eina_Bool +_pick_data_triangle_add(Evas_3D_Pick_Data *data, const Evas_Ray3 *ray, + const Evas_Triangle3 *tri) +{ + Evas_Vec3 e1, e2, tvec, pvec, qvec; + Evas_Real det, inv_det, u, v, t; + + evas_vec3_subtract(&e1, &tri->p1, &tri->p0); + evas_vec3_subtract(&e2, &tri->p2, &tri->p0); + + evas_vec3_cross_product(&pvec, &ray->dir, &e2); + det = evas_vec3_dot_product(&e1, &pvec); + + /* If determinant is near zero, ray lies in plane of triangle. */ + if (det > -0.0000001 && det < 0.0000001) + return EINA_FALSE; + + inv_det = 1.0 / det; + + /* Calculate distance from p0 to ray origin. */ + evas_vec3_subtract(&tvec, &ray->org, &tri->p0); + + /* Calculate U parameter and test bounds. */ + u = evas_vec3_dot_product(&tvec, &pvec) * inv_det; + + if (u < 0.0 || u > 1.0) + return EINA_FALSE; + + /* Prepare to tst V parameter. */ + evas_vec3_cross_product(&qvec, &tvec, &e1); + + /* Calculate V parameter and test bounds. */ + v = evas_vec3_dot_product(&ray->dir, &qvec) * inv_det; + + if (v < 0.0 || u + v > 1.0) + return EINA_FALSE; + + /* Calculate T parameter and test bounds. */ + t = evas_vec3_dot_product(&e2, &qvec) * inv_det; + + if (t >= 0.0 && t <= 1.0) + { + if (!data->picked || t < data->z) + { + data->picked = EINA_TRUE; + data->z = t; + data->u = u; + data->v = v; + return EINA_TRUE; + } + } + + return EINA_FALSE; +} + +static inline void +_position_get(Evas_Vec3 *out, const Evas_3D_Vertex_Buffer *pos0, const Evas_3D_Vertex_Buffer *pos1, + Evas_Real weight, int index) +{ + if (pos1->data == NULL) + { + float *ptr = (float *)((char *)pos0->data + pos0->stride * index); + + out->x = ptr[0]; + out->y = ptr[1]; + out->z = ptr[2]; + } + else + { + float *ptr0, *ptr1; + + ptr0 = (float *)((char *)pos0->data + pos0->stride * index); + ptr1 = (float *)((char *)pos1->data + pos1->stride * index); + + out->x = ptr0[0] * weight + ptr1[0] * (1.0 - weight); + out->y = ptr0[1] * weight + ptr1[1] * (1.0 - weight); + out->z = ptr0[2] * weight + ptr1[2] * (1.0 - weight); + } +} + +static inline void +_pick_data_texcoord_update(Evas_3D_Pick_Data *data, + const Evas_3D_Vertex_Buffer *tex0, const Evas_3D_Vertex_Buffer *tex1, + Evas_Real weight, unsigned int i0, unsigned int i1, unsigned int i2) +{ + Evas_Real s0, s1, s2; + Evas_Real t0, t1, t2; + + if (tex1->data == NULL) + { + float *ptr; + + ptr = (float *)((char *)tex0->data + tex0->stride * i0); + + s0 = ptr[0]; + t0 = ptr[1]; + + ptr = (float *)((char *)tex0->data + tex0->stride * i1); + + s1 = ptr[0]; + t1 = ptr[1]; + + ptr = (float *)((char *)tex0->data + tex0->stride * i2); + + s2 = ptr[0]; + t2 = ptr[1]; + } + else + { + float *ptr0, *ptr1; + + ptr0 = (float *)((char *)tex0->data + tex0->stride * i0); + ptr1 = (float *)((char *)tex1->data + tex1->stride * i0); + + s0 = ptr0[0] * weight + ptr1[0] * (1.0 - weight); + t0 = ptr0[1] * weight + ptr1[1] * (1.0 - weight); + + ptr0 = (float *)((char *)tex0->data + tex0->stride * i1); + ptr1 = (float *)((char *)tex1->data + tex1->stride * i1); + + s1 = ptr0[0] * weight + ptr1[0] * (1.0 - weight); + t1 = ptr0[1] * weight + ptr1[1] * (1.0 - weight); + + ptr0 = (float *)((char *)tex0->data + tex0->stride * i2); + ptr1 = (float *)((char *)tex1->data + tex1->stride * i2); + + s2 = ptr0[0] * weight + ptr1[0] * (1.0 - weight); + t2 = ptr0[1] * weight + ptr1[1] * (1.0 - weight); + } + + data->s = s0 * (1 - data->u - data->v) + s1 * data->u + s2 * data->v; + data->t = t0 * (1 - data->u - data->v) + t1 * data->u + t2 * data->v; +} + + +static inline void +_pick_data_mesh_add(Evas_3D_Pick_Data *data, const Evas_Ray3 *ray, + Evas_3D_Mesh *mesh, int frame, Evas_3D_Node *node) +{ + Evas_3D_Vertex_Buffer pos0, pos1, tex0, tex1; + Evas_Real pos_weight, tex_weight; + Evas_Triangle3 tri; + int i; + + memset(&pos0, 0x00, sizeof(Evas_3D_Vertex_Buffer)); + memset(&pos1, 0x00, sizeof(Evas_3D_Vertex_Buffer)); + memset(&tex0, 0x00, sizeof(Evas_3D_Vertex_Buffer)); + memset(&tex1, 0x00, sizeof(Evas_3D_Vertex_Buffer)); + + evas_3d_mesh_interpolate_vertex_buffer_get(mesh, frame, EVAS_3D_VERTEX_POSITION, + &pos0, &pos1, &pos_weight); + + evas_3d_mesh_interpolate_vertex_buffer_get(mesh, frame, EVAS_3D_VERTEX_TEXCOORD, + &tex0, &tex1, &tex_weight); + + if (mesh->indices) + { + unsigned int i0, i1, i2; + + if (mesh->assembly == EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES) + { + for (i = 0; i < mesh->index_count; i += 3) + { + if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + { + i0 = ((unsigned short *)mesh->indices)[i]; + i1 = ((unsigned short *)mesh->indices)[i + 1]; + i2 = ((unsigned short *)mesh->indices)[i + 2]; + } + else + { + i0 = ((unsigned char *)mesh->indices)[i]; + i1 = ((unsigned char *)mesh->indices)[i + 1]; + i2 = ((unsigned char *)mesh->indices)[i + 2]; + } + + _position_get(&tri.p0, &pos0, &pos1, pos_weight, i0); + _position_get(&tri.p1, &pos0, &pos1, pos_weight, i1); + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i2); + + if (_pick_data_triangle_add(data, ray, &tri)) + { + _pick_data_texcoord_update(data, &tex0, &tex1, tex_weight, i0, i1, i2); + data->mesh = mesh; + data->node = node; + } + } + } + else if (mesh->assembly == EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_STRIP) + { + if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + { + i1 = ((unsigned short *)mesh->indices)[0]; + i2 = ((unsigned short *)mesh->indices)[1]; + } + else + { + i1 = ((unsigned char *)mesh->indices)[0]; + i2 = ((unsigned char *)mesh->indices)[1]; + } + + _position_get(&tri.p1, &pos0, &pos1, pos_weight, i1); + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i2); + + for (i = 0; i < mesh->index_count - 2; i++) + { + tri.p0 = tri.p1; + tri.p1 = tri.p2; + + if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + i2 = ((unsigned short *)mesh->indices)[i + 2]; + else + i2 = ((unsigned char *)mesh->indices)[i + 2]; + + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i2); + + if (_pick_data_triangle_add(data, ray, &tri)) + { + if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + { + i0 = ((unsigned short *)mesh->indices)[i]; + i1 = ((unsigned short *)mesh->indices)[i + 1]; + } + else + { + i0 = ((unsigned char *)mesh->indices)[i]; + i1 = ((unsigned char *)mesh->indices)[i + 1]; + } + + _pick_data_texcoord_update(data, &tex0, &tex1, tex_weight, i0, i1, i2); + data->mesh = mesh; + data->node = node; + } + } + } + else if (mesh->assembly == EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_FAN) + { + if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + { + i0 = ((unsigned short *)mesh->indices)[0]; + i2 = ((unsigned short *)mesh->indices)[1]; + } + else + { + i0 = ((unsigned char *)mesh->indices)[0]; + i2 = ((unsigned char *)mesh->indices)[1]; + } + + _position_get(&tri.p0, &pos0, &pos1, pos_weight, i0); + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i2); + + for (i = 1; i < mesh->index_count - 1; i++) + { + tri.p1 = tri.p2; + + if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + i2 = ((unsigned short *)mesh->indices)[i + 1]; + else + i2 = ((unsigned char *)mesh->indices)[i + 1]; + + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i2); + + if (_pick_data_triangle_add(data, ray, &tri)) + { + if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + i1 = ((unsigned short *)mesh->indices)[i]; + else + i1 = ((unsigned char *)mesh->indices)[i]; + + _pick_data_texcoord_update(data, &tex0, &tex1, tex_weight, i0, i1, i2); + data->mesh = mesh; + data->node = node; + } + } + } + } + else if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_BYTE) + { + if (mesh->assembly == EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES) + { + for (i = 0; i < mesh->index_count; i += 3) + { + _position_get(&tri.p0, &pos0, &pos1, pos_weight, i); + _position_get(&tri.p1, &pos0, &pos1, pos_weight, i + 1); + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i + 2); + + if (_pick_data_triangle_add(data, ray, &tri)) + { + _pick_data_texcoord_update(data, &tex0, &tex1, tex_weight, i, i + 1, i + 2); + data->mesh = mesh; + data->node = node; + } + } + } + else if (mesh->assembly == EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_STRIP) + { + _position_get(&tri.p1, &pos0, &pos1, pos_weight, 0); + _position_get(&tri.p2, &pos0, &pos1, pos_weight, 1); + + for (i = 0; i < mesh->index_count - 2; i++) + { + tri.p0 = tri.p1; + tri.p1 = tri.p2; + + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i + 2); + + if (_pick_data_triangle_add(data, ray, &tri)) + { + _pick_data_texcoord_update(data, &tex0, &tex1, tex_weight, i, i + 1, i + 2); + data->mesh = mesh; + data->node = node; + } + } + } + else if (mesh->assembly == EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_FAN) + { + _position_get(&tri.p0, &pos0, &pos1, pos_weight, 0); + _position_get(&tri.p2, &pos0, &pos1, pos_weight, 1); + + for (i = 1; i < mesh->index_count - 1; i++) + { + tri.p1 = tri.p2; + + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i + 1); + + if (_pick_data_triangle_add(data, ray, &tri)) + { + _pick_data_texcoord_update(data, &tex0, &tex1, tex_weight, 0, i, i + 1); + data->mesh = mesh; + data->node = node; + } + } + } + } +} + +Eina_Bool +_node_pick(Evas_3D_Node *node, void *data) +{ + Evas_Ray3 ray; + Evas_3D_Pick_Data *pick = (Evas_3D_Pick_Data *)data; + Evas_Mat4 mvp; + + if (! evas_box3_ray3_intersect(&node->aabb, &pick->ray_world)) + { + /* Skip entire subtree. */ + return EINA_FALSE; + } + + if (node->type == EVAS_3D_NODE_TYPE_MESH) + { + Eina_Iterator *itr; + void *ptr; + + /* Transform ray into local coordinate space. */ + evas_mat4_multiply(&mvp, &pick->matrix_vp, &node->data.mesh.matrix_local_to_world); + evas_ray3_init(&ray, pick->x, pick->y, &mvp); + + itr = eina_hash_iterator_data_new(node->data.mesh.node_meshes); + + while (eina_iterator_next(itr, &ptr)) + { + Evas_3D_Node_Mesh *nm = (Evas_3D_Node_Mesh *)ptr; + _pick_data_mesh_add(pick, &ray, nm->mesh, nm->frame, node); + } + } + + return EINA_TRUE; +} + +EAPI Eina_Bool +evas_3d_scene_pick(const Evas_3D_Scene *scene, Evas_Real x, Evas_Real y, + Evas_3D_Node **node, Evas_3D_Mesh **mesh, + Evas_Real *s, Evas_Real *t) +{ + /* TODO: Use H/W picking if availabe. */ + Evas_3D_Pick_Data data; + + data.x = ((x * 2.0) / (Evas_Real)scene->w) - 1.0; + data.y = (((scene->h - y - 1) * 2.0) / ((Evas_Real)scene->h)) - 1.0; + + data.picked = EINA_FALSE; + data.z = 1.0; + data.node = NULL; + data.mesh = NULL; + data.s = 0.0; + data.t = 0.0; + + /* Update the scene graph. */ + evas_3d_object_update((Evas_3D_Object *)&scene->base); + + evas_mat4_multiply(&data.matrix_vp, + &scene->camera_node->data.camera.camera->projection, + &scene->camera_node->data.camera.matrix_world_to_eye); + + evas_ray3_init(&data.ray_world, data.x, data.y, &data.matrix_vp); + + + /* Traverse tree while adding meshes into pick data structure. */ + evas_3d_node_tree_traverse(scene->root_node, EVAS_3D_TREE_TRAVERSE_LEVEL_ORDER, EINA_TRUE, + _node_pick, &data); + + if (!data.picked) + return EINA_FALSE; + + if (s) *s = data.s; + if (t) *t = data.t; + if (node) *node = data.node; + if (mesh) *mesh = data.mesh; + + return EINA_TRUE; +} diff --git a/src/lib/evas/canvas/evas_3d_texture.c b/src/lib/evas/canvas/evas_3d_texture.c new file mode 100644 index 0000000000..3b075e7eda --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_texture.c @@ -0,0 +1,480 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include "evas_common_private.h" +#include "evas_private.h" + +static inline void +_texture_proxy_set(Evas_3D_Texture *texture, Evas_Object *eo_src, Evas_Object_Protected_Data *src) +{ + EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, src->proxy, Evas_Object_Proxy_Data, proxy_src) + { + proxy_src->proxy_textures = eina_list_append(proxy_src->proxy_textures, texture); + proxy_src->redraw = EINA_TRUE; + } + EINA_COW_WRITE_END(evas_object_proxy_cow, src->proxy, proxy_src); + + texture->source = eo_src; +} + +static inline void +_texture_proxy_unset(Evas_3D_Texture *texture) +{ + Evas_Object_Protected_Data *src = eo_data_scope_get(texture->source, EVAS_OBJ_CLASS); + + EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, src->proxy, Evas_Object_Proxy_Data, proxy_src); + { + proxy_src->proxy_textures = eina_list_remove(proxy_src->proxy_textures, texture); + + if (eina_list_count(proxy_src->proxy_textures) == 0 && + eina_list_count(proxy_src->proxies) == 0 && + proxy_src->surface != NULL) + { + Evas_Public_Data *e = src->layer->evas; + e->engine.func->image_map_surface_free(e->engine.data.output, + proxy_src->surface); + proxy_src->surface = NULL; + } + + if (proxy_src->src_invisible) + { + proxy_src->src_invisible = EINA_FALSE; + src->changed_src_visible = EINA_TRUE; + evas_object_change(texture->source, src); + evas_object_smart_member_cache_invalidate(texture->source, + EINA_FALSE, EINA_FALSE, EINA_TRUE); + } + } + EINA_COW_WRITE_END(evas_object_proxy_cow, src->proxy, proxy_src); + + texture->source = NULL; +} + +static inline void +_texture_proxy_subrender(Evas_3D_Texture *texture) +{ + /* Code taken from _proxy_subrender() in file evas_object_image.c */ + + Evas_Public_Data *e = eo_data_scope_get(texture->base.evas, EVAS_CLASS); + Evas_Object_Protected_Data *source; + void *ctx, *image; + int w, h; + Eina_Bool is_image; + + if (!texture->source) + return; + + source = eo_data_scope_get(texture->source, EVAS_OBJ_CLASS); + + is_image = eo_isa(texture->source, EVAS_OBJ_IMAGE_CLASS); + + EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, source->proxy, Evas_Object_Proxy_Data, proxy_write) + { + proxy_write->redraw = EINA_FALSE; + + if (is_image) + { + image = source->func->engine_data_get(texture->source); + e->engine.func->image_size_get(e->engine.data.output, image, &w, &h); + } + else + { + w = source->cur->geometry.w; + h = source->cur->geometry.h; + } + + /* We need to redraw surface then */ + if ((proxy_write->surface) && + ((proxy_write->w != w) || (proxy_write->h != h))) + { + e->engine.func->image_map_surface_free(e->engine.data.output, + proxy_write->surface); + proxy_write->surface = NULL; + } + + /* FIXME: Hardcoded alpha 'on' */ + /* FIXME (cont): Should see if the object has alpha */ + if (!proxy_write->surface) + { + proxy_write->surface = e->engine.func->image_map_surface_new + (e->engine.data.output, w, h, 1); + if (!proxy_write->surface) goto end; + proxy_write->w = w; + proxy_write->h = h; + } + + ctx = e->engine.func->context_new(e->engine.data.output); + e->engine.func->context_color_set(e->engine.data.output, ctx, 0, 0, + 0, 0); + e->engine.func->context_render_op_set(e->engine.data.output, ctx, + EVAS_RENDER_COPY); + e->engine.func->rectangle_draw(e->engine.data.output, ctx, + proxy_write->surface, 0, 0, w, h, + EINA_FALSE); + e->engine.func->context_free(e->engine.data.output, ctx); + + ctx = e->engine.func->context_new(e->engine.data.output); + + if (is_image) + { + void *image = source->func->engine_data_get(texture->source); + + if (image) + { + int imagew, imageh; + e->engine.func->image_size_get(e->engine.data.output, image, + &imagew, &imageh); + e->engine.func->image_draw(e->engine.data.output, ctx, + proxy_write->surface, image, + 0, 0, imagew, imageh, 0, 0, w, h, 0, EINA_FALSE); + } + } + else + { + Evas_Proxy_Render_Data proxy_render_data = { + .eo_proxy = NULL, + .proxy_obj = NULL, + .eo_src = texture->source, + .source_clip = EINA_FALSE + }; + + evas_render_mapped(e, texture->source, source, ctx, proxy_write->surface, + -source->cur->geometry.x, + -source->cur->geometry.y, + 1, 0, 0, e->output.w, e->output.h, + &proxy_render_data +#ifdef REND_DBG + , 1 +#endif + , EINA_FALSE); + } + + e->engine.func->context_free(e->engine.data.output, ctx); + proxy_write->surface = e->engine.func->image_dirty_region + (e->engine.data.output, proxy_write->surface, 0, 0, w, h); + } + end: + EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, proxy_write); +} + +static void +_texture_fini(Evas_3D_Texture *texture) +{ + if (texture->engine_data) + { + Evas_Public_Data *e = eo_data_scope_get(texture->base.evas, EVAS_CLASS); + e->engine.func->texture_free(e->engine.data.output, texture->engine_data); + texture->engine_data = NULL; + } + + if (texture->materials) + { + eina_hash_free(texture->materials); + texture->materials = NULL; + } + + if (texture->source) + { + _texture_proxy_unset(texture); + } +} + +static void +_texture_free(Evas_3D_Object *obj) +{ + Evas_3D_Texture *texture = (Evas_3D_Texture *)obj; + + _texture_fini(texture); + free(texture); +} + +static Eina_Bool +_texture_material_change_notify(const Eina_Hash *hash EINA_UNUSED, const void *key, + void *data EINA_UNUSED, void *fdata) +{ + Evas_3D_Material *m = *(Evas_3D_Material **)key; + evas_3d_object_change(&m->base, EVAS_3D_STATE_MATERIAL_TEXTURE, (Evas_3D_Object *)fdata); + return EINA_TRUE; +} + +static void +_texture_change(Evas_3D_Object *obj, Evas_3D_State state EINA_UNUSED, + Evas_3D_Object *ref EINA_UNUSED) +{ + Evas_3D_Texture *texture = (Evas_3D_Texture *)obj; + + if (texture->materials) + eina_hash_foreach(texture->materials, _texture_material_change_notify, obj); +} + +static void +_texture_update(Evas_3D_Object *obj) +{ + Evas_3D_Texture *texture = (Evas_3D_Texture *)obj; + + if (texture->source) + { + Evas_Public_Data *e = eo_data_scope_get(texture->base.evas, EVAS_CLASS); + Evas_Object_Protected_Data *src = eo_data_scope_get(texture->source, EVAS_OBJ_CLASS); + + if (texture->engine_data == NULL) + { + texture->engine_data = e->engine.func->texture_new(e->engine.data.output); + + if (texture->engine_data == NULL) + { + ERR("Failed to create engine-side texture object."); + return; + } + } + + if (src->proxy->surface && !src->proxy->redraw) + { + e->engine.func->texture_image_set(e->engine.data.output, texture->engine_data, + src->proxy->surface); + return; + + } + + texture->proxy_rendering = EINA_TRUE; + _texture_proxy_subrender(texture); + + e->engine.func->texture_image_set(e->engine.data.output, texture->engine_data, + src->proxy->surface); + texture->proxy_rendering = EINA_FALSE; + } +} + +static const Evas_3D_Object_Func texture_func = +{ + _texture_free, + _texture_change, + _texture_update, +}; + +void +evas_3d_texture_material_add(Evas_3D_Texture *texture, Evas_3D_Material *material) +{ + int count = 0; + + if (texture->materials == NULL) + { + texture->materials = eina_hash_pointer_new(NULL); + + if (texture->materials == NULL) + { + ERR("Failed to create hash table."); + return; + } + } + else + count = (int)eina_hash_find(texture->materials, &material); + + /* Increase reference count or add new one if not exist. */ + eina_hash_set(texture->materials, &material, (const void *)(count + 1)); +} + +void +evas_3d_texture_material_del(Evas_3D_Texture *texture, Evas_3D_Material *material) +{ + int count = 0; + + if (texture->materials == NULL) + { + ERR("No material to delete."); + return; + } + + count = (int)eina_hash_find(texture->materials, &material); + + if (count == 1) + eina_hash_del(texture->materials, &material, NULL); + else + eina_hash_set(texture->materials, &material, (const void *)(count - 1)); +} + +Evas_3D_Texture * +evas_3d_texture_new(Evas *e) +{ + Evas_3D_Texture *texture = NULL; + + texture = (Evas_3D_Texture *)calloc(1, sizeof(Evas_3D_Texture)); + + if (texture == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + evas_3d_object_init(&texture->base, e, EVAS_3D_OBJECT_TYPE_TEXTURE, &texture_func); + return texture; +} + +EAPI Evas_3D_Texture * +evas_3d_texture_add(Evas *e) +{ + return evas_3d_texture_new(e); +} + +EAPI void +evas_3d_texture_del(Evas_3D_Texture *texture) +{ + evas_3d_object_unreference(&texture->base); +} + +EAPI Evas * +evas_3d_texture_evas_get(const Evas_3D_Texture *texture) +{ + return texture->base.evas; +} + +EAPI void +evas_3d_texture_data_set(Evas_3D_Texture *texture, Evas_3D_Color_Format color_format, + Evas_3D_Pixel_Format pixel_format, int w, int h, const void *data) +{ + Evas_Public_Data *e = eo_data_scope_get(texture->base.evas, EVAS_CLASS); + + if (texture->engine_data == NULL) + texture->engine_data = e->engine.func->texture_new(e->engine.data.output); + + e->engine.func->texture_data_set(e->engine.data.output, texture->engine_data, + color_format, pixel_format, w, h, data); + + evas_3d_object_change(&texture->base, EVAS_3D_STATE_TEXTURE_DATA, NULL); +} + +EAPI void +evas_3d_texture_file_set(Evas_3D_Texture *texture, const char *file, const char *key) +{ + Evas_Public_Data *e = eo_data_scope_get(texture->base.evas, EVAS_CLASS); + + if (texture->engine_data == NULL) + texture->engine_data = e->engine.func->texture_new(e->engine.data.output); + + e->engine.func->texture_file_set(e->engine.data.output, texture->engine_data, file, key); + evas_3d_object_change(&texture->base, EVAS_3D_STATE_TEXTURE_DATA, NULL); +} + +EAPI void +evas_3d_texture_source_set(Evas_3D_Texture *texture, Evas_Object *source) +{ + Evas_Object_Protected_Data *src; + + if (source == texture->source) + return; + + _texture_fini(texture); + + if (source == NULL) + return; + + if (evas_object_evas_get(source) != texture->base.evas) + { + ERR("Not matching canvas."); + return; + } + + src = eo_data_scope_get(source, EVAS_OBJ_CLASS); + + if (src->delete_me) + { + ERR("Source object is deleted."); + return; + } + + if (src->layer == NULL) + { + ERR("No evas surface associated with the source object."); + return; + } + + _texture_proxy_set(texture, source, src); + evas_3d_object_change(&texture->base, EVAS_3D_STATE_TEXTURE_DATA, NULL); +} + +EAPI void +evas_3d_texture_source_visible_set(Evas_3D_Texture *texture, Eina_Bool visible) +{ + Evas_Object_Protected_Data *src_obj; + + if (texture->source == NULL) + return; + + src_obj = eo_data_scope_get(texture->source, EVAS_OBJ_CLASS); + + if (src_obj->proxy->src_invisible == !visible) + return; + + EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, src_obj->proxy, Evas_Object_Proxy_Data, proxy_write) + proxy_write->src_invisible = !visible; + EINA_COW_WRITE_END(evas_object_proxy_cow, src_obj->proxy, proxy_write); + + src_obj->changed_src_visible = EINA_TRUE; + evas_object_smart_member_cache_invalidate(texture->source, EINA_FALSE, EINA_FALSE, EINA_TRUE); + evas_object_change(texture->source, src_obj); +} + +EAPI Eina_Bool +evas_3d_texture_source_visible_get(const Evas_3D_Texture *texture) +{ + Evas_Object_Protected_Data *src_obj; + + if (texture->source == NULL) + return EINA_FALSE; + + src_obj = eo_data_scope_get(texture->source, EVAS_OBJ_CLASS); + return !src_obj->proxy->src_invisible; +} + +EAPI Evas_3D_Color_Format +evas_3d_texture_color_format_get(const Evas_3D_Texture *texture) +{ + Evas_Public_Data *e = eo_data_scope_get(texture->base.evas, EVAS_CLASS); + Evas_3D_Color_Format format; + + e->engine.func->texture_color_format_get(e->engine.data.output, texture->engine_data, &format); + return format; +} + +EAPI void +evas_3d_texture_size_get(const Evas_3D_Texture *texture, int *w, int *h) +{ + Evas_Public_Data *e = eo_data_scope_get(texture->base.evas, EVAS_CLASS); + e->engine.func->texture_size_get(e->engine.data.output, texture->engine_data, w, h); +} + +EAPI void +evas_3d_texture_wrap_set(Evas_3D_Texture *texture, + Evas_3D_Wrap_Mode s, Evas_3D_Wrap_Mode t) +{ + Evas_Public_Data *e = eo_data_scope_get(texture->base.evas, EVAS_CLASS); + e->engine.func->texture_wrap_set(e->engine.data.output, texture->engine_data, s, t); + evas_3d_object_change(&texture->base, EVAS_3D_STATE_TEXTURE_WRAP, NULL); +} + +EAPI void +evas_3d_texture_wrap_get(const Evas_3D_Texture *texture, + Evas_3D_Wrap_Mode *s, Evas_3D_Wrap_Mode *t) +{ + Evas_Public_Data *e = eo_data_scope_get(texture->base.evas, EVAS_CLASS); + e->engine.func->texture_wrap_get(e->engine.data.output, texture->engine_data, s, t); +} + +EAPI void +evas_3d_texture_filter_set(Evas_3D_Texture *texture, + Evas_3D_Texture_Filter min, Evas_3D_Texture_Filter mag) +{ + Evas_Public_Data *e = eo_data_scope_get(texture->base.evas, EVAS_CLASS); + e->engine.func->texture_filter_set(e->engine.data.output, texture->engine_data, min, mag); + evas_3d_object_change(&texture->base, EVAS_3D_STATE_TEXTURE_FILTER, NULL); +} + +EAPI void +evas_3d_texture_filter_get(const Evas_3D_Texture *texture, + Evas_3D_Texture_Filter *min, Evas_3D_Texture_Filter *mag) +{ + Evas_Public_Data *e = eo_data_scope_get(texture->base.evas, EVAS_CLASS); + e->engine.func->texture_filter_get(e->engine.data.output, texture->engine_data, min, mag); +} diff --git a/src/lib/evas/canvas/evas_image.eo b/src/lib/evas/canvas/evas_image.eo index 20f9c3db95..e6f5e035cf 100644 --- a/src/lib/evas/canvas/evas_image.eo +++ b/src/lib/evas/canvas/evas_image.eo @@ -27,6 +27,7 @@ class Evas_Image (Evas_Object) double dpi; /*@ The new DPI resolution. */ } } + source_clip { set { /*@ @@ -996,6 +997,32 @@ class Evas_Image (Evas_Object) return int; } } + + 3d_scene { + set { + /* + @def evas_obj_image_3d_scene_set + @since 1.8 + + Set the 3D scene on an image object. + + @see evas_object_image_3d_scene_set + */ + } + get { + /* + @def evas_obj_image_3d_scene_get + @since 1.8 + + Get the 3D scene on an image object. + + @see evas_object_image_3d_scene_get + */ + } + values { + Evas_3D_Scene *scene; /*@ 3D scene on an image object. */ + } + } } methods { preload_begin { diff --git a/src/lib/evas/canvas/evas_object_image.c b/src/lib/evas/canvas/evas_object_image.c index 146265021e..52783f574a 100644 --- a/src/lib/evas/canvas/evas_object_image.c +++ b/src/lib/evas/canvas/evas_object_image.c @@ -79,6 +79,8 @@ struct _Evas_Object_Image_State Evas_Object *source; Evas_Map *defmap; + Evas_3D_Scene *scene; + union { const char *file; Eina_File *f; @@ -184,6 +186,9 @@ static void _proxy_unset(Evas_Object *proxy, Evas_Object_Protected_Data *obj, Ev static void _proxy_set(Evas_Object *proxy, Evas_Object *src); static void _proxy_error(Evas_Object *proxy, void *context, void *output, void *surface, int x, int y, Eina_Bool do_async); +static void _3d_set(Evas_Object *eo_obj, Evas_3D_Scene *scene); +static void _3d_unset(Evas_Object *eo_obj, Evas_Object_Protected_Data *image, Evas_Image_Data *o); + static const Evas_Object_Func object_func = { /* methods (compulsory) */ @@ -283,6 +288,7 @@ _evas_object_image_cleanup(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, eo_obj); } if (o->cur->source) _proxy_unset(eo_obj, obj, o); + if (o->cur->scene) _3d_unset(eo_obj, obj, o); } static Eina_Bool @@ -400,6 +406,8 @@ _image_init_set(const Eina_File *f, const char *file, const char *key, { if (o->cur->source) _proxy_unset(eo_obj, obj, o); + if (o->cur->scene) _3d_unset(eo_obj, obj, o); + EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write) { if (f) @@ -763,6 +771,30 @@ _evas_image_source_visible_get(Eo *eo_obj EINA_UNUSED, Evas_Image_Data *o) return visible; } + +EOLIAN static void +_evas_image_3d_scene_set(Eo *eo_obj, Evas_Image_Data *o, Evas_3D_Scene *scene) +{ + Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJ_CLASS); + + if (o->cur->scene == scene) + return; + + _evas_object_image_cleanup(eo_obj, obj, o); + + if (o->cur->u.file || o->cur->key) + evas_object_image_file_set(eo_obj, NULL, NULL); + + if (scene) _3d_set(eo_obj, scene); + else _3d_unset(eo_obj, obj, o); +} + +EOLIAN static Evas_3D_Scene * +_evas_image_3d_scene_get(Eo *eo_obj EINA_UNUSED, Evas_Image_Data *o) +{ + return o->cur->scene; +} + EOLIAN static void _evas_image_border_set(Eo *eo_obj, Evas_Image_Data *o, int l, int r, int t, int b) { @@ -2385,6 +2417,145 @@ _proxy_subrender(Evas *eo_e, Evas_Object *eo_source, Evas_Object *eo_proxy, Evas } static void +_3d_set(Evas_Object *eo_obj, Evas_3D_Scene *scene) +{ + Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJ_CLASS); + Evas_Image_Data *o = eo_data_scope_get(eo_obj, MY_CLASS); + + evas_object_image_file_set(eo_obj, NULL, NULL); + + EINA_COW_WRITE_BEGIN(evas_object_3d_cow, obj->data_3d, Evas_Object_3D_Data, data) + { + data->surface = NULL; + data->w = 0; + data->h = 0; + evas_3d_object_reference(&scene->base); + } + EINA_COW_WRITE_END(evas_object_3d_cow, obj->data_3d, data); + + EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write) + { + state_write->scene = scene; + } + EINA_COW_IMAGE_STATE_WRITE_END(o, state_write); + + scene->images = eina_list_append(scene->images, eo_obj); +} + +static void +_3d_unset(Evas_Object *eo_obj EINA_UNUSED, Evas_Object_Protected_Data *obj, Evas_Image_Data *o) +{ + if (!o->cur->scene) return; + + if (o->cur->scene) + { + EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write) + o->cur->scene->images = eina_list_remove(o->cur->scene->images, eo_obj); + evas_3d_object_unreference(&state_write->scene->base); + state_write->scene = NULL; + EINA_COW_IMAGE_STATE_WRITE_END(o, state_write); + } + + if (o->cur->defmap) + { + EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write) + { + evas_map_free(state_write->defmap); + state_write->defmap = NULL; + } + EINA_COW_IMAGE_STATE_WRITE_END(o, state_write); + } + + EINA_COW_WRITE_BEGIN(evas_object_3d_cow, obj->data_3d, Evas_Object_3D_Data, data) + { + if (data->surface) + { + obj->layer->evas->engine.func->image_free(obj->layer->evas->engine.data.output, + data->surface); + } + + data->surface = NULL; + data->w = 0; + data->h = 0; + } + EINA_COW_WRITE_END(evas_object_3d_cow, obj->data_3d, data); + +} + +static void +_3d_render(Evas *eo_e, Evas_Object *eo_obj EINA_UNUSED, Evas_Object_Protected_Data *obj, Evas_Image_Data *o EINA_UNUSED, Evas_3D_Scene *scene) +{ + Evas_Public_Data *e; + Eina_Bool need_native_set = EINA_FALSE; + Evas_3D_Scene_Data scene_data; + + if (scene == NULL) + return; + + if((scene->w == 0) || (scene->h == 0)) + return; + + e = eo_data_scope_get(eo_e, EVAS_CLASS); + + if (scene->surface != NULL) + { + int w, h; + + e->engine.func->drawable_size_get(e->engine.data.output, scene->surface, &w, &h); + + if ((w != scene->w) || (h != scene->h)) + { + e->engine.func->drawable_free(e->engine.data.output, scene->surface); + scene->surface = NULL; + need_native_set = EINA_TRUE; + } + } + + if (scene->surface == NULL) + { + /* TODO: Hard-coded alpha on. */ + scene->surface = e->engine.func->drawable_new(e->engine.data.output, + scene->w, scene->h, 1); + need_native_set = EINA_TRUE; + } + + EINA_COW_WRITE_BEGIN(evas_object_3d_cow, obj->data_3d, Evas_Object_3D_Data, data) + { + if (need_native_set) + { + data->surface = e->engine.func->image_drawable_set(e->engine.data.output, + data->surface, scene->surface); + } + + data->w = scene->w; + data->h = scene->h; + } + EINA_COW_WRITE_END(evas_object_3d_cow, obj->data_3d, data); + + evas_3d_scene_data_init(&scene_data); + + scene_data.bg_color = scene->bg_color; + scene_data.camera_node = scene->camera_node; + + /* Phase 1 - Update scene graph tree. */ + evas_3d_object_update(&scene->base); + + /* Phase 2 - Do frustum culling and get visible model nodes. */ + evas_3d_node_tree_traverse(scene->root_node, EVAS_3D_TREE_TRAVERSE_LEVEL_ORDER, EINA_TRUE, + evas_3d_node_mesh_collect, &scene_data); + + /* Phase 3 - Collect active light nodes in the scene graph tree. */ + evas_3d_node_tree_traverse(scene->root_node, EVAS_3D_TREE_TRAVERSE_ANY_ORDER, EINA_FALSE, + evas_3d_node_light_collect, &scene_data); + + /* Phase 5 - Draw the scene. */ + e->engine.func->drawable_scene_render(e->engine.data.output, scene->surface, &scene_data); + + /* Clean up temporary resources. */ + evas_3d_scene_data_fini(&scene_data); +} + +static void evas_object_image_unload(Evas_Object *eo_obj, Eina_Bool dirty) { Evas_Image_Data *o; @@ -2608,6 +2779,7 @@ evas_object_image_free(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj) } if (o->cur->key) eina_stringshare_del(o->cur->key); if (o->cur->source) _proxy_unset(eo_obj, obj, o); + if (o->cur->scene) _3d_unset(eo_obj, obj, o); if (obj->layer && obj->layer->evas) { if (o->engine_data) @@ -2857,7 +3029,17 @@ evas_object_image_render(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, v (o->cur->source ? eo_data_scope_get(o->cur->source, EVAS_OBJ_CLASS): NULL); - if (!o->cur->source) + + if (o->cur->scene) + { + _3d_render(obj->layer->evas->evas, eo_obj, obj, o, o->cur->scene); + pixels = obj->data_3d->surface; + imagew = obj->data_3d->w; + imageh = obj->data_3d->h; + uvw = imagew; + uvh = imageh; + } + else if (!o->cur->source) { pixels = evas_process_dirty_pixels(eo_obj, obj, o, output, o->engine_data); /* pixels = o->engine_data; */ @@ -3186,6 +3368,16 @@ evas_object_image_render_pre(Evas_Object *eo_obj, goto done; } } + else if (o->cur->scene) + { + Evas_3D_Scene *scene = o->cur->scene; + + if (evas_3d_object_dirty_get(&scene->base, EVAS_3D_STATE_ANY)) + { + evas_object_render_pre_prev_cur_add(&e->clip_changes, eo_obj, obj); + goto done; + } + } /* now figure what changed and add draw rects */ /* if it just became visible or invisible */ @@ -3710,7 +3902,20 @@ evas_object_image_is_inside(Evas_Object *eo_obj, (o->cur->source ? eo_data_scope_get(o->cur->source, EVAS_OBJ_CLASS): NULL); +#ifdef EVAS_3D + if (o->cur->scene) + { + _3d_render(obj->layer->evas->evas, eo_obj, obj, o, o->cur->scene); + pixels = obj->data_3d->surface; + imagew = obj->data_3d->w; + imageh = obj->data_3d->h; + uvw = imagew; + uvh = imageh; + } + else if (!o->cur->source) +#else if (!o->cur->source) +#endif { pixels = o->engine_data; imagew = o->cur->image.w; diff --git a/src/lib/evas/canvas/evas_object_main.c b/src/lib/evas/canvas/evas_object_main.c index 61b54bb600..6c1597d9ea 100644 --- a/src/lib/evas/canvas/evas_object_main.c +++ b/src/lib/evas/canvas/evas_object_main.c @@ -23,7 +23,7 @@ get_layer_objects(Evas_Layer *l) /* evas internal stuff */ static const Evas_Object_Proxy_Data default_proxy = { - NULL, NULL, 0, 0, NULL, 0, 0, 0, 0 + NULL, NULL, NULL, 0, 0, NULL, 0, 0, 0, 0 }; static const Evas_Object_Map_Data default_map = { { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, NULL, 0, 0, NULL, NULL @@ -39,16 +39,20 @@ Eina_Cow *evas_object_proxy_cow = NULL; Eina_Cow *evas_object_map_cow = NULL; Eina_Cow *evas_object_state_cow = NULL; +Eina_Cow *evas_object_3d_cow = NULL; + static Eina_Bool _init_cow(void) { - if (evas_object_map_cow && evas_object_proxy_cow && evas_object_state_cow) return EINA_TRUE; + if (evas_object_map_cow && evas_object_proxy_cow && evas_object_state_cow && evas_object_3d_cow) return EINA_TRUE; evas_object_proxy_cow = eina_cow_add("Evas Object Proxy", sizeof (Evas_Object_Proxy_Data), 8, &default_proxy, EINA_TRUE); evas_object_map_cow = eina_cow_add("Evas Object Map", sizeof (Evas_Object_Map_Data), 8, &default_map, EINA_TRUE); evas_object_state_cow = eina_cow_add("Evas Object State", sizeof (Evas_Object_Protected_State), 64, &default_state, EINA_FALSE); - if (!(evas_object_map_cow && evas_object_proxy_cow && evas_object_state_cow)) + evas_object_3d_cow = eina_cow_add("Evas Object 3D", sizeof (Evas_Object_3D_Data), 8, &default_proxy, EINA_TRUE); + + if (!(evas_object_map_cow && evas_object_proxy_cow && evas_object_state_cow && evas_object_3d_cow)) { eina_cow_del(evas_object_proxy_cow); eina_cow_del(evas_object_map_cow); @@ -56,6 +60,10 @@ _init_cow(void) evas_object_proxy_cow = NULL; evas_object_map_cow = NULL; evas_object_state_cow = NULL; + + eina_cow_del(evas_object_3d_cow); + evas_object_3d_cow = NULL; + return EINA_FALSE; } @@ -81,6 +89,7 @@ _evas_object_eo_base_constructor(Eo *eo_obj, Evas_Object_Protected_Data *obj) obj->map = eina_cow_alloc(evas_object_map_cow); obj->cur = eina_cow_alloc(evas_object_state_cow); obj->prev = eina_cow_alloc(evas_object_state_cow); + obj->data_3d = eina_cow_alloc(evas_object_3d_cow); } void @@ -188,6 +197,7 @@ evas_object_free(Evas_Object *eo_obj, int clean_layer) eina_cow_free(evas_object_map_cow, (const Eina_Cow_Data**) &obj->map); eina_cow_free(evas_object_state_cow, (const Eina_Cow_Data**) &obj->cur); eina_cow_free(evas_object_state_cow, (const Eina_Cow_Data**) &obj->prev); + eina_cow_free(evas_object_3d_cow, (const Eina_Cow_Data**) &obj->data_3d); eo_data_unref(eo_obj, obj->private_data); obj->private_data = NULL; @@ -204,6 +214,7 @@ evas_object_change(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj) Evas_Object_Protected_Data *obj2; Evas_Object *eo_obj2; Eina_Bool movch = EINA_FALSE; + Evas_3D_Texture *texture; if (!obj->layer) return; if (obj->layer->evas->nochange) return; @@ -233,6 +244,10 @@ evas_object_change(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj) if (!obj2) continue; evas_object_change(eo_obj2, obj2); } + EINA_LIST_FOREACH(obj->proxy->proxy_textures, l, texture) + { + evas_3d_object_change(&texture->base, EVAS_3D_STATE_TEXTURE_DATA, NULL); + } if (obj->smart.parent) { Evas_Object_Protected_Data *smart_parent = eo_data_scope_get(obj->smart.parent, MY_CLASS); @@ -662,6 +677,10 @@ _evas_object_eo_base_destructor(Eo *eo_obj, Evas_Object_Protected_Data *obj) else if (eo_isa(proxy, EVAS_OBJ_TEXT_CLASS)) eo_do(proxy, evas_obj_text_filter_source_set(NULL, eo_obj)); } + + while (obj->proxy->proxy_textures) + evas_3d_texture_source_set(obj->proxy->proxy_textures->data, NULL); + if (obj->cur->clipper) evas_object_clip_unset(eo_obj); evas_object_map_set(eo_obj, NULL); if (obj->is_smart) evas_object_smart_del(eo_obj); diff --git a/src/lib/evas/canvas/evas_render.c b/src/lib/evas/canvas/evas_render.c index 5ffe0f41c1..2db05ffd39 100644 --- a/src/lib/evas/canvas/evas_render.c +++ b/src/lib/evas/canvas/evas_render.c @@ -278,6 +278,17 @@ _evas_proxy_redraw_set(Evas_Public_Data *e, Evas_Object_Protected_Data *obj, //Update the proxies recursively. _evas_proxy_redraw_set(e, proxy, render); } + +#ifdef EVAS_3D + if (obj->proxy->proxy_textures) + { + /* Flag need redraw on proxy texture source */ + EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, obj->proxy, + Evas_Object_Proxy_Data, source) + source->redraw = EINA_TRUE; + EINA_COW_WRITE_END(evas_object_proxy_cow, obj->proxy, source); + } +#endif } static void @@ -298,7 +309,11 @@ _evas_render_phase1_direct(Evas_Public_Data *e, eina_array_data_get(active_objects, i); if (obj->changed) evas_object_clip_recalc(obj); +#ifdef EVAS_3D + if (!obj->proxy->proxies && !obj->proxy->proxy_textures) continue; +#else if (!obj->proxy->proxies) continue; +#endif if (obj->smart.smart) changed = evas_object_smart_changed_get(obj->object); @@ -325,7 +340,11 @@ _evas_render_phase1_direct(Evas_Public_Data *e, obj->func->render_pre(eo_obj, obj, obj->private_data); if (obj->proxy->redraw) _evas_render_prev_cur_clip_cache_add(e, obj); +#ifdef EVAS_3D + if (obj->proxy->proxies || obj->proxy->proxy_textures) +#else if (obj->proxy->proxies) +#endif { if (!obj->smart.smart || evas_object_smart_changed_get(eo_obj)) { diff --git a/src/lib/evas/include/evas_3d_private.h b/src/lib/evas/include/evas_3d_private.h new file mode 100644 index 0000000000..e30dacfec0 --- /dev/null +++ b/src/lib/evas/include/evas_3d_private.h @@ -0,0 +1,377 @@ +#ifndef EVAS_PRIVATE_H +# error You shall not include this header directly +#endif + +#include "Evas_3D.h" +#include "evas_3d_utils.h" + +#define EVAS_3D_VERTEX_ATTRIB_COUNT 5 +#define EVAS_3D_MATERIAL_ATTRIB_COUNT 5 + +typedef struct _Evas_3D_Object Evas_3D_Object; +typedef struct _Evas_3D_Scene_Data Evas_3D_Scene_Data; +typedef struct _Evas_3D_Vertex_Buffer Evas_3D_Vertex_Buffer; +typedef struct _Evas_3D_Mesh_Frame Evas_3D_Mesh_Frame; +typedef struct _Evas_3D_Node_Mesh Evas_3D_Node_Mesh; +typedef struct _Evas_3D_Object_Func Evas_3D_Object_Func; +typedef struct _Evas_3D_Pick_Data Evas_3D_Pick_Data; +typedef struct _Evas_3D_Interpolate_Vertex_Buffer Evas_3D_Interpolate_Vertex_Buffer; + +typedef Eina_Bool (*Evas_3D_Node_Func)(Evas_3D_Node *, void *data); + +typedef enum _Evas_3D_Object_Type +{ + EVAS_3D_OBJECT_TYPE_INVALID = 0, + EVAS_3D_OBJECT_TYPE_SCENE, + EVAS_3D_OBJECT_TYPE_NODE, + EVAS_3D_OBJECT_TYPE_CAMERA, + EVAS_3D_OBJECT_TYPE_LIGHT, + EVAS_3D_OBJECT_TYPE_MODEL, + EVAS_3D_OBJECT_TYPE_MESH, + EVAS_3D_OBJECT_TYPE_TEXTURE, + EVAS_3D_OBJECT_TYPE_MATERIAL, +} Evas_3D_Object_Type; + +typedef enum _Evas_3D_State +{ + EVAS_3D_STATE_MAX = 16, + + EVAS_3D_STATE_ANY = 0, + + EVAS_3D_STATE_SCENE_ROOT_NODE = 1, + EVAS_3D_STATE_SCENE_CAMERA_NODE, + EVAS_3D_STATE_SCENE_BACKGROUND_COLOR, + EVAS_3D_STATE_SCENE_SIZE, + + EVAS_3D_STATE_TEXTURE_DATA = 1, + EVAS_3D_STATE_TEXTURE_WRAP, + EVAS_3D_STATE_TEXTURE_FILTER, + + EVAS_3D_STATE_MATERIAL_ID = 1, + EVAS_3D_STATE_MATERIAL_COLOR, + EVAS_3D_STATE_MATERIAL_TEXTURE, + + EVAS_3D_STATE_MESH_VERTEX_COUNT = 1, + EVAS_3D_STATE_MESH_FRAME, + EVAS_3D_STATE_MESH_MATERIAL, + EVAS_3D_STATE_MESH_TRANSFORM, + EVAS_3D_STATE_MESH_VERTEX_DATA, + EVAS_3D_STATE_MESH_INDEX_DATA, + EVAS_3D_STATE_MESH_VERTEX_ASSEMBLY, + EVAS_3D_STATE_MESH_SHADE_MODE, + + EVAS_3D_STATE_CAMERA_PROJECTION = 1, + + EVAS_3D_STATE_LIGHT_AMBIENT = 1, + EVAS_3D_STATE_LIGHT_DIFFUSE, + EVAS_3D_STATE_LIGHT_SPECULAR, + EVAS_3D_STATE_LIGHT_SPOT_DIR, + EVAS_3D_STATE_LIGHT_SPOT_EXP, + EVAS_3D_STATE_LIGHT_SPOT_CUTOFF, + EVAS_3D_STATE_LIGHT_ATTENUATION, + + EVAS_3D_STATE_NODE_TRANSFORM = 1, + EVAS_3D_STATE_NODE_MESH_GEOMETRY, + EVAS_3D_STATE_NODE_MESH_MATERIAL, + EVAS_3D_STATE_NODE_MESH_FRAME, + EVAS_3D_STATE_NODE_MESH_SHADE_MODE, + EVAS_3D_STATE_NODE_MESH_MATERIAL_ID, + EVAS_3D_STATE_NODE_LIGHT, + EVAS_3D_STATE_NODE_CAMERA, + EVAS_3D_STATE_NODE_PARENT, + EVAS_3D_STATE_NODE_MEMBER, +} Evas_3D_State; + +typedef enum _Evas_3D_Node_Traverse_Type +{ + EVAS_3D_NODE_TRAVERSE_DOWNWARD, + EVAS_3D_NODE_TRAVERSE_UPWARD, +} Evas_3D_Node_Traverse_Type; + +typedef enum _Evas_3D_Tree_Traverse_Type +{ + EVAS_3D_TREE_TRAVERSE_PRE_ORDER, + EVAS_3D_TREE_TRAVERSE_ANY_ORDER = EVAS_3D_TREE_TRAVERSE_PRE_ORDER, + EVAS_3D_TREE_TRAVERSE_POST_ORDER, + EVAS_3D_TREE_TRAVERSE_LEVEL_ORDER, +} Evas_3D_Tree_Traverse_Type; + +struct _Evas_3D_Object_Func +{ + void (*free)(Evas_3D_Object *obj); + void (*change)(Evas_3D_Object *obj, Evas_3D_State state, Evas_3D_Object *ref); + void (*update)(Evas_3D_Object *obj); +}; + +struct _Evas_3D_Object +{ + Evas *evas; + + Evas_3D_Object_Type type; + int ref_count; + Evas_3D_Object_Func func; + + Eina_Bool dirty[EVAS_3D_STATE_MAX]; +}; + +struct _Evas_3D_Scene +{ + Evas_3D_Object base; + + Evas_3D_Node *root_node; + Evas_3D_Node *camera_node; + Evas_Color bg_color; + + void *surface; + int w, h; + Eina_List *images; +}; + +struct _Evas_3D_Node_Mesh +{ + Evas_3D_Node *node; + Evas_3D_Mesh *mesh; + int frame; +}; + +struct _Evas_3D_Node +{ + Evas_3D_Object base; + + Eina_List *members; + Evas_3D_Node *parent; + + Evas_Vec3 position; + Evas_Vec4 orientation; + Evas_Vec3 scale; + + Evas_Vec3 position_world; + Evas_Vec4 orientation_world; + Evas_Vec3 scale_world; + + Eina_Bool position_inherit; + Eina_Bool orientation_inherit; + Eina_Bool scale_inherit; + + Evas_Box3 aabb; + + Evas_3D_Node_Type type; + + /* Camera node. */ + union { + struct { + Evas_3D_Camera *camera; + Evas_Mat4 matrix_world_to_eye; + } camera; + + struct { + Evas_3D_Light *light; + Evas_Mat4 matrix_local_to_world; + } light; + + struct { + Eina_List *meshes; + Eina_Hash *node_meshes; + Evas_Mat4 matrix_local_to_world; + } mesh; + } data; + + /* Scene using this node as root. */ + Eina_Hash *scenes_root; + + /* Scene using this node as camera. */ + Eina_Hash *scenes_camera; +}; + +struct _Evas_3D_Camera +{ + Evas_3D_Object base; + + Evas_Mat4 projection; + Eina_Hash *nodes; +}; + +struct _Evas_3D_Light +{ + Evas_3D_Object base; + + Evas_Color ambient; + Evas_Color diffuse; + Evas_Color specular; + + Eina_Bool directional; + Evas_Real spot_exp; + Evas_Real spot_cutoff; + Evas_Real spot_cutoff_cos; + + Eina_Bool enable_attenuation; + Evas_Real atten_const; + Evas_Real atten_linear; + Evas_Real atten_quad; + + Eina_Hash *nodes; +}; + +struct _Evas_3D_Vertex_Buffer +{ + int element_count; + int stride; + void *data; + int size; + Eina_Bool owns_data; + Eina_Bool mapped; +}; + +struct _Evas_3D_Interpolate_Vertex_Buffer +{ + void *data0; + int stride0; + int size0; + + void *data1; + int stride1; + int size1; + + Evas_Real weight; +}; + +struct _Evas_3D_Mesh_Frame +{ + Evas_3D_Mesh *mesh; + + int frame; + Evas_3D_Material *material; + Evas_Box3 aabb; + + Evas_3D_Vertex_Buffer vertices[EVAS_3D_VERTEX_ATTRIB_COUNT]; +}; + +struct _Evas_3D_Mesh +{ + Evas_3D_Object base; + + Evas_3D_Shade_Mode shade_mode; + + int vertex_count; + int frame_count; + Eina_List *frames; + + Evas_3D_Index_Format index_format; + int index_count; + void *indices; + int index_size; + Eina_Bool owns_indices; + Eina_Bool index_mapped; + + Evas_3D_Vertex_Assembly assembly; + + Eina_Hash *nodes; +}; + +struct _Evas_3D_Texture +{ + Evas_3D_Object base; + + /* List of materials using this texture. */ + Eina_Hash *materials; + + /* Proxy data. */ + Evas_Object *source; + Eina_Bool proxy_rendering; + void *proxy_surface; + + /* Engine-side object. */ + void *engine_data; +}; + +struct _Evas_3D_Material +{ + Evas_3D_Object base; + + struct { + Eina_Bool enable; + Evas_Color color; + Evas_3D_Texture *texture; + } attribs[EVAS_3D_MATERIAL_ATTRIB_COUNT]; + + Evas_Real shininess; + + Eina_Hash *meshes; +}; + +struct _Evas_3D_Scene_Data +{ + Evas_Color bg_color; + Evas_3D_Node *camera_node; + Eina_List *light_nodes; + Eina_List *mesh_nodes; +}; + +struct _Evas_3D_Pick_Data +{ + /* Input */ + Evas_Real x, y; + Evas_Mat4 matrix_vp; + Evas_Ray3 ray_world; + + /* Output */ + Eina_Bool picked; + Evas_Real z; + Evas_3D_Node *node; + Evas_3D_Mesh *mesh; + Evas_Real u, v; + Evas_Real s, t; +}; + +/* Object generic functions. */ +void evas_3d_object_init(Evas_3D_Object *obj, Evas *e, Evas_3D_Object_Type type, const Evas_3D_Object_Func *func); +Evas *evas_3d_object_evas_get(const Evas_3D_Object *obj); +Evas_3D_Object_Type evas_3d_object_type_get(const Evas_3D_Object *obj); + +void evas_3d_object_reference(Evas_3D_Object *obj); +void evas_3d_object_unreference(Evas_3D_Object *obj); +int evas_3d_object_reference_count_get(const Evas_3D_Object *obj); + +void evas_3d_object_change(Evas_3D_Object *obj, Evas_3D_State state, Evas_3D_Object *ref); +Eina_Bool evas_3d_object_dirty_get(const Evas_3D_Object *obj, Evas_3D_State state); +void evas_3d_object_update(Evas_3D_Object *obj); +void evas_3d_object_update_done(Evas_3D_Object *obj); + +/* Node functions. */ +void evas_3d_node_traverse(Evas_3D_Node *from, Evas_3D_Node *to, Evas_3D_Node_Traverse_Type type, Eina_Bool skip, Evas_3D_Node_Func func, void *data); +void evas_3d_node_tree_traverse(Evas_3D_Node *root, Evas_3D_Tree_Traverse_Type type, Eina_Bool skip, Evas_3D_Node_Func func, void *data); +Eina_Bool evas_3d_node_mesh_collect(Evas_3D_Node *node, void *data); +Eina_Bool evas_3d_node_light_collect(Evas_3D_Node *node, void *data); + +void evas_3d_node_scene_root_add(Evas_3D_Node *node, Evas_3D_Scene *scene); +void evas_3d_node_scene_root_del(Evas_3D_Node *node, Evas_3D_Scene *scene); +void evas_3d_node_scene_camera_add(Evas_3D_Node *node, Evas_3D_Scene *scene); +void evas_3d_node_scene_camera_del(Evas_3D_Node *node, Evas_3D_Scene *scene); + +/* Camera functions. */ +void evas_3d_camera_node_add(Evas_3D_Camera *camera, Evas_3D_Node *node); +void evas_3d_camera_node_del(Evas_3D_Camera *camera, Evas_3D_Node *node); + +/* Light functions. */ +void evas_3d_light_node_add(Evas_3D_Light *light, Evas_3D_Node *node); +void evas_3d_light_node_del(Evas_3D_Light *light, Evas_3D_Node *node); + +/* Mesh functions. */ +void evas_3d_mesh_node_add(Evas_3D_Mesh *mesh, Evas_3D_Node *node); +void evas_3d_mesh_node_del(Evas_3D_Mesh *mesh, Evas_3D_Node *node); + +void evas_3d_mesh_interpolate_vertex_buffer_get(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib, Evas_3D_Vertex_Buffer *buffer0, Evas_3D_Vertex_Buffer *buffer1, Evas_Real *weight); + +void evas_3d_mesh_file_md2_set(Evas_3D_Mesh *mesh, const char *file); + +/* Texture functions. */ +void evas_3d_texture_material_add(Evas_3D_Texture *texture, Evas_3D_Material *material); +void evas_3d_texture_material_del(Evas_3D_Texture *texture, Evas_3D_Material *material); + +/* Material functions. */ +void evas_3d_material_mesh_add(Evas_3D_Material *material, Evas_3D_Mesh *mesh); +void evas_3d_material_mesh_del(Evas_3D_Material *material, Evas_3D_Mesh *mesh); + +/* Scene functions. */ +void evas_3d_scene_data_init(Evas_3D_Scene_Data *data); +void evas_3d_scene_data_fini(Evas_3D_Scene_Data *data); diff --git a/src/lib/evas/include/evas_3d_utils.h b/src/lib/evas/include/evas_3d_utils.h new file mode 100644 index 0000000000..8933c45e7a --- /dev/null +++ b/src/lib/evas/include/evas_3d_utils.h @@ -0,0 +1,1539 @@ +#ifndef EVAS_PRIVATE_H +# error You shall not include this header directly +#endif + +#include <math.h> + +#define DEGREE_TO_RADIAN(x) (((x) * M_PI) / 180.0) +#define EVAS_MATRIX_IS_IDENTITY 0x00000001 + +typedef struct _Evas_Color +{ + Evas_Real r; + Evas_Real g; + Evas_Real b; + Evas_Real a; +} Evas_Color; + +typedef struct _Evas_Vec2 +{ + Evas_Real x; + Evas_Real y; +} Evas_Vec2; + +typedef struct _Evas_Vec3 +{ + Evas_Real x; + Evas_Real y; + Evas_Real z; +} Evas_Vec3; + +typedef struct _Evas_Vec4 +{ + Evas_Real x; + Evas_Real y; + Evas_Real z; + Evas_Real w; +} Evas_Vec4; + +typedef struct _Evas_Mat2 +{ + Evas_Real m[4]; + int flags; +} Evas_Mat2; + +typedef struct _Evas_Mat3 +{ + Evas_Real m[9]; + int flags; +} Evas_Mat3; + +typedef struct _Evas_Mat4 +{ + Evas_Real m[16]; + int flags; +} Evas_Mat4; + +typedef struct _Evas_Box2 +{ + Evas_Vec2 p0; + Evas_Vec2 p1; +} Evas_Box2; + +typedef struct _Evas_Box3 +{ + Evas_Vec3 p0; + Evas_Vec3 p1; +} Evas_Box3; + +typedef struct _Evas_Triangle3 +{ + Evas_Vec3 p0; + Evas_Vec3 p1; + Evas_Vec3 p2; +} Evas_Triangle3; + +typedef struct _Evas_Ray3 +{ + Evas_Vec3 org; + Evas_Vec3 dir; +} Evas_Ray3; + +/* 2D vector */ +static inline void +evas_vec2_set(Evas_Vec2 *dst, Evas_Real x, Evas_Real y) +{ + dst->x = x; + dst->y = y; +} + +static inline void +evas_vec2_array_set(Evas_Vec2 *dst, const Evas_Real *v) +{ + dst->x = v[0]; + dst->y = v[1]; +} + +static inline void +evas_vec2_copy(Evas_Vec2 *dst, const Evas_Vec2 *src) +{ + dst->x = src->x; + dst->y = src->y; +} + +static inline void +evas_vec2_negate(Evas_Vec2 *out, const Evas_Vec2 *v) +{ + out->x = -v->x; + out->y = -v->y; +} + +static inline void +evas_vec2_add(Evas_Vec2 *out, const Evas_Vec2 *a, const Evas_Vec2 *b) +{ + out->x = a->x + b->x; + out->y = a->y + b->y; +} + +static inline void +evas_vec2_subtract(Evas_Vec2 *out, const Evas_Vec2 *a, const Evas_Vec2 *b) +{ + out->x = a->x - b->x; + out->y = a->y - b->y; +} + +static inline void +evas_vec2_scale(Evas_Vec2 *out, const Evas_Vec2 *v, Evas_Real scale) +{ + out->x = scale * v->x; + out->y = scale * v->y; +} + +static inline Evas_Real +evas_vec2_dot_product(const Evas_Vec2 *a, const Evas_Vec2 *b) +{ + return (a->x * b->x) + (a->y * b->y); +} + +static inline Evas_Real +evas_vec2_length_get(const Evas_Vec2 *v) +{ + return (Evas_Real)sqrt((double)((v->x * v->x) + (v->y * v->y))); +} + +static inline Evas_Real +evas_vec2_length_square_get(const Evas_Vec2 *v) +{ + return (v->x * v->x) + (v->y * v->y); +} + +static inline Evas_Real +evas_vec2_distance_get(const Evas_Vec2 *a, const Evas_Vec2 *b) +{ + Evas_Vec2 v; + + evas_vec2_subtract(&v, a, b); + return evas_vec2_length_get(&v); +} + +static inline Evas_Real +evas_vec2_distance_square_get(const Evas_Vec2 *a, const Evas_Vec2 *b) +{ + Evas_Vec2 v; + + evas_vec2_subtract(&v, a, b); + return evas_vec2_length_square_get(&v); +} + +static inline void +evas_vec2_normalize(Evas_Vec2 *out, const Evas_Vec2 *v) +{ + /* Assume "v" is not a zero vector */ + evas_vec2_scale(out, v, 1.0 / evas_vec2_length_get(v)); +} + +static inline void +evas_vec2_transform(Evas_Vec2 *out, const Evas_Mat2 *m, const Evas_Vec2 *v) +{ + Evas_Vec2 tmp; + + tmp.x = (m->m[0] * v->x) + (m->m[2] * v->y); + tmp.y = (m->m[1] * v->x) + (m->m[3] * v->y); + + evas_vec2_copy(out, &tmp); +} + +static inline void +evas_vec2_homogeneous_position_transform(Evas_Vec2 *out, const Evas_Mat3 *m, const Evas_Vec2 *v) +{ + Evas_Vec2 tmp; + + tmp.x = (m->m[0] * v->x) + (m->m[3] * v->y) + m->m[6]; + tmp.y = (m->m[1] * v->x) + (m->m[4] * v->y) + m->m[7]; + + evas_vec2_scale(out, &tmp, 1.0 / ((m->m[2] * v->x) + (m->m[5] * v->y) + m->m[8])); +} + +static inline void +evas_vec2_homogeneous_direction_transform(Evas_Vec2 *out, const Evas_Mat3 *m, const Evas_Vec2 *v) +{ + Evas_Vec2 tmp; + + tmp.x = (m->m[0] * v->x) + (m->m[3] * v->y); + tmp.y = (m->m[1] * v->x) + (m->m[4] * v->y); + + evas_vec2_copy(out, &tmp); +} + +/* 3D vector */ +static inline void +evas_vec3_set(Evas_Vec3 *dst, Evas_Real x, Evas_Real y, Evas_Real z) +{ + dst->x = x; + dst->y = y; + dst->z = z; +} + +static inline void +evas_vec3_array_set(Evas_Vec3 *dst, const Evas_Real *v) +{ + dst->x = v[0]; + dst->y = v[1]; + dst->z = v[2]; +} + +static inline void +evas_vec3_copy(Evas_Vec3 *dst, const Evas_Vec3 *src) +{ + dst->x = src->x; + dst->y = src->y; + dst->z = src->z; +} + +static inline void +evas_vec3_negate(Evas_Vec3 *out, const Evas_Vec3 *v) +{ + out->x = -v->x; + out->y = -v->y; + out->z = -v->z; +} + +static inline void +evas_vec3_add(Evas_Vec3 *out, const Evas_Vec3 *a, const Evas_Vec3 *b) +{ + out->x = a->x + b->x; + out->y = a->y + b->y; + out->z = a->z + b->z; +} + +static inline void +evas_vec3_subtract(Evas_Vec3 *out, const Evas_Vec3 *a, const Evas_Vec3 *b) +{ + out->x = a->x - b->x; + out->y = a->y - b->y; + out->z = a->z - b->z; +} + +static inline void +evas_vec3_scale(Evas_Vec3 *out, const Evas_Vec3 *v, Evas_Real scale) +{ + out->x = scale * v->x; + out->y = scale * v->y; + out->z = scale * v->z; +} + +static inline void +evas_vec3_multiply(Evas_Vec3 *out, const Evas_Vec3 *a, const Evas_Vec3 *b) +{ + out->x = a->x * b->x; + out->y = a->y * b->y; + out->z = a->z * b->z; +} + +static inline Evas_Real +evas_vec3_dot_product(const Evas_Vec3 *a, const Evas_Vec3 *b) +{ + return (a->x * b->x) + (a->y * b->y) + (a->z * b->z); +} + +static inline void +evas_vec3_cross_product(Evas_Vec3 *out, const Evas_Vec3 *a, const Evas_Vec3 *b) +{ + Evas_Vec3 tmp; + + tmp.x = a->y * b->z - a->z * b->y; + tmp.y = a->z * b->x - a->x * b->z; + tmp.z = a->x * b->y - a->y * b->x; + + evas_vec3_copy(out, &tmp); +} + +static inline Evas_Real +evas_vec3_length_get(const Evas_Vec3 *v) +{ + return (Evas_Real)sqrt((double)((v->x * v->x) + (v->y * v->y) + (v->z * v->z))); +} + +static inline Evas_Real +evas_vec3_length_square_get(const Evas_Vec3 *v) +{ + return (v->x * v->x) + (v->y * v->y) + (v->z * v->z); +} + +static inline Evas_Real +evas_vec3_distance_get(const Evas_Vec3 *a, const Evas_Vec3 *b) +{ + Evas_Vec3 v; + + evas_vec3_subtract(&v, a, b); + return evas_vec3_length_get(&v); +} + +static inline Evas_Real +evas_vec3_distance_square_get(const Evas_Vec3 *a, const Evas_Vec3 *b) +{ + Evas_Vec3 v; + + evas_vec3_subtract(&v, a, b); + return evas_vec3_length_square_get(&v); +} + +static inline void +evas_vec3_normalize(Evas_Vec3 *out, const Evas_Vec3 *v) +{ + /* Assume "v" is not a zero vector */ + evas_vec3_scale(out, v, 1.0 / evas_vec3_length_get(v)); +} + +static inline void +evas_vec3_transform(Evas_Vec3 *out, const Evas_Vec3 *v, const Evas_Mat3 *m) +{ + Evas_Vec3 tmp; + + if (m->flags & EVAS_MATRIX_IS_IDENTITY) + { + evas_vec3_copy(out, v); + return; + } + + tmp.x = (m->m[0] * v->x) + (m->m[3] * v->y) + (m->m[6] * v->z); + tmp.y = (m->m[1] * v->x) + (m->m[4] * v->y) + (m->m[7] * v->z); + tmp.z = (m->m[2] * v->x) + (m->m[5] * v->y) + (m->m[8] * v->z); + + evas_vec3_copy(out, &tmp); +} + +static inline void +evas_vec3_homogeneous_position_transform(Evas_Vec3 *out, const Evas_Vec3 *v, const Evas_Mat4 *m) +{ + Evas_Vec3 tmp; + + if (m->flags & EVAS_MATRIX_IS_IDENTITY) + { + evas_vec3_copy(out, v); + return; + } + + tmp.x = (m->m[0] * v->x) + (m->m[4] * v->y) + (m->m[8] * v->z) + m->m[12]; + tmp.y = (m->m[1] * v->x) + (m->m[5] * v->y) + (m->m[9] * v->z) + m->m[13]; + tmp.z = (m->m[2] * v->x) + (m->m[6] * v->y) + (m->m[10] * v->z) + m->m[14]; + + evas_vec3_scale(out, &tmp, + 1.0 / ((m->m[3] * v->x) + (m->m[7] * v->y) + (m->m[11] * v->z) + m->m[15])); +} + +static inline void +evas_vec3_homogeneous_direction_transform(Evas_Vec3 *out, const Evas_Vec3 *v, const Evas_Mat4 *m) +{ + Evas_Vec3 tmp; + + if (m->flags & EVAS_MATRIX_IS_IDENTITY) + { + evas_vec3_copy(out, v); + return; + } + + tmp.x = (m->m[0] * v->x) + (m->m[4] * v->y) + (m->m[8] * v->z); + tmp.y = (m->m[1] * v->x) + (m->m[5] * v->y) + (m->m[9] * v->z); + tmp.z = (m->m[2] * v->x) + (m->m[6] * v->y) + (m->m[10] * v->z); + + evas_vec3_copy(out, &tmp); +} + +static inline void +evas_vec3_quaternion_rotate(Evas_Vec3 *out, const Evas_Vec3 *v, const Evas_Vec4 *q) +{ + Evas_Vec3 uv, uuv; + Evas_Vec3 axis; + + evas_vec3_set(&axis, q->x, q->y, q->z); + + evas_vec3_cross_product(&uv, &axis, v); + evas_vec3_cross_product(&uuv, &axis, &uv); + + evas_vec3_scale(&uv, &uv, 2.0 * q->w); + evas_vec3_scale(&uuv, &uuv, 2.0); + + out->x = v->x + uv.x + uuv.x; + out->y = v->y + uv.y + uuv.y; + out->z = v->z + uv.z + uuv.z; +} + +/* 4D vector */ +static inline void +evas_vec4_set(Evas_Vec4 *dst, Evas_Real x, Evas_Real y, Evas_Real z, Evas_Real w) +{ + dst->x = x; + dst->y = y; + dst->z = z; + dst->w = w; +} + +static inline void +evas_vec4_array_set(Evas_Vec4 *dst, const Evas_Real *v) +{ + dst->x = v[0]; + dst->y = v[1]; + dst->z = v[2]; + dst->w = v[3]; +} + +static inline void +evas_vec4_copy(Evas_Vec4 *dst, const Evas_Vec4 *src) +{ + dst->x = src->x; + dst->y = src->y; + dst->z = src->z; + dst->w = src->w; +} + +static inline void +evas_vec4_homogeneous_regulate(Evas_Vec4 *out, const Evas_Vec4 *v) +{ + if (v->w != 0.0) + { + Evas_Real scale = 1.0 / v->w; + + out->x = v->x * scale; + out->y = v->y * scale; + out->z = v->z * scale; + out->w = 1.0; + } +} + +static inline void +evas_vec4_negate(Evas_Vec4 *out, const Evas_Vec4 *v) +{ + out->x = -v->x; + out->y = -v->y; + out->z = -v->z; + out->w = -v->w; +} + +static inline void +evas_vec4_add(Evas_Vec4 *out, const Evas_Vec4 *a, const Evas_Vec4 *b) +{ + out->x = a->x + b->x; + out->y = a->y + b->y; + out->z = a->z + b->z; + out->w = a->w + b->w; +} + +static inline void +evas_vec4_subtract(Evas_Vec4 *out, const Evas_Vec4 *a, const Evas_Vec4 *b) +{ + out->x = a->x - b->x; + out->y = a->y - b->y; + out->z = a->z - b->z; + out->w = a->w - b->w; +} + +static inline void +evas_vec4_scale(Evas_Vec4 *out, const Evas_Vec4 *v, Evas_Real scale) +{ + out->x = scale * v->x; + out->y = scale * v->y; + out->z = scale * v->z; + out->w = scale * v->w; +} + +static inline void +evas_vec4_multiply(Evas_Vec4 *out, const Evas_Vec4 *a, const Evas_Vec4 *b) +{ + out->x = a->x * b->x; + out->y = a->y * b->y; + out->z = a->z * b->z; + out->w = a->w * b->w; +} + +static inline Evas_Real +evas_vec4_length_get(const Evas_Vec4 *v) +{ + return (Evas_Real)sqrt((double)((v->x * v->x) + (v->y * v->y) + + (v->z * v->z) + (v->w + v->w))); +} + +static inline Evas_Real +evas_vec4_length_square_get(const Evas_Vec4 *v) +{ + return (v->x * v->x) + (v->y * v->y) + (v->z * v->z) + (v->w * v->w); +} + +static inline Evas_Real +evas_vec4_distance_get(const Evas_Vec4 *a, const Evas_Vec4 *b) +{ + Evas_Vec4 v; + + evas_vec4_subtract(&v, a, b); + return evas_vec4_length_get(&v); +} + +static inline Evas_Real +evas_vec4_distance_square_get(const Evas_Vec4 *a, const Evas_Vec4 *b) +{ + Evas_Vec4 v; + + evas_vec4_subtract(&v, a, b); + return evas_vec4_length_square_get(&v); +} + +static inline void +evas_vec4_normalize(Evas_Vec4 *out, const Evas_Vec4 *v) +{ + /* Assume "v" is not a zero vector */ + evas_vec4_scale(out, v, 1.0 / evas_vec4_length_get(v)); +} + +static inline void +evas_vec4_transform(Evas_Vec4 *out, const Evas_Vec4 *v, const Evas_Mat4 *m) +{ + Evas_Vec4 tmp; + + if (m->flags & EVAS_MATRIX_IS_IDENTITY) + { + evas_vec4_copy(out, v); + return; + } + + tmp.x = (m->m[0] * v->x) + (m->m[4] * v->y) + (m->m[ 8] * v->z) + (m->m[12] * v->w); + tmp.y = (m->m[1] * v->x) + (m->m[5] * v->y) + (m->m[ 9] * v->z) + (m->m[13] * v->w); + tmp.z = (m->m[2] * v->x) + (m->m[6] * v->y) + (m->m[10] * v->z) + (m->m[14] * v->w); + tmp.w = (m->m[3] * v->x) + (m->m[7] * v->y) + (m->m[11] * v->z) + (m->m[15] * v->w); + + evas_vec4_copy(out, &tmp); +} + +static inline void +evas_vec3_homogeneous_position_set(Evas_Vec3 *out, const Evas_Vec4 *v) +{ + /* Assume "v" is a positional vector. (v->w != 0.0) */ + Evas_Real h = 1.0 / v->w; + + out->x = v->x * h; + out->y = v->y * h; + out->z = v->z * h; +} + +static inline void +evas_vec3_homogeneous_direction_set(Evas_Vec3 *out, const Evas_Vec4 *v) +{ + /* Assume "v" is a directional vector. (v->w == 0.0) */ + out->x = v->x; + out->y = v->y; + out->z = v->z; +} + +static inline void +evas_vec4_homogeneous_position_set(Evas_Vec4 *out, const Evas_Vec3 *v) +{ + out->x = v->x; + out->y = v->y; + out->z = v->z; + out->w = 1.0; +} + +static inline void +evas_vec4_homogeneous_direction_set(Evas_Vec4 *out, const Evas_Vec3 *v) +{ + out->x = v->x; + out->y = v->y; + out->z = v->z; + out->w = 0.0; +} + +/* 4x4 matrix */ +static inline void +evas_mat4_identity_set(Evas_Mat4 *m) +{ + m->m[0] = 1.0; + m->m[1] = 0.0; + m->m[2] = 0.0; + m->m[3] = 0.0; + + m->m[4] = 0.0; + m->m[5] = 1.0; + m->m[6] = 0.0; + m->m[7] = 0.0; + + m->m[8] = 0.0; + m->m[9] = 0.0; + m->m[10] = 1.0; + m->m[11] = 0.0; + + m->m[12] = 0.0; + m->m[13] = 0.0; + m->m[14] = 0.0; + m->m[15] = 1.0; + + m->flags = EVAS_MATRIX_IS_IDENTITY; +} + +static inline void +evas_mat4_array_set(Evas_Mat4 *m, const Evas_Real *v) +{ + memcpy(&m->m[0], v, sizeof(Evas_Real) * 16); + m->flags = 0; +} + +static inline void +evas_mat4_copy(Evas_Mat4 *dst, const Evas_Mat4 *src) +{ + memcpy(dst, src, sizeof(Evas_Mat4)); +} + +static inline void +evas_mat4_nocheck_multiply(Evas_Mat4 *out, const Evas_Mat4 *mat_a, const Evas_Mat4 *mat_b) +{ + Evas_Real *d = &out->m[0]; + const Evas_Real *a = &mat_a->m[0]; + const Evas_Real *b = &mat_b->m[0]; + + if (mat_a->flags & EVAS_MATRIX_IS_IDENTITY) + { + evas_mat4_copy(out, mat_b); + return; + } + + if (mat_b->flags & EVAS_MATRIX_IS_IDENTITY) + { + evas_mat4_copy(out, mat_a); + return; + } + + d[ 0] = a[ 0] * b[ 0] + a[ 4] * b[ 1] + a[ 8] * b[ 2] + a[12] * b [3]; + d[ 4] = a[ 0] * b[ 4] + a[ 4] * b[ 5] + a[ 8] * b[ 6] + a[12] * b [7]; + d[ 8] = a[ 0] * b[ 8] + a[ 4] * b[ 9] + a[ 8] * b[10] + a[12] * b[11]; + d[12] = a[ 0] * b[12] + a[ 4] * b[13] + a[ 8] * b[14] + a[12] * b[15]; + + d[ 1] = a[ 1] * b[ 0] + a[ 5] * b[ 1] + a[ 9] * b[ 2] + a[13] * b [3]; + d[ 5] = a[ 1] * b[ 4] + a[ 5] * b[ 5] + a[ 9] * b[ 6] + a[13] * b [7]; + d[ 9] = a[ 1] * b[ 8] + a[ 5] * b[ 9] + a[ 9] * b[10] + a[13] * b[11]; + d[13] = a[ 1] * b[12] + a[ 5] * b[13] + a[ 9] * b[14] + a[13] * b[15]; + + d[ 2] = a[ 2] * b[ 0] + a[ 6] * b[ 1] + a[10] * b[ 2] + a[14] * b [3]; + d[ 6] = a[ 2] * b[ 4] + a[ 6] * b[ 5] + a[10] * b[ 6] + a[14] * b [7]; + d[10] = a[ 2] * b[ 8] + a[ 6] * b[ 9] + a[10] * b[10] + a[14] * b[11]; + d[14] = a[ 2] * b[12] + a[ 6] * b[13] + a[10] * b[14] + a[14] * b[15]; + + d[ 3] = a[ 3] * b[ 0] + a[ 7] * b[ 1] + a[11] * b[ 2] + a[15] * b [3]; + d[ 7] = a[ 3] * b[ 4] + a[ 7] * b[ 5] + a[11] * b[ 6] + a[15] * b [7]; + d[11] = a[ 3] * b[ 8] + a[ 7] * b[ 9] + a[11] * b[10] + a[15] * b[11]; + d[15] = a[ 3] * b[12] + a[ 7] * b[13] + a[11] * b[14] + a[15] * b[15]; + + out->flags = 0; +} + +static inline void +evas_mat4_multiply(Evas_Mat4 *out, const Evas_Mat4 *mat_a, const Evas_Mat4 *mat_b) +{ + if (out != mat_a && out != mat_b) + { + evas_mat4_nocheck_multiply(out, mat_a, mat_b); + } + else + { + Evas_Mat4 result; + + evas_mat4_nocheck_multiply(&result, mat_a, mat_b); + evas_mat4_copy(out, &result); + } +} + +static inline void +evas_mat4_look_at_set(Evas_Mat4 *m, + const Evas_Vec3 *pos, const Evas_Vec3 *center, const Evas_Vec3 *up) +{ + Evas_Vec3 x, y, z; + + evas_vec3_subtract(&z, pos, center); + evas_vec3_normalize(&z, &z); + + evas_vec3_cross_product(&x, up, &z); + evas_vec3_normalize(&x, &x); + + evas_vec3_cross_product(&y, &z, &x); + evas_vec3_normalize(&y, &y); + + m->m[ 0] = x.x; + m->m[ 1] = y.x; + m->m[ 2] = z.x; + m->m[ 3] = 0.0; + + m->m[ 4] = x.y; + m->m[ 5] = y.y; + m->m[ 6] = z.y; + m->m[ 7] = 0.0; + + m->m[ 8] = x.z; + m->m[ 9] = y.z; + m->m[10] = z.z; + m->m[11] = 0.0; + + m->m[12] = -evas_vec3_dot_product(&x, pos); + m->m[13] = -evas_vec3_dot_product(&y, pos); + m->m[14] = -evas_vec3_dot_product(&z, pos); + m->m[15] = 1.0; + + m->flags = 0; +} + +static inline void +evas_mat4_frustum_set(Evas_Mat4 *m, + Evas_Real left, Evas_Real right, Evas_Real bottom, Evas_Real top, + Evas_Real near, Evas_Real far) +{ + Evas_Real w = right - left; + Evas_Real h = top - bottom; + Evas_Real depth = near - far; + Evas_Real near_2 = 2.0f * near; + + m->m[ 0] = near_2 / w; + m->m[ 1] = 0.0f; + m->m[ 2] = 0.0f; + m->m[ 3] = 0.0f; + + m->m[ 4] = 0.0f; + m->m[ 5] = near_2 / h; + m->m[ 6] = 0.0f; + m->m[ 7] = 0.0f; + + m->m[ 8] = (right + left) / w; + m->m[ 9] = (top + bottom) / h; + m->m[10] = (far + near) / depth; + m->m[11] = -1.0f; + + m->m[12] = 0.0f; + m->m[13] = 0.0f; + m->m[14] = near_2 * far / depth; + m->m[15] = 0.0f; + + m->flags = 0; +} + +static inline void +evas_mat4_ortho_set(Evas_Mat4 *m, + Evas_Real left, Evas_Real right, Evas_Real bottom, Evas_Real top, + Evas_Real near, Evas_Real far) +{ + Evas_Real w = right - left; + Evas_Real h = top - bottom; + Evas_Real depth = near - far; + + m->m[ 0] = 2.0f / w; + m->m[ 1] = 0.0f; + m->m[ 2] = 0.0f; + m->m[ 3] = 0.0f; + + m->m[ 4] = 0.0f; + m->m[ 5] = 2.0f / h; + m->m[ 6] = 0.0f; + m->m[ 7] = 0.0f; + + m->m[ 8] = 0.0f; + m->m[ 9] = 0.0f; + m->m[10] = 2.0f / depth; + m->m[11] = 0.0f; + + m->m[12] = -(right + left) / w; + m->m[13] = -(top + bottom) / h; + m->m[14] = (far + near) / depth; + m->m[15] = 1.0f; + + m->flags = 0; +} + +static inline void +evas_mat4_nocheck_inverse(Evas_Mat4 *out, const Evas_Mat4 *mat) +{ + Evas_Real *d = &out->m[0]; + const Evas_Real *m = &mat->m[0]; + Evas_Real det; + + if (mat->flags & EVAS_MATRIX_IS_IDENTITY) + { + evas_mat4_copy(out, mat); + return; + } + + d[ 0] = m[ 5] * m[10] * m[15] - + m[ 5] * m[11] * m[14] - + m[ 9] * m[ 6] * m[15] + + m[ 9] * m[ 7] * m[14] + + m[13] * m[ 6] * m[11] - + m[13] * m[ 7] * m[10]; + + d[ 4] = -m[ 4] * m[10] * m[15] + + m[ 4] * m[11] * m[14] + + m[ 8] * m[ 6] * m[15] - + m[ 8] * m[ 7] * m[14] - + m[12] * m[ 6] * m[11] + + m[12] * m[ 7] * m[10]; + + d[ 8] = m[ 4] * m[ 9] * m[15] - + m[ 4] * m[11] * m[13] - + m[ 8] * m[ 5] * m[15] + + m[ 8] * m[ 7] * m[13] + + m[12] * m[ 5] * m[11] - + m[12] * m[ 7] * m[ 9]; + + d[12] = -m[ 4] * m[ 9] * m[14] + + m[ 4] * m[10] * m[13] + + m[ 8] * m[ 5] * m[14] - + m[ 8] * m[ 6] * m[13] - + m[12] * m[ 5] * m[10] + + m[12] * m[ 6] * m[ 9]; + + d[ 1] = -m[ 1] * m[10] * m[15] + + m[ 1] * m[11] * m[14] + + m[ 9] * m[ 2] * m[15] - + m[ 9] * m[ 3] * m[14] - + m[13] * m[ 2] * m[11] + + m[13] * m[ 3] * m[10]; + + d[ 5] = m[ 0] * m[10] * m[15] - + m[ 0] * m[11] * m[14] - + m[ 8] * m[ 2] * m[15] + + m[ 8] * m[ 3] * m[14] + + m[12] * m[ 2] * m[11] - + m[12] * m[ 3] * m[10]; + + d[ 9] = -m[ 0] * m[ 9] * m[15] + + m[ 0] * m[11] * m[13] + + m[ 8] * m[ 1] * m[15] - + m[ 8] * m[ 3] * m[13] - + m[12] * m[ 1] * m[11] + + m[12] * m[ 3] * m[ 9]; + + d[13] = m[ 0] * m[ 9] * m[14] - + m[ 0] * m[10] * m[13] - + m[ 8] * m[ 1] * m[14] + + m[ 8] * m[ 2] * m[13] + + m[12] * m[ 1] * m[10] - + m[12] * m[ 2] * m[ 9]; + + d[ 2] = m[ 1] * m[ 6] * m[15] - + m[ 1] * m[ 7] * m[14] - + m[ 5] * m[ 2] * m[15] + + m[ 5] * m[ 3] * m[14] + + m[13] * m[ 2] * m[ 7] - + m[13] * m[ 3] * m[ 6]; + + d[ 6] = -m[ 0] * m[ 6] * m[15] + + m[ 0] * m[ 7] * m[14] + + m[ 4] * m[ 2] * m[15] - + m[ 4] * m[ 3] * m[14] - + m[12] * m[ 2] * m[ 7] + + m[12] * m[ 3] * m[ 6]; + + d[10] = m[ 0] * m[ 5] * m[15] - + m[ 0] * m[ 7] * m[13] - + m[ 4] * m[ 1] * m[15] + + m[ 4] * m[ 3] * m[13] + + m[12] * m[ 1] * m[ 7] - + m[12] * m[ 3] * m[ 5]; + + d[14] = -m[ 0] * m[ 5] * m[14] + + m[ 0] * m[ 6] * m[13] + + m[ 4] * m[ 1] * m[14] - + m[ 4] * m[ 2] * m[13] - + m[12] * m[ 1] * m[ 6] + + m[12] * m[ 2] * m[ 5]; + + d[ 3] = -m[ 1] * m[ 6] * m[11] + + m[ 1] * m[ 7] * m[10] + + m[ 5] * m[ 2] * m[11] - + m[ 5] * m[ 3] * m[10] - + m[ 9] * m[ 2] * m[ 7] + + m[ 9] * m[ 3] * m[ 6]; + + d[ 7] = m[ 0] * m[ 6] * m[11] - + m[ 0] * m[ 7] * m[10] - + m[ 4] * m[ 2] * m[11] + + m[ 4] * m[ 3] * m[10] + + m[ 8] * m[ 2] * m[ 7] - + m[ 8] * m[ 3] * m[ 6]; + + d[11] = -m[ 0] * m[ 5] * m[11] + + m[ 0] * m[ 7] * m[ 9] + + m[ 4] * m[ 1] * m[11] - + m[ 4] * m[ 3] * m[ 9] - + m[ 8] * m[ 1] * m[ 7] + + m[ 8] * m[ 3] * m[ 5]; + + d[15] = m[ 0] * m[ 5] * m[10] - + m[ 0] * m[ 6] * m[ 9] - + m[ 4] * m[ 1] * m[10] + + m[ 4] * m[ 2] * m[ 9] + + m[ 8] * m[ 1] * m[ 6] - + m[ 8] * m[ 2] * m[ 5]; + + det = m[0] * d[0] + m[1] * d[4] + m[2] * d[8] + m[3] * d[12]; + + if (det == 0.0) + return; + + det = 1.0 / det; + + d[ 0] *= det; + d[ 1] *= det; + d[ 2] *= det; + d[ 3] *= det; + d[ 4] *= det; + d[ 5] *= det; + d[ 6] *= det; + d[ 7] *= det; + d[ 8] *= det; + d[ 9] *= det; + d[10] *= det; + d[11] *= det; + d[12] *= det; + d[13] *= det; + d[14] *= det; + d[15] *= det; + + out->flags = 0; +} + +static inline void +evas_mat4_inverse(Evas_Mat4 *out, const Evas_Mat4 *mat) +{ + if (out != mat) + { + evas_mat4_nocheck_inverse(out, mat); + } + else + { + Evas_Mat4 tmp; + + evas_mat4_nocheck_inverse(&tmp, mat); + evas_mat4_copy(out, &tmp); + } +} + +static inline void +evas_normal_matrix_get(Evas_Mat3 *out, const Evas_Mat4 *m) +{ + /* Normal matrix is a transposed matirx of inversed modelview. + * And we need only upper-left 3x3 terms to work with. */ + + Evas_Real det; + Evas_Real a = m->m[0]; + Evas_Real b = m->m[4]; + Evas_Real c = m->m[8]; + Evas_Real d = m->m[1]; + Evas_Real e = m->m[5]; + Evas_Real f = m->m[9]; + Evas_Real g = m->m[2]; + Evas_Real h = m->m[6]; + Evas_Real i = m->m[10]; + + det = a * e * i + b * f * g + c * d * h - g * e * c - h * f * a - i * d * b; + det = 1.0 / det; + + out->m[0] = (e * i - f * h) * det; + out->m[1] = (h * c - i * b) * det; + out->m[2] = (b * f - c * e) * det; + out->m[3] = (g * f - d * i) * det; + out->m[4] = (a * i - g * c) * det; + out->m[5] = (d * c - a * f) * det; + out->m[6] = (d * h - g * e) * det; + out->m[7] = (g * b - a * h) * det; + out->m[8] = (a * e - d * b) * det; + + out->flags = 0; +} + +/* 3x3 matrix */ +static inline void +evas_mat3_identity_set(Evas_Mat3 *m) +{ + m->m[0] = 1.0; + m->m[1] = 0.0; + m->m[2] = 0.0; + m->m[3] = 0.0; + m->m[4] = 1.0; + m->m[5] = 0.0; + m->m[6] = 0.0; + m->m[7] = 0.0; + m->m[8] = 1.0; + + m->flags = EVAS_MATRIX_IS_IDENTITY; +} + +static inline void +evas_mat3_array_set(Evas_Mat3 *m, const Evas_Real *v) +{ + memcpy(&m->m[0], v, sizeof(Evas_Real) * 9); + m->flags = 0; +} + +static inline void +evas_mat3_copy(Evas_Mat3 *dst, const Evas_Mat3 *src) +{ + memcpy(dst, src, sizeof(Evas_Mat3)); +} + +static inline void +evas_mat3_nocheck_multiply(Evas_Mat3 *out, const Evas_Mat3 *mat_a, const Evas_Mat3 *mat_b) +{ + Evas_Real *d = &out->m[0]; + const Evas_Real *a = &mat_a->m[0]; + const Evas_Real *b = &mat_b->m[0]; + + if (mat_a->flags & EVAS_MATRIX_IS_IDENTITY) + { + evas_mat3_copy(out, mat_b); + return; + } + + if (mat_b->flags & EVAS_MATRIX_IS_IDENTITY) + { + evas_mat3_copy(out, mat_a); + return; + } + + d[0] = a[0] * b[0] + a[3] * b[1] + a[6] * b[2]; + d[3] = a[0] * b[3] + a[3] * b[4] + a[6] * b[5]; + d[6] = a[0] * b[6] + a[3] * b[7] + a[6] * b[8]; + + d[1] = a[1] * b[0] + a[4] * b[1] + a[7] * b[2]; + d[4] = a[1] * b[3] + a[4] * b[4] + a[7] * b[5]; + d[7] = a[1] * b[6] + a[4] * b[7] + a[7] * b[8]; + + d[2] = a[2] * b[0] + a[5] * b[1] + a[8] * b[2]; + d[5] = a[2] * b[3] + a[5] * b[4] + a[8] * b[5]; + d[8] = a[2] * b[6] + a[5] * b[7] + a[8] * b[8]; + + out->flags = 0; +} + +static inline void +evas_mat3_multiply(Evas_Mat3 *out, const Evas_Mat3 *mat_a, const Evas_Mat3 *mat_b) +{ + if (out != mat_a && out != mat_b) + { + evas_mat3_nocheck_multiply(out, mat_a, mat_b); + } + else + { + Evas_Mat3 tmp; + + evas_mat3_nocheck_multiply(&tmp, mat_a, mat_b); + evas_mat3_copy(out, &tmp); + } +} + +static inline void +evas_mat3_nocheck_inverse(Evas_Mat3 *out, const Evas_Mat3 *mat) +{ + Evas_Real *d = &out->m[0]; + const Evas_Real *m = &mat->m[0]; + Evas_Real det; + + if (mat->flags & EVAS_MATRIX_IS_IDENTITY) + { + evas_mat3_copy(out, mat); + return; + } + + d[0] = m[4] * m[8] - m[7] * m[5]; + d[1] = m[7] * m[2] - m[1] * m[8]; + d[2] = m[1] * m[5] - m[4] * m[2]; + d[3] = m[6] * m[5] - m[3] * m[8]; + d[4] = m[0] * m[8] - m[6] * m[2]; + d[5] = m[3] * m[2] - m[0] * m[5]; + d[6] = m[3] * m[7] - m[6] * m[4]; + d[7] = m[6] * m[1] - m[0] * m[7]; + d[8] = m[0] * m[4] - m[3] * m[1]; + + det = m[0] * d[0] + m[1] * d[3] + m[2] * d[6]; + + if (det == 0.0) + return; + + det = 1.0 / det; + + d[0] *= det; + d[1] *= det; + d[2] *= det; + d[3] *= det; + d[4] *= det; + d[5] *= det; + d[6] *= det; + d[7] *= det; + d[8] *= det; + + out->flags = 0; +} + +static inline void +evas_mat3_invserse(Evas_Mat3 *out, const Evas_Mat3 *mat) +{ + if (out != mat) + { + evas_mat3_nocheck_inverse(out, mat); + } + else + { + Evas_Mat3 tmp; + + evas_mat3_nocheck_inverse(&tmp, mat); + evas_mat3_copy(out, &tmp); + } +} + +/* 2x2 matrix */ +static inline void +evas_mat2_identity_set(Evas_Mat2 *m) +{ + m->m[0] = 1.0; + m->m[1] = 0.0; + m->m[2] = 0.0; + m->m[3] = 1.0; + + m->flags = EVAS_MATRIX_IS_IDENTITY; +} + +static inline void +evas_mat2_array_set(Evas_Mat2 *m, const Evas_Real *v) +{ + memcpy(&m->m[0], v, sizeof(Evas_Real) * 4); + m->flags = 0; +} + +static inline void +evas_mat2_copy(Evas_Mat2 *dst, const Evas_Mat2 *src) +{ + memcpy(dst, src, sizeof(Evas_Mat2)); +} + +static inline void +evas_mat2_nocheck_multiply(Evas_Mat2 *out, const Evas_Mat2 *mat_a, const Evas_Mat2 *mat_b) +{ + Evas_Real *d = &out->m[0]; + const Evas_Real *a = &mat_a->m[0]; + const Evas_Real *b = &mat_b->m[0]; + + if (mat_a->flags & EVAS_MATRIX_IS_IDENTITY) + { + evas_mat2_copy(out, mat_b); + return; + } + + if (mat_b->flags & EVAS_MATRIX_IS_IDENTITY) + { + evas_mat2_copy(out, mat_a); + return; + } + + d[0] = a[0] * b[0] + a[2] * b[1]; + d[2] = a[0] * b[2] + a[2] * b[3]; + + d[1] = a[1] * b[0] + a[3] * b[1]; + d[3] = a[1] * b[2] + a[3] * b[3]; + + out->flags = 0; +} + +static inline void +evas_mat2_multiply(Evas_Mat2 *out, const Evas_Mat2 *mat_a, const Evas_Mat2 *mat_b) +{ + if (out != mat_a && out != mat_b) + { + evas_mat2_nocheck_multiply(out, mat_a, mat_b); + } + else + { + Evas_Mat2 tmp; + + evas_mat2_nocheck_multiply(&tmp, mat_a, mat_b); + evas_mat2_copy(out, &tmp); + } +} + +static inline void +evas_mat2_nocheck_inverse(Evas_Mat2 *out, const Evas_Mat2 *mat) +{ + Evas_Real *d = &out->m[0]; + const Evas_Real *m = &mat->m[0]; + Evas_Real det; + + if (mat->flags & EVAS_MATRIX_IS_IDENTITY) + { + evas_mat2_copy(out, mat); + return; + } + + det = m[0] * m[3] - m[2] * m[1]; + + if (det == 0.0) + return; + + det = 1.0 / det; + + d[0] = m[3] * det; + d[1] = -m[1] * det; + d[2] = -m[2] * det; + d[3] = m[0] * det; + + out->flags = 0; +} + +static inline void +evas_mat2_invserse(Evas_Mat2 *out, const Evas_Mat2 *mat) +{ + if (out != mat) + { + evas_mat2_nocheck_inverse(out, mat); + } + else + { + Evas_Mat2 tmp; + + evas_mat2_nocheck_inverse(&tmp, mat); + evas_mat2_copy(out, &tmp); + } +} + +static inline void +evas_box2_set(Evas_Box2 *box, Evas_Real x0, Evas_Real y0, Evas_Real x1, Evas_Real y1) +{ + box->p0.x = x0; + box->p0.y = y0; + box->p1.x = x1; + box->p1.y = y1; +} + +static inline void +evas_box3_set(Evas_Box3 *box, Evas_Real x0, Evas_Real y0, Evas_Real z0, Evas_Real x1, Evas_Real y1, Evas_Real z1) +{ + box->p0.x = x0; + box->p0.y = y0; + box->p0.z = z0; + box->p1.x = x1; + box->p1.y = y1; + box->p1.z = z1; +} + +static inline void +evas_box3_empty_set(Evas_Box3 *box) +{ + evas_vec3_set(&box->p0, 0.0, 0.0, 0.0); + evas_vec3_set(&box->p1, 0.0, 0.0, 0.0); +} + +static inline void +evas_box3_copy(Evas_Box3 *dst, const Evas_Box3 *src) +{ + evas_vec3_copy(&dst->p0, &src->p0); + evas_vec3_copy(&dst->p1, &src->p1); +} + +static inline void +evas_box3_union(Evas_Box3 *out, const Evas_Box3 *a, const Evas_Box3 *b) +{ + evas_vec3_set(&out->p0, MIN(a->p0.x, b->p0.x), MIN(a->p0.y, b->p0.y), MIN(a->p0.z, b->p0.z)); + evas_vec3_set(&out->p1, MAX(a->p1.x, b->p1.x), MAX(a->p1.y, b->p1.y), MAX(a->p1.z, b->p1.z)); +} + +static inline void +evas_box3_transform(Evas_Box3 *out EINA_UNUSED, const Evas_Box3 *box EINA_UNUSED, const Evas_Mat4 *mat EINA_UNUSED) +{ + /* TODO: */ +} + +static inline void +evas_mat4_position_get(const Evas_Mat4 *matrix, Evas_Vec4 *position) +{ + Evas_Vec4 pos; + + pos.x = 0.0; + pos.y = 0.0; + pos.z = 0.0; + pos.w = 1.0; + + evas_vec4_transform(position, &pos, matrix); +} + +static inline void +evas_mat4_direction_get(const Evas_Mat4 *matrix, Evas_Vec3 *direction) +{ + /* TODO: Check correctness. */ + + Evas_Vec4 dir; + + dir.x = 0.0; + dir.y = 0.0; + dir.z = 1.0; + dir.w = 1.0; + + evas_vec4_transform(&dir, &dir, matrix); + + direction->x = dir.x; + direction->y = dir.y; + direction->z = dir.z; +} + +static inline void +evas_vec4_quaternion_multiply(Evas_Vec4 *out, const Evas_Vec4 *a, const Evas_Vec4 *b) +{ + Evas_Vec4 r; + + r.x = (a->w * b->x) + (a->x * b->w) + (a->y * b->z) - (a->z * b->y); + r.y = (a->w * b->y) - (a->x * b->z) + (a->y * b->w) + (a->z * b->x); + r.z = (a->w * b->z) + (a->x * b->y) - (a->y * b->x) + (a->z * b->w); + r.w = (a->w * b->w) - (a->x * b->x) - (a->y * b->y) - (a->z * b->z); + + *out = r; +} + +static inline void +evas_vec4_quaternion_inverse(Evas_Vec4 *out, const Evas_Vec4 *q) +{ + Evas_Real norm = (q->x * q->x) + (q->y * q->y) + (q->z * q->z) + (q->w * q->w); + + if (norm > 0.0) + { + Evas_Real inv_norm = 1.0 / norm; + out->x = -q->x * inv_norm; + out->y = -q->y * inv_norm; + out->z = -q->z * inv_norm; + out->w = q->w * inv_norm; + } + else + { + out->x = 0.0; + out->y = 0.0; + out->z = 0.0; + out->w = 0.0; + } +} + +static inline void +evas_vec4_quaternion_rotation_matrix_get(const Evas_Vec4 *q, Evas_Mat3 *mat) +{ + Evas_Real x, y, z; + Evas_Real xx, xy, xz; + Evas_Real yy, yz; + Evas_Real zz; + Evas_Real wx, wy, wz; + + x = 2.0 * q->x; + y = 2.0 * q->y; + z = 2.0 * q->z; + + xx = q->x * x; + xy = q->x * y; + xz = q->x * z; + + yy = q->y * y; + yz = q->y * z; + + zz = q->z * z; + + wx = q->w * x; + wy = q->w * y; + wz = q->w * z; + + mat->m[0] = 1.0 - yy - zz; + mat->m[1] = xy + wz; + mat->m[2] = xz - wy; + mat->m[3] = xy - wz; + mat->m[4] = 1.0 - xx - zz; + mat->m[5] = yz + wx; + mat->m[6] = xz + wy; + mat->m[7] = yz - wx; + mat->m[8] = 1.0 - xx - yy; +} + +static inline void +evas_mat4_build(Evas_Mat4 *out, + const Evas_Vec3 *position, const Evas_Vec4 *orientation, const Evas_Vec3 *scale) +{ + Evas_Mat3 rot; + + evas_vec4_quaternion_rotation_matrix_get(orientation, &rot); + + out->m[ 0] = scale->x * rot.m[0]; + out->m[ 1] = scale->x * rot.m[1]; + out->m[ 2] = scale->x * rot.m[2]; + out->m[ 3] = 0.0; + + out->m[ 4] = scale->y * rot.m[3]; + out->m[ 5] = scale->y * rot.m[4]; + out->m[ 6] = scale->y * rot.m[5]; + out->m[ 7] = 0.0; + + out->m[ 8] = scale->z * rot.m[6]; + out->m[ 9] = scale->z * rot.m[7]; + out->m[10] = scale->z * rot.m[8]; + out->m[11] = 0.0; + + out->m[12] = position->x; + out->m[13] = position->y; + out->m[14] = position->z; + out->m[15] = 1.0; +} + +static inline void +evas_mat4_inverse_build(Evas_Mat4 *out, const Evas_Vec3 *position, + const Evas_Vec4 *orientation, const Evas_Vec3 *scale) +{ + Evas_Vec4 inv_rotation; + Evas_Vec3 inv_scale; + Evas_Vec3 inv_translate; + + Evas_Mat3 rot; + + /* Inverse scale. */ + evas_vec3_set(&inv_scale, 1.0 / scale->x, 1.0 / scale->y, 1.0 / scale->z); + + /* Inverse rotation. */ + evas_vec4_quaternion_inverse(&inv_rotation, orientation); + + /* Inverse translation. */ + evas_vec3_negate(&inv_translate, position); + evas_vec3_quaternion_rotate(&inv_translate, &inv_translate, &inv_rotation); + evas_vec3_multiply(&inv_translate, &inv_translate, &inv_scale); + + /* Get 3x3 rotation matrix. */ + evas_vec4_quaternion_rotation_matrix_get(&inv_rotation, &rot); + + out->m[ 0] = inv_scale.x * rot.m[0]; + out->m[ 1] = inv_scale.y * rot.m[1]; + out->m[ 2] = inv_scale.z * rot.m[2]; + out->m[ 3] = 0.0; + + out->m[ 4] = inv_scale.x * rot.m[3]; + out->m[ 5] = inv_scale.y * rot.m[4]; + out->m[ 6] = inv_scale.z * rot.m[5]; + out->m[ 7] = 0.0; + + out->m[ 8] = inv_scale.x * rot.m[6]; + out->m[ 9] = inv_scale.y * rot.m[7]; + out->m[10] = inv_scale.z * rot.m[8]; + out->m[11] = 0.0; + + out->m[12] = inv_translate.x; + out->m[13] = inv_translate.y; + out->m[14] = inv_translate.z; + out->m[15] = 1.0; +} + +static inline void +evas_color_set(Evas_Color *color, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) +{ + color->r = r; + color->g = g; + color->b = b; + color->a = a; +} + +static inline void +evas_color_blend(Evas_Color *dst, const Evas_Color *c0, const Evas_Color *c1, Evas_Real w) +{ + dst->r = c0->r * w + c1->r * (1.0 - w); + dst->g = c0->g * w + c1->g * (1.0 - w); + dst->b = c0->b * w + c1->b * (1.0 - w); + dst->a = c0->a * w + c1->a * (1.0 - w); +} + +static inline void +evas_ray3_init(Evas_Ray3 *ray, Evas_Real x, Evas_Real y, const Evas_Mat4 *mvp) +{ + Evas_Mat4 mat; + Evas_Vec4 near, far; + + /* Get the matrix which transforms from normalized device coordinate to modeling coodrinate. */ + evas_mat4_inverse(&mat, mvp); + + /* Transform near point. */ + near.x = x; + near.y = y; + near.z = -1.0; + near.w = 1.0; + + evas_vec4_transform(&near, &near, &mat); + + near.w = 1.0 / near.w; + near.x *= near.w; + near.y *= near.w; + near.z *= near.w; + + evas_vec3_set(&ray->org, near.x, near.y, near.z); + + /* Transform far point. */ + far.x = x; + far.y = y; + far.z = 1.0; + far.w = 1.0; + + evas_vec4_transform(&far, &far, &mat); + + far.w = 1.0 / far.w; + far.x *= far.w; + far.y *= far.w; + far.z *= far.w; + + evas_vec3_set(&ray->dir, far.x - near.x, far.y - near.y, far.z - near.z); +} + +static inline Eina_Bool +evas_box3_ray3_intersect(const Evas_Box3 *box EINA_UNUSED, const Evas_Ray3 *ray EINA_UNUSED) +{ + /* TODO: */ + return EINA_TRUE; +} + +static inline Evas_Real +evas_reciprocal_sqrt(Evas_Real x) +{ + union { + float f; + long i; + } u; + + u.f = x; + u.i = 0x5f3759df - (u.i >> 1); + return u.f * (1.5f - u.f * u.f * x * 0.5f); +} diff --git a/src/lib/evas/include/evas_inline.x b/src/lib/evas/include/evas_inline.x index c4e102d372..8952c3b6f7 100644 --- a/src/lib/evas/include/evas_inline.x +++ b/src/lib/evas/include/evas_inline.x @@ -110,7 +110,11 @@ evas_object_is_source_invisible(Evas_Object *eo_obj EINA_UNUSED, Evas_Object_Pro { if (obj->parent_cache.src_invisible_valid) return obj->parent_cache.src_invisible; +#ifdef EVAS_3D + if ((obj->proxy->proxies || obj->proxy->proxy_textures) && obj->proxy->src_invisible) return 1; +#else if (obj->proxy->proxies && obj->proxy->src_invisible) return 1; +#endif if (!obj->smart.parent) return 0; Evas_Object_Protected_Data *smart_parent_pd = eo_data_scope_get(obj->smart.parent, EVAS_OBJ_CLASS); diff --git a/src/lib/evas/include/evas_private.h b/src/lib/evas/include/evas_private.h index f5c0700b07..27bb4803fb 100644 --- a/src/lib/evas/include/evas_private.h +++ b/src/lib/evas/include/evas_private.h @@ -16,6 +16,7 @@ #include "../common/language/evas_bidi_utils.h" #include "../common/language/evas_language_utils.h" +#include "evas_3d_private.h" #define RENDER_METHOD_INVALID 0x00000000 @@ -46,6 +47,7 @@ typedef struct _Evas_Coord_Touch_Point Evas_Coord_Touch_Point; typedef struct _Evas_Object_Proxy_Data Evas_Object_Proxy_Data; typedef struct _Evas_Object_Map_Data Evas_Object_Map_Data; typedef struct _Evas_Proxy_Render_Data Evas_Proxy_Render_Data; +typedef struct _Evas_Object_3D_Data Evas_Object_3D_Data; typedef struct _Evas_Object_Protected_State Evas_Object_Protected_State; typedef struct _Evas_Object_Protected_Data Evas_Object_Protected_Data; @@ -512,6 +514,7 @@ struct _Evas_Map struct _Evas_Object_Proxy_Data { Eina_List *proxies; + Eina_List *proxy_textures; void *surface; int w,h; Eina_List *src_event_in; @@ -537,6 +540,12 @@ struct _Evas_Object_Map_Data RGBA_Map *spans; }; +struct _Evas_Object_3D_Data +{ + void *surface; + int w, h; +}; + struct _Evas_Object_Protected_State { Evas_Object_Protected_Data *clipper; @@ -603,6 +612,7 @@ struct _Evas_Object_Protected_Data // Eina_Cow pointer be careful when writing to it const Evas_Object_Proxy_Data *proxy; const Evas_Object_Map_Data *map; + const Evas_Object_3D_Data *data_3d; // Pointer to the Evas_Object itself Evas_Object *object; @@ -923,6 +933,26 @@ struct _Evas_Func Eina_Bool (*pixel_alpha_get) (void *image, int x, int y, DATA8 *alpha, int src_region_x, int src_region_y, int src_region_w, int src_region_h, int dst_region_x, int dst_region_y, int dst_region_w, int dst_region_h); void (*context_flush) (void *data); + + /* 3D features */ + void *(*drawable_new) (void *data, int w, int h, int alpha); + void (*drawable_free) (void *data, void *drawable); + void (*drawable_size_get) (void *data, void *drawable, int *w, int *h); + void *(*image_drawable_set) (void *data, void *image, void *drawable); + + void (*drawable_scene_render) (void *data, void *drawable, void *scene_data); + + void *(*texture_new) (void *data); + void (*texture_free) (void *data, void *texture); + void (*texture_data_set) (void *data, void *texture, Evas_3D_Color_Format format, Evas_3D_Pixel_Format pixel_format, int w, int h, const void *pixels); + void (*texture_file_set) (void *data, void *texture, const char *file, const char *key); + void (*texture_color_format_get) (void *data, void *texture, Evas_3D_Color_Format *format); + void (*texture_size_get) (void *data, void *texture, int *w, int *h); + void (*texture_wrap_set) (void *data, void *texture, Evas_3D_Wrap_Mode s, Evas_3D_Wrap_Mode t); + void (*texture_wrap_get) (void *data, void *texture, Evas_3D_Wrap_Mode *s, Evas_3D_Wrap_Mode *t); + void (*texture_filter_set) (void *data, void *texture, Evas_3D_Texture_Filter min, Evas_3D_Texture_Filter mag); + void (*texture_filter_get) (void *data, void *texture, Evas_3D_Texture_Filter *min, Evas_3D_Texture_Filter *mag); + void (*texture_image_set) (void *data, void *texture, void *image); }; struct _Evas_Image_Save_Func @@ -1283,6 +1313,8 @@ extern Eina_Cow *evas_object_proxy_cow; extern Eina_Cow *evas_object_map_cow; extern Eina_Cow *evas_object_state_cow; +extern Eina_Cow *evas_object_3d_cow; + extern Eina_Cow *evas_object_image_pixels_cow; extern Eina_Cow *evas_object_image_load_opts_cow; extern Eina_Cow *evas_object_image_state_cow; diff --git a/src/modules/evas/engines/gl_common/evas_gl_3d.c b/src/modules/evas/engines/gl_common/evas_gl_3d.c new file mode 100644 index 0000000000..c41ffbfc26 --- /dev/null +++ b/src/modules/evas/engines/gl_common/evas_gl_3d.c @@ -0,0 +1,1332 @@ +#include <stdio.h> +#include <png.h> +#include "evas_gl_private.h" +#include "evas_gl_3d_private.h" + +void +e3d_texture_param_update(E3D_Texture *texture) +{ + if (texture->is_imported) + return; + + if (texture->wrap_dirty) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture->wrap_s); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture->wrap_t); + texture->wrap_dirty = EINA_FALSE; + } + + if (texture->filter_dirty) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture->filter_min); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture->filter_mag); + texture->filter_dirty = EINA_FALSE; + } +} + +E3D_Texture * +e3d_texture_new(void) +{ + E3D_Texture *texture = NULL; + + texture = (E3D_Texture *)malloc(sizeof(E3D_Texture)); + + if (texture == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + texture->w = 0; + texture->h = 0; + + texture->is_imported = EINA_FALSE; + texture->tex = 0; + texture->format = GL_RGBA; + + texture->wrap_dirty = EINA_TRUE; + texture->wrap_s = GL_CLAMP_TO_EDGE; + texture->wrap_t = GL_CLAMP_TO_EDGE; + + texture->filter_dirty = EINA_TRUE; + texture->filter_min = GL_NEAREST; + texture->filter_mag = GL_NEAREST; + + return texture; +} + +void +e3d_texture_free(E3D_Texture *texture) +{ + if (texture->tex && !texture->is_imported) + glDeleteTextures(1, &texture->tex); + + free(texture); +} + +void +e3d_texture_data_set(E3D_Texture *texture, + Evas_3D_Color_Format color_format, Evas_3D_Pixel_Format pixel_format, + int w, int h, const void *data) +{ + GLenum format; + GLenum iformat; + GLenum type; + + if (color_format == EVAS_3D_COLOR_FORMAT_RGBA) + { + format = GL_RGBA; + iformat = GL_RGBA; + + if (pixel_format == EVAS_3D_PIXEL_FORMAT_8888) + type = GL_UNSIGNED_BYTE; + else if (pixel_format == EVAS_3D_PIXEL_FORMAT_4444) + type = GL_UNSIGNED_SHORT_4_4_4_4; + else if (pixel_format == EVAS_3D_PIXEL_FORMAT_5551) + type = GL_UNSIGNED_SHORT_5_5_5_1; + else + { + ERR("Texture data format mismatch."); + return; + } + } + else if (color_format == EVAS_3D_COLOR_FORMAT_RGB) + { + format = GL_RGB; + iformat = GL_RGB; + + if (pixel_format == EVAS_3D_PIXEL_FORMAT_565) + type = GL_UNSIGNED_SHORT_5_6_5; + else if (pixel_format == EVAS_3D_PIXEL_FORMAT_888) + type = GL_UNSIGNED_BYTE; + else + { + ERR("Texture data format mismatch."); + return; + } + } + else if (color_format == EVAS_3D_COLOR_FORMAT_ALPHA) + { + format = GL_LUMINANCE; + iformat = GL_LUMINANCE; + + if (pixel_format == EVAS_3D_PIXEL_FORMAT_8) + type = GL_UNSIGNED_BYTE; + else + { + ERR("Texture data format mismatch."); + return; + } + } + else + { + ERR("Invalid texture color format"); + return; + } + + if (texture->tex == 0 || texture->is_imported) + { + glGenTextures(1, &texture->tex); + texture->wrap_dirty = EINA_TRUE; + texture->filter_dirty = EINA_TRUE; + } + + glBindTexture(GL_TEXTURE_2D, texture->tex); + glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, iformat, type, data); + + if (texture->wrap_dirty) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture->wrap_s); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texture->wrap_t); + texture->wrap_dirty = EINA_FALSE; + } + + if (texture->filter_dirty) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture->filter_min); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texture->filter_mag); + texture->filter_dirty = EINA_FALSE; + } + + texture->is_imported = EINA_FALSE; + texture->format = format; +} + +void +e3d_texture_file_set(E3D_Texture *texture, const char *file, const char *key EINA_UNUSED) +{ + Evas_3D_Color_Format color_format; + Evas_3D_Pixel_Format pixel_format; + + FILE *fp; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + void *buffer = NULL; + png_bytep *row_pointers = NULL; + + int w, h; + png_byte color_type; + png_byte bit_depth; + png_byte header[8]; + int ret; + int y; + int stride; + + fp = fopen(file, "rb"); + + if (fp == NULL) + { + ERR("Failed to open file %s", file); + goto done; + } + + ret = fread(header, 1, 8, fp); + + if (ret < 8) + goto done; + + if (png_sig_cmp(header, 0, 8)) + { + ERR("%s does not seem to be a png file.", file); + goto done; + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (png_ptr == NULL) + goto done; + + info_ptr = png_create_info_struct(png_ptr); + + if (info_ptr == NULL) + goto done; + + if (setjmp(png_jmpbuf(png_ptr))) + goto done; + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, 8); + + png_read_info(png_ptr, info_ptr); + + bit_depth = png_get_bit_depth(png_ptr, info_ptr); + + if (bit_depth != 8) + { + ERR("Unsupported bit depth %d.", bit_depth); + goto done; + } + + w = png_get_image_width(png_ptr, info_ptr); + h = png_get_image_height(png_ptr, info_ptr); + + if (w < 1 || h < 1 || w > IMG_MAX_SIZE || h > IMG_MAX_SIZE || IMG_TOO_BIG(w, h)) + { + ERR("Image %s(%d x %d) is too big for texture.", file, w, h); + goto done; + } + + color_type = png_get_color_type(png_ptr, info_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY) + { + color_format = EVAS_3D_COLOR_FORMAT_ALPHA; + pixel_format = EVAS_3D_PIXEL_FORMAT_8; + stride = (w + 3) & ~3; + } + else if (color_type == PNG_COLOR_TYPE_RGB) + { + color_format = EVAS_3D_COLOR_FORMAT_RGB; + pixel_format = EVAS_3D_PIXEL_FORMAT_888; + stride = (w * 3 + 3) & ~3; + } + else if (color_type == PNG_COLOR_TYPE_RGBA) + { + color_format = EVAS_3D_COLOR_FORMAT_RGBA; + pixel_format = EVAS_3D_PIXEL_FORMAT_8888; + stride = w * 4; + } + else + { + ERR("Unsupported color format."); + goto done; + } + + row_pointers = (png_bytep *)malloc(sizeof(png_bytep) * h); + + if (row_pointers == NULL) + goto done; + + buffer = malloc(stride * h); + + if (buffer == NULL) + goto done; + + for (y = 0; y < h; y++) + row_pointers[y] = ((png_byte *)buffer) + (h - y - 1)* stride; + + png_read_image(png_ptr, row_pointers); + + e3d_texture_data_set(texture, color_format, pixel_format, w, h, buffer); + +done: + if (png_ptr) + png_destroy_read_struct(&png_ptr, info_ptr ? &info_ptr : NULL, NULL); + + if (fp) + fclose(fp); + + if (row_pointers) + free(row_pointers); + + if (buffer) + free(buffer); +} + +Evas_3D_Color_Format +e3d_texture_color_format_get(E3D_Texture *texture) +{ + if (texture->is_imported) + { + ERR("Cannot get the size of an imported texture."); + return EVAS_3D_COLOR_FORMAT_RGBA; + } + + switch (texture->format) + { + case GL_RGBA: + return EVAS_3D_COLOR_FORMAT_RGBA; + case GL_RGB: + return EVAS_3D_COLOR_FORMAT_RGB; + case GL_ALPHA: + return EVAS_3D_COLOR_FORMAT_ALPHA; + default: + break; + } + + ERR("Invalid texture format."); + return EVAS_3D_COLOR_FORMAT_RGBA; +} + +void +e3d_texture_size_get(const E3D_Texture *texture, int *w, int *h) +{ + if (texture->is_imported) + { + ERR("Invalid operation on an imported texture resource."); + return; + } + + if (w) *w = texture->w; + if (h) *h = texture->h; +} + +void +e3d_texture_import(E3D_Texture *texture, GLuint tex) +{ + if (tex == 0) + { + ERR("Cannot import an invalid texture ID."); + return; + } + + if (texture->tex && !texture->is_imported) + glDeleteTextures(1, &texture->tex); + + texture->tex = tex; + texture->is_imported = EINA_TRUE; +} + +Eina_Bool +e3d_texture_is_imported_get(const E3D_Texture *texture) +{ + return texture->is_imported; +} + +static inline GLenum +_to_gl_texture_wrap(Evas_3D_Wrap_Mode wrap) +{ + switch (wrap) + { + case EVAS_3D_WRAP_MODE_CLAMP: + return GL_CLAMP_TO_EDGE; + case EVAS_3D_WRAP_MODE_REFLECT: + return GL_MIRRORED_REPEAT; + case EVAS_3D_WRAP_MODE_REPEAT: + return GL_REPEAT; + default: + break; + } + + ERR("Invalid texture wrap mode."); + return GL_CLAMP_TO_EDGE; +} + +static inline Evas_3D_Wrap_Mode +_to_e3d_texture_wrap(GLenum wrap) +{ + switch (wrap) + { + case GL_CLAMP_TO_EDGE: + return EVAS_3D_WRAP_MODE_CLAMP; + case GL_MIRRORED_REPEAT: + return EVAS_3D_WRAP_MODE_REFLECT; + case GL_REPEAT: + return EVAS_3D_WRAP_MODE_REPEAT; + default: + break; + } + + ERR("Invalid texture wrap mode."); + return EVAS_3D_WRAP_MODE_CLAMP; +} + +static inline GLenum +_to_gl_texture_filter(Evas_3D_Texture_Filter filter) +{ + switch (filter) + { + case EVAS_3D_TEXTURE_FILTER_NEAREST: + return GL_NEAREST; + case EVAS_3D_TEXTURE_FILTER_LINEAR: + return GL_LINEAR; + case EVAS_3D_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST: + return GL_NEAREST_MIPMAP_NEAREST; + case EVAS_3D_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR: + return GL_NEAREST_MIPMAP_LINEAR; + case EVAS_3D_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST: + return GL_LINEAR_MIPMAP_NEAREST; + case EVAS_3D_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR: + return GL_LINEAR_MIPMAP_LINEAR; + default: + break; + } + + ERR("Invalid texture wrap mode."); + return GL_NEAREST; +} + +static inline Evas_3D_Texture_Filter +_to_e3d_texture_filter(GLenum filter) +{ + switch (filter) + { + case GL_NEAREST: + return EVAS_3D_TEXTURE_FILTER_NEAREST; + case GL_LINEAR: + return EVAS_3D_TEXTURE_FILTER_LINEAR; + case GL_NEAREST_MIPMAP_NEAREST: + return EVAS_3D_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST; + case GL_NEAREST_MIPMAP_LINEAR: + return EVAS_3D_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR; + case GL_LINEAR_MIPMAP_NEAREST: + return EVAS_3D_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST; + case GL_LINEAR_MIPMAP_LINEAR: + return EVAS_3D_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR; + default: + break; + } + + ERR("Invalid texture wrap mode."); + return EVAS_3D_TEXTURE_FILTER_NEAREST; +} + +void +e3d_texture_wrap_set(E3D_Texture *texture, Evas_3D_Wrap_Mode s, Evas_3D_Wrap_Mode t) +{ + GLenum gl_s, gl_t; + + if (texture->is_imported) + { + ERR("Invalid operation on an imported texture resource."); + return; + } + + gl_s = _to_gl_texture_wrap(s); + gl_t = _to_gl_texture_wrap(t); + + if (gl_s == texture->wrap_s && gl_t == texture->wrap_t) + return; + + texture->wrap_s = gl_s; + texture->wrap_t = gl_t; + texture->wrap_dirty = EINA_TRUE; +} + +void +e3d_texture_wrap_get(const E3D_Texture *texture, Evas_3D_Wrap_Mode *s, Evas_3D_Wrap_Mode *t) +{ + if (texture->is_imported) + { + ERR("Invalid operation on an imported texture resource."); + return; + } + + if (s) + *s = _to_e3d_texture_wrap(texture->wrap_s); + + if (t) + *t = _to_e3d_texture_wrap(texture->wrap_t); +} + +void +e3d_texture_filter_set(E3D_Texture *texture, Evas_3D_Texture_Filter min, Evas_3D_Texture_Filter mag) +{ + GLenum gl_min, gl_mag; + + if (texture->is_imported) + { + ERR("Invalid operation on an imported texture resource."); + return; + } + + gl_min = _to_gl_texture_filter(min); + gl_mag = _to_gl_texture_filter(mag); + + if (gl_min == texture->filter_min && gl_mag == texture->filter_mag) + return; + + texture->filter_min = gl_min; + texture->filter_mag = gl_mag; + texture->filter_dirty = EINA_TRUE; +} + +void +e3d_texture_filter_get(const E3D_Texture *texture, + Evas_3D_Texture_Filter *min, Evas_3D_Texture_Filter *mag) +{ + if (texture->is_imported) + { + ERR("Invalid operation on an imported texture resource."); + return; + } + + if (min) + *min = _to_e3d_texture_filter(texture->filter_min); + + if (mag) + *mag = _to_e3d_texture_filter(texture->filter_mag); +} + +E3D_Drawable * +e3d_drawable_new(int w, int h, int alpha, GLenum depth_format, GLenum stencil_format) +{ + E3D_Drawable *drawable; + GLuint tex, fbo; + GLuint depth_stencil_buf = 0; + GLuint depth_buf = 0; + GLuint stencil_buf = 0; + Eina_Bool depth_stencil = EINA_FALSE; + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + if (alpha) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); + +#if GL_GLES + if (depth_format == GL_DEPTH_STENCIL_OES) + { + glGenTextures(1, &depth_stencil_buf); + glBindTexture(GL_TEXTURE_2D, depth_stencil_buf); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_STENCIL_OES, w, h, 0, + GL_DEPTH_STENCIL_OES, GL_UNSIGNED_INT_24_8_OES, NULL); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, depth_stencil_buf, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, depth_stencil_buf, 0); + + depth_stencil = EINA_TRUE; + } +#else + if (depth_format == GL_DEPTH24_STENCIL8) + { + glGenRenderbuffers(1, &depth_stencil_buf); + glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_buf); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, depth_stencil_buf); + + depth_stencil = EINA_TRUE; + } +#endif + + if ((!depth_stencil) && (depth_format)) + { + glGenRenderbuffers(1, &depth_buf); + glBindRenderbuffer(GL_RENDERBUFFER, depth_buf); + glRenderbufferStorage(GL_RENDERBUFFER, depth_format, w, h); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, depth_buf); + } + + if ((!depth_stencil) && (stencil_format)) + { + glGenRenderbuffers(1, &stencil_buf); + glBindRenderbuffer(GL_RENDERBUFFER, stencil_buf); + glRenderbufferStorage(GL_RENDERBUFFER, stencil_format, w, h); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, stencil_buf); + } + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + goto error; + + drawable = (E3D_Drawable *)calloc(1, sizeof(E3D_Drawable)); + + if (drawable == NULL) + goto error; + + drawable->w = w; + drawable->h = h; + drawable->alpha = alpha; + + if (alpha) + drawable->format = GL_RGBA; + else + drawable->format = GL_RGB; + + drawable->depth_format = depth_format; + drawable->stencil_format = stencil_format; + drawable->tex = tex; + drawable->fbo = fbo; + drawable->depth_stencil_buf = depth_stencil_buf; + drawable->depth_buf = depth_buf; + drawable->stencil_buf = stencil_buf; + + return drawable; + +error: + ERR("Drawable creation failed."); + + if (tex) + glDeleteTextures(1, &tex); + + if (fbo) + glDeleteFramebuffers(1, &fbo); + + if (depth_stencil_buf) + { +#ifdef GL_GLES + glDeleteTextures(1, &depth_stencil_buf); +#else + glDeleteRenderbuffers(1, &depth_stencil_buf); +#endif + } + + if (depth_buf) + glDeleteRenderbuffers(1, &depth_buf); + + if (stencil_buf) + glDeleteRenderbuffers(1, &stencil_buf); + + if (drawable) + free(drawable); + + return NULL; +} + +void +e3d_drawable_free(E3D_Drawable *drawable) +{ + if (drawable == NULL) + return; + + if (drawable->tex) + glDeleteTextures(1, &drawable->tex); + + if (drawable->fbo) + glDeleteFramebuffers(1, &drawable->fbo); + + if (drawable->depth_stencil_buf) + { +#ifdef GL_GLES + glDeleteTextures(1, &drawable->depth_stencil_buf); +#else + glDeleteRenderbuffers(1, &drawable->depth_stencil_buf); +#endif + } + + if (drawable->depth_buf) + glDeleteRenderbuffers(1, &drawable->depth_buf); + + if (drawable->stencil_buf) + glDeleteRenderbuffers(1, &drawable->stencil_buf); + + free(drawable); +} + +void +e3d_drawable_size_get(E3D_Drawable *drawable, int *w, int *h) +{ + if (drawable) + { + if (w) *w = drawable->w; + if (h) *h = drawable->h; + } + else + { + if (w) *w = 0; + if (h) *h = 0; + } +} + +GLuint +e3d_drawable_texture_id_get(E3D_Drawable *drawable) +{ + return drawable->tex; +} + +GLenum +e3d_drawable_format_get(E3D_Drawable *drawable) +{ + return drawable->format; +} + +static inline GLuint +_texture_id_get(Evas_3D_Texture *texture) +{ + E3D_Texture *tex = (E3D_Texture *)texture->engine_data; + + return tex->tex; +} + +static inline void +_mesh_frame_find(Evas_3D_Mesh *mesh, int frame, + Eina_List **l, Eina_List **r) +{ + Eina_List *left, *right; + Evas_3D_Mesh_Frame *f0, *f1; + + left = mesh->frames; + right = eina_list_next(left); + + while (right) + { + f0 = (Evas_3D_Mesh_Frame *)eina_list_data_get(left); + f1 = (Evas_3D_Mesh_Frame *)eina_list_data_get(right); + + if (frame >= f0->frame && frame <= f1->frame) + break; + + left = right; + right = eina_list_next(left); + } + + if (right == NULL) + { + if (frame <= f0->frame) + { + *l = NULL; + *r = left; + } + else + { + *l = left; + *r = NULL; + } + } + + *l = left; + *r = right; +} + +static inline void +_vertex_attrib_flag_add(E3D_Draw_Data *data, + Evas_3D_Vertex_Attrib attrib, + Eina_Bool blend) +{ + switch (attrib) + { + case EVAS_3D_VERTEX_POSITION: + data->flags |= E3D_SHADER_FLAG_VERTEX_POSITION; + + if (blend) + data->flags |= E3D_SHADER_FLAG_VERTEX_POSITION_BLEND; + break; + case EVAS_3D_VERTEX_NORMAL: + data->flags |= E3D_SHADER_FLAG_VERTEX_NORMAL; + + if (blend) + data->flags |= E3D_SHADER_FLAG_VERTEX_NORMAL_BLEND; + break; + case EVAS_3D_VERTEX_TANGENT: + data->flags |= E3D_SHADER_FLAG_VERTEX_TANGENT; + + if (blend) + data->flags |= E3D_SHADER_FLAG_VERTEX_TANGENT_BLEND; + break; + case EVAS_3D_VERTEX_COLOR: + data->flags |= E3D_SHADER_FLAG_VERTEX_COLOR; + + if (blend) + data->flags |= E3D_SHADER_FLAG_VERTEX_COLOR_BLEND; + break; + case EVAS_3D_VERTEX_TEXCOORD: + data->flags |= E3D_SHADER_FLAG_VERTEX_TEXCOORD; + + if (blend) + data->flags |= E3D_SHADER_FLAG_VERTEX_TEXCOORD_BLEND; + break; + default: + ERR("Invalid vertex attrib."); + break; + } +} + +static inline void +_material_color_flag_add(E3D_Draw_Data *data, Evas_3D_Material_Attrib attrib) +{ + switch (attrib) + { + case EVAS_3D_MATERIAL_AMBIENT: + data->flags |= E3D_SHADER_FLAG_AMBIENT; + break; + case EVAS_3D_MATERIAL_DIFFUSE: + data->flags |= E3D_SHADER_FLAG_DIFFUSE; + break; + case EVAS_3D_MATERIAL_SPECULAR: + data->flags |= E3D_SHADER_FLAG_SPECULAR; + break; + case EVAS_3D_MATERIAL_EMISSION: + data->flags |= E3D_SHADER_FLAG_EMISSION; + break; + case EVAS_3D_MATERIAL_NORMAL: + ERR("Material attribute normal should not be used with color values."); + break; + default: + ERR("Invalid material attrib."); + break; + } +} + +static inline void +_material_texture_flag_add(E3D_Draw_Data *data, Evas_3D_Material_Attrib attrib, Eina_Bool blend) +{ + switch (attrib) + { + case EVAS_3D_MATERIAL_AMBIENT: + data->flags |= E3D_SHADER_FLAG_AMBIENT; + data->flags |= E3D_SHADER_FLAG_AMBIENT_TEXTURE; + + if (blend) + data->flags |= E3D_SHADER_FLAG_AMBIENT_TEXTURE_BLEND; + break; + case EVAS_3D_MATERIAL_DIFFUSE: + data->flags |= E3D_SHADER_FLAG_DIFFUSE; + data->flags |= E3D_SHADER_FLAG_DIFFUSE_TEXTURE; + + if (blend) + data->flags |= E3D_SHADER_FLAG_DIFFUSE_TEXTURE_BLEND; + break; + case EVAS_3D_MATERIAL_SPECULAR: + data->flags |= E3D_SHADER_FLAG_SPECULAR; + data->flags |= E3D_SHADER_FLAG_SPECULAR_TEXTURE; + + if (blend) + data->flags |= E3D_SHADER_FLAG_SPECULAR_TEXTURE_BLEND; + break; + case EVAS_3D_MATERIAL_EMISSION: + data->flags |= E3D_SHADER_FLAG_EMISSION; + data->flags |= E3D_SHADER_FLAG_EMISSION_TEXTURE; + + if (blend) + data->flags |= E3D_SHADER_FLAG_EMISSION_TEXTURE_BLEND; + break; + case EVAS_3D_MATERIAL_NORMAL: + data->flags |= E3D_SHADER_FLAG_NORMAL_TEXTURE; + + if (blend) + data->flags |= E3D_SHADER_FLAG_NORMAL_TEXTURE_BLEND; + break; + default: + ERR("Invalid material attrib."); + break; + } +} + +static inline Eina_Bool +_vertex_attrib_build(E3D_Draw_Data *data, int frame, + const Eina_List *l, const Eina_List *r, + Evas_3D_Vertex_Attrib attrib) +{ + const Evas_3D_Mesh_Frame *f0 = NULL, *f1 = NULL; + + while (l) + { + f0 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(l); + + if (f0->vertices[attrib].data != NULL) + break; + + l = eina_list_prev(l); + f0 = NULL; + } + + while (r) + { + f1 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(r); + + if (f1->vertices[attrib].data != NULL) + break; + + r = eina_list_next(r); + f1 = NULL; + } + + if (f0 == NULL && f1 == NULL) + return EINA_FALSE; + + if (f0 == NULL) + { + f0 = f1; + f1 = NULL; + } + else if (f1 != NULL) + { + if (frame == f0->frame) + { + f1 = NULL; + } + else if (frame == f1->frame) + { + f0 = f1; + f1 = NULL; + } + } + + data->vertices[attrib].vertex0 = f0->vertices[attrib]; + data->vertices[attrib].vertex0.owns_data = EINA_FALSE; + + if (f1) + { + data->vertices[attrib].vertex1 = f1->vertices[attrib]; + data->vertices[attrib].vertex1.owns_data = EINA_FALSE; + data->vertices[attrib].weight = (f1->frame - frame) / (Evas_Real)(f1->frame - f0->frame); + _vertex_attrib_flag_add(data, attrib, EINA_TRUE); + } + else + { + _vertex_attrib_flag_add(data, attrib, EINA_FALSE); + } + + return EINA_TRUE; +} + +static inline Eina_Bool +_material_color_build(E3D_Draw_Data *data, int frame, + const Eina_List *l, const Eina_List *r, + Evas_3D_Material_Attrib attrib) +{ + const Evas_3D_Mesh_Frame *f0 = NULL, *f1 = NULL; + + while (l) + { + f0 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(l); + + if (f0->material && f0->material->attribs[attrib].enable) + break; + + l = eina_list_prev(l); + f0 = NULL; + } + + while (r) + { + f1 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(r); + + if (f1->material && f1->material->attribs[attrib].enable) + break; + + r = eina_list_next(r); + f1 = NULL; + } + + if (f0 == NULL && f1 == NULL) + return EINA_FALSE; + + if (f0 == NULL) + { + f0 = f1; + f1 = NULL; + } + else if (f1 != NULL) + { + if (frame == f0->frame) + { + f1 = NULL; + } + else if (frame == f1->frame) + { + f0 = f1; + f1 = NULL; + } + } + + if (f1 == NULL) + { + data->materials[attrib].color = f0->material->attribs[attrib].color; + + if (attrib == EVAS_3D_MATERIAL_SPECULAR) + data->shininess = f0->material->shininess; + } + else + { + Evas_Real weight; + + weight = (f1->frame - frame) / (Evas_Real)(f1->frame - f0->frame); + evas_color_blend(&data->materials[attrib].color, + &f0->material->attribs[attrib].color, + &f1->material->attribs[attrib].color, + weight); + + if (attrib == EVAS_3D_MATERIAL_SPECULAR) + { + data->shininess = f0->material->shininess * weight + + f1->material->shininess * (1.0 - weight); + } + } + + _material_color_flag_add(data, attrib); + return EINA_TRUE; +} + +static inline Eina_Bool +_material_texture_build(E3D_Draw_Data *data, int frame, + const Eina_List *l, const Eina_List *r, + Evas_3D_Material_Attrib attrib) +{ + const Evas_3D_Mesh_Frame *f0 = NULL, *f1 = NULL; + + while (l) + { + f0 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(l); + + if (f0->material && + f0->material->attribs[attrib].enable && + f0->material->attribs[attrib].texture != NULL) + break; + + l = eina_list_prev(l); + f0 = NULL; + } + + while (r) + { + f1 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(r); + + if (f1->material && + f1->material->attribs[attrib].enable && + f1->material->attribs[attrib].texture != NULL) + break; + + r = eina_list_next(r); + f1 = NULL; + } + + if (f0 == NULL && f1 == NULL) + return EINA_FALSE; + + if (f0 == NULL) + { + f0 = f1; + f1 = NULL; + } + else if (f1 != NULL) + { + if (frame == f0->frame) + { + f1 = NULL; + } + else if (frame == f1->frame) + { + f0 = f1; + f1 = NULL; + } + } + + data->materials[attrib].sampler0 = data->texture_count++; + data->materials[attrib].tex0 = + (E3D_Texture *)f0->material->attribs[attrib].texture->engine_data; + + if (f1) + { + Evas_Real weight = (f1->frame - frame) / (Evas_Real)(f1->frame - f0->frame); + + data->materials[attrib].sampler1 = data->texture_count++; + data->materials[attrib].tex1 = + (E3D_Texture *)f1->material->attribs[attrib].texture->engine_data; + + data->materials[attrib].texture_weight = weight; + + if (attrib == EVAS_3D_MATERIAL_SPECULAR) + { + data->shininess = f0->material->shininess * weight + + f1->material->shininess * (1.0 - weight); + } + + _material_texture_flag_add(data, attrib, EINA_TRUE); + } + else + { + if (attrib == EVAS_3D_MATERIAL_SPECULAR) + data->shininess = f0->material->shininess; + + _material_texture_flag_add(data, attrib, EINA_FALSE); + } + + return EINA_TRUE; +} + +static inline void +_light_build(E3D_Draw_Data *data, + const Evas_3D_Node *light, + const Evas_Mat4 *matrix_eye) +{ + Evas_3D_Light *l = light->data.light.light; + Evas_Vec3 pos, dir; + + if (l == NULL) + return; + + /* Get light node's position. */ + if (l->directional) + { + data->flags |= E3D_SHADER_FLAG_LIGHT_DIRECTIONAL; + + /* Negative Z. */ + evas_vec3_set(&dir, 0.0, 0.0, 1.0); + evas_vec3_quaternion_rotate(&dir, &dir, &light->orientation); + + /* Transform to eye space. */ + evas_vec3_homogeneous_direction_transform(&dir, &dir, matrix_eye); + evas_vec3_normalize(&dir, &dir); + + data->light.position.x = dir.x; + data->light.position.y = dir.y; + data->light.position.z = dir.z; + data->light.position.w = 0.0; + } + else + { + evas_vec3_copy(&pos, &light->position_world); + evas_vec3_homogeneous_position_transform(&pos, &pos, matrix_eye); + + data->light.position.x = pos.x; + data->light.position.y = pos.y; + data->light.position.z = pos.z; + data->light.position.w = 1.0; + + if (l->enable_attenuation) + { + data->flags |= E3D_SHADER_FLAG_LIGHT_ATTENUATION; + + data->light.atten.x = l->atten_const; + data->light.atten.x = l->atten_linear; + data->light.atten.x = l->atten_quad; + } + + if (l->spot_cutoff < 180.0) + { + data->flags |= E3D_SHADER_FLAG_LIGHT_SPOT; + evas_vec3_set(&dir, 0.0, 0.0, -1.0); + evas_vec3_quaternion_rotate(&dir, &dir, &light->orientation); + evas_vec3_homogeneous_direction_transform(&dir, &dir, matrix_eye); + + data->light.spot_dir = dir; + data->light.spot_exp = l->spot_exp; + data->light.spot_cutoff_cos = l->spot_cutoff_cos; + } + } + + data->light.ambient = l->ambient; + data->light.diffuse = l->diffuse; + data->light.specular = l->specular; +} + +static inline Eina_Bool +_mesh_draw_data_build(E3D_Draw_Data *data, + Evas_3D_Mesh *mesh, int frame, + const Evas_Mat4 *matrix_eye, + const Evas_Mat4 *matrix_mv, + const Evas_Mat4 *matrix_mvp, + const Evas_3D_Node *light) +{ + Eina_List *l, *r; + + if (mesh->frames == NULL) + return EINA_FALSE; + + data->mode = mesh->shade_mode; + data->assembly = mesh->assembly; + data->vertex_count = mesh->vertex_count; + data->index_count = mesh->index_count; + data->index_format = mesh->index_format; + data->indices = mesh->indices; + + evas_mat4_copy(&data->matrix_mvp, matrix_mvp); + evas_mat4_copy(&data->matrix_mv, matrix_mv); + + _mesh_frame_find(mesh, frame, &l, &r); + +#define BUILD(type, arg, check) \ + do { \ + Eina_Bool ret = _##type##_build(data, frame, l, r, EVAS_3D_##arg); \ + if (check && !ret) \ + { \ + ERR("Missing attribute : " #arg); \ + return EINA_FALSE; \ + } \ + } while (0) + + if (mesh->shade_mode == EVAS_3D_SHADE_MODE_VERTEX_COLOR) + { + BUILD(vertex_attrib, VERTEX_POSITION, EINA_TRUE); + BUILD(vertex_attrib, VERTEX_COLOR, EINA_TRUE); + } + else if (mesh->shade_mode == EVAS_3D_SHADE_MODE_DIFFUSE) + { + BUILD(vertex_attrib, VERTEX_POSITION, EINA_TRUE); + BUILD(material_color, MATERIAL_DIFFUSE, EINA_TRUE); + BUILD(material_texture, MATERIAL_DIFFUSE, EINA_FALSE); + + if (_flags_need_tex_coord(data->flags)) + BUILD(vertex_attrib, VERTEX_TEXCOORD, EINA_FALSE); + } + else if (mesh->shade_mode == EVAS_3D_SHADE_MODE_FLAT) + { + BUILD(vertex_attrib, VERTEX_POSITION, EINA_TRUE); + BUILD(vertex_attrib, VERTEX_NORMAL, EINA_TRUE); + + BUILD(material_color, MATERIAL_AMBIENT, EINA_FALSE); + BUILD(material_color, MATERIAL_DIFFUSE, EINA_FALSE); + BUILD(material_color, MATERIAL_SPECULAR, EINA_FALSE); + BUILD(material_color, MATERIAL_EMISSION, EINA_FALSE); + + BUILD(material_texture, MATERIAL_AMBIENT, EINA_FALSE); + BUILD(material_texture, MATERIAL_DIFFUSE, EINA_FALSE); + BUILD(material_texture, MATERIAL_SPECULAR, EINA_FALSE); + BUILD(material_texture, MATERIAL_EMISSION, EINA_FALSE); + + _light_build(data, light, matrix_eye); + evas_normal_matrix_get(&data->matrix_normal, matrix_mv); + + if (_flags_need_tex_coord(data->flags)) + BUILD(vertex_attrib, VERTEX_TEXCOORD, EINA_FALSE); + } + else if (mesh->shade_mode == EVAS_3D_SHADE_MODE_PHONG) + { + BUILD(vertex_attrib, VERTEX_POSITION, EINA_TRUE); + BUILD(vertex_attrib, VERTEX_NORMAL, EINA_TRUE); + + BUILD(material_color, MATERIAL_AMBIENT, EINA_FALSE); + BUILD(material_color, MATERIAL_DIFFUSE, EINA_FALSE); + BUILD(material_color, MATERIAL_SPECULAR, EINA_FALSE); + BUILD(material_color, MATERIAL_EMISSION, EINA_FALSE); + + BUILD(material_texture, MATERIAL_AMBIENT, EINA_FALSE); + BUILD(material_texture, MATERIAL_DIFFUSE, EINA_FALSE); + BUILD(material_texture, MATERIAL_SPECULAR, EINA_FALSE); + BUILD(material_texture, MATERIAL_EMISSION, EINA_FALSE); + + _light_build(data, light, matrix_eye); + evas_normal_matrix_get(&data->matrix_normal, matrix_mv); + + if (_flags_need_tex_coord(data->flags)) + BUILD(vertex_attrib, VERTEX_TEXCOORD, EINA_FALSE); + } + else if (mesh->shade_mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + { + BUILD(vertex_attrib, VERTEX_POSITION, EINA_TRUE); + BUILD(vertex_attrib, VERTEX_NORMAL, EINA_TRUE); + BUILD(vertex_attrib, VERTEX_TEXCOORD, EINA_TRUE); + BUILD(material_texture, MATERIAL_NORMAL, EINA_TRUE); + BUILD(vertex_attrib, VERTEX_TANGENT, EINA_FALSE); + + BUILD(material_color, MATERIAL_AMBIENT, EINA_FALSE); + BUILD(material_color, MATERIAL_DIFFUSE, EINA_FALSE); + BUILD(material_color, MATERIAL_SPECULAR, EINA_FALSE); + BUILD(material_color, MATERIAL_EMISSION, EINA_FALSE); + + BUILD(material_texture, MATERIAL_AMBIENT, EINA_FALSE); + BUILD(material_texture, MATERIAL_DIFFUSE, EINA_FALSE); + BUILD(material_texture, MATERIAL_SPECULAR, EINA_FALSE); + BUILD(material_texture, MATERIAL_EMISSION, EINA_FALSE); + + _light_build(data, light, matrix_eye); + evas_normal_matrix_get(&data->matrix_normal, matrix_mv); + } + + return EINA_TRUE; +} + +static inline void +_mesh_draw(E3D_Renderer *renderer, Evas_3D_Mesh *mesh, int frame, Evas_3D_Node *light, + const Evas_Mat4 *matrix_eye, const Evas_Mat4 *matrix_mv, const Evas_Mat4 *matrix_mvp) +{ + E3D_Draw_Data data; + + memset(&data, 0x00, sizeof(E3D_Draw_Data)); + + if (_mesh_draw_data_build(&data, mesh, frame, matrix_eye, matrix_mv, matrix_mvp, light)) + e3d_renderer_draw(renderer, &data); +} + +void +e3d_drawable_scene_render(E3D_Drawable *drawable, E3D_Renderer *renderer, Evas_3D_Scene_Data *data) +{ + Eina_List *l; + Evas_3D_Node *n; + const Evas_Mat4 *matrix_eye; + Evas_3D_Node *light; + + /* Set up render target. */ + e3d_renderer_target_set(renderer, drawable); + e3d_renderer_clear(renderer, &data->bg_color); + + /* Get eye matrix. */ + matrix_eye = &data->camera_node->data.camera.matrix_world_to_eye; + + EINA_LIST_FOREACH(data->mesh_nodes, l, n) + { + Evas_Mat4 matrix_mv; + Evas_Mat4 matrix_mvp; + Eina_Iterator *it; + void *ptr; + + evas_mat4_multiply(&matrix_mv, matrix_eye, &n->data.mesh.matrix_local_to_world); + evas_mat4_multiply(&matrix_mvp, &data->camera_node->data.camera.camera->projection, + &matrix_mv); + + /* TODO: Get most effective light node. */ + light = eina_list_data_get(data->light_nodes); + + it = eina_hash_iterator_data_new(n->data.mesh.node_meshes); + + while (eina_iterator_next(it, &ptr)) + { + Evas_3D_Node_Mesh *nm = (Evas_3D_Node_Mesh *)ptr; + _mesh_draw(renderer, nm->mesh, nm->frame, light, matrix_eye, &matrix_mv, &matrix_mvp); + } + + eina_iterator_free(it); + } + + e3d_renderer_flush(renderer); +} diff --git a/src/modules/evas/engines/gl_common/evas_gl_3d_common.h b/src/modules/evas/engines/gl_common/evas_gl_3d_common.h new file mode 100644 index 0000000000..3f93ad5c4b --- /dev/null +++ b/src/modules/evas/engines/gl_common/evas_gl_3d_common.h @@ -0,0 +1,37 @@ +#ifndef EVAS_GL_COMMON_H +# error You shall not include this header directly +#endif + +typedef struct _E3D_Texture E3D_Texture; +typedef struct _E3D_Drawable E3D_Drawable; +typedef struct _E3D_Renderer E3D_Renderer; + +/* Texture */ +E3D_Texture *e3d_texture_new(void); +void e3d_texture_free(E3D_Texture *texture); + +void e3d_texture_data_set(E3D_Texture *texture, Evas_3D_Color_Format format, Evas_3D_Pixel_Format pixel_format, int w, int h, const void *data); +void e3d_texture_file_set(E3D_Texture *texture, const char *file, const char *key); +Evas_3D_Color_Format e3d_texture_color_format_get(E3D_Texture *texture); +void e3d_texture_size_get(const E3D_Texture *texture, int *w, int *h); + +void e3d_texture_import(E3D_Texture *texture, GLuint tex); +Eina_Bool e3d_texture_is_imported_get(const E3D_Texture *texture); + +void e3d_texture_wrap_set(E3D_Texture *texture, Evas_3D_Wrap_Mode s, Evas_3D_Wrap_Mode t); +void e3d_texture_wrap_get(const E3D_Texture *texture, Evas_3D_Wrap_Mode *s, Evas_3D_Wrap_Mode *t); + +void e3d_texture_filter_set(E3D_Texture *texture, Evas_3D_Texture_Filter min, Evas_3D_Texture_Filter mag); +void e3d_texture_filter_get(const E3D_Texture *texture, Evas_3D_Texture_Filter *min, Evas_3D_Texture_Filter *mag); + +/* Drawable */ +E3D_Drawable *e3d_drawable_new(int w, int h, int alpha, GLenum depth_format, GLenum stencil_format); +void e3d_drawable_free(E3D_Drawable *drawable); +void e3d_drawable_scene_render(E3D_Drawable *drawable, E3D_Renderer *renderer, Evas_3D_Scene_Data *data); +void e3d_drawable_size_get(E3D_Drawable *drawable, int *w, int *h); +GLuint e3d_drawable_texture_id_get(E3D_Drawable *drawable); +GLenum e3d_drawable_format_get(E3D_Drawable *drawable); + +/* Renderer */ +E3D_Renderer *e3d_renderer_new(void); +void e3d_renderer_free(E3D_Renderer *renderer); diff --git a/src/modules/evas/engines/gl_common/evas_gl_3d_private.h b/src/modules/evas/engines/gl_common/evas_gl_3d_private.h new file mode 100644 index 0000000000..2eb5bf48b4 --- /dev/null +++ b/src/modules/evas/engines/gl_common/evas_gl_3d_private.h @@ -0,0 +1,145 @@ +#ifndef EVAS_GL_3D_PRIVATE_H +#define EVAS_GL_3D_PRIVATE_H + +#include "evas_gl_common.h" + +typedef struct _E3D_Program E3D_Program; +typedef struct _E3D_Draw_Data E3D_Draw_Data; +typedef unsigned long E3D_Shader_Flag; + +#define E3D_SHADER_FLAG_NORMALIZE_NORMALS (1 << 0) +#define E3D_SHADER_FLAG_VERTEX_POSITION (1 << 1) +#define E3D_SHADER_FLAG_VERTEX_POSITION_BLEND (1 << 2) +#define E3D_SHADER_FLAG_VERTEX_NORMAL (1 << 3) +#define E3D_SHADER_FLAG_VERTEX_NORMAL_BLEND (1 << 4) +#define E3D_SHADER_FLAG_VERTEX_TANGENT (1 << 5) +#define E3D_SHADER_FLAG_VERTEX_TANGENT_BLEND (1 << 6) +#define E3D_SHADER_FLAG_VERTEX_COLOR (1 << 7) +#define E3D_SHADER_FLAG_VERTEX_COLOR_BLEND (1 << 8) +#define E3D_SHADER_FLAG_VERTEX_TEXCOORD (1 << 9) +#define E3D_SHADER_FLAG_VERTEX_TEXCOORD_BLEND (1 << 10) +#define E3D_SHADER_FLAG_LIGHT_DIRECTIONAL (1 << 11) +#define E3D_SHADER_FLAG_LIGHT_SPOT (1 << 12) +#define E3D_SHADER_FLAG_LIGHT_ATTENUATION (1 << 13) +#define E3D_SHADER_FLAG_AMBIENT (1 << 14) +#define E3D_SHADER_FLAG_DIFFUSE (1 << 15) +#define E3D_SHADER_FLAG_SPECULAR (1 << 16) +#define E3D_SHADER_FLAG_EMISSION (1 << 17) +#define E3D_SHADER_FLAG_DIFFUSE_TEXTURE (1 << 19) +#define E3D_SHADER_FLAG_AMBIENT_TEXTURE (1 << 20) +#define E3D_SHADER_FLAG_SPECULAR_TEXTURE (1 << 21) +#define E3D_SHADER_FLAG_EMISSION_TEXTURE (1 << 22) +#define E3D_SHADER_FLAG_NORMAL_TEXTURE (1 << 23) +#define E3D_SHADER_FLAG_DIFFUSE_TEXTURE_BLEND (1 << 24) +#define E3D_SHADER_FLAG_AMBIENT_TEXTURE_BLEND (1 << 25) +#define E3D_SHADER_FLAG_SPECULAR_TEXTURE_BLEND (1 << 26) +#define E3D_SHADER_FLAG_EMISSION_TEXTURE_BLEND (1 << 27) +#define E3D_SHADER_FLAG_NORMAL_TEXTURE_BLEND (1 << 28) + +static inline Eina_Bool +_flags_need_tex_coord(E3D_Shader_Flag flags) +{ + return (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE) || + (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE) || + (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE) || + (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE) || + (flags & E3D_SHADER_FLAG_NORMAL_TEXTURE); +} + +struct _E3D_Draw_Data +{ + E3D_Shader_Flag flags; + Evas_3D_Shade_Mode mode; + + Evas_Mat4 matrix_mvp; + Evas_Mat4 matrix_mv; + Evas_Mat3 matrix_normal; + + struct { + Evas_3D_Vertex_Buffer vertex0; + Evas_3D_Vertex_Buffer vertex1; + Evas_Real weight; + } vertices[EVAS_3D_VERTEX_ATTRIB_COUNT]; + + Evas_3D_Vertex_Assembly assembly; + int vertex_count; + int index_count; + Evas_3D_Index_Format index_format; + const void *indices; + + GLint texture_count; + + struct { + Evas_Color color; + GLint sampler0; + GLint sampler1; + E3D_Texture *tex0; + E3D_Texture *tex1; + Evas_Real texture_weight; + } materials[EVAS_3D_MATERIAL_ATTRIB_COUNT]; + + Evas_Real shininess; + + struct { + Evas_Vec4 position; + Evas_Vec3 spot_dir; + Evas_Real spot_exp; + Evas_Real spot_cutoff_cos; + Evas_Vec3 atten; + Evas_Color ambient; + Evas_Color diffuse; + Evas_Color specular; + } light; +}; + +struct _E3D_Texture +{ + int w, h; + + Eina_Bool is_imported; + GLuint tex; + GLenum format; + + Eina_Bool wrap_dirty; + GLenum wrap_s; + GLenum wrap_t; + + Eina_Bool filter_dirty; + GLenum filter_min; + GLenum filter_mag; +}; + +struct _E3D_Drawable +{ + int w, h; + int alpha; + GLenum format; + GLenum depth_format; + GLenum stencil_format; + + GLuint tex; + GLuint fbo; + GLuint depth_stencil_buf; + GLuint depth_buf; + GLuint stencil_buf; +}; + +/* Texture internal functions. */ +void e3d_texture_param_update(E3D_Texture *texture); + +/* Program */ +E3D_Program *e3d_program_new(Evas_3D_Shade_Mode mode, E3D_Shader_Flag flags); +void e3d_program_free(E3D_Program *program); +GLuint e3d_program_id_get(const E3D_Program *program); +Evas_3D_Shade_Mode e3d_program_shade_mode_get(const E3D_Program *program); +E3D_Shader_Flag e3d_program_shader_flags_get(const E3D_Program *program); +void e3d_program_uniform_upload(E3D_Program *program, const E3D_Draw_Data *data); + +/* Renderer */ +void e3d_renderer_target_set(E3D_Renderer *renderer, E3D_Drawable *target); +void e3d_renderer_viewport_set(E3D_Renderer *renderer, int x, int y, int w, int h); +void e3d_renderer_clear(E3D_Renderer *renderer, const Evas_Color *color); +void e3d_renderer_draw(E3D_Renderer *renderer, E3D_Draw_Data *data); +void e3d_renderer_flush(E3D_Renderer *renderer); + +#endif /* EVAS_GL_3D_PRIVATE_H */ diff --git a/src/modules/evas/engines/gl_common/evas_gl_3d_renderer.c b/src/modules/evas/engines/gl_common/evas_gl_3d_renderer.c new file mode 100644 index 0000000000..0f22655c91 --- /dev/null +++ b/src/modules/evas/engines/gl_common/evas_gl_3d_renderer.c @@ -0,0 +1,284 @@ +#include "evas_gl_private.h" +#include "evas_gl_3d_private.h" + +#define E3D_MAX_TEXTURE_COUNT 8 +#define E3D_MAX_VERTEX_ATTRIB_COUNT 8 + +struct _E3D_Renderer +{ + Eina_List *programs; + + GLuint fbo; + GLuint program; + E3D_Texture *textures[E3D_MAX_TEXTURE_COUNT]; + + Eina_Bool vertex_attrib_enable[E3D_MAX_VERTEX_ATTRIB_COUNT]; + Eina_Bool depth_test_enable; +}; + +static inline GLenum +_gl_assembly_get(Evas_3D_Vertex_Assembly assembly) +{ + switch (assembly) + { + case EVAS_3D_VERTEX_ASSEMBLY_POINTS: + return GL_POINTS; + case EVAS_3D_VERTEX_ASSEMBLY_LINES: + return GL_LINES; + case EVAS_3D_VERTEX_ASSEMBLY_LINE_STRIP: + return GL_LINE_STRIP; + case EVAS_3D_VERTEX_ASSEMBLY_LINE_LOOP: + return GL_LINE_LOOP; + case EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES: + return GL_TRIANGLES; + case EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_STRIP: + return GL_TRIANGLE_STRIP; + case EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_FAN: + return GL_TRIANGLE_FAN; + default: + return GL_NONE; + } +} + +static inline void +_renderer_vertex_attrib_array_enable(E3D_Renderer *renderer, int index) +{ + if (renderer->vertex_attrib_enable[index]) + return; + + glEnableVertexAttribArray(index); + renderer->vertex_attrib_enable[index] = EINA_TRUE; +} + +static inline void +_renderer_vertex_attrib_array_disable(E3D_Renderer *renderer, int index) +{ + if (!renderer->vertex_attrib_enable[index]) + return; + + glDisableVertexAttribArray(index); + renderer->vertex_attrib_enable[index] = EINA_FALSE; +} + +static inline void +_renderer_vertex_attrib_pointer_set(E3D_Renderer *renderer EINA_UNUSED, int index, + const Evas_3D_Vertex_Buffer *buffer) +{ + glVertexAttribPointer(index, buffer->element_count, GL_FLOAT, + GL_FALSE, buffer->stride, buffer->data); +} + +static inline void +_renderer_elements_draw(E3D_Renderer *renderer EINA_UNUSED, Evas_3D_Vertex_Assembly assembly, + int count, Evas_3D_Index_Format format, const void *indices) +{ + GLenum mode = _gl_assembly_get(assembly); + + if (format == EVAS_3D_INDEX_FORMAT_UNSIGNED_BYTE) + glDrawElements(mode, count, GL_UNSIGNED_BYTE, indices); + else if (format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + glDrawElements(mode, count, GL_UNSIGNED_SHORT, indices); +} + +static inline void +_renderer_array_draw(E3D_Renderer *renderer EINA_UNUSED, + Evas_3D_Vertex_Assembly assembly, int count) +{ + GLenum mode = _gl_assembly_get(assembly); + + glDrawArrays(mode, 0, count); +} + +static inline void +_renderer_program_use(E3D_Renderer *renderer ,E3D_Program *program) +{ + GLuint prog = e3d_program_id_get(program); + + if (renderer->program != prog) + { + glUseProgram(prog); + renderer->program = prog; + } +} + +static inline void +_renderer_texture_bind(E3D_Renderer *renderer, E3D_Draw_Data *data) +{ + int i; + + for (i = 0; i < EVAS_3D_MATERIAL_ATTRIB_COUNT; i++) + { + if (data->materials[i].tex0) + { + if (renderer->textures[data->materials[i].sampler0] != data->materials[i].tex0) + { + glActiveTexture(GL_TEXTURE0 + data->materials[i].sampler0); + glBindTexture(GL_TEXTURE_2D, data->materials[i].tex0->tex); + e3d_texture_param_update(data->materials[i].tex0); + + renderer->textures[data->materials[i].sampler0] = data->materials[i].tex0; + } + } + + if (data->materials[i].tex1) + { + if (renderer->textures[data->materials[i].sampler1] != data->materials[i].tex1) + { + glActiveTexture(GL_TEXTURE0 + data->materials[i].sampler1); + glBindTexture(GL_TEXTURE_2D, data->materials[i].tex1->tex); + e3d_texture_param_update(data->materials[i].tex1); + + renderer->textures[data->materials[i].sampler1] = data->materials[i].tex1; + } + } + } +} + +static inline void +_renderer_depth_test_enable(E3D_Renderer *renderer, Eina_Bool enable) +{ + if (renderer->depth_test_enable != enable) + { + if (enable) + { + glEnable(GL_DEPTH_TEST); + /* Use default depth func. */ + } + else + { + glDisable(GL_DEPTH_TEST); + } + + renderer->depth_test_enable = enable; + } +} + +E3D_Renderer * +e3d_renderer_new(void) +{ + E3D_Renderer *renderer = NULL; + + renderer = (E3D_Renderer *)calloc(1, sizeof(E3D_Renderer)); + + if (renderer == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + return renderer; +} + +void +e3d_renderer_free(E3D_Renderer *renderer) +{ + Eina_List *l; + E3D_Program *p; + + EINA_LIST_FOREACH(renderer->programs, l, p) + { + e3d_program_free(p); + } + + eina_list_free(renderer->programs); +} + +void +e3d_renderer_target_set(E3D_Renderer *renderer, E3D_Drawable *target) +{ + if (renderer->fbo == target->fbo) + return; + + glBindFramebuffer(GL_FRAMEBUFFER, target->fbo); + glViewport(0, 0, target->w, target->h); + renderer->fbo = target->fbo; +} + +void +e3d_renderer_clear(E3D_Renderer *renderer EINA_UNUSED, const Evas_Color *color) +{ + glClearColor(color->r, color->g, color->b, color->a); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void +e3d_renderer_draw(E3D_Renderer *renderer, E3D_Draw_Data *data) +{ + Eina_List *l; + E3D_Program *program = NULL, *p; + int i, index = 0; + + _renderer_depth_test_enable(renderer, EINA_TRUE); + + EINA_LIST_FOREACH(renderer->programs, l, p) + { + if (e3d_program_shade_mode_get(p) == data->mode && + e3d_program_shader_flags_get(p) == data->flags) + { + program = p; + break; + } + } + + if (program == NULL) + { + program = e3d_program_new(data->mode, data->flags); + + if (program == NULL) + { + ERR("Failed to create shader program."); + return; + } + + renderer->programs = eina_list_append(renderer->programs, program); + } + + _renderer_program_use(renderer, program); + e3d_program_uniform_upload(program, data); + _renderer_texture_bind(renderer, data); + + /* Set up vertex attrib pointers. */ + index = 0; + + for (i = 0; i < EVAS_3D_VERTEX_ATTRIB_COUNT; i++) + { + const Evas_3D_Vertex_Buffer *buffer; + + buffer = &data->vertices[i].vertex0; + + if (buffer->data != NULL) + { + _renderer_vertex_attrib_array_enable(renderer, index); + _renderer_vertex_attrib_pointer_set(renderer, index, buffer); + index++; + } + + buffer = &data->vertices[i].vertex1; + + if (buffer->data != NULL) + { + _renderer_vertex_attrib_array_enable(renderer, index); + _renderer_vertex_attrib_pointer_set(renderer, index, buffer); + index++; + } + } + + while (index < E3D_MAX_VERTEX_ATTRIB_COUNT) + _renderer_vertex_attrib_array_disable(renderer, index++); + + if (data->indices) + { + _renderer_elements_draw(renderer, data->assembly, data->index_count, + data->index_format, data->indices); + } + else + { + _renderer_array_draw(renderer, data->assembly, data->vertex_count); + } +} + +void +e3d_renderer_flush(E3D_Renderer *renderer EINA_UNUSED) +{ + glFlush(); +} diff --git a/src/modules/evas/engines/gl_common/evas_gl_3d_renderer.h b/src/modules/evas/engines/gl_common/evas_gl_3d_renderer.h new file mode 100644 index 0000000000..b06eb8b0d8 --- /dev/null +++ b/src/modules/evas/engines/gl_common/evas_gl_3d_renderer.h @@ -0,0 +1,6 @@ +#ifndef EVAS_GL_3D_RENDERER_H +#define EVAS_GL_3D_RENDERER_H + + + +#endif /* EVAS_GL_3D_RENDERER_H */ diff --git a/src/modules/evas/engines/gl_common/evas_gl_3d_shader.c b/src/modules/evas/engines/gl_common/evas_gl_3d_shader.c new file mode 100644 index 0000000000..a9f63ecbb6 --- /dev/null +++ b/src/modules/evas/engines/gl_common/evas_gl_3d_shader.c @@ -0,0 +1,1482 @@ +#include "evas_gl_3d_private.h" + +typedef enum _E3D_Uniform +{ + E3D_UNIFORM_MATRIX_MVP, + E3D_UNIFORM_MATRIX_MV, + E3D_UNIFORM_MATRIX_NORMAL, + + E3D_UNIFORM_POSITION_WEIGHT, + E3D_UNIFORM_NORMAL_WEIGHT, + E3D_UNIFORM_TANGENT_WEIGHT, + E3D_UNIFORM_COLOR_WEIGHT, + E3D_UNIFORM_TEXCOORD_WEIGHT, + + E3D_UNIFORM_TEXTURE_WEIGHT_AMBIENT, + E3D_UNIFORM_TEXTURE_WEIGHT_DIFFUSE, + E3D_UNIFORM_TEXTURE_WEIGHT_SPECULAR, + E3D_UNIFORM_TEXTURE_WEIGHT_EMISSION, + E3D_UNIFORM_TEXTURE_WEIGHT_NORMAL, + + E3D_UNIFORM_TEXTURE_AMBIENT0, + E3D_UNIFORM_TEXTURE_DIFFUSE0, + E3D_UNIFORM_TEXTURE_SPECULAR0, + E3D_UNIFORM_TEXTURE_EMISSION0, + E3D_UNIFORM_TEXTURE_NORMAL0, + + E3D_UNIFORM_TEXTURE_AMBIENT1, + E3D_UNIFORM_TEXTURE_DIFFUSE1, + E3D_UNIFORM_TEXTURE_SPECULAR1, + E3D_UNIFORM_TEXTURE_EMISSION1, + E3D_UNIFORM_TEXTURE_NORMAL1, + + E3D_UNIFORM_LIGHT_POSITION, + E3D_UNIFORM_LIGHT_SPOT_DIR, + E3D_UNIFORM_LIGHT_SPOT_EXP, + E3D_UNIFORM_LIGHT_SPOT_CUTOFF_COS, + E3D_UNIFORM_LIGHT_ATTENUATION, + E3D_UNIFORM_LIGHT_AMBIENT, + E3D_UNIFORM_LIGHT_DIFFUSE, + E3D_UNIFORM_LIGHT_SPECULAR, + + E3D_UNIFORM_MATERIAL_AMBIENT, + E3D_UNIFORM_MATERIAL_DIFFUSE, + E3D_UNIFORM_MATERIAL_SPECULAR, + E3D_UNIFORM_MATERIAL_EMISSION, + E3D_UNIFORM_MATERIAL_SHININESS, + + E3D_UNIFORM_COUNT, +} E3D_Uniform; + +typedef struct _E3D_Shader_String +{ + char *str; + int size; + int count; +} E3D_Shader_String; + +struct _E3D_Program +{ + GLuint vert; + GLuint frag; + GLuint prog; + + E3D_Shader_Flag flags; + Evas_3D_Shade_Mode mode; + + GLint uniform_locations[E3D_UNIFORM_COUNT]; +}; + +static void _shader_string_init(E3D_Shader_String *string) +{ + string->str = NULL; + string->size = 0; + string->count = 0; +} + +static void _shader_string_fini(E3D_Shader_String *string) +{ + if (string->str) + { + free(string->str); + _shader_string_init(string); + } +} + +void _shader_string_add(E3D_Shader_String *shader, const char *str) +{ + int len; + + if (str == NULL) + return; + + len = strlen(str); + + if ((shader->size - shader->count) < len) + { + int new_size = (shader->count + len) * 2; + char *new_buf = (char *)malloc(new_size + 1); + + if (shader->str) + { + memcpy(new_buf, shader->str, sizeof(char) * shader->count); + free(shader->str); + } + + shader->str = new_buf; + shader->size = new_size; + } + + memcpy(&shader->str[shader->count], str, len + 1); + shader->count += len; +} + +#define ADD_LINE(str) _shader_string_add(shader, str"\n") + +static void +_vertex_shader_string_variable_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode, E3D_Shader_Flag flags) +{ + ADD_LINE("uniform mat4 uMatrixMvp;"); + + /* Vertex attributes. */ + if (flags & E3D_SHADER_FLAG_VERTEX_POSITION) + ADD_LINE("attribute vec4 aPosition0;"); + + if (flags & E3D_SHADER_FLAG_VERTEX_POSITION_BLEND) + { + ADD_LINE("attribute vec4 aPosition1;"); + ADD_LINE("uniform float uPositionWeight;"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_NORMAL) + ADD_LINE("attribute vec4 aNormal0;"); + + if (flags & E3D_SHADER_FLAG_VERTEX_NORMAL_BLEND) + { + ADD_LINE("attribute vec4 aNormal1;"); + ADD_LINE("uniform float uNormalWeight;"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_TANGENT) + ADD_LINE("attribute vec4 aTangent0;"); + + if (flags & E3D_SHADER_FLAG_VERTEX_TANGENT_BLEND) + { + ADD_LINE("attribute vec4 aTangent1;"); + ADD_LINE("uniform float uTangentWeight;"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_COLOR) + ADD_LINE("attribute vec4 aColor0;"); + + if (flags & E3D_SHADER_FLAG_VERTEX_COLOR_BLEND) + { + ADD_LINE("attribute vec4 aColor1;"); + ADD_LINE("uniform float uColorWeight;"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_TEXCOORD) + ADD_LINE("attribute vec4 aTexCoord0;"); + + if (flags & E3D_SHADER_FLAG_VERTEX_TEXCOORD_BLEND) + { + ADD_LINE("attribute vec4 aTexCoord1;"); + ADD_LINE("uniform float uTexCoordWeight;"); + } + + /* Texture coordinate. */ + if (_flags_need_tex_coord(flags)) + ADD_LINE("varying vec2 vTexCoord;"); + + /* Variables for each shade modes. */ + if (mode == EVAS_3D_SHADE_MODE_VERTEX_COLOR) + { + ADD_LINE("varying vec4 vColor;"); + } + else if (mode == EVAS_3D_SHADE_MODE_DIFFUSE) + { + /* Nothing to declare. */ + } + else if (mode == EVAS_3D_SHADE_MODE_FLAT) + { + ADD_LINE("uniform mat3 uMatrixNormal;"); + ADD_LINE("uniform mat4 uMatrixModelview;"); + ADD_LINE("uniform vec4 uLightPosition;"); + + ADD_LINE("varying vec2 vFactor;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_SPOT) + { + ADD_LINE("uniform vec3 uLightSpotDir;"); + ADD_LINE("uniform float uLightSpotExp;"); + ADD_LINE("uniform float uLightSpotCutoffCos;"); + } + + if (flags & E3D_SHADER_FLAG_SPECULAR) + ADD_LINE("uniform float uMaterialShininess;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("uniform vec3 uLightAtten;"); + } + else if (mode == EVAS_3D_SHADE_MODE_PHONG || mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + { + ADD_LINE("uniform mat3 uMatrixNormal;"); + ADD_LINE("uniform mat4 uMatrixModelview;"); + ADD_LINE("uniform vec4 uLightPosition;"); + ADD_LINE("varying vec3 vLightVector;"); + ADD_LINE("varying vec3 vLightHalfVector;"); + + if (mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + ADD_LINE("varying vec3 vEyeVector;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("varying float vLightDist;"); + + if (mode == EVAS_3D_SHADE_MODE_PHONG || (flags & E3D_SHADER_FLAG_VERTEX_TANGENT) == 0) + ADD_LINE("varying vec3 vNormal;"); + } +} + +static void +_vertex_shader_string_func_flat_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode EINA_UNUSED, E3D_Shader_Flag flags) +{ + ADD_LINE("void vertexFlat(vec4 position, vec3 normal) {"); + + ADD_LINE("vec3 lv;"); + ADD_LINE("float factor;"); + + ADD_LINE("normal = uMatrixNormal * normal;"); + ADD_LINE("position = uMatrixModelview * position;"); + + if (flags & E3D_SHADER_FLAG_NORMALIZE_NORMALS) + ADD_LINE("normal = normalize(normal);"); + + if (flags & E3D_SHADER_FLAG_LIGHT_DIRECTIONAL) + { + ADD_LINE("lv = uLightPosition.xyz;"); + } + else + { + ADD_LINE("lv = uLightPosition.xyz - position.xyz;"); + ADD_LINE("lv = normalize(lv);"); + } + + ADD_LINE("factor = max(dot(lv, normal), 0.0);"); + + if (flags & E3D_SHADER_FLAG_LIGHT_SPOT) + { + ADD_LINE("float f = dot(-lv, uLightSpotDir);"); + ADD_LINE("if (f > uLightSpotCutoffCos)"); + ADD_LINE("factor *= pow(f, uLightSpotExp);"); + ADD_LINE("else"); + ADD_LINE("factor = 0.0;"); + } + + ADD_LINE("if (factor > 0.0) {"); + + /* Diffuse term. */ + if (flags & E3D_SHADER_FLAG_DIFFUSE) + ADD_LINE("vFactor.x = factor;"); + else + ADD_LINE("vFactor.x = 0.0;"); + + /* Specular term. */ + if (flags & E3D_SHADER_FLAG_SPECULAR) + { + ADD_LINE("vec3 hv = normalize(normalize(-position.xyz) + lv);"); + ADD_LINE("factor = pow(max(dot(hv, normal), 0.0), uMaterialShininess);"); + ADD_LINE("vFactor.y = factor;"); + } + + ADD_LINE("} else {"); + ADD_LINE("vFactor = vec2(0.0, 0.0);"); + ADD_LINE("}"); + + /* Light attenuation. */ + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + { + ADD_LINE("float dist = length(lv);"); + ADD_LINE("vFactor /= dot(uLightAtten, vec3(1.0, dist, dist * dist));"); + } + + ADD_LINE("}"); +} + +static void +_vertex_shader_string_func_phong_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode EINA_UNUSED, E3D_Shader_Flag flags) +{ + ADD_LINE("void vertexPhong(vec4 position, vec3 normal) {"); + + ADD_LINE("normal = uMatrixNormal * normal;"); + ADD_LINE("position = uMatrixModelview * position;"); + + if (flags & E3D_SHADER_FLAG_NORMALIZE_NORMALS) + ADD_LINE("normal = normalize(normal);"); + + if (flags & E3D_SHADER_FLAG_LIGHT_DIRECTIONAL) + { + ADD_LINE("vLightVector = uLightPosition.xyz;"); + } + else + { + ADD_LINE("vLightVector = uLightPosition.xyz - position.xyz;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("vLightDist = length(vLightVector);"); + + ADD_LINE("vLightVector = normalize(vLightVector);"); + } + + ADD_LINE("vLightHalfVector = normalize(normalize(-position.xyz) + vLightVector);"); + ADD_LINE("vNormal = normal;"); + ADD_LINE("}"); +} + +static void +_vertex_shader_string_func_normal_map_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode EINA_UNUSED, + E3D_Shader_Flag flags) +{ + if ((flags & E3D_SHADER_FLAG_VERTEX_TANGENT) == 0) + { + ADD_LINE("void vertexNormalMap(vec4 position, vec3 normal) {"); + + ADD_LINE("normal = uMatrixNormal * normal;"); + ADD_LINE("position = uMatrixModelview * position;"); + ADD_LINE("vEyeVector = normalize(-position.xyz);"); + + if (flags & E3D_SHADER_FLAG_NORMALIZE_NORMALS) + ADD_LINE("normal = normalize(normal);"); + + if (flags & E3D_SHADER_FLAG_LIGHT_DIRECTIONAL) + { + ADD_LINE("vLightVector = uLightPosition.xyz;"); + } + else + { + ADD_LINE("vLightVector = uLightPosition.xyz - position.xyz;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("vLightDist = length(vLightVector);"); + + ADD_LINE("vLightVector = normalize(vLightVector);"); + } + + ADD_LINE("vLightHalfVector = normalize(vEyeVector + vLightVector);"); + ADD_LINE("vNormal = normal;"); + ADD_LINE("}"); + } + else + { + ADD_LINE("void vertexNormalMap(vec4 position, vec3 normal, vec3 tangent) {"); + + ADD_LINE("vec3 n = normalize(uMatrixNormal * normal);"); + ADD_LINE("vec3 t = normalize(uMatrixNormal * tangent);"); + ADD_LINE("vec3 b = cross(n, t);"); + ADD_LINE("vec3 tmp;"); + + ADD_LINE("position = uMatrixModelview * position;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_DIRECTIONAL) + { + ADD_LINE("vec3 lightDir = uLightPosition.xyz;"); + } + else + { + ADD_LINE("vec3 lightDir = uLightPosition.xyz - position.xyz;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("vLightDist = length(lightDir);"); + + ADD_LINE("lightDir = normalize(lightDir);"); + } + + ADD_LINE("tmp.x = dot(lightDir, t);"); + ADD_LINE("tmp.y = dot(lightDir, b);"); + ADD_LINE("tmp.z = dot(lightDir, n);"); + ADD_LINE("vLightVector = tmp;"); + + ADD_LINE("tmp.x = dot(position.xyz, t);"); + ADD_LINE("tmp.y = dot(position.xyz, b);"); + ADD_LINE("tmp.z = dot(position.xyz, n);"); + ADD_LINE("vEyeVector = normalize(tmp);"); + + ADD_LINE("vec3 hv = normalize(normalize(-position.xyz) + lightDir);"); + ADD_LINE("tmp.x = dot(hv, t);"); + ADD_LINE("tmp.y = dot(hv, b);"); + ADD_LINE("tmp.z = dot(hv, n);"); + ADD_LINE("vLightHalfVector = tmp;"); + + ADD_LINE("}"); + } +} + +static void +_vertex_shader_string_get(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode, E3D_Shader_Flag flags) +{ + /* Add variables - vertex attributes. */ + _vertex_shader_string_variable_add(shader, mode, flags); + + /* Add functions. */ + if (mode == EVAS_3D_SHADE_MODE_FLAT) + _vertex_shader_string_func_flat_add(shader, mode, flags); + else if (mode == EVAS_3D_SHADE_MODE_PHONG) + _vertex_shader_string_func_phong_add(shader, mode, flags); + else if (mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + _vertex_shader_string_func_normal_map_add(shader, mode, flags); + + ADD_LINE("void main() {"); + + /* Process vertex attributes. */ + if (flags & E3D_SHADER_FLAG_VERTEX_POSITION_BLEND) + { + ADD_LINE("vec4 position = aPosition0 * uPositionWeight + "); + ADD_LINE("aPosition1 * (1.0 - uPositionWeight);"); + ADD_LINE("position = vec4(position.xyz, 1.0);"); + } + else if (flags & E3D_SHADER_FLAG_VERTEX_POSITION) + { + ADD_LINE("vec4 position = vec4(aPosition0.xyz, 1.0);"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_NORMAL_BLEND) + { + ADD_LINE("vec3 normal = aNormal0.xyz * uNormalWeight + "); + ADD_LINE("aNormal1.xyz * (1.0 - uNormalWeight);"); + } + else if (flags & E3D_SHADER_FLAG_VERTEX_NORMAL) + { + ADD_LINE("vec3 normal = aNormal0.xyz;"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_TANGENT_BLEND) + { + ADD_LINE("vec3 tangent = aTangent0.xyz * uTangentWeight + "); + ADD_LINE("aTangent1.xyz * (1.0 - uTangentWeight);"); + } + else if (flags & E3D_SHADER_FLAG_VERTEX_TANGENT) + { + ADD_LINE("vec3 tangent = aTangent0.xyz;"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_COLOR_BLEND) + { + ADD_LINE("vec4 color = aColor0 * uColorWeight + aColor1 * (1.0 - uColorWeight);"); + } + else if (flags & E3D_SHADER_FLAG_VERTEX_COLOR) + { + ADD_LINE("vec4 color = aColor0;"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_TEXCOORD_BLEND) + { + ADD_LINE("vTexCoord = aTexCoord0.st * uTexCoordWeight + "); + ADD_LINE("aTexCoord1.st * (1.0 - uTexCoordWeight);"); + } + else if (flags & E3D_SHADER_FLAG_VERTEX_TEXCOORD) + { + ADD_LINE("vTexCoord = aTexCoord0.st;"); + } + + /* Transform vertex position. */ + ADD_LINE("gl_Position = uMatrixMvp * position;"); + + /* Process according to the shade mode. */ + if (mode == EVAS_3D_SHADE_MODE_VERTEX_COLOR) + { + ADD_LINE("vColor = color;"); + } + else if (mode == EVAS_3D_SHADE_MODE_FLAT) + { + ADD_LINE("vertexFlat(position, normal);"); + } + else if (mode == EVAS_3D_SHADE_MODE_PHONG) + { + ADD_LINE("vertexPhong(position, normal);"); + } + else if (mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + { + if (flags & E3D_SHADER_FLAG_VERTEX_TANGENT) + ADD_LINE("vertexNormalMap(position, normal, tangent);"); + else + ADD_LINE("vertexNormalMap(position, normal);"); + } + + ADD_LINE("}"); +} + +static void +_fragment_shader_string_variable_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode, E3D_Shader_Flag flags) +{ + /* Texture coordinate. */ + if (_flags_need_tex_coord(flags)) + ADD_LINE("varying vec2 vTexCoord;"); + + /* Materials. */ + if (flags & E3D_SHADER_FLAG_DIFFUSE) + { + ADD_LINE("uniform vec4 uMaterialDiffuse;"); + + if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE) + { + ADD_LINE("uniform sampler2D uTextureDiffuse0;"); + } + + if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE_BLEND) + { + ADD_LINE("uniform sampler2D uTextureDiffuse1;"); + ADD_LINE("uniform float uTextureDiffuseWeight;"); + } + } + + if (flags & E3D_SHADER_FLAG_SPECULAR) + { + ADD_LINE("uniform vec4 uMaterialSpecular;"); + ADD_LINE("uniform float uMaterialShininess;"); + + if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE) + { + ADD_LINE("uniform sampler2D uTextureSpecular0;"); + } + + if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE_BLEND) + { + ADD_LINE("uniform sampler2D uTextureSpecular1;"); + ADD_LINE("uniform float uTextureSpecularWeight;"); + } + } + + if (flags & E3D_SHADER_FLAG_AMBIENT) + { + ADD_LINE("uniform vec4 uMaterialAmbient;"); + + if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE) + { + ADD_LINE("uniform sampler2D uTextureAmbient0;"); + } + + if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE_BLEND) + { + ADD_LINE("uniform sampler2D uTextureAmbient1;"); + ADD_LINE("uniform float uTextureAmbientWeight;"); + } + } + + if (flags & E3D_SHADER_FLAG_EMISSION) + { + ADD_LINE("uniform vec4 uMaterialEmission;"); + + if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE) + { + ADD_LINE("uniform sampler2D uTextureEmission0;"); + } + + if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE_BLEND) + { + ADD_LINE("uniform sampler2D uTextureEmission1;"); + ADD_LINE("uniform float uTextureEmissionWeight;"); + } + } + + if (mode == EVAS_3D_SHADE_MODE_VERTEX_COLOR) + { + ADD_LINE("varying vec4 vColor;"); + } + else if (mode == EVAS_3D_SHADE_MODE_DIFFUSE) + { + /* Nothing to declare. */ + } + else if (mode == EVAS_3D_SHADE_MODE_FLAT) + { + ADD_LINE("varying vec2 vFactor;"); + + if (flags & E3D_SHADER_FLAG_DIFFUSE) + ADD_LINE("uniform vec4 uLightDiffuse;"); + + if (flags & E3D_SHADER_FLAG_SPECULAR) + ADD_LINE("uniform vec4 uLightSpecular;"); + + if (flags & E3D_SHADER_FLAG_AMBIENT) + ADD_LINE("uniform vec4 uLightAmbient;"); + } + else if (mode == EVAS_3D_SHADE_MODE_PHONG || mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + { + ADD_LINE("varying vec3 vLightVector;"); + ADD_LINE("varying vec3 vLightHalfVector;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_SPOT) + { + ADD_LINE("uniform vec3 uLightSpotDir;"); + ADD_LINE("uniform float uLightSpotExp;"); + ADD_LINE("uniform float uLightSpotCutoffCos;"); + } + + if (flags & E3D_SHADER_FLAG_DIFFUSE) + ADD_LINE("uniform vec4 uLightDiffuse;"); + + if (flags & E3D_SHADER_FLAG_SPECULAR) + ADD_LINE("uniform vec4 uLightSpecular;"); + + if (flags & E3D_SHADER_FLAG_AMBIENT) + ADD_LINE("uniform vec4 uLightAmbient;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("varying float vLightDist;"); + + if (mode == EVAS_3D_SHADE_MODE_PHONG) + { + ADD_LINE("varying vec3 vNormal;"); + } + else /* Normal map. */ + { + ADD_LINE("uniform sampler2D uTextureNormal0;"); + + if (flags & E3D_SHADER_FLAG_NORMAL_TEXTURE_BLEND) + { + ADD_LINE("uniform sampler2D uTextureNormal1;"); + ADD_LINE("uniform float uTextureNormalWeight;"); + } + + ADD_LINE("varying vec3 vEyeVector;"); + + if ((flags & E3D_SHADER_FLAG_VERTEX_TANGENT) == 0) + ADD_LINE("varying vec3 vNormal;"); + } + } +} + +static void +_fragment_shader_string_func_flat_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode EINA_UNUSED, E3D_Shader_Flag flags) +{ + ADD_LINE("void fragmentFlat() {"); + ADD_LINE("vec4 color;"); + + if (flags & E3D_SHADER_FLAG_DIFFUSE) + { + if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureDiffuse0, vTexCoord) * uTextureDiffuseWeight +"); + ADD_LINE("texture2D(uTextureDiffuse1, vTexCoord) * (1.0 - uTextureDiffuseWeight);"); + ADD_LINE("color *= uMaterialDiffuse;"); + } + else if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureDiffuse0, vTexCoord) * uMaterialDiffuse;"); + } + else + { + ADD_LINE("color = uMaterialDiffuse;"); + } + + ADD_LINE("gl_FragColor = uLightDiffuse * color * vFactor.x;"); + } + else + { + ADD_LINE("gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);"); + } + + if (flags & E3D_SHADER_FLAG_SPECULAR) + { + if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureSpecular0, vTexCoord) * uTextureSpecularWeight +"); + ADD_LINE("texture2D(uTextureSpecular1, vTexCoord) * (1.0 - uTextureSpecularWeight);"); + ADD_LINE("color *= uMaterialSpecular;"); + } + else if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureSpecular0, vTexCoord) * uMaterialSpecular;"); + } + else + { + ADD_LINE("color = uMaterialSpecular;"); + } + + ADD_LINE("gl_FragColor += uLightSpecular * color * vFactor.y;"); + } + + if (flags & E3D_SHADER_FLAG_AMBIENT) + { + if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureAmbient0, vTexCoord) * uTextureAmbientWeight +"); + ADD_LINE("texture2D(uTextureAmbient1, vTexCoord) * (1.0 - uTextureAmbientWeight);"); + ADD_LINE("color *= uMaterialAmbient;"); + } + else if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureAmbient0, vTexCoord) * uMaterialAmbient;"); + } + else + { + ADD_LINE("color = uMaterialAmbient;"); + } + + ADD_LINE("gl_FragColor += uLightAmbient * color;"); + } + + if (flags & E3D_SHADER_FLAG_EMISSION) + { + if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureEmission0, vTexCoord) * uTextureEmissionWeight +"); + ADD_LINE("texture2D(uTextureEmission1, vTexCoord) * (1.0 - uTextureEmissionWeight);"); + ADD_LINE("color *= uMaterialEmission;"); + } + else if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureEmission0, vTexCoord) * uMaterialEmission;"); + } + else + { + ADD_LINE("color = uMaterialEmission;"); + } + + ADD_LINE("gl_FragColor += color;"); + } + + ADD_LINE("}"); +} + +static void +_fragment_shader_string_func_phong_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode EINA_UNUSED, E3D_Shader_Flag flags) +{ + ADD_LINE("void fragmentPhong() {"); + ADD_LINE("vec3 normal = normalize(vNormal);"); + ADD_LINE("vec3 lv = normalize(vLightVector);"); + ADD_LINE("float factor = dot(lv, normal);"); + ADD_LINE("vec4 color;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_SPOT) + { + ADD_LINE("float f = dot(-lv, normalize(uLightSpotDir));"); + + ADD_LINE("if (f > uLightSpotCutoffCos)"); + ADD_LINE("factor *= pow(f, uLightSpotExp);"); + ADD_LINE("else"); + ADD_LINE("factor = 0.0;"); + } + + ADD_LINE("if (factor > 0.0) {"); + + /* Diffuse term. */ + if (flags & E3D_SHADER_FLAG_DIFFUSE) + { + if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureDiffuse0, vTexCoord) * uTextureDiffuseWeight +"); + ADD_LINE("texture2D(uTextureDiffuse1, vTexCoord) * (1.0 - uTextureDiffuseWeight);"); + } + else if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureDiffuse0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialDiffuse;"); + } + + ADD_LINE("gl_FragColor = uLightDiffuse * color * factor;"); + } + else + { + ADD_LINE("gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);"); + } + + /* Specular term. */ + if (flags & E3D_SHADER_FLAG_SPECULAR) + { + ADD_LINE("factor = dot(normalize(vLightHalfVector), normal);"); + ADD_LINE("if (factor > 0.0) {"); + ADD_LINE("factor = pow(factor, uMaterialShininess);"); + + if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureSpecular0, vTexCoord) * uTextureSpecularWeight +"); + ADD_LINE("texture2D(uTextureSpecular1, vTexCoord) * (1.0 - uTextureSpecularWeight);"); + } + else if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureSpecular0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialSpecular;"); + } + + ADD_LINE("gl_FragColor += uLightSpecular * color * factor;"); + ADD_LINE("}"); + } + + ADD_LINE("} else {"); + ADD_LINE("gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);"); + ADD_LINE("}"); + + /* Ambient term. */ + if (flags & E3D_SHADER_FLAG_AMBIENT) + { + if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureAmbient0, vTexCoord) * uTextureAmbientWeight +"); + ADD_LINE("texture2D(uTextureAmbient1 * vTexCoord) * (1.0 - uTextureAmbientWeight);"); + } + else if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureAmbient0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialAmbient;"); + } + + ADD_LINE("gl_FragColor += uLightAmbient * color;"); + } + + /* Light attenuation. */ + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("gl_FragColor /= dot(uLightAtten, vec3(1.0, vLightDist, vLightDist * vLightDist));"); + + /* Emission term. */ + if (flags & E3D_SHADER_FLAG_EMISSION) + { + if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureEmission0, vTexCoord) * uTextureEmissionWeight +"); + ADD_LINE("texture2D(uTextureEmission1, vTexCoord) * (1.0 - uTextureEmissionWeight);"); + } + else if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureEmission0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialEmission;"); + } + + ADD_LINE("gl_FragColor += color;"); + } + + ADD_LINE("gl_FragColor.a = 1.0;"); + ADD_LINE("}"); +} + +static void +_fragment_shader_string_func_normal_map_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode EINA_UNUSED, + E3D_Shader_Flag flags) +{ + if ((flags & E3D_SHADER_FLAG_VERTEX_TANGENT) == 0) + { + ADD_LINE("mat3 cotangent_frame(vec3 n, vec3 p, vec2 uv) {"); + ADD_LINE("vec3 dp1 = dFdx(p);"); + ADD_LINE("vec3 dp2 = dFdy(p);"); + ADD_LINE("vec2 duv1 = dFdx(uv);"); + ADD_LINE("vec2 duv2 = dFdy(uv);"); + ADD_LINE("vec3 dp2perp = cross(dp2, n);"); + ADD_LINE("vec3 dp1perp = cross(n, dp1);"); + ADD_LINE("vec3 t = dp2perp * duv1.x + dp1perp * duv2.x;"); + ADD_LINE("vec3 b = dp2perp * duv1.y + dp1perp * duv2.y;"); + ADD_LINE("float invmax = inversesqrt(max(dot(t, t), dot(b, b)));"); + ADD_LINE("return mat3(t * invmax, b * invmax, n);"); + ADD_LINE("}"); + + ADD_LINE("vec3 perturb_normal(vec3 normal) {"); + ADD_LINE("mat3 tbn = cotangent_frame(vNormal, -vEyeVector, vTexCoord);"); + ADD_LINE("return normalize(tbn * normal);"); + ADD_LINE("}"); + } + + ADD_LINE("void fragmentNormalMap() {"); + ADD_LINE("float factor;"); + ADD_LINE("vec3 normal;"); + ADD_LINE("vec4 color;"); + + if (flags & E3D_SHADER_FLAG_NORMAL_TEXTURE_BLEND) + { + ADD_LINE("normal = texture2D(uTextureNormal0, vTexCoord).rgb * uTextureNormalWeight;"); + ADD_LINE("normal += texture2D(uTextureNormal1, vTexCoord).rgb * "); + ADD_LINE("(1.0 - uTextureNormalWeight);"); + } + else + { + ADD_LINE("normal = texture2D(uTextureNormal0, vTexCoord).rgb;"); + } + + ADD_LINE("normal = 2.0 * normal - 1.0;"); + + if ((flags & E3D_SHADER_FLAG_VERTEX_TANGENT) == 0) + { + ADD_LINE("normal = perturb_normal(normal);"); + } + + /* Can we skip this normalization?? */ + ADD_LINE("vec3 lv = normalize(vLightVector);"); + ADD_LINE("normal = normalize(normal);"); + + ADD_LINE("factor = dot(lv, normal);"); + + if (flags & E3D_SHADER_FLAG_LIGHT_SPOT) + { + ADD_LINE("float f = dot(-lv, normalize(uLightSpotDir));"); + + ADD_LINE("if (f > uLightSpotCutoffCos)"); + ADD_LINE("factor *= pow(f, uLightSpotExp);"); + ADD_LINE("else"); + ADD_LINE("factor = 0.0;"); + } + + ADD_LINE("if (factor > 0.0) {"); + + /* Diffuse term. */ + if (flags & E3D_SHADER_FLAG_DIFFUSE) + { + if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureDiffuse0, vTexCoord) * uTextureDiffuseWeight +"); + ADD_LINE("texture2D(uTextureDiffuse1, vTexCoord) * (1.0 - uTextureDiffuseWeight);"); + } + else if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureDiffuse0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialDiffuse;"); + } + + ADD_LINE("gl_FragColor = uLightDiffuse * color * factor;"); + } + else + { + ADD_LINE("gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);"); + } + + /* Specular term. */ + if (flags & E3D_SHADER_FLAG_SPECULAR) + { + ADD_LINE("factor = dot(normalize(vLightHalfVector), normal);"); + ADD_LINE("if (factor > 0.0) {"); + ADD_LINE("factor = pow(factor, uMaterialShininess);"); + + if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureSpecular0, vTexCoord) * uTextureSpecularWeight +"); + ADD_LINE("texture2D(uTextureSpecular1, vTexCoord) * (1.0 - uTextureSpecularWeight);"); + } + else if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureSpecular0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialSpecular;"); + } + + ADD_LINE("gl_FragColor += uLightSpecular * color * factor;"); + ADD_LINE("}"); + } + + ADD_LINE("} else {"); + ADD_LINE("gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);"); + ADD_LINE("}"); + + /* Ambient term. */ + if (flags & E3D_SHADER_FLAG_AMBIENT) + { + if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureAmbient0, vTexCoord) * uTextureAmbientWeight +"); + ADD_LINE("texture2D(uTextureAmbient1, vTexCoord) * (1.0 - uTextureAmbientWeight);"); + } + else if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureAmbient0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialAmbient;"); + } + + ADD_LINE("gl_FragColor += uLightAmbient * color;"); + } + + /* Light attenuation. */ + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("gl_FragColor /= dot(uLightAtten, vec3(1.0, vLightDist, vLightDist * vLightDist));"); + + /* Emission term. */ + if (flags & E3D_SHADER_FLAG_EMISSION) + { + if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureEmission0, vTexCoord) * uTextureEmissionWeight +"); + ADD_LINE("texture2D(uTextureEmission1, vTexCoord) * (1.0 - uTextureEmissionWeight);"); + } + else if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureEmission0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialEmission;"); + } + + ADD_LINE("gl_FragColor += color;"); + } + + ADD_LINE("gl_FragColor.a = 1.0;"); + ADD_LINE("}"); +} + +static void +_fragment_shader_string_get(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode, E3D_Shader_Flag flags) +{ + /* Add variables - vertex attributes. */ + _fragment_shader_string_variable_add(shader, mode, flags); + + /* Add functions. */ + if (mode == EVAS_3D_SHADE_MODE_FLAT) + _fragment_shader_string_func_flat_add(shader, mode, flags); + else if (mode == EVAS_3D_SHADE_MODE_PHONG) + _fragment_shader_string_func_phong_add(shader, mode, flags); + else if (mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + _fragment_shader_string_func_normal_map_add(shader, mode, flags); + + /* Add main function. */ + ADD_LINE("void main() {"); + + if (mode == EVAS_3D_SHADE_MODE_VERTEX_COLOR) + { + ADD_LINE("gl_FragColor = vColor;"); + } + else if (mode == EVAS_3D_SHADE_MODE_DIFFUSE) + { + if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE_BLEND) + { + ADD_LINE("gl_FragColor = (texture2D(uTextureDiffuse0, vTexCoord) *"); + ADD_LINE("uTextureDiffuseWeight + texture2D(uTextureDiffuse1, vTexCoord) *"); + ADD_LINE("(1.0 - uTextureDiffuseWeight)) * uMaterialDiffuse;"); + } + else if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE) + { + ADD_LINE("gl_FragColor = texture2D(uTextureDiffuse0, vTexCoord) * uMaterialDiffuse;"); + } + else + { + ADD_LINE("gl_FragColor = uMaterialDiffuse;"); + } + } + else if (mode == EVAS_3D_SHADE_MODE_FLAT) + { + ADD_LINE("fragmentFlat();"); + } + else if (mode == EVAS_3D_SHADE_MODE_PHONG) + { + ADD_LINE("fragmentPhong();"); + } + else if (mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + { + ADD_LINE("fragmentNormalMap();"); + } + + ADD_LINE("}"); +} + +static inline Eina_Bool +_shader_compile(GLuint shader, const char *src) +{ + GLint ok; + + glShaderSource(shader, 1, &src, NULL); + GLERR(__FUNCTION__, __FILE__, __LINE__, ""); + + glCompileShader(shader); + GLERR(__FUNCTION__, __FILE__, __LINE__, ""); + + glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); + GLERR(__FUNCTION__, __FILE__, __LINE__, ""); + + if (!ok) + { + GLchar *log_str; + GLint len; + GLsizei info_len; + + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); + log_str = (GLchar *)malloc(len); + glGetShaderInfoLog(shader, len, &info_len, log_str); + ERR("Shader compilation failed.\n%s", log_str); + free(log_str); + + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static inline Eina_Bool +_program_build(E3D_Program *program, const char *vert_src, const char *frag_src) +{ + GLint ok; + + /* Create OpenGL vertex & fragment shader object. */ + program->vert = glCreateShader(GL_VERTEX_SHADER); + program->frag = glCreateShader(GL_FRAGMENT_SHADER); + + /* Commpile vertex shader. */ + if (!_shader_compile(program->vert, vert_src)) + { + ERR("Faield to compile vertex shader."); + return EINA_FALSE; + } + + /* Compile fragment shader. */ + if (!_shader_compile(program->frag, frag_src)) + { + ERR("Failed to compile fragment shader."); + return EINA_FALSE; + } + + /* Create OpenGL program object. */ + program->prog = glCreateProgram(); + + /* Attach shaders. */ + glAttachShader(program->prog, program->vert); + GLERR(__FUNCTION__, __FILE__, __LINE__, ""); + + glAttachShader(program->prog, program->frag); + GLERR(__FUNCTION__, __FILE__, __LINE__, ""); + + /* Link program. */ + glLinkProgram(program->prog); + GLERR(__FUNCTION__, __FILE__, __LINE__, ""); + + /* Check link status. */ + glGetProgramiv(program->prog, GL_LINK_STATUS, &ok); + GLERR(__FUNCTION__, __FILE__, __LINE__, ""); + + if (!ok) + { + GLchar *log_str; + GLint len; + GLsizei info_len; + + glGetProgramiv(program->prog, GL_INFO_LOG_LENGTH, &len); + log_str = (GLchar *)malloc(len); + glGetProgramInfoLog(program->prog, len, &info_len, log_str); + ERR("Shader link failed.\n%s", log_str); + free(log_str); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static inline void +_program_vertex_attrib_bind(E3D_Program *program) +{ + GLint index = 0; + + if (program->flags & E3D_SHADER_FLAG_VERTEX_POSITION) + glBindAttribLocation(program->prog, index++, "aPosition0"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_POSITION_BLEND) + glBindAttribLocation(program->prog, index++, "aPosition1"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_NORMAL) + glBindAttribLocation(program->prog, index++, "aNormal0"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_NORMAL_BLEND) + glBindAttribLocation(program->prog, index++, "aNormal1"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_TANGENT) + glBindAttribLocation(program->prog, index++, "aTangent0"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_TANGENT_BLEND) + glBindAttribLocation(program->prog, index++, "aTangent1"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_COLOR) + glBindAttribLocation(program->prog, index++, "aColor0"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_COLOR_BLEND) + glBindAttribLocation(program->prog, index++, "aColor1"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_TEXCOORD) + glBindAttribLocation(program->prog, index++, "aTexCoord0"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_TEXCOORD_BLEND) + glBindAttribLocation(program->prog, index++, "aTexCoord1"); +} + +static const char *uniform_names[] = +{ + "uMatrixMvp", + "uMatrixModelview", + "uMatrixNormal", + "uPositionWeight", + "uNormalWeight", + "uTangentWeight", + "uColorWeight", + "uTexCoordWeight", + "uTextureAmbientWeight", + "uTextureDiffuseWeight", + "uTextureSpecularWeight", + "uTextureEmissionWeight", + "uTextureNormalWeight", + "uTextureAmbient0", + "uTextureDiffuse0", + "uTextureSpecular0", + "uTextureEmission0", + "uTextureNormal0", + "uTextureAmbient1", + "uTextureDiffuse1", + "uTextureSpecular1", + "uTextureEmission1", + "uTextureNormal1", + "uLightPosition", + "uLightSpotDir", + "uLightSpotExp", + "uLightSpotCutoffCos", + "uLightAtten", + "uLightAmbient", + "uLightDiffuse", + "uLightSpecular", + "uMaterialAmbient", + "uMaterialDiffuse", + "uMaterialSpecular", + "uMaterialEmission", + "uMaterialShininess", +}; + +static inline void +_program_uniform_init(E3D_Program *program) +{ + int i; + for (i = 0; i < E3D_UNIFORM_COUNT; i++) + { + program->uniform_locations[i] = glGetUniformLocation(program->prog, uniform_names[i]); + } +} + +static inline void +_uniform_upload(E3D_Uniform u, GLint loc, const E3D_Draw_Data *data) +{ + switch (u) + { + case E3D_UNIFORM_MATRIX_MVP: + glUniformMatrix4fv(loc, 1, EINA_FALSE, &data->matrix_mvp.m[0]); + break; + case E3D_UNIFORM_MATRIX_MV: + glUniformMatrix4fv(loc, 1, EINA_FALSE, &data->matrix_mv.m[0]); + break; + case E3D_UNIFORM_MATRIX_NORMAL: + glUniformMatrix3fv(loc, 1, EINA_FALSE, &data->matrix_normal.m[0]); + break; + case E3D_UNIFORM_POSITION_WEIGHT: + glUniform1f(loc, data->vertices[EVAS_3D_VERTEX_POSITION].weight); + break; + case E3D_UNIFORM_NORMAL_WEIGHT: + glUniform1f(loc, data->vertices[EVAS_3D_VERTEX_NORMAL].weight); + break; + case E3D_UNIFORM_TANGENT_WEIGHT: + glUniform1f(loc, data->vertices[EVAS_3D_VERTEX_TANGENT].weight); + break; + case E3D_UNIFORM_COLOR_WEIGHT: + glUniform1f(loc, data->vertices[EVAS_3D_VERTEX_COLOR].weight); + break; + case E3D_UNIFORM_TEXCOORD_WEIGHT: + glUniform1f(loc, data->vertices[EVAS_3D_VERTEX_TEXCOORD].weight); + break; + case E3D_UNIFORM_TEXTURE_WEIGHT_AMBIENT: + glUniform1f(loc, data->materials[EVAS_3D_MATERIAL_AMBIENT].texture_weight); + break; + case E3D_UNIFORM_TEXTURE_WEIGHT_DIFFUSE: + glUniform1f(loc, data->materials[EVAS_3D_MATERIAL_DIFFUSE].texture_weight); + break; + case E3D_UNIFORM_TEXTURE_WEIGHT_SPECULAR: + glUniform1f(loc, data->materials[EVAS_3D_MATERIAL_SPECULAR].texture_weight); + break; + case E3D_UNIFORM_TEXTURE_WEIGHT_EMISSION: + glUniform1f(loc, data->materials[EVAS_3D_MATERIAL_EMISSION].texture_weight); + break; + case E3D_UNIFORM_TEXTURE_WEIGHT_NORMAL: + glUniform1f(loc, data->materials[EVAS_3D_MATERIAL_NORMAL].texture_weight); + break; + case E3D_UNIFORM_TEXTURE_AMBIENT0: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_AMBIENT].sampler0); + break; + case E3D_UNIFORM_TEXTURE_DIFFUSE0: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_DIFFUSE].sampler0); + break; + case E3D_UNIFORM_TEXTURE_SPECULAR0: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_SPECULAR].sampler0); + break; + case E3D_UNIFORM_TEXTURE_EMISSION0: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_EMISSION].sampler0); + break; + case E3D_UNIFORM_TEXTURE_NORMAL0: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_NORMAL].sampler0); + break; + case E3D_UNIFORM_TEXTURE_AMBIENT1: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_AMBIENT].sampler1); + break; + case E3D_UNIFORM_TEXTURE_DIFFUSE1: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_DIFFUSE].sampler1); + break; + case E3D_UNIFORM_TEXTURE_SPECULAR1: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_SPECULAR].sampler1); + break; + case E3D_UNIFORM_TEXTURE_EMISSION1: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_EMISSION].sampler1); + break; + case E3D_UNIFORM_TEXTURE_NORMAL1: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_NORMAL].sampler1); + break; + case E3D_UNIFORM_LIGHT_POSITION: + glUniform4f(loc, data->light.position.x, data->light.position.y, + data->light.position.z, data->light.position.w); + break; + case E3D_UNIFORM_LIGHT_SPOT_DIR: + glUniform3f(loc, data->light.spot_dir.x, data->light.spot_dir.y, data->light.spot_dir.z); + break; + case E3D_UNIFORM_LIGHT_SPOT_EXP: + glUniform1f(loc, data->light.spot_exp); + break; + case E3D_UNIFORM_LIGHT_SPOT_CUTOFF_COS: + glUniform1f(loc, data->light.spot_cutoff_cos); + break; + case E3D_UNIFORM_LIGHT_ATTENUATION: + glUniform3f(loc, data->light.atten.x, data->light.atten.y, data->light.atten.z); + break; + case E3D_UNIFORM_LIGHT_AMBIENT: + glUniform4f(loc, + data->light.ambient.r, data->light.ambient.g, + data->light.ambient.b, data->light.ambient.a); + break; + case E3D_UNIFORM_LIGHT_DIFFUSE: + glUniform4f(loc, + data->light.diffuse.r, data->light.diffuse.g, + data->light.diffuse.b, data->light.diffuse.a); + break; + case E3D_UNIFORM_LIGHT_SPECULAR: + glUniform4f(loc, + data->light.specular.r, data->light.specular.g, + data->light.specular.b, data->light.specular.a); + break; + case E3D_UNIFORM_MATERIAL_AMBIENT: + glUniform4f(loc, + data->materials[EVAS_3D_MATERIAL_AMBIENT].color.r, + data->materials[EVAS_3D_MATERIAL_AMBIENT].color.g, + data->materials[EVAS_3D_MATERIAL_AMBIENT].color.b, + data->materials[EVAS_3D_MATERIAL_AMBIENT].color.a); + break; + case E3D_UNIFORM_MATERIAL_DIFFUSE: + glUniform4f(loc, + data->materials[EVAS_3D_MATERIAL_DIFFUSE].color.r, + data->materials[EVAS_3D_MATERIAL_DIFFUSE].color.g, + data->materials[EVAS_3D_MATERIAL_DIFFUSE].color.b, + data->materials[EVAS_3D_MATERIAL_DIFFUSE].color.a); + break; + case E3D_UNIFORM_MATERIAL_SPECULAR: + glUniform4f(loc, + data->materials[EVAS_3D_MATERIAL_SPECULAR].color.r, + data->materials[EVAS_3D_MATERIAL_SPECULAR].color.g, + data->materials[EVAS_3D_MATERIAL_SPECULAR].color.b, + data->materials[EVAS_3D_MATERIAL_SPECULAR].color.a); + break; + case E3D_UNIFORM_MATERIAL_EMISSION: + glUniform4f(loc, + data->materials[EVAS_3D_MATERIAL_EMISSION].color.r, + data->materials[EVAS_3D_MATERIAL_EMISSION].color.g, + data->materials[EVAS_3D_MATERIAL_EMISSION].color.b, + data->materials[EVAS_3D_MATERIAL_EMISSION].color.a); + break; + case E3D_UNIFORM_MATERIAL_SHININESS: + glUniform1f(loc, data->shininess); + break; + default: + ERR("Invalid uniform ID."); + break; + } +} + +void +e3d_program_uniform_upload(E3D_Program *program, const E3D_Draw_Data *data) +{ + int i; + for (i = 0; i < E3D_UNIFORM_COUNT; i++) + { + if (program->uniform_locations[i] != -1) + { + _uniform_upload(i, program->uniform_locations[i], data); + } + } +} + +E3D_Program * +e3d_program_new(Evas_3D_Shade_Mode mode, E3D_Shader_Flag flags) +{ + E3D_Shader_String vert, frag; + E3D_Program *program = NULL; + + _shader_string_init(&vert); + _shader_string_init(&frag); + + program = (E3D_Program *)calloc(1, sizeof(E3D_Program)); + + if (program == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + program->prog = glCreateProgram(); + program->vert = glCreateShader(GL_VERTEX_SHADER); + program->frag = glCreateShader(GL_FRAGMENT_SHADER); + program->mode = mode; + program->flags = flags; + + _vertex_shader_string_get(&vert, mode, flags); + _fragment_shader_string_get(&frag, mode, flags); + + if (! _program_build(program, vert.str, frag.str)) + goto error; + + _program_vertex_attrib_bind(program); + _program_uniform_init(program); + + _shader_string_fini(&vert); + _shader_string_fini(&frag); + + return program; + +error: + if (program->prog) + glDeleteProgram(program->prog); + + if (program->vert) + glDeleteShader(program->vert); + + if (program->frag) + glDeleteShader(program->frag); + + _shader_string_fini(&vert); + _shader_string_fini(&frag); + + return NULL; +} + +void +e3d_program_free(E3D_Program *program) +{ + glDeleteProgram(program->prog); + glDeleteShader(program->vert); + glDeleteShader(program->frag); + free(program); +} + +GLuint +e3d_program_id_get(const E3D_Program *program) +{ + return program->prog; +} + +Evas_3D_Shade_Mode +e3d_program_shade_mode_get(const E3D_Program *program) +{ + return program->mode; +} + +E3D_Shader_Flag +e3d_program_shader_flags_get(const E3D_Program *program) +{ + return program->flags; +} diff --git a/src/modules/evas/engines/gl_common/evas_gl_common.h b/src/modules/evas/engines/gl_common/evas_gl_common.h index f35d9c94d0..761d1f3f55 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_common.h +++ b/src/modules/evas/engines/gl_common/evas_gl_common.h @@ -897,4 +897,6 @@ _tex_sub_2d(Evas_Engine_GL_Context *gc, int x, int y, int w, int h, int fmt, int GLERR(__FUNCTION__, __FILE__, __LINE__, ""); } +#include "evas_gl_3d_common.h" + #endif diff --git a/src/modules/evas/engines/gl_x11/evas_engine.c b/src/modules/evas/engines/gl_x11/evas_engine.c index 0d9381ae4e..099b49768d 100644 --- a/src/modules/evas/engines/gl_x11/evas_engine.c +++ b/src/modules/evas/engines/gl_x11/evas_engine.c @@ -67,6 +67,9 @@ struct _Render_Engine void *get_pixels_data; Evas_Object *obj; } func; + + Evas_GL_X11_Context *context_3d; + E3D_Renderer *renderer_3d; }; static int initted = 0; @@ -3676,6 +3679,185 @@ eng_context_flush(void *data) } } +static void +eng_context_3d_use(void *data) +{ + Render_Engine *re = (Render_Engine *)data; + + if (re->context_3d == NULL) + { + re->context_3d = eng_gl_context_new(re->win); + + if (re->context_3d == NULL) + { + ERR("Failed to create OpenGL context for 3D."); + return; + } + } + + eng_gl_context_use(re->context_3d); +} + +static E3D_Renderer * +eng_renderer_3d_get(void *data) +{ + Render_Engine *re = (Render_Engine *)data; + + if (re->renderer_3d == NULL) + { + re->renderer_3d = e3d_renderer_new(); + + if (re->renderer_3d == NULL) + { + ERR("Failed to create 3D renderer."); + return NULL; + } + } + + return re->renderer_3d; +} + +static void * +eng_drawable_new(void *data, int w, int h, int alpha) +{ + eng_context_3d_use(data); +#ifdef GL_GLES + return e3d_drawable_new(w, h, alpha, GL_DEPTH_STENCIL_OES, GL_NONE); +#else + return e3d_drawable_new(w, h, alpha, GL_DEPTH24_STENCIL8, GL_NONE); +#endif +} + +static void +eng_drawable_free(void *data, void *drawable) +{ + eng_context_3d_use(data); + e3d_drawable_free(drawable); +} + +static void +eng_drawable_size_get(void *data EINA_UNUSED, void *drawable, int *w, int *h) +{ + e3d_drawable_size_get((E3D_Drawable *)drawable, w, h); +} + +static void * +eng_image_drawable_set(void *data, void *image, void *drawable) +{ + E3D_Drawable *d = (E3D_Drawable *)drawable; + Evas_Native_Surface ns; + int w, h; + + ns.type = EVAS_NATIVE_SURFACE_OPENGL; + ns.data.opengl.texture_id = e3d_drawable_texture_id_get(d); + ns.data.opengl.framebuffer_id = 0; + ns.data.opengl.internal_format = e3d_drawable_format_get(d); + ns.data.opengl.format = e3d_drawable_format_get(d); + ns.data.opengl.x = 0; + ns.data.opengl.y = 0; + e3d_drawable_size_get(d, &w, &h); + ns.data.opengl.w = w; + ns.data.opengl.h = h; + + return eng_image_native_set(data, image, &ns); +} + +static void +eng_drawable_scene_render(void *data, void *drawable, void *scene_data) +{ + Render_Engine *re = (Render_Engine *)data; + E3D_Renderer *renderer = NULL; + + eng_window_use(re->win); + evas_gl_common_context_flush(re->win->gl_context); + + eng_context_3d_use(data); + renderer = eng_renderer_3d_get(data); + e3d_drawable_scene_render(drawable, renderer, scene_data); +} + +static void * +eng_texture_new(void *data EINA_UNUSED) +{ + return e3d_texture_new(); +} + +static void +eng_texture_free(void *data EINA_UNUSED, void *texture) +{ + e3d_texture_free((E3D_Texture *)texture); +} + +static void +eng_texture_data_set(void *data, void *texture, Evas_3D_Color_Format color_format, + Evas_3D_Pixel_Format pixel_format, int w, int h, const void *pixels) +{ + Render_Engine *re = (Render_Engine *)data; + eng_window_use(re->win); + evas_gl_common_context_flush(re->win->gl_context); + eng_context_3d_use(data); + + e3d_texture_data_set((E3D_Texture *)texture, color_format, pixel_format, w, h, pixels); +} + +static void +eng_texture_file_set(void *data, void *texture, const char *file, const char *key) +{ + Render_Engine *re = (Render_Engine *)data; + eng_window_use(re->win); + evas_gl_common_context_flush(re->win->gl_context); + eng_context_3d_use(data); + + e3d_texture_file_set((E3D_Texture *)texture, file, key); +} + +static void +eng_texture_color_format_get(void *data EINA_UNUSED, void *texture, Evas_3D_Color_Format *format) +{ + *format = e3d_texture_color_format_get((E3D_Texture *)texture); +} + +static void +eng_texture_size_get(void *data EINA_UNUSED, void *texture, int *w, int *h) +{ + e3d_texture_size_get((E3D_Texture *)texture, w, h); +} + +static void +eng_texture_wrap_set(void *data EINA_UNUSED, void *texture, + Evas_3D_Wrap_Mode s, Evas_3D_Wrap_Mode t) +{ + e3d_texture_wrap_set((E3D_Texture *)texture, s, t); +} + +static void +eng_texture_wrap_get(void *data EINA_UNUSED, void *texture, + Evas_3D_Wrap_Mode *s, Evas_3D_Wrap_Mode *t) +{ + e3d_texture_wrap_get((E3D_Texture *)texture, s, t); +} + +static void +eng_texture_filter_set(void *data EINA_UNUSED, void *texture, + Evas_3D_Texture_Filter min, Evas_3D_Texture_Filter mag) +{ + e3d_texture_filter_set((E3D_Texture *)texture, min, mag); +} + +static void +eng_texture_filter_get(void *data EINA_UNUSED, void *texture, + Evas_3D_Texture_Filter *min, Evas_3D_Texture_Filter *mag) +{ + e3d_texture_filter_get((E3D_Texture *)texture, min, mag); +} + +static void +eng_texture_image_set(void *data EINA_UNUSED, void *texture, void *image) +{ + Evas_GL_Image *im = (Evas_GL_Image *)image; + e3d_texture_import((E3D_Texture *)texture, im->tex->pt->texture); +} + static int module_open(Evas_Module *em) { @@ -3800,6 +3982,26 @@ module_open(Evas_Module *em) ORD(context_flush); + /* 3D features */ + ORD(drawable_new); + ORD(drawable_free); + ORD(drawable_size_get); + ORD(image_drawable_set); + + ORD(drawable_scene_render); + + ORD(texture_new); + ORD(texture_free); + ORD(texture_data_set); + ORD(texture_file_set); + ORD(texture_color_format_get); + ORD(texture_size_get); + ORD(texture_wrap_set); + ORD(texture_wrap_get); + ORD(texture_filter_set); + ORD(texture_filter_get); + ORD(texture_image_set); + /* now advertise out own api */ em->functions = (void *)(&func); return 1; diff --git a/src/modules/evas/engines/gl_x11/evas_engine.h b/src/modules/evas/engines/gl_x11/evas_engine.h index c03950b907..2ae3757f44 100644 --- a/src/modules/evas/engines/gl_x11/evas_engine.h +++ b/src/modules/evas/engines/gl_x11/evas_engine.h @@ -58,6 +58,7 @@ extern int _evas_engine_GL_X11_log_dom ; #define CRI(...) EINA_LOG_DOM_CRIT(_evas_engine_GL_X11_log_dom, __VA_ARGS__) typedef struct _Evas_GL_X11_Window Evas_GL_X11_Window; +typedef struct _Evas_GL_X11_Context Evas_GL_X11_Context; struct _Evas_GL_X11_Window { @@ -91,6 +92,20 @@ struct _Evas_GL_X11_Window int surf : 1; }; +struct _Evas_GL_X11_Context +{ +#ifdef GL_GLES + EGLDisplay display; + EGLContext context; + EGLSurface surface; +#else + Display *display; + GLXContext context; + GLXWindow glxwin; + Window win; +#endif +}; + Evas_GL_X11_Window *eng_window_new(Display *disp, Window win, int screen, Visual *vis, Colormap cmap, int depth, int w, int h, int indirect, @@ -104,4 +119,8 @@ void *eng_best_visual_get(Evas_Engine_Info_GL_X11 *einfo); Colormap eng_best_colormap_get(Evas_Engine_Info_GL_X11 *einfo); int eng_best_depth_get(Evas_Engine_Info_GL_X11 *einfo); +Evas_GL_X11_Context *eng_gl_context_new(Evas_GL_X11_Window *win); +void eng_gl_context_free(Evas_GL_X11_Context *context); +void eng_gl_context_use(Evas_GL_X11_Context *context); + #endif diff --git a/src/modules/evas/engines/gl_x11/evas_x_main.c b/src/modules/evas/engines/gl_x11/evas_x_main.c index 1b7cab645b..773bfbabc9 100644 --- a/src/modules/evas/engines/gl_x11/evas_x_main.c +++ b/src/modules/evas/engines/gl_x11/evas_x_main.c @@ -849,3 +849,93 @@ eng_best_depth_get(Evas_Engine_Info_GL_X11 *einfo) } return _evas_gl_x11_vi->depth; } + +Evas_GL_X11_Context * +eng_gl_context_new(Evas_GL_X11_Window *win) +{ + Evas_GL_X11_Context *ctx; +#if GL_GLES + int context_attrs[3] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; +#endif + + if (!win) return NULL; + + ctx = calloc(1, sizeof(Evas_GL_X11_Context)); + if (!ctx) return NULL; + +#if GL_GLES + ctx->context = eglCreateContext(win->egl_disp, win->egl_config, + win->egl_context[0], context_attrs); + + if (!ctx->context) + { + ERR("EGL context creation failed."); + goto error; + } + + ctx->display = win->egl_disp; + ctx->surface = win->egl_surface[0]; +#else + ctx->context = glXCreateContext(win->disp, win->visualinfo, win->context, 1); + + if (!ctx->context) + { + ERR("GLX context creation failed."); + goto error; + } + + ctx->display = win->disp; + ctx->glxwin = win->glxwin; + ctx->win = win->win; +#endif + + return ctx; + +error: + free(ctx); + return NULL; +} + +void +eng_gl_context_free(Evas_GL_X11_Context *ctx) +{ +#if GL_GLES + eglDestroyContext(ctx->display, ctx->context); +#else + glXDestroyContext(ctx->display, ctx->context); +#endif + + free(ctx); +} + +void +eng_gl_context_use(Evas_GL_X11_Context *ctx) +{ +#if GL_GLES + if (eglMakeCurrent(ctx->display, ctx->surface, + ctx->surface, ctx->context) == EGL_FALSE) + { + ERR("eglMakeCurrent() failed."); + } +#else + if (ctx->glxwin) + { + if (!glXMakeContextCurrent(ctx->display, ctx->glxwin, + ctx->glxwin, ctx->context)) + { + ERR("glXMakeContextCurrent(%p, %p, %p, %p) faild.", + (void *)ctx->display, (void *)ctx->glxwin, + (void *)ctx->glxwin, (void *)ctx->context); + } + } + else + { + if (!glXMakeCurrent(ctx->display, ctx->win, ctx->context)) + { + ERR("glXMakeCurrent(%p, %p, %p) failed.", + (void *)ctx->display, (void *)ctx->win, + (void *)ctx->context); + } + } +#endif +} diff --git a/src/modules/evas/engines/software_generic/evas_engine.c b/src/modules/evas/engines/software_generic/evas_engine.c index f3abf88c48..a709720f7f 100644 --- a/src/modules/evas/engines/software_generic/evas_engine.c +++ b/src/modules/evas/engines/software_generic/evas_engine.c @@ -2712,6 +2712,22 @@ static Evas_Func func = eng_multi_font_draw, eng_pixel_alpha_get, NULL, // eng_context_flush - software doesn't use it + NULL, // eng_drawable_new + NULL, // eng_drawable_free + NULL, // eng_drawable_size_get + NULL, // eng_image_drawable_set + NULL, // eng_drawable_render_scene + NULL, // eng_texture_new + NULL, // eng_texture_free + NULL, // eng_texture_data_set + NULL, // eng_texture_file_set + NULL, // eng_texture_color_format_get + NULL, // eng_texture_size_get + NULL, // eng_texture_wrap_set + NULL, // eng_texture_wrap_get + NULL, // eng_texture_filter_set + NULL, // eng_texture_filter_get + NULL, // eng_texture_image_set /* FUTURE software generic calls go here */ }; |