2
$\begingroup$

I have written two custom driver functions that both do the same thing. Return the actual length of a bendy bone taking into account its curvature. A custom function was necessary because the default length property for a bone just gives the length from head to tail disregarding any curvature that is part of the bendy bone.

Of the two functions the first uses self and calculates the length of the bone the expression lives on. The other one uses two arguments, the first being self so the armature can be derived and the second argument being the name of the bendy bone whose length needs to be calculated. Both of them use the same logic for calculating the final length.

My issue is that there seems to be a problem with updates. The output is mostly smooth with the occasional random jitter. This is not dependent on a specific value or even a specific time. I feel this is related to the dependency graph because it seems to resolve itself on scene update. If it happens when I manually manipulate the playhead even letting go of mousedown is enough to refresh the scene and fix it. If it happens during animation playback and I manage to pause on the right moment, just selecting a different bone is enough to fix it.

I don't see any mentions about circular dependencies in the terminal and it also does not feel like the value lags. When doing research I read about drivers having issues with automatically updating when custom properties they depend on are updated through python. Since this function does not use any custom properties it did not seem relevant. I also read about possibly having to tag objects for update but this feels odd to me because a) it works most of the time and I would expect it not to work at all if this was the case b) which objects should I tag for updates? The bendy bone whose length I need may be part of a really complex rig where many things influence its final shape and therefore length.

I have also supplied a blender file with a working example that shows this behavior. The scene contains an animated bendy bone. Right next to it is a bone whose length is driven by "bbone_length_by_name". As you play the animation you will see it scale up and down and occasionally glitch out.

Blender file

I have also added a link to a short video file that shows the jittery behavior. Since writing this post i've noticed that manipulating the viewport seems to make the jittering worse. If I leave the view static the jittering is less.

mp4 link

Since I wondered whether it was a display issue ive also printed the value to the terminal. As you can see there are weird jumps that last just a frame. So its not just a display issue, its actually because the values from the function are wrong sometimes (ive marked a few). These frames are not fixed btw, the spikes are random each playthrough.

value output on each frame

Here is the code:

import bpy
import math
from mathutils import Vector


def get_bbone_segment_location(target_bendybone, segment_index):
    # in worldspace    
    bone_armature = target_bendybone.id_data

    matrix_segment = target_bendybone.bbone_segment_matrix(segment_index, rest=False)
    matrix_final = bone_armature.matrix_world @ target_bendybone.matrix @ matrix_segment
        
    return matrix_final.decompose()[0]


def get_bbone_segment_locations(target_bendybone):
    location_list = []
    num_segments = target_bendybone.bone.bbone_segments
    
    for index in range(num_segments):
        location_list.append(get_bbone_segment_location(target_bendybone, index))

    return location_list


def calculate_length(points):
    total_length = 0.0

    for i in range(len(points) - 1):
        distance = 0.0
        point1 = points[i]
        point2 = points[i + 1]

        distance = (point2 - point1).length
        total_length += distance

    return total_length

# function to use on the bendy bone itself where self can be used for the target_bendybone argument
def get_bbone_length(target_bendybone):
    location_list = get_bbone_segment_locations(target_bendybone)

    return calculate_length(location_list)

# function to use on another bone within the same armature where the name of the bendy_bone needs to be supplied
def get_bbone_length_by_name(self, target_bendybone_name):
    bone_armature = self.id_data
    target_bendybone = bone_armature.pose.bones[target_bendybone_name]

    return get_bbone_length(target_bendybone)


bpy.app.driver_namespace['bbone_length'] = get_bbone_length
bpy.app.driver_namespace['bbone_length_by_name'] = get_bbone_length_by_name

Any help or pointers would be really appreciated because I have no idea how to go from here.

$\endgroup$
2
  • 2
    $\begingroup$ I am not able to reproduce a jittery behavior. Tested on Blender 4.0 and 3.6.5. Try opening the file in older version of Blender. $\endgroup$ Commented Dec 11, 2023 at 0:18
  • $\begingroup$ @RumenBelev Thank you for taking a look! Yes it differs per system. Ive had various people test it and some had trouble reproducing it. The original setup was created in 3.6, i've tried it in the latest 4.0 as well. The problem is not with Blender itself (nor its version) but in the setup. See my answer below. $\endgroup$
    – Jan-Willem
    Commented Dec 11, 2023 at 9:59

1 Answer 1

2
$\begingroup$

It looks like the jittering was caused by a race condition in what was evaluated first. Either the final bendy bone shape or the calculation for its length. As Rumen pointed out it seems to differ per system how badly the jittering occurs. I've heard this from other people who tested this as well. On my system it was very prevalent as the embedded movie shows.

What needed to happen for this to stabilize is to force the evaluation order. You can achieve this by creating variables on the driver itself (so not in the custom driver function) that reference the transform channel of the bone whose length you want to calculate.

You don't need to use this variable anywhere, creating a reference to a transform channel is enough to trigger the evaluation of that bone. It is important you use a transform channel and not just a property on the bone in question since the latter won't trigger an evaluation.

In the example video I used it in its final purpose: to have a bone sliding along a bendy bone chain consisting of three bones that constantly vary in shape. You can see the bone that rides it has a fixed position along the total combined length, smoothly crossing over from bone to bone as if its a single unbroken chain. To be able to do this is what I needed the actual length of a bendy bone for. As you can see its fully stable now.

mp4 link

$\endgroup$

You must log in to answer this question.

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