mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Sculpt: Add primitive tool in Sculpt Mode.
This commit exposes the Add Mesh tools of Object and Edit Mode to Sculpt Mode. This functionality only works when Multires or Dyntopo are disabled. At a high level, the mesh drawn by the gizmo in the 3D viewport is created as a `BMesh`, converted to a `Mesh`, and then joined to the existing geometry. No new functionality or defaults are changed compared to the existing tools. Further changes are planned for future PRs. Addresses the first point of #149216 Pull Request: https://projects.blender.org/blender/blender/pulls/152856
This commit is contained in:
parent
865d6fd489
commit
9c185de6bd
7 changed files with 432 additions and 130 deletions
|
|
@ -3904,6 +3904,8 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
else ()
|
||||
),
|
||||
None,
|
||||
_tools_view3d_add,
|
||||
None,
|
||||
(
|
||||
_defs_sculpt.mask_border,
|
||||
_defs_sculpt.mask_lasso,
|
||||
|
|
|
|||
|
|
@ -3720,6 +3720,10 @@ class VIEW3D_MT_sculpt(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
layout.menu("VIEW3D_MT_add_object", text="Add Primitive")
|
||||
|
||||
layout.separator()
|
||||
|
||||
sculpt_filters_types = [
|
||||
('SMOOTH', iface_("Smooth", i18n_contexts.operator_default)),
|
||||
('SURFACE_SMOOTH', iface_("Surface Smooth")),
|
||||
|
|
@ -3837,6 +3841,33 @@ class VIEW3D_MT_sculpt_trim(Menu):
|
|||
props.trim_mode = 'JOIN'
|
||||
|
||||
|
||||
class VIEW3D_MT_add_object(Menu):
|
||||
bl_label = "Add Primitive"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
props = layout.operator("wm.tool_set_by_id", text="Add Cube")
|
||||
props.name = "builtin.primitive_cube_add"
|
||||
props.space_type = 'VIEW_3D'
|
||||
|
||||
props = layout.operator("wm.tool_set_by_id", text="Add Cylinder")
|
||||
props.name = "builtin.primitive_cylinder_add"
|
||||
props.space_type = 'VIEW_3D'
|
||||
|
||||
props = layout.operator("wm.tool_set_by_id", text="Add Cone")
|
||||
props.name = "builtin.primitive_cone_add"
|
||||
props.space_type = 'VIEW_3D'
|
||||
|
||||
props = layout.operator("wm.tool_set_by_id", text="Add UV Sphere")
|
||||
props.name = "builtin.primitive_uv_sphere_add"
|
||||
props.space_type = 'VIEW_3D'
|
||||
|
||||
props = layout.operator("wm.tool_set_by_id", text="Add Ico Cube")
|
||||
props.name = "builtin.primitive_ico_sphere_add"
|
||||
props.space_type = 'VIEW_3D'
|
||||
|
||||
|
||||
class VIEW3D_MT_sculpt_curves(Menu):
|
||||
bl_label = "Curves"
|
||||
|
||||
|
|
@ -9289,6 +9320,7 @@ classes = (
|
|||
VIEW3D_MT_sculpt_transform,
|
||||
VIEW3D_MT_sculpt_showhide,
|
||||
VIEW3D_MT_sculpt_trim,
|
||||
VIEW3D_MT_add_object,
|
||||
VIEW3D_MT_mask,
|
||||
VIEW3D_MT_face_sets,
|
||||
VIEW3D_MT_face_sets_init,
|
||||
|
|
|
|||
|
|
@ -9,14 +9,25 @@
|
|||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_editmesh.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_mesh.h"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "ED_mesh.hh"
|
||||
#include "ED_object.hh"
|
||||
#include "ED_screen.hh"
|
||||
#include "ED_sculpt.hh"
|
||||
|
||||
#include "GEO_join_geometries.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
|
|
@ -24,10 +35,6 @@
|
|||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "ED_mesh.hh"
|
||||
#include "ED_object.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "mesh_intern.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
|
@ -38,10 +45,12 @@ namespace blender {
|
|||
|
||||
struct MakePrimitiveData {
|
||||
float mat[4][4];
|
||||
bool was_editmode;
|
||||
|
||||
eContextObjectMode original_mode;
|
||||
};
|
||||
|
||||
static Object *make_prim_init(bContext *C,
|
||||
wmOperator *op,
|
||||
const char *idname,
|
||||
const float loc[3],
|
||||
const float rot[3],
|
||||
|
|
@ -51,14 +60,31 @@ static Object *make_prim_init(bContext *C,
|
|||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
Object *obedit;
|
||||
|
||||
r_creation_data->was_editmode = false;
|
||||
if (obedit == nullptr || obedit->type != OB_MESH) {
|
||||
obedit = ed::object::add_type(C, OB_MESH, idname, loc, rot, false, local_view_bits);
|
||||
ed::object::editmode_enter_ex(bmain, scene, obedit, 0);
|
||||
const enum eContextObjectMode original_mode = CTX_data_mode_enum(C);
|
||||
r_creation_data->original_mode = original_mode;
|
||||
|
||||
r_creation_data->was_editmode = true;
|
||||
switch (original_mode) {
|
||||
case CTX_MODE_OBJECT:
|
||||
obedit = ed::object::add_type(C, OB_MESH, idname, loc, rot, false, local_view_bits);
|
||||
ed::object::editmode_enter_ex(bmain, scene, obedit, 0);
|
||||
break;
|
||||
case CTX_MODE_SCULPT:
|
||||
obedit = CTX_data_active_object(C);
|
||||
ed::sculpt_paint::undo::geometry_begin(*scene, *obedit, op);
|
||||
break;
|
||||
case CTX_MODE_EDIT_MESH:
|
||||
obedit = CTX_data_edit_object(C);
|
||||
if (obedit->type != OB_MESH) {
|
||||
obedit = ed::object::add_type(C, OB_MESH, idname, loc, rot, false, local_view_bits);
|
||||
ed::object::editmode_enter_ex(bmain, scene, obedit, 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
obedit = CTX_data_active_object(C);
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
|
||||
ed::object::new_primitive_matrix(C, obedit, loc, rot, scale, r_creation_data->mat);
|
||||
|
|
@ -66,32 +92,95 @@ static Object *make_prim_init(bContext *C,
|
|||
return obedit;
|
||||
}
|
||||
|
||||
static BMesh *make_prim_init_sculpt()
|
||||
{
|
||||
const BMAllocTemplate allocsize{.totvert = 0, .totedge = 0, .totloop = 0, .totface = 0};
|
||||
|
||||
BMeshCreateParams bm_create_params{};
|
||||
bm_create_params.use_toolflags = true;
|
||||
BMesh *bm = BM_mesh_create(&allocsize, &bm_create_params);
|
||||
|
||||
return bm;
|
||||
}
|
||||
|
||||
static void make_prim_finish_sculpt_cancelled(BMesh *bm)
|
||||
{
|
||||
BM_mesh_free(bm);
|
||||
}
|
||||
|
||||
static void init_facesets(Mesh *object_mesh, Mesh *primitive_mesh)
|
||||
{
|
||||
bke::AttributeAccessor object_attributes = object_mesh->attributes();
|
||||
bke::AttributeReader<int> object_face_sets = object_attributes.lookup<int>(".sculpt_face_set");
|
||||
if (!object_face_sets) {
|
||||
return;
|
||||
}
|
||||
|
||||
bke::MutableAttributeAccessor primitive_attributes = primitive_mesh->attributes_for_write();
|
||||
bke::SpanAttributeWriter<int> primitive_face_sets =
|
||||
primitive_attributes.lookup_or_add_for_write_span<int>(".sculpt_face_set",
|
||||
bke::AttrDomain::Face);
|
||||
|
||||
primitive_face_sets.span.fill(object_mesh->face_sets_color_default);
|
||||
primitive_face_sets.finish();
|
||||
}
|
||||
|
||||
static void make_prim_finish_sculpt(bContext *C, Object *ob, BMesh *bm)
|
||||
{
|
||||
Mesh *object_mesh = id_cast<Mesh *>(ob->data);
|
||||
|
||||
BMeshToMeshParams bm_to_mesh_params{};
|
||||
bm_to_mesh_params.calc_object_remap = false;
|
||||
Mesh *primitive_mesh = BKE_mesh_from_bmesh_nomain(bm, &bm_to_mesh_params, object_mesh);
|
||||
BM_mesh_free(bm);
|
||||
|
||||
init_facesets(object_mesh, primitive_mesh);
|
||||
|
||||
bke::GeometrySet joined = geometry::join_geometries(
|
||||
{bke::GeometrySet::from_mesh(object_mesh, bke::GeometryOwnershipType::ReadOnly),
|
||||
bke::GeometrySet::from_mesh(primitive_mesh, bke::GeometryOwnershipType::ReadOnly)},
|
||||
{});
|
||||
|
||||
Mesh *result = joined.get_component_for_write<bke::MeshComponent>().release();
|
||||
|
||||
BKE_id_free(CTX_data_main(C), primitive_mesh);
|
||||
BKE_mesh_nomain_to_mesh(result, object_mesh, ob);
|
||||
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, object_mesh);
|
||||
}
|
||||
|
||||
static void make_prim_finish(bContext *C,
|
||||
Object *obedit,
|
||||
const MakePrimitiveData *creation_data,
|
||||
int enter_editmode)
|
||||
{
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
const bool exit_editmode = ((creation_data->was_editmode == true) && (enter_editmode == false));
|
||||
|
||||
/* Primitive has all verts selected, use vert select flush
|
||||
* to push this up to edges & faces. */
|
||||
EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX);
|
||||
/* TODO(@ideasman42): maintain UV sync for newly created data. */
|
||||
EDBM_uvselect_clear(em);
|
||||
BLI_assert(
|
||||
ELEM(creation_data->original_mode, CTX_MODE_OBJECT, CTX_MODE_SCULPT, CTX_MODE_EDIT_MESH));
|
||||
|
||||
/* Only recalculate edit-mode tessellation if we are staying in edit-mode. */
|
||||
EDBMUpdate_Params params{};
|
||||
params.calc_looptris = !exit_editmode;
|
||||
params.calc_normals = false;
|
||||
params.is_destructive = true;
|
||||
EDBM_update(id_cast<Mesh *>(obedit->data), ¶ms);
|
||||
|
||||
/* userdef */
|
||||
if (exit_editmode) {
|
||||
ed::object::editmode_exit_ex(
|
||||
CTX_data_main(C), CTX_data_scene(C), obedit, ed::object::EM_FREEDATA);
|
||||
if (creation_data->original_mode == CTX_MODE_SCULPT) {
|
||||
ed::sculpt_paint::undo::geometry_end(*obedit);
|
||||
}
|
||||
else {
|
||||
EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX);
|
||||
/* TODO(@ideasman42): maintain UV sync for newly created data. */
|
||||
EDBM_uvselect_clear(em);
|
||||
|
||||
/* Only recalculate edit-mode tessellation if we are staying in edit-mode. */
|
||||
EDBMUpdate_Params params{};
|
||||
params.calc_looptris = creation_data->original_mode == CTX_MODE_EDIT_MESH || enter_editmode;
|
||||
params.calc_normals = false;
|
||||
params.is_destructive = true;
|
||||
EDBM_update(id_cast<Mesh *>(obedit->data), ¶ms);
|
||||
|
||||
if (creation_data->original_mode == CTX_MODE_OBJECT && !enter_editmode) {
|
||||
ed::object::editmode_exit_ex(
|
||||
CTX_data_main(C), CTX_data_scene(C), obedit, ed::object::EM_FREEDATA);
|
||||
}
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obedit);
|
||||
}
|
||||
|
||||
|
|
@ -109,6 +198,7 @@ static wmOperatorStatus add_primitive_plane_exec(bContext *C, wmOperator *op)
|
|||
ed::object::add_generic_get_opts(
|
||||
C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr);
|
||||
obedit = make_prim_init(C,
|
||||
op,
|
||||
CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Plane"),
|
||||
loc,
|
||||
rot,
|
||||
|
|
@ -165,7 +255,6 @@ static wmOperatorStatus add_primitive_cube_exec(bContext *C, wmOperator *op)
|
|||
{
|
||||
MakePrimitiveData creation_data;
|
||||
Object *obedit;
|
||||
BMEditMesh *em;
|
||||
float loc[3], rot[3], scale[3];
|
||||
bool enter_editmode;
|
||||
ushort local_view_bits;
|
||||
|
|
@ -175,6 +264,7 @@ static wmOperatorStatus add_primitive_cube_exec(bContext *C, wmOperator *op)
|
|||
ed::object::add_generic_get_opts(
|
||||
C, op, 'Z', loc, rot, scale, &enter_editmode, &local_view_bits, nullptr);
|
||||
obedit = make_prim_init(C,
|
||||
op,
|
||||
CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Cube"),
|
||||
loc,
|
||||
rot,
|
||||
|
|
@ -182,22 +272,40 @@ static wmOperatorStatus add_primitive_cube_exec(bContext *C, wmOperator *op)
|
|||
local_view_bits,
|
||||
&creation_data);
|
||||
|
||||
em = BKE_editmesh_from_object(obedit);
|
||||
if (creation_data.original_mode == CTX_MODE_SCULPT) {
|
||||
BMesh *bm = make_prim_init_sculpt();
|
||||
|
||||
if (calc_uvs) {
|
||||
ED_mesh_uv_ensure(id_cast<Mesh *>(obedit->data), nullptr);
|
||||
if (!BMO_op_callf(bm,
|
||||
BMO_FLAG_DEFAULTS,
|
||||
"create_cube matrix=%m4 size=%f calc_uvs=%b",
|
||||
creation_data.mat,
|
||||
RNA_float_get(op->ptr, "size"),
|
||||
calc_uvs))
|
||||
{
|
||||
make_prim_finish_sculpt_cancelled(bm);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
make_prim_finish_sculpt(C, obedit, bm);
|
||||
}
|
||||
else {
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
|
||||
if (!EDBM_op_call_and_selectf(em,
|
||||
op,
|
||||
"verts.out",
|
||||
false,
|
||||
"create_cube matrix=%m4 size=%f calc_uvs=%b",
|
||||
creation_data.mat,
|
||||
RNA_float_get(op->ptr, "size"),
|
||||
calc_uvs))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
if (calc_uvs) {
|
||||
ED_mesh_uv_ensure(id_cast<Mesh *>(obedit->data), nullptr);
|
||||
}
|
||||
|
||||
if (!EDBM_op_call_and_selectf(em,
|
||||
op,
|
||||
"verts.out",
|
||||
false,
|
||||
"create_cube matrix=%m4 size=%f calc_uvs=%b",
|
||||
creation_data.mat,
|
||||
RNA_float_get(op->ptr, "size"),
|
||||
calc_uvs))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
/* BMESH_TODO make plane side this: M_SQRT2 - plane (diameter of 1.41 makes it unit size) */
|
||||
|
|
@ -250,6 +358,7 @@ static wmOperatorStatus add_primitive_circle_exec(bContext *C, wmOperator *op)
|
|||
ed::object::add_generic_get_opts(
|
||||
C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr);
|
||||
obedit = make_prim_init(C,
|
||||
op,
|
||||
CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Circle"),
|
||||
loc,
|
||||
rot,
|
||||
|
|
@ -311,7 +420,6 @@ static wmOperatorStatus add_primitive_cylinder_exec(bContext *C, wmOperator *op)
|
|||
{
|
||||
MakePrimitiveData creation_data;
|
||||
Object *obedit;
|
||||
BMEditMesh *em;
|
||||
float loc[3], rot[3], scale[3];
|
||||
bool enter_editmode;
|
||||
ushort local_view_bits;
|
||||
|
|
@ -324,34 +432,60 @@ static wmOperatorStatus add_primitive_cylinder_exec(bContext *C, wmOperator *op)
|
|||
ed::object::add_generic_get_opts(
|
||||
C, op, 'Z', loc, rot, scale, &enter_editmode, &local_view_bits, nullptr);
|
||||
obedit = make_prim_init(C,
|
||||
op,
|
||||
CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Cylinder"),
|
||||
loc,
|
||||
rot,
|
||||
scale,
|
||||
local_view_bits,
|
||||
&creation_data);
|
||||
em = BKE_editmesh_from_object(obedit);
|
||||
|
||||
if (calc_uvs) {
|
||||
ED_mesh_uv_ensure(id_cast<Mesh *>(obedit->data), nullptr);
|
||||
if (creation_data.original_mode == CTX_MODE_SCULPT) {
|
||||
BMesh *bm = make_prim_init_sculpt();
|
||||
|
||||
if (!BMO_op_callf(bm,
|
||||
BMO_FLAG_DEFAULTS,
|
||||
"create_cone segments=%i radius1=%f radius2=%f cap_ends=%b "
|
||||
"cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b",
|
||||
RNA_int_get(op->ptr, "vertices"),
|
||||
RNA_float_get(op->ptr, "radius"),
|
||||
RNA_float_get(op->ptr, "radius"),
|
||||
cap_end,
|
||||
cap_tri,
|
||||
RNA_float_get(op->ptr, "depth"),
|
||||
creation_data.mat,
|
||||
calc_uvs))
|
||||
{
|
||||
make_prim_finish_sculpt_cancelled(bm);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
make_prim_finish_sculpt(C, obedit, bm);
|
||||
}
|
||||
else {
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
|
||||
if (!EDBM_op_call_and_selectf(em,
|
||||
op,
|
||||
"verts.out",
|
||||
false,
|
||||
"create_cone segments=%i radius1=%f radius2=%f cap_ends=%b "
|
||||
"cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b",
|
||||
RNA_int_get(op->ptr, "vertices"),
|
||||
RNA_float_get(op->ptr, "radius"),
|
||||
RNA_float_get(op->ptr, "radius"),
|
||||
cap_end,
|
||||
cap_tri,
|
||||
RNA_float_get(op->ptr, "depth"),
|
||||
creation_data.mat,
|
||||
calc_uvs))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
if (calc_uvs) {
|
||||
ED_mesh_uv_ensure(id_cast<Mesh *>(obedit->data), nullptr);
|
||||
}
|
||||
|
||||
if (!EDBM_op_call_and_selectf(em,
|
||||
op,
|
||||
"verts.out",
|
||||
false,
|
||||
"create_cone segments=%i radius1=%f radius2=%f cap_ends=%b "
|
||||
"cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b",
|
||||
RNA_int_get(op->ptr, "vertices"),
|
||||
RNA_float_get(op->ptr, "radius"),
|
||||
RNA_float_get(op->ptr, "radius"),
|
||||
cap_end,
|
||||
cap_tri,
|
||||
RNA_float_get(op->ptr, "depth"),
|
||||
creation_data.mat,
|
||||
calc_uvs))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
make_prim_finish(C, obedit, &creation_data, enter_editmode);
|
||||
|
|
@ -388,7 +522,6 @@ static wmOperatorStatus add_primitive_cone_exec(bContext *C, wmOperator *op)
|
|||
{
|
||||
MakePrimitiveData creation_data;
|
||||
Object *obedit;
|
||||
BMEditMesh *em;
|
||||
float loc[3], rot[3], scale[3];
|
||||
bool enter_editmode;
|
||||
ushort local_view_bits;
|
||||
|
|
@ -401,34 +534,60 @@ static wmOperatorStatus add_primitive_cone_exec(bContext *C, wmOperator *op)
|
|||
ed::object::add_generic_get_opts(
|
||||
C, op, 'Z', loc, rot, scale, &enter_editmode, &local_view_bits, nullptr);
|
||||
obedit = make_prim_init(C,
|
||||
op,
|
||||
CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Cone"),
|
||||
loc,
|
||||
rot,
|
||||
scale,
|
||||
local_view_bits,
|
||||
&creation_data);
|
||||
em = BKE_editmesh_from_object(obedit);
|
||||
|
||||
if (calc_uvs) {
|
||||
ED_mesh_uv_ensure(id_cast<Mesh *>(obedit->data), nullptr);
|
||||
if (creation_data.original_mode == CTX_MODE_SCULPT) {
|
||||
BMesh *bm = make_prim_init_sculpt();
|
||||
|
||||
if (!BMO_op_callf(bm,
|
||||
BMO_FLAG_DEFAULTS,
|
||||
"create_cone segments=%i radius1=%f radius2=%f cap_ends=%b "
|
||||
"cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b",
|
||||
RNA_int_get(op->ptr, "vertices"),
|
||||
RNA_float_get(op->ptr, "radius1"),
|
||||
RNA_float_get(op->ptr, "radius2"),
|
||||
cap_end,
|
||||
cap_tri,
|
||||
RNA_float_get(op->ptr, "depth"),
|
||||
creation_data.mat,
|
||||
calc_uvs))
|
||||
{
|
||||
make_prim_finish_sculpt_cancelled(bm);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
make_prim_finish_sculpt(C, obedit, bm);
|
||||
}
|
||||
else {
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
|
||||
if (!EDBM_op_call_and_selectf(em,
|
||||
op,
|
||||
"verts.out",
|
||||
false,
|
||||
"create_cone segments=%i radius1=%f radius2=%f cap_ends=%b "
|
||||
"cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b",
|
||||
RNA_int_get(op->ptr, "vertices"),
|
||||
RNA_float_get(op->ptr, "radius1"),
|
||||
RNA_float_get(op->ptr, "radius2"),
|
||||
cap_end,
|
||||
cap_tri,
|
||||
RNA_float_get(op->ptr, "depth"),
|
||||
creation_data.mat,
|
||||
calc_uvs))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
if (calc_uvs) {
|
||||
ED_mesh_uv_ensure(id_cast<Mesh *>(obedit->data), nullptr);
|
||||
}
|
||||
|
||||
if (!EDBM_op_call_and_selectf(em,
|
||||
op,
|
||||
"verts.out",
|
||||
false,
|
||||
"create_cone segments=%i radius1=%f radius2=%f cap_ends=%b "
|
||||
"cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b",
|
||||
RNA_int_get(op->ptr, "vertices"),
|
||||
RNA_float_get(op->ptr, "radius1"),
|
||||
RNA_float_get(op->ptr, "radius2"),
|
||||
cap_end,
|
||||
cap_tri,
|
||||
RNA_float_get(op->ptr, "depth"),
|
||||
creation_data.mat,
|
||||
calc_uvs))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
make_prim_finish(C, obedit, &creation_data, enter_editmode);
|
||||
|
|
@ -478,6 +637,7 @@ static wmOperatorStatus add_primitive_grid_exec(bContext *C, wmOperator *op)
|
|||
ed::object::add_generic_get_opts(
|
||||
C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr);
|
||||
obedit = make_prim_init(C,
|
||||
op,
|
||||
CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Grid"),
|
||||
loc,
|
||||
rot,
|
||||
|
|
@ -553,6 +713,7 @@ static wmOperatorStatus add_primitive_monkey_exec(bContext *C, wmOperator *op)
|
|||
C, op, 'Y', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr);
|
||||
|
||||
obedit = make_prim_init(C,
|
||||
op,
|
||||
CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Suzanne"),
|
||||
loc,
|
||||
rot,
|
||||
|
|
@ -608,7 +769,6 @@ static wmOperatorStatus add_primitive_uvsphere_exec(bContext *C, wmOperator *op)
|
|||
{
|
||||
MakePrimitiveData creation_data;
|
||||
Object *obedit;
|
||||
BMEditMesh *em;
|
||||
float loc[3], rot[3], scale[3];
|
||||
bool enter_editmode;
|
||||
ushort local_view_bits;
|
||||
|
|
@ -618,31 +778,54 @@ static wmOperatorStatus add_primitive_uvsphere_exec(bContext *C, wmOperator *op)
|
|||
ed::object::add_generic_get_opts(
|
||||
C, op, 'Z', loc, rot, scale, &enter_editmode, &local_view_bits, nullptr);
|
||||
obedit = make_prim_init(C,
|
||||
op,
|
||||
CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Sphere"),
|
||||
loc,
|
||||
rot,
|
||||
scale,
|
||||
local_view_bits,
|
||||
&creation_data);
|
||||
em = BKE_editmesh_from_object(obedit);
|
||||
|
||||
if (calc_uvs) {
|
||||
ED_mesh_uv_ensure(id_cast<Mesh *>(obedit->data), nullptr);
|
||||
if (creation_data.original_mode == CTX_MODE_SCULPT) {
|
||||
BMesh *bm = make_prim_init_sculpt();
|
||||
|
||||
if (!BMO_op_callf(
|
||||
bm,
|
||||
BMO_FLAG_DEFAULTS,
|
||||
"create_uvsphere u_segments=%i v_segments=%i radius=%f matrix=%m4 calc_uvs=%b",
|
||||
RNA_int_get(op->ptr, "segments"),
|
||||
RNA_int_get(op->ptr, "ring_count"),
|
||||
RNA_float_get(op->ptr, "radius"),
|
||||
creation_data.mat,
|
||||
calc_uvs))
|
||||
{
|
||||
make_prim_finish_sculpt_cancelled(bm);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
make_prim_finish_sculpt(C, obedit, bm);
|
||||
}
|
||||
else {
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
|
||||
if (!EDBM_op_call_and_selectf(
|
||||
em,
|
||||
op,
|
||||
"verts.out",
|
||||
false,
|
||||
"create_uvsphere u_segments=%i v_segments=%i radius=%f matrix=%m4 calc_uvs=%b",
|
||||
RNA_int_get(op->ptr, "segments"),
|
||||
RNA_int_get(op->ptr, "ring_count"),
|
||||
RNA_float_get(op->ptr, "radius"),
|
||||
creation_data.mat,
|
||||
calc_uvs))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
if (calc_uvs) {
|
||||
ED_mesh_uv_ensure(id_cast<Mesh *>(obedit->data), nullptr);
|
||||
}
|
||||
|
||||
if (!EDBM_op_call_and_selectf(
|
||||
em,
|
||||
op,
|
||||
"verts.out",
|
||||
false,
|
||||
"create_uvsphere u_segments=%i v_segments=%i radius=%f matrix=%m4 calc_uvs=%b",
|
||||
RNA_int_get(op->ptr, "segments"),
|
||||
RNA_int_get(op->ptr, "ring_count"),
|
||||
RNA_float_get(op->ptr, "radius"),
|
||||
creation_data.mat,
|
||||
calc_uvs))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
make_prim_finish(C, obedit, &creation_data, enter_editmode);
|
||||
|
|
@ -679,7 +862,6 @@ static wmOperatorStatus add_primitive_icosphere_exec(bContext *C, wmOperator *op
|
|||
{
|
||||
MakePrimitiveData creation_data;
|
||||
Object *obedit;
|
||||
BMEditMesh *em;
|
||||
float loc[3], rot[3], scale[3];
|
||||
bool enter_editmode;
|
||||
ushort local_view_bits;
|
||||
|
|
@ -689,30 +871,51 @@ static wmOperatorStatus add_primitive_icosphere_exec(bContext *C, wmOperator *op
|
|||
ed::object::add_generic_get_opts(
|
||||
C, op, 'Z', loc, rot, scale, &enter_editmode, &local_view_bits, nullptr);
|
||||
obedit = make_prim_init(C,
|
||||
op,
|
||||
CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Icosphere"),
|
||||
loc,
|
||||
rot,
|
||||
scale,
|
||||
local_view_bits,
|
||||
&creation_data);
|
||||
em = BKE_editmesh_from_object(obedit);
|
||||
|
||||
if (calc_uvs) {
|
||||
ED_mesh_uv_ensure(id_cast<Mesh *>(obedit->data), nullptr);
|
||||
if (creation_data.original_mode == CTX_MODE_SCULPT) {
|
||||
BMesh *bm = make_prim_init_sculpt();
|
||||
|
||||
if (!BMO_op_callf(bm,
|
||||
BMO_FLAG_DEFAULTS,
|
||||
"create_icosphere subdivisions=%i radius=%f matrix=%m4 calc_uvs=%b",
|
||||
RNA_int_get(op->ptr, "subdivisions"),
|
||||
RNA_float_get(op->ptr, "radius"),
|
||||
creation_data.mat,
|
||||
calc_uvs))
|
||||
{
|
||||
make_prim_finish_sculpt_cancelled(bm);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
make_prim_finish_sculpt(C, obedit, bm);
|
||||
}
|
||||
else {
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
|
||||
if (!EDBM_op_call_and_selectf(
|
||||
em,
|
||||
op,
|
||||
"verts.out",
|
||||
false,
|
||||
"create_icosphere subdivisions=%i radius=%f matrix=%m4 calc_uvs=%b",
|
||||
RNA_int_get(op->ptr, "subdivisions"),
|
||||
RNA_float_get(op->ptr, "radius"),
|
||||
creation_data.mat,
|
||||
calc_uvs))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
if (calc_uvs) {
|
||||
ED_mesh_uv_ensure(id_cast<Mesh *>(obedit->data), nullptr);
|
||||
}
|
||||
|
||||
if (!EDBM_op_call_and_selectf(
|
||||
em,
|
||||
op,
|
||||
"verts.out",
|
||||
false,
|
||||
"create_icosphere subdivisions=%i radius=%f matrix=%m4 calc_uvs=%b",
|
||||
RNA_int_get(op->ptr, "subdivisions"),
|
||||
RNA_float_get(op->ptr, "radius"),
|
||||
creation_data.mat,
|
||||
calc_uvs))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
make_prim_finish(C, obedit, &creation_data, enter_editmode);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_paint.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
|
@ -1304,6 +1306,13 @@ static wmOperatorStatus view3d_interactive_add_modal(bContext *C,
|
|||
static bool view3d_interactive_add_poll(bContext *C)
|
||||
{
|
||||
const enum eContextObjectMode mode = CTX_data_mode_enum(C);
|
||||
|
||||
if (mode == CTX_MODE_SCULPT) {
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
return !BKE_sculpt_multires_active(CTX_data_scene(C), ob) &&
|
||||
!BKE_object_sculpt_use_dyntopo(ob);
|
||||
}
|
||||
|
||||
return ELEM(mode, CTX_MODE_OBJECT, CTX_MODE_EDIT_MESH);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1499,6 +1499,7 @@ if(WITH_UI_TESTS)
|
|||
test_sculpt.mask_expand_and_invert
|
||||
test_sculpt.mask_gestures
|
||||
test_sculpt.trim_gestures
|
||||
test_sculpt.primitive_tool_add
|
||||
test_tools.sculpt_mode_toolbar
|
||||
test_undo.compositor_make_group
|
||||
test_undo.text_editor_edit_mode_mix
|
||||
|
|
|
|||
|
|
@ -312,3 +312,38 @@ def trim_gestures():
|
|||
yield from ui.call_operator(e, "Line Trim")
|
||||
yield from e.leftmouse.cursor_motion(ui.cursor_motion_data_y(window))
|
||||
t.assertEqual(mesh.attributes.domain_size('POINT'), 10)
|
||||
|
||||
|
||||
def primitive_tool_add():
|
||||
import bpy
|
||||
e, t, window = ui.test_window()
|
||||
|
||||
yield e.ctrl.tab().s() # Sculpt via pie menu.
|
||||
|
||||
yield from ui.call_menu(e, "Sculpt -> Add Primitive -> Add Cube") # Select add cube tool
|
||||
|
||||
area = ui.get_window_area_by_type(window, 'VIEW_3D')
|
||||
position = (area.x + area.width // 2, area.y + area.height // 2)
|
||||
yield e.cursor_position_set(*position, move=True) # Move mouse to center
|
||||
|
||||
e.leftmouse.press()
|
||||
yield
|
||||
|
||||
pixels = 10
|
||||
for delta in range(pixels):
|
||||
position = (position[0] + delta, position[1] + delta)
|
||||
yield e.cursor_position_set(*position, move=True)
|
||||
|
||||
e.leftmouse.release()
|
||||
yield
|
||||
|
||||
for delta in range(pixels):
|
||||
position = (position[0] - delta, position[1] - delta)
|
||||
yield e.cursor_position_set(*position, move=True)
|
||||
|
||||
e.leftmouse.tap()
|
||||
yield
|
||||
|
||||
mesh = bpy.context.object.data
|
||||
num_faces = mesh.attributes.domain_size('FACE')
|
||||
t.assertEqual(num_faces, 12) # There should be 6 + 6 faces
|
||||
|
|
|
|||
|
|
@ -38,36 +38,56 @@ def sculpt_mode_toolbar():
|
|||
yield e.four()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin_brush.draw_face_sets")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.five()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.primitive_cube_add")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.six()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.primitive_cone_add")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.seven()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.primitive_cylinder_add")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.eight()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.primitive_uv_sphere_add")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.nine()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.primitive_ico_sphere_add")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.b()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.box_mask")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.eight()
|
||||
yield e.shift.three()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.box_hide")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.shift.two()
|
||||
yield e.shift.seven()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.box_face_set")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.shift.six()
|
||||
yield e.ctrl.one()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.box_trim")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.shift.zero()
|
||||
yield e.ctrl.five()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.line_project")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.ctrl.one()
|
||||
yield e.ctrl.six()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.mesh_filter")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.ctrl.two()
|
||||
yield e.ctrl.seven()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.cloth_filter")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.ctrl.three()
|
||||
yield e.ctrl.eight()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.color_filter")
|
||||
|
||||
yield e.shift.space()
|
||||
|
|
@ -75,19 +95,19 @@ def sculpt_mode_toolbar():
|
|||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.face_set_edit")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.ctrl.four()
|
||||
yield e.ctrl.nine()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.mask_by_color")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.ctrl.five()
|
||||
yield e.ctrl.zero()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.move")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.ctrl.six()
|
||||
yield e.alt.one()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.rotate")
|
||||
|
||||
yield e.shift.space()
|
||||
yield e.ctrl.seven()
|
||||
yield e.alt.two()
|
||||
t.assertEqual(window.workspace.tools.from_space_view3d_mode('SCULPT').idname, "builtin.scale")
|
||||
|
||||
yield e.shift.space()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue