Evas: 3D: Introducing 3D scene rendering features
authorTaekyun Kim <tkq.kim@samsung.com>
Fri, 27 Dec 2013 07:56:30 +0000 (16:56 +0900)
committerChunEon Park <hermet@hermet.pe.kr>
Fri, 25 Apr 2014 07:15:41 +0000 (16:15 +0900)
Enable 3D features using --enable-evas-3d=yes when configuring.
APIs are exposed through Evas_3D.h.
Currently, evas-3d is being supported only on gl_x11 engine.

Conflicts:

src/lib/evas/Evas_Eo.h

30 files changed:
configure.ac
src/Makefile_Evas.am
src/lib/evas/Evas.h
src/lib/evas/Evas_3D.h [new file with mode: 0644]
src/lib/evas/canvas/evas_3d_camera.c [new file with mode: 0644]
src/lib/evas/canvas/evas_3d_light.c [new file with mode: 0644]
src/lib/evas/canvas/evas_3d_material.c [new file with mode: 0644]
src/lib/evas/canvas/evas_3d_mesh.c [new file with mode: 0644]
src/lib/evas/canvas/evas_3d_mesh_loader_md2.c [new file with mode: 0644]
src/lib/evas/canvas/evas_3d_node.c [new file with mode: 0644]
src/lib/evas/canvas/evas_3d_object.c [new file with mode: 0644]
src/lib/evas/canvas/evas_3d_scene.c [new file with mode: 0644]
src/lib/evas/canvas/evas_3d_texture.c [new file with mode: 0644]
src/lib/evas/canvas/evas_image.eo
src/lib/evas/canvas/evas_object_image.c
src/lib/evas/canvas/evas_object_main.c
src/lib/evas/include/evas_3d_private.h [new file with mode: 0644]
src/lib/evas/include/evas_3d_utils.h [new file with mode: 0644]
src/lib/evas/include/evas_private.h
src/modules/evas/engines/gl_common/evas_gl_3d.c [new file with mode: 0644]
src/modules/evas/engines/gl_common/evas_gl_3d_common.h [new file with mode: 0644]
src/modules/evas/engines/gl_common/evas_gl_3d_private.h [new file with mode: 0644]
src/modules/evas/engines/gl_common/evas_gl_3d_renderer.c [new file with mode: 0644]
src/modules/evas/engines/gl_common/evas_gl_3d_renderer.h [new file with mode: 0644]
src/modules/evas/engines/gl_common/evas_gl_3d_shader.c [new file with mode: 0644]
src/modules/evas/engines/gl_common/evas_gl_common.h
src/modules/evas/engines/gl_x11/evas_engine.c
src/modules/evas/engines/gl_x11/evas_engine.h
src/modules/evas/engines/gl_x11/evas_x_main.c
src/modules/evas/engines/software_generic/evas_engine.c

index c4801b9..6bf109b 100644 (file)
@@ -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
index 3617f4c..9ca83a8 100644 (file)
@@ -251,6 +251,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 \
@@ -713,6 +731,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 \
index dc4d435..05ac2c0 100644 (file)
@@ -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 (file)
index 0000000..4a25246
--- /dev/null
@@ -0,0 +1,289 @@
+#ifndef _EVAS_3D_H
+#define _EVAS_3D_H
+
+#include <Evas.h>
+
+typedef float   Evas_Real;
+
+typedef struct _Evas_3D_Scene    Evas_3D_Scene;
+typedef struct _Evas_3D_Node     Evas_3D_Node;
+typedef struct _Evas_3D_Camera   Evas_3D_Camera;
+typedef struct _Evas_3D_Light    Evas_3D_Light;
+typedef struct _Evas_3D_Mesh     Evas_3D_Mesh;
+typedef struct _Evas_3D_Texture  Evas_3D_Texture;
+typedef struct _Evas_3D_Material Evas_3D_Material;
+
+typedef enum _Evas_3D_Space
+{
+   EVAS_3D_SPACE_LOCAL,
+   EVAS_3D_SPACE_PARENT,
+   EVAS_3D_SPACE_WORLD,
+} Evas_3D_Space;
+
+typedef enum _Evas_3D_Node_Type
+{
+   EVAS_3D_NODE_TYPE_NODE,
+   EVAS_3D_NODE_TYPE_CAMERA,
+   EVAS_3D_NODE_TYPE_LIGHT,
+   EVAS_3D_NODE_TYPE_MESH,
+} Evas_3D_Node_Type;
+
+typedef enum _Evas_3D_Vertex_Attrib
+{
+   EVAS_3D_VERTEX_POSITION,
+   EVAS_3D_VERTEX_NORMAL,
+   EVAS_3D_VERTEX_TANGENT,
+   EVAS_3D_VERTEX_COLOR,
+   EVAS_3D_VERTEX_TEXCOORD,
+} Evas_3D_Vertex_Attrib;
+
+typedef enum _Evas_3D_Index_Format
+{
+   EVAS_3D_INDEX_FORMAT_NONE,
+   EVAS_3D_INDEX_FORMAT_UNSIGNED_BYTE,
+   EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT,
+} Evas_3D_Index_Format;
+
+typedef enum _Evas_3D_Vertex_Assembly
+{
+   EVAS_3D_VERTEX_ASSEMBLY_POINTS,
+   EVAS_3D_VERTEX_ASSEMBLY_LINES,
+   EVAS_3D_VERTEX_ASSEMBLY_LINE_STRIP,
+   EVAS_3D_VERTEX_ASSEMBLY_LINE_LOOP,
+   EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES,
+   EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_STRIP,
+   EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_FAN,
+} Evas_3D_Vertex_Assembly;
+
+typedef enum _Evas_3D_Color_Format
+{
+   EVAS_3D_COLOR_FORMAT_RGBA,
+   EVAS_3D_COLOR_FORMAT_RGB,
+   EVAS_3D_COLOR_FORMAT_ALPHA,
+} Evas_3D_Color_Format;
+
+typedef enum _Evas_3D_Pixel_Format
+{
+   EVAS_3D_PIXEL_FORMAT_8,
+   EVAS_3D_PIXEL_FORMAT_565,
+   EVAS_3D_PIXEL_FORMAT_888,
+   EVAS_3D_PIXEL_FORMAT_8888,
+   EVAS_3D_PIXEL_FORMAT_4444,
+   EVAS_3D_PIXEL_FORMAT_5551,
+} Evas_3D_Pixel_Format;
+
+typedef enum _Evas_3D_Wrap_Mode
+{
+   EVAS_3D_WRAP_MODE_CLAMP,
+   EVAS_3D_WRAP_MODE_REPEAT,
+   EVAS_3D_WRAP_MODE_REFLECT,
+} Evas_3D_Wrap_Mode;
+
+typedef enum _Evas_3D_Texture_Filter
+{
+   EVAS_3D_TEXTURE_FILTER_NEAREST,
+   EVAS_3D_TEXTURE_FILTER_LINEAR,
+   EVAS_3D_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST,
+   EVAS_3D_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST,
+   EVAS_3D_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR,
+   EVAS_3D_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR,
+} Evas_3D_Texture_Filter;
+
+typedef enum _Evas_3D_Shade_Mode
+{
+   EVAS_3D_SHADE_MODE_VERTEX_COLOR,
+   EVAS_3D_SHADE_MODE_DIFFUSE,
+   EVAS_3D_SHADE_MODE_FLAT,
+   EVAS_3D_SHADE_MODE_PHONG,
+   EVAS_3D_SHADE_MODE_NORMAL_MAP,
+} Evas_3D_Shade_Mode;
+
+typedef enum _Evas_3D_Material_Attrib
+{
+   EVAS_3D_MATERIAL_AMBIENT,
+   EVAS_3D_MATERIAL_DIFFUSE,
+   EVAS_3D_MATERIAL_SPECULAR,
+   EVAS_3D_MATERIAL_EMISSION,
+   EVAS_3D_MATERIAL_NORMAL,
+} Evas_3D_Material_Attrib;
+
+typedef enum _Evas_3D_Mesh_File_Type
+{
+   EVAS_3D_MESH_FILE_TYPE_MD2,
+} Evas_3D_Mesh_File_Type;
+
+typedef enum _Evas_3D_Pick_Type
+{
+   EVAS_3D_PICK_NODE,
+   EVAS_3D_PICK_MESH,
+} Evas_3D_Pick_Type;
+
+/* Image object render target */
+EAPI void               evas_object_image_3d_scene_set(Evas_Object *obj, Evas_3D_Scene *scene) EINA_ARG_NONNULL(1);
+EAPI Evas_3D_Scene     *evas_object_image_3d_scene_get(const Evas_Object *obj) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+/* Scene */
+EAPI Evas_3D_Scene     *evas_3d_scene_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_scene_del(Evas_3D_Scene *scene) EINA_ARG_NONNULL(1);
+EAPI Evas              *evas_3d_scene_evas_get(const Evas_3D_Scene *scene) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+EAPI void               evas_3d_scene_root_node_set(Evas_3D_Scene *scene, Evas_3D_Node *node) EINA_ARG_NONNULL(1);
+EAPI Evas_3D_Node      *evas_3d_scene_root_node_get(const Evas_3D_Scene *scene) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_scene_camera_node_set(Evas_3D_Scene *scene, Evas_3D_Node *node) EINA_ARG_NONNULL(1);
+EAPI Evas_3D_Node      *evas_3d_scene_camera_node_get(const Evas_3D_Scene *scene) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+EAPI void               evas_3d_scene_size_set(Evas_3D_Scene *scene, int w, int h) EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_scene_size_get(const Evas_3D_Scene *scene, int *w, int *h) EINA_ARG_NONNULL(1);
+
+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);
+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);
+
+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);
+
+/* Node */
+EAPI Evas_3D_Node      *evas_3d_node_add(Evas *e, Evas_3D_Node_Type type) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_node_del(Evas_3D_Node *node) EINA_ARG_NONNULL(1);
+EAPI Evas_3D_Node_Type  evas_3d_node_type_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI Evas              *evas_3d_node_evas_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+EAPI void               evas_3d_node_member_add(Evas_3D_Node *node, Evas_3D_Node *member) EINA_ARG_NONNULL(1, 2);
+EAPI void               evas_3d_node_member_del(Evas_3D_Node *node, Evas_3D_Node *member) EINA_ARG_NONNULL(1, 2);
+EAPI Evas_3D_Node      *evas_3d_node_parent_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI const Eina_List   *evas_3d_node_member_list_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+EAPI void               evas_3d_node_position_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z) EINA_ARG_NONNULL(1);
+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);
+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);
+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);
+EAPI void               evas_3d_node_scale_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z) EINA_ARG_NONNULL(1);
+
+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);
+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);
+
+EAPI void               evas_3d_node_position_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_node_orientation_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_node_scale_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) EINA_ARG_NONNULL(1);
+
+EAPI Eina_Bool          evas_3d_node_position_inherit_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI Eina_Bool          evas_3d_node_orientation_inherit_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI Eina_Bool          evas_3d_node_scale_inherit_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+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);
+
+EAPI void               evas_3d_node_camera_set(Evas_3D_Node *node, Evas_3D_Camera *camera) EINA_ARG_NONNULL(1);
+EAPI Evas_3D_Camera    *evas_3d_node_camera_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+EAPI void               evas_3d_node_light_set(Evas_3D_Node *node, Evas_3D_Light *light) EINA_ARG_NONNULL(1);
+EAPI Evas_3D_Light     *evas_3d_node_light_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+EAPI void               evas_3d_node_mesh_add(Evas_3D_Node *node, Evas_3D_Mesh *mesh) EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_node_mesh_del(Evas_3D_Node *node, Evas_3D_Mesh *mesh) EINA_ARG_NONNULL(1);
+EAPI const Eina_List   *evas_3d_node_mesh_list_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_node_mesh_frame_set(Evas_3D_Node *node, Evas_3D_Mesh *mesh, int frame) EINA_ARG_NONNULL(1);
+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);
+
+/* Camera */
+EAPI Evas_3D_Camera    *evas_3d_camera_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_camera_del(Evas_3D_Camera *camera) EINA_ARG_NONNULL(1);
+EAPI Evas              *evas_3d_camera_evas_get(const Evas_3D_Camera *camera) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+EAPI void               evas_3d_camera_projection_matrix_set(Evas_3D_Camera *camera, const Evas_Real *matrix) EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_camera_projection_matrix_get(const Evas_3D_Camera *camera, Evas_Real *matrix) EINA_ARG_NONNULL(1, 2);
+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);
+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);
+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);
+
+/* Light */
+EAPI Evas_3D_Light     *evas_3d_light_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_light_del(Evas_3D_Light *light) EINA_ARG_NONNULL(1);
+EAPI Evas              *evas_3d_light_evas_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+EAPI void               evas_3d_light_directional_set(Evas_3D_Light *light, Eina_Bool directional) EINA_ARG_NONNULL(1);
+EAPI Eina_Bool          evas_3d_light_directional_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+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);
+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);
+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);
+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);
+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);
+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);
+EAPI void               evas_3d_light_spot_exponent_set(Evas_3D_Light *light, Evas_Real exponent) EINA_ARG_NONNULL(1);
+EAPI Evas_Real          evas_3d_light_spot_exponent_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_light_spot_cutoff_set(Evas_3D_Light *light, Evas_Real cutoff) EINA_ARG_NONNULL(1);
+EAPI Evas_Real          evas_3d_light_spot_cutoff_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_light_attenuation_set(Evas_3D_Light *light, Evas_Real constant, Evas_Real linear, Evas_Real quadratic) EINA_ARG_NONNULL(1);
+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);
+
+EAPI void               evas_3d_light_attenuation_enable_set(Evas_3D_Light *light, Eina_Bool enable) EINA_ARG_NONNULL(1);
+EAPI Eina_Bool          evas_3d_light_attenuation_enable_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+/* Mesh */
+EAPI Evas_3D_Mesh      *evas_3d_mesh_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_mesh_del(Evas_3D_Mesh *mesh) EINA_ARG_NONNULL(1);
+EAPI Evas              *evas_3d_mesh_evas_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+EAPI void               evas_3d_mesh_shade_mode_set(Evas_3D_Mesh *mesh, Evas_3D_Shade_Mode mode) EINA_ARG_NONNULL(1);
+EAPI Evas_3D_Shade_Mode evas_3d_mesh_shade_mode_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+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);
+
+EAPI void               evas_3d_mesh_vertex_count_set(Evas_3D_Mesh *mesh, unsigned int count) EINA_ARG_NONNULL(1);
+EAPI int                evas_3d_mesh_vertex_count_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+EAPI void               evas_3d_mesh_frame_add(Evas_3D_Mesh *mesh, int frame) EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_mesh_frame_del(Evas_3D_Mesh *mesh, int frame) EINA_ARG_NONNULL(1);
+
+EAPI void               evas_3d_mesh_frame_material_set(Evas_3D_Mesh *mesh, int frame, Evas_3D_Material *material) EINA_ARG_NONNULL(1);
+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);
+
+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);
+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);
+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);
+EAPI void               evas_3d_mesh_frame_vertex_data_unmap(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib) EINA_ARG_NONNULL(1);
+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);
+
+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);
+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);
+EAPI Evas_3D_Index_Format evas_3d_mesh_index_format_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI int                evas_3d_mesh_index_count_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void              *evas_3d_mesh_index_data_map(Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_mesh_index_data_unmap(Evas_3D_Mesh *mesh) EINA_ARG_NONNULL(1);
+
+EAPI void               evas_3d_mesh_vertex_assembly_set(Evas_3D_Mesh *mesh, Evas_3D_Vertex_Assembly assembly);
+EAPI Evas_3D_Vertex_Assembly evas_3d_mesh_vertex_assembly_get(const Evas_3D_Mesh *mesh);
+
+/* Texture */
+EAPI Evas_3D_Texture   *evas_3d_texture_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_texture_del(Evas_3D_Texture *texture) EINA_ARG_NONNULL(1);
+EAPI Evas              *evas_3d_texture_evas_get(const Evas_3D_Texture *texture) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+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);
+EAPI void               evas_3d_texture_file_set(Evas_3D_Texture *texture, const char *file, const char *key) EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_texture_source_set(Evas_3D_Texture *texture, Evas_Object *source) EINA_ARG_NONNULL(1);
+EAPI Evas_3D_Color_Format evas_3d_texture_color_format_get(const Evas_3D_Texture *texture) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_texture_size_get(const Evas_3D_Texture *texture, int *w, int *h) EINA_ARG_NONNULL(1);
+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);
+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);
+
+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);
+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);
+
+/* Material */
+EAPI Evas_3D_Material  *evas_3d_material_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void               evas_3d_material_del(Evas_3D_Material *material) EINA_ARG_NONNULL(1);
+EAPI Evas              *evas_3d_material_evas_get(const Evas_3D_Material *material) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+EAPI void               evas_3d_material_enable_set(Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, Eina_Bool enable) EINA_ARG_NONNULL(1);
+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);
+
+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);
+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);
+
+EAPI void               evas_3d_material_shininess_set(Evas_3D_Material *material, Evas_Real shininess) EINA_ARG_NONNULL(1);
+EAPI Evas_Real          evas_3d_material_shininess_get(const Evas_3D_Material *material) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+EAPI void               evas_3d_material_texture_set(Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, Evas_3D_Texture *texture) EINA_ARG_NONNULL(1);
+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/canvas/evas_3d_camera.c b/src/lib/evas/canvas/evas_3d_camera.c
new file mode 100644 (file)
index 0000000..fba4e7a
--- /dev/null
@@ -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 (file)
index 0000000..356e8dd
--- /dev/null
@@ -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 (file)
index 0000000..e9a131d
--- /dev/null
@@ -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 (file)
index 0000000..37d7671
--- /dev/null
@@ -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 (file)
index 0000000..7472e27
--- /dev/null
@@ -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 (file)
index 0000000..acbab4a
--- /dev/null
@@ -0,0 +1,1162 @@
+#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);
+
+   Evas_Real w = sqrt(1.0 + x.x + y.y + z.z);
+
+   node->orientation.w = 0.5 * w;
+
+   w = 0.5 / w;
+
+   /* Inverse the axis. */
+   node->orientation.x = (y.z - z.y) * w;
+   node->orientation.y = (z.x - x.z) * w;
+   node->orientation.z = (x.y - y.x) * w;
+
+   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 (file)
index 0000000..75bd241
--- /dev/null
@@ -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 (file)
index 0000000..3327fee
--- /dev/null
@@ -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 (file)
index 0000000..16e3395
--- /dev/null
@@ -0,0 +1,440 @@
+#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)
+          {
+             if (eina_list_count(proxy_src->proxies) == 0)
+               {
+                  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_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,
+                                NULL
+#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 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);
+}
index 3fd2c53..b57ba33 100644 (file)
@@ -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;
          }
       }
+
+      t3d_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 {
index d02a9b1..67b37d8 100644 (file)
@@ -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_t3d_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_t3d_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 */
index b20ecfb..24aadcd 100644 (file)
@@ -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);
diff --git a/src/lib/evas/include/evas_3d_private.h b/src/lib/evas/include/evas_3d_private.h
new file mode 100644 (file)
index 0000000..e30dacf
--- /dev/null
@@ -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 (file)
index 0000000..196d3df
--- /dev/null
@@ -0,0 +1,1526 @@
+#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;
+}
index 8f77c56..f93962b 100644 (file)
@@ -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
@@ -1285,6 +1315,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 (file)
index 0000000..938a542
--- /dev/null
@@ -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_GL_3DES
+   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 (file)
index 0000000..3f93ad5
--- /dev/null
@@ -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 (file)
index 0000000..2eb5bf4
--- /dev/null
@@ -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 (file)
index 0000000..0f22655
--- /dev/null
@@ -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 (file)
index 0000000..b06eb8b
--- /dev/null
@@ -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 (file)
index 0000000..a9f63ec
--- /dev/null
@@ -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;
+}
index a4de175..d61186e 100644 (file)
@@ -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
index bac2557..9ee1ada 100644 (file)
@@ -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;
@@ -3701,6 +3704,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)
 {
@@ -3825,6 +4007,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;
index c03950b..2ae3757 100644 (file)
@@ -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
index 1b7cab6..773bfba 100644 (file)
@@ -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
+}
index 1b8f76a..70a625d 100644 (file)
@@ -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 */
 };