Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/object/object_modifier.c
| Context not available. | |||||
| #include "DNA_meshdata_types.h" | #include "DNA_meshdata_types.h" | ||||
| #include "DNA_object_force.h" | #include "DNA_object_force.h" | ||||
| #include "DNA_scene_types.h" | #include "DNA_scene_types.h" | ||||
| #include "DNA_rigidbody_types.h" | |||||
| #include "DNA_fracture_types.h" | |||||
| #include "BLI_bitmap.h" | #include "BLI_bitmap.h" | ||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| Context not available. | |||||
| #include "BLI_string_utf8.h" | #include "BLI_string_utf8.h" | ||||
| #include "BLI_path_util.h" | #include "BLI_path_util.h" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BLI_kdtree.h" | |||||
| #include "BKE_animsys.h" | #include "BKE_animsys.h" | ||||
| #include "BKE_curve.h" | #include "BKE_curve.h" | ||||
| Context not available. | |||||
| #include "BKE_particle.h" | #include "BKE_particle.h" | ||||
| #include "BKE_softbody.h" | #include "BKE_softbody.h" | ||||
| #include "BKE_editmesh.h" | #include "BKE_editmesh.h" | ||||
| #include "BKE_scene.h" | |||||
| #include "BKE_material.h" | |||||
| #include "BKE_library.h" | |||||
| #include "BKE_rigidbody.h" | |||||
| #include "BKE_group.h" | |||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| Context not available. | |||||
| #include "ED_object.h" | #include "ED_object.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "ED_mesh.h" | #include "ED_mesh.h" | ||||
| #include "ED_physics.h" | |||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| #include "WM_types.h" | #include "WM_types.h" | ||||
| Context not available. | |||||
| Object *ob = ED_object_active_context(C); | Object *ob = ED_object_active_context(C); | ||||
| ModifierData *md = edit_modifier_property_get(op, ob, 0); | ModifierData *md = edit_modifier_property_get(op, ob, 0); | ||||
| int mode_orig = ob->mode; | int mode_orig = ob->mode; | ||||
| //if we have a running fracture job, dont remove the modifier | |||||
| if (md && md->type == eModifierType_Fracture) | |||||
sergey: That's just delaying the headache. No data available for RW should be shared between threads. | |||||
| { | |||||
| FractureModifierData* fmd = (FractureModifierData*)md; | |||||
| if (fmd->execute_threaded && fmd->frac_mesh && fmd->frac_mesh->running == 1) | |||||
| { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| } | |||||
| if (!md || !ED_object_modifier_remove(op->reports, bmain, ob, md)) | if (!md || !ED_object_modifier_remove(op->reports, bmain, ob, md)) | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| Context not available. | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; | ||||
| edit_modifier_properties(ot); | edit_modifier_properties(ot); | ||||
| } | } | ||||
| /****************** rigidbody modifier refresh operator *********************/ | |||||
| typedef struct FractureJob { | |||||
| /* from wmJob */ | |||||
| void *owner; | |||||
| short *stop, *do_update; | |||||
| float *progress; | |||||
| int current_frame, total_progress; | |||||
| struct FractureModifierData *fmd; | |||||
| struct Object* ob; | |||||
| struct Scene* scene; | |||||
| } FractureJob; | |||||
| static void fracture_free(void *customdata) | |||||
| { | |||||
| FractureJob *fj = customdata; | |||||
| MEM_freeN(fj); | |||||
| } | |||||
| static int fracture_breakjob(void *customdata) | |||||
| { | |||||
| /* FractureJob *fj = (FractureJob *)customdata; | |||||
| * return *(fj->stop); */ | |||||
| return G.is_break; /* a workaround solution */ | |||||
| } | |||||
| static float fracture_update(void *customdata) | |||||
| { | |||||
| FractureJob *fj = customdata; | |||||
| float progress; | |||||
| if (fj->fmd->frac_mesh == NULL) | |||||
| return 0.0f; | |||||
| if (fracture_breakjob(fj)) | |||||
| fj->fmd->frac_mesh->cancel = 1; | |||||
| /* *(fj->do_update) = true; useless here...*/ | |||||
| progress = (float)(fj->fmd->frac_mesh->progress_counter) / (float)(fj->total_progress); | |||||
| return progress; | |||||
| } | |||||
| static void fracture_startjob(void *customdata, short *stop, short *do_update, float *progress) | |||||
| { | |||||
| FractureJob *fj = customdata; | |||||
| FractureModifierData *fmd = fj->fmd; | |||||
| Object *ob = fj->ob; | |||||
| Scene* scene = fj->scene; | |||||
| fj->stop = stop; | |||||
| fj->do_update = do_update; | |||||
| fj->progress = progress; | |||||
| *(fj->stop) = 0; /*false*/ | |||||
| G.is_break = false; /* XXX shared with render - replace with job 'stop' switch */ | |||||
| /* arm the modifier... */ | |||||
| fmd->refresh = true; | |||||
| *(fj->do_update) = true; | |||||
| *do_update = true; | |||||
| *stop = 0; | |||||
| /*...and trigger modifier execution HERE*/ | |||||
| makeDerivedMesh(scene, ob, NULL, scene->customdata_mask | CD_MASK_BAREMESH, 0); | |||||
Not Done Inline ActionsAs i understand, the ob here is the object from the scene, which means you're forbidden to call makeDerivedMesh() outside of the scene update, especially from the job. sergey: As i understand, the `ob` here is the object from the scene, which means you're forbidden to… | |||||
| } | |||||
| static void fracture_endjob(void *customdata) | |||||
| { | |||||
| FractureJob *fj = customdata; | |||||
| FractureModifierData *fmd = fj->fmd; | |||||
| fmd->refresh = false; | |||||
| } | |||||
| 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); | |||||
| float cfra = BKE_scene_frame_get(scene); | |||||
| FractureModifierData *rmd; | |||||
| FractureJob *fj; | |||||
| wmJob* wm_job; | |||||
| rmd = (FractureModifierData *)modifiers_findByType(obact, eModifierType_Fracture); | |||||
| if (!rmd || (rmd && rmd->refresh) || (scene->rigidbody_world && cfra != scene->rigidbody_world->pointcache->startframe)) | |||||
| return OPERATOR_CANCELLED; | |||||
| if (!rmd->execute_threaded) { | |||||
| rmd->refresh = true; | |||||
| DAG_id_tag_update(&obact->id, OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, obact); | |||||
| } | |||||
| else { | |||||
| /* job stuff */ | |||||
| int factor, verts, shardprogress, halvingprogress, totalprogress; | |||||
| scene->r.cfra = cfra; | |||||
| /* setup job */ | |||||
| wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Fracture", | |||||
| WM_JOB_PROGRESS, WM_JOB_TYPE_OBJECT_FRACTURE); | |||||
| fj = MEM_callocN(sizeof(FractureJob), "object fracture job"); | |||||
| fj->fmd = rmd; | |||||
| fj->ob = obact; | |||||
| fj->scene = scene; | |||||
| /* if we have shards, totalprogress = shards + islands | |||||
| * if we dont have shards, then calculate number of processed halving steps | |||||
| * if we split island to shards, add both */ | |||||
| factor = (fj->fmd->frac_algorithm == MOD_FRACTURE_BISECT_FAST) ? 4 : 2; | |||||
| shardprogress = fj->fmd->shard_count * (factor+1); /* +1 for the meshisland creation */ | |||||
| if (obact->derivedFinal) { | |||||
| verts = obact->derivedFinal->getNumVerts(obact->derivedFinal); | |||||
| } | |||||
| else { | |||||
| verts = ((Mesh*)obact->data)->totvert; | |||||
| } | |||||
| halvingprogress = (int)(verts / 1000) + (fj->fmd->shard_count * factor); /*-> 1000 size of each partitioned separate loose*/ | |||||
| totalprogress = (rmd->shards_to_islands || rmd->point_source != MOD_FRACTURE_UNIFORM) ? shardprogress + halvingprogress : shardprogress; | |||||
| fj->total_progress = totalprogress; | |||||
| WM_jobs_customdata_set(wm_job, fj, fracture_free); | |||||
| WM_jobs_timer(wm_job, 0.1, NC_WM | ND_JOB, NC_OBJECT | ND_MODIFIER); | |||||
| WM_jobs_callbacks(wm_job, fracture_startjob, NULL, fracture_update, fracture_endjob); | |||||
Not Done Inline ActionsThis line fails, and can possible cause crashes. campbellbarton: This line fails, and can possible cause crashes. | |||||
| WM_jobs_start(CTX_wm_manager(C), wm_job); | |||||
| } | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| static int fracture_refresh_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) | |||||
| { | |||||
| if (WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_OBJECT_FRACTURE)) | |||||
| return OPERATOR_CANCELLED; | |||||
| return fracture_refresh_exec(C, op); | |||||
| } | |||||
| 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); | |||||
| } | |||||
| /****************** rigidbody constraint refresh operator *********************/ | |||||
| static int rigidbody_refresh_constraints_exec(bContext *C, wmOperator *op) | |||||
| { | |||||
| Object *obact = ED_object_active_context(C); | |||||
| FractureModifierData *rmd; | |||||
| Scene *scene = CTX_data_scene(C); | |||||
| float cfra = BKE_scene_frame_get(scene); | |||||
| rmd = (FractureModifierData *)modifiers_findByType(obact, eModifierType_Fracture); | |||||
| if (!rmd || (rmd && rmd->refresh) || (scene->rigidbody_world && cfra != scene->rigidbody_world->pointcache->startframe)) | |||||
| return OPERATOR_CANCELLED; | |||||
| rmd->refresh_constraints = true; | |||||
| DAG_id_tag_update(&obact->id, OB_RECALC_DATA); | |||||
| WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, obact); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| static int rigidbody_refresh_constraints_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) | |||||
| { | |||||
| if (edit_modifier_invoke_properties(C, op) || true) | |||||
| return rigidbody_refresh_constraints_exec(C, op); | |||||
| else | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| void OBJECT_OT_rigidbody_constraints_refresh(wmOperatorType *ot) | |||||
| { | |||||
| ot->name = "RigidBody Constraints Refresh"; | |||||
| ot->description = "Refresh constraints in the Rigid Body modifier"; | |||||
| ot->idname = "OBJECT_OT_rigidbody_constraints_refresh"; | |||||
| ot->poll = fracture_poll; | |||||
| ot->invoke = rigidbody_refresh_constraints_invoke; | |||||
| ot->exec = rigidbody_refresh_constraints_exec; | |||||
| /* flags */ | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; | |||||
| edit_modifier_properties(ot); | |||||
| } | |||||
| void convert_modifier_to_objects(ReportList *reports, Scene* scene, Object* ob, FractureModifierData *rmd) | |||||
| { | |||||
| Base *base_new, *base_old = BKE_scene_base_find(scene, ob); | |||||
| Object *ob_new = NULL; | |||||
| MeshIsland *mi; | |||||
| RigidBodyShardCon* con; | |||||
| int i = 0; | |||||
| int count = BLI_countlist(&rmd->meshIslands); | |||||
| KDTree* objtree = BLI_kdtree_new(count); | |||||
| Object** objs = MEM_callocN(sizeof(Object*) * count, "convert_objs"); | |||||
| float max_con_mass = 0; | |||||
| float min_con_dist = FLT_MAX; | |||||
| rmd->refresh = false; | |||||
| for (mi = rmd->meshIslands.first; mi; mi = mi->next) { | |||||
| float cent[3]; | |||||
| Mesh* me; | |||||
| ModifierData *md; | |||||
| bool foundFracture = false; | |||||
| /* create separate objects for meshislands */ | |||||
| if (ob->type == OB_MESH) { | |||||
| base_new = ED_object_add_duplicate(G.main, scene, base_old, USER_DUP_MESH); | |||||
| ob_new = base_new->object; | |||||
| } | |||||
| else { | |||||
| RigidBodyWorld *rbw = NULL; | |||||
| ob_new = BKE_object_add(G.main, scene, OB_MESH); | |||||
| rbw = scene->rigidbody_world; | |||||
| if (rbw) { | |||||
| /* make rigidbody object settings */ | |||||
| if (ob_new->rigidbody_object == NULL) { | |||||
| ob_new->rigidbody_object = BKE_rigidbody_create_object(scene, ob_new, RBO_TYPE_ACTIVE); | |||||
| } | |||||
| ob_new->rigidbody_object->type = RBO_TYPE_ACTIVE; | |||||
| ob_new->rigidbody_object->flag |= RBO_FLAG_NEEDS_VALIDATE; | |||||
| /* add object to rigid body group */ | |||||
| BKE_group_object_add(rbw->group, ob_new, scene, NULL); | |||||
| DAG_id_tag_update(&ob_new->id, OB_RECALC_OB); | |||||
| } | |||||
| } | |||||
| /* 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(ob_new, give_matarar(ob), *give_totcolp(ob)); | |||||
| me = (Mesh*)ob_new->data; | |||||
| me->edit_btmesh = NULL; | |||||
| DM_to_mesh(mi->physics_mesh, me, ob_new, CD_MASK_MESH); | |||||
| /*set origin to centroid*/ | |||||
| copy_v3_v3(cent, mi->centroid); | |||||
| mul_m4_v3(ob_new->obmat, cent); | |||||
| copy_v3_v3(ob_new->loc, cent); | |||||
| /*set mass*/ | |||||
| ob_new->rigidbody_object->mass = mi->rigidbody->mass; | |||||
| /*store obj indexes in kdtree and objs in array*/ | |||||
| BLI_kdtree_insert(objtree, i, mi->centroid); | |||||
| objs[i] = ob_new; | |||||
| i++; | |||||
| BKE_rigidbody_remove_shard(scene, mi); | |||||
| } | |||||
| BLI_kdtree_balance(objtree); | |||||
| /* go through constraints and find objects by position | |||||
| * constrain them with regular constraints */ | |||||
| if (rmd->use_mass_dependent_thresholds) { | |||||
| max_con_mass = BKE_rigidbody_calc_max_con_mass(ob); | |||||
| } | |||||
| for (con = rmd->meshConstraints.first; con; con = con->next) { | |||||
| int index1 = BLI_kdtree_find_nearest(objtree, con->mi1->centroid, NULL); | |||||
| int index2 = BLI_kdtree_find_nearest(objtree, con->mi2->centroid, NULL); | |||||
| Object* ob1 = objs[index1]; | |||||
| Object* ob2 = objs[index2]; | |||||
| Object* rbcon = BKE_object_add(G.main, scene, OB_EMPTY); | |||||
| int iterations; | |||||
| if (rmd->solver_iterations_override == 0) { | |||||
| iterations = rmd->modifier.scene->rigidbody_world->num_solver_iterations; | |||||
| } | |||||
| else { | |||||
| iterations = rmd->solver_iterations_override; | |||||
| } | |||||
| if (iterations > 0) { | |||||
| con->flag |= RBC_FLAG_OVERRIDE_SOLVER_ITERATIONS; | |||||
| con->num_solver_iterations = iterations; | |||||
| } | |||||
| if ((rmd->use_mass_dependent_thresholds)) { | |||||
| BKE_rigidbody_calc_threshold(max_con_mass, rmd, con); | |||||
| } | |||||
| copy_v3_v3(rbcon->loc, ob1->loc); /*use same settings as in modifier*/ | |||||
| ED_rigidbody_constraint_add(scene, rbcon, con->type, reports); | |||||
| rbcon->rigidbody_constraint->ob1 = ob1; | |||||
| rbcon->rigidbody_constraint->ob2 = ob2; | |||||
| rbcon->rigidbody_constraint->breaking_threshold = con->breaking_threshold; | |||||
| rbcon->rigidbody_constraint->flag |= RBC_FLAG_USE_BREAKING; | |||||
| 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, con); | |||||
| } | |||||
| /* 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); | |||||
| float cfra = BKE_scene_frame_get(scene); | |||||
| FractureModifierData *rmd; | |||||
| RigidBodyWorld *rbw = scene->rigidbody_world; | |||||
| Object* par = NULL; | |||||
| rmd = (FractureModifierData *)modifiers_findByType(obact, eModifierType_Fracture); | |||||
| if (rmd && rmd->refresh) { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| if (rmd && scene->rigidbody_world && cfra == scene->rigidbody_world->pointcache->startframe) { | |||||
| convert_modifier_to_objects(op->reports, scene, obact, rmd); | |||||
| } | |||||
| 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 Group* constraints = rbw->constraints; | |||||
| struct Group* group = rbw->group; | |||||
| RigidBodyWorld *rbwn = NULL; | |||||
| BKE_rigidbody_cache_reset(rbw); | |||||
| BKE_rigidbody_free_world(rbw); | |||||
| 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; | |||||
| } | |||||
| DAG_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, wmEvent *UNUSED(event)) | |||||
| { | |||||
| if (edit_modifier_invoke_properties(C, op) || true) | |||||
| 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); | |||||
| } | |||||
| Context not available. | |||||
That's just delaying the headache. No data available for RW should be shared between threads. It is possible to screw up data used by fracture job in loads of other ways than just removing the modifier.