I did an old post on this many years ago, and while it was a handy method it A.) required CHOPs and B.) didn’t account for all of the possible non-transform parameters you’d be dealing with on a camera, like the focal length.

I’ve been writing up some handy buttons for baking out cameras for use with my fancy new MOPs+ Camera Blender, and figured I’d share the basics of this function here. It’s nothing too complex but being able to accurately bake an object, especially a camera, into world space with minimal fuss is a pretty important thing to have in any cross-platform pipeline.

The function is designed to automatically bake the usual relevant channels on a camera. It separates out the transform channels (CAM_XFORM_PARMS) from any other channels (CAM_PARMS) so that during the bake, the transform parameters are baked in the right transform space: you don’t want to just copy the values of the source camera to the destination camera, because the source camera very likely is driven by or parented to something else. Instead, the very handy hou.Object.worldTransform() function is used to fetch the world space transform of the source camera, and hou.Object.setWorldTransform() is used to transfer that over to the new world space camera. For every other parameter, we can just fetch the values of the old camera and apply them directly. This is done frame-by-frame using the hou.Parm.setKeyframe() function. You can supply any additional parameters you might want, like RS_campro_dofDistance if you’re running Redshift, and those parameters will be added to the bake list.

Anyways, the code. If you save this as mops_camera.py anywhere in $HOUDINI_PATH/scripts/python, you can run it via the following:

 
import mops_camera 
source_cam = hou.node("/obj/myCamera") 
mops_camera.bake_camera(source_cam) 

If you have an extra list of attributes, or you want to manually specify start and end frames:

import mops_camera
source_cam = hou.node("/obj/myCamera")
attrs = ["RS_campro_dofDistance", "RS_campro_dofCoC"]
start = 1
end = 240
mops_camera.bake_camera(source_cam, attrs, start, end)

Anyways, here’s the full script:

CAM_XFORM_PARMS = ["tx", "ty", "tz", "rx", "ry", "rz"]
CAM_PARMS = ["resx", "resy", "aspect", "focal", "aperture", "orthowidth", "near", "far", "shutter", "focus", "fstop"]

import hou

def bake_camera(orig_cam, attrs=None, start=None, end=None):
    """
    Bake the selected camera and all relevant attributes into world space.
    :param orig_cam: The camera object to bake.
    :param attrs: An optional list of parameter names to bake in addition to the default CAM_PARMS.
    :param start: The start frame to bake. Defaults to playbar range.
    :param end: The end frame to bake. Defaults to playbar range.
    :return: The baked camera object.
    """

    parms_to_bake = list()
    parms_to_bake.extend(CAM_XFORM_PARMS)
    parms_to_bake.extend(CAM_PARMS)
    if attrs:
        parms_to_bake.extend(attrs)
        parms_to_bake = list(set(parms_to_bake))

    if start is None:
        start = hou.playbar.playbackRange()[0]
    if end is None:
        end = hou.playbar.playbackRange()[1]

    new_cam = hou.node("/obj").createNode("cam", orig_cam.name()+"_BAKED")
    # start iterating over frames and bake all channels.
    with hou.undos.group("Bake camera"):
        for x in range(int(start), int(end+1)):
            hou.setFrame(x)
            # move baked camera to world space transform of original, then set keys.
            new_cam.setWorldTransform(orig_cam.worldTransform())
            for p in parms_to_bake:
                parm = new_cam.parm(p)
                # we want to bake the transform channels based on the new camera's world transform evaluation.
                # other channels should be evaluated from the original camera.
                if parm.name() in CAM_XFORM_PARMS:
                    parm.setKeyframe(hou.Keyframe(parm.eval()))
                else:
                    parm.setKeyframe(hou.Keyframe(orig_cam.parm(p).eval()))
    return new_cam
Categories: HoudiniPython

6 Comments

Matt · 08/12/2021 at 18:23

Hey. Thanks for the write up.
I was looking for a way to do this myself, as a script I wrote for transferring data between Houdini and After Effects couldn’t handle non standard rotation orders.

The solution I found was to use the Bake Animation ROP, which does exactly what your script is doing.

Set source object to your animated camera, target object to the camera you want to bake to, and then list the parameters you want to bake.
Then you check on “Animation from Parameters” (otherwise only transforms will be baked) and “World Transforms”.

Al3ph · 11/30/2022 at 11:20

Hey, thanks a lot for this script ! Strangely it doesn’t works, it only write the actual key of the original cam, all over the timeline, and same thing for the ‘animation rop’ technique mention before…

    toadstorm · 12/01/2022 at 11:59

    Sounds like something weird about your scene, or about the way you’re running the script. I’ve tested it and used it in production multiple times. If you can share a reproduction of your camera, I can try and figure out what’s going wrong.

      Al3ph · 12/04/2022 at 12:55

      Thanks for your answer. In fact it’s an HDA that creates (via python) a camera morph between several cameras. In order to do this, it populates a chop network inside my HDA with camera weights (calculated elsewhere in my HDA) blended together and exported to my Master cam (the one that is the sum of all morphs).
      It is this final camera that your script is applied to.
      I adapted your script to work as a button on my HDA, adapted the variables to conform to my setup (essentially having the correct original camera). However, when I run the script, it indeed bakes a new bakedcam with my whole timeline, the right channels are correctly baked, but only at the current frame, which is baked identically on the whole timeline…
      I guess the hou.setFrame(x) doesn’t loop correctly, but I can’t understand why…

      Al3ph · 12/04/2022 at 16:29

      Ok, after further investigations, it bakes other from other chops (for ex if I manually make a follow path , it bakes the adequate outputed cam movement), but not from the chop inside my HDA – while this chop affects correctly my Mastercam. As if the chop info were short-circuited.

Al3ph · 12/04/2022 at 12:59

It is as if the transformations induced by the chop on my final cam were not taken into account in the baking

Leave a Reply to Al3ph Cancel reply

Your email address will not be published. Required fields are marked *