I wrote Python's function to rotate a object 'o' direction, step by step, to 'directionW' direction.

When the o has no parent, the function works great. But, when the o has one or more parent, function sometimes works correctly, sometimes wrong. When it works wrong the direction of o don't going to directionW by shortest way, but going in space by way (like 8-digit) around directionW.

Next is the function:

    def RotateToDirectionW( o, directionW, step, adaptivestep, maxunlock ):
    """o - object to step rotate to directionW"""
    """directionW - direction to achieve step by step (unit vector in world coordinates)"""
    """adaptivestep - if True, then rotation angle has minimal value when direction of o near to directionW,"""
    """maxunlock - maximal vector size to unlock, 1E-5 or 0"""

    matLtoW = o.matrix_world
    matWtoL = matLtoW.copy(); matWtoL.invert()
    if o.parent != None:
        matWtoP = o.parent.matrix_world.copy(); matWtoP.invert()
        matWtoP = Matrix.Identity(4)

    # direction of o ( axe z+ ) in world coordinates
    dirW = ( matLtoW @ Vector((0,0,1)) ) - ( matLtoW @ Vector((0,0,0)) )
    dirW.normalize()        # need to normalize angle

    # calculate axe, around what object will be rotated (in world c.)
    axeRotW = dirW.cross( directionW )
    power = axeRotW.length
    # unlock
    # if vector is too small to rotate, take any bigger
    if abs(power)<maxunlock:
        axeRotW = Vector( (maxunlock,maxunlock,maxunlock) )
        power = axeRotW.length

    # transform axe of rotation to parent base coordinates
    # (problem somewhere there)
    axeRotP = (matWtoP.to_quaternion() @ axeRotW

    if adaptivestep:
        angle = step * power
        angle = Vector.angle( dirW, directionW )
        if (angle > step): angle = step

    # rotation
    qutRot = Quaternion( axeRotP, angle )
    o.rotation_euler.rotate( qutRot )

I check every possible intermediate results, they are correct at all. The axe of rotation is correct in world space. I think, problem is when axeRotW converted to parent coordinate space. It look like there is transitional transformation between parent's transformation and o own transformation. I think, i need convert axeRotW to this transitional coordinate system, but there is no information about it.

What is a way to solve the problem? I waste several day of my time to do it, but now i into deadlock.

P.S.: I try, also, to rotate o by bps.transform.rotate(), but this operation can't work with custom rotation axe, XYZ-only.

P.P.S.: A founded 3 conditions for the correct result:

    1. The object o has not parent
    1. The object has parent object with identity (equal 1) 'matrix_parent_inverse'.
    1. The object has parent 3-verticies with same direction of coordinates as parent, and identity 'matrix_parent_inverse'.

P.P.P.S.: To investigate matrix chain calculation, i run script from blender.stackexchange.com/a/160580/15543

import bpy
from mathutils import Matrix

context = bpy.context
ob = context.active_object
M = Matrix()
print("==== calc word matrix ====")
while ob.parent:
    M = (ob.matrix_parent_inverse @ ob.matrix_basis) @ M
    ob = ob.parent

M = ob.matrix_basis @ M


If generic child object was selected, then script prints two equal matrices. But, if somewhere in parent-child chain exists 3-vertices parenting, then the printed matrices was different!!!. F.e.:

---- calc word matrix ----
<Matrix 4x4 (-0.0198,  0.1843,  0.4570, 15.1996)
            ( 0.0012,  0.4574, -0.1844, -4.9117)
            (-0.4928, -0.0063, -0.0188,  3.4083)
            ( 0.0000,  0.0000,  0.0000,  1.0000)>
<Matrix 4x4 (-0.0465, -0.2971,  1.1550, 36.2083)
            (-0.0120,  1.1559,  0.2969, 11.8968)
            (-1.1926,  0.0000, -0.0480,  1.8346)
            ( 0.0000,  0.0000,  0.0000,  1.0000)>

It is possible, there is one addition transformation in parent-child chain. And i need to take into account yellow colored block:

3-vertices parenting add one extra transformation block

How i see, there is no python access to extra v3-parenting transformation matrix. I suppose, with help of some math we can calculate this extra matrix. But how?

  • $\begingroup$ Related blender.stackexchange.com/a/160580/15543 ie it appears it is not being taken into account ob.matrix_parent_inverse may not be identity. $\endgroup$
    – batFINGER
    Jul 18, 2020 at 8:28
  • $\begingroup$ prob a better link blender.stackexchange.com/a/169424/15543 $\endgroup$
    – batFINGER
    Jul 18, 2020 at 8:42
  • $\begingroup$ I clear matrix_parent_inverse now, result is changed, but it not correct anymore. Also i try next conversion: axeRotP = (matWtoP.to_quaternion() @ axeRotW) $\endgroup$
    – Sla.Va
    Jul 18, 2020 at 9:17
  • $\begingroup$ Would also need to convert as well as clear. Basically if the parent inverse is identity then local origin is at parent origin. This is for a mesh but pertinent here blender.stackexchange.com/a/183724/15543 in that aligning object to a face normal vector. The quaternion rotation difference between vectors will calculate axis and angle. $\endgroup$
    – batFINGER
    Jul 18, 2020 at 9:37
  • $\begingroup$ Thanks for suggestions. A founded 3 conditions for the correct result: 1. The object 'o' has not parent 2. The object has parent object with identity 'matrix_parent_inverse'. 3. The object has parent 3-verticies with same direction of coordinates as parent, and identity 'matrix_parent_inverse'. How to take into account 'matrix_parent_inverse' - i don't understand. I try insert transformation with 'o.matrix_parent_inverse' in several places, but, without success. My guess how 'o.matrix_parent_inverse' works and how it works in real - is different. $\endgroup$
    – Sla.Va
    Jul 19, 2020 at 6:33

1 Answer 1



matrix_world = parent.matrix_world * parent.matrix_x * matrix_parent_inverse * matrix_basis

where parent.matrix_x is inaccessible matrix by python, we can't calculate matWtoP from o.parent (in common case) and must to use o.matrix_word. The right transformation is:

# transform axe of rotation to parent base coordinates
matWtoP = o.matrix_basis @ o.matrix_world.inverted()
axeRotP = matWtoP.to_quaternion() @ axeRotW

Most basic source of errors in my investigations above was a order of execution of @-operands in Blender. I had false representation about it and tried to use the order like matWtoP = o.matrix_world.inverted() @ o.matrix_basis, what is wrong.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .