Multires: Unsubdivide and Rebuild Subdivisions

This implements the main unsubdivide algorithm which rebuilds a base mesh and extracts the grid's data from a high resolution mesh.
It includes the Rebuild Subdivisions operator, which generates all subdivision levels down to the level 0 base mesh.

It supports:
- Rebuilding an arbitrary number of levels (Unsubdivide) or as many levels as possible down to level 0 in a single step (Rebuild Subdivisions).
- Rebuilding with already existing grids.
- Meshes with n-gons and triangles
- Meshes with more than 2 faces per edge
- Base mesh made completely out of triangles
- Meshes without poles
- Meshes with multiple disconnected elements at the same subdivision level

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D7372
This commit is contained in:
Pablo Dobarro 2020-04-30 16:47:23 +02:00
parent d4c547b7bd
commit f28875a998
10 changed files with 1503 additions and 3 deletions

View File

@ -698,8 +698,10 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
col.enabled = ob.mode != 'EDIT'
col.operator("object.multires_subdivide", text="Subdivide")
col.operator("object.multires_higher_levels_delete", text="Delete Higher")
col.operator("object.multires_unsubdivide", text="Unsubdivide")
col.operator("object.multires_reshape", text="Reshape")
col.operator("object.multires_base_apply", text="Apply Base")
col.operator("object.multires_rebuild_subdiv", text="Rebuild Subdivisions")
col.prop(md, "uv_smooth", text="")
col.prop(md, "show_only_control_edges")
col.prop(md, "use_creases")

View File

@ -110,6 +110,11 @@ void multiresModifier_del_levels(struct MultiresModifierData *mmd,
void multiresModifier_base_apply(struct Depsgraph *depsgraph,
struct Object *object,
struct MultiresModifierData *mmd);
int multiresModifier_rebuild_subdiv(struct Depsgraph *depsgraph,
struct Object *object,
struct MultiresModifierData *mmd,
int rebuild_limit,
bool switch_view_to_lower_level);
void multiresModifier_subdivide_legacy(struct MultiresModifierData *mmd,
struct Scene *scene,
struct Object *ob,

View File

@ -178,6 +178,7 @@ set(SRC
intern/multires_reshape_util.c
intern/multires_reshape_vertcos.c
intern/multires_subdiv.c
intern/multires_unsubdivide.c
intern/nla.c
intern/node.c
intern/object.c
@ -397,6 +398,7 @@ set(SRC
intern/lib_intern.h
intern/multires_inline.h
intern/multires_reshape.h
intern/multires_unsubdivide.h
intern/pbvh_intern.h
intern/subdiv_converter.h
intern/subdiv_inline.h

View File

@ -156,6 +156,11 @@ bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape
struct Object *object,
struct MultiresModifierData *mmd);
bool multires_reshape_context_create_from_base_mesh(MultiresReshapeContext *reshape_context,
struct Depsgraph *depsgraph,
struct Object *object,
struct MultiresModifierData *mmd);
bool multires_reshape_context_create_from_ccg(MultiresReshapeContext *reshape_context,
struct SubdivCCG *subdiv_ccg,
struct Mesh *base_mesh,

View File

@ -152,6 +152,39 @@ static bool context_verify_or_free(MultiresReshapeContext *reshape_context)
return is_valid;
}
bool multires_reshape_context_create_from_base_mesh(MultiresReshapeContext *reshape_context,
Depsgraph *depsgraph,
Object *object,
MultiresModifierData *mmd)
{
context_zero(reshape_context);
const bool use_render_params = false;
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
Mesh *base_mesh = (Mesh *)object->data;
reshape_context->depsgraph = depsgraph;
reshape_context->object = object;
reshape_context->mmd = mmd;
reshape_context->base_mesh = base_mesh;
reshape_context->subdiv = multires_reshape_create_subdiv(NULL, object, mmd);
reshape_context->need_free_subdiv = true;
reshape_context->reshape.level = multires_get_level(
scene_eval, object, mmd, use_render_params, true);
reshape_context->reshape.grid_size = BKE_subdiv_grid_size_from_level(
reshape_context->reshape.level);
reshape_context->top.level = mmd->totlvl;
reshape_context->top.grid_size = BKE_subdiv_grid_size_from_level(reshape_context->top.level);
context_init_commoon(reshape_context);
return context_verify_or_free(reshape_context);
}
bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape_context,
Depsgraph *depsgraph,
Object *object,
@ -272,9 +305,9 @@ void multires_reshape_context_free(MultiresReshapeContext *reshape_context)
multires_reshape_free_original_grids(reshape_context);
MEM_freeN(reshape_context->face_start_grid_index);
MEM_freeN(reshape_context->ptex_start_grid_index);
MEM_freeN(reshape_context->grid_to_face_index);
MEM_SAFE_FREE(reshape_context->face_start_grid_index);
MEM_SAFE_FREE(reshape_context->ptex_start_grid_index);
MEM_SAFE_FREE(reshape_context->grid_to_face_index);
}
/** \} */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,93 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup bke
*/
#ifndef __BKE_INTERN_MULTIRES_UNSUBDIVIDE_H__
#define __BKE_INTERN_MULTIRES_UNSUBDIVIDE_H__
#include "BLI_sys_types.h"
struct BMesh;
struct Depsgraph;
struct Mesh;
struct MultiresModifierData;
struct Object;
typedef struct MultiresUnsubdivideGrid {
/* For sanity checks. */
int grid_index;
int grid_size;
/* Grid coordinates in object space. */
float (*grid_co)[3];
} MultiresUnsubdivideGrid;
typedef struct MultiresUnsubdivideContext {
/* Input Mesh to unsubdivide. */
struct Mesh *original_mesh;
struct MDisps *original_mdisp;
/* Number of subdivision in the grids of the input mesh. */
int num_original_levels;
/* Level 0 base mesh after applying the maximum amount of unsubdivisions. */
struct Mesh *base_mesh;
/* Limit on how many levels down the unsubdivide operation should create, if possible. */
int max_new_levels;
/* New levels that were created after unsubdividing. */
int num_new_levels;
/* Number of subdivisions that should be applied to the base mesh. (num_new_levels +
* num_original_levels)
*/
int num_total_levels;
/* Data for the new grids, indexed by base mesh loop index. */
int num_grids;
struct MultiresUnsubdivideGrid *base_mesh_grids;
/* Private data. */
struct BMesh *bm_original_mesh;
int *loop_to_face_map;
int *base_to_orig_vmap;
} MultiresUnsubdivideContext;
/* ================================================================================================
* Construct/destruct reshape context.
*/
void multires_unsubdivide_context_init(MultiresUnsubdivideContext *context,
struct Mesh *original_mesh,
struct MultiresModifierData *mmd);
void multires_unsubdivide_context_free(MultiresUnsubdivideContext *context);
/* ================================================================================================
* Rebuild Lower Subdivisions.
*/
/* Rebuilds all subdivision to the level 0 base mesh. */
bool multires_unsubdivide_to_basemesh(MultiresUnsubdivideContext *context);
#endif /* __BKE_INTERN_MULTIRES_UNSUBDIVIDE_H__ */

View File

@ -169,6 +169,8 @@ void OBJECT_OT_multires_subdivide(struct wmOperatorType *ot);
void OBJECT_OT_multires_reshape(struct wmOperatorType *ot);
void OBJECT_OT_multires_higher_levels_delete(struct wmOperatorType *ot);
void OBJECT_OT_multires_base_apply(struct wmOperatorType *ot);
void OBJECT_OT_multires_unsubdivide(struct wmOperatorType *ot);
void OBJECT_OT_multires_rebuild_subdiv(struct wmOperatorType *ot);
void OBJECT_OT_multires_external_save(struct wmOperatorType *ot);
void OBJECT_OT_multires_external_pack(struct wmOperatorType *ot);
void OBJECT_OT_correctivesmooth_bind(struct wmOperatorType *ot);

View File

@ -1738,6 +1738,119 @@ void OBJECT_OT_multires_base_apply(wmOperatorType *ot)
/** \} */
/* ------------------------------------------------------------------- */
/** \name Multires Unsubdivide
* \{ */
static int multires_unsubdivide_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *object = ED_object_active_context(C);
MultiresModifierData *mmd = (MultiresModifierData *)edit_modifier_property_get(
op, object, eModifierType_Multires);
if (!mmd) {
return OPERATOR_CANCELLED;
}
int new_levels = multiresModifier_rebuild_subdiv(depsgraph, object, mmd, 1, true);
if (new_levels == 0) {
BKE_report(op->reports, RPT_ERROR, "Not valid subdivisions found to rebuild a lower level");
return OPERATOR_CANCELLED;
}
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);
return OPERATOR_FINISHED;
}
static int multires_unsubdivide_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (edit_modifier_invoke_properties(C, op)) {
return multires_unsubdivide_exec(C, op);
}
else {
return OPERATOR_CANCELLED;
}
}
void OBJECT_OT_multires_unsubdivide(wmOperatorType *ot)
{
ot->name = "Unsubdivide";
ot->description = "Rebuild a lower subdivision level of the current base mesh";
ot->idname = "OBJECT_OT_multires_unsubdivide";
ot->poll = multires_poll;
ot->invoke = multires_unsubdivide_invoke;
ot->exec = multires_unsubdivide_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Multires Rebuild Subdivisions
* \{ */
static int multires_rebuild_subdiv_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *object = ED_object_active_context(C);
MultiresModifierData *mmd = (MultiresModifierData *)edit_modifier_property_get(
op, object, eModifierType_Multires);
if (!mmd) {
return OPERATOR_CANCELLED;
}
int new_levels = multiresModifier_rebuild_subdiv(depsgraph, object, mmd, INT_MAX, false);
if (new_levels == 0) {
BKE_report(op->reports, RPT_ERROR, "Not valid subdivisions found to rebuild lower levels");
return OPERATOR_CANCELLED;
}
BKE_reportf(op->reports, RPT_INFO, "%d new levels rebuilt", new_levels);
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);
return OPERATOR_FINISHED;
}
static int multires_rebuild_subdiv_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
if (edit_modifier_invoke_properties(C, op)) {
return multires_rebuild_subdiv_exec(C, op);
}
else {
return OPERATOR_CANCELLED;
}
}
void OBJECT_OT_multires_rebuild_subdiv(wmOperatorType *ot)
{
ot->name = "Rebuild Lower Subdivisions";
ot->description =
"Rebuilds all possible subdivisions levels to generate a lower resolution base mesh";
ot->idname = "OBJECT_OT_multires_rebuild_subdiv";
ot->poll = multires_poll;
ot->invoke = multires_rebuild_subdiv_invoke;
ot->exec = multires_rebuild_subdiv_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Skin Modifier
* \{ */

View File

@ -137,6 +137,8 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_multires_reshape);
WM_operatortype_append(OBJECT_OT_multires_higher_levels_delete);
WM_operatortype_append(OBJECT_OT_multires_base_apply);
WM_operatortype_append(OBJECT_OT_multires_unsubdivide);
WM_operatortype_append(OBJECT_OT_multires_rebuild_subdiv);
WM_operatortype_append(OBJECT_OT_multires_external_save);
WM_operatortype_append(OBJECT_OT_multires_external_pack);
WM_operatortype_append(OBJECT_OT_skin_root_mark);