GPv3: Initial sculpt mode #119338

Merged
Lukas Tönne merged 28 commits from LukasTonne/blender:gp3-sculpt-mode into main 2024-04-11 09:40:00 +02:00
8 changed files with 158 additions and 119 deletions
Showing only changes of commit a497064f32 - Show all commits

View File

@ -1894,7 +1894,12 @@ bool BKE_object_has_mode_data(const Object *ob, eObjectMode object_mode)
}
}
else if (object_mode & OB_MODE_SCULPT) {
if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_SCULPT)) {
if (ob->type == OB_MESH) {
if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_SCULPT)) {
return true;
}
}
if (ob->type == OB_GREASE_PENCIL) {
return true;
}
}
@ -2373,7 +2378,8 @@ static void copy_object_pose(Object *obn, const Object *ob, const int flag)
/* XXX Remapping object pointing onto itself should be handled by generic
* BKE_library_remap stuff, but...
* the flush_constraint_targets callback am not sure about, so will delay that for now. */
* the flush_constraint_targets callback am not sure about, so will delay that for now.
*/
LISTBASE_FOREACH (bConstraint *, con, &chan->constraints) {
ListBase targets = {nullptr, nullptr};
@ -2565,8 +2571,8 @@ Object *BKE_object_duplicate(Main *bmain,
copy_flags |= LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING;
}
if (is_root_id) {
/* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate
* all expected linked data. */
/* In case root duplicated ID is linked, assume we want to get a local copy of it and
* duplicate all expected linked data. */
if (ID_IS_LINKED(ob)) {
dupflag |= USER_DUP_LINKED_ID;
}
@ -2693,7 +2699,8 @@ Object *BKE_object_duplicate(Main *bmain,
BKE_libblock_relink_to_newid(bmain, &obn->id, 0);
#ifndef NDEBUG
/* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */
/* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags.
*/
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
BLI_assert((id_iter->tag & LIB_TAG_NEW) == 0);
@ -2792,7 +2799,8 @@ void BKE_object_rot_to_mat3(const Object *ob, float mat[3][3], bool use_drot)
* with the rotation matrix to yield the appropriate rotation
*/
/* Rotations may either be quaternions, eulers (with various rotation orders), or axis-angle. */
/* Rotations may either be quaternions, eulers (with various rotation orders), or axis-angle.
*/
if (ob->rotmode > 0) {
/* Euler rotations
* (will cause gimbal lock, but this can be alleviated a bit with rotation orders). */
@ -3175,7 +3183,8 @@ static void give_parvert(Object *par, int nr, float vec[3])
ListBase *nurb;
/* It is possible that a cycle in the dependency graph was resolved in a way that caused this
* object to be evaluated before its dependencies. In this case the curve cache may be null. */
* object to be evaluated before its dependencies. In this case the curve cache may be null.
*/
if (par->runtime->curve_cache && par->runtime->curve_cache->deformed_nurbs.first != nullptr) {
nurb = &par->runtime->curve_cache->deformed_nurbs;
}
@ -3418,10 +3427,10 @@ blender::float4x4 BKE_object_calc_parent(Depsgraph *depsgraph, Scene *scene, Obj
workob.par3 = ob->par3;
/* The effects of constraints should NOT be included in the parent-inverse matrix. Constraints
* are supposed to be applied after the object's local loc/rot/scale. If the (inverted) effect of
* constraints would be included in the parent inverse matrix, these would be applied before the
* object's local loc/rot/scale instead of after. For example, a "Copy Rotation" constraint would
* rotate the object's local translation as well. See #82156. */
* are supposed to be applied after the object's local loc/rot/scale. If the (inverted) effect
* of constraints would be included in the parent inverse matrix, these would be applied before
* the object's local loc/rot/scale instead of after. For example, a "Copy Rotation" constraint
* would rotate the object's local translation as well. See #82156. */
STRNCPY(workob.parsubstr, ob->parsubstr);
@ -4289,10 +4298,10 @@ static int pc_cmp(const void *a, const void *b)
return 0;
}
/* TODO: Review the usages of this function, currently with copy-on-eval it will be called for orig
* object and then again for evaluated copies of it, think this is bad since there is no guarantee
* that we get the same stack index in both cases? Order is important since this index is used for
* filenames on disk. */
/* TODO: Review the usages of this function, currently with copy-on-eval it will be called for
* orig object and then again for evaluated copies of it, think this is bad since there is no
* guarantee that we get the same stack index in both cases? Order is important since this index
* is used for filenames on disk. */
int BKE_object_insert_ptcache(Object *ob)
{
LinkData *link = nullptr;

View File

@ -9,6 +9,7 @@
* actual mode switching logic is per-object type.
*/
#include "DNA_object_enums.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@ -142,7 +143,9 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode)
}
break;
case OB_GREASE_PENCIL:
if (mode & (OB_MODE_EDIT | OB_MODE_PAINT_GREASE_PENCIL | OB_MODE_WEIGHT_PAINT)) {
if (mode &
(OB_MODE_EDIT | OB_MODE_PAINT_GREASE_PENCIL | OB_MODE_WEIGHT_PAINT | OB_MODE_SCULPT))
{
return true;
}
break;
@ -264,7 +267,13 @@ static bool ed_object_mode_generic_exit_ex(
}
}
else if (ob->mode & OB_MODE_SCULPT) {
if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_SCULPT)) {
if (ob->type == OB_MESH && ob->sculpt && (ob->sculpt->mode_type == OB_MODE_SCULPT)) {
if (only_test) {
return true;
}
ED_object_sculptmode_exit_ex(bmain, depsgraph, scene, ob);
}
if (ob->type == OB_GREASE_PENCIL) {
if (only_test) {
return true;
}

View File

@ -232,7 +232,7 @@ ModifierData *ED_object_modifier_add(
/* set totlvl from existing MDISPS layer if object already had it */
multiresModifier_set_levels_from_disps((MultiresModifierData *)new_md, ob);
if (ob->mode & OB_MODE_SCULPT) {
if ((ob->type == OB_MESH) && (ob->mode & OB_MODE_SCULPT)) {
/* ensure that grid paint mask layer is created */
BKE_sculpt_mask_layers_ensure(nullptr, nullptr, ob, (MultiresModifierData *)new_md);
}
@ -1150,8 +1150,8 @@ bool ED_object_modifier_apply(Main *bmain,
BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data");
return false;
}
if ((ob->mode & OB_MODE_SCULPT) && find_multires_modifier_before(scene, md) &&
(BKE_modifier_is_same_topology(md) == false))
if ((ob->type == OB_MESH) && (ob->mode & OB_MODE_SCULPT) &&
find_multires_modifier_before(scene, md) && (BKE_modifier_is_same_topology(md) == false))
{
BKE_report(reports,
RPT_ERROR,
@ -1722,8 +1722,8 @@ static bool modifier_apply_poll(bContext *C)
return false;
}
if (md != nullptr) {
if ((ob->mode & OB_MODE_SCULPT) && find_multires_modifier_before(scene, md) &&
(BKE_modifier_is_same_topology(md) == false))
if ((ob->type == OB_MESH) && (ob->mode & OB_MODE_SCULPT) &&
find_multires_modifier_before(scene, md) && (BKE_modifier_is_same_topology(md) == false))
{
CTX_wm_operator_poll_msg_set(
C, "Constructive modifier cannot be applied to multi-res data in sculpt mode");
@ -2306,7 +2306,7 @@ static int multires_subdivide_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);
if (object->mode & OB_MODE_SCULPT) {
if (object->type == OB_MESH && object->mode & OB_MODE_SCULPT) {
/* ensure that grid paint mask layer is created */
BKE_sculpt_mask_layers_ensure(
CTX_data_ensure_evaluated_depsgraph(C), CTX_data_main(C), object, mmd);

View File

@ -3956,7 +3956,7 @@ static void do_symmetrical_brush_actions(Sculpt *sd,
bool SCULPT_mode_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
return ob && ob->mode & OB_MODE_SCULPT;
return ob && ob->type == OB_MESH && ob->mode & OB_MODE_SCULPT;
}
bool SCULPT_mode_poll_view3d(bContext *C)

View File

@ -279,6 +279,8 @@ namespace blender::ed::sculpt_paint {
static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
{
BLI_assert(ob->type == OB_MESH);
/* Create persistent sculpt mode data. */
BKE_sculpt_toolsettings_data_ensure(scene);
@ -352,13 +354,10 @@ void ED_object_sculptmode_enter_ex(Main *bmain,
{
using namespace blender::ed::sculpt_paint;
const int mode_flag = OB_MODE_SCULPT;
Mesh *mesh = BKE_mesh_from_object(ob);
/* Enter sculpt mode. */
ob->mode |= mode_flag;
sculpt_init_session(bmain, depsgraph, scene, ob);
if (!(fabsf(ob->scale[0] - ob->scale[1]) < 1e-4f && fabsf(ob->scale[1] - ob->scale[2]) < 1e-4f))
{
BKE_report(
@ -373,62 +372,71 @@ void ED_object_sculptmode_enter_ex(Main *bmain,
ED_paint_cursor_start(paint, SCULPT_mode_poll_view3d);
/* Check dynamic-topology flag; re-enter dynamic-topology mode when changing modes,
* As long as no data was added that is not supported. */
if (mesh->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) {
MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
if (ob->type == OB_MESH) {
Mesh *mesh = BKE_mesh_from_object(ob);
const char *message_unsupported = nullptr;
if (mesh->corners_num != mesh->faces_num * 3) {
message_unsupported = RPT_("non-triangle face");
}
else if (mmd != nullptr) {
message_unsupported = RPT_("multi-res modifier");
}
else {
dyntopo::WarnFlag flag = dyntopo::check_attribute_warning(scene, ob);
if (flag == 0) {
/* pass */
sculpt_init_session(bmain, depsgraph, scene, ob);
/* Check dynamic-topology flag; re-enter dynamic-topology mode when changing modes,
* As long as no data was added that is not supported. */
if (mesh->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) {
MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
const char *message_unsupported = nullptr;
if (mesh->corners_num != mesh->faces_num * 3) {
message_unsupported = RPT_("non-triangle face");
}
else if (flag & dyntopo::VDATA) {
message_unsupported = RPT_("vertex data");
}
else if (flag & dyntopo::EDATA) {
message_unsupported = RPT_("edge data");
}
else if (flag & dyntopo::LDATA) {
message_unsupported = RPT_("face data");
}
else if (flag & dyntopo::MODIFIER) {
message_unsupported = RPT_("constructive modifier");
else if (mmd != nullptr) {
message_unsupported = RPT_("multi-res modifier");
}
else {
BLI_assert(0);
dyntopo::WarnFlag flag = dyntopo::check_attribute_warning(scene, ob);
if (flag == 0) {
/* pass */
}
else if (flag & dyntopo::VDATA) {
message_unsupported = RPT_("vertex data");
}
else if (flag & dyntopo::EDATA) {
message_unsupported = RPT_("edge data");
}
else if (flag & dyntopo::LDATA) {
message_unsupported = RPT_("face data");
}
else if (flag & dyntopo::MODIFIER) {
message_unsupported = RPT_("constructive modifier");
}
else {
BLI_assert(0);
}
}
if ((message_unsupported == nullptr) || force_dyntopo) {
/* Needed because we may be entering this mode before the undo system loads. */
wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
bool has_undo = wm->undo_stack != nullptr;
/* Undo push is needed to prevent memory leak. */
if (has_undo) {
undo::push_begin_ex(ob, "Dynamic topology enable");
}
dyntopo::enable_ex(bmain, depsgraph, ob);
if (has_undo) {
undo::push_node(ob, nullptr, undo::Type::DyntopoBegin);
undo::push_end(ob);
}
}
else {
BKE_reportf(
reports, RPT_WARNING, "Dynamic Topology found: %s, disabled", message_unsupported);
mesh->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY;
}
}
if ((message_unsupported == nullptr) || force_dyntopo) {
/* Needed because we may be entering this mode before the undo system loads. */
wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
bool has_undo = wm->undo_stack != nullptr;
/* Undo push is needed to prevent memory leak. */
if (has_undo) {
undo::push_begin_ex(ob, "Dynamic topology enable");
}
dyntopo::enable_ex(bmain, depsgraph, ob);
if (has_undo) {
undo::push_node(ob, nullptr, undo::Type::DyntopoBegin);
undo::push_end(ob);
}
}
else {
BKE_reportf(
reports, RPT_WARNING, "Dynamic Topology found: %s, disabled", message_unsupported);
mesh->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY;
}
ensure_valid_pivot(ob, scene);
}
if (ob->type == OB_GREASE_PENCIL) {
/* TODO set up grease pencil sculpt */
}
ensure_valid_pivot(ob, scene);
/* Flush object mode. */
DEG_id_tag_update(&ob->id, ID_RECALC_SYNC_TO_EVAL);
@ -448,37 +456,42 @@ void ED_object_sculptmode_exit_ex(Main *bmain, Depsgraph *depsgraph, Scene *scen
{
using namespace blender::ed::sculpt_paint;
const int mode_flag = OB_MODE_SCULPT;
Mesh *mesh = BKE_mesh_from_object(ob);
multires_flush_sculpt_updates(ob);
if (ob->type == OB_MESH) {
Mesh *mesh = BKE_mesh_from_object(ob);
/* Not needed for now. */
multires_flush_sculpt_updates(ob);
/* Not needed for now. */
#if 0
MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd);
MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd);
#endif
/* Always for now, so leaving sculpt mode always ensures scene is in
* a consistent state. */
if (true || /* flush_recalc || */ (ob->sculpt && ob->sculpt->bm)) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
/* Always for now, so leaving sculpt mode always ensures scene is in
* a consistent state. */
if (true || /* flush_recalc || */ (ob->sculpt && ob->sculpt->bm)) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
if (mesh->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) {
/* Dynamic topology must be disabled before exiting sculpt
* mode to ensure the undo stack stays in a consistent
* state. */
dyntopo::disable_with_undo(bmain, depsgraph, scene, ob);
/* Store so we know to re-enable when entering sculpt mode. */
mesh->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY;
}
BKE_sculptsession_free(ob);
}
if (mesh->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) {
/* Dynamic topology must be disabled before exiting sculpt
* mode to ensure the undo stack stays in a consistent
* state. */
dyntopo::disable_with_undo(bmain, depsgraph, scene, ob);
/* Store so we know to re-enable when entering sculpt mode. */
mesh->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY;
if (ob->type == OB_GREASE_PENCIL) {
}
/* Leave sculpt mode. */
ob->mode &= ~mode_flag;
BKE_sculptsession_free(ob);
paint_cursor_delete_textures();
/* Never leave derived meshes behind. */
@ -529,7 +542,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports);
BKE_paint_toolslots_brush_validate(bmain, &ts->sculpt->paint);
if (ob->mode & mode_flag) {
if ((ob->type == OB_MESH) && (ob->mode & mode_flag)) {
Mesh *mesh = static_cast<Mesh *>(ob->data);
/* Dyntopo adds its own undo step. */
if ((mesh->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) == 0) {
@ -944,8 +957,8 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
BKE_sculpt_update_object_for_edit(depsgraph, ob, false);
SCULPT_vertex_random_access_ensure(ss);
/* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move,
* so it needs to be updated here. */
/* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse
* move, so it needs to be updated here. */
SculptCursorGeometryInfo sgi;
const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);

View File

@ -9,6 +9,7 @@
#include <cstdio>
#include <cstring>
#include "DNA_object_types.h"
#include "MEM_guardedalloc.h"
#include "DNA_armature_types.h"
@ -357,31 +358,36 @@ static void stats_object_pose(const Object *ob, SceneStats *stats)
static bool stats_is_object_dynamic_topology_sculpt(const Object *ob)
{
BLI_assert(ob->mode & OB_MODE_SCULPT);
return (ob->sculpt && ob->sculpt->bm);
return (ob->type == OB_MESH && ob->sculpt && ob->sculpt->bm);
}
static void stats_object_sculpt(const Object *ob, SceneStats *stats)
{
BLI_assert(ob->mode & OB_MODE_SCULPT);
if (ob->type == OB_MESH) {
SculptSession *ss = ob->sculpt;
SculptSession *ss = ob->sculpt;
if (ss == nullptr || ss->pbvh == nullptr) {
return;
}
if (ss == nullptr || ss->pbvh == nullptr) {
return;
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
stats->totvertsculpt = ss->totvert;
stats->totfacesculpt = ss->totfaces;
break;
case PBVH_BMESH:
stats->totvertsculpt = ob->sculpt->bm->totvert;
stats->tottri = ob->sculpt->bm->totface;
break;
case PBVH_GRIDS:
stats->totvertsculpt = BKE_pbvh_get_grid_num_verts(ss->pbvh);
stats->totfacesculpt = BKE_pbvh_get_grid_num_faces(ss->pbvh);
break;
}
}
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
stats->totvertsculpt = ss->totvert;
stats->totfacesculpt = ss->totfaces;
break;
case PBVH_BMESH:
stats->totvertsculpt = ob->sculpt->bm->totvert;
stats->tottri = ob->sculpt->bm->totface;
break;
case PBVH_GRIDS:
stats->totvertsculpt = BKE_pbvh_get_grid_num_verts(ss->pbvh);
stats->totfacesculpt = BKE_pbvh_get_grid_num_faces(ss->pbvh);
break;
if (ob->type == OB_GREASE_PENCIL) {
/* TODO. */
}
}

View File

@ -891,7 +891,7 @@ static TransConvertTypeInfo *convert_type_get(const TransInfo *t, Object **r_obj
return &TransConvertType_Cursor3D;
}
if (!(t->options & CTX_PAINT_CURVE) && (t->spacetype == SPACE_VIEW3D) && ob &&
(ob->mode == OB_MODE_SCULPT) && ob->sculpt)
(ob->type == OB_MESH) && (ob->mode == OB_MODE_SCULPT) && ob->sculpt)
{
return &TransConvertType_Sculpt;
}

View File

@ -886,8 +886,10 @@ static int gizmo_3d_foreach_selected(const bContext *C,
}
else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
if (ob->mode & OB_MODE_SCULPT) {
totsel = 1;
run_coord_with_matrix(ob->sculpt->pivot_pos, false, ob->object_to_world().ptr());
if (ob->sculpt) {
totsel = 1;
run_coord_with_matrix(ob->sculpt->pivot_pos, false, ob->object_to_world().ptr());
}
}
}
else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) {