1
$\begingroup$

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()
    else:
        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
    else:
        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 ====")
print(ob.matrix_world)
while ob.parent:
    M = (ob.matrix_parent_inverse @ ob.matrix_basis) @ M
    ob = ob.parent

M = ob.matrix_basis @ M

print(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?

$\endgroup$
8
  • $\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 '20 at 8:28
  • $\begingroup$ prob a better link blender.stackexchange.com/a/169424/15543 $\endgroup$ – batFINGER Jul 18 '20 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 '20 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 '20 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 '20 at 6:33
0
$\begingroup$

Because

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.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.