From 026eba8523ccaac5c46eb2ab94d857ba53d29994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 25 May 2020 15:34:54 +0200 Subject: [PATCH 1/3] Fix T76941: "Set Inverse" in Child Of constraint broken with armatures When the Child Of constraint is owned by a bone, before the constraint is run the matrix is converted from world to pose space. However, setting the inverse should also take the armature object's transform into account. --- source/blender/blenkernel/intern/constraint.c | 4 ++ tests/python/bl_constraints.py | 71 +++++++++++++++++-- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 099fdacf401..9a42d2f82f2 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -951,6 +951,10 @@ static void childof_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar /* If requested, compute the inverse matrix from the computed parent matrix. */ if (data->flag & CHILDOF_SET_INVERSE) { invert_m4_m4(data->invmat, parmat); + if (cob->pchan != NULL) { + mul_m4_series(data->invmat, data->invmat, cob->ob->obmat); + } + copy_m4_m4(inverse_matrix, data->invmat); data->flag &= ~CHILDOF_SET_INVERSE; diff --git a/tests/python/bl_constraints.py b/tests/python/bl_constraints.py index 9fce8acc84e..323dd874ac0 100644 --- a/tests/python/bl_constraints.py +++ b/tests/python/bl_constraints.py @@ -56,21 +56,37 @@ class AbstractConstraintTests(unittest.TestCase): actual, expect, places=places, delta=delta, msg=f'Matrix of object {object_name!r} failed: {actual} != {expect} at element [{row}][{col}]') - def matrix(self, object_name: str) -> Matrix: - """Return the evaluated world matrix.""" + def _get_eval_object(self, object_name: str) -> bpy.types.Object: + """Return the evaluated object.""" depsgraph = bpy.context.view_layer.depsgraph depsgraph.update() ob_orig = bpy.context.scene.objects[object_name] ob_eval = ob_orig.evaluated_get(depsgraph) + return ob_eval + + def matrix(self, object_name: str) -> Matrix: + """Return the evaluated world matrix.""" + ob_eval = self._get_eval_object(object_name) return ob_eval.matrix_world + def bone_matrix(self, object_name: str, bone_name: str) -> Matrix: + """Return the evaluated world matrix of the bone.""" + ob_eval = self._get_eval_object(object_name) + bone = ob_eval.pose.bones[bone_name] + return ob_eval.matrix_world @ bone.matrix + def matrix_test(self, object_name: str, expect: Matrix): """Assert that the object's world matrix is as expected.""" actual = self.matrix(object_name) self.assert_matrix(actual, expect, object_name) + def bone_matrix_test(self, object_name: str, bone_name: str, expect: Matrix): + """Assert that the bone's world matrix is as expected.""" + actual = self.bone_matrix(object_name, bone_name) + self.assert_matrix(actual, expect, object_name) + def constraint_context(self, constraint_name: str, owner_name: str='') -> dict: - """Return a context suitable for calling constraint operators. + """Return a context suitable for calling object constraint operators. Assumes the owner is called "{constraint_name}.owner" if owner_name=''. """ @@ -84,6 +100,30 @@ class AbstractConstraintTests(unittest.TestCase): } return context + def bone_constraint_context(self, constraint_name: str, owner_name: str='', bone_name: str='') -> dict: + """Return a context suitable for calling bone constraint operators. + + Assumes the owner's object is called "{constraint_name}.owner" if owner_name=''. + Assumes the bone is called "{constraint_name}.bone" if bone_name=''. + """ + + owner_name = owner_name or f'{constraint_name}.owner' + bone_name = bone_name or f'{constraint_name}.bone' + + owner = bpy.context.scene.objects[owner_name] + pose_bone = owner.pose.bones[bone_name] + + constraint = pose_bone.constraints[constraint_name] + context = { + **bpy.context.copy(), + 'object': owner, + 'active_object': owner, + 'active_pose_bone': pose_bone, + 'constraint': constraint, + 'owner': pose_bone, + } + return context + class ChildOfTest(AbstractConstraintTests): layer_collection = 'Child Of' @@ -153,7 +193,7 @@ class ChildOfTest(AbstractConstraintTests): )) self.matrix_test('Child Of.object.owner', initial_matrix) - context = self.constraint_context('Child Of', owner_name='Child Of.object.owner') + context = self.constraint_context('Child Of', owner_name='Child Of.object.owner',) bpy.ops.constraint.childof_set_inverse(context, constraint='Child Of') self.matrix_test('Child Of.object.owner', Matrix(( (0.9992386102676392, 0.019843991845846176, -0.03359176218509674, 0.10000000149011612), @@ -188,6 +228,29 @@ class ChildOfTest(AbstractConstraintTests): bpy.ops.constraint.childof_clear_inverse(context, constraint='Child Of') self.matrix_test('Child Of.armature.owner', initial_matrix) + def test_bone_owner(self): + """Child Of: bone owns constraint, targeting object.""" + initial_matrix = Matrix(( + (0.9992387890815735, -0.03359174728393555, -0.019843988120555878, -2.999999523162842), + (-0.02588011883199215, -0.1900751143693924, -0.9814283847808838, 2.0), + (0.029196053743362427, 0.9811949133872986, -0.190799742937088, 0.9999999403953552), + (0.0, 0.0, 0.0, 1.0), + )) + self.bone_matrix_test('Child Of.bone.owner', 'Child Of.bone', initial_matrix) + + context = self.bone_constraint_context('Child Of', owner_name='Child Of.bone.owner') + bpy.ops.constraint.childof_set_inverse(context, constraint='Child Of', owner='BONE') + + self.bone_matrix_test('Child Of.bone.owner', 'Child Of.bone', Matrix(( + (0.9659260511398315, 0.2588191032409668, 4.656613428188905e-10, -2.999999761581421), + (-3.725290742551124e-09, 1.4901162970204496e-08, -1.0, 0.9999999403953552), + (-0.2588191032409668, 0.965925931930542, 0.0, 0.9999999403953552), + (0.0, 0.0, 0.0, 1.0), + ))) + + bpy.ops.constraint.childof_clear_inverse(context, constraint='Child Of', owner='BONE') + self.bone_matrix_test('Child Of.bone.owner', 'Child Of.bone', initial_matrix) + def test_vertexgroup_simple_parent(self): """Child Of: simple evaluation of vertex group parent.""" initial_matrix = Matrix(( From ec0ba8e3b5ee2d5f5ef9239641db6550bf5b77b7 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Sun, 24 May 2020 17:26:31 +0200 Subject: [PATCH 2/3] VR: Fix big performance bottleneck for simple scenes Blender's main loop puts the main thread to sleep for 5ms if no user input was received from the OS. We never want that to happen while the VR session is running, which runs on the main thread too. For simpler scenes, where the viewport already draws fast, this may have quite some impact. E.g. in my tests, the classroom scene went from ~55 to quite stable 90 FPS in solid mode (total render time as measured and averaged by Windows Mixed Reality utilities). With Eevee, it only went from 41 to 47 FPS. In complex files, there's barely a difference. E.g. less than 1 FPS increase in a Spring file (both Solid mode and Eevee). --- source/blender/windowmanager/xr/intern/wm_xr.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c index 69c9034d51f..90f30809136 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr.c +++ b/source/blender/windowmanager/xr/intern/wm_xr.c @@ -126,7 +126,12 @@ void wm_xr_exit(wmWindowManager *wm) bool wm_xr_events_handle(wmWindowManager *wm) { if (wm->xr.runtime && wm->xr.runtime->context) { - return GHOST_XrEventsHandle(wm->xr.runtime->context); + GHOST_XrEventsHandle(wm->xr.runtime->context); + + /* wm_window_process_events() uses the return value to determine if it can put the main thread + * to sleep for some milliseconds. We never want that to happen while the VR session runs on + * the main thread. So always return true. */ + return true; } return false; } From 219ce574b56585eb5db056983a8acf08f7ff9991 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Mon, 25 May 2020 11:53:02 +0200 Subject: [PATCH 3/3] Revert "UI: View3D Cursor Changes" This change is yet to be followed by a more comprehensive design proposal including: * How to differentiate the modes apart. * More clear definition of tools and the rules for their components (gizmo, cursor). * Selection as a non-tool vs drag option. This can be revisited for 2.90 with more time. For now the UI team agrees to revert this. -- This reverts commit 4aa703aa1430bc53f19e2cc7182e70db1a916f13. --- .../startup/bl_ui/space_toolsystem_toolbar.py | 3 --- source/blender/editors/space_view3d/space_view3d.c | 12 +++++++++++- .../space_view3d/view3d_gizmo_navigate_type.c | 7 +++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 3c9355d0017..8d1f23e5df0 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -137,7 +137,6 @@ class _defs_view3d_generic: idname="builtin.measure", label="Measure", description=description, - cursor='CROSSHAIR', icon="ops.view3d.ruler", widget="VIEW3D_GGT_ruler", keymap="3D View Tool: Measure", @@ -375,7 +374,6 @@ class _defs_view3d_select: label="Select Lasso", icon="ops.generic.select_lasso", widget=None, - cursor='DEFAULT', keymap="3D View Tool: Select Lasso", draw_settings=draw_settings, ) @@ -400,7 +398,6 @@ class _defs_view3d_select: label="Select Circle", icon="ops.generic.select_circle", widget=None, - cursor='DEFAULT', keymap="3D View Tool: Select Circle", draw_settings=draw_settings, draw_cursor=draw_cursor, diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index a5c99016434..51607c5daa3 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1082,9 +1082,19 @@ static void view3d_main_region_message_subscribe(const struct bContext *C, } } +/* concept is to retrieve cursor type context-less */ static void view3d_main_region_cursor(wmWindow *win, ScrArea *area, ARegion *region) { - if (!WM_cursor_set_from_tool(win, area, region)) { + if (WM_cursor_set_from_tool(win, area, region)) { + return; + } + + ViewLayer *view_layer = WM_window_get_active_view_layer(win); + Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); + if (obedit) { + WM_cursor_set(win, WM_CURSOR_EDIT); + } + else { WM_cursor_set(win, WM_CURSOR_DEFAULT); } } diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c index 3301e28c90c..3ce4c8dc9a8 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c @@ -536,9 +536,12 @@ static int gizmo_axis_test_select(bContext *UNUSED(C), wmGizmo *gz, const int mv return -1; } -static int gizmo_axis_cursor_get(wmGizmo *UNUSED(gz)) +static int gizmo_axis_cursor_get(wmGizmo *gz) { - return WM_CURSOR_DEFAULT; + if (gz->highlight_part > 0) { + return WM_CURSOR_EDIT; + } + return WM_CURSOR_NSEW_SCROLL; } void VIEW3D_GT_navigate_rotate(wmGizmoType *gzt)