[evas/evas_3D] Billboard mechanism
authorOleksandr Shcherbina <o.shcherbina@samsung.com>
Mon, 6 Apr 2015 05:10:28 +0000 (14:10 +0900)
committerChunEon Park <chuneon.park@samsung.com>
Mon, 6 Apr 2015 05:10:28 +0000 (14:10 +0900)
Summary:
Add pointer to target billboard node in Evas_3D_Node
Skip set flags change orientation for billboard node
Add method node_billboard_update to use it for change orientation during traverse
by nodes
Split API evas_3d_node_look_at_set to have possibility change orientation of node
without set flags

Reviewers: cedric, Hermet

Subscribers: cedric

Differential Revision: https://phab.enlightenment.org/D2245

src/examples/evas/Makefile.am
src/examples/evas/evas-3d-shadows.c
src/examples/evas/resources/images/billboard.png [new file with mode: 0644]
src/lib/evas/Evas_Eo.h
src/lib/evas/canvas/evas_3d_node.c
src/lib/evas/canvas/evas_3d_node.eo
src/lib/evas/include/evas_private.h

index 0218472..6a47303 100644 (file)
@@ -359,6 +359,7 @@ resources/images/four_NM_height.tga \
 resources/images/rocks_NM_height.tga \
 resources/images/grid.png \
 resources/images/grid_n.png \
+resources/images/billboard.png \
 resources/models/mesh_for_mmap.eet \
 resources/models/mesh_for_mmap.md2 \
 resources/models/mesh_for_mmap.obj \
index c1eb087..701a474 100644 (file)
@@ -1,9 +1,11 @@
 /**
- * This example illustrating use of shadows in the scene and callbacks(clicked, collision).
+ * This example illustrating use of shadows, callbacks(clicked, collision),
+ * and technic of the billboard.
  * Model and cube are clickable. Model detects collision with sphere.
  * Cube detects collision with sphere, model and cone.
  * @see evas_3d_scene_shadows_enable_set(Eina_Bool _shadows_enabled)
  * @see evas_3d_object_callback_register
+ * @see evas_3d_billboard_set/get
  *
  * @verbatim
  * gcc -o evas-3d-shadows evas-3d-shadows.c evas-3d-primitives.c `pkg-config --libs --cflags efl evas ecore ecore-evas eo eina` -lm
@@ -37,7 +39,8 @@
 #define SPECULAR_LIGHT 1.0, 1.0, 1.0
 
 static const char *model_path = PACKAGE_EXAMPLES_DIR EVAS_MODEL_FOLDER "/sonic.md2";
-
+static const char *image_path = PACKAGE_EXAMPLES_DIR EVAS_IMAGE_FOLDER "/sonic.png";
+static const char *b_image_path = PACKAGE_EXAMPLES_DIR EVAS_IMAGE_FOLDER "/billboard.png";
 static const vec2 tex_scale = {1, 1};
 static const vec2 fence_tex_scale = {80, 6};
 
@@ -89,6 +92,7 @@ typedef struct _Body_3D
    Eo     *material;
    Eo     *mesh;
    Eo     *node;
+   Eo     *texture;
 } Body_3D;
 
 typedef struct _Scene_Data
@@ -99,6 +103,7 @@ typedef struct _Scene_Data
    Eo     *camera;
    Eo     *light_node;
    Eo     *light;
+   Eo     *mediator;
 
    Body_3D     sphere;
    Body_3D     cube;
@@ -107,6 +112,9 @@ typedef struct _Scene_Data
    Body_3D     model;
    Body_3D     cone;
    Body_3D     fence;
+   Body_3D     billboard;
+
+   Eina_Bool   init;
 } Scene_Data;
 
 static void
@@ -114,23 +122,37 @@ _show_help()
 {
    fprintf(stdout, "Press 'w'/'s' key to move up/down object\n");
    fprintf(stdout, "Press 'a'/'d' key to move left/right object\n");
-   fprintf(stdout, "Press 'q'/'e' key to to move near/far object\n");
+   fprintf(stdout, "Press 'q'/'e' key to move near/far object\n");
    fprintf(stdout, "Cude and model can be moved.\n");
    fprintf(stdout, "Cube detects intersection with model, sphere, cone\n");
    fprintf(stdout, "Model detects intersection with sphere\n");
+   fprintf(stdout, "Press '1'/'2' key to change kind of node - billboard/normal model\n");
+   fprintf(stdout, "Press Up/Down key to change position of camera\n");
+   fprintf(stdout, "Press 'i' key to return initial view of scene\n");
 }
 
 static Eina_Bool
 _animate_scene(void *data)
 {
-   static int frame = 0;
-   Body_3D *body = (Body_3D *)data;
-
-   eo_do(body->node, evas_3d_node_mesh_frame_set(body->mesh, frame));
-
-   /*frame += 32;*/
-
-   if (frame > 256 * 20) frame = 0;
+   static float angle = 0;
+   Evas_Real x, y, z;
+   Scene_Data *scene = (Scene_Data *)data;
+   if (scene->init)
+     {
+        eo_do(scene->mediator,
+              evas_3d_node_position_get(EVAS_3D_SPACE_PARENT, &x, &y, &z));
+        eo_do(scene->mediator,
+              evas_3d_node_position_set(sin(angle) * 20 , y , cos(angle) * 20),
+              evas_3d_node_look_at_set(EVAS_3D_SPACE_PARENT, 0.0, 3.0, 0.0,
+                                        EVAS_3D_SPACE_PARENT, 0.0, 5.0, 0.0));
+        angle += 0.005;
+        if (angle > 360) angle = 0.0;
+     }
+   else
+     eo_do(scene->mediator,
+           evas_3d_node_position_set(0.0, 6.0, 12.0),
+           evas_3d_node_look_at_set(EVAS_3D_SPACE_PARENT, 0.0, 3.0, 0.0,
+                                    EVAS_3D_SPACE_PARENT, 0.0, 5.0, 0.0));
 
    return EINA_TRUE;
 }
@@ -241,6 +263,9 @@ _fence_setup(Body_3D *fence)
          evas_3d_material_enable_set(EVAS_3D_MATERIAL_SPECULAR, EINA_TRUE),
          evas_3d_material_enable_set(EVAS_3D_MATERIAL_NORMAL, EINA_TRUE),
          evas_3d_material_texture_set(EVAS_3D_MATERIAL_NORMAL, texture1),
+         evas_3d_material_color_set(EVAS_3D_MATERIAL_AMBIENT, 1.0, 1.0, 1.0, 1.0),
+         evas_3d_material_color_set(EVAS_3D_MATERIAL_DIFFUSE, 1.0, 1.0, 1.0, 1.0),
+         evas_3d_material_color_set(EVAS_3D_MATERIAL_SPECULAR, 1.0, 1.0, 1.0, 1.0),
          evas_3d_material_shininess_set(100.0));
    fence->mesh = eo_add(EVAS_3D_MESH_CLASS, evas);
    evas_3d_add_cylinder_frame(fence->mesh, 0, 50, fence_tex_scale);
@@ -292,9 +317,9 @@ _box_setup(Body_3D *box)
 static void
 _model_setup(Body_3D *model)
 {
-   Eo *texture = eo_add(EVAS_3D_TEXTURE_CLASS, evas);
-   eo_do(texture,
-         evas_3d_texture_file_set(PACKAGE_EXAMPLES_DIR EVAS_IMAGE_FOLDER "/sonic.png", NULL),
+   model->texture = eo_add(EVAS_3D_TEXTURE_CLASS, evas);
+   eo_do(model->texture,
+         evas_3d_texture_file_set(image_path, NULL),
          evas_3d_texture_filter_set(EVAS_3D_TEXTURE_FILTER_NEAREST,
                                     EVAS_3D_TEXTURE_FILTER_NEAREST),
          evas_3d_texture_wrap_set(EVAS_3D_WRAP_MODE_REPEAT,
@@ -302,8 +327,8 @@ _model_setup(Body_3D *model)
    model->material = eo_add(EVAS_3D_MATERIAL_CLASS, evas);
 
    eo_do(model->material,
-         evas_3d_material_texture_set(EVAS_3D_MATERIAL_DIFFUSE, texture),
-         evas_3d_material_texture_set(EVAS_3D_MATERIAL_AMBIENT, texture),
+         evas_3d_material_texture_set(EVAS_3D_MATERIAL_DIFFUSE, model->texture),
+         evas_3d_material_texture_set(EVAS_3D_MATERIAL_AMBIENT, model->texture),
          evas_3d_material_enable_set(EVAS_3D_MATERIAL_AMBIENT, EINA_TRUE),
          evas_3d_material_enable_set(EVAS_3D_MATERIAL_DIFFUSE, EINA_TRUE),
          evas_3d_material_enable_set(EVAS_3D_MATERIAL_SPECULAR, EINA_TRUE),
@@ -311,7 +336,6 @@ _model_setup(Body_3D *model)
 
 
    model->mesh = eo_add(EVAS_3D_MESH_CLASS, evas);
-
    eo_do(model->mesh,
          efl_file_set(model_path, NULL),
          evas_3d_mesh_frame_material_set(0, model->material),
@@ -326,24 +350,69 @@ _model_setup(Body_3D *model)
 }
 
 static void
+_billboard_setup(Scene_Data *data)
+{
+   data->billboard.texture = eo_add(EVAS_3D_TEXTURE_CLASS, evas);
+   eo_do(data->billboard.texture,
+         evas_3d_texture_file_set(b_image_path, NULL),
+         evas_3d_texture_filter_set(EVAS_3D_TEXTURE_FILTER_NEAREST,
+                                    EVAS_3D_TEXTURE_FILTER_NEAREST),
+         evas_3d_texture_wrap_set(EVAS_3D_WRAP_MODE_REPEAT,
+                                  EVAS_3D_WRAP_MODE_REPEAT));
+
+   data->billboard.mesh = eo_add(EVAS_3D_MESH_CLASS, evas);
+   evas_3d_add_square_frame(data->billboard.mesh, 0);
+
+   _body_material_set(&(data->billboard), 1.0, 1.0, 1.0);
+
+   eo_do(data->billboard.material,
+         evas_3d_material_texture_set(EVAS_3D_MATERIAL_DIFFUSE, data->billboard.texture));
+
+   eo_do(data->billboard.mesh,
+         evas_3d_mesh_frame_material_set(0, data->billboard.material),
+         evas_3d_mesh_alpha_func_set(EVAS_3D_COMPARISON_GREATER, 0),
+         evas_3d_mesh_alpha_test_enable_set(EINA_TRUE),
+         evas_3d_mesh_shade_mode_set(EVAS_3D_SHADE_MODE_DIFFUSE),
+         evas_3d_mesh_blending_enable_set(EINA_TRUE),
+         evas_3d_mesh_blending_func_set(EVAS_3D_BLEND_SRC_ALPHA,
+                                        EVAS_3D_BLEND_ONE_MINUS_SRC_ALPHA));
+
+   data->billboard.node = eo_add(EVAS_3D_NODE_CLASS, evas,
+                                 evas_3d_node_constructor(EVAS_3D_NODE_TYPE_MESH));
+   eo_do(data->billboard.node,
+         evas_3d_node_mesh_add(data->billboard.mesh),
+         evas_3d_node_position_set(0.0, 2.0, 0.0),
+         evas_3d_node_scale_set(2.2, 4.6, 4.0));
+
+   eo_do(data->billboard.node,
+         evas_3d_node_billboard_target_set(data->mediator));
+}
+
+static void
 _camera_setup(Scene_Data *data)
 {
    data->camera = eo_add(EVAS_3D_CAMERA_CLASS, evas);
+   data->mediator = eo_add(EVAS_3D_NODE_CLASS, evas,
+                           evas_3d_node_constructor(EVAS_3D_NODE_TYPE_NODE));
 
    eo_do(data->camera,
-         evas_3d_camera_projection_perspective_set(50.0, 1.0, 2.0, 50.0));
+         evas_3d_camera_projection_perspective_set(50.0, 1.0, 2.0, 100.0));
 
   data->camera_node =
       eo_add(EVAS_3D_NODE_CLASS, evas,
                     evas_3d_node_constructor(EVAS_3D_NODE_TYPE_CAMERA));
 
   eo_do(data->camera_node,
-        evas_3d_node_camera_set(data->camera),
+        evas_3d_node_camera_set(data->camera));
+
+
+  eo_do(data->mediator,
         evas_3d_node_position_set(0.0, 6.0, 12.0),
         evas_3d_node_look_at_set(EVAS_3D_SPACE_PARENT, 0.0, 3.0, 0.0,
                                   EVAS_3D_SPACE_PARENT, 0.0, 5.0, 0.0));
 
-  eo_do(data->root_node, evas_3d_node_member_add(data->camera_node));
+  eo_do(data->mediator, evas_3d_node_member_add(data->camera_node));
+  eo_do(data->root_node, evas_3d_node_member_add(data->mediator));
 }
 
 static void
@@ -362,7 +431,7 @@ _light_setup(Scene_Data *data)
                     evas_3d_node_constructor(EVAS_3D_NODE_TYPE_LIGHT));
    eo_do(data->light_node,
          evas_3d_node_light_set(data->light),
-         evas_3d_node_position_set(50.0, 50.0, 20.0),
+         evas_3d_node_position_set(50.0, 50.0, 70.0),
          evas_3d_node_look_at_set(EVAS_3D_SPACE_PARENT, 15.0, 0.0, -5.0,
                                   EVAS_3D_SPACE_PARENT, 0.0, 0.0, 1.0));
    eo_do(data->root_node, evas_3d_node_member_add(data->light_node));
@@ -371,6 +440,8 @@ _light_setup(Scene_Data *data)
 static void
 _scene_setup(Scene_Data *data)
 {
+   data->init = EINA_FALSE;
+
    data->scene = eo_add(EVAS_3D_SCENE_CLASS, evas);
 
    eo_do(data->scene,
@@ -392,8 +463,9 @@ _scene_setup(Scene_Data *data)
    _model_setup(&data->model);
    _cone_setup(&data->cone);
    _fence_setup(&data->fence);
+   _billboard_setup(data);
 
-   eo_do(data->root_node, 
+   eo_do(data->root_node,
          evas_3d_node_member_add(data->sphere.node),
          evas_3d_node_member_add(data->cube.node),
          evas_3d_node_member_add(data->cylinder.node),
@@ -412,6 +484,7 @@ static void
 _on_key_down(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *eo EINA_UNUSED, void *event_info)
 {
    Evas_Event_Key_Down *ev = event_info;
+   Scene_Data *scene = (Scene_Data *)data;
    if (!strcmp("w", ev->key))
      {
         Evas_Real x, y, z;
@@ -448,6 +521,34 @@ _on_key_down(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *eo EINA_U
         eo_do(choosed_node, evas_3d_node_position_get(EVAS_3D_SPACE_PARENT, &x, &y, &z));
         eo_do(choosed_node, evas_3d_node_position_set(x, y, z + STEP));
      }
+   else if(!strcmp("1", ev->key))
+     {
+        eo_do(scene->root_node, evas_3d_node_member_del(scene->model.node));
+        eo_do(scene->root_node, evas_3d_node_member_add(scene->billboard.node));
+     }
+   else if(!strcmp("2", ev->key))
+     {
+        eo_do(scene->root_node, evas_3d_node_member_add(scene->model.node));
+        eo_do(scene->root_node, evas_3d_node_member_del(scene->billboard.node));
+     }
+   else if(!strcmp("Up", ev->key))
+     {
+        Evas_Real x, y, z;
+        eo_do(scene->camera_node, evas_3d_node_position_get(EVAS_3D_SPACE_PARENT, &x, &y, &z));
+        eo_do(scene->camera_node, evas_3d_node_position_set(x, y, z + STEP));
+     }
+   else if(!strcmp("Down", ev->key))
+     {
+        Evas_Real x, y, z;
+        eo_do(scene->camera_node, evas_3d_node_position_get(EVAS_3D_SPACE_PARENT, &x, &y, &z));
+        eo_do(scene->camera_node, evas_3d_node_position_set(x, y, z - STEP));
+     }
+   else if (!strcmp("i", ev->key))
+     {
+        scene->init = !scene->init;
+        eo_do(scene->model.node, evas_3d_node_position_set(0.0, 0.0, 0.0));
+        eo_do(scene->billboard.node, evas_3d_node_position_set(0.0, 2.0, 0.0));
+     }
    else
      {
         _show_help();
@@ -551,12 +652,15 @@ main(void)
    eo_do(data.model.node, eo_event_callback_add(EVAS_3D_OBJECT_EVENT_CLICKED, _cb_clicked, NULL));
    eo_do(data.model.node, eo_event_callback_add(EVAS_3D_OBJECT_EVENT_COLLISION, _cb_collision, nodes1));
 
+   eo_do(data.billboard.node, eo_event_callback_add(EVAS_3D_OBJECT_EVENT_CLICKED, _cb_clicked, NULL));
+   eo_do(data.billboard.node, eo_event_callback_add(EVAS_3D_OBJECT_EVENT_COLLISION, _cb_collision, nodes1));
+
    evas_object_event_callback_add(image, EVAS_CALLBACK_MOUSE_DOWN, _on_mouse_down, &data);
    evas_object_event_callback_add(image, EVAS_CALLBACK_KEY_DOWN, _on_key_down, &data);
 
    /* Add animator. */
    ecore_animator_frametime_set(0.008);
-   anim = ecore_animator_add(_animate_scene, &data.model);
+   anim = ecore_animator_add(_animate_scene, &data);
 
    /* Enter main loop. */
    ecore_main_loop_begin();
diff --git a/src/examples/evas/resources/images/billboard.png b/src/examples/evas/resources/images/billboard.png
new file mode 100644 (file)
index 0000000..0a6915d
Binary files /dev/null and b/src/examples/evas/resources/images/billboard.png differ
index 1e1c639..1e1fb59 100644 (file)
@@ -516,6 +516,7 @@ typedef enum _Evas_3D_State
    EVAS_3D_STATE_NODE_PARENT_ORIENTATION,
    EVAS_3D_STATE_NODE_PARENT_SCALE,
    EVAS_3D_STATE_NODE_MEMBER,
+   EVAS_3D_STATE_NODE_PARENT_BILLBOARD,
 } Evas_3D_State;
 
 /**
index 1f69cdc..746665e 100644 (file)
@@ -7,6 +7,9 @@
 
 Evas_3D_Mesh_Frame *evas_3d_mesh_frame_find(Evas_3D_Mesh_Data *pd, int frame);
 
+static void
+_look_at_set(Evas_3D_Node_Data *pd, Evas_Vec3 *target, Evas_Vec3 *up);
+
 static Eina_Stringshare *
 _generate_unic_color_key(Evas_Color *color, Evas_Color *bg_color, Evas_3D_Node *node, Evas_3D_Mesh *mesh,
                          Eina_Bool init)
@@ -156,9 +159,13 @@ _evas_3d_node_evas_3d_object_change_notify(Eo *obj, Evas_3D_Node_Data *pd, Evas_
        {
           eo_do(n, evas_3d_object_change(EVAS_3D_STATE_NODE_PARENT_SCALE, obj));
        }
-   if (orientation)
+   if (orientation && !(pd->billboard_target))
      EINA_LIST_FOREACH(pd->members, l, n)
        {
+          /*Skip change orientation if node is billboard*/
+          Evas_3D_Node_Data *pdm = eo_data_scope_get(n, EVAS_3D_NODE_CLASS);
+          if (pdm->billboard_target)
+            continue;
           eo_do(n, evas_3d_object_change(EVAS_3D_STATE_NODE_PARENT_ORIENTATION, obj));
        }
    if (position)
@@ -286,6 +293,46 @@ _node_transform_update(Evas_3D_Node *node, void *data EINA_UNUSED)
 }
 
 static Eina_Bool
+_node_billboard_update(Evas_3D_Node *node, void *data EINA_UNUSED)
+{
+   Evas_Vec3   target;
+   Evas_Vec3   up;
+   Evas_3D_Node_Data *pd_node = eo_data_scope_get(node, MY_CLASS);
+   if (pd_node->billboard_target)
+     {
+        Evas_3D_Node_Data *pd_target = eo_data_scope_get(pd_node->billboard_target,
+                                                       MY_CLASS);
+        evas_vec3_set(&target, pd_target->position.x, pd_target->position.y,
+                      pd_target->position.z);
+        evas_vec3_set(&up, 0, 1, 0);
+
+        _look_at_set(pd_node, &target, &up);
+
+        if (pd_node->type == EVAS_3D_NODE_TYPE_MESH)
+          {
+             evas_mat4_build(&pd_node->data.mesh.matrix_local_to_world,
+                             &pd_node->position, &pd_node->orientation,
+                             &pd_node->scale);
+          }
+        else if (pd_node->type == EVAS_3D_NODE_TYPE_LIGHT)
+          {
+             evas_mat4_build(&pd_node->data.light.matrix_local_to_world,
+                             &pd_node->position, &pd_node->orientation,
+                             &pd_node->scale);
+          }
+        else if (pd_node->type == EVAS_3D_NODE_TYPE_CAMERA)
+          {
+             evas_mat4_inverse_build(&pd_node->data.light.matrix_local_to_world,
+                                     &pd_node->position, &pd_node->orientation,
+                                     &pd_node->scale);
+          }
+        else
+          ERR("Not supported type of node: line %d in file %s", __LINE__ , __FILE__);
+     }
+   return EINA_TRUE;
+}
+
+static Eina_Bool
 _node_item_update(Evas_3D_Node *node, void *data EINA_UNUSED)
 {
    Evas_3D_Node_Data *pd = eo_data_scope_get(node, EVAS_3D_NODE_CLASS);
@@ -589,7 +636,9 @@ _evas_3d_node_evas_3d_object_update_notify(Eo *obj, Evas_3D_Node_Data *pd EINA_U
    /* Update transform. */
    evas_3d_node_tree_traverse(obj, EVAS_3D_TREE_TRAVERSE_LEVEL_ORDER, EINA_FALSE,
                               _node_transform_update, NULL);
-
+   /*Update billboard*/
+   evas_3d_node_tree_traverse(obj, EVAS_3D_TREE_TRAVERSE_ANY_ORDER, EINA_FALSE,
+                              _node_billboard_update, NULL);
    /* Update AABB. */
    evas_3d_node_tree_traverse(obj, EVAS_3D_TREE_TRAVERSE_POST_ORDER, EINA_FALSE,
                               _node_aabb_update, NULL);
@@ -1035,6 +1084,7 @@ _evas_3d_node_constructor(Eo *obj, Evas_3D_Node_Data *pd, Evas_3D_Node_Type type
    pd->orientation_inherit = EINA_TRUE;
    pd->scale_inherit = EINA_TRUE;
    pd->data.mesh.node_meshes = 0;
+   pd->billboard_target = NULL;
 
    evas_box3_set(&pd->aabb, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
 
@@ -1310,62 +1360,15 @@ _evas_3d_node_scale_inherit_get(Eo *obj EINA_UNUSED, Evas_3D_Node_Data *pd)
    return pd->scale_inherit;
 }
 
-EOLIAN static void
-_evas_3d_node_look_at_set(Eo *obj, Evas_3D_Node_Data *pd,
-                         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)
+static void
+_look_at_set(Evas_3D_Node_Data *pd, Evas_Vec3 *target, Evas_Vec3 *up)
 {
-   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)
-     {
-       evas_vec3_set(&up, ux, uy, uz);
-        //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, &pd->position, &target);
+   evas_vec3_subtract(&z, &pd->position, target);
    evas_vec3_normalize(&z, &z);
 
-   evas_vec3_cross_product(&x, &up, &z);
+   evas_vec3_cross_product(&x, up, &z);
    evas_vec3_normalize(&x, &x);
 
    evas_vec3_cross_product(&y, &z, &x);
@@ -1416,6 +1419,60 @@ _evas_3d_node_look_at_set(Eo *obj, Evas_3D_Node_Data *pd,
         pd->orientation.x = (z.x + x.z) * s;
         pd->orientation.y = (y.z + z.y) * s;
      }
+}
+
+EOLIAN static void
+_evas_3d_node_look_at_set(Eo *obj, Evas_3D_Node_Data *pd,
+                         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;
+
+   /* 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)
+     {
+       evas_vec3_set(&up, ux, uy, uz);
+        //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;
+     }
+
+   _look_at_set(pd, &target, &up);
 
    eo_do(obj, evas_3d_object_change(EVAS_3D_STATE_NODE_TRANSFORM_ORIENTATION, NULL));
 }
@@ -1624,4 +1681,20 @@ _evas_3d_node_bounding_sphere_get(Eo *obj EINA_UNUSED, Evas_3D_Node_Data *pd, Ev
    if (r) *r = pd->bsphere.radius;
 }
 
+EOLIAN static void
+_evas_3d_node_billboard_target_set(Eo *obj EINA_UNUSED, Evas_3D_Node_Data *pd,
+                                   Eo *target)
+{
+   if (pd->billboard_target != target)
+     {
+        pd->billboard_target = target;
+        eo_do(obj, evas_3d_object_change(EVAS_3D_STATE_NODE_PARENT_BILLBOARD, NULL));
+     }
+}
+
+EOLIAN static Evas_3D_Node *
+_evas_3d_node_billboard_target_get(Eo *obj EINA_UNUSED, Evas_3D_Node_Data *pd)
+{
+   return pd->billboard_target;
+}
 #include "canvas/evas_3d_node.eo.c"
index b602fd2..4ecb261 100644 (file)
@@ -576,6 +576,35 @@ class Evas_3D_Node (Evas_3D_Object, Evas.Common_Interface)
             int frame; /*@ The animation frame number.*/
          }
       }
+
+      billboard_target {
+         set {
+            /*
+              Set behavior of node like billboard object.
+
+              @param node          The given node.
+              @param billboard     Pointer to target node for given node.
+
+              @ingroup Evas_3D_Node
+             */
+         }
+         get {
+            /*
+              Get the target node for billboard object.
+
+              @param node          The given node.
+              @return              The pointer to target node for billboard object,
+                                   or @c NULL if there're none.
+
+              @see evas_3d_node_billboard_set()
+
+              @ingroup Evas_3D_Node
+             */
+         }
+         values {
+            Eo *target;      /*@ Target node.*/
+         }
+      }
    }
    implements {
       Eo.Base.destructor;
index c44a240..03df174 100644 (file)
@@ -209,6 +209,7 @@ struct _Evas_3D_Node
 {
    Eina_List        *members;
    Evas_3D_Node     *parent;
+   Evas_3D_Node     *billboard_target;
 
    Evas_Vec3         position;
    Evas_Vec4         orientation;