diff --git a/legacy/ephysics/src/bin/Makefile.am b/legacy/ephysics/src/bin/Makefile.am index 809f80b2db..10c5e1f4de 100644 --- a/legacy/ephysics/src/bin/Makefile.am +++ b/legacy/ephysics/src/bin/Makefile.am @@ -19,6 +19,7 @@ test.c \ test_bouncing_ball.c \ test_bouncing_text.c \ test_camera.c \ +test_camera_track.c \ test_colliding_balls.c \ test_collision_detection.c \ test_collision_filter.c \ diff --git a/legacy/ephysics/src/bin/test.c b/legacy/ephysics/src/bin/test.c index 537d5b7d66..53b675127d 100644 --- a/legacy/ephysics/src/bin/test.c +++ b/legacy/ephysics/src/bin/test.c @@ -12,6 +12,7 @@ int _ephysics_test_log_dom = -1; void test_bouncing_ball(void *data, Evas_Object *obj, void *event_info); void test_bouncing_text(void *data, Evas_Object *obj, void *event_info); void test_camera(void *data, Evas_Object *obj, void *event_info); +void test_camera_track(void *data, Evas_Object *obj, void *event_info); void test_colliding_balls(void *data, Evas_Object *obj, void *event_info); void test_collision(void *data, Evas_Object *obj, void *event_info); void test_collision_filter(void *data, Evas_Object *obj, void *event_info); @@ -65,7 +66,6 @@ update_object_cb(void *data, EPhysics_Body *body, void *event_info) edje_object_message_send(edje, EDJE_MESSAGE_FLOAT, SHADOW_ALPHA_ID, &msg); } - void test_clean(Test_Data *test_data) { @@ -154,6 +154,7 @@ _main_win_add(char *autorun __UNUSED__, Eina_Bool test_win_only __UNUSED__) ADD_TEST("BOUNCING BALL", test_bouncing_ball); ADD_TEST("BOUNCING TEXT", test_bouncing_text); ADD_TEST("CAMERA", test_camera); + ADD_TEST("CAMERA TRACK", test_camera_track); ADD_TEST("COLLIDING BALLS", test_colliding_balls); ADD_TEST("COLLISION DETECTION", test_collision); ADD_TEST("COLLISION FILTER", test_collision_filter); diff --git a/legacy/ephysics/src/bin/test_camera_track.c b/legacy/ephysics/src/bin/test_camera_track.c new file mode 100644 index 0000000000..e2bf0c6cfa --- /dev/null +++ b/legacy/ephysics/src/bin/test_camera_track.c @@ -0,0 +1,192 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "ephysics_test.h" + +typedef struct _Track_Data Track_Data; + +struct _Track_Data { + Test_Data base; + EPhysics_Body *body; + Evas_Object *sp; +}; + +static void +_track_apply(Track_Data *track_data) +{ + Eina_Bool hor = EINA_FALSE; + Eina_Bool ver = EINA_FALSE; + EPhysics_Camera *camera; + EPhysics_Body *body; + int mode; + + body = track_data->body; + camera = ephysics_world_camera_get(track_data->base.world); + mode = (int) elm_spinner_value_get(track_data->sp); + + switch (mode) + { + case 1: + hor = EINA_TRUE; + break; + case 3: + hor = EINA_TRUE; + case 2: + ver = EINA_TRUE; + } + + INF("Tracking mode: hor = %i, ver = %i", hor, ver); + ephysics_camera_body_track(camera, body, hor, ver); +} + +static void +_tracking_mode_cb(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + _track_apply(data); +} + +static void +_world_populate(Track_Data *track_data) +{ + static const char *colors[] = {"blue-cube", "purple-cube"}; + Evas_Object *cube, *sphere, *shadow; + EPhysics_Body *body; + int i, color, row; + + for (i = 0; i < 9; i++) + { + color = i % 3 % 2; + row = i / 3; + + cube = elm_image_add(track_data->base.win); + elm_image_file_set( + cube, PACKAGE_DATA_DIR "/" EPHYSICS_TEST_THEME ".edj", + colors[color]); + evas_object_move(cube, i * 70, (row + 2) * 70); + evas_object_resize(cube, 70, 70); + evas_object_show(cube); + track_data->base.evas_objs = eina_list_append( + track_data->base.evas_objs, cube); + + body = ephysics_body_box_add(track_data->base.world); + ephysics_body_evas_object_set(body, cube, EINA_TRUE); + ephysics_body_restitution_set(body, 0.95); + ephysics_body_friction_set(body, 0.1); + ephysics_body_mass_set(body, 0); + track_data->base.bodies = eina_list_append( + track_data->base.bodies, body); + } + + shadow = elm_layout_add(track_data->base.win); + elm_layout_file_set( + shadow, PACKAGE_DATA_DIR "/" EPHYSICS_TEST_THEME ".edj", + "shadow-ball"); + evas_object_move(shadow, 0, FLOOR_Y); + evas_object_resize(shadow, 54, 3); + evas_object_show(shadow); + track_data->base.evas_objs = eina_list_append(track_data->base.evas_objs, + shadow); + + sphere = elm_image_add(track_data->base.win); + elm_image_file_set( + sphere, PACKAGE_DATA_DIR "/" EPHYSICS_TEST_THEME ".edj", "green-ball"); + evas_object_move(sphere, 0, 80); + evas_object_resize(sphere, 54, 54); + evas_object_show(sphere); + track_data->base.evas_objs = eina_list_append(track_data->base.evas_objs, + sphere); + + body = ephysics_body_circle_add(track_data->base.world); + ephysics_body_evas_object_set(body, sphere, EINA_TRUE); + ephysics_body_event_callback_add(body, + EPHYSICS_CALLBACK_BODY_UPDATE, + update_object_cb, shadow); + ephysics_body_restitution_set(body, 0.95); + ephysics_body_friction_set(body, 0.1); + ephysics_body_central_impulse_apply(body, 3, 0); + track_data->body = body; + track_data->base.bodies = eina_list_append(track_data->base.bodies, body); +} + +static void +_restart(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) +{ + Track_Data *track_data = data; + EPhysics_Camera *camera; + + DBG("Restart pressed"); + + test_clean((Test_Data *)track_data); + _world_populate(track_data); + + camera = ephysics_world_camera_get(track_data->base.world); + ephysics_camera_position_set(camera, 50, 40); + _track_apply(track_data); +} + +static void +_win_del(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Track_Data *track_data = data; + + test_clean((Test_Data *)track_data); + evas_object_del(track_data->base.layout); + ephysics_world_del(track_data->base.world); + free(track_data); + ephysics_shutdown(); +} + +void +test_camera_track(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + EPhysics_Body *boundary; + EPhysics_World *world; + Track_Data *track_data; + Evas_Object *sp; + + if (!ephysics_init()) + return; + + track_data = calloc(1, sizeof(Track_Data)); + if (!track_data) + { + ERR("Failed to create test data"); + ephysics_shutdown(); + return; + } + + test_win_add((Test_Data *) track_data, "Camera Track", EINA_FALSE); + evas_object_smart_callback_add(track_data->base.win, + "delete,request", _win_del, track_data); + + elm_layout_signal_callback_add(track_data->base.layout, "restart", + "test-theme", _restart, track_data); + + sp = elm_spinner_add(track_data->base.win); + elm_spinner_min_max_set(sp, 0, 3); + elm_spinner_step_set(sp, 1); + elm_spinner_wrap_set(sp, EINA_TRUE); + elm_spinner_special_value_add(sp, 0, "No tracking"); + elm_spinner_special_value_add(sp, 1, "Horizontal tracking"); + elm_spinner_special_value_add(sp, 2, "Vertical tracking"); + elm_spinner_special_value_add(sp, 3, "Full tracking"); + elm_spinner_editable_set(sp, EINA_FALSE); + elm_object_style_set(sp, "ephysics-test"); + evas_object_smart_callback_add(sp, "delay,changed", _tracking_mode_cb, + track_data); + elm_layout_content_set(track_data->base.layout, "extra_input", sp); + track_data->sp = sp; + + world = ephysics_world_new(); + ephysics_world_render_geometry_set(world, 50, 40, WIDTH - 100, FLOOR_Y - 40); + track_data->base.world = world; + + boundary = ephysics_body_box_add(track_data->base.world); + ephysics_body_mass_set(boundary, 0); + ephysics_body_geometry_set(boundary, 0, FLOOR_Y, WIDTH * 4, 10); + ephysics_body_restitution_set(boundary, 0.65); + ephysics_body_friction_set(boundary, 4); + + _world_populate(track_data); +} diff --git a/legacy/ephysics/src/lib/EPhysics.h b/legacy/ephysics/src/lib/EPhysics.h index ad5e358b48..8c42394fc4 100644 --- a/legacy/ephysics/src/lib/EPhysics.h +++ b/legacy/ephysics/src/lib/EPhysics.h @@ -119,6 +119,18 @@ EAPI int ephysics_shutdown(void); * @} */ +/** + * @typedef EPhysics_Body + * + * Body handle, represents an object on EPhysics world. + * + * Created with @ref ephysics_body_circle_add() or @ref ephysics_body_box_add() + * and deleted with @ref ephysics_body_del(). + * + * @ingroup EPhysics_Body + */ +typedef struct _EPhysics_Body EPhysics_Body; + /** * @defgroup EPhysics_Camera EPhysics Camera * @ingroup EPhysics @@ -158,6 +170,9 @@ typedef struct _EPhysics_Camera EPhysics_Camera; /**< Camera handle, used to zoo * So if you have a scene larger than the render area, camera handling can * be very useful. * + * This function will make camera stop tracking a body set with + * @ref ephysics_camera_body_track(). + * * @note This change will be noticed on the next physics tick, so evas objects * will be updated taking the camera's new position in account. * @@ -187,6 +202,58 @@ EAPI void ephysics_camera_position_set(EPhysics_Camera *camera, Evas_Coord x, Ev */ EAPI void ephysics_camera_position_get(const EPhysics_Camera *camera, Evas_Coord *x, Evas_Coord *y); +/** + * @brief + * Set camera to track a body. + * + * When a body is tracked, the camera will move automatically, following + * this body. It will keeps the body centralized on rendered area. + * If it will be centralized horizontally and / or vertically depends + * if parameters @p horizontal and @p vertical are set to @c EINA_TRUE. + * + * Default updates (@ref ephysics_body_evas_object_update()) + * will take care of updating evas objects associated + * to the bodies correctly. But if you need to do it yourself, you'll need + * to take camera's position in consideration, using + * @ref ephysics_camera_position_get(). + * + * @note This change will be noticed on the next physics tick, so evas objects + * will be updated taking the camera's new position in account. + * + * @param camera The world's camera. + * @param body The body tracked by the @p camera, or @c NULL if camera isn't + * tracking any body. + * @param horizontal @c EINA_TRUE if @p body is tracked on x axis, + * @c EINA_FALSE otherwise; + * @param vertical @c EINA_TRUE if @p body is tracked on y axis, + * @c EINA_FALSE otherwise; + * + * @see ephysics_camera_tracked_body_get(). + * @see ephysics_camera_position_set(). + * @see ephysics_world_camera_get(). + * + * @ingroup EPhysics_Camera + */ +EAPI void ephysics_camera_body_track(EPhysics_Camera *camera, EPhysics_Body *body, Eina_Bool horizontal, Eina_Bool vertical); + +/** + * @brief + * Get body tracked by camera. + * + * @param camera The world's camera. + * @param body The body tracked by the @p camera, or @c NULL if camera isn't + * tracking any body. + * @param horizontal @c EINA_TRUE if @p body is tracked on x axis, + * @c EINA_FALSE otherwise; + * @param vertical @c EINA_TRUE if @p body is tracked on y axis, + * @c EINA_FALSE otherwise; + * + * @see ephysics_camera_body_track() for more details. + * + * @ingroup EPhysics_Camera + */ +EAPI void ephysics_camera_tracked_body_get(EPhysics_Camera *camera, EPhysics_Body **body, Eina_Bool *horizontal, Eina_Bool *vertical); + /** * @brief * Set camera's zoom. @@ -236,7 +303,6 @@ EAPI double ephysics_camera_zoom_get(const EPhysics_Camera *camera); * @} */ - /** * @defgroup EPhysics_World EPhysics World * @ingroup EPhysics @@ -926,8 +992,6 @@ EAPI Eina_Bool ephysics_world_bodies_outside_left_autodel_get(const EPhysics_Wor * moved or rotated. */ -typedef struct _EPhysics_Body EPhysics_Body; /**< Body handle, represents an object on EPhysics world. Created with @ref ephysics_body_circle_add() or @ref ephysics_body_box_add() and deleted with @ref ephysics_body_del(). */ - /** * @typedef EPhysics_Body_Collision * @@ -971,7 +1035,7 @@ typedef enum _EPhysics_Callback_Body_Type /** * @typedef EPhysics_Body_Event_Cb * - * EPhysics bode event callback function signature. + * EPhysics body event callback function signature. * * Callbacks can be registered for events like body updating or deleting. * diff --git a/legacy/ephysics/src/lib/ephysics_camera.cpp b/legacy/ephysics/src/lib/ephysics_camera.cpp index 01f62a471a..9da54d5f6c 100644 --- a/legacy/ephysics/src/lib/ephysics_camera.cpp +++ b/legacy/ephysics/src/lib/ephysics_camera.cpp @@ -12,10 +12,62 @@ extern "C" { struct _EPhysics_Camera { EPhysics_World *world; + EPhysics_Body *target; double zoom; int x, y; + Eina_Bool track_horizontal:1; + Eina_Bool track_vertical:1; + Eina_Bool moved:1; }; +static void +_ephysics_camera_target_move_cb(void *data, EPhysics_Body *body, void *event_info __UNUSED__) +{ + EPhysics_Camera *camera = (EPhysics_Camera *) data; + int x, y, w, h, ww, wh; + + ephysics_body_geometry_get(body, &x, &y, &w, &h); + ephysics_world_render_geometry_get(camera->world, NULL, NULL, &ww, &wh); + + if (camera->track_horizontal) + { + camera->x = x + w / 2 - ww / 2; + camera->moved = EINA_TRUE; + } + + if (camera->track_vertical) + { + camera->y = y + h / 2 - wh / 2; + camera->moved = EINA_TRUE; + } + + DBG("Camera position set to (%i, %i).", camera->x, camera->y); +} + +static void +_ephysics_camera_target_del_cb(void *data, EPhysics_Body *body, void *event_info) +{ + EPhysics_Camera *camera = (EPhysics_Camera *) data; + + camera->target = NULL; + camera->track_horizontal = EINA_FALSE; + camera->track_vertical = EINA_FALSE; + + INF("Camera isn't tracking body %p anymore.", body); +} + +void +ephysics_camera_moved_set(EPhysics_Camera *camera, Eina_Bool moved) +{ + camera->moved = moved; +} + +Eina_Bool +ephysics_camera_moved_get(const EPhysics_Camera *camera) +{ + return camera->moved; +} + EPhysics_Camera * ephysics_camera_add(EPhysics_World *world) { @@ -56,8 +108,25 @@ ephysics_camera_position_set(EPhysics_Camera *camera, Evas_Coord x, Evas_Coord y return; } + if (camera->target) + { + INF("Camera isn't tracking body %p anymore.", camera->target); + + ephysics_body_event_callback_del(camera->target, + EPHYSICS_CALLBACK_BODY_UPDATE, + _ephysics_camera_target_move_cb); + ephysics_body_event_callback_del(camera->target, + EPHYSICS_CALLBACK_BODY_DEL, + _ephysics_camera_target_del_cb); + camera->target = NULL; + camera->track_horizontal = EINA_FALSE; + camera->track_vertical = EINA_FALSE; + } + camera->x = x; camera->y = y; + + INF("Camera position set to (%i, %i).", x, y); } EAPI void @@ -73,6 +142,65 @@ ephysics_camera_position_get(const EPhysics_Camera *camera, Evas_Coord *x, Evas_ if (y) *y = camera->y; } +EAPI void +ephysics_camera_body_track(EPhysics_Camera *camera, EPhysics_Body *body, Eina_Bool horizontal, Eina_Bool vertical) +{ + if (!camera) + { + ERR("Camera can't track body, camera is null."); + return; + } + + camera->track_horizontal = !!horizontal; + camera->track_vertical = !!vertical; + + if ((body) && (camera->target == body)) + { + INF("Camera already tracking body %p.", body); + INF("Camera tracking: hor = %i, ver = %i.", !!horizontal, !!vertical); + return; + } + + if (camera->target) + { + ephysics_body_event_callback_del(camera->target, + EPHYSICS_CALLBACK_BODY_UPDATE, + _ephysics_camera_target_move_cb); + ephysics_body_event_callback_del(camera->target, + EPHYSICS_CALLBACK_BODY_DEL, + _ephysics_camera_target_del_cb); + } + + camera->target = body; + + if (!body) + { + INF("Camera isn't tracking any body."); + return; + } + + ephysics_body_event_callback_add(body, EPHYSICS_CALLBACK_BODY_UPDATE, + _ephysics_camera_target_move_cb, camera); + ephysics_body_event_callback_add(body, EPHYSICS_CALLBACK_BODY_DEL, + _ephysics_camera_target_del_cb, camera); + + INF("Camera is tracking body %p.", body); +} + +EAPI void +ephysics_camera_tracked_body_get(EPhysics_Camera *camera, EPhysics_Body **body, Eina_Bool *horizontal, Eina_Bool *vertical) +{ + if (!camera) + { + ERR("Can't get tracked body, camera is null."); + return; + } + + if (body) *body = camera->target; + if (horizontal) *horizontal = camera->track_horizontal; + if (vertical) *vertical = camera->track_vertical; +} + EAPI void ephysics_camera_zoom_set(EPhysics_Camera *camera, double zoom) { diff --git a/legacy/ephysics/src/lib/ephysics_private.h b/legacy/ephysics/src/lib/ephysics_private.h index 37c89cb15a..44b20d0e1e 100644 --- a/legacy/ephysics/src/lib/ephysics_private.h +++ b/legacy/ephysics/src/lib/ephysics_private.h @@ -74,6 +74,8 @@ void ephysics_body_active_set(EPhysics_Body *body, Eina_Bool active); EPhysics_Camera *ephysics_camera_add(EPhysics_World *world); void ephysics_camera_del(EPhysics_Camera *camera); +void ephysics_camera_moved_set(EPhysics_Camera *camera, Eina_Bool moved); +Eina_Bool ephysics_camera_moved_get(const EPhysics_Camera *camera); #ifdef __cplusplus } diff --git a/legacy/ephysics/src/lib/ephysics_world.cpp b/legacy/ephysics/src/lib/ephysics_world.cpp index 64c4c6ebd6..e21ebd76f9 100644 --- a/legacy/ephysics/src/lib/ephysics_world.cpp +++ b/legacy/ephysics/src/lib/ephysics_world.cpp @@ -68,16 +68,31 @@ struct _ephysics_world_ovelap_filter_cb : public btOverlapFilterCallback static void _ephysics_world_tick_cb(btDynamicsWorld *dynamics_world, btScalar timeStep) { - EPhysics_World *world; - btCollisionObjectArray objects = dynamics_world->getCollisionObjectArray(); + Eina_Bool world_active, camera_moved, tx, ty; + btCollisionObjectArray objects; EPhysics_World_Callback *cb; - Eina_Bool world_active = EINA_FALSE; + btRigidBody *rigid_body; + EPhysics_World *world; + EPhysics_Body *body; world = (EPhysics_World *) dynamics_world->getWorldUserInfo(); + world_active = EINA_FALSE; + + camera_moved = ephysics_camera_moved_get(world->camera); + if (camera_moved) + ephysics_camera_moved_set(world->camera, EINA_FALSE); + ephysics_camera_tracked_body_get(world->camera, &body, &tx, &ty); + if ((body) && (tx || ty)) + { + rigid_body = ephysics_body_rigid_body_get(body); + if (rigid_body->isActive()) + camera_moved = EINA_TRUE; + } + + objects = dynamics_world->getCollisionObjectArray(); for (int i = 0; i < objects.size(); i++) { - EPhysics_Body *body; btRigidBody *rigid_body = btRigidBody::upcast(objects[i]); if (!rigid_body) continue; @@ -90,7 +105,11 @@ _ephysics_world_tick_cb(btDynamicsWorld *dynamics_world, btScalar timeStep) world_active = EINA_TRUE; } else - ephysics_body_active_set(body, EINA_FALSE); + { + ephysics_body_active_set(body, EINA_FALSE); + if (camera_moved) + ephysics_body_evas_object_update_select(body); + } } if (world->active == world_active) return;