719 lines
21 KiB
C
719 lines
21 KiB
C
|
/*
|
||
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU General Public License
|
||
|
* as published by the Free Software Foundation; either version 2
|
||
|
* of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software Foundation,
|
||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
*
|
||
|
* The Original Code is Copyright (C) 2013 Blender Foundation
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* The Original Code is: all of this file.
|
||
|
*
|
||
|
* Contributor(s): Joshua Leung, Sergej Reich
|
||
|
*
|
||
|
* ***** END GPL LICENSE BLOCK *****
|
||
|
*/
|
||
|
|
||
|
/** \file rigidbody.c
|
||
|
* \ingroup blenkernel
|
||
|
* \brief Blender-side interface and methods for dealing with Rigid Body simulations
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <stddef.h>
|
||
|
#include <float.h>
|
||
|
#include <math.h>
|
||
|
#include <limits.h>
|
||
|
|
||
|
#include "MEM_guardedalloc.h"
|
||
|
|
||
|
#include "BLI_blenlib.h"
|
||
|
#include "BLI_math.h"
|
||
|
|
||
|
#include "RBI_api.h"
|
||
|
|
||
|
#include "DNA_anim_types.h"
|
||
|
#include "DNA_group_types.h"
|
||
|
#include "DNA_mesh_types.h"
|
||
|
#include "DNA_meshdata_types.h"
|
||
|
#include "DNA_object_types.h"
|
||
|
#include "DNA_object_force.h"
|
||
|
#include "DNA_rigidbody_types.h"
|
||
|
#include "DNA_scene_types.h"
|
||
|
|
||
|
#include "BKE_animsys.h"
|
||
|
#include "BKE_cdderivedmesh.h"
|
||
|
#include "BKE_effect.h"
|
||
|
#include "BKE_group.h"
|
||
|
#include "BKE_object.h"
|
||
|
#include "BKE_mesh.h"
|
||
|
#include "BKE_pointcache.h"
|
||
|
#include "BKE_rigidbody.h"
|
||
|
#include "BKE_global.h"
|
||
|
#include "BKE_utildefines.h"
|
||
|
|
||
|
#include "RNA_access.h"
|
||
|
|
||
|
/* ************************************** */
|
||
|
/* Memory Management */
|
||
|
|
||
|
/* Freeing Methods --------------------- */
|
||
|
|
||
|
/* Free rigidbody world */
|
||
|
void BKE_rigidbody_free_world(RigidBodyWorld *rbw)
|
||
|
{
|
||
|
GroupObject *go;
|
||
|
/* sanity check */
|
||
|
if (!rbw)
|
||
|
return;
|
||
|
|
||
|
if (rbw->physics_world) {
|
||
|
/* free physics references, we assume that all physics objects in will have been added to the world */
|
||
|
if (rbw->group) {
|
||
|
for (go = rbw->group->gobject.first; go; go = go->next) {
|
||
|
if (go->ob && go->ob->rigidbody_object) {
|
||
|
RigidBodyOb *rbo = go->ob->rigidbody_object;
|
||
|
|
||
|
if (rbo->physics_object)
|
||
|
RB_dworld_remove_body(rbw->physics_world, rbo->physics_object);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/* free dynamics world */
|
||
|
RB_dworld_delete(rbw->physics_world);
|
||
|
}
|
||
|
if (rbw->objects)
|
||
|
free(rbw->objects);
|
||
|
|
||
|
/* free rigidbody world itself */
|
||
|
MEM_freeN(rbw);
|
||
|
}
|
||
|
|
||
|
/* Free RigidBody settings and sim instances */
|
||
|
void BKE_rigidbody_free_object(Object *ob)
|
||
|
{
|
||
|
RigidBodyOb *rbo = (ob) ? ob->rigidbody_object : NULL;
|
||
|
|
||
|
/* sanity check */
|
||
|
if (rbo == NULL)
|
||
|
return;
|
||
|
|
||
|
/* free physics references */
|
||
|
if (rbo->physics_object) {
|
||
|
RB_body_delete(rbo->physics_object);
|
||
|
rbo->physics_object = NULL;
|
||
|
}
|
||
|
|
||
|
if (rbo->physics_shape) {
|
||
|
RB_shape_delete(rbo->physics_shape);
|
||
|
rbo->physics_shape = NULL;
|
||
|
}
|
||
|
|
||
|
/* free data itself */
|
||
|
MEM_freeN(rbo);
|
||
|
ob->rigidbody_object = NULL;
|
||
|
}
|
||
|
|
||
|
/* Copying Methods --------------------- */
|
||
|
|
||
|
/* These just copy the data, clearing out references to physics objects.
|
||
|
* Anything that uses them MUST verify that the copied object will
|
||
|
* be added to relevant groups later...
|
||
|
*/
|
||
|
|
||
|
RigidBodyOb *BKE_rigidbody_copy_object(Object *ob)
|
||
|
{
|
||
|
RigidBodyOb *rboN = NULL;
|
||
|
|
||
|
if (ob->rigidbody_object) {
|
||
|
/* just duplicate the whole struct first (to catch all the settings) */
|
||
|
rboN = MEM_dupallocN(ob->rigidbody_object);
|
||
|
|
||
|
/* tag object as needing to be verified */
|
||
|
rboN->flag |= RBO_FLAG_NEEDS_VALIDATE;
|
||
|
|
||
|
/* clear out all the fields which need to be revalidated later */
|
||
|
rboN->physics_object = NULL;
|
||
|
rboN->physics_shape = NULL;
|
||
|
}
|
||
|
|
||
|
/* return new copy of settings */
|
||
|
return rboN;
|
||
|
}
|
||
|
|
||
|
/* ************************************** */
|
||
|
/* Setup Utilities - Validate Sim Instances */
|
||
|
|
||
|
/* create collision shape of mesh - convex hull */
|
||
|
static rbCollisionShape *rigidbody_get_shape_convexhull_from_mesh(Object *ob, float margin, bool *can_embed)
|
||
|
{
|
||
|
rbCollisionShape *shape = NULL;
|
||
|
Mesh *me = NULL;
|
||
|
|
||
|
if (ob->type == OB_MESH && ob->data) {
|
||
|
me = ob->data;
|
||
|
}
|
||
|
else {
|
||
|
printf("ERROR: cannot make Convex Hull collision shape for non-Mesh object\n");
|
||
|
}
|
||
|
|
||
|
if (me && me->totvert) {
|
||
|
shape = RB_shape_new_convex_hull((float *)me->mvert, sizeof(MVert), me->totvert, margin, can_embed);
|
||
|
}
|
||
|
else {
|
||
|
printf("ERROR: no vertices to define Convex Hull collision shape with\n");
|
||
|
}
|
||
|
|
||
|
return shape;
|
||
|
}
|
||
|
|
||
|
/* create collision shape of mesh - triangulated mesh
|
||
|
* returns NULL if creation fails.
|
||
|
*/
|
||
|
static rbCollisionShape *rigidbody_get_shape_trimesh_from_mesh(Object *ob)
|
||
|
{
|
||
|
rbCollisionShape *shape = NULL;
|
||
|
|
||
|
if (ob->type == OB_MESH) {
|
||
|
DerivedMesh *dm = CDDM_from_mesh(ob->data, ob);
|
||
|
|
||
|
MVert *mvert;
|
||
|
MFace *mface;
|
||
|
int totvert;
|
||
|
int totface;
|
||
|
|
||
|
/* ensure mesh validity, then grab data */
|
||
|
DM_ensure_tessface(dm);
|
||
|
|
||
|
mvert = (dm) ? dm->getVertArray(dm) : NULL;
|
||
|
totvert = (dm) ? dm->getNumVerts(dm) : 0;
|
||
|
mface = (dm) ? dm->getTessFaceArray(dm) : NULL;
|
||
|
totface = (dm) ? dm->getNumTessFaces(dm) : 0;
|
||
|
|
||
|
/* sanity checking - potential case when no data will be present */
|
||
|
if ((totvert == 0) || (totface == 0)) {
|
||
|
printf("WARNING: no geometry data converted for Mesh Collision Shape (ob = %s)\n", ob->id.name + 2);
|
||
|
}
|
||
|
else {
|
||
|
rbMeshData *mdata;
|
||
|
int i;
|
||
|
|
||
|
/* init mesh data for collision shape */
|
||
|
mdata = RB_trimesh_data_new();
|
||
|
|
||
|
/* loop over all faces, adding them as triangles to the collision shape
|
||
|
* (so for some faces, more than triangle will get added)
|
||
|
*/
|
||
|
for (i = 0; (i < totface) && (mface) && (mvert); i++, mface++) {
|
||
|
/* add first triangle - verts 1,2,3 */
|
||
|
{
|
||
|
MVert *va = (IN_RANGE(mface->v1, 0, totvert)) ? (mvert + mface->v1) : (mvert);
|
||
|
MVert *vb = (IN_RANGE(mface->v2, 0, totvert)) ? (mvert + mface->v2) : (mvert);
|
||
|
MVert *vc = (IN_RANGE(mface->v3, 0, totvert)) ? (mvert + mface->v3) : (mvert);
|
||
|
|
||
|
RB_trimesh_add_triangle(mdata, va->co, vb->co, vc->co);
|
||
|
}
|
||
|
|
||
|
/* add second triangle if needed - verts 1,3,4 */
|
||
|
if (mface->v4) {
|
||
|
MVert *va = (IN_RANGE(mface->v1, 0, totvert)) ? (mvert + mface->v1) : (mvert);
|
||
|
MVert *vb = (IN_RANGE(mface->v3, 0, totvert)) ? (mvert + mface->v3) : (mvert);
|
||
|
MVert *vc = (IN_RANGE(mface->v4, 0, totvert)) ? (mvert + mface->v4) : (mvert);
|
||
|
|
||
|
RB_trimesh_add_triangle(mdata, va->co, vb->co, vc->co);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* construct collision shape
|
||
|
*
|
||
|
* These have been chosen to get better speed/accuracy tradeoffs with regards
|
||
|
* to limitations of each:
|
||
|
* - BVH-Triangle Mesh: for passive objects only. Despite having greater
|
||
|
* speed/accuracy, they cannot be used for moving objects.
|
||
|
* - GImpact Mesh: for active objects. These are slower and less stable,
|
||
|
* but are more flexible for general usage.
|
||
|
*/
|
||
|
if (ob->rigidbody_object->type == RBO_TYPE_PASSIVE) {
|
||
|
shape = RB_shape_new_trimesh(mdata);
|
||
|
}
|
||
|
else {
|
||
|
shape = RB_shape_new_gimpact_mesh(mdata);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* cleanup temp data */
|
||
|
if (dm) {
|
||
|
dm->release(dm);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
printf("ERROR: cannot make Triangular Mesh collision shape for non-Mesh object\n");
|
||
|
}
|
||
|
|
||
|
return shape;
|
||
|
}
|
||
|
|
||
|
/* Create new physics sim collision shape for object and store it,
|
||
|
* or remove the existing one first and replace...
|
||
|
*/
|
||
|
void BKE_rigidbody_validate_sim_shape(Object *ob, short rebuild)
|
||
|
{
|
||
|
RigidBodyOb *rbo = ob->rigidbody_object;
|
||
|
rbCollisionShape *new_shape = NULL;
|
||
|
BoundBox *bb = NULL;
|
||
|
float size[3] = {1.0f, 1.0f, 1.0f};
|
||
|
float radius = 1.0f;
|
||
|
float height = 1.0f;
|
||
|
float capsule_height;
|
||
|
float hull_margin = 0.0f;
|
||
|
bool can_embed = true;
|
||
|
|
||
|
/* sanity check */
|
||
|
if (rbo == NULL)
|
||
|
return;
|
||
|
|
||
|
/* don't create a new shape if we already have one and don't want to rebuild it */
|
||
|
if (rbo->physics_shape && !rebuild)
|
||
|
return;
|
||
|
|
||
|
/* if automatically determining dimensions, use the Object's boundbox
|
||
|
* - assume that all quadrics are standing upright on local z-axis
|
||
|
* - assume even distribution of mass around the Object's pivot
|
||
|
* (i.e. Object pivot is centralised in boundbox)
|
||
|
*/
|
||
|
// XXX: all dimensions are auto-determined now... later can add stored settings for this
|
||
|
/* get object dimensions without scaling */
|
||
|
bb = BKE_object_boundbox_get(ob);
|
||
|
if (bb) {
|
||
|
size[0] = (bb->vec[4][0] - bb->vec[0][0]);
|
||
|
size[1] = (bb->vec[2][1] - bb->vec[0][1]);
|
||
|
size[2] = (bb->vec[1][2] - bb->vec[0][2]);
|
||
|
}
|
||
|
mul_v3_fl(size, 0.5f);
|
||
|
|
||
|
if (ELEM3(rbo->shape, RB_SHAPE_CAPSULE, RB_SHAPE_CYLINDER, RB_SHAPE_CONE)) {
|
||
|
/* take radius as largest x/y dimension, and height as z-dimension */
|
||
|
radius = MAX2(size[0], size[1]);
|
||
|
height = size[2];
|
||
|
}
|
||
|
else if (rbo->shape == RB_SHAPE_SPHERE) {
|
||
|
/* take radius to the the largest dimension to try and encompass everything */
|
||
|
radius = MAX3(size[0], size[1], size[2]);
|
||
|
}
|
||
|
|
||
|
/* create new shape */
|
||
|
switch (rbo->shape) {
|
||
|
case RB_SHAPE_BOX:
|
||
|
new_shape = RB_shape_new_box(size[0], size[1], size[2]);
|
||
|
break;
|
||
|
|
||
|
case RB_SHAPE_SPHERE:
|
||
|
new_shape = RB_shape_new_sphere(radius);
|
||
|
break;
|
||
|
|
||
|
case RB_SHAPE_CAPSULE:
|
||
|
capsule_height = (height - radius) * 2.0f;
|
||
|
new_shape = RB_shape_new_capsule(radius, (capsule_height > 0.0f) ? capsule_height : 0.0f);
|
||
|
break;
|
||
|
case RB_SHAPE_CYLINDER:
|
||
|
new_shape = RB_shape_new_cylinder(radius, height);
|
||
|
break;
|
||
|
case RB_SHAPE_CONE:
|
||
|
new_shape = RB_shape_new_cone(radius, height * 2.0f);
|
||
|
break;
|
||
|
|
||
|
case RB_SHAPE_CONVEXH:
|
||
|
/* try to emged collision margin */
|
||
|
if (!(rbo->flag & RBO_FLAG_USE_MARGIN))
|
||
|
hull_margin = 0.04f;
|
||
|
new_shape = rigidbody_get_shape_convexhull_from_mesh(ob, hull_margin, &can_embed);
|
||
|
if (!(rbo->flag & RBO_FLAG_USE_MARGIN))
|
||
|
rbo->margin = (can_embed) ? 0.04f : 0.0f; /* RB_TODO ideally we shouldn't directly change the margin here */
|
||
|
break;
|
||
|
case RB_SHAPE_TRIMESH:
|
||
|
new_shape = rigidbody_get_shape_trimesh_from_mesh(ob);
|
||
|
break;
|
||
|
}
|
||
|
/* assign new collision shape if creation was successful */
|
||
|
if (new_shape) {
|
||
|
if (rbo->physics_shape)
|
||
|
RB_shape_delete(rbo->physics_shape);
|
||
|
rbo->physics_shape = new_shape;
|
||
|
RB_shape_set_margin(rbo->physics_shape, RBO_GET_MARGIN(rbo));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* --------------------- */
|
||
|
|
||
|
/* Create physics sim representation of object given RigidBody settings
|
||
|
* < rebuild: even if an instance already exists, replace it
|
||
|
*/
|
||
|
void BKE_rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, short rebuild)
|
||
|
{
|
||
|
RigidBodyOb *rbo = (ob) ? ob->rigidbody_object : NULL;
|
||
|
float loc[3];
|
||
|
float rot[4];
|
||
|
|
||
|
/* sanity checks:
|
||
|
* - object doesn't have RigidBody info already: then why is it here?
|
||
|
*/
|
||
|
if (rbo == NULL)
|
||
|
return;
|
||
|
|
||
|
/* make sure collision shape exists */
|
||
|
if (rbo->physics_shape == NULL || rebuild)
|
||
|
BKE_rigidbody_validate_sim_shape(ob, true);
|
||
|
|
||
|
if (rbo->physics_object) {
|
||
|
if (rebuild == false)
|
||
|
RB_dworld_remove_body(rbw->physics_world, rbo->physics_object);
|
||
|
}
|
||
|
if (!rbo->physics_object || rebuild) {
|
||
|
/* remove rigid body if it already exists before creating a new one */
|
||
|
if (rbo->physics_object) {
|
||
|
RB_body_delete(rbo->physics_object);
|
||
|
}
|
||
|
|
||
|
mat4_to_loc_quat(loc, rot, ob->obmat);
|
||
|
|
||
|
rbo->physics_object = RB_body_new(rbo->physics_shape, loc, rot);
|
||
|
|
||
|
RB_body_set_friction(rbo->physics_object, rbo->friction);
|
||
|
RB_body_set_restitution(rbo->physics_object, rbo->restitution);
|
||
|
|
||
|
RB_body_set_damping(rbo->physics_object, rbo->lin_damping, rbo->ang_damping);
|
||
|
RB_body_set_sleep_thresh(rbo->physics_object, rbo->lin_sleep_thresh, rbo->ang_sleep_thresh);
|
||
|
RB_body_set_activation_state(rbo->physics_object, rbo->flag & RBO_FLAG_USE_DEACTIVATION);
|
||
|
|
||
|
if (rbo->type == RBO_TYPE_PASSIVE || rbo->flag & RBO_FLAG_START_DEACTIVATED)
|
||
|
RB_body_deactivate(rbo->physics_object);
|
||
|
|
||
|
|
||
|
RB_body_set_linear_factor(rbo->physics_object,
|
||
|
(ob->protectflag & OB_LOCK_LOCX) == 0,
|
||
|
(ob->protectflag & OB_LOCK_LOCY) == 0,
|
||
|
(ob->protectflag & OB_LOCK_LOCZ) == 0);
|
||
|
RB_body_set_angular_factor(rbo->physics_object,
|
||
|
(ob->protectflag & OB_LOCK_ROTX) == 0,
|
||
|
(ob->protectflag & OB_LOCK_ROTY) == 0,
|
||
|
(ob->protectflag & OB_LOCK_ROTZ) == 0);
|
||
|
|
||
|
RB_body_set_mass(rbo->physics_object, RBO_GET_MASS(rbo));
|
||
|
RB_body_set_kinematic_state(rbo->physics_object, rbo->flag & RBO_FLAG_KINEMATIC || rbo->flag & RBO_FLAG_DISABLED);
|
||
|
}
|
||
|
|
||
|
if (rbw && rbw->physics_world)
|
||
|
RB_dworld_add_body(rbw->physics_world, rbo->physics_object, rbo->col_groups);
|
||
|
}
|
||
|
|
||
|
/* --------------------- */
|
||
|
|
||
|
/* Create physics sim world given RigidBody world settings */
|
||
|
// NOTE: this does NOT update object references that the scene uses, in case those aren't ready yet!
|
||
|
void BKE_rigidbody_validate_sim_world(Scene *scene, RigidBodyWorld *rbw, short rebuild)
|
||
|
{
|
||
|
/* sanity checks */
|
||
|
if (rbw == NULL)
|
||
|
return;
|
||
|
|
||
|
/* create new sim world */
|
||
|
if (rebuild || rbw->physics_world == NULL) {
|
||
|
if (rbw->physics_world)
|
||
|
RB_dworld_delete(rbw->physics_world);
|
||
|
rbw->physics_world = RB_dworld_new(scene->physics_settings.gravity);
|
||
|
}
|
||
|
|
||
|
RB_dworld_set_solver_iterations(rbw->physics_world, rbw->num_solver_iterations);
|
||
|
RB_dworld_set_split_impulse(rbw->physics_world, rbw->flag & RBW_FLAG_USE_SPLIT_IMPULSE);
|
||
|
}
|
||
|
|
||
|
/* ************************************** */
|
||
|
/* Setup Utilities - Create Settings Blocks */
|
||
|
|
||
|
/* Set up RigidBody world */
|
||
|
RigidBodyWorld *BKE_rigidbody_create_world(Scene *scene)
|
||
|
{
|
||
|
/* try to get whatever RigidBody world that might be representing this already */
|
||
|
RigidBodyWorld *rbw;
|
||
|
|
||
|
/* sanity checks
|
||
|
* - there must be a valid scene to add world to
|
||
|
* - there mustn't be a sim world using this group already
|
||
|
*/
|
||
|
if (scene == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
/* create a new sim world */
|
||
|
rbw = MEM_callocN(sizeof(RigidBodyWorld), "RigidBodyWorld");
|
||
|
|
||
|
/* set default settings */
|
||
|
rbw->ltime = PSFRA;
|
||
|
|
||
|
rbw->time_scale = 1.0f;
|
||
|
|
||
|
rbw->steps_per_second = 60; /* Bullet default (60 Hz) */
|
||
|
rbw->num_solver_iterations = 10; /* 10 is bullet default */
|
||
|
|
||
|
/* return this sim world */
|
||
|
return rbw;
|
||
|
}
|
||
|
|
||
|
/* Add rigid body settings to the specified object */
|
||
|
RigidBodyOb *BKE_rigidbody_create_object(Scene *scene, Object *ob, short type)
|
||
|
{
|
||
|
RigidBodyOb *rbo;
|
||
|
RigidBodyWorld *rbw = scene->rigidbody_world;
|
||
|
|
||
|
/* sanity checks
|
||
|
* - rigidbody world must exist
|
||
|
* - object must exist
|
||
|
* - cannot add rigid body if it already exists
|
||
|
*/
|
||
|
if (ob == NULL || (ob->rigidbody_object != NULL))
|
||
|
return NULL;
|
||
|
|
||
|
/* create new settings data, and link it up */
|
||
|
rbo = MEM_callocN(sizeof(RigidBodyOb), "RigidBodyOb");
|
||
|
|
||
|
/* set default settings */
|
||
|
rbo->type = type;
|
||
|
|
||
|
rbo->mass = 1.0f;
|
||
|
|
||
|
rbo->friction = 0.5f; /* best when non-zero. 0.5 is Bullet default */
|
||
|
rbo->restitution = 0.0f; /* best when zero. 0.0 is Bullet default */
|
||
|
|
||
|
rbo->margin = 0.04f; /* 0.04 (in meters) is Bullet default */
|
||
|
|
||
|
rbo->lin_sleep_thresh = 0.4f; /* 0.4 is half of Bullet default */
|
||
|
rbo->ang_sleep_thresh = 0.5f; /* 0.5 is half of Bullet default */
|
||
|
|
||
|
rbo->lin_damping = 0.04f; /* 0.04 is game engine default */
|
||
|
rbo->ang_damping = 0.1f; /* 0.1 is game engine default */
|
||
|
|
||
|
rbo->col_groups = 1;
|
||
|
|
||
|
/* use triangle meshes for passive objects
|
||
|
* use convex hulls for active objects since dynamic triangle meshes are very unstable
|
||
|
*/
|
||
|
if (type == RBO_TYPE_ACTIVE)
|
||
|
rbo->shape = RB_SHAPE_CONVEXH;
|
||
|
else
|
||
|
rbo->shape = RB_SHAPE_TRIMESH;
|
||
|
|
||
|
/* set initial transform */
|
||
|
mat4_to_loc_quat(rbo->pos, rbo->orn, ob->obmat);
|
||
|
|
||
|
/* flag cache as outdated */
|
||
|
BKE_rigidbody_cache_reset(rbw);
|
||
|
|
||
|
/* return this object */
|
||
|
return rbo;
|
||
|
}
|
||
|
|
||
|
/* ************************************** */
|
||
|
/* Utilities API */
|
||
|
|
||
|
/* Get RigidBody world for the given scene, creating one if needed
|
||
|
* < scene: Scene to find active Rigid Body world for
|
||
|
*/
|
||
|
RigidBodyWorld *BKE_rigidbody_get_world(Scene *scene)
|
||
|
{
|
||
|
/* sanity check */
|
||
|
if (scene == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
return scene->rigidbody_world;
|
||
|
}
|
||
|
|
||
|
void BKE_rigidbody_remove_object(Scene *scene, Object *ob)
|
||
|
{
|
||
|
RigidBodyWorld *rbw = scene->rigidbody_world;
|
||
|
RigidBodyOb *rbo = ob->rigidbody_object;
|
||
|
GroupObject *go;
|
||
|
int i;
|
||
|
|
||
|
if (rbw) {
|
||
|
/* remove from rigidbody world, free object won't do this */
|
||
|
if (rbw->physics_world && rbo->physics_object)
|
||
|
RB_dworld_remove_body(rbw->physics_world, rbo->physics_object);
|
||
|
|
||
|
/* remove object from array */
|
||
|
if (rbw && rbw->objects) {
|
||
|
for (i = 0; i < rbw->numbodies; i++) {
|
||
|
if (rbw->objects[i] == ob) {
|
||
|
rbw->objects[i] = NULL;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/* remove object's settings */
|
||
|
BKE_rigidbody_free_object(ob);
|
||
|
/* flag cache as outdated */
|
||
|
BKE_rigidbody_cache_reset(rbw);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ************************************** */
|
||
|
/* Simulation Interface - Bullet */
|
||
|
|
||
|
/* Update object array and rigid body count so they're in sync with the rigid body group */
|
||
|
static void rigidbody_update_ob_array(RigidBodyWorld *rbw)
|
||
|
{
|
||
|
GroupObject *go;
|
||
|
int i, n;
|
||
|
|
||
|
n = BLI_countlist(&rbw->group->gobject);
|
||
|
|
||
|
if (rbw->numbodies != n) {
|
||
|
rbw->numbodies = n;
|
||
|
rbw->objects = realloc(rbw->objects, sizeof(Object *) * rbw->numbodies);
|
||
|
}
|
||
|
|
||
|
for (go = rbw->group->gobject.first, i = 0; go; go = go->next, i++) {
|
||
|
Object *ob = go->ob;
|
||
|
rbw->objects[i] = ob;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void rigidbody_update_sim_world(Scene *scene, RigidBodyWorld *rbw)
|
||
|
{
|
||
|
float adj_gravity[3];
|
||
|
|
||
|
/* adjust gravity to take effector weights into account */
|
||
|
if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
|
||
|
copy_v3_v3(adj_gravity, scene->physics_settings.gravity);
|
||
|
}
|
||
|
else {
|
||
|
zero_v3(adj_gravity);
|
||
|
}
|
||
|
|
||
|
/* update gravity, since this RNA setting is not part of RigidBody settings */
|
||
|
RB_dworld_set_gravity(rbw->physics_world, adj_gravity);
|
||
|
|
||
|
/* update object array in case there are changes */
|
||
|
rigidbody_update_ob_array(rbw);
|
||
|
}
|
||
|
|
||
|
static void rigidbody_update_sim_ob(Scene *scene, RigidBodyWorld *rbw, Object *ob, RigidBodyOb *rbo)
|
||
|
{
|
||
|
float loc[3];
|
||
|
float rot[4];
|
||
|
float scale[3];
|
||
|
|
||
|
/* only update if rigid body exists */
|
||
|
if (rbo->physics_object == NULL)
|
||
|
return;
|
||
|
|
||
|
mat4_decompose(loc, rot, scale, ob->obmat);
|
||
|
|
||
|
/* update scale for all objects */
|
||
|
RB_body_set_scale(rbo->physics_object, scale);
|
||
|
/* compensate for embedded convex hull collision margin */
|
||
|
if (!(rbo->flag & RBO_FLAG_USE_MARGIN) && rbo->shape == RB_SHAPE_CONVEXH)
|
||
|
RB_shape_set_margin(rbo->physics_shape, RBO_GET_MARGIN(rbo) * MIN3(scale[0], scale[1], scale[2]));
|
||
|
|
||
|
/* update rigid body location and rotation for kinematic bodies */
|
||
|
if (rbo->flag & RBO_FLAG_KINEMATIC) {
|
||
|
RB_body_activate(rbo->physics_object);
|
||
|
RB_body_set_loc_rot(rbo->physics_object, loc, rot);
|
||
|
}
|
||
|
/* NOTE: passive objects don't need to be updated since they don't move */
|
||
|
|
||
|
/* NOTE: no other settings need to be explicitly updated here,
|
||
|
* since RNA setters take care of the rest :)
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
/* Updates and validates world, bodies and shapes.
|
||
|
* < rebuild: rebuild entire simulation
|
||
|
*/
|
||
|
static void rigidbody_update_simulation(Scene *scene, RigidBodyWorld *rbw, int rebuild)
|
||
|
{
|
||
|
GroupObject *go;
|
||
|
|
||
|
/* update world */
|
||
|
if (rebuild)
|
||
|
BKE_rigidbody_validate_sim_world(scene, rbw, true);
|
||
|
rigidbody_update_sim_world(scene, rbw);
|
||
|
|
||
|
/* update objects */
|
||
|
for (go = rbw->group->gobject.first; go; go = go->next) {
|
||
|
Object *ob = go->ob;
|
||
|
|
||
|
if (ob && ob->type == OB_MESH) {
|
||
|
/* validate that we've got valid object set up here... */
|
||
|
RigidBodyOb *rbo = ob->rigidbody_object;
|
||
|
/* update transformation matrix of the object so we don't get a frame of lag for simple animations */
|
||
|
BKE_object_where_is_calc(scene, ob);
|
||
|
|
||
|
if (rbo == NULL) {
|
||
|
/* Since this object is included in the sim group but doesn't have
|
||
|
* rigid body settings (perhaps it was added manually), add!
|
||
|
* - assume object to be active? That is the default for newly added settings...
|
||
|
*/
|
||
|
ob->rigidbody_object = BKE_rigidbody_create_object(scene, ob, RBO_TYPE_ACTIVE);
|
||
|
BKE_rigidbody_validate_sim_object(rbw, ob, true);
|
||
|
|
||
|
rbo = ob->rigidbody_object;
|
||
|
}
|
||
|
else {
|
||
|
/* perform simulation data updates as tagged */
|
||
|
/* refresh object... */
|
||
|
if (rebuild) {
|
||
|
/* World has been rebuilt so rebuild object */
|
||
|
BKE_rigidbody_validate_sim_object(rbw, ob, true);
|
||
|
}
|
||
|
else if (rbo->flag & RBO_FLAG_NEEDS_VALIDATE) {
|
||
|
BKE_rigidbody_validate_sim_object(rbw, ob, false);
|
||
|
}
|
||
|
/* refresh shape... */
|
||
|
if (rbo->flag & RBO_FLAG_NEEDS_RESHAPE) {
|
||
|
/* mesh/shape data changed, so force shape refresh */
|
||
|
BKE_rigidbody_validate_sim_shape(ob, true);
|
||
|
/* now tell RB sim about it */
|
||
|
// XXX: we assume that this can only get applied for active/passive shapes that will be included as rigidbodies
|
||
|
RB_body_set_collision_shape(rbo->physics_object, rbo->physics_shape);
|
||
|
}
|
||
|
rbo->flag &= ~(RBO_FLAG_NEEDS_VALIDATE | RBO_FLAG_NEEDS_RESHAPE);
|
||
|
}
|
||
|
|
||
|
/* update simulation object... */
|
||
|
rigidbody_update_sim_ob(scene, rbw, ob, rbo);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Sync rigid body and object transformations */
|
||
|
void BKE_rigidbody_sync_transforms(Scene *scene, Object *ob, float ctime)
|
||
|
{
|
||
|
// RB_TODO implement this
|
||
|
}
|
||
|
|
||
|
void BKE_rigidbody_cache_reset(RigidBodyWorld *rbw)
|
||
|
{
|
||
|
// RB_TODO implement this
|
||
|
}
|
||
|
|
||
|
/* ------------------ */
|
||
|
|
||
|
/* Run RigidBody simulation for the specified physics world */
|
||
|
void BKE_rigidbody_do_simulation(Scene *scene, float ctime)
|
||
|
{
|
||
|
// RB_TODO implement this
|
||
|
}
|
||
|
/* ************************************** */
|