diff options
Diffstat (limited to 'source/blender/editors/object')
-rw-r--r-- | source/blender/editors/object/object_intern.h | 5 | ||||
-rw-r--r-- | source/blender/editors/object/object_modifier.c | 1115 | ||||
-rw-r--r-- | source/blender/editors/object/object_ops.c | 6 |
3 files changed, 1124 insertions, 2 deletions
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 33f821126a2..89d8714dbca 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -160,6 +160,11 @@ void OBJECT_OT_correctivesmooth_bind(struct wmOperatorType *ot); void OBJECT_OT_meshdeform_bind(struct wmOperatorType *ot); void OBJECT_OT_explode_refresh(struct wmOperatorType *ot); void OBJECT_OT_ocean_bake(struct wmOperatorType *ot); +void OBJECT_OT_fracture_refresh(struct wmOperatorType *ot); +void OBJECT_OT_fracture_anim_bind(struct wmOperatorType *ot); +void OBJECT_OT_fracture_pack(wmOperatorType *ot); +void OBJECT_OT_rigidbody_convert_to_objects(struct wmOperatorType *ot); +void OBJECT_OT_rigidbody_convert_to_keyframes(struct wmOperatorType *ot); void OBJECT_OT_skin_root_mark(struct wmOperatorType *ot); void OBJECT_OT_skin_loose_mark_clear(struct wmOperatorType *ot); void OBJECT_OT_skin_radii_equalize(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index c7291076234..8e7a7a71906 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -36,6 +36,9 @@ #include "DNA_meshdata_types.h" #include "DNA_object_force_types.h" #include "DNA_scene_types.h" +#include "DNA_rigidbody_types.h" +#include "DNA_fracture_types.h" +#include "DNA_collection_types.h" #include "BLI_bitmap.h" #include "BLI_math.h" @@ -44,14 +47,17 @@ #include "BLI_string_utf8.h" #include "BLI_path_util.h" #include "BLI_utildefines.h" +#include "BLI_kdtree.h" #include "BKE_animsys.h" #include "BKE_curve.h" +#include "BKE_collection.h" #include "BKE_context.h" #include "BKE_DerivedMesh.h" #include "BKE_displist.h" #include "BKE_editmesh.h" #include "BKE_effect.h" +#include "BKE_fracture.h" #include "BKE_global.h" #include "BKE_key.h" #include "BKE_lattice.h" @@ -67,8 +73,14 @@ #include "BKE_ocean.h" #include "BKE_paint.h" #include "BKE_particle.h" +#include "BKE_pointcache.h" #include "BKE_report.h" +#include "BKE_softbody.h" +#include "BKE_editmesh.h" #include "BKE_scene.h" +#include "BKE_material.h" +#include "BKE_library.h" +#include "BKE_rigidbody.h" #include "BKE_softbody.h" #include "DEG_depsgraph.h" @@ -83,12 +95,19 @@ #include "ED_object.h" #include "ED_screen.h" #include "ED_mesh.h" +#include "ED_physics.h" +#include "ED_keyframing.h" +#include "ED_anim_api.h" //clean keyframes + +#include "UI_interface.h" #include "WM_api.h" #include "WM_types.h" #include "object_intern.h" +#include "PIL_time.h" + static void modifier_skin_customdata_delete(struct Object *ob); /******************************** API ****************************/ @@ -234,10 +253,10 @@ bool ED_object_iter_other( int totfound = include_orig ? 0 : 1; for (ob = bmain->object.first; ob && totfound < users; - ob = ob->id.next) + ob = ob->id.next) { if (((ob != orig_ob) || include_orig) && - (ob->data == orig_ob->data)) + (ob->data == orig_ob->data)) { if (callback(ob, callback_data)) return true; @@ -2383,3 +2402,1095 @@ void OBJECT_OT_surfacedeform_bind(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; edit_modifier_properties(ot); } + +static bool fracture_poll(bContext *C) +{ + return edit_modifier_poll_generic(C, &RNA_FractureModifier, 0); +} + +static int fracture_refresh_exec(bContext *C, wmOperator *op) +{ + Object *obact = ED_object_active_context(C); + Scene *scene = CTX_data_scene(C); + Main *bmain = CTX_data_main(C); + + float cfra = BKE_scene_frame_get(scene); + double start = 1.0; + FractureModifierData *rmd; + + rmd = (FractureModifierData *)modifiers_findByType(obact, eModifierType_Fracture); + if (!rmd) + return OPERATOR_CANCELLED; + + if (scene->rigidbody_world && scene->rigidbody_world->shared->pointcache) + { + RigidBodyWorld *rbw = scene->rigidbody_world; + if (BKE_rigidbody_check_sim_running(rbw, cfra) && + (rbw->ltime > rbw->shared->pointcache->startframe || rbw->ltime == rbw->shared->pointcache->endframe)) + { + BKE_report(op->reports, RPT_WARNING, "Please jump back to cache start frame in order to refracture"); + return OPERATOR_CANCELLED; + } + + BKE_rigidbody_cache_reset(scene); + } + + if (scene->rigidbody_world != NULL) + { + start = (double)scene->rigidbody_world->shared->pointcache->startframe; + //free a possible bake... + scene->rigidbody_world->shared->pointcache->flag &= ~PTCACHE_BAKED; + } + + if (!rmd || (rmd && (rmd->shared->flag & MOD_FRACTURE_REFRESH))) { + rmd->shared->flag &= ~MOD_FRACTURE_REFRESH; + return OPERATOR_CANCELLED; + } + + if (rmd->shared->anim_bind) { + MEM_freeN(rmd->shared->anim_bind); + rmd->shared->anim_bind = NULL; + rmd->shared->anim_bind_len = 0; + } + + BKE_scene_frame_set(scene, start); + + //add first rigidbody already here, seems to trigger an important depsgraph update + if (! obact->rigidbody_object) + { + ED_rigidbody_object_add(bmain, scene, obact, RBO_TYPE_ACTIVE, op->reports, false); + } + + rmd->shared->flag |= MOD_FRACTURE_REFRESH; + rmd->last_frame = 0; + + DEG_id_tag_update(&obact->id, ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM | ID_RECALC_ANIMATION | + ID_RECALC_COPY_ON_WRITE | ID_RECALC_BASE_FLAGS); + DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE | ID_RECALC_BASE_FLAGS); + + //DEG_id_tag_update(&obact->id, OB_RECALC_DATA | DEG_TAG_COPY_ON_WRITE); + //DEG_id_tag_update(&scene->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, obact); + WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, NULL); + + WM_event_add_notifier(C, NC_WINDOW, NULL); + WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING, NULL); + + return OPERATOR_FINISHED; +} + +static void apply_transform(Depsgraph *depsgraph, Object* ob, Scene* scene, float smat[3][3]) { + + float mat[4][4]; + + copy_m4_m3(mat, smat); + + /* apply to object data */ + if (ob->type == OB_MESH) { + Mesh *me = ob->data; + + multiresModifier_scale_disp(depsgraph, scene, ob); + + /* adjust data */ + BKE_mesh_transform(me, mat, true); + + /* update normals */ + BKE_mesh_calc_normals(me); + } + else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + float scale = 1.0f; + Curve *cu = ob->data; + + scale = mat3_to_scale(smat); + BKE_curve_transform_ex(cu, mat, true, true, scale); + } + else if (ob->type == OB_FONT) { + Curve *cu = ob->data; + int i; + float scale; + + scale = mat3_to_scale(smat); + + for (i = 0; i < cu->totbox; i++) { + TextBox *tb = &cu->tb[i]; + tb->x *= scale; + tb->y *= scale; + tb->w *= scale; + tb->h *= scale; + } + + cu->fsize *= scale; + } +} + +static void apply_scale(Depsgraph *depsgraph, Object* ob, Scene* scene) +{ + float smat[3][3]; + /*better apply scale prior to fracture, else shards get distorted*/ + BKE_object_scale_to_mat3(ob, smat); + + /* apply to object data */ + apply_transform(depsgraph, ob, scene, smat); + + /*clear scale too*/ + ob->scale[0] = ob->scale[1] = ob->scale[2] = 1.0f; +} + +static int fracture_refresh_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + Scene* scene = CTX_data_scene(C); + Object* ob = CTX_data_active_object(C); + Depsgraph* depsgraph = CTX_data_depsgraph(C); + + if (edit_modifier_invoke_properties(C, op)) + { + apply_scale(depsgraph, ob, scene); + return fracture_refresh_exec(C, op); + } + + return OPERATOR_CANCELLED; +} + +static int fracture_anim_bind_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *obact = ED_object_active_context(C); + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + FractureModifierData *rmd; + + rmd = (FractureModifierData *)modifiers_findByType(obact, eModifierType_Fracture); + if (!rmd) + return OPERATOR_CANCELLED; + + //restore kinematic here, before bind (and not afterwards TODO !) +#if 0 + if (scene && scene->rigidbody_world) { + BKE_restoreKinematic(scene->rigidbody_world, true); + BKE_rigidbody_rebuild_world(depsgraph, scene, scene->rigidbody_world->shared->pointcache->startframe); + } +#endif + BKE_fracture_animated_loc_rot(rmd, obact, true, depsgraph); + + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, NULL); + + DEG_id_tag_update(&obact->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, obact); + + return OPERATOR_FINISHED; +} + + +static int fracture_anim_bind_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (edit_modifier_invoke_properties(C, op)) + { + return fracture_anim_bind_exec(C, op); + } + + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_fracture_refresh(wmOperatorType *ot) +{ + ot->name = "Fracture Refresh"; + ot->description = "Refresh data in the Fracture modifier"; + ot->idname = "OBJECT_OT_fracture_refresh"; + + ot->poll = fracture_poll; + ot->invoke = fracture_refresh_invoke; + ot->exec = fracture_refresh_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); + + //RNA_def_boolean(ot->srna, "reset", false, "Reset Shards", + // "Reset all shards in next refracture, instead of keeping similar ones"); +} + +static void do_add_group_unchecked(Main *bmain, Collection* group, Object *ob) +{ + CollectionObject *go; + + go = MEM_callocN(sizeof(CollectionObject), "groupobject"); + BLI_addtail(&group->gobject, go); + go->ob = ob; + + //BKE_collection_object_add(bmain, group, ob); +} + +static bool do_unchecked_constraint_add(Main *bmain, Scene *scene, Object *ob, RigidBodyShardCon *con, ReportList *reports) +{ + RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene); + + /* check that object doesn't already have a constraint */ + if (ob->rigidbody_constraint) { + BKE_reportf(reports, RPT_INFO, "Object '%s' already has a Rigid Body Constraint", ob->id.name + 2); + return false; + } + /* create constraint group if it doesn't already exits */ + if (rbw->constraints == NULL) { + rbw->constraints = BKE_collection_add(bmain, NULL, "RigidBodyConstraints"); + } + /* make rigidbody constraint settings */ + ob->rigidbody_constraint = BKE_rigidbody_create_constraint(scene, ob, con->type, con); + ob->rigidbody_constraint->flag |= RBC_FLAG_NEEDS_VALIDATE; + + /* add constraint to rigid body constraint group */ + do_add_group_unchecked(bmain, rbw->constraints, ob); + + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + return true; +} + +static Object* do_convert_meshisland_to_object(Main* bmain, Shard *mi, Scene* scene, Collection* g, Object* ob, + RigidBodyWorld *rbw, int i, Object*** objs, KDTree **objtree, + ViewLayer *layer) +{ + Mesh* me; + ModifierData *md; + bool foundFracture = false; + Object* ob_new = NULL; + MVert* mv = NULL; + int v = 0; + + char *name = mi->name[0] != '\0' ? BLI_strdupn(mi->name, MAX_ID_NAME) : BLI_strdupcat(ob->id.name + 2, "_shard"); + + ob_new = BKE_object_add(bmain, scene, layer, OB_MESH, name); + //BKE_collection_object_remove(bmain, layer->active_collection->collection, ob_new, false); + + { //TODO, this still necessary ? + copy_m4_m4(ob_new->obmat, ob->obmat); + copy_v3_v3(ob_new->rot, ob->rot); + copy_qt_qt(ob_new->quat, ob->quat); + copy_v3_v3(ob_new->rotAxis, ob->rotAxis); + ob_new->rotAngle = ob->rotAngle; + copy_v3_v3(ob_new->scale, ob->scale); + } + + if (rbw) { + rbw->shared->pointcache->flag |= PTCACHE_OUTDATED; + /* make rigidbody object settings */ + if (ob_new->rigidbody_object == NULL) { + ob_new->rigidbody_object = BKE_rigidbody_create_object(scene, ob_new, RBO_TYPE_ACTIVE, mi); + } + //ob_new->rigidbody_object->type = RBO_TYPE_ACTIVE; + ob_new->rigidbody_object->flag |= RBO_FLAG_NEEDS_VALIDATE; + + /* add object to rigid body group */ + do_add_group_unchecked(bmain, rbw->group, ob_new); + + DEG_id_tag_update(&ob_new->id, ID_RECALC_ALL); + } + + do_add_group_unchecked(bmain, g, ob_new); + + /* throw away all modifiers before fracture, result is stored inside it */ + while (ob_new->modifiers.first != NULL) { + md = ob_new->modifiers.first; + if (md->type == eModifierType_Fracture) { + /*remove fracture itself too*/ + foundFracture = true; + BLI_remlink(&ob_new->modifiers, md); + modifier_free(md); + md = NULL; + } + else if (!foundFracture) { + BLI_remlink(&ob_new->modifiers, md); + modifier_free(md); + md = NULL; + } + /* XXX else keep following modifiers, or apply them ? */ + } + + assign_matarar(bmain, ob_new, give_matarar(ob), *give_totcolp(ob)); + + ob_new->data = BKE_mesh_copy(bmain, mi->mesh);//BKE_fracture_mesh_copy(mi->mesh, ob); + me = (Mesh*)ob_new->data; + me->edit_mesh = NULL; + + //correct vertex positions, they are off by centroid location + for (mv = me->mvert, v = 0; v < me->totvert; v++, mv++) + { + sub_v3_v3(mv->co, mi->loc); + } + + { + //here we seem to need the "Impact_size..." hmmm that was a hack to store something else inside + //Shard *s = BLI_findlink(&fmd->shared->frac_mesh->shard_map, mi->id); + //if (s) + { + float loc[3], rot[4], mat[4][4], size[3] = {1.0f, 1.0f, 1.0f}; + + mat4_to_loc_quat(loc, rot, ob->obmat); + add_v3_v3(loc, mi->rigidbody->pos); + //copy_v3_v3(size, s->impact_size); + + mul_qt_qtqt(rot, rot, mi->rot); + //mi->rot -> initial rotation, but since they cant be rotated independently from object, its unit_qt here + //except fore external mode... there it can be set, so in general use it... since we dont want separate external + //mode any more + + copy_v3_v3(ob_new->scale, size); + copy_v3_v3(ob_new->loc, loc); + copy_qt_qt(ob_new->quat, rot); + quat_to_eulO(ob_new->rot, ob_new->rotmode, rot); + quat_to_axis_angle(ob_new->rotAxis, &ob_new->rotAngle, rot); + + loc_quat_size_to_mat4(mat, loc, rot, size); + copy_m4_m4(ob_new->obmat, mat); + invert_m4_m4(ob_new->imat, ob_new->obmat); + } + } + + /*store obj indexes in kdtree and objs in array*/ + BLI_kdtree_insert(*objtree, i, mi->loc); + (*objs)[i] = ob_new; + + BKE_rigidbody_remove_shard(scene, mi); + + return ob_new; +} + +static Object* do_convert_constraints(Main* bmain, FractureModifierData *fmd, RigidBodyShardCon* con, + Scene* scene, Object* ob, KDTree *objtree, Object **objs, + float max_con_mass, ReportList* reports, ViewLayer *layer) +{ + int index1 = BLI_kdtree_find_nearest(objtree, con->mi1->loc, NULL); + int index2 = BLI_kdtree_find_nearest(objtree, con->mi2->loc, NULL); + Object* ob1 = objs[index1]; + Object* ob2 = objs[index2]; + char *name; + Object* rbcon; + int iterations; + + if (con->name[0] != '\0') + { + name = BLI_strdupn(con->name, MAX_ID_NAME); + } + else + { + name = BLI_strdupcat(ob->id.name + 2, "_con"); + } + + rbcon = BKE_object_add(bmain, scene, layer, OB_EMPTY, name); + //if (fmd->fracture_mode == MOD_FRACTURE_EXTERNAL) + { + rbcon->rotmode = ROT_MODE_QUAT; + } + + if (fmd->solver_iterations_override == 0) { + iterations = scene->rigidbody_world->num_solver_iterations; + } + else { + iterations = fmd->solver_iterations_override; + } + + if (iterations > 0) { + con->flag |= RBC_FLAG_OVERRIDE_SOLVER_ITERATIONS; + con->num_solver_iterations = iterations; + } + + if ((fmd->flag & MOD_FRACTURE_USE_MASS_DEP_THRESHOLDS)) { + BKE_rigidbody_calc_threshold(max_con_mass, fmd, con); + } + + + { + /*TODO XXX, take own transform into account too*/ + float loc[3], rot[4], mat[4][4], size[3] = {1.0f, 1.0f, 1.0f}; + + mat4_to_quat(rot, ob->obmat); + mul_v3_m4v3(loc, ob->obmat, con->pos); + mul_qt_qtqt(rot, rot, con->orn); + + copy_v3_v3(rbcon->loc, loc); + copy_qt_qt(rbcon->quat, rot); + quat_to_eulO(rbcon->rot, rbcon->rotmode, rot); + quat_to_axis_angle(rbcon->rotAxis, &rbcon->rotAngle, rot); + + loc_quat_size_to_mat4(mat, loc, rot, size); + copy_m4_m4(rbcon->obmat, mat); + + + if (fmd->flag & MOD_FRACTURE_USE_MASS_DEP_THRESHOLDS) { + BKE_rigidbody_calc_threshold(max_con_mass, fmd, con); + } + } + + /*omit check for existing objects in group, since this seems very slow, and should not be necessary in this internal function*/ + do_unchecked_constraint_add(bmain, scene, rbcon, con, reports); + + rbcon->rigidbody_constraint->ob1 = ob1; + rbcon->rigidbody_constraint->ob2 = ob2; + + if (con->flag & RBC_FLAG_OVERRIDE_SOLVER_ITERATIONS) { + rbcon->rigidbody_constraint->flag |= RBC_FLAG_OVERRIDE_SOLVER_ITERATIONS; + rbcon->rigidbody_constraint->num_solver_iterations = iterations; + } + + BKE_rigidbody_remove_shard_con(scene->rigidbody_world, con); + + return rbcon; +} + +static void convert_modifier_to_objects(Main* bmain, ReportList *reports, Scene* scene, Object* ob, + FractureModifierData *rmd, ViewLayer *layer) +{ + Shard *mi; + RigidBodyShardCon* con; + int i = 0; + RigidBodyWorld *rbw = scene->rigidbody_world; + const char *name = BLI_strdupcat(ob->id.name, "_conv"); + Collection *g = BKE_collection_add(bmain, scene->master_collection, name); + + int count = BLI_listbase_count(&rmd->shared->shards); + KDTree* objtree = BLI_kdtree_new(count); + Object** objs = MEM_callocN(sizeof(Object*) * count, "convert_objs"); + float max_con_mass = 0; + + /*use a common array for both constraints and objects ! */ + double start; + + rmd->shared->flag &= ~MOD_FRACTURE_REFRESH; + MEM_freeN((void*)name); + + if (rbw) + rbw->shared->pointcache->flag |= PTCACHE_OUTDATED; + + start = PIL_check_seconds_timer(); + + for (mi = rmd->shared->shards.first; mi; mi = mi->next) { + do_convert_meshisland_to_object(bmain, mi, scene, g, ob, rbw, i, &objs, &objtree, layer); + i++; + } + + printf("Converting Islands to Objects done, %g\n", PIL_check_seconds_timer() - start); + + BLI_kdtree_balance(objtree); + + /* go through constraints and find objects by position + * constrain them with regular constraints */ + + if (rmd->flag & MOD_FRACTURE_USE_MASS_DEP_THRESHOLDS) { + max_con_mass = BKE_rigidbody_calc_max_con_mass(ob); + } + + /* now do scene trick again for constraints */ + + start = PIL_check_seconds_timer(); + + if (rmd->flag & MOD_FRACTURE_USE_CONSTRAINTS) + { + for (con = rmd->shared->constraints.first; con; con = con->next) { + do_convert_constraints(bmain, rmd, con, scene, ob, objtree, objs, max_con_mass, reports, layer); + i++; + } + } + + printf("Converting Constraints to Objects done, %g\n", PIL_check_seconds_timer() - start); + + /* free array and kdtree*/ + MEM_freeN(objs); + BLI_kdtree_free(objtree); +} + +static int rigidbody_convert_exec(bContext *C, wmOperator *op) +{ + Object *obact = ED_object_active_context(C); + Scene *scene = CTX_data_scene(C); + Main* bmain = CTX_data_main(C); + ViewLayer *layer = CTX_data_view_layer(C); + float cfra = BKE_scene_frame_get(scene); + FractureModifierData *rmd; + + rmd = (FractureModifierData *)modifiers_findByType(obact, eModifierType_Fracture); + if (rmd && (rmd->shared->flag & MOD_FRACTURE_REFRESH)) { + return OPERATOR_CANCELLED; + } + + if (!rmd || (scene->rigidbody_world && cfra != scene->rigidbody_world->shared->pointcache->startframe)) + { + BKE_report(op->reports, RPT_WARNING, "Unable to convert to objects, either no Fracture Modifier present or not on startframe" ); + return OPERATOR_CANCELLED; + } + + convert_modifier_to_objects(bmain, op->reports, scene, obact, rmd, layer); + +#if 0 + if (rbw) { + /* flatten the cache and throw away all traces of the modifiers */ + short steps_per_second = rbw->steps_per_second; + short num_solver_iterations = rbw->num_solver_iterations; + int flag = rbw->flag; + float time_scale = rbw->time_scale; + struct Collection* constraints = rbw->constraints; + struct Collection* group = rbw->group; + RigidBodyWorld *rbwn = NULL; + + BKE_rigidbody_cache_reset(scene); + BKE_rigidbody_free_world(scene); + scene->rigidbody_world = NULL; + rbwn = BKE_rigidbody_create_world(scene); + rbwn->time_scale = time_scale; + rbwn->flag = flag | RBW_FLAG_NEEDS_REBUILD; + rbwn->num_solver_iterations = num_solver_iterations; + rbwn->steps_per_second = steps_per_second; + rbwn->group = group; + rbwn->constraints = constraints; + + scene->rigidbody_world = rbwn; + } +#endif + +#if 0 + if (rmd->fracture_mode == MOD_FRACTURE_EXTERNAL) + { + ED_object_base_free_and_unlink(bmain, scene, obact); + } +#endif + + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); + + return OPERATOR_FINISHED; +} + +static int rigidbody_convert_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + + if (edit_modifier_invoke_properties(C, op)) + return rigidbody_convert_exec(C, op); + else + return OPERATOR_CANCELLED; +} + + +void OBJECT_OT_rigidbody_convert_to_objects(wmOperatorType *ot) +{ + ot->name = "RigidBody Convert To Objects"; + ot->description = "Convert the Rigid Body modifier shards to real objects"; + ot->idname = "OBJECT_OT_rigidbody_convert_to_objects"; + + ot->poll = fracture_poll; + ot->invoke = rigidbody_convert_invoke; + ot->exec = rigidbody_convert_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); +} + +#if 0 +static bAnimContext* make_anim_context(Scene* scene, Object* ob) +{ + bAnimContext *ac = MEM_callocN(sizeof(bAnimContext), "make_anim_context"); + + /* craft a dummy animcontext here, sigh... why cant we just pass the object and action ? */ + ac->scene = scene; + if (scene) { + ac->markers = &scene->markers; + ac->obact = ob; + } + + ac->sa = NULL; + ac->ar = NULL; + ac->sl = NULL; + ac->spacetype = 0; + ac->regiontype = 0; + + if (ob->adt) + { + ac->data = ob->adt->action; + ac->datatype = ANIMCONT_ACTION; + ac->mode = SACTCONT_ACTION; + } + + return ac; +} +#endif + +MINLINE void div_v3_v3(float r[3], const float a[3], const float b[3]) +{ + if (b[0] != 0) + r[0] = a[0] / b[0]; + else + r[0] = a[0]; + + if (b[1] != 0) + r[1] = a[1] / b[1]; + else + r[1] = a[1]; + + if (b[2] != 0) + r[2] = a[2] / b[2]; + else + r[2] = a[2]; +} + +MINLINE void div_v4_v4(float r[4], const float a[4], const float b[4]) +{ + if (b[0] != 0) + r[0] = a[0] / b[0]; + else + r[0] = a[0]; + + if (b[1] != 0) + r[1] = a[1] / b[1]; + else + r[1] = a[1]; + + if (b[2] != 0) + r[2] = a[2] / b[2]; + else + r[2] = a[2]; + + if (b[3] != 0) + r[3] = a[3] / b[3]; + else + r[3] = a[3]; +} + +MINLINE void compare_v3_fl(bool r[3], const float a[3], const float b) +{ + r[0] = fabsf(1.0f - a[0]) > b; + r[1] = fabsf(1.0f - a[1]) > b; + r[2] = fabsf(1.0f - a[2]) > b; +} + +MINLINE void compare_v4_fl(bool r[4], const float a[4], const float b) +{ + r[0] = fabsf(1.0f - a[0]) > b; + r[1] = fabsf(1.0f - a[1]) > b; + r[2] = fabsf(1.0f - a[2]) > b; + r[3] = fabsf(1.0f - a[3]) > b; +} + +static Object* do_convert_meshIsland(Main* bmain, Depsgraph *depsgraph, Shard *mi, + Collection* gr, Object* ob, Scene* scene, int start, int end, int count, + Object* parent,bool is_baked, float obloc[3], + float diff[3], int *j, float threshold, int step, bool calc_handles, + ReportList* reports, ViewLayer* layer, bContext* C) +{ + int i = 0; + Object* ob_new = NULL; + Mesh* me; + float cent[3]; + float prevloc[3] = {0, 0, 0}; + float prevploc[3] = {0, 0, 0}; + float prevrot[4] = {0, 0, 0, 0}; + float prevprot[4] = {0, 0, 0, 0}; + bool adaptive = threshold > 0; + + char *name = BLI_strdupcat(ob->id.name + 2, "_key"); + + ob_new = BKE_object_add(bmain, scene, layer, OB_MESH, name); + //BKE_collection_object_remove(bmain, layer->active_collection->collection, ob_new, false); + + //this and keyframing quats hopefully solves the sudden rotation / gimbal lock (?) issue + ob_new->rotmode = ROT_MODE_QUAT; + + ED_object_parent_set(NULL, C, scene, ob_new, parent, PAR_OBJECT, false, false, NULL); + + assign_matarar(bmain, ob_new, give_matarar(ob), *give_totcolp(ob)); + + do_add_group_unchecked(bmain, gr, ob_new); + + ob_new->data = BKE_fracture_mesh_copy(mi->mesh, ob); + me = (Mesh*)ob_new->data; + me->edit_mesh = NULL; + + //last parameter here means deferring removing the bake after all has been converted. + ED_rigidbody_object_add(bmain, scene, ob_new, RBO_TYPE_ACTIVE, reports, true); + ob_new->rigidbody_object->flag |= RBO_FLAG_KINEMATIC; + ob_new->rigidbody_object->mass = mi->rigidbody->mass; + + /*set origin to centroid*/ + copy_v3_v3(cent, mi->loc); + mul_m4_v3(ob_new->obmat, cent); + copy_v3_v3(ob_new->loc, cent); + + { + if (start < mi->startframe) { + start = mi->startframe; + } + + if (end > mi->endframe) { + end = mi->endframe; + } + } + + if (mi->rigidbody->type == RBO_TYPE_ACTIVE) + { + //bAnimContext *ac; + int stepp = adaptive ? 1 : step; + short flag = calc_handles ? 0 : INSERTKEY_FAST | INSERTKEY_NO_USERPREF | INSERTKEY_NEEDED; + for (i = start; i < end; i += stepp) + { + char handle = U.keyhandles_new; + char interp = U.ipo_new; + bool dostep = adaptive ? (((i + start) % step == 0) || i == start || i == end) : true; + float size[3] = {1, 1, 1}; + bool locset[3] = {true, true, true}; + bool rotset[4] = {true, true, true, true}; + + if (dostep) { + //if adaptive and step is on same frame, prefer adaptive vector handle + U.keyhandles_new = adaptive ? HD_VECT : HD_AUTO; + U.ipo_new = BEZT_IPO_BEZ; + } + else if (adaptive && !dostep) { + U.keyhandles_new = HD_VECT; + U.ipo_new = BEZT_IPO_BEZ; + } + + //adaptive optimization, only try to insert necessary frames... but doesnt INSERT_NEEDED do the same ?! + if (adaptive || !dostep) + { + locset[0] = false; + locset[1] = false; + locset[2] = false; + + rotset[0] = false; + rotset[1] = false; + rotset[2] = false; + rotset[3] = false; + } + + copy_v3_v3(size, ob->scale); + + if (adaptive || dostep) + { + float loc[3] = {0.0f, 0.0f, 0.0f}, rot[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float mat[4][4]; + + //is there a bake, if yes... use that (disabled for now, odd probs...) + if (is_baked) + { + { + int x = i - start; + + loc[0] = mi->locs[x * 3]; + loc[1] = mi->locs[x * 3+1]; + loc[2] = mi->locs[x * 3+2]; + + rot[0] = mi->rots[x * 4]; + rot[1] = mi->rots[x * 4+1]; + rot[2] = mi->rots[x * 4+2]; + rot[3] = mi->rots[x * 4+3]; + + + mul_qt_qtqt(rot, rot, mi->rot); + + if (i >= start + 1) + { + //taken from rigidbody.py + // make quaternion compatible with the previous one + //if q1.dot(q2) < 0.0: + // obj.rotation_quaternion = -q2 + //else: + // obj.rotation_quaternion = q2 + + if (dot_qtqt(prevrot, rot) < 0.0) { + negate_v4(rot); + } + } + + if (adaptive) + { + float diffloc[3], diffploc[3], diffrot[4], diffprot[4], difflq[3], diffrq[4]; + if (i >= start + 2) + { + sub_v3_v3v3(diffploc, prevploc, prevloc); + sub_v3_v3v3(diffloc, prevloc, loc); + div_v3_v3(difflq, diffploc, diffloc); + compare_v3_fl(locset, difflq, threshold); + + sub_v4_v4v4(diffprot, prevprot, prevrot); + sub_v4_v4v4(diffrot, prevrot, rot); + div_v4_v4(diffrq, diffprot, diffrot); + compare_v4_fl(rotset, diffrq, threshold); + } + + if (i >= start + 1) + { + copy_v3_v3(prevploc, prevloc); + copy_v4_v4(prevprot, prevrot); + } + + copy_v3_v3(prevloc, loc); + copy_v4_v4(prevrot, rot); + } + + if (dostep) + { + locset[0] = true; + locset[1] = true; + locset[2] = true; + + rotset[0] = true; + rotset[1] = true; + rotset[2] = true; + rotset[3] = true; + } + } + } + + sub_v3_v3(loc, obloc); + add_v3_v3(loc, diff); + + loc_quat_size_to_mat4(mat, loc, rot, size); + + if (locset[0] || locset[1] || locset[2] || rotset[0] || rotset[1] || rotset[2] || rotset[3]) { + BKE_scene_frame_set(scene, (double)i); + } + + copy_m4_m4(ob_new->obmat, mat); + + copy_v3_v3(ob_new->loc, loc); + copy_qt_qt(ob_new->quat, rot); + + quat_to_compatible_eul(ob_new->rot, ob_new->rot, rot); + copy_v3_v3(ob_new->scale, size); + } + + if (locset[0]) + insert_keyframe(bmain, depsgraph, NULL, (ID*)ob_new, NULL, "Location", "location", 0, i, BEZT_KEYTYPE_KEYFRAME, NULL, flag); + + if (locset[1]) + insert_keyframe(bmain, depsgraph, NULL, (ID*)ob_new, NULL, "Location", "location", 1, i, BEZT_KEYTYPE_KEYFRAME, NULL, flag); + + if (locset[2]) + insert_keyframe(bmain, depsgraph, NULL, (ID*)ob_new, NULL, "Location", "location", 2, i, BEZT_KEYTYPE_KEYFRAME, NULL, flag); + + if (rotset[0]) + insert_keyframe(bmain, depsgraph, NULL, (ID*)ob_new, NULL, "Rotation", "rotation_quaternion", 0, i, BEZT_KEYTYPE_KEYFRAME, NULL, flag); + + if (rotset[1]) + insert_keyframe(bmain, depsgraph, NULL, (ID*)ob_new, NULL, "Rotation", "rotation_quaternion", 1, i, BEZT_KEYTYPE_KEYFRAME, NULL, flag); + + if (rotset[2]) + insert_keyframe(bmain, depsgraph, NULL, (ID*)ob_new, NULL, "Rotation", "rotation_quaternion", 2, i, BEZT_KEYTYPE_KEYFRAME, NULL, flag); + + if (rotset[3]) + insert_keyframe(bmain, depsgraph, NULL, (ID*)ob_new, NULL, "Rotation", "rotation_quaternion", 3, i, BEZT_KEYTYPE_KEYFRAME, NULL, flag); + + //restore values + U.keyhandles_new = handle; + U.ipo_new = interp; + } + + //mi->frame_count = 0; + //mi->start_frame = cache->startframe; + + /*if (i + step > end || i == end) + { + ac = make_anim_context(scene, ob_new); + clean_action_keys(ac, threshold, false); + MEM_freeN(ac); + }*/ + } + else + { + printf("Skipping keyframing for passive shard...\n"); + + mul_m4_v3(ob->obmat, ob_new->loc); + sub_v3_v3(ob_new->loc, obloc); + add_v3_v3(ob_new->loc, diff); + + copy_qt_qt(ob_new->quat, ob->quat); + + mul_qt_qtqt(ob_new->quat, ob_new->quat, mi->rot); + + copy_v3_v3(ob_new->rot, ob->rot); + copy_v3_v3(ob_new->scale, ob->scale); + } + + (*j)++; + printf("Converted %d from %d objects....\n", *j, count); + + return ob_new; +} + +static bool convert_modifier_to_keyframes(bContext *C, FractureModifierData* fmd, Collection* gr, Object* ob, Scene* scene, + int start, int end, float threshold, int step, bool calc_handles, + ReportList *reports) +{ + ViewLayer *layer = CTX_data_view_layer(C); + Main* bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + + bool is_baked = false; + Shard *mi = NULL; + Object *parent = NULL; + int count = BLI_listbase_count(&fmd->shared->shards); + char *name = BLI_strdupcat(ob->id.name + 2, "_p_key"); + float diff[3] = {0.0f, 0.0f, 0.0f}; + float obloc[3]; + int k = 0; + double starttime; + + is_baked = true; + + parent = BKE_object_add(bmain, scene, layer, OB_EMPTY, name); + BKE_mesh_center_of_surface(ob->data, obloc); + copy_v3_v3(parent->loc, ob->loc); + sub_v3_v3v3(diff, obloc, parent->loc); + + starttime = PIL_check_seconds_timer(); + for (mi = fmd->shared->shards.first; mi; mi = mi->next) + { + do_convert_meshIsland(bmain, depsgraph, mi, gr, ob, scene, start, end, count, + parent, is_baked, obloc, diff, &k, + threshold, step, calc_handles, reports, layer, C); + } + printf("Converting Islands to Keyframed Objects done, %g\n", PIL_check_seconds_timer() - starttime); + + BKE_scene_frame_set(scene, (double)start); + + return true; +} + +static int rigidbody_convert_keyframes_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Main *bmain = CTX_data_main(C); + Collection *gr = NULL; + FractureModifierData* rmd = NULL; + PointCache* cache = NULL; + + if (scene && scene->rigidbody_world) + { + cache = scene->rigidbody_world->shared->pointcache; + } + + if (cache) + { + float threshold = RNA_float_get(op->ptr, "threshold"); + int start = RNA_int_get(op->ptr, "start_frame"); + int end = RNA_int_get(op->ptr, "end_frame"); + int step = RNA_int_get(op->ptr, "step"); + bool calc_handles = RNA_boolean_get(op->ptr, "calc_handles"); + int frame = 0; + + //fill conversion array cache + if (cache->startframe > start) + { + start = cache->startframe; + } + + if (cache->endframe < end) + { + end = cache->endframe; + } + + CTX_DATA_BEGIN(C, Object *, selob, selected_objects) + { + rmd = (FractureModifierData *)modifiers_findByType(selob, eModifierType_Fracture); + if (rmd && (rmd->shared->flag & MOD_FRACTURE_REFRESH)) { + return OPERATOR_CANCELLED; + } + + //force a transform sync, gah + if (cache->flag & PTCACHE_BAKED || !(cache->flag & PTCACHE_OUTDATED)) + { + for (frame = start; frame < end; frame++) + { + BKE_scene_frame_set(scene, (float)frame); + BKE_rigidbody_do_simulation(depsgraph, scene, (float)frame); + BKE_object_where_is_calc_time(depsgraph, scene, selob, (float)frame); + } + } + + if (rmd) { + int count = BLI_listbase_count(&rmd->shared->shards); + + if (count == 0) + { + //gaaah, undo frees sim data, rebuild.... + rmd->shared->flag |= MOD_FRACTURE_REFRESH; + makeDerivedMesh(depsgraph, scene, selob, NULL, scene->customdata_mask | CD_MASK_BAREMESH, 0); + count = BLI_listbase_count(&rmd->shared->shards); + + if (count == 0) + { + BKE_report(op->reports, RPT_WARNING, "No meshislands found, please execute fracture and simulate first"); + return OPERATOR_CANCELLED; + } + } + + gr = BKE_collection_add(bmain, NULL, "Converted"); + convert_modifier_to_keyframes(C, rmd, gr, selob, scene, start, end, threshold, step, calc_handles, op->reports); + } + } + + CTX_DATA_END; + + //free a possible bake... because we added new rigidbodies, and this would mess up the mesh + if (scene->rigidbody_world && scene->rigidbody_world->shared->pointcache) { + scene->rigidbody_world->shared->pointcache->flag &= ~PTCACHE_BAKED; + } + + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, NULL); + } + + return OPERATOR_FINISHED; +} + +static int rigidbody_convert_keyframes_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + return WM_operator_props_dialog_popup(C, op, 10 * UI_UNIT_X, 5 * UI_UNIT_Y); +} + +void OBJECT_OT_rigidbody_convert_to_keyframes(wmOperatorType *ot) +{ + + ot->name = "Convert To Keyframed Objects"; + ot->description = "Convert the Rigid Body modifier shards to keyframed real objects"; + ot->idname = "OBJECT_OT_rigidbody_convert_to_keyframes"; + + //ot->poll = fracture_poll; + ot->exec = rigidbody_convert_keyframes_exec; + ot->invoke = rigidbody_convert_keyframes_invoke; + + RNA_def_int(ot->srna, "start_frame", 1, 0, 300000, "Start Frame", "", 0, 300000); + RNA_def_int(ot->srna, "end_frame", 250, 0, 300000, "End Frame", "", 0, 300000); + RNA_def_int(ot->srna, "step", 1, 1, 10000, "Step", "", 1, 10000); + RNA_def_float(ot->srna, "threshold", 0.2f, 0.0f, FLT_MAX, "Threshold", "Ratio of change in location or rotation which has to be exceeded to set a keyframe", + 0.0f, 1000.0f); + RNA_def_boolean(ot->srna, "calc_handles", false, "Handles", + "Smoothes step handles and makes adaptive handles vector, useful in conjunction with large step size, very slow with small step size"); + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + //edit_modifier_properties(ot); +} + +void OBJECT_OT_fracture_anim_bind(wmOperatorType *ot) +{ + ot->name = "Fracture Animated Mesh Bind"; + ot->description = "Bind animated mesh vertices to mesh islands in Fracture Modifier"; + ot->idname = "OBJECT_OT_fracture_anim_bind"; + + ot->poll = fracture_poll; + ot->invoke = fracture_anim_bind_invoke; + ot->exec = fracture_anim_bind_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); +} + diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 024fa24480a..940450aa161 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -160,6 +160,12 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_explode_refresh); WM_operatortype_append(OBJECT_OT_ocean_bake); + /* fracture modifier */ + WM_operatortype_append(OBJECT_OT_fracture_refresh); + WM_operatortype_append(OBJECT_OT_fracture_anim_bind); + WM_operatortype_append(OBJECT_OT_rigidbody_convert_to_objects); + WM_operatortype_append(OBJECT_OT_rigidbody_convert_to_keyframes); + WM_operatortype_append(OBJECT_OT_constraint_add); WM_operatortype_append(OBJECT_OT_constraint_add_with_targets); WM_operatortype_append(POSE_OT_constraint_add); |