Changeset View
Standalone View
source/blender/editors/object/object_remesh.c
| Context not available. | |||||
| static int voxel_remesh_exec(bContext *C, wmOperator *op) | static int voxel_remesh_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Object *ob = CTX_data_active_object(C); | Object *ob = CTX_data_active_object(C); | ||||
| Main *bmain = CTX_data_main(C); | |||||
| Mesh *mesh = ob->data; | Mesh *mesh = ob->data; | ||||
| Mesh *new_mesh; | Mesh *new_mesh; | ||||
| Context not available. | |||||
| } | } | ||||
| BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); | BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); | ||||
| DEG_relations_tag_update(bmain); | |||||
| DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); | DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); | ||||
| WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); | WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); | ||||
| Context not available. | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| } | } | ||||
| enum { | |||||
| QUADRIFLOW_REMESH_RATIO = 1, | |||||
| QUADRIFLOW_REMESH_EDGE_LENGTH, | |||||
| QUADRIFLOW_REMESH_FACES, | |||||
| }; | |||||
brecht: `QUA_` -> `QUADRIFOW_REMESH_` | |||||
| /****************** quadriflow remesh operator *********************/ | |||||
| typedef struct QuadriFlowJob { | |||||
| /* from wmJob */ | |||||
| struct Object *owner; | |||||
| short *stop, *do_update; | |||||
| float *progress; | |||||
| int target_faces; | |||||
| int seed; | |||||
| bool use_preserve_sharp; | |||||
| bool use_preserve_boundary; | |||||
| bool use_mesh_curvature; | |||||
| bool preserve_paint_mask; | |||||
| bool smooth_normals; | |||||
| int success; | |||||
| } QuadriFlowJob; | |||||
| static void quadriflow_free_job(void *customdata) | |||||
| { | |||||
| QuadriFlowJob *qj = customdata; | |||||
| MEM_freeN(qj); | |||||
| } | |||||
| /* called by quadriflowjob, only to check job 'stop' value */ | |||||
| static int quadriflow_break_job(void *customdata) | |||||
| { | |||||
| QuadriFlowJob *qj = (QuadriFlowJob *)customdata; | |||||
| // return *(qj->stop); | |||||
| /* this is not nice yet, need to make the jobs list template better | |||||
| * for identifying/acting upon various different jobs */ | |||||
| /* but for now we'll reuse the render break... */ | |||||
| bool should_break = (G.is_break); | |||||
| if (should_break) { | |||||
| qj->success = -1; | |||||
| } | |||||
| return should_break; | |||||
| } | |||||
| /* called by oceanbake, wmJob sends notifier */ | |||||
| static void quadriflow_update_job(void *customdata, float progress, int *cancel) | |||||
| { | |||||
| QuadriFlowJob *qj = customdata; | |||||
| if (quadriflow_break_job(qj)) { | |||||
| *cancel = 1; | |||||
| } | |||||
| else { | |||||
| *cancel = 0; | |||||
| } | |||||
Done Inline Actions/home/brecht/dev/worktree/source/blender/editors/object/object_remesh.c: In function ‘quadriflow_remesh_exec’:
/home/brecht/dev/worktree/source/blender/editors/object/object_remesh.c:218:30: warning: passing argument 1 of ‘BKE_mesh_smooth_flag_set’ from incompatible pointer type [-Wincompatible-pointer-types]
BKE_mesh_smooth_flag_set(ob, true);
^~
In file included from /home/brecht/dev/worktree/source/blender/editors/object/object_remesh.c:44:0:
/home/brecht/dev/worktree/source/blender/blenkernel/BKE_mesh.h:186:6: note: expected ‘struct Mesh *’ but argument is of type ‘Object * {aka struct Object *}’
void BKE_mesh_smooth_flag_set(struct Mesh *me, const bool use_smooth);
^~~~~~~~~~~~~~~~~~~~~~~~brecht: ```
/home/brecht/dev/worktree/source/blender/editors/object/object_remesh.c: In function… | |||||
Done Inline ActionsThis is a straight up build error on windows. LazyDodo: This is a straight up build error on windows. | |||||
| *(qj->do_update) = true; | |||||
| *(qj->progress) = progress; | |||||
| } | |||||
| static void quadriflow_start_job(void *customdata, short *stop, short *do_update, float *progress) | |||||
| { | |||||
| QuadriFlowJob *qj = customdata; | |||||
| qj->stop = stop; | |||||
| qj->do_update = do_update; | |||||
| qj->progress = progress; | |||||
| qj->success = 1; | |||||
| G.is_break = false; /* XXX shared with render - replace with job 'stop' switch */ | |||||
| Object *ob = qj->owner; | |||||
| Mesh *mesh = ob->data; | |||||
| Mesh *new_mesh; | |||||
| new_mesh = BKE_mesh_remesh_quadriflow_to_mesh_nomain(mesh, | |||||
| qj->target_faces, | |||||
| qj->seed, | |||||
| qj->use_preserve_sharp, | |||||
| qj->use_preserve_boundary, | |||||
| qj->use_mesh_curvature, | |||||
| quadriflow_update_job, | |||||
| (void *)qj); | |||||
| if (!new_mesh) { | |||||
| *do_update = true; | |||||
| *stop = 0; | |||||
| if (qj->success == 1) { | |||||
Done Inline ActionsBooleans should start with use_ or another verb. brecht: Booleans should start with `use_` or another verb. | |||||
| /* This is not a user cancelation event */ | |||||
| qj->success = 0; | |||||
| } | |||||
Done Inline ActionsRename to preserve_paint_mask for consistency with voxel remesh. brecht: Rename to `preserve_paint_mask` for consistency with voxel remesh. | |||||
| return; | |||||
| } | |||||
| if (ob->mode == OB_MODE_SCULPT) { | |||||
| ED_sculpt_undo_geometry_begin(ob); | |||||
| } | |||||
| Mesh *obj_mesh_copy = NULL; | |||||
| if (qj->preserve_paint_mask) { | |||||
| obj_mesh_copy = BKE_mesh_new_nomain_from_template(mesh, mesh->totvert, 0, 0, 0, 0); | |||||
| CustomData_copy( | |||||
| &mesh->vdata, &obj_mesh_copy->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, mesh->totvert); | |||||
| for (int i = 0; i < mesh->totvert; i++) { | |||||
| copy_v3_v3(obj_mesh_copy->mvert[i].co, mesh->mvert[i].co); | |||||
| } | |||||
| } | |||||
Done Inline ActionsThe name here should indicate that it is a number of faces. brecht: The name here should indicate that it is a number of faces. | |||||
Done Inline ActionsMissing spaces at the end of sentences. JacquesLucke: Missing spaces at the end of sentences.
Also, this number does not seem to correlate to the… | |||||
| BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true); | |||||
Done Inline ActionsThis description can simply be something like: `The approximate amount of faces of the new mesh." brecht: This description can simply be something like: `The approximate amount of faces of the new mesh. | |||||
Done Inline ActionsDon't abbreviate automatic to auto. But also it's not clear what automatic means in this case, what is it based on then? brecht: Don't abbreviate automatic to auto. But also it's not clear what automatic means in this case… | |||||
| if (qj->preserve_paint_mask) { | |||||
| BKE_remesh_reproject_paint_mask(mesh, obj_mesh_copy); | |||||
| BKE_mesh_free(obj_mesh_copy); | |||||
| } | |||||
Done Inline Actionsyou -> use But it's not clear at what a random seed even does here, that should be described. brecht: you -> use
But it's not clear at what a random seed even does here, that should be described. | |||||
| if (qj->smooth_normals) { | |||||
| BKE_mesh_smooth_flag_set(ob->data, true); | |||||
| } | |||||
| if (ob->mode == OB_MODE_SCULPT) { | |||||
Done Inline ActionsSpecifying an absolute number of faces is not very easy, it's hard to estimate how many you need. I think we can change this to a percentage of the number of faces in the input mesh. Seems more intuitive to say you want 10% or 400% of the number of faces? brecht: Specifying an absolute number of faces is not very easy, it's hard to estimate how many you… | |||||
Done Inline ActionsI don't think having it as a percentage would be very useful. I think it is best to just have an approximate number like it is now. The face count is very loosely specified anyways. Perhaps it would be better to expose the target edge length of the quads? zeddb: I don't think having it as a percentage would be very useful.
Sculpt meshes are usually very… | |||||
Done Inline ActionsThe current solution requires the user to find the number of faces the mesh has and do the math manually, or to use trial and error to find an appropriate number of faces. Being able to say "more faces" or "fewer faces" is easier. Being able to specify the target edge length is fine as an alternate option, but I still think a percentage is the most convenient option to use by default. For target edge length you again have to look at the dimensions of the object and do some math or use trial and error. brecht: The current solution requires the user to find the number of faces the mesh has and do the math… | |||||
Done Inline ActionsThe current method does not require the user to do the math manually. If we do a percentage as you suggest then people will have to look up the face count and do math in their head. I don't see how it would be easier than simply a target number of faces. zeddb: The current method does not require the user to do the math manually.
If we do a percentage as… | |||||
| ED_sculpt_undo_geometry_end(ob); | |||||
| } | |||||
| BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); | |||||
| *do_update = true; | |||||
| *stop = 0; | |||||
| } | |||||
| static void quadriflow_end_job(void *customdata) | |||||
| { | |||||
| QuadriFlowJob *qj = customdata; | |||||
| Object *ob = qj->owner; | |||||
| WM_set_locked_interface(G_MAIN->wm.first, false); | |||||
| if (qj->success > 0) { | |||||
| DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); | |||||
| WM_reportf(RPT_INFO, "QuadriFlow: Completed remeshing!"); | |||||
| } | |||||
| else { | |||||
| if (qj->success == 0) { | |||||
Done Inline ActionsInput target number of faces in the new mesh In general, try to use consistent terminology and avoid implementation details. Right now there is a mix of current/original, amount/number/count. brecht: `Input target number of faces in the new mesh`
`Specify target number of faces relative to the… | |||||
| WM_reportf(RPT_ERROR, "QuadriFlow: remeshing failed!"); | |||||
| } | |||||
| else { | |||||
| WM_report(RPT_WARNING, "QuadriFlow: remeshing canceled!"); | |||||
| } | |||||
| } | |||||
| } | |||||
| static int quadriflow_remesh_exec(bContext *C, wmOperator *op) | |||||
Done Inline ActionsCalculates -> Create brecht: `Calculates` -> `Create` | |||||
| { | |||||
| QuadriFlowJob *job = MEM_mallocN(sizeof(QuadriFlowJob), "QuadriFlowJob"); | |||||
| job->owner = CTX_data_active_object(C); | |||||
| job->target_faces = RNA_int_get(op->ptr, "target_faces"); | |||||
| job->seed = RNA_int_get(op->ptr, "seed"); | |||||
| job->use_preserve_sharp = RNA_boolean_get(op->ptr, "use_preserve_sharp"); | |||||
| job->use_preserve_boundary = RNA_boolean_get(op->ptr, "use_preserve_boundary"); | |||||
| job->use_mesh_curvature = RNA_boolean_get(op->ptr, "use_mesh_curvature"); | |||||
| job->preserve_paint_mask = RNA_boolean_get(op->ptr, "preserve_paint_mask"); | |||||
| job->smooth_normals = RNA_boolean_get(op->ptr, "smooth_normals"); | |||||
| wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), | |||||
| CTX_wm_window(C), | |||||
| CTX_data_scene(C), | |||||
Done Inline ActionsPreserve sharp -> Preserve Sharp, and similar capitalization for other properties. brecht: `Preserve sharp` -> `Preserve Sharp`, and similar capitalization for other properties. | |||||
| "QuadriFlow Remesh", | |||||
| WM_JOB_PROGRESS, | |||||
| WM_JOB_TYPE_QUADRIFLOW_REMESH); | |||||
Done Inline ActionsDoes "border" mean edges with only a single adjacent face? If so, call this "boundary" for consistency with edit mode tools. brecht: Does "border" mean edges with only a single adjacent face? If so, call this "boundary" for… | |||||
| WM_jobs_customdata_set(wm_job, job, quadriflow_free_job); | |||||
| WM_jobs_timer(wm_job, 0.1, NC_GEOM | ND_DATA, NC_GEOM | ND_DATA); | |||||
| WM_jobs_callbacks(wm_job, quadriflow_start_job, NULL, NULL, quadriflow_end_job); | |||||
| WM_set_locked_interface(CTX_wm_manager(C), true); | |||||
| WM_jobs_start(CTX_wm_manager(C), wm_job); | |||||
| return OPERATOR_FINISHED; | |||||
Done Inline ActionsCan you explain in more detail what adaptive scale means? brecht: Can you explain in more detail what adaptive scale means? | |||||
| } | |||||
| static bool quadriflow_check(bContext *C, wmOperator *op) | |||||
| { | |||||
| int mode = RNA_enum_get(op->ptr, "mode"); | |||||
| if (mode == QUADRIFLOW_REMESH_EDGE_LENGTH) { | |||||
| float area = RNA_float_get(op->ptr, "mesh_area"); | |||||
| if (area < 0.0f) { | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| area = BKE_mesh_calc_area(ob->data); | |||||
| RNA_float_set(op->ptr, "mesh_area", area); | |||||
| } | |||||
| int num_faces; | |||||
| float edge_len = RNA_float_get(op->ptr, "target_edge_length"); | |||||
| num_faces = area / (edge_len * edge_len); | |||||
Done Inline ActionsNumber of faces should not be the default value, should be either edge length or ratio. brecht: Number of faces should not be the default value, should be either edge length or ratio. | |||||
| RNA_int_set(op->ptr, "target_faces", num_faces); | |||||
Done Inline ActionsFace Count Mode -> Mode brecht: `Face Count Mode` -> `Mode` | |||||
| } | |||||
Done Inline ActionsHow to calculate the number of output faces -> How to specify the amount of detail for the new mesh brecht: `How to calculate the number of output faces` -> `How to specify the amount of detail for the… | |||||
| else if (mode == QUADRIFLOW_REMESH_RATIO) { | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| Mesh *mesh = ob->data; | |||||
| int num_faces; | |||||
| float ratio = RNA_float_get(op->ptr, "target_ratio"); | |||||
| num_faces = mesh->totpoly * ratio; | |||||
Done Inline ActionsThe face count ratio in respect to the original mesh -> Relative number of faces compared to the current mesh brecht: `The face count ratio in respect to the original mesh` -> `Relative number of faces compared to… | |||||
| RNA_int_set(op->ptr, "target_faces", num_faces); | |||||
| } | |||||
| return true; | |||||
Done Inline Actionstarget_edge_len -> target_edge_length brecht: `target_edge_len` -> `target_edge_length` | |||||
| } | |||||
| /* Hide the target variables if they are not active */ | |||||
| static bool quadriflow_poll_property(const bContext *C, wmOperator *op, const PropertyRNA *prop) | |||||
| { | |||||
Done Inline ActionsThe desired edge length on the output quads -> Target edge length in the new mesh brecht: `The desired edge length on the output quads` -> `Target edge length in the new mesh` | |||||
| const char *prop_id = RNA_property_identifier(prop); | |||||
| if (STRPREFIX(prop_id, "target")) { | |||||
| int mode = RNA_enum_get(op->ptr, "mode"); | |||||
| if (STREQ(prop_id, "target_edge_length") && mode != QUADRIFLOW_REMESH_EDGE_LENGTH) { | |||||
| return false; | |||||
| } | |||||
| else if (STREQ(prop_id, "target_faces")) { | |||||
Done Inline ActionsNum of quads -> Number of Faces brecht: `Num of quads` -> `Number of Faces` | |||||
| if (mode != QUADRIFLOW_REMESH_FACES) { | |||||
Done Inline ActionsThe approximate amount of faces (quads) on the new mesh -> Approximate number of faces (quads) in the new mesh. brecht: `The approximate amount of faces (quads) on the new mesh` -> `Approximate number of faces… | |||||
| /* Make sure we can edit the target_faces value even if it doesn't start as EDITABLE */ | |||||
| float area = RNA_float_get(op->ptr, "mesh_area"); | |||||
Done Inline ActionsRemove this part of the comment, set the minimum value to 1 and let users use Ratio for that instead. brecht: Remove this part of the comment, set the minimum value to `1` and let users use `Ratio` for… | |||||
| if (area < -0.8f) { | |||||
| area += 0.2f; | |||||
Done Inline Actions255 -> INT_MAX brecht: `255` -> `INT_MAX` | |||||
| /* Make sure we have up to date values from the start */ | |||||
| RNA_def_property_flag((PropertyRNA *)prop, PROP_EDITABLE); | |||||
| quadriflow_check((bContext *)C, op); | |||||
| } | |||||
| /* Only disable input */ | |||||
| RNA_def_property_clear_flag((PropertyRNA *)prop, PROP_EDITABLE); | |||||
| } | |||||
| else { | |||||
| RNA_def_property_flag((PropertyRNA *)prop, PROP_EDITABLE); | |||||
| } | |||||
Done Inline Actions10.0f -> FLT_MAX brecht: `10.0f` -> `FLT_MAX` | |||||
| } | |||||
| else if (STREQ(prop_id, "target_ratio") && mode != QUADRIFLOW_REMESH_RATIO) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| static const EnumPropertyItem mode_type_items[] = { | |||||
| {QUADRIFLOW_REMESH_RATIO, | |||||
| "RATIO", | |||||
| 0, | |||||
| "Ratio", | |||||
| "Specify target number of faces relative to the current mesh"}, | |||||
| {QUADRIFLOW_REMESH_EDGE_LENGTH, | |||||
| "EDGE", | |||||
| 0, | |||||
Done Inline ActionsThis property only really makes sense in the "adjust last operator" panel, and even then it's a hack. I suggest to just remove this property as the operator displays a popup and multiple values can be tweaked without triggering a recompute. brecht: This property only really makes sense in the "adjust last operator" panel, and even then it's a… | |||||
| "Edge Length", | |||||
| "Input target edge length in the new mesh"}, | |||||
| {QUADRIFLOW_REMESH_FACES, "FACES", 0, "Faces", "Input target number of faces in the new mesh"}, | |||||
| {0, NULL, 0, NULL, NULL}, | |||||
| }; | |||||
| void OBJECT_OT_quadriflow_remesh(wmOperatorType *ot) | |||||
| { | |||||
| /* identifiers */ | |||||
| ot->name = "QuadriFlow Remesh"; | |||||
| ot->description = | |||||
| "Create a new quad based mesh using the surface data of the current mesh. All data " | |||||
| "layers will be lost"; | |||||
| ot->idname = "OBJECT_OT_quadriflow_remesh"; | |||||
| /* api callbacks */ | |||||
| ot->poll = object_remesh_poll; | |||||
| ot->poll_property = quadriflow_poll_property; | |||||
| ot->check = quadriflow_check; | |||||
| ot->invoke = WM_operator_props_popup_confirm; | |||||
| ot->exec = quadriflow_remesh_exec; | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| PropertyRNA *prop; | |||||
| /* properties */ | |||||
| RNA_def_boolean(ot->srna, | |||||
| "use_preserve_sharp", | |||||
| false, | |||||
| "Preserve Sharp", | |||||
| "Try to preserve sharp features on the mesh"); | |||||
| RNA_def_boolean(ot->srna, | |||||
| "use_preserve_boundary", | |||||
| false, | |||||
| "Preserve Mesh Boundary", | |||||
| "Try to preserve mesh boundary on the mesh"); | |||||
| RNA_def_boolean(ot->srna, | |||||
| "use_mesh_curvature", | |||||
| false, | |||||
| "Use Mesh Curvature", | |||||
| "Take the mesh curvature into account when remeshing"); | |||||
| RNA_def_boolean(ot->srna, | |||||
| "preserve_paint_mask", | |||||
| false, | |||||
| "Preserve Paint Mask", | |||||
| "Reproject the paint mask onto the new mesh"); | |||||
| RNA_def_boolean(ot->srna, | |||||
| "smooth_normals", | |||||
| false, | |||||
| "Smooth Normals", | |||||
| "Set the output mesh normals to smooth"); | |||||
| RNA_def_enum(ot->srna, | |||||
| "mode", | |||||
| mode_type_items, | |||||
| 0, | |||||
| "Mode", | |||||
| "How to specify the amount of detail for the new mesh"); | |||||
| prop = RNA_def_float(ot->srna, | |||||
| "target_ratio", | |||||
| 1, | |||||
| 0, | |||||
| FLT_MAX, | |||||
| "Ratio", | |||||
| "Relative number of faces compared to the current mesh", | |||||
| 0.0f, | |||||
| 1.0f); | |||||
| prop = RNA_def_float(ot->srna, | |||||
| "target_edge_length", | |||||
| 0.1f, | |||||
| 0.0000001f, | |||||
| FLT_MAX, | |||||
| "Edge Length", | |||||
| "Target edge length in the new mesh", | |||||
| 0.00001f, | |||||
| 1.0f); | |||||
| prop = RNA_def_int(ot->srna, | |||||
| "target_faces", | |||||
| 1, | |||||
| 1, | |||||
| INT_MAX, | |||||
| "Number of Faces", | |||||
| "Approximate number of faces (quads) in the new mesh", | |||||
| 1, | |||||
| INT_MAX); | |||||
| prop = RNA_def_float( | |||||
| ot->srna, | |||||
| "mesh_area", | |||||
| -1.0f, | |||||
| -FLT_MAX, | |||||
| FLT_MAX, | |||||
| "Old Object Face Area", | |||||
| "This property is only used to cache the object area for later calculations", | |||||
| 0.0f, | |||||
| FLT_MAX); | |||||
| RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); | |||||
| RNA_def_int(ot->srna, | |||||
| "seed", | |||||
| 0, | |||||
| 0, | |||||
| INT_MAX, | |||||
| "Seed", | |||||
| "Random seed to use with the solver. Different seeds will cause the remesher to " | |||||
| "come up with different quad layouts on the mesh", | |||||
| 0, | |||||
| 255); | |||||
| } | |||||
| Context not available. | |||||
QUA_ -> QUADRIFOW_REMESH_