#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Evas.h>
#include <Ecore.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#include <BulletCollision/CollisionShapes/btShapeHull.h>
#include <LinearMath/btGeometryUtil.h>
#pragma GCC diagnostic pop
#include <math.h>
#include "ephysics_private.h"
#include "ephysics_trimesh.h"
#include "ephysics_body_materials.h"
#ifdef __cplusplus
extern "C" {
#endif
#define BODY_CLOTH_CHECK() \
do { \
if (body->type == EPHYSICS_BODY_TYPE_CLOTH) \
{ \
WRN("Not supported for cloth"); \
return; \
} \
} while(0)
typedef struct _EPhysics_Body_Callback EPhysics_Body_Callback;
typedef struct _EPhysics_Body_Evas_Stacking EPhysics_Body_Evas_Stacking;
typedef struct _EPhysics_Body_Soft_Body_Slice EPhysics_Body_Soft_Body_Slice;
typedef struct _EPhysics_Body_Face_Obj EPhysics_Body_Face_Obj;
typedef struct _EPhysics_Quaternion EPhysics_Quaternion;
struct _EPhysics_Body_Callback {
EINA_INLIST;
void (*func) (void *data, EPhysics_Body *body, void *event_info);
void *data;
EPhysics_Callback_Body_Type type;
Eina_Bool deleted:1;
};
struct _EPhysics_Body_Collision {
EPhysics_Body *contact_body;
Evas_Coord x;
Evas_Coord y;
Evas_Coord z;
};
struct _EPhysics_Body_Evas_Stacking {
Evas_Object *evas;
float stacking;
};
struct _EPhysics_Body_Soft_Body_Slice
{
Evas_Object *evas_obj;
int index;
struct {
double x;
double y;
} p[3];
float stacking;
};
struct _EPhysics_Body_Face_Obj {
EPhysics_Body_Face face;
Evas_Object *obj;
};
static void
_ephysics_body_cloth_anchor_mass_reset(EPhysics_Body *body)
{
double anchor_mass;
anchor_mass = 1 / (body->soft_body->m_nodes.size() * 0.025);
for (int i = 0; i < body->soft_body->m_anchors.size(); i++)
body->soft_body->m_anchors[i].m_node->m_im = anchor_mass;
DBG("Cloth anchors mass reset.");
}
EAPI void
_ephysics_body_soft_body_light_apply(Evas_Map *m, Evas_Coord lx, Evas_Coord ly, Evas_Coord lz, int lr, int lg, int lb, int ar, int ag, int ab, Evas_Coord bx, Evas_Coord by, Evas_Coord bz)
{
double x, y, z, nx, ny, nz, ln, br;
Evas_Coord mx, my, mz, mr, mg, mb, ma;
int i;
for (i = 0; i < 4; i++)
{
evas_map_point_coord_get(m, i, &mx, &my, &mz);
evas_map_point_color_get(m, i, NULL, NULL, NULL, &ma);
x = mx;
y = my;
z = mz;
nx = sqrt(pow(bx - x, 2));
ny = sqrt(pow(by - y, 2));
nz = sqrt(pow(bz - z, 2));
ln = nx + ny + nz;
if (ln != 0.0)
{
nx /= ln;
ny /= ln;
nz /= ln;
}
x = lx - bx;
y = ly - by;
z = lz - bz;
ln = pow(x, 2) + pow(y, 2) + pow(z, 2);
ln = sqrt(ln);
if (ln != 0.0)
{
x /= ln;
y /= ln;
z /= ln;
}
br = (nx * x) + (ny * y) + (nz * z);
if (br < 0.0) br = 0.0;
mr = ar + ((lr - ar) * br);
mg = ag + ((lg - ag) * br);
mb = ab + ((lb - ab) * br);
evas_map_point_color_set(m, i, mr, mg, mb, ma);
}
}
static void
_ephysics_body_soft_body_slices_apply(EPhysics_Body *body, Evas_Object *evas_obj, Eina_List *slices)
{
double rate;
void *list_data;
Eina_List *l;
Evas_Coord wy, wh, y0, y1, y2, x0, x1, x2, z0, z1, z2, w, h, bx, by, bz;
Evas_Map *map;
btVector3 p0, p1, p2;
btSoftBody::tFaceArray faces;
EPhysics_Body_Soft_Body_Slice *slice;
int lr, lg, lb, ar, ag, ab;
Evas_Coord lx, ly, lz;
Eina_Bool light = EINA_FALSE;
EPhysics_Camera *camera;
int px, py, pz, foc;
Eina_Bool perspective = EINA_FALSE;
camera = ephysics_world_camera_get(body->world);
rate = ephysics_world_rate_get(body->world);
ephysics_world_render_geometry_get(body->world, NULL, &wy, NULL, NULL, &wh,
NULL);
evas_object_geometry_get(evas_obj, NULL, NULL, &w, &h);
ephysics_body_geometry_get(body, &bx, &by, &bz, NULL, NULL, NULL);
if ((body->light_apply) ||
(ephysics_world_light_all_bodies_get(body->world)))
{
ephysics_world_point_light_position_get(body->world, &lx, &ly, &lz);
ephysics_world_point_light_color_get(body->world, &lr, &lg, &lb);
ephysics_world_ambient_light_color_get(body->world, &ar, &ag, &ab);
light = EINA_TRUE;
}
if (ephysics_camera_perspective_enabled_get(camera))
{
ephysics_camera_perspective_get(camera, &px, &py, &pz, &foc);
perspective = EINA_TRUE;
}
EINA_LIST_FOREACH(slices, l, list_data)
{
slice = (EPhysics_Body_Soft_Body_Slice *)list_data;
faces = body->soft_body->m_faces;
p0 = faces[slice->index].m_n[0]->m_x;
p1 = faces[slice->index].m_n[1]->m_x;
p2 = faces[slice->index].m_n[2]->m_x;
slice->stacking = p0.z() + p1.z() + p2.z();
map = evas_map_new(4);
evas_map_point_image_uv_set(map, 0, slice->p[0].x * w,
slice->p[0].y * h);
evas_map_point_image_uv_set(map, 1, slice->p[1].x * w,
slice->p[1].y * h);
evas_map_point_image_uv_set(map, 2, slice->p[2].x * w,
slice->p[2].y * h);
evas_map_point_image_uv_set(map, 3, slice->p[2].x * w,
slice->p[2].y * h);
x0 = p0.x() * rate;
x1 = p1.x() * rate;
x2 = p2.x() * rate;
y0 = wh + wy - (p0.y() * rate);
y1 = wh + wy - (p1.y() * rate);
y2 = wh + wy - (p2.y() * rate);
z0 = p0.z() * rate;
z1 = p1.z() * rate;
z2 = p2.z() * rate;
evas_map_point_coord_set(map, 0, x0, y0, z0);
evas_map_point_coord_set(map, 1, x1, y1, z1);
evas_map_point_coord_set(map, 2, x2, y2, z2);
evas_map_point_coord_set(map, 3, x2, y2, z2);
if (perspective)
evas_map_util_3d_perspective(map, px, py, pz, foc);
if (body->back_face_culling)
{
if (evas_map_util_clockwise_get(map))
evas_object_show(slice->evas_obj);
else
{
evas_map_free(map);
evas_object_hide(slice->evas_obj);
continue;
}
}
if (light)
_ephysics_body_soft_body_light_apply(map, lx, ly, lz, lr, lg, lb, ar,
ag, ab, bx, by, bz);
evas_object_map_set(slice->evas_obj, map);
evas_object_map_enable_set(slice->evas_obj, EINA_TRUE);
evas_map_free(map);
}
}
static inline double
_ephysics_body_soft_body_slice_calc(double val, double delta, double max)
{
double ret = val + delta / max;
if (ret < 0)
ret = 0;
else if (ret > 1)
ret = 1;
return ret;
}
static EPhysics_Body_Soft_Body_Slice *
_ephysics_body_soft_body_slice_new(EPhysics_Body *body, double delta, double max, int index)
{
EPhysics_Body_Soft_Body_Slice *slice;
btSoftBody::tFaceArray faces;
slice = (EPhysics_Body_Soft_Body_Slice *)calloc(
1, sizeof(EPhysics_Body_Soft_Body_Slice));
if (!slice)
{
ERR("Couldn't allocate EPhysics_Soft_Body_Slice memory.");
return NULL;
}
faces = body->soft_body->m_faces;
slice->index = index;
slice->p[0].x = _ephysics_body_soft_body_slice_calc(
faces[slice->index].m_n[0]->m_x.x(), delta, max);
slice->p[0].y = 1 - _ephysics_body_soft_body_slice_calc(
faces[slice->index].m_n[0]->m_x.y(), delta, max);
slice->p[1].x = _ephysics_body_soft_body_slice_calc(
faces[slice->index].m_n[1]->m_x.x(), delta, max);
slice->p[1].y = 1 - _ephysics_body_soft_body_slice_calc(
faces[slice->index].m_n[1]->m_x.y(), delta, max);
slice->p[2].x = _ephysics_body_soft_body_slice_calc(
faces[slice->index].m_n[2]->m_x.x(), delta, max);
slice->p[2].y = 1 - _ephysics_body_soft_body_slice_calc(
faces[slice->index].m_n[2]->m_x.y(), delta, max);
return slice;
}
static Eina_List *
_ephysics_body_soft_body_slices_get(EPhysics_Body *body)
{
Eina_List *l, *slices_list, *slices = NULL;
void *ldata, *slice_data;
EPhysics_Body_Face_Slice *face_slice;
EINA_LIST_FOREACH(body->faces_slices, l, ldata)
{
face_slice = (EPhysics_Body_Face_Slice *)ldata;
EINA_LIST_FOREACH(face_slice->slices, slices_list, slice_data)
slices = eina_list_append(slices, slice_data);
}
return slices;
}
EAPI int
ephysics_body_soft_body_slice_index_get(EPhysics_Body *body, Evas_Object *slice)
{
Eina_List *slices;
void *ldata;
EPhysics_Body_Soft_Body_Slice *slice_data;
if (!body)
{
ERR("Can't get soft body slice index, body is null.");
return -1;
}
if (body->type == EPHYSICS_BODY_TYPE_RIGID)
{
ERR("Can't get soft body slice index, operation not allowed for rigid"
" bodies.");
return -1;
}
slices = _ephysics_body_soft_body_slices_get(body);
EINA_LIST_FREE(slices, ldata)
{
slice_data = (EPhysics_Body_Soft_Body_Slice *)ldata;
if (slice_data->evas_obj == slice)
return slice_data->index;
}
return -1;
}
static void
_ephysics_body_soft_body_slice_del_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
EPhysics_Body_Soft_Body_Slice *slice = (EPhysics_Body_Soft_Body_Slice *)data;
slice->evas_obj = NULL;
evas_object_event_callback_del(obj, EVAS_CALLBACK_DEL,
_ephysics_body_soft_body_slice_del_cb);
}
static void
_ephysics_body_soft_body_slices_init(EPhysics_Body *body, Evas_Object *obj, Eina_List *slices)
{
EPhysics_Body_Soft_Body_Slice *slice = NULL;
btVector3 p0, p1, p2;
void *slice_data;
Evas_Coord w, h;
Eina_List *l;
Evas *evas;
Evas_Object *parent;
evas = evas_object_evas_get(obj);
evas_object_geometry_get(obj, NULL, NULL, &w, &h);
parent = evas_object_smart_parent_get(obj);
EINA_LIST_FOREACH(slices, l, slice_data)
{
slice = (EPhysics_Body_Soft_Body_Slice *) slice_data;
slice->evas_obj = evas_object_image_filled_add(evas);
evas_object_layer_set(slice->evas_obj,
evas_object_layer_get(obj));
evas_object_image_source_set(slice->evas_obj, obj);
evas_object_image_source_events_set(slice->evas_obj, EINA_TRUE);
evas_object_resize(slice->evas_obj, w, h);
evas_object_show(slice->evas_obj);
evas_object_image_smooth_scale_set(slice->evas_obj, EINA_TRUE);
evas_object_event_callback_add(slice->evas_obj, EVAS_CALLBACK_DEL,
_ephysics_body_soft_body_slice_del_cb,
slice);
if (parent) evas_object_smart_member_add(slice->evas_obj, parent);
}
if (slice)
evas_object_image_source_visible_set(slice->evas_obj, EINA_FALSE);
_ephysics_body_soft_body_slices_apply(body, obj, slices);
}
static void
_ephysics_body_soft_body_slices_free(Eina_List *slices)
{
EPhysics_Body_Soft_Body_Slice *slice;
void *slice_data;
EINA_LIST_FREE(slices, slice_data)
{
slice = (EPhysics_Body_Soft_Body_Slice *)slice_data;
if (slice->evas_obj)
evas_object_del(slice->evas_obj);
free(slice);
}
}
static void
_ephysics_body_soft_body_slices_clean(Eina_List *slices)
{
EPhysics_Body_Soft_Body_Slice *slice;
void *slice_data;
Eina_List *l;
EINA_LIST_FOREACH(slices, l, slice_data)
{
slice = (EPhysics_Body_Soft_Body_Slice *)slice_data;
if (slice->evas_obj)
evas_object_del(slice->evas_obj);
}
}
static btTransform
_ephysics_body_transform_get(const EPhysics_Body *body)
{
btTransform trans;
btVector3 center;
btScalar radius;
if (body->type == EPHYSICS_BODY_TYPE_RIGID)
{
body->rigid_body->getMotionState()->getWorldTransform(trans);
return trans;
}
body->soft_body->getCollisionShape()->getBoundingSphere(center, radius);
trans.setIdentity();
trans.setOrigin(center);
return trans;
}
static int
_ephysics_body_evas_stacking_sort_cb(const void *d1, const void *d2)
{
const EPhysics_Body_Evas_Stacking *stacking1, *stacking2;
stacking1 = (const EPhysics_Body_Evas_Stacking *)d1;
stacking2 = (const EPhysics_Body_Evas_Stacking *)d2;
if (!stacking1) return 1;
if (!stacking2) return -1;
if (stacking1->stacking < stacking2->stacking) return -1;
if (stacking2->stacking > stacking2->stacking) return 1;
return 0;
}
static EPhysics_Body_Evas_Stacking *
_ephysics_body_evas_stacking_new(Evas_Object *obj, float index)
{
EPhysics_Body_Evas_Stacking *stacking;
stacking = (EPhysics_Body_Evas_Stacking *)calloc(
1, sizeof(EPhysics_Body_Evas_Stacking));
if (!stacking)
{
ERR("Could not allocate ephysics soft body evas stacking data.");
return NULL;
}
stacking->evas = obj;
stacking->stacking = index;
return stacking;
}
void
ephysics_body_evas_objects_restack(EPhysics_World *world)
{
void *data, *slice_data, *stack_data;
EPhysics_Body *body;
btTransform trans;
EPhysics_Body_Evas_Stacking *stacking;
EPhysics_Body_Soft_Body_Slice *slice;
Eina_List *l, *slices, *bodies, *stack_list = NULL;
Evas_Object *prev_obj = NULL;
Eina_Hash *hash;
Eina_Iterator *it;
int layer;
Eina_List *ll = NULL;
bodies = ephysics_world_bodies_get(world);
if (!eina_list_count(bodies))
return;
hash = eina_hash_int32_new(NULL);
EINA_LIST_FREE(bodies, data)
{
body = (EPhysics_Body *)data;
if (body->deleted) continue;
if (body->type == EPHYSICS_BODY_TYPE_RIGID)
{
if (!body->evas_obj) continue;
trans = _ephysics_body_transform_get(body);
stacking = _ephysics_body_evas_stacking_new(body->evas_obj,
trans.getOrigin().z());
if (!stacking) goto error;
layer = evas_object_layer_get(stacking->evas);
stack_list = (Eina_List *)eina_hash_find(hash, &layer);
stack_list = eina_list_append(stack_list, stacking);
eina_hash_set(hash, &layer, stack_list);
continue;
}
slices = _ephysics_body_soft_body_slices_get(body);
if (!slices) continue;
EINA_LIST_FREE(slices, slice_data)
{
slice = (EPhysics_Body_Soft_Body_Slice *)slice_data;
stacking = _ephysics_body_evas_stacking_new(slice->evas_obj,
slice->stacking);
if (!stacking) goto error;
layer = evas_object_layer_get(stacking->evas);
stack_list = (Eina_List *)eina_hash_find(hash, &layer);
stack_list = eina_list_append(stack_list, stacking);
eina_hash_set(hash, &layer, stack_list);
}
}
it = eina_hash_iterator_data_new(hash);
while (eina_iterator_next(it, &data))
{
stack_list = (Eina_List *)data;
stack_list = eina_list_sort(stack_list, eina_list_count(stack_list),
_ephysics_body_evas_stacking_sort_cb);
prev_obj = NULL;
ll = eina_list_append(ll, stack_list);
EINA_LIST_FOREACH(stack_list, l, stack_data)
{
stacking = (EPhysics_Body_Evas_Stacking *)stack_data;
if (prev_obj)
evas_object_stack_below(stacking->evas, prev_obj);
prev_obj = stacking->evas;
}
}
eina_iterator_free(it);
EINA_LIST_FREE(ll, stack_data)
{
stack_list = (Eina_List *) stack_data;
eina_hash_del_by_data(hash, stack_list);
EINA_LIST_FREE(stack_list, slice_data)
free(slice_data);
}
eina_hash_free(hash);
return;
error:
ERR("Could not allocate evas stacking data memory.");
eina_hash_free(hash);
}
static void
_ephysics_body_transform_set(EPhysics_Body *body, btTransform trans)
{
btTransform origin;
if (body->type != EPHYSICS_BODY_TYPE_RIGID)
{
origin = _ephysics_body_transform_get(body);
body->soft_body->translate(trans.getOrigin() - origin.getOrigin());
return;
}
body->rigid_body->getMotionState()->setWorldTransform(trans);
}
void
ephysics_body_activate(const EPhysics_Body *body, Eina_Bool activate)
{
if (body->type == EPHYSICS_BODY_TYPE_RIGID)
{
body->rigid_body->activate(activate);
return;
}
body->soft_body->activate(activate);
}
static void
_ephysics_body_forces_update(EPhysics_Body *body)
{
body->force.x = body->rigid_body->getTotalForce().getX();
body->force.y = body->rigid_body->getTotalForce().getY();
body->force.z = body->rigid_body->getTotalForce().getZ();
body->force.torque_x = body->rigid_body->getTotalTorque().getX();
body->force.torque_y = body->rigid_body->getTotalTorque().getY();
body->force.torque_z = body->rigid_body->getTotalTorque().getZ();
body->rigid_body->clearForces();
DBG("forces updated: %lf, %lf, %lf", body->force.x, body->force.y,
body->force.z);
DBG("torque updated: %lf, %lf, %lf", body->force.torque_x,
body->force.torque_y, body->force.torque_z);
}
static inline void
_ephysics_body_sleeping_threshold_set(EPhysics_Body *body, double linear_threshold, double angular_threshold, double rate)
{
body->rigid_body->setSleepingThresholds(linear_threshold / rate,
angular_threshold / RAD_TO_DEG);
}
static inline void
_ephysics_body_linear_velocity_set(EPhysics_Body *body, double x, double y, double z, double rate)
{
btVector3 linear_velocity = btVector3(x / rate, -y / rate, z / rate);
ephysics_body_activate(body, EINA_TRUE);
if (body->rigid_body)
body->rigid_body->setLinearVelocity(linear_velocity);
if (body->soft_body)
{
for (int i = 0; i < body->soft_body->m_nodes.size(); i++)
body->soft_body->m_nodes[i].m_v = linear_velocity;
}
}
static void
_ephysics_body_event_callback_del(EPhysics_Body *body, EPhysics_Body_Callback *cb)
{
if (cb->deleted) return;
cb->deleted = EINA_TRUE;
if (body->walking)
{
body->to_delete = eina_list_append(body->to_delete, cb);
return;
}
body->callbacks = eina_inlist_remove(body->callbacks, EINA_INLIST_GET(cb));
free(cb);
}
static Eina_Bool
_ephysics_body_event_callback_call(EPhysics_Body *body, EPhysics_Callback_Body_Type type, void *event_info)
{
Eina_Bool called = EINA_FALSE;
EPhysics_Body_Callback *cb;
void *clb;
body->walking++;
EINA_INLIST_FOREACH(body->callbacks, cb)
{
if ((cb->type == type) && (!cb->deleted))
{
cb->func(cb->data, body, event_info);
called = EINA_TRUE;
}
}
body->walking--;
if (body->walking > 0) return called;
EINA_LIST_FREE(body->to_delete, clb)
{
cb = (EPhysics_Body_Callback *) clb;
body->callbacks = eina_inlist_remove(body->callbacks,
EINA_INLIST_GET(cb));
free(cb);
}
return called;
}
void
ephysics_body_active_set(EPhysics_Body *body, Eina_Bool active)
{
if (body->active == !!active) return;
body->active = !!active;
if (active) return;
_ephysics_body_event_callback_call(body, EPHYSICS_CALLBACK_BODY_STOPPED,
(void *) body->evas_obj);
};
Eina_Bool
ephysics_body_filter_collision(EPhysics_Body *body0, EPhysics_Body *body1)
{
Eina_List *l;
void *grp;
if ((!body0->collision_groups) || (!body1->collision_groups))
return EINA_TRUE;
EINA_LIST_FOREACH(body0->collision_groups, l, grp)
{
if (eina_list_data_find(body1->collision_groups, grp))
return EINA_TRUE;
}
return EINA_FALSE;
}
EAPI Eina_Bool
ephysics_body_collision_group_add(EPhysics_Body *body, const char *group)
{
Eina_Stringshare *group_str;
if (!body)
{
ERR("Can't add body collision group, body is null.");
return EINA_FALSE;
}
ephysics_world_lock_take(body->world);
group_str = eina_stringshare_add(group);
if (eina_list_data_find(body->collision_groups, group_str))
{
INF("Body already added to group: %s", group);
eina_stringshare_del(group_str);
ephysics_world_lock_release(body->world);
return EINA_TRUE;
}
body->collision_groups = eina_list_append(body->collision_groups, group_str);
ephysics_world_lock_release(body->world);
return EINA_TRUE;
}
EAPI Eina_Bool
ephysics_body_collision_group_del(EPhysics_Body *body, const char *group)
{
Eina_Stringshare *group_str;
if (!body)
{
ERR("Can't remove body collision group, body is null.");
return EINA_FALSE;
}
ephysics_world_lock_take(body->world);
group_str = eina_stringshare_add(group);
if (!eina_list_data_find(body->collision_groups, group_str))
{
INF("Body isn't part of group: %s", group);
eina_stringshare_del(group_str);
ephysics_world_lock_release(body->world);
return EINA_TRUE;
}
body->collision_groups = eina_list_remove(body->collision_groups, group_str);
eina_stringshare_del(group_str);
eina_stringshare_del(group_str);
ephysics_world_lock_release(body->world);
return EINA_TRUE;
}
EAPI const Eina_List *
ephysics_body_collision_group_list_get(const EPhysics_Body *body)
{
if (!body)
{
ERR("Can't get the body's collision group, body is null.");
return NULL;
}
return body->collision_groups;
}
static EPhysics_Body *
_ephysics_body_new(EPhysics_World *world, btScalar mass, double cm_x, double cm_y, double cm_z)
{
EPhysics_Body *body;
double rate;
body = (EPhysics_Body *) calloc(1, sizeof(EPhysics_Body));
if (!body)
{
ERR("Couldn't create a new body instance.");
return NULL;
}
rate = ephysics_world_rate_get(world);
body->scale[0] = 1;
body->scale[1] = 1;
body->scale[2] = 1;
body->size.w = rate;
body->size.h = rate;
body->size.d = rate;
body->mass = mass;
body->world = world;
body->cm.x = cm_x;
body->cm.y = cm_y;
body->cm.z = cm_z;
return body;
}
static EPhysics_Body *
_ephysics_body_rigid_body_add(EPhysics_World *world, btCollisionShape *collision_shape, const char *type, double cm_x, double cm_y, double cm_z)
{
btRigidBody::btRigidBodyConstructionInfo *rigid_body_ci;
btDefaultMotionState *motion_state;
btRigidBody *rigid_body;
EPhysics_Body *body;
btScalar mass = 1;
btVector3 inertia;
if (!collision_shape)
{
ERR("Couldn't create a %s shape.", type);
return NULL;
}
body = _ephysics_body_new(world, mass, cm_x, cm_y, cm_z);
if (!body)
{
ERR("Couldn't create a new body instance.");
goto err_body;
}
motion_state = new btDefaultMotionState();
if (!motion_state)
{
ERR("Couldn't create a motion state.");
goto err_motion_state;
}
inertia = btVector3(0, 0, 0);
collision_shape->calculateLocalInertia(mass, inertia);
rigid_body_ci = new btRigidBody::btRigidBodyConstructionInfo(
mass, motion_state, collision_shape, inertia);
if (!rigid_body_ci)
{
ERR("Couldn't create a rigid body construction info.");
goto err_rigid_body_ci;
}
rigid_body = new btRigidBody(*rigid_body_ci);
if (!rigid_body)
{
ERR("Couldn't create a rigid body.");
goto err_rigid_body;
}
body->type = EPHYSICS_BODY_TYPE_RIGID;
body->collision_shape = collision_shape;
body->rigid_body = rigid_body;
body->rigid_body->setUserPointer(body);
body->rigid_body->setLinearFactor(btVector3(1, 1, 0));
body->rigid_body->setAngularFactor(btVector3(0, 0, 1));
if (!ephysics_world_body_add(body->world, body))
{
ERR("Couldn't add body to world's bodies list");
goto err_world_add;
}
delete rigid_body_ci;
INF("Body %p of type %s added.", body, type);
return body;
err_world_add:
delete rigid_body;
err_rigid_body:
delete rigid_body_ci;
err_rigid_body_ci:
delete motion_state;
err_motion_state:
free(body);
err_body:
delete collision_shape;
return NULL;
}
static void
_ephysics_body_evas_obj_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
EPhysics_Body *body = (EPhysics_Body *) data;
if (body->default_face)
_ephysics_body_soft_body_slices_clean(body->default_face->slices);
body->evas_obj = NULL;
DBG("Evas object deleted. Updating body: %p", body);
}
static void
_ephysics_body_soft_body_anchors_rebuild(int node, btRigidBody *rigid_body, btSoftBody *soft_body)
{
btTransform world_trans = rigid_body->getWorldTransform();
btVector3 local = world_trans.inverse() * soft_body->m_nodes[node].m_x;
for (int i = 0; i < soft_body->m_anchors.size(); i++)
{
if (soft_body->m_anchors[i].m_node == &soft_body->m_nodes[node])
soft_body->m_anchors[i].m_local = local;
}
}
void
ephysics_body_soft_body_bending_constraints_generate(EPhysics_Body *body)
{
btSoftBody *soft_body = body->soft_body;
for (; body->bending_constraints; body->bending_constraints--)
soft_body->generateBendingConstraints(2, soft_body->m_materials
[body->material_index]);
}
static void
_ephysics_body_cloth_constraints_rebuild(EPhysics_Body *body)
{
btRigidBody *rigid_body;
btSoftBody *soft_body;
btSoftBody::Node *node;
btSoftBody::Anchor anchor;
int anchors_size = body->soft_body->m_anchors.size();
soft_body = body->soft_body;
if (anchors_size)
{
rigid_body = soft_body->m_anchors[0].m_body;
for (int m = 0; m < anchors_size; m++)
{
anchor = soft_body->m_anchors[m];
node = anchor.m_node;
for (int n = 0; n < soft_body->m_nodes.size(); n++)
{
if (node == &soft_body->m_nodes[n])
_ephysics_body_soft_body_anchors_rebuild(n, rigid_body,
soft_body);
}
}
}
soft_body->generateClusters(0);
ephysics_body_soft_body_bending_constraints_generate(body);
}
static void
_ephysics_body_soft_body_constraints_rebuild(EPhysics_Body *body)
{
btSoftBody *soft_body = body->soft_body;
btRigidBody *rigid_body = body->rigid_body;
if (soft_body->m_anchors.size() > 0)
{
for (int i = 0; i < soft_body->m_nodes.size(); i++)
_ephysics_body_soft_body_anchors_rebuild(i, rigid_body, soft_body);
}
else
{
for (int i = 0; i < soft_body->m_nodes.size(); i++)
soft_body->appendAnchor(i, rigid_body);
}
soft_body->generateClusters(0);
ephysics_body_soft_body_bending_constraints_generate(body);
}
inline static double
_ephysics_body_volume_get(const EPhysics_Body *body)
{
btVector3 vector = body->collision_shape->getLocalScaling();
return vector.x() * vector.y() * vector.z();
}
void
_ephysics_body_soft_body_dragging_set(EPhysics_Body *body, int triangle)
{
btSoftBody::Face face;
btSoftBody::Node *node;
body->dragging_data.triangle = triangle;
body->dragging_data.dragging = EINA_TRUE;
face = body->soft_body->m_faces[triangle];
node = face.m_n[0];
body->dragging_data.mass = node->m_im;
}
static void
_ephysics_body_soft_body_mass_set(EPhysics_Body *body, double mass)
{
int valid_nodes;
btSoftBody::Node node;
double inverse_mass;
if (body->type == EPHYSICS_BODY_TYPE_SOFT)
body->soft_body->setTotalMass(mass);
else
{
valid_nodes = 0;
for (int i = 0; i < body->soft_body->m_nodes.size(); i++)
{
node = body->soft_body->m_nodes[i];
if (node.m_im && !node.m_battach)
valid_nodes++;
}
inverse_mass = 1 / (mass / valid_nodes);
if (body->dragging_data.dragging)
{
valid_nodes++;
inverse_mass = 1 / (mass / valid_nodes);
body->dragging_data.mass = inverse_mass;
}
for (int i = 0; i < body->soft_body->m_nodes.size(); i++)
{
node = body->soft_body->m_nodes[i];
if (node.m_im && !node.m_battach)
node.m_im = inverse_mass;
}
}
}
static void
_ephysics_body_mass_set(EPhysics_Body *body, double mass)
{
btVector3 inertia(0, 0, 0);
if (body->density)
mass = body->density * _ephysics_body_volume_get(body);
if (body->soft_body)
_ephysics_body_soft_body_mass_set(body, mass);
else
{
body->collision_shape->calculateLocalInertia(mass, inertia);
body->rigid_body->setMassProps(mass, inertia);
body->rigid_body->updateInertiaTensor();
}
body->mass = mass;
DBG("Body %p mass changed to %lf.", body, mass);
}
static void
_ephysics_body_geometry_set(EPhysics_Body *body, Evas_Coord x, Evas_Coord y, Evas_Coord z, Evas_Coord w, Evas_Coord h, Evas_Coord d, double rate)
{
double mx, my, mz, sx, sy, sz;
btTransform trans;
int wy, height;
btVector3 body_scale, old_scale;
ephysics_world_render_geometry_get(body->world, NULL, &wy, NULL,
NULL, &height, NULL);
height += wy;
mx = (x + w * body->cm.x) / rate;
my = (height - (y + h * body->cm.y)) / rate;
mz = (z + d * body->cm.z) / rate;
sx = (w <= 0) ? 1 : w / rate;
sy = (h <= 0) ? 1 : h / rate;
sz = (d <= 0) ? 1 : d / rate;
trans = _ephysics_body_transform_get(body);
trans.setOrigin(btVector3(mx, my, mz));
body_scale = btVector3(sx, sy, sz);
old_scale = btVector3(body->scale[0], body->scale[1], body->scale[2]);
if (body->type == EPHYSICS_BODY_TYPE_SOFT)
{
body->soft_body->scale(btVector3(1, 1, 1) / old_scale);
body->soft_body->scale(body_scale);
body->rigid_body->proceedToTransform(trans);
_ephysics_body_transform_set(body, trans);
_ephysics_body_soft_body_constraints_rebuild(body);
}
else if (body->type == EPHYSICS_BODY_TYPE_CLOTH)
{
body->soft_body->setTotalMass(body->mass, true);
body->soft_body->scale(btVector3(1, 1, 1) / old_scale);
body->soft_body->scale(body_scale);
_ephysics_body_transform_set(body, trans);
_ephysics_body_cloth_constraints_rebuild(body);
body->soft_body->setTotalMass(body->mass, false);
_ephysics_body_cloth_anchor_mass_reset(body);
}
else
{
body->collision_shape->setLocalScaling(body_scale);
body->rigid_body->proceedToTransform(trans);
if (!body->rigid_body->isStaticObject())
_ephysics_body_mass_set(body, ephysics_body_mass_get(body));
}
_ephysics_body_transform_set(body, trans);
ephysics_body_activate(body, EINA_TRUE);
body->size.w = w;
body->size.h = h;
body->size.d = d;
body->scale[0] = sx;
body->scale[1] = sy;
body->scale[2] = sz;
DBG("Body %p position changed to (%lf, %lf, %lf).", body, mx, my, mz);
DBG("Body %p scale changed to (%lf, %lf, %lf).", body, sx, sy, sz);
}
static void
_ephysics_body_resize(EPhysics_Body *body, Evas_Coord w, Evas_Coord h, Evas_Coord d)
{
Evas_Coord bx, by, bz;
double rate, sx, sy, sz;
btVector3 body_scale, center;
btTransform trans;
rate = ephysics_world_rate_get(body->world);
sx = w / rate;
sy = h / rate;
sz = d / rate;
DBG("Body %p scale changed to (%lf, %lf, %lf).", body, sx, sy, sz);
body_scale = btVector3(sx, sy, sz);
if (body->type == EPHYSICS_BODY_TYPE_SOFT)
{
btVector3 old_scale(body->scale[0], body->scale[1], body->scale[2]);
trans = _ephysics_body_transform_get(body);
body->soft_body->scale(btVector3(1, 1, 1) / old_scale);
body->soft_body->scale(body_scale);
_ephysics_body_transform_set(body, trans);
body->rigid_body->proceedToTransform(trans);
_ephysics_body_soft_body_constraints_rebuild(body);
}
else if (body->type == EPHYSICS_BODY_TYPE_CLOTH)
{
ephysics_body_geometry_get(body, &bx, &by, &bz, NULL, NULL, NULL);
_ephysics_body_geometry_set(body, bx, by, bz, w, h, d, rate);
return;
}
else
{
body->collision_shape->setLocalScaling(body_scale);
if(!body->rigid_body->isStaticObject())
_ephysics_body_mass_set(body, ephysics_body_mass_get(body));
}
body->size.w = w;
body->size.h = h;
body->size.d = d;
body->scale[0] = sx;
body->scale[1] = sy;
body->scale[2] = sz;
ephysics_body_activate(body, EINA_TRUE);
}
static void
_ephysics_body_move(EPhysics_Body *body, Evas_Coord x, Evas_Coord y, Evas_Coord z)
{
double rate, mx, my, mz;
btTransform trans;
int wy, height;
btVector3 body_scale;
rate = ephysics_world_rate_get(body->world);
ephysics_world_render_geometry_get(body->world, NULL, &wy, NULL,
NULL, &height, NULL);
height += wy;
mx = (x + body->size.w * body->cm.x) / rate;
my = (height - (y + body->size.h * body->cm.y)) / rate;
mz = (z + body->size.d * body->cm.z) / rate;
trans = _ephysics_body_transform_get(body);
trans.setOrigin(btVector3(mx, my, mz));
if (body->type == EPHYSICS_BODY_TYPE_CLOTH)
_ephysics_body_transform_set(body, trans);
else if (body->type == EPHYSICS_BODY_TYPE_SOFT)
{
_ephysics_body_transform_set(body, trans);
body->rigid_body->proceedToTransform(trans);
body->rigid_body->getMotionState()->setWorldTransform(trans);
}
else
{
body->rigid_body->proceedToTransform(trans);
body->rigid_body->getMotionState()->setWorldTransform(trans);
}
ephysics_body_activate(body, EINA_TRUE);
DBG("Body %p position changed to (%lf, %lf, %lf).", body, mx, my, mz);
}
static void
_ephysics_body_evas_obj_resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
EPhysics_Body *body = (EPhysics_Body *) data;
int w, h;
evas_object_geometry_get(obj, NULL, NULL, &w, &h);
if ((w == body->size.w) && (h == body->size.h))
return;
DBG("Resizing body %p to w=%i, h=%i, d=%i", body, w, h, body->size.d);
ephysics_world_lock_take(body->world);
_ephysics_body_resize(body, w, h, body->size.d);
if (body->type == EPHYSICS_BODY_TYPE_CLOTH)
{
_ephysics_body_soft_body_slices_clean(body->default_face->slices);
_ephysics_body_soft_body_slices_init(body, body->evas_obj,
body->default_face->slices);
}
ephysics_world_lock_release(body->world);
}
static void
_ephysics_body_soft_body_evas_restack_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
EPhysics_Body *body = (EPhysics_Body *)data;
Eina_List *slices;
void *ldata;
EPhysics_Body_Soft_Body_Slice *slice;
short layer = evas_object_layer_get(obj);
slices = _ephysics_body_soft_body_slices_get(body);
EINA_LIST_FREE(slices, ldata)
{
slice = (EPhysics_Body_Soft_Body_Slice *)ldata;
evas_object_layer_set(slice->evas_obj, layer);
}
DBG("Body's slices layer reset to: %d", layer);
}
static void
_ephysics_body_face_slice_del(EPhysics_Body_Face_Slice *face_slice)
{
_ephysics_body_soft_body_slices_free(face_slice->slices);
free(face_slice->points_deform);
free(face_slice);
}
static EPhysics_Body_Face_Slice *
_ephysics_body_face_slice_add(EPhysics_Body *body, EPhysics_Body_Face face)
{
EPhysics_Body_Face_Slice *face_slice;
face_slice = (EPhysics_Body_Face_Slice *)calloc(1,
sizeof(EPhysics_Body_Face_Slice));
if (!face_slice)
return NULL;
face_slice->face = face;
body->faces_slices = eina_list_append(body->faces_slices, face_slice);
face_slice->body = body;
return face_slice;
DBG("New face slice added to body %p", body);
}
static EPhysics_Body_Face_Slice *
_ephysics_body_face_slice_get(EPhysics_Body *body, EPhysics_Body_Face face)
{
Eina_List *l;
void *ldata;
EPhysics_Body_Face_Slice *face_slice = NULL;
EINA_LIST_FOREACH(body->faces_slices, l, ldata)
{
if (((EPhysics_Body_Face_Slice *)ldata)->face == face)
{
face_slice = (EPhysics_Body_Face_Slice *)ldata;
break;
}
}
return face_slice;
}
static EPhysics_Body_Face_Obj *
_ephysics_body_face_evas_object_get(EPhysics_Body *body, EPhysics_Body_Face face)
{
Eina_List *l;
void *ldata;
EPhysics_Body_Face_Obj *face_obj;
EINA_LIST_FOREACH(body->face_objs, l, ldata)
{
face_obj = (EPhysics_Body_Face_Obj *)ldata;
if (face_obj->face == face)
return face_obj;
}
DBG("Could not find requested face");
return NULL;
}
static void
_ephysics_body_del(EPhysics_Body *body)
{
EPhysics_Body_Callback *cb;
void *ldata;
void *group;
EPhysics_Body_Face_Slice *face_slice;
if (body->evas_obj)
{
evas_object_event_callback_del(body->evas_obj, EVAS_CALLBACK_DEL,
_ephysics_body_evas_obj_del_cb);
evas_object_event_callback_del(body->evas_obj, EVAS_CALLBACK_RESIZE,
_ephysics_body_evas_obj_resize_cb);
if (body->faces_slices)
evas_object_event_callback_del(body->evas_obj, EVAS_CALLBACK_RESTACK,
_ephysics_body_soft_body_evas_restack_cb);
}
while (body->callbacks)
{
cb = EINA_INLIST_CONTAINER_GET(body->callbacks,
EPhysics_Body_Callback);
body->callbacks = eina_inlist_remove(body->callbacks, body->callbacks);
free(cb);
}
ephysics_constraint_body_del(body);
EINA_LIST_FREE(body->collision_groups, group)
eina_stringshare_del((Eina_Stringshare *)group);
EINA_LIST_FREE(body->faces_slices, ldata)
{
face_slice = (EPhysics_Body_Face_Slice *)ldata;
if (_ephysics_body_face_evas_object_get(body, face_slice->face))
ephysics_body_face_evas_object_unset(body, face_slice->face);
_ephysics_body_face_slice_del(face_slice);
}
if (body->rigid_body)
{
delete body->rigid_body->getMotionState();
delete body->collision_shape;
delete body->rigid_body;
}
delete body->soft_body;
free(body);
}
static void
_ephysics_body_evas_obj_map_apply(EPhysics_Body *body, Evas_Map *map, Evas_Object *obj, Eina_Bool bfc, Eina_Bool update_cw)
{
EPhysics_Camera *camera = ephysics_world_camera_get(body->world);
if (ephysics_camera_perspective_enabled_get(camera))
{
int px, py, z0, foc;
ephysics_camera_perspective_get(camera, &px, &py, &z0, &foc);
evas_map_util_3d_perspective(map, px, py, z0, foc);
}
if (bfc)
{
if (evas_map_util_clockwise_get(map))
{
if (update_cw)
body->clockwise = EINA_TRUE;
evas_object_show(obj);
}
else
{
if (update_cw)
body->clockwise = EINA_FALSE;
evas_map_free(map);
evas_object_hide(obj);
return;
}
}
else
evas_object_show(obj);
if ((body->light_apply) ||
(ephysics_world_light_all_bodies_get(body->world)))
{
int lr, lg, lb, ar, ag, ab;
Evas_Coord lx, ly, lz;
ephysics_world_point_light_position_get(body->world, &lx, &ly, &lz);
ephysics_world_point_light_color_get(body->world, &lr, &lg, &lb);
ephysics_world_ambient_light_color_get(body->world, &ar, &ag, &ab);
evas_map_util_3d_lighting(map, lx, ly, lz, lr, lg, lb, ar, ag, ab);
}
evas_object_map_set(obj, map);
evas_object_map_enable_set(obj, EINA_TRUE);
evas_map_free(map);
}
static void
_ephysics_cloth_face_objs_update(EPhysics_Body *body EINA_UNUSED)
{
}
static void
_ephysics_cylinder_face_objs_update(EPhysics_Body *body)
{
int bx, by, x, y, z, wx, wy, wh, cx, cy;
EPhysics_Body_Face_Obj *face_obj;
EPhysics_Camera *camera;
btQuaternion quat;
btTransform trans;
Evas_Map *map;
Eina_List *l;
double rate;
void *ldata;
ephysics_world_render_geometry_get(body->world, &wx, &wy, NULL,
NULL, &wh, NULL);
camera = ephysics_world_camera_get(body->world);
ephysics_camera_position_get(camera, &cx, &cy);
cx -= wx;
cy -= wy;
rate = ephysics_world_rate_get(body->world);
trans = _ephysics_body_transform_get(body);
bx = (int) (trans.getOrigin().getX() * rate) - cx;
by = wh + wy - (int) (trans.getOrigin().getY() * rate) - cy;
x = bx - body->size.w / 2;
y = by - body->size.h / 2;
z = (int) (trans.getOrigin().getZ() * rate);
quat = trans.getRotation();
quat.normalize();
EINA_LIST_FOREACH(body->face_objs, l, ldata)
{
Evas_Object *obj;
Evas_Coord w, h;
face_obj = (EPhysics_Body_Face_Obj *)ldata;
obj = face_obj->obj;
evas_object_geometry_get(obj, NULL, NULL, &w, &h);
evas_object_move(obj, x, y);
if ((!w) || (!h))
{
DBG("Evas object with no geometry: %p, w=%i h=%i", obj, w, h);
continue;
}
map = evas_map_new(4);
switch(face_obj->face)
{
case EPHYSICS_BODY_CYLINDER_FACE_MIDDLE_FRONT:
evas_map_util_points_populate_from_object_full(map, obj, z);
break;
case EPHYSICS_BODY_CYLINDER_FACE_MIDDLE_BACK:
evas_map_util_points_populate_from_object_full(map, obj, z + 1);
evas_map_util_3d_rotate(map, 0, 180, 0, bx, by, z + 1);
break;
case EPHYSICS_BODY_CYLINDER_FACE_FRONT:
evas_map_util_points_populate_from_object_full(
map, obj, z - body->size.d / 2);
break;
case EPHYSICS_BODY_CYLINDER_FACE_BACK:
evas_map_util_points_populate_from_object_full(
map, obj, z + body->size.d / 2);
evas_map_util_3d_rotate(map, 0, 180, 0, bx, by,
z + body->size.d / 2);
break;
default:
WRN("Face %i not updated", face_obj->face);
evas_map_free(map);
continue;
}
evas_map_util_quat_rotate(map, quat.x(), -quat.y(), quat.z(), -quat.w(),
bx, by, z);
_ephysics_body_evas_obj_map_apply(body, map, obj, EINA_TRUE,
EINA_FALSE);
}
}
static void
_ephysics_box_face_objs_update(EPhysics_Body *body)
{
EPhysics_Body_Face_Obj *face_obj;
int i, x, y, z, wx, wy, wh, cx, cy;
EPhysics_Camera *camera;
Evas_Coord v[8][3];
btQuaternion quat;
btTransform trans;
btBoxShape *shape;
Evas_Map *map;
Eina_List *l;
double rate;
void *ldata;
ephysics_world_render_geometry_get(body->world, &wx, &wy, NULL,
NULL, &wh, NULL);
camera = ephysics_world_camera_get(body->world);
ephysics_camera_position_get(camera, &cx, &cy);
cx -= wx;
cy -= wy;
rate = ephysics_world_rate_get(body->world);
trans = _ephysics_body_transform_get(body);
x = (int) (trans.getOrigin().getX() * rate) - cx;
y = wh + wy - (int) (trans.getOrigin().getY() * rate) - cy;
z = (int) (trans.getOrigin().getZ() * rate);
shape = (btBoxShape *) body->collision_shape;
for (i = 0; i < 8; i++)
{
btVector3 vertice;
shape->getVertex(i, vertice);
v[i][0] = vertice.getX() * rate + x;
v[i][1] = - vertice.getY() * rate + y;
v[i][2] = vertice.getZ() * rate + z;
}
quat = trans.getRotation();
quat.normalize();
EINA_LIST_FOREACH(body->face_objs, l, ldata)
{
Evas_Object *obj;
Evas_Coord w, h;
face_obj = (EPhysics_Body_Face_Obj *)ldata;
obj = face_obj->obj;
evas_object_geometry_get(obj, NULL, NULL, &w, &h);
map = evas_map_new(4);
evas_map_point_image_uv_set(map, 0, 0, 0);
evas_map_point_image_uv_set(map, 1, w, 0);
evas_map_point_image_uv_set(map, 2, w, h);
evas_map_point_image_uv_set(map, 3, 0, h);
switch(face_obj->face)
{
case EPHYSICS_BODY_BOX_FACE_MIDDLE_FRONT:
evas_map_point_coord_set(map, 0, v[5][0], v[5][1], z);
evas_map_point_coord_set(map, 1, v[4][0], v[4][1], z);
evas_map_point_coord_set(map, 2, v[6][0], v[6][1], z);
evas_map_point_coord_set(map, 3, v[7][0], v[7][1], z);
break;
case EPHYSICS_BODY_BOX_FACE_MIDDLE_BACK:
evas_map_point_coord_set(map, 0, v[0][0], v[0][1], z);
evas_map_point_coord_set(map, 1, v[1][0], v[1][1], z);
evas_map_point_coord_set(map, 2, v[3][0], v[3][1], z);
evas_map_point_coord_set(map, 3, v[2][0], v[2][1], z);
break;
case EPHYSICS_BODY_BOX_FACE_FRONT:
evas_map_point_coord_set(map, 0, v[5][0], v[5][1], v[5][2]);
evas_map_point_coord_set(map, 1, v[4][0], v[4][1], v[4][2]);
evas_map_point_coord_set(map, 2, v[6][0], v[6][1], v[6][2]);
evas_map_point_coord_set(map, 3, v[7][0], v[7][1], v[7][2]);
break;
case EPHYSICS_BODY_BOX_FACE_BACK:
evas_map_point_coord_set(map, 0, v[0][0], v[0][1], v[0][2]);
evas_map_point_coord_set(map, 1, v[1][0], v[1][1], v[1][2]);
evas_map_point_coord_set(map, 2, v[3][0], v[3][1], v[3][2]);
evas_map_point_coord_set(map, 3, v[2][0], v[2][1], v[2][2]);
break;
case EPHYSICS_BODY_BOX_FACE_RIGHT:
evas_map_point_coord_set(map, 0, v[4][0], v[4][1], v[4][2]);
evas_map_point_coord_set(map, 1, v[0][0], v[0][1], v[0][2]);
evas_map_point_coord_set(map, 2, v[2][0], v[2][1], v[2][2]);
evas_map_point_coord_set(map, 3, v[6][0], v[6][1], v[6][2]);
break;
case EPHYSICS_BODY_BOX_FACE_LEFT:
evas_map_point_coord_set(map, 0, v[1][0], v[1][1], v[1][2]);
evas_map_point_coord_set(map, 1, v[5][0], v[5][1], v[5][2]);
evas_map_point_coord_set(map, 2, v[7][0], v[7][1], v[7][2]);
evas_map_point_coord_set(map, 3, v[3][0], v[3][1], v[3][2]);
break;
case EPHYSICS_BODY_BOX_FACE_TOP:
evas_map_point_coord_set(map, 0, v[1][0], v[1][1], v[1][2]);
evas_map_point_coord_set(map, 1, v[0][0], v[0][1], v[0][2]);
evas_map_point_coord_set(map, 2, v[4][0], v[4][1], v[4][2]);
evas_map_point_coord_set(map, 3, v[5][0], v[5][1], v[5][2]);
break;
case EPHYSICS_BODY_BOX_FACE_BOTTOM:
evas_map_point_coord_set(map, 0, v[7][0], v[7][1], v[7][2]);
evas_map_point_coord_set(map, 1, v[6][0], v[6][1], v[6][2]);
evas_map_point_coord_set(map, 2, v[2][0], v[2][1], v[2][2]);
evas_map_point_coord_set(map, 3, v[3][0], v[3][1], v[3][2]);
break;
default:
WRN("Face %i not updated", face_obj->face);
evas_map_free(map);
continue;
}
evas_map_util_quat_rotate(map, quat.x(), -quat.y(), quat.z(), -quat.w(),
x, y, z);
_ephysics_body_evas_obj_map_apply(body, map, obj, EINA_TRUE,
EINA_FALSE);
}
}
static void
_ephysics_body_evas_object_update(EPhysics_Body *body, Evas_Object *evas_obj)
{
int bx, by, x, y, z, w, h, wx, wy, wh, cx, cy;
EPhysics_Camera *camera;
btTransform trans;
btQuaternion quat;
Evas_Map *map;
double rate;
trans = _ephysics_body_transform_get(body);
ephysics_world_render_geometry_get(body->world, &wx, &wy, NULL,
NULL, &wh, NULL);
camera = ephysics_world_camera_get(body->world);
ephysics_camera_position_get(camera, &cx, &cy);
cx -= wx;
cy -= wy;
evas_object_geometry_get(evas_obj, NULL, NULL, &w, &h);
rate = ephysics_world_rate_get(body->world);
bx = (int) (trans.getOrigin().getX() * rate) - cx;
by = wh + wy - (int) (trans.getOrigin().getY() * rate) - cy;
x = bx - w * body->cm.x;
y = by - h * body->cm.y;
z = (int) (trans.getOrigin().getZ() * rate);
evas_object_move(evas_obj, x, y);
if ((!w) || (!h))
{
DBG("Evas object with no geometry: %p, w=%i h=%i", evas_obj, w, h);
return;
}
if (body->type != EPHYSICS_BODY_TYPE_RIGID)
{
if (!body->back_face_culling)
evas_object_show(body->evas_obj);
_ephysics_body_soft_body_slices_apply(body, body->evas_obj,
body->default_face->slices);
return;
}
map = evas_map_new(4);
evas_map_util_points_populate_from_object(map, body->evas_obj);
quat = trans.getRotation();
quat.normalize();
evas_map_util_quat_rotate(map, quat.x(), -quat.y(), quat.z(), -quat.w(),
bx, by, z);
_ephysics_body_evas_obj_map_apply(body, map, evas_obj,
body->back_face_culling, EINA_TRUE);
}
static void
_ephysics_body_soft_body_update(EPhysics_Body *body)
{
Eina_List *l;
void *ldata;
EPhysics_Body_Face_Slice *face_slice;
EPhysics_Body_Face_Obj *face_obj;
EINA_LIST_FOREACH(body->faces_slices, l, ldata)
{
face_slice = (EPhysics_Body_Face_Slice *)ldata;
face_obj = _ephysics_body_face_evas_object_get(body,
face_slice->face);
if (!face_obj) continue;
_ephysics_body_soft_body_slices_apply(body, face_obj->obj,
face_slice->slices);
}
}
static void
_ephysics_body_evas_object_default_update(EPhysics_Body *body)
{
if (body->face_objs)
{
if (body->type == EPHYSICS_BODY_TYPE_CLOTH)
_ephysics_cloth_face_objs_update(body);
else if (body->type == EPHYSICS_BODY_TYPE_RIGID)
{
if (body->shape == EPHYSICS_BODY_SHAPE_CYLINDER)
_ephysics_cylinder_face_objs_update(body);
else if (body->shape == EPHYSICS_BODY_SHAPE_BOX)
_ephysics_box_face_objs_update(body);
}
else if (body->type == EPHYSICS_BODY_TYPE_SOFT)
_ephysics_body_soft_body_update(body);
if (body->evas_obj)
evas_object_hide(body->evas_obj);
return;
}
if (!body->evas_obj)
return;
_ephysics_body_evas_object_update(body, body->evas_obj);
}
static void
_ephysics_body_outside_render_area_check(EPhysics_Body *body)
{
int wx, wy, wz, ww, wh, wd, bx, by, bz, bw, bh, bd;
ephysics_world_render_geometry_get(body->world, &wx, &wy, &wz,
&ww, &wh, &wd);
ephysics_body_geometry_get(body, &bx, &by, &bz, &bw, &bh, &bd);
// FIXME: check what should be done regarding rotated bodies
if (((ephysics_world_bodies_outside_top_autodel_get(body->world)) &&
(by + bh < wy)) ||
((ephysics_world_bodies_outside_bottom_autodel_get(body->world)) &&
(by > wy + wh)) ||
((ephysics_world_bodies_outside_left_autodel_get(body->world)) &&
(bx + bh < wx)) ||
((ephysics_world_bodies_outside_right_autodel_get(body->world)) &&
(bx > wx + ww)) ||
((ephysics_world_bodies_outside_front_autodel_get(body->world)) &&
(bz + bd < wz)) ||
((ephysics_world_bodies_outside_back_autodel_get(body->world)) &&
(bz > wz + wd)))
{
DBG("Body %p out of render area", body);
ephysics_body_del(body);
}
}
void
ephysics_body_forces_apply(EPhysics_Body *body)
{
if (!((body->force.x) || (body->force.y) || (body->force.z) ||
(body->force.torque_x) || (body->force.torque_y) ||
(body->force.torque_z)))
return;
DBG("body: %p, applying forces: %lf, %lf, %lf", body, body->force.x,
body->force.y, body->force.z);
ephysics_body_activate(body, EINA_TRUE);
body->rigid_body->applyCentralForce(btVector3(body->force.x,
body->force.y,
body->force.z));
body->rigid_body->applyTorque(btVector3(body->force.torque_x,
body->force.torque_y,
body->force.torque_z));
}
void
ephysics_body_recalc(EPhysics_Body *body, double rate)
{
Evas_Coord x, y, z, w, h, d;
double vx, vy, vz, lt, at;
ephysics_body_geometry_get(body, &x, &y, &z, &w, &h, &d);
ephysics_body_linear_velocity_get(body, &vx, &vy, &vz);
ephysics_body_sleeping_threshold_get(body, <, &at);
_ephysics_body_geometry_set(body, x, y, z, w, h, d, rate);
_ephysics_body_linear_velocity_set(body, vx, vy, vz, rate);
_ephysics_body_sleeping_threshold_set(body, lt, at, rate);
}
void
ephysics_body_evas_object_update_select(EPhysics_Body *body)
{
Eina_Bool callback_called = EINA_FALSE;
if (!body || body->deleted)
return;
callback_called = _ephysics_body_event_callback_call(
body, EPHYSICS_CALLBACK_BODY_UPDATE, (void *) body->evas_obj);
if (!callback_called)
_ephysics_body_evas_object_default_update(body);
if (ephysics_world_bodies_outside_autodel_get(body->world))
_ephysics_body_outside_render_area_check(body);
}
EAPI void
ephysics_body_collision_position_get(const EPhysics_Body_Collision *collision, Evas_Coord *x, Evas_Coord *y, Evas_Coord *z)
{
if (!collision)
{
ERR("Can't get body's collision data, collision is null.");
return;
}
if (x) *x = collision->x;
if (y) *y = collision->y;
if (z) *z = collision->z;
}
EAPI EPhysics_Body *
ephysics_body_collision_contact_body_get(const EPhysics_Body_Collision *collision)
{
if (!collision)
{
ERR("Can't get body's collision contact body, collision is null.");
return NULL;
}
return collision->contact_body;
}
void
ephysics_body_contact_processed(EPhysics_Body *body, EPhysics_Body *contact_body, btVector3 position)
{
EPhysics_Body_Collision *collision;
EPhysics_World *world;;
double rate;
int wy, wh;
if ((!body) || (!contact_body) || (body->collision_cb < 1))
return;
collision = (EPhysics_Body_Collision *)calloc(
1, sizeof(EPhysics_Body_Collision));
if (!collision)
{
ERR("Can't allocate collision data structure.");
return;
}
world = contact_body->world;
ephysics_world_render_geometry_get(world, NULL, &wy, NULL, NULL, &wh, NULL);
rate = ephysics_world_rate_get(world);
collision->contact_body = contact_body;
collision->x = position.getX() * rate;
collision->y = wh + wy - (position.getY() * rate);
collision->z = position.getZ() * rate;
_ephysics_body_event_callback_call(body, EPHYSICS_CALLBACK_BODY_COLLISION,
(void *) collision);
free(collision);
}
btRigidBody *
ephysics_body_rigid_body_get(const EPhysics_Body *body)
{
return body->rigid_body;
}
btSoftBody *
ephysics_body_soft_body_get(const EPhysics_Body *body)
{
return body->soft_body;
}
EAPI void
ephysics_body_soft_body_anchor_hardness_set(EPhysics_Body *body, double hardness)
{
if (!body)
{
ERR("Can't set soft body's anchor hardness, body is null.");
return;
}
if (!body->soft_body)
{
ERR("Can't set soft body's anchor hardness, body seems not to be a soft"
" body.");
return;
}
if (hardness < 0 || hardness > 100)
{
ERR("Can't set soft body's anchor hardness, it must be between 0 and"
" 100.");
return;
}
ephysics_world_lock_take(body->world);
body->anchor_hardness = EINA_TRUE;
body->soft_body->m_cfg.kAHR = 1 - (hardness / 100);
ephysics_world_lock_release(body->world);
DBG("Soft body anchor hardness set to: %lf", hardness);
}
EAPI double
ephysics_body_soft_body_anchor_hardness_get(EPhysics_Body *body)
{
if (!body)
{
ERR("Can't get soft body's anchor hardness, body is null.");
return -1;
}
if (!body->soft_body)
{
ERR("Can't get soft body's anchor hardness, body seems not to be a soft"
" body.");
return -1;
}
return body->soft_body->m_cfg.kAHR * 100;
}
EAPI void
ephysics_body_soft_body_drag_coefficient_set(EPhysics_Body *body, double coefficient)
{
if (!body)
{
ERR("Can't set soft body's drag coefficient, body is null.");
return;
}
if (!body->soft_body)
{
ERR("Can't set soft body's drag coefficient, body seems not to be a soft"
" body.");
return;
}
body->soft_body->m_cfg.kDG = coefficient;
DBG("Soft body drag coefficient set to: %lf", coefficient);
}
EAPI double
ephysics_body_