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
[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
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
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 \
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 \
#ifdef EFL_EO_API_SUPPORT
#include <Evas_Eo.h>
#endif
-
#ifdef __cplusplus
}
#endif
--- /dev/null
+#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 */
--- /dev/null
+#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);
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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);
+}
double dpi; /*@ The new DPI resolution. */
}
}
+
source_clip {
set {
/*@
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 {
Evas_Object *source;
Evas_Map *defmap;
+ Evas_3D_Scene *scene;
+
union {
const char *file;
Eina_File *f;
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) */
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
{
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)
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)
{
EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, proxy_write);
}
+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)
{
}
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)
(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; */
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 */
/* 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
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);
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;
}
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
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;
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;
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);
--- /dev/null
+#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);
--- /dev/null
+#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;
+}
#include "../common/language/evas_bidi_utils.h"
#include "../common/language/evas_language_utils.h"
+#include "evas_3d_private.h"
#define RENDER_METHOD_INVALID 0x00000000
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;
struct _Evas_Object_Proxy_Data
{
Eina_List *proxies;
+ Eina_List *proxy_textures;
void *surface;
int w,h;
Eina_List *src_event_in;
RGBA_Map *spans;
};
+struct _Evas_Object_3D_Data
+{
+ void *surface;
+ int w, h;
+};
+
struct _Evas_Object_Protected_State
{
Evas_Object_Protected_Data *clipper;
// 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;
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
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;
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
--- /dev/null
+#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 */
--- /dev/null
+#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();
+}
--- /dev/null
+#ifndef EVAS_GL_3D_RENDERER_H
+#define EVAS_GL_3D_RENDERER_H
+
+
+
+#endif /* EVAS_GL_3D_RENDERER_H */
--- /dev/null
+#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;
+}
GLERR(__FUNCTION__, __FILE__, __LINE__, "");
}
+#include "evas_gl_3d_common.h"
+
#endif
void *get_pixels_data;
Evas_Object *obj;
} func;
+
+ Evas_GL_X11_Context *context_3d;
+ E3D_Renderer *renderer_3d;
};
static int initted = 0;
}
}
+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)
{
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;
#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
{
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,
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
}
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
+}
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 */
};