Real-Time Viewport Recorder
This python script records the manual movement of geometry with the mouse in the viewport over the time and creates keyframes for Transformation/Rotation/Scale.
Some of the features:
– Provide the option to record specific parameters(Transformation/ Rotation/ Scale)
– Provide delete keyframe option for specific parameters(Transformation/ Rotation/ Scale)
– option to specify keyframe on every Nth frame
My work process
When you install the digital asset, on the UI you can select what parameters to bake. In addition, the every Nth frame
First First I have tried to use the For loop to create keyframes on each frame, so I wrote the following code, but the problem with for loop is that it only runs within one frame, not over time. So it only records the parameter values on the first frame
node = hou.pwd()
# Add code to modify contained geometries.
# Use drop down menu to select examples.
import hou
import time
hou.maxThreads()
sel = hou.selectedNodes()
buff = “”
if len(sel) < 1:
buff = “No Nodes Selected”
hou.ui.displayMessage(buff)
else:
parmeter = []
node = sel[0]
parmeter.append(node.parm(“tx”))
parmeter.append(node.parm(“ty”))
parmeter.append(node.parm(“tz”))
parmeter.append(node.parm(“rx”))
parmeter.append(node.parm(“ry”))
parmeter.append(node.parm(“rz”))
parmeter.append(node.parm(“sx”))
parmeter.append(node.parm(“sy”))
parmeter.append(node.parm(“sz”))
parmeterlist = []
parametername = []
for i in parmeter:
parmeterlist.append(i)
parametername.append(i.description() + “(” + i.name() + “)”)
record = hou.ui.selectFromList(parametername, message=’Choose parameters to record keys’)
timeline = range(int(hou.expandString(‘$RFSTART’)), int(hou.expandString(‘$RFEND’)) + 1 )
for i in record:
Value = {}
for f in timeline:
Value[f] = ParmList[i].evalAtFrame(f)
parmeterlist[i].deleteAllKeyframes()
for f in timeline:
setKey = hou.Keyframe()
setKey.setFrame(f)
setKey.setValue( Value[f] )
parmeterlist[i].setKeyframe(setKey)
hou.setFrame(f)
After doing more research I find out that I have to use my python code as a solver. So simply by creating making a python node in SOP level and writing the following code I was able to add the current Transformation/Rotation/Scale to a list over the time
node = hou.pwd()
geo = node.geometry()
# Add code to modify contents of geo.
# Use drop down menu to select examples.
import hou
import time
hou.maxThreads()
sel = hou.selectedNodes()
parms = []
node = sel[0]
currentposition = []
currentposition.append(hou.Vector3(node.parm(‘tx’).eval(), node.parm(‘ty’).eval(), node.parm(‘tz’).eval()))
currentposition.append(hou.Vector3(node.parm(‘rx’).eval(), node.parm(‘ry’).eval(), node.parm(‘rz’).eval()))
currentposition.append(hou.Vector3(node.parm(‘sx’).eval(), node.parm(‘sy’).eval(), node.parm(‘sz’).eval()))
print currentposition[0]
Since the code constantly was adding the current Transformation/Rotation/Scale of moving object a list, I was able to play the timeline bake the current position on each frame. In addition, I added some features to the code like bake only specified parameters, or delete baked keyframes for specified parameters, and keyframe on every Nth frame.
# This code is called when instances of this object cook.
# Call hou.pwd().setCookTransform(matrix).
# – kwargs[‘cooktime’] = evaluation time (may be different than global time)
node = hou.pwd()
# Add code to modify contained geometries.
# Use drop down menu to select examples.
import hou
recorder = hou.node(‘/obj/recorder1’)
nth = recorder.parm(“parm2”).eval()
sel = hou.selectedNodes()
node = sel[0]
currentpos = []
currentpos.append(hou.Vector3(node.parm(‘tx’).eval(), node.parm(‘ty’).eval(), node.parm(‘tz’).eval()))
currentpos.append(hou.Vector3(node.parm(‘rx’).eval(), node.parm(‘ry’).eval(), node.parm(‘rz’).eval()))
currentpos.append(hou.Vector3(node.parm(‘sx’).eval(), node.parm(‘sy’).eval(), node.parm(‘sz’).eval()))
currentframe = hou.frame()
setKey = hou.Keyframe()
setKey.setExpression(“bezier()”, hou.exprLanguage.Hscript)
if(currentframe%nth == 0):
setKey.setFrame(hou.frame())
#———-position———–
if (recorder.parm(“transformation”).eval() == 1):
cpx = currentpos[0][0]
setKey.setValue(cpx)
setKey.setInAccel(1.0)
setKey.setAccel(1.0)
setKey.setInSlopeAuto(True)
setKey.setSlopeAuto(True)
objposx = node.parm(‘tx’)
objposx.setKeyframe(setKey)
cpy = currentpos[0][1]
setKey.setValue(cpy)
setKey.setInAccel(1.0)
setKey.setAccel(1.0)
setKey.setInSlopeAuto(True)
setKey.setSlopeAuto(True)
objposy = node.parm(‘ty’)
objposy.setKeyframe(setKey)
cpz = currentpos[0][2]
setKey.setValue(cpz)
setKey.setInAccel(1.0)
setKey.setAccel(1.0)
setKey.setInSlopeAuto(True)
setKey.setSlopeAuto(True)
objposz = node.parm(‘tz’)
objposz.setKeyframe(setKey)
if (recorder.parm(“deltrans”).eval() == 1):
removeposkey = node.parmTuple(“t”)
removeposkey.deleteAllKeyframes()
#———-Rotation———–
if (recorder.parm(“rotation”).eval() == 1):
crx = currentpos[1][0]
setKey.setValue(crx)
setKey.setInAccel(1.0)
setKey.setAccel(1.0)
setKey.setInSlopeAuto(True)
setKey.setSlopeAuto(True)
objrotx = node.parm(‘rx’)
objrotx.setKeyframe(setKey)
cry = currentpos[1][1]
setKey.setValue(cry)
setKey.setInAccel(1.0)
setKey.setAccel(1.0)
setKey.setInSlopeAuto(True)
setKey.setSlopeAuto(True)
objroty = node.parm(‘ry’)
objroty.setKeyframe(setKey)
crz = currentpos[1][2]
setKey.setValue(crz)
setKey.setInAccel(1.0)
setKey.setAccel(1.0)
setKey.setInSlopeAuto(True)
setKey.setSlopeAuto(True)
objrotz = node.parm(‘rz’)
objrotz.setKeyframe(setKey)
if (recorder.parm(“delrot”).eval() == 1):
removerotkey = node.parmTuple(“r”)
removerotkey.deleteAllKeyframes()
#———-Scale———–
if (recorder.parm(“scale2”).eval() == 1):
csx = currentpos[2][0]
setKey.setValue(csx)
setKey.setInAccel(1.0)
setKey.setAccel(1.0)
setKey.setInSlopeAuto(True)
setKey.setSlopeAuto(True)
objscalex = node.parm(‘sx’)
objscalex.setKeyframe(setKey)
csy = currentpos[2][1]
setKey.setValue(csy)
setKey.setInAccel(1.0)
setKey.setAccel(1.0)
setKey.setInSlopeAuto(True)
setKey.setSlopeAuto(True)
objscaley = node.parm(‘sy’)
objscaley.setKeyframe(setKey)
csz = currentpos[2][2]
setKey.setValue(csz)
setKey.setInAccel(1.0)
setKey.setAccel(1.0)
setKey.setInSlopeAuto(True)
setKey.setSlopeAuto(True)
objscalez = node.parm(‘sz’)
objscalez.setKeyframe(setKey)
if (recorder.parm(“delscale”).eval() == 1):
removesclkey = node.parmTuple(“s”)
removesclkey.deleteAllKeyframes()
Converting the script to Digital Asset
When I had my script as a SOP python node, the keyframing speed was not as fast as playing timeline. Surprisingly, when I converted my script to Houdini Digital Asset, it was baking the object movement much faster than before.
Conclusion
At the beginning, it was very challenging to learn different python functions in Houdini. Because resources about python in Houdini are a bit limited. The best resources that I found was on Sidefx and Odforce forums. Also, Houdini documentations about python is another good resource. I am pretty happy with the result of my project, however, my code is not recording properly the camera animation. I recently did more research about hou.Vector4 function and how to store the viewport data in a matrix. Therefore, I am planning to improve my code in the next version. Finally, I enjoyed and learned a lot about python programming in Houdini through this project. You can download the HDA from here