4
$\begingroup$

I have a few rocks in my scene. Lets say the scene is in space and I want to add some random rotation to the rocks. Since there are a few of them, I don#t want to go to every rocks properties and apply a driver by hand.

So my question is:

How do I create a driver that creates a random but laminar rotation on all axis and how can I copy that driver to multiple objects?

Thanks in advance

$\endgroup$
  • $\begingroup$ here's how to create random driver blender.stackexchange.com/a/26597/5113 $\endgroup$ – Chebhou Jul 14 '15 at 21:25
  • $\begingroup$ @Chebhou this is a jittering driver, i eant a continious rotation $\endgroup$ – Marten Zander Jul 14 '15 at 21:29
  • $\begingroup$ instead of prop use the frame number ( which keep changing and thus create a continuous animation ) $\endgroup$ – Chebhou Jul 14 '15 at 21:35
  • $\begingroup$ I don't think your mission can be accomplished with JUST a driver. You might consider editing your question to ask for any animation technique, but wait a little while to see if someone can come up with an answer that does match your question. $\endgroup$ – Mutant Bob Nov 13 '15 at 20:45
3
$\begingroup$

Alternatively I would suggest playing with Animation Nodes Addon for a procedural animation:

enter image description here

Create an Object Group (CtrlG), loop through the desired objects via Group Input node, add a Random Vector node to get a random rotation value, add a Time Info node and plug the current frame value into the time input socket of a Animate Vector node in order to drive the animation:

enter image description here Basic node setup (click to enlarge)

enter image description here


The node setup above is easily expandable. In this case it probably makes sense rotating each object at a different speed, depending on their individual size. Via Object Info node it's possible to obtain the scale of each object, calculate a unique float value from the scale vector and plug the result into the duration value of the Animate Vector node:

enter image description here Different rotation speed based on the scale of each object (click to enlarge)

enter image description here

$\endgroup$
  • 1
    $\begingroup$ oh nice, I didn't even know there was animation nodes. Thanks a lot! $\endgroup$ – Marten Zander Jul 16 '15 at 9:27
  • $\begingroup$ One thing that seems weird is those look like they might not be rotating around a consistent axis, more like they are wobbling. $\endgroup$ – Mutant Bob Nov 14 '15 at 22:07
3
$\begingroup$

You don't need a driver to accomplish this. Simply select all the rock elements you want to affect. Then use the Randomize Transform command and edit the XYZ Rotation values in the side panel.

This will give you a randomized starting rotation for each rock.

enter image description here

Now, with all the rocks selected, click CTRL+A and choose Apply Rotation.

Now we want to add a driver to the XYZ rotation values for each rock. We want this value to be constant throughout the animation. We can do this by using a multiple of the frame number.

First, select all the rocks and add them to a group named "ROCKS".

Then open up the text editor and run this script:

import bpy
import random

for obj in bpy.data.objects:
    randomSpeed = random.randint(1,20)*0.001

    for group in obj.users_group:
        if group.name == "ROCKS":
            for i in range(0,3):
                fCurveOb = obj.driver_add("rotation_euler", i)
                drv = fCurveOb.driver
                drv.type = "SCRIPTED"
                drv.expression = "frame*" + str(randomSpeed)
                drv.show_debug_info = True

This will set the XYZ rotation values to increment each frame. You can increase the rotation rate by increasing the 0.01 value.

$\endgroup$
  • $\begingroup$ good idea but i don't want to keyframe the animation with fixed values $\endgroup$ – Marten Zander Jul 14 '15 at 20:05
  • $\begingroup$ sorry about that, updated my answer $\endgroup$ – Todd McIntosh Jul 14 '15 at 20:17
  • $\begingroup$ sorry dude, maybe I am not expressing myself correctly. This is actually what I dont want. I don't know the length of the scene I want to render yet. that's why I want to have an continiously animated rotation on each object, without messing arround and extending any set keyframes to define an end of the scene. I want to tell the object at one point, how it should behave and than never touch it again. That's why I thought of drivers. $\endgroup$ – Marten Zander Jul 14 '15 at 20:27
  • $\begingroup$ So far I solved my problem by adding a driver to each axis rotation with a scripted expression like "0.01*frame" or something. But since the factor "0.01" is fixed, the rotations kinda look like the same. As well as I still can't apply the driver easily to the other objects $\endgroup$ – Marten Zander Jul 14 '15 at 20:27
  • $\begingroup$ @SlimMarten Did you get a chance to test my solution? $\endgroup$ – Todd McIntosh Jul 15 '15 at 4:40
2
$\begingroup$

Here's a python script that gives each object its own quaternion fcurves using sinusoidal easing backed up by some fancy application of high school trig:

import bpy
import random
from math import *
from mathutils import *

def random_in_circle():
    while True:
        x = random.random()*2-1
        y = random.random()*2-1
        l2 = x * x + y * y
        if (l2<=0):
            continue
        if (l2 <=1):
            return x,y

def random_axis():
    z = random.random()*2-1
    theta = random.random()*pi*2
    r = sqrt(1-z*z)
    x = cos(theta)*r
    y = sin(theta)*r

    return [ x,y,z]

def random_quaternion():
    """
    http://mathworld.wolfram.com/HyperspherePointPicking.html
    """
    x,y = random_in_circle()
    z_,w_ = random_in_circle()

    r5 =sqrt( ( 1-x*x - y*y) / (z_*z_+w_*w_))

    z = z_ * r5
    w = w_ * r5

    return [x,y,z,w]


def rig_quaternion_channel(action, channel, period, a, b):
    """
    This is heavy-duty voodoo to figure out what keyframes to use with sinusoidal easing
    to reconstruct a curve of the form
     a*cos(theta) + b*sin(theta)
     by converting it to the form
     c*sin(theta+phi)
    """
    c = sqrt(a * a + b * b)
    phi = -atan2(a, b)
    fc = action.fcurves.new(data_path="rotation_quaternion", index=channel)
    fc.keyframe_points.add(5)
    vals = [0, 1, 0, -1, 0]
    for j in range(5):
        kp = fc.keyframe_points[j]
        frame = 1 + ( phi / (2 * pi) + j / 4.0) * 2 * period
        kp.co = ( frame, c * vals[j])
        kp.interpolation = 'SINE'
        if 0 == j % 2:
            kp.easing = 'EASE_OUT'
        else:
            kp.easing = 'EASE_IN'
    fc.modifiers.new('CYCLES')


def rig_random_rotation2(obj, scn):
    """
      This rigs obj with a random rotation about a random axis using quaternions and fcurves with sinusoidal easing.
       I feel pretty smug for having pulled this off - RF
    """
    q1 = Quaternion(random_quaternion())
    axis = Vector(random_axis())

    #print( [ q1, axis ])

    # w2 = cos(theta/2)
    # x2 = axis.x*sin(theta/2)
    # y2 = axis.y*sin(theta/2)
    # z2 = axis.z*sin(theta/2)
    # w' = w1*w2 - x1*x2 - y1*y2 - z1*z2
    # w' = w1*cos(theta/2) - x1*axis.x*sin(theta/2) - y1*axis.y*sin(theta/2)- z1*axis.z*sin(theta/2)
    # w' = w1*cos(theta/2) - (x1*axis.2 +y1*axis.y+z1*axis.z)*sin(theta/2)

    period=(2*pi/random_rotation_speed_radians()) * scn.render.fps

    obj.rotation_mode = "QUATERNION"
    obj.animation_data_clear()
    obj.animation_data_create()
    action = obj.animation_data.action = bpy.data.actions.new("groovy")

    """
    Given
    * one orientation quaternion q1,
    and
    * a rotation axis
     use the formula for q2(theta) = Quaternion(axis, theta)
      and the formula for q3 = q1*q2
      figure out how q3 relates to theta, and reduce each w,x,y,z channel to an expression of the form
       q3[i] = a_i * cos(theta/2) + b_i * sin(theta/2)
       and pass those coefficients to rig_quaternion_channel so it can rig the fcurves correctly
    """

    rig_quaternion_channel(action, 0, period, q1.w, -q1.x * axis.x - q1.y * axis.y - q1.z * axis.z)
    rig_quaternion_channel(action, 1, period, q1.x,  q1.w * axis.x - q1.z * axis.y + q1.y * axis.z)
    rig_quaternion_channel(action, 2, period, q1.y,  q1.z * axis.x + q1.w * axis.y - q1.x * axis.z)
    rig_quaternion_channel(action, 3, period, q1.z, -q1.y * axis.x + q1.x * axis.y + q1.w * axis.z)


def random_rotation_speed_radians():
    return random.random() + 1


def mission2(scn):
    for obj in scn.objects:
        if obj.select:
            rig_random_rotation2(obj, scn)

#
#
#

scn = bpy.context.scene
mission2(scn)

This code is now the subject of http://web.purplefrog.com/~thoth/blender/python-cookbook/animate-random-spin.html and the math behind it is explained at http://web.purplefrog.com/~thoth/blender/python-cookbook/narratives/animate-random-spin.html .

$\endgroup$
1
$\begingroup$

Here is an animation technique that relies on using a parent Empty to give the object a random orientation, but have it spin on a fixed axis:

import bpy
import random
from math import *

def random_in_circle():
    while True:
        x = random.random()*2-1
        y = random.random()*2-1
        l2 = x * x + y * y
        if (l2<=0):
            continue
        if (l2 <=1):
            return x,y

def random_quaternion():
    """
    http://mathworld.wolfram.com/HyperspherePointPicking.html
    """
    x,y = random_in_circle()
    z_,w_ = random_in_circle()

    r5 =sqrt( ( 1-x*x - y*y) / (z_*z_+w_*w_))

    z = z_ * r5
    w = w_ * r5

    return [x,y,z,w]

def random_rotation_speed_radians():
    return random.random() + 1

def rig_random_rotation(obj, scn):
    # make the object spin on its Z axis
    obj.rotation_euler = (0,0,0)
    obj.keyframe_insert(data_path='rotation_euler', frame=1)
    obj.rotation_euler = (0,0,random_rotation_speed_radians())
    obj.keyframe_insert(data_path='rotation_euler', frame=1+scn.render.fps)
    # rig the keyframes for linear interpolation and extrapolation so it keeps spinning for the duration of the animation
    for fc in obj.animation_data.action.fcurves:
        if fc.data_path == "rotation_euler":
            fc.extrapolation = 'LINEAR'
            for kp in fc.keyframe_points:
                kp.interpolation='LINEAR'

    # now we make the axis random by parenting the object to an Empty with a thoroughly random orientation
    parent = obj.parent
    if parent is None:
        parent = bpy.data.objects.new("rotation axis of %s"%obj.name, None)
        scn.objects.link(parent)
        obj.parent = parent
        parent.layers = [i==19 for i in range(len(parent.layers))]
    parent.rotation_mode = 'QUATERNION'
    parent.rotation_quaternion = random_quaternion()

    # there is probably a way to avoid the empty and
    # give the object a looping quaternion rotation on a random axis using sinusoidal easing,
    # but I'm a little too lazy to work out the math right this instant.


def mission1(scn):
    for obj in scn.objects:
        if obj.select:
            rig_random_rotation(obj, scn)

#
#
#

scn = bpy.context.scene
mission1(scn)

http://web.purplefrog.com/~thoth/blender/python-cookbook/animate-random-spin.html

$\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.