Changeset View
Standalone View
source/blender/blenkernel/intern/rigidbody.c
| Context not available. | |||||
| * | * | ||||
campbellbarton: This file contains a _lot_ of copypasted code. I can see why its done, but seems like a bad… | |||||
| * The Original Code is: all of this file. | * The Original Code is: all of this file. | ||||
| * | * | ||||
| * Contributor(s): Joshua Leung, Sergej Reich | * Contributor(s): Joshua Leung, Sergej Reich, Martin Felke | ||||
| * | * | ||||
| * ***** END GPL LICENSE BLOCK ***** | * ***** END GPL LICENSE BLOCK ***** | ||||
| */ | */ | ||||
| Context not available. | |||||
| #include "BLI_blenlib.h" | #include "BLI_blenlib.h" | ||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_kdtree.h" | |||||
| #ifdef WITH_BULLET | #ifdef WITH_BULLET | ||||
| # include "RBI_api.h" | # include "RBI_api.h" | ||||
| #endif | #endif | ||||
| #include "DNA_anim_types.h" | |||||
| #include "DNA_group_types.h" | #include "DNA_group_types.h" | ||||
| #include "DNA_mesh_types.h" | |||||
| #include "DNA_meshdata_types.h" | #include "DNA_meshdata_types.h" | ||||
| #include "DNA_object_types.h" | #include "DNA_object_types.h" | ||||
| #include "DNA_object_force.h" | #include "DNA_object_force.h" | ||||
| #include "DNA_rigidbody_types.h" | #include "DNA_rigidbody_types.h" | ||||
| #include "DNA_scene_types.h" | #include "DNA_scene_types.h" | ||||
| #include "BKE_animsys.h" | |||||
| #include "BKE_cdderivedmesh.h" | #include "BKE_cdderivedmesh.h" | ||||
| #include "BKE_effect.h" | #include "BKE_effect.h" | ||||
| #include "BKE_global.h" | #include "BKE_global.h" | ||||
| #include "BKE_group.h" | |||||
| #include "BKE_library.h" | #include "BKE_library.h" | ||||
| #include "BKE_mesh.h" | #include "BKE_mesh.h" | ||||
| #include "BKE_object.h" | #include "BKE_object.h" | ||||
| #include "BKE_pointcache.h" | #include "BKE_pointcache.h" | ||||
| #include "BKE_rigidbody.h" | #include "BKE_rigidbody.h" | ||||
| #include "BKE_utildefines.h" | |||||
| #include "BKE_library.h" | |||||
| #include "BKE_main.h" | |||||
| #include "BKE_modifier.h" | |||||
| #include "BKE_scene.h" | |||||
| #include "BKE_depsgraph.h" | |||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "bmesh.h" | |||||
| #ifdef WITH_BULLET | #ifdef WITH_BULLET | ||||
| static bool isModifierActive(FractureModifierData *rmd) { | |||||
Not Done Inline ActionsThis could be simplified/improved by doing: (rmd->modifier.mode & (eModifierMode_Realtime | eModifierMode_Render)) is more compact and still does the same job aligorith: This could be simplified/improved by doing:
(rmd->modifier.mode & (eModifierMode_Realtime |… | |||||
Not Done Inline ActionsCodestyle aligorith: Codestyle | |||||
| return ((rmd != NULL) && (rmd->modifier.mode & (eModifierMode_Realtime | eModifierMode_Render)) && (rmd->refresh == false)); | |||||
| } | |||||
| static void calc_dist_angle(RigidBodyShardCon *con, float *dist, float *angle) | |||||
Not Done Inline ActionsCodestyle:
aligorith: Codestyle:
* Asterix placement
* If this function isn't supposed to be public, it needs to be… | |||||
| { | |||||
| float q1[4], q2[4], qdiff[4], axis[3]; | |||||
| if ((con->mi1->rigidbody == NULL) || (con->mi2->rigidbody == NULL)) { | |||||
| *dist = 0; | |||||
| *angle = 0; | |||||
| return; | |||||
| } | |||||
| sub_v3_v3v3(axis, con->mi1->rigidbody->pos, con->mi2->rigidbody->pos); | |||||
| *dist = len_v3(axis); | |||||
| copy_qt_qt(q1, con->mi1->rigidbody->orn); | |||||
| copy_qt_qt(q2, con->mi2->rigidbody->orn); | |||||
| invert_qt(q1); | |||||
| mul_qt_qtqt(qdiff, q1, q2); | |||||
| quat_to_axis_angle(axis, angle, qdiff); | |||||
| } | |||||
Not Done Inline ActionsWhy this conversion? All rotation angles internally should be radians not degrees aligorith: Why this conversion? All rotation angles internally should be radians not degrees | |||||
| void BKE_rigidbody_start_dist_angle(RigidBodyShardCon *con) | |||||
| { | |||||
| /* store starting angle and distance per constraint*/ | |||||
| float dist, angle; | |||||
| calc_dist_angle(con, &dist, &angle); | |||||
| con->start_dist = dist; | |||||
| con->start_angle = angle; | |||||
| } | |||||
| float BKE_rigidbody_calc_max_con_mass(Object *ob) | |||||
| { | |||||
| FractureModifierData *rmd; | |||||
| ModifierData *md; | |||||
| RigidBodyShardCon *con; | |||||
| float max_con_mass = 0, con_mass; | |||||
| for (md = ob->modifiers.first; md; md = md->next) { | |||||
| if (md->type == eModifierType_Fracture) { | |||||
| rmd = (FractureModifierData *)md; | |||||
| for (con = rmd->meshConstraints.first; con; con = con->next) { | |||||
| if ((con->mi1 != NULL && con->mi1->rigidbody != NULL) && | |||||
| (con->mi2 != NULL && con->mi2->rigidbody != NULL)) { | |||||
| con_mass = con->mi1->rigidbody->mass + con->mi2->rigidbody->mass; | |||||
| if (con_mass > max_con_mass) { | |||||
| max_con_mass = con_mass; | |||||
| } | |||||
| } | |||||
| } | |||||
| return max_con_mass; | |||||
| } | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| float BKE_rigidbody_calc_min_con_dist(Object *ob) | |||||
| { | |||||
| FractureModifierData *rmd; | |||||
| ModifierData *md; | |||||
| RigidBodyShardCon *con; | |||||
| float min_con_dist = FLT_MAX, con_dist, con_vec[3]; | |||||
| for (md = ob->modifiers.first; md; md = md->next) { | |||||
| if (md->type == eModifierType_Fracture) { | |||||
| rmd = (FractureModifierData *)md; | |||||
| for (con = rmd->meshConstraints.first; con; con = con->next) { | |||||
| if ((con->mi1 != NULL && con->mi1->rigidbody != NULL) && | |||||
| (con->mi2 != NULL && con->mi2->rigidbody != NULL)) { | |||||
| sub_v3_v3v3(con_vec, con->mi1->centroid, con->mi2->centroid); | |||||
| con_dist = len_v3(con_vec); | |||||
| if (con_dist < min_con_dist) { | |||||
| min_con_dist = con_dist; | |||||
| } | |||||
| } | |||||
| } | |||||
| return min_con_dist; | |||||
| } | |||||
| } | |||||
| return FLT_MAX; | |||||
| } | |||||
| void BKE_rigidbody_calc_threshold(float max_con_mass, FractureModifierData *rmd, RigidBodyShardCon *con) { | |||||
| float max_thresh, thresh = 0.0f, con_mass; | |||||
| if ((max_con_mass == 0) && (rmd->use_mass_dependent_thresholds)) { | |||||
| return; | |||||
| } | |||||
| if ((con->mi1 == NULL) || (con->mi2 == NULL)) { | |||||
| return; | |||||
| } | |||||
| max_thresh = rmd->breaking_threshold; | |||||
| if ((con->mi1->rigidbody != NULL) && (con->mi2->rigidbody != NULL)) { | |||||
| con_mass = con->mi1->rigidbody->mass + con->mi2->rigidbody->mass; | |||||
| if (rmd->use_mass_dependent_thresholds) | |||||
| { | |||||
| thresh = (con_mass / max_con_mass) * max_thresh; | |||||
| } | |||||
| con->breaking_threshold = thresh; | |||||
| } | |||||
| } | |||||
| static int DM_mesh_minmax(DerivedMesh *dm, float r_min[3], float r_max[3]) | |||||
| { | |||||
| MVert *v; | |||||
Not Done Inline ActionsFrom the looks of things, these two functions may be better off in the DerivedMesh files? aligorith: From the looks of things, these two functions may be better off in the DerivedMesh files? | |||||
Not Done Inline ActionsProbably yes. I wanted to keep my code in less files btw, reducing the need to change other unrelated files. scorpion81: Probably yes. I wanted to keep my code in less files btw, reducing the need to change other… | |||||
| int i = 0; | |||||
| for (i = 0; i < dm->numVertData; i++) { | |||||
| v = CDDM_get_vert(dm, i); | |||||
| minmax_v3v3_v3(r_min, r_max, v->co); | |||||
| } | |||||
| return (dm->numVertData != 0); | |||||
| } | |||||
| static void DM_mesh_boundbox(DerivedMesh *bm, float r_loc[3], float r_size[3]) | |||||
| { | |||||
| float min[3], max[3]; | |||||
Not Done Inline ActionsSame as above aligorith: Same as above | |||||
| float mloc[3], msize[3]; | |||||
| if (!r_loc) r_loc = mloc; | |||||
| if (!r_size) r_size = msize; | |||||
| INIT_MINMAX(min, max); | |||||
| if (!DM_mesh_minmax(bm, min, max)) { | |||||
| min[0] = min[1] = min[2] = -1.0f; | |||||
| max[0] = max[1] = max[2] = 1.0f; | |||||
| } | |||||
| mid_v3_v3v3(r_loc, min, max); | |||||
| r_size[0] = (max[0] - min[0]) / 2.0f; | |||||
| r_size[1] = (max[1] - min[1]) / 2.0f; | |||||
| r_size[2] = (max[2] - min[2]) / 2.0f; | |||||
| } | |||||
| /* helper function to calculate volume of rigidbody object */ | |||||
| float BKE_rigidbody_calc_volume(DerivedMesh *dm, RigidBodyOb *rbo) | |||||
| { | |||||
| float loc[3] = {0.0f, 0.0f, 0.0f}; | |||||
Not Done Inline ActionsSee comment in rigidbody_object.c aligorith: See comment in rigidbody_object.c | |||||
| float size[3] = {1.0f, 1.0f, 1.0f}; | |||||
| float radius = 1.0f; | |||||
| float height = 1.0f; | |||||
| float volume = 0.0f; | |||||
| /* 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) | |||||
| * - boundbox gives full width | |||||
| */ | |||||
| /* XXX: all dimensions are auto-determined now... later can add stored settings for this*/ | |||||
| DM_mesh_boundbox(dm, loc, size); | |||||
| if (ELEM(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]) * 0.5f; | |||||
| height = size[2]; | |||||
| } | |||||
| else if (rbo->shape == RB_SHAPE_SPHERE) { | |||||
| /* take radius to the the largest dimension to try and encompass everything */ | |||||
| radius = max_fff(size[0], size[1], size[2]) * 0.5f; | |||||
| } | |||||
| /* calculate volume as appropriate */ | |||||
| switch (rbo->shape) { | |||||
| case RB_SHAPE_SPHERE: | |||||
| volume = 4.0f / 3.0f * (float)M_PI * radius * radius * radius; | |||||
| break; | |||||
| /* for now, assume that capsule is close enough to a cylinder... */ | |||||
| case RB_SHAPE_CAPSULE: | |||||
| case RB_SHAPE_CYLINDER: | |||||
| volume = (float)M_PI * radius * radius * height; | |||||
| break; | |||||
| case RB_SHAPE_CONE: | |||||
| volume = (float)M_PI / 3.0f * radius * radius * height; | |||||
| break; | |||||
| /* for now, all mesh shapes are just treated as boxes... | |||||
| * NOTE: this may overestimate the volume, but other methods are overkill | |||||
| */ | |||||
| case RB_SHAPE_BOX: | |||||
| case RB_SHAPE_CONVEXH: | |||||
| case RB_SHAPE_TRIMESH: | |||||
| volume = size[0] * size[1] * size[2]; | |||||
| if (size[0] == 0) { | |||||
| volume = size[1] * size[2]; | |||||
| } | |||||
| else if (size[1] == 0) { | |||||
| volume = size[0] * size[2]; | |||||
| } | |||||
| else if (size[2] == 0) { | |||||
| volume = size[0] * size[1]; | |||||
| } | |||||
| break; | |||||
| #if 0 // XXX: not defined yet | |||||
| case RB_SHAPE_COMPOUND: | |||||
| volume = 0.0f; | |||||
| break; | |||||
| #endif | |||||
| } | |||||
| /* return the volume calculated */ | |||||
| return volume; | |||||
| } | |||||
| void BKE_rigidbody_calc_shard_mass(Object *ob, MeshIsland *mi, DerivedMesh *orig_dm) | |||||
| { | |||||
| DerivedMesh *dm_ob = orig_dm, *dm_mi; | |||||
| float vol_mi = 0, mass_mi = 0, vol_ob = 0, mass_ob = 0; | |||||
| if (dm_ob == NULL) { | |||||
| /* fallback method */ | |||||
| if (ob->type == OB_MESH) { | |||||
| /* if we have a mesh, determine its volume */ | |||||
| dm_ob = CDDM_from_mesh(ob->data); | |||||
| vol_ob = BKE_rigidbody_calc_volume(dm_ob, ob->rigidbody_object); | |||||
| } | |||||
| else { | |||||
| /* else get object boundbox as last resort */ | |||||
| float dim[3]; | |||||
| BKE_object_dimensions_get(ob, dim); | |||||
| vol_ob = dim[0] * dim[1] * dim[2]; | |||||
| } | |||||
| } | |||||
| mass_ob = ob->rigidbody_object->mass; | |||||
| if (vol_ob > 0) { | |||||
| dm_mi = mi->physics_mesh; | |||||
| vol_mi = BKE_rigidbody_calc_volume(dm_mi, mi->rigidbody); | |||||
| mass_mi = (vol_mi / vol_ob) * mass_ob; | |||||
| mi->rigidbody->mass = mass_mi; | |||||
| } | |||||
| if (mi->rigidbody->type == RBO_TYPE_ACTIVE) { | |||||
| if (mi->rigidbody->mass == 0) | |||||
| mi->rigidbody->mass = 0.001; /* set a minimum mass for active objects */ | |||||
| } | |||||
| /* only active bodies need mass update */ | |||||
| if ((mi->rigidbody->physics_object) && (mi->rigidbody->type == RBO_TYPE_ACTIVE)) { | |||||
| RB_body_set_mass(mi->rigidbody->physics_object, RBO_GET_MASS(mi->rigidbody)); | |||||
| } | |||||
| if (orig_dm == NULL && dm_ob != NULL) | |||||
| { | |||||
| /* free temp dm, if it hasnt been passed in */ | |||||
| dm_ob->needsFree = 1; | |||||
| dm_ob->release(dm_ob); | |||||
| } | |||||
| } | |||||
| static void initNormals(struct MeshIsland *mi, Object *ob, FractureModifierData *fmd) | |||||
| { | |||||
| /* hrm have to init Normals HERE, because we cant do this in readfile.c in case the file is loaded (have no access to the Object there) */ | |||||
| if (mi->vertno == NULL && mi->vertices_cached != NULL) { | |||||
| KDTreeNearest n; | |||||
| int index = 0, i = 0; | |||||
| MVert mvrt; | |||||
| DerivedMesh *dm = ob->derivedFinal; | |||||
| if (dm == NULL) { | |||||
| dm = CDDM_from_mesh(ob->data); | |||||
| } | |||||
| if (fmd->nor_tree == NULL) { | |||||
| /* HRRRRRMMMM need to build the kdtree here as well if we start the sim after loading and not refreshing, again, no access to object.... */ | |||||
| int i = 0, totvert; | |||||
| KDTree *tree; | |||||
| MVert *mv, *mvert; | |||||
| mvert = dm->getVertArray(dm); | |||||
| totvert = dm->getNumVerts(dm); | |||||
| tree = BLI_kdtree_new(totvert); | |||||
| for (i = 0, mv = mvert; i < totvert; i++, mv++) { | |||||
| BLI_kdtree_insert(tree, i, mv->co); | |||||
| } | |||||
| BLI_kdtree_balance(tree); | |||||
| fmd->nor_tree = tree; | |||||
| } | |||||
| mi->vertno = MEM_callocN(sizeof(short) * 3 * mi->vertex_count, "mi->vertno"); | |||||
| for (i = 0; i < mi->vertex_count; i++) { | |||||
| MVert *v = mi->vertices_cached[i]; | |||||
| index = BLI_kdtree_find_nearest(fmd->nor_tree, v->co, &n); | |||||
| dm->getVert(dm, index, &mvrt); | |||||
| mi->vertno[i * 3] = mvrt.no[0]; | |||||
| mi->vertno[i * 3 + 1] = mvrt.no[1]; | |||||
| mi->vertno[i * 3 + 2] = mvrt.no[2]; | |||||
| } | |||||
| if (ob->derivedFinal == NULL) { | |||||
| dm->needsFree = 1; | |||||
| dm->release(dm); | |||||
| dm = NULL; | |||||
| } | |||||
| } | |||||
| } | |||||
| void BKE_rigidbody_update_cell(struct MeshIsland *mi, Object *ob, float loc[3], float rot[4], FractureModifierData *rmd) | |||||
| { | |||||
| float startco[3], centr[3], size[3]; | |||||
| short startno[3]; | |||||
| int j; | |||||
| bool invalidData; | |||||
| /* hrm have to init Normals HERE, because we cant do this in readfile.c in case the file is loaded (have no access to the Object there)*/ | |||||
| if (mi->vertno == NULL && rmd->fix_normals) { | |||||
| initNormals(mi, ob, rmd); | |||||
| } | |||||
| invalidData = (loc[0] == FLT_MIN) || (rot[0] == FLT_MIN); | |||||
| if (invalidData) { | |||||
| return; | |||||
| } | |||||
| invert_m4_m4(ob->imat, ob->obmat); | |||||
| mat4_to_size(size, ob->obmat); | |||||
| for (j = 0; j < mi->vertex_count; j++) { | |||||
| struct MVert *vert; | |||||
| float fno[3]; | |||||
| if (!mi->vertices_cached) { | |||||
| return; | |||||
| } | |||||
| vert = mi->vertices_cached[j]; | |||||
| if (vert == NULL) continue; | |||||
| if (vert->co == NULL) break; | |||||
| if (rmd->refresh == true) break; | |||||
| startco[0] = mi->vertco[j * 3]; | |||||
| startco[1] = mi->vertco[j * 3 + 1]; | |||||
| startco[2] = mi->vertco[j * 3 + 2]; | |||||
| if (rmd->fix_normals) { | |||||
| startno[0] = mi->vertno[j * 3]; | |||||
| startno[1] = mi->vertno[j * 3 + 1]; | |||||
| startno[2] = mi->vertno[j * 3 + 2]; | |||||
| normal_short_to_float_v3(fno, startno); | |||||
| mul_qt_v3(rot, fno); | |||||
| normal_float_to_short_v3(vert->no, fno); | |||||
| } | |||||
| copy_v3_v3(vert->co, startco); | |||||
| mul_v3_v3(vert->co, size); | |||||
| mul_qt_v3(rot, vert->co); | |||||
| copy_v3_v3(centr, mi->centroid); | |||||
| mul_v3_v3(centr, size); | |||||
| mul_qt_v3(rot, centr); | |||||
| sub_v3_v3(vert->co, centr); | |||||
| add_v3_v3(vert->co, loc); | |||||
| mul_m4_v3(ob->imat, vert->co); | |||||
| } | |||||
| ob->recalc |= OB_RECALC_ALL; | |||||
| } | |||||
| /* ************************************** */ | /* ************************************** */ | ||||
| /* Memory Management */ | /* Memory Management */ | ||||
| Context not available. | |||||
| } | } | ||||
| } | } | ||||
| /* free dynamics world */ | /* free dynamics world */ | ||||
| RB_dworld_delete(rbw->physics_world); | if (rbw->physics_world != NULL) | ||||
| RB_dworld_delete(rbw->physics_world); | |||||
| } | } | ||||
| if (rbw->objects) | if (rbw->objects) | ||||
| free(rbw->objects); | MEM_freeN(rbw->objects); | ||||
| if (rbw->cache_index_map) { | |||||
| MEM_freeN(rbw->cache_index_map); | |||||
| rbw->cache_index_map = NULL; | |||||
| } | |||||
| if (rbw->cache_offset_map) { | |||||
| MEM_freeN(rbw->cache_offset_map); | |||||
| rbw->cache_offset_map = NULL; | |||||
| } | |||||
| /* free cache */ | /* free cache */ | ||||
| BKE_ptcache_free_list(&(rbw->ptcaches)); | BKE_ptcache_free_list(&(rbw->ptcaches)); | ||||
| Context not available. | |||||
| } | } | ||||
| /* create collision shape of mesh - convex hull */ | /* create collision shape of mesh - convex hull */ | ||||
| static rbCollisionShape *rigidbody_get_shape_convexhull_from_mesh(Object *ob, float margin, bool *can_embed) | static rbCollisionShape *rigidbody_get_shape_convexhull_from_mesh(Mesh *me, float margin, bool *can_embed) | ||||
| { | { | ||||
| rbCollisionShape *shape = NULL; | rbCollisionShape *shape = NULL; | ||||
| DerivedMesh *dm = NULL; | int totvert = me->totvert; | ||||
| MVert *mvert = NULL; | MVert *mvert = me->mvert; | ||||
| int totvert = 0; | |||||
| if (ob->type == OB_MESH && ob->data) { | if (me && totvert) { | ||||
| dm = rigidbody_get_mesh(ob); | shape = RB_shape_new_convex_hull((float *)mvert, sizeof(MVert), totvert, margin, can_embed); | ||||
| mvert = (dm) ? dm->getVertArray(dm) : NULL; | |||||
| totvert = (dm) ? dm->getNumVerts(dm) : 0; | |||||
| } | } | ||||
| else { | else { | ||||
| printf("ERROR: cannot make Convex Hull collision shape for non-Mesh object\n"); | printf("ERROR: no vertices to define Convex Hull collision shape with\n"); | ||||
| } | } | ||||
| if (totvert) { | return shape; | ||||
| } | |||||
| static rbCollisionShape *rigidbody_get_shape_convexhull_from_dm(DerivedMesh *dm, float margin, bool *can_embed) | |||||
| { | |||||
| rbCollisionShape *shape = NULL; | |||||
| int totvert = dm->getNumVerts(dm); | |||||
| MVert *mvert = dm->getVertArray(dm); | |||||
| if (dm && totvert) { | |||||
| shape = RB_shape_new_convex_hull((float *)mvert, sizeof(MVert), totvert, margin, can_embed); | shape = RB_shape_new_convex_hull((float *)mvert, sizeof(MVert), totvert, margin, can_embed); | ||||
| } | } | ||||
| else { | else { | ||||
| printf("ERROR: no vertices to define Convex Hull collision shape with\n"); | printf("ERROR: no vertices to define Convex Hull collision shape with\n"); | ||||
| } | } | ||||
| if (dm && ob->rigidbody_object->mesh_source == RBO_MESH_BASE) | return shape; | ||||
| dm->release(dm); | } | ||||
| /* create collision shape of mesh - triangulated mesh | |||||
Not Done Inline Actionsanother almost exact copy, not great. campbellbarton: another almost exact copy, not great. | |||||
| * returns NULL if creation fails. | |||||
| */ | |||||
| static rbCollisionShape *rigidbody_get_shape_trimesh_from_mesh_shard(DerivedMesh *dmm, Object *ob) | |||||
| { | |||||
| rbCollisionShape *shape = NULL; | |||||
| if (ob->type == OB_MESH) { | |||||
| DerivedMesh *dm = NULL; | |||||
| MVert *mvert; | |||||
| MFace *mface; | |||||
| int totvert; | |||||
| int totface; | |||||
| int tottris = 0; | |||||
| int triangle_index = 0; | |||||
| dm = CDDM_copy(dmm); | |||||
| /* ensure mesh validity, then grab data */ | |||||
| if (dm == NULL) | |||||
| return NULL; | |||||
| 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; | |||||
| /* count triangles */ | |||||
| for (i = 0; i < totface; i++) { | |||||
| (mface[i].v4) ? (tottris += 2) : (tottris += 1); | |||||
| } | |||||
| /* init mesh data for collision shape */ | |||||
| mdata = RB_trimesh_data_new(tottris, totvert); | |||||
| RB_trimesh_add_vertices(mdata, (float *)mvert, totvert, sizeof(MVert)); | |||||
| /* 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 */ | |||||
| RB_trimesh_add_triangle_indices(mdata, triangle_index, mface->v1, mface->v2, mface->v3); | |||||
| triangle_index++; | |||||
| /* add second triangle if needed - verts 1,3,4 */ | |||||
| if (mface->v4) { | |||||
| RB_trimesh_add_triangle_indices(mdata, triangle_index, mface->v1, mface->v3, mface->v4); | |||||
| triangle_index++; | |||||
| } | |||||
| } | |||||
| RB_trimesh_finish(mdata); | |||||
| /* 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 /*&& ob->rigidbody_object->mesh_source == RBO_MESH_BASE*/) { | |||||
| dm->needsFree = 1; | |||||
| dm->release(dm); | |||||
| dm = NULL; | |||||
| } | |||||
| } | |||||
Not Done Inline ActionsThis is an almost exact duplicate of rigidbody_get_shape_trimesh_from_mesh, (looks like this file contains a lot of copy-pasted code, which should be a last resort). campbellbarton: This is an almost exact duplicate of `rigidbody_get_shape_trimesh_from_mesh`, (looks like this… | |||||
| else { | |||||
| printf("ERROR: cannot make Triangular Mesh collision shape for non-Mesh object\n"); | |||||
| } | |||||
| return shape; | return shape; | ||||
| } | } | ||||
| Context not available. | |||||
| case RB_SHAPE_CONVEXH: | case RB_SHAPE_CONVEXH: | ||||
| /* try to emged collision margin */ | /* try to emged collision margin */ | ||||
| has_volume = (MIN3(size[0], size[1], size[2]) > 0.0f); | has_volume = (MIN3(size[0], size[1], size[2]) > 0.0f); | ||||
| if (!(rbo->flag & RBO_FLAG_USE_MARGIN) && has_volume) | if (!(rbo->flag & RBO_FLAG_USE_MARGIN) && has_volume) | ||||
| hull_margin = 0.04f; | hull_margin = 0.04f; | ||||
| new_shape = rigidbody_get_shape_convexhull_from_mesh(ob, hull_margin, &can_embed); | if (ob->type == OB_MESH && ob->data) { | ||||
| new_shape = rigidbody_get_shape_convexhull_from_mesh((Mesh *)ob->data, hull_margin, &can_embed); | |||||
| } | |||||
| else { | |||||
| printf("ERROR: cannot make Convex Hull collision shape for non-Mesh object\n"); | |||||
| } | |||||
| if (!(rbo->flag & RBO_FLAG_USE_MARGIN)) | 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 */ | rbo->margin = (can_embed && has_volume) ? 0.04f : 0.0f; /* RB_TODO ideally we shouldn't directly change the margin here */ | ||||
| break; | break; | ||||
| case RB_SHAPE_TRIMESH: | case RB_SHAPE_TRIMESH: | ||||
| new_shape = rigidbody_get_shape_trimesh_from_mesh(ob); | new_shape = rigidbody_get_shape_trimesh_from_mesh(ob); | ||||
| Context not available. | |||||
| } | } | ||||
| } | } | ||||
| /* --------------------- */ | |||||
| /* helper function to calculate volume of rigidbody object */ | /* Create new physics sim collision shape for object and store it, | ||||
| // TODO: allow a parameter to specify method used to calculate this? | * or remove the existing one first and replace... | ||||
| void BKE_rigidbody_calc_volume(Object *ob, float *r_vol) | */ | ||||
Not Done Inline ActionsWith all the changes happening here, it's almost getting a bit difficult to track the multiple things changing here... (I'm tempted to say that it would be easier to review if there was firstly a patch to prepare the existing code for extending with sub-object stuff, and then in a second patch the necessary new stuff is introduced :). aligorith: With all the changes happening here, it's almost getting a bit difficult to track the multiple… | |||||
| void BKE_rigidbody_validate_sim_shard_shape(MeshIsland *mi, Object *ob, short rebuild) | |||||
| { | { | ||||
| RigidBodyOb *rbo = ob->rigidbody_object; | RigidBodyOb *rbo = mi->rigidbody; | ||||
| rbCollisionShape *new_shape = NULL; | |||||
| float size[3] = {1.0f, 1.0f, 1.0f}; | float size[3] = {1.0f, 1.0f, 1.0f}, loc[3] = {0.0f, 0.0f, 0.0f}; | ||||
| float radius = 1.0f; | float radius = 1.0f; | ||||
| float height = 1.0f; | float height = 1.0f; | ||||
| float capsule_height; | |||||
| float hull_margin = 0.0f; | |||||
| bool can_embed = true; | |||||
| bool has_volume; | |||||
| float min[3], max[3]; | |||||
| /* sanity check */ | |||||
| if (rbo == NULL) | |||||
| return; | |||||
| float volume = 0.0f; | /* 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 | /* if automatically determining dimensions, use the Object's boundbox | ||||
| * - assume that all quadrics are standing upright on local z-axis | * - assume that all quadrics are standing upright on local z-axis | ||||
| * - assume even distribution of mass around the Object's pivot | * - assume even distribution of mass around the Object's pivot | ||||
| * (i.e. Object pivot is centralized in boundbox) | * (i.e. Object pivot is centralized in boundbox) | ||||
| * - boundbox gives full width | |||||
| */ | */ | ||||
| // XXX: all dimensions are auto-determined now... later can add stored settings for this | // XXX: all dimensions are auto-determined now... later can add stored settings for this | ||||
| BKE_object_dimensions_get(ob, size); | /* get object dimensions without scaling */ | ||||
| INIT_MINMAX(min, max); | |||||
| if (!DM_mesh_minmax(mi->physics_mesh, min, max)) { | |||||
| min[0] = min[1] = min[2] = -1.0f; | |||||
| max[0] = max[1] = max[2] = 1.0f; | |||||
| } | |||||
| mid_v3_v3v3(loc, min, max); | |||||
| size[0] = (max[0] - min[0]) / 2.0f; | |||||
| size[1] = (max[1] - min[1]) / 2.0f; | |||||
| size[2] = (max[2] - min[2]) / 2.0f; | |||||
| if (ELEM(rbo->shape, RB_SHAPE_CAPSULE, RB_SHAPE_CYLINDER, RB_SHAPE_CONE)) { | if (ELEM(rbo->shape, RB_SHAPE_CAPSULE, RB_SHAPE_CYLINDER, RB_SHAPE_CONE)) { | ||||
| /* take radius as largest x/y dimension, and height as z-dimension */ | /* take radius as largest x/y dimension, and height as z-dimension */ | ||||
| radius = MAX2(size[0], size[1]) * 0.5f; | radius = MAX2(size[0], size[1]); | ||||
| height = size[2]; | height = size[2]; | ||||
| } | } | ||||
| else if (rbo->shape == RB_SHAPE_SPHERE) { | else if (rbo->shape == RB_SHAPE_SPHERE) { | ||||
| /* take radius to the the largest dimension to try and encompass everything */ | /* take radius to the the largest dimension to try and encompass everything */ | ||||
| radius = max_fff(size[0], size[1], size[2]) * 0.5f; | radius = MAX3(size[0], size[1], size[2]); | ||||
| } | } | ||||
| /* calculate volume as appropriate */ | /* create new shape */ | ||||
| switch (rbo->shape) { | switch (rbo->shape) { | ||||
| case RB_SHAPE_BOX: | case RB_SHAPE_BOX: | ||||
| volume = size[0] * size[1] * size[2]; | new_shape = RB_shape_new_box(size[0], size[1], size[2]); | ||||
| break; | break; | ||||
| case RB_SHAPE_SPHERE: | case RB_SHAPE_SPHERE: | ||||
| volume = 4.0f / 3.0f * (float)M_PI * radius * radius * radius; | new_shape = RB_shape_new_sphere(radius); | ||||
| break; | break; | ||||
| /* for now, assume that capsule is close enough to a cylinder... */ | |||||
| case RB_SHAPE_CAPSULE: | 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: | case RB_SHAPE_CYLINDER: | ||||
| volume = (float)M_PI * radius * radius * height; | new_shape = RB_shape_new_cylinder(radius, height); | ||||
| break; | break; | ||||
| case RB_SHAPE_CONE: | case RB_SHAPE_CONE: | ||||
| volume = (float)M_PI / 3.0f * radius * radius * height; | new_shape = RB_shape_new_cone(radius, height * 2.0f); | ||||
| break; | break; | ||||
| case RB_SHAPE_CONVEXH: | case RB_SHAPE_CONVEXH: | ||||
| case RB_SHAPE_TRIMESH: | /* try to emged collision margin */ | ||||
| { | has_volume = (MIN3(size[0], size[1], size[2]) > 0.0f); | ||||
| if (ob->type == OB_MESH) { | |||||
| DerivedMesh *dm = rigidbody_get_mesh(ob); | |||||
| MVert *mvert; | |||||
| MFace *mface; | |||||
| int totvert, totface; | |||||
| /* ensure mesh validity, then grab data */ | |||||
| if (dm == NULL) | |||||
| return; | |||||
| DM_ensure_tessface(dm); | |||||
| mvert = dm->getVertArray(dm); | |||||
| totvert = dm->getNumVerts(dm); | |||||
| mface = dm->getTessFaceArray(dm); | |||||
| totface = dm->getNumTessFaces(dm); | |||||
| if (totvert > 0 && totface > 0) { | |||||
| BKE_mesh_calc_volume(mvert, totvert, mface, totface, &volume, NULL); | |||||
| } | |||||
| /* cleanup temp data */ | |||||
| if (ob->rigidbody_object->mesh_source == RBO_MESH_BASE) { | |||||
| dm->release(dm); | |||||
| } | |||||
| } | |||||
| else { | |||||
| /* rough estimate from boundbox as fallback */ | |||||
| /* XXX could implement other types of geometry here (curves, etc.) */ | |||||
| volume = size[0] * size[1] * size[2]; | |||||
| } | |||||
| break; | |||||
| } | |||||
| #if 0 // XXX: not defined yet | if (!(rbo->flag & RBO_FLAG_USE_MARGIN) && has_volume) | ||||
| case RB_SHAPE_COMPOUND: | hull_margin = 0.04f; | ||||
| volume = 0.0f; | new_shape = rigidbody_get_shape_convexhull_from_dm(mi->physics_mesh, 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_shard(mi->physics_mesh, ob); | |||||
| break; | break; | ||||
| #endif | |||||
| } | } | ||||
| /* assign new collision shape if creation was successful */ | |||||
| /* return the volume calculated */ | if (new_shape) { | ||||
| if (r_vol) *r_vol = volume; | 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_shard_shape(mi, ob, true); | |||||
| } | |||||
| } | } | ||||
| void BKE_rigidbody_calc_center_of_mass(Object *ob, float r_com[3]) | |||||
| { | |||||
| RigidBodyOb *rbo = ob->rigidbody_object; | |||||
| float size[3] = {1.0f, 1.0f, 1.0f}; | /* --------------------- */ | ||||
| float height = 1.0f; | |||||
| zero_v3(r_com); | /* Create physics sim representation of shard given RigidBody settings | ||||
| * < rebuild: even if an instance already exists, replace it | |||||
| */ | |||||
| void BKE_rigidbody_validate_sim_shard(RigidBodyWorld *rbw, MeshIsland *mi, Object *ob, short rebuild) | |||||
| { | |||||
| RigidBodyOb *rbo = (mi) ? mi->rigidbody : NULL; | |||||
| float loc[3]; | |||||
| float rot[4]; | |||||
| /* if automatically determining dimensions, use the Object's boundbox | /* sanity checks: | ||||
| * - assume that all quadrics are standing upright on local z-axis | * - object doesn't have RigidBody info already: then why is it here? | ||||
| * - assume even distribution of mass around the Object's pivot | |||||
| * (i.e. Object pivot is centralized in boundbox) | |||||
| * - boundbox gives full width | |||||
| */ | */ | ||||
| // XXX: all dimensions are auto-determined now... later can add stored settings for this | if (rbo == NULL) | ||||
| BKE_object_dimensions_get(ob, size); | return; | ||||
| /* calculate volume as appropriate */ | /* make sure collision shape exists */ | ||||
| switch (rbo->shape) { | /* FIXME we shouldn't always have to rebuild collision shapes when rebuilding objects, but it's needed for constraints to update correctly */ | ||||
| case RB_SHAPE_BOX: | if (rbo->physics_shape == NULL || rebuild) | ||||
| case RB_SHAPE_SPHERE: | BKE_rigidbody_validate_sim_shard_shape(mi, ob, true); | ||||
| case RB_SHAPE_CAPSULE: | |||||
| case RB_SHAPE_CYLINDER: | if (rbo->physics_object) { | ||||
| break; | 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); | |||||
| } | |||||
| case RB_SHAPE_CONE: | copy_v3_v3(loc, rbo->pos); | ||||
| /* take radius as largest x/y dimension, and height as z-dimension */ | copy_v4_v4(rot, rbo->orn); | ||||
| height = size[2]; | |||||
| /* cone is geometrically centered on the median, | rbo->physics_object = RB_body_new(rbo->physics_shape, loc, rot); | ||||
| * center of mass is 1/4 up from the base | |||||
| */ | |||||
| r_com[2] = -0.25f * height; | |||||
| break; | |||||
| case RB_SHAPE_CONVEXH: | RB_body_set_friction(rbo->physics_object, rbo->friction); | ||||
| case RB_SHAPE_TRIMESH: | RB_body_set_restitution(rbo->physics_object, rbo->restitution); | ||||
| { | |||||
| if (ob->type == OB_MESH) { | RB_body_set_damping(rbo->physics_object, rbo->lin_damping, rbo->ang_damping); | ||||
| DerivedMesh *dm = rigidbody_get_mesh(ob); | RB_body_set_sleep_thresh(rbo->physics_object, rbo->lin_sleep_thresh, rbo->ang_sleep_thresh); | ||||
| MVert *mvert; | RB_body_set_activation_state(rbo->physics_object, rbo->flag & RBO_FLAG_USE_DEACTIVATION); | ||||
| MFace *mface; | |||||
| int totvert, totface; | if (rbo->type == RBO_TYPE_PASSIVE || rbo->flag & RBO_FLAG_START_DEACTIVATED) | ||||
| RB_body_deactivate(rbo->physics_object); | |||||
| /* ensure mesh validity, then grab data */ | |||||
| if (dm == NULL) | |||||
| return; | RB_body_set_linear_factor(rbo->physics_object, | ||||
| (ob->protectflag & OB_LOCK_LOCX) == 0, | |||||
| DM_ensure_tessface(dm); | (ob->protectflag & OB_LOCK_LOCY) == 0, | ||||
| (ob->protectflag & OB_LOCK_LOCZ) == 0); | |||||
| mvert = dm->getVertArray(dm); | RB_body_set_angular_factor(rbo->physics_object, | ||||
| totvert = dm->getNumVerts(dm); | (ob->protectflag & OB_LOCK_ROTX) == 0, | ||||
| mface = dm->getTessFaceArray(dm); | (ob->protectflag & OB_LOCK_ROTY) == 0, | ||||
| totface = dm->getNumTessFaces(dm); | (ob->protectflag & OB_LOCK_ROTZ) == 0); | ||||
| if (totvert > 0 && totface > 0) { | |||||
| BKE_mesh_calc_volume(mvert, totvert, mface, totface, NULL, r_com); | |||||
| } | |||||
| /* cleanup temp data */ | |||||
| if (ob->rigidbody_object->mesh_source == RBO_MESH_BASE) { | |||||
| dm->release(dm); | |||||
| } | |||||
| } | |||||
| break; | |||||
| } | |||||
| #if 0 // XXX: not defined yet | RB_body_set_mass(rbo->physics_object, RBO_GET_MASS(rbo)); | ||||
| case RB_SHAPE_COMPOUND: | RB_body_set_kinematic_state(rbo->physics_object, rbo->flag & RBO_FLAG_KINEMATIC || rbo->flag & RBO_FLAG_DISABLED); | ||||
| volume = 0.0f; | |||||
| break; | |||||
| #endif | |||||
| } | } | ||||
| if (rbw && rbw->physics_world && rbo->physics_object) | |||||
| RB_dworld_add_body(rbw->physics_world, rbo->physics_object, rbo->col_groups); | |||||
| rbo->flag &= ~RBO_FLAG_NEEDS_VALIDATE; | |||||
| } | } | ||||
| /* --------------------- */ | /* --------------------- */ | ||||
| /** | /* Create physics sim representation of object given RigidBody settings | ||||
| * Create physics sim representation of object given RigidBody settings | * < rebuild: even if an instance already exists, replace it | ||||
| * | |||||
| * \param rebuild Even if an instance already exists, replace it | |||||
| */ | */ | ||||
| static void rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, bool rebuild) | static void rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, bool rebuild) | ||||
| { | { | ||||
| Context not available. | |||||
| /* --------------------- */ | /* --------------------- */ | ||||
| /** | /* Create physics sim representation of constraint given rigid body constraint settings | ||||
| * Create physics sim representation of constraint given rigid body constraint settings | * < rebuild: even if an instance already exists, replace it | ||||
| * | |||||
| * \param rebuild Even if an instance already exists, replace it | |||||
| */ | */ | ||||
| static void rigidbody_validate_sim_constraint(RigidBodyWorld *rbw, Object *ob, bool rebuild) | static void rigidbody_validate_sim_constraint(RigidBodyWorld *rbw, Object *ob, bool rebuild) | ||||
| { | { | ||||
| Context not available. | |||||
| RB_constraint_set_damping_6dof_spring(rbc->physics_constraint, RB_LIMIT_LIN_Z, rbc->spring_damping_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); | RB_constraint_set_equilibrium_6dof_spring(rbc->physics_constraint); | ||||
| /* fall-through */ | /* 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 representation of constraint given rigid body constraint settings | |||||
| * < rebuild: even if an instance already exists, replace it | |||||
| */ | |||||
| void BKE_rigidbody_validate_sim_shard_constraint(RigidBodyWorld *rbw, RigidBodyShardCon *rbc, short rebuild) | |||||
| { | |||||
| float loc[3]; | |||||
| float rot[4]; | |||||
| float lin_lower; | |||||
| float lin_upper; | |||||
| float ang_lower; | |||||
| float ang_upper; | |||||
| rbRigidBody *rb1; | |||||
| rbRigidBody *rb2; | |||||
| /* sanity checks: | |||||
| * - object should have a rigid body constraint | |||||
| * - rigid body constraint should have at least one constrained object | |||||
| */ | |||||
| if (rbc == NULL) { | |||||
Not Done Inline Actionsthis is copypasting ~165 lines of code, (almost verbatim). seems like it could be avoided in this case. campbellbarton: this is copypasting ~165 lines of code, (almost verbatim).
seems like it could be avoided in… | |||||
| return; | |||||
| } | |||||
| if (ELEM(NULL, rbc->mi1, rbc->mi1->rigidbody, rbc->mi2, rbc->mi2->rigidbody)) { | |||||
| 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->mi1->rigidbody) | |||||
| { | |||||
| rb1 = rbc->mi1->rigidbody->physics_object; | |||||
| } | |||||
| if (rbc->mi2->rigidbody) | |||||
| { | |||||
| rb2 = rbc->mi2->rigidbody->physics_object; | |||||
| } | |||||
| if (rbc->physics_constraint) { | |||||
| if (rebuild == false) | |||||
| RB_dworld_remove_constraint(rbw->physics_world, rbc->physics_constraint); | |||||
| } | |||||
| if (rbc->physics_constraint == NULL || rebuild) { | |||||
| /* 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; | |||||
| } | |||||
| /* do this for all constraints */ | |||||
| copy_v3_v3(loc, rbc->mi1->rigidbody->pos); | |||||
| copy_v4_v4(rot, rbc->mi1->rigidbody->orn); | |||||
| 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: | case RBC_TYPE_6DOF: | ||||
| if (rbc->type == RBC_TYPE_6DOF) /* a litte awkward but avoids duplicate code for limits */ | 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); | rbc->physics_constraint = RB_constraint_new_6dof(loc, rot, rb1, rb2); | ||||
| if (rbc->flag & RBC_FLAG_USE_LIMIT_LIN_X) | if (rbc->flag & RBC_FLAG_USE_LIMIT_LIN_X) | ||||
| Context not available. | |||||
| rbw->pointcache = BKE_ptcache_add(&(rbw->ptcaches)); | rbw->pointcache = BKE_ptcache_add(&(rbw->ptcaches)); | ||||
| rbw->pointcache->step = 1; | rbw->pointcache->step = 1; | ||||
| rbw->object_changed = false; | |||||
| rbw->refresh_modifiers = false; | |||||
| rbw->objects = MEM_mallocN(sizeof(Object *), "objects"); | |||||
| rbw->cache_index_map = MEM_mallocN(sizeof(RigidBodyOb *), "cache_index_map"); | |||||
| rbw->cache_offset_map = MEM_mallocN(sizeof(int), "cache_offset_map"); | |||||
| /* return this sim world */ | /* return this sim world */ | ||||
| return rbw; | return rbw; | ||||
| } | } | ||||
| /* Add rigid body settings to the specified shard */ | |||||
| RigidBodyOb *BKE_rigidbody_create_shard(Scene *scene, Object *ob, MeshIsland *mi) | |||||
| { | |||||
| RigidBodyOb *rbo; | |||||
| RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene); | |||||
| float centr[3], size[3]; | |||||
| /* sanity checks | |||||
| * - rigidbody world must exist | |||||
| * - shard must exist | |||||
| * - cannot add rigid body if it already exists | |||||
| */ | |||||
| if (mi == NULL || (mi->rigidbody != NULL)) | |||||
| return NULL; | |||||
| if (ob->type != OB_MESH && ob->type != OB_FONT && ob->type != OB_CURVE && ob->type != OB_SURF) { | |||||
| return NULL; | |||||
| } | |||||
| if ((((Mesh *)ob->data)->totvert == 0) && (ob->type == OB_MESH)) { | |||||
| return NULL; | |||||
| } | |||||
| /* Add rigid body world and group if they don't exist for convenience */ | |||||
| if (rbw == NULL) { | |||||
| rbw = BKE_rigidbody_create_world(scene); | |||||
| BKE_rigidbody_validate_sim_world(scene, rbw, false); | |||||
| scene->rigidbody_world = rbw; | |||||
| } | |||||
| if (rbw->group == NULL) { | |||||
| rbw->group = BKE_group_add(G.main, "RigidBodyWorld"); | |||||
| } | |||||
| /* make rigidbody object settings */ | |||||
| if (ob->rigidbody_object == NULL) { | |||||
| ob->rigidbody_object = BKE_rigidbody_create_object(scene, ob, mi->ground_weight > 0.5f ? RBO_TYPE_PASSIVE : RBO_TYPE_ACTIVE); | |||||
| } | |||||
| else { | |||||
| ob->rigidbody_object->type = mi->ground_weight > 0.5f ? RBO_TYPE_PASSIVE : RBO_TYPE_ACTIVE; | |||||
| ob->rigidbody_object->flag |= RBO_FLAG_NEEDS_VALIDATE; | |||||
| } | |||||
| if (!BKE_group_object_exists(rbw->group, ob)) | |||||
| BKE_group_object_add(rbw->group, ob, scene, NULL); | |||||
| DAG_id_tag_update(&ob->id, OB_RECALC_OB); | |||||
| /* since we are always member of an object, dupe its settings, | |||||
| * create new settings data, and link it up */ | |||||
| rbo = BKE_rigidbody_copy_object(ob); | |||||
| rbo->type = mi->ground_weight > 0.5f ? RBO_TYPE_PASSIVE : RBO_TYPE_ACTIVE; | |||||
| /* set initial transform */ | |||||
| mat4_to_loc_quat(rbo->pos, rbo->orn, ob->obmat); | |||||
| mat4_to_size(size, ob->obmat); | |||||
| //add initial "offset" (centroid), maybe subtract ob->obmat ?? (not sure) | |||||
| copy_v3_v3(centr, mi->centroid); | |||||
| mul_v3_v3(centr, size); | |||||
| mul_qt_v3(rbo->orn, centr); | |||||
| add_v3_v3(rbo->pos, centr); | |||||
| /* return this object */ | |||||
| return rbo; | |||||
| } | |||||
| RigidBodyWorld *BKE_rigidbody_world_copy(RigidBodyWorld *rbw) | RigidBodyWorld *BKE_rigidbody_world_copy(RigidBodyWorld *rbw) | ||||
| { | { | ||||
| RigidBodyWorld *rbwn = MEM_dupallocN(rbw); | RigidBodyWorld *rbwn = MEM_dupallocN(rbw); | ||||
| Context not available. | |||||
| if (rbwn->constraints) | if (rbwn->constraints) | ||||
| id_us_plus(&rbwn->constraints->id); | id_us_plus(&rbwn->constraints->id); | ||||
| rbwn->pointcache = BKE_ptcache_copy_list(&rbwn->ptcaches, &rbw->ptcaches, false); | rbwn->pointcache = BKE_ptcache_copy_list(&rbwn->ptcaches, &rbw->ptcaches, true); | ||||
| rbwn->objects = NULL; | rbwn->objects = NULL; | ||||
| rbwn->physics_world = NULL; | rbwn->physics_world = NULL; | ||||
| rbwn->numbodies = 0; | rbwn->numbodies = 0; | ||||
| rbwn->cache_index_map = NULL; | |||||
| rbwn->cache_offset_map = NULL; | |||||
| return rbwn; | return rbwn; | ||||
| } | } | ||||
| Context not available. | |||||
| else | else | ||||
| rbo->shape = RB_SHAPE_TRIMESH; | rbo->shape = RB_SHAPE_TRIMESH; | ||||
| rbo->mesh_source = RBO_MESH_DEFORM; | rbo->mesh_source = RBO_MESH_DEFORM; | ||||
| /* 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 */ | |||||
| /* set initial transform */ | rbc->limit_lin_x_lower = -1.0f; | ||||
| mat4_to_loc_quat(rbo->pos, rbo->orn, ob->obmat); | 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 */ | /* flag cache as outdated */ | ||||
| BKE_rigidbody_cache_reset(rbw); | BKE_rigidbody_cache_reset(rbw); | ||||
| /* return this object */ | /* return this object */ | ||||
| return rbo; | return rbc; | ||||
| } | } | ||||
| /* Add rigid body constraint to the specified object */ | /* Add rigid body constraint to the specified object */ | ||||
| RigidBodyCon *BKE_rigidbody_create_constraint(Scene *scene, Object *ob, short type) | RigidBodyShardCon *BKE_rigidbody_create_shard_constraint(Scene *scene, short type) | ||||
| { | { | ||||
| RigidBodyCon *rbc; | RigidBodyShardCon *rbc; | ||||
| RigidBodyWorld *rbw = scene->rigidbody_world; | RigidBodyWorld *rbw = scene->rigidbody_world; | ||||
| /* sanity checks | /* sanity checks | ||||
| Context not available. | |||||
| * - object must exist | * - object must exist | ||||
| * - cannot add constraint if it already exists | * - cannot add constraint if it already exists | ||||
| */ | */ | ||||
| if (ob == NULL || (ob->rigidbody_constraint != NULL)) | |||||
| return NULL; | |||||
| /* create new settings data, and link it up */ | /* create new settings data, and link it up */ | ||||
| rbc = MEM_callocN(sizeof(RigidBodyCon), "RigidBodyCon"); | rbc = MEM_callocN(sizeof(RigidBodyShardCon), "RigidBodyCon"); | ||||
| /* set default settings */ | /* set default settings */ | ||||
| rbc->type = type; | rbc->type = type; | ||||
| rbc->ob1 = NULL; | rbc->mi1 = NULL; | ||||
| rbc->ob2 = NULL; | rbc->mi2 = NULL; | ||||
| rbc->flag |= RBC_FLAG_ENABLED; | rbc->flag |= RBC_FLAG_ENABLED; | ||||
| rbc->flag |= RBC_FLAG_DISABLE_COLLISIONS; | rbc->flag &= ~RBC_FLAG_DISABLE_COLLISIONS; | ||||
| rbc->flag |= RBC_FLAG_USE_BREAKING; | |||||
| rbc->breaking_threshold = 10.0f; /* no good default here, just use 10 for now */ | rbc->breaking_threshold = 1.0f; /* no good default here, just use 10 for now */ | ||||
| rbc->num_solver_iterations = 10; /* 10 is Bullet default */ | rbc->num_solver_iterations = 10; /* 10 is Bullet default */ | ||||
| rbc->limit_lin_x_lower = -1.0f; | rbc->limit_lin_x_lower = -1.0f; | ||||
| Context not available. | |||||
| return scene->rigidbody_world; | return scene->rigidbody_world; | ||||
| } | } | ||||
| void BKE_rigidbody_remove_shard_con(Scene *scene, RigidBodyShardCon *con) | |||||
| { | |||||
| RigidBodyWorld *rbw = scene->rigidbody_world; | |||||
| if (rbw && rbw->physics_world && con->physics_constraint) { | |||||
| RB_dworld_remove_constraint(rbw->physics_world, con->physics_constraint); | |||||
| RB_constraint_delete(con->physics_constraint); | |||||
| con->physics_constraint = NULL; | |||||
| } | |||||
| } | |||||
| void BKE_rigidbody_remove_shard(Scene *scene, MeshIsland *mi) | |||||
| { | |||||
| RigidBodyWorld *rbw = scene->rigidbody_world; | |||||
| int i = 0; | |||||
| /* rbw can be NULL directly after linking / appending objects without their original scenes | |||||
| * if an attempt to refracture is done then, this would crash here with null pointer access */ | |||||
| if (mi->rigidbody != NULL && rbw != NULL) { | |||||
| RigidBodyShardCon *con; | |||||
| for (i = 0; i < mi->participating_constraint_count; i++) { | |||||
| con = mi->participating_constraints[i]; | |||||
| BKE_rigidbody_remove_shard_con(scene, con); | |||||
| } | |||||
| if (rbw->physics_world && mi->rigidbody && mi->rigidbody->physics_object) | |||||
| RB_dworld_remove_body(rbw->physics_world, mi->rigidbody->physics_object); | |||||
| if (mi->rigidbody->physics_object) { | |||||
| RB_body_delete(mi->rigidbody->physics_object); | |||||
| mi->rigidbody->physics_object = NULL; | |||||
| } | |||||
| if (mi->rigidbody->physics_shape) { | |||||
| RB_shape_delete(mi->rigidbody->physics_shape); | |||||
| mi->rigidbody->physics_shape = NULL; | |||||
| } | |||||
| /* this SHOULD be the correct global index */ | |||||
| /* need to check whether we didnt create the rigidbody world manually already, prior to fracture, in this | |||||
| * case cache_index_map might be not initialized ! checking numbodies here, they should be 0 in a fresh | |||||
| * rigidbody world */ | |||||
| if (rbw->cache_index_map != NULL && rbw->numbodies > 0) | |||||
| rbw->cache_index_map[mi->linear_index] = NULL; | |||||
| } | |||||
| } | |||||
| void BKE_rigidbody_remove_object(Scene *scene, Object *ob) | void BKE_rigidbody_remove_object(Scene *scene, Object *ob) | ||||
| { | { | ||||
| RigidBodyWorld *rbw = scene->rigidbody_world; | RigidBodyWorld *rbw = scene->rigidbody_world; | ||||
| RigidBodyOb *rbo = ob->rigidbody_object; | RigidBodyOb *rbo = ob->rigidbody_object; | ||||
| RigidBodyCon *rbc; | RigidBodyCon *rbc; | ||||
| GroupObject *go; | GroupObject *go; | ||||
| ModifierData *md; | |||||
| FractureModifierData *rmd; | |||||
| RigidBodyShardCon *con; | |||||
| MeshIsland *mi; | |||||
| int i; | int i; | ||||
| bool modFound = false; | |||||
| if (rbw) { | if (rbw) { | ||||
| /* remove from rigidbody world, free object won't do this */ | for (md = ob->modifiers.first; md; md = md->next) { | ||||
| if (rbw->physics_world && rbo->physics_object) | |||||
| RB_dworld_remove_body(rbw->physics_world, rbo->physics_object); | if (md->type == eModifierType_Fracture) | ||||
| { | |||||
| rmd = (FractureModifierData *)md; | |||||
| modFound = true; | |||||
| for (con = rmd->meshConstraints.first; con; con = con->next) { | |||||
| if (rbw && rbw->physics_world && con->physics_constraint) { | |||||
| RB_dworld_remove_constraint(rbw->physics_world, con->physics_constraint); | |||||
| RB_constraint_delete(con->physics_constraint); | |||||
| con->physics_constraint = NULL; | |||||
| } | |||||
| } | |||||
| /* remove object from array */ | for (mi = rmd->meshIslands.first; mi; mi = mi->next) { | ||||
| if (rbw && rbw->objects) { | if (mi->rigidbody != NULL) { | ||||
| for (i = 0; i < rbw->numbodies; i++) { | if (rbw->physics_world && mi->rigidbody && mi->rigidbody->physics_object) | ||||
| if (rbw->objects[i] == ob) { | RB_dworld_remove_body(rbw->physics_world, mi->rigidbody->physics_object); | ||||
| rbw->objects[i] = NULL; | if (mi->rigidbody->physics_object) { | ||||
| break; | RB_body_delete(mi->rigidbody->physics_object); | ||||
| mi->rigidbody->physics_object = NULL; | |||||
| } | |||||
| if (mi->rigidbody->physics_shape) { | |||||
| RB_shape_delete(mi->rigidbody->physics_shape); | |||||
| mi->rigidbody->physics_shape = NULL; | |||||
| } | |||||
| /* this SHOULD be the correct global index*/ | |||||
| if (rbw->cache_index_map) | |||||
| rbw->cache_index_map[mi->linear_index] = NULL; | |||||
| MEM_freeN(mi->rigidbody); | |||||
| mi->rigidbody = NULL; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* remove object from rigid body constraints */ | if (!modFound) { | ||||
| if (rbw->constraints) { | /* remove from rigidbody world, free object won't do this */ | ||||
| for (go = rbw->constraints->gobject.first; go; go = go->next) { | if (rbw->physics_world && rbo->physics_object) | ||||
| Object *obt = go->ob; | RB_dworld_remove_body(rbw->physics_world, rbo->physics_object); | ||||
| if (obt && obt->rigidbody_constraint) { | |||||
| rbc = obt->rigidbody_constraint; | /* remove object from array */ | ||||
| if (ELEM(ob, rbc->ob1, rbc->ob2)) { | if (rbw && rbw->objects) { | ||||
| BKE_rigidbody_remove_constraint(scene, obt); | for (i = 0; i < rbw->numbodies; i++) { | ||||
| int index = rbw->cache_offset_map[i]; | |||||
| if (rbw->objects[index] == ob) { | |||||
| rbw->objects[index] = NULL; | |||||
| } | |||||
| if (rbo == rbw->cache_index_map[i]) { | |||||
| rbw->cache_index_map[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) { | |||||
| rbc->ob1 = NULL; | |||||
| rbc->flag |= RBC_FLAG_NEEDS_VALIDATE; | |||||
| } | |||||
| if (rbc->ob2 == ob) { | |||||
| rbc->ob2 = NULL; | |||||
| rbc->flag |= RBC_FLAG_NEEDS_VALIDATE; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* remove object's settings */ | |||||
| BKE_rigidbody_free_object(ob); | |||||
| } | } | ||||
| } | } | ||||
| /* remove object's settings */ | |||||
| BKE_rigidbody_free_object(ob); | |||||
| /* flag cache as outdated */ | /* flag cache as outdated */ | ||||
| BKE_rigidbody_cache_reset(rbw); | BKE_rigidbody_cache_reset(rbw); | ||||
| } | } | ||||
| Context not available. | |||||
| BKE_rigidbody_cache_reset(rbw); | BKE_rigidbody_cache_reset(rbw); | ||||
| } | } | ||||
| static int rigidbody_group_count_items(const ListBase *group, int *r_num_objects, int *r_num_shards) | |||||
| { | |||||
| int num_gobjects = 0; | |||||
| ModifierData *md; | |||||
| FractureModifierData *rmd; | |||||
| GroupObject *gob; | |||||
| if (r_num_objects == NULL || r_num_shards == NULL) | |||||
| { | |||||
| return num_gobjects; | |||||
| } | |||||
| *r_num_objects = 0; | |||||
| *r_num_shards = 0; | |||||
| for (gob = group->first; gob; gob = gob->next) { | |||||
| bool found_modifiers = false; | |||||
| for (md = gob->ob->modifiers.first; md; md = md->next) { | |||||
Not Done Inline ActionsSome comments about these two methods:
So, the revised code would look something like: static int rigidbody_group_count_items(const ListBase *group, int *r_num_objects, int *r_num_shards)
{
int num_gobjects = 0;
*r_num_objects = 0; // XXX: with null check on it first
*r_num_shards = 0; // XXX: with null check on it first
for (gob = group->first; gob; gob = gob->next) {
bool found_modifiers = false;
for (md = gob->ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Fracture) {
found_modifiers = true;
*r_num_shards += ...
}
}
if (found_modifiers == false) {
*r_num_objects++;
}
num_gobjects++;
}
return num_gobjects;
}aligorith: Some comments about these two methods:
1) It would be more efficient if you just had one… | |||||
| if (md->type == eModifierType_Fracture) { | |||||
| rmd = (FractureModifierData *)md; | |||||
| if (isModifierActive(rmd)) | |||||
| { | |||||
| found_modifiers = true; | |||||
| *r_num_shards += BLI_countlist(&rmd->meshIslands); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (found_modifiers == false) { | |||||
| (*r_num_objects)++; | |||||
| } | |||||
| num_gobjects++; | |||||
| } | |||||
| return num_gobjects; | |||||
| } | |||||
| /* ************************************** */ | /* ************************************** */ | ||||
| /* Simulation Interface - Bullet */ | /* Simulation Interface - Bullet */ | ||||
| Context not available. | |||||
| static void rigidbody_update_ob_array(RigidBodyWorld *rbw) | static void rigidbody_update_ob_array(RigidBodyWorld *rbw) | ||||
| { | { | ||||
| GroupObject *go; | GroupObject *go; | ||||
| int i, n; | ModifierData *md; | ||||
| FractureModifierData *rmd; | |||||
| MeshIsland *mi; | |||||
| int i, j, l = 0, m = 0, n = 0, counter = 0; | |||||
| bool ismapped = false; | |||||
| if (rbw->objects != NULL) { | |||||
| MEM_freeN(rbw->objects); | |||||
| rbw->objects = NULL; | |||||
| } | |||||
| if (rbw->cache_index_map != NULL) { | |||||
| MEM_freeN(rbw->cache_index_map); | |||||
| rbw->cache_index_map = NULL; | |||||
| } | |||||
| if (rbw->cache_offset_map != NULL) { | |||||
| MEM_freeN(rbw->cache_offset_map); | |||||
| rbw->cache_offset_map = NULL; | |||||
| } | |||||
| n = BLI_countlist(&rbw->group->gobject); | l = rigidbody_group_count_items(&rbw->group->gobject, &m, &n); | ||||
| if (rbw->numbodies != n) { | rbw->numbodies = m + n; | ||||
| rbw->numbodies = n; | rbw->objects = MEM_mallocN(sizeof(Object *) * l, "objects"); | ||||
| rbw->objects = realloc(rbw->objects, sizeof(Object *) * rbw->numbodies); | rbw->cache_index_map = MEM_mallocN(sizeof(RigidBodyOb *) * rbw->numbodies, "cache_index_map"); | ||||
| } | rbw->cache_offset_map = MEM_mallocN(sizeof(int) * rbw->numbodies, "cache_offset_map"); | ||||
| printf("RigidbodyCount changed: %d\n", rbw->numbodies); | |||||
| for (go = rbw->group->gobject.first, i = 0; go; go = go->next, i++) { | for (go = rbw->group->gobject.first, i = 0; go; go = go->next, i++) { | ||||
| Object *ob = go->ob; | Object *ob = go->ob; | ||||
| rbw->objects[i] = ob; | rbw->objects[i] = ob; | ||||
| for (md = ob->modifiers.first; md; md = md->next) { | |||||
| if (md->type == eModifierType_Fracture) { | |||||
| rmd = (FractureModifierData *)md; | |||||
| if (isModifierActive(rmd)) { | |||||
| for (mi = rmd->meshIslands.first, j = 0; mi; mi = mi->next) { | |||||
| rbw->cache_index_map[counter] = mi->rigidbody; /* map all shards of an object to this object index*/ | |||||
| rbw->cache_offset_map[counter] = i; | |||||
| mi->linear_index = counter; | |||||
| counter++; | |||||
| j++; | |||||
| } | |||||
| ismapped = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (!ismapped) { | |||||
| rbw->cache_index_map[counter] = ob->rigidbody_object; /*1 object 1 index here (normal case)*/ | |||||
| rbw->cache_offset_map[counter] = i; | |||||
| counter++; | |||||
| } | |||||
| ismapped = false; | |||||
| } | } | ||||
| } | } | ||||
| Context not available. | |||||
| rigidbody_update_ob_array(rbw); | rigidbody_update_ob_array(rbw); | ||||
| } | } | ||||
| static void rigidbody_update_sim_ob(Scene *scene, RigidBodyWorld *rbw, Object *ob, RigidBodyOb *rbo) | static void rigidbody_update_sim_ob(Scene *scene, RigidBodyWorld *rbw, Object *ob, RigidBodyOb *rbo, float centroid[3]) | ||||
| { | { | ||||
| float loc[3]; | float loc[3]; | ||||
| float rot[4]; | float rot[4]; | ||||
| float scale[3]; | float scale[3], centr[3]; | ||||
| /* only update if rigid body exists */ | /* only update if rigid body exists */ | ||||
| if (rbo->physics_object == NULL) | if (rbo->physics_object == NULL) | ||||
| Context not available. | |||||
| RB_shape_trimesh_update(rbo->physics_shape, (float *)mvert, totvert, sizeof(MVert), bb->vec[0], bb->vec[6]); | RB_shape_trimesh_update(rbo->physics_shape, (float *)mvert, totvert, sizeof(MVert), bb->vec[0], bb->vec[6]); | ||||
| } | } | ||||
| } | } | ||||
| copy_v3_v3(centr, centroid); | |||||
| mat4_decompose(loc, rot, scale, ob->obmat); | mat4_decompose(loc, rot, scale, ob->obmat); | ||||
| /* update scale for all objects */ | /* update scale for all objects */ | ||||
| Context not available. | |||||
| /* update rigid body location and rotation for kinematic bodies */ | /* update rigid body location and rotation for kinematic bodies */ | ||||
| if (rbo->flag & RBO_FLAG_KINEMATIC || (ob->flag & SELECT && G.moving & G_TRANSFORM_OBJ)) { | if (rbo->flag & RBO_FLAG_KINEMATIC || (ob->flag & SELECT && G.moving & G_TRANSFORM_OBJ)) { | ||||
| mul_v3_v3(centr, scale); | |||||
| mul_qt_v3(rot, centr); | |||||
| add_v3_v3(loc, centr); | |||||
| RB_body_activate(rbo->physics_object); | RB_body_activate(rbo->physics_object); | ||||
| RB_body_set_loc_rot(rbo->physics_object, loc, rot); | RB_body_set_loc_rot(rbo->physics_object, loc, rot); | ||||
| } | } | ||||
| Context not available. | |||||
| /* create dummy 'point' which represents last known position of object as result of sim */ | /* 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? | // 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_position(rbo->physics_object, eff_loc); | ||||
| //mul_v3_v3(centr, scale); | |||||
| //add_v3_v3(eff_loc, centr); | |||||
| RB_body_get_linear_velocity(rbo->physics_object, eff_vel); | RB_body_get_linear_velocity(rbo->physics_object, eff_vel); | ||||
| pd_point_from_loc(scene, eff_loc, eff_vel, 0, &epoint); | pd_point_from_loc(scene, eff_loc, eff_vel, 0, &epoint); | ||||
| Context not available. | |||||
| */ | */ | ||||
| } | } | ||||
| /** | static void validateShard(RigidBodyWorld *rbw, MeshIsland *mi, Object *ob, int rebuild) | ||||
| * Updates and validates world, bodies and shapes. | { | ||||
| * | if (mi == NULL || mi->rigidbody == NULL) { | ||||
| * \param rebuild Rebuild entire simulation | return; | ||||
| } | |||||
| if (rebuild) { // && (mi->rigidbody->flag & RBO_FLAG_NEEDS_VALIDATE)) { | |||||
| /* World has been rebuilt so rebuild object */ | |||||
| BKE_rigidbody_validate_sim_shard(rbw, mi, ob, true); | |||||
| } | |||||
| else if (mi->rigidbody->flag & RBO_FLAG_NEEDS_VALIDATE) { | |||||
| BKE_rigidbody_validate_sim_shard(rbw, mi, ob, false); | |||||
| } | |||||
| /* refresh shape... */ | |||||
| if (mi->rigidbody->flag & RBO_FLAG_NEEDS_RESHAPE) { | |||||
| /* mesh/shape data changed, so force shape refresh */ | |||||
| BKE_rigidbody_validate_sim_shard_shape(mi, 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(mi->rigidbody->physics_object, mi->rigidbody->physics_shape); | |||||
| } | |||||
| mi->rigidbody->flag &= ~(RBO_FLAG_NEEDS_VALIDATE | RBO_FLAG_NEEDS_RESHAPE); | |||||
| } | |||||
| /* Updates and validates world, bodies and shapes. | |||||
| * < rebuild: rebuild entire simulation | |||||
| */ | */ | ||||
| static void rigidbody_update_simulation(Scene *scene, RigidBodyWorld *rbw, bool rebuild) | static void rigidbody_update_simulation(Scene *scene, RigidBodyWorld *rbw, bool rebuild) | ||||
| { | { | ||||
| GroupObject *go; | GroupObject *go; | ||||
| MeshIsland *mi = NULL; | |||||
| float centroid[3] = {0, 0, 0}; | |||||
| RigidBodyShardCon *rbsc; | |||||
| /* update world */ | /* update world */ | ||||
| if (rebuild) | if (rebuild) { | ||||
| BKE_rigidbody_validate_sim_world(scene, rbw, true); | BKE_rigidbody_validate_sim_world(scene, rbw, true); | ||||
| rigidbody_update_sim_world(scene, rbw); | rigidbody_update_sim_world(scene, rbw); | ||||
| /* XXX TODO For rebuild: remove all constraints first. | |||||
| * Otherwise we can end up deleting objects that are still | |||||
| * referenced by constraints, corrupting bullet's internal list. | |||||
| * | |||||
| * Memory management needs redesign here, this is just a dirty workaround. | |||||
| */ | |||||
| if (rebuild && rbw->constraints) { | |||||
| for (go = rbw->constraints->gobject.first; go; go = go->next) { | |||||
| Object *ob = go->ob; | |||||
| if (ob) { | |||||
| RigidBodyCon *rbc = ob->rigidbody_constraint; | |||||
| if (rbc && rbc->physics_constraint) { | |||||
| RB_dworld_remove_constraint(rbw->physics_world, rbc->physics_constraint); | |||||
| RB_constraint_delete(rbc->physics_constraint); | |||||
| rbc->physics_constraint = NULL; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| /* update objects */ | /* update objects */ | ||||
| for (go = rbw->group->gobject.first; go; go = go->next) { | for (go = rbw->group->gobject.first; go; go = go->next) { | ||||
| Object *ob = go->ob; | Object *ob = go->ob; | ||||
| ModifierData *md = NULL; | |||||
| FractureModifierData *rmd = NULL; | |||||
| if (ob && ob->type == OB_MESH) { | if (ob && (ob->type == OB_MESH || ob->type == OB_CURVE || ob->type == OB_SURF || ob->type == OB_FONT)) { | ||||
| /* 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) { | /* check for fractured objects which want to participate first, then handle other normal objects*/ | ||||
| /* Since this object is included in the sim group but doesn't have | for (md = ob->modifiers.first; md; md = md->next) { | ||||
| * rigid body settings (perhaps it was added manually), add! | if (md->type == eModifierType_Fracture) { | ||||
| * - assume object to be active? That is the default for newly added settings... | rmd = (FractureModifierData *)md; | ||||
| */ | break; | ||||
| ob->rigidbody_object = BKE_rigidbody_create_object(scene, ob, RBO_TYPE_ACTIVE); | } | ||||
| rigidbody_validate_sim_object(rbw, ob, true); | } | ||||
| if (isModifierActive(rmd)) { | |||||
| float max_con_mass = 0; | |||||
| int count = BLI_countlist(&rmd->meshIslands); | |||||
| for (mi = rmd->meshIslands.first; mi; mi = mi->next) { | |||||
| if (mi->rigidbody == NULL) { | |||||
| continue; | |||||
| } | |||||
| else { /* as usual, but for each shard now, and no constraints*/ | |||||
| /* perform simulation data updates as tagged */ | |||||
| /* refresh object... */ | |||||
| int do_rebuild = rebuild; | |||||
| float weight = mi->thresh_weight; | |||||
| int breaking_percentage = rmd->breaking_percentage_weighted ? (rmd->breaking_percentage * weight) : rmd->breaking_percentage; | |||||
| if (rmd->breaking_percentage > 0 || (rmd->breaking_percentage_weighted && weight > 0)) { | |||||
| int broken_cons = 0, cons = 0, i = 0; | |||||
| RigidBodyShardCon *con; | |||||
| cons = mi->participating_constraint_count; | |||||
| /* calc ratio of broken cons here, per MeshIsland and flag the rest to be broken too*/ | |||||
| for (i = 0; i < cons; i++) { | |||||
| con = mi->participating_constraints[i]; | |||||
| if (con && con->physics_constraint) { | |||||
| if (!RB_constraint_is_enabled(con->physics_constraint)) { | |||||
| broken_cons++; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (cons > 0) { | |||||
| if ((float)broken_cons / (float)cons * 100 >= breaking_percentage) { | |||||
| /* break all cons if over percentage */ | |||||
| for (i = 0; i < cons; i++) { | |||||
| con = mi->participating_constraints[i]; | |||||
| if (con) { | |||||
| con->flag &= ~RBC_FLAG_ENABLED; | |||||
| con->flag |= RBC_FLAG_NEEDS_VALIDATE; | |||||
| if (con->physics_constraint) { | |||||
| RB_constraint_set_enabled(con->physics_constraint, false); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| validateShard(rbw, count == 0 ? NULL : mi, ob, do_rebuild); | |||||
| } | |||||
| /* update simulation object... */ | |||||
| rigidbody_update_sim_ob(scene, rbw, ob, mi->rigidbody, mi->centroid); | |||||
| } | |||||
| if (rmd->use_mass_dependent_thresholds) { | |||||
| max_con_mass = BKE_rigidbody_calc_max_con_mass(ob); | |||||
| } | |||||
| for (rbsc = rmd->meshConstraints.first; rbsc; rbsc = rbsc->next) { | |||||
| float weight = MIN2(rbsc->mi1->thresh_weight, rbsc->mi2->thresh_weight); | |||||
| float breaking_angle = rmd->breaking_angle_weighted ? rmd->breaking_angle * weight : rmd->breaking_angle; | |||||
| float breaking_distance = rmd->breaking_distance_weighted ? rmd->breaking_distance * weight : rmd->breaking_distance; | |||||
| int iterations; | |||||
| if (rmd->solver_iterations_override == 0) { | |||||
| iterations = rbw->num_solver_iterations; | |||||
| } | |||||
| else { | |||||
| iterations = rmd->solver_iterations_override; | |||||
| } | |||||
| if (iterations > 0) { | |||||
| rbsc->flag |= RBC_FLAG_OVERRIDE_SOLVER_ITERATIONS; | |||||
| rbsc->num_solver_iterations = iterations; | |||||
| } | |||||
| if ((rmd->use_mass_dependent_thresholds)) { | |||||
| BKE_rigidbody_calc_threshold(max_con_mass, rmd, rbsc); | |||||
| } | |||||
| if (((rmd->breaking_angle) > 0) || (rmd->breaking_angle_weighted && weight > 0) || | |||||
| (((rmd->breaking_distance > 0) || (rmd->breaking_distance_weighted && weight > 0)) && !rebuild)) | |||||
| { | |||||
| float dist, angle, distdiff, anglediff; | |||||
| calc_dist_angle(rbsc, &dist, &angle); | |||||
| anglediff = fabs(angle - rbsc->start_angle); | |||||
| distdiff = fabs(dist - rbsc->start_dist); | |||||
| if ((rmd->breaking_angle > 0 || (rmd->breaking_angle_weighted && weight > 0)) && | |||||
| (anglediff > breaking_angle)) | |||||
| { | |||||
| rbsc->flag &= ~RBC_FLAG_ENABLED; | |||||
| rbsc->flag |= RBC_FLAG_NEEDS_VALIDATE; | |||||
| if (rbsc->physics_constraint) { | |||||
| RB_constraint_set_enabled(rbsc->physics_constraint, false); | |||||
| } | |||||
| } | |||||
| if ((rmd->breaking_distance > 0 || (rmd->breaking_distance_weighted && weight > 0)) && | |||||
| (distdiff > breaking_distance)) | |||||
| { | |||||
| rbsc->flag &= ~RBC_FLAG_ENABLED; | |||||
| rbsc->flag |= RBC_FLAG_NEEDS_VALIDATE; | |||||
| if (rbsc->physics_constraint) { | |||||
| RB_constraint_set_enabled(rbsc->physics_constraint, false); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (rebuild) { | |||||
| /* World has been rebuilt so rebuild constraint */ | |||||
| BKE_rigidbody_validate_sim_shard_constraint(rbw, rbsc, true); | |||||
| BKE_rigidbody_start_dist_angle(rbsc); | |||||
| } | |||||
| else if (rbsc->flag & RBC_FLAG_NEEDS_VALIDATE) { | |||||
| BKE_rigidbody_validate_sim_shard_constraint(rbw, rbsc, false); | |||||
| } | |||||
| if (rbsc->physics_constraint && rbw && rbw->rebuild_comp_con) { | |||||
| RB_constraint_set_enabled(rbsc->physics_constraint, true); | |||||
| } | |||||
| rbo = ob->rigidbody_object; | rbsc->flag &= ~RBC_FLAG_NEEDS_VALIDATE; | ||||
| } | |||||
| } | } | ||||
| else { | else { | ||||
| /* perform simulation data updates as tagged */ | /* validate that we've got valid object set up here... */ | ||||
| /* refresh object... */ | RigidBodyOb *rbo = ob->rigidbody_object; | ||||
| if (rebuild) { | /* update transformation matrix of the object so we don't get a frame of lag for simple animations */ | ||||
| /* World has been rebuilt so rebuild object */ | 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); | |||||
| rigidbody_validate_sim_object(rbw, ob, true); | rigidbody_validate_sim_object(rbw, ob, true); | ||||
| rbo = ob->rigidbody_object; | |||||
| } | } | ||||
| else if (rbo->flag & RBO_FLAG_NEEDS_VALIDATE) { | else { | ||||
| rigidbody_validate_sim_object(rbw, ob, false); | /* perform simulation data updates as tagged */ | ||||
| } | /* refresh object... */ | ||||
| /* refresh shape... */ | if (rebuild) { | ||||
| if (rbo->flag & RBO_FLAG_NEEDS_RESHAPE) { | /* World has been rebuilt so rebuild object */ | ||||
| /* mesh/shape data changed, so force shape refresh */ | rigidbody_validate_sim_object(rbw, ob, true); | ||||
| rigidbody_validate_sim_shape(ob, true); | } | ||||
| /* now tell RB sim about it */ | else if (rbo->flag & RBO_FLAG_NEEDS_VALIDATE) { | ||||
| // XXX: we assume that this can only get applied for active/passive shapes that will be included as rigidbodies | rigidbody_validate_sim_object(rbw, ob, false); | ||||
| RB_body_set_collision_shape(rbo->physics_object, rbo->physics_shape); | } | ||||
| /* refresh shape... */ | |||||
| if (rbo->flag & RBO_FLAG_NEEDS_RESHAPE) { | |||||
| /* mesh/shape data changed, so force shape refresh */ | |||||
| 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); | |||||
| } | } | ||||
| rbo->flag &= ~(RBO_FLAG_NEEDS_VALIDATE | RBO_FLAG_NEEDS_RESHAPE); | |||||
| } | |||||
| /* update simulation object... */ | /* update simulation object... */ | ||||
| rigidbody_update_sim_ob(scene, rbw, ob, rbo); | rigidbody_update_sim_ob(scene, rbw, ob, rbo, centroid); | ||||
| } | |||||
| } | } | ||||
| rbw->refresh_modifiers = false; | |||||
| } | } | ||||
| /* update constraints */ | /* update constraints */ | ||||
| if (rbw->constraints == NULL) /* no constraints, move on */ | if (rbw->constraints == NULL) /* no constraints, move on */ | ||||
| return; | return; | ||||
| Context not available. | |||||
| static void rigidbody_update_simulation_post_step(RigidBodyWorld *rbw) | static void rigidbody_update_simulation_post_step(RigidBodyWorld *rbw) | ||||
| { | { | ||||
| GroupObject *go; | GroupObject *go; | ||||
| ModifierData *md; | |||||
| FractureModifierData *rmd; | |||||
| int modFound = false; | |||||
| RigidBodyOb *rbo; | |||||
| MeshIsland *mi; | |||||
| for (go = rbw->group->gobject.first; go; go = go->next) { | for (go = rbw->group->gobject.first; go; go = go->next) { | ||||
| Object *ob = go->ob; | Object *ob = go->ob; | ||||
| //handle fractured rigidbodies, maybe test for psys as well ? | |||||
| for (md = ob->modifiers.first; md; md = md->next) { | |||||
| if (md->type == eModifierType_Fracture) { | |||||
| rmd = (FractureModifierData *)md; | |||||
| if (isModifierActive(rmd)) { | |||||
| for (mi = rmd->meshIslands.first; mi; mi = mi->next) { | |||||
| rbo = mi->rigidbody; | |||||
| if (!rbo) continue; | |||||
| /* 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); | |||||
| } | |||||
| } | |||||
| modFound = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (ob) { | /* handle regular rigidbodies */ | ||||
| if (ob && !modFound) { | |||||
| RigidBodyOb *rbo = ob->rigidbody_object; | RigidBodyOb *rbo = ob->rigidbody_object; | ||||
| /* reset kinematic state for transformed objects */ | /* reset kinematic state for transformed objects */ | ||||
| if (rbo && (ob->flag & SELECT) && (G.moving & G_TRANSFORM_OBJ)) { | if (rbo && (ob->flag & SELECT) && (G.moving & G_TRANSFORM_OBJ)) { | ||||
| Context not available. | |||||
| RB_body_deactivate(rbo->physics_object); | RB_body_deactivate(rbo->physics_object); | ||||
| } | } | ||||
| } | } | ||||
| modFound = false; | |||||
| } | } | ||||
| } | } | ||||
| Context not available. | |||||
| /* Sync rigid body and object transformations */ | /* Sync rigid body and object transformations */ | ||||
| void BKE_rigidbody_sync_transforms(RigidBodyWorld *rbw, Object *ob, float ctime) | void BKE_rigidbody_sync_transforms(RigidBodyWorld *rbw, Object *ob, float ctime) | ||||
| { | { | ||||
| RigidBodyOb *rbo = ob->rigidbody_object; | RigidBodyOb *rbo = NULL; | ||||
| FractureModifierData *rmd = NULL; | |||||
| MeshIsland *mi; | |||||
| ModifierData *md; | |||||
| float centr[3], size[3]; | |||||
| int modFound = false; | |||||
| bool exploOK = false; | |||||
| /* keep original transform for kinematic and passive objects */ | if (rbw == NULL) | ||||
| if (ELEM(NULL, rbw, rbo) || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE) | |||||
| return; | return; | ||||
| /* use rigid body transform after cache start frame if objects is not being transformed */ | for (md = ob->modifiers.first; md; md = md->next) { | ||||
| if (BKE_rigidbody_check_sim_running(rbw, ctime) && !(ob->flag & SELECT && G.moving & G_TRANSFORM_OBJ)) { | if (md->type == eModifierType_Fracture) { | ||||
| float mat[4][4], size_mat[4][4], size[3]; | rmd = (FractureModifierData *)md; | ||||
| exploOK = !rmd->explo_shared || (rmd->explo_shared && rmd->frac_mesh && rmd->dm); | |||||
| if (isModifierActive(rmd) && exploOK) { | |||||
| modFound = true; | |||||
| if ((ob->flag & SELECT && G.moving & G_TRANSFORM_OBJ) || | |||||
| ((ob->rigidbody_object) && (ob->rigidbody_object->flag & RBO_FLAG_KINEMATIC))) | |||||
| { | |||||
| /* update "original" matrix */ | |||||
| copy_m4_m4(rmd->origmat, ob->obmat); | |||||
| if (ob->flag & SELECT && G.moving & G_TRANSFORM_OBJ && rbw) { | |||||
| RigidBodyShardCon *con; | |||||
| rbw->object_changed = true; | |||||
| BKE_rigidbody_cache_reset(rbw); | |||||
| /* re-enable all constraints as well */ | |||||
| for (con = rmd->meshConstraints.first; con; con = con->next) { | |||||
| con->flag |= RBC_FLAG_ENABLED; | |||||
| con->flag |= RBC_FLAG_NEEDS_VALIDATE; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (!is_zero_m4(rmd->origmat) && rbw && !rbw->object_changed) { | |||||
| copy_m4_m4(ob->obmat, rmd->origmat); | |||||
| } | |||||
| for (mi = rmd->meshIslands.first; mi; mi = mi->next) { | |||||
| rbo = mi->rigidbody; | |||||
| if (!rbo) { | |||||
| continue; | |||||
| } | |||||
| /* 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)) { | |||||
| normalize_qt(rbo->orn); // RB_TODO investigate why quaternion isn't normalized at this point | /* keep original transform when the simulation is muted */ | ||||
| quat_to_mat4(mat, rbo->orn); | if (rbw->flag & RBW_FLAG_MUTED) | ||||
| copy_v3_v3(mat[3], rbo->pos); | return; | ||||
| } | |||||
| /* otherwise set rigid body transform to current obmat*/ | |||||
| else { | |||||
| mat4_to_size(size, ob->obmat); | mat4_to_loc_quat(rbo->pos, rbo->orn, ob->obmat); | ||||
| size_to_mat4(size_mat, size); | mat4_to_size(size, ob->obmat); | ||||
| mul_m4_m4m4(mat, mat, size_mat); | copy_v3_v3(centr, mi->centroid); | ||||
| mul_v3_v3(centr, size); | |||||
| mul_qt_v3(rbo->orn, centr); | |||||
| add_v3_v3(rbo->pos, centr); | |||||
| } | |||||
| BKE_rigidbody_update_cell(mi, ob, rbo->pos, rbo->orn, rmd); | |||||
| } | |||||
| break; | |||||
| } | |||||
| } | |||||
| copy_m4_m4(ob->obmat, mat); | modFound = false; | ||||
| } | } | ||||
| /* otherwise set rigid body transform to current obmat */ | |||||
| else { | if (!modFound) | ||||
| mat4_to_loc_quat(rbo->pos, rbo->orn, ob->obmat); | { | ||||
| 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); | |||||
| /* keep original transform when the simulation is muted */ | |||||
| if (rbw->flag & RBW_FLAG_MUTED) | |||||
| return; | |||||
| /*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 { | |||||
| if (ob->flag & SELECT && G.moving & G_TRANSFORM_OBJ) | |||||
| rbw->object_changed = true; | |||||
| mat4_to_loc_quat(rbo->pos, rbo->orn, ob->obmat); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| /* Used when canceling transforms - return rigidbody and object to initial states */ | /* 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) | 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; | RigidBodyOb *rbo; | ||||
| ModifierData *md; | |||||
| /* return rigid body and object to their initial states */ | FractureModifierData *rmd; | ||||
| copy_v3_v3(rbo->pos, ob->loc); | |||||
| copy_v3_v3(ob->loc, loc); | md = modifiers_findByType(ob, eModifierType_Fracture); | ||||
| if (md != NULL) | |||||
| if (ob->rotmode > 0) { | { | ||||
| eulO_to_quat(rbo->orn, ob->rot, ob->rotmode); | MeshIsland *mi; | ||||
| copy_v3_v3(ob->rot, rot); | rmd = (FractureModifierData *)md; | ||||
| } | copy_m4_m4(rmd->origmat, ob->obmat); | ||||
| else if (ob->rotmode == ROT_MODE_AXISANGLE) { | for (mi = rmd->meshIslands.first; mi; mi = mi->next) | ||||
| axis_angle_to_quat(rbo->orn, ob->rotAxis, ob->rotAngle); | { | ||||
| copy_v3_v3(ob->rotAxis, rotAxis); | rbo = mi->rigidbody; | ||||
| ob->rotAngle = rotAngle; | /* return rigid body and object to their initial states */ | ||||
| copy_v3_v3(rbo->pos, ob->loc); | |||||
| add_v3_v3(rbo->pos, mi->centroid); | |||||
| 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); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| else { | else { | ||||
| copy_qt_qt(rbo->orn, ob->quat); | rbo = ob->rigidbody_object; | ||||
| copy_qt_qt(ob->quat, quat); | /* return rigid body and object to their initial states */ | ||||
| } | copy_v3_v3(rbo->pos, ob->loc); | ||||
| if (rbo->physics_object) { | copy_v3_v3(ob->loc, loc); | ||||
| /* allow passive objects to return to original transform */ | |||||
| if (rbo->type == RBO_TYPE_PASSIVE) | if (ob->rotmode > 0) { | ||||
| RB_body_set_kinematic_state(rbo->physics_object, true); | eulO_to_quat(rbo->orn, ob->rot, ob->rotmode); | ||||
| RB_body_set_loc_rot(rbo->physics_object, rbo->pos, rbo->orn); | 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) | |||||
| } | } | ||||
| // 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) | // 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) | ||||
| } | } | ||||
| Context not available. | |||||
| rbw->pointcache->flag |= PTCACHE_OUTDATED; | rbw->pointcache->flag |= PTCACHE_OUTDATED; | ||||
| } | } | ||||
| /* ------------------ */ | /* ------------------ */ | ||||
| /* Rebuild rigid body world */ | /* Rebuild rigid body world */ | ||||
| Context not available. | |||||
| PointCache *cache; | PointCache *cache; | ||||
| PTCacheID pid; | PTCacheID pid; | ||||
| int startframe, endframe; | int startframe, endframe; | ||||
| int shards = 0, objects = 0, num = 0; | |||||
| BKE_ptcache_id_from_rigidbody(&pid, NULL, rbw); | BKE_ptcache_id_from_rigidbody(&pid, NULL, rbw); | ||||
| BKE_ptcache_id_time(&pid, scene, ctime, &startframe, &endframe, NULL); | BKE_ptcache_id_time(&pid, scene, ctime, &startframe, &endframe, NULL); | ||||
| cache = rbw->pointcache; | cache = rbw->pointcache; | ||||
| /* flag cache as outdated if we don't have a world or number of objects in the simulation has changed */ | /* 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)) { | num = rigidbody_group_count_items(&rbw->group->gobject, &shards, &objects); | ||||
| if (rbw->physics_world == NULL || rbw->numbodies != num) { | |||||
| cache->flag |= PTCACHE_OUTDATED; | cache->flag |= PTCACHE_OUTDATED; | ||||
| } | } | ||||
| Context not available. | |||||
| cache = rbw->pointcache; | cache = rbw->pointcache; | ||||
| if (ctime <= startframe) { | if (ctime <= startframe) { | ||||
| /* rebuild constraints */ | |||||
| rbw->rebuild_comp_con = true; | |||||
| rbw->ltime = startframe; | rbw->ltime = startframe; | ||||
| if ((rbw->object_changed)) | |||||
| { /* flag modifier refresh at their next execution XXX TODO -> still used ? */ | |||||
| rbw->refresh_modifiers = true; | |||||
| rbw->object_changed = false; | |||||
| rigidbody_update_simulation(scene, rbw, true); | |||||
| } | |||||
| return; | return; | ||||
| } | } | ||||
| /* make sure we don't go out of cache frame range */ | /* make sure we don't go out of cache frame range */ | ||||
| Context not available. | |||||
| /* don't try to run the simulation if we don't have a world yet but allow reading baked cache */ | /* 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)) | if (rbw->physics_world == NULL && !(cache->flag & PTCACHE_BAKED)) | ||||
| return; | return; | ||||
| else if (rbw->objects == NULL) | else if ((rbw->objects == NULL) || (rbw->cache_index_map == NULL)) | ||||
| rigidbody_update_ob_array(rbw); | rigidbody_update_ob_array(rbw); | ||||
| /* try to read from cache */ | /* try to read from cache */ | ||||
| // RB_TODO deal with interpolated, old and baked results | // RB_TODO deal with interpolated, old and baked results | ||||
| if (BKE_ptcache_read(&pid, ctime)) { | if (BKE_ptcache_read(&pid, ctime)) { | ||||
| Context not available. | |||||
| BKE_ptcache_write(&pid, startframe); | BKE_ptcache_write(&pid, startframe); | ||||
| } | } | ||||
| if (rbw->ltime > startframe) { | |||||
| rbw->rebuild_comp_con = false; | |||||
| } | |||||
| /* update and validate simulation */ | /* update and validate simulation */ | ||||
| rigidbody_update_simulation(scene, rbw, false); | rigidbody_update_simulation(scene, rbw, false); | ||||
| Context not available. | |||||
| # pragma GCC diagnostic ignored "-Wunused-parameter" | # pragma GCC diagnostic ignored "-Wunused-parameter" | ||||
| #endif | #endif | ||||
| void BKE_rigidbody_free_world(RigidBodyWorld *rbw) {} | void BKE_rigidbody_free_world(RigidBodyWorld *rbw) { | ||||
| void BKE_rigidbody_free_object(Object *ob) {} | } | ||||
| void BKE_rigidbody_free_constraint(Object *ob) {} | void BKE_rigidbody_free_object(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_free_constraint(Object *ob) { | ||||
| void BKE_rigidbody_relink_constraint(RigidBodyCon *rbc) {} | } | ||||
| void BKE_rigidbody_validate_sim_world(Scene *scene, RigidBodyWorld *rbw, bool rebuild) {} | struct RigidBodyOb *BKE_rigidbody_copy_object(Object *ob) { | ||||
| void BKE_rigidbody_calc_volume(Object *ob, float *r_vol) { if (r_vol) *r_vol = 0.0f; } | return NULL; | ||||
| void BKE_rigidbody_calc_center_of_mass(Object *ob, float r_com[3]) { zero_v3(r_com); } | } | ||||
| struct RigidBodyWorld *BKE_rigidbody_create_world(Scene *scene) { return NULL; } | struct RigidBodyCon *BKE_rigidbody_copy_constraint(Object *ob) { | ||||
| struct RigidBodyWorld *BKE_rigidbody_world_copy(RigidBodyWorld *rbw) { return NULL; } | return NULL; | ||||
| void BKE_rigidbody_world_groups_relink(struct RigidBodyWorld *rbw) {} | } | ||||
| struct RigidBodyOb *BKE_rigidbody_create_object(Scene *scene, Object *ob, short type) { return NULL; } | void BKE_rigidbody_relink_constraint(RigidBodyCon *rbc) { | ||||
| 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_validate_sim_world(Scene *scene, RigidBodyWorld *rbw, bool rebuild) { | ||||
| void BKE_rigidbody_remove_object(Scene *scene, Object *ob) {} | } | ||||
| void BKE_rigidbody_remove_constraint(Scene *scene, Object *ob) {} | void BKE_rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, short rebuild) { | ||||
| 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) {} | void BKE_rigidbody_validate_sim_constraint(RigidBodyWorld *rbw, Object *ob, short rebuild) { | ||||
| bool BKE_rigidbody_check_sim_running(RigidBodyWorld *rbw, float ctime) { return false; } | } | ||||
| void BKE_rigidbody_cache_reset(RigidBodyWorld *rbw) {} | void BKE_rigidbody_validate_sim_world(Scene *scene, RigidBodyWorld *rbw, short rebuild) { | ||||
| void BKE_rigidbody_rebuild_world(Scene *scene, float ctime) {} | } | ||||
| void BKE_rigidbody_do_simulation(Scene *scene, float ctime) {} | void BKE_rigidbody_calc_volume(Object *ob, float *r_vol) { | ||||
| if (r_vol) *r_vol = 0.0f; | |||||
| } | |||||
| void BKE_rigidbody_calc_center_of_mass(Object *ob, float r_com[3]) { | |||||
| zero_v3(r_com); | |||||
| } | |||||
| 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) { | |||||
| } | |||||
| 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; | |||||
| } | |||||
| void BKE_rigidbody_cache_reset(RigidBodyWorld *rbw) { | |||||
| } | |||||
| void BKE_rigidbody_rebuild_world(Scene *scene, float ctime) { | |||||
| } | |||||
| void BKE_rigidbody_do_simulation(Scene *scene, float ctime) { | |||||
| } | |||||
| #ifdef __GNUC__ | #ifdef __GNUC__ | ||||
| # pragma GCC diagnostic pop | # pragma GCC diagnostic pop | ||||
| Context not available. | |||||
This file contains a _lot_ of copypasted code. I can see why its done, but seems like a bad workaround for shards not being objects.
In some cases I found older versions of the functions have been copied, where the original versions have since been changed. (maybe fixed?), see: rigidbody_get_shape_trimesh_from_mesh_shard for an example.