Objects: new 3D cursor alignment option when adding objects
The choices are now World, View and 3D Cursor. This breaks Python API compatibility, add-ons that add objects with this parameter will need to be updated. Differential Revision: https://developer.blender.org/D4706
This commit is contained in:
@@ -34,6 +34,7 @@ import bpy
|
|||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
BoolProperty,
|
BoolProperty,
|
||||||
FloatVectorProperty,
|
FloatVectorProperty,
|
||||||
|
EnumProperty,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -66,36 +67,32 @@ def add_object_align_init(context, operator):
|
|||||||
properties.location = location.to_translation()
|
properties.location = location.to_translation()
|
||||||
|
|
||||||
# rotation
|
# rotation
|
||||||
view_align = (context.preferences.edit.object_align == 'VIEW')
|
add_align_preference = context.preferences.edit.object_align
|
||||||
view_align_force = False
|
|
||||||
if operator:
|
if operator:
|
||||||
if properties.is_property_set("view_align"):
|
if not properties.is_property_set("rotation"):
|
||||||
view_align = view_align_force = operator.view_align
|
# So one of "align" and "rotation" will be set
|
||||||
else:
|
properties.align = add_align_preference
|
||||||
if properties.is_property_set("rotation"):
|
|
||||||
# ugh, 'view_align' callback resets
|
|
||||||
value = properties.rotation[:]
|
|
||||||
properties.view_align = view_align
|
|
||||||
properties.rotation = value
|
|
||||||
del value
|
|
||||||
else:
|
|
||||||
properties.view_align = view_align
|
|
||||||
|
|
||||||
if operator and (properties.is_property_set("rotation") and
|
if properties.align == 'WORLD':
|
||||||
not view_align_force):
|
rotation = properties.rotation.to_matrix().to_4x4()
|
||||||
|
elif properties.align == 'VIEW':
|
||||||
rotation = Euler(properties.rotation).to_matrix().to_4x4()
|
|
||||||
else:
|
|
||||||
if view_align and space_data:
|
|
||||||
rotation = space_data.region_3d.view_matrix.to_3x3().inverted()
|
rotation = space_data.region_3d.view_matrix.to_3x3().inverted()
|
||||||
rotation.resize_4x4()
|
rotation.resize_4x4()
|
||||||
|
properties.rotation = rotation.to_euler()
|
||||||
|
elif properties.align == 'CURSOR':
|
||||||
|
rotation = context.scene.cursor.rotation_euler.to_matrix().to_4x4()
|
||||||
|
properties.rotation = rotation.to_euler()
|
||||||
|
else:
|
||||||
|
rotation = properties.rotation.to_matrix().to_4x4()
|
||||||
|
else:
|
||||||
|
if (add_align_preference == 'VIEW') and space_data:
|
||||||
|
rotation = space_data.region_3d.view_matrix.to_3x3().inverted()
|
||||||
|
rotation.resize_4x4()
|
||||||
|
elif add_align_preference == 'CURSOR':
|
||||||
|
rotation = context.scene.cursor.rotation_euler.to_matrix().to_4x4()
|
||||||
else:
|
else:
|
||||||
rotation = Matrix()
|
rotation = Matrix()
|
||||||
|
|
||||||
# set the operator properties
|
|
||||||
if operator:
|
|
||||||
properties.rotation = rotation.to_euler()
|
|
||||||
|
|
||||||
return location @ rotation
|
return location @ rotation
|
||||||
|
|
||||||
|
|
||||||
@@ -168,14 +165,20 @@ def object_data_add(context, obdata, operator=None, name=None):
|
|||||||
|
|
||||||
|
|
||||||
class AddObjectHelper:
|
class AddObjectHelper:
|
||||||
def view_align_update_callback(self, _context):
|
def align_update_callback(self, _context):
|
||||||
if not self.view_align:
|
if self.align == 'WORLD':
|
||||||
self.rotation.zero()
|
self.rotation.zero()
|
||||||
|
|
||||||
view_align: BoolProperty(
|
align_items = (
|
||||||
name="Align to View",
|
('WORLD', "World", "Align the new object to the world"),
|
||||||
default=False,
|
('VIEW', "View", "Align the new object to the view"),
|
||||||
update=view_align_update_callback,
|
('CURSOR', "3D Cursor", "Use the 3D cursor orientation for the new object")
|
||||||
|
)
|
||||||
|
align: EnumProperty(
|
||||||
|
name="Align",
|
||||||
|
items=align_items,
|
||||||
|
default='WORLD',
|
||||||
|
update=align_update_callback,
|
||||||
)
|
)
|
||||||
location: FloatVectorProperty(
|
location: FloatVectorProperty(
|
||||||
name="Location",
|
name="Location",
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ class AddTorus(Operator, object_utils.AddObjectHelper):
|
|||||||
col = layout.column(align=True)
|
col = layout.column(align=True)
|
||||||
col.prop(self, "generate_uvs")
|
col.prop(self, "generate_uvs")
|
||||||
col.separator()
|
col.separator()
|
||||||
col.prop(self, "view_align")
|
col.prop(self, "align")
|
||||||
|
|
||||||
col = layout.column(align=True)
|
col = layout.column(align=True)
|
||||||
col.label(text="Location")
|
col.label(text="Location")
|
||||||
|
|||||||
@@ -912,7 +912,7 @@ class LoadImageAsEmpty:
|
|||||||
'INVOKE_REGION_WIN',
|
'INVOKE_REGION_WIN',
|
||||||
type='IMAGE',
|
type='IMAGE',
|
||||||
location=cursor,
|
location=cursor,
|
||||||
view_align=self.view_align,
|
align=('VIEW' if self.view_align else 'WORLD'),
|
||||||
)
|
)
|
||||||
|
|
||||||
obj = context.active_object
|
obj = context.active_object
|
||||||
|
|||||||
@@ -75,9 +75,16 @@ class AddBox(bpy.types.Operator):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# generic transform props
|
# generic transform props
|
||||||
view_align: BoolProperty(
|
align_items = (
|
||||||
name="Align to View",
|
('WORLD', "World", "Align the new object to the world"),
|
||||||
default=False,
|
('VIEW', "View", "Align the new object to the view"),
|
||||||
|
('CURSOR', "3D Cursor", "Use the 3D cursor orientation for the new object")
|
||||||
|
)
|
||||||
|
align: EnumProperty(
|
||||||
|
name="Align",
|
||||||
|
items=align_items,
|
||||||
|
default='WORLD',
|
||||||
|
update=AddObjectHelper.align_update_callback,
|
||||||
)
|
)
|
||||||
location: FloatVectorProperty(
|
location: FloatVectorProperty(
|
||||||
name="Location",
|
name="Location",
|
||||||
|
|||||||
@@ -161,6 +161,18 @@ static EnumPropertyItem lightprobe_type_items[] = {
|
|||||||
{0, NULL, 0, NULL, NULL},
|
{0, NULL, 0, NULL, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ObjectAlign {
|
||||||
|
ALIGN_WORLD,
|
||||||
|
ALIGN_VIEW,
|
||||||
|
ALIGN_CURSOR,
|
||||||
|
} ALIGN_OPTIONS;
|
||||||
|
|
||||||
|
static const EnumPropertyItem align_options[] = {
|
||||||
|
{ALIGN_WORLD, "WORLD", 0, "World", "Align the new object to the world"},
|
||||||
|
{ALIGN_VIEW, "VIEW", 0, "View", "Align the new object to the view"},
|
||||||
|
{ALIGN_CURSOR, "CURSOR", 0, "3D Cursor", "Use the 3D cursor orientation for the new object"},
|
||||||
|
{0, NULL, 0, NULL, NULL}};
|
||||||
|
|
||||||
/************************** Exported *****************************/
|
/************************** Exported *****************************/
|
||||||
|
|
||||||
void ED_object_location_from_view(bContext *C, float loc[3])
|
void ED_object_location_from_view(bContext *C, float loc[3])
|
||||||
@@ -291,16 +303,15 @@ void ED_object_add_generic_props(wmOperatorType *ot, bool do_editmode)
|
|||||||
{
|
{
|
||||||
PropertyRNA *prop;
|
PropertyRNA *prop;
|
||||||
|
|
||||||
/* note: this property gets hidden for add-camera operator */
|
|
||||||
prop = RNA_def_boolean(
|
|
||||||
ot->srna, "view_align", 0, "Align to View", "Align the new object to the view");
|
|
||||||
RNA_def_property_update_runtime(prop, view_align_update);
|
|
||||||
|
|
||||||
if (do_editmode) {
|
if (do_editmode) {
|
||||||
prop = RNA_def_boolean(
|
prop = RNA_def_boolean(
|
||||||
ot->srna, "enter_editmode", 0, "Enter Editmode", "Enter editmode when adding this object");
|
ot->srna, "enter_editmode", 0, "Enter Editmode", "Enter editmode when adding this object");
|
||||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||||
}
|
}
|
||||||
|
/* note: this property gets hidden for add-camera operator */
|
||||||
|
prop = RNA_def_enum(
|
||||||
|
ot->srna, "align", align_options, ALIGN_WORLD, "Align", "The alignment of the new object");
|
||||||
|
RNA_def_property_update_runtime(prop, view_align_update);
|
||||||
|
|
||||||
prop = RNA_def_float_vector_xyz(ot->srna,
|
prop = RNA_def_float_vector_xyz(ot->srna,
|
||||||
"location",
|
"location",
|
||||||
@@ -392,23 +403,42 @@ bool ED_object_add_generic_get_opts(bContext *C,
|
|||||||
rot = _rot;
|
rot = _rot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prop = RNA_struct_find_property(op->ptr, "align");
|
||||||
|
int alignment = RNA_property_enum_get(op->ptr, prop);
|
||||||
|
bool alignment_set = RNA_property_is_set(op->ptr, prop);
|
||||||
|
|
||||||
if (RNA_struct_property_is_set(op->ptr, "rotation")) {
|
if (RNA_struct_property_is_set(op->ptr, "rotation")) {
|
||||||
*is_view_aligned = false;
|
*is_view_aligned = false;
|
||||||
}
|
}
|
||||||
else if (RNA_struct_property_is_set(op->ptr, "view_align")) {
|
else if (alignment_set) {
|
||||||
*is_view_aligned = RNA_boolean_get(op->ptr, "view_align");
|
*is_view_aligned = alignment == ALIGN_VIEW;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
*is_view_aligned = (U.flag & USER_ADD_VIEWALIGNED) != 0;
|
*is_view_aligned = (U.flag & USER_ADD_VIEWALIGNED) != 0;
|
||||||
RNA_boolean_set(op->ptr, "view_align", *is_view_aligned);
|
if (*is_view_aligned) {
|
||||||
|
RNA_property_enum_set(op->ptr, prop, ALIGN_VIEW);
|
||||||
|
alignment = ALIGN_VIEW;
|
||||||
|
}
|
||||||
|
else if (U.flag & USER_ADD_CURSORALIGNED) {
|
||||||
|
RNA_property_enum_set(op->ptr, prop, ALIGN_CURSOR);
|
||||||
|
alignment = ALIGN_CURSOR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*is_view_aligned) {
|
switch (alignment) {
|
||||||
|
case ALIGN_WORLD:
|
||||||
|
RNA_float_get_array(op->ptr, "rotation", rot);
|
||||||
|
break;
|
||||||
|
case ALIGN_VIEW:
|
||||||
ED_object_rotation_from_view(C, rot, view_align_axis);
|
ED_object_rotation_from_view(C, rot, view_align_axis);
|
||||||
RNA_float_set_array(op->ptr, "rotation", rot);
|
RNA_float_set_array(op->ptr, "rotation", rot);
|
||||||
|
break;
|
||||||
|
case ALIGN_CURSOR: {
|
||||||
|
const Scene *scene = CTX_data_scene(C);
|
||||||
|
copy_v3_v3(rot, scene->cursor.rotation_euler);
|
||||||
|
RNA_float_set_array(op->ptr, "rotation", rot);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
RNA_float_get_array(op->ptr, "rotation", rot);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -690,7 +720,7 @@ static int object_camera_add_exec(bContext *C, wmOperator *op)
|
|||||||
float loc[3], rot[3];
|
float loc[3], rot[3];
|
||||||
|
|
||||||
/* force view align for cameras */
|
/* force view align for cameras */
|
||||||
RNA_boolean_set(op->ptr, "view_align", true);
|
RNA_enum_set(op->ptr, "align", ALIGN_VIEW);
|
||||||
|
|
||||||
if (!ED_object_add_generic_get_opts(
|
if (!ED_object_add_generic_get_opts(
|
||||||
C, op, 'Z', loc, rot, &enter_editmode, &local_view_bits, NULL)) {
|
C, op, 'Z', loc, rot, &enter_editmode, &local_view_bits, NULL)) {
|
||||||
@@ -732,7 +762,7 @@ void OBJECT_OT_camera_add(wmOperatorType *ot)
|
|||||||
ED_object_add_generic_props(ot, true);
|
ED_object_add_generic_props(ot, true);
|
||||||
|
|
||||||
/* hide this for cameras, default */
|
/* hide this for cameras, default */
|
||||||
prop = RNA_struct_type_find_property(ot->srna, "view_align");
|
prop = RNA_struct_type_find_property(ot->srna, "align");
|
||||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -833,7 +833,7 @@ typedef enum eUserPref_Flag {
|
|||||||
USER_TOOLTIPS = (1 << 11),
|
USER_TOOLTIPS = (1 << 11),
|
||||||
USER_TWOBUTTONMOUSE = (1 << 12),
|
USER_TWOBUTTONMOUSE = (1 << 12),
|
||||||
USER_NONUMPAD = (1 << 13),
|
USER_NONUMPAD = (1 << 13),
|
||||||
USER_FLAG_UNUSED_14 = (1 << 14), /* cleared */
|
USER_ADD_CURSORALIGNED = (1 << 14),
|
||||||
USER_FILECOMPRESS = (1 << 15),
|
USER_FILECOMPRESS = (1 << 15),
|
||||||
USER_SAVE_PREVIEWS = (1 << 16),
|
USER_SAVE_PREVIEWS = (1 << 16),
|
||||||
USER_CUSTOM_RANGE = (1 << 17),
|
USER_CUSTOM_RANGE = (1 << 17),
|
||||||
|
|||||||
@@ -4347,7 +4347,12 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
|
|||||||
"VIEW",
|
"VIEW",
|
||||||
0,
|
0,
|
||||||
"View",
|
"View",
|
||||||
"Align newly added objects facing the active 3D View direction"},
|
"Align newly added objects to the active 3D View direction"},
|
||||||
|
{USER_ADD_CURSORALIGNED,
|
||||||
|
"CURSOR",
|
||||||
|
0,
|
||||||
|
"3D Cursor",
|
||||||
|
"Align newly added objects to the 3D Cursor's rotation"},
|
||||||
{0, NULL, 0, NULL, NULL},
|
{0, NULL, 0, NULL, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -582,7 +582,7 @@ cube_shell_face = (
|
|||||||
|
|
||||||
|
|
||||||
def make_cube(scene):
|
def make_cube(scene):
|
||||||
bpy.ops.mesh.primitive_cube_add(view_align=False,
|
bpy.ops.mesh.primitive_cube_add(align='WORLD',
|
||||||
enter_editmode=False,
|
enter_editmode=False,
|
||||||
location=(0, 0, 0),
|
location=(0, 0, 0),
|
||||||
rotation=(0, 0, 0),
|
rotation=(0, 0, 0),
|
||||||
@@ -652,7 +652,7 @@ def make_cube_shell_extra(scene):
|
|||||||
|
|
||||||
|
|
||||||
def make_monkey(scene):
|
def make_monkey(scene):
|
||||||
bpy.ops.mesh.primitive_monkey_add(view_align=False,
|
bpy.ops.mesh.primitive_monkey_add(align='WORLD',
|
||||||
enter_editmode=False,
|
enter_editmode=False,
|
||||||
location=(0, 0, 0),
|
location=(0, 0, 0),
|
||||||
rotation=(0, 0, 0),
|
rotation=(0, 0, 0),
|
||||||
|
|||||||
Reference in New Issue
Block a user