12
$\begingroup$

I have a default Blender cylinder positioned upright atop a ground plane:

A cylinder atop a ground plane

How can I rig the cylinder so that can be animated to wiggle/jitter (perhaps procedurally with noise f-curve modifiers on two of its rotation axis) without penetrating the floor?

Rotated cylinder on ground plane

A simple parent-child relationship as in the image above is sufficient to rotate (and translate) the cylinder on 1 axis in either a positive OR negative direction.

I'm looking for a setup that would allow me easily to rotate the cylinder on two or even three axis in both a positive and negative direction.

I imagine this would be possible with some drivers applied to the XYZ coordinates using trigonometric equations but the math escapes me. It might also be possible using a particular bone setup with the correct constraints but my attempts to find something that works so far have not succeeded.

Thanks for your assistance.

$\endgroup$
19
+50
$\begingroup$

There is no need for complicated trigonometry or drivers. You can do this with constraints.
Constraints are faster than drivers and have no security problems (manual).

The Cylinder Rig

Create the following bones. (Side view. Note, that the orientations of the bones are all the same.)

enter image description here

CTRL_cylinder has no parent or child. The other bones are in the following hierarchy:
floorRotation > orientation > DEF_cylinder > Cylinder (object)

  • Add a Copy Rotation constraint from CTRL_cylinder to DEF_cylinder.
  • Add a Copy Rotation constraint from CTRL_cylinder to orientation.

enter image description here

  • Add a Locked Track constraint to orientation. The Target is CTRL_cylinder, with Head/Tail at 1. Set the To field to -Z and Lock Y.

enter image description here

  • Add a Copy Location constraint from CTRL_cylinder to floorRotation.
  • Add a Locked Track constraint from CTRL_cylinder to floorRotation.
    Yet again, Head/Tail is at 1. Set the To field to Y and Lock Z.

enter image description here

I suggest the rotation order YZX for CTRL_cylinder. Add a crazy control shape for CTRL_cylinder and set it to be displayed At DEF_cylinder. The animator can move, rotate and roll the cylinder. Optionally, the Y axis can be locked, if no twisting is desired.

finished setup


BONUS CONTENT

Two Axis (Rocker) Setup

This is useful for objects with two or more pivots, like feet, chairs, ... or a cube.

  1. Create the following setup. The CTRL_cube is not in the hierarchy. The other bones are parented as such:
    y_copy > frontPivot > backPivot > DEF_cube > Cube (object)
    enter image description here
  2. The rotation order of CTRL_cube is XYZ with Z locked.
    • Add a Copy Location constraint from CTRL_cube to y_copy.
    • Add a Copy Rotation constraint from CTRL_cube to y_copy with only Y checked and Local Space for Target and Owner.
    • Add a Copy Rotation constraint from the CTRL_cube to frontPivot.
    • Add a Copy Rotation constraint from the CTRL_cube to backPivot. enter image description here
  3. Finally, limit the rotation of the frontPivot, to prevent the cube from going under the floor.
    • Add a Limit Rotation constraint to the frontPivot in Local Space, For Transform and with the X Limits 0 and 90. enter image description here

The result can be moved rotated and rocked from front to back. With additional pivots and their respective Limit Rotation constraints any number of edges can be available for rocking.

enter image description here
Added a stylisch Bone Shape for CTRL_cube At DEF_Cube.

$\endgroup$
  • $\begingroup$ This is fantastic, exactly what I need! Thank you! I cannot award the bounty for another 13 hours but will do so then. $\endgroup$ – BlenderBro Oct 5 '18 at 10:42
  • 1
    $\begingroup$ @BlenderBro Leave the bounty open, someone could come up with an even more fitting answer. $\endgroup$ – Leander Oct 5 '18 at 11:25
6
$\begingroup$

It doesn't seem you would need complex trigonometric equations here. You only wish to keep the object above ground(or on the ground), let's do just that.

Using a Driver

You can set up a driver for z location with a scripted function that finds the lowest vertex and then adjusts the location of the object accordingly:

import bpy

def aboveGround(object_name,floor_z):
    obj = bpy.data.objects[object_name]
    mesh = obj.data
    z = 7 # because it's a lucky number... 
          # ...and 0 seems to be buggy inside the driver for some reason
    for verts in mesh.vertices:
        world_space_co = obj.matrix_world * verts.co
        if world_space_co[2] < z :
            z = world_space_co[2]

    loc_z = obj.location[2] 

    return loc_z - z + floor_z   
#    # or this if we don't want the object to stick to the ground when it's above :
#    if z<floor_z:
#        return loc_z - z + floor_z
#    else:
#        return loc_z

# add function to driver_namespace
bpy.app.driver_namespace['aboveGround'] = aboveGround

You would need to have this in a text block and check Register (Autorun Python Script will need to be enabled in the user preferences as well) so it runs when you open the file:

check Register

You can just run it manually as well.

Then you can create a driver for z location of your object and put the function as the expression: create a driver for z location of your object and put the function as the expression


Using a Scene Update App Handler

It would also be possible to have only a function that would correct the position while working so you could set keyframes. Sort of like a tool. You could use a scene update app handler for that. You could just register it when you need it and then remove it once you don't need it any more:

import bpy


def AboveGroundHandler(scene):
    obj = bpy.data.objects['Plane']
    mesh = obj.data
    floor_z = 0
    z = 7 
    for verts in mesh.vertices:
        world_space_co = obj.matrix_world * verts.co
        if world_space_co[2] < z :
            z = world_space_co[2]

    obj.location[2] = obj.location[2] - z + floor_z   

bpy.app.handlers.scene_update_pre.append(AboveGroundHandler)
#bpy.app.handlers.scene_update_pre.clear() # to remove it(can be run from Python console)
$\endgroup$
  • $\begingroup$ Thanks for great script! I want to say, if driver expression produces error after opening a file, just add ".py" after script name in text editor (w/o quotes) $\endgroup$ – Serge L Oct 5 '18 at 9:44
  • $\begingroup$ (I wrote a trig. version of your driver, but for cylinders only, so it's not as general as yours, and not worth posting.).. What triggers a scene_update_pre handler? Would it work during interpolation between key frames? $\endgroup$ – Robin Betts Oct 5 '18 at 9:49
  • $\begingroup$ I believe drivers are intended for use in animation, I think it would be better to use them. I just thought, that if there is a cylinder wiggling it might bounce up and down so if one wanted to animate it manually, that could be simplified using this handler method as a simple tool for just placing the object in correct positions while the keyframes are set - that was the thought process. I think it might lead to unexpected results if used in animation. $\endgroup$ – Martynas Žiemys Oct 5 '18 at 9:58
  • $\begingroup$ A really interesting, general purpose solution, thank you! I gotta go with Leander's constraint setup though due to its comparative ease of use, and for the reasons stated at the top of their answer. $\endgroup$ – BlenderBro Oct 5 '18 at 10:48
  • $\begingroup$ Leander's solution is really great. That would probably be my choice as well. I don't do much animation, so I did not think of it. $\endgroup$ – Martynas Žiemys Oct 5 '18 at 11:07

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.