This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/blenkernel/intern/rigidbody.c

1382 lines
45 KiB
C
Raw Normal View History

/*
* ***** 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"
2013-01-23 07:26:39 +00:00
#ifdef WITH_BULLET
# include "RBI_api.h"
#endif
#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_global.h"
#include "BKE_group.h"
#include "BKE_library.h"
#include "BKE_mesh.h"
#include "BKE_object.h"
#include "BKE_pointcache.h"
#include "BKE_rigidbody.h"
#include "BKE_utildefines.h"
#include "RNA_access.h"
2013-01-23 07:26:39 +00:00
#ifdef WITH_BULLET
/* ************************************** */
/* Memory Management */
/* Freeing Methods --------------------- */
/* Free rigidbody world */
void BKE_rigidbody_free_world(RigidBodyWorld *rbw)
{
/* 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 */
GroupObject *go;
if (rbw->constraints) {
for (go = rbw->constraints->gobject.first; go; go = go->next) {
if (go->ob && go->ob->rigidbody_constraint) {
RigidBodyCon *rbc = go->ob->rigidbody_constraint;
if (rbc->physics_constraint)
RB_dworld_remove_constraint(rbw->physics_world, rbc->physics_constraint);
}
}
}
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 cache */
BKE_ptcache_free_list(&(rbw->ptcaches));
rbw->pointcache = NULL;
/* free effector weights */
if (rbw->effector_weights)
MEM_freeN(rbw->effector_weights);
/* 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;
}
/* Free RigidBody constraint and sim instance */
void BKE_rigidbody_free_constraint(Object *ob)
{
RigidBodyCon *rbc = (ob) ? ob->rigidbody_constraint : NULL;
/* sanity check */
if (rbc == NULL)
return;
/* free physics reference */
if (rbc->physics_constraint) {
RB_constraint_delete(rbc->physics_constraint);
rbc->physics_constraint = NULL;
}
/* free data itself */
MEM_freeN(rbc);
ob->rigidbody_constraint = 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;
}
RigidBodyCon *BKE_rigidbody_copy_constraint(Object *ob)
{
RigidBodyCon *rbcN = NULL;
if (ob->rigidbody_constraint) {
/* just duplicate the whole struct first (to catch all the settings) */
rbcN = MEM_dupallocN(ob->rigidbody_constraint);
/* tag object as needing to be verified */
rbcN->flag |= RBC_FLAG_NEEDS_VALIDATE;
/* clear out all the fields which need to be revalidated later */
rbcN->physics_constraint = NULL;
}
/* return new copy of settings */
return rbcN;
}
/* preserve relationships between constraints and rigid bodies after duplication */
void BKE_rigidbody_relink_constraint(RigidBodyCon *rbc)
{
ID_NEW(rbc->ob1);
ID_NEW(rbc->ob2);
}
/* ************************************** */
/* 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;
bool has_volume;
/* 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 centralized 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 */
has_volume = (MIN3(size[0], size[1], size[2]) > 0.0f);
if (!(rbo->flag & RBO_FLAG_USE_MARGIN) && has_volume)
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 && has_volume) ? 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));
}
else { /* otherwise fall back to box shape */
rbo->shape = RB_SHAPE_BOX;
BKE_rigidbody_validate_sim_shape(ob, true);
}
}
/* --------------------- */
/* 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 */
/* FIXME we shouldn't always have to rebuild collision shapes when rebuilding objects, but it's needed for constraints to update correctly */
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,
2013-01-23 23:42:18 +00:00
(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 representation of constraint given rigid body constraint settings
* < rebuild: even if an instance already exists, replace it
*/
void BKE_rigidbody_validate_sim_constraint(RigidBodyWorld *rbw, Object *ob, short rebuild)
{
RigidBodyCon *rbc = (ob) ? ob->rigidbody_constraint : NULL;
float loc[3];
float rot[4];
float lin_lower;
float lin_upper;
float ang_lower;
float ang_upper;
/* sanity checks:
* - object should have a rigid body constraint
* - rigid body constraint should have at least one constrained object
*/
if (rbc == NULL) {
return;
}
if (ELEM4(NULL, rbc->ob1, rbc->ob1->rigidbody_object, rbc->ob2, rbc->ob2->rigidbody_object)) {
if (rbc->physics_constraint) {
RB_dworld_remove_constraint(rbw->physics_world, rbc->physics_constraint);
RB_constraint_delete(rbc->physics_constraint);
rbc->physics_constraint = NULL;
}
return;
}
if (rbc->physics_constraint) {
if (rebuild == false)
RB_dworld_remove_constraint(rbw->physics_world, rbc->physics_constraint);
}
if (rbc->physics_constraint == NULL || rebuild) {
rbRigidBody *rb1 = rbc->ob1->rigidbody_object->physics_object;
rbRigidBody *rb2 = rbc->ob2->rigidbody_object->physics_object;
/* remove constraint if it already exists before creating a new one */
if (rbc->physics_constraint) {
RB_constraint_delete(rbc->physics_constraint);
rbc->physics_constraint = NULL;
}
mat4_to_loc_quat(loc, rot, ob->obmat);
if (rb1 && rb2) {
switch (rbc->type) {
case RBC_TYPE_POINT:
rbc->physics_constraint = RB_constraint_new_point(loc, rb1, rb2);
break;
case RBC_TYPE_FIXED:
rbc->physics_constraint = RB_constraint_new_fixed(loc, rot, rb1, rb2);
break;
case RBC_TYPE_HINGE:
rbc->physics_constraint = RB_constraint_new_hinge(loc, rot, rb1, rb2);
if (rbc->flag & RBC_FLAG_USE_LIMIT_ANG_Z) {
RB_constraint_set_limits_hinge(rbc->physics_constraint, rbc->limit_ang_z_lower, rbc->limit_ang_z_upper);
}
else
RB_constraint_set_limits_hinge(rbc->physics_constraint, 0.0f, -1.0f);
break;
case RBC_TYPE_SLIDER:
rbc->physics_constraint = RB_constraint_new_slider(loc, rot, rb1, rb2);
if (rbc->flag & RBC_FLAG_USE_LIMIT_LIN_X)
RB_constraint_set_limits_slider(rbc->physics_constraint, rbc->limit_lin_x_lower, rbc->limit_lin_x_upper);
else
RB_constraint_set_limits_slider(rbc->physics_constraint, 0.0f, -1.0f);
break;
case RBC_TYPE_PISTON:
rbc->physics_constraint = RB_constraint_new_piston(loc, rot, rb1, rb2);
if (rbc->flag & RBC_FLAG_USE_LIMIT_LIN_X) {
lin_lower = rbc->limit_lin_x_lower;
lin_upper = rbc->limit_lin_x_upper;
}
else {
lin_lower = 0.0f;
lin_upper = -1.0f;
}
if (rbc->flag & RBC_FLAG_USE_LIMIT_ANG_X) {
ang_lower = rbc->limit_ang_x_lower;
ang_upper = rbc->limit_ang_x_upper;
}
else {
ang_lower = 0.0f;
ang_upper = -1.0f;
}
RB_constraint_set_limits_piston(rbc->physics_constraint, lin_lower, lin_upper, ang_lower, ang_upper);
break;
case RBC_TYPE_6DOF_SPRING:
rbc->physics_constraint = RB_constraint_new_6dof_spring(loc, rot, rb1, rb2);
RB_constraint_set_spring_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_X, rbc->flag & RBC_FLAG_USE_SPRING_X);
RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_X, rbc->spring_stiffness_x);
RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_X, rbc->spring_damping_x);
RB_constraint_set_spring_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Y, rbc->flag & RBC_FLAG_USE_SPRING_Y);
RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Y, rbc->spring_stiffness_y);
RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Y, rbc->spring_damping_y);
RB_constraint_set_spring_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Z, rbc->flag & RBC_FLAG_USE_SPRING_Z);
RB_constraint_set_stiffness_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Z, rbc->spring_stiffness_z);
RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Z, rbc->spring_damping_z);
RB_constraint_set_equilibrium_6dof_spring(rbc->physics_constraint);
/* fall through */
case RBC_TYPE_6DOF:
if (rbc->type == RBC_TYPE_6DOF) /* a litte awkward but avoids duplicate code for limits */
rbc->physics_constraint = RB_constraint_new_6dof(loc, rot, rb1, rb2);
if (rbc->flag & RBC_FLAG_USE_LIMIT_LIN_X)
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_LIN_X, rbc->limit_lin_x_lower, rbc->limit_lin_x_upper);
else
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_LIN_X, 0.0f, -1.0f);
if (rbc->flag & RBC_FLAG_USE_LIMIT_LIN_Y)
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_LIN_Y, rbc->limit_lin_y_lower, rbc->limit_lin_y_upper);
else
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_LIN_Y, 0.0f, -1.0f);
if (rbc->flag & RBC_FLAG_USE_LIMIT_LIN_Z)
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_LIN_Z, rbc->limit_lin_z_lower, rbc->limit_lin_z_upper);
else
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_LIN_Z, 0.0f, -1.0f);
if (rbc->flag & RBC_FLAG_USE_LIMIT_ANG_X)
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_ANG_X, rbc->limit_ang_x_lower, rbc->limit_ang_x_upper);
else
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_ANG_X, 0.0f, -1.0f);
if (rbc->flag & RBC_FLAG_USE_LIMIT_ANG_Y)
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_ANG_Y, rbc->limit_ang_y_lower, rbc->limit_ang_y_upper);
else
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_ANG_Y, 0.0f, -1.0f);
if (rbc->flag & RBC_FLAG_USE_LIMIT_ANG_Z)
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_ANG_Z, rbc->limit_ang_z_lower, rbc->limit_ang_z_upper);
else
RB_constraint_set_limits_6dof(rbc->physics_constraint, RB_LIMIT_ANG_Z, 0.0f, -1.0f);
break;
case RBC_TYPE_MOTOR:
rbc->physics_constraint = RB_constraint_new_motor(loc, rot, rb1, rb2);
RB_constraint_set_enable_motor(rbc->physics_constraint, rbc->flag & RBC_FLAG_USE_MOTOR_LIN, rbc->flag & RBC_FLAG_USE_MOTOR_ANG);
RB_constraint_set_max_impulse_motor(rbc->physics_constraint, rbc->motor_lin_max_impulse, rbc->motor_ang_max_impulse);
RB_constraint_set_target_velocity_motor(rbc->physics_constraint, rbc->motor_lin_target_velocity, rbc->motor_ang_target_velocity);
break;
}
}
else { /* can't create constraint without both rigid bodies */
return;
}
RB_constraint_set_enabled(rbc->physics_constraint, rbc->flag & RBC_FLAG_ENABLED);
if (rbc->flag & RBC_FLAG_USE_BREAKING)
RB_constraint_set_breaking_threshold(rbc->physics_constraint, rbc->breaking_threshold);
else
RB_constraint_set_breaking_threshold(rbc->physics_constraint, FLT_MAX);
if (rbc->flag & RBC_FLAG_OVERRIDE_SOLVER_ITERATIONS)
RB_constraint_set_solver_iterations(rbc->physics_constraint, rbc->num_solver_iterations);
else
RB_constraint_set_solver_iterations(rbc->physics_constraint, -1);
}
if (rbw && rbw->physics_world && rbc->physics_constraint) {
RB_dworld_add_constraint(rbw->physics_world, rbc->physics_constraint, rbc->flag & RBC_FLAG_DISABLE_COLLISIONS);
}
}
/* --------------------- */
/* 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->effector_weights = BKE_add_effector_weights(NULL);
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 */
rbw->pointcache = BKE_ptcache_add(&(rbw->ptcaches));
rbw->pointcache->step = 1;
/* return this sim world */
return rbw;
}
RigidBodyWorld *BKE_rigidbody_world_copy(RigidBodyWorld *rbw)
{
RigidBodyWorld *rbwn = MEM_dupallocN(rbw);
if (rbw->effector_weights)
rbwn->effector_weights = MEM_dupallocN(rbw->effector_weights);
if (rbwn->group)
id_us_plus(&rbwn->group->id);
if (rbwn->constraints)
id_us_plus(&rbwn->constraints->id);
rbwn->pointcache = BKE_ptcache_copy_list(&rbwn->ptcaches, &rbw->ptcaches, FALSE);
rbwn->objects = NULL;
rbwn->physics_world = NULL;
rbwn->numbodies = 0;
return rbwn;
}
void BKE_rigidbody_world_groups_relink(RigidBodyWorld *rbw)
{
if (rbw->group && rbw->group->id.newid)
2013-04-25 16:35:57 +00:00
rbw->group = (Group *)rbw->group->id.newid;
if (rbw->constraints && rbw->constraints->id.newid)
2013-04-25 16:35:57 +00:00
rbw->constraints = (Group *)rbw->constraints->id.newid;
if (rbw->effector_weights->group && rbw->effector_weights->group->id.newid)
2013-04-25 16:35:57 +00:00
rbw->effector_weights->group = (Group *)rbw->effector_weights->group->id.newid;
}
/* 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;
}
/* Add rigid body constraint to the specified object */
RigidBodyCon *BKE_rigidbody_create_constraint(Scene *scene, Object *ob, short type)
{
RigidBodyCon *rbc;
RigidBodyWorld *rbw = scene->rigidbody_world;
/* sanity checks
* - rigidbody world must exist
* - object must exist
* - cannot add constraint if it already exists
*/
if (ob == NULL || (ob->rigidbody_constraint != NULL))
return NULL;
/* create new settings data, and link it up */
rbc = MEM_callocN(sizeof(RigidBodyCon), "RigidBodyCon");
/* set default settings */
rbc->type = type;
rbc->ob1 = NULL;
rbc->ob2 = NULL;
rbc->flag |= RBC_FLAG_ENABLED;
rbc->flag |= RBC_FLAG_DISABLE_COLLISIONS;
rbc->breaking_threshold = 10.0f; /* no good default here, just use 10 for now */
rbc->num_solver_iterations = 10; /* 10 is Bullet default */
rbc->limit_lin_x_lower = -1.0f;
rbc->limit_lin_x_upper = 1.0f;
rbc->limit_lin_y_lower = -1.0f;
rbc->limit_lin_y_upper = 1.0f;
rbc->limit_lin_z_lower = -1.0f;
rbc->limit_lin_z_upper = 1.0f;
rbc->limit_ang_x_lower = -M_PI_4;
rbc->limit_ang_x_upper = M_PI_4;
rbc->limit_ang_y_lower = -M_PI_4;
rbc->limit_ang_y_upper = M_PI_4;
rbc->limit_ang_z_lower = -M_PI_4;
rbc->limit_ang_z_upper = M_PI_4;
rbc->spring_damping_x = 0.5f;
rbc->spring_damping_y = 0.5f;
rbc->spring_damping_z = 0.5f;
rbc->spring_stiffness_x = 10.0f;
rbc->spring_stiffness_y = 10.0f;
rbc->spring_stiffness_z = 10.0f;
rbc->motor_lin_max_impulse = 1.0f;
rbc->motor_lin_target_velocity = 1.0f;
rbc->motor_ang_max_impulse = 1.0f;
rbc->motor_ang_target_velocity = 1.0f;
/* flag cache as outdated */
BKE_rigidbody_cache_reset(rbw);
/* return this object */
return rbc;
}
/* ************************************** */
/* 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;
RigidBodyCon *rbc;
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 from rigid body constraints */
if (rbw->constraints) {
for (go = rbw->constraints->gobject.first; go; go = go->next) {
Object *obt = go->ob;
if (obt && obt->rigidbody_constraint) {
rbc = obt->rigidbody_constraint;
if (rbc->ob1 == ob) {
BKE_rigidbody_remove_constraint(scene, obt);
}
if (rbc->ob2 == ob) {
BKE_rigidbody_remove_constraint(scene, obt);
}
}
}
}
}
/* remove object's settings */
BKE_rigidbody_free_object(ob);
/* flag cache as outdated */
BKE_rigidbody_cache_reset(rbw);
}
void BKE_rigidbody_remove_constraint(Scene *scene, Object *ob)
{
RigidBodyWorld *rbw = scene->rigidbody_world;
RigidBodyCon *rbc = ob->rigidbody_constraint;
/* remove from rigidbody world, free object won't do this */
if (rbw && rbw->physics_world && rbc->physics_constraint) {
RB_dworld_remove_constraint(rbw->physics_world, rbc->physics_constraint);
}
/* remove object's settings */
BKE_rigidbody_free_constraint(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);
mul_v3_fl(adj_gravity, rbw->effector_weights->global_gravity * rbw->effector_weights->weight[0]);
}
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]));
/* make transformed objects temporarily kinmatic so that they can be moved by the user during simulation */
if (ob->flag & SELECT && G.moving & G_TRANSFORM_OBJ) {
RB_body_set_kinematic_state(rbo->physics_object, TRUE);
RB_body_set_mass(rbo->physics_object, 0.0f);
}
/* update rigid body location and rotation for kinematic bodies */
if (rbo->flag & RBO_FLAG_KINEMATIC || (ob->flag & SELECT && G.moving & G_TRANSFORM_OBJ)) {
RB_body_activate(rbo->physics_object);
RB_body_set_loc_rot(rbo->physics_object, loc, rot);
}
/* update influence of effectors - but don't do it on an effector */
/* only dynamic bodies need effector update */
else if (rbo->type == RBO_TYPE_ACTIVE && ((ob->pd == NULL) || (ob->pd->forcefield == PFIELD_NULL))) {
EffectorWeights *effector_weights = rbw->effector_weights;
EffectedPoint epoint;
ListBase *effectors;
/* get effectors present in the group specified by effector_weights */
effectors = pdInitEffectors(scene, ob, NULL, effector_weights);
if (effectors) {
float eff_force[3] = {0.0f, 0.0f, 0.0f};
float eff_loc[3], eff_vel[3];
/* create dummy 'point' which represents last known position of object as result of sim */
// XXX: this can create some inaccuracies with sim position, but is probably better than using unsimulated vals?
RB_body_get_position(rbo->physics_object, eff_loc);
RB_body_get_linear_velocity(rbo->physics_object, eff_vel);
pd_point_from_loc(scene, eff_loc, eff_vel, 0, &epoint);
/* calculate net force of effectors, and apply to sim object
* - we use 'central force' since apply force requires a "relative position" which we don't have...
*/
pdDoEffectors(effectors, NULL, effector_weights, &epoint, eff_force, NULL);
if (G.f & G_DEBUG)
printf("\tapplying force (%f,%f,%f) to '%s'\n", eff_force[0], eff_force[1], eff_force[2], ob->id.name + 2);
/* activate object in case it is deactivated */
if (!is_zero_v3(eff_force))
RB_body_activate(rbo->physics_object);
RB_body_apply_central_force(rbo->physics_object, eff_force);
}
else if (G.f & G_DEBUG)
printf("\tno forces to apply to '%s'\n", ob->id.name + 2);
/* cleanup */
pdEndEffectors(&effectors);
}
/* 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);
}
}
/* update constraints */
if (rbw->constraints == NULL) /* no constraints, move on */
return;
for (go = rbw->constraints->gobject.first; go; go = go->next) {
Object *ob = go->ob;
if (ob) {
/* validate that we've got valid object set up here... */
RigidBodyCon *rbc = ob->rigidbody_constraint;
/* 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 (rbc == NULL) {
/* Since this object is included in the group but doesn't have
* constraint settings (perhaps it was added manually), add!
*/
ob->rigidbody_constraint = BKE_rigidbody_create_constraint(scene, ob, RBC_TYPE_FIXED);
BKE_rigidbody_validate_sim_constraint(rbw, ob, true);
rbc = ob->rigidbody_constraint;
}
else {
/* perform simulation data updates as tagged */
if (rebuild) {
/* World has been rebuilt so rebuild constraint */
BKE_rigidbody_validate_sim_constraint(rbw, ob, true);
}
else if (rbc->flag & RBC_FLAG_NEEDS_VALIDATE) {
BKE_rigidbody_validate_sim_constraint(rbw, ob, false);
}
rbc->flag &= ~RBC_FLAG_NEEDS_VALIDATE;
}
}
}
}
static void rigidbody_update_simulation_post_step(RigidBodyWorld *rbw)
{
GroupObject *go;
for (go = rbw->group->gobject.first; go; go = go->next) {
Object *ob = go->ob;
if (ob) {
RigidBodyOb *rbo = ob->rigidbody_object;
/* reset kinematic state for transformed objects */
if (ob->flag & SELECT && G.moving & G_TRANSFORM_OBJ) {
RB_body_set_kinematic_state(rbo->physics_object, rbo->flag & RBO_FLAG_KINEMATIC || rbo->flag & RBO_FLAG_DISABLED);
RB_body_set_mass(rbo->physics_object, RBO_GET_MASS(rbo));
/* deactivate passive objects so they don't interfere with deactivation of active objects */
if (rbo->type == RBO_TYPE_PASSIVE)
RB_body_deactivate(rbo->physics_object);
}
}
}
}
bool BKE_rigidbody_check_sim_running(RigidBodyWorld *rbw, float ctime)
{
return (rbw && (rbw->flag & RBW_FLAG_MUTED) == 0 && ctime > rbw->pointcache->startframe);
}
/* Sync rigid body and object transformations */
void BKE_rigidbody_sync_transforms(RigidBodyWorld *rbw, Object *ob, float ctime)
{
RigidBodyOb *rbo = ob->rigidbody_object;
/* keep original transform for kinematic and passive objects */
if (ELEM(NULL, rbw, rbo) || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE)
return;
/* use rigid body transform after cache start frame if objects is not being transformed */
if (BKE_rigidbody_check_sim_running(rbw, ctime) && !(ob->flag & SELECT && G.moving & G_TRANSFORM_OBJ)) {
float mat[4][4], size_mat[4][4], size[3];
normalize_qt(rbo->orn); // RB_TODO investigate why quaternion isn't normalized at this point
quat_to_mat4(mat, rbo->orn);
copy_v3_v3(mat[3], rbo->pos);
mat4_to_size(size, ob->obmat);
size_to_mat4(size_mat, size);
mul_m4_m4m4(mat, mat, size_mat);
copy_m4_m4(ob->obmat, mat);
}
/* otherwise set rigid body transform to current obmat */
else {
mat4_to_loc_quat(rbo->pos, rbo->orn, ob->obmat);
}
}
Bugfix [#33970] Background Scene does not show animation of rigid body objects This was caused by multiple instantiations of the same basic problem. The rigidbody handling code often assumed that "scene" pointers referred to the scene where an object participating in the sim resided (and where the rigidbody world for that sim lived). However, when dealing with background sets, "scene" often only refers to the active scene, and not the set that the object actually came from. Hence, the rigidbody code would often (wrongly) conclude that there was nothing to do. For example, we may have the following backgound set/scene chaining scenario: "active" <-- ... <-- set i (rigidbody objects live here) <-- ... <-- set n The fix here is a multi-part fix: 1) Moved sim-world calculation from BKE_scene_update_newframe() to scene_update_tagged_recursive() + This is currently the only way that rigidbody sims in background sets will get calculated, as part of the recursion - These checks will get run on each update. <--- FIXME!!! 2) Tweaked depsgraph code so that when checking if there are any time-dependent features on objects to tag for updating, the checking is done relative to the scene that the object actually resides in (and not the active scene). Otherwise, even if we recalculate the sim, the affected objects won't get tagged for updating. This tagging is needed to actually flush the transforms out of the RigidBodyObject structs (written by the sim/cache) and into the Object transforms (obmat's) 3) Removed the requirement for rigidbody world to actually exist before we can flush rigidbody transforms. In many cases, it should be sufficient to assume that because the object with rigidbody data attached has been tagged for updates, it should have updates to perform. Of course, we still check on this data if we've got it, but that's only if the sim is in the active scene. - TODO: if we have further problems, we should investigate passing the "actual" scene down alongside the "active" scene for BKE_object_handle_update().
2013-02-15 11:49:22 +00:00
/* Used when cancelling transforms - return rigidbody and object to initial states */
void BKE_rigidbody_aftertrans_update(Object *ob, float loc[3], float rot[3], float quat[4], float rotAxis[3], float rotAngle)
{
RigidBodyOb *rbo = ob->rigidbody_object;
/* return rigid body and object to their initial states */
copy_v3_v3(rbo->pos, ob->loc);
copy_v3_v3(ob->loc, loc);
if (ob->rotmode > 0) {
eulO_to_quat(rbo->orn, ob->rot, ob->rotmode);
copy_v3_v3(ob->rot, rot);
}
else if (ob->rotmode == ROT_MODE_AXISANGLE) {
axis_angle_to_quat(rbo->orn, ob->rotAxis, ob->rotAngle);
copy_v3_v3(ob->rotAxis, rotAxis);
ob->rotAngle = rotAngle;
}
else {
copy_qt_qt(rbo->orn, ob->quat);
copy_qt_qt(ob->quat, quat);
}
if (rbo->physics_object) {
/* allow passive objects to return to original transform */
if (rbo->type == RBO_TYPE_PASSIVE)
RB_body_set_kinematic_state(rbo->physics_object, TRUE);
RB_body_set_loc_rot(rbo->physics_object, rbo->pos, rbo->orn);
}
// RB_TODO update rigid body physics object's loc/rot for dynamic objects here as well (needs to be done outside bullet's update loop)
}
void BKE_rigidbody_cache_reset(RigidBodyWorld *rbw)
{
if (rbw)
rbw->pointcache->flag |= PTCACHE_OUTDATED;
}
/* ------------------ */
/* Rebuild rigid body world */
/* NOTE: this needs to be called before frame update to work correctly */
void BKE_rigidbody_rebuild_world(Scene *scene, float ctime)
{
RigidBodyWorld *rbw = scene->rigidbody_world;
PointCache *cache;
PTCacheID pid;
int startframe, endframe;
BKE_ptcache_id_from_rigidbody(&pid, NULL, rbw);
BKE_ptcache_id_time(&pid, scene, ctime, &startframe, &endframe, NULL);
cache = rbw->pointcache;
/* flag cache as outdated if we don't have a world or number of objects in the simulation has changed */
if (rbw->physics_world == NULL || rbw->numbodies != BLI_countlist(&rbw->group->gobject)) {
cache->flag |= PTCACHE_OUTDATED;
}
if (ctime <= startframe + 1 && rbw->ltime == startframe) {
if (cache->flag & PTCACHE_OUTDATED) {
BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
rigidbody_update_simulation(scene, rbw, true);
BKE_ptcache_validate(cache, (int)ctime);
cache->last_exact = 0;
cache->flag &= ~PTCACHE_REDO_NEEDED;
}
}
}
/* Run RigidBody simulation for the specified physics world */
void BKE_rigidbody_do_simulation(Scene *scene, float ctime)
{
float timestep;
RigidBodyWorld *rbw = scene->rigidbody_world;
PointCache *cache;
PTCacheID pid;
int startframe, endframe;
BKE_ptcache_id_from_rigidbody(&pid, NULL, rbw);
BKE_ptcache_id_time(&pid, scene, ctime, &startframe, &endframe, NULL);
cache = rbw->pointcache;
if (ctime <= startframe) {
rbw->ltime = startframe;
return;
}
/* make sure we don't go out of cache frame range */
else if (ctime > endframe) {
ctime = endframe;
}
/* don't try to run the simulation if we don't have a world yet but allow reading baked cache */
if (rbw->physics_world == NULL && !(cache->flag & PTCACHE_BAKED))
return;
else if (rbw->objects == NULL)
rigidbody_update_ob_array(rbw);
/* try to read from cache */
// RB_TODO deal with interpolated, old and baked results
if (BKE_ptcache_read(&pid, ctime)) {
BKE_ptcache_validate(cache, (int)ctime);
rbw->ltime = ctime;
return;
}
/* advance simulation, we can only step one frame forward */
if (ctime == rbw->ltime + 1 && !(cache->flag & PTCACHE_BAKED)) {
/* write cache for first frame when on second frame */
if (rbw->ltime == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact == 0)) {
BKE_ptcache_write(&pid, startframe);
}
/* update and validate simulation */
rigidbody_update_simulation(scene, rbw, false);
/* calculate how much time elapsed since last step in seconds */
timestep = 1.0f / (float)FPS * (ctime - rbw->ltime) * rbw->time_scale;
/* step simulation by the requested timestep, steps per second are adjusted to take time scale into account */
RB_dworld_step_simulation(rbw->physics_world, timestep, INT_MAX, 1.0f / (float)rbw->steps_per_second * min_ff(rbw->time_scale, 1.0f));
rigidbody_update_simulation_post_step(rbw);
/* write cache for current frame */
BKE_ptcache_validate(cache, (int)ctime);
BKE_ptcache_write(&pid, (unsigned int)ctime);
rbw->ltime = ctime;
}
}
/* ************************************** */
2013-01-23 07:26:39 +00:00
#else /* WITH_BULLET */
/* stubs */
#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
void BKE_rigidbody_free_world(RigidBodyWorld *rbw) {}
void BKE_rigidbody_free_object(Object *ob) {}
void BKE_rigidbody_free_constraint(Object *ob) {}
struct RigidBodyOb *BKE_rigidbody_copy_object(Object *ob) { return NULL; }
struct RigidBodyCon *BKE_rigidbody_copy_constraint(Object *ob) { return NULL; }
void BKE_rigidbody_relink_constraint(RigidBodyCon *rbc) {}
2013-01-23 07:26:39 +00:00
void BKE_rigidbody_validate_sim_shape(Object *ob, short rebuild) {}
void BKE_rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, short rebuild) {}
void BKE_rigidbody_validate_sim_constraint(RigidBodyWorld *rbw, Object *ob, short rebuild) {}
void BKE_rigidbody_validate_sim_world(Scene *scene, RigidBodyWorld *rbw, short rebuild) {}
struct RigidBodyWorld *BKE_rigidbody_create_world(Scene *scene) { return NULL; }
struct RigidBodyWorld *BKE_rigidbody_world_copy(RigidBodyWorld *rbw) { return NULL; }
void BKE_rigidbody_world_groups_relink(struct RigidBodyWorld *rbw) {}
2013-01-23 07:26:39 +00:00
struct RigidBodyOb *BKE_rigidbody_create_object(Scene *scene, Object *ob, short type) { return NULL; }
struct RigidBodyCon *BKE_rigidbody_create_constraint(Scene *scene, Object *ob, short type) { return NULL; }
struct RigidBodyWorld *BKE_rigidbody_get_world(Scene *scene) { return NULL; }
void BKE_rigidbody_remove_object(Scene *scene, Object *ob) {}
void BKE_rigidbody_remove_constraint(Scene *scene, Object *ob) {}
void BKE_rigidbody_sync_transforms(RigidBodyWorld *rbw, Object *ob, float ctime) {}
void BKE_rigidbody_aftertrans_update(Object *ob, float loc[3], float rot[3], float quat[4], float rotAxis[3], float rotAngle) {}
bool BKE_rigidbody_check_sim_running(RigidBodyWorld *rbw, float ctime) { return false; }
2013-01-23 07:26:39 +00:00
void BKE_rigidbody_cache_reset(RigidBodyWorld *rbw) {}
void BKE_rigidbody_rebuild_world(Scene *scene, float ctime) {}
2013-01-23 07:26:39 +00:00
void BKE_rigidbody_do_simulation(Scene *scene, float ctime) {}
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif
#endif /* WITH_BULLET */