{"id":827,"date":"2021-06-16T15:48:37","date_gmt":"2021-06-16T22:48:37","guid":{"rendered":"http:\/\/www.toadstorm.com\/blog\/?p=827"},"modified":"2021-10-06T19:25:06","modified_gmt":"2021-10-07T02:25:06","slug":"baking-cameras-in-houdini-again","status":"publish","type":"post","link":"https:\/\/www.toadstorm.com\/blog\/?p=827","title":{"rendered":"Baking cameras in Houdini (again)"},"content":{"rendered":"\n<p>I did an <a href=\"https:\/\/www.toadstorm.com\/blog\/?p=267\">old post<\/a> on this many years ago, and while it was a handy method it A.) required CHOPs and B.) didn&#8217;t account for all of the possible non-transform parameters you&#8217;d be dealing with on a camera, like the focal length.<\/p>\n\n\n\n<p>I&#8217;ve been writing up some handy buttons for baking out cameras for use with my fancy new <a href=\"https:\/\/twitter.com\/motionoperators\/status\/1404981912603566080\">MOPs+ Camera Blender<\/a>, and figured I&#8217;d share the basics of this function here. It&#8217;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.<\/p>\n\n\n\n<p>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&#8217;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 <code>hou.Object.worldTransform()<\/code> function is used to fetch the world space transform of the source camera, and <code>hou.Object.setWorldTransform()<\/code> 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 <code>hou.Parm.setKeyframe()<\/code> function. You can supply any additional parameters you might want, like <code>RS_campro_dofDistance<\/code> if you&#8217;re running Redshift, and those parameters will be added to the bake list.<\/p>\n\n\n\n<p>Anyways, the code. If you save this as <code>mops_camera.py<\/code> anywhere in $HOUDINI_PATH\/scripts\/python, you can run it via the following:<\/p>\n\n\n\n<pre class=\"brush: python; title: ; notranslate\" title=\"\"> \nimport mops_camera \nsource_cam = hou.node(&quot;\/obj\/myCamera&quot;) \nmops_camera.bake_camera(source_cam) \n<\/pre>\n\n\n\n<p>If you have an extra list of attributes, or you want to manually specify start and end frames:<\/p>\n\n\n\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\nimport mops_camera\nsource_cam = hou.node(&quot;\/obj\/myCamera&quot;)\nattrs = &#x5B;&quot;RS_campro_dofDistance&quot;, &quot;RS_campro_dofCoC&quot;]\nstart = 1\nend = 240\nmops_camera.bake_camera(source_cam, attrs, start, end)\n<\/pre>\n\n\n\n<p>Anyways, here&#8217;s the full script:<\/p>\n\n\n\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\nCAM_XFORM_PARMS = &#x5B;&quot;tx&quot;, &quot;ty&quot;, &quot;tz&quot;, &quot;rx&quot;, &quot;ry&quot;, &quot;rz&quot;]\nCAM_PARMS = &#x5B;&quot;resx&quot;, &quot;resy&quot;, &quot;aspect&quot;, &quot;focal&quot;, &quot;aperture&quot;, &quot;orthowidth&quot;, &quot;near&quot;, &quot;far&quot;, &quot;shutter&quot;, &quot;focus&quot;, &quot;fstop&quot;]\n\nimport hou\n\ndef bake_camera(orig_cam, attrs=None, start=None, end=None):\n    &quot;&quot;&quot;\n    Bake the selected camera and all relevant attributes into world space.\n    :param orig_cam: The camera object to bake.\n    :param attrs: An optional list of parameter names to bake in addition to the default CAM_PARMS.\n    :param start: The start frame to bake. Defaults to playbar range.\n    :param end: The end frame to bake. Defaults to playbar range.\n    :return: The baked camera object.\n    &quot;&quot;&quot;\n\n    parms_to_bake = list()\n    parms_to_bake.extend(CAM_XFORM_PARMS)\n    parms_to_bake.extend(CAM_PARMS)\n    if attrs:\n        parms_to_bake.extend(attrs)\n        parms_to_bake = list(set(parms_to_bake))\n\n    if start is None:\n        start = hou.playbar.playbackRange()&#x5B;0]\n    if end is None:\n        end = hou.playbar.playbackRange()&#x5B;1]\n\n    new_cam = hou.node(&quot;\/obj&quot;).createNode(&quot;cam&quot;, orig_cam.name()+&quot;_BAKED&quot;)\n    # start iterating over frames and bake all channels.\n    with hou.undos.group(&quot;Bake camera&quot;):\n        for x in range(int(start), int(end+1)):\n            hou.setFrame(x)\n            # move baked camera to world space transform of original, then set keys.\n            new_cam.setWorldTransform(orig_cam.worldTransform())\n            for p in parms_to_bake:\n                parm = new_cam.parm(p)\n                # we want to bake the transform channels based on the new camera's world transform evaluation.\n                # other channels should be evaluated from the original camera.\n                if parm.name() in CAM_XFORM_PARMS:\n                    parm.setKeyframe(hou.Keyframe(parm.eval()))\n                else:\n                    parm.setKeyframe(hou.Keyframe(orig_cam.parm(p).eval()))\n    return new_cam\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I did an old post on this many years ago, and while it was a handy method it A.) required CHOPs and B.) didn&#8217;t account for all of the possible non-transform parameters you&#8217;d be dealing with on a camera, like the focal length. I&#8217;ve been writing up some handy buttons [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[30,6],"tags":[],"_links":{"self":[{"href":"https:\/\/www.toadstorm.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/827"}],"collection":[{"href":"https:\/\/www.toadstorm.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.toadstorm.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.toadstorm.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.toadstorm.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=827"}],"version-history":[{"count":12,"href":"https:\/\/www.toadstorm.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/827\/revisions"}],"predecessor-version":[{"id":842,"href":"https:\/\/www.toadstorm.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/827\/revisions\/842"}],"wp:attachment":[{"href":"https:\/\/www.toadstorm.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=827"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.toadstorm.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=827"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.toadstorm.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=827"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}