summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChunEon Park <hermet@hermet.pe.kr>2014-04-10 21:54:26 +0900
committerChunEon Park <hermet@hermet.pe.kr>2014-04-10 21:54:26 +0900
commitdb453635a3c6670f3cacb119da4364dbfff0e32e (patch)
tree951426f68d13c00895a589f834d3d1dd661aacce
parentf3c835d6fc1639b5a83b1496422f9f717e427c5d (diff)
parent398c4199d31411bdebedbf657b74f00f16e7d2d8 (diff)
downloadefl-db453635a3c6670f3cacb119da4364dbfff0e32e.tar.gz
Merge branch 'devs/cedric/evas-3d' of ssh://git.enlightenment.org/core/efl into devs/cedric/evas-3d
-rw-r--r--configure.ac17
-rw-r--r--src/Makefile_Evas.am26
-rw-r--r--src/examples/evas/EarthDiffuse.pngbin0 -> 2123166 bytes
-rw-r--r--src/examples/evas/Makefile.am25
-rw-r--r--src/examples/evas/evas-3d-cube.c260
-rw-r--r--src/examples/evas/evas-3d-cube2.c321
-rw-r--r--src/examples/evas/evas-3d-md2.c165
-rw-r--r--src/examples/evas/evas-3d-pick.c397
-rw-r--r--src/examples/evas/evas-3d-proxy.c297
-rw-r--r--src/examples/evas/normal_lego.pngbin0 -> 3166 bytes
-rw-r--r--src/examples/evas/sonic.md2bin0 -> 660576 bytes
-rw-r--r--src/examples/evas/sonic.pngbin0 -> 309072 bytes
-rw-r--r--src/lib/evas/Evas.h1
-rw-r--r--src/lib/evas/Evas_3D.h2151
-rw-r--r--src/lib/evas/Evas_Eo.h33
-rw-r--r--src/lib/evas/canvas/evas_3d_camera.c159
-rw-r--r--src/lib/evas/canvas/evas_3d_light.c273
-rw-r--r--src/lib/evas/canvas/evas_3d_material.c221
-rw-r--r--src/lib/evas/canvas/evas_3d_mesh.c828
-rw-r--r--src/lib/evas/canvas/evas_3d_mesh_loader_md2.c440
-rw-r--r--src/lib/evas/canvas/evas_3d_node.c1197
-rw-r--r--src/lib/evas/canvas/evas_3d_object.c129
-rw-r--r--src/lib/evas/canvas/evas_3d_scene.c622
-rw-r--r--src/lib/evas/canvas/evas_3d_texture.c480
-rw-r--r--src/lib/evas/canvas/evas_image.eo27
-rw-r--r--src/lib/evas/canvas/evas_object_image.c207
-rw-r--r--src/lib/evas/canvas/evas_object_main.c25
-rw-r--r--src/lib/evas/canvas/evas_render.c19
-rw-r--r--src/lib/evas/include/evas_3d_private.h377
-rw-r--r--src/lib/evas/include/evas_3d_utils.h1539
-rw-r--r--src/lib/evas/include/evas_inline.x4
-rw-r--r--src/lib/evas/include/evas_private.h32
-rw-r--r--src/modules/evas/engines/gl_common/evas_gl_3d.c1332
-rw-r--r--src/modules/evas/engines/gl_common/evas_gl_3d_common.h37
-rw-r--r--src/modules/evas/engines/gl_common/evas_gl_3d_private.h145
-rw-r--r--src/modules/evas/engines/gl_common/evas_gl_3d_renderer.c284
-rw-r--r--src/modules/evas/engines/gl_common/evas_gl_3d_renderer.h6
-rw-r--r--src/modules/evas/engines/gl_common/evas_gl_3d_shader.c1482
-rw-r--r--src/modules/evas/engines/gl_common/evas_gl_common.h2
-rw-r--r--src/modules/evas/engines/gl_x11/evas_engine.c202
-rw-r--r--src/modules/evas/engines/gl_x11/evas_engine.h19
-rw-r--r--src/modules/evas/engines/gl_x11/evas_x_main.c90
-rw-r--r--src/modules/evas/engines/software_generic/evas_engine.c16
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
new file mode 100644
index 0000000000..3bcf58b3fe
--- /dev/null
+++ b/src/examples/evas/EarthDiffuse.png
Binary files differ
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
new file mode 100644
index 0000000000..6a74ad9774
--- /dev/null
+++ b/src/examples/evas/normal_lego.png
Binary files differ
diff --git a/src/examples/evas/sonic.md2 b/src/examples/evas/sonic.md2
new file mode 100644
index 0000000000..228edb9fb7
--- /dev/null
+++ b/src/examples/evas/sonic.md2
Binary files differ
diff --git a/src/examples/evas/sonic.png b/src/examples/evas/sonic.png
new file mode 100644
index 0000000000..01cb2ede8e
--- /dev/null
+++ b/src/examples/evas/sonic.png
Binary files differ
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 */
};