From: Taekyun Kim Date: Fri, 27 Dec 2013 07:56:30 +0000 (+0900) Subject: Evas: 3D: Introducing 3D scene rendering features X-Git-Tag: v1.10.0-alpha1~87 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8fda63173063e03e1d48c8b026ecf8b94298162c;p=platform%2Fupstream%2Fefl.git Evas: 3D: Introducing 3D scene rendering features 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 --- diff --git a/configure.ac b/configure.ac index c4801b9..6bf109b 100644 --- a/configure.ac +++ b/configure.ac @@ -1811,6 +1811,22 @@ AC_DEFINE_IF([EVAS_CSERVE2], [1], [Shared cache server.]) AM_CONDITIONAL([EVAS_CSERVE2], [test "x${want_evas_cserve2}" = "xyes"]) +# Evas 3D +AC_ARG_ENABLE([evas-3d], + [AC_HELP_STRING([--enable-evas-3d], + [enable 3D scene graph features on evas. @<:default=disabled@:>@])], + [ + if test "x${enableval}" = "xyes" ; then + want_evas_3d="yes" + else + want_evas_3d="no" + fi + ], + [want_evas_3d="no"]) + +AC_DEFINE_IF([EVAS_3D], [test "x${want_evas_3d}" = "xyes"], [1], [3D scene graph rendering.]) +AM_CONDITIONAL([EVAS_3D], [test "x${want_evas_3d}" = "xyes"]) + ### Configuration ## Tile rotation @@ -1852,6 +1868,7 @@ EFL_ADD_FEATURE([EVAS], [harfbuzz]) EFL_ADD_FEATURE([EVAS], [cserve], [${want_evas_cserve2}]) EFL_ADD_FEATURE([EVAS], [tile-rotate]) EFL_ADD_FEATURE([EVAS], [dither-mask], [${build_evas_dither_mask}]) +EFL_ADD_FEATURE([EVAS], [evas-3d], [${want_evas_3d}]) EFL_LIB_END([Evas]) #### End of Evas diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am index 3617f4c..9ca83a8 100644 --- a/src/Makefile_Evas.am +++ b/src/Makefile_Evas.am @@ -251,6 +251,24 @@ lib/evas/file/evas_path.h lib_evas_libevas_la_SOURCES += \ $(lib_evas_file_SOURCES) +# 3D +dist_installed_evasmainheaders_DATA += lib/evas/Evas_3D.h + +noinst_HEADERS += \ +lib/evas/include/evas_3d_utils.h \ +lib/evas/include/evas_3d_private.h + +lib_evas_libevas_la_SOURCES += \ +lib/evas/canvas/evas_3d_object.c \ +lib/evas/canvas/evas_3d_scene.c \ +lib/evas/canvas/evas_3d_node.c \ +lib/evas/canvas/evas_3d_camera.c \ +lib/evas/canvas/evas_3d_light.c \ +lib/evas/canvas/evas_3d_mesh.c \ +lib/evas/canvas/evas_3d_texture.c \ +lib/evas/canvas/evas_3d_material.c \ +lib/evas/canvas/evas_3d_mesh_loader_md2.c + # Engine lib_evas_libevas_la_SOURCES += \ lib/evas/common/evas_op_copy_main_.c \ @@ -713,6 +731,14 @@ modules/evas/engines/gl_common/shader/yuy2_nomul_frag.h \ modules/evas/engines/gl_common/shader/yuy2_nomul_vert.h \ modules/evas/engines/gl_common/shader/yuy2_vert.h +# 3D +GL_COMMON_SOURCES += \ +modules/evas/engines/gl_common/evas_gl_3d_common.h \ +modules/evas/engines/gl_common/evas_gl_3d_private.h \ +modules/evas/engines/gl_common/evas_gl_3d.c \ +modules/evas/engines/gl_common/evas_gl_3d_renderer.c \ +modules/evas/engines/gl_common/evas_gl_3d_shader.c + EXTRA_DIST += \ modules/evas/engines/gl_common/shader/compile.sh \ modules/evas/engines/gl_common/shader/make-c-str.sh \ diff --git a/src/lib/evas/Evas.h b/src/lib/evas/Evas.h index dc4d435..05ac2c0 100644 --- a/src/lib/evas/Evas.h +++ b/src/lib/evas/Evas.h @@ -292,7 +292,6 @@ extern "C" { #ifdef EFL_EO_API_SUPPORT #include #endif - #ifdef __cplusplus } #endif diff --git a/src/lib/evas/Evas_3D.h b/src/lib/evas/Evas_3D.h new file mode 100644 index 0000000..4a25246 --- /dev/null +++ b/src/lib/evas/Evas_3D.h @@ -0,0 +1,289 @@ +#ifndef _EVAS_3D_H +#define _EVAS_3D_H + +#include + +typedef float Evas_Real; + +typedef struct _Evas_3D_Scene Evas_3D_Scene; +typedef struct _Evas_3D_Node Evas_3D_Node; +typedef struct _Evas_3D_Camera Evas_3D_Camera; +typedef struct _Evas_3D_Light Evas_3D_Light; +typedef struct _Evas_3D_Mesh Evas_3D_Mesh; +typedef struct _Evas_3D_Texture Evas_3D_Texture; +typedef struct _Evas_3D_Material Evas_3D_Material; + +typedef enum _Evas_3D_Space +{ + EVAS_3D_SPACE_LOCAL, + EVAS_3D_SPACE_PARENT, + EVAS_3D_SPACE_WORLD, +} Evas_3D_Space; + +typedef enum _Evas_3D_Node_Type +{ + EVAS_3D_NODE_TYPE_NODE, + EVAS_3D_NODE_TYPE_CAMERA, + EVAS_3D_NODE_TYPE_LIGHT, + EVAS_3D_NODE_TYPE_MESH, +} Evas_3D_Node_Type; + +typedef enum _Evas_3D_Vertex_Attrib +{ + EVAS_3D_VERTEX_POSITION, + EVAS_3D_VERTEX_NORMAL, + EVAS_3D_VERTEX_TANGENT, + EVAS_3D_VERTEX_COLOR, + EVAS_3D_VERTEX_TEXCOORD, +} Evas_3D_Vertex_Attrib; + +typedef enum _Evas_3D_Index_Format +{ + EVAS_3D_INDEX_FORMAT_NONE, + EVAS_3D_INDEX_FORMAT_UNSIGNED_BYTE, + EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT, +} Evas_3D_Index_Format; + +typedef enum _Evas_3D_Vertex_Assembly +{ + EVAS_3D_VERTEX_ASSEMBLY_POINTS, + EVAS_3D_VERTEX_ASSEMBLY_LINES, + EVAS_3D_VERTEX_ASSEMBLY_LINE_STRIP, + EVAS_3D_VERTEX_ASSEMBLY_LINE_LOOP, + EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES, + EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_STRIP, + EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_FAN, +} Evas_3D_Vertex_Assembly; + +typedef enum _Evas_3D_Color_Format +{ + EVAS_3D_COLOR_FORMAT_RGBA, + EVAS_3D_COLOR_FORMAT_RGB, + EVAS_3D_COLOR_FORMAT_ALPHA, +} Evas_3D_Color_Format; + +typedef enum _Evas_3D_Pixel_Format +{ + EVAS_3D_PIXEL_FORMAT_8, + EVAS_3D_PIXEL_FORMAT_565, + EVAS_3D_PIXEL_FORMAT_888, + EVAS_3D_PIXEL_FORMAT_8888, + EVAS_3D_PIXEL_FORMAT_4444, + EVAS_3D_PIXEL_FORMAT_5551, +} Evas_3D_Pixel_Format; + +typedef enum _Evas_3D_Wrap_Mode +{ + EVAS_3D_WRAP_MODE_CLAMP, + EVAS_3D_WRAP_MODE_REPEAT, + EVAS_3D_WRAP_MODE_REFLECT, +} Evas_3D_Wrap_Mode; + +typedef enum _Evas_3D_Texture_Filter +{ + EVAS_3D_TEXTURE_FILTER_NEAREST, + EVAS_3D_TEXTURE_FILTER_LINEAR, + EVAS_3D_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST, + EVAS_3D_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST, + EVAS_3D_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR, + EVAS_3D_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR, +} Evas_3D_Texture_Filter; + +typedef enum _Evas_3D_Shade_Mode +{ + EVAS_3D_SHADE_MODE_VERTEX_COLOR, + EVAS_3D_SHADE_MODE_DIFFUSE, + EVAS_3D_SHADE_MODE_FLAT, + EVAS_3D_SHADE_MODE_PHONG, + EVAS_3D_SHADE_MODE_NORMAL_MAP, +} Evas_3D_Shade_Mode; + +typedef enum _Evas_3D_Material_Attrib +{ + EVAS_3D_MATERIAL_AMBIENT, + EVAS_3D_MATERIAL_DIFFUSE, + EVAS_3D_MATERIAL_SPECULAR, + EVAS_3D_MATERIAL_EMISSION, + EVAS_3D_MATERIAL_NORMAL, +} Evas_3D_Material_Attrib; + +typedef enum _Evas_3D_Mesh_File_Type +{ + EVAS_3D_MESH_FILE_TYPE_MD2, +} Evas_3D_Mesh_File_Type; + +typedef enum _Evas_3D_Pick_Type +{ + EVAS_3D_PICK_NODE, + EVAS_3D_PICK_MESH, +} Evas_3D_Pick_Type; + +/* Image object render target */ +EAPI void evas_object_image_3d_scene_set(Evas_Object *obj, Evas_3D_Scene *scene) EINA_ARG_NONNULL(1); +EAPI Evas_3D_Scene *evas_object_image_3d_scene_get(const Evas_Object *obj) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/* Scene */ +EAPI Evas_3D_Scene *evas_3d_scene_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void evas_3d_scene_del(Evas_3D_Scene *scene) EINA_ARG_NONNULL(1); +EAPI Evas *evas_3d_scene_evas_get(const Evas_3D_Scene *scene) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_scene_root_node_set(Evas_3D_Scene *scene, Evas_3D_Node *node) EINA_ARG_NONNULL(1); +EAPI Evas_3D_Node *evas_3d_scene_root_node_get(const Evas_3D_Scene *scene) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void evas_3d_scene_camera_node_set(Evas_3D_Scene *scene, Evas_3D_Node *node) EINA_ARG_NONNULL(1); +EAPI Evas_3D_Node *evas_3d_scene_camera_node_get(const Evas_3D_Scene *scene) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_scene_size_set(Evas_3D_Scene *scene, int w, int h) EINA_ARG_NONNULL(1); +EAPI void evas_3d_scene_size_get(const Evas_3D_Scene *scene, int *w, int *h) EINA_ARG_NONNULL(1); + +EAPI void evas_3d_scene_background_color_set(Evas_3D_Scene *scene, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) EINA_ARG_NONNULL(1); +EAPI void evas_3d_scene_background_color_get(const Evas_3D_Scene *scene, Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) EINA_ARG_NONNULL(1); + +EAPI Eina_Bool evas_3d_scene_pick(const Evas_3D_Scene *scene, Evas_Real x, Evas_Real y, Evas_3D_Node **node, Evas_3D_Mesh **mesh, Evas_Real *s, Evas_Real *t) EINA_ARG_NONNULL(1); + +/* Node */ +EAPI Evas_3D_Node *evas_3d_node_add(Evas *e, Evas_3D_Node_Type type) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void evas_3d_node_del(Evas_3D_Node *node) EINA_ARG_NONNULL(1); +EAPI Evas_3D_Node_Type evas_3d_node_type_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI Evas *evas_3d_node_evas_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_node_member_add(Evas_3D_Node *node, Evas_3D_Node *member) EINA_ARG_NONNULL(1, 2); +EAPI void evas_3d_node_member_del(Evas_3D_Node *node, Evas_3D_Node *member) EINA_ARG_NONNULL(1, 2); +EAPI Evas_3D_Node *evas_3d_node_parent_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI const Eina_List *evas_3d_node_member_list_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_node_position_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z) EINA_ARG_NONNULL(1); +EAPI void evas_3d_node_orientation_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z, Evas_Real w) EINA_ARG_NONNULL(1); +EAPI void evas_3d_node_orientation_euler_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z) EINA_ARG_NONNULL(1); +EAPI void evas_3d_node_orientation_angle_axis_set(Evas_3D_Node *node, Evas_Real angle, Evas_Real x, Evas_Real y, Evas_Real z) EINA_ARG_NONNULL(1); +EAPI void evas_3d_node_scale_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z) EINA_ARG_NONNULL(1); + +EAPI void evas_3d_node_position_get(const Evas_3D_Node *node, Evas_3D_Space space, Evas_Real *x, Evas_Real *y, Evas_Real *z) EINA_ARG_NONNULL(1); +EAPI void evas_3d_node_orientation_get(const Evas_3D_Node *node, Evas_3D_Space space, Evas_Real *x, Evas_Real *y, Evas_Real *z, Evas_Real *w) EINA_ARG_NONNULL(1); +EAPI void evas_3d_node_scale_get(const Evas_3D_Node *node, Evas_3D_Space space, Evas_Real *x, Evas_Real *y, Evas_Real *z) EINA_ARG_NONNULL(1); + +EAPI void evas_3d_node_position_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) EINA_ARG_NONNULL(1); +EAPI void evas_3d_node_orientation_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) EINA_ARG_NONNULL(1); +EAPI void evas_3d_node_scale_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) EINA_ARG_NONNULL(1); + +EAPI Eina_Bool evas_3d_node_position_inherit_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI Eina_Bool evas_3d_node_orientation_inherit_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI Eina_Bool evas_3d_node_scale_inherit_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_node_look_at_set(Evas_3D_Node *node, Evas_3D_Space target_space, Evas_Real x, Evas_Real y, Evas_Real z, Evas_3D_Space up_space, Evas_Real ux, Evas_Real uy, Evas_Real uz) EINA_ARG_NONNULL(1); + +EAPI void evas_3d_node_camera_set(Evas_3D_Node *node, Evas_3D_Camera *camera) EINA_ARG_NONNULL(1); +EAPI Evas_3D_Camera *evas_3d_node_camera_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_node_light_set(Evas_3D_Node *node, Evas_3D_Light *light) EINA_ARG_NONNULL(1); +EAPI Evas_3D_Light *evas_3d_node_light_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_node_mesh_add(Evas_3D_Node *node, Evas_3D_Mesh *mesh) EINA_ARG_NONNULL(1); +EAPI void evas_3d_node_mesh_del(Evas_3D_Node *node, Evas_3D_Mesh *mesh) EINA_ARG_NONNULL(1); +EAPI const Eina_List *evas_3d_node_mesh_list_get(const Evas_3D_Node *node) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void evas_3d_node_mesh_frame_set(Evas_3D_Node *node, Evas_3D_Mesh *mesh, int frame) EINA_ARG_NONNULL(1); +EAPI int evas_3d_node_mesh_frame_get(const Evas_3D_Node *node, Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/* Camera */ +EAPI Evas_3D_Camera *evas_3d_camera_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void evas_3d_camera_del(Evas_3D_Camera *camera) EINA_ARG_NONNULL(1); +EAPI Evas *evas_3d_camera_evas_get(const Evas_3D_Camera *camera) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_camera_projection_matrix_set(Evas_3D_Camera *camera, const Evas_Real *matrix) EINA_ARG_NONNULL(1); +EAPI void evas_3d_camera_projection_matrix_get(const Evas_3D_Camera *camera, Evas_Real *matrix) EINA_ARG_NONNULL(1, 2); +EAPI void evas_3d_camera_projection_perspective_set(Evas_3D_Camera *camera, Evas_Real fovy, Evas_Real aspect, Evas_Real near, Evas_Real far) EINA_ARG_NONNULL(1); +EAPI void evas_3d_camera_projection_frustum_set(Evas_3D_Camera *camera, Evas_Real left, Evas_Real right, Evas_Real bottom, Evas_Real top, Evas_Real near, Evas_Real far) EINA_ARG_NONNULL(1); +EAPI void evas_3d_camera_projection_ortho_set(Evas_3D_Camera *camera, Evas_Real left, Evas_Real right, Evas_Real bottom, Evas_Real top, Evas_Real near, Evas_Real far) EINA_ARG_NONNULL(1); + +/* Light */ +EAPI Evas_3D_Light *evas_3d_light_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void evas_3d_light_del(Evas_3D_Light *light) EINA_ARG_NONNULL(1); +EAPI Evas *evas_3d_light_evas_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_light_directional_set(Evas_3D_Light *light, Eina_Bool directional) EINA_ARG_NONNULL(1); +EAPI Eina_Bool evas_3d_light_directional_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_light_ambient_set(Evas_3D_Light *light, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) EINA_ARG_NONNULL(1); +EAPI void evas_3d_light_ambient_get(const Evas_3D_Light *light, Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) EINA_ARG_NONNULL(1); +EAPI void evas_3d_light_diffuse_set(Evas_3D_Light *light, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) EINA_ARG_NONNULL(1); +EAPI void evas_3d_light_diffuse_get(const Evas_3D_Light *light, Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) EINA_ARG_NONNULL(1); +EAPI void evas_3d_light_specular_set(Evas_3D_Light *light, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) EINA_ARG_NONNULL(1); +EAPI void evas_3d_light_specular_get(const Evas_3D_Light *light, Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) EINA_ARG_NONNULL(1); +EAPI void evas_3d_light_spot_exponent_set(Evas_3D_Light *light, Evas_Real exponent) EINA_ARG_NONNULL(1); +EAPI Evas_Real evas_3d_light_spot_exponent_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void evas_3d_light_spot_cutoff_set(Evas_3D_Light *light, Evas_Real cutoff) EINA_ARG_NONNULL(1); +EAPI Evas_Real evas_3d_light_spot_cutoff_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void evas_3d_light_attenuation_set(Evas_3D_Light *light, Evas_Real constant, Evas_Real linear, Evas_Real quadratic) EINA_ARG_NONNULL(1); +EAPI void evas_3d_light_attenuation_get(const Evas_3D_Light *light, Evas_Real *constant, Evas_Real *linear, Evas_Real *quadratic) EINA_ARG_NONNULL(1); + +EAPI void evas_3d_light_attenuation_enable_set(Evas_3D_Light *light, Eina_Bool enable) EINA_ARG_NONNULL(1); +EAPI Eina_Bool evas_3d_light_attenuation_enable_get(const Evas_3D_Light *light) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/* Mesh */ +EAPI Evas_3D_Mesh *evas_3d_mesh_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void evas_3d_mesh_del(Evas_3D_Mesh *mesh) EINA_ARG_NONNULL(1); +EAPI Evas *evas_3d_mesh_evas_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_mesh_shade_mode_set(Evas_3D_Mesh *mesh, Evas_3D_Shade_Mode mode) EINA_ARG_NONNULL(1); +EAPI Evas_3D_Shade_Mode evas_3d_mesh_shade_mode_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_mesh_file_set(Evas_3D_Mesh *mesh, Evas_3D_Mesh_File_Type type, const char *file, const char *key) EINA_ARG_NONNULL(1); + +EAPI void evas_3d_mesh_vertex_count_set(Evas_3D_Mesh *mesh, unsigned int count) EINA_ARG_NONNULL(1); +EAPI int evas_3d_mesh_vertex_count_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_mesh_frame_add(Evas_3D_Mesh *mesh, int frame) EINA_ARG_NONNULL(1); +EAPI void evas_3d_mesh_frame_del(Evas_3D_Mesh *mesh, int frame) EINA_ARG_NONNULL(1); + +EAPI void evas_3d_mesh_frame_material_set(Evas_3D_Mesh *mesh, int frame, Evas_3D_Material *material) EINA_ARG_NONNULL(1); +EAPI Evas_3D_Material *evas_3d_mesh_frame_material_get(const Evas_3D_Mesh *mesh, int frame) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_mesh_frame_vertex_data_set(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib, int stride, const void *data) EINA_ARG_NONNULL(1); +EAPI void evas_3d_mesh_frame_vertex_data_copy_set(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib, int stride, const void *data) EINA_ARG_NONNULL(1); +EAPI void *evas_3d_mesh_frame_vertex_data_map(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void evas_3d_mesh_frame_vertex_data_unmap(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib) EINA_ARG_NONNULL(1); +EAPI int evas_3d_mesh_frame_vertex_stride_get(const Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_mesh_index_data_set(Evas_3D_Mesh *mesh, Evas_3D_Index_Format format, int count, const void *indices) EINA_ARG_NONNULL(1); +EAPI void evas_3d_mesh_index_data_copy_set(Evas_3D_Mesh *mesh, Evas_3D_Index_Format format, int count, const void *indices) EINA_ARG_NONNULL(1); +EAPI Evas_3D_Index_Format evas_3d_mesh_index_format_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI int evas_3d_mesh_index_count_get(const Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void *evas_3d_mesh_index_data_map(Evas_3D_Mesh *mesh) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void evas_3d_mesh_index_data_unmap(Evas_3D_Mesh *mesh) EINA_ARG_NONNULL(1); + +EAPI void evas_3d_mesh_vertex_assembly_set(Evas_3D_Mesh *mesh, Evas_3D_Vertex_Assembly assembly); +EAPI Evas_3D_Vertex_Assembly evas_3d_mesh_vertex_assembly_get(const Evas_3D_Mesh *mesh); + +/* Texture */ +EAPI Evas_3D_Texture *evas_3d_texture_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void evas_3d_texture_del(Evas_3D_Texture *texture) EINA_ARG_NONNULL(1); +EAPI Evas *evas_3d_texture_evas_get(const Evas_3D_Texture *texture) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_texture_data_set(Evas_3D_Texture *texture, Evas_3D_Color_Format color_format, Evas_3D_Pixel_Format pixel_format, int w, int h, const void *data); +EAPI void evas_3d_texture_file_set(Evas_3D_Texture *texture, const char *file, const char *key) EINA_ARG_NONNULL(1); +EAPI void evas_3d_texture_source_set(Evas_3D_Texture *texture, Evas_Object *source) EINA_ARG_NONNULL(1); +EAPI Evas_3D_Color_Format evas_3d_texture_color_format_get(const Evas_3D_Texture *texture) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void evas_3d_texture_size_get(const Evas_3D_Texture *texture, int *w, int *h) EINA_ARG_NONNULL(1); +EAPI void evas_3d_texture_wrap_set(Evas_3D_Texture *texture, Evas_3D_Wrap_Mode s, Evas_3D_Wrap_Mode t) EINA_ARG_NONNULL(1); +EAPI void evas_3d_texture_wrap_get(const Evas_3D_Texture *texture, Evas_3D_Wrap_Mode *s, Evas_3D_Wrap_Mode *t) EINA_ARG_NONNULL(1); + +EAPI void evas_3d_texture_filter_set(Evas_3D_Texture *texture, Evas_3D_Texture_Filter min, Evas_3D_Texture_Filter mag) EINA_ARG_NONNULL(1); +EAPI void evas_3d_texture_filter_get(const Evas_3D_Texture *texture, Evas_3D_Texture_Filter *min, Evas_3D_Texture_Filter *mag) EINA_ARG_NONNULL(1); + +/* Material */ +EAPI Evas_3D_Material *evas_3d_material_add(Evas *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void evas_3d_material_del(Evas_3D_Material *material) EINA_ARG_NONNULL(1); +EAPI Evas *evas_3d_material_evas_get(const Evas_3D_Material *material) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_material_enable_set(Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, Eina_Bool enable) EINA_ARG_NONNULL(1); +EAPI Eina_Bool evas_3d_material_enable_get(const Evas_3D_Material *material, Evas_3D_Material_Attrib attrib) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_material_color_set(Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) EINA_ARG_NONNULL(1); +EAPI void evas_3d_material_color_get(const Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) EINA_ARG_NONNULL(1); + +EAPI void evas_3d_material_shininess_set(Evas_3D_Material *material, Evas_Real shininess) EINA_ARG_NONNULL(1); +EAPI Evas_Real evas_3d_material_shininess_get(const Evas_3D_Material *material) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI void evas_3d_material_texture_set(Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, Evas_3D_Texture *texture) EINA_ARG_NONNULL(1); +EAPI Evas_3D_Texture *evas_3d_material_texture_get(const Evas_3D_Material *material, Evas_3D_Material_Attrib attrib) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +#endif /* _EVAS_3D_H */ diff --git a/src/lib/evas/canvas/evas_3d_camera.c b/src/lib/evas/canvas/evas_3d_camera.c new file mode 100644 index 0000000..fba4e7a --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_camera.c @@ -0,0 +1,159 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "evas_common_private.h" +#include "evas_private.h" + +static void +_camera_free(Evas_3D_Object *obj) +{ + Evas_3D_Camera *camera = (Evas_3D_Camera *)obj; + + if (camera->nodes) + eina_hash_free(camera->nodes); + + free(camera); +} + +static Eina_Bool +_camera_node_change_notify(const Eina_Hash *hash EINA_UNUSED, const void *key, + void *data EINA_UNUSED, void *fdata) +{ + Evas_3D_Node *n = *(Evas_3D_Node **)key; + evas_3d_object_change(&n->base, EVAS_3D_STATE_NODE_CAMERA, (Evas_3D_Object *)fdata); + return EINA_TRUE; +} + +static void +_camera_change(Evas_3D_Object *obj, Evas_3D_State state EINA_UNUSED, + Evas_3D_Object *ref EINA_UNUSED) +{ + Evas_3D_Camera *camera = (Evas_3D_Camera *)obj; + + if (camera->nodes) + eina_hash_foreach(camera->nodes, _camera_node_change_notify, obj); +} + +static const Evas_3D_Object_Func camera_func = +{ + _camera_free, + _camera_change, + NULL, +}; + +void +evas_3d_camera_node_add(Evas_3D_Camera *camera, Evas_3D_Node *node) +{ + int count = 0; + + if (camera->nodes == NULL) + { + camera->nodes = eina_hash_pointer_new(NULL); + + if (camera->nodes == NULL) + { + ERR("Failed to create hash table."); + return; + } + } + else + count = (int)eina_hash_find(camera->nodes, &node); + + eina_hash_set(camera->nodes, &node, (const void *)(count + 1)); +} + +void +evas_3d_camera_node_del(Evas_3D_Camera *camera, Evas_3D_Node *node) +{ + int count = 0; + + if (camera->nodes == NULL) + { + ERR("No node to delete."); + return; + } + + count = (int)eina_hash_find(camera->nodes, &node); + + if (count == 1) + eina_hash_del(camera->nodes, &node, NULL); + else + eina_hash_set(camera->nodes, &node, (const void *)(count - 1)); +} + +Evas_3D_Camera * +evas_3d_camera_new(Evas *e) +{ + Evas_3D_Camera *camera = NULL; + + camera = (Evas_3D_Camera *)calloc(1, sizeof(Evas_3D_Camera)); + + if (camera == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + evas_3d_object_init(&camera->base, e, EVAS_3D_OBJECT_TYPE_CAMERA, &camera_func); + return camera; +} + +EAPI Evas_3D_Camera * +evas_3d_camera_add(Evas *e) +{ + return evas_3d_camera_new(e); +} + +EAPI void +evas_3d_camera_del(Evas_3D_Camera *camera) +{ + evas_3d_object_unreference(&camera->base); +} + +EAPI Evas * +evas_3d_camera_evas_get(const Evas_3D_Camera *camera) +{ + return camera->base.evas; +} + +EAPI void +evas_3d_camera_projection_matrix_set(Evas_3D_Camera *camera, const Evas_Real *matrix) +{ + evas_mat4_array_set(&camera->projection, matrix); + evas_3d_object_change(&camera->base, EVAS_3D_STATE_CAMERA_PROJECTION, NULL); +} + +EAPI void +evas_3d_camera_projection_matrix_get(const Evas_3D_Camera *camera, Evas_Real *matrix) +{ + memcpy(matrix, &camera->projection.m[0], sizeof(Evas_Real) * 16); +} + +EAPI void +evas_3d_camera_projection_perspective_set(Evas_3D_Camera *camera, Evas_Real fovy, Evas_Real aspect, Evas_Real near, Evas_Real far) +{ + Evas_Real xmax; + Evas_Real ymax; + + ymax = near * (Evas_Real)tan((double)fovy * M_PI / 360.0); + xmax = ymax * aspect; + + evas_mat4_frustum_set(&camera->projection, -xmax, xmax, -ymax, ymax, near, far); + evas_3d_object_change(&camera->base, EVAS_3D_STATE_CAMERA_PROJECTION, NULL); +} + +EAPI void +evas_3d_camera_projection_frustum_set(Evas_3D_Camera *camera, Evas_Real left, Evas_Real right, Evas_Real bottom, Evas_Real top, Evas_Real near, Evas_Real far) +{ + evas_mat4_frustum_set(&camera->projection, left, right, bottom, top, near, far); + evas_3d_object_change(&camera->base, EVAS_3D_STATE_CAMERA_PROJECTION, NULL); +} + +EAPI void +evas_3d_camera_projection_ortho_set(Evas_3D_Camera *camera, Evas_Real left, Evas_Real right, Evas_Real bottom, Evas_Real top, Evas_Real near, Evas_Real far) +{ + evas_mat4_ortho_set(&camera->projection, left, right, bottom, top, near, far); + evas_3d_object_change(&camera->base, EVAS_3D_STATE_CAMERA_PROJECTION, NULL); +} diff --git a/src/lib/evas/canvas/evas_3d_light.c b/src/lib/evas/canvas/evas_3d_light.c new file mode 100644 index 0000000..356e8dd --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_light.c @@ -0,0 +1,273 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include "evas_common_private.h" +#include "evas_private.h" + +static void +_light_free(Evas_3D_Object *obj) +{ + Evas_3D_Light *light = (Evas_3D_Light *)obj; + + if (light->nodes) + eina_hash_free(light->nodes); + + free(light); +} + +static Eina_Bool +_light_node_change_notify(const Eina_Hash *hash EINA_UNUSED, const void *key, + void *data EINA_UNUSED, void *fdata) +{ + Evas_3D_Node *n = *(Evas_3D_Node **)key; + evas_3d_object_change(&n->base, EVAS_3D_STATE_NODE_LIGHT, (Evas_3D_Object *)fdata); + return EINA_TRUE; +} + +static void +_light_change(Evas_3D_Object *obj, Evas_3D_State state EINA_UNUSED, + Evas_3D_Object *ref EINA_UNUSED) +{ + Evas_3D_Light *light = (Evas_3D_Light *)obj; + + if (light->nodes) + eina_hash_foreach(light->nodes, _light_node_change_notify, obj); +} + +static const Evas_3D_Object_Func light_func = +{ + _light_free, + _light_change, + NULL, +}; + +void +evas_3d_light_node_add(Evas_3D_Light *light, Evas_3D_Node *node) +{ + int count = 0; + + if (light->nodes == NULL) + { + light->nodes = eina_hash_pointer_new(NULL); + + if (light->nodes == NULL) + { + ERR("Failed to create hash table."); + return; + } + } + else + count = (int)eina_hash_find(light->nodes, &node); + + eina_hash_set(light->nodes, &node, (const void *)(count + 1)); +} + +void +evas_3d_light_node_del(Evas_3D_Light *light, Evas_3D_Node *node) +{ + int count = 0; + + if (light->nodes == NULL) + { + ERR("No node to delete."); + return; + } + + count = (int)eina_hash_find(light->nodes, &node); + + if (count == 1) + eina_hash_del(light->nodes, &node, NULL); + else + eina_hash_set(light->nodes, &node, (const void *)(count - 1)); +} + +Evas_3D_Light * +evas_3d_light_new(Evas *e) +{ + Evas_3D_Light *light = NULL; + + light = (Evas_3D_Light *)calloc(1, sizeof(Evas_3D_Light)); + + if (light == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + evas_3d_object_init(&light->base, e, EVAS_3D_OBJECT_TYPE_LIGHT, &light_func); + + evas_color_set(&light->ambient, 0.0, 0.0, 0.0, 1.0); + evas_color_set(&light->diffuse, 1.0, 1.0, 1.0, 1.0); + evas_color_set(&light->specular, 1.0, 1.0, 1.0, 1.0); + + light->spot_exp = 0.0; + light->spot_cutoff = 180.0; + light->spot_cutoff_cos = -1.0; + + light->atten_const = 1.0; + light->atten_linear = 0.0; + light->atten_quad = 0.0; + + return light; +} + +EAPI Evas_3D_Light * +evas_3d_light_add(Evas *e) +{ + return evas_3d_light_new(e); +} + +EAPI void +evas_3d_light_del(Evas_3D_Light *light) +{ + evas_3d_object_unreference(&light->base); +} + +EAPI Evas * +evas_3d_light_evas_get(const Evas_3D_Light *light) +{ + return light->base.evas; +} + +EAPI void +evas_3d_light_directional_set(Evas_3D_Light *light, Eina_Bool directional) +{ + if (light->directional != directional) + { + light->directional = directional; + evas_3d_object_change(&light->base, EVAS_3D_STATE_ANY, NULL); + } +} + +EAPI Eina_Bool +evas_3d_light_directional_get(const Evas_3D_Light *light) +{ + return light->directional; +} + +EAPI void +evas_3d_light_ambient_set(Evas_3D_Light *light, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) +{ + light->ambient.r = r; + light->ambient.g = g; + light->ambient.b = b; + light->ambient.a = a; + + evas_3d_object_change(&light->base, EVAS_3D_STATE_LIGHT_AMBIENT, NULL); +} + +EAPI void +evas_3d_light_ambient_get(const Evas_3D_Light *light, + Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) +{ + if (r) *r = light->ambient.r; + if (g) *g = light->ambient.g; + if (b) *b = light->ambient.b; + if (a) *a = light->ambient.a; +} + +EAPI void +evas_3d_light_diffuse_set(Evas_3D_Light *light, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) +{ + light->diffuse.r = r; + light->diffuse.g = g; + light->diffuse.b = b; + light->diffuse.a = a; + + evas_3d_object_change(&light->base, EVAS_3D_STATE_LIGHT_DIFFUSE, NULL); +} + +EAPI void +evas_3d_light_diffuse_get(const Evas_3D_Light *light, + Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) +{ + if (r) *r = light->diffuse.r; + if (g) *g = light->diffuse.g; + if (b) *b = light->diffuse.b; + if (a) *a = light->diffuse.a; +} + +EAPI void +evas_3d_light_specular_set(Evas_3D_Light *light, Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) +{ + light->specular.r = r; + light->specular.g = g; + light->specular.b = b; + light->specular.a = a; + + evas_3d_object_change(&light->base, EVAS_3D_STATE_LIGHT_SPECULAR, NULL); +} + +EAPI void +evas_3d_light_specular_get(const Evas_3D_Light *light, + Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) +{ + if (r) *r = light->specular.r; + if (g) *g = light->specular.g; + if (b) *b = light->specular.b; + if (a) *a = light->specular.a; +} + +EAPI void +evas_3d_light_spot_exponent_set(Evas_3D_Light *light, Evas_Real exponent) +{ + light->spot_exp = exponent; + evas_3d_object_change(&light->base, EVAS_3D_STATE_LIGHT_SPOT_EXP, NULL); +} + +EAPI Evas_Real +evas_3d_light_spot_exponent_get(const Evas_3D_Light *light) +{ + return light->spot_exp; +} + +EAPI void +evas_3d_light_spot_cutoff_set(Evas_3D_Light *light, Evas_Real cutoff) +{ + light->spot_cutoff = cutoff; + light->spot_cutoff_cos = cos(cutoff * M_PI / 180.0); + evas_3d_object_change(&light->base, EVAS_3D_STATE_LIGHT_SPOT_CUTOFF, NULL); +} + +EAPI Evas_Real +evas_3d_light_spot_cutoff_get(const Evas_3D_Light *light) +{ + return light->spot_cutoff; +} + +EAPI void +evas_3d_light_attenuation_set(Evas_3D_Light *light, + Evas_Real constant, Evas_Real linear, Evas_Real quadratic) +{ + light->atten_const = constant; + light->atten_linear = linear; + light->atten_quad = quadratic; + evas_3d_object_change(&light->base, EVAS_3D_STATE_LIGHT_ATTENUATION, NULL); +} + +EAPI void +evas_3d_light_attenuation_get(const Evas_3D_Light *light, Evas_Real *constant, Evas_Real *linear, Evas_Real *quadratic) +{ + if (constant) *constant = light->atten_const; + if (linear) *linear = light->atten_linear; + if (quadratic) *quadratic = light->atten_quad; +} + +EAPI void +evas_3d_light_attenuation_enable_set(Evas_3D_Light *light, Eina_Bool enable) +{ + if (light->enable_attenuation != enable) + { + light->enable_attenuation = enable; + evas_3d_object_change(&light->base, EVAS_3D_STATE_LIGHT_ATTENUATION, NULL); + } +} + +EAPI Eina_Bool +evas_3d_light_attenuation_enable_get(const Evas_3D_Light *light) +{ + return light->enable_attenuation; +} diff --git a/src/lib/evas/canvas/evas_3d_material.c b/src/lib/evas/canvas/evas_3d_material.c new file mode 100644 index 0000000..e9a131d --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_material.c @@ -0,0 +1,221 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "evas_common_private.h" +#include "evas_private.h" + +static void +_material_free(Evas_3D_Object *obj) +{ + int i; + Evas_3D_Material *material = (Evas_3D_Material *)obj; + + if (material->meshes) + eina_hash_free(material->meshes); + + for (i = 0; i < EVAS_3D_MATERIAL_ATTRIB_COUNT; i++) + { + if (material->attribs[i].texture) + { + evas_3d_texture_material_del(material->attribs[i].texture, material); + evas_3d_object_unreference(&material->attribs[i].texture->base); + } + } + + free(material); +} + +static Eina_Bool +_material_mesh_change_notify(const Eina_Hash *hash EINA_UNUSED, const void *key, + void *data EINA_UNUSED, void *fdata) +{ + Evas_3D_Mesh *m = *(Evas_3D_Mesh **)key; + evas_3d_object_change(&m->base, EVAS_3D_STATE_MESH_MATERIAL, (Evas_3D_Object *)fdata); + return EINA_TRUE; +} + +static void +_material_change(Evas_3D_Object *obj, Evas_3D_State state EINA_UNUSED, + Evas_3D_Object *ref EINA_UNUSED) +{ + Evas_3D_Material *material = (Evas_3D_Material *)obj; + + if (material->meshes) + eina_hash_foreach(material->meshes, _material_mesh_change_notify, obj); +} + +static void +_material_update(Evas_3D_Object *obj) +{ + int i; + Evas_3D_Material *material = (Evas_3D_Material *)obj; + + for (i = 0; i < EVAS_3D_MATERIAL_ATTRIB_COUNT; i++) + { + if (material->attribs[i].enable) + { + if (material->attribs[i].texture) + evas_3d_object_update(&material->attribs[i].texture->base); + } + } +} + +static const Evas_3D_Object_Func material_func = +{ + _material_free, + _material_change, + _material_update, +}; + +void +evas_3d_material_mesh_add(Evas_3D_Material *material, Evas_3D_Mesh *mesh) +{ + int count = 0; + + if (material->meshes == NULL) + { + material->meshes = eina_hash_pointer_new(NULL); + + if (material->meshes == NULL) + { + ERR("Failed to create hash table."); + return; + } + } + else + count = (int)eina_hash_find(material->meshes, &mesh); + + eina_hash_set(material->meshes, &mesh, (const void *)(count + 1)); +} + +void +evas_3d_material_mesh_del(Evas_3D_Material *material, Evas_3D_Mesh *mesh) +{ + int count = 0; + + if (material->meshes == NULL) + { + ERR("No mesh to delete."); + return; + } + + count = (int)eina_hash_find(material->meshes, &mesh); + + if (count == 1) + eina_hash_del(material->meshes, &mesh, NULL); + else + eina_hash_set(material->meshes, &mesh, (const void *)(count - 1)); +} + +Evas_3D_Material * +evas_3d_material_new(Evas *e) +{ + Evas_3D_Material *material = NULL; + + material = (Evas_3D_Material *)calloc(1, sizeof(Evas_3D_Material)); + + if (material == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + evas_3d_object_init(&material->base, e, EVAS_3D_OBJECT_TYPE_MATERIAL, &material_func); + + evas_color_set(&material->attribs[EVAS_3D_MATERIAL_AMBIENT].color, 0.2, 0.2, 0.2, 1.0); + evas_color_set(&material->attribs[EVAS_3D_MATERIAL_DIFFUSE].color, 0.8, 0.8, 0.8, 1.0); + evas_color_set(&material->attribs[EVAS_3D_MATERIAL_SPECULAR].color, 1.0, 1.0, 1.0, 1.0); + evas_color_set(&material->attribs[EVAS_3D_MATERIAL_EMISSION].color, 0.0, 0.0, 0.0, 1.0); + material->shininess = 150.0; + + return material; +} + +EAPI Evas_3D_Material * +evas_3d_material_add(Evas *e) +{ + return evas_3d_material_new(e); +} + +EAPI void +evas_3d_material_del(Evas_3D_Material *material) +{ + evas_3d_object_unreference(&material->base); +} + +EAPI Evas * +evas_3d_material_evas_get(const Evas_3D_Material *material) +{ + return material->base.evas; +} + +EAPI void +evas_3d_material_enable_set(Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, + Eina_Bool enable) +{ + material->attribs[attrib].enable = enable; +} + +EAPI Eina_Bool +evas_3d_material_enable_get(const Evas_3D_Material *material, Evas_3D_Material_Attrib attrib) +{ + return material->attribs[attrib].enable; +} + +EAPI void +evas_3d_material_color_set(Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, + Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) +{ + evas_color_set(&material->attribs[attrib].color, r, g, b, a); + evas_3d_object_change(&material->base, EVAS_3D_STATE_MATERIAL_COLOR, NULL); +} + +EAPI void +evas_3d_material_color_get(const Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, + Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) +{ + if (r) *r = material->attribs[attrib].color.r; + if (g) *g = material->attribs[attrib].color.g; + if (b) *b = material->attribs[attrib].color.b; + if (a) *a = material->attribs[attrib].color.a; +} + +EAPI void +evas_3d_material_shininess_set(Evas_3D_Material *material, Evas_Real shininess) +{ + material->shininess = shininess; +} + +EAPI Evas_Real +evas_3d_material_shininess_get(const Evas_3D_Material *material) +{ + return material->shininess; +} + +EAPI void +evas_3d_material_texture_set(Evas_3D_Material *material, Evas_3D_Material_Attrib attrib, + Evas_3D_Texture *texture) +{ + if (material->attribs[attrib].texture != texture) + { + if (material->attribs[attrib].texture) + { + evas_3d_texture_material_del(material->attribs[attrib].texture, material); + evas_3d_object_unreference(&material->attribs[attrib].texture->base); + } + + material->attribs[attrib].texture = texture; + evas_3d_texture_material_add(texture, material); + evas_3d_object_reference(&texture->base); + } + + evas_3d_object_change(&material->base, EVAS_3D_STATE_MATERIAL_TEXTURE, NULL); +} + +EAPI Evas_3D_Texture * +evas_3d_material_texture_get(const Evas_3D_Material *material, Evas_3D_Material_Attrib attrib) +{ + return material->attribs[attrib].texture; +} diff --git a/src/lib/evas/canvas/evas_3d_mesh.c b/src/lib/evas/canvas/evas_3d_mesh.c new file mode 100644 index 0000000..37d7671 --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_mesh.c @@ -0,0 +1,828 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#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 vertex_count; i++) + { + *dst++ = src[0]; + + src = (float *)((char *)src + stride); + } + } + else if (element_count == 2) + { + for (i = 0; i vertex_count; i++) + { + *dst++ = src[0]; + *dst++ = src[1]; + + src = (float *)((char *)src + stride); + } + } + else if (element_count == 3) + { + for (i = 0; i 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 vertex_count; i++) + { + *dst++ = src[0]; + *dst++ = src[1]; + *dst++ = src[2]; + *dst++ = src[3]; + + src = (float *)((char *)src + stride); + } + } + } + + evas_3d_object_change(&mesh->base, EVAS_3D_STATE_MESH_VERTEX_DATA, NULL); +} + +EAPI void * +evas_3d_mesh_frame_vertex_data_map(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib) +{ + Evas_3D_Mesh_Frame *f = evas_3d_mesh_frame_find(mesh, frame); + + if (f == NULL) + { + ERR("Not existing mesh frame."); + return NULL; + } + + if (f->vertices[attrib].mapped) + { + ERR("Try to map alreadly mapped data."); + return NULL; + } + + f->vertices[attrib].mapped = EINA_TRUE; + return f->vertices[attrib].data; +} + +EAPI void +evas_3d_mesh_frame_vertex_data_unmap(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib) +{ + Evas_3D_Mesh_Frame *f = evas_3d_mesh_frame_find(mesh, frame); + + if (f == NULL) + { + ERR("Not existing mesh frame."); + return; + } + + if (!f->vertices[attrib].mapped) + { + ERR("Try to unmap data which is not mapped yet."); + return; + } + + f->vertices[attrib].mapped = EINA_FALSE; +} + +EAPI int +evas_3d_mesh_frame_vertex_stride_get(const Evas_3D_Mesh *mesh, int frame, + Evas_3D_Vertex_Attrib attrib) +{ + Evas_3D_Mesh_Frame *f = evas_3d_mesh_frame_find((Evas_3D_Mesh *)mesh, frame); + + if (f == NULL) + { + ERR("Not existing mesh frame."); + return 0; + } + + return f->vertices[attrib].stride; +} + +EAPI void +evas_3d_mesh_index_data_set(Evas_3D_Mesh *mesh, Evas_3D_Index_Format format, int count, + const void *indices) +{ + if (mesh->owns_indices && mesh->indices) + free(mesh->indices); + + mesh->index_format = format; + mesh->index_count = count; + mesh->index_size = 0; + mesh->indices = (void *)indices; + mesh->owns_indices = EINA_FALSE; + + evas_3d_object_change(&mesh->base, EVAS_3D_STATE_MESH_INDEX_DATA, NULL); +} + +EAPI void +evas_3d_mesh_index_data_copy_set(Evas_3D_Mesh *mesh, Evas_3D_Index_Format format, int count, const void *indices) +{ + int size; + + if (format == EVAS_3D_INDEX_FORMAT_UNSIGNED_BYTE) + { + size = count * sizeof(unsigned char); + } + else if (format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + { + size = count * sizeof(unsigned short); + } + else + { + ERR("Invalid index format."); + return; + } + + if (!mesh->owns_indices || mesh->index_size < size) + { + if (mesh->owns_indices && mesh->indices) + free(mesh->indices); + + mesh->indices = malloc(size); + + if (mesh->indices == NULL) + { + ERR("Failed to allocate memory."); + return; + } + + mesh->index_size = size; + mesh->owns_indices = EINA_TRUE; + } + + mesh->index_format = format; + mesh->index_count = count; + + if (indices) + memcpy(mesh->indices, indices, size); +} + +EAPI Evas_3D_Index_Format +evas_3d_mesh_index_format_get(const Evas_3D_Mesh *mesh) +{ + return mesh->index_format; +} + +EAPI int +evas_3d_mesh_index_count_get(const Evas_3D_Mesh *mesh) +{ + return mesh->index_count; +} + +EAPI void * +evas_3d_mesh_index_data_map(Evas_3D_Mesh *mesh) +{ + if (mesh->index_mapped) + { + ERR("Try to map alreadly mapped data."); + return NULL; + } + + mesh->index_mapped = EINA_TRUE; + return mesh->indices; +} + +EAPI void +evas_3d_mesh_index_data_unmap(Evas_3D_Mesh *mesh) +{ + if (!mesh->index_mapped) + { + ERR("Try to unmap data which is not mapped yet."); + return; + } + + mesh->index_mapped = EINA_FALSE; +} + +EAPI void +evas_3d_mesh_vertex_assembly_set(Evas_3D_Mesh *mesh, Evas_3D_Vertex_Assembly assembly) +{ + mesh->assembly = assembly; + evas_3d_object_change(&mesh->base, EVAS_3D_STATE_MESH_VERTEX_ASSEMBLY, NULL); +} + +EAPI Evas_3D_Vertex_Assembly +evas_3d_mesh_vertex_assembly_get(const Evas_3D_Mesh *mesh) +{ + return mesh->assembly; +} + +EAPI void +evas_3d_mesh_file_set(Evas_3D_Mesh *mesh, Evas_3D_Mesh_File_Type type, + const char *file, const char *key EINA_UNUSED) +{ + _mesh_fini(mesh); + _mesh_init(mesh); + + if (file == NULL) + return; + + switch (type) + { + case EVAS_3D_MESH_FILE_TYPE_MD2: + evas_3d_mesh_file_md2_set(mesh, file); + break; + default: + ERR("Invalid mesh file type."); + break; + } +} + +static inline void +_mesh_frame_find(Evas_3D_Mesh *mesh, int frame, + Eina_List **l, Eina_List **r) +{ + Eina_List *left, *right; + Evas_3D_Mesh_Frame *f0, *f1; + + left = mesh->frames; + right = eina_list_next(left); + + while (right) + { + f0 = (Evas_3D_Mesh_Frame *)eina_list_data_get(left); + f1 = (Evas_3D_Mesh_Frame *)eina_list_data_get(right); + + if (frame >= f0->frame && frame <= f1->frame) + break; + + left = right; + right = eina_list_next(left); + } + + if (right == NULL) + { + if (frame <= f0->frame) + { + *l = NULL; + *r = left; + } + else + { + *l = left; + *r = NULL; + } + } + + *l = left; + *r = right; +} + +void +evas_3d_mesh_interpolate_vertex_buffer_get(Evas_3D_Mesh *mesh, int frame, + Evas_3D_Vertex_Attrib attrib, + Evas_3D_Vertex_Buffer *buf0, + Evas_3D_Vertex_Buffer *buf1, + Evas_Real *weight) +{ + Eina_List *l, *r; + const Evas_3D_Mesh_Frame *f0 = NULL, *f1 = NULL; + + _mesh_frame_find(mesh, frame, &l, &r); + + while (l) + { + f0 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(l); + + if (f0->vertices[attrib].data != NULL) + break; + + l = eina_list_prev(l); + f0 = NULL; + } + + while (r) + { + f1 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(r); + + if (f1->vertices[attrib].data != NULL) + break; + + r = eina_list_next(r); + f1 = NULL; + } + + if (f0 == NULL && f1 == NULL) + return; + + if (f0 == NULL) + { + f0 = f1; + } + else if (f1 != NULL) + { + if (frame == f0->frame) + { + f1 = NULL; + } + else if (frame == f1->frame) + { + f0 = f1; + f1 = NULL; + } + } + + buf0->data = f0->vertices[attrib].data; + buf0->stride = f0->vertices[attrib].stride; + buf0->size = f0->vertices[attrib].size; + + if (f1) + { + buf1->data = f1->vertices[attrib].data; + buf1->stride = f1->vertices[attrib].stride; + buf1->size = f1->vertices[attrib].size; + + *weight = (f1->frame - frame) / (Evas_Real)(f1->frame - f0->frame); + } + else + { + buf1->data = NULL; + buf1->stride = 0; + buf1->size = 0; + + *weight = 1.0; + } +} diff --git a/src/lib/evas/canvas/evas_3d_mesh_loader_md2.c b/src/lib/evas/canvas/evas_3d_mesh_loader_md2.c new file mode 100644 index 0000000..7472e27 --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_mesh_loader_md2.c @@ -0,0 +1,440 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "evas_common_private.h" +#include "evas_private.h" + +#define PACKED __attribute__((__packed__)) + +#define MD2_MAGIC_NUMBER 844121161 +#define MD2_VERSION 8 +#define MD2_FRAME_SCALE 256 + +/* Structures for reading data from file. */ +typedef struct _MD2_Header MD2_Header; +typedef struct _MD2_Vertex MD2_Vertex; +typedef struct _MD2_Frame MD2_Frame; +typedef struct _MD2_Triangle MD2_Triangle; +typedef struct _MD2_Texcoord MD2_Texcoord; + +struct PACKED _MD2_Header +{ + int magic; + int version; + + int skin_width; + int skin_height; + + int frame_size; + + int skins_count; + int vertex_count; + int texcoord_count; + int triangle_count; + int glcmd_count; + int frame_count; + + int offset_skins; + int offset_texcoords; + int offset_triangles; + int offset_frames; + int offset_glcmds; + int offset_end; +}; + +struct PACKED _MD2_Vertex +{ + unsigned char pos[3]; + unsigned char normal_idx; +}; + +struct PACKED _MD2_Frame +{ + float scale[3]; + float trans[3]; + char name[16]; + MD2_Vertex vertices[1]; +}; + +struct PACKED _MD2_Triangle +{ + unsigned short vertex_idx[3]; + unsigned short texcoord_idx[3]; +}; + +struct PACKED _MD2_Texcoord +{ + short s; + short t; +}; + +typedef struct _MD2_Loader +{ + Eina_File *file; + char *map; + int size; + + int skin_width; + int skin_height; + + int frame_count; + int frame_size; + char *frames; + + int vertex_count; + int triangle_count; + int texcoord_count; + + MD2_Triangle *triangles; + MD2_Texcoord *texcoords; +} MD2_Loader; + +static const float normal_table[162][3] = +{ + {-0.525731f, 0.000000f, 0.850651f}, + {-0.442863f, 0.238856f, 0.864188f}, + {-0.295242f, 0.000000f, 0.955423f}, + {-0.309017f, 0.500000f, 0.809017f}, + {-0.162460f, 0.262866f, 0.951056f}, + { 0.000000f, 0.000000f, 1.000000f}, + { 0.000000f, 0.850651f, 0.525731f}, + {-0.147621f, 0.716567f, 0.681718f}, + { 0.147621f, 0.716567f, 0.681718f}, + { 0.000000f, 0.525731f, 0.850651f}, + { 0.309017f, 0.500000f, 0.809017f}, + { 0.525731f, 0.000000f, 0.850651f}, + { 0.295242f, 0.000000f, 0.955423f}, + { 0.442863f, 0.238856f, 0.864188f}, + { 0.162460f, 0.262866f, 0.951056f}, + {-0.681718f, 0.147621f, 0.716567f}, + {-0.809017f, 0.309017f, 0.500000f}, + {-0.587785f, 0.425325f, 0.688191f}, + {-0.850651f, 0.525731f, 0.000000f}, + {-0.864188f, 0.442863f, 0.238856f}, + {-0.716567f, 0.681718f, 0.147621f}, + {-0.688191f, 0.587785f, 0.425325f}, + {-0.500000f, 0.809017f, 0.309017f}, + {-0.238856f, 0.864188f, 0.442863f}, + {-0.425325f, 0.688191f, 0.587785f}, + {-0.716567f, 0.681718f, -0.147621f}, + {-0.500000f, 0.809017f, -0.309017f}, + {-0.525731f, 0.850651f, 0.000000f}, + { 0.000000f, 0.850651f, -0.525731f}, + {-0.238856f, 0.864188f, -0.442863f}, + { 0.000000f, 0.955423f, -0.295242f}, + {-0.262866f, 0.951056f, -0.162460f}, + { 0.000000f, 1.000000f, 0.000000f}, + { 0.000000f, 0.955423f, 0.295242f}, + {-0.262866f, 0.951056f, 0.162460f}, + { 0.238856f, 0.864188f, 0.442863f}, + { 0.262866f, 0.951056f, 0.162460f}, + { 0.500000f, 0.809017f, 0.309017f}, + { 0.238856f, 0.864188f, -0.442863f}, + { 0.262866f, 0.951056f, -0.162460f}, + { 0.500000f, 0.809017f, -0.309017f}, + { 0.850651f, 0.525731f, 0.000000f}, + { 0.716567f, 0.681718f, 0.147621f}, + { 0.716567f, 0.681718f, -0.147621f}, + { 0.525731f, 0.850651f, 0.000000f}, + { 0.425325f, 0.688191f, 0.587785f}, + { 0.864188f, 0.442863f, 0.238856f}, + { 0.688191f, 0.587785f, 0.425325f}, + { 0.809017f, 0.309017f, 0.500000f}, + { 0.681718f, 0.147621f, 0.716567f}, + { 0.587785f, 0.425325f, 0.688191f}, + { 0.955423f, 0.295242f, 0.000000f}, + { 1.000000f, 0.000000f, 0.000000f}, + { 0.951056f, 0.162460f, 0.262866f}, + { 0.850651f, -0.525731f, 0.000000f}, + { 0.955423f, -0.295242f, 0.000000f}, + { 0.864188f, -0.442863f, 0.238856f}, + { 0.951056f, -0.162460f, 0.262866f}, + { 0.809017f, -0.309017f, 0.500000f}, + { 0.681718f, -0.147621f, 0.716567f}, + { 0.850651f, 0.000000f, 0.525731f}, + { 0.864188f, 0.442863f, -0.238856f}, + { 0.809017f, 0.309017f, -0.500000f}, + { 0.951056f, 0.162460f, -0.262866f}, + { 0.525731f, 0.000000f, -0.850651f}, + { 0.681718f, 0.147621f, -0.716567f}, + { 0.681718f, -0.147621f, -0.716567f}, + { 0.850651f, 0.000000f, -0.525731f}, + { 0.809017f, -0.309017f, -0.500000f}, + { 0.864188f, -0.442863f, -0.238856f}, + { 0.951056f, -0.162460f, -0.262866f}, + { 0.147621f, 0.716567f, -0.681718f}, + { 0.309017f, 0.500000f, -0.809017f}, + { 0.425325f, 0.688191f, -0.587785f}, + { 0.442863f, 0.238856f, -0.864188f}, + { 0.587785f, 0.425325f, -0.688191f}, + { 0.688191f, 0.587785f, -0.425325f}, + {-0.147621f, 0.716567f, -0.681718f}, + {-0.309017f, 0.500000f, -0.809017f}, + { 0.000000f, 0.525731f, -0.850651f}, + {-0.525731f, 0.000000f, -0.850651f}, + {-0.442863f, 0.238856f, -0.864188f}, + {-0.295242f, 0.000000f, -0.955423f}, + {-0.162460f, 0.262866f, -0.951056f}, + { 0.000000f, 0.000000f, -1.000000f}, + { 0.295242f, 0.000000f, -0.955423f}, + { 0.162460f, 0.262866f, -0.951056f}, + {-0.442863f, -0.238856f, -0.864188f}, + {-0.309017f, -0.500000f, -0.809017f}, + {-0.162460f, -0.262866f, -0.951056f}, + { 0.000000f, -0.850651f, -0.525731f}, + {-0.147621f, -0.716567f, -0.681718f}, + { 0.147621f, -0.716567f, -0.681718f}, + { 0.000000f, -0.525731f, -0.850651f}, + { 0.309017f, -0.500000f, -0.809017f}, + { 0.442863f, -0.238856f, -0.864188f}, + { 0.162460f, -0.262866f, -0.951056f}, + { 0.238856f, -0.864188f, -0.442863f}, + { 0.500000f, -0.809017f, -0.309017f}, + { 0.425325f, -0.688191f, -0.587785f}, + { 0.716567f, -0.681718f, -0.147621f}, + { 0.688191f, -0.587785f, -0.425325f}, + { 0.587785f, -0.425325f, -0.688191f}, + { 0.000000f, -0.955423f, -0.295242f}, + { 0.000000f, -1.000000f, 0.000000f}, + { 0.262866f, -0.951056f, -0.162460f}, + { 0.000000f, -0.850651f, 0.525731f}, + { 0.000000f, -0.955423f, 0.295242f}, + { 0.238856f, -0.864188f, 0.442863f}, + { 0.262866f, -0.951056f, 0.162460f}, + { 0.500000f, -0.809017f, 0.309017f}, + { 0.716567f, -0.681718f, 0.147621f}, + { 0.525731f, -0.850651f, 0.000000f}, + {-0.238856f, -0.864188f, -0.442863f}, + {-0.500000f, -0.809017f, -0.309017f}, + {-0.262866f, -0.951056f, -0.162460f}, + {-0.850651f, -0.525731f, 0.000000f}, + {-0.716567f, -0.681718f, -0.147621f}, + {-0.716567f, -0.681718f, 0.147621f}, + {-0.525731f, -0.850651f, 0.000000f}, + {-0.500000f, -0.809017f, 0.309017f}, + {-0.238856f, -0.864188f, 0.442863f}, + {-0.262866f, -0.951056f, 0.162460f}, + {-0.864188f, -0.442863f, 0.238856f}, + {-0.809017f, -0.309017f, 0.500000f}, + {-0.688191f, -0.587785f, 0.425325f}, + {-0.681718f, -0.147621f, 0.716567f}, + {-0.442863f, -0.238856f, 0.864188f}, + {-0.587785f, -0.425325f, 0.688191f}, + {-0.309017f, -0.500000f, 0.809017f}, + {-0.147621f, -0.716567f, 0.681718f}, + {-0.425325f, -0.688191f, 0.587785f}, + {-0.162460f, -0.262866f, 0.951056f}, + { 0.442863f, -0.238856f, 0.864188f}, + { 0.162460f, -0.262866f, 0.951056f}, + { 0.309017f, -0.500000f, 0.809017f}, + { 0.147621f, -0.716567f, 0.681718f}, + { 0.000000f, -0.525731f, 0.850651f}, + { 0.425325f, -0.688191f, 0.587785f}, + { 0.587785f, -0.425325f, 0.688191f}, + { 0.688191f, -0.587785f, 0.425325f}, + {-0.955423f, 0.295242f, 0.000000f}, + {-0.951056f, 0.162460f, 0.262866f}, + {-1.000000f, 0.000000f, 0.000000f}, + {-0.850651f, 0.000000f, 0.525731f}, + {-0.955423f, -0.295242f, 0.000000f}, + {-0.951056f, -0.162460f, 0.262866f}, + {-0.864188f, 0.442863f, -0.238856f}, + {-0.951056f, 0.162460f, -0.262866f}, + {-0.809017f, 0.309017f, -0.500000f}, + {-0.864188f, -0.442863f, -0.238856f}, + {-0.951056f, -0.162460f, -0.262866f}, + {-0.809017f, -0.309017f, -0.500000f}, + {-0.681718f, 0.147621f, -0.716567f}, + {-0.681718f, -0.147621f, -0.716567f}, + {-0.850651f, 0.000000f, -0.525731f}, + {-0.688191f, 0.587785f, -0.425325f}, + {-0.587785f, 0.425325f, -0.688191f}, + {-0.425325f, 0.688191f, -0.587785f}, + {-0.425325f, -0.688191f, -0.587785f}, + {-0.587785f, -0.425325f, -0.688191f}, + {-0.688191f, -0.587785f, -0.425325f}, +}; + +static inline void +_md2_loader_fini(MD2_Loader *loader) +{ + if (loader->map) + { + eina_file_map_free(loader->file, loader->map); + loader->map = NULL; + } + + if (loader->file) + { + eina_file_close(loader->file); + loader->file = NULL; + } +} + +static inline Eina_Bool +_md2_loader_init(MD2_Loader *loader, const char *file) +{ + MD2_Header header; + + memset(loader, 0x00, sizeof(MD2_Loader)); + + /* Open given file. */ + loader->file = eina_file_open(file, 0); + + if (loader->file == NULL) + { + ERR("Failed to open file %s\n", file); + goto error; + } + + /* Check file size. We require a file larger than MD2 header size. */ + loader->size = eina_file_size_get(loader->file); + + if (loader->size < (int)sizeof(MD2_Header)) + goto error; + + /* Map the file. */ + loader->map = eina_file_map_all(loader->file, EINA_FILE_SEQUENTIAL); + + if (loader->map == NULL) + goto error; + + /* Read header. */ + memcpy(&header, loader->map, sizeof(MD2_Header)); + + /* Check identity */ + if (header.magic != MD2_MAGIC_NUMBER || header.version != MD2_VERSION) + goto error; + + /* Check offsets */ + if (header.offset_skins > header.offset_end) + goto error; + + if (header.offset_texcoords > header.offset_end) + goto error; + + if (header.offset_triangles > header.offset_end) + goto error; + + if (header.offset_frames > header.offset_end) + goto error; + + if (header.offset_glcmds > header.offset_end) + goto error; + + if (header.offset_end > loader->size) + goto error; + + loader->skin_width = header.skin_width; + loader->skin_height = header.skin_height; + + loader->frame_count = header.frame_count; + loader->frame_size = header.frame_size; + loader->frames = loader->map + header.offset_frames; + + loader->vertex_count = header.vertex_count; + loader->triangle_count = header.triangle_count; + loader->texcoord_count = header.texcoord_count; + + loader->triangles = (MD2_Triangle *)(loader->map + header.offset_triangles); + loader->texcoords = (MD2_Texcoord *)(loader->map + header.offset_texcoords); + return EINA_TRUE; + +error: + _md2_loader_fini(loader); + return EINA_FALSE; +} + +void +evas_3d_mesh_file_md2_set(Evas_3D_Mesh *mesh, const char *file) +{ + MD2_Loader loader; + int i, j, k; + float *pos, *nor, *tex; + int stride_pos, stride_nor, stride_tex; + float s_scale, t_scale; + + /* Initialize MD2 loader (Open file and read MD2 head ant etc) */ + if (!_md2_loader_init(&loader, file)) + { + ERR("Failed to initialize MD2 loader."); + return; + } + + s_scale = 1.0 / (float)(loader.skin_width - 1); + t_scale = 1.0 / (float)(loader.skin_height - 1); + + evas_3d_mesh_vertex_count_set(mesh, loader.triangle_count * 3); + evas_3d_mesh_vertex_assembly_set(mesh, EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES); + + /* Load frames */ + for (i = 0; i < loader.frame_count; i++) + { + const MD2_Frame *frame = (const MD2_Frame *)(loader.frames + loader.frame_size * i); + int f = i * MD2_FRAME_SCALE; + + /* Add a mesh frame. */ + evas_3d_mesh_frame_add(mesh, f); + + /* Allocate vertex buffer for the frame. */ + evas_3d_mesh_frame_vertex_data_copy_set(mesh, f, EVAS_3D_VERTEX_POSITION, 0, NULL); + evas_3d_mesh_frame_vertex_data_copy_set(mesh, f, EVAS_3D_VERTEX_NORMAL, 0, NULL); + evas_3d_mesh_frame_vertex_data_copy_set(mesh, f, EVAS_3D_VERTEX_TEXCOORD, 0, NULL); + + /* Map vertex buffer. */ + pos = (float *)evas_3d_mesh_frame_vertex_data_map(mesh, f, EVAS_3D_VERTEX_POSITION); + nor = (float *)evas_3d_mesh_frame_vertex_data_map(mesh, f, EVAS_3D_VERTEX_NORMAL); + tex = (float *)evas_3d_mesh_frame_vertex_data_map(mesh, f, EVAS_3D_VERTEX_TEXCOORD); + + stride_pos = evas_3d_mesh_frame_vertex_stride_get(mesh, f, EVAS_3D_VERTEX_POSITION); + stride_nor = evas_3d_mesh_frame_vertex_stride_get(mesh, f, EVAS_3D_VERTEX_NORMAL); + stride_tex = evas_3d_mesh_frame_vertex_stride_get(mesh, f, EVAS_3D_VERTEX_TEXCOORD); + + if (stride_pos == 0) + stride_pos = sizeof(float) * 3; + + if (stride_nor == 0) + stride_nor = sizeof(float) * 3; + + if (stride_tex == 0) + stride_tex = sizeof(float) * 2; + + for (j = 0; j < loader.triangle_count; j++) + { + const MD2_Triangle *tri = &loader.triangles[j]; + + for (k = 0; k < 3; k++) + { + unsigned int tidx, vidx; + float *p, *n, *t; + + tidx = tri->texcoord_idx[k]; + vidx = tri->vertex_idx[k]; + + p = (float *)((char *)pos + stride_pos * (j * 3 + k)); + n = (float *)((char *)nor + stride_nor * (j * 3 + k)); + t = (float *)((char *)tex + stride_tex * (j * 3 + k)); + + p[0] = frame->vertices[vidx].pos[0] * frame->scale[0] + frame->trans[0]; + p[1] = frame->vertices[vidx].pos[1] * frame->scale[1] + frame->trans[1]; + p[2] = frame->vertices[vidx].pos[2] * frame->scale[2] + frame->trans[2]; + + n[0] = normal_table[frame->vertices[vidx].normal_idx][0]; + n[1] = normal_table[frame->vertices[vidx].normal_idx][1]; + n[2] = normal_table[frame->vertices[vidx].normal_idx][2]; + + t[0] = loader.texcoords[tidx].s * s_scale; + t[1] = 1.0 - loader.texcoords[tidx].t * t_scale; + } + } + + /* Unmap vertex buffer. */ + evas_3d_mesh_frame_vertex_data_unmap(mesh, f, EVAS_3D_VERTEX_POSITION); + evas_3d_mesh_frame_vertex_data_unmap(mesh, f, EVAS_3D_VERTEX_NORMAL); + evas_3d_mesh_frame_vertex_data_unmap(mesh, f, EVAS_3D_VERTEX_TEXCOORD); + } + + _md2_loader_fini(&loader); +} diff --git a/src/lib/evas/canvas/evas_3d_node.c b/src/lib/evas/canvas/evas_3d_node.c new file mode 100644 index 0000000..acbab4a --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_node.c @@ -0,0 +1,1162 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "evas_common_private.h" +#include "evas_private.h" + +static inline Evas_3D_Node_Mesh * +_node_mesh_new(Evas_3D_Node *node, Evas_3D_Mesh *mesh) +{ + Evas_3D_Node_Mesh *nm = (Evas_3D_Node_Mesh *)malloc(sizeof(Evas_3D_Node_Mesh)); + + if (nm == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + nm->node = node; + nm->mesh = mesh; + nm->frame = 0; + + return nm; +} + +static inline void +_node_mesh_free(Evas_3D_Node_Mesh *nm) +{ + free(nm); +} + +static void +_node_mesh_free_func(void *data) +{ + _node_mesh_free((Evas_3D_Node_Mesh *)data); +} + +static Eina_Bool +_node_scene_root_change_notify(const Eina_Hash *hash EINA_UNUSED, const void *key, + void *data EINA_UNUSED, void *fdata) +{ + Evas_3D_Scene *s = *(Evas_3D_Scene **)key; + evas_3d_object_change(&s->base, EVAS_3D_STATE_SCENE_ROOT_NODE, (Evas_3D_Object *)fdata); + return EINA_TRUE; +} + +static Eina_Bool +_node_scene_camera_change_notify(const Eina_Hash *hash EINA_UNUSED, const void *key, + void *data EINA_UNUSED, void *fdata) +{ + Evas_3D_Scene *s = *(Evas_3D_Scene **)key; + evas_3d_object_change(&s->base, EVAS_3D_STATE_SCENE_CAMERA_NODE, (Evas_3D_Object *)fdata); + return EINA_TRUE; +} + +static void +_node_change(Evas_3D_Object *obj, Evas_3D_State state EINA_UNUSED, Evas_3D_Object *ref EINA_UNUSED) +{ + Evas_3D_Node *node = (Evas_3D_Node *)obj; + Eina_List *l; + Evas_3D_Node *n; + + /* Notify all scenes using this node that it has changed. */ + if (node->scenes_root) + eina_hash_foreach(node->scenes_root, _node_scene_root_change_notify, obj); + + if (node->scenes_camera) + eina_hash_foreach(node->scenes_camera, _node_scene_camera_change_notify, obj); + + /* Notify parent that a member has changed. */ + if (node->parent) + evas_3d_object_change(&node->parent->base, EVAS_3D_STATE_NODE_MEMBER, obj); + + /* Notify members that the parent has changed. */ + EINA_LIST_FOREACH(node->members, l, n) + { + evas_3d_object_change(&n->base, EVAS_3D_STATE_NODE_PARENT, obj); + } +} + +static Eina_Bool +_node_transform_update(Evas_3D_Node *node, void *data EINA_UNUSED) +{ + if (evas_3d_object_dirty_get(&node->base, EVAS_3D_STATE_NODE_TRANSFORM) || + evas_3d_object_dirty_get(&node->base, EVAS_3D_STATE_NODE_PARENT)) + { + if (node->parent) + { + const Evas_Vec3 *scale_parent = &node->parent->scale_world; + const Evas_Vec4 *orientation_parent = &node->parent->orientation_world; + + /* Orienatation */ + if (node->orientation_inherit) + { + evas_vec4_quaternion_multiply(&node->orientation_world, + orientation_parent, &node->orientation); + } + else + { + node->orientation_world = node->orientation; + } + + /* Scale */ + if (node->scale_inherit) + evas_vec3_multiply(&node->scale_world, scale_parent, &node->scale); + else + node->scale_world = node->scale; + + /* Position */ + if (node->position_inherit) + { + evas_vec3_multiply(&node->position_world, &node->position, scale_parent); + evas_vec3_quaternion_rotate(&node->position_world, &node->position_world, + orientation_parent); + evas_vec3_add(&node->position_world, &node->position_world, + &node->parent->position_world); + } + else + { + node->position_world = node->position; + } + } + else + { + node->position_world = node->position; + node->orientation_world = node->orientation; + node->scale_world = node->scale; + } + + if (node->type == EVAS_3D_NODE_TYPE_CAMERA) + { + evas_mat4_inverse_build(&node->data.camera.matrix_world_to_eye, + &node->position_world, &node->orientation_world, + &node->scale_world); + } + else if (node->type == EVAS_3D_NODE_TYPE_LIGHT) + { + } + else if (node->type == EVAS_3D_NODE_TYPE_MESH) + { + evas_mat4_build(&node->data.mesh.matrix_local_to_world, + &node->position_world, &node->orientation_world, &node->scale_world); + } +/* + if (node->parent) + { + evas_mat4_nocheck_multiply(&node->matrix_local_to_world, + &node->parent->matrix_local_to_world, + &node->matrix_local_to_parent); + } + else + { + evas_mat4_copy(&node->matrix_local_to_world, &node->matrix_local_to_parent); + }*/ + } + + return EINA_TRUE; +} + +static Eina_Bool +_node_item_update(Evas_3D_Node *node, void *data EINA_UNUSED) +{ + if (node->type == EVAS_3D_NODE_TYPE_CAMERA) + { + if (node->data.camera.camera) + evas_3d_object_update(&node->data.camera.camera->base); + } + else if (node->type == EVAS_3D_NODE_TYPE_LIGHT) + { + if (node->data.light.light) + evas_3d_object_update(&node->data.light.light->base); + } + else if (node->type == EVAS_3D_NODE_TYPE_MESH) + { + Eina_List *l; + Evas_3D_Mesh *m; + + EINA_LIST_FOREACH(node->data.mesh.meshes, l, m) + { + evas_3d_object_update(&m->base); + } + } + + return EINA_TRUE; +} + +static Eina_Bool +_node_aabb_update(Evas_3D_Node *node, void *data EINA_UNUSED) +{ + if (evas_3d_object_dirty_get(&node->base, EVAS_3D_STATE_NODE_TRANSFORM) || + evas_3d_object_dirty_get(&node->base, EVAS_3D_STATE_NODE_MESH_GEOMETRY) || + evas_3d_object_dirty_get(&node->base, EVAS_3D_STATE_NODE_MESH_FRAME) || + evas_3d_object_dirty_get(&node->base, EVAS_3D_STATE_NODE_MEMBER)) + { + Eina_List *l; + Evas_3D_Node *n; + + /* Update AABB of this node. */ + evas_box3_empty_set(&node->aabb); + + EINA_LIST_FOREACH(node->members, l, n) + { + evas_box3_union(&node->aabb, &node->aabb, &n->aabb); + } + + if (node->type == EVAS_3D_NODE_TYPE_MESH) + { + /* TODO: */ + } + } + + return EINA_TRUE; +} + +static Eina_Bool +_node_update_done(Evas_3D_Node *node, void *data EINA_UNUSED) +{ + evas_3d_object_update_done(&node->base); + return EINA_TRUE; +} + +static void +_node_update(Evas_3D_Object *obj) +{ + Evas_3D_Node *node = (Evas_3D_Node *)obj; + + /* Update transform. */ + evas_3d_node_tree_traverse(node, EVAS_3D_TREE_TRAVERSE_LEVEL_ORDER, EINA_FALSE, + _node_transform_update, NULL); + + /* Update AABB. */ + evas_3d_node_tree_traverse(node, EVAS_3D_TREE_TRAVERSE_POST_ORDER, EINA_FALSE, + _node_aabb_update, NULL); + + /* Update node item. */ + evas_3d_node_tree_traverse(node, EVAS_3D_TREE_TRAVERSE_ANY_ORDER, EINA_FALSE, + _node_item_update, NULL); + + /* Mark all nodes in the tree as up-to-date. */ + evas_3d_node_tree_traverse(node, EVAS_3D_TREE_TRAVERSE_ANY_ORDER, EINA_FALSE, + _node_update_done, NULL); +} + +static void +_node_free(Evas_3D_Object *obj) +{ + Evas_3D_Node *node = (Evas_3D_Node *)obj; + + if (node->members) + { + Eina_List *l; + Evas_3D_Node *n; + + EINA_LIST_FOREACH(node->members, l, n) + { + evas_3d_object_unreference(&n->base); + } + + eina_list_free(node->members); + } + + if (node->data.mesh.meshes) + { + Eina_List *l; + Evas_3D_Mesh *m; + + EINA_LIST_FOREACH(node->data.mesh.meshes, l, m) + { + evas_3d_mesh_node_del(m, node); + evas_3d_object_unreference(&m->base); + } + + eina_list_free(node->data.mesh.meshes); + } + + if (node->data.mesh.node_meshes) + eina_hash_free(node->data.mesh.node_meshes); + + if (node->scenes_root) + eina_hash_free(node->scenes_root); + + if (node->scenes_camera) + eina_hash_free(node->scenes_camera); + + free(node); +} + +static const Evas_3D_Object_Func node_func = +{ + _node_free, + _node_change, + _node_update, +}; + +void +evas_3d_node_scene_root_add(Evas_3D_Node *node, Evas_3D_Scene *scene) +{ + int count = 0; + + if (node->scenes_root == NULL) + { + node->scenes_root = eina_hash_pointer_new(NULL); + + if (node->scenes_root == NULL) + { + ERR("Failed to create hash table."); + return; + } + } + else + count = (int)eina_hash_find(node->scenes_root, &scene); + + eina_hash_set(node->scenes_root, &scene, (const void *)(count + 1)); +} + +void +evas_3d_node_scene_root_del(Evas_3D_Node *node, Evas_3D_Scene *scene) +{ + int count = 0; + + if (node->scenes_root == NULL) + { + ERR("No scene to delete."); + return; + } + + count = (int)eina_hash_find(node->scenes_root, &scene); + + if (count == 1) + eina_hash_del(node->scenes_root, &scene, NULL); + else + eina_hash_set(node->scenes_root, &scene, (const void *)(count - 1)); +} + +void +evas_3d_node_scene_camera_add(Evas_3D_Node *node, Evas_3D_Scene *scene) +{ + int count = 0; + + if (node->scenes_camera == NULL) + { + node->scenes_camera = eina_hash_pointer_new(NULL); + + if (node->scenes_camera == NULL) + { + ERR("Failed to create hash table."); + return; + } + } + else + count = (int)eina_hash_find(node->scenes_camera, &scene); + + eina_hash_set(node->scenes_camera, &scene, (const void *)(count + 1)); +} + +void +evas_3d_node_scene_camera_del(Evas_3D_Node *node, Evas_3D_Scene *scene) +{ + int count = 0; + + if (node->scenes_camera == NULL) + { + ERR("No scene to delete."); + return; + } + + count = (int)eina_hash_find(node->scenes_camera, &scene); + + if (count == 1) + eina_hash_del(node->scenes_camera, &scene, NULL); + else + eina_hash_set(node->scenes_camera, &scene, (const void *)(count - 1)); +} + +Evas_3D_Node * +evas_3d_node_new(Evas *e, Evas_3D_Node_Type type) +{ + Evas_3D_Node *node = NULL; + + node = (Evas_3D_Node *)calloc(1, sizeof(Evas_3D_Node)); + + if (node == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + evas_3d_object_init(&node->base, e, EVAS_3D_OBJECT_TYPE_NODE, &node_func); + + evas_vec3_set(&node->position, 0.0, 0.0, 0.0); + evas_vec4_set(&node->orientation, 0.0, 0.0, 0.0, 0.0); + evas_vec3_set(&node->scale, 1.0, 1.0, 1.0); + + evas_vec3_set(&node->position_world, 0.0, 0.0, 0.0); + evas_vec4_set(&node->orientation_world, 0.0, 0.0, 0.0, 1.0); + evas_vec3_set(&node->scale_world, 1.0, 1.0, 1.0); + + node->position_inherit = EINA_TRUE; + node->orientation_inherit = EINA_TRUE; + node->scale_inherit = EINA_TRUE; + + evas_box3_set(&node->aabb, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + + node->type = type; + + if (type == EVAS_3D_NODE_TYPE_MESH) + { + node->data.mesh.node_meshes = eina_hash_pointer_new(_node_mesh_free_func); + + if (node->data.mesh.node_meshes == NULL) + { + ERR("Failed to create node mesh table."); + _node_free(&node->base); + return NULL; + } + } + + return node; +} + +void +evas_3d_node_traverse(Evas_3D_Node *from, Evas_3D_Node *to, Evas_3D_Node_Traverse_Type type, + Eina_Bool skip, Evas_3D_Node_Func func, void *data) +{ + Eina_List *nodes = NULL, *n; + Evas_3D_Node *node = NULL; + + if (from == NULL || func == NULL) + goto error; + + if (type == EVAS_3D_NODE_TRAVERSE_DOWNWARD) + { + if (to == NULL) + goto error; + + node = to; + + do { + nodes = eina_list_prepend(nodes, (const void *)node); + + if (node == from) + break; + + node = node->parent; + + if (node == NULL) + goto error; + } while (1); + } + else if (type == EVAS_3D_NODE_TRAVERSE_UPWARD) + { + node = from; + + do { + nodes = eina_list_append(nodes, (const void *)node); + + if (node == to) + break; + + node = node->parent; + + if (node == NULL) + { + if (to == NULL) + break; + + goto error; + } + } while (1); + } + + EINA_LIST_FOREACH(nodes, n, node) + { + if (!func(node, data) && skip) + break; + } + + eina_list_free(nodes); + return; + +error: + ERR("Node traverse error."); + + if (nodes) + eina_list_free(nodes); +} + +void +evas_3d_node_tree_traverse(Evas_3D_Node *root, Evas_3D_Tree_Traverse_Type type, + Eina_Bool skip, Evas_3D_Node_Func func, void *data) +{ + Eina_List *nodes = NULL, *l; + Evas_3D_Node *node = NULL, *n, *last; + + if (root == NULL || func == NULL) + return; + + if (type == EVAS_3D_TREE_TRAVERSE_LEVEL_ORDER) + { + /* Put the root node in the queue. */ + nodes = eina_list_append(nodes, root); + + while (eina_list_count(nodes) > 0) + { + /* Dequeue a node. */ + node = eina_list_data_get(nodes); + nodes = eina_list_remove_list(nodes, nodes); + + /* Call node function on the node. */ + if (func(node, data) || !skip) + { + /* Enqueue member nodes. */ + EINA_LIST_FOREACH(node->members, l, n) + { + nodes = eina_list_append(nodes, n); + } + } + } + } + else if (type == EVAS_3D_TREE_TRAVERSE_PRE_ORDER) + { + /* Put the root node in the stack. */ + nodes = eina_list_append(nodes, root); + + while (eina_list_count(nodes) > 0) + { + /* Pop a node from the stack. */ + node = eina_list_data_get(nodes); + nodes = eina_list_remove_list(nodes, nodes); + + /* Call node function on the node. */ + if (func(node, data) || !skip) + { + /* Push member nodes into the stack. */ + EINA_LIST_REVERSE_FOREACH(node->members, l, n) + { + nodes = eina_list_prepend(nodes, n); + } + } + } + } + else if (type == EVAS_3D_TREE_TRAVERSE_POST_ORDER) + { + if (skip) + { + ERR("Using skip with post order traversal has no effect."); + return; + } + + /* Put the root node in the stack. */ + nodes = eina_list_append(nodes, root); + last = NULL; + + while (eina_list_count(nodes) > 0) + { + /* Peek a node from the stack. */ + node = eina_list_data_get(nodes); + + if (eina_list_count(node->members) == 0) + { + /* The peeked node is a leaf node, + * so visit it and pop from the stack. */ + func(node, data); + nodes = eina_list_remove_list(nodes, nodes); + + /* Save the last visited node. */ + last = node; + } + else + { + /* If the peeked node is not a leaf node, + * there can be only two possible cases. + * + * 1. the parent of the last visited node. + * 2. a sibling of the last visited node. + * + * If the last visited node is a direct child of the peeked node, + * we have no unvisted child nodes for the peeked node, so we have to visit + * the peeked node and pop from the stack. + * + * Otherwise it should be a sibling of the peeked node, so we have to push + * its childs into the stack. */ + + if (last && last->parent == node) + { + /* Visit the node as it doesn't have any unvisited child node. */ + func(node, data); + nodes = eina_list_remove_list(nodes, nodes); + + /* Save the last visited node. */ + last = node; + } + else + { + /* Push child nodes into the stack. */ + EINA_LIST_REVERSE_FOREACH(node->members, l, n) + { + nodes = eina_list_prepend(nodes, n); + } + } + } + } + } + + if (nodes != NULL) + eina_list_free(nodes); +} + +Eina_Bool +_node_is_visible(Evas_3D_Node *node EINA_UNUSED, Evas_3D_Node *camera_node EINA_UNUSED) +{ + /* TODO: */ + return EINA_TRUE; +} + +Eina_Bool +evas_3d_node_mesh_collect(Evas_3D_Node *node, void *data) +{ + Evas_3D_Scene_Data *scene_data = (Evas_3D_Scene_Data *)data; + + if (!_node_is_visible(node, scene_data->camera_node)) + { + /* Skip entire sub-tree of this node. */ + return EINA_FALSE; + } + + if (node->type == EVAS_3D_NODE_TYPE_MESH) + scene_data->mesh_nodes = eina_list_append(scene_data->mesh_nodes, node); + + return EINA_TRUE; +} + +Eina_Bool +evas_3d_node_light_collect(Evas_3D_Node *node, void *data) +{ + Evas_3D_Scene_Data *scene_data = (Evas_3D_Scene_Data *)data; + + if (node->type == EVAS_3D_NODE_TYPE_LIGHT) + scene_data->light_nodes = eina_list_append(scene_data->light_nodes, node); + + return EINA_TRUE; +} + +EAPI Evas_3D_Node * +evas_3d_node_add(Evas *e, Evas_3D_Node_Type type) +{ + return evas_3d_node_new(e, type); +} + +EAPI void +evas_3d_node_del(Evas_3D_Node *node) +{ + evas_3d_object_unreference(&node->base); +} + +EAPI Evas_3D_Node_Type +evas_3d_node_type_get(const Evas_3D_Node *node) +{ + return node->type; +} + +EAPI Evas * +evas_3d_node_evas_get(const Evas_3D_Node *node) +{ + return node->base.evas; +} + +EAPI void +evas_3d_node_member_add(Evas_3D_Node *node, Evas_3D_Node *member) +{ + if (node == member) + { + ERR("Failed to add a member node (adding to itself)."); + return; + } + + if (member->parent == node) + return; + + if (member->parent) + { + /* Detaching from previous parent. */ + member->parent->members = eina_list_remove(member->parent->members, member); + + /* Mark changed. */ + evas_3d_object_change(&member->parent->base, EVAS_3D_STATE_NODE_MEMBER, NULL); + } + else + { + /* Should get a new reference. */ + evas_3d_object_reference(&member->base); + } + + /* Add the member node. */ + node->members = eina_list_append(node->members, (const void *)member); + member->parent = node; + + /* Mark changed. */ + evas_3d_object_change(&member->base, EVAS_3D_STATE_NODE_PARENT, NULL); + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_MEMBER, NULL); +} + +EAPI void +evas_3d_node_member_del(Evas_3D_Node *node, Evas_3D_Node *member) +{ + if (member->parent != node) + { + ERR("Failed to delete a member node (not a member of the given node)"); + return; + } + + /* Delete the member node. */ + node->members = eina_list_remove(node->members, member); + member->parent = NULL; + + /* Mark modified object as changed. */ + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_MEMBER, NULL); + evas_3d_object_change(&member->base, EVAS_3D_STATE_NODE_PARENT, NULL); + + /* Decrease reference count. */ + evas_3d_object_unreference(&member->base); +} + +EAPI Evas_3D_Node * +evas_3d_node_parent_get(const Evas_3D_Node *node) +{ + return node->parent; +} + +EAPI const Eina_List * +evas_3d_node_member_list_get(const Evas_3D_Node *node) +{ + return node->members; +} + +EAPI void +evas_3d_node_position_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z) +{ + node->position.x = x; + node->position.y = y; + node->position.z = z; + + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI void +evas_3d_node_orientation_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z, Evas_Real w) +{ + node->orientation.x = x; + node->orientation.y = y; + node->orientation.z = z; + node->orientation.w = w; + + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI void +evas_3d_node_orientation_angle_axis_set(Evas_3D_Node *node, + Evas_Real angle, Evas_Real x, Evas_Real y, Evas_Real z) +{ + Evas_Real half_angle = 0.5 * DEGREE_TO_RADIAN(angle); + Evas_Real s = sin(half_angle); + Evas_Vec3 axis; + + evas_vec3_set(&axis, x, y, z); + evas_vec3_normalize(&axis, &axis); + + node->orientation.w = cos(half_angle); + node->orientation.x = s * axis.x; + node->orientation.y = s * axis.y; + node->orientation.z = s * axis.z; + + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI void +evas_3d_node_scale_set(Evas_3D_Node *node, Evas_Real x, Evas_Real y, Evas_Real z) +{ + node->scale.x = x; + node->scale.y = y; + node->scale.z = z; + + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI void +evas_3d_node_position_get(const Evas_3D_Node *node, Evas_3D_Space space, + Evas_Real *x, Evas_Real *y, Evas_Real *z) +{ + if (space == EVAS_3D_SPACE_LOCAL) + { + if (x) *x = 0.0; + if (y) *y = 0.0; + if (z) *z = 0.0; + } + else if (space == EVAS_3D_SPACE_PARENT) + { + if (x) *x = node->position.x; + if (y) *y = node->position.y; + if (z) *z = node->position.z; + } + else if (space == EVAS_3D_SPACE_WORLD) + { + evas_3d_object_update((Evas_3D_Object *)&node->base); + + if (x) *x = node->position_world.x; + if (y) *y = node->position_world.y; + if (z) *z = node->position_world.z; + } +} + +EAPI void +evas_3d_node_orientation_get(const Evas_3D_Node *node, Evas_3D_Space space, + Evas_Real *x, Evas_Real *y, Evas_Real *z, Evas_Real *w) +{ + if (space == EVAS_3D_SPACE_LOCAL) + { + if (x) *x = 0.0; + if (y) *y = 0.0; + if (z) *z = 0.0; + if (w) *w = 0.0; + } + else if (space == EVAS_3D_SPACE_PARENT) + { + if (x) *x = node->orientation.x; + if (y) *y = node->orientation.y; + if (z) *z = node->orientation.z; + if (w) *w = node->orientation.w; + } + else if (space == EVAS_3D_SPACE_WORLD) + { + evas_3d_object_update((Evas_3D_Object *)&node->base); + + if (x) *x = node->orientation_world.x; + if (y) *y = node->orientation_world.y; + if (z) *z = node->orientation_world.z; + if (w) *w = node->orientation_world.w; + } + +} + +EAPI void +evas_3d_node_scale_get(const Evas_3D_Node *node, Evas_3D_Space space, + Evas_Real *x, Evas_Real *y, Evas_Real *z) +{ + if (space == EVAS_3D_SPACE_LOCAL) + { + if (x) *x = 0.0; + if (y) *y = 0.0; + if (z) *z = 0.0; + } + else if (space == EVAS_3D_SPACE_PARENT) + { + if (x) *x = node->scale.x; + if (y) *y = node->scale.y; + if (z) *z = node->scale.z; + } + else if (space == EVAS_3D_SPACE_WORLD) + { + evas_3d_object_update((Evas_3D_Object *)&node->base); + + if (x) *x = node->scale_world.x; + if (y) *y = node->scale_world.y; + if (z) *z = node->scale_world.z; + } +} + +EAPI void +evas_3d_node_position_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) +{ + node->position_inherit = inherit; + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI void +evas_3d_node_orientation_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) +{ + node->orientation_inherit = inherit; + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI void +evas_3d_node_scale_inherit_set(Evas_3D_Node *node, Eina_Bool inherit) +{ + node->scale_inherit = inherit; + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI Eina_Bool +evas_3d_node_position_inherit_get(const Evas_3D_Node *node) +{ + return node->position_inherit; +} + +EAPI Eina_Bool +evas_3d_node_orientation_inherit_get(const Evas_3D_Node *node) +{ + return node->orientation_inherit; +} + +EAPI Eina_Bool +evas_3d_node_scale_inherit_get(const Evas_3D_Node *node) +{ + return node->scale_inherit; +} + +EAPI void +evas_3d_node_look_at_set(Evas_3D_Node *node, + Evas_3D_Space target_space, Evas_Real tx, Evas_Real ty, Evas_Real tz, + Evas_3D_Space up_space, Evas_Real ux, Evas_Real uy, Evas_Real uz) +{ + Evas_Vec3 target; + Evas_Vec3 up; + Evas_Vec3 x, y, z; + + /* Target position in parent space. */ + if (target_space == EVAS_3D_SPACE_LOCAL) + { + ERR("TODO:"); + return; + } + else if (target_space == EVAS_3D_SPACE_PARENT) + { + evas_vec3_set(&target, tx, ty, tz); + } + else if (target_space == EVAS_3D_SPACE_WORLD) + { + ERR("TODO:"); + return; + } + else + { + ERR("Invalid coordinate space."); + return; + } + + if (up_space == EVAS_3D_SPACE_LOCAL) + { + ERR("TODO:"); + return; + } + else if (up_space == EVAS_3D_SPACE_PARENT) + { + evas_vec3_set(&up, ux, uy, uz); + } + else if (up_space == EVAS_3D_SPACE_WORLD) + { + ERR("TODO:"); + return; + } + else + { + ERR("Invalid coordinate space."); + return; + } + + /* From now on, everything takes place in parent space. */ + evas_vec3_subtract(&z, &node->position, &target); + evas_vec3_normalize(&z, &z); + + evas_vec3_cross_product(&x, &up, &z); + evas_vec3_normalize(&x, &x); + + evas_vec3_cross_product(&y, &z, &x); + evas_vec3_normalize(&y, &y); + + Evas_Real w = sqrt(1.0 + x.x + y.y + z.z); + + node->orientation.w = 0.5 * w; + + w = 0.5 / w; + + /* Inverse the axis. */ + node->orientation.x = (y.z - z.y) * w; + node->orientation.y = (z.x - x.z) * w; + node->orientation.z = (x.y - y.x) * w; + + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_TRANSFORM, NULL); +} + +EAPI void +evas_3d_node_camera_set(Evas_3D_Node *node, Evas_3D_Camera *camera) +{ + if (node->type != EVAS_3D_NODE_TYPE_CAMERA) + { + ERR("Node type mismatch."); + return; + } + + if (node->data.camera.camera == camera) + return; + + if (node->data.camera.camera) + { + /* Detach previous camera object. */ + evas_3d_camera_node_del(node->data.camera.camera, node); + evas_3d_object_unreference(&node->data.camera.camera->base); + } + + node->data.camera.camera = camera; + evas_3d_object_reference(&camera->base); + + /* Register change notification on the camera for this node. */ + evas_3d_camera_node_add(camera, node); + + /* Mark changed. */ + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_CAMERA, NULL); +} + +EAPI Evas_3D_Camera * +evas_3d_node_camera_get(const Evas_3D_Node *node) +{ + return node->data.camera.camera; +} + +EAPI void +evas_3d_node_light_set(Evas_3D_Node *node, Evas_3D_Light *light) +{ + if (node->type != EVAS_3D_NODE_TYPE_LIGHT) + { + ERR("Node type mismatch."); + return; + } + + if (node->data.light.light == light) + return; + + if (node->data.light.light) + { + /* Detach previous light object. */ + evas_3d_light_node_del(node->data.light.light, node); + evas_3d_object_unreference(&node->data.light.light->base); + } + + node->data.light.light = light; + evas_3d_object_reference(&light->base); + + /* Register change notification on the light for this node. */ + evas_3d_light_node_add(light, node); + + /* Mark changed. */ + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_LIGHT, NULL); +} + +EAPI Evas_3D_Light * +evas_3d_node_light_get(const Evas_3D_Node *node) +{ + return node->data.light.light; +} + +EAPI void +evas_3d_node_mesh_add(Evas_3D_Node *node, Evas_3D_Mesh *mesh) +{ + Evas_3D_Node_Mesh *nm = NULL; + + if (node->type != EVAS_3D_NODE_TYPE_MESH) + { + ERR("Node type mismatch."); + return; + } + + if (eina_hash_find(node->data.mesh.node_meshes, mesh) != NULL) + { + ERR("The mesh is already added to the node."); + return; + } + + if ((nm = _node_mesh_new(node, mesh)) == NULL) + { + ERR("Failed to create node mesh."); + return; + } + + /* TODO: Find node mesh and add if it does not exist. */ + if (!eina_hash_add(node->data.mesh.node_meshes, mesh, nm)) + { + ERR("Failed to add a mesh to mesh table."); + _node_mesh_free(nm); + return; + } + + node->data.mesh.meshes = eina_list_append(node->data.mesh.meshes, mesh); + evas_3d_object_reference(&mesh->base); + + /* Register change notification. */ + evas_3d_mesh_node_add(mesh, node); + + /* Mark changed. */ + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_MESH_GEOMETRY, NULL); + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_MESH_MATERIAL, NULL); +} + +EAPI void +evas_3d_node_mesh_del(Evas_3D_Node *node, Evas_3D_Mesh *mesh) +{ + if (node->type != EVAS_3D_NODE_TYPE_MESH) + { + ERR("Node type mismatch."); + return; + } + + if (!eina_hash_del(node->data.mesh.node_meshes, mesh, NULL)) + { + ERR("The given mesh doesn't belong to this node."); + return; + } + + node->data.mesh.meshes = eina_list_remove(node->data.mesh.meshes, mesh); + evas_3d_mesh_node_del(mesh, node); + evas_3d_object_unreference(&mesh->base); + + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_MESH_GEOMETRY, NULL); + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_MESH_MATERIAL, NULL); +} + +EAPI const Eina_List * +evas_3d_node_mesh_list_get(const Evas_3D_Node *node) +{ + return node->data.mesh.meshes; +} + +EAPI void +evas_3d_node_mesh_frame_set(Evas_3D_Node *node, Evas_3D_Mesh *mesh, int frame) +{ + Evas_3D_Node_Mesh *nm = NULL; + + if (node->type != EVAS_3D_NODE_TYPE_MESH) + { + ERR("Node type mismatch."); + return; + } + + if ((nm = eina_hash_find(node->data.mesh.node_meshes, mesh)) == NULL) + { + ERR("The given mesh doesn't belongs to this node."); + return; + } + + nm->frame = frame; + evas_3d_object_change(&node->base, EVAS_3D_STATE_NODE_MESH_FRAME, NULL); +} + +EAPI int +evas_3d_node_mesh_frame_get(const Evas_3D_Node *node, Evas_3D_Mesh *mesh) +{ + Evas_3D_Node_Mesh *nm = NULL; + + if (node->type != EVAS_3D_NODE_TYPE_MESH) + { + ERR("Node type mismatch."); + return 0; + } + + if ((nm = eina_hash_find(node->data.mesh.node_meshes, mesh)) == NULL) + { + ERR("The given mesh doesn't belongs to this node."); + return 0; + } + + return nm->frame; +} diff --git a/src/lib/evas/canvas/evas_3d_object.c b/src/lib/evas/canvas/evas_3d_object.c new file mode 100644 index 0000000..75bd241 --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_object.c @@ -0,0 +1,129 @@ +#include "evas_common_private.h" +#include "evas_private.h" + +#define REF_COUNT_CHECK(obj) \ + do { \ + if ((obj)->ref_count < 1) \ + { \ + ERR("Invalid reference count.") + +#define REF_COUNT_CHECK_END() \ + } \ + } while (0) + +#define OBJ_TYPE_CHECK(obj, type) \ + do { \ + if ((obj)->type != type) \ + { \ + ERR("3D object type check failed.") + +#define OBJ_TYPE_CHECK_END() \ + } \ + } while (0) + +void +evas_3d_object_init(Evas_3D_Object *obj, + Evas *e, Evas_3D_Object_Type type, const Evas_3D_Object_Func *func) +{ + obj->evas = e; + obj->type = type; + obj->ref_count = 1; + obj->func = *func; + memset(&obj->dirty[0], 0x00, sizeof(Eina_Bool) * EVAS_3D_STATE_MAX); +} + +void +evas_3d_object_reference(Evas_3D_Object *obj) +{ + REF_COUNT_CHECK(obj); + return; + REF_COUNT_CHECK_END(); + + obj->ref_count++; +} + +void +evas_3d_object_unreference(Evas_3D_Object *obj) +{ + if (obj->ref_count < 1) + { + printf("gotcha\n"); + } + + REF_COUNT_CHECK(obj); + return; + REF_COUNT_CHECK_END(); + + obj->ref_count--; + + if (obj->ref_count == 0) + obj->func.free(obj); +} + +int +evas_3d_object_reference_count_get(const Evas_3D_Object *obj) +{ + REF_COUNT_CHECK(obj); + return 0; + REF_COUNT_CHECK_END(); + + return obj->ref_count; +} + +Evas * +evas_3d_object_evas_get(const Evas_3D_Object *obj) +{ + REF_COUNT_CHECK(obj); + return NULL; + REF_COUNT_CHECK_END(); + + return obj->evas; +} + +Evas_3D_Object_Type +evas_3d_object_type_get(const Evas_3D_Object *obj) +{ + REF_COUNT_CHECK(obj); + return EVAS_3D_OBJECT_TYPE_INVALID; + REF_COUNT_CHECK_END(); + + return obj->type; +} + +Eina_Bool +evas_3d_object_dirty_get(const Evas_3D_Object *obj, Evas_3D_State state) +{ + return obj->dirty[state]; +} + +void +evas_3d_object_change(Evas_3D_Object *obj, Evas_3D_State state, Evas_3D_Object *ref) +{ + /* Skip already dirty properties. */ + if (obj->dirty[state]) + return; + + obj->dirty[state] = EINA_TRUE; + obj->dirty[EVAS_3D_STATE_ANY] = EINA_TRUE; + + if (obj->func.change) + obj->func.change(obj, state, ref); +} + +void +evas_3d_object_update(Evas_3D_Object *obj) +{ + if (!obj->dirty[EVAS_3D_STATE_ANY]) + return; + + if (obj->func.update) + obj->func.update(obj); + + evas_3d_object_update_done(obj); +} + +void +evas_3d_object_update_done(Evas_3D_Object *obj) +{ + memset(&obj->dirty[0], 0x00, sizeof(Eina_Bool) * EVAS_3D_STATE_MAX); +} diff --git a/src/lib/evas/canvas/evas_3d_scene.c b/src/lib/evas/canvas/evas_3d_scene.c new file mode 100644 index 0000000..3327fee --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_scene.c @@ -0,0 +1,622 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "evas_common_private.h" +#include "evas_private.h" + +void +evas_3d_scene_data_init(Evas_3D_Scene_Data *data) +{ + data->camera_node = NULL; + data->light_nodes = NULL; + data->mesh_nodes = NULL; +} + +void +evas_3d_scene_data_fini(Evas_3D_Scene_Data *data) +{ + if (data->light_nodes) + eina_list_free(data->light_nodes); + + if (data->mesh_nodes) + eina_list_free(data->mesh_nodes); +} + +static void +_scene_change(Evas_3D_Object *obj, Evas_3D_State state EINA_UNUSED, Evas_3D_Object *ref EINA_UNUSED) +{ + Evas_3D_Scene *scene = (Evas_3D_Scene *)obj; + Eina_List *l; + Evas_Object *eo; + + EINA_LIST_FOREACH(scene->images, l, eo) + { + Evas_Object_Protected_Data *obj = eo_data_scope_get(eo, EVAS_OBJ_CLASS); + evas_object_change(eo, obj); + } +} + +static void +_scene_update(Evas_3D_Object *obj) +{ + Evas_3D_Scene *scene = (Evas_3D_Scene *)obj; + + if (scene->root_node) + evas_3d_object_update(&scene->root_node->base); + + if (scene->camera_node) + evas_3d_object_update(&scene->camera_node->base); +} + +static void +_scene_free(Evas_3D_Object *obj) +{ + Evas_3D_Scene *scene = (Evas_3D_Scene *)obj; + + if (scene->root_node) + { + evas_3d_node_scene_root_del(scene->root_node, scene); + evas_3d_object_unreference(&scene->root_node->base); + } + + if (scene->camera_node) + { + evas_3d_node_scene_camera_del(scene->camera_node, scene); + evas_3d_object_unreference(&scene->camera_node->base); + } + + if (scene->images) + eina_list_free(scene->images); + + free(scene); +} + +static const Evas_3D_Object_Func scene_func = +{ + _scene_free, + _scene_change, + _scene_update, +}; + +Evas_3D_Scene * +evas_3d_scene_new(Evas *e) +{ + Evas_3D_Scene *scene = NULL; + + scene = (Evas_3D_Scene *)calloc(1, sizeof(Evas_3D_Scene)); + + if (scene == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + evas_3d_object_init(&scene->base, e, EVAS_3D_OBJECT_TYPE_SCENE, &scene_func); + evas_color_set(&scene->bg_color, 0.0, 0.0, 0.0, 0.0); + + return scene; +} + +EAPI Evas_3D_Scene * +evas_3d_scene_add(Evas *e) +{ + return evas_3d_scene_new(e); +} + +EAPI void +evas_3d_scene_del(Evas_3D_Scene *scene) +{ + evas_3d_object_unreference(&scene->base); +} + +EAPI Evas * +evas_3d_scene_evas_get(const Evas_3D_Scene *scene) +{ + return scene->base.evas; +} + +EAPI void +evas_3d_scene_root_node_set(Evas_3D_Scene *scene, Evas_3D_Node *node) +{ + if (scene->root_node == node) + return; + + if (scene->root_node) + { + evas_3d_node_scene_root_del(scene->root_node, scene); + evas_3d_object_unreference(&scene->root_node->base); + } + + scene->root_node = node; + + if (node) + { + evas_3d_object_reference(&node->base); + evas_3d_node_scene_root_add(node, scene); + } + + evas_3d_object_change(&scene->base, EVAS_3D_STATE_SCENE_ROOT_NODE, NULL); +} + +EAPI Evas_3D_Node * +evas_3d_scene_root_node_get(const Evas_3D_Scene *scene) +{ + return scene->root_node; +} + +EAPI void +evas_3d_scene_camera_node_set(Evas_3D_Scene *scene, Evas_3D_Node *node) +{ + if (scene->camera_node == node) + return; + + if (scene->camera_node) + { + evas_3d_node_scene_camera_del(scene->camera_node, scene); + evas_3d_object_unreference(&scene->camera_node->base); + } + + scene->camera_node = node; + + if (node) + { + evas_3d_object_reference(&node->base); + evas_3d_node_scene_camera_add(node, scene); + } + + evas_3d_object_change(&scene->base, EVAS_3D_STATE_SCENE_CAMERA_NODE, NULL); +} + +EAPI Evas_3D_Node * +evas_3d_scene_camera_node_get(const Evas_3D_Scene *scene) +{ + return scene->camera_node; +} + +EAPI void +evas_3d_scene_size_set(Evas_3D_Scene *scene, int w, int h) +{ + scene->w = w; + scene->h = h; + evas_3d_object_change(&scene->base, EVAS_3D_STATE_SCENE_SIZE, NULL); +} + +EAPI void +evas_3d_scene_size_get(const Evas_3D_Scene *scene, int *w, int *h) +{ + if (w) *w = scene->w; + if (h) *h = scene->h; +} + +EAPI void +evas_3d_scene_background_color_set(Evas_3D_Scene *scene, + Evas_Real r, Evas_Real g, Evas_Real b, Evas_Real a) +{ + evas_color_set(&scene->bg_color, r, g, b, a); + evas_3d_object_change(&scene->base, EVAS_3D_STATE_SCENE_BACKGROUND_COLOR, NULL); +} + +EAPI void +evas_3d_scene_background_color_get(const Evas_3D_Scene *scene, + Evas_Real *r, Evas_Real *g, Evas_Real *b, Evas_Real *a) +{ + if (r) *r = scene->bg_color.r; + if (g) *g = scene->bg_color.g; + if (b) *b = scene->bg_color.b; + if (a) *a = scene->bg_color.a; +} + +static inline Eina_Bool +_pick_data_triangle_add(Evas_3D_Pick_Data *data, const Evas_Ray3 *ray, + const Evas_Triangle3 *tri) +{ + Evas_Vec3 e1, e2, tvec, pvec, qvec; + Evas_Real det, inv_det, u, v, t; + + evas_vec3_subtract(&e1, &tri->p1, &tri->p0); + evas_vec3_subtract(&e2, &tri->p2, &tri->p0); + + evas_vec3_cross_product(&pvec, &ray->dir, &e2); + det = evas_vec3_dot_product(&e1, &pvec); + + /* If determinant is near zero, ray lies in plane of triangle. */ + if (det > -0.0000001 && det < 0.0000001) + return EINA_FALSE; + + inv_det = 1.0 / det; + + /* Calculate distance from p0 to ray origin. */ + evas_vec3_subtract(&tvec, &ray->org, &tri->p0); + + /* Calculate U parameter and test bounds. */ + u = evas_vec3_dot_product(&tvec, &pvec) * inv_det; + + if (u < 0.0 || u > 1.0) + return EINA_FALSE; + + /* Prepare to tst V parameter. */ + evas_vec3_cross_product(&qvec, &tvec, &e1); + + /* Calculate V parameter and test bounds. */ + v = evas_vec3_dot_product(&ray->dir, &qvec) * inv_det; + + if (v < 0.0 || u + v > 1.0) + return EINA_FALSE; + + /* Calculate T parameter and test bounds. */ + t = evas_vec3_dot_product(&e2, &qvec) * inv_det; + + if (t >= 0.0 && t <= 1.0) + { + if (!data->picked || t < data->z) + { + data->picked = EINA_TRUE; + data->z = t; + data->u = u; + data->v = v; + return EINA_TRUE; + } + } + + return EINA_FALSE; +} + +static inline void +_position_get(Evas_Vec3 *out, const Evas_3D_Vertex_Buffer *pos0, const Evas_3D_Vertex_Buffer *pos1, + Evas_Real weight, int index) +{ + if (pos1->data == NULL) + { + float *ptr = (float *)((char *)pos0->data + pos0->stride * index); + + out->x = ptr[0]; + out->y = ptr[1]; + out->z = ptr[2]; + } + else + { + float *ptr0, *ptr1; + + ptr0 = (float *)((char *)pos0->data + pos0->stride * index); + ptr1 = (float *)((char *)pos1->data + pos1->stride * index); + + out->x = ptr0[0] * weight + ptr1[0] * (1.0 - weight); + out->y = ptr0[1] * weight + ptr1[1] * (1.0 - weight); + out->z = ptr0[2] * weight + ptr1[2] * (1.0 - weight); + } +} + +static inline void +_pick_data_texcoord_update(Evas_3D_Pick_Data *data, + const Evas_3D_Vertex_Buffer *tex0, const Evas_3D_Vertex_Buffer *tex1, + Evas_Real weight, unsigned int i0, unsigned int i1, unsigned int i2) +{ + Evas_Real s0, s1, s2; + Evas_Real t0, t1, t2; + + if (tex1->data == NULL) + { + float *ptr; + + ptr = (float *)((char *)tex0->data + tex0->stride * i0); + + s0 = ptr[0]; + t0 = ptr[1]; + + ptr = (float *)((char *)tex0->data + tex0->stride * i1); + + s1 = ptr[0]; + t1 = ptr[1]; + + ptr = (float *)((char *)tex0->data + tex0->stride * i2); + + s2 = ptr[0]; + t2 = ptr[1]; + } + else + { + float *ptr0, *ptr1; + + ptr0 = (float *)((char *)tex0->data + tex0->stride * i0); + ptr1 = (float *)((char *)tex1->data + tex1->stride * i0); + + s0 = ptr0[0] * weight + ptr1[0] * (1.0 - weight); + t0 = ptr0[1] * weight + ptr1[1] * (1.0 - weight); + + ptr0 = (float *)((char *)tex0->data + tex0->stride * i1); + ptr1 = (float *)((char *)tex1->data + tex1->stride * i1); + + s1 = ptr0[0] * weight + ptr1[0] * (1.0 - weight); + t1 = ptr0[1] * weight + ptr1[1] * (1.0 - weight); + + ptr0 = (float *)((char *)tex0->data + tex0->stride * i2); + ptr1 = (float *)((char *)tex1->data + tex1->stride * i2); + + s2 = ptr0[0] * weight + ptr1[0] * (1.0 - weight); + t2 = ptr0[1] * weight + ptr1[1] * (1.0 - weight); + } + + data->s = s0 * (1 - data->u - data->v) + s1 * data->u + s2 * data->v; + data->t = t0 * (1 - data->u - data->v) + t1 * data->u + t2 * data->v; +} + + +static inline void +_pick_data_mesh_add(Evas_3D_Pick_Data *data, const Evas_Ray3 *ray, + Evas_3D_Mesh *mesh, int frame, Evas_3D_Node *node) +{ + Evas_3D_Vertex_Buffer pos0, pos1, tex0, tex1; + Evas_Real pos_weight, tex_weight; + Evas_Triangle3 tri; + int i; + + memset(&pos0, 0x00, sizeof(Evas_3D_Vertex_Buffer)); + memset(&pos1, 0x00, sizeof(Evas_3D_Vertex_Buffer)); + memset(&tex0, 0x00, sizeof(Evas_3D_Vertex_Buffer)); + memset(&tex1, 0x00, sizeof(Evas_3D_Vertex_Buffer)); + + evas_3d_mesh_interpolate_vertex_buffer_get(mesh, frame, EVAS_3D_VERTEX_POSITION, + &pos0, &pos1, &pos_weight); + + evas_3d_mesh_interpolate_vertex_buffer_get(mesh, frame, EVAS_3D_VERTEX_TEXCOORD, + &tex0, &tex1, &tex_weight); + + if (mesh->indices) + { + unsigned int i0, i1, i2; + + if (mesh->assembly == EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES) + { + for (i = 0; i < mesh->index_count; i += 3) + { + if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + { + i0 = ((unsigned short *)mesh->indices)[i]; + i1 = ((unsigned short *)mesh->indices)[i + 1]; + i2 = ((unsigned short *)mesh->indices)[i + 2]; + } + else + { + i0 = ((unsigned char *)mesh->indices)[i]; + i1 = ((unsigned char *)mesh->indices)[i + 1]; + i2 = ((unsigned char *)mesh->indices)[i + 2]; + } + + _position_get(&tri.p0, &pos0, &pos1, pos_weight, i0); + _position_get(&tri.p1, &pos0, &pos1, pos_weight, i1); + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i2); + + if (_pick_data_triangle_add(data, ray, &tri)) + { + _pick_data_texcoord_update(data, &tex0, &tex1, tex_weight, i0, i1, i2); + data->mesh = mesh; + data->node = node; + } + } + } + else if (mesh->assembly == EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_STRIP) + { + if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + { + i1 = ((unsigned short *)mesh->indices)[0]; + i2 = ((unsigned short *)mesh->indices)[1]; + } + else + { + i1 = ((unsigned char *)mesh->indices)[0]; + i2 = ((unsigned char *)mesh->indices)[1]; + } + + _position_get(&tri.p1, &pos0, &pos1, pos_weight, i1); + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i2); + + for (i = 0; i < mesh->index_count - 2; i++) + { + tri.p0 = tri.p1; + tri.p1 = tri.p2; + + if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + i2 = ((unsigned short *)mesh->indices)[i + 2]; + else + i2 = ((unsigned char *)mesh->indices)[i + 2]; + + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i2); + + if (_pick_data_triangle_add(data, ray, &tri)) + { + if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + { + i0 = ((unsigned short *)mesh->indices)[i]; + i1 = ((unsigned short *)mesh->indices)[i + 1]; + } + else + { + i0 = ((unsigned char *)mesh->indices)[i]; + i1 = ((unsigned char *)mesh->indices)[i + 1]; + } + + _pick_data_texcoord_update(data, &tex0, &tex1, tex_weight, i0, i1, i2); + data->mesh = mesh; + data->node = node; + } + } + } + else if (mesh->assembly == EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_FAN) + { + if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + { + i0 = ((unsigned short *)mesh->indices)[0]; + i2 = ((unsigned short *)mesh->indices)[1]; + } + else + { + i0 = ((unsigned char *)mesh->indices)[0]; + i2 = ((unsigned char *)mesh->indices)[1]; + } + + _position_get(&tri.p0, &pos0, &pos1, pos_weight, i0); + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i2); + + for (i = 1; i < mesh->index_count - 1; i++) + { + tri.p1 = tri.p2; + + if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + i2 = ((unsigned short *)mesh->indices)[i + 1]; + else + i2 = ((unsigned char *)mesh->indices)[i + 1]; + + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i2); + + if (_pick_data_triangle_add(data, ray, &tri)) + { + if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + i1 = ((unsigned short *)mesh->indices)[i]; + else + i1 = ((unsigned char *)mesh->indices)[i]; + + _pick_data_texcoord_update(data, &tex0, &tex1, tex_weight, i0, i1, i2); + data->mesh = mesh; + data->node = node; + } + } + } + } + else if (mesh->index_format == EVAS_3D_INDEX_FORMAT_UNSIGNED_BYTE) + { + if (mesh->assembly == EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES) + { + for (i = 0; i < mesh->index_count; i += 3) + { + _position_get(&tri.p0, &pos0, &pos1, pos_weight, i); + _position_get(&tri.p1, &pos0, &pos1, pos_weight, i + 1); + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i + 2); + + if (_pick_data_triangle_add(data, ray, &tri)) + { + _pick_data_texcoord_update(data, &tex0, &tex1, tex_weight, i, i + 1, i + 2); + data->mesh = mesh; + data->node = node; + } + } + } + else if (mesh->assembly == EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_STRIP) + { + _position_get(&tri.p1, &pos0, &pos1, pos_weight, 0); + _position_get(&tri.p2, &pos0, &pos1, pos_weight, 1); + + for (i = 0; i < mesh->index_count - 2; i++) + { + tri.p0 = tri.p1; + tri.p1 = tri.p2; + + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i + 2); + + if (_pick_data_triangle_add(data, ray, &tri)) + { + _pick_data_texcoord_update(data, &tex0, &tex1, tex_weight, i, i + 1, i + 2); + data->mesh = mesh; + data->node = node; + } + } + } + else if (mesh->assembly == EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_FAN) + { + _position_get(&tri.p0, &pos0, &pos1, pos_weight, 0); + _position_get(&tri.p2, &pos0, &pos1, pos_weight, 1); + + for (i = 1; i < mesh->index_count - 1; i++) + { + tri.p1 = tri.p2; + + _position_get(&tri.p2, &pos0, &pos1, pos_weight, i + 1); + + if (_pick_data_triangle_add(data, ray, &tri)) + { + _pick_data_texcoord_update(data, &tex0, &tex1, tex_weight, 0, i, i + 1); + data->mesh = mesh; + data->node = node; + } + } + } + } +} + +Eina_Bool +_node_pick(Evas_3D_Node *node, void *data) +{ + Evas_Ray3 ray; + Evas_3D_Pick_Data *pick = (Evas_3D_Pick_Data *)data; + Evas_Mat4 mvp; + + if (! evas_box3_ray3_intersect(&node->aabb, &pick->ray_world)) + { + /* Skip entire subtree. */ + return EINA_FALSE; + } + + if (node->type == EVAS_3D_NODE_TYPE_MESH) + { + Eina_Iterator *itr; + void *ptr; + + /* Transform ray into local coordinate space. */ + evas_mat4_multiply(&mvp, &pick->matrix_vp, &node->data.mesh.matrix_local_to_world); + evas_ray3_init(&ray, pick->x, pick->y, &mvp); + + itr = eina_hash_iterator_data_new(node->data.mesh.node_meshes); + + while (eina_iterator_next(itr, &ptr)) + { + Evas_3D_Node_Mesh *nm = (Evas_3D_Node_Mesh *)ptr; + _pick_data_mesh_add(pick, &ray, nm->mesh, nm->frame, node); + } + } + + return EINA_TRUE; +} + +EAPI Eina_Bool +evas_3d_scene_pick(const Evas_3D_Scene *scene, Evas_Real x, Evas_Real y, + Evas_3D_Node **node, Evas_3D_Mesh **mesh, + Evas_Real *s, Evas_Real *t) +{ + /* TODO: Use H/W picking if availabe. */ + Evas_3D_Pick_Data data; + + data.x = ((x * 2.0) / (Evas_Real)scene->w) - 1.0; + data.y = (((scene->h - y - 1) * 2.0) / ((Evas_Real)scene->h)) - 1.0; + + data.picked = EINA_FALSE; + data.z = 1.0; + data.node = NULL; + data.mesh = NULL; + data.s = 0.0; + data.t = 0.0; + + /* Update the scene graph. */ + evas_3d_object_update((Evas_3D_Object *)&scene->base); + + evas_mat4_multiply(&data.matrix_vp, + &scene->camera_node->data.camera.camera->projection, + &scene->camera_node->data.camera.matrix_world_to_eye); + + evas_ray3_init(&data.ray_world, data.x, data.y, &data.matrix_vp); + + + /* Traverse tree while adding meshes into pick data structure. */ + evas_3d_node_tree_traverse(scene->root_node, EVAS_3D_TREE_TRAVERSE_LEVEL_ORDER, EINA_TRUE, + _node_pick, &data); + + if (!data.picked) + return EINA_FALSE; + + if (s) *s = data.s; + if (t) *t = data.t; + if (node) *node = data.node; + if (mesh) *mesh = data.mesh; + + return EINA_TRUE; +} diff --git a/src/lib/evas/canvas/evas_3d_texture.c b/src/lib/evas/canvas/evas_3d_texture.c new file mode 100644 index 0000000..16e3395 --- /dev/null +++ b/src/lib/evas/canvas/evas_3d_texture.c @@ -0,0 +1,440 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#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); +} diff --git a/src/lib/evas/canvas/evas_image.eo b/src/lib/evas/canvas/evas_image.eo index 3fd2c53..b57ba33 100644 --- a/src/lib/evas/canvas/evas_image.eo +++ b/src/lib/evas/canvas/evas_image.eo @@ -27,6 +27,7 @@ class Evas_Image (Evas_Object) double dpi; /*@ The new DPI resolution. */ } } + source_clip { set { /*@ @@ -996,6 +997,32 @@ class Evas_Image (Evas_Object) return int; } } + + 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 { diff --git a/src/lib/evas/canvas/evas_object_image.c b/src/lib/evas/canvas/evas_object_image.c index d02a9b1..67b37d8 100644 --- a/src/lib/evas/canvas/evas_object_image.c +++ b/src/lib/evas/canvas/evas_object_image.c @@ -79,6 +79,8 @@ struct _Evas_Object_Image_State Evas_Object *source; Evas_Map *defmap; + Evas_3D_Scene *scene; + union { const char *file; Eina_File *f; @@ -184,6 +186,9 @@ static void _proxy_unset(Evas_Object *proxy, Evas_Object_Protected_Data *obj, Ev static void _proxy_set(Evas_Object *proxy, Evas_Object *src); static void _proxy_error(Evas_Object *proxy, void *context, void *output, void *surface, int x, int y, Eina_Bool do_async); +static void _3d_set(Evas_Object *eo_obj, Evas_3D_Scene *scene); +static void _3d_unset(Evas_Object *eo_obj, Evas_Object_Protected_Data *image, Evas_Image_Data *o); + static const Evas_Object_Func object_func = { /* methods (compulsory) */ @@ -283,6 +288,7 @@ _evas_object_image_cleanup(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, eo_obj); } if (o->cur->source) _proxy_unset(eo_obj, obj, o); + if (o->cur->scene) _3d_unset(eo_obj, obj, o); } static Eina_Bool @@ -400,6 +406,8 @@ _image_init_set(const Eina_File *f, const char *file, const char *key, { if (o->cur->source) _proxy_unset(eo_obj, obj, o); + if (o->cur->scene) _3d_unset(eo_obj, obj, o); + EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write) { if (f) @@ -763,6 +771,30 @@ _evas_image_source_visible_get(Eo *eo_obj EINA_UNUSED, Evas_Image_Data *o) return visible; } + +EOLIAN static void +_evas_image_t3d_scene_set(Eo *eo_obj, Evas_Image_Data *o, Evas_3D_Scene *scene) +{ + Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJ_CLASS); + + if (o->cur->scene == scene) + return; + + _evas_object_image_cleanup(eo_obj, obj, o); + + if (o->cur->u.file || o->cur->key) + evas_object_image_file_set(eo_obj, NULL, NULL); + + if (scene) _3d_set(eo_obj, scene); + else _3d_unset(eo_obj, obj, o); +} + +EOLIAN static Evas_3D_Scene * +_evas_image_t3d_scene_get(Eo *eo_obj EINA_UNUSED, Evas_Image_Data *o) +{ + return o->cur->scene; +} + EOLIAN static void _evas_image_border_set(Eo *eo_obj, Evas_Image_Data *o, int l, int r, int t, int b) { @@ -2385,6 +2417,145 @@ _proxy_subrender(Evas *eo_e, Evas_Object *eo_source, Evas_Object *eo_proxy, Evas } static void +_3d_set(Evas_Object *eo_obj, Evas_3D_Scene *scene) +{ + Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJ_CLASS); + Evas_Image_Data *o = eo_data_scope_get(eo_obj, MY_CLASS); + + evas_object_image_file_set(eo_obj, NULL, NULL); + + EINA_COW_WRITE_BEGIN(evas_object_3d_cow, obj->data_3d, Evas_Object_3D_Data, data) + { + data->surface = NULL; + data->w = 0; + data->h = 0; + evas_3d_object_reference(&scene->base); + } + EINA_COW_WRITE_END(evas_object_3d_cow, obj->data_3d, data); + + EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write) + { + state_write->scene = scene; + } + EINA_COW_IMAGE_STATE_WRITE_END(o, state_write); + + scene->images = eina_list_append(scene->images, eo_obj); +} + +static void +_3d_unset(Evas_Object *eo_obj EINA_UNUSED, Evas_Object_Protected_Data *obj, Evas_Image_Data *o) +{ + if (!o->cur->scene) return; + + if (o->cur->scene) + { + EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write) + o->cur->scene->images = eina_list_remove(o->cur->scene->images, eo_obj); + evas_3d_object_unreference(&state_write->scene->base); + state_write->scene = NULL; + EINA_COW_IMAGE_STATE_WRITE_END(o, state_write); + } + + if (o->cur->defmap) + { + EINA_COW_IMAGE_STATE_WRITE_BEGIN(o, state_write) + { + evas_map_free(state_write->defmap); + state_write->defmap = NULL; + } + EINA_COW_IMAGE_STATE_WRITE_END(o, state_write); + } + + EINA_COW_WRITE_BEGIN(evas_object_3d_cow, obj->data_3d, Evas_Object_3D_Data, data) + { + if (data->surface) + { + obj->layer->evas->engine.func->image_free(obj->layer->evas->engine.data.output, + data->surface); + } + + data->surface = NULL; + data->w = 0; + data->h = 0; + } + EINA_COW_WRITE_END(evas_object_3d_cow, obj->data_3d, data); + +} + +static void +_3d_render(Evas *eo_e, Evas_Object *eo_obj EINA_UNUSED, Evas_Object_Protected_Data *obj, Evas_Image_Data *o EINA_UNUSED, Evas_3D_Scene *scene) +{ + Evas_Public_Data *e; + Eina_Bool need_native_set = EINA_FALSE; + Evas_3D_Scene_Data scene_data; + + if (scene == NULL) + return; + + if((scene->w == 0) || (scene->h == 0)) + return; + + e = eo_data_scope_get(eo_e, EVAS_CLASS); + + if (scene->surface != NULL) + { + int w, h; + + e->engine.func->drawable_size_get(e->engine.data.output, scene->surface, &w, &h); + + if ((w != scene->w) || (h != scene->h)) + { + e->engine.func->drawable_free(e->engine.data.output, scene->surface); + scene->surface = NULL; + need_native_set = EINA_TRUE; + } + } + + if (scene->surface == NULL) + { + /* TODO: Hard-coded alpha on. */ + scene->surface = e->engine.func->drawable_new(e->engine.data.output, + scene->w, scene->h, 1); + need_native_set = EINA_TRUE; + } + + EINA_COW_WRITE_BEGIN(evas_object_3d_cow, obj->data_3d, Evas_Object_3D_Data, data) + { + if (need_native_set) + { + data->surface = e->engine.func->image_drawable_set(e->engine.data.output, + data->surface, scene->surface); + } + + data->w = scene->w; + data->h = scene->h; + } + EINA_COW_WRITE_END(evas_object_3d_cow, obj->data_3d, data); + + evas_3d_scene_data_init(&scene_data); + + scene_data.bg_color = scene->bg_color; + scene_data.camera_node = scene->camera_node; + + /* Phase 1 - Update scene graph tree. */ + evas_3d_object_update(&scene->base); + + /* Phase 2 - Do frustum culling and get visible model nodes. */ + evas_3d_node_tree_traverse(scene->root_node, EVAS_3D_TREE_TRAVERSE_LEVEL_ORDER, EINA_TRUE, + evas_3d_node_mesh_collect, &scene_data); + + /* Phase 3 - Collect active light nodes in the scene graph tree. */ + evas_3d_node_tree_traverse(scene->root_node, EVAS_3D_TREE_TRAVERSE_ANY_ORDER, EINA_FALSE, + evas_3d_node_light_collect, &scene_data); + + /* Phase 5 - Draw the scene. */ + e->engine.func->drawable_scene_render(e->engine.data.output, scene->surface, &scene_data); + + /* Clean up temporary resources. */ + evas_3d_scene_data_fini(&scene_data); +} + +static void evas_object_image_unload(Evas_Object *eo_obj, Eina_Bool dirty) { Evas_Image_Data *o; @@ -2608,6 +2779,7 @@ evas_object_image_free(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj) } if (o->cur->key) eina_stringshare_del(o->cur->key); if (o->cur->source) _proxy_unset(eo_obj, obj, o); + if (o->cur->scene) _3d_unset(eo_obj, obj, o); if (obj->layer && obj->layer->evas) { if (o->engine_data) @@ -2857,7 +3029,17 @@ evas_object_image_render(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, v (o->cur->source ? eo_data_scope_get(o->cur->source, EVAS_OBJ_CLASS): NULL); - if (!o->cur->source) + + if (o->cur->scene) + { + _3d_render(obj->layer->evas->evas, eo_obj, obj, o, o->cur->scene); + pixels = obj->data_3d->surface; + imagew = obj->data_3d->w; + imageh = obj->data_3d->h; + uvw = imagew; + uvh = imageh; + } + else if (!o->cur->source) { pixels = evas_process_dirty_pixels(eo_obj, obj, o, output, o->engine_data); /* pixels = o->engine_data; */ @@ -3186,6 +3368,16 @@ evas_object_image_render_pre(Evas_Object *eo_obj, goto done; } } + else if (o->cur->scene) + { + Evas_3D_Scene *scene = o->cur->scene; + + if (evas_3d_object_dirty_get(&scene->base, EVAS_3D_STATE_ANY)) + { + evas_object_render_pre_prev_cur_add(&e->clip_changes, eo_obj, obj); + goto done; + } + } /* now figure what changed and add draw rects */ /* if it just became visible or invisible */ diff --git a/src/lib/evas/canvas/evas_object_main.c b/src/lib/evas/canvas/evas_object_main.c index b20ecfb..24aadcd 100644 --- a/src/lib/evas/canvas/evas_object_main.c +++ b/src/lib/evas/canvas/evas_object_main.c @@ -23,7 +23,7 @@ get_layer_objects(Evas_Layer *l) /* evas internal stuff */ static const Evas_Object_Proxy_Data default_proxy = { - NULL, NULL, 0, 0, NULL, 0, 0, 0, 0 + NULL, NULL, NULL, 0, 0, NULL, 0, 0, 0, 0 }; static const Evas_Object_Map_Data default_map = { { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, NULL, 0, 0, NULL, NULL @@ -39,16 +39,20 @@ Eina_Cow *evas_object_proxy_cow = NULL; Eina_Cow *evas_object_map_cow = NULL; Eina_Cow *evas_object_state_cow = NULL; +Eina_Cow *evas_object_3d_cow = NULL; + static Eina_Bool _init_cow(void) { - if (evas_object_map_cow && evas_object_proxy_cow && evas_object_state_cow) return EINA_TRUE; + if (evas_object_map_cow && evas_object_proxy_cow && evas_object_state_cow && evas_object_3d_cow) return EINA_TRUE; evas_object_proxy_cow = eina_cow_add("Evas Object Proxy", sizeof (Evas_Object_Proxy_Data), 8, &default_proxy, EINA_TRUE); evas_object_map_cow = eina_cow_add("Evas Object Map", sizeof (Evas_Object_Map_Data), 8, &default_map, EINA_TRUE); evas_object_state_cow = eina_cow_add("Evas Object State", sizeof (Evas_Object_Protected_State), 64, &default_state, EINA_FALSE); - if (!(evas_object_map_cow && evas_object_proxy_cow && evas_object_state_cow)) + evas_object_3d_cow = eina_cow_add("Evas Object 3D", sizeof (Evas_Object_3D_Data), 8, &default_proxy, EINA_TRUE); + + if (!(evas_object_map_cow && evas_object_proxy_cow && evas_object_state_cow && evas_object_3d_cow)) { eina_cow_del(evas_object_proxy_cow); eina_cow_del(evas_object_map_cow); @@ -56,6 +60,10 @@ _init_cow(void) evas_object_proxy_cow = NULL; evas_object_map_cow = NULL; evas_object_state_cow = NULL; + + eina_cow_del(evas_object_3d_cow); + evas_object_3d_cow = NULL; + return EINA_FALSE; } @@ -81,6 +89,7 @@ _evas_object_eo_base_constructor(Eo *eo_obj, Evas_Object_Protected_Data *obj) obj->map = eina_cow_alloc(evas_object_map_cow); obj->cur = eina_cow_alloc(evas_object_state_cow); obj->prev = eina_cow_alloc(evas_object_state_cow); + obj->data_3d = eina_cow_alloc(evas_object_3d_cow); } void @@ -188,6 +197,7 @@ evas_object_free(Evas_Object *eo_obj, int clean_layer) eina_cow_free(evas_object_map_cow, (const Eina_Cow_Data**) &obj->map); eina_cow_free(evas_object_state_cow, (const Eina_Cow_Data**) &obj->cur); eina_cow_free(evas_object_state_cow, (const Eina_Cow_Data**) &obj->prev); + eina_cow_free(evas_object_3d_cow, (const Eina_Cow_Data**) &obj->data_3d); eo_data_unref(eo_obj, obj->private_data); obj->private_data = NULL; @@ -204,6 +214,7 @@ evas_object_change(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj) Evas_Object_Protected_Data *obj2; Evas_Object *eo_obj2; Eina_Bool movch = EINA_FALSE; + Evas_3D_Texture *texture; if (!obj->layer) return; if (obj->layer->evas->nochange) return; @@ -233,6 +244,10 @@ evas_object_change(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj) if (!obj2) continue; evas_object_change(eo_obj2, obj2); } + EINA_LIST_FOREACH(obj->proxy->proxy_textures, l, texture) + { + evas_3d_object_change(&texture->base, EVAS_3D_STATE_TEXTURE_DATA, NULL); + } if (obj->smart.parent) { Evas_Object_Protected_Data *smart_parent = eo_data_scope_get(obj->smart.parent, MY_CLASS); diff --git a/src/lib/evas/include/evas_3d_private.h b/src/lib/evas/include/evas_3d_private.h new file mode 100644 index 0000000..e30dacf --- /dev/null +++ b/src/lib/evas/include/evas_3d_private.h @@ -0,0 +1,377 @@ +#ifndef EVAS_PRIVATE_H +# error You shall not include this header directly +#endif + +#include "Evas_3D.h" +#include "evas_3d_utils.h" + +#define EVAS_3D_VERTEX_ATTRIB_COUNT 5 +#define EVAS_3D_MATERIAL_ATTRIB_COUNT 5 + +typedef struct _Evas_3D_Object Evas_3D_Object; +typedef struct _Evas_3D_Scene_Data Evas_3D_Scene_Data; +typedef struct _Evas_3D_Vertex_Buffer Evas_3D_Vertex_Buffer; +typedef struct _Evas_3D_Mesh_Frame Evas_3D_Mesh_Frame; +typedef struct _Evas_3D_Node_Mesh Evas_3D_Node_Mesh; +typedef struct _Evas_3D_Object_Func Evas_3D_Object_Func; +typedef struct _Evas_3D_Pick_Data Evas_3D_Pick_Data; +typedef struct _Evas_3D_Interpolate_Vertex_Buffer Evas_3D_Interpolate_Vertex_Buffer; + +typedef Eina_Bool (*Evas_3D_Node_Func)(Evas_3D_Node *, void *data); + +typedef enum _Evas_3D_Object_Type +{ + EVAS_3D_OBJECT_TYPE_INVALID = 0, + EVAS_3D_OBJECT_TYPE_SCENE, + EVAS_3D_OBJECT_TYPE_NODE, + EVAS_3D_OBJECT_TYPE_CAMERA, + EVAS_3D_OBJECT_TYPE_LIGHT, + EVAS_3D_OBJECT_TYPE_MODEL, + EVAS_3D_OBJECT_TYPE_MESH, + EVAS_3D_OBJECT_TYPE_TEXTURE, + EVAS_3D_OBJECT_TYPE_MATERIAL, +} Evas_3D_Object_Type; + +typedef enum _Evas_3D_State +{ + EVAS_3D_STATE_MAX = 16, + + EVAS_3D_STATE_ANY = 0, + + EVAS_3D_STATE_SCENE_ROOT_NODE = 1, + EVAS_3D_STATE_SCENE_CAMERA_NODE, + EVAS_3D_STATE_SCENE_BACKGROUND_COLOR, + EVAS_3D_STATE_SCENE_SIZE, + + EVAS_3D_STATE_TEXTURE_DATA = 1, + EVAS_3D_STATE_TEXTURE_WRAP, + EVAS_3D_STATE_TEXTURE_FILTER, + + EVAS_3D_STATE_MATERIAL_ID = 1, + EVAS_3D_STATE_MATERIAL_COLOR, + EVAS_3D_STATE_MATERIAL_TEXTURE, + + EVAS_3D_STATE_MESH_VERTEX_COUNT = 1, + EVAS_3D_STATE_MESH_FRAME, + EVAS_3D_STATE_MESH_MATERIAL, + EVAS_3D_STATE_MESH_TRANSFORM, + EVAS_3D_STATE_MESH_VERTEX_DATA, + EVAS_3D_STATE_MESH_INDEX_DATA, + EVAS_3D_STATE_MESH_VERTEX_ASSEMBLY, + EVAS_3D_STATE_MESH_SHADE_MODE, + + EVAS_3D_STATE_CAMERA_PROJECTION = 1, + + EVAS_3D_STATE_LIGHT_AMBIENT = 1, + EVAS_3D_STATE_LIGHT_DIFFUSE, + EVAS_3D_STATE_LIGHT_SPECULAR, + EVAS_3D_STATE_LIGHT_SPOT_DIR, + EVAS_3D_STATE_LIGHT_SPOT_EXP, + EVAS_3D_STATE_LIGHT_SPOT_CUTOFF, + EVAS_3D_STATE_LIGHT_ATTENUATION, + + EVAS_3D_STATE_NODE_TRANSFORM = 1, + EVAS_3D_STATE_NODE_MESH_GEOMETRY, + EVAS_3D_STATE_NODE_MESH_MATERIAL, + EVAS_3D_STATE_NODE_MESH_FRAME, + EVAS_3D_STATE_NODE_MESH_SHADE_MODE, + EVAS_3D_STATE_NODE_MESH_MATERIAL_ID, + EVAS_3D_STATE_NODE_LIGHT, + EVAS_3D_STATE_NODE_CAMERA, + EVAS_3D_STATE_NODE_PARENT, + EVAS_3D_STATE_NODE_MEMBER, +} Evas_3D_State; + +typedef enum _Evas_3D_Node_Traverse_Type +{ + EVAS_3D_NODE_TRAVERSE_DOWNWARD, + EVAS_3D_NODE_TRAVERSE_UPWARD, +} Evas_3D_Node_Traverse_Type; + +typedef enum _Evas_3D_Tree_Traverse_Type +{ + EVAS_3D_TREE_TRAVERSE_PRE_ORDER, + EVAS_3D_TREE_TRAVERSE_ANY_ORDER = EVAS_3D_TREE_TRAVERSE_PRE_ORDER, + EVAS_3D_TREE_TRAVERSE_POST_ORDER, + EVAS_3D_TREE_TRAVERSE_LEVEL_ORDER, +} Evas_3D_Tree_Traverse_Type; + +struct _Evas_3D_Object_Func +{ + void (*free)(Evas_3D_Object *obj); + void (*change)(Evas_3D_Object *obj, Evas_3D_State state, Evas_3D_Object *ref); + void (*update)(Evas_3D_Object *obj); +}; + +struct _Evas_3D_Object +{ + Evas *evas; + + Evas_3D_Object_Type type; + int ref_count; + Evas_3D_Object_Func func; + + Eina_Bool dirty[EVAS_3D_STATE_MAX]; +}; + +struct _Evas_3D_Scene +{ + Evas_3D_Object base; + + Evas_3D_Node *root_node; + Evas_3D_Node *camera_node; + Evas_Color bg_color; + + void *surface; + int w, h; + Eina_List *images; +}; + +struct _Evas_3D_Node_Mesh +{ + Evas_3D_Node *node; + Evas_3D_Mesh *mesh; + int frame; +}; + +struct _Evas_3D_Node +{ + Evas_3D_Object base; + + Eina_List *members; + Evas_3D_Node *parent; + + Evas_Vec3 position; + Evas_Vec4 orientation; + Evas_Vec3 scale; + + Evas_Vec3 position_world; + Evas_Vec4 orientation_world; + Evas_Vec3 scale_world; + + Eina_Bool position_inherit; + Eina_Bool orientation_inherit; + Eina_Bool scale_inherit; + + Evas_Box3 aabb; + + Evas_3D_Node_Type type; + + /* Camera node. */ + union { + struct { + Evas_3D_Camera *camera; + Evas_Mat4 matrix_world_to_eye; + } camera; + + struct { + Evas_3D_Light *light; + Evas_Mat4 matrix_local_to_world; + } light; + + struct { + Eina_List *meshes; + Eina_Hash *node_meshes; + Evas_Mat4 matrix_local_to_world; + } mesh; + } data; + + /* Scene using this node as root. */ + Eina_Hash *scenes_root; + + /* Scene using this node as camera. */ + Eina_Hash *scenes_camera; +}; + +struct _Evas_3D_Camera +{ + Evas_3D_Object base; + + Evas_Mat4 projection; + Eina_Hash *nodes; +}; + +struct _Evas_3D_Light +{ + Evas_3D_Object base; + + Evas_Color ambient; + Evas_Color diffuse; + Evas_Color specular; + + Eina_Bool directional; + Evas_Real spot_exp; + Evas_Real spot_cutoff; + Evas_Real spot_cutoff_cos; + + Eina_Bool enable_attenuation; + Evas_Real atten_const; + Evas_Real atten_linear; + Evas_Real atten_quad; + + Eina_Hash *nodes; +}; + +struct _Evas_3D_Vertex_Buffer +{ + int element_count; + int stride; + void *data; + int size; + Eina_Bool owns_data; + Eina_Bool mapped; +}; + +struct _Evas_3D_Interpolate_Vertex_Buffer +{ + void *data0; + int stride0; + int size0; + + void *data1; + int stride1; + int size1; + + Evas_Real weight; +}; + +struct _Evas_3D_Mesh_Frame +{ + Evas_3D_Mesh *mesh; + + int frame; + Evas_3D_Material *material; + Evas_Box3 aabb; + + Evas_3D_Vertex_Buffer vertices[EVAS_3D_VERTEX_ATTRIB_COUNT]; +}; + +struct _Evas_3D_Mesh +{ + Evas_3D_Object base; + + Evas_3D_Shade_Mode shade_mode; + + int vertex_count; + int frame_count; + Eina_List *frames; + + Evas_3D_Index_Format index_format; + int index_count; + void *indices; + int index_size; + Eina_Bool owns_indices; + Eina_Bool index_mapped; + + Evas_3D_Vertex_Assembly assembly; + + Eina_Hash *nodes; +}; + +struct _Evas_3D_Texture +{ + Evas_3D_Object base; + + /* List of materials using this texture. */ + Eina_Hash *materials; + + /* Proxy data. */ + Evas_Object *source; + Eina_Bool proxy_rendering; + void *proxy_surface; + + /* Engine-side object. */ + void *engine_data; +}; + +struct _Evas_3D_Material +{ + Evas_3D_Object base; + + struct { + Eina_Bool enable; + Evas_Color color; + Evas_3D_Texture *texture; + } attribs[EVAS_3D_MATERIAL_ATTRIB_COUNT]; + + Evas_Real shininess; + + Eina_Hash *meshes; +}; + +struct _Evas_3D_Scene_Data +{ + Evas_Color bg_color; + Evas_3D_Node *camera_node; + Eina_List *light_nodes; + Eina_List *mesh_nodes; +}; + +struct _Evas_3D_Pick_Data +{ + /* Input */ + Evas_Real x, y; + Evas_Mat4 matrix_vp; + Evas_Ray3 ray_world; + + /* Output */ + Eina_Bool picked; + Evas_Real z; + Evas_3D_Node *node; + Evas_3D_Mesh *mesh; + Evas_Real u, v; + Evas_Real s, t; +}; + +/* Object generic functions. */ +void evas_3d_object_init(Evas_3D_Object *obj, Evas *e, Evas_3D_Object_Type type, const Evas_3D_Object_Func *func); +Evas *evas_3d_object_evas_get(const Evas_3D_Object *obj); +Evas_3D_Object_Type evas_3d_object_type_get(const Evas_3D_Object *obj); + +void evas_3d_object_reference(Evas_3D_Object *obj); +void evas_3d_object_unreference(Evas_3D_Object *obj); +int evas_3d_object_reference_count_get(const Evas_3D_Object *obj); + +void evas_3d_object_change(Evas_3D_Object *obj, Evas_3D_State state, Evas_3D_Object *ref); +Eina_Bool evas_3d_object_dirty_get(const Evas_3D_Object *obj, Evas_3D_State state); +void evas_3d_object_update(Evas_3D_Object *obj); +void evas_3d_object_update_done(Evas_3D_Object *obj); + +/* Node functions. */ +void evas_3d_node_traverse(Evas_3D_Node *from, Evas_3D_Node *to, Evas_3D_Node_Traverse_Type type, Eina_Bool skip, Evas_3D_Node_Func func, void *data); +void evas_3d_node_tree_traverse(Evas_3D_Node *root, Evas_3D_Tree_Traverse_Type type, Eina_Bool skip, Evas_3D_Node_Func func, void *data); +Eina_Bool evas_3d_node_mesh_collect(Evas_3D_Node *node, void *data); +Eina_Bool evas_3d_node_light_collect(Evas_3D_Node *node, void *data); + +void evas_3d_node_scene_root_add(Evas_3D_Node *node, Evas_3D_Scene *scene); +void evas_3d_node_scene_root_del(Evas_3D_Node *node, Evas_3D_Scene *scene); +void evas_3d_node_scene_camera_add(Evas_3D_Node *node, Evas_3D_Scene *scene); +void evas_3d_node_scene_camera_del(Evas_3D_Node *node, Evas_3D_Scene *scene); + +/* Camera functions. */ +void evas_3d_camera_node_add(Evas_3D_Camera *camera, Evas_3D_Node *node); +void evas_3d_camera_node_del(Evas_3D_Camera *camera, Evas_3D_Node *node); + +/* Light functions. */ +void evas_3d_light_node_add(Evas_3D_Light *light, Evas_3D_Node *node); +void evas_3d_light_node_del(Evas_3D_Light *light, Evas_3D_Node *node); + +/* Mesh functions. */ +void evas_3d_mesh_node_add(Evas_3D_Mesh *mesh, Evas_3D_Node *node); +void evas_3d_mesh_node_del(Evas_3D_Mesh *mesh, Evas_3D_Node *node); + +void evas_3d_mesh_interpolate_vertex_buffer_get(Evas_3D_Mesh *mesh, int frame, Evas_3D_Vertex_Attrib attrib, Evas_3D_Vertex_Buffer *buffer0, Evas_3D_Vertex_Buffer *buffer1, Evas_Real *weight); + +void evas_3d_mesh_file_md2_set(Evas_3D_Mesh *mesh, const char *file); + +/* Texture functions. */ +void evas_3d_texture_material_add(Evas_3D_Texture *texture, Evas_3D_Material *material); +void evas_3d_texture_material_del(Evas_3D_Texture *texture, Evas_3D_Material *material); + +/* Material functions. */ +void evas_3d_material_mesh_add(Evas_3D_Material *material, Evas_3D_Mesh *mesh); +void evas_3d_material_mesh_del(Evas_3D_Material *material, Evas_3D_Mesh *mesh); + +/* Scene functions. */ +void evas_3d_scene_data_init(Evas_3D_Scene_Data *data); +void evas_3d_scene_data_fini(Evas_3D_Scene_Data *data); diff --git a/src/lib/evas/include/evas_3d_utils.h b/src/lib/evas/include/evas_3d_utils.h new file mode 100644 index 0000000..196d3df --- /dev/null +++ b/src/lib/evas/include/evas_3d_utils.h @@ -0,0 +1,1526 @@ +#ifndef EVAS_PRIVATE_H +# error You shall not include this header directly +#endif + +#include + +#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; +} diff --git a/src/lib/evas/include/evas_private.h b/src/lib/evas/include/evas_private.h index 8f77c56..f93962b 100644 --- a/src/lib/evas/include/evas_private.h +++ b/src/lib/evas/include/evas_private.h @@ -16,6 +16,7 @@ #include "../common/language/evas_bidi_utils.h" #include "../common/language/evas_language_utils.h" +#include "evas_3d_private.h" #define RENDER_METHOD_INVALID 0x00000000 @@ -46,6 +47,7 @@ typedef struct _Evas_Coord_Touch_Point Evas_Coord_Touch_Point; typedef struct _Evas_Object_Proxy_Data Evas_Object_Proxy_Data; typedef struct _Evas_Object_Map_Data Evas_Object_Map_Data; typedef struct _Evas_Proxy_Render_Data Evas_Proxy_Render_Data; +typedef struct _Evas_Object_3D_Data Evas_Object_3D_Data; typedef struct _Evas_Object_Protected_State Evas_Object_Protected_State; typedef struct _Evas_Object_Protected_Data Evas_Object_Protected_Data; @@ -512,6 +514,7 @@ struct _Evas_Map struct _Evas_Object_Proxy_Data { Eina_List *proxies; + Eina_List *proxy_textures; void *surface; int w,h; Eina_List *src_event_in; @@ -537,6 +540,12 @@ struct _Evas_Object_Map_Data RGBA_Map *spans; }; +struct _Evas_Object_3D_Data +{ + void *surface; + int w, h; +}; + struct _Evas_Object_Protected_State { Evas_Object_Protected_Data *clipper; @@ -603,6 +612,7 @@ struct _Evas_Object_Protected_Data // Eina_Cow pointer be careful when writing to it const Evas_Object_Proxy_Data *proxy; const Evas_Object_Map_Data *map; + const Evas_Object_3D_Data *data_3d; // Pointer to the Evas_Object itself Evas_Object *object; @@ -923,6 +933,26 @@ struct _Evas_Func Eina_Bool (*pixel_alpha_get) (void *image, int x, int y, DATA8 *alpha, int src_region_x, int src_region_y, int src_region_w, int src_region_h, int dst_region_x, int dst_region_y, int dst_region_w, int dst_region_h); void (*context_flush) (void *data); + + /* 3D features */ + void *(*drawable_new) (void *data, int w, int h, int alpha); + void (*drawable_free) (void *data, void *drawable); + void (*drawable_size_get) (void *data, void *drawable, int *w, int *h); + void *(*image_drawable_set) (void *data, void *image, void *drawable); + + void (*drawable_scene_render) (void *data, void *drawable, void *scene_data); + + void *(*texture_new) (void *data); + void (*texture_free) (void *data, void *texture); + void (*texture_data_set) (void *data, void *texture, Evas_3D_Color_Format format, Evas_3D_Pixel_Format pixel_format, int w, int h, const void *pixels); + void (*texture_file_set) (void *data, void *texture, const char *file, const char *key); + void (*texture_color_format_get) (void *data, void *texture, Evas_3D_Color_Format *format); + void (*texture_size_get) (void *data, void *texture, int *w, int *h); + void (*texture_wrap_set) (void *data, void *texture, Evas_3D_Wrap_Mode s, Evas_3D_Wrap_Mode t); + void (*texture_wrap_get) (void *data, void *texture, Evas_3D_Wrap_Mode *s, Evas_3D_Wrap_Mode *t); + void (*texture_filter_set) (void *data, void *texture, Evas_3D_Texture_Filter min, Evas_3D_Texture_Filter mag); + void (*texture_filter_get) (void *data, void *texture, Evas_3D_Texture_Filter *min, Evas_3D_Texture_Filter *mag); + void (*texture_image_set) (void *data, void *texture, void *image); }; struct _Evas_Image_Save_Func @@ -1285,6 +1315,8 @@ extern Eina_Cow *evas_object_proxy_cow; extern Eina_Cow *evas_object_map_cow; extern Eina_Cow *evas_object_state_cow; +extern Eina_Cow *evas_object_3d_cow; + extern Eina_Cow *evas_object_image_pixels_cow; extern Eina_Cow *evas_object_image_load_opts_cow; extern Eina_Cow *evas_object_image_state_cow; diff --git a/src/modules/evas/engines/gl_common/evas_gl_3d.c b/src/modules/evas/engines/gl_common/evas_gl_3d.c new file mode 100644 index 0000000..938a542 --- /dev/null +++ b/src/modules/evas/engines/gl_common/evas_gl_3d.c @@ -0,0 +1,1332 @@ +#include +#include +#include "evas_gl_private.h" +#include "evas_gl_3d_private.h" + +void +e3d_texture_param_update(E3D_Texture *texture) +{ + if (texture->is_imported) + return; + + if (texture->wrap_dirty) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture->wrap_s); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture->wrap_t); + texture->wrap_dirty = EINA_FALSE; + } + + if (texture->filter_dirty) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture->filter_min); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture->filter_mag); + texture->filter_dirty = EINA_FALSE; + } +} + +E3D_Texture * +e3d_texture_new(void) +{ + E3D_Texture *texture = NULL; + + texture = (E3D_Texture *)malloc(sizeof(E3D_Texture)); + + if (texture == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + texture->w = 0; + texture->h = 0; + + texture->is_imported = EINA_FALSE; + texture->tex = 0; + texture->format = GL_RGBA; + + texture->wrap_dirty = EINA_TRUE; + texture->wrap_s = GL_CLAMP_TO_EDGE; + texture->wrap_t = GL_CLAMP_TO_EDGE; + + texture->filter_dirty = EINA_TRUE; + texture->filter_min = GL_NEAREST; + texture->filter_mag = GL_NEAREST; + + return texture; +} + +void +e3d_texture_free(E3D_Texture *texture) +{ + if (texture->tex && !texture->is_imported) + glDeleteTextures(1, &texture->tex); + + free(texture); +} + +void +e3d_texture_data_set(E3D_Texture *texture, + Evas_3D_Color_Format color_format, Evas_3D_Pixel_Format pixel_format, + int w, int h, const void *data) +{ + GLenum format; + GLenum iformat; + GLenum type; + + if (color_format == EVAS_3D_COLOR_FORMAT_RGBA) + { + format = GL_RGBA; + iformat = GL_RGBA; + + if (pixel_format == EVAS_3D_PIXEL_FORMAT_8888) + type = GL_UNSIGNED_BYTE; + else if (pixel_format == EVAS_3D_PIXEL_FORMAT_4444) + type = GL_UNSIGNED_SHORT_4_4_4_4; + else if (pixel_format == EVAS_3D_PIXEL_FORMAT_5551) + type = GL_UNSIGNED_SHORT_5_5_5_1; + else + { + ERR("Texture data format mismatch."); + return; + } + } + else if (color_format == EVAS_3D_COLOR_FORMAT_RGB) + { + format = GL_RGB; + iformat = GL_RGB; + + if (pixel_format == EVAS_3D_PIXEL_FORMAT_565) + type = GL_UNSIGNED_SHORT_5_6_5; + else if (pixel_format == EVAS_3D_PIXEL_FORMAT_888) + type = GL_UNSIGNED_BYTE; + else + { + ERR("Texture data format mismatch."); + return; + } + } + else if (color_format == EVAS_3D_COLOR_FORMAT_ALPHA) + { + format = GL_LUMINANCE; + iformat = GL_LUMINANCE; + + if (pixel_format == EVAS_3D_PIXEL_FORMAT_8) + type = GL_UNSIGNED_BYTE; + else + { + ERR("Texture data format mismatch."); + return; + } + } + else + { + ERR("Invalid texture color format"); + return; + } + + if (texture->tex == 0 || texture->is_imported) + { + glGenTextures(1, &texture->tex); + texture->wrap_dirty = EINA_TRUE; + texture->filter_dirty = EINA_TRUE; + } + + glBindTexture(GL_TEXTURE_2D, texture->tex); + glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, iformat, type, data); + + if (texture->wrap_dirty) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture->wrap_s); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texture->wrap_t); + texture->wrap_dirty = EINA_FALSE; + } + + if (texture->filter_dirty) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture->filter_min); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texture->filter_mag); + texture->filter_dirty = EINA_FALSE; + } + + texture->is_imported = EINA_FALSE; + texture->format = format; +} + +void +e3d_texture_file_set(E3D_Texture *texture, const char *file, const char *key EINA_UNUSED) +{ + Evas_3D_Color_Format color_format; + Evas_3D_Pixel_Format pixel_format; + + FILE *fp; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + void *buffer = NULL; + png_bytep *row_pointers = NULL; + + int w, h; + png_byte color_type; + png_byte bit_depth; + png_byte header[8]; + int ret; + int y; + int stride; + + fp = fopen(file, "rb"); + + if (fp == NULL) + { + ERR("Failed to open file %s", file); + goto done; + } + + ret = fread(header, 1, 8, fp); + + if (ret < 8) + goto done; + + if (png_sig_cmp(header, 0, 8)) + { + ERR("%s does not seem to be a png file.", file); + goto done; + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (png_ptr == NULL) + goto done; + + info_ptr = png_create_info_struct(png_ptr); + + if (info_ptr == NULL) + goto done; + + if (setjmp(png_jmpbuf(png_ptr))) + goto done; + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, 8); + + png_read_info(png_ptr, info_ptr); + + bit_depth = png_get_bit_depth(png_ptr, info_ptr); + + if (bit_depth != 8) + { + ERR("Unsupported bit depth %d.", bit_depth); + goto done; + } + + w = png_get_image_width(png_ptr, info_ptr); + h = png_get_image_height(png_ptr, info_ptr); + + if (w < 1 || h < 1 || w > IMG_MAX_SIZE || h > IMG_MAX_SIZE || IMG_TOO_BIG(w, h)) + { + ERR("Image %s(%d x %d) is too big for texture.", file, w, h); + goto done; + } + + color_type = png_get_color_type(png_ptr, info_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY) + { + color_format = EVAS_3D_COLOR_FORMAT_ALPHA; + pixel_format = EVAS_3D_PIXEL_FORMAT_8; + stride = (w + 3) & ~3; + } + else if (color_type == PNG_COLOR_TYPE_RGB) + { + color_format = EVAS_3D_COLOR_FORMAT_RGB; + pixel_format = EVAS_3D_PIXEL_FORMAT_888; + stride = (w * 3 + 3) & ~3; + } + else if (color_type == PNG_COLOR_TYPE_RGBA) + { + color_format = EVAS_3D_COLOR_FORMAT_RGBA; + pixel_format = EVAS_3D_PIXEL_FORMAT_8888; + stride = w * 4; + } + else + { + ERR("Unsupported color format."); + goto done; + } + + row_pointers = (png_bytep *)malloc(sizeof(png_bytep) * h); + + if (row_pointers == NULL) + goto done; + + buffer = malloc(stride * h); + + if (buffer == NULL) + goto done; + + for (y = 0; y < h; y++) + row_pointers[y] = ((png_byte *)buffer) + (h - y - 1)* stride; + + png_read_image(png_ptr, row_pointers); + + e3d_texture_data_set(texture, color_format, pixel_format, w, h, buffer); + +done: + if (png_ptr) + png_destroy_read_struct(&png_ptr, info_ptr ? &info_ptr : NULL, NULL); + + if (fp) + fclose(fp); + + if (row_pointers) + free(row_pointers); + + if (buffer) + free(buffer); +} + +Evas_3D_Color_Format +e3d_texture_color_format_get(E3D_Texture *texture) +{ + if (texture->is_imported) + { + ERR("Cannot get the size of an imported texture."); + return EVAS_3D_COLOR_FORMAT_RGBA; + } + + switch (texture->format) + { + case GL_RGBA: + return EVAS_3D_COLOR_FORMAT_RGBA; + case GL_RGB: + return EVAS_3D_COLOR_FORMAT_RGB; + case GL_ALPHA: + return EVAS_3D_COLOR_FORMAT_ALPHA; + default: + break; + } + + ERR("Invalid texture format."); + return EVAS_3D_COLOR_FORMAT_RGBA; +} + +void +e3d_texture_size_get(const E3D_Texture *texture, int *w, int *h) +{ + if (texture->is_imported) + { + ERR("Invalid operation on an imported texture resource."); + return; + } + + if (w) *w = texture->w; + if (h) *h = texture->h; +} + +void +e3d_texture_import(E3D_Texture *texture, GLuint tex) +{ + if (tex == 0) + { + ERR("Cannot import an invalid texture ID."); + return; + } + + if (texture->tex && !texture->is_imported) + glDeleteTextures(1, &texture->tex); + + texture->tex = tex; + texture->is_imported = EINA_TRUE; +} + +Eina_Bool +e3d_texture_is_imported_get(const E3D_Texture *texture) +{ + return texture->is_imported; +} + +static inline GLenum +_to_gl_texture_wrap(Evas_3D_Wrap_Mode wrap) +{ + switch (wrap) + { + case EVAS_3D_WRAP_MODE_CLAMP: + return GL_CLAMP_TO_EDGE; + case EVAS_3D_WRAP_MODE_REFLECT: + return GL_MIRRORED_REPEAT; + case EVAS_3D_WRAP_MODE_REPEAT: + return GL_REPEAT; + default: + break; + } + + ERR("Invalid texture wrap mode."); + return GL_CLAMP_TO_EDGE; +} + +static inline Evas_3D_Wrap_Mode +_to_e3d_texture_wrap(GLenum wrap) +{ + switch (wrap) + { + case GL_CLAMP_TO_EDGE: + return EVAS_3D_WRAP_MODE_CLAMP; + case GL_MIRRORED_REPEAT: + return EVAS_3D_WRAP_MODE_REFLECT; + case GL_REPEAT: + return EVAS_3D_WRAP_MODE_REPEAT; + default: + break; + } + + ERR("Invalid texture wrap mode."); + return EVAS_3D_WRAP_MODE_CLAMP; +} + +static inline GLenum +_to_gl_texture_filter(Evas_3D_Texture_Filter filter) +{ + switch (filter) + { + case EVAS_3D_TEXTURE_FILTER_NEAREST: + return GL_NEAREST; + case EVAS_3D_TEXTURE_FILTER_LINEAR: + return GL_LINEAR; + case EVAS_3D_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST: + return GL_NEAREST_MIPMAP_NEAREST; + case EVAS_3D_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR: + return GL_NEAREST_MIPMAP_LINEAR; + case EVAS_3D_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST: + return GL_LINEAR_MIPMAP_NEAREST; + case EVAS_3D_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR: + return GL_LINEAR_MIPMAP_LINEAR; + default: + break; + } + + ERR("Invalid texture wrap mode."); + return GL_NEAREST; +} + +static inline Evas_3D_Texture_Filter +_to_e3d_texture_filter(GLenum filter) +{ + switch (filter) + { + case GL_NEAREST: + return EVAS_3D_TEXTURE_FILTER_NEAREST; + case GL_LINEAR: + return EVAS_3D_TEXTURE_FILTER_LINEAR; + case GL_NEAREST_MIPMAP_NEAREST: + return EVAS_3D_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST; + case GL_NEAREST_MIPMAP_LINEAR: + return EVAS_3D_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR; + case GL_LINEAR_MIPMAP_NEAREST: + return EVAS_3D_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST; + case GL_LINEAR_MIPMAP_LINEAR: + return EVAS_3D_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR; + default: + break; + } + + ERR("Invalid texture wrap mode."); + return EVAS_3D_TEXTURE_FILTER_NEAREST; +} + +void +e3d_texture_wrap_set(E3D_Texture *texture, Evas_3D_Wrap_Mode s, Evas_3D_Wrap_Mode t) +{ + GLenum gl_s, gl_t; + + if (texture->is_imported) + { + ERR("Invalid operation on an imported texture resource."); + return; + } + + gl_s = _to_gl_texture_wrap(s); + gl_t = _to_gl_texture_wrap(t); + + if (gl_s == texture->wrap_s && gl_t == texture->wrap_t) + return; + + texture->wrap_s = gl_s; + texture->wrap_t = gl_t; + texture->wrap_dirty = EINA_TRUE; +} + +void +e3d_texture_wrap_get(const E3D_Texture *texture, Evas_3D_Wrap_Mode *s, Evas_3D_Wrap_Mode *t) +{ + if (texture->is_imported) + { + ERR("Invalid operation on an imported texture resource."); + return; + } + + if (s) + *s = _to_e3d_texture_wrap(texture->wrap_s); + + if (t) + *t = _to_e3d_texture_wrap(texture->wrap_t); +} + +void +e3d_texture_filter_set(E3D_Texture *texture, Evas_3D_Texture_Filter min, Evas_3D_Texture_Filter mag) +{ + GLenum gl_min, gl_mag; + + if (texture->is_imported) + { + ERR("Invalid operation on an imported texture resource."); + return; + } + + gl_min = _to_gl_texture_filter(min); + gl_mag = _to_gl_texture_filter(mag); + + if (gl_min == texture->filter_min && gl_mag == texture->filter_mag) + return; + + texture->filter_min = gl_min; + texture->filter_mag = gl_mag; + texture->filter_dirty = EINA_TRUE; +} + +void +e3d_texture_filter_get(const E3D_Texture *texture, + Evas_3D_Texture_Filter *min, Evas_3D_Texture_Filter *mag) +{ + if (texture->is_imported) + { + ERR("Invalid operation on an imported texture resource."); + return; + } + + if (min) + *min = _to_e3d_texture_filter(texture->filter_min); + + if (mag) + *mag = _to_e3d_texture_filter(texture->filter_mag); +} + +E3D_Drawable * +e3d_drawable_new(int w, int h, int alpha, GLenum depth_format, GLenum stencil_format) +{ + E3D_Drawable *drawable; + GLuint tex, fbo; + GLuint depth_stencil_buf = 0; + GLuint depth_buf = 0; + GLuint stencil_buf = 0; + Eina_Bool depth_stencil = EINA_FALSE; + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + if (alpha) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); + +#if GL_GL_3DES + if (depth_format == GL_DEPTH_STENCIL_OES) + { + glGenTextures(1, &depth_stencil_buf); + glBindTexture(GL_TEXTURE_2D, depth_stencil_buf); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_STENCIL_OES, w, h, 0, + GL_DEPTH_STENCIL_OES, GL_UNSIGNED_INT_24_8_OES, NULL); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, depth_stencil_buf, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, depth_stencil_buf, 0); + + depth_stencil = EINA_TRUE; + } +#else + if (depth_format == GL_DEPTH24_STENCIL8) + { + glGenRenderbuffers(1, &depth_stencil_buf); + glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_buf); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, depth_stencil_buf); + + depth_stencil = EINA_TRUE; + } +#endif + + if ((!depth_stencil) && (depth_format)) + { + glGenRenderbuffers(1, &depth_buf); + glBindRenderbuffer(GL_RENDERBUFFER, depth_buf); + glRenderbufferStorage(GL_RENDERBUFFER, depth_format, w, h); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, depth_buf); + } + + if ((!depth_stencil) && (stencil_format)) + { + glGenRenderbuffers(1, &stencil_buf); + glBindRenderbuffer(GL_RENDERBUFFER, stencil_buf); + glRenderbufferStorage(GL_RENDERBUFFER, stencil_format, w, h); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, stencil_buf); + } + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + goto error; + + drawable = (E3D_Drawable *)calloc(1, sizeof(E3D_Drawable)); + + if (drawable == NULL) + goto error; + + drawable->w = w; + drawable->h = h; + drawable->alpha = alpha; + + if (alpha) + drawable->format = GL_RGBA; + else + drawable->format = GL_RGB; + + drawable->depth_format = depth_format; + drawable->stencil_format = stencil_format; + drawable->tex = tex; + drawable->fbo = fbo; + drawable->depth_stencil_buf = depth_stencil_buf; + drawable->depth_buf = depth_buf; + drawable->stencil_buf = stencil_buf; + + return drawable; + +error: + ERR("Drawable creation failed."); + + if (tex) + glDeleteTextures(1, &tex); + + if (fbo) + glDeleteFramebuffers(1, &fbo); + + if (depth_stencil_buf) + { +#ifdef GL_GLES + glDeleteTextures(1, &depth_stencil_buf); +#else + glDeleteRenderbuffers(1, &depth_stencil_buf); +#endif + } + + if (depth_buf) + glDeleteRenderbuffers(1, &depth_buf); + + if (stencil_buf) + glDeleteRenderbuffers(1, &stencil_buf); + + if (drawable) + free(drawable); + + return NULL; +} + +void +e3d_drawable_free(E3D_Drawable *drawable) +{ + if (drawable == NULL) + return; + + if (drawable->tex) + glDeleteTextures(1, &drawable->tex); + + if (drawable->fbo) + glDeleteFramebuffers(1, &drawable->fbo); + + if (drawable->depth_stencil_buf) + { +#ifdef GL_GLES + glDeleteTextures(1, &drawable->depth_stencil_buf); +#else + glDeleteRenderbuffers(1, &drawable->depth_stencil_buf); +#endif + } + + if (drawable->depth_buf) + glDeleteRenderbuffers(1, &drawable->depth_buf); + + if (drawable->stencil_buf) + glDeleteRenderbuffers(1, &drawable->stencil_buf); + + free(drawable); +} + +void +e3d_drawable_size_get(E3D_Drawable *drawable, int *w, int *h) +{ + if (drawable) + { + if (w) *w = drawable->w; + if (h) *h = drawable->h; + } + else + { + if (w) *w = 0; + if (h) *h = 0; + } +} + +GLuint +e3d_drawable_texture_id_get(E3D_Drawable *drawable) +{ + return drawable->tex; +} + +GLenum +e3d_drawable_format_get(E3D_Drawable *drawable) +{ + return drawable->format; +} + +static inline GLuint +_texture_id_get(Evas_3D_Texture *texture) +{ + E3D_Texture *tex = (E3D_Texture *)texture->engine_data; + + return tex->tex; +} + +static inline void +_mesh_frame_find(Evas_3D_Mesh *mesh, int frame, + Eina_List **l, Eina_List **r) +{ + Eina_List *left, *right; + Evas_3D_Mesh_Frame *f0, *f1; + + left = mesh->frames; + right = eina_list_next(left); + + while (right) + { + f0 = (Evas_3D_Mesh_Frame *)eina_list_data_get(left); + f1 = (Evas_3D_Mesh_Frame *)eina_list_data_get(right); + + if (frame >= f0->frame && frame <= f1->frame) + break; + + left = right; + right = eina_list_next(left); + } + + if (right == NULL) + { + if (frame <= f0->frame) + { + *l = NULL; + *r = left; + } + else + { + *l = left; + *r = NULL; + } + } + + *l = left; + *r = right; +} + +static inline void +_vertex_attrib_flag_add(E3D_Draw_Data *data, + Evas_3D_Vertex_Attrib attrib, + Eina_Bool blend) +{ + switch (attrib) + { + case EVAS_3D_VERTEX_POSITION: + data->flags |= E3D_SHADER_FLAG_VERTEX_POSITION; + + if (blend) + data->flags |= E3D_SHADER_FLAG_VERTEX_POSITION_BLEND; + break; + case EVAS_3D_VERTEX_NORMAL: + data->flags |= E3D_SHADER_FLAG_VERTEX_NORMAL; + + if (blend) + data->flags |= E3D_SHADER_FLAG_VERTEX_NORMAL_BLEND; + break; + case EVAS_3D_VERTEX_TANGENT: + data->flags |= E3D_SHADER_FLAG_VERTEX_TANGENT; + + if (blend) + data->flags |= E3D_SHADER_FLAG_VERTEX_TANGENT_BLEND; + break; + case EVAS_3D_VERTEX_COLOR: + data->flags |= E3D_SHADER_FLAG_VERTEX_COLOR; + + if (blend) + data->flags |= E3D_SHADER_FLAG_VERTEX_COLOR_BLEND; + break; + case EVAS_3D_VERTEX_TEXCOORD: + data->flags |= E3D_SHADER_FLAG_VERTEX_TEXCOORD; + + if (blend) + data->flags |= E3D_SHADER_FLAG_VERTEX_TEXCOORD_BLEND; + break; + default: + ERR("Invalid vertex attrib."); + break; + } +} + +static inline void +_material_color_flag_add(E3D_Draw_Data *data, Evas_3D_Material_Attrib attrib) +{ + switch (attrib) + { + case EVAS_3D_MATERIAL_AMBIENT: + data->flags |= E3D_SHADER_FLAG_AMBIENT; + break; + case EVAS_3D_MATERIAL_DIFFUSE: + data->flags |= E3D_SHADER_FLAG_DIFFUSE; + break; + case EVAS_3D_MATERIAL_SPECULAR: + data->flags |= E3D_SHADER_FLAG_SPECULAR; + break; + case EVAS_3D_MATERIAL_EMISSION: + data->flags |= E3D_SHADER_FLAG_EMISSION; + break; + case EVAS_3D_MATERIAL_NORMAL: + ERR("Material attribute normal should not be used with color values."); + break; + default: + ERR("Invalid material attrib."); + break; + } +} + +static inline void +_material_texture_flag_add(E3D_Draw_Data *data, Evas_3D_Material_Attrib attrib, Eina_Bool blend) +{ + switch (attrib) + { + case EVAS_3D_MATERIAL_AMBIENT: + data->flags |= E3D_SHADER_FLAG_AMBIENT; + data->flags |= E3D_SHADER_FLAG_AMBIENT_TEXTURE; + + if (blend) + data->flags |= E3D_SHADER_FLAG_AMBIENT_TEXTURE_BLEND; + break; + case EVAS_3D_MATERIAL_DIFFUSE: + data->flags |= E3D_SHADER_FLAG_DIFFUSE; + data->flags |= E3D_SHADER_FLAG_DIFFUSE_TEXTURE; + + if (blend) + data->flags |= E3D_SHADER_FLAG_DIFFUSE_TEXTURE_BLEND; + break; + case EVAS_3D_MATERIAL_SPECULAR: + data->flags |= E3D_SHADER_FLAG_SPECULAR; + data->flags |= E3D_SHADER_FLAG_SPECULAR_TEXTURE; + + if (blend) + data->flags |= E3D_SHADER_FLAG_SPECULAR_TEXTURE_BLEND; + break; + case EVAS_3D_MATERIAL_EMISSION: + data->flags |= E3D_SHADER_FLAG_EMISSION; + data->flags |= E3D_SHADER_FLAG_EMISSION_TEXTURE; + + if (blend) + data->flags |= E3D_SHADER_FLAG_EMISSION_TEXTURE_BLEND; + break; + case EVAS_3D_MATERIAL_NORMAL: + data->flags |= E3D_SHADER_FLAG_NORMAL_TEXTURE; + + if (blend) + data->flags |= E3D_SHADER_FLAG_NORMAL_TEXTURE_BLEND; + break; + default: + ERR("Invalid material attrib."); + break; + } +} + +static inline Eina_Bool +_vertex_attrib_build(E3D_Draw_Data *data, int frame, + const Eina_List *l, const Eina_List *r, + Evas_3D_Vertex_Attrib attrib) +{ + const Evas_3D_Mesh_Frame *f0 = NULL, *f1 = NULL; + + while (l) + { + f0 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(l); + + if (f0->vertices[attrib].data != NULL) + break; + + l = eina_list_prev(l); + f0 = NULL; + } + + while (r) + { + f1 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(r); + + if (f1->vertices[attrib].data != NULL) + break; + + r = eina_list_next(r); + f1 = NULL; + } + + if (f0 == NULL && f1 == NULL) + return EINA_FALSE; + + if (f0 == NULL) + { + f0 = f1; + f1 = NULL; + } + else if (f1 != NULL) + { + if (frame == f0->frame) + { + f1 = NULL; + } + else if (frame == f1->frame) + { + f0 = f1; + f1 = NULL; + } + } + + data->vertices[attrib].vertex0 = f0->vertices[attrib]; + data->vertices[attrib].vertex0.owns_data = EINA_FALSE; + + if (f1) + { + data->vertices[attrib].vertex1 = f1->vertices[attrib]; + data->vertices[attrib].vertex1.owns_data = EINA_FALSE; + data->vertices[attrib].weight = (f1->frame - frame) / (Evas_Real)(f1->frame - f0->frame); + _vertex_attrib_flag_add(data, attrib, EINA_TRUE); + } + else + { + _vertex_attrib_flag_add(data, attrib, EINA_FALSE); + } + + return EINA_TRUE; +} + +static inline Eina_Bool +_material_color_build(E3D_Draw_Data *data, int frame, + const Eina_List *l, const Eina_List *r, + Evas_3D_Material_Attrib attrib) +{ + const Evas_3D_Mesh_Frame *f0 = NULL, *f1 = NULL; + + while (l) + { + f0 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(l); + + if (f0->material && f0->material->attribs[attrib].enable) + break; + + l = eina_list_prev(l); + f0 = NULL; + } + + while (r) + { + f1 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(r); + + if (f1->material && f1->material->attribs[attrib].enable) + break; + + r = eina_list_next(r); + f1 = NULL; + } + + if (f0 == NULL && f1 == NULL) + return EINA_FALSE; + + if (f0 == NULL) + { + f0 = f1; + f1 = NULL; + } + else if (f1 != NULL) + { + if (frame == f0->frame) + { + f1 = NULL; + } + else if (frame == f1->frame) + { + f0 = f1; + f1 = NULL; + } + } + + if (f1 == NULL) + { + data->materials[attrib].color = f0->material->attribs[attrib].color; + + if (attrib == EVAS_3D_MATERIAL_SPECULAR) + data->shininess = f0->material->shininess; + } + else + { + Evas_Real weight; + + weight = (f1->frame - frame) / (Evas_Real)(f1->frame - f0->frame); + evas_color_blend(&data->materials[attrib].color, + &f0->material->attribs[attrib].color, + &f1->material->attribs[attrib].color, + weight); + + if (attrib == EVAS_3D_MATERIAL_SPECULAR) + { + data->shininess = f0->material->shininess * weight + + f1->material->shininess * (1.0 - weight); + } + } + + _material_color_flag_add(data, attrib); + return EINA_TRUE; +} + +static inline Eina_Bool +_material_texture_build(E3D_Draw_Data *data, int frame, + const Eina_List *l, const Eina_List *r, + Evas_3D_Material_Attrib attrib) +{ + const Evas_3D_Mesh_Frame *f0 = NULL, *f1 = NULL; + + while (l) + { + f0 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(l); + + if (f0->material && + f0->material->attribs[attrib].enable && + f0->material->attribs[attrib].texture != NULL) + break; + + l = eina_list_prev(l); + f0 = NULL; + } + + while (r) + { + f1 = (const Evas_3D_Mesh_Frame *)eina_list_data_get(r); + + if (f1->material && + f1->material->attribs[attrib].enable && + f1->material->attribs[attrib].texture != NULL) + break; + + r = eina_list_next(r); + f1 = NULL; + } + + if (f0 == NULL && f1 == NULL) + return EINA_FALSE; + + if (f0 == NULL) + { + f0 = f1; + f1 = NULL; + } + else if (f1 != NULL) + { + if (frame == f0->frame) + { + f1 = NULL; + } + else if (frame == f1->frame) + { + f0 = f1; + f1 = NULL; + } + } + + data->materials[attrib].sampler0 = data->texture_count++; + data->materials[attrib].tex0 = + (E3D_Texture *)f0->material->attribs[attrib].texture->engine_data; + + if (f1) + { + Evas_Real weight = (f1->frame - frame) / (Evas_Real)(f1->frame - f0->frame); + + data->materials[attrib].sampler1 = data->texture_count++; + data->materials[attrib].tex1 = + (E3D_Texture *)f1->material->attribs[attrib].texture->engine_data; + + data->materials[attrib].texture_weight = weight; + + if (attrib == EVAS_3D_MATERIAL_SPECULAR) + { + data->shininess = f0->material->shininess * weight + + f1->material->shininess * (1.0 - weight); + } + + _material_texture_flag_add(data, attrib, EINA_TRUE); + } + else + { + if (attrib == EVAS_3D_MATERIAL_SPECULAR) + data->shininess = f0->material->shininess; + + _material_texture_flag_add(data, attrib, EINA_FALSE); + } + + return EINA_TRUE; +} + +static inline void +_light_build(E3D_Draw_Data *data, + const Evas_3D_Node *light, + const Evas_Mat4 *matrix_eye) +{ + Evas_3D_Light *l = light->data.light.light; + Evas_Vec3 pos, dir; + + if (l == NULL) + return; + + /* Get light node's position. */ + if (l->directional) + { + data->flags |= E3D_SHADER_FLAG_LIGHT_DIRECTIONAL; + + /* Negative Z. */ + evas_vec3_set(&dir, 0.0, 0.0, 1.0); + evas_vec3_quaternion_rotate(&dir, &dir, &light->orientation); + + /* Transform to eye space. */ + evas_vec3_homogeneous_direction_transform(&dir, &dir, matrix_eye); + evas_vec3_normalize(&dir, &dir); + + data->light.position.x = dir.x; + data->light.position.y = dir.y; + data->light.position.z = dir.z; + data->light.position.w = 0.0; + } + else + { + evas_vec3_copy(&pos, &light->position_world); + evas_vec3_homogeneous_position_transform(&pos, &pos, matrix_eye); + + data->light.position.x = pos.x; + data->light.position.y = pos.y; + data->light.position.z = pos.z; + data->light.position.w = 1.0; + + if (l->enable_attenuation) + { + data->flags |= E3D_SHADER_FLAG_LIGHT_ATTENUATION; + + data->light.atten.x = l->atten_const; + data->light.atten.x = l->atten_linear; + data->light.atten.x = l->atten_quad; + } + + if (l->spot_cutoff < 180.0) + { + data->flags |= E3D_SHADER_FLAG_LIGHT_SPOT; + evas_vec3_set(&dir, 0.0, 0.0, -1.0); + evas_vec3_quaternion_rotate(&dir, &dir, &light->orientation); + evas_vec3_homogeneous_direction_transform(&dir, &dir, matrix_eye); + + data->light.spot_dir = dir; + data->light.spot_exp = l->spot_exp; + data->light.spot_cutoff_cos = l->spot_cutoff_cos; + } + } + + data->light.ambient = l->ambient; + data->light.diffuse = l->diffuse; + data->light.specular = l->specular; +} + +static inline Eina_Bool +_mesh_draw_data_build(E3D_Draw_Data *data, + Evas_3D_Mesh *mesh, int frame, + const Evas_Mat4 *matrix_eye, + const Evas_Mat4 *matrix_mv, + const Evas_Mat4 *matrix_mvp, + const Evas_3D_Node *light) +{ + Eina_List *l, *r; + + if (mesh->frames == NULL) + return EINA_FALSE; + + data->mode = mesh->shade_mode; + data->assembly = mesh->assembly; + data->vertex_count = mesh->vertex_count; + data->index_count = mesh->index_count; + data->index_format = mesh->index_format; + data->indices = mesh->indices; + + evas_mat4_copy(&data->matrix_mvp, matrix_mvp); + evas_mat4_copy(&data->matrix_mv, matrix_mv); + + _mesh_frame_find(mesh, frame, &l, &r); + +#define BUILD(type, arg, check) \ + do { \ + Eina_Bool ret = _##type##_build(data, frame, l, r, EVAS_3D_##arg); \ + if (check && !ret) \ + { \ + ERR("Missing attribute : " #arg); \ + return EINA_FALSE; \ + } \ + } while (0) + + if (mesh->shade_mode == EVAS_3D_SHADE_MODE_VERTEX_COLOR) + { + BUILD(vertex_attrib, VERTEX_POSITION, EINA_TRUE); + BUILD(vertex_attrib, VERTEX_COLOR, EINA_TRUE); + } + else if (mesh->shade_mode == EVAS_3D_SHADE_MODE_DIFFUSE) + { + BUILD(vertex_attrib, VERTEX_POSITION, EINA_TRUE); + BUILD(material_color, MATERIAL_DIFFUSE, EINA_TRUE); + BUILD(material_texture, MATERIAL_DIFFUSE, EINA_FALSE); + + if (_flags_need_tex_coord(data->flags)) + BUILD(vertex_attrib, VERTEX_TEXCOORD, EINA_FALSE); + } + else if (mesh->shade_mode == EVAS_3D_SHADE_MODE_FLAT) + { + BUILD(vertex_attrib, VERTEX_POSITION, EINA_TRUE); + BUILD(vertex_attrib, VERTEX_NORMAL, EINA_TRUE); + + BUILD(material_color, MATERIAL_AMBIENT, EINA_FALSE); + BUILD(material_color, MATERIAL_DIFFUSE, EINA_FALSE); + BUILD(material_color, MATERIAL_SPECULAR, EINA_FALSE); + BUILD(material_color, MATERIAL_EMISSION, EINA_FALSE); + + BUILD(material_texture, MATERIAL_AMBIENT, EINA_FALSE); + BUILD(material_texture, MATERIAL_DIFFUSE, EINA_FALSE); + BUILD(material_texture, MATERIAL_SPECULAR, EINA_FALSE); + BUILD(material_texture, MATERIAL_EMISSION, EINA_FALSE); + + _light_build(data, light, matrix_eye); + evas_normal_matrix_get(&data->matrix_normal, matrix_mv); + + if (_flags_need_tex_coord(data->flags)) + BUILD(vertex_attrib, VERTEX_TEXCOORD, EINA_FALSE); + } + else if (mesh->shade_mode == EVAS_3D_SHADE_MODE_PHONG) + { + BUILD(vertex_attrib, VERTEX_POSITION, EINA_TRUE); + BUILD(vertex_attrib, VERTEX_NORMAL, EINA_TRUE); + + BUILD(material_color, MATERIAL_AMBIENT, EINA_FALSE); + BUILD(material_color, MATERIAL_DIFFUSE, EINA_FALSE); + BUILD(material_color, MATERIAL_SPECULAR, EINA_FALSE); + BUILD(material_color, MATERIAL_EMISSION, EINA_FALSE); + + BUILD(material_texture, MATERIAL_AMBIENT, EINA_FALSE); + BUILD(material_texture, MATERIAL_DIFFUSE, EINA_FALSE); + BUILD(material_texture, MATERIAL_SPECULAR, EINA_FALSE); + BUILD(material_texture, MATERIAL_EMISSION, EINA_FALSE); + + _light_build(data, light, matrix_eye); + evas_normal_matrix_get(&data->matrix_normal, matrix_mv); + + if (_flags_need_tex_coord(data->flags)) + BUILD(vertex_attrib, VERTEX_TEXCOORD, EINA_FALSE); + } + else if (mesh->shade_mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + { + BUILD(vertex_attrib, VERTEX_POSITION, EINA_TRUE); + BUILD(vertex_attrib, VERTEX_NORMAL, EINA_TRUE); + BUILD(vertex_attrib, VERTEX_TEXCOORD, EINA_TRUE); + BUILD(material_texture, MATERIAL_NORMAL, EINA_TRUE); + BUILD(vertex_attrib, VERTEX_TANGENT, EINA_FALSE); + + BUILD(material_color, MATERIAL_AMBIENT, EINA_FALSE); + BUILD(material_color, MATERIAL_DIFFUSE, EINA_FALSE); + BUILD(material_color, MATERIAL_SPECULAR, EINA_FALSE); + BUILD(material_color, MATERIAL_EMISSION, EINA_FALSE); + + BUILD(material_texture, MATERIAL_AMBIENT, EINA_FALSE); + BUILD(material_texture, MATERIAL_DIFFUSE, EINA_FALSE); + BUILD(material_texture, MATERIAL_SPECULAR, EINA_FALSE); + BUILD(material_texture, MATERIAL_EMISSION, EINA_FALSE); + + _light_build(data, light, matrix_eye); + evas_normal_matrix_get(&data->matrix_normal, matrix_mv); + } + + return EINA_TRUE; +} + +static inline void +_mesh_draw(E3D_Renderer *renderer, Evas_3D_Mesh *mesh, int frame, Evas_3D_Node *light, + const Evas_Mat4 *matrix_eye, const Evas_Mat4 *matrix_mv, const Evas_Mat4 *matrix_mvp) +{ + E3D_Draw_Data data; + + memset(&data, 0x00, sizeof(E3D_Draw_Data)); + + if (_mesh_draw_data_build(&data, mesh, frame, matrix_eye, matrix_mv, matrix_mvp, light)) + e3d_renderer_draw(renderer, &data); +} + +void +e3d_drawable_scene_render(E3D_Drawable *drawable, E3D_Renderer *renderer, Evas_3D_Scene_Data *data) +{ + Eina_List *l; + Evas_3D_Node *n; + const Evas_Mat4 *matrix_eye; + Evas_3D_Node *light; + + /* Set up render target. */ + e3d_renderer_target_set(renderer, drawable); + e3d_renderer_clear(renderer, &data->bg_color); + + /* Get eye matrix. */ + matrix_eye = &data->camera_node->data.camera.matrix_world_to_eye; + + EINA_LIST_FOREACH(data->mesh_nodes, l, n) + { + Evas_Mat4 matrix_mv; + Evas_Mat4 matrix_mvp; + Eina_Iterator *it; + void *ptr; + + evas_mat4_multiply(&matrix_mv, matrix_eye, &n->data.mesh.matrix_local_to_world); + evas_mat4_multiply(&matrix_mvp, &data->camera_node->data.camera.camera->projection, + &matrix_mv); + + /* TODO: Get most effective light node. */ + light = eina_list_data_get(data->light_nodes); + + it = eina_hash_iterator_data_new(n->data.mesh.node_meshes); + + while (eina_iterator_next(it, &ptr)) + { + Evas_3D_Node_Mesh *nm = (Evas_3D_Node_Mesh *)ptr; + _mesh_draw(renderer, nm->mesh, nm->frame, light, matrix_eye, &matrix_mv, &matrix_mvp); + } + + eina_iterator_free(it); + } + + e3d_renderer_flush(renderer); +} diff --git a/src/modules/evas/engines/gl_common/evas_gl_3d_common.h b/src/modules/evas/engines/gl_common/evas_gl_3d_common.h new file mode 100644 index 0000000..3f93ad5 --- /dev/null +++ b/src/modules/evas/engines/gl_common/evas_gl_3d_common.h @@ -0,0 +1,37 @@ +#ifndef EVAS_GL_COMMON_H +# error You shall not include this header directly +#endif + +typedef struct _E3D_Texture E3D_Texture; +typedef struct _E3D_Drawable E3D_Drawable; +typedef struct _E3D_Renderer E3D_Renderer; + +/* Texture */ +E3D_Texture *e3d_texture_new(void); +void e3d_texture_free(E3D_Texture *texture); + +void e3d_texture_data_set(E3D_Texture *texture, Evas_3D_Color_Format format, Evas_3D_Pixel_Format pixel_format, int w, int h, const void *data); +void e3d_texture_file_set(E3D_Texture *texture, const char *file, const char *key); +Evas_3D_Color_Format e3d_texture_color_format_get(E3D_Texture *texture); +void e3d_texture_size_get(const E3D_Texture *texture, int *w, int *h); + +void e3d_texture_import(E3D_Texture *texture, GLuint tex); +Eina_Bool e3d_texture_is_imported_get(const E3D_Texture *texture); + +void e3d_texture_wrap_set(E3D_Texture *texture, Evas_3D_Wrap_Mode s, Evas_3D_Wrap_Mode t); +void e3d_texture_wrap_get(const E3D_Texture *texture, Evas_3D_Wrap_Mode *s, Evas_3D_Wrap_Mode *t); + +void e3d_texture_filter_set(E3D_Texture *texture, Evas_3D_Texture_Filter min, Evas_3D_Texture_Filter mag); +void e3d_texture_filter_get(const E3D_Texture *texture, Evas_3D_Texture_Filter *min, Evas_3D_Texture_Filter *mag); + +/* Drawable */ +E3D_Drawable *e3d_drawable_new(int w, int h, int alpha, GLenum depth_format, GLenum stencil_format); +void e3d_drawable_free(E3D_Drawable *drawable); +void e3d_drawable_scene_render(E3D_Drawable *drawable, E3D_Renderer *renderer, Evas_3D_Scene_Data *data); +void e3d_drawable_size_get(E3D_Drawable *drawable, int *w, int *h); +GLuint e3d_drawable_texture_id_get(E3D_Drawable *drawable); +GLenum e3d_drawable_format_get(E3D_Drawable *drawable); + +/* Renderer */ +E3D_Renderer *e3d_renderer_new(void); +void e3d_renderer_free(E3D_Renderer *renderer); diff --git a/src/modules/evas/engines/gl_common/evas_gl_3d_private.h b/src/modules/evas/engines/gl_common/evas_gl_3d_private.h new file mode 100644 index 0000000..2eb5bf4 --- /dev/null +++ b/src/modules/evas/engines/gl_common/evas_gl_3d_private.h @@ -0,0 +1,145 @@ +#ifndef EVAS_GL_3D_PRIVATE_H +#define EVAS_GL_3D_PRIVATE_H + +#include "evas_gl_common.h" + +typedef struct _E3D_Program E3D_Program; +typedef struct _E3D_Draw_Data E3D_Draw_Data; +typedef unsigned long E3D_Shader_Flag; + +#define E3D_SHADER_FLAG_NORMALIZE_NORMALS (1 << 0) +#define E3D_SHADER_FLAG_VERTEX_POSITION (1 << 1) +#define E3D_SHADER_FLAG_VERTEX_POSITION_BLEND (1 << 2) +#define E3D_SHADER_FLAG_VERTEX_NORMAL (1 << 3) +#define E3D_SHADER_FLAG_VERTEX_NORMAL_BLEND (1 << 4) +#define E3D_SHADER_FLAG_VERTEX_TANGENT (1 << 5) +#define E3D_SHADER_FLAG_VERTEX_TANGENT_BLEND (1 << 6) +#define E3D_SHADER_FLAG_VERTEX_COLOR (1 << 7) +#define E3D_SHADER_FLAG_VERTEX_COLOR_BLEND (1 << 8) +#define E3D_SHADER_FLAG_VERTEX_TEXCOORD (1 << 9) +#define E3D_SHADER_FLAG_VERTEX_TEXCOORD_BLEND (1 << 10) +#define E3D_SHADER_FLAG_LIGHT_DIRECTIONAL (1 << 11) +#define E3D_SHADER_FLAG_LIGHT_SPOT (1 << 12) +#define E3D_SHADER_FLAG_LIGHT_ATTENUATION (1 << 13) +#define E3D_SHADER_FLAG_AMBIENT (1 << 14) +#define E3D_SHADER_FLAG_DIFFUSE (1 << 15) +#define E3D_SHADER_FLAG_SPECULAR (1 << 16) +#define E3D_SHADER_FLAG_EMISSION (1 << 17) +#define E3D_SHADER_FLAG_DIFFUSE_TEXTURE (1 << 19) +#define E3D_SHADER_FLAG_AMBIENT_TEXTURE (1 << 20) +#define E3D_SHADER_FLAG_SPECULAR_TEXTURE (1 << 21) +#define E3D_SHADER_FLAG_EMISSION_TEXTURE (1 << 22) +#define E3D_SHADER_FLAG_NORMAL_TEXTURE (1 << 23) +#define E3D_SHADER_FLAG_DIFFUSE_TEXTURE_BLEND (1 << 24) +#define E3D_SHADER_FLAG_AMBIENT_TEXTURE_BLEND (1 << 25) +#define E3D_SHADER_FLAG_SPECULAR_TEXTURE_BLEND (1 << 26) +#define E3D_SHADER_FLAG_EMISSION_TEXTURE_BLEND (1 << 27) +#define E3D_SHADER_FLAG_NORMAL_TEXTURE_BLEND (1 << 28) + +static inline Eina_Bool +_flags_need_tex_coord(E3D_Shader_Flag flags) +{ + return (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE) || + (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE) || + (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE) || + (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE) || + (flags & E3D_SHADER_FLAG_NORMAL_TEXTURE); +} + +struct _E3D_Draw_Data +{ + E3D_Shader_Flag flags; + Evas_3D_Shade_Mode mode; + + Evas_Mat4 matrix_mvp; + Evas_Mat4 matrix_mv; + Evas_Mat3 matrix_normal; + + struct { + Evas_3D_Vertex_Buffer vertex0; + Evas_3D_Vertex_Buffer vertex1; + Evas_Real weight; + } vertices[EVAS_3D_VERTEX_ATTRIB_COUNT]; + + Evas_3D_Vertex_Assembly assembly; + int vertex_count; + int index_count; + Evas_3D_Index_Format index_format; + const void *indices; + + GLint texture_count; + + struct { + Evas_Color color; + GLint sampler0; + GLint sampler1; + E3D_Texture *tex0; + E3D_Texture *tex1; + Evas_Real texture_weight; + } materials[EVAS_3D_MATERIAL_ATTRIB_COUNT]; + + Evas_Real shininess; + + struct { + Evas_Vec4 position; + Evas_Vec3 spot_dir; + Evas_Real spot_exp; + Evas_Real spot_cutoff_cos; + Evas_Vec3 atten; + Evas_Color ambient; + Evas_Color diffuse; + Evas_Color specular; + } light; +}; + +struct _E3D_Texture +{ + int w, h; + + Eina_Bool is_imported; + GLuint tex; + GLenum format; + + Eina_Bool wrap_dirty; + GLenum wrap_s; + GLenum wrap_t; + + Eina_Bool filter_dirty; + GLenum filter_min; + GLenum filter_mag; +}; + +struct _E3D_Drawable +{ + int w, h; + int alpha; + GLenum format; + GLenum depth_format; + GLenum stencil_format; + + GLuint tex; + GLuint fbo; + GLuint depth_stencil_buf; + GLuint depth_buf; + GLuint stencil_buf; +}; + +/* Texture internal functions. */ +void e3d_texture_param_update(E3D_Texture *texture); + +/* Program */ +E3D_Program *e3d_program_new(Evas_3D_Shade_Mode mode, E3D_Shader_Flag flags); +void e3d_program_free(E3D_Program *program); +GLuint e3d_program_id_get(const E3D_Program *program); +Evas_3D_Shade_Mode e3d_program_shade_mode_get(const E3D_Program *program); +E3D_Shader_Flag e3d_program_shader_flags_get(const E3D_Program *program); +void e3d_program_uniform_upload(E3D_Program *program, const E3D_Draw_Data *data); + +/* Renderer */ +void e3d_renderer_target_set(E3D_Renderer *renderer, E3D_Drawable *target); +void e3d_renderer_viewport_set(E3D_Renderer *renderer, int x, int y, int w, int h); +void e3d_renderer_clear(E3D_Renderer *renderer, const Evas_Color *color); +void e3d_renderer_draw(E3D_Renderer *renderer, E3D_Draw_Data *data); +void e3d_renderer_flush(E3D_Renderer *renderer); + +#endif /* EVAS_GL_3D_PRIVATE_H */ diff --git a/src/modules/evas/engines/gl_common/evas_gl_3d_renderer.c b/src/modules/evas/engines/gl_common/evas_gl_3d_renderer.c new file mode 100644 index 0000000..0f22655 --- /dev/null +++ b/src/modules/evas/engines/gl_common/evas_gl_3d_renderer.c @@ -0,0 +1,284 @@ +#include "evas_gl_private.h" +#include "evas_gl_3d_private.h" + +#define E3D_MAX_TEXTURE_COUNT 8 +#define E3D_MAX_VERTEX_ATTRIB_COUNT 8 + +struct _E3D_Renderer +{ + Eina_List *programs; + + GLuint fbo; + GLuint program; + E3D_Texture *textures[E3D_MAX_TEXTURE_COUNT]; + + Eina_Bool vertex_attrib_enable[E3D_MAX_VERTEX_ATTRIB_COUNT]; + Eina_Bool depth_test_enable; +}; + +static inline GLenum +_gl_assembly_get(Evas_3D_Vertex_Assembly assembly) +{ + switch (assembly) + { + case EVAS_3D_VERTEX_ASSEMBLY_POINTS: + return GL_POINTS; + case EVAS_3D_VERTEX_ASSEMBLY_LINES: + return GL_LINES; + case EVAS_3D_VERTEX_ASSEMBLY_LINE_STRIP: + return GL_LINE_STRIP; + case EVAS_3D_VERTEX_ASSEMBLY_LINE_LOOP: + return GL_LINE_LOOP; + case EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES: + return GL_TRIANGLES; + case EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_STRIP: + return GL_TRIANGLE_STRIP; + case EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_FAN: + return GL_TRIANGLE_FAN; + default: + return GL_NONE; + } +} + +static inline void +_renderer_vertex_attrib_array_enable(E3D_Renderer *renderer, int index) +{ + if (renderer->vertex_attrib_enable[index]) + return; + + glEnableVertexAttribArray(index); + renderer->vertex_attrib_enable[index] = EINA_TRUE; +} + +static inline void +_renderer_vertex_attrib_array_disable(E3D_Renderer *renderer, int index) +{ + if (!renderer->vertex_attrib_enable[index]) + return; + + glDisableVertexAttribArray(index); + renderer->vertex_attrib_enable[index] = EINA_FALSE; +} + +static inline void +_renderer_vertex_attrib_pointer_set(E3D_Renderer *renderer EINA_UNUSED, int index, + const Evas_3D_Vertex_Buffer *buffer) +{ + glVertexAttribPointer(index, buffer->element_count, GL_FLOAT, + GL_FALSE, buffer->stride, buffer->data); +} + +static inline void +_renderer_elements_draw(E3D_Renderer *renderer EINA_UNUSED, Evas_3D_Vertex_Assembly assembly, + int count, Evas_3D_Index_Format format, const void *indices) +{ + GLenum mode = _gl_assembly_get(assembly); + + if (format == EVAS_3D_INDEX_FORMAT_UNSIGNED_BYTE) + glDrawElements(mode, count, GL_UNSIGNED_BYTE, indices); + else if (format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT) + glDrawElements(mode, count, GL_UNSIGNED_SHORT, indices); +} + +static inline void +_renderer_array_draw(E3D_Renderer *renderer EINA_UNUSED, + Evas_3D_Vertex_Assembly assembly, int count) +{ + GLenum mode = _gl_assembly_get(assembly); + + glDrawArrays(mode, 0, count); +} + +static inline void +_renderer_program_use(E3D_Renderer *renderer ,E3D_Program *program) +{ + GLuint prog = e3d_program_id_get(program); + + if (renderer->program != prog) + { + glUseProgram(prog); + renderer->program = prog; + } +} + +static inline void +_renderer_texture_bind(E3D_Renderer *renderer, E3D_Draw_Data *data) +{ + int i; + + for (i = 0; i < EVAS_3D_MATERIAL_ATTRIB_COUNT; i++) + { + if (data->materials[i].tex0) + { + if (renderer->textures[data->materials[i].sampler0] != data->materials[i].tex0) + { + glActiveTexture(GL_TEXTURE0 + data->materials[i].sampler0); + glBindTexture(GL_TEXTURE_2D, data->materials[i].tex0->tex); + e3d_texture_param_update(data->materials[i].tex0); + + renderer->textures[data->materials[i].sampler0] = data->materials[i].tex0; + } + } + + if (data->materials[i].tex1) + { + if (renderer->textures[data->materials[i].sampler1] != data->materials[i].tex1) + { + glActiveTexture(GL_TEXTURE0 + data->materials[i].sampler1); + glBindTexture(GL_TEXTURE_2D, data->materials[i].tex1->tex); + e3d_texture_param_update(data->materials[i].tex1); + + renderer->textures[data->materials[i].sampler1] = data->materials[i].tex1; + } + } + } +} + +static inline void +_renderer_depth_test_enable(E3D_Renderer *renderer, Eina_Bool enable) +{ + if (renderer->depth_test_enable != enable) + { + if (enable) + { + glEnable(GL_DEPTH_TEST); + /* Use default depth func. */ + } + else + { + glDisable(GL_DEPTH_TEST); + } + + renderer->depth_test_enable = enable; + } +} + +E3D_Renderer * +e3d_renderer_new(void) +{ + E3D_Renderer *renderer = NULL; + + renderer = (E3D_Renderer *)calloc(1, sizeof(E3D_Renderer)); + + if (renderer == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + return renderer; +} + +void +e3d_renderer_free(E3D_Renderer *renderer) +{ + Eina_List *l; + E3D_Program *p; + + EINA_LIST_FOREACH(renderer->programs, l, p) + { + e3d_program_free(p); + } + + eina_list_free(renderer->programs); +} + +void +e3d_renderer_target_set(E3D_Renderer *renderer, E3D_Drawable *target) +{ + if (renderer->fbo == target->fbo) + return; + + glBindFramebuffer(GL_FRAMEBUFFER, target->fbo); + glViewport(0, 0, target->w, target->h); + renderer->fbo = target->fbo; +} + +void +e3d_renderer_clear(E3D_Renderer *renderer EINA_UNUSED, const Evas_Color *color) +{ + glClearColor(color->r, color->g, color->b, color->a); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void +e3d_renderer_draw(E3D_Renderer *renderer, E3D_Draw_Data *data) +{ + Eina_List *l; + E3D_Program *program = NULL, *p; + int i, index = 0; + + _renderer_depth_test_enable(renderer, EINA_TRUE); + + EINA_LIST_FOREACH(renderer->programs, l, p) + { + if (e3d_program_shade_mode_get(p) == data->mode && + e3d_program_shader_flags_get(p) == data->flags) + { + program = p; + break; + } + } + + if (program == NULL) + { + program = e3d_program_new(data->mode, data->flags); + + if (program == NULL) + { + ERR("Failed to create shader program."); + return; + } + + renderer->programs = eina_list_append(renderer->programs, program); + } + + _renderer_program_use(renderer, program); + e3d_program_uniform_upload(program, data); + _renderer_texture_bind(renderer, data); + + /* Set up vertex attrib pointers. */ + index = 0; + + for (i = 0; i < EVAS_3D_VERTEX_ATTRIB_COUNT; i++) + { + const Evas_3D_Vertex_Buffer *buffer; + + buffer = &data->vertices[i].vertex0; + + if (buffer->data != NULL) + { + _renderer_vertex_attrib_array_enable(renderer, index); + _renderer_vertex_attrib_pointer_set(renderer, index, buffer); + index++; + } + + buffer = &data->vertices[i].vertex1; + + if (buffer->data != NULL) + { + _renderer_vertex_attrib_array_enable(renderer, index); + _renderer_vertex_attrib_pointer_set(renderer, index, buffer); + index++; + } + } + + while (index < E3D_MAX_VERTEX_ATTRIB_COUNT) + _renderer_vertex_attrib_array_disable(renderer, index++); + + if (data->indices) + { + _renderer_elements_draw(renderer, data->assembly, data->index_count, + data->index_format, data->indices); + } + else + { + _renderer_array_draw(renderer, data->assembly, data->vertex_count); + } +} + +void +e3d_renderer_flush(E3D_Renderer *renderer EINA_UNUSED) +{ + glFlush(); +} diff --git a/src/modules/evas/engines/gl_common/evas_gl_3d_renderer.h b/src/modules/evas/engines/gl_common/evas_gl_3d_renderer.h new file mode 100644 index 0000000..b06eb8b --- /dev/null +++ b/src/modules/evas/engines/gl_common/evas_gl_3d_renderer.h @@ -0,0 +1,6 @@ +#ifndef EVAS_GL_3D_RENDERER_H +#define EVAS_GL_3D_RENDERER_H + + + +#endif /* EVAS_GL_3D_RENDERER_H */ diff --git a/src/modules/evas/engines/gl_common/evas_gl_3d_shader.c b/src/modules/evas/engines/gl_common/evas_gl_3d_shader.c new file mode 100644 index 0000000..a9f63ec --- /dev/null +++ b/src/modules/evas/engines/gl_common/evas_gl_3d_shader.c @@ -0,0 +1,1482 @@ +#include "evas_gl_3d_private.h" + +typedef enum _E3D_Uniform +{ + E3D_UNIFORM_MATRIX_MVP, + E3D_UNIFORM_MATRIX_MV, + E3D_UNIFORM_MATRIX_NORMAL, + + E3D_UNIFORM_POSITION_WEIGHT, + E3D_UNIFORM_NORMAL_WEIGHT, + E3D_UNIFORM_TANGENT_WEIGHT, + E3D_UNIFORM_COLOR_WEIGHT, + E3D_UNIFORM_TEXCOORD_WEIGHT, + + E3D_UNIFORM_TEXTURE_WEIGHT_AMBIENT, + E3D_UNIFORM_TEXTURE_WEIGHT_DIFFUSE, + E3D_UNIFORM_TEXTURE_WEIGHT_SPECULAR, + E3D_UNIFORM_TEXTURE_WEIGHT_EMISSION, + E3D_UNIFORM_TEXTURE_WEIGHT_NORMAL, + + E3D_UNIFORM_TEXTURE_AMBIENT0, + E3D_UNIFORM_TEXTURE_DIFFUSE0, + E3D_UNIFORM_TEXTURE_SPECULAR0, + E3D_UNIFORM_TEXTURE_EMISSION0, + E3D_UNIFORM_TEXTURE_NORMAL0, + + E3D_UNIFORM_TEXTURE_AMBIENT1, + E3D_UNIFORM_TEXTURE_DIFFUSE1, + E3D_UNIFORM_TEXTURE_SPECULAR1, + E3D_UNIFORM_TEXTURE_EMISSION1, + E3D_UNIFORM_TEXTURE_NORMAL1, + + E3D_UNIFORM_LIGHT_POSITION, + E3D_UNIFORM_LIGHT_SPOT_DIR, + E3D_UNIFORM_LIGHT_SPOT_EXP, + E3D_UNIFORM_LIGHT_SPOT_CUTOFF_COS, + E3D_UNIFORM_LIGHT_ATTENUATION, + E3D_UNIFORM_LIGHT_AMBIENT, + E3D_UNIFORM_LIGHT_DIFFUSE, + E3D_UNIFORM_LIGHT_SPECULAR, + + E3D_UNIFORM_MATERIAL_AMBIENT, + E3D_UNIFORM_MATERIAL_DIFFUSE, + E3D_UNIFORM_MATERIAL_SPECULAR, + E3D_UNIFORM_MATERIAL_EMISSION, + E3D_UNIFORM_MATERIAL_SHININESS, + + E3D_UNIFORM_COUNT, +} E3D_Uniform; + +typedef struct _E3D_Shader_String +{ + char *str; + int size; + int count; +} E3D_Shader_String; + +struct _E3D_Program +{ + GLuint vert; + GLuint frag; + GLuint prog; + + E3D_Shader_Flag flags; + Evas_3D_Shade_Mode mode; + + GLint uniform_locations[E3D_UNIFORM_COUNT]; +}; + +static void _shader_string_init(E3D_Shader_String *string) +{ + string->str = NULL; + string->size = 0; + string->count = 0; +} + +static void _shader_string_fini(E3D_Shader_String *string) +{ + if (string->str) + { + free(string->str); + _shader_string_init(string); + } +} + +void _shader_string_add(E3D_Shader_String *shader, const char *str) +{ + int len; + + if (str == NULL) + return; + + len = strlen(str); + + if ((shader->size - shader->count) < len) + { + int new_size = (shader->count + len) * 2; + char *new_buf = (char *)malloc(new_size + 1); + + if (shader->str) + { + memcpy(new_buf, shader->str, sizeof(char) * shader->count); + free(shader->str); + } + + shader->str = new_buf; + shader->size = new_size; + } + + memcpy(&shader->str[shader->count], str, len + 1); + shader->count += len; +} + +#define ADD_LINE(str) _shader_string_add(shader, str"\n") + +static void +_vertex_shader_string_variable_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode, E3D_Shader_Flag flags) +{ + ADD_LINE("uniform mat4 uMatrixMvp;"); + + /* Vertex attributes. */ + if (flags & E3D_SHADER_FLAG_VERTEX_POSITION) + ADD_LINE("attribute vec4 aPosition0;"); + + if (flags & E3D_SHADER_FLAG_VERTEX_POSITION_BLEND) + { + ADD_LINE("attribute vec4 aPosition1;"); + ADD_LINE("uniform float uPositionWeight;"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_NORMAL) + ADD_LINE("attribute vec4 aNormal0;"); + + if (flags & E3D_SHADER_FLAG_VERTEX_NORMAL_BLEND) + { + ADD_LINE("attribute vec4 aNormal1;"); + ADD_LINE("uniform float uNormalWeight;"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_TANGENT) + ADD_LINE("attribute vec4 aTangent0;"); + + if (flags & E3D_SHADER_FLAG_VERTEX_TANGENT_BLEND) + { + ADD_LINE("attribute vec4 aTangent1;"); + ADD_LINE("uniform float uTangentWeight;"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_COLOR) + ADD_LINE("attribute vec4 aColor0;"); + + if (flags & E3D_SHADER_FLAG_VERTEX_COLOR_BLEND) + { + ADD_LINE("attribute vec4 aColor1;"); + ADD_LINE("uniform float uColorWeight;"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_TEXCOORD) + ADD_LINE("attribute vec4 aTexCoord0;"); + + if (flags & E3D_SHADER_FLAG_VERTEX_TEXCOORD_BLEND) + { + ADD_LINE("attribute vec4 aTexCoord1;"); + ADD_LINE("uniform float uTexCoordWeight;"); + } + + /* Texture coordinate. */ + if (_flags_need_tex_coord(flags)) + ADD_LINE("varying vec2 vTexCoord;"); + + /* Variables for each shade modes. */ + if (mode == EVAS_3D_SHADE_MODE_VERTEX_COLOR) + { + ADD_LINE("varying vec4 vColor;"); + } + else if (mode == EVAS_3D_SHADE_MODE_DIFFUSE) + { + /* Nothing to declare. */ + } + else if (mode == EVAS_3D_SHADE_MODE_FLAT) + { + ADD_LINE("uniform mat3 uMatrixNormal;"); + ADD_LINE("uniform mat4 uMatrixModelview;"); + ADD_LINE("uniform vec4 uLightPosition;"); + + ADD_LINE("varying vec2 vFactor;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_SPOT) + { + ADD_LINE("uniform vec3 uLightSpotDir;"); + ADD_LINE("uniform float uLightSpotExp;"); + ADD_LINE("uniform float uLightSpotCutoffCos;"); + } + + if (flags & E3D_SHADER_FLAG_SPECULAR) + ADD_LINE("uniform float uMaterialShininess;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("uniform vec3 uLightAtten;"); + } + else if (mode == EVAS_3D_SHADE_MODE_PHONG || mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + { + ADD_LINE("uniform mat3 uMatrixNormal;"); + ADD_LINE("uniform mat4 uMatrixModelview;"); + ADD_LINE("uniform vec4 uLightPosition;"); + ADD_LINE("varying vec3 vLightVector;"); + ADD_LINE("varying vec3 vLightHalfVector;"); + + if (mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + ADD_LINE("varying vec3 vEyeVector;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("varying float vLightDist;"); + + if (mode == EVAS_3D_SHADE_MODE_PHONG || (flags & E3D_SHADER_FLAG_VERTEX_TANGENT) == 0) + ADD_LINE("varying vec3 vNormal;"); + } +} + +static void +_vertex_shader_string_func_flat_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode EINA_UNUSED, E3D_Shader_Flag flags) +{ + ADD_LINE("void vertexFlat(vec4 position, vec3 normal) {"); + + ADD_LINE("vec3 lv;"); + ADD_LINE("float factor;"); + + ADD_LINE("normal = uMatrixNormal * normal;"); + ADD_LINE("position = uMatrixModelview * position;"); + + if (flags & E3D_SHADER_FLAG_NORMALIZE_NORMALS) + ADD_LINE("normal = normalize(normal);"); + + if (flags & E3D_SHADER_FLAG_LIGHT_DIRECTIONAL) + { + ADD_LINE("lv = uLightPosition.xyz;"); + } + else + { + ADD_LINE("lv = uLightPosition.xyz - position.xyz;"); + ADD_LINE("lv = normalize(lv);"); + } + + ADD_LINE("factor = max(dot(lv, normal), 0.0);"); + + if (flags & E3D_SHADER_FLAG_LIGHT_SPOT) + { + ADD_LINE("float f = dot(-lv, uLightSpotDir);"); + ADD_LINE("if (f > uLightSpotCutoffCos)"); + ADD_LINE("factor *= pow(f, uLightSpotExp);"); + ADD_LINE("else"); + ADD_LINE("factor = 0.0;"); + } + + ADD_LINE("if (factor > 0.0) {"); + + /* Diffuse term. */ + if (flags & E3D_SHADER_FLAG_DIFFUSE) + ADD_LINE("vFactor.x = factor;"); + else + ADD_LINE("vFactor.x = 0.0;"); + + /* Specular term. */ + if (flags & E3D_SHADER_FLAG_SPECULAR) + { + ADD_LINE("vec3 hv = normalize(normalize(-position.xyz) + lv);"); + ADD_LINE("factor = pow(max(dot(hv, normal), 0.0), uMaterialShininess);"); + ADD_LINE("vFactor.y = factor;"); + } + + ADD_LINE("} else {"); + ADD_LINE("vFactor = vec2(0.0, 0.0);"); + ADD_LINE("}"); + + /* Light attenuation. */ + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + { + ADD_LINE("float dist = length(lv);"); + ADD_LINE("vFactor /= dot(uLightAtten, vec3(1.0, dist, dist * dist));"); + } + + ADD_LINE("}"); +} + +static void +_vertex_shader_string_func_phong_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode EINA_UNUSED, E3D_Shader_Flag flags) +{ + ADD_LINE("void vertexPhong(vec4 position, vec3 normal) {"); + + ADD_LINE("normal = uMatrixNormal * normal;"); + ADD_LINE("position = uMatrixModelview * position;"); + + if (flags & E3D_SHADER_FLAG_NORMALIZE_NORMALS) + ADD_LINE("normal = normalize(normal);"); + + if (flags & E3D_SHADER_FLAG_LIGHT_DIRECTIONAL) + { + ADD_LINE("vLightVector = uLightPosition.xyz;"); + } + else + { + ADD_LINE("vLightVector = uLightPosition.xyz - position.xyz;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("vLightDist = length(vLightVector);"); + + ADD_LINE("vLightVector = normalize(vLightVector);"); + } + + ADD_LINE("vLightHalfVector = normalize(normalize(-position.xyz) + vLightVector);"); + ADD_LINE("vNormal = normal;"); + ADD_LINE("}"); +} + +static void +_vertex_shader_string_func_normal_map_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode EINA_UNUSED, + E3D_Shader_Flag flags) +{ + if ((flags & E3D_SHADER_FLAG_VERTEX_TANGENT) == 0) + { + ADD_LINE("void vertexNormalMap(vec4 position, vec3 normal) {"); + + ADD_LINE("normal = uMatrixNormal * normal;"); + ADD_LINE("position = uMatrixModelview * position;"); + ADD_LINE("vEyeVector = normalize(-position.xyz);"); + + if (flags & E3D_SHADER_FLAG_NORMALIZE_NORMALS) + ADD_LINE("normal = normalize(normal);"); + + if (flags & E3D_SHADER_FLAG_LIGHT_DIRECTIONAL) + { + ADD_LINE("vLightVector = uLightPosition.xyz;"); + } + else + { + ADD_LINE("vLightVector = uLightPosition.xyz - position.xyz;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("vLightDist = length(vLightVector);"); + + ADD_LINE("vLightVector = normalize(vLightVector);"); + } + + ADD_LINE("vLightHalfVector = normalize(vEyeVector + vLightVector);"); + ADD_LINE("vNormal = normal;"); + ADD_LINE("}"); + } + else + { + ADD_LINE("void vertexNormalMap(vec4 position, vec3 normal, vec3 tangent) {"); + + ADD_LINE("vec3 n = normalize(uMatrixNormal * normal);"); + ADD_LINE("vec3 t = normalize(uMatrixNormal * tangent);"); + ADD_LINE("vec3 b = cross(n, t);"); + ADD_LINE("vec3 tmp;"); + + ADD_LINE("position = uMatrixModelview * position;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_DIRECTIONAL) + { + ADD_LINE("vec3 lightDir = uLightPosition.xyz;"); + } + else + { + ADD_LINE("vec3 lightDir = uLightPosition.xyz - position.xyz;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("vLightDist = length(lightDir);"); + + ADD_LINE("lightDir = normalize(lightDir);"); + } + + ADD_LINE("tmp.x = dot(lightDir, t);"); + ADD_LINE("tmp.y = dot(lightDir, b);"); + ADD_LINE("tmp.z = dot(lightDir, n);"); + ADD_LINE("vLightVector = tmp;"); + + ADD_LINE("tmp.x = dot(position.xyz, t);"); + ADD_LINE("tmp.y = dot(position.xyz, b);"); + ADD_LINE("tmp.z = dot(position.xyz, n);"); + ADD_LINE("vEyeVector = normalize(tmp);"); + + ADD_LINE("vec3 hv = normalize(normalize(-position.xyz) + lightDir);"); + ADD_LINE("tmp.x = dot(hv, t);"); + ADD_LINE("tmp.y = dot(hv, b);"); + ADD_LINE("tmp.z = dot(hv, n);"); + ADD_LINE("vLightHalfVector = tmp;"); + + ADD_LINE("}"); + } +} + +static void +_vertex_shader_string_get(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode, E3D_Shader_Flag flags) +{ + /* Add variables - vertex attributes. */ + _vertex_shader_string_variable_add(shader, mode, flags); + + /* Add functions. */ + if (mode == EVAS_3D_SHADE_MODE_FLAT) + _vertex_shader_string_func_flat_add(shader, mode, flags); + else if (mode == EVAS_3D_SHADE_MODE_PHONG) + _vertex_shader_string_func_phong_add(shader, mode, flags); + else if (mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + _vertex_shader_string_func_normal_map_add(shader, mode, flags); + + ADD_LINE("void main() {"); + + /* Process vertex attributes. */ + if (flags & E3D_SHADER_FLAG_VERTEX_POSITION_BLEND) + { + ADD_LINE("vec4 position = aPosition0 * uPositionWeight + "); + ADD_LINE("aPosition1 * (1.0 - uPositionWeight);"); + ADD_LINE("position = vec4(position.xyz, 1.0);"); + } + else if (flags & E3D_SHADER_FLAG_VERTEX_POSITION) + { + ADD_LINE("vec4 position = vec4(aPosition0.xyz, 1.0);"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_NORMAL_BLEND) + { + ADD_LINE("vec3 normal = aNormal0.xyz * uNormalWeight + "); + ADD_LINE("aNormal1.xyz * (1.0 - uNormalWeight);"); + } + else if (flags & E3D_SHADER_FLAG_VERTEX_NORMAL) + { + ADD_LINE("vec3 normal = aNormal0.xyz;"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_TANGENT_BLEND) + { + ADD_LINE("vec3 tangent = aTangent0.xyz * uTangentWeight + "); + ADD_LINE("aTangent1.xyz * (1.0 - uTangentWeight);"); + } + else if (flags & E3D_SHADER_FLAG_VERTEX_TANGENT) + { + ADD_LINE("vec3 tangent = aTangent0.xyz;"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_COLOR_BLEND) + { + ADD_LINE("vec4 color = aColor0 * uColorWeight + aColor1 * (1.0 - uColorWeight);"); + } + else if (flags & E3D_SHADER_FLAG_VERTEX_COLOR) + { + ADD_LINE("vec4 color = aColor0;"); + } + + if (flags & E3D_SHADER_FLAG_VERTEX_TEXCOORD_BLEND) + { + ADD_LINE("vTexCoord = aTexCoord0.st * uTexCoordWeight + "); + ADD_LINE("aTexCoord1.st * (1.0 - uTexCoordWeight);"); + } + else if (flags & E3D_SHADER_FLAG_VERTEX_TEXCOORD) + { + ADD_LINE("vTexCoord = aTexCoord0.st;"); + } + + /* Transform vertex position. */ + ADD_LINE("gl_Position = uMatrixMvp * position;"); + + /* Process according to the shade mode. */ + if (mode == EVAS_3D_SHADE_MODE_VERTEX_COLOR) + { + ADD_LINE("vColor = color;"); + } + else if (mode == EVAS_3D_SHADE_MODE_FLAT) + { + ADD_LINE("vertexFlat(position, normal);"); + } + else if (mode == EVAS_3D_SHADE_MODE_PHONG) + { + ADD_LINE("vertexPhong(position, normal);"); + } + else if (mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + { + if (flags & E3D_SHADER_FLAG_VERTEX_TANGENT) + ADD_LINE("vertexNormalMap(position, normal, tangent);"); + else + ADD_LINE("vertexNormalMap(position, normal);"); + } + + ADD_LINE("}"); +} + +static void +_fragment_shader_string_variable_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode, E3D_Shader_Flag flags) +{ + /* Texture coordinate. */ + if (_flags_need_tex_coord(flags)) + ADD_LINE("varying vec2 vTexCoord;"); + + /* Materials. */ + if (flags & E3D_SHADER_FLAG_DIFFUSE) + { + ADD_LINE("uniform vec4 uMaterialDiffuse;"); + + if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE) + { + ADD_LINE("uniform sampler2D uTextureDiffuse0;"); + } + + if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE_BLEND) + { + ADD_LINE("uniform sampler2D uTextureDiffuse1;"); + ADD_LINE("uniform float uTextureDiffuseWeight;"); + } + } + + if (flags & E3D_SHADER_FLAG_SPECULAR) + { + ADD_LINE("uniform vec4 uMaterialSpecular;"); + ADD_LINE("uniform float uMaterialShininess;"); + + if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE) + { + ADD_LINE("uniform sampler2D uTextureSpecular0;"); + } + + if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE_BLEND) + { + ADD_LINE("uniform sampler2D uTextureSpecular1;"); + ADD_LINE("uniform float uTextureSpecularWeight;"); + } + } + + if (flags & E3D_SHADER_FLAG_AMBIENT) + { + ADD_LINE("uniform vec4 uMaterialAmbient;"); + + if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE) + { + ADD_LINE("uniform sampler2D uTextureAmbient0;"); + } + + if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE_BLEND) + { + ADD_LINE("uniform sampler2D uTextureAmbient1;"); + ADD_LINE("uniform float uTextureAmbientWeight;"); + } + } + + if (flags & E3D_SHADER_FLAG_EMISSION) + { + ADD_LINE("uniform vec4 uMaterialEmission;"); + + if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE) + { + ADD_LINE("uniform sampler2D uTextureEmission0;"); + } + + if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE_BLEND) + { + ADD_LINE("uniform sampler2D uTextureEmission1;"); + ADD_LINE("uniform float uTextureEmissionWeight;"); + } + } + + if (mode == EVAS_3D_SHADE_MODE_VERTEX_COLOR) + { + ADD_LINE("varying vec4 vColor;"); + } + else if (mode == EVAS_3D_SHADE_MODE_DIFFUSE) + { + /* Nothing to declare. */ + } + else if (mode == EVAS_3D_SHADE_MODE_FLAT) + { + ADD_LINE("varying vec2 vFactor;"); + + if (flags & E3D_SHADER_FLAG_DIFFUSE) + ADD_LINE("uniform vec4 uLightDiffuse;"); + + if (flags & E3D_SHADER_FLAG_SPECULAR) + ADD_LINE("uniform vec4 uLightSpecular;"); + + if (flags & E3D_SHADER_FLAG_AMBIENT) + ADD_LINE("uniform vec4 uLightAmbient;"); + } + else if (mode == EVAS_3D_SHADE_MODE_PHONG || mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + { + ADD_LINE("varying vec3 vLightVector;"); + ADD_LINE("varying vec3 vLightHalfVector;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_SPOT) + { + ADD_LINE("uniform vec3 uLightSpotDir;"); + ADD_LINE("uniform float uLightSpotExp;"); + ADD_LINE("uniform float uLightSpotCutoffCos;"); + } + + if (flags & E3D_SHADER_FLAG_DIFFUSE) + ADD_LINE("uniform vec4 uLightDiffuse;"); + + if (flags & E3D_SHADER_FLAG_SPECULAR) + ADD_LINE("uniform vec4 uLightSpecular;"); + + if (flags & E3D_SHADER_FLAG_AMBIENT) + ADD_LINE("uniform vec4 uLightAmbient;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("varying float vLightDist;"); + + if (mode == EVAS_3D_SHADE_MODE_PHONG) + { + ADD_LINE("varying vec3 vNormal;"); + } + else /* Normal map. */ + { + ADD_LINE("uniform sampler2D uTextureNormal0;"); + + if (flags & E3D_SHADER_FLAG_NORMAL_TEXTURE_BLEND) + { + ADD_LINE("uniform sampler2D uTextureNormal1;"); + ADD_LINE("uniform float uTextureNormalWeight;"); + } + + ADD_LINE("varying vec3 vEyeVector;"); + + if ((flags & E3D_SHADER_FLAG_VERTEX_TANGENT) == 0) + ADD_LINE("varying vec3 vNormal;"); + } + } +} + +static void +_fragment_shader_string_func_flat_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode EINA_UNUSED, E3D_Shader_Flag flags) +{ + ADD_LINE("void fragmentFlat() {"); + ADD_LINE("vec4 color;"); + + if (flags & E3D_SHADER_FLAG_DIFFUSE) + { + if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureDiffuse0, vTexCoord) * uTextureDiffuseWeight +"); + ADD_LINE("texture2D(uTextureDiffuse1, vTexCoord) * (1.0 - uTextureDiffuseWeight);"); + ADD_LINE("color *= uMaterialDiffuse;"); + } + else if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureDiffuse0, vTexCoord) * uMaterialDiffuse;"); + } + else + { + ADD_LINE("color = uMaterialDiffuse;"); + } + + ADD_LINE("gl_FragColor = uLightDiffuse * color * vFactor.x;"); + } + else + { + ADD_LINE("gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);"); + } + + if (flags & E3D_SHADER_FLAG_SPECULAR) + { + if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureSpecular0, vTexCoord) * uTextureSpecularWeight +"); + ADD_LINE("texture2D(uTextureSpecular1, vTexCoord) * (1.0 - uTextureSpecularWeight);"); + ADD_LINE("color *= uMaterialSpecular;"); + } + else if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureSpecular0, vTexCoord) * uMaterialSpecular;"); + } + else + { + ADD_LINE("color = uMaterialSpecular;"); + } + + ADD_LINE("gl_FragColor += uLightSpecular * color * vFactor.y;"); + } + + if (flags & E3D_SHADER_FLAG_AMBIENT) + { + if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureAmbient0, vTexCoord) * uTextureAmbientWeight +"); + ADD_LINE("texture2D(uTextureAmbient1, vTexCoord) * (1.0 - uTextureAmbientWeight);"); + ADD_LINE("color *= uMaterialAmbient;"); + } + else if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureAmbient0, vTexCoord) * uMaterialAmbient;"); + } + else + { + ADD_LINE("color = uMaterialAmbient;"); + } + + ADD_LINE("gl_FragColor += uLightAmbient * color;"); + } + + if (flags & E3D_SHADER_FLAG_EMISSION) + { + if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureEmission0, vTexCoord) * uTextureEmissionWeight +"); + ADD_LINE("texture2D(uTextureEmission1, vTexCoord) * (1.0 - uTextureEmissionWeight);"); + ADD_LINE("color *= uMaterialEmission;"); + } + else if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureEmission0, vTexCoord) * uMaterialEmission;"); + } + else + { + ADD_LINE("color = uMaterialEmission;"); + } + + ADD_LINE("gl_FragColor += color;"); + } + + ADD_LINE("}"); +} + +static void +_fragment_shader_string_func_phong_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode EINA_UNUSED, E3D_Shader_Flag flags) +{ + ADD_LINE("void fragmentPhong() {"); + ADD_LINE("vec3 normal = normalize(vNormal);"); + ADD_LINE("vec3 lv = normalize(vLightVector);"); + ADD_LINE("float factor = dot(lv, normal);"); + ADD_LINE("vec4 color;"); + + if (flags & E3D_SHADER_FLAG_LIGHT_SPOT) + { + ADD_LINE("float f = dot(-lv, normalize(uLightSpotDir));"); + + ADD_LINE("if (f > uLightSpotCutoffCos)"); + ADD_LINE("factor *= pow(f, uLightSpotExp);"); + ADD_LINE("else"); + ADD_LINE("factor = 0.0;"); + } + + ADD_LINE("if (factor > 0.0) {"); + + /* Diffuse term. */ + if (flags & E3D_SHADER_FLAG_DIFFUSE) + { + if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureDiffuse0, vTexCoord) * uTextureDiffuseWeight +"); + ADD_LINE("texture2D(uTextureDiffuse1, vTexCoord) * (1.0 - uTextureDiffuseWeight);"); + } + else if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureDiffuse0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialDiffuse;"); + } + + ADD_LINE("gl_FragColor = uLightDiffuse * color * factor;"); + } + else + { + ADD_LINE("gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);"); + } + + /* Specular term. */ + if (flags & E3D_SHADER_FLAG_SPECULAR) + { + ADD_LINE("factor = dot(normalize(vLightHalfVector), normal);"); + ADD_LINE("if (factor > 0.0) {"); + ADD_LINE("factor = pow(factor, uMaterialShininess);"); + + if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureSpecular0, vTexCoord) * uTextureSpecularWeight +"); + ADD_LINE("texture2D(uTextureSpecular1, vTexCoord) * (1.0 - uTextureSpecularWeight);"); + } + else if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureSpecular0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialSpecular;"); + } + + ADD_LINE("gl_FragColor += uLightSpecular * color * factor;"); + ADD_LINE("}"); + } + + ADD_LINE("} else {"); + ADD_LINE("gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);"); + ADD_LINE("}"); + + /* Ambient term. */ + if (flags & E3D_SHADER_FLAG_AMBIENT) + { + if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureAmbient0, vTexCoord) * uTextureAmbientWeight +"); + ADD_LINE("texture2D(uTextureAmbient1 * vTexCoord) * (1.0 - uTextureAmbientWeight);"); + } + else if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureAmbient0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialAmbient;"); + } + + ADD_LINE("gl_FragColor += uLightAmbient * color;"); + } + + /* Light attenuation. */ + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("gl_FragColor /= dot(uLightAtten, vec3(1.0, vLightDist, vLightDist * vLightDist));"); + + /* Emission term. */ + if (flags & E3D_SHADER_FLAG_EMISSION) + { + if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureEmission0, vTexCoord) * uTextureEmissionWeight +"); + ADD_LINE("texture2D(uTextureEmission1, vTexCoord) * (1.0 - uTextureEmissionWeight);"); + } + else if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureEmission0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialEmission;"); + } + + ADD_LINE("gl_FragColor += color;"); + } + + ADD_LINE("gl_FragColor.a = 1.0;"); + ADD_LINE("}"); +} + +static void +_fragment_shader_string_func_normal_map_add(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode EINA_UNUSED, + E3D_Shader_Flag flags) +{ + if ((flags & E3D_SHADER_FLAG_VERTEX_TANGENT) == 0) + { + ADD_LINE("mat3 cotangent_frame(vec3 n, vec3 p, vec2 uv) {"); + ADD_LINE("vec3 dp1 = dFdx(p);"); + ADD_LINE("vec3 dp2 = dFdy(p);"); + ADD_LINE("vec2 duv1 = dFdx(uv);"); + ADD_LINE("vec2 duv2 = dFdy(uv);"); + ADD_LINE("vec3 dp2perp = cross(dp2, n);"); + ADD_LINE("vec3 dp1perp = cross(n, dp1);"); + ADD_LINE("vec3 t = dp2perp * duv1.x + dp1perp * duv2.x;"); + ADD_LINE("vec3 b = dp2perp * duv1.y + dp1perp * duv2.y;"); + ADD_LINE("float invmax = inversesqrt(max(dot(t, t), dot(b, b)));"); + ADD_LINE("return mat3(t * invmax, b * invmax, n);"); + ADD_LINE("}"); + + ADD_LINE("vec3 perturb_normal(vec3 normal) {"); + ADD_LINE("mat3 tbn = cotangent_frame(vNormal, -vEyeVector, vTexCoord);"); + ADD_LINE("return normalize(tbn * normal);"); + ADD_LINE("}"); + } + + ADD_LINE("void fragmentNormalMap() {"); + ADD_LINE("float factor;"); + ADD_LINE("vec3 normal;"); + ADD_LINE("vec4 color;"); + + if (flags & E3D_SHADER_FLAG_NORMAL_TEXTURE_BLEND) + { + ADD_LINE("normal = texture2D(uTextureNormal0, vTexCoord).rgb * uTextureNormalWeight;"); + ADD_LINE("normal += texture2D(uTextureNormal1, vTexCoord).rgb * "); + ADD_LINE("(1.0 - uTextureNormalWeight);"); + } + else + { + ADD_LINE("normal = texture2D(uTextureNormal0, vTexCoord).rgb;"); + } + + ADD_LINE("normal = 2.0 * normal - 1.0;"); + + if ((flags & E3D_SHADER_FLAG_VERTEX_TANGENT) == 0) + { + ADD_LINE("normal = perturb_normal(normal);"); + } + + /* Can we skip this normalization?? */ + ADD_LINE("vec3 lv = normalize(vLightVector);"); + ADD_LINE("normal = normalize(normal);"); + + ADD_LINE("factor = dot(lv, normal);"); + + if (flags & E3D_SHADER_FLAG_LIGHT_SPOT) + { + ADD_LINE("float f = dot(-lv, normalize(uLightSpotDir));"); + + ADD_LINE("if (f > uLightSpotCutoffCos)"); + ADD_LINE("factor *= pow(f, uLightSpotExp);"); + ADD_LINE("else"); + ADD_LINE("factor = 0.0;"); + } + + ADD_LINE("if (factor > 0.0) {"); + + /* Diffuse term. */ + if (flags & E3D_SHADER_FLAG_DIFFUSE) + { + if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureDiffuse0, vTexCoord) * uTextureDiffuseWeight +"); + ADD_LINE("texture2D(uTextureDiffuse1, vTexCoord) * (1.0 - uTextureDiffuseWeight);"); + } + else if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureDiffuse0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialDiffuse;"); + } + + ADD_LINE("gl_FragColor = uLightDiffuse * color * factor;"); + } + else + { + ADD_LINE("gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);"); + } + + /* Specular term. */ + if (flags & E3D_SHADER_FLAG_SPECULAR) + { + ADD_LINE("factor = dot(normalize(vLightHalfVector), normal);"); + ADD_LINE("if (factor > 0.0) {"); + ADD_LINE("factor = pow(factor, uMaterialShininess);"); + + if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureSpecular0, vTexCoord) * uTextureSpecularWeight +"); + ADD_LINE("texture2D(uTextureSpecular1, vTexCoord) * (1.0 - uTextureSpecularWeight);"); + } + else if (flags & E3D_SHADER_FLAG_SPECULAR_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureSpecular0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialSpecular;"); + } + + ADD_LINE("gl_FragColor += uLightSpecular * color * factor;"); + ADD_LINE("}"); + } + + ADD_LINE("} else {"); + ADD_LINE("gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);"); + ADD_LINE("}"); + + /* Ambient term. */ + if (flags & E3D_SHADER_FLAG_AMBIENT) + { + if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureAmbient0, vTexCoord) * uTextureAmbientWeight +"); + ADD_LINE("texture2D(uTextureAmbient1, vTexCoord) * (1.0 - uTextureAmbientWeight);"); + } + else if (flags & E3D_SHADER_FLAG_AMBIENT_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureAmbient0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialAmbient;"); + } + + ADD_LINE("gl_FragColor += uLightAmbient * color;"); + } + + /* Light attenuation. */ + if (flags & E3D_SHADER_FLAG_LIGHT_ATTENUATION) + ADD_LINE("gl_FragColor /= dot(uLightAtten, vec3(1.0, vLightDist, vLightDist * vLightDist));"); + + /* Emission term. */ + if (flags & E3D_SHADER_FLAG_EMISSION) + { + if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE_BLEND) + { + ADD_LINE("color = texture2D(uTextureEmission0, vTexCoord) * uTextureEmissionWeight +"); + ADD_LINE("texture2D(uTextureEmission1, vTexCoord) * (1.0 - uTextureEmissionWeight);"); + } + else if (flags & E3D_SHADER_FLAG_EMISSION_TEXTURE) + { + ADD_LINE("color = texture2D(uTextureEmission0, vTexCoord);"); + } + else + { + ADD_LINE("color = uMaterialEmission;"); + } + + ADD_LINE("gl_FragColor += color;"); + } + + ADD_LINE("gl_FragColor.a = 1.0;"); + ADD_LINE("}"); +} + +static void +_fragment_shader_string_get(E3D_Shader_String *shader, + Evas_3D_Shade_Mode mode, E3D_Shader_Flag flags) +{ + /* Add variables - vertex attributes. */ + _fragment_shader_string_variable_add(shader, mode, flags); + + /* Add functions. */ + if (mode == EVAS_3D_SHADE_MODE_FLAT) + _fragment_shader_string_func_flat_add(shader, mode, flags); + else if (mode == EVAS_3D_SHADE_MODE_PHONG) + _fragment_shader_string_func_phong_add(shader, mode, flags); + else if (mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + _fragment_shader_string_func_normal_map_add(shader, mode, flags); + + /* Add main function. */ + ADD_LINE("void main() {"); + + if (mode == EVAS_3D_SHADE_MODE_VERTEX_COLOR) + { + ADD_LINE("gl_FragColor = vColor;"); + } + else if (mode == EVAS_3D_SHADE_MODE_DIFFUSE) + { + if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE_BLEND) + { + ADD_LINE("gl_FragColor = (texture2D(uTextureDiffuse0, vTexCoord) *"); + ADD_LINE("uTextureDiffuseWeight + texture2D(uTextureDiffuse1, vTexCoord) *"); + ADD_LINE("(1.0 - uTextureDiffuseWeight)) * uMaterialDiffuse;"); + } + else if (flags & E3D_SHADER_FLAG_DIFFUSE_TEXTURE) + { + ADD_LINE("gl_FragColor = texture2D(uTextureDiffuse0, vTexCoord) * uMaterialDiffuse;"); + } + else + { + ADD_LINE("gl_FragColor = uMaterialDiffuse;"); + } + } + else if (mode == EVAS_3D_SHADE_MODE_FLAT) + { + ADD_LINE("fragmentFlat();"); + } + else if (mode == EVAS_3D_SHADE_MODE_PHONG) + { + ADD_LINE("fragmentPhong();"); + } + else if (mode == EVAS_3D_SHADE_MODE_NORMAL_MAP) + { + ADD_LINE("fragmentNormalMap();"); + } + + ADD_LINE("}"); +} + +static inline Eina_Bool +_shader_compile(GLuint shader, const char *src) +{ + GLint ok; + + glShaderSource(shader, 1, &src, NULL); + GLERR(__FUNCTION__, __FILE__, __LINE__, ""); + + glCompileShader(shader); + GLERR(__FUNCTION__, __FILE__, __LINE__, ""); + + glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); + GLERR(__FUNCTION__, __FILE__, __LINE__, ""); + + if (!ok) + { + GLchar *log_str; + GLint len; + GLsizei info_len; + + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); + log_str = (GLchar *)malloc(len); + glGetShaderInfoLog(shader, len, &info_len, log_str); + ERR("Shader compilation failed.\n%s", log_str); + free(log_str); + + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static inline Eina_Bool +_program_build(E3D_Program *program, const char *vert_src, const char *frag_src) +{ + GLint ok; + + /* Create OpenGL vertex & fragment shader object. */ + program->vert = glCreateShader(GL_VERTEX_SHADER); + program->frag = glCreateShader(GL_FRAGMENT_SHADER); + + /* Commpile vertex shader. */ + if (!_shader_compile(program->vert, vert_src)) + { + ERR("Faield to compile vertex shader."); + return EINA_FALSE; + } + + /* Compile fragment shader. */ + if (!_shader_compile(program->frag, frag_src)) + { + ERR("Failed to compile fragment shader."); + return EINA_FALSE; + } + + /* Create OpenGL program object. */ + program->prog = glCreateProgram(); + + /* Attach shaders. */ + glAttachShader(program->prog, program->vert); + GLERR(__FUNCTION__, __FILE__, __LINE__, ""); + + glAttachShader(program->prog, program->frag); + GLERR(__FUNCTION__, __FILE__, __LINE__, ""); + + /* Link program. */ + glLinkProgram(program->prog); + GLERR(__FUNCTION__, __FILE__, __LINE__, ""); + + /* Check link status. */ + glGetProgramiv(program->prog, GL_LINK_STATUS, &ok); + GLERR(__FUNCTION__, __FILE__, __LINE__, ""); + + if (!ok) + { + GLchar *log_str; + GLint len; + GLsizei info_len; + + glGetProgramiv(program->prog, GL_INFO_LOG_LENGTH, &len); + log_str = (GLchar *)malloc(len); + glGetProgramInfoLog(program->prog, len, &info_len, log_str); + ERR("Shader link failed.\n%s", log_str); + free(log_str); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static inline void +_program_vertex_attrib_bind(E3D_Program *program) +{ + GLint index = 0; + + if (program->flags & E3D_SHADER_FLAG_VERTEX_POSITION) + glBindAttribLocation(program->prog, index++, "aPosition0"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_POSITION_BLEND) + glBindAttribLocation(program->prog, index++, "aPosition1"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_NORMAL) + glBindAttribLocation(program->prog, index++, "aNormal0"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_NORMAL_BLEND) + glBindAttribLocation(program->prog, index++, "aNormal1"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_TANGENT) + glBindAttribLocation(program->prog, index++, "aTangent0"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_TANGENT_BLEND) + glBindAttribLocation(program->prog, index++, "aTangent1"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_COLOR) + glBindAttribLocation(program->prog, index++, "aColor0"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_COLOR_BLEND) + glBindAttribLocation(program->prog, index++, "aColor1"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_TEXCOORD) + glBindAttribLocation(program->prog, index++, "aTexCoord0"); + + if (program->flags & E3D_SHADER_FLAG_VERTEX_TEXCOORD_BLEND) + glBindAttribLocation(program->prog, index++, "aTexCoord1"); +} + +static const char *uniform_names[] = +{ + "uMatrixMvp", + "uMatrixModelview", + "uMatrixNormal", + "uPositionWeight", + "uNormalWeight", + "uTangentWeight", + "uColorWeight", + "uTexCoordWeight", + "uTextureAmbientWeight", + "uTextureDiffuseWeight", + "uTextureSpecularWeight", + "uTextureEmissionWeight", + "uTextureNormalWeight", + "uTextureAmbient0", + "uTextureDiffuse0", + "uTextureSpecular0", + "uTextureEmission0", + "uTextureNormal0", + "uTextureAmbient1", + "uTextureDiffuse1", + "uTextureSpecular1", + "uTextureEmission1", + "uTextureNormal1", + "uLightPosition", + "uLightSpotDir", + "uLightSpotExp", + "uLightSpotCutoffCos", + "uLightAtten", + "uLightAmbient", + "uLightDiffuse", + "uLightSpecular", + "uMaterialAmbient", + "uMaterialDiffuse", + "uMaterialSpecular", + "uMaterialEmission", + "uMaterialShininess", +}; + +static inline void +_program_uniform_init(E3D_Program *program) +{ + int i; + for (i = 0; i < E3D_UNIFORM_COUNT; i++) + { + program->uniform_locations[i] = glGetUniformLocation(program->prog, uniform_names[i]); + } +} + +static inline void +_uniform_upload(E3D_Uniform u, GLint loc, const E3D_Draw_Data *data) +{ + switch (u) + { + case E3D_UNIFORM_MATRIX_MVP: + glUniformMatrix4fv(loc, 1, EINA_FALSE, &data->matrix_mvp.m[0]); + break; + case E3D_UNIFORM_MATRIX_MV: + glUniformMatrix4fv(loc, 1, EINA_FALSE, &data->matrix_mv.m[0]); + break; + case E3D_UNIFORM_MATRIX_NORMAL: + glUniformMatrix3fv(loc, 1, EINA_FALSE, &data->matrix_normal.m[0]); + break; + case E3D_UNIFORM_POSITION_WEIGHT: + glUniform1f(loc, data->vertices[EVAS_3D_VERTEX_POSITION].weight); + break; + case E3D_UNIFORM_NORMAL_WEIGHT: + glUniform1f(loc, data->vertices[EVAS_3D_VERTEX_NORMAL].weight); + break; + case E3D_UNIFORM_TANGENT_WEIGHT: + glUniform1f(loc, data->vertices[EVAS_3D_VERTEX_TANGENT].weight); + break; + case E3D_UNIFORM_COLOR_WEIGHT: + glUniform1f(loc, data->vertices[EVAS_3D_VERTEX_COLOR].weight); + break; + case E3D_UNIFORM_TEXCOORD_WEIGHT: + glUniform1f(loc, data->vertices[EVAS_3D_VERTEX_TEXCOORD].weight); + break; + case E3D_UNIFORM_TEXTURE_WEIGHT_AMBIENT: + glUniform1f(loc, data->materials[EVAS_3D_MATERIAL_AMBIENT].texture_weight); + break; + case E3D_UNIFORM_TEXTURE_WEIGHT_DIFFUSE: + glUniform1f(loc, data->materials[EVAS_3D_MATERIAL_DIFFUSE].texture_weight); + break; + case E3D_UNIFORM_TEXTURE_WEIGHT_SPECULAR: + glUniform1f(loc, data->materials[EVAS_3D_MATERIAL_SPECULAR].texture_weight); + break; + case E3D_UNIFORM_TEXTURE_WEIGHT_EMISSION: + glUniform1f(loc, data->materials[EVAS_3D_MATERIAL_EMISSION].texture_weight); + break; + case E3D_UNIFORM_TEXTURE_WEIGHT_NORMAL: + glUniform1f(loc, data->materials[EVAS_3D_MATERIAL_NORMAL].texture_weight); + break; + case E3D_UNIFORM_TEXTURE_AMBIENT0: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_AMBIENT].sampler0); + break; + case E3D_UNIFORM_TEXTURE_DIFFUSE0: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_DIFFUSE].sampler0); + break; + case E3D_UNIFORM_TEXTURE_SPECULAR0: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_SPECULAR].sampler0); + break; + case E3D_UNIFORM_TEXTURE_EMISSION0: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_EMISSION].sampler0); + break; + case E3D_UNIFORM_TEXTURE_NORMAL0: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_NORMAL].sampler0); + break; + case E3D_UNIFORM_TEXTURE_AMBIENT1: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_AMBIENT].sampler1); + break; + case E3D_UNIFORM_TEXTURE_DIFFUSE1: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_DIFFUSE].sampler1); + break; + case E3D_UNIFORM_TEXTURE_SPECULAR1: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_SPECULAR].sampler1); + break; + case E3D_UNIFORM_TEXTURE_EMISSION1: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_EMISSION].sampler1); + break; + case E3D_UNIFORM_TEXTURE_NORMAL1: + glUniform1i(loc, data->materials[EVAS_3D_MATERIAL_NORMAL].sampler1); + break; + case E3D_UNIFORM_LIGHT_POSITION: + glUniform4f(loc, data->light.position.x, data->light.position.y, + data->light.position.z, data->light.position.w); + break; + case E3D_UNIFORM_LIGHT_SPOT_DIR: + glUniform3f(loc, data->light.spot_dir.x, data->light.spot_dir.y, data->light.spot_dir.z); + break; + case E3D_UNIFORM_LIGHT_SPOT_EXP: + glUniform1f(loc, data->light.spot_exp); + break; + case E3D_UNIFORM_LIGHT_SPOT_CUTOFF_COS: + glUniform1f(loc, data->light.spot_cutoff_cos); + break; + case E3D_UNIFORM_LIGHT_ATTENUATION: + glUniform3f(loc, data->light.atten.x, data->light.atten.y, data->light.atten.z); + break; + case E3D_UNIFORM_LIGHT_AMBIENT: + glUniform4f(loc, + data->light.ambient.r, data->light.ambient.g, + data->light.ambient.b, data->light.ambient.a); + break; + case E3D_UNIFORM_LIGHT_DIFFUSE: + glUniform4f(loc, + data->light.diffuse.r, data->light.diffuse.g, + data->light.diffuse.b, data->light.diffuse.a); + break; + case E3D_UNIFORM_LIGHT_SPECULAR: + glUniform4f(loc, + data->light.specular.r, data->light.specular.g, + data->light.specular.b, data->light.specular.a); + break; + case E3D_UNIFORM_MATERIAL_AMBIENT: + glUniform4f(loc, + data->materials[EVAS_3D_MATERIAL_AMBIENT].color.r, + data->materials[EVAS_3D_MATERIAL_AMBIENT].color.g, + data->materials[EVAS_3D_MATERIAL_AMBIENT].color.b, + data->materials[EVAS_3D_MATERIAL_AMBIENT].color.a); + break; + case E3D_UNIFORM_MATERIAL_DIFFUSE: + glUniform4f(loc, + data->materials[EVAS_3D_MATERIAL_DIFFUSE].color.r, + data->materials[EVAS_3D_MATERIAL_DIFFUSE].color.g, + data->materials[EVAS_3D_MATERIAL_DIFFUSE].color.b, + data->materials[EVAS_3D_MATERIAL_DIFFUSE].color.a); + break; + case E3D_UNIFORM_MATERIAL_SPECULAR: + glUniform4f(loc, + data->materials[EVAS_3D_MATERIAL_SPECULAR].color.r, + data->materials[EVAS_3D_MATERIAL_SPECULAR].color.g, + data->materials[EVAS_3D_MATERIAL_SPECULAR].color.b, + data->materials[EVAS_3D_MATERIAL_SPECULAR].color.a); + break; + case E3D_UNIFORM_MATERIAL_EMISSION: + glUniform4f(loc, + data->materials[EVAS_3D_MATERIAL_EMISSION].color.r, + data->materials[EVAS_3D_MATERIAL_EMISSION].color.g, + data->materials[EVAS_3D_MATERIAL_EMISSION].color.b, + data->materials[EVAS_3D_MATERIAL_EMISSION].color.a); + break; + case E3D_UNIFORM_MATERIAL_SHININESS: + glUniform1f(loc, data->shininess); + break; + default: + ERR("Invalid uniform ID."); + break; + } +} + +void +e3d_program_uniform_upload(E3D_Program *program, const E3D_Draw_Data *data) +{ + int i; + for (i = 0; i < E3D_UNIFORM_COUNT; i++) + { + if (program->uniform_locations[i] != -1) + { + _uniform_upload(i, program->uniform_locations[i], data); + } + } +} + +E3D_Program * +e3d_program_new(Evas_3D_Shade_Mode mode, E3D_Shader_Flag flags) +{ + E3D_Shader_String vert, frag; + E3D_Program *program = NULL; + + _shader_string_init(&vert); + _shader_string_init(&frag); + + program = (E3D_Program *)calloc(1, sizeof(E3D_Program)); + + if (program == NULL) + { + ERR("Failed to allocate memory."); + return NULL; + } + + program->prog = glCreateProgram(); + program->vert = glCreateShader(GL_VERTEX_SHADER); + program->frag = glCreateShader(GL_FRAGMENT_SHADER); + program->mode = mode; + program->flags = flags; + + _vertex_shader_string_get(&vert, mode, flags); + _fragment_shader_string_get(&frag, mode, flags); + + if (! _program_build(program, vert.str, frag.str)) + goto error; + + _program_vertex_attrib_bind(program); + _program_uniform_init(program); + + _shader_string_fini(&vert); + _shader_string_fini(&frag); + + return program; + +error: + if (program->prog) + glDeleteProgram(program->prog); + + if (program->vert) + glDeleteShader(program->vert); + + if (program->frag) + glDeleteShader(program->frag); + + _shader_string_fini(&vert); + _shader_string_fini(&frag); + + return NULL; +} + +void +e3d_program_free(E3D_Program *program) +{ + glDeleteProgram(program->prog); + glDeleteShader(program->vert); + glDeleteShader(program->frag); + free(program); +} + +GLuint +e3d_program_id_get(const E3D_Program *program) +{ + return program->prog; +} + +Evas_3D_Shade_Mode +e3d_program_shade_mode_get(const E3D_Program *program) +{ + return program->mode; +} + +E3D_Shader_Flag +e3d_program_shader_flags_get(const E3D_Program *program) +{ + return program->flags; +} diff --git a/src/modules/evas/engines/gl_common/evas_gl_common.h b/src/modules/evas/engines/gl_common/evas_gl_common.h index a4de175..d61186e 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_common.h +++ b/src/modules/evas/engines/gl_common/evas_gl_common.h @@ -897,4 +897,6 @@ _tex_sub_2d(Evas_Engine_GL_Context *gc, int x, int y, int w, int h, int fmt, int GLERR(__FUNCTION__, __FILE__, __LINE__, ""); } +#include "evas_gl_3d_common.h" + #endif diff --git a/src/modules/evas/engines/gl_x11/evas_engine.c b/src/modules/evas/engines/gl_x11/evas_engine.c index bac2557..9ee1ada 100644 --- a/src/modules/evas/engines/gl_x11/evas_engine.c +++ b/src/modules/evas/engines/gl_x11/evas_engine.c @@ -67,6 +67,9 @@ struct _Render_Engine void *get_pixels_data; Evas_Object *obj; } func; + + Evas_GL_X11_Context *context_3d; + E3D_Renderer *renderer_3d; }; static int initted = 0; @@ -3701,6 +3704,185 @@ eng_context_flush(void *data) } } +static void +eng_context_3d_use(void *data) +{ + Render_Engine *re = (Render_Engine *)data; + + if (re->context_3d == NULL) + { + re->context_3d = eng_gl_context_new(re->win); + + if (re->context_3d == NULL) + { + ERR("Failed to create OpenGL context for 3D."); + return; + } + } + + eng_gl_context_use(re->context_3d); +} + +static E3D_Renderer * +eng_renderer_3d_get(void *data) +{ + Render_Engine *re = (Render_Engine *)data; + + if (re->renderer_3d == NULL) + { + re->renderer_3d = e3d_renderer_new(); + + if (re->renderer_3d == NULL) + { + ERR("Failed to create 3D renderer."); + return NULL; + } + } + + return re->renderer_3d; +} + +static void * +eng_drawable_new(void *data, int w, int h, int alpha) +{ + eng_context_3d_use(data); +#ifdef GL_GLES + return e3d_drawable_new(w, h, alpha, GL_DEPTH_STENCIL_OES, GL_NONE); +#else + return e3d_drawable_new(w, h, alpha, GL_DEPTH24_STENCIL8, GL_NONE); +#endif +} + +static void +eng_drawable_free(void *data, void *drawable) +{ + eng_context_3d_use(data); + e3d_drawable_free(drawable); +} + +static void +eng_drawable_size_get(void *data EINA_UNUSED, void *drawable, int *w, int *h) +{ + e3d_drawable_size_get((E3D_Drawable *)drawable, w, h); +} + +static void * +eng_image_drawable_set(void *data, void *image, void *drawable) +{ + E3D_Drawable *d = (E3D_Drawable *)drawable; + Evas_Native_Surface ns; + int w, h; + + ns.type = EVAS_NATIVE_SURFACE_OPENGL; + ns.data.opengl.texture_id = e3d_drawable_texture_id_get(d); + ns.data.opengl.framebuffer_id = 0; + ns.data.opengl.internal_format = e3d_drawable_format_get(d); + ns.data.opengl.format = e3d_drawable_format_get(d); + ns.data.opengl.x = 0; + ns.data.opengl.y = 0; + e3d_drawable_size_get(d, &w, &h); + ns.data.opengl.w = w; + ns.data.opengl.h = h; + + return eng_image_native_set(data, image, &ns); +} + +static void +eng_drawable_scene_render(void *data, void *drawable, void *scene_data) +{ + Render_Engine *re = (Render_Engine *)data; + E3D_Renderer *renderer = NULL; + + eng_window_use(re->win); + evas_gl_common_context_flush(re->win->gl_context); + + eng_context_3d_use(data); + renderer = eng_renderer_3d_get(data); + e3d_drawable_scene_render(drawable, renderer, scene_data); +} + +static void * +eng_texture_new(void *data EINA_UNUSED) +{ + return e3d_texture_new(); +} + +static void +eng_texture_free(void *data EINA_UNUSED, void *texture) +{ + e3d_texture_free((E3D_Texture *)texture); +} + +static void +eng_texture_data_set(void *data, void *texture, Evas_3D_Color_Format color_format, + Evas_3D_Pixel_Format pixel_format, int w, int h, const void *pixels) +{ + Render_Engine *re = (Render_Engine *)data; + eng_window_use(re->win); + evas_gl_common_context_flush(re->win->gl_context); + eng_context_3d_use(data); + + e3d_texture_data_set((E3D_Texture *)texture, color_format, pixel_format, w, h, pixels); +} + +static void +eng_texture_file_set(void *data, void *texture, const char *file, const char *key) +{ + Render_Engine *re = (Render_Engine *)data; + eng_window_use(re->win); + evas_gl_common_context_flush(re->win->gl_context); + eng_context_3d_use(data); + + e3d_texture_file_set((E3D_Texture *)texture, file, key); +} + +static void +eng_texture_color_format_get(void *data EINA_UNUSED, void *texture, Evas_3D_Color_Format *format) +{ + *format = e3d_texture_color_format_get((E3D_Texture *)texture); +} + +static void +eng_texture_size_get(void *data EINA_UNUSED, void *texture, int *w, int *h) +{ + e3d_texture_size_get((E3D_Texture *)texture, w, h); +} + +static void +eng_texture_wrap_set(void *data EINA_UNUSED, void *texture, + Evas_3D_Wrap_Mode s, Evas_3D_Wrap_Mode t) +{ + e3d_texture_wrap_set((E3D_Texture *)texture, s, t); +} + +static void +eng_texture_wrap_get(void *data EINA_UNUSED, void *texture, + Evas_3D_Wrap_Mode *s, Evas_3D_Wrap_Mode *t) +{ + e3d_texture_wrap_get((E3D_Texture *)texture, s, t); +} + +static void +eng_texture_filter_set(void *data EINA_UNUSED, void *texture, + Evas_3D_Texture_Filter min, Evas_3D_Texture_Filter mag) +{ + e3d_texture_filter_set((E3D_Texture *)texture, min, mag); +} + +static void +eng_texture_filter_get(void *data EINA_UNUSED, void *texture, + Evas_3D_Texture_Filter *min, Evas_3D_Texture_Filter *mag) +{ + e3d_texture_filter_get((E3D_Texture *)texture, min, mag); +} + +static void +eng_texture_image_set(void *data EINA_UNUSED, void *texture, void *image) +{ + Evas_GL_Image *im = (Evas_GL_Image *)image; + e3d_texture_import((E3D_Texture *)texture, im->tex->pt->texture); +} + static int module_open(Evas_Module *em) { @@ -3825,6 +4007,26 @@ module_open(Evas_Module *em) ORD(context_flush); + /* 3D features */ + ORD(drawable_new); + ORD(drawable_free); + ORD(drawable_size_get); + ORD(image_drawable_set); + + ORD(drawable_scene_render); + + ORD(texture_new); + ORD(texture_free); + ORD(texture_data_set); + ORD(texture_file_set); + ORD(texture_color_format_get); + ORD(texture_size_get); + ORD(texture_wrap_set); + ORD(texture_wrap_get); + ORD(texture_filter_set); + ORD(texture_filter_get); + ORD(texture_image_set); + /* now advertise out own api */ em->functions = (void *)(&func); return 1; diff --git a/src/modules/evas/engines/gl_x11/evas_engine.h b/src/modules/evas/engines/gl_x11/evas_engine.h index c03950b..2ae3757 100644 --- a/src/modules/evas/engines/gl_x11/evas_engine.h +++ b/src/modules/evas/engines/gl_x11/evas_engine.h @@ -58,6 +58,7 @@ extern int _evas_engine_GL_X11_log_dom ; #define CRI(...) EINA_LOG_DOM_CRIT(_evas_engine_GL_X11_log_dom, __VA_ARGS__) typedef struct _Evas_GL_X11_Window Evas_GL_X11_Window; +typedef struct _Evas_GL_X11_Context Evas_GL_X11_Context; struct _Evas_GL_X11_Window { @@ -91,6 +92,20 @@ struct _Evas_GL_X11_Window int surf : 1; }; +struct _Evas_GL_X11_Context +{ +#ifdef GL_GLES + EGLDisplay display; + EGLContext context; + EGLSurface surface; +#else + Display *display; + GLXContext context; + GLXWindow glxwin; + Window win; +#endif +}; + Evas_GL_X11_Window *eng_window_new(Display *disp, Window win, int screen, Visual *vis, Colormap cmap, int depth, int w, int h, int indirect, @@ -104,4 +119,8 @@ void *eng_best_visual_get(Evas_Engine_Info_GL_X11 *einfo); Colormap eng_best_colormap_get(Evas_Engine_Info_GL_X11 *einfo); int eng_best_depth_get(Evas_Engine_Info_GL_X11 *einfo); +Evas_GL_X11_Context *eng_gl_context_new(Evas_GL_X11_Window *win); +void eng_gl_context_free(Evas_GL_X11_Context *context); +void eng_gl_context_use(Evas_GL_X11_Context *context); + #endif diff --git a/src/modules/evas/engines/gl_x11/evas_x_main.c b/src/modules/evas/engines/gl_x11/evas_x_main.c index 1b7cab6..773bfba 100644 --- a/src/modules/evas/engines/gl_x11/evas_x_main.c +++ b/src/modules/evas/engines/gl_x11/evas_x_main.c @@ -849,3 +849,93 @@ eng_best_depth_get(Evas_Engine_Info_GL_X11 *einfo) } return _evas_gl_x11_vi->depth; } + +Evas_GL_X11_Context * +eng_gl_context_new(Evas_GL_X11_Window *win) +{ + Evas_GL_X11_Context *ctx; +#if GL_GLES + int context_attrs[3] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; +#endif + + if (!win) return NULL; + + ctx = calloc(1, sizeof(Evas_GL_X11_Context)); + if (!ctx) return NULL; + +#if GL_GLES + ctx->context = eglCreateContext(win->egl_disp, win->egl_config, + win->egl_context[0], context_attrs); + + if (!ctx->context) + { + ERR("EGL context creation failed."); + goto error; + } + + ctx->display = win->egl_disp; + ctx->surface = win->egl_surface[0]; +#else + ctx->context = glXCreateContext(win->disp, win->visualinfo, win->context, 1); + + if (!ctx->context) + { + ERR("GLX context creation failed."); + goto error; + } + + ctx->display = win->disp; + ctx->glxwin = win->glxwin; + ctx->win = win->win; +#endif + + return ctx; + +error: + free(ctx); + return NULL; +} + +void +eng_gl_context_free(Evas_GL_X11_Context *ctx) +{ +#if GL_GLES + eglDestroyContext(ctx->display, ctx->context); +#else + glXDestroyContext(ctx->display, ctx->context); +#endif + + free(ctx); +} + +void +eng_gl_context_use(Evas_GL_X11_Context *ctx) +{ +#if GL_GLES + if (eglMakeCurrent(ctx->display, ctx->surface, + ctx->surface, ctx->context) == EGL_FALSE) + { + ERR("eglMakeCurrent() failed."); + } +#else + if (ctx->glxwin) + { + if (!glXMakeContextCurrent(ctx->display, ctx->glxwin, + ctx->glxwin, ctx->context)) + { + ERR("glXMakeContextCurrent(%p, %p, %p, %p) faild.", + (void *)ctx->display, (void *)ctx->glxwin, + (void *)ctx->glxwin, (void *)ctx->context); + } + } + else + { + if (!glXMakeCurrent(ctx->display, ctx->win, ctx->context)) + { + ERR("glXMakeCurrent(%p, %p, %p) failed.", + (void *)ctx->display, (void *)ctx->win, + (void *)ctx->context); + } + } +#endif +} diff --git a/src/modules/evas/engines/software_generic/evas_engine.c b/src/modules/evas/engines/software_generic/evas_engine.c index 1b8f76a..70a625d 100644 --- a/src/modules/evas/engines/software_generic/evas_engine.c +++ b/src/modules/evas/engines/software_generic/evas_engine.c @@ -2712,6 +2712,22 @@ static Evas_Func func = eng_multi_font_draw, eng_pixel_alpha_get, NULL, // eng_context_flush - software doesn't use it + NULL, // eng_drawable_new + NULL, // eng_drawable_free + NULL, // eng_drawable_size_get + NULL, // eng_image_drawable_set + NULL, // eng_drawable_render_scene + NULL, // eng_texture_new + NULL, // eng_texture_free + NULL, // eng_texture_data_set + NULL, // eng_texture_file_set + NULL, // eng_texture_color_format_get + NULL, // eng_texture_size_get + NULL, // eng_texture_wrap_set + NULL, // eng_texture_wrap_get + NULL, // eng_texture_filter_set + NULL, // eng_texture_filter_get + NULL, // eng_texture_image_set /* FUTURE software generic calls go here */ };